Merge "Create ActivityClientRecord early in preExecute" into sc-dev
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8d39d8a..7149096 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -237,7 +237,6 @@
 import java.util.TimeZone;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
 /**
@@ -338,6 +337,11 @@
      */
     @UnsupportedAppUsage
     final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
+    /**
+     * Maps from activity token to local record of the activities that are preparing to be launched.
+     */
+    final Map<IBinder, ActivityClientRecord> mLaunchingActivities =
+            Collections.synchronizedMap(new ArrayMap<IBinder, ActivityClientRecord>());
     /** The activities to be truly destroyed (not include relaunch). */
     final Map<IBinder, ClientTransactionItem> mActivitiesToBeDestroyed =
             Collections.synchronizedMap(new ArrayMap<IBinder, ClientTransactionItem>());
@@ -347,7 +351,6 @@
     // Number of activities that are currently visible on-screen.
     @UnsupportedAppUsage
     int mNumVisibleActivities = 0;
-    private final AtomicInteger mNumLaunchingActivities = new AtomicInteger();
     @GuardedBy("mAppThread")
     private int mLastProcessState = PROCESS_STATE_UNKNOWN;
     @GuardedBy("mAppThread")
@@ -3256,6 +3259,21 @@
     }
 
     @Override
+    public void addLaunchingActivity(IBinder token, ActivityClientRecord activity) {
+        mLaunchingActivities.put(token, activity);
+    }
+
+    @Override
+    public ActivityClientRecord getLaunchingActivity(IBinder token) {
+        return mLaunchingActivities.get(token);
+    }
+
+    @Override
+    public void removeLaunchingActivity(IBinder token) {
+        mLaunchingActivities.remove(token);
+    }
+
+    @Override
     public ActivityClientRecord getActivityClient(IBinder token) {
         return mActivities.get(token);
     }
@@ -3299,7 +3317,7 @@
             // Defer the top state for VM to avoid aggressive JIT compilation affecting activity
             // launch time.
             if (processState == ActivityManager.PROCESS_STATE_TOP
-                    && mNumLaunchingActivities.get() > 0) {
+                    && !mLaunchingActivities.isEmpty()) {
                 mPendingProcessState = processState;
                 mH.postDelayed(this::applyPendingProcessState, PENDING_TOP_PROCESS_STATE_TIMEOUT);
             } else {
@@ -3315,7 +3333,7 @@
         // Handle the pending configuration if the process state is changed from cached to
         // non-cached. Except the case where there is a launching activity because the
         // LaunchActivityItem will handle it.
-        if (wasCached && !isCachedProcessState() && mNumLaunchingActivities.get() == 0) {
+        if (wasCached && !isCachedProcessState() && mLaunchingActivities.isEmpty()) {
             final Configuration pendingConfig =
                     mConfigurationController.getPendingConfiguration(false /* clearPending */);
             if (pendingConfig == null) {
@@ -3353,11 +3371,6 @@
         }
     }
 
-    @Override
-    public void countLaunchingActivities(int num) {
-        mNumLaunchingActivities.getAndAdd(num);
-    }
-
     @UnsupportedAppUsage
     public final void sendActivityResult(
             IBinder token, String id, int requestCode,
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index c752f34..115101c 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -82,9 +82,6 @@
     /** Set current process state. */
     public abstract void updateProcessState(int processState, boolean fromIpc);
 
-    /** Count how many activities are launching. */
-    public abstract void countLaunchingActivities(int num);
-
     // Execute phase related logic and handlers. Methods here execute actual lifecycle transactions
     // and deliver callbacks.
 
@@ -193,6 +190,26 @@
             FixedRotationAdjustments fixedRotationAdjustments);
 
     /**
+     * Add {@link ActivityClientRecord} that is preparing to be launched.
+     * @param token Activity token.
+     * @param activity An initialized instance of {@link ActivityClientRecord} to use during launch.
+     */
+    public abstract void addLaunchingActivity(IBinder token, ActivityClientRecord activity);
+
+    /**
+     * Get {@link ActivityClientRecord} that is preparing to be launched.
+     * @param token Activity token.
+     * @return An initialized instance of {@link ActivityClientRecord} to use during launch.
+     */
+    public abstract ActivityClientRecord getLaunchingActivity(IBinder token);
+
+    /**
+     * Remove {@link ActivityClientRecord} from the launching activity list.
+     * @param token Activity token.
+     */
+    public abstract void removeLaunchingActivity(IBinder token);
+
+    /**
      * Get {@link android.app.ActivityThread.ActivityClientRecord} instance that corresponds to the
      * provided token.
      */
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index d5585db..032b57e 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -40,7 +40,8 @@
 
     @Override
     public void preExecute(android.app.ClientTransactionHandler client, IBinder token) {
-        final ActivityClientRecord r = getActivityClientRecord(client, token);
+        final ActivityClientRecord r = getActivityClientRecord(client, token,
+                true /* includeLaunching */);
         // Notify the client of an upcoming change in the token configuration. This ensures that
         // batches of config change items only process the newest configuration.
         client.updatePendingActivityConfiguration(r, mConfiguration);
diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java
index f7d7e9d..a539812 100644
--- a/core/java/android/app/servertransaction/ActivityTransactionItem.java
+++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java
@@ -55,15 +55,40 @@
 
     @NonNull ActivityClientRecord getActivityClientRecord(
             @NonNull ClientTransactionHandler client, IBinder token) {
-        final ActivityClientRecord r = client.getActivityClient(token);
+        return getActivityClientRecord(client, token, false /* includeLaunching */);
+    }
+
+    /**
+     * Get the {@link ActivityClientRecord} instance that corresponds to the provided token.
+     * @param client Target client handler.
+     * @param token Target activity token.
+     * @param includeLaunching Indicate to also find the {@link ActivityClientRecord} in launching
+     *                         activity list. It should be noted that there is no activity in
+     *                         {@link ActivityClientRecord} from the launching activity list.
+     * @return The {@link ActivityClientRecord} instance that corresponds to the provided token.
+     */
+    @NonNull ActivityClientRecord getActivityClientRecord(
+            @NonNull ClientTransactionHandler client, IBinder token, boolean includeLaunching) {
+        ActivityClientRecord r = client.getActivityClient(token);
+        if (r != null) {
+            if (client.getActivity(token) == null) {
+                throw new IllegalArgumentException("Activity must not be null to execute "
+                        + "transaction item");
+            }
+            return r;
+        }
+        // The activity may not be launched yet. Fallback to check launching activity.
+        if (includeLaunching) {
+            r = client.getLaunchingActivity(token);
+        }
         if (r == null) {
             throw new IllegalArgumentException("Activity client record must not be null to execute "
                     + "transaction item");
         }
-        if (client.getActivity(token) == null) {
-            throw new IllegalArgumentException("Activity must not be null to execute "
-                    + "transaction item");
-        }
+
+        // We don't need to check the activity of launching activity client records because they
+        // have not been launched yet.
+
         return r;
     }
 }
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index e281a02..34e4fcd 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -82,7 +82,12 @@
 
     @Override
     public void preExecute(ClientTransactionHandler client, IBinder token) {
-        client.countLaunchingActivities(1);
+        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
+                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
+                mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
+                client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
+                mLaunchedFromBubble);
+        client.addLaunchingActivity(token, r);
         client.updateProcessState(mProcState, false);
         client.updatePendingConfiguration(mCurConfig);
         if (mActivityClientController != null) {
@@ -94,11 +99,7 @@
     public void execute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
-        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
-                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
-                mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
-                client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
-                mLaunchedFromBubble);
+        ActivityClientRecord r = client.getLaunchingActivity(token);
         client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
@@ -106,7 +107,7 @@
     @Override
     public void postExecute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
-        client.countLaunchingActivities(-1);
+        client.removeLaunchingActivity(token);
     }
 
 
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 944367e..4b8a347 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -40,7 +40,8 @@
 
     @Override
     public void preExecute(ClientTransactionHandler client, IBinder token) {
-        final ActivityClientRecord r = getActivityClientRecord(client, token);
+        final ActivityClientRecord r = getActivityClientRecord(client, token,
+                true /* includeLaunching */);
         // Notify the client of an upcoming change in the token configuration. This ensures that
         // batches of config change items only process the newest configuration.
         client.updatePendingActivityConfiguration(r, mConfiguration);