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 26dcf00cd6b5f..ea80b8bb52869 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2293,7 +2293,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; } @@ -2301,7 +2307,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 e120754498a71..1eb4122188651 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -6066,6 +6066,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. @@ -6076,7 +6077,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; @@ -6089,7 +6091,7 @@ class Task extends WindowContainer { if (isLeafTask()) { if (isFocusableAndVisible()) { - someActivityResumed = resumeTopActivityInnerLocked(prev, options); + someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause); } } else { int idx = mChildren.size() - 1; @@ -6102,7 +6104,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. @@ -6130,8 +6133,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; @@ -6227,7 +6237,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)); } /**