Android 12 source code analysis - Application layer 2 (General organization and startup process of SystemUI)

Android 12 source code analysis - Application layer 2 (General organization and startup process of SystemUI)

In the previous article, we introduced how SystemUI uses IDE for editing and debugging. This is the most basic basis for analyzing SystemUI. I hope readers can master it as much as possible.

This article will introduce the general organizational structure of SystemUI and its startup process. After reading this article, you will know:

  1. Why SystemUI chooses to use Dagger2
  2. How to create a new module in SystemUI
  3. SystemUI startup process

Before reading, please think about the following questions with me:

  1. What functions does SystemUI need to complete?
  2. Do the various functions need to communicate with each other?
  3. If communication needs to occur between functions, how to organize the reference relationships between them?
  4. Does each function need to communicate with system services?
  5. If each function needs to communicate with system services, how to efficiently organize the references between them?
  6. Are there dependencies between various functions?
  7. Are there any order requirements for starting various functions?

In response to the above problems 1, 2, and 3, we can see from the interface that SystemUI includes: lock screen, status bar, navigation bar, Toast display, volume adjustment and other functions. These functions may reference each other. For example, the status bar module needs to know the situation of the lock screen module to decide whether to hide some status bar icons.

These functions will more or less interact with the system. For example, the lock screen module needs to know the status of the power button to decide whether to display or hide the lock screen; another example is whether each functional module should display night mode, etc.

From here we already know that each module needs to refer to each other and communicate with the system. To this end, it is necessary to design a good architecture so that each module can easily obtain the desired objects, and the creation of these objects may also require Depends on the creation of other objects. In fact, the dependencies between the various objects of SystemUI are relatively complex. If you create each object manually, you need to write a lot of code. To do this, we use a new component management method: DI (dependency injection)

The core idea of ​​dependency injection is: when writing code, developers no longer explicitly edit how each object should be created, but the dependency injection library decides how to create the object. SystemUI chose Dagger2 as its dependency injection library.

Note: What needs to be explained here is dependency? Dependency means that a component A needs to use another component B in the process of completing its functions, which is called A's dependency on B. A and B can be classes, objects, modules, etc. So what is injection? Injection is the process of providing dependencies to components.

Now we have designed how to get the desired objects between each module - Dagger dependency injection.

Now think about how to communicate between SystemUI and the system? This involves how to divide SystemUI. As the basic UI composition of the entire system, SystemUI occupies a very important position in the entire Android system. In order to adapt to more diverse scenarios such as Android TV and Android Car, they may have different SystemUI, and at the same time to meet the modular design. Therefore, SystemUI is designed as an apk that resides in memory.

Now that SystemUI has been designed as an apk, it runs in another process alone. To communicate with the system, it only interacts through the four major components of Android and uses Android's Binder to interact. Then SystemUI becomes a collection of various Services, Activity, BroadcastReceiver, and ContentProvider. Then enable these components when appropriate.

Combined with the previous thinking, each functional module, such as lock screen, status bar, and navigation bar, can be displayed in the correct place as long as it is placed in the appropriate component. At the same time, in order to facilitate access to other modules, we also use Dagger for assistance.

Everything seems good, but is it really good? Did you miss an important question? Is there any order among these modules? In order to quickly enter the system, currently SystemUI does not make this requirement. If a certain module needs to wait for another module to be ready before it can work normally, then the design logic needs to be adjusted.

Check out the components of SystemUI

From the above, we know that SystemUI is put into four major components as a whole, so let's check AndroidManifest.xml to see which components are defined.

On the android-12.0.0_r34 branch, AndroidManifest.xml has 38 Activities, 11 Services, 4 providers, and 11 receivers

Next, I will give a brief description of each component, and in subsequent chapters, we will introduce in detail how these components are started and what functions they complete.

Activities

  1. LongScreenShotActivity: The view used for long screenshots. This Activity is called up when the user decides to take a long screenshot.
  2. ScreenRecordDialog: The option box view that pops up when recording the screen.
  3. TunerActivity: This is a fine-tuning interface for developers. You can use the following command to open the interface entrance
adb shell pm enable com.android.systemui/com.android.systemui.tuner.TunerActivity

Then enter the interface in Settings->System->System UI Tuner

  1. DemoMode: The Demo mode of SystemUI is also used by developers. It is a functional supplement to TunerActivity and can be turned on in the developer options.
  2. ForceReSizableInfoActivity: Pop-up applications cannot run in split-screen mode or secondary screens
  3. UsbPermissionActivity: Confirm USB permission pop-up box
  4. UsbResolverActivity: Select an application pop-up box for the USB device
  5. UsbConfirmActivity: Pops up a view to determine whether to use an app, which is the subsequent view of UsbResolverActivity
  6. SensorUseStartedActivity: When the sensor is in privacy mode, the pop-up box when you want to use the sensor
  7. TvUnblockSensorActivity: Same as SensorUseStartedActivity, except this is the view used on the TV
  8. UsbAccessoryUriActivity: A box pops up allowing you to download the application corresponding to this USB device.
  9. UsbContaminantActivity: A box pops up, indicating that USB has been deactivated. The reason for deactivation may be that there is stolen goods at the USB port, etc.
  10. UsbDebuggingActivity: Pops up whether to allow USB debugging
  11. UsbDebuggingActivityAlias: This is the alias of UsbDebuggingActivity
  12. WifiDebuggingActivity: Pops up whether to allow wireless debugging on the network
  13. WifiDebuggingActivityAlias: is the alias of WifiDebuggingActivity
  14. WifiDebuggingSecondaryUserActivity: The currently logged-in user cannot enable the wireless debugging function, and needs to switch to the primary user
  15. NetworkOverLimitActivity: Pop-up data traffic has reached the upper limit
  16. MediaProjectionPermissionActivity: Multimedia projection permission confirmation
  17. TvNotificationPanelActivity: dedicated to TV, pops up a message box
  18. SlicePermissionActivity: Slice permission bullet box
  19. DessertCase:One of the easter eggs
  20. MLandActivity: easter egg game
  21. PeopleSpaceActivity: Prompt the location of Pepole Space UI, new features in android 11
  22. LaunchConversationActivity: When the session is clicked, expand the view, new features in Android 11
  23. WorkLockActivity: Unlock the work profile interface
  24. CreateUserActivity: Create user view
  25. Somnambulator: Screensaver
  26. BrightnessDialog: Brightness pop-up box
  27. ForegroundServicesDialog: A pop-up box that displays foreground services
  28. ChooserActivity: Pops up a box to let the user choose which application to open to handle the current Intent
  29. ControlsProviderSelectorActivity pops up "Select the application to add a controller"
  30. ControlsEditingActivity: Edit controller, drag and drop to edit
  31. ControlsFavoritingActivity:Controller, preferences
  32. ControlsActivity: List device controllers
  33. WalletActivity: electronic wallet
  34. ControlsRequestDialog:control request to add a device controller pop-up box

Note: The Controls here are controllers of external devices, such as controllers in whole-house intelligence.

The above is just a very simple overview, and some common component logic and UI details will appear in subsequent articles.

Seeing this, some readers may ask: The above activities do not seem to have a status bar and lock screen. Are their views not in these activities?

To explore this issue, we need to look at the remaining components first.

Services

  1. SystemUIService: Wow, what a refreshing name, this Service contains most of the internal functions of SystemUI, and it is also the top priority of our SystemUI source code analysis.
  2. SystemUISecondaryUserService: In the case of multiple users, this service ensures that the SystemUI functions of multiple users are normal
  3. SystemUIAuxiliaryDumpService: for development and use, dump the information of each necessary component and view it
  4. TakeScreenshotService: Screen capture related services
  5. RecordingService: Screen recording related services
  6. ImageWallpaper: Wallpaper related services
  7. PeopleBackupFollowUpJob: People service ui-related services
  8. DessertCaseDream:Little easter egg
  9. KeyguardService: lock screen related services
  10. AuxiliaryPersistenceWrapper$DeletionJobService: Services related to external device controllers
  11. DozeService: Services related to Doze

ContentProvider篇

  1. FileProvider: Provide files
  2. KeyguardSliceProvider: Provide lock screen Slice
  3. ClockOptionsProvider: Provides a clock preview for the selector program
  4. PeopleProvider: Returns a People Tile preview for the given shortcut

BroadcastReceiver篇

  1. ScreenshotServiceErrorReceiver: Screenshot failed broadcast receiver
  2. SysuiRestartReceiver: Restart SystemUI broadcast receiver
  3. ActionProxyReceiver: A broadcast receiver that intercepts share and edit intents to facilitate processing of some things in advance.
  4. DeleteScreenshotReceiver: Delete the screenshot broadcast receiver
  5. SmartActionsReceiver: After the user clicks the smart action in the notification, it is used to receive the corresponding broadcast and execute the smart action.
  6. ControlsRequestReciver: Broadcast receiver that receives requests to add controllers
  7. TunerService$ClearReciver: clear broadcast receiver used to call TunerService
  8. KeyboardShortcutsReceiver: Broadcast receiver that shows or hides keyboard shortcuts
  9. MediaOutputDialogReceiver: Broadcast receiver that receives media output Intent
  10. PeopleSpaceWidgetPinnedReceiver: This receiver is called when a contact Tile widget is added
  11. PeopleSpaceWidgetProvider: Implementation of the PeopleSpace widget

After reading to this point, readers still have questions - where are the lock screen and status bar of SystemUI displayed? Can the UI be displayed in the Service? It's obviously unreasonable. So how are Android's lock screen and status bar displayed?

Tip: In addition to the components listed above to display views, SystemUI also displays views by interacting directly with WindowManager. What~~ Do you sigh, the design architecture of android is really a bit confusing.

Not shown here, will be explained in detail later. Next, we need to deal with the aforementioned Dagger2 and how it handles the reference relationships of various components in SystemUI.

SystemUI internal component design

We want Dagger2 to manage the dependencies of each component, so we must tell Dagger2 what dependencies are there, and what method should we use? Is it described by xml file? Or use another way?

Dagger2 uses java annotations to describe the dependencies between them. At the same time, in order to improve performance, Dagger2 will generate different java objects based on annotations during compilation, and then arrange all dependencies and life cycles in the generated java objects.

The annotations used to express various dependencies are called drawing a graph for Dagger2. Next, we will combine the examples in SystemUI to see how SystemUI draws a graph for Dagger2.

Application of Dagger2 in SystemUI

In our idea, we need a top-most object such as RootManager. Then we can obtain each object we need based on this RootManager.

In SystemUI, there is still such a RootManager. It is: GlobalRootComponent. For each module of SystemUI, if you want to get the object you want, you can get it through GlobalRootComponent.

Note: Readers, when reading this, will definitely be very confused as to why it is called Component instead of Manager. After all, Manager is so common in Android. This is because SystemUI uses Dagger2's abstraction. In Dagger2, Component represents a component. In fact, it is a container that contains all the dependencies it can provide. Therefore, GlobalRootComponent is a component that can be provided to all dependencies.

So let's take a look at how to draw a picture for GlobalRootComponent.

//@Singeton:告诉Dagger2所有带有@singtone注解的对象,生命周期一致。此处表示全局唯一
//@Component(xxx):告诉Dagger2,定义了一个Component组件
//modules={xxx}:告诉Dagger2,这个组件依赖这些模块。关于模块的概念,见后文
@Singleton
@Component(modules = {
    
    
        GlobalModule.class,
        SysUISubcomponentModule.class,
        WMModule.class})
//此处是interface接口定义,Dagger2会生成对应的实现类,并按照我们给Dagger2的注解图,管理好
//各个对象的依赖和创建
public interface GlobalRootComponent {
    
    

    //@Component.Builder:
    //告诉Dagger2,这个是创建GlobalRootComponent的Builder类
    //请务必要思考:为什么此处的对象创建要用Builder模式,而不是工厂模式?
    @Component.Builder
    interface Builder {
    
    
        @BindsInstance
        Builder context(Context context);

        GlobalRootComponent build();
    }

    //提供一个方法,这个方法的返回类型,就是这个组件可以提供的依赖,此处表示可以提供
    //WMComponent.Builder对象
    WMComponent.Builder getWMComponentBuilder();

    //表示此组件可以提供,SysUIComponent.Builder对象
    SysUIComponent.Builder getSysUIComponent();

    //注:上面两个方法提供的返回类型可以看到,他们依然是一个Component

    //表示可以提供ThreadFactory对象
    ThreadFactory createThreadFactory();
}

In the above example, we mentioned the concept of module. Before introducing this concept, let's think about a question: What if there are many dependencies in GlobalRootComponent? If each one is drawn on the diagram, it will look messy, so dagger2 provides a function to logically divide these different dependencies using modules. Then you only need to specify the following when drawing the Component:

@Component(modules={
    
    xxx.class})

We select SysUISubComponentModule to view, the source code is as follows:

//@Module:告诉Dagger2,这个module内的所有依赖,逻辑划分为:SysUISubcomponentModule
//subcomponents = {SysUIComponent.class}:告诉Dagger2,这个模块含有SysUIComponent子组件
//关于子组件的概念,我们下文介绍
@Module(subcomponents = {
    
    SysUIComponent.class})
public abstract class SysUISubcomponentModule {
    
    
}

In the above example, we mentioned subcomponent. Before introducing this concept, let’s think about another question: If a Component provides an object to other users, should the provided object be created every time or only once? Woolen cloth? This involves the life cycle of objects. In order to better manage the life cycle, we recommend that objects that belong to the same life cycle be placed in a subcomponent. Therefore, all objects in the above SysUIComponent subcomponent will belong to the same life cycle. Of course, subcomponent can not only isolate the life cycle, but also isolate the module to make the code clearer.

So let's see how this subcomponent is informed to Dagger2.

//@SysUISingleton:告诉Dagger2所有SysUISingleton注解的对象生命周期相同
//@Subcomponent:告诉Dagger2,这是一个子组件
//modules={xxx}:告诉dagger2,这个子组件有这么些module的需要依赖
@SysUISingleton
@Subcomponent(modules = {
    
    
        DefaultComponentBinder.class,
        DependencyProvider.class,
        SystemUIBinder.class,
        SystemUIModule.class,
        SystemUIDefaultModule.class})
public interface SysUIComponent {
    
    

    //告诉Dagger2生命周期
    @SysUISingleton
    //告诉Dagger2这个子组件的Builder接口定义
    @Subcomponent.Builder
    interface Builder {
    
    
        //省略若干相同部分

        //@BindsInstance:告诉Dagger2,将t绑定到这个Builder对象中
        //在Dagger2根据我们画的图,会根据这个Builder接口,生成一个SysUIComponentBuilder对象
        //在这个对象中,会有一个成员,类型为Optional<TaskSurfaceHelper>名字为setTaskSurfaceHelper.
        //然后这个setTaskSurfaceHelper()接口函数的实现,就会将参数传入的t保存在setTaskSurfaceHelper成员中。
        //这个过程就叫做:绑定实例,也即@BindsInstance的语义
        @BindsInstance
        Builder setTaskSurfaceHelper(Optional<TaskSurfaceHelper> t);

        //任何一个Builder接口,都必须有一个build()函数,且返回类型为需要构建的对象类型,此处即为SysUIComponent
        SysUIComponent build();
    }

    //定义了一个默认方法,这个方法什么也没有做
    default void init() {
    
    
        // Do nothing
    }

    //告诉Dagger2它的生命周期
    @SysUISingleton
    //subcomponent和component一样,如果想要对外提供依赖,就可以定义任何一个函数,函数的返回类型就是
    //被提供对象的类型。
    BootCompleteCacheImpl provideBootCacheImpl();


    //省略若干相同部分

    //当返回类型为空,而传入类型不为空的时候,表示需要向传入类型对象(SystemUIAppComponentFactory)
    //中被@inject标记的成员赋值,叫做注入
    //理论上,函数名为任意值,但是此种函数,几乎只会完成注入的功能,因此此函数最后都叫做inject
    void inject(SystemUIAppComponentFactory factory);

    //省略若干相同部分
}

In the above inject function, we can see what SystemUIAppComponentFactory looks like. The source code is as follows:

public class SystemUIAppComponentFactory extends AppComponentFactory {
    
    

    //@Inject:告诉Dagger2,这个成员,需要Dagger2的注入。
    //可是Dagger2又是如何知道,该怎么创建ContextComponentHelper的呢?
    //这就是我们给Dagger2画图的作用,我们已经提前画好图给Dagger2,告诉它应该
    //怎么创建这个ContextComponentHelper
    @Inject
    public ContextComponentHelper mComponentHelper;

}

Next, let’s take a look at how we draw pictures for ContextComponentHelper. The source code is as follows:

public interface ContextComponentHelper {
    
    
    //省略若干无用部分
}

From the above source code, we can know that it does not have any annotations, that is, it is not drawn in the Dagger2 diagram. There are other classes drawn in the Dagger2 diagram. The implementation class of ContextComponentHelper is: ContextComponentResolver. The source code is as follows:

//@SysUISingleton:告诉Dagger2它的生命周期
@SysUISingleton
public class ContextComponentResolver implements ContextComponentHelper {
    
    
    private final Map<Class<?>, Provider<Activity>> mActivityCreators;
    private final Map<Class<?>, Provider<Service>> mServiceCreators;
    private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
    private final Map<Class<?>, Provider<RecentsImplementation>> mRecentsCreators;
    private final Map<Class<?>, Provider<BroadcastReceiver>> mBroadcastReceiverCreators;

    //@Inject:此处就是告诉Dagger图,注入Dagger2的各种辅助功能,帮助创建这个对象
    //在创建对象的时候,需要它的各种参数,而这些参数又应该怎么被Dagger2提供呢?
    //只要我们把需要的参数,画好图给Dagger2即可,过程就和这个ContextComponentResolver一样啦,
    //在构造器上面标注一下@Inject就可以了
    @Inject
    ContextComponentResolver(Map<Class<?>, Provider<Activity>> activityCreators,
            Map<Class<?>, Provider<Service>> serviceCreators,
            Map<Class<?>, Provider<SystemUI>> systemUICreators,
            Map<Class<?>, Provider<RecentsImplementation>> recentsCreators,
            Map<Class<?>, Provider<BroadcastReceiver>> broadcastReceiverCreators) {
    
    
        mActivityCreators = activityCreators;
        mServiceCreators = serviceCreators;
        mSystemUICreators = systemUICreators;
        mRecentsCreators = recentsCreators;
        mBroadcastReceiverCreators = broadcastReceiverCreators;
    }

    //省略若干无用部分
}

Seeing this, we already know roughly how to use Dagger2 (how to provide a picture to it). But if you think about it carefully, there is still a problem - if some classes are not created by us, then we can't add @Inject to the constructor, so how can we let Dagger2 know: when it needs this object, How should it be created? At this point Dagger2 provides another annotation @Provides.

We take GlobalModule as an example to illustrate. The source code is as follows:

//@Module:告诉Dagger2,定义一个逻辑模块,这个模块包含FrameworkServicesModule,GlobalConcurrencyModule
@Module(includes = {
    
    
        FrameworkServicesModule.class,
        GlobalConcurrencyModule.class})
public class GlobalModule {
    
    

    //@Provides:告诉Dagger2,如果需要DisplayMetrics对象,就调用provideDisplayMetrics()函数即可
    //至于这个函数需要的参数Context,该怎么创建,Dagger2已经能够从我们给它的图中自动找到了
    @Provides
    public DisplayMetrics provideDisplayMetrics(Context context) {
    
    
        DisplayMetrics displayMetrics = new DisplayMetrics();
        context.getDisplay().getMetrics(displayMetrics);
        return displayMetrics;
    }

}

So far, we have basically introduced how to draw pictures in Dagger2 (that is, how to use annotations), such as how to use @Component, @SubComponent, @Inject, @Provides, @Module, etc. There are other annotations in Dagger2 that have not been introduced, but this That's enough for the rest of this article. For other annotations, you can directly refer to the Dagger2 documentation. This article only focuses on the analysis of SystemUI.

But the above is just a drawing, and does not mention how to use it. Next, we will combine the startup process of SystemUI to see how to use the Dagger2 picture drawn above.

SystemUI startup process

The startup of any Apk starts from its four major components, and before the four major components start, it will check whether there is a custom Application, and if so, it will first create the Application.

Starting from Android 9, an AppComponentFactory has been added to perform corresponding operations before creating the four major components. Like Application, it is configured in AndroidManifest.xml, as follows:

<application
        android:name=".SystemUIApplication"
        .
        .
        .
        android:appComponentFactory=".SystemUIAppComponentFactory">
        <!--省略若干不相干话题-->
</application>

From this configuration we can see the following startup process:

SystemUIAppComponentFactory->SystemUIApplication->a component to be started (four major Android components).

Before further analyzing this process, let’s first take a look at who started SystemUI

The starting point of system_server

But some readers may ask: Who started SystemUI? You see that other apps are started by clicking on the icon. Who starts SystemUI?

Correct answer: SystemUI is started by a process called sytstem_server in the Android system. system_server is started at boot time, and then system_server starts various key services, including starting SystemUI. This will be explained in detail when system_server is analyzed later.

Only a brief description of how system_server starts SystemUI is given here:

  1. After the system starts the system_server process, it will execute
new SystemServer().run();
  1. In the run() method, various services will be started, including:
  • Start boot service
  • core services
  • other service
  1. When starting other services, SystemUI will be started (through Intent). To get the specific components that start SystemUI,
    you can get it through Android's PackageManager.

  2. The PacakgeManager obtains the specific component name by reading the configuration config_systemUIServiceComponent. In the Android system, this configuration
    is

<string name="config_systemUIServiceComponent" translatable="false"
            >com.android.systemui/com.android.systemui.SystemUIService</string>

It can be seen that this is exactly the component we defined in SystemUI.

Then we can summarize the startup process of SystemUI

  1. The Android system startup is complete, start system_server
  2. system_server, according to the configuration, starts SystemUI components through Intent
  3. Before SystemUI starts the component, it will first create the SystemUIAppComponentFactory object and then call its corresponding method.
  4. Next, SystemUI will create SystemUIApplication and then call its corresponding method
  5. Finally, SystemUI will create SystemUIService and call the corresponding method. Before creating SystemUIService, it will call the corresponding method of the SystemUIAppComponentFactory object created in step 3.

Why use SystemUIAppComponentFactory

The source code of SystemUIAppComponentFactory is as follows:

public class SystemUIAppComponentFactory extends AppComponentFactory {
    
    

    private static final String TAG = "AppComponentFactory";
    @Inject
    public ContextComponentHelper mComponentHelper;

    public SystemUIAppComponentFactory() {
    
    
        super();
    }

    @NonNull
    @Override
    //在创建Application之前,这个函数被调用
    public Application instantiateApplicationCompat(
            @NonNull ClassLoader cl, @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    
    
        //调用父类方法,创建Application,此处会创建AndroidManifest.xml中配置的类
        //也即SystemUIApplication
        Application app = super.instantiateApplicationCompat(cl, className);
        //倘若创建的组件是ContextInitializer,则注册一个回调
        //请一定注意:虽然此处创建了Application,但是它还不能当做Context来使用
        if (app instanceof ContextInitializer) {
    
    
            ((ContextInitializer) app).setContextAvailableCallback(
                    context -> {
    
    
                        //1.在回调中,首先创建SystemUIFactory对象
                        SystemUIFactory.createFromConfig(context);
                        //2.通过这个SystemUIFactory得到SysUIComponent
                        //3.注入SystemUIAppComponentFactory中的成员,见上一小节
                        SystemUIFactory.getInstance().getSysUIComponent().inject(
                                SystemUIAppComponentFactory.this);
                    }
            );
        }

        return app;
    }

    @NonNull
    @Override
    //ContentProvider被创建之前,该函数被回调
    public ContentProvider instantiateProviderCompat(
            @NonNull ClassLoader cl, @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    
    
        //省略若干
        //此处没有列出内容,原因是:它的逻辑和上一个函数一样
    }

    @NonNull
    @Override
    public Activity instantiateActivityCompat(@NonNull ClassLoader cl, @NonNull String className,
            @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    
    
        //省略若干
        //此处没有列出内容,原因是:它的逻辑和上一个函数一样
    }

    @NonNull
    @Override
    public Service instantiateServiceCompat(
            @NonNull ClassLoader cl, @NonNull String className, Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    
    
        //判断是否为空,如果是,则再次注入
        //第一次注入,在instantiateApplicationCompat()函数设置的回调中,
        //这个回调由SystemUIApplication的onCreate()触发
        if (mComponentHelper == null) {
    
    
            // This shouldn't happen, but does when a device is freshly formatted.
            // Bug filed against framework to take a look: http://b/141008541
            SystemUIFactory.getInstance().getSysUIComponent().inject(
                    SystemUIAppComponentFactory.this);
        }
        //注意:这里的Service的创建
        //1. 先查询mComponentHelper中是否有对应的Service
        //2. 如果有则直接用,如果没有则调用父类方法创建
        //对于SystemUIService而言,它的构造函数有@Inject注解,因此当调用mComponentHelper.resolveService时,能够正确返回SystemUIService
        //请思考:为什么这个不要系统自己创建?
        //答案:因为SystemUIService,需要有其他依赖对象,若是由系统创建,那么必然会有
        //像SystemUIService.setXXX()之类的函数,会增加代码和逻辑。如果由Dagger2来创建则不会有
        //这些烦恼
        Service service = mComponentHelper.resolveService(className);
        if (service != null) {
    
    
            return service;
        }
        return super.instantiateServiceCompat(cl, className, intent);
        
    }

    @NonNull
    @Override
    public BroadcastReceiver instantiateReceiverCompat(@NonNull ClassLoader cl,
            @NonNull String className, @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    
    
        //省略若干
        //此处没有列出内容,原因是:它的逻辑和上一个函数一样
    }

}

In this class, the most important functions are three points:

  1. Creation of corresponding components, such as SystemUIApplication, SystemUIService, etc.
  2. Initialization of mComponentHelper, that is, through injection
  3. Before the Service component is created, it may first query whether there is already a component in mComponentHelper, and if so, use it directly.

Before looking at the creation of SystemUIApplication and SystemUIService, we still have to think about a question again:
Why use the SystemUIAppComponentFactory class? Is this class really reasonable? Is there a better alternative?

I think the answer lies in the creation of SystemUIService. It is the use of the SystemUIAppComponentFactory class that allows us to better inject dependencies

Before entering SystemUIService, the first thing created is SystemUIApplication. Let's see what it does.

SystemUIApplication

The source code is as follows:

public class SystemUIApplication extends Application implements
        SystemUIAppComponentFactory.ContextInitializer {
    
    

    public SystemUIApplication() {
    
    
        super();
        Log.v(TAG, "SystemUIApplication constructed.");
        // SysUI may be building without protolog preprocessing in some cases
        ProtoLog.REQUIRE_PROTOLOGTOOL = false;
    }

    @Override
    public void onCreate() {
    
    
        super.onCreate();
        Log.v(TAG, "SystemUIApplication created.");
        //用于跟踪启动和关闭的时序数据
        TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
                Trace.TRACE_TAG_APP);
        log.traceBegin("DependencyInjection");
        //这就是初始化各种Dagger2依赖的地方,这个回调在SystemUIAppComponentFactory中被设置
        mContextAvailableCallback.onContextAvailable(this);
        //有了Dagger2,就是直接使用对应的组件
        mRootComponent = SystemUIFactory.getInstance().getRootComponent();
        mSysUIComponent = SystemUIFactory.getInstance().getSysUIComponent();
        mComponentHelper = mSysUIComponent.getContextComponentHelper();
        mBootCompleteCache = mSysUIComponent.provideBootCacheImpl();
        log.traceEnd();

        //设置主题
        setTheme(R.style.Theme_SystemUI);

        //判断是否为主进程
        if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
    
    
            //监听系统的启动广播
            IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
            bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);

            //设置线程渲染优先级
            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) {
    
    
                    //mBootCompleteCache表示的是:是否SytemUI的各种服务启动完成
                    //这些服务的启动,可能早于系统启动完成广播,也可能晚于系统启动完成广播

                    //1. 如果SystemUI的各种服务已经启动完成则直接返回
                    if (mBootCompleteCache.isBootComplete()) return;

                    if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
                    //2. 如果没有启动完成,则挨个启动
                    unregisterReceiver(this);
                    mBootCompleteCache.setBootComplete();
                    if (mServicesStarted) {
    
    
                        final int N = mServices.length;
                        for (int i = 0; i < N; i++) {
    
    
                            mServices[i].onBootCompleted();
                        }
                    }
                }
            }, bootCompletedFilter);

            //监听是否Local改变
            //如果Local改变,则通知中的显示就需要改变,如中英文切换等
            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 {
    
    
            //如果是子进程则会进入此部分逻辑

            //如果是主用户下的子进程,则什么也不做,直接返回
            String processName = ActivityThread.currentProcessName();
            ApplicationInfo info = getApplicationInfo();
            if (processName != null && processName.startsWith(info.processName + ":")) {
    
    
                return;
            }
            //如果不是主用户,则需要去启动必要的SystemUI组件
            startSecondaryUserServicesIfNeeded();
        }
    }

    //省略若干,简单代码

}

The code of SystemUIApplication is relatively simple and has been marked in comments. Next look at SystemUIService

SystemUIService

The source code is as follows:

public class SystemUIService extends Service {
    
    

    //省略若干,简单代码

    //@Inject:嘿嘿,这就是给Dagger画的图,好让Dagger2知道怎么创建SystemUIService
    @Inject
    public SystemUIService(
            @Main Handler mainHandler,
            DumpHandler dumpHandler,
            BroadcastDispatcher broadcastDispatcher,
            LogBufferFreezer logBufferFreezer,
            BatteryStateNotifier batteryStateNotifier) {
    
    
        //省略赋值代码
    }

    @Override
    public void onCreate() {
    
    
        super.onCreate();

        //对没错,startServicesIfNeeded作为整个SystemUI关键服务的启动源头,就在这里了。
        //在进入分析之前,先思考:为什么要放在这里执行呢?就不能直接放在SystemUIApplication中吗?
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();

        //LogBufferFreezer接收bugreport开始的广播,然后停止对应的LogBuffer的记录
        mLogBufferFreezer.attach(mBroadcastDispatcher);

        //是否监听电池的状态,并且会提示在通知栏上
        if (getResources().getBoolean(R.bool.config_showNotificationForUnknownBatteryState)) {
    
    
            mBatteryStateNotifier.startListening();
        }

        //调试代码,用debug.crash_sysui触发RescueParty
        if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {
    
    
            throw new RuntimeException();
        }

        //Binder调试相关
        //如果太多binder调用就触发onLimitReached回调
        if (Build.IS_DEBUGGABLE) {
    
    
            //设置Binder代理计数开
            BinderInternal.nSetBinderProxyCountEnabled(true);
            //配置Binder代理触发BinderProxyLimitListener回调的最高和最低阈值,最低表示:只有降到最低以下,才能再次触发
            BinderInternal.nSetBinderProxyCountWatermarks(1000,900);
            //设置BinderProxyLimitListener监听
            BinderInternal.setBinderProxyCountCallback(
                    new BinderInternal.BinderProxyLimitListener() {
    
    
                        @Override
                        public void onLimitReached(int uid) {
    
    
                            Slog.w(SystemUIApplication.TAG,
                                    "uid " + uid + " sent too many Binder proxies to uid "
                                    + Process.myUid());
                        }
                    }, mMainHandler);
        }

        //启动DumpService,如果系统运行bugreport,SystemUIAuxiliaryDumpService会将SystemUI中的一些关键数据dump出来
        startServiceAsUser(
                new Intent(getApplicationContext(), SystemUIAuxiliaryDumpService.class),
                UserHandle.SYSTEM);
    }

    //省略若干,简单代码
}

Next, let’s look at the specific content of the startServicesIfNeeded() function

startServicesIfNeeded() function

This function is located in the SystemUIApplication class, and the source code is as follows:

public void startServicesIfNeeded() {
    
    
    //1. 获取需要start的服务列表
    //2. 然后调用startServicesIfNeed()继续启动
    //注意:看到这里,其实大家应该大胆假设,getSystemUIServiceComponents函数
    //是不是通过Dagger2的依赖得到的。如果不是为什么?
    String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
    startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
}

private void startServicesIfNeeded(String metricsPrefix, String[] services) {
    
    
    //省略判断

    mServices = new SystemUI[services.length];

    //启动完成缓存对象的修改,简单,略
    
    //首先获取DumpManager
    final DumpManager dumpManager = mSysUIComponent.createDumpManager();

    //trace跟踪点,略

    //挨个启动服务
    // 1. 首先查看mComponentHelper是否有缓存,如果有则直接使用
    // 2. 如果没有则反射创建
    // 3. 创建完成调用start()
    // 4. 判断是否系统启动完成,如果完成则调用onBootCompleted()
    // 5. 将启动的服务,加入DumpManager中,以便bugreport触发其dump
    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;
}

As can be seen from the above, the main function of SystemUIService is to call the start() and onBootCompleted() functions of each service. Complete startup.

Now please think about why this part of the content should be placed in SystemUIApplication, but can't it be placed directly in SystemUIService?

Before answering this question, let's first take a look at how the getSystemUIServiceComponents() function gets the services that need to be started.

The getSystemUIServiceComponents function obtains the service to be started

The source code is as follows:

public String[] getSystemUIServiceComponents(Resources resources) {
    
    
    return resources.getStringArray(R.array.config_systemUIServiceComponents);
}

The above function is obtained through the configured string array. You may be curious - why is it different from Dagger2? Why configure a string array for such a convenient thing?

Why is it configured this way? I think there are several main reasons:

  1. When compiling AOSP code, you can use the override function to place these resource files in a custom directory for modification purposes.
  2. For historical reasons, it was configured like this before, haha

Now let's think about why the functional logic of starting the service cannot be placed in SystemUIservice but in SystemUIApplication.
If you want to put it in SystemUIApplication, why not start it through SystemUIApplication, but use SystemUIService to start it.

  1. In addition to SystemUIService starting services, in the case of multiple users, some services also need to be started. At this time, SystemUI application

    SystemUIApplication is called first without calling SystemUIService. Because the triggering of SystemUIService is started by system_server.

    In addition, the startup logic of the monitoring system needs to be processed in a unified manner. It becomes natural to put the startup logic into SystemUIApplication.

Note: Obviously this leads to a bug. If SystemUI reports an error midway and stops running, when it runs again, can the various
services started by SystemUIService be initialized correctly? Obviously not, this often happens when I am writing this article.

  1. Since the startup logic has been put into SystemUIApplication, can't SystemUIApplication start this part of the service?

    Why should a separate SystemUIService be used as the entry point to start? To answer this question, you need to know that Android applications are started by starting a component to be run. That is, if system_server wants to run SystemUI, it must start a certain component, not just Application.

    Therefore, we need a component for startup, which is SystemUI. It is responsible for starting each specific service. In addition to monitoring the boot broadcast and cooperating with multiple users, this part of the content is completed in SystemUIApplication.

    And because SystemUIService requires dependency injection, SystemUIAppComponentFactory was created to implement the corresponding dependency injection.

So far, we have fully figured out the startup process of SystemUIService, SystemUIApplication, SystemUIAppComponentFactory
and why they allocate functions in this way. The summary is as follows.

  1. After system_server starts, it starts to start various services
  2. When starting other services, you will first get the name of the component to start systemui through PackageManager, and then start the systemui component according to the name.
  3. The name obtained in the above step is SystemUIService.
  4. When SystemUIService is started, SystemUIApplication will be created first, and SystemUIAppComponentFactory will be called to add corresponding dependency injection before creation.
  5. After SystemUIApplication is created, it will listen to the system's startup broadcast.
  6. Then create SystemUIService. Before creating it, the corresponding method of SystemUIAppComponentFactory will be called to add dependency injection.
  7. After creating SystemUIService, start various services through SystemUIApplication

At this point, the entire SystemUI startup is completed.

Add a custom service to SystemUI

With the previous analysis, we now need to test it: write a custom service module. In this module, we only print out its startup process.

  1. Create a new class called PrintLogService. This class can inherit SystemUI. as follows
public class PrintLogService extends SystemUI{
    
    

    private String TAG = "PrintLogService";

    //使用@Inject标记,让Dagger2自动管理依赖
    @Inject
    public PrintLogService(Context context) {
    
    
        super(context);
    }

    //简单打印log
    @Override
    public void start() {
    
    
        Slog.d(TAG, "Start PrintLogService");
    }
    //简单打印log
    @Override
    protected void onBootCompleted() {
    
    
        Slog.d(TAG,"PrintLogService boot completed");
    }

}
  1. Put the name of this class into the configuration array, config_systemUIServiceComponents, as follows:
<!-- 最后一行加入我们自定义的服务 -->
    <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>
        <item>com.android.systemui.PrintLogService</item>
    </string-array>

  1. Use the following command to compile and push it to the mobile phone
mmm frameworks/base/packages/SystemUI
adb root
adb remount
adb shell rm -rf system_ext/priv-app/SystemUI
adb push out/**/system_ext/priv-app/SystemUI /system_ext/priv-app/

Then use kill to kill the existing SystemUI process.

  1. From the log we can see the following output

Insert image description here

This means that our customized service started successfully! ! !

This article is finished! !

In this article, we only focus on mContextAvailableCallback.onContextAvailable(this);
this is about the initialization of various Components in Dagger2. In the next article, we will start from this function, explore the initialization of various Components in SystemUI, and understand SystemUI How each component should be used.

Guess you like

Origin blog.csdn.net/xiaowanbiao123/article/details/132409010