Start process of next activity with top priority in advance

In common cases, to resume the next activity we need to wait for the
current one to be paused. Since starting a process for activity is
asynchronous, if we already know the process of next activity has not
started yet, we can start the process earlier so the time waiting for
the pause to complete can be saved.

Also if the launching activity is going to be the top app, we can set
the top schedule group right after its process is started so the start
time before actually launching the activity can be improved.

Although before the current activity is paused, the next top activity
may still change and results some empty processes. That should not be
a common case and the process is still useful when going back the stack,
and the empty background processes are easier to be reclaimed.

Bug: 123043091
Test: AppLaunchTest
Test: Launch calculator from launcher, the event log am_proc_start will
      show "pre-top-activity".
Test: Cold launch a top activity, the system log should not show
      "not expected top priority".
Test: Use startActivities to start serveral activities in a sequence.
      Check "adb shell cat /proc/$pid/task/$pid/cgroup" for each process.
      Only the last one has top-app, others are background.

Change-Id: I9601b66e7cc0855fd7c2b573ded31fcf8d0711ae
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1344727..d306b85 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3549,7 +3549,7 @@
         mStackSupervisor.scheduleRestartTimeout(this);
     }
 
-    private boolean isProcessRunning() {
+    boolean isProcessRunning() {
         WindowProcessController proc = app;
         if (proc == null) {
             proc = mAtmService.mProcessNames.get(processName, info.applicationInfo.uid);
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 74c3069..156fb98 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2743,7 +2743,7 @@
         final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0
                 && !lastResumedCanPip;
 
-        boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
+        boolean pausing = display.pauseBackStacks(userLeaving, next, false);
         if (mResumedActivity != null) {
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "resumeTopActivityLocked: Pausing " + mResumedActivity);
@@ -2759,6 +2759,13 @@
             if (next.attachedToProcess()) {
                 next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
                         true /* activityChange */, false /* updateOomAdj */);
+            } else if (!next.isProcessRunning()) {
+                // Since the start-process is asynchronous, if we already know the process of next
+                // activity isn't running, we can start the process earlier to save the time to wait
+                // for the current activity to be paused.
+                final boolean isTop = this == display.getFocusedStack();
+                mService.startProcessAsync(next, false /* knownToBeDead */, isTop,
+                        isTop ? "pre-top-activity" : "pre-activity");
             }
             if (lastResumed != null) {
                 lastResumed.setWillCloseOrEnterPip(true);
@@ -2827,7 +2834,7 @@
         // that the previous one will be hidden soon.  This way it can know
         // to ignore it when computing the desired screen orientation.
         boolean anim = true;
-        final DisplayContent dc = getDisplay().mDisplayContent;
+        final DisplayContent dc = display.mDisplayContent;
         if (prev != null) {
             if (prev.finishing) {
                 if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
@@ -2983,7 +2990,7 @@
                 next.clearOptionsLocked();
                 transaction.setLifecycleStateRequest(
                         ResumeActivityItem.obtain(next.app.getReportedProcState(),
-                                getDisplay().mDisplayContent.isNextTransitionForward()));
+                                dc.isNextTransitionForward()));
                 mService.getLifecycleManager().scheduleTransaction(transaction);
 
                 if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index c992a69..94800fc 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -980,20 +980,8 @@
             r.notifyUnknownVisibilityLaunched();
         }
 
-        try {
-            if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
-                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
-                        + r.processName);
-            }
-            // Post message to start process to avoid possible deadlock of calling into AMS with the
-            // ATMS lock held.
-            final Message msg = PooledLambda.obtainMessage(
-                    ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
-                    r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
-            mService.mH.sendMessage(msg);
-        } finally {
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-        }
+        final boolean isTop = andResume && r.isTopRunningActivity();
+        mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
     }
 
     boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f76d0eb..d4b49aa 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5631,6 +5631,24 @@
         mH.sendMessage(m);
     }
 
+    void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
+            String hostingType) {
+        try {
+            if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
+                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
+                        + activity.processName);
+            }
+            // Post message to start process to avoid possible deadlock of calling into AMS with the
+            // ATMS lock held.
+            final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,
+                    mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,
+                    isTop, hostingType, activity.intent.getComponent());
+            mH.sendMessage(m);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
     void setBooting(boolean booting) {
         mAmInternal.setBooting(booting);
     }