在 Android KitKat 中接收彩信

2024-01-16

所以这个视频Android 4.4 短信 API http://www.youtube.com/watch?v=mdq0R2WQssQ#DevBytes 解释了 KitKat 中 SMS API 的最新变化。他们还提供了示例项目的链接。http://goo.gl/uQ3Nih http://goo.gl/uQ3Nih

他们建议您在服务中处理彩信的接收。一切看起来都很好,除了他们忽略提及最没有记录的部分。如何实际处理传入的彩信。

这是该项目的示例https://gist.github.com/lawloretienne/8970938 https://gist.github.com/lawloretienne/8970938

我尝试过“处理彩信”

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

我可以从意图中获取额外信息,但我可以提取的唯一有意义的信息是发送彩信的号码。

谁能指出我如何解决这个问题的正确方向?

我注意到 WAP_PUSH_MESSAGE 包含一些内容,FROM、SUBJECT 和 CONTENT_LOCATION。

内容位置似乎是包含彩信内容的 URL。我怎样才能访问这个?

这是该 URL 的示例

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

其中 X 是我正在测试的设备的电话号码中的数字。

看起来美国 T-Mobile 的 MMSC(多媒体消息服务中心)是http://mms.msg.eng.t-mobile.com/mms/wapenc

根据这个清单:http://www.activeexperts.com/xmstoolkit/mmsclist/ http://www.activexperts.com/xmstoolkit/mmsclist/


文档为零,因此这里有一些信息可以提供帮助。

1) com.google.android.mms.pdu 来自源代码。您需要 Pdu 实用程序。

2) 您从传入的 mms 广播的字节数组 extra 中获取通知推送 (intent.getByteArrayExtra("data"))。

3) 将通知推送解析为 GenericPdu (new PduParser(rawPdu).parse())。

4) 您需要 TransactionSettings 来与运营商的 wap 服务器进行通信。我在下面#5 之后得到了交易设置。我用:

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

5) 强制通过 wifi 进行网络通信。我使用以下内容。

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;
}

我还喜欢使用基于反射的方法来改进 EnsureRouteToHost 功能:

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) 使用 Pdu 解析器从生成的字节数组中解析 GenericPdu。然后您可以提取正文并投射到多媒体消息 Pdu。

9) 然后您可以迭代 PDU 的各个部分。

使用彩信有无数的事情需要考虑。我想到的一件事是幻灯片是多么烦人,所以我所做的就是检测 PDU 中是否有超过 1 个部分,然后复制标头并创建单独的 MultimediaMessagePdu,将它们分别保存到手机的彩信内容提供商。不要忘记复制标题,特别是如果您支持群组消息传递。群组消息传送则是另一回事,因为 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 通信,除非用户确实想要彩信交付报告。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Android KitKat 中接收彩信 的相关文章

随机推荐

  • 如何避免时差为负时间?

    我正在开发一个使用 Java8 Time 的应用程序 我面临一个问题 假设时间 A 是 08 00 时间 B 是 17 00 那么这两个时间之间的差异将是 9 小时 在我的情况下这是正确的 但如果时间 A 是 18 00 时间 B 是 02
  • 使用绝对时区的Javascript倒计时?

    我有一个 javascript 倒计时器 它通过获取指定的日期和时间并将其与当前日期和时间进行比较来工作 问题是 当前时间是相对于用户时区的 因此用户之间的剩余时间是不同的 如何让计时器倒计时到特定时区的某个时间 在我的例子中为 GMT 5
  • Java 的 MessageFormat 未本地化小写日期中的葡萄牙月份

    月份名称以大写字母而不是小写字母开头 正如他们应该的那样 https meta wikimedia org wiki Capitalization of Wiktionary pages Capitalization of month na
  • 关系的时间复杂度 T(n) = T(n-1) + T(n/2) + n

    对于关系 T n T n 1 T n 2 n 我可以先解出项 T n 1 n 它给出 O n 2 然后解出项 T n 2 O n 2 吗 根据主定理 它也给出了 O n 2 或者它是错误的 不 你不能用主定理来解决它 你需要使用来解决它阿克
  • 创建一个对象,其类型在字符串参数中

    我有一个 TreeView 其中包含节点 当用户单击某个节点时 应创建并显示相应的 CrystalReport 文档 例如 我的节点名称是 PeriodReport1 PeriodReport2 PeriodReport3 我的 Cryst
  • 无法找到并单击条件条款按钮

    我是 pupeeteer 的新手 首先我要做的是加载页面并单击按钮 但是 它无法找到该元素 我认为这是因为我需要找到父元素或父元素的父元素
  • Asp.net Mvc 4 和 Web Api 中的路由

    我可以同时使用以下两个路由规则吗 config Routes MapHttpRoute name ActionApi routeTemplate api controller action id defaults new id RouteP
  • IE11 中奇怪的 @font-face 问题(仅在您检查开发人员工具中的元素后才呈现)

    我有一些网络字体可以在除 IE11 之外的所有浏览器中正常工作 奇怪的是 如果你进入这个页面 然后使用 IE11 开发人员工具检查 功能 要点 突然所有字体都可以工作了 有任何想法吗 font face font family Helvet
  • POSIX“tee”命令如何工作?

    tee newOutputFile lt existingInputFile gt newOutputFile2 究竟会怎样tee接受论点 会是这样吗 Tee将首先处理newOutputFile lt existingInputFile所以
  • CSS3连续旋转动画(就像加载日晷)

    我正在尝试使用 PNG 和 CSS3 动画来复制 Apple 风格的活动指示器 日晷加载图标 我让图像旋转并连续进行 但动画完成后在进行下一次旋转之前似乎有延迟 webkit keyframes rotate from webkit tra
  • JfreeChart 中的自动缩放 Y 轴

    我在用JFreeChart在我的 Java 应用程序中创建蜡烛图 然而 我的图表最终看起来像这样 http imageshack us photo my images 69 capturebhx png http imageshack us
  • 出现分段错误

    我看到很多关于出现分段错误在SO中的C程序中 我认为参考这里的内容会很棒 这是一个涉及某些导致分段错误的情况的问题 我的答案发布在下面 正如一些答案中所写 所有情况下的行为都是未定义的 尽管很多人都将他们视为分段故障 所以这个问题是关于是什
  • 如何有条件地使用“平面图”运算符? (角度2/rxjs)

    我想要实现的是有条件地运行一系列可观察的结果 return observable map response gt response flatmap response1 gt observable1 response1 flatmap res
  • 找不到符号 if 语句错误

    我一直在编写一个小程序来娱乐 但我收到了这个错误 Compilation error time 0 11 memory 380672 signal 0Main java 22 error cannot find symbol string
  • 从列表中选择 5 个不同的元素?

    从 python 列表中选择 5 个不同元素并将它们添加到新列表中的最佳方法是什么 谢谢您的帮助 假设您希望随机选择它们并且new list已经定义了 import random new list random sample old lis
  • 如何实现隐式方法的中间类型?

    假设我想提供方法foo在现有类型上A在我的控制之外 据我所知 在 Scala 中执行此操作的规范方法是实现从A到某种实现的类型foo 现在我基本上看到两个选择 为此目的定义一个单独的 甚至可能是隐藏的类 protected class Fo
  • 当用户拒绝地理定位服务时,我会收到拒绝事件吗?

    我的意思是 如果我在网页上有一个按钮 当用户单击该按钮时 然后就可以获取用户的地理位置信息 但在此之前 浏览器会询问用户是否接受使用地理定位服务 如果用户拒绝地理定位服务 我可以获得拒绝事件吗 Yes Example navigator g
  • 如何通过nodejs实现APNS通知?

    现在有人有一个很好的 npm 模块来实现 Apple PUSH 通知吗 一个简单的例子就很好了 我找到的解决方案如下 它使用apn module var apn require apn var ca entrust 2048 ca cer
  • 将一个函数中的可变参数传递给 D 中的另一个函数

    我有一个可变的 D 风格函数foo format 这是一个包装器writefln 我想做这样的事情 foo format lt gt writefln format 本质上 将省略号参数传递给 writefln 我知道这在 C C 中并不容
  • 在 Android KitKat 中接收彩信

    所以这个视频Android 4 4 短信 API http www youtube com watch v mdq0R2WQssQ DevBytes 解释了 KitKat 中 SMS API 的最新变化 他们还提供了示例项目的链接 http