1. 问题描绘
用户操作出的点击Message界面无任何呼应的问题,可是也不发生ANR。
2. log剖析
与Launcher的同事交流后,继续剖析startActivityFromRecents为什么没有把Message发动起来。
查看SystemLog,发现Launcher调用startActivityFromRecents办法发动Message后,紧接着便是以下log:
是后台Activitiy发动的策略约束了本次发动:“BAL_BLOCK”,可是不太清楚的是为什么,这是第一个疑问点。
别的这一次发动Message用的Intent为:
intent: Intent { flg=0x1010c000 cmp=com.google.android.apps.messaging/.main.MainActivity (has extras) };
原因是之前发动MainActivity用的便是这个Intent,这次是直接复用了。
而上一次发动MainActivity是在:
01-25 00:08:08.497936 1454 1636 I ActivityTaskManager: START u0 {flg=0x1010c000 cmp=com.google.android.apps.messaging/.main.MainActivity (has extras)} with LAUNCH_MULTIPLE from uid 10149 (realCallingUid=10081) (BAL_ALLOW_PENDING_INTENT) result code=0
这次应该是在Launcher的Recents发动的,因而Intent也是复用的。
继续往前排查,能从log中找到的三次发动Message的状况都是现已出现问题了:
因而从现在的log中无法得知首次是怎么发动“com.google.android.apps.messaging/.main.MainActivity”的,咱们本地在Launcher界面点击Message图标,发动的是“com.google.android.apps.messaging.ui.ConversationListActivity”,这是第二个疑问点。
总结一下,问题中总共有两个疑问点,假如要复现这个问题,需求弄清这两个疑问点:
1、后台BAL策略为何约束了Message的发动,回来了BAL_BLOCK。
2、“com.google.android.apps.messaging/.main.MainActivity”是怎么发动的,咱们本地在Launcher界面点击Message图标,发动的是“com.google.android.apps.messaging.ui.ConversationListActivity”。
3. BAL_BLOCK剖析
仍是先看log:
这些log是在BackgroundActivityStartController.checkBackgroundActivityStart办法中被打印的。
看BackgroundActivityStartController.checkBackgroundActivityStart办法代码:
从log上知道,这儿传入的originatingPendingIntent为null,那么局部变量useCallingUidState为true,也便是说,会走这这段逻辑:
这儿应该是答应Home App能够发动Activity的,可是实际上咱们却没有在这儿return,而是在该办法的最终return了BAL_BLOCK,原因则可能是因为callingPackage(callingUid)和realCallingPackage(realCallingUid)不一致:
尽管真正的发动Message的是”com.tcl.android.launcher“,可是这儿的calingPackage用的是” com.google.android.apps.messaging“,导致没有办法在这儿return。
4. callingPackage和realCallingPackage
再看下callingPackage和realCallingPackage都是在哪里赋值的。
因为此流程是经过Launcher调用startActivityFromRecents开始的,因而咱们能够知道具体的调用堆栈为:
ActivityTaskManagerService.startActivityFromRecents
-> ActivityTaskSupervisor.startActivityFromRecents
-> ActivityStarterController.startActivityInPackage
-> ActivityStarter.execute
-> ActivityStarter.executeRequest
-> BackgroundActivityStartController.checkBackgroundActivityStart
最终发现关键点就在ActivityTaskManagerService.startActivityFromRecents:
这儿剖析callingUid和realCallingUid可能要更简略一点,和callingPackage和realCallingPackage是一样的。
依据这儿最终调用的ActivityStartController.startActivityInPackage办法的参数列表可知:
callingUid便是这儿的局部变量taskCallingUid,而realCallingUid为传参callingUid。
4.1 realCallingUid
上面剖析realCallingUid便是ActivityTaskSupervisor.startActivityFromRecents办法的传参callingUid:
而ActivityTaskSupervisor.startActivityFromRecents是ActivityTaskManagerService.startActivityFromRecents调用的:
而该api开始是Launcher那边调用的,因而这儿的callingUid便是Launcher的uid。
4.2 callingUid
如上面剖析,callingUid便是ActivityTaskSupervisor.startActivityFromRecents办法中的局部变量taskCallingUid,是从Task的mCallingUid成员变量取值的:
taskCallingUid = task.mCallingUid;
而这儿的局部变量task则是经过RootWindowContainer.anyTaskForId办法去取的。
RootWindowContainer.anyTaskForId办法依据传入的taskId,从两个当地去取契合该taskId的Task:
1)、假如能从现有的Task中找到一个契合条件的,就回来这个Task。
2)、假如现有的Task都不契合条件,则从前史Task,即RecentTasks中去找。
即这个Task是之前发动过的(不管现在是否还存在),因而假如咱们想要让取得的这个Task的mCallingUid是“com.google.android.apps.messaging”,那么就需求让这个Task创立的时分是由Message本身发动的。
假如从Launcher点击Message图标发动Message,那taskCallingUid便是”com.tcl.android.launcher“:
这边尝试了多种发动Message的方法,发现经过接收到短信后点击Notification的方法发动Message,能够完成Message的Task.mCallingUid对应为Message:
那么完成callingPackage(callingUid)和realCallingPackage(realCallingUid)不一致的办法能够是:
1)、经过点击Notification的方法发动Message,Task.mCallingUid对应为Message。
2)、点击Back键将Message毁掉,然后经过Launcher发动Message,并且需求让Launcher以复用intent的方法去发动Message,比如从Recents界面发动。
如此一来,就能够完成callingPackage(callingUid)和realCallingPackage(realCallingUid)不一致,从而在发动Message的流程中就能够回来BAL_BLOCK了。
这儿处理了咱们在第一节提出的一疑问1:“后台BAL策略为何约束了Message的发动,回来了BAL_BLOCK。”
不过这种方法发动的是”com.google.android.apps.messaging.ui.ConversationListActivity“,不是咱们想要的“com.google.android.apps.messaging/.main.MainActivity”,也即疑问二:“com.google.android.apps.messaging/.main.MainActivity”是怎么发动的。
这儿直接放定论吧,经过本地各种实验,发现假如经过Phone向联系人发送短信的方法,跳转到Message,能够将”com.google.android.apps.messaging/.main.MainActivity“发动起来。
最终还有一点要注意:
调用RootWindowContainer.anyTaskForId依据传入的taskId寻找Task的时分,不能从RootWindowContainer中找到一个现有的Task,而要从RecentTasks中找前史Task(从前被创立,后边从RootWindowContainer中被remove了)。假如该Task有一个RootActivity,那么就不会在最终调用ActivityStarterController.startActivityInPackage去走startActivity的流程:
而是直接把该Task移动到前台,然后回来ActivityManager.START_TASK_TO_FRONT:
因而为了复现问题的场景,咱们需求确保走到这儿的时分,Message对应的Task是现已被移除了,也便是说是从RecentTasks中拿到了Message的Task。所以即使为了完成这一步,咱们也需求发动”com.google.android.apps.messaging/.main.MainActivity“,该Activity会在接收到Back事件的时分去finish,然后整个Task才会被remove掉,后续咱们就能够从RecentTasks中找回来。而假如发动的是”com.google.android.apps.messaging.ui.ConversationListActivity“,那么因为该Activity接收到Back事件不会走finish,因而这儿便是从现有的Task中拿Message的Task了,无法复现到问题。
5. 复现问题
那么最终复现该问题的稳定步骤为(导航模式为手势):
1)、将Message对应的Task从Recents铲除(铲除之前的影响)。
2)、接受到短信,然后点击Notification跳转到短信(Message本身发动自己的Task)。
3)、上滑回到Launcher,然后进入Phone,经过点击“Send a message”的方法跳转到Message(发动”com.google.android.apps.messaging/.main.MainActivity“)。
4)、回到Message后经过侧滑再次回到Phone(类似于点击Back,回到Phone的一起,Message的Activity和Task也被移除)。
5)、经过在底部左滑的方法切换到Message —— KO,发现界面无法点击(laucnher调用startActivityFromRecents的api,并且复用之前Message发动本身的时分的Intent)。
经过以上剖析可知该问题为google原生问题,同样在pixel上也能够复现。