From c49b7a9cd0dd8b5450f511cf3f304c4862f609f2 Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Wed, 5 May 2021 16:55:10 -0700 Subject: [PATCH] Defer pausing apps when bringing up recents This adds an ActivityOption call "transient launch" which can be used to tell WM that this particular launch is likely temporary and may not be committed. This is for recents where it exists for the duration of the transition, but then disappears once the user's intention is resolved. The effect of this is that it won't pause the running app while in recents -- instead deferring pause until the transition finishes and the final visibility states are committed. Bug: 183993884 Test: enable shell transitions, open app, swipe to overview and check dumpsys to see that the app is still RESUMED Change-Id: Iba6b8faf4e42c645bfd0305b93b7e0b542fd2c96 --- core/java/android/app/ActivityOptions.java | 30 +++++++++++++++++++ .../android/server/wm/ActivityStarter.java | 7 +++-- .../server/wm/RootWindowContainer.java | 9 +++++- .../core/java/com/android/server/wm/Task.java | 20 +++++++++---- .../server/wm/RootWindowContainerTests.java | 2 +- 5 files changed, 59 insertions(+), 9 deletions(-) diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 80f1e6eab9df8..306b54d140196 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -327,6 +327,9 @@ public class ActivityOptions { private static final String KEY_LAUNCHED_FROM_BUBBLE = "android.activity.launchTypeBubble"; + /** See {@link #setTransientLaunch()}. */ + private static final String KEY_TRANSIENT_LAUNCH = "android.activity.transientLaunch"; + /** * @see #setLaunchCookie * @hide @@ -414,6 +417,7 @@ public class ActivityOptions { private int mSplashScreenThemeResId; private boolean mRemoveWithTaskOrganizer; private boolean mLaunchedFromBubble; + private boolean mTransientLaunch; /** * Create an ActivityOptions specifying a custom animation to run when @@ -1166,6 +1170,7 @@ public class ActivityOptions { mSplashScreenThemeResId = opts.getInt(KEY_SPLASH_SCREEN_THEME); mRemoveWithTaskOrganizer = opts.getBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER); mLaunchedFromBubble = opts.getBoolean(KEY_LAUNCHED_FROM_BUBBLE); + mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH); } /** @@ -1662,6 +1667,28 @@ public class ActivityOptions { return mLaunchedFromBubble; } + /** + * Sets whether the activity launch is part of a transient operation. If it is, it will not + * cause lifecycle changes in existing activities even if it were to occlude them (ie. other + * activities occluded by this one will not be paused or stopped until the launch is committed). + * As a consequence, it will start immediately since it doesn't need to wait for other + * lifecycles to evolve. Current user is recents. + * @hide + */ + public ActivityOptions setTransientLaunch() { + mTransientLaunch = true; + return this; + } + + /** + * @see #setTransientLaunch() + * @return whether the activity launch is part of a transient operation. + * @hide + */ + public boolean getTransientLaunch() { + return mTransientLaunch; + } + /** * Update the current values in this ActivityOptions from those supplied * in otherOptions. Any values @@ -1902,6 +1929,9 @@ public class ActivityOptions { if (mLaunchedFromBubble) { b.putBoolean(KEY_LAUNCHED_FROM_BUBBLE, mLaunchedFromBubble); } + if (mTransientLaunch) { + b.putBoolean(KEY_TRANSIENT_LAUNCH, mTransientLaunch); + } return b; } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 0d3c74ef318b9..7bc29f2963c43 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -192,6 +192,7 @@ class ActivityStarter { private boolean mKeepCurTransition; private boolean mAvoidMoveToFront; private boolean mFrozeTaskList; + private boolean mTransientLaunch; // We must track when we deliver the new intent since multiple code paths invoke // {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used @@ -1792,7 +1793,7 @@ class ActivityStarter { mTargetRootTask.moveToFront("startActivityInner"); } mRootWindowContainer.resumeFocusedTasksTopActivities( - mTargetRootTask, mStartActivity, mOptions); + mTargetRootTask, mStartActivity, mOptions, mTransientLaunch); } } mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetRootTask); @@ -2209,6 +2210,7 @@ class ActivityStarter { mKeepCurTransition = false; mAvoidMoveToFront = false; mFrozeTaskList = false; + mTransientLaunch = false; mVoiceSession = null; mVoiceInteractor = null; @@ -2311,6 +2313,7 @@ class ActivityStarter { mDoResume = false; mAvoidMoveToFront = true; } + mTransientLaunch = mOptions.getTransientLaunch(); mTargetRootTask = Task.fromWindowContainerToken(mOptions.getLaunchRootTask()); } @@ -2642,7 +2645,7 @@ class ActivityStarter { } if (mTargetRootTask.isFocusable()) { mRootWindowContainer.resumeFocusedTasksTopActivities(mTargetRootTask, null, - mOptions); + mOptions, mTransientLaunch); } else { mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 0879ddd53a395..3a99a46bd31f3 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2292,7 +2292,13 @@ class RootWindowContainer extends WindowContainer boolean resumeFocusedTasksTopActivities( Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions) { + return resumeFocusedTasksTopActivities(targetRootTask, target, targetOptions, + false /* deferPause */); + } + boolean resumeFocusedTasksTopActivities( + Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions, + boolean deferPause) { if (!mTaskSupervisor.readyToResume()) { return false; } @@ -2300,7 +2306,8 @@ class RootWindowContainer extends WindowContainer boolean result = false; if (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea() || getTopDisplayFocusedRootTask() == targetRootTask)) { - result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions); + result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions, + deferPause); } for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 20995656f6938..19b5f090b740e 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -6059,6 +6059,7 @@ class Task extends WindowContainer { * @param prev The previously resumed activity, for when in the process * of pausing; can be null to call from elsewhere. * @param options Activity options. + * @param deferPause When {@code true}, this will not pause back tasks. * * @return Returns true if something is being resumed, or false if * nothing happened. @@ -6069,7 +6070,8 @@ class Task extends WindowContainer { * right activity for the current system state. */ @GuardedBy("mService") - boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) { + boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options, + boolean deferPause) { if (mInResumeTopActivity) { // Don't even start recursing. return false; @@ -6082,7 +6084,7 @@ class Task extends WindowContainer { if (isLeafTask()) { if (isFocusableAndVisible()) { - someActivityResumed = resumeTopActivityInnerLocked(prev, options); + someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause); } } else { int idx = mChildren.size() - 1; @@ -6095,7 +6097,8 @@ class Task extends WindowContainer { break; } - someActivityResumed |= child.resumeTopActivityUncheckedLocked(prev, options); + someActivityResumed |= child.resumeTopActivityUncheckedLocked(prev, options, + deferPause); // Doing so in order to prevent IndexOOB since hierarchy might changes while // resuming activities, for example dismissing split-screen while starting // non-resizeable activity. @@ -6123,8 +6126,15 @@ class Task extends WindowContainer { return someActivityResumed; } + /** @see #resumeTopActivityUncheckedLocked(ActivityRecord, ActivityOptions, boolean) */ @GuardedBy("mService") - private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { + boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) { + return resumeTopActivityUncheckedLocked(prev, options, false /* skipPause */); + } + + @GuardedBy("mService") + private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options, + boolean deferPause) { if (!mAtmService.isBooting() && !mAtmService.isBooted()) { // Not ready yet! return false; @@ -6220,7 +6230,7 @@ class Task extends WindowContainer { lastResumed = lastFocusedRootTask.getResumedActivity(); } - boolean pausing = taskDisplayArea.pauseBackTasks(next); + boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next); if (mResumedActivity != null) { ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Pausing %s", mResumedActivity); pausing |= startPausingLocked(false /* uiSleeping */, next, diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 7614579b9c3b9..9267285b446b6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -560,7 +560,7 @@ public class RootWindowContainerTests extends WindowTestsBase { // Verify the target task should resume its activity. verify(rootTask, times(1)).resumeTopActivityUncheckedLocked( - eq(activity), eq(null /* targetOptions */)); + eq(activity), eq(null /* targetOptions */), eq(false)); } /**