不能接收彩信的原因及自动收发彩信解决方案

来源:互联网 发布:茉莉机器人3.3源码 编辑:程序博客网 时间:2024/05/23 13:54
目前测试的结果,有很多朋友已经可以完美接收彩信。另一部分无法接收彩信的原因,正在进一步研究中。但只要有能够接收的,就说明这个问题已经得到了初步的解决。而且从理论上来讲,这个解决方案对所有的水货机均适用。


彩信的发送就不用说了,设置正确的apn,即可发送彩信。下面主要说一下彩信的接收问题。

1、不能接收彩信的原因

在大量搜索网上资料的基础上,经过几天的测试和分析,找到了不能接收彩信的初步原因。

彩信,按照电信规范,是分两步发送的。
第一步,发送WAP-PUSH格式的短信通知,该短信通知含有一个url,该url指向彩信内容的存储地址。
第二步,手机解析出这个url,向url发送获取请求,下载彩信内容到本地,完成彩信的接收。

向手机发送彩信之后,手机上没有任何的反映,说明第一步的彩信通知都没有接收到。

我写了一个Receiver,拦截系统中广播的android.provider.Telephony.WAP_PUSH_RECEIVED消息,结果没有收到这个广播。证明彩信通知并没有上传到app应用层,那么有可能在framework框架层,彩信通知就被拒绝了。

在Receiver中添加android.provider.Telephony.SMS_REJECTED,收到了系统广播的消息。说明彩信通知确实是被framework层给reject了。

取出/system/framework/framework.jar,解压出classes.dex,对dex文件进行baksmali,再对照android系统源码进行流程分析(虽然只是简单的一句话,但确实够我忙活了一段时间。。。。。。),最终确定,问题在com.android.internal.telephony.cdma.CdmaSMSDispatcher.smali中。

对照android源码,找出问题的关键代码段:


if (SmsEnvelope.TELESERVICE_WAP == teleService) {return processCdmaWapPdu(sms.getUserData(), sms.messageRef,sms.getOriginatingAddress());}


查找smali中对应的TELESERVICE_WAP,发现其设定的值是0x1004,换算成10进制就是4100。

修改CdmaSMSDispatcher.smali,使之在返回sms rejected的原因时,填入彩信通知的TeleServiceId,发现电信的TeleServiceId确实是65002,中国电信CDMA终端规范也证实了这个判断。

那么原因找到了,由于TeleServiceId的不同,导致手机自带的美版ROM无法识别65002的电信Id,所以拒绝接收彩信通知。


2、彩信接收解决方案

找出原因来了,当时我就想,这应该就比较简单了吧。。。。。。

2.1 修改TeleServiceId

定位CdmaSMSDispatcher.smali中判断TeleServiceId的语句:

const/16 v10, 0x1004

把0x1004(4100)修改成0xfdea(65002),使之识别出这是一个WAP-PUSH,进而转入processCdmaWapPdu流程,处理彩信通知的pdu。

编译所有的smali文件,更新classes.dex,替换本机中的framework.jar,重启手机。

怀着激动的心情发送彩信,一直在等。。。。。。终于等来了。。。。。。“您的手机终端不支持接收彩信,请到mmbox.vnet.cn中提取”。。。。。。

2.2 修改pdu处理流程

问题一定出在processCdmaWapPdu这个方法里。又经过了一段时间的调试和分析(又是简单的一句话,背后是N个小时的连续奋战和N元钱的彩信发送费用。。。。。。),终于发现,pdu解析有问题。

processCdmaWapPdu传入的第一个参数,sms.getUserData(),获取的是pdu中的包括msg identifier在内的完整userData,而processCdmaWapPdu中的代码,对传入的参数是按照CHARi来进行处理的,所以导致获取的totalSegments和segment完全是错的,系统认为收到的彩信通知只是一个片断,所以把这个片断存入数据库,等待后面的片断。可后面永远都不会再有任何相关的片断了。。。。。。

解决方案用java写出来是这样的:

userData = sms.getUserData();BitwiseInputStream bis = BitwiseInputStream.new(userData);bis.skip(69);userData = bis.readByteArray(userData.length * 8 - 72);processCdmaWapPdu(userData,......)


意思就是说,把byte[]数组的userData转化成bit流,跳过最前面的69个bit(头部数据),取bit流(CHARi数据),扔掉最后的3个bit的000,把取到的bit流再转化成byte[]数组。

然后,把这段java代码人肉翻译成dalvik虚拟机的字节码。。。。。。。


move-object/from16 v11, v10 # v10: userDataarray-length v11, v11 # v11: userData.lengthnew-instance v12, Lcom/android/internal/util/BitwiseInputStream;invoke-direct {v12, v10}, Lcom/android/internal/util/BitwiseInputStream;-><init>([B)V # v12: bisconst/16 v8, 0x45invoke-virtual {v12, v8}, Lcom/android/internal/util/BitwiseInputStream;->skip(I)Vmul-int/lit8 v11, v11, 0x8add-int/lit8 v11, v11, -0x48 # userData.length*8 - 72invoke-virtual {v12, v11}, Lcom/android/internal/util/BitwiseInputStream;->readByteArray(I)[Bmove-result-object v10


编译,替换,重启,搞定。

事后,我有一点点小纳闷,这套原始代码是美版ROM用的,难道美帝国主义CDMA运营商的彩信,传过来的userData,直接就是CHARi吗,不包括头部的数据?否则按照ROM中的代码,他们也肯定是无法接收彩信的。这个问题看来还要美帝自己来回答了。我们自己的好用就行,不去解放水深火热受剥削受压迫中的美国人民了。。。。。。


3、自动接收和发送彩信

搞定了彩信的接收,现在发送和接收都没有问题了。但还有一个小小的缺憾,就是发送和接收彩信时,需要先手动连接3G,不能做到自动收发。

除了我个人的完美主义倾向在作怪之外,我承认我也喜欢上了这种修改工作,身心俱疲的同时,身心俱爽。。。。。。

3.1 不能自动收发的原因

经过一段时间的研究(这个研究时间倒不是很长,比上面研究彩信接收的时间短多了),发现在收发彩信时,系统会自动进行3G网络连接,然后另起一个线程收发彩信。

但关键问题是,3G网络连接是需要时间的,在ppp拨号的客户端还在和服务端卿卿我我互传数据包确认关系的时候,收发彩信的线程就已经开始工作了,所以当然,在连接10.0.0.200的MMSC时,是会被refused的,然后程序抛出ioexception异常,收发事务被标记失败,最后系统断开3G连接。彩信的收发企图就这样被扼杀了。

3.2 解决方案

知道了原因,再解决就只是时间问题了。解决方案有很多种,我采用的是,在getPdu和sendPdu这两个方法的最前部,加入ensureRouteToHost方法,同时修改ensureRouteToHost,加入一个带有超时退出的循环,判断requestRouteToHost的值。

如果3G尚未连接,就sleep(1000)之后再试,直到从route上判断3G建立连接成功,退出ensureRouteToHost,进入收发彩信的流程。

如果60秒内3G仍未成功建立连接(要么信号不好,要不就是你欠费了。。。。。。),抛出ioexception异常,通知系统收发彩信失败,系统自动进入内置的重试排期计划。

直接上com.android.mms.transaction.Transaction.smali中插入的超时循环dalvik字节码:


const/16 v10, 0x3c:cond_4add-int/lit8 v10, v10, -0x1if-eqz v10, :cond_5invoke-virtual {v0, v7, v1}, Landroid/net/ConnectivityManager;->requestRouteToHost(II)Zmove-result v4if-nez v4, :cond_3const-wide v11, 0x3e8invoke-static {v11, v12}, Ljava/lang/Thread;->sleep(J)Vgoto :cond_4.line 195:cond_5

目前在我本机测试的情况,可以完美收发彩信。在3G关闭的情况下,发送和接收彩信,系统可自动连接3G,收发成功后自动断开,极大地满足了我个人的完美主义倾向和老清新风格。。。。。。