【问题分析】界面点击无响应,非ANR【Android 14】

在这里插入图片描述

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,也就是说,会走这这段逻辑:

219              // Always allow home application to start activities.
220              if (isHomeApp(callingUid, callingPackage)) {
221                  return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT,
222                          /*background*/ false, callingUid, realCallingUid, intent,
223                          "Home app");
224              }

这里应该是允许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方法的参数列表可知:

    final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
            String callingPackage, @Nullable String callingFeatureId, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
            BackgroundStartPrivileges backgroundStartPrivileges)

callingUid就是这里的局部变量taskCallingUid,而realCallingUid为传参callingUid。

4.1 realCallingUid

上面分析realCallingUid就是ActivityTaskSupervisor.startActivityFromRecents方法的传参callingUid:

    int startActivityFromRecents(int callingPid, int callingUid, int taskId,
            SafeActivityOptions options) {

而ActivityTaskSupervisor.startActivityFromRecents是ActivityTaskManagerService.startActivityFromRecents调用的

    @Override
    public final int startActivityFromRecents(int taskId, Bundle bOptions) {
        mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
                "startActivityFromRecents()");

        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
        final long origId = Binder.clearCallingIdentity();
        try {
            // ......
            return mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
                    safeOptions);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

而该api最初是Launcher那边调用的,因此这里的callingUid就是Launcher的uid。

4.2 callingUid

如上面分析,callingUid就是ActivityTaskSupervisor.startActivityFromRecents方法中的局部变量taskCallingUid,是从Task的mCallingUid成员变量取值的:

taskCallingUid = task.mCallingUid;

而这里的局部变量task则是通过RootWindowContainer.anyTaskForId方法去取的。

                task = mRootWindowContainer.anyTaskForId(taskId,
                        MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);

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上也可以复现。

  • 15
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值