Loading services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +2 −3 Original line number Diff line number Diff line Loading @@ -2894,10 +2894,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { private boolean mIncludeInvisibleAndFinishing; private boolean mIgnoringKeyguard; ActivityRecord getOpaqueActivity( @NonNull WindowContainer<?> container, boolean ignoringKeyguard) { ActivityRecord getOpaqueActivity(@NonNull WindowContainer<?> container) { mIncludeInvisibleAndFinishing = true; mIgnoringKeyguard = ignoringKeyguard; mIgnoringKeyguard = true; return container.getActivity(this, true /* traverseTopToBottom */, null /* boundary */); } Loading services/core/java/com/android/server/wm/TaskFragment.java +7 −2 Original line number Diff line number Diff line Loading @@ -1090,8 +1090,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { return true; } // Including finishing Activity if the TaskFragment is becoming invisible in the transition. return mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(this, true /* ignoringKeyguard */) == null; return mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(this) == null; } /** Loading Loading @@ -1734,6 +1733,12 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (!hasDirectChildActivities()) { return false; } if (mResumedActivity != null && mTransitionController.isTransientLaunch(mResumedActivity)) { // Even if the transient activity is occluded, defer pausing (addToStopping will still // be called) it until the transient transition is done. So the current resuming // activity won't need to wait for additional pause complete. return false; } ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag =%s " + "mResumedActivity=%s", this, mResumedActivity); Loading services/core/java/com/android/server/wm/Transition.java +8 −11 Original line number Diff line number Diff line Loading @@ -477,20 +477,17 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (transientRoot == null) continue; final WindowContainer<?> rootParent = transientRoot.getParent(); if (rootParent == null || rootParent.getTopChild() == transientRoot) continue; final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor.mOpaqueActivityHelper .getOpaqueActivity(rootParent, true /* ignoringKeyguard */); if (transientRoot.compareTo(topOpaque.getRootTask()) < 0) { for (int j = rootParent.getChildCount() - 1; j >= 0; --j) { final WindowContainer<?> sibling = rootParent.getChildAt(j); if (sibling == transientRoot) break; if (!sibling.getWindowConfiguration().isAlwaysOnTop() && mController.mAtm .mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(sibling) != null) { occludedCount++; break; } } if (occludedCount == numTransient) { for (int i = mTransientLaunches.size() - 1; i >= 0; --i) { if (mTransientLaunches.keyAt(i).isDescendantOf(task)) { // Keep transient activity visible until transition finished, so it won't pause // with transient-hide tasks that may delay resuming the next top. return true; } } if (occludedCount == numTransient) { // Let transient-hide activities pause before transition is finished. return false; } Loading services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +13 −5 Original line number Diff line number Diff line Loading @@ -1631,6 +1631,8 @@ public class TransitionTests extends WindowTestsBase { transition.collect(taskA); transition.setTransientLaunch(recent, taskA); taskRecent.moveToFront("move-recent-to-front"); recent.setVisibility(true); recent.setState(ActivityRecord.State.RESUMED, "test"); // During collecting and playing, the recent is on top so it is visible naturally. // While B needs isTransientVisible to keep visibility because it is occluded by recents. Loading @@ -1643,15 +1645,21 @@ public class TransitionTests extends WindowTestsBase { // Switch to another task. For example, use gesture navigation to switch tasks. taskB.moveToFront("move-b-to-front"); appB.setVisibility(true); // The previous app (taskA) should be paused first so it loses transient visible. Because // visually it is taskA -> taskB, the pause -> resume order should be the same. assertFalse(controller.isTransientVisible(taskA)); // Keep the recent visible so there won't be 2 activities pausing at the same time. It is // to avoid the latency to resume the current top, i.e. appB. assertTrue(controller.isTransientVisible(taskRecent)); // The recent is paused after the transient transition is finished. controller.finishTransition(ActionChain.testFinish(transition)); // The recent is occluded by appB. assertFalse(controller.isTransientVisible(taskRecent)); // Active transient launch won't be paused if the transition is not finished. It is to // avoid the latency to resume the current top (appB) by waiting for both recent and appA // to complete pause. assertEquals(recent, taskRecent.getResumedActivity()); assertFalse(taskRecent.startPausing(false /* uiSleeping */, appB /* resuming */, "test")); // ActivityRecord#makeInvisible will add the invisible recent to the stopping list. // So when the transition finished, the recent can still be notified to pause and stop. mDisplayContent.ensureActivitiesVisible(null /* starting */, true /* notifyClients */); assertTrue(mSupervisor.mStoppingActivities.contains(recent)); } @Test Loading Loading
services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +2 −3 Original line number Diff line number Diff line Loading @@ -2894,10 +2894,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { private boolean mIncludeInvisibleAndFinishing; private boolean mIgnoringKeyguard; ActivityRecord getOpaqueActivity( @NonNull WindowContainer<?> container, boolean ignoringKeyguard) { ActivityRecord getOpaqueActivity(@NonNull WindowContainer<?> container) { mIncludeInvisibleAndFinishing = true; mIgnoringKeyguard = ignoringKeyguard; mIgnoringKeyguard = true; return container.getActivity(this, true /* traverseTopToBottom */, null /* boundary */); } Loading
services/core/java/com/android/server/wm/TaskFragment.java +7 −2 Original line number Diff line number Diff line Loading @@ -1090,8 +1090,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { return true; } // Including finishing Activity if the TaskFragment is becoming invisible in the transition. return mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(this, true /* ignoringKeyguard */) == null; return mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(this) == null; } /** Loading Loading @@ -1734,6 +1733,12 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (!hasDirectChildActivities()) { return false; } if (mResumedActivity != null && mTransitionController.isTransientLaunch(mResumedActivity)) { // Even if the transient activity is occluded, defer pausing (addToStopping will still // be called) it until the transient transition is done. So the current resuming // activity won't need to wait for additional pause complete. return false; } ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag =%s " + "mResumedActivity=%s", this, mResumedActivity); Loading
services/core/java/com/android/server/wm/Transition.java +8 −11 Original line number Diff line number Diff line Loading @@ -477,20 +477,17 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (transientRoot == null) continue; final WindowContainer<?> rootParent = transientRoot.getParent(); if (rootParent == null || rootParent.getTopChild() == transientRoot) continue; final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor.mOpaqueActivityHelper .getOpaqueActivity(rootParent, true /* ignoringKeyguard */); if (transientRoot.compareTo(topOpaque.getRootTask()) < 0) { for (int j = rootParent.getChildCount() - 1; j >= 0; --j) { final WindowContainer<?> sibling = rootParent.getChildAt(j); if (sibling == transientRoot) break; if (!sibling.getWindowConfiguration().isAlwaysOnTop() && mController.mAtm .mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(sibling) != null) { occludedCount++; break; } } if (occludedCount == numTransient) { for (int i = mTransientLaunches.size() - 1; i >= 0; --i) { if (mTransientLaunches.keyAt(i).isDescendantOf(task)) { // Keep transient activity visible until transition finished, so it won't pause // with transient-hide tasks that may delay resuming the next top. return true; } } if (occludedCount == numTransient) { // Let transient-hide activities pause before transition is finished. return false; } Loading
services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +13 −5 Original line number Diff line number Diff line Loading @@ -1631,6 +1631,8 @@ public class TransitionTests extends WindowTestsBase { transition.collect(taskA); transition.setTransientLaunch(recent, taskA); taskRecent.moveToFront("move-recent-to-front"); recent.setVisibility(true); recent.setState(ActivityRecord.State.RESUMED, "test"); // During collecting and playing, the recent is on top so it is visible naturally. // While B needs isTransientVisible to keep visibility because it is occluded by recents. Loading @@ -1643,15 +1645,21 @@ public class TransitionTests extends WindowTestsBase { // Switch to another task. For example, use gesture navigation to switch tasks. taskB.moveToFront("move-b-to-front"); appB.setVisibility(true); // The previous app (taskA) should be paused first so it loses transient visible. Because // visually it is taskA -> taskB, the pause -> resume order should be the same. assertFalse(controller.isTransientVisible(taskA)); // Keep the recent visible so there won't be 2 activities pausing at the same time. It is // to avoid the latency to resume the current top, i.e. appB. assertTrue(controller.isTransientVisible(taskRecent)); // The recent is paused after the transient transition is finished. controller.finishTransition(ActionChain.testFinish(transition)); // The recent is occluded by appB. assertFalse(controller.isTransientVisible(taskRecent)); // Active transient launch won't be paused if the transition is not finished. It is to // avoid the latency to resume the current top (appB) by waiting for both recent and appA // to complete pause. assertEquals(recent, taskRecent.getResumedActivity()); assertFalse(taskRecent.startPausing(false /* uiSleeping */, appB /* resuming */, "test")); // ActivityRecord#makeInvisible will add the invisible recent to the stopping list. // So when the transition finished, the recent can still be notified to pause and stop. mDisplayContent.ensureActivitiesVisible(null /* starting */, true /* notifyClients */); assertTrue(mSupervisor.mStoppingActivities.contains(recent)); } @Test Loading