0 x00 to introduce

In addition to the notification bar, there are many components such as shortcuts, battery and other components are also displayed in SystemUI.

Common UI components include, but are not limited to, a complete list of SystemUI service components.

  • The status barStatusBar
  • The navigation barNavigationBar
  • Notification barNotificationPanel
  • Shortcut barQSPanel
  • The recent taskRecent
  • Keyboard lockKeyguard

A standard Android SystemUI looks something like this

Of course, most manufacturers will deeply customize the SystemUI style according to their own needs, for example, the SystemUI on my Huawei phone looks like this

Notification bar

Shortcut bar

0x01 Startup Process

The startup process consists of two main parts

  • inframeworkIn the startSystemUIService
  • inSystemUIServiceIn the startSystemUIVarious components required

inframeworkIn the process

SystemUI is a system Application, so it is also an APK with entry Application, but it is started by the SystemServer process. We search for this class on the source website cs.android.com/ and see the following path is the class we are looking for.

frameworks/base/services/java/com/android/server/SystemServer.java
Copy the code

Note that this article uses code from the Android-12.0.0_R4 branch

This is the entry point to the SystemServer process, which launches a number of system-related applications, including SystemUI. Find its main method

/**
 * The main entry point from zygote.
 */
public static void main(String[] args) {
    new SystemServer().run();

}
Copy the code

From the comments on this method, SystemServer is started by the Zygote process. Next, look at the run() method.

private void run(){ ... // Start services. try {t.racebegin ("StartServices"); startBootstrapServices(t); startCoreServices(t); startOtherServices(t); } catch (Throwable ex) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting system services", ex); throw ex; } finally { t.traceEnd(); // StartServices } ... // omit code}Copy the code

Here are three ways to Start the service. Let’s go straight to the startOtherServices(t) method.

Why not look at the other methods, because I’ve already looked at the other methods, and the SystemUI service is started in startOtherServices()

/** * Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized. */ private void startOtherServices(@NonNull TimingsTraceAndSlog t) { ... // omit code. T. raceBegin("StartSystemUI"); try { startSystemUi(context, windowManagerF); } catch (Throwable e) { reportWtf("starting System UI", e); } t.traceEnd(); t.traceEnd(); // startOtherServices }Copy the code

This method is also quite long, but the key method startSystemUi() is easy to find from the logs and comments

private static void startSystemUi(Context context, WindowManagerService windowManager) {
    PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
    Intent intent = new Intent();
    intent.setComponent(pm.getSystemUiServiceComponent());    
    intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);

    //Slog.d(TAG, "Starting service: " + intent);        
    context.startServiceAsUser(intent, UserHandle.SYSTEM);        
    windowManager.onSystemUiStarted();
}
Copy the code

Can see through this PackageManagerInternal. GetSystemUiServiceComponent () to obtain SystemUIService components, and then start the service by startServiceAsUser methods.

Continue searching at cs.android.com or simply click on the PackageManagerInternal class to take you to the following path

frameworks/base/services/core/java/android/content/pm/PackageManagerInternal.java
Copy the code

Can see that this is an abstract class, and getSystemUiServiceComponent is abstract methods

/**
 * @return The SystemUI service component name.
 */    
public abstract ComponentName getSystemUiServiceComponent();
Copy the code

So we look at the localServices.getService () method above, which jumps to the following directory

frameworks/base/core/java/com/android/server/LocalServices.java
Copy the code

You can see that there are two key methods getService() and addService()

/** * Returns a local service instance that implements the specified interface. * * @param type The type of service. * @return The service object. */ @SuppressWarnings("unchecked") public static <T> T getService(Class<T> type) { synchronized (sLocalServiceObjects) { return (T) sLocalServiceObjects.get(type); } } /** * Adds a service instance of the specified interface to the global registry of local services. */ public static <T> void addService(Class<T> type, T service) { synchronized (sLocalServiceObjects) { if (sLocalServiceObjects.containsKey(type)) { throw new IllegalStateException("Overriding service registration"); } sLocalServiceObjects.put(type, service); }}Copy the code

So if we go back to the PackagerManagerInternal class page and click on the class name, we’ll see the following page pop up

See the extension PackageManagerService, see if it looks like the implementation target we’re looking for, click on that target to go to the PackageManagerService class.

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
Copy the code

You can see there is a PackageManagerInternalImpl internal class extends the PackageManagerInternal, find our target method

private class PackageManagerInternalImpl extends PackageManagerInternal { ... / / omitted code @ Override public the ComponentName getSystemUiServiceComponent () {return ComponentName.unflattenFromString(mContext.getResources().getString(com.android.internal.R.string.config_systemUIService Component)); } // omit code}Copy the code

Can see ComonentName from an internal resource file for the com. Android. Internal. R.s. Tring config_systemUIServiceComponent this internal can be searched in the following path

frameworks/base/core/res/res/values/config.xml
Copy the code

Specifically defined as

<! -- SystemUi service component --> <string name="config_systemUIServiceComponent" translatable="false">com.android.systemui/com.android.systemui.SystemUIService</string>Copy the code

The SystemUIService is defined in the SystemUI application, so the rest of the process moves to the SystemUI application

The subtotal

SystemServer in the framework of the run method to start the system needs a variety of services, including SystemUIService. Obtain the SystemUIService configuration name from PackageManagerInternal and start the service using startServiceAsUser(). The export attribute of the service should be true because the service is defined in the upper SystemUI application layer. The next step is to test the conjecture.

Processes in SystemUI

SystemUI in the source path is

frameworks/base/packages/SystemUI/
Copy the code

First open the manifest file to see SystemUIService configuration

<application android:name=".SystemUIApplication" android:persistent="true" ... android:directBootAware="true" tools:replace="android:appComponentFactory" android:appComponentFactory=".SystemUIAppComponentFactory"> ... // omit code <! -- Broadcast receiver that gets the broadcast at boot time and starts up everything else. TODO: Should have an android:permission attribute --> <service android:name="SystemUIService" android:exported="true" /> ...Copy the code

So here we have three things

  • SystemUIThe entrance toSystemUIApplication
  • SystemUIpersistentApplication, even if it happenscrashThe system will still pull up the application
  • That’s what we did in the last videoSystemUIServiceexportedattribute

SystemUIApplication

The code is in the following path

frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
Copy the code

Look at the onCreate method

@Override public void onCreate() { super.onCreate(); Log.v(TAG, "SystemUIApplication created."); // This line is used to setup Dagger's dependency injection and should be kept at the // top of this method. TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",Trace.TRACE_TAG_APP); log.traceBegin("DependencyInjection"); mContextAvailableCallback.onContextAvailable(this); mRootComponent = SystemUIFactory.getInstance().getRootComponent(); mSysUIComponent = SystemUIFactory.getInstance().getSysUIComponent(); mComponentHelper = mSysUIComponent.getContextComponentHelper(); mBootCompleteCache = mSysUIComponent.provideBootCacheImpl(); log.traceEnd(); // Set the application theme that is inherited by all services. Note that setting the // application theme in the manifest does only work for activities. Keep this in sync with // the theme set there. setTheme(R.style.Theme_SystemUI);  If (process.myUserHandle ().equals(userhandle.system)) { For example, IntentFilter bootCompletedFilter = new IntentFilter(intent.action_boot_completed); bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); // If SF GPU context priority is set to realtime, then SysUI should run at high. // The priority is defaulted at medium. int sfPriority = SurfaceControl.getGPUContextPriority(); Log.i(TAG, "Found SurfaceFlinger's GPU Priority: " + sfPriority); if (sfPriority == ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_REALTIME_NV) { Log.i(TAG, "Setting SysUI's GPU Context priority to: "+ ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG); ThreadedRendererCompat.setContextPriority(ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG); } registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (mBootCompleteCache.isBootComplete()) return; if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received"); unregisterReceiver(this); mBootCompleteCache.setBootComplete(); if (mServicesStarted) { final int N = mServices.length; for (int i = 0; i < N; i++) { mServices[i].onBootCompleted(); } } } }, bootCompletedFilter); IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) { if (! mBootCompleteCache.isBootComplete()) return; // Update names of SystemUi notification channels NotificationChannels.createAll(context); } } }, localeChangedFilter); } else { // We don't need to startServices for sub-process that is doing some tasks. // (screenshots, sweetsweetdesserts or tuner ..) String processName = ActivityThread.currentProcessName(); ApplicationInfo info = getApplicationInfo(); if (processName ! = null && processName.startsWith(info.processName + ":")) { return; } // For a secondary user, boot-completed will never be called because it has already // been broadcasted on startup for the primary SystemUI process. Instead, for // components which require the SystemUI component to be initialized per-user, we // start those components now for the current non-system user. startSecondaryUserServicesIfNeeded(); }}Copy the code

The onCreate method does some initialization and then there’s a call to process.myUserHandle ().equals(userhandle.system) so there are two branches

  • If it’s started by the system it’s going to go into this branch and register a listenerboot completedWhen fully started, each component is notifiedonBootCompleted
  • If the system is not started, for example, in the case of multi-user login, this time the system has actually started, it will goelseBranch into thestartSecondaryUserServicesIfNeeded()Used to startSystemUIRequired service components. This branch starts the corresponding service based on the user.

Note that the Service components here are not the Services of the big Four; they are plain Java classes that handle the various Service logic.

/** * Ensures that all the Secondary user SystemUI services are running. If they are already * running, this is a no-op. This is needed to conditionally start all the services, as we only * need to have it in the main process. * <p>This method must only be called from the main thread.</p> */ void  startSecondaryUserServicesIfNeeded() { String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponentsPerUser(getResources()); startServicesIfNeeded(/* metricsPrefix= */ "StartSecondaryServices", names); }Copy the code

In this method, the service component name is obtained through SystemUIFactory, and startServiceIfNeeded() is executed to start it

/**
 * Returns the list of system UI components that should be started per user.
 */    
 public String[] getSystemUIServiceComponentsPerUser(Resources resources) {
     return resources.getStringArray(R.array.config_systemUIServiceComponentsPerUser);
}
Copy the code

The config_systemUIServiceComponentsPerUser can guess this is based on user defined in the name to start the component list, now here is a component

frameworks/base/packages/SystemUI/res/values/config.xml
Copy the code
<! -- SystemUI Services (per user): The classes of the stuff to start for each user. This is a subset of the config_systemUIServiceComponents --> <string-array name="config_systemUIServiceComponentsPerUser" translatable="false"> <item>com.android.systemui.util.NotificationChannels</item> </string-array>Copy the code

StartServicesIfNeeded () is then entered, which is constructed by reflection into an ARRAY of mServices and executes the start() method for each component. Each component is now started.

private void startServicesIfNeeded(String metricsPrefix, String[] services) { if (mServicesStarted) { return; } mServices = new SystemUI[services.length]; . Final int N = services.length; for (int i = 0; i < N; i++) { String clsName = services[i]; if (DEBUG) Log.d(TAG, "loading: " + clsName); log.traceBegin(metricsPrefix + clsName); long ti = System.currentTimeMillis(); try { SystemUI obj = mComponentHelper.resolveSystemUI(clsName); if (obj == null) { Constructor constructor = Class.forName(clsName).getConstructor(Context.class); obj = (SystemUI) constructor.newInstance(this); } mServices[i] = obj; } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException ex) { throw new RuntimeException(ex); } if (DEBUG) Log.d(TAG, "running: " + mServices[i]); mServices[i].start(); log.traceEnd(); // Warn if initialization of component takes too long ti = System.currentTimeMillis() - ti; if (ti > 1000) { Log.w(TAG, "Initialization of " + clsName + " took " + ti + " ms"); } if (mBootCompleteCache.isBootComplete()) { mServices[i].onBootCompleted(); } dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]); } mSysUIComponent.getInitController().executePostInitTasks(); log.traceEnd(); mServicesStarted = true; }Copy the code

The subtotal

The SystemUIApplication performs some initialization work in onCreate. If it is a service started by the system, it registers a notification such as boot Completed. After the system is started, the notification is sent to each component. If it is in a multi-user environment, from the configuration file to a com. The android. Systemui. Util. NotificationChannels components, and then through reflection after build mServices array and start it. Next, SystemUIService

This is the real Android “service”

SystemUIService

It can be found in the following path

frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java
Copy the code

The class logic is simple, just one thing: call startServicesIfNeeded() in SystemUIApplication. Note that this is an overloaded method with no arguments

@Override public void onCreate() { super.onCreate(); // Start all of SystemUI ((SystemUIApplication) getApplication()).startServicesIfNeeded(); // Finish initializing dump logic mLogBufferFreezer.attach(mBroadcastDispatcher); // If configured, set up a battery notification if (getResources().getBoolean(R.bool.config_showNotificationForUnknownBatteryState)) { mBatteryStateNotifier.startListening(); }... // omit code}Copy the code

Jump to SystemUIApplication again

/**
 * Makes sure that all the SystemUI services are running. If they are already running, this is a
 * no-op. This is needed to conditinally start all the services, as we only need to have it in
 * the main process.
 * <p>This method must only be called from the main thread.</p>
 */
public void startServicesIfNeeded() {
    String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
    startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);

}
Copy the code

Similarly, you can find all the SystemUI component definitions in the following path

frameworks/base/packages/SystemUI/res/values/config.xml
Copy the code

This is all the SystemUI components currently used by the Android-12.0.0_R4 branch, where the class information of each component is defined

<! -- SystemUI Services: The classes of the stuff to start. --> <string-array name="config_systemUIServiceComponents" translatable="false"> <item>com.android.systemui.util.NotificationChannels</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.statusbar.phone.StatusBar</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.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.biometrics.AuthController</item> <item>com.android.systemui.SliceBroadcastRelayHandler</item> <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item> <item>com.android.systemui.theme.ThemeOverlayController</item> <item>com.android.systemui.accessibility.WindowMagnification</item> <item>com.android.systemui.accessibility.SystemActions</item> <item>com.android.systemui.toast.ToastUI</item> <item>com.android.systemui.wmshell.WMShell</item> </string-array>Copy the code

This is eventually executed in the startServicesIfNeeded(names) method with parameters, which we have seen above, where components are constructed by reflection and stored in an mServices array. MServices are defined in SystemUIApplication, which holds all started components.

/**
 * Hold a reference on the stuff we start.
 */    
private SystemUI[] mServices;
Copy the code

Another message you can see here is that all components are implementations of the SystemUI class. Due to space constraints, the analysis of each component will be explained in a later article. SystemUIApplication is the entry to SystemUI. It does some initialization in the onCreate method, registering to listen for notifications, etc. In the case of multiple users, a component NotificationChannels is started; Then enter SystemUIService, which also executes the startServicesIfNeeded() method of SystemUIApplication in the onCreate method, And put all the services in the mServices array.

0 x02 summary

SystemUI is a persistent application that is started by the operating system

  • AndroidThe system is created after startupSystemServerProcess, which will start various services required by the system, includingSystemUIService.
  • SystemUIServiceAfter startup, enter the application layerSystemUI, in theSystemUIApplicationIt first initializes the listenerboot completedAfter the system starts, each component is notifiedonBootCompleted.
  • When enteringSystemUIServiceIs still executed inSystemUIApplicationIn thestartServicesIfNeeded()The no-parameter method starts allSystemUIComponent in.
  • The final service startup logic is inSystemUIApplicationInside, and all stored inmServicesIn the array.

0 x03 reference

  • Read the source code online at cs.android.com