Loading core/java/android/window/flags/windowing_frontend.aconfig +9 −0 Original line number Diff line number Diff line Loading @@ -472,6 +472,15 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "transfer_starting_window_to_next_when_invisible" namespace: "windowing_frontend" description: "Transfer the starting window to the next non-finishing activity when the top activity becomes invisible that contains the starting window." bug: "436461530" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "predictive_back_callback_cancellation_fix" Loading services/core/java/com/android/server/wm/ActivityRecord.java +51 −1 Original line number Diff line number Diff line Loading @@ -4600,6 +4600,11 @@ final class ActivityRecord extends WindowToken { firstWindowDrawn = true; } if (fromActivity.isVisible()) { // Collect this activity in case it isn't yet visible from resume. if (Flags.transferStartingWindowToNextWhenInvisible() && !isVisibleRequested()) { mTransitionController.collect(this); } setVisible(true); setVisibleRequested(true); mWmService.mAnimator.addSurfaceVisibilityUpdate(this); Loading Loading @@ -4691,6 +4696,46 @@ final class ActivityRecord extends WindowToken { }); } /** * Tries to transfer the starting window from this activity in the task but will not visible * anymore. This is a common scenario apps use: A trampoline activity is started on top of an * existing Task, the starting windowing should transfer to the activity below once the * trampoline activity finishes. */ void transferStartingWindowToNextRunningIfNeeded() { if (mStartingData == null) { return; } final ActivityRecord next = task.topRunningActivity(); if (next == null || next == this) { return; } final WindowState mainWin = next.findMainWindow(false); if (mainWin != null && mainWin.mWinAnimator.getShown()) { // Next activity already has a visible window, so doesn't need to transfer the starting // window from this activity to here. The starting window will be removed with this // activity. return; } final StartingData tmpStartingData = mStartingData; if (tmpStartingData != null && tmpStartingData.mAssociatedTask == null && mTransitionController.isCollecting(this) && tmpStartingData instanceof SnapshotStartingData) { final Rect myBounds = getBounds(); final Rect nextBounds = next.getBounds(); if (!myBounds.equals(nextBounds)) { // Mark as no animation, so these changes won't merge into playing transition. if (mTransitionController.inPlayingTransition(this)) { mTransitionController.setNoAnimation(next); mTransitionController.setNoAnimation(this); } removeStartingWindow(); return; } } next.transferStartingWindow(this); } boolean isKeyguardLocked() { return (mDisplayContent != null) ? mDisplayContent.isKeyguardLocked() : mRootWindowContainer.getDefaultDisplay().isKeyguardLocked(); Loading Loading @@ -5446,6 +5491,9 @@ final class ActivityRecord extends WindowToken { setVisibleRequested(visible); if (!visible) { if (Flags.transferStartingWindowToNextWhenInvisible()) { transferStartingWindowToNextRunningIfNeeded(); } // Because starting window was transferred, this activity may be a trampoline which has // been occluded by next activity. If it has added windows, set client visibility // immediately to avoid the client getting RELAYOUT_RES_FIRST_TIME from relayout and Loading @@ -5470,8 +5518,10 @@ final class ActivityRecord extends WindowToken { ProtoLog.v(WM_DEBUG_ADD_REMOVE, "No longer Stopped: %s", this); mAppStopped = false; if (!Flags.transferStartingWindowToNextWhenInvisible()) { transferStartingWindowFromHiddenAboveTokenIfNeeded(); } } requestUpdateWallpaperIfNeeded(); // Defer committing visibility until transition starts. Loading services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +31 −0 Original line number Diff line number Diff line Loading @@ -3123,6 +3123,7 @@ public class ActivityRecordTests extends WindowTestsBase { } @Test @DisableFlags(Flags.FLAG_TRANSFER_STARTING_WINDOW_TO_NEXT_WHEN_INVISIBLE) public void testTryTransferStartingWindowFromHiddenAboveToken() { registerTestStartingWindowOrganizer(); // Add two tasks on top of each other. Loading Loading @@ -3152,6 +3153,36 @@ public class ActivityRecordTests extends WindowTestsBase { assertHasStartingWindow(activityBottom); } @Test @EnableFlags(Flags.FLAG_TRANSFER_STARTING_WINDOW_TO_NEXT_WHEN_INVISIBLE) public void testTryTransferStartingWindowToNextRunningIfNeeded() { registerTestStartingWindowOrganizer(); // Add two tasks on top of each other. final ActivityRecord activityTop = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityRecord activityBottom = new ActivityBuilder(mAtm).build(); activityTop.getTask().addChild(activityBottom, 0); // Add a starting window. activityTop.addStartingWindow(mPackageName, android.R.style.Theme, null, true, true, false, true, false, false, false); waitUntilHandlersIdle(); final WindowState startingWindow = activityTop.mStartingWindow; assertNotNull(startingWindow); // Make the top one invisible, and try transferring the starting window from the top to the // bottom one. activityTop.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */); waitUntilHandlersIdle(); // Expect getFrozenInsetsState will be null when transferring the starting window. assertNull(startingWindow.getFrozenInsetsState()); // Assert that the bottom window now has the starting window. assertNoStartingWindow(activityTop); assertHasStartingWindow(activityBottom); } @Test @EnableFlags(Flags.FLAG_ENSURE_STARTING_WINDOW_REMOVE_FROM_TASK) public void testStartingWindowInTaskFragment_RemoveAfterTrampolineInvisible() { Loading Loading
core/java/android/window/flags/windowing_frontend.aconfig +9 −0 Original line number Diff line number Diff line Loading @@ -472,6 +472,15 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "transfer_starting_window_to_next_when_invisible" namespace: "windowing_frontend" description: "Transfer the starting window to the next non-finishing activity when the top activity becomes invisible that contains the starting window." bug: "436461530" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "predictive_back_callback_cancellation_fix" Loading
services/core/java/com/android/server/wm/ActivityRecord.java +51 −1 Original line number Diff line number Diff line Loading @@ -4600,6 +4600,11 @@ final class ActivityRecord extends WindowToken { firstWindowDrawn = true; } if (fromActivity.isVisible()) { // Collect this activity in case it isn't yet visible from resume. if (Flags.transferStartingWindowToNextWhenInvisible() && !isVisibleRequested()) { mTransitionController.collect(this); } setVisible(true); setVisibleRequested(true); mWmService.mAnimator.addSurfaceVisibilityUpdate(this); Loading Loading @@ -4691,6 +4696,46 @@ final class ActivityRecord extends WindowToken { }); } /** * Tries to transfer the starting window from this activity in the task but will not visible * anymore. This is a common scenario apps use: A trampoline activity is started on top of an * existing Task, the starting windowing should transfer to the activity below once the * trampoline activity finishes. */ void transferStartingWindowToNextRunningIfNeeded() { if (mStartingData == null) { return; } final ActivityRecord next = task.topRunningActivity(); if (next == null || next == this) { return; } final WindowState mainWin = next.findMainWindow(false); if (mainWin != null && mainWin.mWinAnimator.getShown()) { // Next activity already has a visible window, so doesn't need to transfer the starting // window from this activity to here. The starting window will be removed with this // activity. return; } final StartingData tmpStartingData = mStartingData; if (tmpStartingData != null && tmpStartingData.mAssociatedTask == null && mTransitionController.isCollecting(this) && tmpStartingData instanceof SnapshotStartingData) { final Rect myBounds = getBounds(); final Rect nextBounds = next.getBounds(); if (!myBounds.equals(nextBounds)) { // Mark as no animation, so these changes won't merge into playing transition. if (mTransitionController.inPlayingTransition(this)) { mTransitionController.setNoAnimation(next); mTransitionController.setNoAnimation(this); } removeStartingWindow(); return; } } next.transferStartingWindow(this); } boolean isKeyguardLocked() { return (mDisplayContent != null) ? mDisplayContent.isKeyguardLocked() : mRootWindowContainer.getDefaultDisplay().isKeyguardLocked(); Loading Loading @@ -5446,6 +5491,9 @@ final class ActivityRecord extends WindowToken { setVisibleRequested(visible); if (!visible) { if (Flags.transferStartingWindowToNextWhenInvisible()) { transferStartingWindowToNextRunningIfNeeded(); } // Because starting window was transferred, this activity may be a trampoline which has // been occluded by next activity. If it has added windows, set client visibility // immediately to avoid the client getting RELAYOUT_RES_FIRST_TIME from relayout and Loading @@ -5470,8 +5518,10 @@ final class ActivityRecord extends WindowToken { ProtoLog.v(WM_DEBUG_ADD_REMOVE, "No longer Stopped: %s", this); mAppStopped = false; if (!Flags.transferStartingWindowToNextWhenInvisible()) { transferStartingWindowFromHiddenAboveTokenIfNeeded(); } } requestUpdateWallpaperIfNeeded(); // Defer committing visibility until transition starts. Loading
services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +31 −0 Original line number Diff line number Diff line Loading @@ -3123,6 +3123,7 @@ public class ActivityRecordTests extends WindowTestsBase { } @Test @DisableFlags(Flags.FLAG_TRANSFER_STARTING_WINDOW_TO_NEXT_WHEN_INVISIBLE) public void testTryTransferStartingWindowFromHiddenAboveToken() { registerTestStartingWindowOrganizer(); // Add two tasks on top of each other. Loading Loading @@ -3152,6 +3153,36 @@ public class ActivityRecordTests extends WindowTestsBase { assertHasStartingWindow(activityBottom); } @Test @EnableFlags(Flags.FLAG_TRANSFER_STARTING_WINDOW_TO_NEXT_WHEN_INVISIBLE) public void testTryTransferStartingWindowToNextRunningIfNeeded() { registerTestStartingWindowOrganizer(); // Add two tasks on top of each other. final ActivityRecord activityTop = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityRecord activityBottom = new ActivityBuilder(mAtm).build(); activityTop.getTask().addChild(activityBottom, 0); // Add a starting window. activityTop.addStartingWindow(mPackageName, android.R.style.Theme, null, true, true, false, true, false, false, false); waitUntilHandlersIdle(); final WindowState startingWindow = activityTop.mStartingWindow; assertNotNull(startingWindow); // Make the top one invisible, and try transferring the starting window from the top to the // bottom one. activityTop.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */); waitUntilHandlersIdle(); // Expect getFrozenInsetsState will be null when transferring the starting window. assertNull(startingWindow.getFrozenInsetsState()); // Assert that the bottom window now has the starting window. assertNoStartingWindow(activityTop); assertHasStartingWindow(activityBottom); } @Test @EnableFlags(Flags.FLAG_ENSURE_STARTING_WINDOW_REMOVE_FROM_TASK) public void testStartingWindowInTaskFragment_RemoveAfterTrampolineInvisible() { Loading