上半年遇到的一些service绑定失败的分析

来源:互联网 发布:审判者极速踢腿宏编程 编辑:程序博客网 时间:2024/05/20 05:56

对于近期4.4和5.x上出现服务无法绑定的问题分析如下:

【具体原因分析】

一、    用户侧数据

我们从用户侧拿到的servicedump信息:

从用户侧拿到的sevices文件,都有这几特征:


解释这几个值。

1.serviceRecord:就是用于服务对象本身

1.App=null:说明服务于进程没有关联起来

2.lastStartid=124;说明主进程调用startservice接近124次了。对这个频繁的调用,只有两种可能:

A.      进程被杀后服务自启,如果要触发这个条件,相当于进程短时间内一直被kill,从log看,是可以排除这种情况

B.       客户端判断绑定没成功进行重邦定,很显然,这个124值就是我们一直在调用

1.      至于这个restartDelay这个值:说明这个ServiceRecord是要延时启动的。

2.       

二、    源代码信息疑点

从上面的这些信息。我们结合源码,看到两个可疑点:

无论是何种方式启动或重启都会调用这个方法:bringUpServiceLocked,这两处(标红的1,2)会直接return,这种分支执行的结果就是永远都无法绑定成功。

大家先记住这张图。后面分析会用到


第一种情况:

首先我们app==null.所以不可能是第一种情况,接下来重点分析第二种情况。

第二种情况:

首先,出现二种情况的条件是如下:

Ø  whileRestarting=false;

Ø  r.restartDelay>0   //上面我们抓到的dump可以看到restartDelay是大于0的。哪我们主要看这个值whileRestarting是什么情况了。因为这里失败时,是我们主动调用了startservice和bindservices。

查代码后,发现有三个地方会调用:

 

1.        startServiceInnerLocked-->bringUpServiceLocked(r, service.getFlags(), callerFg, false);

2.        bindServiceLocked-->bringUpServiceLocked(s,service.getFlags(), callerFg, false)

3.        performServiceRestartLocked-->bringUpServiceLocked(r,r.intent.getIntent().getFlags(), r.createdFromFg, true);

 

我们调用start和bindservice都是传的false,而restartdelay这个值大于0.坑爷啊, 这样就是死循环,无论如何也是邦定不成功了(而performServiceRestartLocked这个方法只有系统重启service才会调用,非我们主动调用,所以无关)

 

Ps:Startbind传进来的值我们无法改变,哪问题一定是出现这个restartDelay这个值上。这个值大于0:我们猜想是上次系统重启了服务,但启动的过程中失败了,而restartDelay这个值又没有重置,导致后面重启一直死循环接着看下面的分析:

 

三、    为什么restartDelay没有被重置0

看看重置代码如下:


而这个方法只有这个方法会调用:


从这里看,调用的条件满足任一要求即可:

1.此servicecRecord在重启服务列表里面

2.uid不相等

我们来看下调用这个方法都有哪些方法及传的参数:

1 startServiceLocked-->unscheduleServiceRestartLocked(r,callingUid, false)

2 bindServiceLocked -->unscheduleServiceRestartLocked(s,callerApp.info.uid, false)

3 bringDownServiceLocked--> unscheduleServiceRestartLocked(r, 0,true)

注:start和bind的调用都是用户主动触发的,能进入这个逻辑的只有一个条件:此服务已在重启列表里面。Ps: 只有系统重启的服务才会放到重启列表里

哪剩下我们看下这个bringDownServiceLocked-->unscheduleServiceRestartLocked(r, 0, true)他传的是0.,自然这个值肯定resetRestartCounter是会被调用的。

找了下,要执行bringDownServiceLocked这个方法:有如下方法:


1.      这5个方法中,我圈中的3个方法是和重启服务强相关的:

2.      第1个方法调用,意味着服务走完了完整的调用流程。

3.      第2个方法的引用方法有如下:

4.       

5.      第5个方法正是服务被杀后走的第一个方法:

重点看这个方法:killServicesLocked,这个方法只要重启都会到到这个方法:

这个方法很长:做的事有三件:

1.      清除app的服务状态,相当于初始化一些变量:


2.      清除所有连接

3.      清理此app剩余的服务,因为有些是要重启scheduleServiceRestartLocked,有些是没必要的bringDownServiceLocked:

 

结合上面的分析:结合此处标注的这三处圈圈都是有可能对restartDelay重置的。

1.      removeConnectionLockedàbringDownServiceIfNeededLockedàbringDownServiceLocked

2.      第二处和第三处均是:bringDownServiceLocked.

3.      以上几处均没用调用
按时间来执行,首先执行的是:removeConnectionLocked这个方法:


Remove这个方法会清除掉此进程的所有服务连接。并unbind的,但如果Un失败,就会调用

serviceProcessGoneLocked—》serviceDoneExecutingLocked(r, true, true)


一定会执行这个方法,此时会把此服务从r.app里面remove掉,这样此进程里面就再也没有这个服务了。此时如果是bind_auto_creat标记的服务和delay就会被重置

回到killservicelock方法上来:此时,由于app里面已经没有这个serviceRecord了,但这个表里还是有的同,后面的遍历app.services是没用了。

因为我们的服务已被remove,所以这个服务永远也不会启动了 。

后面的bringDownServiceLocked.和scheduleServiceRestartLocked就无法执行了。所以这次启动服务也就失败了。如果此时我们检测到service没有起来:主动start/bind。会有以下两种情况:

1.如果带BIND_AUTO_CREATE创建的服务会因为已重置,所以可以正常启动。如果remove后判断BIND_AUTO_CREATE这前出现异常。和2一样的。

2.如果不带,哪无论如何启动,都无法绑定服务

原因:

1 startServiceLocked-->unscheduleServiceRestartLocked(r,callingUid, false)

2 bindServiceLocked -->unscheduleServiceRestartLocked(s,callerApp.info.uid, false)

满足restartDelay重置要求uid不一样或在restartService队列里面,均不满足

接下来 ,他们分别都会调用到bringUpServiceLocked,且他们的启动传的wileRestarting都是false

1.startServiceInnerLocked -->bringUpServiceLocked(r,service.getFlags(), callerFg, false);

2.bindServiceLocked-->bringUpServiceLocked(s, service.getFlags(),callerFg, false)

然后restartDelay这个值是大于0.


这里直接进入return。无限死循环,永远都无法绑定。这就是问题产生的原因。

谷歌源代码中的解决已在6.0上更新:在这个方法中:bringUpServiceLocked中remove()成功后并进行重置可解决这个。

解决思路:当进程重启失败(removecontect时异常)时,因为上次成功时已remove且重置, delay值0.这时我们检测到没绑定,进行starft/bind就能绑定成功

也就是说:如果没有这个,哪start/bind永远也绑定不成功。

2.哪这样说:是不是进程只要这个地方出现异常。后面只要kill掉这个进程,后面永远也是无法绑定的。


四、    不调用stop或unbind出现的概率更高?

哪为什么不执行解绑操作会加大出现的概率呢:我们看下解绑定的调用逻辑:

1.unbindServiceLocked(IServiceConnectionconnection)-->removeConnectionLocked(r, null, null)

-->bringDownServiceIfNeededLocked(s,true, hasAutoCreate)-->bringDownServiceLocked(r)

-->unscheduleServiceRestartLocked(r, 0,true)

2.stopServiceLockedà bringDownServiceIfNeededLocked()àbringDownServiceLockedà unscheduleServiceRestartLocked(r,0, true)


看到了吧,unbind会调用到这个,且传入的uid是为0,如果我们不调用,明显会出现restartDelay不会重置,出现这个情况的概率就会大大增加。而主动调用概率当然就会降低了。

但由于系统里面服务还是有很多状态的。比如说,如果出现服务hasAutoCreate创建返回为true,哪也是直接返回,就不会调用重置。其次,如果我调用stop的时候或Unbind的时候,出现此服务在mPendingServices队列里,这时unbind也是不会调用ret的,均直接return。


 

五、    为什么4.4和5.x的版本会多点?

相比起来,serviceDoneExecutingLocked里多出了一个这个方法:

4.4以上的版本:见上面分析所用的serviceDoneExecutingLocked,参数上多了一个finishing,代码上多了一个r.app.remove,而这个正是罪魁祸首

 

4.4以下的版本:没有这个方法


 

其次还有一个问题区别:

如上面所说:如果要重置,会调用到这以下几个方法:

1 startServiceLocked-->unscheduleServiceRestartLocked(r,callingUid, false)

2 bindServiceLocked -->unscheduleServiceRestartLocked(s,callerApp.info.uid, false)

 

4.4以下:


每次启动必然会重置。所以4.4以后就算会失败,也不会出现这个问题

4.4以及以上:要满足以下条件,在restart列表里,但其实如果重启失败,会remove,但如果失败,刚上我上面所说,移除连续时异常,哪这个东西还没有加入到restart列表。

 

 

六、    综上所述,现在安全了么?其实这个问题还是存在的,只是概率变低了而已。无法彻底解决

1.4.4到5.x的版本,其实只要重启过 ,delay都是不会重置的。除非你主动调用了stop或Unbind,如果是没有stop和unbind直接异常通出,也是会出现这种情况的

2.发生这个问题:必然是已经重启过一次,且重启成功了,后面再次被kill.重启失败才会产生

 

 

0 0
原创粉丝点击