“This is the 24th day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

Analysis of the Startup process of the Launcher

After starting AMS from SystemServer, the systemReady function of AMS is used to start the startup process of the application Launcher. In this process, the systemReady function of AMS is used to start the application Launcher. By calling ActivityStarter’s execute function, you end up in the startActivityInner function of the ActivityStarter object, Add the corresponding ActivityRecord to ActivityStack through ActivityStack’s startActivityLocked function, And resumeFocusedStacksTopActivities through RootWindowContainer object function to launch this Activity, we analysis this process in the future, and ultimately the Launcher application specific process of the main interface display

The ActivityRecord object is added to the ActivityStack process analysis

The ActivityRecord class is a class that contains activity-specific information and is typically used as an instance to be added to the ActivityStack stack through ActivityStack’s startActivityLocked function

/ / note: the first parameter to the incoming ActivityRecord object for the ActivityStarter. ExecuteRequest a ActivityRecord object initialization function
// The second parameter is an ActivityRecord object that contains information about the main Activity of the application Launcher
// The third argument newTask is true
// The fourth parameter keepCurTransition is false
// The fifth parameter options is an ActivityOptions initialized when the ActivityStartController object calls startHomeActivity
// a fusion of the ActivityOptions object set after the SafeActivityOptions object is created
void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity,
        boolean newTask, boolean keepCurTransition, ActivityOptions options) {
    // The Task obtained here is not null
    Task rTask = r.getTask();
    / / judgment according to the code, the options here. GetAvoidMoveToFront () is not set, so here is false, namely whole allowMoveToFront parameter to true
    // The isOrhasTask parameter returns true
    final boolean allowMoveToFront = options == null| |! options.getAvoidMoveToFront();final boolean isOrhasTask = rTask == this || hasChild(rTask);
    // mLaunchTaskBehind tasks get placed at the back of the task stack.
    if(! r.mLaunchTaskBehind && allowMoveToFront && (! isOrhasTask || newTask)) {// Last activity in task had been removed or ActivityManagerService is reusing task.
        // Insert or replace.
        // Might not even be in.
        // Set the current rTask parameter to
        positionChildAtTop(rTask);
    }
    Task task = null;
    if(! newTask && isOrhasTask) {/ /... The branch cannot run, where newTask is true, so skip
    }

    // Place a new activity at top of stack, so it is next to interact with the user.

    // If we are not placing the new activity frontmost, we do not want to deliver the
    // onUserLeaving callback to the actual frontmost activity
    final Task activityTask = r.getTask();
    if(task == activityTask && mChildren.indexOf(task) ! = (getChildCount() -1)) {
        / /... The branch cannot run, where newTask is true, so skip
    }

    task = activityTask;

    / /... The log printing code is omitted
    // Next place the above initialized ActivityRecord object at the top of the mWindowList in the current Task
    task.positionChildAtTop(r);

    // The transition animation and starting window are not needed if {@code allowMoveToFront}
    // is false, because the activity won't be visible.
    The hasActivity() function here returns true because the previous code adds ActivityRecord to the Task
    AllowMoveToFront = true
    // So here the if branch enters
    if((! isHomeOrRecentsStack() || hasActivity()) && allowMoveToFront) {/ /... The startup mode of the Activity is not described here
    }
    / /... Invalid branch code omitted
}
Copy the code

The main Task here is to store the ActivityRecord object at the TOP of the Task parameter mWindowList, which is essentially an ActivityStack object inherited from the Task class

Then start the current Activity in another way

The specific start process of the Activity

As can be seen from the above analysis, during the startup of an Activity, Mainly through RootWindowContainer resumeFocusedStacksTopActivities function add corresponding to the Task object (ActivityRecord object in ActivityStack object) to run

RootWindowContainer.java
boolean resumeFocusedStacksTopActivities( ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {

    / /... If the condition judgment is not met, the code is omitted

    boolean result = false;
    / / analysis of the code, the targetStack here. IsTopStackInDisplayArea function returns true
    if(targetStack ! =null && (targetStack.isTopStackInDisplayArea()
            || getTopDisplayFocusedStack() == targetStack)) {
        / / call the corresponding ActivityStack targetStack resumeTopActivityUncheckedLocked function object
        // Notice that both parameters are passed in from ActivityStarter. Target contains the ActivityRecord object with basic information about the Activity to be started
        // targetOptions is an ActivityOptions initialized when startHomeActivity is called from the ActivityStartController object described earlier
        // a fusion of the ActivityOptions object set after the SafeActivityOptions object is created
        result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
    }
    / /... And function temporarily not related, temporarily not analyzed, code omission
    return result;
}

ActivityStack.java
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
    / /... Condition judgment does not satisfy code omission

    boolean result = false;
    try {
        // Protect against recursion.
        mInResumeTopActivity = true;
        / / continue to call resumeTopActivityInnerLocked function
        result = resumeTopActivityInnerLocked(prev, options);

        / /...
    } finally {
        mInResumeTopActivity = false;
    }

    return result;
}

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
    / /... Condition judgment does not satisfy code omission

    // Find the next top-most activity to resume in this stack that is not finishing and is
    // focusable. If it is not focusable, we will fall into the case below to resume the
    // top activity in the next focusable task.
    ActivityRecord next = topRunningActivity(true /* focusableOnly */);

    final booleanhasRunningActivity = next ! =null;

    / /... Condition judgment does not satisfy code omission

    mRootWindowContainer.cancelInitializingActivities();

    // Remember how we'll process this pause/resume situation, and ensure
    // that the state is reset however we wind up proceeding.
    boolean userLeaving = mStackSupervisor.mUserLeaving;
    mStackSupervisor.mUserLeaving = false;

    / /... Condition judgment does not satisfy code omission

    next.delayedResume = false;
    final TaskDisplayArea taskDisplayArea = getDisplayArea();

    / /... Condition judgment does not satisfy code omission

    // The activity may be waiting for stop, but that is no longer
    // appropriate for it.
    mStackSupervisor.mStoppingActivities.remove(next);
    next.setSleeping(false);

    / /... Condition judgment does not satisfy code omission
    mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);

    ActivityRecord lastResumed = null;
    final ActivityStack lastFocusedStack = taskDisplayArea.getLastFocusedStack();
    / /... Condition judgment does not satisfy code omission

    // We are starting up the next activity, so tell the window manager
    // 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 = taskDisplayArea.mDisplayContent;
    / /... Condition judgment does not satisfy code omission

    if (anim) {
        next.applyOptionsLocked();
    } else {
        next.clearOptionsLocked();
    }

    mStackSupervisor.mNoAnimActivities.clear();

    if (next.attachedToProcess()) {
        / /... Condition judgment does not satisfy code omission
    } else {
        // Whoops, need to restart this activity!
        if(! next.hasBeenLaunched) { next.hasBeenLaunched =true;
        } else {
            if (SHOW_APP_STARTING_PREVIEW) {
                next.showStartingWindow(null /* prev */.false /* newTask */.false /* taskSwich */);
            }
            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
        }
        if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Restarting " + next);
        // This is where the startSpecificActivity function is eventually called
        mStackSupervisor.startSpecificActivity(next, true.true);
    }
    return true;
}
Copy the code

From the code above, this is where the startSpecificActivity function of the ActivityStackSupervisor object is eventually called

void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
    / /... Function independent code omitted

    final boolean isTop = andResume && r.isTopRunningActivity();
    mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
}
Copy the code

The startProcessAsync function of ATMS is called here

ATMS.java
void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
        String hostingType) {
    try {
        / /...
        // 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);
    }
    / /...
}
Copy the code

As you can see, a Message is initialized in the PooledLambda object’s obtainMessage function and processed in the ATms.h object

static <A, B, C, D, E, F, G> Message obtainMessage(
        HeptConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
                ? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
    synchronized (Message.sPoolSync) {
        PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                function, 7.0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null.null.null.null);
        returnMessage.obtain().setCallback(callback.recycleOnUse()); }}Copy the code

A PooledRunnable object is initialized, and the Message setCallback function is set to handle the Message. When the Handler calls sendMessage to send the Message, The Message is added to the MessageQueue corresponding to the Handler, and the corresponding Message is retrieved from the corresponding Looper object through an infinite loop. The dispatchMessage function of the corresponding Handler object is called to distribute the message, while the dispatchMessage function of the Handler is called

Handler.java
public void dispatchMessage(@NonNull Message msg) {
    if(msg.callback ! =null) {
        handleCallback(msg);
    } else {
        if(mCallback ! =null) {
            if (mCallback.handleMessage(msg)) {
                return; } } handleMessage(msg); }}Copy the code

That is, when Handler’s dispatchMessage is called and Message’s callback parameter is set, the Handler’s handleCallback function is called directly

private static void handleCallback(Message message) {
    message.callback.run();
}
Copy the code

That is, the callback will run and look back at the structure of the PooledRunnable class

classDiagram
PooledLambda <|-- PooledRunnable
Runnable <|-- PooledRunnable
ThrowingRunnable <|-- PooledRunnable
TraceNameSupplier <|-- PooledRunnable
<<interface>>PooledRunnable
<<interface>>PooledLambda
<<interface>>Runnable
<<interface>>ThrowingRunnable
<<interface>>TraceNameSupplier

That is to say, the PooledRunnable here is a Runnable object, therefore, call its run function, will eventually run to the generated Message is the first parameter to the incoming ActivityManagerInternal: : startProcess function index,

classDiagram
ActivityManagerService *-- LocalService
ActivityManagerInternal <|-- LocalService

Tracing an instance of the ActivityManagerInternal object mAmInternal shows that it is an ams.localService object, so

/ / here it is important to note that this function was introduced into the six parameters of track their code, can know in Message through PooledLambda. ObtainMessage initialization function
// The first argument is the function index, the second argument is the object instance that calls the first function index argument, and the third to eighth arguments are the six arguments passed in to this point
public void startProcess(String processName, ApplicationInfo info, boolean knownToBeDead,
        boolean isTop, String hostingType, ComponentName hostingName) {
    try {
        / /...
        synchronized (ActivityManagerService.this) {
            // If the process is known as top app, set a hint so when the process is
            // started, the top priority can be applied immediately to avoid cpu being
            // preempted by other processes before attaching the process of top app.
            startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */.new HostingRecord(hostingType, hostingName, isTop),
                    ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */.false /* isolated */.true /* keepIfLarge */); }}/ /...
}

final ProcessRecord startProcessLocked(String processName,
        ApplicationInfo info, boolean knownToBeDead, int intentFlags,
        HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
        boolean isolated, boolean keepIfLarge) {
    return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
            hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
            keepIfLarge, null /* ABI override */.null /* entryPoint */.null /* entryPointArgs */.null /* crashHandler */);
}

ProcessList.java
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
        boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
        int zygotePolicyFlags, boolean allowWhileBooting, boolean isolated, int isolatedUid,
        boolean keepIfLarge, String abiOverride, String entryPoint, String[] entryPointArgs,
        Runnable crashHandler) {
    long startTime = SystemClock.uptimeMillis();
    ProcessRecord app;
    if(! isolated) { app = getProcessRecordLocked(processName, info.uid, keepIfLarge); checkSlow(startTime,"startProcess: after getProcessRecord");

        if((intentFlags & Intent.FLAG_FROM_BACKGROUND) ! =0) {
            / /... Branch code omission
        } else {
            // When the user is explicitly starting a process, then clear its
            // crash count so that we won't make it bad until they see at
            // least one crash dialog again, and make the process good again
            // if it had been bad.
            / /... The log printing code is omitted
            mService.mAppErrors.resetProcessCrashTimeLocked(info);
            if (mService.mAppErrors.isBadProcess(info.processName, info.uid)) {
                / /... The log printing code is omitted
                mService.mAppErrors.clearBadProcess(info.processName, info.uid);
                if(app ! =null) {
                    app.bad = false; }}}}/ /... Branch code omission

    if (app == null) {
        checkSlow(startTime, "startProcess: creating new process record");
        // Create a ProcessRecord and add it to ProcessList
        app = newProcessRecordLocked(info, processName, isolated, isolatedUid, hostingRecord);
        / /... Branch code omission
        app.crashHandler = crashHandler;
        app.isolatedEntryPoint = entryPoint;
        app.isolatedEntryPointArgs = entryPointArgs;
        if(precedence ! =null) {
            app.mPrecedence = precedence;
            precedence.mSuccessor = app;
        }
        checkSlow(startTime, "startProcess: done creating new process record");
    }
    / /... Branch code omission

    // Call startProcessLocked to start the ProcessRecord created above
    final boolean success =
            startProcessLocked(app, hostingRecord, zygotePolicyFlags, abiOverride);
    checkSlow(startTime, "startProcess: done starting proc!");
    return success ? app : null;
}

boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
        int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
        boolean mountExtStorageFull, String abiOverride) {
    / /...

    try {
        / /...
        final String seInfo = app.info.seInfo
                + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
        // Start the process. It will either succeed and return a result containing
        // the PID of the new process, or else throw a RuntimeException.
        final String entryPoint = "android.app.ActivityThread";
        // The function startProcessLocked will eventually be called
        return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,
                runtimeFlags, zygotePolicyFlags, mountExternal, seInfo, requiredAbi,
                instructionSet, invokeWith, startTime);
    }
    / /... catch remoteexception code delete
}

boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app,
        int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
        String seInfo, String requiredAbi, String instructionSet, String invokeWith,
        long startTime) {
    app.pendingStart = true;
    app.killedByAm = false;
    app.removed = false;
    app.killed = false;
    / /...
    app.mDisabledCompatChanges = null;
    if(mPlatformCompat ! =null) {
        app.mDisabledCompatChanges = mPlatformCompat.getDisabledChanges(app.info);
    }
    final longstartSeq = app.startSeq = ++mProcStartSeqCounter; app.setStartParams(uid, hostingRecord, seInfo, startTime); app.setUsingWrapper(invokeWith ! =null|| Zygote.getWrapProperty(app.processName) ! =null);
    mPendingStarts.put(startSeq, app);

    if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
        / /...
        // Run the corresponding Runnable directly and run it
        mService.mProcStartHandler.post(() -> handleProcessStart(
                app, entryPoint, gids, runtimeFlags, zygotePolicyFlags, mountExternal,
                requiredAbi, instructionSet, invokeWith, startSeq));
        return true;
    } else {
        / /...}}private void handleProcessStart(final ProcessRecord app, final String entryPoint,
        final int[] gids, final int runtimeFlags, int zygotePolicyFlags,
        final int mountExternal, final String requiredAbi, final String instructionSet,
        final String invokeWith, final long startSeq) {
    / /...
    try {
        // startProcess, which starts the ActivityThread object
        final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
                entryPoint, app, app.startUid, gids, runtimeFlags, zygotePolicyFlags,
                mountExternal, app.seInfo, requiredAbi, instructionSet, invokeWith,
                app.startTime);

        synchronized(mService) { handleProcessStartedLocked(app, startResult, startSeq); }}/ /...
}

private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
        ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
        int mountExternal, String seInfo, String requiredAbi, String instructionSet,
        String invokeWith, long startTime) {
    try {
        / /...
        / / according to the previous code, the hostingRecord here. MHostingZygote = REGULAR_ZYGOTE
        final Process.ProcessStartResult startResult;
        if (hostingRecord.usesWebviewZygote()) {
            / /...
        } else if (hostingRecord.usesAppZygote()) {
            / /...
        } else {
            / / launch process, side entruPoint = "android. App. ActivityThread"
            startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                    app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                    app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
                    isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
                    whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
                    new String[]{PROC_START_SEQ_IDENT + app.startSeq});
        }
        checkSlow(startTime, "startProcess: returned from zygote!");
        return startResult;
    } finally{ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); }}Copy the code

Finally, fork a Process in Zygote using the start function of Process and call the corresponding function to initialize the application using the main function of the ActivityThread object and the parameters passed in. The ActivityThread object is one for each process, which of course needs to be registered in AMS

conclusion

In this article, we analyzed the startup process of the application Launcher, the initialization of the ActivityThread object, and the specific way zygote starts the ActivityThread. We will look into the code later

The main analysis of the code flow is as follows

  1. After initializing the ActivityRecord object in the ActivityStarter executeRequest function, through ActivityStack’s startActivityLocked function, Insert the ActivityRecord object into the TOP position of the mWindowList of the ActivityStack object to save
  2. Through RootWindowContainer resumeFocusedStacksTopActivities function, call to ActivityStack resumeTopActivityUncheckedLocked function, And finally calls the startSpecificActivity function of the ActivityStackSupervisor object
  3. In startSpecificActivity, the startProcessAsync function of ATMS is used to initialize the application Launcher process by calling the startProcess function of ams. LocalService. And launch the main screen of the application Launcher from the ActivityThread entry
  4. Activitythreads are entry points to application processes, and each application process contains an ActivityThread object, which is registered and managed in AMS

Flowchart for starting an application process