草庐IT

android - 在 Android KitKat 中接收彩信

coder 2023-11-18 原文

所以这个视频Android 4.4 SMS APIs来自#DevBytes 的文章解释了 KitKat 中 SMS API 的最新变化。他们还提供了示例项目的链接。 http://goo.gl/uQ3Nih

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

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

我试过“处理彩信”

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

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

谁能为我指出正确的方向,告诉我如何去做?

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

内容位置似乎是包含 MMS 内容的 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.activexperts.com/xmstoolkit/mmsclist/

最佳答案

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

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

2) 您从传入的 mms 广播的额外字节数组中获得通知推送 (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) 从生成的字节数组中使用 PduParser 来解析 GenericPdu。然后您可以提取正文并转换为 MultimediaMessagePdu。

9) 然后你可以迭代PDU的部分。

使用 MMS 需要考虑的事情数不胜数。我想到的一件事是幻灯片有多烦人,所以我要做的是检测 PDU 中是否有超过 1 个部分,然后我复制标题并创建单独的 MultimediaMessagePdu,我将它们分别保存到手机的彩信内容提供程序.不要忘记复制标题,特别是如果您支持群发消息。群组消息传递是另一回事,因为 PDU 中的传入电话号码并不能说明全部情况 (MultitimediaMessagePdu.mmpdu())。您使用以下代码提取的 header 中有更多联系人。

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 交付报告。

关于android - 在 Android KitKat 中接收彩信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21748209/

有关android - 在 Android KitKat 中接收彩信的更多相关文章

  1. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  2. ruby-on-rails - 如何使用 Rack 接收 JSON 对象 - 2

    我有一个非常简单的RubyRack服务器,例如:app=Proc.newdo|env|req=Rack::Request.new(env).paramspreq.inspect[200,{'Content-Type'=>'text/plain'},['Somebody']]endRack::Handler::Thin.run(app,:Port=>4001,:threaded=>true)每当我使用JSON对象向服务器发送POSTHTTP请求时:{"session":{"accountId":String,"callId":String,"from":Object,"headers":

  3. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

  4. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  5. ruby - 如何理解 Ruby 中的发送者和接收者? - 2

    我很难理解Ruby中sender和receiver的实际含义。它们一般是什么意思?到目前为止,我只是将它们理解为方法调用和获取其返回值的调用。但是,我知道我的理解还远远不够。谁能给我一个Ruby中发送者和接收者的具体解释? 最佳答案 面向对象中的一个核心概念是消息传递和早期概念化,这在很大程度上借鉴了计算的Actor模型。艾伦·凯(AlanKay)创造了面向对象一词并发明了最早的OO语言之一SmallTalk,他拥有voicedregretatusingatermwhichputthefocusonobjectsinsteadofo

  6. ruby - 接收 block 作为参数的模拟方法 - 2

    我有一个或多或少这样的场景classAdefinitialize(&block)b=B.new(&block)endend我正在对A类进行单元测试,我想知道B#new是否正在接收传递给A#new的block。我使用Mocha作为模拟框架。这可能吗? 最佳答案 我用Mocha和RSpec都试过了,虽然我可以通过测试,但行为不正确。从我的实验中,我得出结论,验证block是否已通过是不可能的。问题:为什么要传递一个block作为参数?block将用于什么目的?什么时候调用?也许这确实是您应该用类似的东西测试的行为:classBlockP

  7. ruby-on-rails - Rails 5 Rspec 接收 ActionController::Params - 2

    我刚刚升级到Rails5。在我的规范中,我有以下内容期望(模型).toreceive(:update).with(foo:'bar')但是,由于params不再扩展Hash而现在是ActionController::Parameters规范失败了,因为with()期待一个散列,但它实际上是ActionController::Parameters是否有更好的方法在Rspec中做同样的事情,例如不同的方法with_hash?我可以使用解决这个问题expect(model).toreceive(:update).with(hash_including(foo:'bar'))但这只是检查参数是

  8. ruby - 如果 `self` 始终是 Ruby 中的隐含接收者,为什么 `self.puts` 不起作用? - 2

    在Ruby中,我的理解是self是任何裸方法调用的隐含接收者。然而:~:irb>>puts"foo"foo=>nil>>self.puts"foo"NoMethodError:privatemethod`puts'calledformain:Object这是什么原因?如果有任何帮助:>>method(:puts).owner=>Kernel 最佳答案 私有(private)方法不能有接收者我认为答案是这样的:Ruby强制方法隐私的方式是它不允许使用显式接收者调用私有(private)方法。一个例子:classBakerdefbake

  9. ruby-on-rails - 如何在 Ruby on Rails 中发送和接收加密的电子邮件? - 2

    我有一个Rails应用程序,可以在某些事件上触发电子邮件。这些电子邮件被发送到一个单独的公司,该公司将在回复时向电子邮件添加一些额外的数据。这一切都已理解并有效,我正在解析回复、提取数据并且一切正常。我现在被要求加密电子邮件。有没有人对执行此操作的最佳方法有任何经验/想法?我无法保证第3方将使用哪种电子邮件客户端,因此我需要一个可以在许多电子邮件客户端中通用的解决方案。加密必须在我发送电子邮件时由我的应用程序进行,在回复时由客户端应用程序(Outlook、Thunderbird、Entourage等)进行。然后我需要接收加密的电子邮件,对其进行解密和解析以提取我需要的新信息。谁能指出可

  10. Ruby:跨进程转发接收者、参数和 block - 2

    给定这样的代码:p=procdo|*args,&block|pselfpargspblock[]ifblockendq=procdo|*args,&block|p'before'instance_exec(*args,&p)endo=Object.newo.define_singleton_method(:my_meth,q)o.my_meth(1,2){3}如何在保留q的接收者的同时将调用从p完全转发到q?基本上我也想打印3,但是instance_exec和所有ruby​​方法一样,只能占用一个block。是否可以在不更改p的情况下,让我可以互换使用p和q(我的想法是让q有时包装p)

随机推荐