diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index df2b2a395ea70..e898f570a6b88 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1309,6 +1309,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-711194343": { + "message": "Setting Activity.mLauncherTaskBehind to false. Activity=%s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "-706481945": { "message": "TaskFragment parent info changed name=%s parentTaskId=%d", "level": "VERBOSE", @@ -2587,6 +2593,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/WindowState.java" }, + "599897753": { + "message": "Previous Activity is %s. Back type is %s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "600140673": { "message": "checkBootAnimationComplete: Waiting for anim complete", "level": "INFO", @@ -2671,12 +2683,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "664667685": { - "message": "Activity %s: enableOnBackInvokedCallback=false. Returning null BackNavigationInfo.", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "665256544": { "message": "All windows drawn!", "level": "DEBUG", @@ -2887,6 +2893,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/TaskFragment.java" }, + "948208142": { + "message": "Setting Activity.mLauncherTaskBehind to true. Activity=%s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "950074526": { "message": "setLockTaskMode: Can't lock due to auth", "level": "WARN", @@ -3103,6 +3115,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "1172542963": { + "message": "onBackNavigationDone backType=%s, task=%s, prevTaskTopActivity=%s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "1178653181": { "message": "Old wallpaper still the target.", "level": "VERBOSE", @@ -3415,11 +3433,11 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "1554795024": { - "message": "Previous Activity is %s", + "1544805551": { + "message": "Skipping app transition animation. task=%s", "level": "DEBUG", "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" + "at": "com\/android\/server\/wm\/Task.java" }, "1557732761": { "message": "For Intent %s bringing to top: %s", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index e50ad385faf93..08cb252cdf432 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -52,14 +52,17 @@ import com.android.wm.shell.common.annotations.ShellMainThread; */ public class BackAnimationController implements RemoteCallable { - private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability"; - public static final boolean IS_ENABLED = SystemProperties - .getInt(BACK_PREDICTABILITY_PROP, 1) > 0; private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP = "persist.debug.back_predictability_progress_threshold"; + // By default, enable new back dispatching without any animations. + private static final int BACK_PREDICTABILITY_PROP = + SystemProperties.getInt("persist.debug.back_predictability", 1); + public static final boolean IS_ENABLED = BACK_PREDICTABILITY_PROP > 0; private static final int PROGRESS_THRESHOLD = SystemProperties .getInt(BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP, -1); private static final String TAG = "BackAnimationController"; + @VisibleForTesting + boolean mEnableAnimations = (BACK_PREDICTABILITY_PROP & (1 << 1)) != 0; /** * Location of the initial touch event of the back gesture. @@ -255,7 +258,7 @@ public class BackAnimationController implements RemoteCallable 0; + return BACK_PREDICTABILITY_PROP > 0; } static boolean isScreenshotEnabled() { - return false; + return (BACK_PREDICTABILITY_PROP & SCREENSHOT_MASK) != 0; + } + + private static boolean isAnimationEnabled() { + return (BACK_PREDICTABILITY_PROP & ANIMATIONS_MASK) != 0; } /** @@ -93,14 +102,17 @@ class BackNavigationController { ActivityRecord prev; WindowContainer removedWindowContainer; ActivityRecord activityRecord; + ActivityRecord prevTaskTopActivity = null; SurfaceControl animationLeashParent; WindowConfiguration taskWindowConfiguration; HardwareBuffer screenshotBuffer = null; + SurfaceControl screenshotSurface; int prevTaskId; int prevUserId; RemoteAnimationTarget topAppTarget; SurfaceControl animLeash; - IOnBackInvokedCallback callback = null; + IOnBackInvokedCallback applicationCallback = null; + IOnBackInvokedCallback systemCallback = null; synchronized (task.mWmService.mGlobalLock) { @@ -116,15 +128,14 @@ class BackNavigationController { removedWindowContainer = activityRecord; taskWindowConfiguration = window.getWindowConfiguration(); } - IOnBackInvokedCallback applicationCallback = null; - IOnBackInvokedCallback systemCallback = null; if (window != null) { applicationCallback = window.getApplicationOnBackInvokedCallback(); - callback = applicationCallback; - if (callback == null) { - systemCallback = window.getSystemOnBackInvokedCallback(); - callback = systemCallback; - } + systemCallback = window.getSystemOnBackInvokedCallback(); + } + if (applicationCallback == null && systemCallback == null) { + // Return null when either there's no window, or apps have just initialized and + // have not finished registering callbacks. + return null; } ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, " @@ -133,24 +144,24 @@ class BackNavigationController { task, activityRecord, applicationCallback, systemCallback); // TODO Temp workaround for Sysui until b/221071505 is fixed - if (activityRecord == null && callback != null) { + if (activityRecord == null && applicationCallback != null) { return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK, null /* topWindowLeash */, null /* screenshotSurface */, null /* screenshotBuffer */, null /* taskWindowConfiguration */, null /* onBackNavigationDone */, - callback /* onBackInvokedCallback */); + applicationCallback /* onBackInvokedCallback */); } // For IME and Home, either a callback is registered, or we do nothing. In both cases, // we don't need to pass the leashes below. if (activityRecord == null || task.getDisplayContent().getImeContainer().isVisible() || activityRecord.isActivityTypeHome()) { - if (callback != null) { + if (applicationCallback != null) { return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK, null /* topWindowLeash */, null /* screenshotSurface */, null /* screenshotBuffer */, null /* taskWindowConfiguration */, null /* onBackNavigationDone */, - callback /* onBackInvokedCallback */); + applicationCallback /* onBackInvokedCallback */); } else { return null; } @@ -159,12 +170,12 @@ class BackNavigationController { prev = task.getActivity( (r) -> !r.finishing && r.getTask() == task && !r.isTopRunningActivity()); - if (callback != null) { + if (applicationCallback != null) { return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK, null /* topWindowLeash */, null /* screenshotSurface */, null /* screenshotBuffer */, null /* taskWindowConfiguration */, null /* onBackNavigationDone */, - callback /* onBackInvokedCallback */); + applicationCallback /* onBackInvokedCallback */); } else if (prev != null) { backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; } else if (task.returnsToHomeRootTask()) { @@ -188,8 +199,8 @@ class BackNavigationController { prevTaskId = prevTask != null ? prevTask.mTaskId : 0; prevUserId = prevTask != null ? prevTask.mUserId : 0; - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Activity is %s", - prev != null ? prev.mActivityComponent : null); + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Activity is %s. " + + "Back type is %s", prev != null ? prev.mActivityComponent : null, backType); //TODO(207481538) Remove once the infrastructure to support per-activity screenshot is // implemented. For now we simply have the mBackScreenshots hash map that dumbly @@ -204,6 +215,7 @@ class BackNavigationController { return null; } // Prepare a leash to animate the current top window + // TODO(b/220934562): Use surface animator to better manage animation conflicts. animLeash = removedWindowContainer.makeAnimationLeash() .setName("BackPreview Leash for " + removedWindowContainer) .setHidden(false) @@ -231,12 +243,30 @@ class BackNavigationController { activityRecord.windowType); } - SurfaceControl.Builder builder = new SurfaceControl.Builder() + screenshotSurface = new SurfaceControl.Builder() .setName("BackPreview Screenshot for " + prev) .setParent(animationLeashParent) .setHidden(false) - .setBLASTLayer(); - SurfaceControl screenshotSurface = builder.build(); + .setBLASTLayer() + .build(); + if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) { + task.mBackGestureStarted = true; + // Make launcher show from behind by marking its top activity as visible and + // launch-behind to bump its visibility for the duration of the back gesture. + prevTaskTopActivity = prevTask.getTopNonFinishingActivity(); + if (prevTaskTopActivity != null) { + if (!prevTaskTopActivity.mVisibleRequested) { + prevTaskTopActivity.setVisibility(true); + } + prevTaskTopActivity.mLaunchTaskBehind = true; + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Setting Activity.mLauncherTaskBehind to true. Activity=%s", + prevTaskTopActivity); + prevTaskTopActivity.mRootWindowContainer.ensureActivitiesVisible( + null /* starting */, 0 /* configChanges */, + false /* preserveWindows */); + } + } // Find a screenshot of the previous activity @@ -253,17 +283,20 @@ class BackNavigationController { WindowContainer finalRemovedWindowContainer = removedWindowContainer; try { - activityRecord.token.linkToDeath( - () -> resetSurfaces(finalRemovedWindowContainer), 0); + activityRecord.token.linkToDeath(() -> resetSurfaces(finalRemovedWindowContainer), 0); } catch (RemoteException e) { Slog.e(TAG, "Failed to link to death", e); resetSurfaces(removedWindowContainer); return null; } - RemoteCallback onBackNavigationDone = new RemoteCallback( - result -> resetSurfaces(finalRemovedWindowContainer - )); + int finalBackType = backType; + final IOnBackInvokedCallback callback = + applicationCallback != null ? applicationCallback : systemCallback; + ActivityRecord finalPrevTaskTopActivity = prevTaskTopActivity; + RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone( + result, finalRemovedWindowContainer, finalBackType, task, + finalPrevTaskTopActivity)); return new BackNavigationInfo(backType, topAppTarget, screenshotSurface, @@ -273,6 +306,39 @@ class BackNavigationController { callback); } + private void onBackNavigationDone( + Bundle result, WindowContainer windowContainer, int backType, + Task task, ActivityRecord prevTaskTopActivity) { + SurfaceControl surfaceControl = windowContainer.getSurfaceControl(); + boolean triggerBack = result != null + ? result.getBoolean(BackNavigationInfo.KEY_TRIGGER_BACK) + : false; + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, " + + "task=%s, prevTaskTopActivity=%s", backType, task, prevTaskTopActivity); + + if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) { + if (triggerBack) { + if (surfaceControl != null && surfaceControl.isValid()) { + // When going back to home, hide the task surface before it is re-parented to + // avoid flicker. + SurfaceControl.Transaction t = windowContainer.getSyncTransaction(); + t.hide(surfaceControl); + t.apply(); + } + } + if (prevTaskTopActivity != null && !triggerBack) { + // Restore the launch-behind state. + task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevTaskTopActivity.token); + prevTaskTopActivity.mLaunchTaskBehind = false; + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Setting Activity.mLauncherTaskBehind to false. Activity=%s", + prevTaskTopActivity); + } + } else { + task.mBackGestureStarted = false; + } + resetSurfaces(windowContainer); + } private HardwareBuffer getActivitySnapshot(@NonNull Task task, ComponentName activityComponent) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index cd7ebe327c919..dd4dd5f5ef96e 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -72,6 +72,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP; import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; @@ -609,6 +610,12 @@ class Task extends TaskFragment { boolean mLastSurfaceShowing = true; + /** + * Tracks if a back gesture is in progress. + * Skips any system transition animations if this is set to {@code true}. + */ + boolean mBackGestureStarted = false; + private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent, Intent _affinityIntent, String _affinity, String _rootAffinity, ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset, @@ -3322,6 +3329,14 @@ class Task extends TaskFragment { } }); } + } else if (mBackGestureStarted) { + // Cancel playing transitions if a back navigation animation is in progress. + // This bit is set by {@link BackNavigationController} when a back gesture is started. + // It is used as a one-off transition overwrite that is cleared when the back gesture + // is committed and triggers a transition, or when the gesture is cancelled. + mBackGestureStarted = false; + mDisplayContent.mSkipAppTransitionAnimation = true; + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Skipping app transition animation. task=%s", this); } else { super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources); } diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index c21a5b6ff7eac..92550a3107d51 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -30,7 +30,10 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.hardware.HardwareBuffer; import android.platform.test.annotations.Presubmit; +import android.window.BackEvent; import android.window.BackNavigationInfo; +import android.window.IOnBackInvokedCallback; +import android.window.OnBackInvokedDispatcher; import android.window.TaskSnapshot; import org.junit.Before; @@ -42,15 +45,19 @@ import org.junit.runner.RunWith; public class BackNavigationControllerTests extends WindowTestsBase { private BackNavigationController mBackNavigationController; + private IOnBackInvokedCallback mOnBackInvokedCallback; @Before public void setUp() throws Exception { mBackNavigationController = new BackNavigationController(); + mOnBackInvokedCallback = createBackCallback(); } @Test public void backTypeHomeWhenBackToLauncher() { Task task = createTopTaskWithActivity(); + registerSystemOnBackInvokedCallback(); + BackNavigationInfo backNavigationInfo = mBackNavigationController.startBackNavigation(task, new StubTransaction()); assertThat(backNavigationInfo).isNotNull(); @@ -63,6 +70,8 @@ public class BackNavigationControllerTests extends WindowTestsBase { Task taskA = createTask(mDefaultDisplay); createActivityRecord(taskA); Task task = createTopTaskWithActivity(); + registerSystemOnBackInvokedCallback(); + BackNavigationInfo backNavigationInfo = mBackNavigationController.startBackNavigation(task, new StubTransaction()); assertThat(backNavigationInfo).isNotNull(); @@ -75,6 +84,8 @@ public class BackNavigationControllerTests extends WindowTestsBase { Task task = createTopTaskWithActivity(); mAtm.setFocusedTask(task.mTaskId, createAppWindow(task, FIRST_APPLICATION_WINDOW, "window").mActivityRecord); + registerSystemOnBackInvokedCallback(); + BackNavigationInfo backNavigationInfo = mBackNavigationController.startBackNavigation(task, new StubTransaction()); assertThat(backNavigationInfo).isNotNull(); @@ -89,6 +100,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { public void backNavInfoFullyPopulated() { Task task = createTopTaskWithActivity(); createAppWindow(task, FIRST_APPLICATION_WINDOW, "window"); + registerSystemOnBackInvokedCallback(); // We need a mock screenshot so TaskSnapshotController taskSnapshotController = createMockTaskSnapshotController(); @@ -104,6 +116,30 @@ public class BackNavigationControllerTests extends WindowTestsBase { assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull(); } + @Test + public void preparesForBackToHome() { + Task task = createTopTaskWithActivity(); + ActivityRecord activity = task.getTopActivity(false, false); + registerSystemOnBackInvokedCallback(); + + BackNavigationInfo backNavigationInfo = + mBackNavigationController.startBackNavigation(task, new StubTransaction()); + assertThat(typeToString(backNavigationInfo.getType())) + .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME)); + } + + @Test + public void backTypeCallback() { + Task task = createTopTaskWithActivity(); + ActivityRecord activity = task.getTopActivity(false, false); + registerApplicationOnBackInvokedCallback(); + + BackNavigationInfo backNavigationInfo = + mBackNavigationController.startBackNavigation(task, new StubTransaction()); + assertThat(typeToString(backNavigationInfo.getType())) + .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK)); + } + @NonNull private TaskSnapshotController createMockTaskSnapshotController() { TaskSnapshotController taskSnapshotController = mock(TaskSnapshotController.class); @@ -126,4 +162,30 @@ public class BackNavigationControllerTests extends WindowTestsBase { mAtm.setFocusedTask(task.mTaskId, record); return task; } + + private void registerSystemOnBackInvokedCallback() { + mWm.getFocusedWindowLocked().setOnBackInvokedCallback( + mOnBackInvokedCallback, OnBackInvokedDispatcher.PRIORITY_SYSTEM); + } + + private void registerApplicationOnBackInvokedCallback() { + mWm.getFocusedWindowLocked().setOnBackInvokedCallback( + mOnBackInvokedCallback, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + } + + private IOnBackInvokedCallback createBackCallback() { + return new IOnBackInvokedCallback.Stub() { + @Override + public void onBackStarted() { } + + @Override + public void onBackProgressed(BackEvent backEvent) { } + + @Override + public void onBackCancelled() { } + + @Override + public void onBackInvoked() { } + }; + } }