0x00 介绍
SystemUI 是系统应用,其实就是 Android 系统上常见下拉面板,除了通知栏还有很多组件例如快捷键、电量等组件等也是在 SystemUI 中展示。
常见 UI 组件有(包含但不限于,完整列表可以查看 SystemUI 服务组件列表)
- 状态栏
StatusBar - 导航栏
NavigationBar - 通知栏
NotificationPanel - 快捷按键栏
QSPanel - 最近任务
Recent - 键盘锁
Keyguard
标准 Android 系统中 SystemUI 大概是长这样
当然大多数厂商会根据自身需求对 SystemUI 的样式进行深度定制,例如在我华为手机中的 SystemUI 长这样
通知栏
快捷键栏
0x01 启动流程
启动流程主要包括两个部分
- 在
framework中启动SystemUIService - 在
SystemUIService中启动SystemUI所需的各种组件
在 framework 中的流程
SystemUI 是系统应用,所以它也是一个 APK,有入口 Application,只不过它是由 SystemServer 进程进行启动的。
我们在源码官网上 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) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
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 方法启动了服务。
继续在 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 中的流程
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的入口为SystemUIApplicationSystemUI是persistent应用,即使发生了crash系统依然会拉起这个应用- 验证了上一节中
SystemUIService的exported属性
SystemUIApplication
代码在以下路径
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();
log.traceEnd();
// Set the application theme that is inherited by all services. Note that setting the
// application theme in the manifest does only work for activities. Keep this in sync with
// the theme set there.
setTheme(R.style.Theme_SystemUI);
if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
// 一般情况下走这里来,例如开关机启动系统
IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
// If SF GPU context priority is set to realtime, then SysUI should run at high.
// The priority is defaulted at medium.
int sfPriority = SurfaceControl.getGPUContextPriority();
Log.i(TAG, "Found SurfaceFlinger's GPU Priority: " + sfPriority);
if (sfPriority == ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_REALTIME_NV) {
Log.i(TAG, "Setting SysUI's GPU Context priority to: "+ ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
ThreadedRendererCompat.setContextPriority(ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
}
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mBootCompleteCache.isBootComplete()) return;
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
unregisterReceiver(this);
mBootCompleteCache.setBootComplete();
if (mServicesStarted) {
final int N = mServices.length;
for (int i = 0; i < N; i++) {
mServices[i].onBootCompleted();
}
}
}
}, bootCompletedFilter);
IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
if (!mBootCompleteCache.isBootComplete()) return;
// Update names of SystemUi notification channels
NotificationChannels.createAll(context);
}
}
}, localeChangedFilter);
} else {
// We don't need to startServices for sub-process that is doing some tasks.
// (screenshots, sweetsweetdesserts or tuner ..)
String processName = ActivityThread.currentProcessName();
ApplicationInfo info = getApplicationInfo();
if (processName != null && processName.startsWith(info.processName + ":")) {
return;
}
// For a secondary user, boot-completed will never be called because it has already
// been broadcasted on startup for the primary SystemUI process. Instead, for
// components which require the SystemUI component to be initialized per-user, we
// start those components now for the current non-system user.
startSecondaryUserServicesIfNeeded();
}
}
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);
}
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;
}
小结一下
SystemUIApplication 在 onCreate 中做一些初始化工作,如果是系统启动的服务,则进行 boot completed 等通知的注册,然后系统启动完成后,通知到各个组件;
如果是在多用户环境下,从配置文件中获取到一个 com.android.systemui.util.NotificationChannels 组件,然后通过反射构建之后存在 mServices 数组中并启动它。
接下来看 SystemUIService
这个才是真正
Android中的“服务”
SystemUIService
可以在以下路径找到
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 引用
- 在线源码阅读 cs.android.com