Refactor start activity methods

There were several startActivity* methods that looks similar, but not
entirely the same. Re-organized the code flow and remove some confused
methods, like setMayWait(), to make people easier to follow.

Bug: 139449647
Test: atest WmTests CtsWindowManagerDeviceTestCases
Change-Id: If1c3cc2bef9fbeecde9ec456515c9979b8286f97
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 63cec1a..2c4d893 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -284,7 +284,7 @@
                 .setRequestCode(requestCode)
                 .setStartFlags(startFlags)
                 .setActivityOptions(options)
-                .setMayWait(userId)
+                .setUserId(userId)
                 .setInTask(inTask)
                 .setOriginatingPendingIntent(originatingPendingIntent)
                 .setAllowBackgroundActivityStart(allowBackgroundActivityStart)
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 2ac681c..0146500 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -199,7 +199,7 @@
     private IVoiceInteractor mVoiceInteractor;
 
     // Last activity record we attempted to start
-    private final ActivityRecord[] mLastStartActivityRecord = new ActivityRecord[1];
+    private ActivityRecord mLastStartActivityRecord;
     // The result of the last activity we attempted to start.
     private int mLastStartActivityResult;
     // Time in milli seconds we attempted to start the last activity.
@@ -212,7 +212,8 @@
      * to avoid unnecessarily retaining parameters. Note that the request is ignored when
      * {@link #startResolvedActivity} is invoked directly.
      */
-    private Request mRequest = new Request();
+    @VisibleForTesting
+    Request mRequest = new Request();
 
     /**
      * An interface that to provide {@link ActivityStarter} instances to the controller. This is
@@ -299,7 +300,8 @@
      * {@link #dump(PrintWriter, String)} and therefore cannot be cleared immediately after
      * execution.
      */
-    private static class Request {
+    @VisibleForTesting
+    static class Request {
         private static final int DEFAULT_CALLING_UID = -1;
         private static final int DEFAULT_CALLING_PID = 0;
         static final int DEFAULT_REAL_CALLING_UID = -1;
@@ -307,6 +309,7 @@
 
         IApplicationThread caller;
         Intent intent;
+        // A copy of the original requested intent, in case for ephemeral app launch.
         Intent ephemeralIntent;
         String resolvedType;
         ActivityInfo activityInfo;
@@ -344,13 +347,6 @@
         boolean allowPendingRemoteAnimationRegistryLookup;
 
         /**
-         * Indicates that we should wait for the result of the start request. This flag is set when
-         * {@link ActivityStarter#setMayWait(int)} is called.
-         * {@see ActivityStarter#startActivityMayWait}.
-         */
-        boolean mayWait;
-
-        /**
          * Ensure constructed request matches reset instance.
          */
         Request() {
@@ -388,7 +384,6 @@
             globalConfig = null;
             userId = 0;
             waitResult = null;
-            mayWait = false;
             avoidMoveToFront = false;
             allowPendingRemoteAnimationRegistryLookup = true;
             filterCallingUid = UserHandle.USER_NULL;
@@ -427,7 +422,6 @@
             globalConfig = request.globalConfig;
             userId = request.userId;
             waitResult = request.waitResult;
-            mayWait = request.mayWait;
             avoidMoveToFront = request.avoidMoveToFront;
             allowPendingRemoteAnimationRegistryLookup
                     = request.allowPendingRemoteAnimationRegistryLookup;
@@ -435,6 +429,77 @@
             originatingPendingIntent = request.originatingPendingIntent;
             allowBackgroundActivityStart = request.allowBackgroundActivityStart;
         }
+
+        /**
+         * Resolve activity from the given intent for this launch.
+         */
+        void resolveActivity(ActivityStackSupervisor supervisor) {
+            if (realCallingPid == Request.DEFAULT_REAL_CALLING_PID) {
+                realCallingPid = Binder.getCallingPid();
+            }
+            if (realCallingUid == Request.DEFAULT_REAL_CALLING_UID) {
+                realCallingUid = Binder.getCallingUid();
+            }
+
+            if (callingUid >= 0) {
+                callingPid = -1;
+            } else if (caller == null) {
+                callingPid = realCallingPid;
+                callingUid = realCallingUid;
+            } else {
+                callingPid = callingUid = -1;
+            }
+
+            // Save a copy in case ephemeral needs it
+            ephemeralIntent = new Intent(intent);
+            // Don't modify the client's object!
+            intent = new Intent(intent);
+            if (intent.getComponent() != null
+                    && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
+                    && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
+                    && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
+                    && supervisor.mService.getPackageManagerInternalLocked()
+                            .isInstantAppInstallerComponent(intent.getComponent())) {
+                // Intercept intents targeted directly to the ephemeral installer the ephemeral
+                // installer should never be started with a raw Intent; instead adjust the intent
+                // so it looks like a "normal" instant app launch.
+                intent.setComponent(null /* component */);
+            }
+
+            resolveInfo = supervisor.resolveIntent(intent, resolvedType, userId,
+                    0 /* matchFlags */,
+                    computeResolveFilterUid(callingUid, realCallingUid, filterCallingUid));
+            if (resolveInfo == null) {
+                final UserInfo userInfo = supervisor.getUserInfo(userId);
+                if (userInfo != null && userInfo.isManagedProfile()) {
+                    // Special case for managed profiles, if attempting to launch non-cryto aware
+                    // app in a locked managed profile from an unlocked parent allow it to resolve
+                    // as user will be sent via confirm credentials to unlock the profile.
+                    final UserManager userManager = UserManager.get(supervisor.mService.mContext);
+                    boolean profileLockedAndParentUnlockingOrUnlocked = false;
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        final UserInfo parent = userManager.getProfileParent(userId);
+                        profileLockedAndParentUnlockingOrUnlocked = (parent != null)
+                                && userManager.isUserUnlockingOrUnlocked(parent.id)
+                                && !userManager.isUserUnlockingOrUnlocked(userId);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                    if (profileLockedAndParentUnlockingOrUnlocked) {
+                        resolveInfo = supervisor.resolveIntent(intent, resolvedType, userId,
+                                PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                                computeResolveFilterUid(callingUid, realCallingUid,
+                                        filterCallingUid));
+                    }
+                }
+            }
+
+            // Collect information about the target of the Intent.
+            activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags,
+                    profilerInfo);
+        }
     }
 
     ActivityStarter(ActivityStartController controller, ActivityTaskManagerService service,
@@ -494,46 +559,95 @@
         mRequest.set(starter.mRequest);
     }
 
-    ActivityRecord getStartActivity() {
-        return mStartActivity;
-    }
-
     boolean relatedToPackage(String packageName) {
-        return (mLastStartActivityRecord[0] != null
-                && packageName.equals(mLastStartActivityRecord[0].packageName))
+        return (mLastStartActivityRecord != null
+                && packageName.equals(mLastStartActivityRecord.packageName))
                 || (mStartActivity != null && packageName.equals(mStartActivity.packageName));
     }
 
     /**
-     * Starts an activity based on the request parameters provided earlier.
+     * Starts an activity based on the provided {@link ActivityRecord} and environment parameters.
+     * Note that this method is called internally as well as part of {@link #executeRequest}.
+     */
+    void startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
+        try {
+            mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(r.intent);
+            mLastStartReason = "startResolvedActivity";
+            mLastStartActivityTimeMs = System.currentTimeMillis();
+            mLastStartActivityRecord = r;
+            mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
+                    voiceInteractor, startFlags, doResume, options, inTask,
+                    false /* restrictedBgActivity */);
+            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult,
+                    mLastStartActivityRecord);
+        } finally {
+            onExecutionComplete();
+        }
+    }
+
+    /**
+     * Resolve necessary information according the request parameters provided earlier, and execute
+     * the request which begin the journey of starting an activity.
      * @return The starter result.
      */
     int execute() {
         try {
-            // TODO(b/64750076): Look into passing request directly to these methods to allow
-            // for transactional diffs and preprocessing.
-            if (mRequest.mayWait) {
-                return startActivityMayWait(mRequest.caller, mRequest.callingUid,
-                        mRequest.callingPackage, mRequest.realCallingPid, mRequest.realCallingUid,
-                        mRequest.intent, mRequest.resolvedType,
-                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
-                        mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
-                        mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
-                        mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
-                        mRequest.inTask, mRequest.reason,
-                        mRequest.allowPendingRemoteAnimationRegistryLookup,
-                        mRequest.originatingPendingIntent, mRequest.allowBackgroundActivityStart);
-            } else {
-                return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
-                        mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
-                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
-                        mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,
-                        mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,
-                        mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,
-                        mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
-                        mRequest.outActivity, mRequest.inTask, mRequest.reason,
-                        mRequest.allowPendingRemoteAnimationRegistryLookup,
-                        mRequest.originatingPendingIntent, mRequest.allowBackgroundActivityStart);
+            // Refuse possible leaked file descriptors
+            if (mRequest.intent != null && mRequest.intent.hasFileDescriptors()) {
+                throw new IllegalArgumentException("File descriptors passed in Intent");
+            }
+
+            mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mRequest.intent);
+
+            if (mRequest.activityInfo == null) {
+                mRequest.resolveActivity(mSupervisor);
+            }
+
+            int res;
+            synchronized (mService.mGlobalLock) {
+                final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
+                stack.mConfigWillChange = mRequest.globalConfig != null
+                        && mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;
+                if (DEBUG_CONFIGURATION) {
+                    Slog.v(TAG_CONFIGURATION, "Starting activity when config will change = "
+                            + stack.mConfigWillChange);
+                }
+
+                final long origId = Binder.clearCallingIdentity();
+
+                res = resolveToHeavyWeightSwitcherIfNeeded();
+                if (res != START_SUCCESS) {
+                    return res;
+                }
+                res = executeRequest(mRequest);
+
+                Binder.restoreCallingIdentity(origId);
+
+                if (stack.mConfigWillChange) {
+                    // If the caller also wants to switch to a new configuration, do so now.
+                    // This allows a clean switch, as we are waiting for the current activity
+                    // to pause (so we will not destroy it), and have not yet started the
+                    // next activity.
+                    mService.mAmInternal.enforceCallingPermission(
+                            android.Manifest.permission.CHANGE_CONFIGURATION,
+                            "updateConfiguration()");
+                    stack.mConfigWillChange = false;
+                    if (DEBUG_CONFIGURATION) {
+                        Slog.v(TAG_CONFIGURATION,
+                                "Updating to new configuration after starting activity.");
+                    }
+                    mService.updateConfigurationLocked(mRequest.globalConfig, null, false);
+                }
+
+                // Notify ActivityMetricsLogger that the activity has launched.
+                // ActivityMetricsLogger will then wait for the windows to be drawn and populate
+                // WaitResult.
+                mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res,
+                        mLastStartActivityRecord);
+                return getExternalResult(mRequest.waitResult == null ? res
+                        : waitForResult(res, mLastStartActivityRecord));
             }
         } finally {
             onExecutionComplete();
@@ -541,89 +655,167 @@
     }
 
     /**
-     * Starts an activity based on the provided {@link ActivityRecord} and environment parameters.
-     * Note that this method is called internally as well as part of {@link #startActivity}.
-     *
-     * @return The start result.
+     * Updates the request to heavy-weight switch if this is a heavy-weight process while there
+     * already have another, different heavy-weight process running.
      */
-    int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
-        try {
-            mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(r.intent);
-            mLastStartReason = "startResolvedActivity";
-            mLastStartActivityTimeMs = System.currentTimeMillis();
-            mLastStartActivityRecord[0] = r;
-            mLastStartActivityResult = startActivity(r, sourceRecord, voiceSession, voiceInteractor,
-                    startFlags, doResume, options, inTask, mLastStartActivityRecord,
-                    false /* restrictedBgActivity */);
-            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult,
-                    mLastStartActivityRecord[0]);
-            return mLastStartActivityResult;
-        } finally {
-            onExecutionComplete();
-        }
-    }
-
-    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
-            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
-            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
-            SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
-            ActivityRecord[] outActivity, TaskRecord inTask, String reason,
-            boolean allowPendingRemoteAnimationRegistryLookup,
-            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
-
-        if (TextUtils.isEmpty(reason)) {
-            throw new IllegalArgumentException("Need to specify a reason.");
-        }
-        mLastStartReason = reason;
-        mLastStartActivityTimeMs = System.currentTimeMillis();
-        mLastStartActivityRecord[0] = null;
-
-        mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType,
-                aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
-                callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
-                options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
-                inTask, allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
-                allowBackgroundActivityStart);
-
-        if (outActivity != null) {
-            // mLastStartActivityRecord[0] is set in the call to startActivity above.
-            outActivity[0] = mLastStartActivityRecord[0];
+    private int resolveToHeavyWeightSwitcherIfNeeded() {
+        if (mRequest.activityInfo == null || !mService.mHasHeavyWeightFeature
+                || (mRequest.activityInfo.applicationInfo.privateFlags
+                        & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) == 0) {
+            return START_SUCCESS;
         }
 
-        return getExternalResult(mLastStartActivityResult);
-    }
+        if (!mRequest.activityInfo.processName.equals(
+                mRequest.activityInfo.applicationInfo.packageName)) {
+            return START_SUCCESS;
+        }
 
-    static int getExternalResult(int result) {
-        // Aborted results are treated as successes externally, but we must track them internally.
-        return result != START_ABORTED ? result : START_SUCCESS;
+        final WindowProcessController heavy = mService.mHeavyWeightProcess;
+        if (heavy == null || (heavy.mInfo.uid == mRequest.activityInfo.applicationInfo.uid
+                && heavy.mName.equals(mRequest.activityInfo.processName))) {
+            return START_SUCCESS;
+        }
+
+        int appCallingUid = mRequest.callingUid;
+        if (mRequest.caller != null) {
+            WindowProcessController callerApp = mService.getProcessController(mRequest.caller);
+            if (callerApp != null) {
+                appCallingUid = callerApp.mInfo.uid;
+            } else {
+                Slog.w(TAG, "Unable to find app for caller " + mRequest.caller + " (pid="
+                        + mRequest.callingPid + ") when starting: " + mRequest.intent.toString());
+                SafeActivityOptions.abort(mRequest.activityOptions);
+                return ActivityManager.START_PERMISSION_DENIED;
+            }
+        }
+
+        final IIntentSender target = mService.getIntentSenderLocked(
+                ActivityManager.INTENT_SENDER_ACTIVITY, "android" /* packageName */, appCallingUid,
+                mRequest.userId, null /* token */, null /* resultWho*/, 0 /* requestCode*/,
+                new Intent[] { mRequest.intent }, new String[] { mRequest.resolvedType },
+                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT,
+                null /* bOptions */);
+
+        final Intent newIntent = new Intent();
+        if (mRequest.requestCode >= 0) {
+            // Caller is requesting a result.
+            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
+        }
+        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT, new IntentSender(target));
+        heavy.updateIntentForHeavyWeightActivity(newIntent);
+        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
+                mRequest.activityInfo.packageName);
+        newIntent.setFlags(mRequest.intent.getFlags());
+        newIntent.setClassName("android" /* packageName */,
+                HeavyWeightSwitcherActivity.class.getName());
+        mRequest.intent = newIntent;
+        mRequest.resolvedType = null;
+        mRequest.caller = null;
+        mRequest.callingUid = Binder.getCallingUid();
+        mRequest.callingPid = Binder.getCallingPid();
+        mRequest.componentSpecified = true;
+        mRequest.resolveInfo = mSupervisor.resolveIntent(mRequest.intent, null /* resolvedType */,
+                mRequest.userId, 0 /* matchFlags */,
+                computeResolveFilterUid(mRequest.callingUid, mRequest.realCallingUid,
+                        mRequest.filterCallingUid));
+        mRequest.activityInfo =
+                mRequest.resolveInfo != null ? mRequest.resolveInfo.activityInfo : null;
+        if (mRequest.activityInfo != null) {
+            mRequest.activityInfo = mService.mAmInternal.getActivityInfoForUser(
+                    mRequest.activityInfo, mRequest.userId);
+        }
+
+        return START_SUCCESS;
     }
 
     /**
-     * Called when execution is complete. Sets state indicating completion and proceeds with
-     * recycling if appropriate.
+     * Wait for activity launch completes.
      */
-    private void onExecutionComplete() {
-        mController.onExecutionComplete(this);
+    private int waitForResult(int res, ActivityRecord r) {
+        mRequest.waitResult.result = res;
+        switch(res) {
+            case START_SUCCESS: {
+                mSupervisor.mWaitingActivityLaunched.add(mRequest.waitResult);
+                do {
+                    try {
+                        mService.mGlobalLock.wait();
+                    } catch (InterruptedException e) {
+                    }
+                } while (mRequest.waitResult.result != START_TASK_TO_FRONT
+                        && !mRequest.waitResult.timeout && mRequest.waitResult.who == null);
+                if (mRequest.waitResult.result == START_TASK_TO_FRONT) {
+                    res = START_TASK_TO_FRONT;
+                }
+                break;
+            }
+            case START_DELIVERED_TO_TOP: {
+                mRequest.waitResult.timeout = false;
+                mRequest.waitResult.who = r.mActivityComponent;
+                mRequest.waitResult.totalTime = 0;
+                break;
+            }
+            case START_TASK_TO_FRONT: {
+                mRequest.waitResult.launchState =
+                        r.attachedToProcess() ? LAUNCH_STATE_HOT : LAUNCH_STATE_COLD;
+                // ActivityRecord may represent a different activity, but it should not be
+                // in the resumed state.
+                if (r.nowVisible && r.isState(RESUMED)) {
+                    mRequest.waitResult.timeout = false;
+                    mRequest.waitResult.who = r.mActivityComponent;
+                    mRequest.waitResult.totalTime = 0;
+                } else {
+                    final long startTimeMs = SystemClock.uptimeMillis();
+                    mSupervisor.waitActivityVisible(r.mActivityComponent, mRequest.waitResult,
+                            startTimeMs);
+                    // Note: the timeout variable is not currently not ever set.
+                    do {
+                        try {
+                            mService.mGlobalLock.wait();
+                        } catch (InterruptedException e) {
+                        }
+                    } while (!mRequest.waitResult.timeout && mRequest.waitResult.who == null);
+                }
+                break;
+            }
+        }
+        return res;
     }
 
-    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
-            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
-            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
-            SafeActivityOptions options,
-            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
-            TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
-            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
-        mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
+    /**
+     * Executing activity start request and starts the journey of starting an activity. Here
+     * begins with performing several preliminary checks. The normally activity launch flow will
+     * go through {@link #startActivityUnchecked} to {@link #startActivityInner}.
+     */
+    private int executeRequest(Request request) {
+        if (TextUtils.isEmpty(request.reason)) {
+            throw new IllegalArgumentException("Need to specify a reason.");
+        }
+        mLastStartReason = request.reason;
+        mLastStartActivityTimeMs = System.currentTimeMillis();
+        mLastStartActivityRecord = null;
+
+        final IApplicationThread caller = request.caller;
+        Intent intent = request.intent;
+        String resolvedType = request.resolvedType;
+        ActivityInfo aInfo = request.activityInfo;
+        ResolveInfo rInfo = request.resolveInfo;
+        final IVoiceInteractionSession voiceSession = request.voiceSession;
+        final IBinder resultTo = request.resultTo;
+        String resultWho = request.resultWho;
+        int requestCode = request.requestCode;
+        int callingPid = request.callingPid;
+        int callingUid = request.callingUid;
+        String callingPackage = request.callingPackage;
+        final int realCallingPid = request.realCallingPid;
+        final int realCallingUid = request.realCallingUid;
+        final int startFlags = request.startFlags;
+        final SafeActivityOptions options = request.activityOptions;
+        TaskRecord inTask = request.inTask;
+
         int err = ActivityManager.START_SUCCESS;
         // Pull the optional Ephemeral Installer-only bundle out of the options early.
-        final Bundle verificationBundle
-                = options != null ? options.popAppVerificationBundle() : null;
+        final Bundle verificationBundle =
+                options != null ? options.popAppVerificationBundle() : null;
 
         WindowProcessController callerApp = null;
         if (caller != null) {
@@ -632,16 +824,14 @@
                 callingPid = callerApp.getPid();
                 callingUid = callerApp.mInfo.uid;
             } else {
-                Slog.w(TAG, "Unable to find app for caller " + caller
-                        + " (pid=" + callingPid + ") when starting: "
-                        + intent.toString());
+                Slog.w(TAG, "Unable to find app for caller " + caller + " (pid=" + callingPid
+                        + ") when starting: " + intent.toString());
                 err = ActivityManager.START_PERMISSION_DENIED;
             }
         }
 
         final int userId = aInfo != null && aInfo.applicationInfo != null
                 ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
-
         if (err == ActivityManager.START_SUCCESS) {
             Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
                     + "} from uid " + callingUid);
@@ -651,8 +841,9 @@
         ActivityRecord resultRecord = null;
         if (resultTo != null) {
             sourceRecord = mRootActivityContainer.isInAnyStack(resultTo);
-            if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
-                    "Will send result to " + resultTo + " " + sourceRecord);
+            if (DEBUG_RESULTS) {
+                Slog.v(TAG_RESULTS, "Will send result to " + resultTo + " " + sourceRecord);
+            }
             if (sourceRecord != null) {
                 if (requestCode >= 0 && !sourceRecord.finishing) {
                     resultRecord = sourceRecord;
@@ -661,10 +852,9 @@
         }
 
         final int launchFlags = intent.getFlags();
-
         if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
-            // Transfer the result target from the source activity to the new
-            // one being started, including any failures.
+            // Transfer the result target from the source activity to the new one being started,
+            // including any failures.
             if (requestCode >= 0) {
                 SafeActivityOptions.abort(options);
                 return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
@@ -680,16 +870,16 @@
                 resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
             }
             if (sourceRecord.launchedFromUid == callingUid) {
-                // The new activity is being launched from the same uid as the previous
-                // activity in the flow, and asking to forward its result back to the
-                // previous.  In this case the activity is serving as a trampoline between
-                // the two, so we also want to update its launchedFromPackage to be the
-                // same as the previous activity.  Note that this is safe, since we know
-                // these two packages come from the same uid; the caller could just as
-                // well have supplied that same package name itself.  This specifially
-                // deals with the case of an intent picker/chooser being launched in the app
-                // flow to redirect to an activity picked by the user, where we want the final
-                // activity to consider it to have been launched by the previous app activity.
+                // The new activity is being launched from the same uid as the previous activity
+                // in the flow, and asking to forward its result back to the previous.  In this
+                // case the activity is serving as a trampoline between the two, so we also want
+                // to update its launchedFromPackage to be the same as the previous activity.
+                // Note that this is safe, since we know these two packages come from the same
+                // uid; the caller could just as well have supplied that same package name itself
+                // . This specifially deals with the case of an intent picker/chooser being
+                // launched in the app flow to redirect to an activity picked by the user, where
+                // we want the final activity to consider it to have been launched by the
+                // previous app activity.
                 callingPackage = sourceRecord.launchedFromPackage;
             }
         }
@@ -708,19 +898,18 @@
 
         if (err == ActivityManager.START_SUCCESS && sourceRecord != null
                 && sourceRecord.getTaskRecord().voiceSession != null) {
-            // If this activity is being launched as part of a voice session, we need
-            // to ensure that it is safe to do so.  If the upcoming activity will also
-            // be part of the voice session, we can only launch it if it has explicitly
-            // said it supports the VOICE category, or it is a part of the calling app.
+            // If this activity is being launched as part of a voice session, we need to ensure
+            // that it is safe to do so.  If the upcoming activity will also be part of the voice
+            // session, we can only launch it if it has explicitly said it supports the VOICE
+            // category, or it is a part of the calling app.
             if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0
                     && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
                 try {
                     intent.addCategory(Intent.CATEGORY_VOICE);
                     if (!mService.getPackageManager().activitySupportsIntent(
                             intent.getComponent(), intent, resolvedType)) {
-                        Slog.w(TAG,
-                                "Activity being started in current voice task does not support voice: "
-                                        + intent);
+                        Slog.w(TAG, "Activity being started in current voice task does not support "
+                                + "voice: " + intent);
                         err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
                     }
                 } catch (RemoteException e) {
@@ -737,8 +926,7 @@
                 if (!mService.getPackageManager().activitySupportsIntent(intent.getComponent(),
                         intent, resolvedType)) {
                     Slog.w(TAG,
-                            "Activity being started in new voice task does not support: "
-                                    + intent);
+                            "Activity being started in new voice task does not support: " + intent);
                     err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
                 }
             } catch (RemoteException e) {
@@ -760,7 +948,7 @@
         }
 
         boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
-                requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
+                requestCode, callingPid, callingUid, callingPackage, request.ignoreTargetSecurity,
                 inTask != null, callerApp, resultRecord, resultStack);
         abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
@@ -774,7 +962,8 @@
                         "shouldAbortBackgroundActivityStart");
                 restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
                         callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
-                        originatingPendingIntent, allowBackgroundActivityStart, intent);
+                        request.originatingPendingIntent, request.allowBackgroundActivityStart,
+                        intent);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             }
@@ -783,15 +972,15 @@
         // Merge the two options bundles, while realCallerOptions takes precedence.
         ActivityOptions checkedOptions = options != null
                 ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
-        if (allowPendingRemoteAnimationRegistryLookup) {
+        if (request.allowPendingRemoteAnimationRegistryLookup) {
             checkedOptions = mService.getActivityStartController()
                     .getPendingRemoteAnimationRegistry()
                     .overrideOptionsIfNeeded(callingPackage, checkedOptions);
         }
         if (mService.mController != null) {
             try {
-                // The Intent we give to the watcher has the extra data
-                // stripped off, since it can contain private information.
+                // The Intent we give to the watcher has the extra data stripped off, since it
+                // can contain private information.
                 Intent watchIntent = intent.cloneFilter();
                 abort |= !mService.mController.activityStarting(watchIntent,
                         aInfo.applicationInfo.packageName);
@@ -820,8 +1009,8 @@
                 resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
                         null /* data */);
             }
-            // We pretend to the caller that it was really started, but
-            // they will just get a cancel result.
+            // We pretend to the caller that it was really started, but they will just get a
+            // cancel result.
             ActivityOptions.abort(checkedOptions);
             return START_ABORTED;
         }
@@ -832,7 +1021,7 @@
         if (aInfo != null) {
             if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                     aInfo.packageName, userId)) {
-                IIntentSender target = mService.getIntentSenderLocked(
+                final IIntentSender target = mService.getIntentSenderLocked(
                         ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
                         callingUid, userId, null, null, 0, new Intent[]{intent},
                         new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
@@ -870,7 +1059,7 @@
 
                 rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, 0,
                         computeResolveFilterUid(
-                                callingUid, realCallingUid, mRequest.filterCallingUid));
+                                callingUid, realCallingUid, request.filterCallingUid));
                 aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
                         null /*profilerInfo*/);
 
@@ -889,7 +1078,7 @@
         // starts either the intent we resolved here [on install error] or the ephemeral
         // app [on install success].
         if (rInfo != null && rInfo.auxiliaryInfo != null) {
-            intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent,
+            intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent,
                     callingPackage, verificationBundle, resolvedType, userId);
             resolvedType = null;
             callingUid = realCallingUid;
@@ -898,13 +1087,11 @@
             aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
         }
 
-        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
+        final ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
                 callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
-                resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
-                mSupervisor, checkedOptions, sourceRecord);
-        if (outActivity != null) {
-            outActivity[0] = r;
-        }
+                resultRecord, resultWho, requestCode, request.componentSpecified,
+                voiceSession != null, mSupervisor, checkedOptions, sourceRecord);
+        mLastStartActivityRecord = r;
 
         if (r.appTimeTracker == null && sourceRecord != null) {
             // If the caller didn't specify an explicit time tracker, we want to continue
@@ -932,10 +1119,52 @@
         mService.onStartActivitySetDidAppSwitch();
         mController.doPendingActivityLaunches(false);
 
-        final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
-                true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);
-        mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]);
-        return res;
+        mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
+                request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,
+                restrictedBgActivity);
+
+        if (request.outActivity != null) {
+            request.outActivity[0] = mLastStartActivityRecord;
+        }
+
+        return getExternalResult(mLastStartActivityResult);
+    }
+
+    /**
+     * Return true if background activity is really aborted.
+     *
+     * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere.
+     */
+    private boolean handleBackgroundActivityAbort(ActivityRecord r) {
+        // TODO(b/131747138): Remove toast and refactor related code in R release.
+        final boolean abort = !mService.isBackgroundActivityStartsEnabled();
+        if (!abort) {
+            return false;
+        }
+        final ActivityRecord resultRecord = r.resultTo;
+        final String resultWho = r.resultWho;
+        int requestCode = r.requestCode;
+        if (resultRecord != null) {
+            resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
+                    null /* data */);
+        }
+        // We pretend to the caller that it was really started to make it backward compatible, but
+        // they will just get a cancel result.
+        ActivityOptions.abort(r.pendingOptions);
+        return true;
+    }
+
+    static int getExternalResult(int result) {
+        // Aborted results are treated as successes externally, but we must track them internally.
+        return result != START_ABORTED ? result : START_SUCCESS;
+    }
+
+    /**
+     * Called when execution is complete. Sets state indicating completion and proceeds with
+     * recycling if appropriate.
+     */
+    private void onExecutionComplete() {
+        mController.onExecutionComplete(this);
     }
 
     boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
@@ -1110,8 +1339,8 @@
         // We're waiting for an activity launch to finish, but that activity simply
         // brought another activity to front. We must also handle the case where the task is already
         // in the front as a result of the trampoline activity being in the same task (it will be
-        // considered focused as the trampoline will be finished). Let startActivityMayWait() know
-        // about this, so it waits for the new activity to become visible instead.
+        // considered focused as the trampoline will be finished). Let them know about this, so
+        // it waits for the new activity to become visible instead, {@link #waitResultIfNeeded}.
         mSupervisor.reportWaitingActivityLaunchedIfNeeded(r, result);
 
         if (startedActivityStack == null) {
@@ -1141,241 +1370,6 @@
         }
     }
 
-    private int startActivityMayWait(IApplicationThread caller, int callingUid,
-            String callingPackage, int requestRealCallingPid, int requestRealCallingUid,
-            Intent intent, String resolvedType, IVoiceInteractionSession voiceSession,
-            IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, WaitResult outResult,
-            Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
-            int userId, TaskRecord inTask, String reason,
-            boolean allowPendingRemoteAnimationRegistryLookup,
-            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
-        // Refuse possible leaked file descriptors
-        if (intent != null && intent.hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-        mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
-        boolean componentSpecified = intent.getComponent() != null;
-
-        final int realCallingPid = requestRealCallingPid != Request.DEFAULT_REAL_CALLING_PID
-                ? requestRealCallingPid
-                : Binder.getCallingPid();
-        final int realCallingUid = requestRealCallingUid != Request.DEFAULT_REAL_CALLING_UID
-                ? requestRealCallingUid
-                : Binder.getCallingUid();
-
-        int callingPid;
-        if (callingUid >= 0) {
-            callingPid = -1;
-        } else if (caller == null) {
-            callingPid = realCallingPid;
-            callingUid = realCallingUid;
-        } else {
-            callingPid = callingUid = -1;
-        }
-
-        // Save a copy in case ephemeral needs it
-        final Intent ephemeralIntent = new Intent(intent);
-        // Don't modify the client's object!
-        intent = new Intent(intent);
-        if (componentSpecified
-                && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
-                && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
-                && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
-                && mService.getPackageManagerInternalLocked()
-                        .isInstantAppInstallerComponent(intent.getComponent())) {
-            // intercept intents targeted directly to the ephemeral installer the
-            // ephemeral installer should never be started with a raw Intent; instead
-            // adjust the intent so it looks like a "normal" instant app launch
-            intent.setComponent(null /*component*/);
-            componentSpecified = false;
-        }
-
-        ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
-                0 /* matchFlags */,
-                        computeResolveFilterUid(
-                                callingUid, realCallingUid, mRequest.filterCallingUid));
-        if (rInfo == null) {
-            UserInfo userInfo = mSupervisor.getUserInfo(userId);
-            if (userInfo != null && userInfo.isManagedProfile()) {
-                // Special case for managed profiles, if attempting to launch non-cryto aware
-                // app in a locked managed profile from an unlocked parent allow it to resolve
-                // as user will be sent via confirm credentials to unlock the profile.
-                UserManager userManager = UserManager.get(mService.mContext);
-                boolean profileLockedAndParentUnlockingOrUnlocked = false;
-                long token = Binder.clearCallingIdentity();
-                try {
-                    UserInfo parent = userManager.getProfileParent(userId);
-                    profileLockedAndParentUnlockingOrUnlocked = (parent != null)
-                            && userManager.isUserUnlockingOrUnlocked(parent.id)
-                            && !userManager.isUserUnlockingOrUnlocked(userId);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
-                if (profileLockedAndParentUnlockingOrUnlocked) {
-                    rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
-                            PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                            computeResolveFilterUid(
-                                    callingUid, realCallingUid, mRequest.filterCallingUid));
-                }
-            }
-        }
-        // Collect information about the target of the Intent.
-        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
-
-        synchronized (mService.mGlobalLock) {
-            final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
-            stack.mConfigWillChange = globalConfig != null
-                    && mService.getGlobalConfiguration().diff(globalConfig) != 0;
-            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Starting activity when config will change = " + stack.mConfigWillChange);
-
-            final long origId = Binder.clearCallingIdentity();
-
-            if (aInfo != null &&
-                    (aInfo.applicationInfo.privateFlags
-                            & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 &&
-                    mService.mHasHeavyWeightFeature) {
-                // This may be a heavy-weight process!  Check to see if we already
-                // have another, different heavy-weight process running.
-                if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
-                    final WindowProcessController heavy = mService.mHeavyWeightProcess;
-                    if (heavy != null && (heavy.mInfo.uid != aInfo.applicationInfo.uid
-                            || !heavy.mName.equals(aInfo.processName))) {
-                        int appCallingUid = callingUid;
-                        if (caller != null) {
-                            WindowProcessController callerApp =
-                                    mService.getProcessController(caller);
-                            if (callerApp != null) {
-                                appCallingUid = callerApp.mInfo.uid;
-                            } else {
-                                Slog.w(TAG, "Unable to find app for caller " + caller
-                                        + " (pid=" + callingPid + ") when starting: "
-                                        + intent.toString());
-                                SafeActivityOptions.abort(options);
-                                return ActivityManager.START_PERMISSION_DENIED;
-                            }
-                        }
-
-                        IIntentSender target = mService.getIntentSenderLocked(
-                                ActivityManager.INTENT_SENDER_ACTIVITY, "android",
-                                appCallingUid, userId, null, null, 0, new Intent[] { intent },
-                                new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
-                                        | PendingIntent.FLAG_ONE_SHOT, null);
-
-                        Intent newIntent = new Intent();
-                        if (requestCode >= 0) {
-                            // Caller is requesting a result.
-                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
-                        }
-                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
-                                new IntentSender(target));
-                        heavy.updateIntentForHeavyWeightActivity(newIntent);
-                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
-                                aInfo.packageName);
-                        newIntent.setFlags(intent.getFlags());
-                        newIntent.setClassName("android",
-                                HeavyWeightSwitcherActivity.class.getName());
-                        intent = newIntent;
-                        resolvedType = null;
-                        caller = null;
-                        callingUid = Binder.getCallingUid();
-                        callingPid = Binder.getCallingPid();
-                        componentSpecified = true;
-                        rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId,
-                                0 /* matchFlags */, computeResolveFilterUid(
-                                        callingUid, realCallingUid, mRequest.filterCallingUid));
-                        aInfo = rInfo != null ? rInfo.activityInfo : null;
-                        if (aInfo != null) {
-                            aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
-                        }
-                    }
-                }
-            }
-
-            final ActivityRecord[] outRecord = new ActivityRecord[1];
-            int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
-                    voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
-                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
-                    ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
-                    allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
-                    allowBackgroundActivityStart);
-
-            Binder.restoreCallingIdentity(origId);
-
-            if (stack.mConfigWillChange) {
-                // If the caller also wants to switch to a new configuration,
-                // do so now.  This allows a clean switch, as we are waiting
-                // for the current activity to pause (so we will not destroy
-                // it), and have not yet started the next activity.
-                mService.mAmInternal.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
-                        "updateConfiguration()");
-                stack.mConfigWillChange = false;
-                if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Updating to new configuration after starting activity.");
-                mService.updateConfigurationLocked(globalConfig, null, false);
-            }
-
-            // Notify ActivityMetricsLogger that the activity has launched. ActivityMetricsLogger
-            // will then wait for the windows to be drawn and populate WaitResult.
-            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]);
-            if (outResult != null) {
-                outResult.result = res;
-
-                final ActivityRecord r = outRecord[0];
-
-                switch(res) {
-                    case START_SUCCESS: {
-                        mSupervisor.mWaitingActivityLaunched.add(outResult);
-                        do {
-                            try {
-                                mService.mGlobalLock.wait();
-                            } catch (InterruptedException e) {
-                            }
-                        } while (outResult.result != START_TASK_TO_FRONT
-                                && !outResult.timeout && outResult.who == null);
-                        if (outResult.result == START_TASK_TO_FRONT) {
-                            res = START_TASK_TO_FRONT;
-                        }
-                        break;
-                    }
-                    case START_DELIVERED_TO_TOP: {
-                        outResult.timeout = false;
-                        outResult.who = r.mActivityComponent;
-                        outResult.totalTime = 0;
-                        break;
-                    }
-                    case START_TASK_TO_FRONT: {
-                        outResult.launchState =
-                                r.attachedToProcess() ? LAUNCH_STATE_HOT : LAUNCH_STATE_COLD;
-                        // ActivityRecord may represent a different activity, but it should not be
-                        // in the resumed state.
-                        if (r.nowVisible && r.isState(RESUMED)) {
-                            outResult.timeout = false;
-                            outResult.who = r.mActivityComponent;
-                            outResult.totalTime = 0;
-                        } else {
-                            final long startTimeMs = SystemClock.uptimeMillis();
-                            mSupervisor.waitActivityVisible(
-                                    r.mActivityComponent, outResult, startTimeMs);
-                            // Note: the timeout variable is not currently not ever set.
-                            do {
-                                try {
-                                    mService.mGlobalLock.wait();
-                                } catch (InterruptedException e) {
-                                }
-                            } while (!outResult.timeout && outResult.who == null);
-                        }
-                        break;
-                    }
-                }
-            }
-
-            return res;
-        }
-    }
-
     /**
      * Compute the logical UID based on which the package manager would filter
      * app components i.e. based on which the instant app policy would be applied
@@ -1393,17 +1387,22 @@
                 : (customCallingUid >= 0 ? customCallingUid : actualCallingUid);
     }
 
-    private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
+    /**
+     * Start an activity while most of preliminary checks has been done and caller has been
+     * confirmed that holds necessary permissions to do so.
+     * Here also ensures that the starting activity is removed if the start wasn't successful.
+     */
+    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
                 IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                 int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
-                ActivityRecord[] outActivity, boolean restrictedBgActivity) {
+                boolean restrictedBgActivity) {
         int result = START_CANCELED;
         final ActivityStack startedActivityStack;
         try {
             mService.deferWindowLayout();
             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
             result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
-                    startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
+                    startFlags, doResume, options, inTask, restrictedBgActivity);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             startedActivityStack = handleStartResult(r, result);
@@ -1459,34 +1458,16 @@
     }
 
     /**
-     * Return true if background activity is really aborted.
+     * Start an activity and determine if the activity should be adding to the top of an existing
+     * task or delivered new intent to an existing activity. Also manipulating the activity task
+     * onto requested or valid stack/display.
      *
-     * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere.
+     * Note: This method should only be called from {@link #startActivityUnchecked}.
      */
-    private boolean handleBackgroundActivityAbort(ActivityRecord r) {
-        // TODO(b/131747138): Remove toast and refactor related code in R release.
-        final boolean abort = !mService.isBackgroundActivityStartsEnabled();
-        if (!abort) {
-            return false;
-        }
-        final ActivityRecord resultRecord = r.resultTo;
-        final String resultWho = r.resultWho;
-        int requestCode = r.requestCode;
-        if (resultRecord != null) {
-            resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
-                    null /* data */);
-        }
-        // We pretend to the caller that it was really started to make it backward compatible, but
-        // they will just get a cancel result.
-        ActivityOptions.abort(r.pendingOptions);
-        return true;
-    }
-
-    // Note: This method should only be called from {@link startActivity}.
     private int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
-            ActivityRecord[] outActivity, boolean restrictedBgActivity) {
+            boolean restrictedBgActivity) {
         setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                 voiceInteractor, restrictedBgActivity);
 
@@ -1528,7 +1509,7 @@
         final ActivityRecord targetTaskTop = newTask ? null : targetTask.getTopActivity();
         if (targetTaskTop != null) {
             // Recycle the target task for this launch.
-            startResult = recycleTask(targetTask, targetTaskTop, reusedActivity, outActivity);
+            startResult = recycleTask(targetTask, targetTaskTop, reusedActivity);
             if (startResult != START_SUCCESS) {
                 return startResult;
             }
@@ -1690,7 +1671,7 @@
      * - Determine whether need to add a new activity on top or just brought the task to front.
      */
     private int recycleTask(TaskRecord targetTask, ActivityRecord targetTaskTop,
-            ActivityRecord reusedActivity, ActivityRecord[] outActivity) {
+            ActivityRecord reusedActivity) {
         // True if we are clearing top and resetting of a standard (default) launch mode
         // ({@code LAUNCH_MULTIPLE}) activity. The existing activity will be finished.
         final boolean clearTopAndResetStandardLaunchMode =
@@ -1729,13 +1710,11 @@
 
         setTargetStackIfNeeded(targetTaskTop);
 
-        final ActivityRecord outResult =
-                outActivity != null && outActivity.length > 0 ? outActivity[0] : null;
-
         // When there is a reused activity and the current result is a trampoline activity,
         // set the reused activity as the result.
-        if (outResult != null && (outResult.finishing || outResult.noDisplay)) {
-            outActivity[0] = targetTaskTop;
+        if (mLastStartActivityRecord != null
+                && (mLastStartActivityRecord.finishing || mLastStartActivityRecord.noDisplay)) {
+            mLastStartActivityRecord = targetTaskTop;
         }
 
         if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -1777,12 +1756,11 @@
         // We didn't do anything...  but it was needed (a.k.a., client don't use that intent!)
         // And for paranoia, make sure we have correctly resumed the top activity.
         resumeTargetStackIfNeeded();
-        if (outActivity != null && outActivity.length > 0) {
-            // The reusedActivity could be finishing, for example of starting an activity with
-            // FLAG_ACTIVITY_CLEAR_TOP flag. In that case, return the top running activity in the
-            // task instead.
-            outActivity[0] = targetTaskTop.finishing ? targetTask.getTopActivity() : targetTaskTop;
-        }
+        // The reusedActivity could be finishing, for example of starting an activity with
+        // FLAG_ACTIVITY_CLEAR_TOP flag. In that case, return the top running activity in the
+        // task instead.
+        mLastStartActivityRecord =
+                targetTaskTop.finishing ? targetTask.getTopActivity() : targetTaskTop;
         return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
     }
 
@@ -2652,12 +2630,6 @@
         return this;
     }
 
-    ActivityStarter setEphemeralIntent(Intent intent) {
-        mRequest.ephemeralIntent = intent;
-        return this;
-    }
-
-
     ActivityStarter setResolvedType(String type) {
         mRequest.resolvedType = type;
         return this;
@@ -2809,13 +2781,6 @@
         return this;
     }
 
-    ActivityStarter setMayWait(int userId) {
-        mRequest.mayWait = true;
-        mRequest.userId = userId;
-
-        return this;
-    }
-
     ActivityStarter setAllowPendingRemoteAnimationRegistryLookup(boolean allowLookup) {
         mRequest.allowPendingRemoteAnimationRegistryLookup = allowLookup;
         return this;
@@ -2845,11 +2810,10 @@
         pw.print(prefix);
         pw.print("mLastStartActivityResult=");
         pw.println(mLastStartActivityResult);
-        ActivityRecord r = mLastStartActivityRecord[0];
-        if (r != null) {
+        if (mLastStartActivityRecord != null) {
             pw.print(prefix);
             pw.println("mLastStartActivityRecord:");
-            r.dump(pw, prefix + "  ", true /* dumpAll */);
+            mLastStartActivityRecord.dump(pw, prefix + "  ", true /* dumpAll */);
         }
         if (mStartActivity != null) {
             pw.print(prefix);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 14df505..ba825725 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1056,7 +1056,7 @@
                 .setStartFlags(startFlags)
                 .setProfilerInfo(profilerInfo)
                 .setActivityOptions(bOptions)
-                .setMayWait(userId)
+                .setUserId(userId)
                 .execute();
 
     }
@@ -1227,7 +1227,7 @@
                     .setRequestCode(requestCode)
                     .setStartFlags(startFlags)
                     .setActivityOptions(bOptions)
-                    .setMayWait(userId)
+                    .setUserId(userId)
                     .setProfilerInfo(profilerInfo)
                     .setWaitResult(res)
                     .execute();
@@ -1254,7 +1254,7 @@
                     .setStartFlags(startFlags)
                     .setGlobalConfiguration(config)
                     .setActivityOptions(bOptions)
-                    .setMayWait(userId)
+                    .setUserId(userId)
                     .execute();
         }
     }
@@ -1384,7 +1384,7 @@
                     .setRequestCode(requestCode)
                     .setStartFlags(startFlags)
                     .setActivityOptions(bOptions)
-                    .setMayWait(userId)
+                    .setUserId(userId)
                     .setIgnoreTargetSecurity(ignoreTargetSecurity)
                     .setFilterCallingUid(isResolver ? 0 /* system */ : targetUid)
                     // The target may well be in the background, which would normally prevent it
@@ -1432,7 +1432,7 @@
                 .setStartFlags(startFlags)
                 .setProfilerInfo(profilerInfo)
                 .setActivityOptions(bOptions)
-                .setMayWait(userId)
+                .setUserId(userId)
                 .setAllowBackgroundActivityStart(true)
                 .execute();
     }
@@ -1448,7 +1448,7 @@
                 .setCallingPackage(callingPackage)
                 .setResolvedType(resolvedType)
                 .setActivityOptions(bOptions)
-                .setMayWait(userId)
+                .setUserId(userId)
                 .setAllowBackgroundActivityStart(true)
                 .execute();
     }
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 1eb7455..c5e190d 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -155,7 +155,7 @@
                 .setCallingPackage(callingPackage)
                 .setResolvedType(resolvedType)
                 .setActivityOptions(bOptions)
-                .setMayWait(callingUser)
+                .setUserId(callingUser)
                 .setInTask(tr)
                 .execute();
     }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index b7d25c3..2dae126 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -452,7 +452,7 @@
                 .setCallingUid(mRecentsUid)
                 .setCallingPackage(mRecentsComponent.getPackageName())
                 .setActivityOptions(new SafeActivityOptions(options))
-                .setMayWait(mUserId)
+                .setUserId(mUserId)
                 .execute();
     }