Car Android application development and analysis - SystemUI "function" and "source code structure" analysis

In the previous videos and articles, we have introduced the basic knowledge required for the entire vehicle Android application development:

  1. [Video transcript] Vehicle-mounted Android application development and analysis-Into the vehicle-mounted operating system-Nuggets
  2. [Video transcript] Vehicle Android application development and analysis - AOSP download and compilation - Nuggets
  3. [Video transcript] Car Android application development and analysis-Developing system applications-Nuggets
  4. [Video transcript] Vehicle-mounted Android application development and analysis - AIDL practice and packaging (Part 1) - Nuggets
  5. [Video transcript] Vehicle-mounted Android application development and analysis - AIDL practice and packaging (Part 2) - Nuggets

At the beginning of this issue, we will introduce the implementation of in-vehicle applications in native Android Automotive and its principles. The first thing I want to introduce is a very important system application in vehicle application development, the UI of the Android system - SystemUI.

Since the native Android system SystemUIhas a large amount of code and a very complex content, here I will select SystemUImodules that are of reference significance for vehicle development and introduce them. There will be about 4-5 issues of content, which are mainly divided into the following modules:

  1. SystemUI "Function" and "Source Code Structure"
  2. The implementation principle of SystemUI "navigation bar" and "status bar",
  3. The implementation principle of SystemUI "Notification Bar" and "Quick Control"
  4. Implementation principle of SystemUI "Recent Tasks"

So in this issue, let’s first analyze the “function” and “source code structure” of SystemUI. You can get the following benefits from reading this issue:

  1. understand what isSystemUI
  2. Understand SystemUIwhat are the main functions implemented in
  3. Understand SystemUIthe structure of source code
  4. Understand SystemUIhow it is started by the system and its initialization sequence.

Introduction to SystemUI

In the Android system, SystemUIit is a system-level APP, which provides the user interface of the system and system_serveris started by the process.

SystemUIIt does not belong to the process itself system_server, it is an independent process. Its HMI includes status bar, navigation bar, notification bar, lock screen, recent tasks, etc.

SystemServer is a program started by the Zogyte process. It is responsible for starting and managing various core services in the Android system. For example: ActivityManagerService and PackageManagerService, these services also run in the system_server process, providing various functions and interfaces for the Android system to be called by applications and other services. The Android Framework we often say is actually composed of these Services.

SystemUI function introduction

SystemUIThis part will mainly introduce those modules that are of reference value when we customize our own .

  • Status Bar

StatusBar is responsible for displaying status information such as time, power, signals, and notifications.

  • Navigation Bar

NavigationBar displays buttons such as return, home page, and recent tasks. In car Android, we most often call it the Dock Bar (DockBar). It is generally responsible for displaying shortcut controls and entrances to commonly used functions such as car control, homepage, and Bluetooth phone calls.

  • notification bar

NotificationPanel, an interface for displaying and controlling notifications. In actual car projects, the notification bar and [Message Center] are often merged into an independent APP.

  • Quick control

QuickSettings, this panel allows users to quickly adjust some commonly used settings, such as brightness, airplane mode, Bluetooth, etc. The QS panel has multiple states, including the primary expanded panel (Quick Quick Settings, QQS) and the complete QS panel (Quick Settings, QS). Users can open or close the QS panel by pulling down the notification bar.

  • Other functions

Some system-level dialog boxes, pop-up windows, animations, screensavers, etc. These contents are relatively simple and will not be introduced again. Some functions such as lock screen and media control are not involved much in the development of in-vehicle SystemUI and will not be introduced again.

SystemUI source code structure

SystemUIThe source code location depends on the Android version and device type you are using. This video is based on the source code of Android 13 for analysis.

SystemUI source code location and structure

The source code of Android 13 SystemUIis located in the ** frameworks/base/packages/SystemUI ** directory.

SystemUIThe source code is mainly composed of Java and XML files. The Java files implement SystemUIvarious functions and logic, and the XML files define SystemUIthe interface and resources. SystemUIThe source code also contains some tests, tools, documents and other auxiliary files. SystemUIThe source code structure is as follows:

  • animation: Contains some animation-related classes and resources.
  • checks: Contains some code checking and formatting tools.
  • compose: Contains some interface components written using Jetpack Compose.
  • customization: Contains some classes and resources for customizing SystemUI.
  • docs: Contains some documentation and documentation.
  • monet: Contains some classes and resources for implementing the Material theme.
  • plugin: Contains some classes and interfaces for implementing plug-in functions.
  • plugin_core: Contains some basic classes and interfaces used to support plug-in functions.
  • res: Contains some common resource files, such as layouts, pictures, strings, etc.
  • res-keyguard: Contains some resource files for the lock screen interface.
  • res-product: Contains some resource files for a specific product or device.
  • screenshot: Contains some classes and resources for screenshot functionality.
  • scripts: Contains some script files used to compile or run SystemUI.
  • shared: Contains classes and interfaces for sharing with other applications or modules.
  • src: Contains the main source code files of SystemUI, classified according to functions or modules, such as statusbar, navigationbar, notification, keyguard, recents, etc.
  • src-debug: Contains some source code files for debugging or testing SystemUI.
  • src-release: Contains some source code files used to release or optimize SystemUI.
  • tests: Contains some source code files used to test or verify SystemUI.
  • tools: Contains some tool files for developing or analyzing SystemUI.
  • unfold: Contains some classes and resources for supporting folding screen devices.

CarSystemUI source code structure

The source code of the car SystemUIis located in the ** /packages/apps/Car/SystemUI ** directory, CarSystemUIwhich is for SystemUIreuse and expansion. CarSystemUIThe source code structure is as follows:

  • res: Contains some common resource files, such as layouts, pictures, strings, etc.

  • res-keyguard: Contains some resource files for the lock screen interface.

  • samples: Contains skinning resources for CarSystemUI, mainly using Android's RRO mechanism.

  • src: Contains the main source code files of CarSystemUI, classified according to functions or modules, such as statusbar, navigationbar, notification, keyguard, recents, etc. Some of these files are modifications or extensions to files with the same name in SystemUI, and some are new files used to implement functions or logic unique to vehicle-mounted equipment.

    • car: Contains some classes and resources used to support functions or logic unique to car equipment, such as CarSystemUIFactory, CarNavigationBarController, CarStatusBarController, etc.
    • wm: Contains some classes and resources for managing window mode and layout, such as SplitScreenController, PipController, TaskStackListenerImpl, etc.
    • wmshell: Contains some classes and resources used to provide window shell functions, such as WmShellImpl, WmShellModule, WmShellStartableModule, etc.
    • Other subdirectories and files: Except for the above three subdirectories, other subdirectories and files are basically the same or similar to those in SystemUI, except for some modifications or extensions for vehicle-mounted devices. For example, the StatusBar class does not display the battery icon on the car device, but displays the gasoline icon.
  • tests: Contains some source code files used to test or verify CarSystemUI

Modify and compile SystemUI

Executed in the root directory of the Android source code mm SystemUI, this will compile the SystemUI module and its dependencies. If you have modified other modules, such as frameworks/base, you can also execute it mm framework-minus-apexto compile the framework module.

After compilation is completed, you can use the adb command to push the new SystemUI.apk to the device and restart the SystemUI process. The specific commands are as follows:

adb root
adb remount
adb push out/target/product/emulator_x86/system_ext/priv-app/CarSystemUI/CarSystemUI.apk /system_ext/priv-app/CarSystemUI/
adb shell ps -lef | grep systemui
adb shell kill <pid>

If the simulator displays a read only prompt when executing the remount command, you need to close the simulator first and use the following command to start the simulator.

emulator -writable-system -netdelay none -netspeed full
adb root
adb remount
adb reboot // 重启模拟器

SystemUI startup sequence

SystemUIThe startup sequence refers to SystemUIthe loading and initialization process of a system application during the startup of the Android system.

SystemUI startup process

When the Android system is started, system_serverthe process will ActivityManagerServicestart a service named com.android.systemui.SystemUIServiceSystemUI . This service is the entry class and it inherits the Service class.

SystemServer source code location: /frameworks/base/services/java/com/android/server/SystemServer.java

  mActivityManagerService.systemReady(() -> {
            Slog.i(TAG, "Making services ready");
            //...
            t.traceBegin("StartSystemUI");
            try {
                startSystemUi(context, windowManagerF);
            } catch (Throwable e) {
                reportWtf("starting System UI", e);
            }
            t.traceEnd();
        }, t);

From here we can see that SystemUIit is essentially a Service, and the Component obtained through Pm is com.android.systemui/.SystemUIService . The startSystemUi code details are as follows:

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();
}

The above is SystemUIthe startup process. Next, we continue to see SystemUIhow to initialize.

SystemUI initialization process

SystemUIThe initialization process is divided into the following steps:

  1. Application initialization

SystemUIApplication source code location: /frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java

After startup, the onCreate method of Application will first be called and initialized SystemUIin the onCreate method . SystemUIHere I divide it into four parts.

  • first part
@Override
public void onCreate() {
    super.onCreate();
    Log.v(TAG, "SystemUIApplication created.");
    // TimingsTraceLog 是一个用于跟踪代码执行时间的工具类,它可以在traceview中看到。
    TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",Trace.TRACE_TAG_APP);
    log.traceBegin("DependencyInjection");
    // 此行用于设置Dagger的依赖注入,并应保持在onrecate方法的顶部。
    mInitializer = mContextAvailableCallback.onContextAvailable(this);
    mSysUIComponent = mInitializer.getSysUIComponent();
    // BootCompleteCacheImpl 是一个用于缓存 BOOT_COMPLETED 广播的实现类。
    mBootCompleteCache = mSysUIComponent.provideBootCacheImpl();
    log.traceEnd();

    // 设置主线程Looper的traceTag,这样就可以在traceview中看到主线程的消息处理情况了。
    Looper.getMainLooper().setTraceTag(Trace.TRACE_TAG_APP);
    // 设置所有服务继承的应用程序主题。请注意,仅在清单中设置应用程序主题仅适用于活动。请将其与在那里设置的主题同步。
    setTheme(R.style.Theme_SystemUI);
    ...见第二部分
}

The first part does not contain much content. It mainly involves getting some created components in SystemUI through Dagger and setting up some debugging tools.

  • the second part

First determine whether the current process belongs to the system user . SystemUIThen according to the context priority of the renderer set by the SF GPU context priority setting , the last opened SystemServerbinder calls trace tracking.

@Override
public void onCreate() {
    super.onCreate();
    ...见第一部分
    // 判断当前进程是否是系统进程。如果是系统进程,那么就注册 BOOT_COMPLETED 广播接收器。
    if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
        // 创建 BOOT_COMPLETED 广播接收器的意图过滤器。
        IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
        bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);

        // 如果SF GPU上下文优先级设置为实时,则SysUI应以高优先级运行。优先级默认为中等。
        int sfPriority = SurfaceControl.getGPUContextPriority();
        Log.i(TAG, "Found SurfaceFlinger's GPU Priority: " + sfPriority);
        if (sfPriority == ThreadedRenderer.EGL_CONTEXT_PRIORITY_REALTIME_NV) {
            Log.i(TAG, "Setting SysUI's GPU Context priority to: "+ ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
            // 设置SysUI的GPU上下文优先级为高。
            ThreadedRenderer.setContextPriority(ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
            // ThreadedRenderer可以简单理解为一个渲染器,它可以在后台线程中渲染视图层次结构。优先级越高,渲染速度越快。
        }
        // 在system_server上为源自SysUI的调用启用trace跟踪
        try {
            ActivityManager.getService().enableBinderTracing();
        } catch (RemoteException e) {
            Log.e(TAG, "Unable to enable binder tracing", e);
        }
        ...见第三部分
    } else {
        ...见第四部分
    }
}

ThreadedRendererIt can be simply understood as a renderer that renders the view hierarchy in a background thread. The higher the priority, the faster the rendering speed. For its specific function, you can refer to: A brief introduction to understanding Android hardware acceleration - Nuggets

Process.myUserHandle()You can get the user type of the current process. If you are engaged in mobile APP development, the multi-user mechanism of the Android system is rarely involved. However, because a car is a tool with shared attributes, multiple family members may use one car, so Android multi-users are more common in vehicle-mounted Android development.

When we add a new user in "System" and "Multi-User" in the system settings and switch to this new user, another SystemUIprocess will actually be started. The user ID of the new SystemUIprocess will start from U1X , the user of the original SystemUI The ID is always U0 .

Regarding multi-users of Android, you can refer to the official information: Supporting Multi-Users - Android . I will also write a separate blog later to explain the multi-user mechanism of the Android system.

  • the third part

Register to listen to the boot broadcast, and after SystemUIServicestartup, notify SystemUIother components in the system that "system startup is complete."

        // 注册 BOOT_COMPLETED 广播接收器。
        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();
                // 判断SystemUIService是否启动
                if (mServicesStarted) {
                    final int N = mServices.length;
                    for (int i = 0; i < N; i++) {
                    // 通知SystemUI中各个组件,系统启动完成。
                        mServices[i].onBootCompleted();
                    }
                }
            }
        }, bootCompletedFilter);

        // Intent.ACTION_LOCALE_CHANGED 是系统语言发生变化时发送的广播。
        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;
                    // 更新SystemUi通知通道的名称
                    NotificationChannels.createAll(context);
                }
            }
        }, localeChangedFilter);
  • fourth part

If the current user is not a system user , call the startSecondaryUserServicesIfNeeded method.

    } else {
        // 我们不需要为正在执行某些任务的子进程初始化组件。例如:截图进程等
        String processName = ActivityThread.currentProcessName();
        ApplicationInfo info = getApplicationInfo();
        if (processName != null && processName.startsWith(info.processName + ":")) {
            return;
        }
        // 对于第二个用户,boot-completed永远不会被调用,因为它已经在启动时为主SystemUI进程广播了
        // 对于需要每个用户初始化SystemUI组件的组件,我们现在为当前非系统用户启动这些组件。
        startSecondaryUserServicesIfNeeded();
    }

SystemUIThe startSecondaryUserServicesIfNeeded method also initializes the functional components through the startServicesIfNeeded method . We will analyze how to initialize it specifically later.

void startSecondaryUserServicesIfNeeded() {
    // 对startables进行排序,以便我们获得确定的顺序。
    Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(Comparator.comparing(Class::getName));
    sortedStartables.putAll(mSysUIComponent.getPerUserStartables());
    startServicesIfNeeded(sortedStartables, "StartSecondaryServices", null);
}

At this point, let’s briefly summarize SystemUIApplicationthe most important tasks. In fact, there are only two:

① A functional component that monitors the boot broadcast in the system user space and notifies it .SystemUI

② In non-system user space, functional components are directly initialized.SystemUI

  1. Start SystemUIService

After the Application completes initialization, SystemUIService will be started immediately.

SystemUIService source code location: /frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java

SystemUIServiceIn the onCreate() method, the ((SystemUIApplication) getApplication()).startServicesIfNeeded() method will be called

@Override
public void onCreate() {
    super.onCreate();
    // Start all of SystemUI
    ((SystemUIApplication) getApplication()).startServicesIfNeeded();
    ...
}

There may be a question here: Why not write the relevant logic of startServicesIfNeeded in the Service instead of writing it in the Application?

This is because when the current user is not a system user , startSecondaryUserServicesIfNeeded also needs to call the startServicesIfNeeded method to initialize the component, so simply write all the initialization logic into the Application.

public void startServicesIfNeeded() {
    // vendorComponent 是一个字符串,它的值是:com.android.systemui.VendorServices
    // com.android.systemui.VendorServices 是一个空类,它的作用是在SysUI启动时,启动一些第三方服务。
    final String vendorComponent = mInitializer.getVendorComponent(getResources());

    // 对startables进行排序,以便我们获得确定的顺序
    // TODO: make #start idempotent and require users of CoreStartable to call it.
    Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
            Comparator.comparing(Class::getName));
    sortedStartables.putAll(mSysUIComponent.getStartables());
    sortedStartables.putAll(mSysUIComponent.getPerUserStartables());
    startServicesIfNeeded(sortedStartables, "StartServices", vendorComponent);
}
  • Before Android 13

This method will use reflection to create and start a series of services based on the definitions in the configuration file config_systemUIServiceComponents or config_systemUIServiceComponentsPerUser , such as , , , etc. Each of these services extends an interface called SystemUI .SystemUIStatusBarNavigationBarNotificationPanelKeyguard

SystemUIA Context will be provided for them, and onConfigurationChanged and onBootCompleted callbacks will be provided for them. These services are SystemUIthe main components and are responsible for providing various functions and interfaces.

  • Android 13 and later

A new one is added vendorComponent, vendorComponent is a string, its value is: com.android.systemui.VendorServices. VendorServicesInherited from CoreStartablebut without any internal implementation, Google's design purpose is that it can be used to start some third-party services when SysUI is started.

Before Android 13, each SystemUIservice also relied on Dependencycustom dependency injection provided by the class to obtain some SystemUIobjects that span the life cycle. But after Android 13, SystemUIthe creation of functional components and dependency injection are automatically completed by Dagger.

private void startServicesIfNeeded(Map<Class<?>, Provider<CoreStartable>> startables,String metricsPrefix,String vendorComponent) {
    if (mServicesStarted) {
        return;
    }
    mServices = new CoreStartable[startables.size() + (vendorComponent == null ? 0 : 1)];

    if (!mBootCompleteCache.isBootComplete()) {
        // 检查BOOT_COMPLETED是否已经发送。如果是这样,我们不需要等待它。
        // see ActivityManagerService.finishBooting()
        if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
            mBootCompleteCache.setBootComplete();
            if (DEBUG) {
                Log.v(TAG, "BOOT_COMPLETED was already sent");
            }
        }
    }

    mDumpManager = mSysUIComponent.createDumpManager();

    Log.v(TAG, "Starting SystemUI services for user " + Process.myUserHandle().getIdentifier() + ".");
    TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",Trace.TRACE_TAG_APP);
    log.traceBegin(metricsPrefix);

    int i = 0;
    for (Map.Entry<Class<?>, Provider<CoreStartable>> entry : startables.entrySet()) {
        String clsName = entry.getKey().getName();
        int j = i;  // Copied to make lambda happy.
        // timeInitialization 记录初始化的时间
        timeInitialization(clsName,
                () -> mServices[j] = startStartable(clsName, entry.getValue()),
                log,
                metricsPrefix);
        i++;
    }

    if (vendorComponent != null) {
        timeInitialization(
                vendorComponent,
                () -> mServices[mServices.length - 1] =
                        startAdditionalStartable(vendorComponent),
                log,
                metricsPrefix);
    }

    for (i = 0; i < mServices.length; i++) {
        if (mBootCompleteCache.isBootComplete()) {
            mServices[i].onBootCompleted();
        }

        mDumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
    }
    mSysUIComponent.getInitController().executePostInitTasks();
    log.traceEnd();

    mServicesStarted = true;
}

About how to use Dagger in SystemUI of Android13. You can read the official documentation: frameworks/base/packages/SystemUI/docs/dagger.md

Let’s summarize SystemUIServicethe initialization process again, which can be summarized into the following four steps:

①Call the startServicesIfNeeded method in SystemUIApplication

② The startServicesIfNeeded method obtains the created functional components of SystemUI through Dagger, and sorts them according to the package name and class name.

③Call the start() method of the SystemUI functional component in sequence and record the time taken.

④When receiving the BOOT_COMPLETED broadcast or checking that the boot has been completed in the SystemProperty, the onBootCompleted() of the functional component is called in sequence to complete the initialization.SystemUI SystemUI

Summarize

In this issue, we briefly introduce SystemUIthe functions, source code structure and startup sequence of the Android system.

Recently, both videos and blog updates have been very slow. The reason is actually that I posted a dynamic description on station B. Due to layoffs, I will have to spend more time at work for quite some time.

Okay, thank you for reading, I hope it is helpful to you, and see you in the next issue.

Guess you like

Origin blog.csdn.net/linkwj/article/details/131590001