Android Launcher and SystemUI startup

1. Prerequisite summary

Launcher, the desktop program, is the first application process opened after system services are started. It mainly manages the display and opening of various user apps inside the phone. During the process of opening Launcher, SystemUI such as navigation bar and other layouts are also loaded one after another. to the screen.

Launcher is started after the initialization of various system services is completed, that is, at the end of SystemServer’s startOtherService() method

2. Startup process

1. SystemServer.run()

//com.android.server.SystemServer
private void run() {
    try{
        ...
        //1. Create system Context
        createSystemContext();
        ...
    }catch(..){..}
    ...
    try{
        ...
        //2. Start other services
        startOtherServices();
        ...
    }catch(..){..}
    //......
}

System services such as: AMS\ATMS, PMS, WMS, etc. are all started and initialized in the run() method of SystemServer. I will not introduce too much about the startup of system services. The main thing here is to create a system Context to prepare for subsequent startup. , and then enter the startOtherServices method

2. startOhterServices()

//com.android.server.SystemServer
private void startOtherServices() {
    final Context context = mSystemContext;
    ...
    //Initialization of various services such as wms, ims and so on
    ...
    //Prepare for startup through AMS
    mActivityManagerService.systemReady(() -> {
        ...
        try {
            //2. Start the system UI
            startSystemUi(context, windowManagerF);
        } catch (Throwable e) {
            reportWtf("starting System UI", e);
        }
        ...
    },BOOT_TIMINGS_TRACE_LOG);
}

In the startOtherServices method, after first completing the necessary system services, AMS starts preparations for launching the Launcher. The systemReady method of AMS is called to pass in a Callback, and the system UI is started in the Callback.

3. Start SystemUI

//com.android.server.SystemServer
private static void startSystemUi(Context context, WindowManagerService windowManager) {
    //Start system UI Service
    Intent intent = new Intent();
    intent.setComponent(new ComponentName("com.android.systemui",
            "com.android.systemui.SystemUIService"));
    intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
    //Slog.d(TAG, "Starting service: " + intent);
    context.startServiceAsUser(intent, UserHandle.SYSTEM);
    windowManager.onSystemUiStarted();
}

Initialize the UI by starting the Service

//com.android.systemui.SystemUIService
public class SystemUIService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        //Execute the creation method in SystemUIApplication
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();
        ...
    }
    ...
}

In the onCreate() method of Service, the startServicesIfNeeded method of the current Application is called to continue initialization.

In the previous createSystemContext() method, the Context is created and the Application object is also created. The specific instance type of the Application is obtained by parsing the corresponding AndroidManifest.xml. The xml parsed by the Application created here is in /frameworks/base/packages /SystemUI package

//frameworks/base/packages/SystemUI/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     package="com.android.systemui"
     android:sharedUserId="android.uid.systemui"
     coreApp="true">
     ...
      <application
          //Application class
          android:name=".SystemUIApplication"
          android:persistent="true"
          android:allowClearUserData="false"
          android:allowBackup="false"
          .....
      </application>
</manifest>
          

Continue execution in SystemUIApplication

//com.android.systemui.SystemUIApplication
public void startServicesIfNeeded() {
    //Get the name that needs to initialize the system UI
    String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
    startServicesIfNeeded(names);
}

private void startServicesIfNeeded(String[] services) {
    if (mServicesStarted) {
        return;
    }
    //SystemUI collection, system UI all inherit from SystemUI
    mServices = new SystemUI[services.length];
    ...
    final int N = services.length;
    for (int i = 0; i < N; i + + ) {
        String clsName = services[i];
        ...
        Class cls;
        try {
            //Instantiate each system UI through reflection
            cls = Class.forName(clsName);
            Object o = cls.newInstance();
            if (o instanceof SystemUI.Injector) {
                o = ((SystemUI.Injector) o).apply(this);
            }
            mServices[i] = (SystemUI) o;
        }......

        mServices[i].mContext = this;
        mServices[i].mComponents = mComponents;
        if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
        //Initialize system UI (4-1)
        mServices[i].start();
        ...
        if (mBootCompleted) {
            mServices[i].onBootCompleted();
        }
    }
    .......
}

Get the names of each SystemUI class that needs to be initialized from config.xml, then create a SystemUI array and traverse the collection to create instances through reflection, and finally call the SystemUI.start() method to initialize and render each SystemUI.

config_systemUIServiceComponents is defined in the /frameworks/base/packages/SystemUI/res/values/config.xml file, which includes a series of UIs such as the top status bar, notifications, keyboard, etc.

<!-- SystemUI Services: The classes of the stuff to start. -->
<string-array name="config_systemUIServiceComponents" translatable="false">
    <item>com.android.systemui.Dependency</item>
    <item>com.android.systemui.util.NotificationChannels</item>
    <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
    <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
    <item>com.android.systemui.recents.Recents</item>
    <item>com.android.systemui.volume.VolumeUI</item>
    <item>com.android.systemui.stackdivider.Divider</item>
    <item>com.android.systemui.SystemBars</item>
    <item>com.android.systemui.usb.StorageNotification</item>
    <item>com.android.systemui.power.PowerUI</item>
    <item>com.android.systemui.media.RingtonePlayer</item>
    <item>com.android.systemui.keyboard.KeyboardUI</item>
    <item>com.android.systemui.pip.PipUI</item>
    <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
    <item>@string/config_systemUIVendorServiceComponent</item>
    <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
    <item>com.android.systemui.LatencyTester</item>
    <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
    <item>com.android.systemui.ScreenDecorations</item>
    <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
    <item>com.android.systemui.SliceBroadcastRelayHandler</item>
</string-array>

Take SystemBars as an example:

//Inherited from SystemUI
public class SystemBars extends SystemUI {
    ...
    //Start() method executed
    @Override
    public void start() {
        if (DEBUG) Log.d(TAG, "start");
        //Create StatusBar
        createStatusBarFromConfig();
    }
    ...
    private void createStatusBarFromConfig() {
        if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
        //Get StatusBar class name
        //com.android.systemui.statusbar.phone.StatusBar
        final String clsName = mContext.getString(R.string.config_statusBarComponent);
        if (clsName == null || clsName.length() == 0) {
            throw andLog("No status bar component configured", null);
        }
        Class<?> cls = null;
        try {
            cls = mContext.getClassLoader().loadClass(clsName);
        } catch (Throwable t) {
            throw andLog("Error loading status bar component: " + clsName, t);
        }
        try {
            //Create StatusBar object through reflection
            mStatusBar = (SystemUI) cls.newInstance();
        } catch (Throwable t) {
            throw andLog("Error creating status bar component: " + clsName, t);
        }
        mStatusBar.mContext = mContext;
        mStatusBar.mComponents = mComponents;
        if (mStatusBar instanceof StatusBar) {
            SystemUIFactory.getInstance().getRootComponent()
                .getStatusBarInjector()
                .createStatusBar((StatusBar) mStatusBar);
        }
        //StatusBar also inherits SystemUI and continues to execute the start method.
        mStatusBar.start();
        if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
    }
    ...
}

The StatusBar object is created in SystemBar’s start(). StatusBar is also inherited from SystemUI. Execute the start() method again.

//com.android.systemui.statusbar.phone.StatusBar

//Inherit SystemUI
public class StatusBar extends SystemUI implements ...{
    ...
    @Override
    public void start() {
        ...
        //Get WMS
        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        ...
        mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
        ...
        //Create and add the view to the screen
        createAndAddWindows(result);
        ...
    }
    ...
    
    public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
        //Create StatusView
        makeStatusBarView(result);
        mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
        //Adding to Window involves WMS drawing
        mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
    }
 }

You can see that the View is actually created in the StatusBar and drawn to the screen through WMS.

4. Start Launcher

Continue back to the SystemServer code

//com.android.server.SystemServer
private void startOtherServices() {
    ...
    //Prepare for startup through AMS
    mActivityManagerService.systemReady(() -> {
        ...
        try {
            //2. Start the system UI
            startSystemUi(context, windowManagerF);
        } catch (Throwable e) {
            reportWtf("starting System UI", e);
        }
        ...
    },BOOT_TIMINGS_TRACE_LOG);
}

Enter the AMS.systemReady() method

//com.android.server.am.ActivityManagerService.java
public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
    synchronized(this) {
        if (mSystemReady) {
            // If it has been initialized once, execute the callback directly.
            if (goingCallback != null) {
                goingCallback.run();
            }
            return;
        }

        mLocalDeviceIdleController
            = LocalServices.getService(DeviceIdleController.LocalService.class);
        //Prepare to complete configuration information
        mActivityTaskManager.onSystemReady();
        mUserController.onSystemReady();
        mAppOpsService.systemReady();
        mSystemReady = true;
    }
    ...
    //Execute callback to initialize system UI and other operations
    if (goingCallback != null) goingCallback.run();
    ...
    synchronized (this) {
        ...
        //Start Launcher
        mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
        ...
    }
}

Internal operations are performed to prevent multiple initialization starts. After configuring various information, call startHomeOnAllDisplays of ActivityTaskManagerInternal to start the Launcher.

Here’s a brief look at the creation process of mAtmInternal

1. mAtmInternal is the ActivityTaskManagerInternal type and manages AtivityTaskManager

Functional interface. It is obtained in the ActivityManagerService constructor

public ActivityManagerService(Context systemContext,
        ActivityTaskManagerService atm) {
    ...
    //Get the specific implementation class through ATMI.class
    mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
    ...
}

LocalServices maintains a Map internally, which is mainly used to access some local interface services. Get the implementation class of ActivityTaskManagerInternal here.

2. ActivityTaskManagerInternal is an interface and the specific implementation class is ActivityTaskManagerService.

InternalLocalService

public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    ...
    final class LocalService extends ActivityTaskManagerInternal {
        ...
    }
    ...
}

3. The creation of LocalService is in the constructor of ATMS, and the addition to LocalServices is in the ATMS

When the service starts

public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    ...
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public ActivityTaskManagerService(Context context) {
        ...
        mInternal = new LocalService();
        ...
    }
    ...
    //ATMS start
    private void start() {
        //Add to LocalServices, key is ATMI.class
        LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
    }
}

The startup of ATMS is carried out in SystemServer, so I won’t go into details here.

Continue back to the mAtmInternal.startHomeOnAllDisplays method

final class LocalService extends ActivityTaskManagerInternal {
    ...
    @Override
    public boolean startHomeOnAllDisplays(int userId, String reason) {
        synchronized (mGlobalLock) {
            //Continue executing the RootWindowContainer method
            return mRootWindowContainer.startHomeOnAllDisplays(userId, reason);
        }
    }
    ...
}

RootWindowContainer.startHomeOnAllDisplays

class RootActivityContainer extends ConfigurationContainer
        implements DisplayManager.DisplayListener {
    ...
    boolean startHomeOnAllDisplays(int userId, String reason) {
        boolean homeStarted = false;
        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
            final int displayId = mActivityDisplays.get(i).mDisplayId;
            //1. Obtain and continue to call the startup method
            homeStarted |= startHomeOnDisplay(userId, reason, displayId);
        }
        return homeStarted;
    }
    ...
    
    boolean startHomeOnDisplay(int userId, String reason, int displayId) {
        //2,
        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.
        if (displayId == INVALID_DISPLAY) {
            final ActivityStack 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);
            result |= startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
                allowInstrumenting, fromHomeKey);
        }
        return result;
    }
    
    boolean startHomeOnTaskDisplayArea(int userId, String reason, int displayId, boolean allowInstrumenting,
        boolean fromHomeKey) {
        ...
        //Launcher's Intent
        Intent homeIntent = null;
        ActivityInfo aInfo = null;
        //3. Get Launcher’s Activity information
        if (taskDisplayArea == getDefaultTaskDisplayArea()) {
            homeIntent = mService.getHomeIntent();
            aInfo = resolveHomeActivity(userId, homeIntent);
        } else if (shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) {
            Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, taskDisplayArea);
            aInfo = info.first;
            homeIntent = info.second;
        }
        ...
        //Check whether startup is allowed
        //There are internal judgments on the system operating mode, TopAction and split screen
        if (!canStartHomeOnDisplayArea(aInfo, displayId, allowInstrumenting)) {
            return false;
        }

        //Update Launcher's startup information
        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
        // Updates the extra information of the intent.
        if (fromHomeKey) {
            homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true);
        }
        // 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) + ":" + displayId;
        //4. Start Launcher Activity
        mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
            displayId);
        return true;
    }
}

Finally, obtain the ActivityStartController object through ATMS and call startHomeActivity to start the Launcher.

The subsequent startup process is similar to the cold startup process of clicking the App application. The next blog will be published after the sorting is completed.

Three, finally

This article is compiled and published based on my own understanding of the Android 10 version source code. Many of the details are not detailed enough and require further understanding. If there are errors, please correct me. If you have a better process analysis, please discuss it together.