Loading services/core/java/com/android/server/wm/BackNavigationController.java +27 −10 Original line number Diff line number Diff line Loading @@ -235,6 +235,7 @@ class BackNavigationController { // We don't have an application callback, let's find the destination of the back gesture // The search logic should align with ActivityClientController#finishActivity prevActivity = currentTask.topRunningActivity(currentActivity.token, INVALID_TASK_ID); final boolean isOccluded = isKeyguardOccluded(window); // TODO Dialog window does not need to attach on activity, check // window.mAttrs.type != TYPE_BASE_APPLICATION if ((window.getParent().getChildCount() > 1 Loading @@ -244,16 +245,24 @@ class BackNavigationController { backType = BackNavigationInfo.TYPE_DIALOG_CLOSE; removedWindowContainer = window; } else if (prevActivity != null) { if (!isOccluded || prevActivity.canShowWhenLocked()) { // We have another Activity in the same currentTask to go to backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; removedWindowContainer = currentActivity; prevTask = prevActivity.getTask(); } else { backType = BackNavigationInfo.TYPE_CALLBACK; } } else if (currentTask.returnsToHomeRootTask()) { if (isOccluded) { backType = BackNavigationInfo.TYPE_CALLBACK; } else { // Our Task should bring back to home removedWindowContainer = currentTask; prevTask = currentTask.getDisplayArea().getRootHomeTask(); backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; mShowWallpaper = true; } } else if (currentActivity.isRootOfTask()) { // TODO(208789724): Create single source of truth for this, maybe in // RootWindowContainer Loading @@ -267,7 +276,9 @@ class BackNavigationController { backType = BackNavigationInfo.TYPE_CALLBACK; } else { prevActivity = prevTask.getTopNonFinishingActivity(); if (prevTask.isActivityTypeHome()) { if (prevActivity == null || (isOccluded && !prevActivity.canShowWhenLocked())) { backType = BackNavigationInfo.TYPE_CALLBACK; } else if (prevTask.isActivityTypeHome()) { backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; mShowWallpaper = true; } else { Loading Loading @@ -323,6 +334,12 @@ class BackNavigationController { return mAnimationTargets.mComposed && mAnimationTargets.mWaitTransition; } boolean isKeyguardOccluded(WindowState focusWindow) { final KeyguardController kc = mWindowManagerService.mAtmService.mKeyguardController; final int displayId = focusWindow.getDisplayId(); return kc.isKeyguardLocked(displayId) && kc.isDisplayOccluded(displayId); } // For legacy transition. /** * Once we find the transition targets match back animation targets, remove the target from Loading services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +57 −15 Original line number Diff line number Diff line Loading @@ -20,24 +20,23 @@ import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.window.BackNavigationInfo.typeToString; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.HardwareBuffer; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.WindowManager; Loading @@ -48,7 +47,6 @@ import android.window.IOnBackInvokedCallback; import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedCallbackInfo; import android.window.OnBackInvokedDispatcher; import android.window.TaskSnapshot; import android.window.WindowOnBackInvokedDispatcher; import com.android.server.LocalServices; Loading @@ -67,6 +65,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { private BackNavigationController mBackNavigationController; private WindowManagerInternal mWindowManagerInternal; private BackAnimationAdapter mBackAnimationAdapter; private Task mRootHomeTask; @Before public void setUp() throws Exception { Loading @@ -76,6 +75,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal); mBackNavigationController.setWindowManager(mWm); mBackAnimationAdapter = mock(BackAnimationAdapter.class); mRootHomeTask = initHomeActivity(); } @Test Loading @@ -101,7 +101,8 @@ public class BackNavigationControllerTests extends WindowTestsBase { ActivityRecord recordA = createActivityRecord(taskA); Mockito.doNothing().when(recordA).reparentSurfaceControl(any(), any()); withSystemCallback(createTopTaskWithActivity()); final Task topTask = createTopTaskWithActivity(); withSystemCallback(topTask); BackNavigationInfo backNavigationInfo = startBackNavigation(); assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull(); assertThat(typeToString(backNavigationInfo.getType())) Loading @@ -111,6 +112,20 @@ public class BackNavigationControllerTests extends WindowTestsBase { verify(mBackNavigationController).scheduleAnimationLocked( eq(BackNavigationInfo.TYPE_CROSS_TASK), any(), eq(mBackAnimationAdapter), any()); // reset drawning status topTask.forAllWindows(w -> { makeWindowVisibleAndDrawn(w); }, true); setupKeyguardOccluded(); backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK)); doReturn(true).when(recordA).canShowWhenLocked(); backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_TASK)); } @Test Loading @@ -137,6 +152,20 @@ public class BackNavigationControllerTests extends WindowTestsBase { assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY)); // reset drawing status testCase.recordFront.forAllWindows(w -> { makeWindowVisibleAndDrawn(w); }, true); setupKeyguardOccluded(); backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK)); doReturn(true).when(testCase.recordBack).canShowWhenLocked(); backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY)); } @Test Loading Loading @@ -164,12 +193,17 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Test public void preparesForBackToHome() { Task task = createTopTaskWithActivity(); withSystemCallback(task); final Task topTask = createTopTaskWithActivity(); withSystemCallback(topTask); BackNavigationInfo backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME)); setupKeyguardOccluded(); backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK)); } @Test Loading Loading @@ -302,14 +336,22 @@ public class BackNavigationControllerTests extends WindowTestsBase { }; } @NonNull private TaskSnapshotController createMockTaskSnapshotController() { TaskSnapshotController taskSnapshotController = mock(TaskSnapshotController.class); TaskSnapshot taskSnapshot = mock(TaskSnapshot.class); when(taskSnapshot.getHardwareBuffer()).thenReturn(mock(HardwareBuffer.class)); when(taskSnapshotController.getSnapshot(anyInt(), anyInt(), anyBoolean(), anyBoolean())) .thenReturn(taskSnapshot); return taskSnapshotController; private Task initHomeActivity() { final Task task = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask(); task.forAllLeafTasks((t) -> { if (t.getTopMostActivity() == null) { final ActivityRecord r = createActivityRecord(t); Mockito.doNothing().when(t).reparentSurfaceControl(any(), any()); Mockito.doNothing().when(r).reparentSurfaceControl(any(), any()); } }, true); return task; } private void setupKeyguardOccluded() { final KeyguardController kc = mRootHomeTask.mTaskSupervisor.getKeyguardController(); doReturn(true).when(kc).isKeyguardLocked(anyInt()); doReturn(true).when(kc).isDisplayOccluded(anyInt()); } @NonNull Loading Loading
services/core/java/com/android/server/wm/BackNavigationController.java +27 −10 Original line number Diff line number Diff line Loading @@ -235,6 +235,7 @@ class BackNavigationController { // We don't have an application callback, let's find the destination of the back gesture // The search logic should align with ActivityClientController#finishActivity prevActivity = currentTask.topRunningActivity(currentActivity.token, INVALID_TASK_ID); final boolean isOccluded = isKeyguardOccluded(window); // TODO Dialog window does not need to attach on activity, check // window.mAttrs.type != TYPE_BASE_APPLICATION if ((window.getParent().getChildCount() > 1 Loading @@ -244,16 +245,24 @@ class BackNavigationController { backType = BackNavigationInfo.TYPE_DIALOG_CLOSE; removedWindowContainer = window; } else if (prevActivity != null) { if (!isOccluded || prevActivity.canShowWhenLocked()) { // We have another Activity in the same currentTask to go to backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; removedWindowContainer = currentActivity; prevTask = prevActivity.getTask(); } else { backType = BackNavigationInfo.TYPE_CALLBACK; } } else if (currentTask.returnsToHomeRootTask()) { if (isOccluded) { backType = BackNavigationInfo.TYPE_CALLBACK; } else { // Our Task should bring back to home removedWindowContainer = currentTask; prevTask = currentTask.getDisplayArea().getRootHomeTask(); backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; mShowWallpaper = true; } } else if (currentActivity.isRootOfTask()) { // TODO(208789724): Create single source of truth for this, maybe in // RootWindowContainer Loading @@ -267,7 +276,9 @@ class BackNavigationController { backType = BackNavigationInfo.TYPE_CALLBACK; } else { prevActivity = prevTask.getTopNonFinishingActivity(); if (prevTask.isActivityTypeHome()) { if (prevActivity == null || (isOccluded && !prevActivity.canShowWhenLocked())) { backType = BackNavigationInfo.TYPE_CALLBACK; } else if (prevTask.isActivityTypeHome()) { backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; mShowWallpaper = true; } else { Loading Loading @@ -323,6 +334,12 @@ class BackNavigationController { return mAnimationTargets.mComposed && mAnimationTargets.mWaitTransition; } boolean isKeyguardOccluded(WindowState focusWindow) { final KeyguardController kc = mWindowManagerService.mAtmService.mKeyguardController; final int displayId = focusWindow.getDisplayId(); return kc.isKeyguardLocked(displayId) && kc.isDisplayOccluded(displayId); } // For legacy transition. /** * Once we find the transition targets match back animation targets, remove the target from Loading
services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +57 −15 Original line number Diff line number Diff line Loading @@ -20,24 +20,23 @@ import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.window.BackNavigationInfo.typeToString; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.HardwareBuffer; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.WindowManager; Loading @@ -48,7 +47,6 @@ import android.window.IOnBackInvokedCallback; import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedCallbackInfo; import android.window.OnBackInvokedDispatcher; import android.window.TaskSnapshot; import android.window.WindowOnBackInvokedDispatcher; import com.android.server.LocalServices; Loading @@ -67,6 +65,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { private BackNavigationController mBackNavigationController; private WindowManagerInternal mWindowManagerInternal; private BackAnimationAdapter mBackAnimationAdapter; private Task mRootHomeTask; @Before public void setUp() throws Exception { Loading @@ -76,6 +75,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal); mBackNavigationController.setWindowManager(mWm); mBackAnimationAdapter = mock(BackAnimationAdapter.class); mRootHomeTask = initHomeActivity(); } @Test Loading @@ -101,7 +101,8 @@ public class BackNavigationControllerTests extends WindowTestsBase { ActivityRecord recordA = createActivityRecord(taskA); Mockito.doNothing().when(recordA).reparentSurfaceControl(any(), any()); withSystemCallback(createTopTaskWithActivity()); final Task topTask = createTopTaskWithActivity(); withSystemCallback(topTask); BackNavigationInfo backNavigationInfo = startBackNavigation(); assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull(); assertThat(typeToString(backNavigationInfo.getType())) Loading @@ -111,6 +112,20 @@ public class BackNavigationControllerTests extends WindowTestsBase { verify(mBackNavigationController).scheduleAnimationLocked( eq(BackNavigationInfo.TYPE_CROSS_TASK), any(), eq(mBackAnimationAdapter), any()); // reset drawning status topTask.forAllWindows(w -> { makeWindowVisibleAndDrawn(w); }, true); setupKeyguardOccluded(); backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK)); doReturn(true).when(recordA).canShowWhenLocked(); backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_TASK)); } @Test Loading @@ -137,6 +152,20 @@ public class BackNavigationControllerTests extends WindowTestsBase { assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY)); // reset drawing status testCase.recordFront.forAllWindows(w -> { makeWindowVisibleAndDrawn(w); }, true); setupKeyguardOccluded(); backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK)); doReturn(true).when(testCase.recordBack).canShowWhenLocked(); backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY)); } @Test Loading Loading @@ -164,12 +193,17 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Test public void preparesForBackToHome() { Task task = createTopTaskWithActivity(); withSystemCallback(task); final Task topTask = createTopTaskWithActivity(); withSystemCallback(topTask); BackNavigationInfo backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME)); setupKeyguardOccluded(); backNavigationInfo = startBackNavigation(); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK)); } @Test Loading Loading @@ -302,14 +336,22 @@ public class BackNavigationControllerTests extends WindowTestsBase { }; } @NonNull private TaskSnapshotController createMockTaskSnapshotController() { TaskSnapshotController taskSnapshotController = mock(TaskSnapshotController.class); TaskSnapshot taskSnapshot = mock(TaskSnapshot.class); when(taskSnapshot.getHardwareBuffer()).thenReturn(mock(HardwareBuffer.class)); when(taskSnapshotController.getSnapshot(anyInt(), anyInt(), anyBoolean(), anyBoolean())) .thenReturn(taskSnapshot); return taskSnapshotController; private Task initHomeActivity() { final Task task = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask(); task.forAllLeafTasks((t) -> { if (t.getTopMostActivity() == null) { final ActivityRecord r = createActivityRecord(t); Mockito.doNothing().when(t).reparentSurfaceControl(any(), any()); Mockito.doNothing().when(r).reparentSurfaceControl(any(), any()); } }, true); return task; } private void setupKeyguardOccluded() { final KeyguardController kc = mRootHomeTask.mTaskSupervisor.getKeyguardController(); doReturn(true).when(kc).isKeyguardLocked(anyInt()); doReturn(true).when(kc).isDisplayOccluded(anyInt()); } @NonNull Loading