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

Today is January 31, 2022, the Lunar New Year’s Eve, I wish all programmers brothers and sisters, the year of the Tiger is auspicious, huhu shengwei, everything goes well, good luck in the coming year!!

Analysis of the application Launcher startup process after the system starts

In the system startup process, through the Zygote process fork a SystemServer process, and in this process, started the system must be a variety of core services, among which AMS is an important one. After AMS starts the SystemServer process, it will call a systemReady function to complete some operations after the system starts, including the startup process of the application Launcher, and the code is as follows:

ActivityManagerService.java
The bootingSystemUser parameter indicates whether the current login user is a system user
if (bootingSystemUser) {
    The mAtmInternal argument is obtained from the getService function of LocalServices
    / / but when ActivityTaskManagerService initialization LocalServiecs will pass the addService function to register the object
    / / this return mAtmInternal parameter is actually a ActivityTaskManagerService. LocalService object
    mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
}
Copy the code

In light of above remark parsing, here will call ActivityTaskManagerService. LocalService. StartHomeOnAllDisplays function, therefore

ActivityTaskManagerService.java
public boolean startHomeOnAllDisplays(int userId, String reason) {
    // mGlobalLock parameter lock, which is a WindowManagerGlobalLock object, is also used in other services to lock synchronization operations
    synchronized (mGlobalLock) {
        // The startHomeOnAllDisplays function of the mRootWindowContainer object is called
        // The mRootWindowContainer parameter is the object corresponding to the mRoot parameter in WindowManagerService
        The mRootWindowContainer argument is obtained by calling AMS's setWindowManager function and then ATMS.LocalServices' setWindowManager function
        // Therefore, it is important to note that the mWmService parameter of the RootWindowContainer object corresponds to the WindowManagerService object
        / / mService parameters corresponding ActivityTaskManagerService object
        returnmRootWindowContainer.startHomeOnAllDisplays(userId, reason); }}Copy the code

The RootWindowContainer class diagram is as follows:

classDiagram
WindowContainer <|-- RootWindowContainer
ActivityTaskManagerService -- RootWindowContainer : mStackSupervisor
Handler <|-- MyHandler
RootWindowContainer <-- MyHandler : mHandler
ActivityStackSupervisor <-- RootWindowContainer : mRootWindowContainer
MyHandler --* RootWindowContainer
class ActivityTaskManagerService {
    #mStackSupervisor : ActivityStackSupervisor
}
class RootWindowContainer {
    #mService : ActivityTaskManagerService
    -mHandler : Handler
    #mStackSupervisor : ActivityStackSupervisor
    +setWindowManager(WindowManagerService)
}
class WindowContainer {
    #mWmService : WindowManagerService
}
class ActivityStackSupervisor {
    #mRootWindowContainer : RootWindowContainer
}

Based on the previous code, the startHomeOnAllDisplays function, which is the RootWindowContainer object, is called

RootWindowContainer.java
UserId = userhandle. USER_SYSTEM, reason = "systemReady"
boolean startHomeOnAllDisplays(int userId, String reason) {
    boolean homeStarted = false;
    GetChildCount getChildCount getChildCount getChildCount getChildCount getChildCount getChildCount getChildCount getChildCount getChildCount getChildCount getChildCount getChildCount getChildCount getChildCount getChildCount getChildCount getChildCount
    // mChildren is a WindowList object containing data that is used when the setWindowManager function is called,
    // The number of displays obtained from DisplayManagerService,
    // The DisplayManagerService load default display device is described in this article
    for (int i = getChildCount() - 1; i >= 0; i--) {
        // Get the displayId for the new DisplayContent
        final int displayId = getChildAt(i).mDisplayId;
        // Call startHomeOnDisplay
        homeStarted |= startHomeOnDisplay(userId, reason, displayId);
    }
    return homeStarted;
}
Copy the code

As you can see from the code, the main screen of the multiple Launcher is launched for multiple display devices. Of course, in this case, we only analyze the default display device, so

RootWindowContainer.java
boolean startHomeOnDisplay(int userId, String reason, int displayId) {
    return startHomeOnDisplay(userId, reason, displayId, false /* allowInstrumenting */.false /* fromHomeKey */);
}

boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting, boolean fromHomeKey) {
    // Fallback to top focused display or default display if the displayId is invalid.
    // The displayId part obtained by DisplayManagerService is INVALID_DISPLAY, so the criteria are not met
    if (displayId == INVALID_DISPLAY) {
        finalActivityStack stack = getTopDisplayFocusedStack(); displayId = stack ! =null ? stack.getDisplayId() : DEFAULT_DISPLAY;
    }
    
    final DisplayContent display = getDisplayContent(displayId);
    boolean result = false;
    for (int tcNdx = display.getTaskDisplayAreaCount() - 1; tcNdx >= 0; --tcNdx) {
        final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tcNdx);
        / / will call startHomeOnTaskDisplayArea function
        result |= startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
                allowInstrumenting, fromHomeKey);
    }
    return result;
}

boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,
            boolean allowInstrumenting, boolean fromHomeKey) {
    / /... Function irrelevant code, omitted here
    if (taskDisplayArea == getDefaultTaskDisplayArea()) {
        // 1. Use the ATMS getHomeIntent function to obtain an Intent object
        // This Intent object
        // homeIntent.action = Intent.ACTION_MAIN
        FLAG_DEBUG_TRIAGED_MISSING // Flags for homeIntent include intent.flag_debug_triaged_missing
        // The category of homeIntent contains intent.category_HOME
        homeIntent = mService.getHomeIntent();
        // 2. Get an ActivityInfo object that contains startup information
        aInfo = resolveHomeActivity(userId, homeIntent);
    }
    / /... Function irrelevant code, omitted here

    // Updates the home component of the intent.
    homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
    homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
    / /... Condition judgment cannot be met, code omitted
    // Update the reason for ANR debugging to verify if the user activity is the one that
    // actually launched.
    final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
            aInfo.applicationInfo.uid) + ":" + taskDisplayArea.getDisplayId();
    // 3. Use the startHomeActivity function of the ActivityStartController object
    mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
            taskDisplayArea);
    return true;
}
Copy the code

As you can see from the above code, there are basically three things going on here

  1. The ATMS getHomeIntent function gets an Intent object that starts an Activity
  2. Get the ActivityInfo object for the startup screen using the resolveHomeActivity function
  3. The corresponding Activity is started using the startHomeActivity function of the ActivityStartController object

So let’s look at it one by one

Gets an Intent object that starts the Activity

Intent getHomeIntent(a) {
    // Initializes an Intent object with the action value intent.action_main
    Intent intent = newIntent(mTopAction, mTopData ! =null ? Uri.parse(mTopData) : null);
    // Set the corresponding Component, null here
    intent.setComponent(mTopComponent);
    // Add flags,
    intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
    if(mFactoryTest ! = FactoryTest.FACTORY_TEST_LOW_LEVEL) {/ / add a category
        intent.addCategory(Intent.CATEGORY_HOME);
    }
    return intent;
}
Copy the code

As you can see, the code here is relatively simple: Initialize an Intent and set its parameters

Gets the ActivityInfo object for the startup screen

RootWindowContainer.java
ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
    final int flags = ActivityManagerService.STOCK_PM_FLAGS;
    final ComponentName comp = homeIntent.getComponent(); // null
    ActivityInfo aInfo = null;
    try {
        if(comp ! =null) {
            / /... Condition not met, code omitted
        } else {
            // Get the content type of the Intent, null
            final String resolvedType =
                    homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
            / / AppGlobals getPackageManager () function returns a PackageManagerService object
            // This function iterates through all the applications in the system and retrieves the corresponding ResolveInfo object that meets the most criteria
            final ResolveInfo info = AppGlobals.getPackageManager()
                    .resolveIntent(homeIntent, resolvedType, flags, userId);
            // aInfo corresponds to ActivityInfo of ResolveInfo
            if(info ! =null) { aInfo = info.activityInfo; }}}/ /... Catch exceptions, code omitted
    aInfo = new ActivityInfo(aInfo);
    aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
    return aInfo;
}
Copy the code

The result is a corresponding ActivityInfo object with the most qualified application information

Start the corresponding Activity

/ / ATMS getActivityStartController function returns a ActivityStartController object
// This object is initialized in the ATMS initialize function
mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason, taskDisplayArea);
Copy the code

The ActivityStartController class diagram is shown below

classDiagram ActivityStarter *-- DefaultFactory ActivityStarter *-- Factory ActivityStartController <-- DefaultFactory :  mFactory ActivityStartController <-- ActivityTaskManagerService : mService ActivityStartController <-- ActivityStackSupervisor : mSupervisor ActivityStartController <-- StartHandler : mHandler Handler <|-- StartHandler Factory <|-- DefaultFactory ActivityStartController *--StartHandler ActivityStartController --> DefaultFactory : mController ActivityTaskManagerService --> DefaultFactory : mService ActivityStackSupervisor --> DefaultFactory : mSupervisor <<interface>> Factory ActivityTaskManagerService <-- ActivityStackSupervisor : mSupervisor class ActivityTaskManagerService { +getActivityStartController() }

As you can see from the class diagram, the startHomeActivity function of the ActivityStartController object is called here

void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, TaskDisplayArea taskDisplayArea) {
    final ActivityOptions options = ActivityOptions.makeBasic();
    // Set the mode to full-screen
    options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
    if(! ActivityRecord.isResolverActivity(aInfo.name)) {// The resolver activity shouldn't be put in home stack because when the foreground is
        // standard type activity, the resolver activity should be put on the top of current
        // foreground instead of bring home stack to front.
        options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
    }
    final int displayId = taskDisplayArea.getDisplayId();
    options.setLaunchDisplayId(displayId);
    options.setLaunchTaskDisplayArea(taskDisplayArea.mRemoteToken
            .toWindowContainerToken());

    // The home activity will be started later, defer resuming to avoid unneccerary operations
    // (e.g. start home recursively) when creating home stack.
    mSupervisor.beginDeferResume();
    final ActivityStack homeStack;
    try {
        // Create an ActivityStack object
        // Make sure home stack exists on display area.
        homeStack = taskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
    } finally {
        mSupervisor.endDeferResume();
    }
    The obtainStarter function initializes an ActivityStarter object and sets its Intent and reason parameters
    // When the ActivityStarter object is initialized, a Request object is initialized, and the setup functions here are used to set the parameters of the Request
    // Finally run the Request through the execute function
    mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
            .setOutActivity(tmpOutRecord)
            .setCallingUid(0)
            .setActivityInfo(aInfo)
            .setActivityOptions(options.toBundle())
            .execute();
    mLastHomeActivityStartRecord = tmpOutRecord[0];
    / /...
}

ActivityStarter.java
int execute(a) {
    try {
        / /... Feature-independent code, omitted here
        int res;
        synchronized (mService.mGlobalLock) {
            final booleanglobalConfigWillChange = mRequest.globalConfig ! =null&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) ! =0;
            final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();
            if(stack ! =null) {
                stack.mConfigWillChange = globalConfigWillChange;
            }
            / /... Log printing, function irrelevant code, omitted here
            // Call executeRequest to run the Request
            res = executeRequest(mRequest);
            / /... Feature-independent code, omitted here
            // Notify ActivityMetricsLogger that the activity has launched.
            // ActivityMetricsLogger will then wait for the windows to be drawn and populate
            // WaitResult.
            // Notifies the Activity to start
            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,
                    mLastStartActivityRecord);
            return getExternalResult(mRequest.waitResult == null? res : waitForResult(res, mLastStartActivityRecord)); }}finally {
        // Run completeonExecutionComplete(); }}Copy the code

As shown above, the executeRequest function is eventually called to continue running

ActivityStarter.java
private int executeRequest(Request request) {
    / /... Some condition check, here directly omit temporarily do not look
    final ActivityRecord r = newActivityRecord(mService, callerApp, callingPid, callingUid, callingPackage, callingFeatureId, intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, request.componentSpecified, voiceSession ! =null, mSupervisor, checkedOptions,
            sourceRecord);
    mLastStartActivityRecord = r;
    / /...
    final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();
    / /...
    mService.onStartActivitySetDidAppSwitch();
    mController.doPendingActivityLaunches(false);
    // startActivityUnchecked
    mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
            request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,
            restrictedBgActivity, intentGrants);
    / /...
    return mLastStartActivityResult;
}

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, Task inTask,
            boolean restrictedBgActivity, NeededUriGrants intentGrants) {
    / /...
    // Call startActivityInner
    result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
            startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
    / /...
    return result;
}

int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, Task inTask,
            boolean restrictedBgActivity, NeededUriGrants intentGrants) {
    / /...mTargetStack.startActivityLocked(mStartActivity, topStack ! =null ? topStack.getTopNonFinishingActivity() : null, newTask,
            mKeepCurTransition, mOptions);
    / /...
    mRootWindowContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);

    // Update the recent tasks list immediately when the activity starts
    mSupervisor.mRecentTasks.add(mStartActivity.getTask());
    mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(),
            mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetStack);

    return START_SUCCESS;
}
Copy the code

The startActivityInner function starts the application Launcher with the data contained in the ActivityInfo object. How to start the application Launcher will be discussed in the next article

conclusion

Let’s start by summarizing the process of starting the application Launcher

  1. In the SystemServer startOtherServices function, the systemReady function of ActivityManagerService is called to notify the system of the completion of startup
  2. Call the startHomeOnAllDisplays function of the ATMS service in the ams. systemReady function to start the load and startup process of the Launcher
  3. Call the RootWindowContainer object’s startHomeOnAllDisplays function, where the DisplayManagerService gets the corresponding display device ID (displayId). Then get need to display area, finally through RootWindowContainner startHomeOnTaskDisplayArea function to continue the boot process
  4. In the RootWindowContainner startHomeOnTaskDisplayArea function, through ATMS to ActivityStartController object, and call its startHomeActivity function, at the same time in this function, Apply a specific Intent attribute (Action =) to the Launcher Intent.action_main and intent.category_home) find the ActivityInfo for all the Launcher criteria in the system
  5. Corresponding ActiivtyInfo object based on the above, start the Launcher application in startHomeActivity ActivityStarterController object function

The sequence diagram corresponding to the above process is as follows: