Android14 SystemUI StatusBar 加载过程

184 阅读4分钟

在android 系统中有两个StatusBar , 锁屏: keyguard_status_bar.xml 解锁:super_status_bar.xml

解锁 StatusBar

在SystemUI启动的的时候最终会调用 CoreStartable 实现类中 start() 方法, 具体过程可以参考:Android14 SystemUI 启动过程

阅读SystemUI 代码可知道在 CentralSurfacesImpl 启动的时候会添加 StatusBar 到 Windows 上, 下面开始看具体代码 frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java

public void start() {

    // 省略部分代码
    mBarService = IStatusBarService.Stub.asInterface(
            ServiceManager.getService(Context.STATUS_BAR_SERVICE));

    mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
    mWallpaperSupported = mWallpaperManager.isWallpaperSupported();

    RegisterStatusBarResult result = null;
    try {
        result = mBarService.registerStatusBar(mCommandQueue);
    } catch (RemoteException ex) {
        ex.rethrowFromSystemServer();
    }

    createAndAddWindows(result);
     // 省略部分代码
}

public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
    makeStatusBarView(result);
    mNotificationShadeWindowController.attach();
    mStatusBarWindowController.attach();
}
   

在这里我们看到调用了 mStatusBarWindowController.attach(); 下面看一下mStatusBarWindowController是何时创建的

 @Inject
public CentralSurfacesImpl(
            // 省略部分代码
            StatusBarWindowController statusBarWindowController,
            // 省略部分代码
    ) {
        // 省略部分代码
        mStatusBarWindowController = statusBarWindowController;
       // 省略部分代码
    }

CentralSurfacesImpl 的构造方法中注入的。 再看一下 StatusBarWindowController 创建的时候做了些什么 frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java

@Inject
public StatusBarWindowController(
        Context context,
        @StatusBarWindowModule.InternalWindowView StatusBarWindowView statusBarWindowView,
        WindowManager windowManager,
        IWindowManager iWindowManager,
        StatusBarContentInsetsProvider contentInsetsProvider,
        FragmentService fragmentService,
        @Main Resources resources,
        Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) {
    mContext = context;
    mWindowManager = windowManager;
    mIWindowManager = iWindowManager;
    mContentInsetsProvider = contentInsetsProvider;
    mStatusBarWindowView = statusBarWindowView;
    mFragmentService = fragmentService;
    mLaunchAnimationContainer = mStatusBarWindowView.findViewById(
            R.id.status_bar_launch_animation_container);
    mLpChanged = new WindowManager.LayoutParams();

    if (mBarHeight < 0) {
        mBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
    }
    unfoldTransitionProgressProvider.ifPresent(
            unfoldProgressProvider -> unfoldProgressProvider.addCallback(
                    new JankMonitorTransitionProgressListener(
                            /* attachedViewProvider=*/ () -> mStatusBarWindowView)));
}

上面代码可以发现在构造的时候注入了StatusBarWindowView 对象并且计算了StatusBar 的高度。

下面看一下StatusBarWindowView 注入过程,这里需要先看一下StatusBarWindowModule frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt

@Module
abstract class StatusBarWindowModule {
    /**
     * Provides a [StatusBarWindowView].
     *
     * Only [StatusBarWindowController] should inject the view.
     */
    @Module
    companion object {
        @JvmStatic
        @Provides
        @SysUISingleton
        @InternalWindowView
        fun providesStatusBarWindowView(layoutInflater: LayoutInflater): StatusBarWindowView {
            return layoutInflater.inflate(
                R.layout.super_status_bar,
                /* root= */null
            ) as StatusBarWindowView?
                ?: throw IllegalStateException(
                    "R.layout.super_status_bar could not be properly inflated"
                )
        }
    }

    @Retention(AnnotationRetention.BINARY)
    @Qualifier
    protected annotation class InternalWindowView
}

这是一个dagger注入的module, 里面主要负责注入 StatusBarWindowView的, 但是这里需要注意一下, 这里inflater 了 StatusBar的 layout( super_status_bar ), 可以知道 StatusBarWindowView 是 StatusBar 的 RootView。

再继续看一下StatusBar 的高度是如何获取的 frameworks/base/core/java/com/android/internal/policy/SystemBarUtils.java

/**
 * Gets the status bar height.
 */
public static int getStatusBarHeight(Context context) {
    return getStatusBarHeight(context.getResources(), context.getDisplay().getCutout());
}

/**
 * Gets the status bar height with a specific display cutout.
 */
public static int getStatusBarHeight(Resources res, DisplayCutout cutout) {
    final int defaultSize = res.getDimensionPixelSize(R.dimen.status_bar_height_default);
    final int safeInsetTop = cutout == null ? 0 : cutout.getSafeInsetTop(); 
    final int waterfallInsetTop = cutout == null ? 0 : cutout.getWaterfallInsets().top;
    // The status bar height should be:
    // Max(top cutout size, (status bar default height + waterfall top size))
    return Math.max(safeInsetTop, defaultSize + waterfallInsetTop);
}

首先获取默认状态栏高度:defaultSize从资源文件中读取(R.dimen.status_bar_height_default), 获取安全插入区域顶部大小:safeInsetTop,如果没有cutout则为0, 获取瀑布流插入区域顶部大小:waterfallInsetTop,如果没有cutout则为0,这里需要注意, 刘海屏/挖孔屏是通过safeInsetTop控制, 曲面屏是通过waterfallInsetTop控制

最后在回到StatusBarWindowController 中看看一下attach 方法

public void attach() {
    // Now that the status bar window encompasses the sliding panel and its
    // translucent backdrop, the entire thing is made TRANSLUCENT and is
    // hardware-accelerated.
    Trace.beginSection("StatusBarWindowController.getBarLayoutParams");
    mLp = getBarLayoutParams(mContext.getDisplay().getRotation());
    Trace.endSection();

    mWindowManager.addView(mStatusBarWindowView, mLp);
    mLpChanged.copyFrom(mLp);

    mContentInsetsProvider.addCallback(this::calculateStatusBarLocationsForAllRotations);
    calculateStatusBarLocationsForAllRotations();
    mIsAttached = true;
    apply(mCurrentState);
}

这里主要将StatusBarWindowView 添加到 WindowManager 中显示

锁屏 StatusBar

锁屏StatusBar 加载过程要比解锁后加载要复杂一些。 先介绍一下keyguard_status_bar.xml 是如何被加载的 frameworks/base/packages/SystemUI/res/layout/status_bar_expanded.xml

<?xml version="1.0" encoding="utf-8"?>
<com.android.systemui.shade.NotificationPanelView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/notification_panel"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent">


    <com.android.systemui.shade.NotificationsQuickSettingsContainer
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="@integer/notification_panel_layout_gravity"
        android:id="@+id/notification_container_parent"
        android:clipToPadding="false"
        android:clipChildren="false">

        <include
            layout="@layout/keyguard_status_bar"
            android:visibility="invisible" />

    </com.android.systemui.shade.NotificationsQuickSettingsContainer>

</com.android.systemui.shade.NotificationPanelView>

可以看到status_bar_expanded.xml中加载了keyguard_status_bar.xml 在看一下status_bar_expanded.xml 是如何被加载的 frameworks/base/packages/SystemUI/res/layout/super_notification_shade.xml

<?xml version="1.0" encoding="utf-8"?>

<!-- This is the notification shade window. -->
<com.android.systemui.shade.NotificationShadeWindowView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:sysui="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <com.android.systemui.statusbar.LightRevealScrim
        android:id="@+id/light_reveal_scrim"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <include layout="@layout/status_bar_expanded"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:visibility="invisible" />
</com.android.systemui.shade.NotificationShadeWindowView>

可以看到super_notification_shade.xml中加载了status_bar_expanded.xml 总结一下: keyguard_status_bar.xml -> status_bar_expanded.xml -> super_notification_shade.xmlsuper_notification_shade.xml 中 RootView 是 NotificationShadeWindowView 布局文件先介绍到这里,这里需要记住这个RootView

下面看代码部分,继续看CentralSurfacesImpl 这个类中的createAndAddWindows 方法

public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
    makeStatusBarView(result);
    mNotificationShadeWindowController.attach();
    mStatusBarWindowController.attach();
}

这里调用到了 makeStatusBarView

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
    inflateStatusBarWindow();
}

protected NotificationShadeWindowView mNotificationShadeWindowView;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
private void inflateStatusBarWindow() {
    mNotificationShadeWindowView = mCentralSurfacesComponent.getNotificationShadeWindowView();
    mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
}

在这里我们看到了上面布局文件中的RootView(NotificationShadeWindowView), 那么这个是怎么创建的的, 首先要看一下CentralSurfacesComponent frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java

public interface CentralSurfacesComponent {
    /**
     * Scope annotation for singleton items within the CentralSurfacesComponent.
     */
    @Documented
    @Retention(RUNTIME)
    @Scope
    @interface CentralSurfacesScope {}

    /**
     * Creates a {@link NotificationShadeWindowView}.
     */
    NotificationShadeWindowView getNotificationShadeWindowView();
}

frameworks/base/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt

@Module
abstract class ShadeModule {

    @Binds
    @IntoMap
    @ClassKey(AuthRippleController::class)
    abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable

    companion object {
        const val SHADE_HEADER = "large_screen_shade_header"

        @Provides
        @SysUISingleton
        // TODO(b/277762009): Do something similar to
        //  {@link StatusBarWindowModule.InternalWindowView} so that only
        //  {@link NotificationShadeWindowViewController} can inject this view.
        fun providesNotificationShadeWindowView(
            layoutInflater: LayoutInflater,
        ): NotificationShadeWindowView {
            return layoutInflater.inflate(R.layout.super_notification_shade, /* root= */ null)
                as NotificationShadeWindowView?
                ?: throw IllegalStateException(
                    "R.layout.super_notification_shade could not be properly inflated"
                )
        }
    }
}

调用CentralSurfacesComponent中的getNotificationShadeWindowView() 方法会执行 ShadeModuleprovidesNotificationShadeWindowView 方法注入 inflate 后的 RootView(NotificationShadeWindowView)

到这里RootView 创建完毕, 然后通过NotificationShadeWindowControllersetNotificationShadeView将RootView和NotificationShadeWindowController对象进行绑定。

这里说一下NotificationShadeWindowController, 它是个接口, 真正的实现类是NotificationShadeWindowControllerImpl

再继续看CentralSurfacesImpl 这个类中的createAndAddWindows 方法

public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
    makeStatusBarView(result);
    mNotificationShadeWindowController.attach();
    mStatusBarWindowController.attach();
}

执行NotificationShadeWindowControllerImpl 对象的attach() 方法, frameworks/base/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java

@Override
public void attach() {
    mLp = new LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT,
            LayoutParams.TYPE_NOTIFICATION_SHADE,
            LayoutParams.FLAG_NOT_FOCUSABLE
                    | LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                    | LayoutParams.FLAG_SPLIT_TOUCH
                    | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                    | LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
            PixelFormat.TRANSLUCENT);
    mLp.token = new Binder();
    mLp.gravity = Gravity.TOP;
    mLp.setFitInsetsTypes(0 /* types */);
    mLp.setTitle("NotificationShade");
    mLp.packageName = mContext.getPackageName();
    mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
    mLp.privateFlags |= PRIVATE_FLAG_OPTIMIZE_MEASURE;

    mLp.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
    mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;

    mWindowManager.addView(mNotificationShadeView, mLp);
}

这里主要将NotificationShadeView 添加到 WindowManager 中显示。

到这里StatusBar 的加载过程就介绍结束了。在介绍锁屏StatusBar 加载过程中后面可能会有疑问, 后面很多类名字为什么都是NotificationXXX , 是因为这个地方主要功能是Notification, 锁屏 StatusBar 只是一小部分