Activity生命周期递归问题查看

news2024/12/22 19:35:03

这类问题一般比较难分析,符合以下情况的才有可能分析出来:

  • 能够复现并调试
  • 有问题时的堆栈以及对应的event log

TaskFragment#shouldSleepActivities 方法导致递归

There is a recursion among check for sleep and complete pause during sleeping

关键log

01-08 08:45:42.946  1941  5167 I wm_finish_activity: [0,240239432,5187,com.ss.android.auto/com.ss.android.account.v2.view.AccountLoginActivity,app-request]
01-08 08:45:42.949  1941  5167 I wm_pause_activity: [0,240239432,com.ss.android.auto/com.ss.android.account.v2.view.AccountLoginActivity,userLeaving=false,finish]
01-08 08:45:42.977 30679 30679 I wm_on_top_resumed_lost_called: [240239432,com.ss.android.account.v2.view.AccountLoginActivity,topStateChangedWhenResumed]
01-08 08:45:42.980  1941  2054 I WindowManager: Display0 turned off...
// 问题发生在息屏场景
01-08 08:45:43.001  1941  2054 I wm_sleep_token: [0,1,ScreenOff]
// 问题AccountLoginActivity已经完成onPaused回调
01-08 08:45:43.007 30679 30679 I wm_on_paused_called: [0,240239432,com.ss.android.account.v2.view.AccountLoginActivity,performPause,1]
01-08 08:45:43.011  1941 22598 I wm_set_resumed_activity: [0,com.ss.android.auto/.ugc.video.activity.UgcNewDetailActivity,resumeTopActivity - onActivityStateChanged]
01-08 08:45:43.016  1941 22598 I wm_resume_activity: [0,212386622,5187,com.ss.android.auto/.ugc.video.activity.UgcNewDetailActivity]
01-08 08:45:43.021  1941 22598 I wm_add_to_stopping: [0,240239432,com.ss.android.auto/com.ss.android.account.v2.view.AccountLoginActivity,completeFinishing]
// 重复打印很多次,表明递归
01-08 08:45:43.022  1941  2009 I am_wtf  : [0,1941,system_server,-1,ActivityTaskManager,Going to pause when pause is already pending for ActivityRecord
{e51c348 u0 com.ss.android.auto/com.ss.android.account.v2.view.AccountLoginActivity} t5187 f}} state=FINISHING]
01-08 08:45:43.045  1941  2009 I am_wtf  : [0,1941,system_server,-1,ActivityTaskManager,Going to pause when pause is already pending for ActivityRecord
{e51c348 u0 com.ss.android.auto/com.ss.android.account.v2.view.AccountLoginActivity}
t5187 f}} state=PAUSED]
......

关键堆栈

一直在重复TaskFragment.startPausing -> TaskFragment.sleepIfPossible 的堆栈

  at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1690)
  at com.android.server.wm.TaskFragment.sleepIfPossible(TaskFragment.java:856)
  at com.android.server.wm.Task.lambda$goToSleepIfPossible$20(Task.java:5425)
  at com.android.server.wm.Task$$ExternalSyntheticLambda9.accept(unavailable:6)
  at com.android.server.wm.TaskFragment.forAllLeafTaskFragments(TaskFragment.java:1952)
  at com.android.server.wm.Task.lambda$forAllLeafTasksAndLeafTaskFragments$14(Task.java:3699)
  at com.android.server.wm.Task$$ExternalSyntheticLambda31.accept(unavailable:6)
  at com.android.server.wm.Task.forAllLeafTasks(Task.java:3644)
  at com.android.server.wm.Task.forAllLeafTasksAndLeafTaskFragments(Task.java:3687)
  at com.android.server.wm.Task.goToSleepIfPossible(Task.java:5424)
  at com.android.server.wm.Task.checkReadyForSleep(Task.java:5406)
  at com.android.server.wm.ActivityRecord.addToStopping(ActivityRecord.java:6974)
  at com.android.server.wm.ActivityRecord.completeFinishing(ActivityRecord.java:4091)
  at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1854)
  at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1696)
   ... 
   ...
  at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1696)
  at com.android.server.wm.TaskFragment.sleepIfPossible(TaskFragment.java:856)
  at com.android.server.wm.Task.lambda$goToSleepIfPossible$20(Task.java:5425)
  at com.android.server.wm.Task$$ExternalSyntheticLambda9.accept(unavailable:6)
  at com.android.server.wm.TaskFragment.forAllLeafTaskFragments(TaskFragment.java:1952)
  at com.android.server.wm.Task.lambda$forAllLeafTasksAndLeafTaskFragments$14(Task.java:3699)
  at com.android.server.wm.Task$$ExternalSyntheticLambda31.accept(unavailable:6)
  at com.android.server.wm.Task.forAllLeafTasks(Task.java:3644)
  at com.android.server.wm.Task.forAllLeafTasksAndLeafTaskFragments(Task.java:3687)
  at com.android.server.wm.Task.goToSleepIfPossible(Task.java:5424)
  at com.android.server.wm.Task.checkReadyForSleep(Task.java:5406)
  at com.android.server.wm.ActivityRecord.addToStopping(ActivityRecord.java:6974)
  at com.android.server.wm.ActivityRecord.completeFinishing(ActivityRecord.java:4091)
  at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1854)
  at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1696)
  
  at com.android.server.wm.TaskFragment.sleepIfPossible(TaskFragment.java:856)
  at com.android.server.wm.Task.lambda$goToSleepIfPossible$20(Task.java:5425)
  at com.android.server.wm.Task$$ExternalSyntheticLambda9.accept(unavailable:6)
  at com.android.server.wm.TaskFragment.forAllLeafTaskFragments(TaskFragment.java:1952)
  at com.android.server.wm.Task.lambda$forAllLeafTasksAndLeafTaskFragments$14(Task.java:3699)
  at com.android.server.wm.Task$$ExternalSyntheticLambda31.accept(unavailable:6)
  at com.android.server.wm.Task.forAllLeafTasks(Task.java:3644)
  at com.android.server.wm.Task.forAllLeafTasksAndLeafTaskFragments(Task.java:3687)
  at com.android.server.wm.Task.goToSleepIfPossible(Task.java:5424)
  at com.android.server.wm.Task.checkReadyForSleep(Task.java:5406)
  at com.android.server.wm.ActivityRecord.completeResumeLocked(ActivityRecord.java:6766)
  at com.android.server.wm.TaskFragment.resumeTopActivity(TaskFragment.java:1609)
  at com.android.server.wm.Task.resumeTopActivityInnerLocked(Task.java:5665)
  at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5593)
  at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5648)
  at com.android.server.wm.ActivityRecord.makeActiveIfNeeded(ActivityRecord.java:6551)
  at com.android.server.wm.RootWindowContainer.lambda$resumeFocusedTasksTopActivities$18(RootWindowContainer.java:2468)
  at com.android.server.wm.RootWindowContainer$$ExternalSyntheticLambda17.accept(unavailable:15)
  at com.android.server.wm.Task.forAllRootTasks(Task.java:3656)
  at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2127)
  at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2127)
  at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2127)
  ... repeated 2 times
  at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2120)
  at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2446)
  at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2424)
  at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2419)
  at com.android.server.wm.ActivityRecord.addToFinishingAndWaitForIdle(ActivityRecord.java:4178)
  at com.android.server.wm.ActivityRecord.completeFinishing(ActivityRecord.java:4095)
  at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1854)
  at com.android.server.wm.ActivityRecord.activityPaused(ActivityRecord.java:6784)
  at com.android.server.wm.ActivityClientController.activityPaused(ActivityClientController.java:191)
  - locked <0x093a97ff> (a com.android.server.wm.WindowManagerGlobalLock)
  at android.app.IActivityClientController$Stub.onTransact(IActivityClientController.java:582)
  at com.android.server.wm.ActivityClientController.onTransact(ActivityClientController.java:131)
  at android.os.Binder.execTransactInternal(Binder.java:1285)
  at android.os.Binder.execTransact(Binder.java:1249)

关键代码

首先查看关键log中重复log打印的地方在TaskFragment#startPausing()

  at com.android.server.wm.Task.goToSleepIfPossible(Task.java:5424)
  // 这里的shouldSleepActivities返回true,才会走到上面的goToSleepIfPossible
  at com.android.server.wm.Task.checkReadyForSleep(Task.java:5406)
  at com.android.server.wm.ActivityRecord.addToStopping(ActivityRecord.java:6974)
  at com.android.server.wm.ActivityRecord.completeFinishing(ActivityRecord.java:4091)
  at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1854)  
  // shouldSleepActivities 返回false,才会走到上面的completePause
  at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1696)
  1. 因为上面有走到TaskFragment#completePause,所以这里的shouldSleepActivities会返回false
// TaskFragment.java
boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming,
        String reason) {
    ......
    if (mPausingActivity != null) {
        Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
                + " state=" + mPausingActivity.getState());
        // 按理谷歌这里已经做了规避递归,此处看log应该还没亮屏,不会走到completePause陷入递归
        if (!shouldSleepActivities()) {
            // Avoid recursion among check for sleep and complete pause during sleeping.
            // Because activity will be paused immediately after resume, just let pause
            // be completed by the order of activity paused from clients.
            completePause(false, resuming);
        }
    }
  1. 因为Task.checkReadyForSleep后面有走到Task.goToSleepIfPossible,所以这里的shouldSleepActivities会返回true
// Task.java
void checkReadyForSleep() {
    if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
        mTaskSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
    }
}
  1. 但是对同一个Task而言,一个方法基本不会在一个堆栈里既返回false,又返回true。从shouldSleepActivities本身查看下。
    查看代码可以得出,Task继承自TaskFragment,但是TaskFragment的shouldSleepActivities永远返回false,而Task中的shouldSleepActivities在此息屏场景下应该是返回true的。
//Task.java
boolean shouldSleepActivities() {
    final DisplayContent display = mDisplayContent;
    final boolean isKeyguardGoingAway = (mDisplayContent != null)
            ? mDisplayContent.isKeyguardGoingAway()
            : mRootWindowContainer.getDefaultDisplay().isKeyguardGoingAway();

    // Do not sleep activities in this root task if we're marked as focused and the keyguard
    // is in the process of going away.
    if (isKeyguardGoingAway && isFocusedRootTaskOnDisplay()
            // Avoid resuming activities on secondary displays since we don't want bubble
            // activities to be resumed while bubble is still collapsed.
            // TODO(b/113840485): Having keyguard going away state for secondary displays.
            && display.isDefaultDisplay) {
        return false;
    }

    return display != null ? display.isSleeping() : mAtmService.isSleepingLocked();
} 
// TaskFragment.java
boolean shouldSleepActivities() {
    return false;
}

总结 & 解决

合理推测:
默认情况下Task是TaskFragment子类,Task重写了shouldSleepActivities方法的话就应该执行Task中的方法。但是在平行窗口模式下,Task中包含TaskFragment对象,且jira中也可看到只在pad/Fold等有平行窗口的机型上上报。

本地验证 :
本地测试发现,平行窗口右侧页面最上方是诸如登录页面的半透明theme样式的页面finish且屏幕此时息屏(本地android studio 赋值DisplayContent的mSleeping为true)可复现该问题。

问题解决:
在这里插入图片描述

给谷歌提patch:
https://android-review.googlesource.com/c/platform/frameworks/base/+/2383980

TaskFragment#getVisibility 未处理小窗模式导致递归

复现步骤

  1. 给微信设置应用锁,以小窗方式打开微信
  2. 息屏后亮屏,应用锁会启动在微信Task最上面
    结果:锁屏解锁后屏幕无反应,过一阵才恢复正常进入桌面

关键log

其次按照上面复现步骤确定ConfirmAccessControl本来的状态应该为resume

.....
// ConfirmAccessControl 一直重复打印resume、pause相关生命周期
// 注意这里每次的线程id不同,说明不是同一个线程堆栈出现递归
07-14 17:40:50.029  1000  2339  2381 I wm_resume_activity: [0,201462615,23,com.miui.securitycenter/com.miui.applicationlock.ConfirmAccessControl]
07-14 17:40:50.032  1000  2339  2381 I wm_pause_activity: [0,201462615,com.miui.securitycenter/com.miui.applicationlock.ConfirmAccessControl,userLeaving=false,pauseBackTasks]
07-14 17:40:50.182  1000  9884  9884 I wm_on_resume_called: [0,201462615,com.miui.applicationlock.ConfirmAccessControl,RESUME_ACTIVITY,62]
07-14 17:40:50.188  1000  9884  9884 I wm_on_paused_called: [0,201462615,com.miui.applicationlock.ConfirmAccessControl,performPause,2]
07-14 17:40:50.195  1000  2339  5156 I wm_resume_activity: [0,201462615,23,com.miui.securitycenter/com.miui.applicationlock.ConfirmAccessControl]
07-14 17:40:50.197  1000  2339  5156 I wm_pause_activity: [0,201462615,com.miui.securitycenter/com.miui.applicationlock.ConfirmAccessControl,userLeaving=false,pauseBackTasks]
07-14 17:40:50.312  1000  9884  9884 I wm_on_resume_called: [0,201462615,com.miui.applicationlock.ConfirmAccessControl,RESUME_ACTIVITY,50]
07-14 17:40:50.317  1000  9884  9884 I wm_on_paused_called: [0,201462615,com.miui.applicationlock.ConfirmAccessControl,performPause,4]
.......

关键堆栈

从关键log可以看出一直在重复执行pause流程,所以在PauseActivityItem 打断点,发现system server的binder线程一直重复执行如下堆栈,且针对的也确实都是ConfirmAccessControl页面。
形成了ActivityClientController.activityPaused -> ClientLifecycleManager.scheduleTransaction,system server <-> app之间的递归。

Breakpoint reached
// system server binder app去执行pause流程
at com.android.server.wm.ClientLifecycleManager.scheduleTransaction(ClientLifecycleManager.java:69)
at com.android.server.wm.TaskFragment.schedulePauseActivity(TaskFragment.java:1847)
at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1789)
at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1686)
at com.android.server.wm.TaskDisplayArea.lambda$pauseBackTasks$8$com-android-server-wm-TaskDisplayArea(TaskDisplayArea.java:1400)
at com.android.server.wm.TaskDisplayArea$$ExternalSyntheticLambda5.accept(D8$$SyntheticClass:-1)
at com.android.server.wm.Task.forAllLeafTasks(Task.java:3673)
at com.android.server.wm.WindowContainer.forAllLeafTasks(WindowContainer.java:2116)
at com.android.server.wm.TaskDisplayArea.pauseBackTasks(TaskDisplayArea.java:1392)
// 第二次出现resume逻辑,且此次resume的是应用锁下面LauncherUI页面,不合理
at com.android.server.wm.TaskFragment.resumeTopActivity(TaskFragment.java:1325)
at com.android.server.wm.Task.lambda$resumeTopActivityInnerLocked$24(Task.java:5787)
at com.android.server.wm.Task$$ExternalSyntheticLambda33.accept(D8$$SyntheticClass:-1)
at com.android.server.wm.TaskFragment.forAllLeafTaskFragments(TaskFragment.java:1976)
at com.android.server.wm.TaskFragment.forAllLeafTaskFragments(TaskFragment.java:1964)
at com.android.server.wm.Task.resumeTopActivityInnerLocked(Task.java:5780)
at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5702)
at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5762)
at com.android.server.wm.ActivityRecord.makeActiveIfNeeded(ActivityRecord.java:6801)
at com.android.server.wm.EnsureActivitiesVisibleHelper.setActivityVisibilityState(EnsureActivitiesVisibleHelper.java:226)
at com.android.server.wm.EnsureActivitiesVisibleHelper.process(EnsureActivitiesVisibleHelper.java:144)
at com.android.server.wm.TaskFragment.updateActivityVisibilities(TaskFragment.java:1181)
at com.android.server.wm.Task.lambda$ensureActivitiesVisible$23(Task.java:5598)
at com.android.server.wm.Task$$ExternalSyntheticLambda23.accept(D8$$SyntheticClass:-1)
at com.android.server.wm.Task.forAllLeafTasks(Task.java:3673)
at com.android.server.wm.Task.ensureActivitiesVisible(Task.java:5597)
at com.android.server.wm.TaskDisplayArea.lambda$ensureActivitiesVisible$11(TaskDisplayArea.java:1997)
at com.android.server.wm.TaskDisplayArea$$ExternalSyntheticLambda2.accept(D8$$SyntheticClass:-1)
at com.android.server.wm.Task.forAllRootTasks(Task.java:3685)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2151)
at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2144)
at com.android.server.wm.TaskDisplayArea.ensureActivitiesVisible(TaskDisplayArea.java:1996)
// 第一次准备resume ConfirmAccessControl页面,正确流程
at com.android.server.wm.TaskFragment.resumeTopActivity(TaskFragment.java:1221)
at com.android.server.wm.Task.resumeTopActivityInnerLocked(Task.java:5779)
at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5702)
at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5727)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2531)
at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2517)
at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1917)
at com.android.server.wm.ActivityRecord.activityPaused(ActivityRecord.java:7035)
// app进程binder 系统pause已完成
at com.android.server.wm.ActivityClientController.activityPaused(ActivityClientController.java:195)
at android.app.IActivityClientController$Stub.onTransact(IActivityClientController.java:592)
at com.android.server.wm.ActivityClientController.onTransact(ActivityClientController.java:133)
at android.os.Binder.execTransactInternal(Binder.java:1285)
at android.os.Binder.execTransact(Binder.java:1249)
PauseActivityItem{finished=false,userLeaving=false,configChanges=0,dontReport=false,autoEnteringPip=false}

关键代码

不合理的TaskFragment结构

从上面堆栈可以看出第二次resume 应用锁下面的LauncherUI页面不太合理,查看微信Task发现如下不合理的结构:
在这里插入图片描述
所以推测上面的第一次resume是正常resume RootTask的直接child ConfirmAccessControl页面,第二次resume 异常resume了Task的child的TaskFragment中的LauncherUI页面。

从上面堆栈可以看出第二次resume的触发点是下面resumeTopActivityInnerLocked方法的forAllLeafTaskFragments的callback中。

而forAllLeafTaskFragments 主要是遍历Task下面的TaskFragment的并执行resumeTopActivity操作,一般callback执行场景应该是在平行窗口的场景中,确保左右两个TaskFragment中的top页面能正常resume。

@GuardedBy("mService")
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options,
        boolean deferPause) {
    if (!mAtmService.isBooting() && !mAtmService.isBooted()) {
        // Not ready yet!
        return false;
    }
    final ActivityRecord topActivity = topRunningActivity(true /* focusableOnly */);
    if (topActivity == null) {
        // There are no activities left in this task, let's look somewhere else.
        return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options);
    }
    final boolean[] resumed = new boolean[1];
    // 第一次resume,正常页面
    final TaskFragment topFragment = topActivity.getTaskFragment();
    resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);
    forAllLeafTaskFragments(f -> {
        if (topFragment == f) {
            return;
        }
        if (!f.canBeResumed(null /* starting */)) {
            return;
        }
        // 第二次resume到TaskFragment中的页面
        resumed[0] |= f.resumeTopActivity(prev, options, deferPause);
    }, true);
    return resumed[0];
}

查看该Task中的TaskFragment创建是否合理:

// 微信进程创建TaskFragment堆栈如下
createTaskFragment:482, WindowContainerTransaction (android.window)
createTaskFragment:251, JetpackTaskFragmentOrganizer (androidx.window.extensions.embedding)
createTaskFragment:434, SplitPresenter (androidx.window.extensions.embedding)
createTaskFragment:241, JetpackTaskFragmentOrganizer (androidx.window.extensions.embedding)
createTaskFragmentAndReparentActivity:262, JetpackTaskFragmentOrganizer (androidx.window.extensions.embedding)
expandActivity:199, JetpackTaskFragmentOrganizer (androidx.window.extensions.embedding)
expandActivity:682, SplitController (androidx.window.extensions.embedding)
resolveActivityToContainer:560, SplitController (androidx.window.extensions.embedding)
onActivityCreated:494, SplitController (androidx.window.extensions.embedding)
onActivityPostCreated:1796, SplitController$LifecycleCallbacks (androidx.window.extensions.embedding)
dispatchActivityPostCreated:395, Application (android.app)
dispatchActivityPostCreated:1486, Activity (android.app)
performCreate:8590, Activity (android.app)

本地调试微信的WelcomeActivity满足了mSplitRules规则,走了expandActivity的逻辑,导致创建了TaskFragment,但这些规则是微信自己适配谷歌平行窗口的,所以不能从创建TaskFragment来规避该问题。
在这里插入图片描述

// SplitController.java
@VisibleForTesting
boolean resolveActivityToContainer(@NonNull Activity activity, boolean isOnReparent) {
    // 1. Whether the new launched activity should always expand.
    if (shouldExpand(activity, null /* intent */)) {
        expandActivity(activity);
        return true;
    }

全屏场景正常

本地测试全屏场景下按以上步骤不能复现问题,经调试发现不满足canBeResumed,直接return,走不到下面的resumeTopActivity流程,形成不了递归。

// Task.java
@GuardedBy("mService")
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options,
        boolean deferPause) {
    .......
    final boolean[] resumed = new boolean[1];
    final TaskFragment topFragment = topActivity.getTaskFragment();
    resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);
    forAllLeafTaskFragments(f -> {
        if (topFragment == f) {
            return;
        }
        // 全屏情况下,不满足canBeResumed,直接return,走不到下面的resumeTopActivity流程,形成不了递归
         if (!f.canBeResumed(null /* starting */)) {
            return;
        }
        resumed[0] |= f.resumeTopActivity(prev, options, deferPause);
    }, true);
    return resumed[0];
} 

所以来看看canBeResumed方法

// TaskFragment.java
boolean canBeResumed(@Nullable ActivityRecord starting) {
    // No need to resume activity in TaskFragment that is not visible.
    return isTopActivityFocusable()
            && getVisibility(starting) == TASK_FRAGMENT_VISIBILITY_VISIBLE;
} 
  • 全屏时getVisibility(starting)获取到的是TASK_FRAGMENT_VISIBILITY_INVISIBLE,所以此处canBeResumed返回false
  • 而小窗下获取到的是TASK_FRAGMENT_VISIBILITY_VISIBLE,所以会导致这个问题
// TaskFragment.java
int getVisibility(ActivityRecord starting) {
        ........
        // 这里的other即ConfirmAccessControl页面
         final int otherWindowingMode = other.getWindowingMode();
        if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
            if (isTranslucent(other, starting)) {
                // Can be visible behind a translucent fullscreen TaskFragment.
                gotTranslucentFullscreen = true;
                continue;
            }
            // 全屏直接返回,不递归
            return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
        // 小窗的mode为WINDOWING_MODE_FREEFORM,也不满足下面的条件
         } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
                 && other.matchParentBounds()) {
            if (isTranslucent(other, starting)) {
                // Can be visible behind a translucent TaskFragment.
                gotTranslucentFullscreen = true;
                continue;
            }
            // Multi-window TaskFragment that matches parent bounds would occlude other children
            return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
        }

       .....
    // 小窗最后走的这里的流程,返回TASK_FRAGMENT_VISIBILITY_VISIBLE
     // Lastly - check if there is a translucent fullscreen TaskFragment on top.
    return gotTranslucentFullscreen
            ? TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
            : TASK_FRAGMENT_VISIBILITY_VISIBLE;
}

总结 & 解决

合理总结:
上面的getVisibility方法中,小窗应该也跟multi window mode一样处理,毕竟它覆盖住了同一层级底下的TaskFragment,所以该TaskFragment的getVisibility应该也返回TASK_FRAGMENT_VISIBILITY_INVISIBLE。

给谷歌提patch:
在这里插入图片描述
谷歌merge
https://android-review.googlesource.com/c/platform/frameworks/base/+/2674876

WPC#updateTopResumingActivityInProcessIfNeeded方法未处理Stopping列表

关键log

// 看event log是startPausing过程中陷入递归
08-19 00:27:07.266 1000 11789 17981 I wm_create_activity: [0,93149810,1422,com.taobao.idlefish/.maincontainer.activity.MainActivity,android.intent.action.MAIN,NULL,NULL,270598144]
08-19 00:27:08.036 1000 11789 17981 I wm_restart_activity: [0,93149810,1422,com.taobao.idlefish/.maincontainer.activity.MainActivity]
08-19 00:27:08.978 1000 11789 21836 I wm_finish_activity: [0,197701613,1422,com.lbe.security.miui/com.android.packageinstaller.permission.ui.GrantPermissionsActivity,app-request]
08-19 00:27:09.000 10284 16745 16745 I wm_on_create_called: [0,93149810,com.taobao.idlefish.maincontainer.activity.MainActivity,performCreate,395]
// 陷入递归的页面为GrantPermissionsActivity
08-19 00:27:09.008 1000 11789 21836 I wm_pause_activity: [0,197701613,com.lbe.security.miui/com.android.packageinstaller.permission.ui.GrantPermissionsActivity,userLeaving=false,finish]
08-19 00:27:09.035 10190 13482 13482 I wm_on_paused_called: [0,197701613,com.android.packageinstaller.permission.ui.GrantPermissionsActivity,performPause,0]
// 重复打印的log
08-19 00:27:09.056  1000 11789 11844 I am_wtf  : [0,11789,system_server,-1,ActivityTaskManager,Going to pause when pause is already pending for ActivityRecord\\\{bc8afed u0 com.lbe.security.miui/com.android.packageinstaller.permission.ui.GrantPermissionsActivity t1422 f}} state=FINISHING]
.......
08-19 00:28:03.749  1000 11789 11844 I am_wtf  : [0,11789,system_server,-1,ActivityTaskManager,Going to pause when pause is already pending for ActivityRecord\\{bc8afed u0 com.lbe.security.miui/com.android.packageinstaller.permission.ui.GrantPermissionsActivity t1422 f}} state=FINISHING]

关键堆栈

看下面堆栈可以看出TaskFragment.startPausing- >WindowProcessController.updateTopResumingActivityInProcessIfNeeded之间的递归。

  at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1773)
 ........
// 第二次循环堆栈
  at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1779)
  at com.android.server.wm.WindowProcessController.updateTopResumingActivityInProcessIfNeeded(WindowProcessController.java:925)
  at com.android.server.wm.ActivityRecord.canResumeByCompat(ActivityRecord.java:11696)
  at com.android.server.wm.ActivityRecord.shouldBeResumed(ActivityRecord.java:7019)
  at com.android.server.wm.ActivityRecord.shouldResumeActivity(ActivityRecord.java:7007)
  at com.android.server.wm.ActivityRecord.makeActiveIfNeeded(ActivityRecord.java:6940)
  at com.android.server.wm.RootWindowContainer.lambda$resumeFocusedTasksTopActivities$17(RootWindowContainer.java:2654)
  at com.android.server.wm.RootWindowContainer$$ExternalSyntheticLambda14.accept(unavailable:13)
  at com.android.server.wm.Task.forAllRootTasks(Task.java:3677)
  at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2231)
  at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2231)
  at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2231)
  ... repeated 2 times
  at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2224)
  at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2628)
  at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2606)
  at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2601)
  at com.android.server.wm.ActivityRecord.addToFinishingAndWaitForIdle(ActivityRecord.java:4405)
  at com.android.server.wm.ActivityRecord.completeFinishing(ActivityRecord.java:4322)
 at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1951)
  // 第一次循环堆栈
  at com.android.server.wm.TaskFragment.startPausing(TaskFragment.java:1779)
  at com.android.server.wm.WindowProcessController.updateTopResumingActivityInProcessIfNeeded(WindowProcessController.java:925)
  at com.android.server.wm.ActivityRecord.canResumeByCompat(ActivityRecord.java:11696)
  at com.android.server.wm.ActivityRecord.shouldBeResumed(ActivityRecord.java:7019)
  at com.android.server.wm.ActivityRecord.shouldResumeActivity(ActivityRecord.java:7007)
  at com.android.server.wm.ActivityRecord.makeActiveIfNeeded(ActivityRecord.java:6940)
  at com.android.server.wm.RootWindowContainer.lambda$resumeFocusedTasksTopActivities$17(RootWindowContainer.java:2654)
  at com.android.server.wm.RootWindowContainer$$ExternalSyntheticLambda14.accept(unavailable:13)
  at com.android.server.wm.Task.forAllRootTasks(Task.java:3677)
  at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2231)
  at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2231)
  at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2231)
  ... repeated 2 times
  at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2224)
  at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2628)
  at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2606)
  at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2601)
  at com.android.server.wm.ActivityRecord.addToFinishingAndWaitForIdle(ActivityRecord.java:4405)
  at com.android.server.wm.ActivityRecord.completeFinishing(ActivityRecord.java:4322)
  at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1951)
  at com.android.server.wm.ActivityRecord.activityPaused(ActivityRecord.java:7188)
  at com.android.server.wm.ActivityClientController.activityPaused(ActivityClientController.java:243)
  - locked <0x08c0fce1> (a com.android.server.wm.WindowManagerGlobalLock)
  at android.app.IActivityClientController$Stub.onTransact(IActivityClientController.java:673)
  at com.android.server.wm.ActivityClientController.onTransact(ActivityClientController.java:171)
  at android.os.Binder.execTransactInternal(Binder.java:1346)
  at android.os.Binder.execTransact(Binder.java:1282)

关键代码

log打印的地方:

// TaskFragment.java
boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming,
        String reason) {
    ......
    if (mPausingActivity != null) {
        Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
                + " state=" + mPausingActivity.getState());
        if (!shouldSleepActivities()) {
            // Avoid recursion among check for sleep and complete pause during sleeping.
            // Because activity will be paused immediately after resume, just let pause
            // be completed by the order of activity paused from clients.
            completePause(false, resuming);
        }
    }

主要看下updateTopResumingActivityInProcessIfNeeded 的代码逻辑

  1. 上面陷入pause循环的activity所在Task对应的app的targetSdkVersion 小于29
    上面的event log可以看出发生递归的GrantPermissionsActivity 处于task#1422,对应的app为com.taobao.idlefish
// 1. 该app的targetSdk确实小于28
  Package [com.taobao.idlefish] (4d601a0):
    appId=10284
    pkg=Package{55da159 com.taobao.idlefish}
   .......
    versionCode=340 minSdk=21 targetSdk=28
  1. mPreQTopResumedActivity 和 pause循环的activity处于同一个Task/TaskFragment
    因为InitActivity为闲鱼的第一个actiivty,所以这里的topDisplay为null,赋值InitActivity到mPreQTopResumedActivity
  2. mPreQTopResumedActivity 不为null且处于RESUMED状态
// topRunningActivity为MainActivity,但是这里这个生命周期有点奇怪,无法推断出具体的场景
08-19 00:27:07.266 1000 11789 17981 I wm_create_activity: [0,93149810,1422,com.taobao.idlefish/.maincontainer.activity.MainActivity,android.intent.action.MAIN,NULL,NULL,270598144]
08-19 00:27:08.036 1000 11789 17981 I wm_restart_activity: [0,93149810,1422,com.taobao.idlefish/.maincontainer.activity.MainActivity]
// 因为InitActivity还没执行pause,所以它的状态仍然是RESUMED
08-19 00:27:08.041  1000 11789 17981 I wm_add_to_stopping: [0,3457984,com.taobao.idlefish/com.taobao.fleamarket.home.activity.InitActivity,makeInvisible]
// WindowProcessController#updateTopResumingActivityInProcessIfNeeded
boolean updateTopResumingActivityInProcessIfNeeded(@NonNull ActivityRecord activity) {
    
// 对于大部分app来说,基本在这里直接返回,只有targetSdk小于Q的app才有可能发生这个问题
    if (mInfo.targetSdkVersion >= Q || mPreQTopResumedActivity == activity) {
        return true;
    }

    if (!activity.isAttached()) {
        // No need to update if the activity hasn't attach to any display.
        return false;
    }

    boolean canUpdate = false;
    // 因为堆栈走到了下面的taskFrag.startPausing,所以mPreQTopResumedActivity != null   
    // 但是对于Task中的第一个Activity而言,此处的mPreQTopResumedActivity为null,topDisplay为null
    final DisplayContent topDisplay =
            (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isAttached())
                    ? mPreQTopResumedActivity.mDisplayContent
                    : null;
    // Update the topmost activity if current top activity is
    // - not on any display OR
    // - no longer visible OR
    // - not focusable (in PiP mode for instance)
    // 第一个Activity会走如下if语句
     if (topDisplay == null
            || !mPreQTopResumedActivity.isVisibleRequested()
            || !mPreQTopResumedActivity.isFocusable()) {
        canUpdate = true;
    }

    final DisplayContent display = activity.mDisplayContent;
    // Update the topmost activity if the current top activity wasn't on top of the other one.
    if (!canUpdate && topDisplay.compareTo(display) < 0) {
        canUpdate = true;
    }

    // Update the topmost activity if the activity has higher z-order than the current
    // top-resumed activity.
    if (!canUpdate) {
        final ActivityRecord ar = topDisplay.getActivity(r -> r == activity,
                true /* traverseTopToBottom */, mPreQTopResumedActivity);
        if (ar != null && ar != mPreQTopResumedActivity) {
            canUpdate = true;
        }
    }
    // 第一个Activity直接赋值到mPreQTopResumedActivity中
    if (canUpdate) {
        // Make sure the previous top activity in the process no longer be resumed.
        if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) {
            final TaskFragment taskFrag = mPreQTopResumedActivity.getTaskFragment();
            if (taskFrag != null) {
                boolean userLeaving = taskFrag.shouldBeVisible(null);
                taskFrag.startPausing(userLeaving, false /* uiSleeping */,
                        activity, "top-resumed-changed");
            }
        }
        mPreQTopResumedActivity = activity;
    }
    return canUpdate;
} 

总结 & 解决

本地正常情况基本都是先pause在执行addToStoppping流程,所以InitActivity基本应该都是Paused状态而不是Resumed状态。

或许可以通过如下修改来规避这个问题,但是具体场景不可预知,无法验证,而且看这个场景发生此次递归的概率极小。暂不提change,先观察复现。

// WindowProcessController#updateTopResumingActivityInProcessIfNeeded
         if (canUpdate) {
             // Make sure the previous top activity in the process no longer be resumed.
-            if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) {
+            if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)
+                    && !mAtm.mTaskSupervisor.mStoppingActivities.contains(mPreQTopResumedActivity)) {
                 final TaskFragment taskFrag = mPreQTopResumedActivity.getTaskFragment();
                 if (taskFrag != null) {
                     boolean userLeaving = taskFrag.shouldBeVisible(null);
                     taskFrag.startPausing(userLeaving, false /* uiSleeping */,
        activity, "top-resumed-changed");
@@ -926,7 +927,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
                             activity, "top-resumed-changed");
                 }
             }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1010293.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

dlib库详解及Python环境安装指南

dlib是一个开源的机器学习库&#xff0c;它包含了众多的机器学习算法&#xff0c;例如分类、回归、聚类等。此外&#xff0c;dlib还包含了众多的数据处理、模型训练等工具&#xff0c;使得其在机器学习领域被广泛应用。本文将详细介绍dlib库的基本概念、功能&#xff0c;以及如…

删除数据库

MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129334507?spm1001.2014.3001.5502 语法格式: drop database 数据库名称;这个命令谨慎使用,俗话说:删库跑路! 案列:删除testing数据库,并验证 mysql> show databases; -----------…

Kernel for SQL Database Recovery 21.1 Crack

SQL Server恢复工具 Kernel for SQL Database Recovery 21.1 具有针对不同 SQL Server 版本的全面恢复选项。它具有预览和选择功能来恢复精确的数据库对象。 好处 SQL 数据库恢复可为您带来多种好处。 完全恢复所有数据库组件 将损坏的 MDF/NDF 文件有效恢复到 Live SQL Serve…

HDMI 直通 ILA 调试实验

FPGA教程学习 第十四章 HDMI 直通 ILA 调试实验 文章目录 FPGA教程学习前言实验原理程序设计实验过程实验尝试总结TODO 前言 HDMI 输入直通到 HDMI 输出的显示&#xff0c;完成一个简单的 HDMI 输入输出检测。 实验原理 开发板 HDMI 输出接口芯片使用 ADV7511&#xff0c;HD…

【深度学习】 Python 和 NumPy 系列教程(廿二):Matplotlib详解:2、3d绘图类型(8)3D饼图(3D Pie Chart)

一、前言 Python是一种高级编程语言&#xff0c;由Guido van Rossum于1991年创建。它以简洁、易读的语法而闻名&#xff0c;并且具有强大的功能和广泛的应用领域。Python具有丰富的标准库和第三方库&#xff0c;可以用于开发各种类型的应用程序&#xff0c;包括Web开发、数据分…

期权交易保证金比例一般是多少?

期权交易是一种非常受欢迎的投资方式之一&#xff0c;它为期权市场带来了更为多样化和灵活化的交易形式。而其中的期权卖方保证金比例是期权交易中的一个重要指标&#xff0c;直接关系到投资者的风险与收益&#xff0c;下文介绍期权交易保证金比例一般是多少&#xff1f;本文来…

第六章 图 九、拓扑排序

一、AOV网 二、拓扑排序 删除入度为0的结点。 第一次遍历&#xff0c;入度为0的点为0和2&#xff0c;将他们加入排序序列0->2 第二次遍历&#xff0c;入度为0的点为1和4&#xff0c;将他们加入排序序列0->2->1->4 第三次遍历&#xff0c;入度为0的点为3&#xf…

【webrtc】VCMSessionInfo 合并一个可解码的帧

知乎大神的概括&#xff1a;VCMFrameBuffer 帧中包含VCMSessionInfo的处理&#xff0c;对VPX、h264(分析Nalus)的同一帧中的所有包进行过滤并进行完整帧组帧&#xff0c;用于sink给后续的解码。用于解码器的所以插入的数据都是VCMPacketframe_buffer指向一帧的起始数据地址&…

Django系列:Django简介与MTV架构体系概述

Django系列 Django简介与MTV架构体系概述 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/132890054 【介…

企业架构LNMP学习笔记43

memcached的使用&#xff1a; 命令行连接和操作&#xff1a; telnet连接使用&#xff1a; memcached默认使用启动服务占用tcp 11211端口&#xff0c;可以通过telnet进行连接使用。 安装telnet进行连接&#xff1a; 连接成功&#xff0c;敲击多次&#xff0c;如果看到error&…

Linux常用命令字典篇

Linux命令 1. 翻页查看文件 less [-N] 文件名&#xff1a;可以向后翻页&#xff0c;也可以向前翻页&#xff0c;-N表示显示行号 more 文件名&#xff1a;仅可以向后翻页 2. 端口占用信息查看 netstat -tunlp | grep 端口号&#xff1a;查看端口号对应的信息 lsof i: 端口号…

Marin说PCB之封装设计系列---(02)--异形焊盘的封装设计总结

每天下班回家看电视本来是一件很美好的事情&#xff0c;可是正当我磕着瓜子看着异人之下的时候&#xff0c;手机突然响起来了&#xff0c;我以为是我们组哪个同事找我呢。一接电话居然是我的老朋友陈世美陈总&#xff0c;江湖人称少妇杀手。给我打电话主要是说他最近遇到一个异…

vite和webpack的区别

vite和webpack的区别 1、前言2、Webpack2.1 Webpack简述2.2 Webpack常用插件 3、Vite3.1 Vite简述3.2 Vite插件推荐 4、区别4.1 开发模式不同4.2 打包效率不同4.3 插件生态不同4.4 配置复杂度不同4.5 热更新机制不同 5、总结 1、前言 Webpack和Vite是现代前端开发中非常重要的…

线性代数的本质(九)——二次型与合同

文章目录 二次型与合同二次型与标准型二次型的分类度量矩阵与合同 二次型与合同 二次型与标准型 Grant&#xff1a;二次型研究的是二次曲面在不同基下的坐标变换 由解析几何的知识&#xff0c;我们了解到二次函数的一次项和常数项只是对函数图像进行平移&#xff0c;并不会改变…

HSRP(热备份路由选择协议)的概念,原理与配置实验

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 梦想从未散场&#xff0c;传奇永不落幕&#xff0c;持续更新优质网络知识、Python知识、Linux知识以及各种小技巧&#xff0c;愿你我共同在CSDN进步 目录 一、了解HSRP协议 1. 什么是HSRP协议 2、HSRP协议的…

2023年主流固定资管理系统的特征

随着科技的不断发展&#xff0c;固定资产管理系统也在不断演进&#xff0c;以满足企业日益增长的管理需求。在2023年&#xff0c;主流固定资产管理系统将呈现出一些重要的特征&#xff0c;包括RFID功能、低代码平台功能和云计算功能。易点易动固定资产管理系统正是结合了这些特…

UWB芯片DW3000之PDOA测向实现源码

介绍 DW3000芯片的双天线端口特性可以测量无线输入信号的相位。当与天线响应的信息相结合时,这些信息可以用来帮助确定到达的方向和传输的位置。 根据设备的不同,将有一个或两个天线端口。具有两个天线端口的设备称为PDoA部件,而其他是非PDoA部件(见表1)。当涉及到到达相位…

算法分析与设计编程题 贪心算法

活动安排问题 题目描述 解题代码 vector<bool> greedySelector(vector<vector<int>>& intervals) {int n intervals.size();// 将活动区间按结束时间的从小到大排序auto cmp [](vector<int>& interval1, vector<int>& interval2…

(文末赠书)我为什么推荐应该人手一本《人月神话》

能点进来的朋友&#xff0c;说明你肯定是计算机工作的朋友或者对这本书正在仔细琢磨着的朋友。 文章目录 1、人人都会编程的时代&#xff0c;我们如何留存?2、小故事说明项目管理着为什么必看这本书3、如何评价《人月神话&#xff1a;纪念典藏版》4、本书的目录&#xff08;好…

模方新建工程时,显示空三与模型坐标系不一致怎么解决

答:检查空三xml与模型的metadata.xml的坐标系是否一致&#xff0c;metadata文件是否有在data目录外面。 模方是一款针对实景三维模型的冗余碎片、水面残缺、道路不平、标牌破损、纹理拉伸模糊等共性问题研发的实景三维模型修复编辑软件。模方4.0新增单体化建模模块&#xff0c;…