SystemUI
是系统应用,其实就是 Android
系统上常见下拉面板,除了通知栏还有很多组件例如快捷键、电量等组件等也是在 SystemUI
中展示。常见 UI
组件有(包含但不限于,完整列表可以查看 SystemUI
服务组件列表[1])
•状态栏 StatusBar
•导航栏 NavigationBar
•通知栏 NotificationPanel
•快捷按键栏 QSPanel
•最近任务 Recent
•键盘锁 Keyguard
标准 Android
系统中 SystemUI
大概是长这样
当然大多数厂商会根据自身需求对 SystemUI
的样式进行深度定制,例如在我华为手机中的 SystemUI
长这样
通知栏
快捷键栏
0x01 启动流程
启动流程主要包括两个部分
•在 framework
中启动 SystemUIService
•在 SystemUIService
中启动 SystemUI
所需的各种组件
framework
中的流程SystemUI
是系统应用,所以它也是一个 APK
,有入口 Application
,只不过它是由 SystemServer
进程进行启动的。
我们在源码官网上 https://cs.android.com/ 搜索这个类,看到以下这个路径就是我们要找的类。
frameworks/base/services/java/com/android/server/SystemServer.java
注意本文使用的是 android-12.0.0_r4 分支中的代码
这个就是 SystemServer
进程的入口,它会启动很多系统相关的应用,其中就包括 SystemUI。找到它的 main 方法
/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();
}
从这个方法的注释来看,SystemServer
是由 zygote
进程启动的。接下来看run()
方法。
private void run(){
...
// 省略代码
// Start services.
try {
t.traceBegin("StartServices");
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
} catch (Throwable ex) {
...// 省略d代码
throw ex;
} finally {
t.traceEnd(); // StartServices
}
...
// 省略代码
}
这个方法很长,根据注释找到 Start Services
的方法,这里有三个启动服务的方法,我们直接进入到 startOtherServices(t)
方法。
为何不看其它方法,因为其它方法我已经看过来,SystemUI 服务的启动就是在 startOtherServices() 里面
/**
* Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized.
*/
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
// 省略代码
t.traceBegin("StartSystemUI");
try {
startSystemUi(context, windowManagerF);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
t.traceEnd();
t.traceEnd(); // startOtherServices
}
这个方法也很长,不过根据日志和注释也很容易找到关键方法 startSystemUi()
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();
}
可以看到这里通过 PackageManagerInternal.getSystemUiServiceComponent()
获取到 SystemUIService
组件,然后通过 startServiceAsUser
方法启动了服务。
继续在 https://cs.android.com 中搜索或者直接点击 PackageManagerInternal
类,可以进入到以下路径
frameworks/base/services/core/java/android/content/pm/PackageManagerInternal.java
可以看到这是一个抽象类,而且 getSystemUiServiceComponent
也是抽象方法
/**
* @return The SystemUI service component name.
*/
public abstract ComponentName getSystemUiServiceComponent();
所以我们要看上面 LocalServices.getService()
方法,点击该方法会跳转到以下目录
frameworks/base/core/java/com/android/server/LocalServices.java
可以看到有两个关键方法 getService()
和 addService()
/**
* Returns a local service instance that implements the specified interface.
*
* @param type The type of service.
* @return The service object.
*/
@SuppressWarnings("unchecked")
public static <T> T getService(Class<T> type) {
synchronized (sLocalServiceObjects) {
return (T) sLocalServiceObjects.get(type);
}
}
/**
* Adds a service instance of the specified interface to the global registry of local services.
*/
public static <T> void addService(Class<T> type, T service){
synchronized (sLocalServiceObjects) {
if (sLocalServiceObjects.containsKey(type)) {
throw new IllegalStateException("Overriding service registration");
}
sLocalServiceObjects.put(type, service);
}
}
看到这里我们知道这个 PackagerManagerInternal
是从其它地方设置进来的,所以我们回到 PackagerManagerInternal
这个类页面并点击这个类名称,会看到以下页面弹出
看到扩展者 PackageManagerService
,看情况像是我们要找的实现目标,点击该目标进入到 PackageManagerService
这个类。
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
可以看到里面有一个 PackageManagerInternalImpl
的内部类扩展了 PackageManagerInternal
,在其中找到我们的目标方法
private class PackageManagerInternalImpl extends PackageManagerInternal {
...
// 省略代码
@Override
public ComponentName getSystemUiServiceComponent() {
return ComponentName.unflattenFromString(mContext.getResources().getString(com.android.internal.R.string.config_systemUIServiceComponent));
}
// 省略代码
}
可以看到 ComonentName
是从一个内部资源文件中获取到的 com.android.internal.R.string.config_systemUIServiceComponent
这个内部可以在以下路径中搜索到
frameworks/base/core/res/res/values/config.xml
具体定义为
<!-- SystemUi service component -->
<string name="config_systemUIServiceComponent" translatable="false">com.android.systemui/com.android.systemui.SystemUIService</string>
这个 SystemUIService
是在 SystemUI
应用中定义的,所以接下来的流程将转到 SystemUI 应用中来
framework
中的 SystemServer
中 run
方法启动了系统所需要的各种服务,其中就包括 SystemUIService
。
具体是通过 PackageManagerInternal
获取到 SystemUIService
的配置名称,使用 startServiceAsUser()
来启动。
在这里可以判断由于 SystemUIService
是定义在上层 SystemUI
应用层的,所以这个 service
的 export
属性应该为 true
。接下来的流程我们可以验证这个猜想。
SystemUI
在源码中路径为
frameworks/base/packages/SystemUI/
首先打开 manifest
文件看下 SystemUIService
的配置
<application
android:name=".SystemUIApplication"
android:persistent="true"
...
android:directBootAware="true"
tools:replace="android:appComponentFactory"
android:appComponentFactory=".SystemUIAppComponentFactory">
...
// 省略代码
<!-- Broadcast receiver that gets the broadcast at boot time and starts
up everything else.
TODO: Should have an android:permission attribute
-->
<service android:name="SystemUIService"
android:exported="true"
/>
...
这里我们知道了三个信息
•SystemUI
的入口为 SystemUIApplication
•SystemUI
是 persistent
应用,即使发生了 crash
系统依然会拉起这个应用•验证了上一节中 SystemUIService
的 exported
属性
代码在以下路径
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
查看 onCreate
方法
@Override
public void onCreate() {
super.onCreate();
Log.v(TAG, "SystemUIApplication created.");
// This line is used to setup Dagger's dependency injection and should be kept at the
// top of this method.
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",Trace.TRACE_TAG_APP);
log.traceBegin("DependencyInjection");
mContextAvailableCallback.onContextAvailable(this);
mRootComponent = SystemUIFactory.getInstance().getRootComponent();
mSysUIComponent = SystemUIFactory.getInstance().getSysUIComponent();
mComponentHelper = mSysUIComponent.getContextComponentHelper();
mBootCompleteCache = mSysUIComponent.provideBootCacheImpl();
... // 省略代码
if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
// 一般情况下走这里来,例如开关机启动系统
IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
... // 省略代码
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mBootCompleteCache.isBootComplete()) return;
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
unregisterReceiver(this);
mBootCompleteCache.setBootComplete();
if (mServicesStarted) {
final int N = mServices.length;
for (int i = 0; i < N; i++) {
mServices[i].onBootCompleted();
}
}
}
}, bootCompletedFilter);
IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
if (!mBootCompleteCache.isBootComplete()) return;
// Update names of SystemUi notification channels
NotificationChannels.createAll(context);
}
}
}, localeChangedFilter);
} else {
// We don't need to startServices for sub-process that is doing some tasks.
// (screenshots, sweetsweetdesserts or tuner ..)
// 多用户使用走这里
... // 省略代码
startSecondaryUserServicesIfNeeded();
}
}
onCreate
方法会做一些初始化操作,然后会有一个判断 Process.myUserHandle().equals(UserHandle.SYSTEM)
所以这里会出现两个分支
•如果是系统启动的就会进入到这个分支中,注册监听 boot completed
的通知,最后启动完全启动后就会通知各个组件 onBootCompleted
•如果不是系统启动,例如多用户登录使用的情况,这时候系统其实已经启动过了,就会走 else
的分支进入到 startSecondaryUserServicesIfNeeded()
用于启动 SystemUI
所需的服务组件,这个分支是根据用户来启动相应的服务的。
注意这里的服务组件并不是四大组件中的 Service ,它们是普通的 Java 类,用来处理各种服务逻辑的。
/**
* Ensures that all the Secondary user SystemUI services are running. If they are already
* running, this is a no-op. This is needed to conditionally start all the services, as we only
* need to have it in the main process.
* <p>This method must only be called from the main thread.</p>
*/
void startSecondaryUserServicesIfNeeded() {
String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponentsPerUser(getResources());
startServicesIfNeeded(/* metricsPrefix= */ "StartSecondaryServices", names);
}
在此方法中通过 SystemUIFactory
获取到服务组件名称,然后执行 startServiceIfNeeded()
进行启动
/**
* Returns the list of system UI components that should be started per user.
*/
public String[] getSystemUIServiceComponentsPerUser(Resources resources) {
return resources.getStringArray(R.array.config_systemUIServiceComponentsPerUser);
}
其中 config_systemUIServiceComponentsPerUser
定义在 看名字可以猜测这个是根据用户来启动的组件列表,目前这里就一个组件
frameworks/base/packages/SystemUI/res/values/config.xml
<!-- SystemUI Services (per user): The classes of the stuff to start for each user. This is a subset of the config_systemUIServiceComponents -->
<string-array name="config_systemUIServiceComponentsPerUser" translatable="false">
<item>com.android.systemui.util.NotificationChannels</item>
</string-array>
然后就进入到 startServicesIfNeeded()
,这里通过反射进行构造后存在一个 mServices
数组里面,并且执行了各个组件的 start()
方法,至此各个组件就完成了启动工作。
private void startServicesIfNeeded(String metricsPrefix, String[] services) {
if (mServicesStarted) {
return;
}
mServices = new SystemUI[services.length];
...
// 省略代码
final int N = services.length;
for (int i = 0; i < N; i++) {
String clsName = services[i];
if (DEBUG) Log.d(TAG, "loading: " + clsName);
log.traceBegin(metricsPrefix + clsName);
long ti = System.currentTimeMillis();
try {
SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
if (obj == null) {
Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
obj = (SystemUI) constructor.newInstance(this);
}
mServices[i] = obj;
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException
| InvocationTargetException ex) {
throw new RuntimeException(ex);
}
...
// 省略代码
mServices[i].start();
log.traceEnd();
...
// 省略代码
if (mBootCompleteCache.isBootComplete()) {
mServices[i].onBootCompleted();
}
...
// 省略代码
mServicesStarted = true;
}
SystemUIApplication
在 onCreate
中做一些初始化工作,如果是系统启动的服务,则进行 boot completed
等通知的注册,然后系统启动完成后,通知到各个组件;如果是在多用户环境下,从配置文件中获取到一个 com.android.systemui.util.NotificationChannels
组件,然后通过反射构建之后存在 mServices
数组中并启动它。
接下来看 SystemUIService
这个才是真正
Android
中的“服务”
可以在以下路径找到
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java
这个类逻辑很简单,就一件事:执行 SystemUIApplication
中的 startServicesIfNeeded()
注意这个是一个没有参数的重载方法
@Override
public void onCreate() {
super.onCreate();
// Start all of SystemUI
((SystemUIApplication) getApplication()).startServicesIfNeeded();
// Finish initializing dump logic
mLogBufferFreezer.attach(mBroadcastDispatcher);
// If configured, set up a battery notification
if (getResources().getBoolean(R.bool.config_showNotificationForUnknownBatteryState)) {
mBatteryStateNotifier.startListening();
}
...
// 省略代码
}
再次跳转到 SystemUIApplication
中
/**
* Makes sure that all the SystemUI services are running. If they are already running, this is a
* no-op. This is needed to conditinally start all the services, as we only need to have it in
* the main process.
* <p>This method must only be called from the main thread.</p>
*/
public void startServicesIfNeeded() {
String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
}
同样地,在以下路径可以找到所有的 SystemUI
组件定义
frameworks/base/packages/SystemUI/res/values/config.xml
这个是目前 android-12.0.0_r4 分支用到的所有 SystemUI
组件,这里定义了各个组件的类信息
<!-- SystemUI Services: The classes of the stuff to start. -->
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.keyguard.KeyguardViewMediator</item>
<item>com.android.systemui.recents.Recents</item>
<item>com.android.systemui.volume.VolumeUI</item>
<item>com.android.systemui.statusbar.phone.StatusBar</item>
<item>com.android.systemui.usb.StorageNotification</item>
<item>com.android.systemui.power.PowerUI</item>
<item>com.android.systemui.media.RingtonePlayer</item>
<item>com.android.systemui.keyboard.KeyboardUI</item>
<item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
<item>@string/config_systemUIVendorServiceComponent</item>
<item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
<item>com.android.systemui.LatencyTester</item>
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.ScreenDecorations</item>
<item>com.android.systemui.biometrics.AuthController</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
<item>com.android.systemui.theme.ThemeOverlayController</item>
<item>com.android.systemui.accessibility.WindowMagnification</item>
<item>com.android.systemui.accessibility.SystemActions</item>
<item>com.android.systemui.toast.ToastUI</item>
<item>com.android.systemui.wmshell.WMShell</item>
</string-array>
最终会执行到 startServicesIfNeeded(names)
带参数的方法里面,这个方法在上面已经看过,就是通过反射将各个组件构造后存在 mServices
数组里面。 mServices
是在 SystemUIApplication
中定义的,它保存了所有启动的组件。
/**
* Hold a reference on the stuff we start.
*/
private SystemUI[] mServices;
这里还可以看到一个信息就是所有组件都是 SystemUI
这个类的实现。篇幅所限,对各个组件的分析将在之后的文章中说明。
SystemServer
启动 SystemUIService
之后,就进入到了应用层中,SystemUIApplication
是 SystemUI
的入口,在 onCreate
方法中做了一些初始化工作,注册监听通知等操作;如果是多用户则会启动了一个组件 NotificationChannels
;然后就进入到 SystemUIService
中,它在 onCreate
方法中也是执行了 SystemUIApplication
中的 startServicesIfNeeded()
方法,并把所有的服务都存在 mServices
数组中。
0x02 总结
SystemUI
是一个 persistent
应用,它由操作系统启动,主要流程为
•Android
系统在开机后会创建 SystemServer
进程,它会启动各种系统所需要的服务,其中就包括 SystemUIService
。•SystemUIService
启动后进入到应用层 SystemUI
中,在 SystemUIApplication
它首先会初始化监听boot completed
等通知,待系统完成启动后会通知各个组件 onBootCompleted
。•在进入 SystemUIService
中依然执行的 SystemUIApplication
中的startServicesIfNeeded()
无参方法启动所有 SystemUI
中的组件。•最终的服务启动逻辑都是在 SystemUIApplication
里面,并且都保存在 mServices
数组中。0x03 引用
•在线源码阅读 https://cs.android.com
[1]
服务组件列表: https://cs.android.com/android/platform/superproject/+/android-12.0.0_r4:frameworks/base/packages/SystemUI/;bpv=0;bpt=0