From ee0990f3bbc0afe1b669b90b9b390001db1f62db Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Wed, 26 May 2021 08:41:04 +0800 Subject: [PATCH] Migrate resuming/pausing activity to TaskFragment The resumed, pausing and last-paused activities are now set at the leaf task fragment. Also fix some unit tests that having a task containing both child activity and child task. Bug: 189384393 Test: build and boot Change-Id: I0e4b00130ffa624e7dab6d53cbd9f8f95bc5b3d8 --- data/etc/services.core.protolog.json | 300 +++--- .../com/android/server/wm/ActivityRecord.java | 71 +- .../server/wm/ActivityTaskManagerService.java | 4 +- .../server/wm/ActivityTaskSupervisor.java | 4 +- .../server/wm/RootWindowContainer.java | 22 +- .../core/java/com/android/server/wm/Task.java | 911 ++--------------- .../android/server/wm/TaskDisplayArea.java | 22 +- .../com/android/server/wm/TaskFragment.java | 945 +++++++++++++++++- .../android/server/wm/WindowContainer.java | 22 + .../server/wm/WindowProcessController.java | 14 +- .../server/wm/ActivityRecordTests.java | 6 +- .../com/android/server/wm/RootTaskTests.java | 29 +- .../server/wm/RootWindowContainerTests.java | 2 +- .../src/com/android/server/wm/TaskTests.java | 3 +- 14 files changed, 1262 insertions(+), 1093 deletions(-) diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 3c9086dde0214..1803b06083423 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -103,6 +103,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "-2010331310": { + "message": "resumeTopActivity: Top activity resumed (dontWaitForPause) %s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-2006946193": { "message": "setClientVisible: %s clientVisible=%b Callers=%s", "level": "VERBOSE", @@ -211,6 +217,12 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/TaskOrganizerController.java" }, + "-1886145147": { + "message": "resumeTopActivity: Going to sleep and all paused", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-1884933373": { "message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s", "level": "INFO", @@ -247,12 +259,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", "at": "com\/android\/server\/wm\/AppTransition.java" }, - "-1861864501": { - "message": "resumeTopActivityLocked: Going to sleep and all paused", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-1844540996": { "message": " Initial targets: %s", "level": "VERBOSE", @@ -325,12 +331,6 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimationController.java" }, - "-1768090656": { - "message": "Re-launching after pause: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-1750384749": { "message": "Launch on display check: allow launch on public display", "level": "DEBUG", @@ -415,12 +415,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-1655805455": { - "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-1647332198": { "message": "remove RecentTask %s when finishing user %d", "level": "INFO", @@ -433,6 +427,12 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java" }, + "-1633115609": { + "message": "Key dispatch not paused for screen off", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-1632122349": { "message": "Changing surface while display frozen: %s", "level": "VERBOSE", @@ -487,6 +487,12 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, + "-1564228464": { + "message": "App died while pausing: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-1559645910": { "message": "Looking for task of type=%s, taskAffinity=%s, intent=%s, info=%s, preferredTDA=%s", "level": "DEBUG", @@ -565,12 +571,6 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityStarter.java" }, - "-1492696222": { - "message": "App died during pause, not stopping: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-1480772131": { "message": "No app or window is requesting an orientation, return %d for display id=%d", "level": "VERBOSE", @@ -637,12 +637,24 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1421296808": { + "message": "Moving to RESUMED: %s (in existing)", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-1419762046": { "message": "moveRootTaskToDisplay: moving taskId=%d to displayId=%d", "level": "DEBUG", "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" }, + "-1419461256": { + "message": "resumeTopActivity: Resumed %s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-1413901262": { "message": "startRecentsActivity(): intent=%s", "level": "DEBUG", @@ -805,6 +817,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-1187377055": { + "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-1176488860": { "message": "SURFACE isSecure=%b: %s", "level": "INFO", @@ -919,12 +937,6 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, - "-1066383762": { - "message": "Sleep still waiting to pause %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-1060365734": { "message": "Attempted to add QS dialog window with bad token %s. Aborting.", "level": "WARN", @@ -979,6 +991,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-957060823": { + "message": "Moving to PAUSING: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-951939129": { "message": "Unregister task organizer=%s uid=%d", "level": "VERBOSE", @@ -1231,12 +1249,6 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, - "-672228342": { - "message": "resumeTopActivityLocked: Top activity resumed %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-668956537": { "message": " THUMBNAIL %s: CREATE", "level": "INFO", @@ -1261,11 +1273,11 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "-650261962": { - "message": "Sleep needs to pause %s", + "-648891906": { + "message": "Activity not running or entered PiP, resuming next.", "level": "VERBOSE", "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" + "at": "com\/android\/server\/wm\/TaskFragment.java" }, "-641258376": { "message": "realStartActivityLocked: Skipping start of r=%s some activities pausing...", @@ -1303,12 +1315,6 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-606328116": { - "message": "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-597091183": { "message": "Delete TaskDisplayArea uid=%d", "level": "VERBOSE", @@ -1369,23 +1375,17 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowAnimator.java" }, - "-533690126": { - "message": "resumeTopActivityLocked: Resumed %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-532081937": { "message": " Commit activity becoming invisible: %s", "level": "VERBOSE", "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, - "-527683022": { - "message": "resumeTopActivityLocked: Skip resume: some activity pausing.", + "-521613870": { + "message": "App died during pause, not stopping: %s", "level": "VERBOSE", "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" + "at": "com\/android\/server\/wm\/TaskFragment.java" }, "-519504830": { "message": "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s isEntrance=%b Callers=%s", @@ -1471,18 +1471,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" }, - "-427457280": { - "message": "App died while pausing: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, - "-417514857": { - "message": "Key dispatch not paused for screen off", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-415865166": { "message": "findFocusedWindow: Found new focus @ %s", "level": "VERBOSE", @@ -1591,11 +1579,17 @@ "group": "WM_DEBUG_LOCKTASK", "at": "com\/android\/server\/wm\/LockTaskController.java" }, - "-303497363": { - "message": "reparent: moving activity=%s to task=%d at %d", + "-312353598": { + "message": "Executing finish of activity: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, + "-310337305": { + "message": "Activity config changed during resume: %s, new next: %s", "level": "INFO", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" }, "-302468788": { "message": "Expected target rootTask=%s to be top most but found rootTask=%s", @@ -1615,12 +1609,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-279436615": { - "message": "Moving to PAUSING: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-262984451": { "message": "Relaunch failed %s", "level": "INFO", @@ -1645,12 +1633,6 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-234244777": { - "message": "Activity config changed during resume: %s, new next: %s", - "level": "INFO", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-230587670": { "message": "SyncGroup %d: Unfinished container: %s", "level": "VERBOSE", @@ -1717,12 +1699,6 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-118786523": { - "message": "Resume failed; resetting state to %s: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-116086365": { "message": "******************** ENABLING SCREEN!", "level": "INFO", @@ -1771,6 +1747,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/Session.java" }, + "-80004683": { + "message": "Resume failed; resetting state to %s: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-50336993": { "message": "moveFocusableActivityToTop: activity=%s", "level": "DEBUG", @@ -1903,12 +1885,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowContextListenerController.java" }, - "94402792": { - "message": "Moving to RESUMED: %s (in existing)", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "95216706": { "message": "hideIme target: %s ", "level": "DEBUG", @@ -1927,6 +1903,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/AppTransitionController.java" }, + "102618780": { + "message": "resumeTopActivity: Pausing %s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "108170907": { "message": "Add starting %s: startingData=%s", "level": "VERBOSE", @@ -2167,6 +2149,18 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "327461496": { + "message": "Complete pause: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, + "341055768": { + "message": "resumeTopActivity: Skip resume: need to start pausing", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "342460966": { "message": "DRAG %s: pos=(%d,%d)", "level": "INFO", @@ -2221,11 +2215,11 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "391189028": { - "message": "pauseBackTasks: task=%s mResumedActivity=%s", - "level": "DEBUG", + "378825104": { + "message": "Enqueueing pending pause: %s", + "level": "VERBOSE", "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskDisplayArea.java" + "at": "com\/android\/server\/wm\/TaskFragment.java" }, "397105698": { "message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found", @@ -2359,6 +2353,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowSurfaceController.java" }, + "573582981": { + "message": "reparent: moving activity=%s to new task fragment in task=%d at %d", + "level": "INFO", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "579298675": { "message": "Moving to DESTROYED: %s (removed from history)", "level": "VERBOSE", @@ -2461,6 +2461,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "660908897": { + "message": "Auto-PIP allowed, entering PIP mode directly: %s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "662572728": { "message": "Attempted to add a toast window with bad token %s. Aborting.", "level": "WARN", @@ -2479,12 +2485,24 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "669361121": { + "message": "Sleep still need to stop %d activities", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "674932310": { "message": "Setting Intent of %s to target %s", "level": "VERBOSE", "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/Task.java" }, + "675705156": { + "message": "resumeTopActivity: Top activity resumed %s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "685047360": { "message": "Resizing window %s", "level": "VERBOSE", @@ -2515,12 +2533,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" }, - "709500946": { - "message": "resumeTopActivityLocked: Skip resume: need to start pausing", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "715749922": { "message": "Allowlisting %d:%s", "level": "WARN", @@ -2623,12 +2635,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, - "897964776": { - "message": "Complete pause: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "898863925": { "message": "Attempted to add QS dialog window with unknown token %s. Aborting.", "level": "WARN", @@ -2653,6 +2659,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowStateAnimator.java" }, + "935418348": { + "message": "resumeTopActivity: Skip resume: some activity pausing.", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "950074526": { "message": "setLockTaskMode: Can't lock due to auth", "level": "WARN", @@ -2701,11 +2713,11 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" }, - "988389910": { - "message": "resumeTopActivityLocked: Pausing %s", - "level": "DEBUG", + "987903142": { + "message": "Sleep needs to pause %s", + "level": "VERBOSE", "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" + "at": "com\/android\/server\/wm\/TaskFragment.java" }, "996960396": { "message": "Starting Transition %d", @@ -2713,18 +2725,18 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, - "1001509841": { - "message": "Auto-PIP allowed, entering PIP mode directly: %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "1001904964": { "message": "***** BOOT TIMEOUT: forcing display enabled", "level": "WARN", "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1011462000": { + "message": "Re-launching after pause: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "1023413388": { "message": "Finish waiting for pause of: %s", "level": "VERBOSE", @@ -3157,12 +3169,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", "at": "com\/android\/server\/wm\/WindowContainer.java" }, - "1585450696": { - "message": "resumeTopActivityLocked: Restarting %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "1589610525": { "message": "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: anim=%s transit=%s isEntrance=true Callers=%s", "level": "VERBOSE", @@ -3337,6 +3343,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1814259538": { + "message": "pauseBackTasks: taskFrag=%s mResumedActivity=%s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskDisplayArea.java" + }, "1822314934": { "message": "Expected target rootTask=%s to restored behind rootTask=%s but it is behind rootTask=%s", "level": "WARN", @@ -3379,18 +3391,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "1837992242": { - "message": "Executing finish of activity: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, - "1847414670": { - "message": "Activity not running or entered PiP, resuming next.", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "1853793312": { "message": "Notify removed startingWindow %s", "level": "VERBOSE", @@ -3403,6 +3403,12 @@ "group": "WM_DEBUG_FOCUS", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1856783490": { + "message": "resumeTopActivity: Restarting %s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "1865125884": { "message": "finishScreenTurningOn: mAwake=%b, mScreenOnEarly=%b, mScreenOnFully=%b, mKeyguardDrawComplete=%b, mWindowManagerDrawComplete=%b", "level": "DEBUG", @@ -3415,30 +3421,24 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "1884961873": { - "message": "Sleep still need to stop %d activities", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "1891501279": { "message": "cancelAnimation(): reason=%s", "level": "DEBUG", "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "1894239744": { - "message": "Enqueueing pending pause: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "1903353011": { "message": "notifyAppStopped: %s", "level": "VERBOSE", "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1912291550": { + "message": "Sleep still waiting to pause %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "1918448345": { "message": "Task appeared taskId=%d", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5be860d22037a..c5d62abc87e32 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1281,10 +1281,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return task; } + @Nullable + TaskFragment getTaskFragment() { + WindowContainer parent = getParent(); + return parent != null ? parent.asTaskFragment() : null; + } + @Override void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { - final Task oldTask = oldParent != null ? (Task) oldParent : null; - final Task newTask = newParent != null ? (Task) newParent : null; + final Task oldTask = oldParent != null ? ((TaskFragment) oldParent).getTask() : null; + final Task newTask = newParent != null ? ((TaskFragment) newParent).getTask() : null; this.task = newTask; super.onParentChanged(newParent, oldParent); @@ -1342,11 +1348,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A updateColorTransform(); - if (oldTask != null) { - oldTask.cleanUpActivityReferences(this); + if (oldParent != null) { + ((TaskFragment) oldParent).cleanUpActivityReferences(this); } - if (newTask != null && isState(RESUMED)) { - newTask.setResumedActivity(this, "onParentChanged"); + + if (newParent != null && isState(RESUMED)) { + ((TaskFragment) newParent).setResumedActivity(this, "onParentChanged"); } if (rootTask != null && rootTask.topRunningActivity() == this) { @@ -2275,23 +2282,24 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** - * Reparents this activity into {@param newTask} at the provided {@param position}. The caller - * should ensure that the {@param newTask} is not already the parent of this activity. + * Reparents this activity into {@param newTaskFrag} at the provided {@param position}. The + * caller should ensure that the {@param newTaskFrag} is not already the parent of this + * activity. */ - void reparent(Task newTask, int position, String reason) { + void reparent(TaskFragment newTaskFrag, int position, String reason) { if (getParent() == null) { Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken); return; } - final Task prevTask = task; - if (prevTask == newTask) { - throw new IllegalArgumentException(reason + ": task=" + newTask + final TaskFragment prevTaskFrag = getTaskFragment(); + if (prevTaskFrag == newTaskFrag) { + throw new IllegalArgumentException(reason + ": task fragment =" + newTaskFrag + " is already the parent of r=" + this); } ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reparent: moving activity=%s" - + " to task=%d at %d", this, task.mTaskId, position); - reparent(newTask, position); + + " to new task fragment in task=%d at %d", this, task.mTaskId, position); + reparent(newTaskFrag, position); } private boolean isHomeIntent(Intent intent) { @@ -2857,7 +2865,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } final Task rootTask = getRootTask(); - final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getResumedActivity() == null) + final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getTopResumedActivity() == null) && rootTask.isFocusedRootTaskOnDisplay() // Do not adjust focus task because the task will be reused to launch new activity. && !task.isClearingToReuseTask(); @@ -2936,12 +2944,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Tell window manager to prepare for this one to be removed. setVisibility(false); - if (task.getPausingActivity() == null) { + if (task.getTopPausingActivity() == null) { ProtoLog.v(WM_DEBUG_STATES, "Finish needs to pause: %s", this); if (DEBUG_USER_LEAVING) { Slog.v(TAG_USER_LEAVING, "finish() => pause with userLeaving=false"); } - task.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, + task.startPausing(false /* userLeaving */, false /* uiSleeping */, null /* resuming */, "finish"); } @@ -3260,8 +3268,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (DEBUG_SWITCH) { final Task task = getTask(); Slog.v(TAG_SWITCH, "Safely destroying " + this + " in state " + getState() - + " resumed=" + task.getResumedActivity() - + " pausing=" + task.getPausingActivity() + + " resumed=" + task.getTopResumedActivity() + + " pausing=" + task.getTopPausingActivity() + " for reason " + reason); } return destroyImmediately(reason); @@ -3340,7 +3348,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * Note: Call before {@link #removeFromHistory(String)}. */ void cleanUp(boolean cleanServices, boolean setState) { - task.cleanUpActivityReferences(this); + getTaskFragment().cleanUpActivityReferences(this); clearLastParentBeforePip(); deferRelaunchUntilPaused = false; @@ -4821,8 +4829,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mState = state; - if (task != null) { - task.onActivityStateChanged(this, state, reason); + if (getTaskFragment() != null) { + getTaskFragment().onActivityStateChanged(this, state, reason); } // The WindowManager interprets the app stopping signal as @@ -5128,7 +5136,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // If the app is capable of entering PIP, we should try pausing it now // so it can PIP correctly. if (deferHidingClient) { - task.startPausingLocked(false /* uiSleeping */, + getTaskFragment().startPausing(false /* uiSleeping */, null /* resuming */, "makeInvisible"); break; } @@ -5228,7 +5236,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ private boolean shouldBeResumed(ActivityRecord activeActivity) { return shouldMakeActive(activeActivity) && isFocusable() - && getTask().getVisibility(activeActivity) == TASK_FRAGMENT_VISIBILITY_VISIBLE + && getTaskFragment().getVisibility(activeActivity) + == TASK_FRAGMENT_VISIBILITY_VISIBLE && canResumeByCompat(); } @@ -5371,16 +5380,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_STATES, "Activity paused: token=%s, timeout=%b", appToken, timeout); - if (task != null) { + final TaskFragment taskFragment = getTaskFragment(); + if (taskFragment != null) { removePauseTimeout(); - final ActivityRecord pausingActivity = task.getPausingActivity(); + final ActivityRecord pausingActivity = taskFragment.getPausingActivity(); if (pausingActivity == this) { ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSED: %s %s", this, (timeout ? "(due to timeout)" : " (pause complete)")); mAtmService.deferWindowLayout(); try { - task.completePauseLocked(true /* resumeNext */, null /* resumingActivity */); + taskFragment.completePause(true /* resumeNext */, null /* resumingActivity */); } finally { mAtmService.continueWindowLayout(); } @@ -6029,9 +6039,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return this; } // Try to use the one which is closest to top. - ActivityRecord r = rootTask.getResumedActivity(); + ActivityRecord r = rootTask.getTopResumedActivity(); if (r == null) { - r = rootTask.getPausingActivity(); + r = rootTask.getTopPausingActivity(); } if (r != null) { return r; @@ -6109,7 +6119,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // This would be redundant. return false; } - if (isState(RESUMED) || getRootTask() == null || this == task.getPausingActivity() + if (isState(RESUMED) || getRootTask() == null + || this == getTaskFragment().getPausingActivity() || !mHaveState || !stopped) { // We're not ready for this kind of thing. return false; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index b5814d4d94537..c0a72539a4f05 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1204,8 +1204,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // If this is coming from the currently resumed activity, it is // effectively saying that app switches are allowed at this point. final Task topFocusedRootTask = getTopDisplayFocusedRootTask(); - if (topFocusedRootTask != null && topFocusedRootTask.getResumedActivity() != null - && topFocusedRootTask.getResumedActivity().info.applicationInfo.uid + if (topFocusedRootTask != null && topFocusedRootTask.getTopResumedActivity() != null + && topFocusedRootTask.getTopResumedActivity().info.applicationInfo.uid == Binder.getCallingUid()) { mAppSwitchesAllowed = true; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 7fe0f5be287c6..3cd70412eada7 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2064,7 +2064,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { void updateTopResumedActivityIfNeeded() { final ActivityRecord prevTopActivity = mTopResumedActivity; final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); - if (topRootTask == null || topRootTask.getResumedActivity() == prevTopActivity) { + if (topRootTask == null || topRootTask.getTopResumedActivity() == prevTopActivity) { if (mService.isSleepingLocked()) { // There won't be a next resumed activity. The top process should still be updated // according to the current top focused activity. @@ -2086,7 +2086,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } // Update the current top activity. - mTopResumedActivity = topRootTask.getResumedActivity(); + mTopResumedActivity = topRootTask.getTopResumedActivity(); scheduleTopResumedActivityStateIfNeeded(); mService.updateTopApp(mTopResumedActivity); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index edfc4001152b1..bc2533cfefd61 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1859,7 +1859,7 @@ class RootWindowContainer extends WindowContainer if (focusedRootTask == null) { return null; } - final ActivityRecord resumedActivity = focusedRootTask.getResumedActivity(); + final ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity(); if (resumedActivity != null && resumedActivity.app != null) { return resumedActivity; } @@ -1881,11 +1881,11 @@ class RootWindowContainer extends WindowContainer // foreground. WindowProcessController fgApp = getItemFromRootTasks(rootTask -> { if (isTopDisplayFocusedRootTask(rootTask)) { - final ActivityRecord resumedActivity = rootTask.getResumedActivity(); + final ActivityRecord resumedActivity = rootTask.getTopResumedActivity(); if (resumedActivity != null) { return resumedActivity.app; - } else if (rootTask.getPausingActivity() != null) { - return rootTask.getPausingActivity().app; + } else if (rootTask.getTopPausingActivity() != null) { + return rootTask.getTopPausingActivity().app; } } return null; @@ -2377,7 +2377,9 @@ class RootWindowContainer extends WindowContainer if (displayShouldSleep) { rootTask.goToSleepIfPossible(false /* shuttingDown */); } else { - rootTask.awakeFromSleepingLocked(); + rootTask.forAllLeafTaskFragments( + taskFragment -> taskFragment.awakeFromSleeping(), + true /* traverseTopToBottom */); if (rootTask.isFocusedRootTaskOnDisplay() && !mTaskSupervisor.getKeyguardController() .isKeyguardOrAodShowing(display.mDisplayId)) { @@ -2737,8 +2739,8 @@ class RootWindowContainer extends WindowContainer if (DEBUG_SWITCH) { Slog.v(TAG_SWITCH, "Destroying " + r + " in state " + r.getState() - + " resumed=" + r.getTask().getResumedActivity() + " pausing=" - + r.getTask().getPausingActivity() + " for reason " + + " resumed=" + r.getTask().getTopResumedActivity() + " pausing=" + + r.getTask().getTopPausingActivity() + " for reason " + mDestroyAllActivitiesReason); } @@ -3325,7 +3327,7 @@ class RootWindowContainer extends WindowContainer if (rootTask == null || !rootTask.hasActivity()) { continue; } - final ActivityRecord resumedActivity = rootTask.getResumedActivity(); + final ActivityRecord resumedActivity = rootTask.getTopResumedActivity(); if (resumedActivity == null || !resumedActivity.idle) { ProtoLog.d(WM_DEBUG_STATES, "allResumedActivitiesIdle: rootTask=%d %s " + "not idle", rootTask.getRootTaskId(), resumedActivity); @@ -3340,7 +3342,7 @@ class RootWindowContainer extends WindowContainer boolean allResumedActivitiesVisible() { boolean[] foundResumed = {false}; final boolean foundInvisibleResumedActivity = forAllRootTasks(rootTask -> { - final ActivityRecord r = rootTask.getResumedActivity(); + final ActivityRecord r = rootTask.getTopResumedActivity(); if (r != null) { if (!r.nowVisible) { return true; @@ -3358,7 +3360,7 @@ class RootWindowContainer extends WindowContainer boolean allPausedActivitiesComplete() { boolean[] pausing = {true}; final boolean hasActivityNotCompleted = forAllLeafTasks(task -> { - final ActivityRecord r = task.getPausingActivity(); + final ActivityRecord r = task.getTopPausingActivity(); if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) { ProtoLog.d(WM_DEBUG_STATES, "allPausedActivitiesComplete: " + "r=%s state=%s", r, r.getState()); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index a4d36eeecd9e9..90201043b0744 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -41,7 +41,6 @@ import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; -import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; @@ -63,7 +62,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; -import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; @@ -83,19 +81,11 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN; import static com.android.server.wm.ActivityRecord.TRANSFER_SPLASH_SCREEN_COPYING; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CLEANUP; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION; @@ -123,7 +113,6 @@ import static com.android.server.wm.Task.ActivityState.PAUSED; import static com.android.server.wm.Task.ActivityState.PAUSING; import static com.android.server.wm.Task.ActivityState.RESUMED; import static com.android.server.wm.Task.ActivityState.STARTED; -import static com.android.server.wm.Task.ActivityState.STOPPING; import static com.android.server.wm.TaskProto.ACTIVITY_TYPE; import static com.android.server.wm.TaskProto.AFFINITY; import static com.android.server.wm.TaskProto.BOUNDS; @@ -166,14 +155,8 @@ import android.app.AppGlobals; import android.app.IActivityController; import android.app.PictureInPictureParams; import android.app.RemoteAction; -import android.app.ResultInfo; import android.app.TaskInfo; import android.app.WindowConfiguration; -import android.app.servertransaction.ActivityResultItem; -import android.app.servertransaction.ClientTransaction; -import android.app.servertransaction.NewIntentItem; -import android.app.servertransaction.PauseActivityItem; -import android.app.servertransaction.ResumeActivityItem; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -255,16 +238,9 @@ import java.util.function.Predicate; */ class Task extends TaskFragment { private static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_ATM; - static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE; private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS; - private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK; static final String TAG_TASKS = TAG + POSTFIX_TASKS; - private static final String TAG_APP = TAG + POSTFIX_APP; static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP; - private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE; - private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS; - private static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK; - private static final String TAG_STATES = TAG + POSTFIX_STATES; private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION; private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING; @@ -307,10 +283,6 @@ class Task extends TaskFragment { private static final String ATTR_LAST_SNAPSHOT_CONTENT_INSETS = "last_snapshot_content_insets"; private static final String ATTR_LAST_SNAPSHOT_BUFFER_SIZE = "last_snapshot_buffer_size"; - // Set to false to disable the preview that is shown while a new activity - // is being started. - private static final boolean SHOW_APP_STARTING_PREVIEW = true; - // How long to wait for all background Activities to redraw following a call to // convertToTranslucent(). private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000; @@ -554,29 +526,6 @@ class Task extends TaskFragment { /** ActivityRecords that are exiting, but still on screen for animations. */ final ArrayList mExitingActivities = new ArrayList<>(); - /** - * When we are in the process of pausing an activity, before starting the - * next one, this variable holds the activity that is currently being paused. - * - * Only set at leaf tasks. - */ - @Nullable - private ActivityRecord mPausingActivity = null; - - /** - * This is the last activity that we put into the paused state. This is - * used to determine if we need to do an activity transition while sleeping, - * when we normally hold the top activity paused. - */ - ActivityRecord mLastPausedActivity = null; - - /** - * Current activity that is resumed, or null if there is none. - * Only set at leaf tasks. - */ - @Nullable - private ActivityRecord mResumedActivity = null; - private boolean mForceShowForAllUsers; /** When set, will force the task to report as invisible. */ @@ -1453,64 +1402,54 @@ class Task extends TaskFragment { } } - void cleanUpActivityReferences(ActivityRecord r) { - // mPausingActivity is set at leaf task - if (mPausingActivity != null && mPausingActivity == r) { - mPausingActivity = null; - } - - if (mResumedActivity != null && mResumedActivity == r) { - setResumedActivity(null, "cleanUpActivityReferences"); - } - - final WindowContainer parent = getParent(); - if (parent != null && parent.asTask() != null) { - parent.asTask().cleanUpActivityReferences(r); - return; - } - r.removeTimeouts(); - mExitingActivities.remove(r); - } - - /** @return the currently resumed activity. */ - ActivityRecord getResumedActivity() { + /** Returns the currently topmost resumed activity. */ + @Nullable + ActivityRecord getTopResumedActivity() { if (isLeafTask()) { - return mResumedActivity; + final ActivityRecord[] resumedActivity = new ActivityRecord[1]; + forAllLeafTaskFragments(fragment -> { + if (fragment.getResumedActivity() != null) { + resumedActivity[0] = fragment.getResumedActivity(); + return true; + } + return false; + }); + return resumedActivity[0]; } - final Task task = getTask(t -> t.mResumedActivity != null, true /* traverseTopToBottom */); - return task != null ? task.mResumedActivity : null; - } - - @VisibleForTesting - void setPausingActivity(ActivityRecord pausing) { - mPausingActivity = pausing; + for (int i = mChildren.size() - 1; i >= 0; --i) { + ActivityRecord resumedActivity = mChildren.get(i).asTask().getTopResumedActivity(); + if (resumedActivity != null) { + return resumedActivity; + } + } + return null; } /** - * @return the currently pausing activity of this task or the topmost pausing activity of the - * child tasks + * Returns the currently topmost pausing activity. */ - ActivityRecord getPausingActivity() { + @Nullable + ActivityRecord getTopPausingActivity() { if (isLeafTask()) { - return mPausingActivity; + final ActivityRecord[] pausingActivity = new ActivityRecord[1]; + forAllLeafTaskFragments(fragment -> { + if (fragment.getPausingActivity() != null) { + pausingActivity[0] = fragment.getPausingActivity(); + return true; + } + return false; + }); + return pausingActivity[0]; } - final Task task = getTask(t -> t.mPausingActivity != null, true /* traverseTopToBottom */); - return task != null ? task.mPausingActivity : null; - } - - void setResumedActivity(ActivityRecord r, String reason) { - warnForNonLeafTask("setResumedActivity"); - if (mResumedActivity == r) { - return; + for (int i = mChildren.size() - 1; i >= 0; --i) { + ActivityRecord pausingActivity = mChildren.get(i).asTask().getTopPausingActivity(); + if (pausingActivity != null) { + return pausingActivity; + } } - - if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK, - "setResumedActivity task:" + this + " + from: " - + mResumedActivity + " to:" + r + " reason:" + reason); - mResumedActivity = r; - mTaskSupervisor.updateTopResumedActivityIfNeeded(); + return null; } void updateTaskMovement(boolean toTop, int position) { @@ -2190,32 +2129,6 @@ class Task extends TaskFragment { } } - /** - * This should be called when an child activity changes state. This should only - * be called from - * {@link ActivityRecord#setState(ActivityState, String)} . - * @param record The {@link ActivityRecord} whose state has changed. - * @param state The new state. - * @param reason The reason for the change. - */ - void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) { - warnForNonLeafTask("onActivityStateChanged"); - if (record == mResumedActivity && state != RESUMED) { - setResumedActivity(null, reason + " - onActivityStateChanged"); - } - - if (state == RESUMED) { - if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) { - Slog.v(TAG_ROOT_TASK, "set resumed activity to:" + record + " reason:" + reason); - } - setResumedActivity(record, reason + " - onActivityStateChanged"); - if (record == mRootWindowContainer.getTopResumedActivity()) { - mAtmService.setResumedActivityUncheckLocked(record, reason); - } - mTaskSupervisor.mRecentTasks.add(record.getTask()); - } - } - private void onConfigurationChangedInner(Configuration newParentConfig) { // Check if the new configuration supports persistent bounds (eg. is Freeform) and if so // restore the last recorded non-fullscreen bounds. @@ -3080,12 +2993,12 @@ class Task extends TaskFragment { // and focused application if needed. focusableTask.moveToFront(myReason); // Top display focused root task is changed, update top resumed activity if needed. - if (rootTask.getResumedActivity() != null) { + if (rootTask.getTopResumedActivity() != null) { mTaskSupervisor.updateTopResumedActivityIfNeeded(); // Set focused app directly because if the next focused activity is already resumed // (e.g. the next top activity is on a different display), there won't have activity // state change to update it. - mAtmService.setResumedActivityUncheckLocked(rootTask.getResumedActivity(), reason); + mAtmService.setResumedActivityUncheckLocked(rootTask.getTopResumedActivity(), reason); } return rootTask; } @@ -4119,15 +4032,6 @@ class Task extends TaskFragment { return this; } - /** - * Returns true if the task should be visible. - * - * @param starting The currently starting activity or null if there is none. - */ - boolean shouldBeVisible(ActivityRecord starting) { - return getVisibility(starting) != TASK_FRAGMENT_VISIBILITY_INVISIBLE; - } - ActivityRecord isInTask(ActivityRecord r) { if (r == null) { return null; @@ -5352,19 +5256,6 @@ class Task extends TaskFragment { r.completeResumeLocked(); } - void awakeFromSleepingLocked() { - if (!isLeafTask()) { - forAllLeafTasks((task) -> task.awakeFromSleepingLocked(), - true /* traverseTopToBottom */); - return; - } - - if (mPausingActivity != null) { - Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause"); - mPausingActivity.activityPaused(true); - } - } - void checkReadyForSleep() { if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) { mTaskSupervisor.checkReadyForSleepLocked(true /* allowDelay */); @@ -5383,302 +5274,13 @@ class Task extends TaskFragment { * the process of going to sleep (checkReadyForSleep will be called when that process finishes). */ boolean goToSleepIfPossible(boolean shuttingDown) { - if (!isLeafTask()) { - final int[] sleepInProgress = {0}; - forAllLeafTasks((t) -> { - if (!t.goToSleepIfPossible(shuttingDown)) { - sleepInProgress[0]++; - } - }, true); - return sleepInProgress[0] == 0; - } - - boolean shouldSleep = true; - if (mResumedActivity != null) { - // Still have something resumed; can't sleep until it is paused. - ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity); - if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING, - "Sleep => pause with userLeaving=false"); - - startPausingLocked(false /* userLeaving */, true /* uiSleeping */, null /* resuming */, - "sleep"); - shouldSleep = false ; - } else if (mPausingActivity != null) { - // Still waiting for something to pause; can't sleep yet. - ProtoLog.v(WM_DEBUG_STATES, "Sleep still waiting to pause %s", mPausingActivity); - shouldSleep = false; - } - - if (!shuttingDown) { - if (containsActivityFromRootTask(mTaskSupervisor.mStoppingActivities)) { - // Still need to tell some activities to stop; can't sleep yet. - ProtoLog.v(WM_DEBUG_STATES, "Sleep still need to stop %d activities", - mTaskSupervisor.mStoppingActivities.size()); - - mTaskSupervisor.scheduleIdle(); - shouldSleep = false; + final int[] sleepInProgress = {0}; + forAllLeafTaskFragments((f) -> { + if (!f.sleepIfPossible(shuttingDown)) { + sleepInProgress[0]++; } - } - - if (shouldSleep) { - ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, - !PRESERVE_WINDOWS); - } - - return shouldSleep; - } - - private boolean containsActivityFromRootTask(List rs) { - for (ActivityRecord r : rs) { - if (r.getRootTask() == this) { - return true; - } - } - return false; - } - - final boolean startPausingLocked(boolean uiSleeping, ActivityRecord resuming, String reason) { - return startPausingLocked(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason); - } - - /** - * Start pausing the currently resumed activity. It is an error to call this if there - * is already an activity being paused or there is no resumed activity. - * - * @param userLeaving True if this should result in an onUserLeaving to the current activity. - * @param uiSleeping True if this is happening with the user interface going to sleep (the - * screen turning off). - * @param resuming The activity we are currently trying to resume or null if this is not being - * called as part of resuming the top activity, so we shouldn't try to instigate - * a resume here if not null. - * @param reason The reason of pausing the activity. - * @return Returns true if an activity now is in the PAUSING state, and we are waiting for - * it to tell us when it is done. - */ - final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, - ActivityRecord resuming, String reason) { - if (!isLeafTask()) { - final int[] pausing = {0}; - forAllLeafTasks((t) -> { - if (t.startPausingLocked(userLeaving, uiSleeping, resuming, reason)) { - pausing[0]++; - } - }, true /* traverseTopToBottom */); - return pausing[0] > 0; - } - - 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. - completePauseLocked(false, resuming); - } - } - ActivityRecord prev = mResumedActivity; - - if (prev == null) { - if (resuming == null) { - Slog.wtf(TAG, "Trying to pause when nothing is resumed"); - mRootWindowContainer.resumeFocusedTasksTopActivities(); - } - return false; - } - - if (prev == resuming) { - Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed"); - return false; - } - - ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev); - mPausingActivity = prev; - mLastPausedActivity = prev; - if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) { - mTaskSupervisor.mNoHistoryActivities.add(prev); - } - prev.setState(PAUSING, "startPausingLocked"); - prev.getTask().touchActiveTime(); - - mAtmService.updateCpuStats(); - - boolean pauseImmediately = false; - boolean shouldAutoPip = false; - if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) { - // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous - // activity to be paused, while at the same time resuming the new resume activity - // only if the previous activity can't go into Pip since we want to give Pip - // activities a chance to enter Pip before resuming the next activity. - final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState( - "shouldResumeWhilePausing", userLeaving); - if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) { - shouldAutoPip = true; - } else if (!lastResumedCanPip) { - pauseImmediately = true; - } else { - // The previous activity may still enter PIP even though it did not allow auto-PIP. - } - } - - boolean didAutoPip = false; - if (prev.attachedToProcess()) { - if (shouldAutoPip) { - ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode " - + "directly: %s", prev); - - didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs); - mPausingActivity = null; - } else { - ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev); - try { - EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev), - prev.shortComponentName, "userLeaving=" + userLeaving, reason); - - mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(), - prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving, - prev.configChangeFlags, pauseImmediately)); - } catch (Exception e) { - // Ignore exception, if process died other code will cleanup. - Slog.w(TAG, "Exception thrown during pause", e); - mPausingActivity = null; - mLastPausedActivity = null; - mTaskSupervisor.mNoHistoryActivities.remove(prev); - } - } - } else { - mPausingActivity = null; - mLastPausedActivity = null; - mTaskSupervisor.mNoHistoryActivities.remove(prev); - } - - // If we are not going to sleep, we want to ensure the device is - // awake until the next activity is started. - if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) { - mTaskSupervisor.acquireLaunchWakelock(); - } - - // If already entered PIP mode, no need to keep pausing. - if (mPausingActivity != null && !didAutoPip) { - // Have the window manager pause its key dispatching until the new - // activity has started. If we're pausing the activity just because - // the screen is being turned off and the UI is sleeping, don't interrupt - // key dispatch; the same activity will pick it up again on wakeup. - if (!uiSleeping) { - prev.pauseKeyDispatchingLocked(); - } else { - ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off"); - } - - if (pauseImmediately) { - // If the caller said they don't want to wait for the pause, then complete - // the pause now. - completePauseLocked(false, resuming); - return false; - - } else { - prev.schedulePauseTimeout(); - return true; - } - - } else { - // This activity either failed to schedule the pause or it entered PIP mode, - // so just treat it as being paused now. - ProtoLog.v(WM_DEBUG_STATES, "Activity not running or entered PiP, resuming next."); - if (resuming == null) { - mRootWindowContainer.resumeFocusedTasksTopActivities(); - } - return false; - } - } - - @VisibleForTesting - void completePauseLocked(boolean resumeNext, ActivityRecord resuming) { - // Complete the pausing process of a pausing activity, so it doesn't make sense to - // operate on non-leaf tasks. - warnForNonLeafTask("completePauseLocked"); - - ActivityRecord prev = mPausingActivity; - ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev); - - if (prev != null) { - prev.setWillCloseOrEnterPip(false); - final boolean wasStopping = prev.isState(STOPPING); - prev.setState(PAUSED, "completePausedLocked"); - if (prev.finishing) { - // We will update the activity visibility later, no need to do in - // completeFinishing(). Updating visibility here might also making the next - // activities to be resumed, and could result in wrong app transition due to - // lack of previous activity information. - ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev); - prev = prev.completeFinishing(false /* updateVisibility */, - "completePausedLocked"); - } else if (prev.hasProcess()) { - ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s " - + "wasStopping=%b visibleRequested=%b", prev, wasStopping, - prev.mVisibleRequested); - if (prev.deferRelaunchUntilPaused) { - // Complete the deferred relaunch that was waiting for pause to complete. - ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev); - prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch); - } else if (wasStopping) { - // We are also stopping, the stop request must have gone soon after the pause. - // We can't clobber it, because the stop confirmation will not be handled. - // We don't need to schedule another stop, we only need to let it happen. - prev.setState(STOPPING, "completePausedLocked"); - } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) { - // Clear out any deferred client hide we might currently have. - prev.setDeferHidingClient(false); - // If we were visible then resumeTopActivities will release resources before - // stopping. - prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */, - "completePauseLocked"); - } - } else { - ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev); - prev = null; - } - // It is possible the activity was freezing the screen before it was paused. - // In that case go ahead and remove the freeze this activity has on the screen - // since it is no longer visible. - if (prev != null) { - prev.stopFreezingScreenLocked(true /*force*/); - } - mPausingActivity = null; - } - - if (resumeNext) { - final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); - if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) { - mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev, null); - } else { - checkReadyForSleep(); - final ActivityRecord top = - topRootTask != null ? topRootTask.topRunningActivity() : null; - if (top == null || (prev != null && top != prev)) { - // If there are no more activities available to run, do resume anyway to start - // something. Also if the top activity on the root task is not the just paused - // activity, we need to go ahead and resume it to ensure we complete an - // in-flight app switch. - mRootWindowContainer.resumeFocusedTasksTopActivities(); - } - } - } - - if (prev != null) { - prev.resumeKeyDispatchingLocked(); - } - - mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS); - - // Notify when the task stack has changed, but only if visibilities changed (not just - // focus). Also if there is an active root pinned task - we always want to notify it about - // task stack changes, because its positioning may depend on it. - if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause - || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) { - mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged(); - mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false; - } + }, true); + return sleepInProgress[0] == 0; } boolean isTopRootTaskInDisplayArea() { @@ -5732,12 +5334,8 @@ class Task extends TaskFragment { boolean preserveWindows, boolean notifyClients) { mTaskSupervisor.beginActivityVisibilityUpdate(); try { - forAllTaskFragments(fragment -> { - if (!fragment.hasDirectChildActivities()) { - return; - } - - fragment.ensureFragmentActivitiesVisible(starting, configChanges, preserveWindows, + forAllLeafTaskFragments(fragment -> { + fragment.updateActivityVisibilities(starting, configChanges, preserveWindows, notifyClients); }, true /* traverseTopToBottom */); @@ -5921,383 +5519,18 @@ class Task extends TaskFragment { return false; } - // Find the next top-most activity to resume in this root task that is not finishing and is - // focusable. If it is not focusable, we will fall into the case below to resume the - // top activity in the next focusable task. - ActivityRecord next = topRunningActivity(true /* focusableOnly */); - - final boolean hasRunningActivity = next != null; - - // TODO: Maybe this entire condition can get removed? - if (hasRunningActivity && !isAttached()) { - return false; - } - mRootWindowContainer.cancelInitializingActivities(); - if (!hasRunningActivity) { - // There are no activities left in the root task, let's look somewhere else. + if (topRunningActivity(true /* focusableOnly */) == null) { + // There are no activities left in this task, let's look somewhere else. return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options); } - next.delayedResume = false; - final TaskDisplayArea taskDisplayArea = getDisplayArea(); - - // If the top activity is the resumed one, nothing to do. - if (mResumedActivity == next && next.isState(RESUMED) - && taskDisplayArea.allResumedActivitiesComplete()) { - // Make sure we have executed any pending transitions, since there - // should be nothing left to do at this point. - executeAppTransition(options); - // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible - // we still want to check if the visibility of other windows have changed (e.g. bringing - // a fullscreen window forward to cover another freeform activity.) - if (taskDisplayArea.inMultiWindowMode()) { - taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, - false /* preserveWindows */, true /* notifyClients */); - } - ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity " - + "resumed %s", next); - return false; - } - - if (!next.canResumeByCompat()) { - return false; - } - - // If we are currently pausing an activity, then don't do anything until that is done. - final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete(); - if (!allPausedComplete) { - ProtoLog.v(WM_DEBUG_STATES, - "resumeTopActivityLocked: Skip resume: some activity pausing."); - - return false; - } - - // If we are sleeping, and there is no resumed activity, and the top activity is paused, - // well that is the state we want. - if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) { - // Make sure we have executed any pending transitions, since there - // should be nothing left to do at this point. - executeAppTransition(options); - ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Going to sleep and" - + " all paused"); - return false; - } - - // Make sure that the user who owns this activity is started. If not, - // we will just leave it as is because someone should be bringing - // another user's activities to the top of the stack. - if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) { - Slog.w(TAG, "Skipping resume of top activity " + next - + ": user " + next.mUserId + " is stopped"); - return false; - } - - // The activity may be waiting for stop, but that is no longer - // appropriate for it. - mTaskSupervisor.mStoppingActivities.remove(next); - - if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next); - - mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid); - - ActivityRecord lastResumed = null; - final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask(); - if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTask()) { - // So, why aren't we using prev here??? See the param comment on the method. prev - // doesn't represent the last resumed activity. However, the last focus stack does if - // it isn't null. - lastResumed = lastFocusedRootTask.getResumedActivity(); - } - - boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next); - if (mResumedActivity != null) { - ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Pausing %s", mResumedActivity); - pausing |= startPausingLocked(false /* uiSleeping */, next, - "resumeTopActivityInnerLocked"); - } - if (pausing) { - ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivityLocked: Skip resume: need to" - + " start pausing"); - // At this point we want to put the upcoming activity's process - // at the top of the LRU list, since we know we will be needing it - // very soon and it would be a waste to let it get killed if it - // happens to be sitting towards the end. - if (next.attachedToProcess()) { - next.app.updateProcessInfo(false /* updateServiceConnectionActivities */, - true /* activityChange */, false /* updateOomAdj */, - false /* addPendingTopUid */); - } else if (!next.isProcessRunning()) { - // Since the start-process is asynchronous, if we already know the process of next - // activity isn't running, we can start the process earlier to save the time to wait - // for the current activity to be paused. - final boolean isTop = this == taskDisplayArea.getFocusedRootTask(); - mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop, - isTop ? "pre-top-activity" : "pre-activity"); - } - if (lastResumed != null) { - lastResumed.setWillCloseOrEnterPip(true); - } - return true; - } else if (mResumedActivity == next && next.isState(RESUMED) - && taskDisplayArea.allResumedActivitiesComplete()) { - // It is possible for the activity to be resumed when we paused back stacks above if the - // next activity doesn't have to wait for pause to complete. - // So, nothing else to-do except: - // Make sure we have executed any pending transitions, since there - // should be nothing left to do at this point. - executeAppTransition(options); - ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity resumed " - + "(dontWaitForPause) %s", next); - return true; - } - - // If the most recent activity was noHistory but was only stopped rather - // than stopped+finished because the device went to sleep, we need to make - // sure to finish it as we're making a new activity topmost. - if (shouldSleepActivities()) { - mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next); - } - - if (prev != null && prev != next && next.nowVisible) { - - // The next activity is already visible, so hide the previous - // activity's windows right now so we can show the new one ASAP. - // We only do this if the previous is finishing, which should mean - // it is on top of the one being resumed so hiding it quickly - // is good. Otherwise, we want to do the normal route of allowing - // the resumed activity to be shown so we can decide if the - // previous should actually be hidden depending on whether the - // new one is found to be full-screen or not. - if (prev.finishing) { - prev.setVisibility(false); - if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, - "Not waiting for visible to hide: " + prev - + ", nowVisible=" + next.nowVisible); - } else { - if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, - "Previous already visible but still waiting to hide: " + prev - + ", nowVisible=" + next.nowVisible); - } - - } - - // Launching this app's activity, make sure the app is no longer - // considered stopped. - try { - mTaskSupervisor.getActivityMetricsLogger() - .notifyBeforePackageUnstopped(next.packageName); - mAtmService.getPackageManager().setPackageStoppedState( - next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */ - } catch (RemoteException e1) { - } catch (IllegalArgumentException e) { - Slog.w(TAG, "Failed trying to unstop package " - + next.packageName + ": " + e); - } - - // We are starting up the next activity, so tell the window manager - // that the previous one will be hidden soon. This way it can know - // to ignore it when computing the desired screen orientation. - boolean anim = true; - final DisplayContent dc = taskDisplayArea.mDisplayContent; - if (prev != null) { - if (prev.finishing) { - if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, - "Prepare close transition: prev=" + prev); - if (mTaskSupervisor.mNoAnimActivities.contains(prev)) { - anim = false; - dc.prepareAppTransition(TRANSIT_NONE); - } else { - dc.prepareAppTransition(TRANSIT_CLOSE); - } - prev.setVisibility(false); - } else { - if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, - "Prepare open transition: prev=" + prev); - if (mTaskSupervisor.mNoAnimActivities.contains(next)) { - anim = false; - dc.prepareAppTransition(TRANSIT_NONE); - } else { - dc.prepareAppTransition(TRANSIT_OPEN, - next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0); - } - } - } else { - if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous"); - if (mTaskSupervisor.mNoAnimActivities.contains(next)) { - anim = false; - dc.prepareAppTransition(TRANSIT_NONE); - } else { - dc.prepareAppTransition(TRANSIT_OPEN); - } - } - - if (anim) { - next.applyOptionsAnimation(); - } else { - next.abortAndClearOptionsAnimation(); - } - - mTaskSupervisor.mNoAnimActivities.clear(); - - if (next.attachedToProcess()) { - if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next - + " stopped=" + next.stopped - + " visibleRequested=" + next.mVisibleRequested); - - // If the previous activity is translucent, force a visibility update of - // the next activity, so that it's added to WM's opening app list, and - // transition animation can be set up properly. - // For example, pressing Home button with a translucent activity in focus. - // Launcher is already visible in this case. If we don't add it to opening - // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a - // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation. - final boolean lastActivityTranslucent = lastFocusedRootTask != null - && (lastFocusedRootTask.inMultiWindowMode() - || (lastFocusedRootTask.mLastPausedActivity != null - && !lastFocusedRootTask.mLastPausedActivity.occludesParent())); - - // This activity is now becoming visible. - if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) { - next.setVisibility(true); - } - - // schedule launch ticks to collect information about slow apps. - next.startLaunchTickingLocked(); - - ActivityRecord lastResumedActivity = - lastFocusedRootTask == null ? null : lastFocusedRootTask.getResumedActivity(); - final ActivityState lastState = next.getState(); - - mAtmService.updateCpuStats(); - - ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next); - - next.setState(RESUMED, "resumeTopActivityInnerLocked"); - - // Have the window manager re-evaluate the orientation of - // the screen based on the new activity order. - boolean notUpdated = true; - - // Activity should also be visible if set mLaunchTaskBehind to true (see - // ActivityRecord#shouldBeVisibleIgnoringKeyguard()). - if (shouldBeVisible(next)) { - // We have special rotation behavior when here is some active activity that - // requests specific orientation or Keyguard is locked. Make sure all activity - // visibilities are set correctly as well as the transition is updated if needed - // to get the correct rotation behavior. Otherwise the following call to update - // the orientation may cause incorrect configurations delivered to client as a - // result of invisible window resize. - // TODO: Remove this once visibilities are set correctly immediately when - // starting an activity. - notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(), - true /* markFrozenIfConfigChanged */, false /* deferResume */); - } - - if (notUpdated) { - // The configuration update wasn't able to keep the existing - // instance of the activity, and instead started a new one. - // We should be all done, but let's just make sure our activity - // is still at the top and schedule another run if something - // weird happened. - ActivityRecord nextNext = topRunningActivity(); - ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: " - + "%s, new next: %s", next, nextNext); - if (nextNext != next) { - // Do over! - mTaskSupervisor.scheduleResumeTopActivities(); - } - if (!next.mVisibleRequested || next.stopped) { - next.setVisibility(true); - } - next.completeResumeLocked(); - return true; - } - - try { - final ClientTransaction transaction = - ClientTransaction.obtain(next.app.getThread(), next.appToken); - // Deliver all pending results. - ArrayList a = next.results; - if (a != null) { - final int N = a.size(); - if (!next.finishing && N > 0) { - if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, - "Delivering results to " + next + ": " + a); - transaction.addCallback(ActivityResultItem.obtain(a)); - } - } - - if (next.newIntents != null) { - transaction.addCallback( - NewIntentItem.obtain(next.newIntents, true /* resume */)); - } - - // Well the app will no longer be stopped. - // Clear app token stopped state in window manager if needed. - next.notifyAppResumed(next.stopped); - - EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next), - next.getTask().mTaskId, next.shortComponentName); - - mAtmService.getAppWarningsLocked().onResumeActivity(next); - next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState); - next.abortAndClearOptionsAnimation(); - transaction.setLifecycleStateRequest( - ResumeActivityItem.obtain(next.app.getReportedProcState(), - dc.isNextTransitionForward())); - mAtmService.getLifecycleManager().scheduleTransaction(transaction); - - ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Resumed %s", next); - } catch (Exception e) { - // Whoops, need to restart this activity! - ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: " - + "%s", lastState, next); - next.setState(lastState, "resumeTopActivityInnerLocked"); - - // lastResumedActivity being non-null implies there is a lastStack present. - if (lastResumedActivity != null) { - lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked"); - } - - Slog.i(TAG, "Restarting because process died: " + next); - if (!next.hasBeenLaunched) { - next.hasBeenLaunched = true; - } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null - && lastFocusedRootTask.isTopRootTaskInDisplayArea()) { - next.showStartingWindow(false /* taskSwitch */); - } - mTaskSupervisor.startSpecificActivity(next, true, false); - return true; - } - - // From this point on, if something goes wrong there is no way - // to recover the activity. - try { - next.completeResumeLocked(); - } catch (Exception e) { - // If any exception gets thrown, toss away this - // activity and try the next one. - Slog.w(TAG, "Exception thrown during resume of " + next, e); - next.finishIfPossible("resume-exception", true /* oomAdj */); - return true; - } - } else { - // Whoops, need to restart this activity! - if (!next.hasBeenLaunched) { - next.hasBeenLaunched = true; - } else { - if (SHOW_APP_STARTING_PREVIEW) { - next.showStartingWindow(false /* taskSwich */); - } - if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next); - } - ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Restarting %s", next); - mTaskSupervisor.startSpecificActivity(next, true, true); - } - - return true; + final boolean[] resumed = new boolean[1]; + forAllLeafTaskFragments(f -> { + resumed[0] |= f.resumeTopActivity(prev, options, deferPause); + }, true); + return resumed[0]; } /** @@ -7036,29 +6269,6 @@ class Task extends TaskFragment { } } - /** - * Reset local parameters because an app's activity died. - * @param app The app of the activity that died. - * @return {@code true} if the process of the pausing activity is died. - */ - boolean handleAppDied(WindowProcessController app) { - warnForNonLeafTask("handleAppDied"); - boolean isPausingDied = false; - if (mPausingActivity != null && mPausingActivity.app == app) { - ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s", - mPausingActivity); - mPausingActivity = null; - isPausingDied = true; - } - if (mLastPausedActivity != null && mLastPausedActivity.app == app) { - if (mLastPausedActivity.isNoHistory()) { - mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity); - } - mLastPausedActivity = null; - } - return isPausingDied; - } - boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient, String dumpPackage, final boolean needSep) { Runnable headerPrinter = () -> { @@ -7083,9 +6293,9 @@ class Task extends TaskFragment { printed = true; } - printed |= printThisActivity(pw, getPausingActivity(), dumpPackage, false, + printed |= printThisActivity(pw, getTopPausingActivity(), dumpPackage, false, " mPausingActivity: ", null); - printed |= printThisActivity(pw, getResumedActivity(), dumpPackage, false, + printed |= printThisActivity(pw, getTopResumedActivity(), dumpPackage, false, " mResumedActivity: ", null); if (dumpAll) { printed |= printThisActivity(pw, mLastPausedActivity, dumpPackage, false, @@ -7537,6 +6747,7 @@ class Task extends TaskFragment { return mAnimatingActivityRegistry; } + @Override void executeAppTransition(ActivityOptions options) { mDisplayContent.executeAppTransition(); ActivityOptions.abort(options); @@ -7559,10 +6770,6 @@ class Task extends TaskFragment { return display != null ? display.isSleeping() : mAtmService.isSleepingLocked(); } - boolean shouldSleepOrShutDownActivities() { - return shouldSleepActivities() || mAtmService.mShuttingDown; - } - private Rect getRawBounds() { return super.getBounds(); } @@ -7587,8 +6794,8 @@ class Task extends TaskFragment { proto.write(DISPLAY_ID, getDisplayId()); proto.write(ROOT_TASK_ID, getRootTaskId()); - if (mResumedActivity != null) { - mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY); + if (getTopResumedActivity() != null) { + getTopResumedActivity().writeIdentifierToProto(proto, RESUMED_ACTIVITY); } if (realActivity != null) { proto.write(REAL_ACTIVITY, realActivity.flattenToShortString()); diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index fa118402a9e1a..eae78fa309022 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -467,7 +467,7 @@ final class TaskDisplayArea extends DisplayArea { // Update the top resumed activity because the preferred top focusable task may be changed. mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded(); - final ActivityRecord r = child.getResumedActivity(); + final ActivityRecord r = child.getTopResumedActivity(); if (r != null && r == mRootWindowContainer.getTopResumedActivity()) { mAtmService.setResumedActivityUncheckLocked(r, "positionChildAt"); } @@ -1373,11 +1373,11 @@ final class TaskDisplayArea extends DisplayArea { } // TODO(b/111541062): Move this into Task#getResumedActivity() // Check if the focused root task has the resumed activity - ActivityRecord resumedActivity = focusedRootTask.getResumedActivity(); + ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity(); if (resumedActivity == null || resumedActivity.app == null) { // If there is no registered resumed activity in the root task or it is not running - // try to use previously resumed one. - resumedActivity = focusedRootTask.getPausingActivity(); + resumedActivity = focusedRootTask.getTopPausingActivity(); if (resumedActivity == null || resumedActivity.app == null) { // If previously resumed activity doesn't work either - find the topmost running // activity that can be focused. @@ -1425,7 +1425,7 @@ final class TaskDisplayArea extends DisplayArea { continue; } - final ActivityRecord r = mChildren.get(i).asTask().getResumedActivity(); + final ActivityRecord r = mChildren.get(i).asTask().getTopResumedActivity(); if (r != null && !r.isState(RESUMED)) { return false; } @@ -1451,14 +1451,14 @@ final class TaskDisplayArea extends DisplayArea { */ boolean pauseBackTasks(ActivityRecord resuming) { final int[] someActivityPaused = {0}; - forAllLeafTasks((task) -> { - final ActivityRecord resumedActivity = task.getResumedActivity(); + forAllLeafTaskFragments((taskFragment) -> { + final ActivityRecord resumedActivity = taskFragment.getResumedActivity(); if (resumedActivity != null - && (task.getVisibility(resuming) != TASK_FRAGMENT_VISIBILITY_VISIBLE - || !task.isTopActivityFocusable())) { - ProtoLog.d(WM_DEBUG_STATES, "pauseBackTasks: task=%s " - + "mResumedActivity=%s", task, resumedActivity); - if (task.startPausingLocked(false /* uiSleeping*/, + && (taskFragment.getVisibility(resuming) != TASK_FRAGMENT_VISIBILITY_VISIBLE + || !taskFragment.isTopActivityFocusable())) { + ProtoLog.d(WM_DEBUG_STATES, "pauseBackTasks: taskFrag=%s " + + "mResumedActivity=%s", taskFragment, resumedActivity); + if (taskFragment.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) { someActivityPaused[0]++; } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index d6408c057bd53..845c2482561fc 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -20,17 +20,50 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; +import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; +import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_OPEN; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.Task.ActivityState.PAUSED; +import static com.android.server.wm.Task.ActivityState.PAUSING; +import static com.android.server.wm.Task.ActivityState.RESUMED; +import static com.android.server.wm.Task.ActivityState.STOPPING; import android.annotation.IntDef; import android.annotation.Nullable; +import android.app.ActivityOptions; +import android.app.ResultInfo; +import android.app.servertransaction.ActivityResultItem; +import android.app.servertransaction.ClientTransaction; +import android.app.servertransaction.NewIntentItem; +import android.app.servertransaction.PauseActivityItem; +import android.app.servertransaction.ResumeActivityItem; +import android.os.RemoteException; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; +import java.util.function.Function; /** * A basic container that can be used to contain activities or other {@link TaskFragment}, which @@ -55,6 +88,15 @@ class TaskFragment extends WindowContainer { /** TaskFragment is completely invisible. */ static final int TASK_FRAGMENT_VISIBILITY_INVISIBLE = 2; + private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskFragment" : TAG_ATM; + private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; + private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS; + private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION; + private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING; + + /** Set to false to disable the preview that is shown while a new activity is being started. */ + static final boolean SHOW_APP_STARTING_PREVIEW = true; + final ActivityTaskManagerService mAtmService; final ActivityTaskSupervisor mTaskSupervisor; final RootWindowContainer mRootWindowContainer; @@ -62,6 +104,30 @@ class TaskFragment extends WindowContainer { // The TaskFragment that adjacent to this one. private TaskFragment mAdjacentTaskFragment; + /** + * When we are in the process of pausing an activity, before starting the + * next one, this variable holds the activity that is currently being paused. + * + * Only set at leaf task fragments. + */ + @Nullable + private ActivityRecord mPausingActivity = null; + + /** + * This is the last activity that we put into the paused state. This is + * used to determine if we need to do an activity transition while sleeping, + * when we normally hold the top activity paused. + */ + // TODO(b/189384393): check whether this should work on root task or leaf. + ActivityRecord mLastPausedActivity = null; + + /** + * Current activity that is resumed, or null if there is none. + * Only set at leaf task fragments. + */ + @Nullable + private ActivityRecord mResumedActivity = null; + private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper = new EnsureActivitiesVisibleHelper(this); @@ -82,11 +148,81 @@ class TaskFragment extends WindowContainer { return mAdjacentTaskFragment; } + /** @return the currently resumed activity. */ + ActivityRecord getResumedActivity() { + return mResumedActivity; + } + + void setResumedActivity(ActivityRecord r, String reason) { + warnForNonLeafTaskFragment("setResumedActivity"); + if (mResumedActivity == r) { + return; + } + + if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) { + Slog.d(TAG, "setResumedActivity taskFrag:" + this + " + from: " + + mResumedActivity + " to:" + r + " reason:" + reason); + } + mResumedActivity = r; + mTaskSupervisor.updateTopResumedActivityIfNeeded(); + } + + @VisibleForTesting + void setPausingActivity(ActivityRecord pausing) { + mPausingActivity = pausing; + } + + ActivityRecord getPausingActivity() { + return mPausingActivity; + } + + int getDisplayId() { + final DisplayContent dc = getDisplayContent(); + return dc != null ? dc.mDisplayId : INVALID_DISPLAY; + } + + @Nullable + Task getTask() { + if (asTask() != null) { + return asTask(); + } + + TaskFragment parent = getParent() != null ? getParent().asTaskFragment() : null; + return parent != null ? parent.getTask() : null; + } + + @Override + @Nullable + TaskDisplayArea getDisplayArea() { + return (TaskDisplayArea) super.getDisplayArea(); + } + + /** + * Returns the root {@link TaskFragment}, which is usually also a {@link Task}. + */ + TaskFragment getRootTaskFragment() { + final WindowContainer parent = getParent(); + if (parent == null) return this; + + final TaskFragment parentTaskFragment = parent.asTaskFragment(); + return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment(); + } + @Override TaskFragment asTaskFragment() { return this; } + + /** + * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}. + */ + private void warnForNonLeafTaskFragment(String func) { + if (!isLeafTaskFragment()) { + Slog.w(TAG, func + " on non-leaf task fragment " + this); + } + } + boolean hasDirectChildActivities() { for (int i = mChildren.size() - 1; i >= 0; --i) { if (mChildren.get(i).asActivityRecord() != null) { @@ -96,6 +232,25 @@ class TaskFragment extends WindowContainer { return false; } + void cleanUpActivityReferences(ActivityRecord r) { + if (mPausingActivity != null && mPausingActivity == r) { + mPausingActivity = null; + } + + if (mResumedActivity != null && mResumedActivity == r) { + setResumedActivity(null, "cleanUpActivityReferences"); + } + + // TODO(b/189384393): why clean up the references in parents while the references are + // kept in the leaf? + final WindowContainer parent = getParent(); + if (parent != null && parent.asTaskFragment() != null) { + parent.asTaskFragment().cleanUpActivityReferences(r); + return; + } + r.removeTimeouts(); + } + /** * Returns whether this TaskFragment is currently forced to be hidden for any reason. */ @@ -103,6 +258,125 @@ class TaskFragment extends WindowContainer { return false; } + boolean isLeafTaskFragment() { + for (int i = mChildren.size() - 1; i >= 0; --i) { + if (mChildren.get(i).asTaskFragment() != null) { + return false; + } + } + return true; + } + + /** + * This should be called when an child activity changes state. This should only + * be called from + * {@link ActivityRecord#setState(Task.ActivityState, String)} . + * @param record The {@link ActivityRecord} whose state has changed. + * @param state The new state. + * @param reason The reason for the change. + */ + void onActivityStateChanged(ActivityRecord record, Task.ActivityState state, String reason) { + warnForNonLeafTaskFragment("onActivityStateChanged"); + if (record == mResumedActivity && state != RESUMED) { + setResumedActivity(null, reason + " - onActivityStateChanged"); + } + + if (state == RESUMED) { + if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) { + Slog.v(TAG, "set resumed activity to:" + record + " reason:" + reason); + } + setResumedActivity(record, reason + " - onActivityStateChanged"); + if (record == mRootWindowContainer.getTopResumedActivity()) { + mAtmService.setResumedActivityUncheckLocked(record, reason); + } + mTaskSupervisor.mRecentTasks.add(record.getTask()); + } + } + + /** + * Resets local parameters because an app's activity died. + * @param app The app of the activity that died. + * @return {@code true} if the process of the pausing activity is died. + */ + boolean handleAppDied(WindowProcessController app) { + warnForNonLeafTaskFragment("handleAppDied"); + boolean isPausingDied = false; + if (mPausingActivity != null && mPausingActivity.app == app) { + ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s", + mPausingActivity); + mPausingActivity = null; + isPausingDied = true; + } + if (mLastPausedActivity != null && mLastPausedActivity.app == app) { + if (mLastPausedActivity.isNoHistory()) { + mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity); + } + mLastPausedActivity = null; + } + return isPausingDied; + } + + void awakeFromSleeping() { + if (mPausingActivity != null) { + Slog.d(TAG, "awakeFromSleeping: previously pausing activity didn't pause"); + mPausingActivity.activityPaused(true); + } + } + + /** + * Tries to put the activities in the task fragment to sleep. + * + * If the task fragment is not in a state where its activities can be put to sleep, this + * function will start any necessary actions to move the task fragment into such a state. + * It is expected that this function get called again when those actions complete. + * + * @param shuttingDown {@code true} when the called because the device is shutting down. + * @return true if the root task finished going to sleep, false if the root task only started + * the process of going to sleep (checkReadyForSleep will be called when that process finishes). + */ + boolean sleepIfPossible(boolean shuttingDown) { + boolean shouldSleep = true; + if (mResumedActivity != null) { + // Still have something resumed; can't sleep until it is paused. + ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity); + startPausing(false /* userLeaving */, true /* uiSleeping */, null /* resuming */, + "sleep"); + shouldSleep = false; + } else if (mPausingActivity != null) { + // Still waiting for something to pause; can't sleep yet. + ProtoLog.v(WM_DEBUG_STATES, "Sleep still waiting to pause %s", mPausingActivity); + shouldSleep = false; + } + + if (!shuttingDown) { + if (containsStoppingActivity()) { + // Still need to tell some activities to stop; can't sleep yet. + ProtoLog.v(WM_DEBUG_STATES, "Sleep still need to stop %d activities", + mTaskSupervisor.mStoppingActivities.size()); + + mTaskSupervisor.scheduleIdle(); + shouldSleep = false; + } + } + + if (shouldSleep) { + updateActivityVisibilities(null /* starting */, 0 /* configChanges */, + !PRESERVE_WINDOWS, true /* notifyClients */); + } + + return shouldSleep; + } + + private boolean containsStoppingActivity() { + for (int i = mTaskSupervisor.mStoppingActivities.size() - 1; i >= 0; --i) { + ActivityRecord r = mTaskSupervisor.mStoppingActivities.get(i); + if (r.getTaskFragment() == this) { + return true; + } + } + return false; + } + /** * Returns true if the TaskFragment is translucent and can have other contents visible behind * it if needed. A TaskFragment is considered translucent if it don't contain a visible or @@ -167,8 +441,6 @@ class TaskFragment extends WindowContainer { * @param starting The currently starting activity or null if there is none. */ @TaskFragmentVisibility - // TODO(b/189384393): Review the usage of this method and see if that needs to be done via - // TaskFragment vs. Task. int getVisibility(ActivityRecord starting) { if (!isAttached() || isForceHidden()) { return TASK_FRAGMENT_VISIBILITY_INVISIBLE; @@ -336,10 +608,386 @@ class TaskFragment extends WindowContainer { return false; } - void ensureFragmentActivitiesVisible(@Nullable ActivityRecord starting, int configChanges, + final void updateActivityVisibilities(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows, boolean notifyClients) { - mEnsureActivitiesVisibleHelper.process( - starting, configChanges, preserveWindows, notifyClients); + mTaskSupervisor.beginActivityVisibilityUpdate(); + try { + mEnsureActivitiesVisibleHelper.process( + starting, configChanges, preserveWindows, notifyClients); + } finally { + mTaskSupervisor.endActivityVisibilityUpdate(); + } + } + + final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options, + boolean deferPause) { + ActivityRecord next = topRunningActivity(true /* focusableOnly */); + if (next == null || !next.canResumeByCompat()) { + return false; + } + + next.delayedResume = false; + final TaskDisplayArea taskDisplayArea = getDisplayArea(); + + // If the top activity is the resumed one, nothing to do. + if (mResumedActivity == next && next.isState(RESUMED) + && taskDisplayArea.allResumedActivitiesComplete()) { + // Make sure we have executed any pending transitions, since there + // should be nothing left to do at this point. + executeAppTransition(options); + // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible + // we still want to check if the visibility of other windows have changed (e.g. bringing + // a fullscreen window forward to cover another freeform activity.) + if (taskDisplayArea.inMultiWindowMode()) { + taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + false /* preserveWindows */, true /* notifyClients */); + } + ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity " + + "resumed %s", next); + return false; + } + + // If we are currently pausing an activity, then don't do anything until that is done. + final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete(); + if (!allPausedComplete) { + ProtoLog.v(WM_DEBUG_STATES, + "resumeTopActivity: Skip resume: some activity pausing."); + return false; + } + + // If we are sleeping, and there is no resumed activity, and the top activity is paused, + // well that is the state we want. + if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) { + // Make sure we have executed any pending transitions, since there + // should be nothing left to do at this point. + executeAppTransition(options); + ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Going to sleep and" + + " all paused"); + return false; + } + + // Make sure that the user who owns this activity is started. If not, + // we will just leave it as is because someone should be bringing + // another user's activities to the top of the stack. + if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) { + Slog.w(TAG, "Skipping resume of top activity " + next + + ": user " + next.mUserId + " is stopped"); + return false; + } + + // The activity may be waiting for stop, but that is no longer + // appropriate for it. + mTaskSupervisor.mStoppingActivities.remove(next); + + if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next); + + mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid); + + ActivityRecord lastResumed = null; + final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask(); + if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTaskFragment().asTask()) { + // So, why aren't we using prev here??? See the param comment on the method. prev + // doesn't represent the last resumed activity. However, the last focus stack does if + // it isn't null. + lastResumed = lastFocusedRootTask.getTopResumedActivity(); + } + + boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next); + if (mResumedActivity != null) { + ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Pausing %s", mResumedActivity); + pausing |= startPausingInner(mTaskSupervisor.mUserLeaving, false /* uiSleeping */, + next, "resumeTopActivity"); + } + if (pausing) { + ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: need to" + + " start pausing"); + // At this point we want to put the upcoming activity's process + // at the top of the LRU list, since we know we will be needing it + // very soon and it would be a waste to let it get killed if it + // happens to be sitting towards the end. + if (next.attachedToProcess()) { + next.app.updateProcessInfo(false /* updateServiceConnectionActivities */, + true /* activityChange */, false /* updateOomAdj */, + false /* addPendingTopUid */); + } else if (!next.isProcessRunning()) { + // Since the start-process is asynchronous, if we already know the process of next + // activity isn't running, we can start the process earlier to save the time to wait + // for the current activity to be paused. + final boolean isTop = this == taskDisplayArea.getFocusedRootTask(); + mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop, + isTop ? "pre-top-activity" : "pre-activity"); + } + if (lastResumed != null) { + lastResumed.setWillCloseOrEnterPip(true); + } + return true; + } else if (mResumedActivity == next && next.isState(RESUMED) + && taskDisplayArea.allResumedActivitiesComplete()) { + // It is possible for the activity to be resumed when we paused back stacks above if the + // next activity doesn't have to wait for pause to complete. + // So, nothing else to-do except: + // Make sure we have executed any pending transitions, since there + // should be nothing left to do at this point. + executeAppTransition(options); + ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity resumed " + + "(dontWaitForPause) %s", next); + return true; + } + + // If the most recent activity was noHistory but was only stopped rather + // than stopped+finished because the device went to sleep, we need to make + // sure to finish it as we're making a new activity topmost. + if (shouldSleepActivities()) { + mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next); + } + + if (prev != null && prev != next && next.nowVisible) { + // The next activity is already visible, so hide the previous + // activity's windows right now so we can show the new one ASAP. + // We only do this if the previous is finishing, which should mean + // it is on top of the one being resumed so hiding it quickly + // is good. Otherwise, we want to do the normal route of allowing + // the resumed activity to be shown so we can decide if the + // previous should actually be hidden depending on whether the + // new one is found to be full-screen or not. + if (prev.finishing) { + prev.setVisibility(false); + if (DEBUG_SWITCH) { + Slog.v(TAG_SWITCH, "Not waiting for visible to hide: " + prev + + ", nowVisible=" + next.nowVisible); + } + } else { + if (DEBUG_SWITCH) { + Slog.v(TAG_SWITCH, "Previous already visible but still waiting to hide: " + prev + + ", nowVisible=" + next.nowVisible); + } + } + } + + // Launching this app's activity, make sure the app is no longer + // considered stopped. + try { + mTaskSupervisor.getActivityMetricsLogger() + .notifyBeforePackageUnstopped(next.packageName); + mAtmService.getPackageManager().setPackageStoppedState( + next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */ + } catch (RemoteException e1) { + } catch (IllegalArgumentException e) { + Slog.w(TAG, "Failed trying to unstop package " + + next.packageName + ": " + e); + } + + // We are starting up the next activity, so tell the window manager + // that the previous one will be hidden soon. This way it can know + // to ignore it when computing the desired screen orientation. + boolean anim = true; + final DisplayContent dc = taskDisplayArea.mDisplayContent; + if (prev != null) { + if (prev.finishing) { + if (DEBUG_TRANSITION) { + Slog.v(TAG_TRANSITION, "Prepare close transition: prev=" + prev); + } + if (mTaskSupervisor.mNoAnimActivities.contains(prev)) { + anim = false; + dc.prepareAppTransition(TRANSIT_NONE); + } else { + dc.prepareAppTransition(TRANSIT_CLOSE); + } + prev.setVisibility(false); + } else { + if (DEBUG_TRANSITION) { + Slog.v(TAG_TRANSITION, "Prepare open transition: prev=" + prev); + } + if (mTaskSupervisor.mNoAnimActivities.contains(next)) { + anim = false; + dc.prepareAppTransition(TRANSIT_NONE); + } else { + dc.prepareAppTransition(TRANSIT_OPEN, + next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0); + } + } + } else { + if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous"); + if (mTaskSupervisor.mNoAnimActivities.contains(next)) { + anim = false; + dc.prepareAppTransition(TRANSIT_NONE); + } else { + dc.prepareAppTransition(TRANSIT_OPEN); + } + } + + if (anim) { + next.applyOptionsAnimation(); + } else { + next.abortAndClearOptionsAnimation(); + } + + mTaskSupervisor.mNoAnimActivities.clear(); + + if (next.attachedToProcess()) { + if (DEBUG_SWITCH) { + Slog.v(TAG_SWITCH, "Resume running: " + next + " stopped=" + next.stopped + + " visibleRequested=" + next.mVisibleRequested); + } + + // If the previous activity is translucent, force a visibility update of + // the next activity, so that it's added to WM's opening app list, and + // transition animation can be set up properly. + // For example, pressing Home button with a translucent activity in focus. + // Launcher is already visible in this case. If we don't add it to opening + // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a + // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation. + final boolean lastActivityTranslucent = lastFocusedRootTask != null + && (lastFocusedRootTask.inMultiWindowMode() + || (lastFocusedRootTask.mLastPausedActivity != null + && !lastFocusedRootTask.mLastPausedActivity.occludesParent())); + + // This activity is now becoming visible. + if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) { + next.setVisibility(true); + } + + // schedule launch ticks to collect information about slow apps. + next.startLaunchTickingLocked(); + + ActivityRecord lastResumedActivity = + lastFocusedRootTask == null ? null + : lastFocusedRootTask.getTopResumedActivity(); + final Task.ActivityState lastState = next.getState(); + + mAtmService.updateCpuStats(); + + ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next); + + next.setState(RESUMED, "resumeTopActivity"); + + // Have the window manager re-evaluate the orientation of + // the screen based on the new activity order. + boolean notUpdated = true; + + // Activity should also be visible if set mLaunchTaskBehind to true (see + // ActivityRecord#shouldBeVisibleIgnoringKeyguard()). + if (shouldBeVisible(next)) { + // We have special rotation behavior when here is some active activity that + // requests specific orientation or Keyguard is locked. Make sure all activity + // visibilities are set correctly as well as the transition is updated if needed + // to get the correct rotation behavior. Otherwise the following call to update + // the orientation may cause incorrect configurations delivered to client as a + // result of invisible window resize. + // TODO: Remove this once visibilities are set correctly immediately when + // starting an activity. + notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(), + true /* markFrozenIfConfigChanged */, false /* deferResume */); + } + + if (notUpdated) { + // The configuration update wasn't able to keep the existing + // instance of the activity, and instead started a new one. + // We should be all done, but let's just make sure our activity + // is still at the top and schedule another run if something + // weird happened. + ActivityRecord nextNext = topRunningActivity(); + ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: " + + "%s, new next: %s", next, nextNext); + if (nextNext != next) { + // Do over! + mTaskSupervisor.scheduleResumeTopActivities(); + } + if (!next.mVisibleRequested || next.stopped) { + next.setVisibility(true); + } + next.completeResumeLocked(); + return true; + } + + try { + final ClientTransaction transaction = + ClientTransaction.obtain(next.app.getThread(), next.appToken); + // Deliver all pending results. + ArrayList a = next.results; + if (a != null) { + final int size = a.size(); + if (!next.finishing && size > 0) { + if (DEBUG_RESULTS) { + Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a); + } + transaction.addCallback(ActivityResultItem.obtain(a)); + } + } + + if (next.newIntents != null) { + transaction.addCallback( + NewIntentItem.obtain(next.newIntents, true /* resume */)); + } + + // Well the app will no longer be stopped. + // Clear app token stopped state in window manager if needed. + next.notifyAppResumed(next.stopped); + + EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next), + next.getTask().mTaskId, next.shortComponentName); + + mAtmService.getAppWarningsLocked().onResumeActivity(next); + next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState); + next.abortAndClearOptionsAnimation(); + transaction.setLifecycleStateRequest( + ResumeActivityItem.obtain(next.app.getReportedProcState(), + dc.isNextTransitionForward())); + mAtmService.getLifecycleManager().scheduleTransaction(transaction); + + ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next); + } catch (Exception e) { + // Whoops, need to restart this activity! + ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: " + + "%s", lastState, next); + next.setState(lastState, "resumeTopActivityInnerLocked"); + + // lastResumedActivity being non-null implies there is a lastStack present. + if (lastResumedActivity != null) { + lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked"); + } + + Slog.i(TAG, "Restarting because process died: " + next); + if (!next.hasBeenLaunched) { + next.hasBeenLaunched = true; + } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null + && lastFocusedRootTask.isTopRootTaskInDisplayArea()) { + next.showStartingWindow(false /* taskSwitch */); + } + mTaskSupervisor.startSpecificActivity(next, true, false); + return true; + } + + // From this point on, if something goes wrong there is no way + // to recover the activity. + try { + next.completeResumeLocked(); + } catch (Exception e) { + // If any exception gets thrown, toss away this + // activity and try the next one. + Slog.w(TAG, "Exception thrown during resume of " + next, e); + next.finishIfPossible("resume-exception", true /* oomAdj */); + return true; + } + } else { + // Whoops, need to restart this activity! + if (!next.hasBeenLaunched) { + next.hasBeenLaunched = true; + } else { + if (SHOW_APP_STARTING_PREVIEW) { + next.showStartingWindow(false /* taskSwich */); + } + if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next); + } + ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Restarting %s", next); + mTaskSupervisor.startSpecificActivity(next, true, true); + } + + return true; + } + + boolean shouldSleepOrShutDownActivities() { + return shouldSleepActivities() || mAtmService.mShuttingDown; } /** @@ -351,23 +999,302 @@ class TaskFragment extends WindowContainer { return getVisibility(starting) != TASK_FRAGMENT_VISIBILITY_INVISIBLE; } - void forAllTaskFragments(Consumer callback, boolean traverseTopToBottom) { + final boolean startPausing(boolean uiSleeping, ActivityRecord resuming, String reason) { + return startPausing(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason); + } + + final boolean startPausing(boolean userLeaving, boolean uiSleeping, + ActivityRecord resuming, String reason) { + final int[] pausing = {0}; + forAllLeafTaskFragments((f) -> { + if (f.startPausingInner(userLeaving, uiSleeping, resuming, reason)) { + pausing[0]++; + } + }, true /* traverseTopToBottom */); + return pausing[0] > 0; + } + + /** + * Start pausing the currently resumed activity. It is an error to call this if there + * is already an activity being paused or there is no resumed activity. + * + * @param userLeaving True if this should result in an onUserLeaving to the current activity. + * @param uiSleeping True if this is happening with the user interface going to sleep (the + * screen turning off). + * @param resuming The activity we are currently trying to resume or null if this is not being + * called as part of resuming the top activity, so we shouldn't try to instigate + * a resume here if not null. + * @param reason The reason of pausing the activity. + * @return Returns true if an activity now is in the PAUSING state, and we are waiting for + * it to tell us when it is done. + */ + private boolean startPausingInner(boolean userLeaving, boolean uiSleeping, + ActivityRecord resuming, String reason) { + if (!hasDirectChildActivities()) { + return false; + } + + 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); + } + } + ActivityRecord prev = mResumedActivity; + + if (prev == null) { + if (resuming == null) { + Slog.wtf(TAG, "Trying to pause when nothing is resumed"); + mRootWindowContainer.resumeFocusedTasksTopActivities(); + } + return false; + } + + if (prev == resuming) { + Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed"); + return false; + } + + ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev); + mPausingActivity = prev; + mLastPausedActivity = prev; + if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) { + mTaskSupervisor.mNoHistoryActivities.add(prev); + } + prev.setState(PAUSING, "startPausingLocked"); + prev.getTask().touchActiveTime(); + + mAtmService.updateCpuStats(); + + boolean pauseImmediately = false; + boolean shouldAutoPip = false; + if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) { + // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous + // activity to be paused, while at the same time resuming the new resume activity + // only if the previous activity can't go into Pip since we want to give Pip + // activities a chance to enter Pip before resuming the next activity. + final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState( + "shouldResumeWhilePausing", userLeaving); + if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) { + shouldAutoPip = true; + } else if (!lastResumedCanPip) { + pauseImmediately = true; + } else { + // The previous activity may still enter PIP even though it did not allow auto-PIP. + } + } + + boolean didAutoPip = false; + if (prev.attachedToProcess()) { + if (shouldAutoPip) { + ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode " + + "directly: %s", prev); + + didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs); + mPausingActivity = null; + } else { + ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev); + try { + EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev), + prev.shortComponentName, "userLeaving=" + userLeaving, reason); + + mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(), + prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving, + prev.configChangeFlags, pauseImmediately)); + } catch (Exception e) { + // Ignore exception, if process died other code will cleanup. + Slog.w(TAG, "Exception thrown during pause", e); + mPausingActivity = null; + mLastPausedActivity = null; + mTaskSupervisor.mNoHistoryActivities.remove(prev); + } + } + } else { + mPausingActivity = null; + mLastPausedActivity = null; + mTaskSupervisor.mNoHistoryActivities.remove(prev); + } + + // If we are not going to sleep, we want to ensure the device is + // awake until the next activity is started. + if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) { + mTaskSupervisor.acquireLaunchWakelock(); + } + + // If already entered PIP mode, no need to keep pausing. + if (mPausingActivity != null && !didAutoPip) { + // Have the window manager pause its key dispatching until the new + // activity has started. If we're pausing the activity just because + // the screen is being turned off and the UI is sleeping, don't interrupt + // key dispatch; the same activity will pick it up again on wakeup. + if (!uiSleeping) { + prev.pauseKeyDispatchingLocked(); + } else { + ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off"); + } + + if (pauseImmediately) { + // If the caller said they don't want to wait for the pause, then complete + // the pause now. + completePause(false, resuming); + return false; + + } else { + prev.schedulePauseTimeout(); + return true; + } + + } else { + // This activity either failed to schedule the pause or it entered PIP mode, + // so just treat it as being paused now. + ProtoLog.v(WM_DEBUG_STATES, "Activity not running or entered PiP, resuming next."); + if (resuming == null) { + mRootWindowContainer.resumeFocusedTasksTopActivities(); + } + return false; + } + } + + @VisibleForTesting + void completePause(boolean resumeNext, ActivityRecord resuming) { + // Complete the pausing process of a pausing activity, so it doesn't make sense to + // operate on non-leaf tasks. + // warnForNonLeafTask("completePauseLocked"); + + ActivityRecord prev = mPausingActivity; + ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev); + + if (prev != null) { + prev.setWillCloseOrEnterPip(false); + final boolean wasStopping = prev.isState(STOPPING); + prev.setState(PAUSED, "completePausedLocked"); + if (prev.finishing) { + // We will update the activity visibility later, no need to do in + // completeFinishing(). Updating visibility here might also making the next + // activities to be resumed, and could result in wrong app transition due to + // lack of previous activity information. + ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev); + prev = prev.completeFinishing(false /* updateVisibility */, + "completePausedLocked"); + } else if (prev.hasProcess()) { + ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s " + + "wasStopping=%b visibleRequested=%b", prev, wasStopping, + prev.mVisibleRequested); + if (prev.deferRelaunchUntilPaused) { + // Complete the deferred relaunch that was waiting for pause to complete. + ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev); + prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch); + } else if (wasStopping) { + // We are also stopping, the stop request must have gone soon after the pause. + // We can't clobber it, because the stop confirmation will not be handled. + // We don't need to schedule another stop, we only need to let it happen. + prev.setState(STOPPING, "completePausedLocked"); + } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) { + // Clear out any deferred client hide we might currently have. + prev.setDeferHidingClient(false); + // If we were visible then resumeTopActivities will release resources before + // stopping. + prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */, + "completePauseLocked"); + } + } else { + ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev); + prev = null; + } + // It is possible the activity was freezing the screen before it was paused. + // In that case go ahead and remove the freeze this activity has on the screen + // since it is no longer visible. + if (prev != null) { + prev.stopFreezingScreenLocked(true /*force*/); + } + mPausingActivity = null; + } + + if (resumeNext) { + final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); + if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) { + mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev, null); + } else { + // checkReadyForSleep(); + final ActivityRecord top = + topRootTask != null ? topRootTask.topRunningActivity() : null; + if (top == null || (prev != null && top != prev)) { + // If there are no more activities available to run, do resume anyway to start + // something. Also if the top activity on the root task is not the just paused + // activity, we need to go ahead and resume it to ensure we complete an + // in-flight app switch. + mRootWindowContainer.resumeFocusedTasksTopActivities(); + } + } + } + + if (prev != null) { + prev.resumeKeyDispatchingLocked(); + } + + mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS); + + // Notify when the task stack has changed, but only if visibilities changed (not just + // focus). Also if there is an active root pinned task - we always want to notify it about + // task stack changes, because its positioning may depend on it. + if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause + || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) { + mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged(); + mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false; + } + } + + @Override + void forAllLeafTaskFragments(Consumer callback, boolean traverseTopToBottom) { final int count = mChildren.size(); + boolean isLeafTaskFrag = true; if (traverseTopToBottom) { for (int i = count - 1; i >= 0; --i) { final TaskFragment child = mChildren.get(i).asTaskFragment(); if (child != null) { - child.forAllTaskFragments(callback, traverseTopToBottom); + isLeafTaskFrag = false; + child.forAllLeafTaskFragments(callback, traverseTopToBottom); } } } else { for (int i = 0; i < count; i++) { final TaskFragment child = mChildren.get(i).asTaskFragment(); if (child != null) { - child.forAllTaskFragments(callback, traverseTopToBottom); + isLeafTaskFrag = false; + child.forAllLeafTaskFragments(callback, traverseTopToBottom); } } } - callback.accept(this); + if (isLeafTaskFrag) callback.accept(this); + } + + @Override + boolean forAllLeafTaskFragments(Function callback) { + boolean isLeafTaskFrag = true; + for (int i = mChildren.size() - 1; i >= 0; --i) { + final TaskFragment child = mChildren.get(i).asTaskFragment(); + if (child != null) { + isLeafTaskFrag = false; + if (child.forAllLeafTaskFragments(callback)) { + return true; + } + } + } + if (isLeafTaskFrag) { + return callback.apply(this); + } + return false; + } + + void executeAppTransition(ActivityOptions options) { + // No app transition applied to the task fragment. + } + + boolean shouldSleepActivities() { + return false; } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 339819d34eee3..7b8cfc4e2245f 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1671,6 +1671,15 @@ class WindowContainer extends ConfigurationContainer< return false; } + boolean forAllLeafTaskFragments(Function callback) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + if (mChildren.get(i).forAllLeafTaskFragments(callback)) { + return true; + } + } + return false; + } + /** * For all root tasks at or below this container call the callback. * @@ -1739,6 +1748,19 @@ class WindowContainer extends ConfigurationContainer< } } + void forAllLeafTaskFragments(Consumer callback, boolean traverseTopToBottom) { + final int count = mChildren.size(); + if (traverseTopToBottom) { + for (int i = count - 1; i >= 0; --i) { + mChildren.get(i).forAllLeafTaskFragments(callback, traverseTopToBottom); + } + } else { + for (int i = 0; i < count; i++) { + mChildren.get(i).forAllLeafTaskFragments(callback, traverseTopToBottom); + } + } + } + /** * For all root tasks at or below this container call the callback. * diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 26cfbdf806812..593b06d110453 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -735,10 +735,10 @@ public class WindowProcessController extends ConfigurationContainer