diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0b0b704815895..c929cbbac2637 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2134,7 +2134,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return activityInfo != null ? activityInfo.applicationInfo : null; }); - final int typeParameter = mWmService.mStartingSurfaceController + final int typeParameter = StartingSurfaceController .makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated, useEmpty, useLegacy, activityAllDrawn); @@ -6616,13 +6616,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return splashScreenThemeResId; } + void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch, + boolean startActivity, ActivityRecord sourceRecord) { + showStartingWindow(prev, newTask, taskSwitch, isProcessRunning(), startActivity, + sourceRecord); + } + /** * @param prev Previous activity which contains a starting window. + * @param processRunning Whether the client process is running. * @param startActivity Whether this activity is just created from starter. * @param sourceRecord The source activity which start this activity. */ void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch, - boolean startActivity, ActivityRecord sourceRecord) { + boolean processRunning, boolean startActivity, ActivityRecord sourceRecord) { if (mTaskOverlay) { // We don't show starting window for overlay activities. return; @@ -6647,7 +6654,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A && task.getActivity((r) -> !r.finishing && r != this) == null; final boolean scheduled = addStartingWindow(packageName, resolvedTheme, - prev, newTask || newSingleActivity, taskSwitch, isProcessRunning(), + prev, newTask || newSingleActivity, taskSwitch, processRunning, allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty, allDrawn); if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) { Slog.d(TAG, "Scheduled starting window for " + this); diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index ecc85878353a8..87fb2902dddbd 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -455,6 +455,10 @@ public class ActivityStartController { // Lock the loop to ensure the activities launched in a sequence. synchronized (mService.mGlobalLock) { mService.deferWindowLayout(); + // To avoid creating multiple starting window when creating starting multiples + // activities, we defer the creation of the starting window once all start request + // are processed + mService.mWindowManager.mStartingSurfaceController.beginDeferAddStartingWindow(); try { for (int i = 0; i < starters.length; i++) { final int startResult = starters[i].setResultTo(resultTo) @@ -480,6 +484,7 @@ public class ActivityStartController { } } } finally { + mService.mWindowManager.mStartingSurfaceController.endDeferAddStartingWindow(); mService.continueWindowLayout(); } } diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java index 5fe40766fb66a..eb73cd8072046 100644 --- a/services/core/java/com/android/server/wm/StartingSurfaceController.java +++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java @@ -34,6 +34,7 @@ import android.content.pm.ApplicationInfo; import android.util.Slog; import android.window.TaskSnapshot; +import java.util.ArrayList; import java.util.function.Supplier; /** @@ -45,6 +46,14 @@ public class StartingSurfaceController { private final WindowManagerService mService; private final SplashScreenExceptionList mSplashScreenExceptionsList; + // Cache status while deferring add starting window + boolean mInitProcessRunning; + boolean mInitNewTask; + boolean mInitTaskSwitch; + private final ArrayList mDeferringAddStartActivities = + new ArrayList<>(); + private boolean mDeferringAddStartingWindow; + public StartingSurfaceController(WindowManagerService wm) { mService = wm; mSplashScreenExceptionsList = new SplashScreenExceptionList(wm.mContext.getMainExecutor()); @@ -70,7 +79,7 @@ public class StartingSurfaceController { return mSplashScreenExceptionsList.isException(packageName, targetSdk, infoProvider); } - int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch, + static int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, boolean useEmpty, boolean useLegacy, boolean activityDrawn) { int parameter = 0; @@ -142,6 +151,82 @@ public class StartingSurfaceController { } } + private static final class DeferringStartingWindowRecord { + final ActivityRecord mDeferring; + final ActivityRecord mPrev; + final ActivityRecord mSource; + + DeferringStartingWindowRecord(ActivityRecord deferring, ActivityRecord prev, + ActivityRecord source) { + mDeferring = deferring; + mPrev = prev; + mSource = source; + } + } + + /** + * Shows a starting window while starting a new activity. Do not use this method to create a + * starting window for an existing activity. + */ + void showStartingWindow(ActivityRecord target, ActivityRecord prev, + boolean newTask, boolean isTaskSwitch, ActivityRecord source) { + if (mDeferringAddStartingWindow) { + addDeferringRecord(target, prev, newTask, isTaskSwitch, source); + } else { + target.showStartingWindow(prev, newTask, isTaskSwitch, true /* startActivity */, + source); + } + } + + /** + * Queueing the starting activity status while deferring add starting window. + * @see Task#startActivityLocked + */ + private void addDeferringRecord(ActivityRecord deferring, ActivityRecord prev, + boolean newTask, boolean isTaskSwitch, ActivityRecord source) { + // Set newTask, taskSwitch, processRunning form first activity because those can change + // after first activity started. + if (mDeferringAddStartActivities.isEmpty()) { + mInitProcessRunning = deferring.isProcessRunning(); + mInitNewTask = newTask; + mInitTaskSwitch = isTaskSwitch; + } + mDeferringAddStartActivities.add(new DeferringStartingWindowRecord( + deferring, prev, source)); + } + + private void showStartingWindowFromDeferringActivities() { + // Attempt to add starting window from the top-most activity. + for (int i = mDeferringAddStartActivities.size() - 1; i >= 0; --i) { + final DeferringStartingWindowRecord next = mDeferringAddStartActivities.get(i); + next.mDeferring.showStartingWindow(next.mPrev, mInitNewTask, mInitTaskSwitch, + mInitProcessRunning, true /* startActivity */, next.mSource); + // If one succeeds, it is done. + if (next.mDeferring.mStartingData != null) { + break; + } + } + mDeferringAddStartActivities.clear(); + } + + /** + * Begin deferring add starting window in one pass. + * This is used to deferring add starting window while starting multiples activities because + * system only need to provide a starting window to the top-visible activity. + * Most call {@link #endDeferAddStartingWindow} when starting activities process finished. + * @see #endDeferAddStartingWindow() + */ + void beginDeferAddStartingWindow() { + mDeferringAddStartingWindow = true; + } + + /** + * End deferring add starting window. + */ + void endDeferAddStartingWindow() { + mDeferringAddStartingWindow = false; + showStartingWindowFromDeferringActivities(); + } final class StartingSurface { private final Task mTask; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 7617726e1fcd3..43038ce326498 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -5149,8 +5149,8 @@ class Task extends TaskFragment { final ActivityRecord prev = baseTask.getActivity( a -> a.mStartingData != null && a.showToCurrentUser()); - r.showStartingWindow(prev, newTask, isTaskSwitch, - true /* startActivity */, sourceRecord); + mWmService.mStartingSurfaceController.showStartingWindow(r, prev, newTask, + isTaskSwitch, sourceRecord); } } else { // If this is the first activity, don't do any fancy animations,