In the previous videos and articles, we have introduced the basic knowledge required for the entire vehicle Android application development:
- [Video transcript] Vehicle-mounted Android application development and analysis-Into the vehicle-mounted operating system-Nuggets
- [Video transcript] Vehicle Android application development and analysis - AOSP download and compilation - Nuggets
- [Video transcript] Car Android application development and analysis-Developing system applications-Nuggets
- [Video transcript] Vehicle-mounted Android application development and analysis - AIDL practice and packaging (Part 1) - Nuggets
- [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 SystemUI
has a large amount of code and a very complex content, here I will select SystemUI
modules 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:
- SystemUI "Function" and "Source Code Structure"
- The implementation principle of SystemUI "navigation bar" and "status bar",
- The implementation principle of SystemUI "Notification Bar" and "Quick Control"
- 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:
- understand what is
SystemUI
- Understand
SystemUI
what are the main functions implemented in - Understand
SystemUI
the structure of source code - Understand
SystemUI
how it is started by the system and its initialization sequence.
Introduction to SystemUI
In the Android system, SystemUI
it is a system-level APP, which provides the user interface of the system and system_server
is started by the process.
SystemUI
It 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
SystemUI
This 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
SystemUI
The 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 SystemUI
is located in the ** frameworks/base/packages/SystemUI ** directory.
SystemUI
The source code is mainly composed of Java and XML files. The Java files implement SystemUI
various functions and logic, and the XML files define SystemUI
the interface and resources. SystemUI
The source code also contains some tests, tools, documents and other auxiliary files. SystemUI
The 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 SystemUI
is located in the ** /packages/apps/Car/SystemUI ** directory, CarSystemUI
which is for SystemUI
reuse and expansion. CarSystemUI
The 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-apex
to 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
SystemUI
The startup sequence refers to SystemUI
the 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_server
the process will ActivityManagerService
start 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 SystemUI
it 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 SystemUI
the startup process. Next, we continue to see SystemUI
how to initialize.
SystemUI initialization process
SystemUI
The initialization process is divided into the following steps:
- 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 SystemUI
in the onCreate method . SystemUI
Here 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 . SystemUI
Then according to the context priority of the renderer set by the SF GPU context priority setting , the last opened SystemServer
binder 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 {
...见第四部分
}
}
ThreadedRenderer
It 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 SystemUI
process will actually be started. The user ID of the new SystemUI
process 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 SystemUIService
startup, notify SystemUI
other 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();
}
SystemUI
The 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 SystemUIApplication
the 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
- 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
SystemUIService
In 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 .SystemUI
StatusBar
NavigationBar
NotificationPanel
Keyguard
SystemUI
A Context will be provided for them, and onConfigurationChanged and onBootCompleted callbacks will be provided for them. These services are SystemUI
the 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. VendorServices
Inherited from CoreStartable
but 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 SystemUI
service also relied on Dependency
custom dependency injection provided by the class to obtain some SystemUI
objects that span the life cycle. But after Android 13, SystemUI
the 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 SystemUIService
the 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 SystemUI
the 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.