黑莓OS5.0系统的断网问题一直是困扰众多莓友的重要问题,对此我也一直是耿耿于怀。最近看到一些帖子说只要修改软件的联网方式,使用cmwap接入即可绕过断网故障,我自己通过对uc和Gmail的修改也证实了这一点,而这也是我对找出断网的原因产生了浓厚的兴趣。这是断网后试图用uc打开网页时从手机工程模式下的Event Log中截取到的错误信息,包含从uc开始调用网络连接到发生错误的调用堆栈信息。可以看到,导致uc无法建立网络连接的错误发生在net_rim_os-4.cod中SocketHelper类的checkDataConnectivity函数中。从函数名来看,checkDataConnectivity就是在检测是否可以建立数据连接,那么这个函数为什么会抛出异常呢?让我们来看这个函数的反编译代码:static public final checkDataConnectivity( net.rim.device.cldc.io.utility.RIMConnectionParameters ); // address: 0 offset: 17525{ enter_narrow // 0: dd (221) ldc literal_110:"true" // 1: 28 (40) aload_0 // 2: 3f (63) getstatic_lib RETRY_NO_CONTEXT // RIMConnectionParameters // 3: 6e (110) invokevirtual java.lang.String getParameter( net.rim.device.cldc.io.utility.RIMConnectionParameters, int ) // pc=2 // 4: 01 (1) invokevirtual_short .equals // idx=1 pc=2 // 5: de (222) istore_1 // 6: 4e (78) iload_1 // 7: 38 (56) ifeq Label18 // 8: 93 (147) invokestatic_lib assertRRISignatures( ) // ControlledAccess // 9: 08 (8) goto Label18 // 10: a1 (161) astore_2 // 11: 57 (87) new_lib java.util.Calendar//java.util.Calendar // 12: b9 (185) dup // 13: cf (207) aload_2 // 14: 41 (65) invokevirtual java.lang.String getMessage( java.lang.Throwable ) // pc=1 // 15: 01 (1) invokespecial_lib java.io.IOException. // pc=2 // 16: 06 (6) athrow // 17: bc (188)Label18: getstatic net.rim.device.cldc.io.socket.SocketHelper.field_15022 // 18: 6d (109) ifeq Label29 // 19: 93 (147) iload_1 // 20: 38 (56) ifne Label29 // 21: 96 (150) invokestatic_lib net.rim.device.cldc.io.datarecovery.DataRecovery getInstance( ) // 22: 08 (8) invokevirtual boolean isConnectionAvailable( net.rim.device.cldc.io.datarecovery.DataRecovery ) // pc=1 // 23: 01 (1) ifne Label29 // 24: 96 (150) new_lib java.util.Calendar//java.util.Calendar // 25: b9 (185) dup // 26: cf (207) invokespecial_lib java.io.IOException. // pc=1 // 27: 06 (6) athrow // 28: bc (188)Label29: return // 29: 1f (31)}由代码可见,这个函数主要是为了调用DataRecovery.isConnectionAvailable()函数,如果返回false,则抛出异常。到此可以基本断定,日志中IOException的出现是由于DataRecovery.isConnectionAvailable()函数返回了false。那么再来看DataRecovery.isConnectionAvailable()函数的反编译代码:public boolean isConnectionAvailable( net.rim.device.cldc.io.datarecovery.DataRecovery ); // address: 0 offset: 16865{ enter_narrow // 0: dd (221) synch // 1: 12 (18) aload_0_getfield ._errorLevel // ofs = -1 ord = 0 addr = 3 // 2: 67 (103) bipush 4 // 3: 24 (36) if_icmpge Label7 // 4: 99 (153) iconst_1 // 5: 2c (44) ireturn // 6: 18 (24)Label7: iconst_0 // 7: 23 (35) ireturn // 8: 18 (24)}这个函数更简单,只要_errorLevel大于等于4,就返回false,否则返回true。难道说如此困扰我们的断网问题,其原因只是因为这个变量的值大于等于4?!而这个值又是如何变成大于等于4的?整个DataRecovery类中,仅有一个函数修改了_errorLevel的值:fileReport。fileReport函数比较长,此处就不全部列出了,仅截取其中与_errorLevel有关的部分:public fileReport( net.rim.device.cldc.io.datarecovery.DataRecovery, int, int, int ); // address: 0 offset: 16514{ iload_1 // 9: 38 (56) tableswitch : tablesize=3 low=-1 table 0 default -1 : Label129 0 : Label11 1 : Label42Label11: …… aload_0_getfield ._errorLevel // ofs = -1 ord = 0 addr = 3 // 19: 67 (103) bipush 4 // 20: 24 (36) if_icmplt Label28 // 21: 9b (155) aload_0 // 22: 3f (63) iconst_0 // 23: 23 (35) putfield ._errorLevel // ofs = -1 ord = 0 addr = 3 // 24: 5f (95) ……Label28: aload_0 // 28: 3f (63) iconst_0 // 29: 23 (35) putfield ._errorLevel // ofs = -1 ord = 0 addr = 3 // 30: 5f (95) ……Label42: aload_0 // 42: 3f (63) aload_0_getfield ._errorLevel // ofs = -1 ord = 0 addr = 3 // 43: 67 (103) iconst_1 // 44: 2c (44) iadd // 45: 7a (122) putfield ._errorLevel // ofs = -1 ord = 0 addr = 3 // 46: 5f (95) ……Label129: …… return // 131: 1f (31)}由函数可见,当fileReport的第一个参数值为0时,_errorLevel会被重置为0,而参数值为1时,_errorLevel会递增。也就是说系统在出现某种状况的时候,会用参数值1来调用DataRecovery.fileReport()函数,使_errorLevel值递增,当_errorLevel值累积到大于等于4的时候,我们的BB就断网了……事实真的是这样吗?我们结合以下现象来进行验证:1. 断网只影响GPRS、EDGE、3G、WIFI等联网方式,采用BIS、BES上网时不会断网;在连接字串中写明WAPGagewayIP等信息的也不会受断网影响我在JDE开发环境内写了一个小的网络连接测试程序,并用调试方式在模拟器上运行,运用JDE的VM Byte Code调试能力,结合反编译得到的代码对整个网络连接的建立过程进行了跟踪调试,结果证明,采用BIS、BES方式和写明WAPGagewayIP信息的,其连接过程中完全不会调用到SocketHelper.checkDataConnectivity()或DataRecovery.isConnectionAvailable()函数,而其它连接方式下,无论是用socket还是用http连接,都会调用SocketHelper.checkDataConnectivity(),并且没有任何分支语句用来跳过该函数。2. 断网只影响第三方应用程序,官方浏览器不受影响说到这个问题,我们要回头再看看SocketHelper.checkDataConnectivity()函数的代码,这回给出整理后的java代码,注意其中标红的部分:public final static checkDataConnectivity(RIMConnectionParameters parameters) throws IOException{ boolean retryNoContext = (parameters.getParameter(RIMConnectionParameters.RETRY_NO_CONTEXT).equals("true")); if (retryNoContext) { try { ControlledAccess.assertRRISignatures(); } catch (Throwable t) { throw new IOException(t.getMessage()); } } if (checkConnectivity && !retryNoContext) { if (!DataRecovery.isConnectionAvailable()) throw new IOException(); }}也就是说,如果连接字串中具备“;retrynocontext=true”属性,并且应用程序具备了RRI签名,就可以无视连接检测!而RRI签名是黑莓系统内核使用的签名,根本不对外公开!接下来不用说大家也该猜到了,我在系统浏览器HTTPStackAdapter类中的private final HttpConnection routine_17191(int, String, String, BrowserConfigRecord, int, int )函数内找到了添加retrynocontext属性后再建立连接的代码: getstatic_lib RETRY_NO_CONTEXT // RIMConnectionParameters // 87: 6e (110) ldc literal_125:"true" // 88: 28 (40) invokevirtual setParameter( net.rim.device.cldc.io.utility.RIMConnectionParameters, int, java.lang.String ) // pc=3 // 89: 01 (1) …… invokestatic_lib javax.microedition.io.Connection open( net.rim.device.cldc.io.utility.URL, int, boolean ) // RIMConnector // 186: 08 (8)这说明RIM知道网络连接有可能会有问题,却给自己开发的应用程序留了一个只有自己才能使用的后门!至于RIM是确实没有发现在DataRecovery的处理上有Bug还是有其它的原因大家自己猜想吧。从以上这些分析中我们可以看到,DataRecovery中的_errorLevel的值是决定我们的BB断不断网的关键。然而这仅仅是浮于表面的部分,隐藏在后面的是整个DataRecovery以及fileReport函数的处理机制。这些我没有做更深入的分析,希望有感兴趣的朋友做进一步的研究。下面再说说我是如何尝试解决断网问题,又是如何失败的。 *************************接上面的**********************第一次尝试:既然调用fileReport函数并将第一个参数设置为0就可以将_errorLevel复位为0,那么我写个程序去调它一下不就行了!查api文档发现DataRecovery类没有公开,就自己根据反编译的结果写个简单的类,因为是要作为api使用的,因此所有方法都不用具体实现。编译后把原先的net_rim_api.jar解包,再把编译好的DataRecovery.class打包进去,api就造好了。接下来写好了程序,调试运行,一切OK!心情好的不得了!准备签名装手机时,噩梦到来,需要RIMAPPSA2签名(就是RRI签名)!这个我真的没有……不理会,把能签的签了,装机,结果因为缺少签名无法运行。此次尝试到此彻底失败。第二次尝试:调用DataRecovery.isConnectionAvailable()函数是在net_rim_os-4.cod中,直接修改这个模块的二进制代码,使其永远不抛出异常不就行了。找到模拟器目录下的net_rim_os.cod文件并解压,发现其数字签名是假的!这样修改内容就不会产生验证的问题了。用HEX编辑工具对net_rim_os-4.cod进行了修改后,重新打包生成net_rim_os.cod文件并覆盖回原目录。启动模拟器后报错,奇怪,数字签名是假的怎么还会报错?将模拟器清除一下再启动,OK了!接下来就是修改实际的net_rim_os.cod文件了。安装了484后再安装794混刷包,重新进行修改并覆盖原文件,最后刷机。过程一直很顺利,可结束的时候报出net_rim_os.cod文件签名无效,手机无法正常启动。怪了,怎么还有签名验证?再仔细一看,恍然大悟,模拟器里的文件签名是假的,可真正的刷机文件签名可是真的……此次尝试到此又彻底失败。第三次尝试:既然以上两次尝试都败在签名这里,那么有没有可能从根本上把签名验证废掉?在net_rim_cldc中找到了verifySignature函数,把这个函数咔嚓掉不就可以跳过验证机制了。重新修改后再刷机,再次失败,这回屏幕上显示一个错误代码,让重装软件。此次尝试到此再次彻底失败。现在我已经彻底没招儿了。哪位朋友如果与黑莓官方的人有联系,可以将上面的问题向他们反映反映。或者哪位英文比较好,帮忙把本文翻译翻译,贴到RIM官方论坛之类的地方,希望能引起他们的注意。再补充一点:我对比了OS4.7与OS5.0,发现4.7版本中根本没有SocketHelper类,在建立网络连接的过程中也不会调用DataRecovery.isConnectionAvailable()函数。也就是说验证网络是否能够连接这个机制是从OS5.0系统开始引入的,这也是OS4.7或以下系统不会断网的原因。 wap接入点的时候并没有将代码完整,所以导致断网,请供应wap接入点的选择,并且在写程序的时候,将代码CMWAP连接方式填写完整,即包含下面的关键字:deviceside=true;WapGatewayAPN=cmwap;WapGatewayIP=10.0.0.172;WapGatewayPort=9201并不是非要选择cmwap连接方式,你可以设置3种模式供用户选择,这里只是强调在编写cmwap模式时,请将代码填写完整,原作者发现黑莓版UC没有将代码填写完整。 建议大家暂时使用4.6版的rom。等第三方应用程序的开发人员真正按照5.0版的开发时再使用。今天仔细看了开发手册其中说到: 调用 Connector.open(),将 udp 指定为协议,然后将返回的对象转换为 DatagramConnection 对象, 以打开数据报连接。 (DatagramConnection)Connector.open("udp://host:dest_port[;src_port]/apn"); 要通过数据报连接接收数据,请在数据报连接上调用 receive()。 receive() 方法将阻止其它操作,直 至收到数据包。 如果未收到答复,则使用计时器重新传输请求或关闭连接。 uc使用的这个方法收数据,当收不到数据时。uc就不停地占用端口资源,最后第三方软件就都上不去了。BB的内置程序是使用另一个方式调用叫做:无线接入系列 第三方程序都没有使用这个方法,当第三方程序收不到数据时,应该自己做一个定时器,超时后由第三方程序自己关闭连接,以便再次连接。但是这些开发者没注意到这个问题,以为捕获了异常就可以了。这是开发者的问题。BB的开发者肯定知道这个问题,所以BB内置的程序都做了定时器来判断连接超时的问题。建议大家暂时不要用QQ2010,shangmail,uc等软件。这些软件运行一段时间就会断网的。还有一个问题,这些软件不是你在菜单里选择的时候才运行的。BB启动的时候他们后台的核心模块就已经运行了。不信你有这些软件的话,开机一定时间就断网了。根据分析结论是第三方应用程序造成的: 当手机没有信号或信号不好时,总之是不能进行网络数据访问时。第三方应用程序不断调用BB的HTTP协议类。 例如 1 uc在没有信号时会每分钟调用20次,也就是说每3秒调用一次。 2 BB的HTTP协议类会不断地返回空指针异常。 3 但是uc并没有把调用的线程关闭掉。 4 那就是说当你没有信号时,uc每分钟会增加执行20个线程,那么5分钟就是100个线程。 5 你在电脑上试试开100个IE浏览器,你的机器肯定完蛋了。 6 所以5.0系统会做限制的,就是每个程序也许只能开有数线程。超过了就不能再开线程了。 7 所以这时候你的uc已经不能再开线程访问网络了。返回的全是空指针错误,也就是线程没有被创建。 8 uc当然不能再联网了。 9 所以BB内置的程序都没有这样的问题。怎么都不会断网的。10 所以你重起BB时又可以访问了,就像重起电脑一样。11 为什么4.6版没有问题呢。其实在4.6版时第三方应用程序也是在不断的调用,但是线程都会被关闭。12 因为4.6和4.5的开发平台是一样的。4.5平台已经很成熟了,开发者经验 也比较丰富,所以不会出现太多问题。13 但是开发者比较懒,还是沿用4.5的方法开发。5.0版的平台完全不一样,API都变化了很多。14 BB在开发者说明中关于访问模式在开发手册上已经建议使用5.0版的新方法。uc断网后,再连接网络时,速度非常快的就返回了错误 40经查后台log 直接抛空指针异常了。而且没有调用系统的API。直接异常。ROM里自带的归类为 附加程序。自己安装归类为 第三方。经测试 附加程序 中的绝对不断网。第三方的会断网。建议第三方应用程序开发者好好学学BB新的平台中应用程序进程和线程的控制方法的改变。结论:1 在连接字串中写明WAPGagewayIP等信息的不会受断网影响2 当fileReport的第一个参数值为0时,_errorLevel会被重置为0,而参数值为1时,_errorLevel会递增。也就是说系统在出现某种状况的时候,会用参数值1来调用DataRecovery.fileReport()函数,使_errorLevel值递增,当_errorLevel值累积到大于等于4的时候,我们的BB就断网了……可以用内置的程序做一个定时器来判断连接超时的问题。当_errorLevel等于3的时候断开网络连接,使之不超过4。3 8900断网绝对不是硬件问题。4 绝对不是ROM问题,断网只有第三方应用才断,BB的内置软件不会断网的。5 第三方应用程序的开发者还没学会使用新平台。
|