前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SystemUI 开发之启动流程(一)

SystemUI 开发之启动流程(一)

作者头像
阳仔
发布2021-12-08 09:14:49
1.2K0
发布2021-12-08 09:14:49
举报
文章被收录于专栏:终身开发者终身开发者

0x00 介绍

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/ 搜索这个类,看到以下这个路径就是我们要找的类。

代码语言:javascript
复制
frameworks/base/services/java/com/android/server/SystemServer.java

注意本文使用的是 android-12.0.0_r4 分支中的代码

这个就是 SystemServer 进程的入口,它会启动很多系统相关的应用,其中就包括 SystemUI。找到它的 main 方法

代码语言:javascript
复制
/**
 * The main entry point from zygote.
 */
public static void main(String[] args) {
    new SystemServer().run();
}

从这个方法的注释来看,SystemServer 是由 zygote 进程启动的。接下来看run() 方法。

代码语言:javascript
复制
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() 里面

代码语言:javascript
复制
/**
 * 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()

代码语言:javascript
复制
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类,可以进入到以下路径

代码语言:javascript
复制
frameworks/base/services/core/java/android/content/pm/PackageManagerInternal.java

可以看到这是一个抽象类,而且 getSystemUiServiceComponent 也是抽象方法

代码语言:javascript
复制
/**
 * @return The SystemUI service component name.
 */    
public abstract ComponentName getSystemUiServiceComponent();

所以我们要看上面 LocalServices.getService() 方法,点击该方法会跳转到以下目录

代码语言:javascript
复制
frameworks/base/core/java/com/android/server/LocalServices.java

可以看到有两个关键方法 getService()addService()

代码语言:javascript
复制
/**
 * 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 这个类。

代码语言:javascript
复制
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

可以看到里面有一个 PackageManagerInternalImpl 的内部类扩展了 PackageManagerInternal ,在其中找到我们的目标方法

代码语言:javascript
复制
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 这个内部可以在以下路径中搜索到

代码语言:javascript
复制
frameworks/base/core/res/res/values/config.xml

具体定义为

代码语言:javascript
复制
<!-- SystemUi service component -->
<string name="config_systemUIServiceComponent" translatable="false">com.android.systemui/com.android.systemui.SystemUIService</string>

这个 SystemUIService 是在 SystemUI 应用中定义的,所以接下来的流程将转到 SystemUI 应用中来

小结一下

framework 中的 SystemServerrun 方法启动了系统所需要的各种服务,其中就包括 SystemUIService。 具体是通过 PackageManagerInternal 获取到 SystemUIService 的配置名称,使用 startServiceAsUser() 来启动。 在这里可以判断由于 SystemUIService 是定义在上层 SystemUI 应用层的,所以这个 serviceexport 属性应该为 true。接下来的流程我们可以验证这个猜想。

在 SystemUI 中的流程

SystemUI 在源码中路径为

代码语言:javascript
复制
frameworks/base/packages/SystemUI/

首先打开 manifest 文件看下 SystemUIService 的配置

代码语言:javascript
复制
    <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 的入口为 SystemUIApplicationSystemUIpersistent 应用,即使发生了 crash 系统依然会拉起这个应用•验证了上一节中 SystemUIServiceexported 属性

SystemUIApplication

代码在以下路径

代码语言:javascript
复制
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java

查看 onCreate 方法

代码语言:javascript
复制
@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 类,用来处理各种服务逻辑的。

代码语言:javascript
复制
/**
* 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() 进行启动

代码语言:javascript
复制
/**
 * 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 定义在 看名字可以猜测这个是根据用户来启动的组件列表,目前这里就一个组件

代码语言:javascript
复制
frameworks/base/packages/SystemUI/res/values/config.xml
代码语言:javascript
复制
<!-- 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() 方法,至此各个组件就完成了启动工作。

代码语言:javascript
复制
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;
    }

小结一下

SystemUIApplicationonCreate 中做一些初始化工作,如果是系统启动的服务,则进行 boot completed 等通知的注册,然后系统启动完成后,通知到各个组件;如果是在多用户环境下,从配置文件中获取到一个 com.android.systemui.util.NotificationChannels 组件,然后通过反射构建之后存在 mServices 数组中并启动它。 接下来看 SystemUIService

这个才是真正 Android 中的“服务”

SystemUIService

可以在以下路径找到

代码语言:javascript
复制
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java

这个类逻辑很简单,就一件事:执行 SystemUIApplication 中的 startServicesIfNeeded() 注意这个是一个没有参数的重载方法

代码语言:javascript
复制
@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

代码语言:javascript
复制
/**
 * 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 组件定义

代码语言:javascript
复制
frameworks/base/packages/SystemUI/res/values/config.xml

这个是目前 android-12.0.0_r4 分支用到的所有 SystemUI 组件,这里定义了各个组件的类信息

代码语言:javascript
复制
    <!-- 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 中定义的,它保存了所有启动的组件。

代码语言:javascript
复制
/**
 * Hold a reference on the stuff we start.
 */    
private SystemUI[] mServices;

这里还可以看到一个信息就是所有组件都是 SystemUI 这个类的实现。篇幅所限,对各个组件的分析将在之后的文章中说明。

小结一下

SystemServer 启动 SystemUIService 之后,就进入到了应用层中,SystemUIApplicationSystemUI 的入口,在 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

References

[1] 服务组件列表: https://cs.android.com/android/platform/superproject/+/android-12.0.0_r4:frameworks/base/packages/SystemUI/;bpv=0;bpt=0

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 终身开发者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00 介绍
  • SystemUI 是系统应用,其实就是 Android 系统上常见下拉面板,除了通知栏还有很多组件例如快捷键、电量等组件等也是在 SystemUI 中展示。
    • 在 framework 中的流程
      • 小结一下
        • 在 SystemUI 中的流程
          • SystemUIApplication
            • 小结一下
              • SystemUIService
                • 小结一下
                  • References
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档