Получать MMS-сообщения в Android KitKat

Итак, это видео Android 4.4 SMS API из #DevBytes объясняет последние изменения в API SMS в KitKat. Они также предоставляют ссылку на образец проекта. http://goo.gl/uQ3Nih

Они предлагают вам обрабатывать получение MMS в службе. Который все выглядит хорошо, за исключением того, что они пренебрегают упоминанием самой недокументированной пьесы. Как фактически обрабатывать входящие MMS.

Вот образец из проекта https://gist.github.com/lawloretienne/8970938

Я пытался "обрабатывать MMS"

https://gist.github.com/lawloretienne/8971050

Я могу получить дополнительные функции от намерения, но единственная значимая вещь, которую я могу извлечь, - это номер, из которого было отправлено MMS.

Может ли кто-нибудь указать мне в правильном направлении, как это сделать?

Я заметил, что WAP_PUSH_MESSAGE содержит несколько вещей: FROM, SUBJECT и CONTENT_LOCATION.

Расположение содержимого - это URL-адрес, в котором содержится содержимое MMS. Как я могу получить к нему доступ?

Вот пример этого URL

https://atl1mmsget.msg.eng.t-mobile.com/mms/wapenc?location=XXXXXXXXXXX_14zbwk&rid=027

Где X - это цифра на номер телефона устройства, на котором я тестирую.

Похоже, что служба MMSC (Центр обслуживания мультимедийных сообщений) для T-Mobile в США - http://mms.msg.eng.t-mobile.com/mms/wapenc

В соответствии с этим списком: http://www.activexperts.com/xmstoolkit/mmsclist/

Ответ 1

Там есть нулевая документация, поэтому здесь можно найти некоторую информацию.

1) com.google.android.mms.pdu из источника. Вам нужны утилиты Pdu.

2) Вы получаете извещение уведомления от байтового массива дополнительно от входящего широковещательного сообщения mms (intent.getByteArrayExtra( "данные" )).

3) Разберите уведомление в GenericPdu (новый PduParser (rawPdu).parse()).

4) Для связи с WAP-сервером оператора вам понадобятся TransactionSettings. Я получаю настройки транзакции после №5 ниже. Я использую:

TransactionSettings transactionSettings = new TransactionSettings(mContext, mConnMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_MMS).getExtraInfo());

5) Соедините сеть с Wi-Fi. Я использую следующее.

private boolean beginMmsConnectivity() {
    try {
        int result = mConnMgr.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_MMS);
        NetworkInfo info = mConnMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_MMS);
        boolean isAvailable = info != null && info.isConnected() && result == Phone.APN_ALREADY_ACTIVE && !Phone.REASON_VOICE_CALL_ENDED.equals(info.getReason());
        return isAvailable;
    } catch(Exception e) {
        return false;
    }
}

6) Затем вам необходимо обеспечить маршрут к хосту.

private static void ensureRouteToHost(ConnectivityManager cm, String url, TransactionSettings settings) throws IOException {
    int inetAddr;
    if (settings.isProxySet()) {
        String proxyAddr = settings.getProxyAddress();
        inetAddr = lookupHost(proxyAddr);
        if (inetAddr == -1) {
            throw new IOException("Cannot establish route for " + url + ": Unknown host");
        } else {
            if (!cm.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_MMS, inetAddr))
                throw new IOException("Cannot establish route to proxy " + inetAddr);
        }
    } else {
        Uri uri = Uri.parse(url);
        inetAddr = lookupHost(uri.getHost());
        if (inetAddr == -1) {
            throw new IOException("Cannot establish route for " + url + ": Unknown host");
        } else {
            if (!cm.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_MMS, inetAddr))
                throw new IOException("Cannot establish route to " + inetAddr + " for " + url);
        }
    }
}

Здесь метод lookupHost:

private static int lookupHost(String hostname) {
    InetAddress inetAddress;
    try {
        inetAddress = InetAddress.getByName(hostname);
    } catch (UnknownHostException e) {
        return -1;
    }
    byte[] addrBytes;
    int addr;
    addrBytes = inetAddress.getAddress();
    addr = ((addrBytes[3] & 0xff) << 24) | ((addrBytes[2] & 0xff) << 16) | ((addrBytes[1] & 0xff) << 8) | (addrBytes[0] & 0xff);
    return addr;
}

Мне также нравится использовать метод, основанный на отражении, для улучшения функции обеспечения RouteToHost:

private static void ensureRouteToHostFancy(ConnectivityManager cm, String url, TransactionSettings settings) throws IOException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    Method m = cm.getClass().getMethod("requestRouteToHostAddress", new Class[] { int.class, InetAddress.class });
    InetAddress inetAddr;
    if (settings.isProxySet()) {
        String proxyAddr = settings.getProxyAddress();
        try {
            inetAddr = InetAddress.getByName(proxyAddr);
        } catch (UnknownHostException e) {
            throw new IOException("Cannot establish route for " + url + ": Unknown proxy " + proxyAddr);
        }
        if (!(Boolean) m.invoke(cm, new Object[] { ConnectivityManager.TYPE_MOBILE_MMS, inetAddr }))
            throw new IOException("Cannot establish route to proxy " + inetAddr);
    } else {
        Uri uri = Uri.parse(url);
        try {
            inetAddr = InetAddress.getByName(uri.getHost());
        } catch (UnknownHostException e) {
            throw new IOException("Cannot establish route for " + url + ": Unknown host");
        }
        if (!(Boolean) m.invoke(cm, new Object[] { ConnectivityManager.TYPE_MOBILE_MMS, inetAddr }))
            throw new IOException("Cannot establish route to " + inetAddr + " for " + url);
    }
}

7) После обеспечения маршрута к хосту вам может понадобиться HttpUtls из источника. Я сильно изменил мою реализацию, используя OkHttp для улучшения связи.

byte[] rawPdu = HttpUtils.httpConnection(mContext, mContentLocation, null, HttpUtils.HTTP_GET_METHOD, mTransactionSettings.isProxySet(), mTransactionSettings.getProxyAddress(), mTransactionSettings.getProxyPort());

8). Из полученного массива байтов используйте PduParser для оплаты GenericPdu. Затем вы можете извлечь тело и бросить на MultimediaMessagePdu.

9) Затем вы можете выполнять итерацию частей PDU.

Есть множество вещей, которые следует учитывать при использовании MMS. Одна вещь, которая приходит на ум, - это то, как раздражают слайд-шоу, поэтому я обнаруживаю, что в PDU есть более 1 части, затем я копирую заголовки и создаю отдельный MultimediaMessagePdu, из которых я сохраняю их в телефоне., Не забудьте скопировать заголовки, особенно если вы поддерживаете групповые сообщения. Групповая передача сообщений - еще одна история, потому что номер телефона incomging в PDU не рассказывает всю историю (MultimediaMessagePdu.mmpdu()). Там больше контактов в заголовке, который вы извлекаете, используя следующий код.

private HashSet<String> getRecipients(GenericPdu pdu) {
    PduHeaders header = pdu.getPduHeaders();
    HashMap<Integer, EncodedStringValue[]> addressMap = new HashMap<Integer, EncodedStringValue[]>(ADDRESS_FIELDS.length);
    for (int addrType : ADDRESS_FIELDS) {
        EncodedStringValue[] array = null;
        if (addrType == PduHeaders.FROM) {
            EncodedStringValue v = header.getEncodedStringValue(addrType);
            if (v != null) {
                array = new EncodedStringValue[1];
                array[0] = v;
            }
        } else {
            array = header.getEncodedStringValues(addrType);
        }
        addressMap.put(addrType, array);
    }
    HashSet<String> recipients = new HashSet<String>();
    loadRecipients(PduHeaders.FROM, recipients, addressMap, false);
    loadRecipients(PduHeaders.TO, recipients, addressMap, true);
    return recipients;
}

Здесь метод получателей нагрузки:

private void loadRecipients(int addressType, HashSet<String> recipients, HashMap<Integer, EncodedStringValue[]> addressMap, boolean excludeMyNumber) {
    EncodedStringValue[] array = addressMap.get(addressType);
    if (array == null) {
        return;
    }
    // If the TO recipients is only a single address, then we can skip loadRecipients when
    // we're excluding our own number because we know that address is our own.
    if (excludeMyNumber && array.length == 1) {
        return;
    }
    String myNumber = excludeMyNumber ? mTelephonyManager.getLine1Number() : null;
    for (EncodedStringValue v : array) {
        if (v != null) {
            String number = v.getString();
            if ((myNumber == null || !PhoneNumberUtils.compare(number, myNumber)) && !recipients.contains(number)) {
                // Only add numbers which aren't my own number.
                recipients.add(number);
            }
        }
    }
}

Здесь, как итерировать части MultimediaMessagePdu.

private void processPduAttachments() throws Exception {
    if (mGenericPdu instanceof MultimediaMessagePdu) {
        PduBody body = ((MultimediaMessagePdu) mGenericPdu).getBody();
        if (body != null) {
            int partsNum = body.getPartsNum();
            for (int i = 0; i < partsNum; i++) {
                try {
                    PduPart part = body.getPart(i);
                    if (part == null || part.getData() == null || part.getContentType() == null || part.getName() == null)
                        continue;
                    String partType = new String(part.getContentType());
                    String partName = new String(part.getName());
                    Log.d("Part Name: " + partName);
                    Log.d("Part Type: " + partType);
                    if (ContentType.isTextType(partType)) {
                    } else if (ContentType.isImageType(partType)) {
                    } else if (ContentType.isVideoType(partType)) {
                    } else if (ContentType.isAudioType(partType)) {
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    // Bad part shouldn't ruin the party for the other parts
                }
            }
        }
    } else {
        Log.d("Not a MultimediaMessagePdu PDU");
    }
}

Есть много других соображений, таких как анимированная поддержка GIF, что вполне возможно:) Некоторые операторы поддерживают отчеты о подтверждении и отчеты о доставке тоже, вы, скорее всего, пренебрегаете этими wap-коммуникациями, если пользователь действительно не хочет получать отчеты о доставке mms.