SystemUI 有哪内容
从表面上看, 我们看到的状态栏、通知栏、下拉菜单、导航栏、锁屏、最近任务、低电提示等系统页面都是 SystemUI 的。SystemUI,在源码目录中位于: framework/base/packages 目录下, 可见 SystemUI 和 framework 是关联的, SystemUI 依赖了很多内部 API , 系统资源, SystemUI 编译是要依赖系统源码的。
SystemUI 也是一个应用,不过这个应用特殊之处在于他没有启动图标、也没有入口 Activity 。他的入口程序是一个服务:SystemUIService。 这个服务会被系统服务拉起来, 这个服务起来, SystemUI 应用进程就创建起来了,具体启动过程后面会分析。除了 SystemUIService , SystemUI 还有很多服务, 例如: 负责锁屏的KeyguardService、负责最近任务的 RecentsSystemUserService、负责壁纸的 ImageWallpaper 、负责截屏的TakeScreenshotService 等。



下面是PhoneStatusBarView的view 树形图:

PanelHolder
PanelHolder是用户下拉 status bar 后得到的 view。它主要包含 QuickSettings 和 Notification panel 两个部分。PanelHolder是一个继承自FrameLayout的自定义view,它的内容是通过include status_bar_expanded.xml进行填充的。PanelHolder的布局比较复杂,为了提高view的重用性大量的使用了include标签。下面是PanelHolder的view树形图, 只给出了了主要的view:

架构关系
在系统服务中,有一个服务是专门为 SystemUI 的状态栏服务的, 这个服务就是 StatusbarManagerService (简称:SMS),和这个服务关系比较密切的服务是 WindowManagerService(简称:WMS), SMS 主要管控的是状态栏、导航栏, 例如:我们可以设置全屏、沉浸式状态栏都是 SMS 在起作用。

services组件启动时配置列表 : (R.array.config_systemUIServiceComponents)
所有 SystemUIService 都是继承自 SystemUI.class , SystemUI.class 是一个抽象类
<item>com.android.systemui.util.NotificationChannels</item> 通知信息 <item>com.android.systemui.keyguard.KeyguardViewMediator</item> 锁屏 <item>com.android.systemui.recents.Recents</item> 近期列表 Android 10之后近期列表的显示被移到Launcher里面了。在Launcher3的一个 类中TouchInteractionService.java IBinder mMyBinder = new IOverviewProxy.Stub() 通过AIDL的方法与systemUI通信 ———————————————— <item>com.android.systemui.volume.VolumeUI</item> 声音UI显示 <item>com.android.systemui.statusbar.phone.StatusBar</item> 状态栏及下拉面板 <item>com.android.systemui.usb.StorageNotification</item> usb通知管理 <item>com.android.systemui.power.PowerUI</item> 电源UI显示管理 <item>com.android.systemui.media.RingtonePlayer</item> 播放铃声 <item>com.android.systemui.keyboard.KeyboardUI</item>键盘UI <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> Toast <item>com.android.systemui.wmshell.WMShell</item>
一、SystemUI的启动流程
1、SystemServer
SystemServer启动后,会在Main Thread启动ActivityManagerService,当ActivityManagerService systemReady后,会去启动SystemUIService。
/frameworks/base/services/java/com/android/server/SystemServer.java
①main
/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();
}
②run
private void run() {
t.traceBegin("InitBeforeStartServices");
....
// Create the system service manager.
mSystemServiceManager = new SystemServiceManager(mSystemContext);
mSystemServiceManager.setStartInfo(mRuntimeRestart,
mRuntimeStartElapsedTime, mRuntimeStartUptime);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
....
}
③mActivityManagerService.systemReady
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();
....
}
④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);
//通过startServiceAsUser,SystemUIService就启动了,即SystemUI进程开机启动
context.startServiceAsUser(intent, UserHandle.SYSTEM);
windowManager.onSystemUiStarted();
}
2、systemUIService
在SystemUIService的onCreate方法中会调用SystemUIApplication的startServicesIfNeeded方法,这个方法会调用 startServicesIfNeeded(SERVICES)方法启动一系列服务
@Override
public void onCreate() {
super.onCreate();
// Start all of SystemUI
((SystemUIApplication) getApplication()).startServicesIfNeeded();
3、SystemUIApplication
/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
①startServicesIfNeeded()
public void startServicesIfNeeded() {
//获取所有的服务的路径,所有SERVICES统一继承了SystemUI类:
String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
}
②startServicesIfNeeded(String metricsPrefix, String[] services)
在重载方法中将每一个名称通过反射来得到实例对象,然后依次调用每一个SystemUI的子类的start方法启动每一个模块。
private void startServicesIfNeeded(String metricsPrefix, String[] services) {
if (mServicesStarted) {
return;
}
mServices = new SystemUI[services.length];
//检查一下,也许在我们开始之前很久它就已经完成了
if (!mBootCompleteCache.isBootComplete()) {
// check to see if maybe it was already completed long before we began
// see ActivityManagerService.finishBooting()
if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
mBootCompleteCache.setBootComplete();
if (DEBUG) {
Log.v(TAG, "BOOT_COMPLETED was already sent");
}
}
}
final DumpManager dumpManager = mRootComponent.createDumpManager();
Log.v(TAG, "Starting SystemUI services for user " +
Process.myUserHandle().getIdentifier() + ".");
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Trace.TRACE_TAG_APP);
//开始追踪
log.traceBegin(metricsPrefix);
final int N = services.length;
//遍历services这个数组
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]);
//依次调用service的start方法启动服务
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]);
}
mRootComponent.getInitController().executePostInitTasks();
//结束追踪
log.traceEnd();
mServicesStarted = true;
}
4、SystemUI
/**
* @see SystemUIApplication#startServicesIfNeeded()
*系统界面应用 如果需要,启动服务
*/
public abstract class SystemUI implements Dumpable {
protected final Context mContext;
public SystemUI(Context context) {
mContext = context;
}
public abstract void start();
protected void onConfigurationChanged(Configuration newConfig) {
}
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
}
protected void onBootCompleted() {
}
public static void overrideNotificationAppName(Context context, Notification.Builder n,
boolean system) {
final Bundle extras = new Bundle();
String appName = system
? context.getString(com.android.internal.R.string.notification_app_name_system)
: context.getString(com.android.internal.R.string.notification_app_name_settings);
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName);
n.addExtras(extras);
}
}
二、状态栏
1、SystemBars
SystemBars加载基本全部SystemUI的界面显示,由前面可知调用的start方法实际上是每一个继承于SytemUI的子类中的方法
①start

②createStatusBarFromConfig
从string资源文件里面读取class name,通过java的反射机制实例化对象,然后调用start()方法启动,class name的值如下图:
private void createStatusBarFromConfig() {
......
String clsName = mContext.getString(R.string.config_statusBarComponent);
......
try {
cls = mContext.getClassLoader().loadClass(clsName);
} catch (Throwable t) {
throw andLog("Error loading status bar component: " + clsName, t);
}
try {
mStatusBar = (BaseStatusBar) cls.newInstance();
} catch (Throwable t) {
throw andLog("Error creating status bar component: " + clsName, t);
}
......
mStatusBar.start();
......
}
③String
<!-- Component to be used as the status bar service. Must implement the IStatusBar
interface. This name is in the ComponentName flattened format (package/class) -->
<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
2、StatusBar
①createAndAddWindows
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
//创建状态栏
makeStatusBarView(result);
mNotificationShadeWindowController.attach();
//创建状态栏的窗口
mStatusBarWindowController.attach();
}
②makeStatusBarView
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
final Context context = mContext;
.....
FragmentHostManager.get(mPhoneStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
//CollapsedStatusBarFragment 替换 status_bar_container(状态栏通知显示区域)
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
PhoneStatusBarView oldStatusBarView = mStatusBarView;
mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
//传递statusBar处理下拉事件
mStatusBarView.setBar(this);
//传递 NotificationPanelView 显示下拉UI控制
mStatusBarView.setPanel(mNotificationPanelViewController);
mStatusBarView.setScrimController(mScrimController);
//初始化通知栏区域
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
......
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();
.....
2、CollapsedStatusBarFragment
①onCreateView
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.status_bar, container, false);
}
②initNotificationIconArea
public void initNotificationIconArea(NotificationIconAreaController
notificationIconAreaController) {
//notification_icon_area是在status_bar.xml的布局,它是属于通知Notification
//获取到 notification_icon_area,FrameLayout转为ViewGroup,
ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
//调用 notificationIconAreaController 获取通知要显示的view(LinearLayout)
//在4中跟进
mNotificationIconAreaInner =
notificationIconAreaController.getNotificationInnerAreaView();
//如果已经有显示的view,通过 view 父布局将其自身remove,然后再重新addView。
//最后将 mNotificationIconAreaInner 显示出来(设置透明度为1,visibility为VISIBLE)
if (mNotificationIconAreaInner.getParent() != null) {
((ViewGroup) mNotificationIconAreaInner.getParent())
.removeView(mNotificationIconAreaInner);
}
notificationIconArea.addView(mNotificationIconAreaInner);
//与上面一样
ViewGroup statusBarCenteredIconArea = mStatusBar.findViewById(R.id.centered_icon_area);
mCenteredIconArea = notificationIconAreaController.getCenteredNotificationAreaView();
if (mCenteredIconArea.getParent() != null) {
((ViewGroup) mCenteredIconArea.getParent())
.removeView(mCenteredIconArea);
}
statusBarCenteredIconArea.addView(mCenteredIconArea);
//默认为显示,直到我们知道其他情况
// Default to showing until we know otherwise.
showNotificationIconArea(false);
}
③showNotificationIconArea
当状态栏下拉时,状态栏中的图标icon会慢慢的变成透明和不可见,就是通过hideSystemIconArea(true), hideNotificationIconArea(true)
//当状态栏下拉时,设置状态栏中的图标icon会慢慢的变成透明和不可见
public void hideNotificationIconArea(boolean animate) {
animateHide(mNotificationIconAreaInner, animate);
animateHide(mCenteredIconArea, animate);
}
//设置状态栏图标透明度为1,visibility为VISIBLE
public void showNotificationIconArea(boolean animate) {
animateShow(mNotificationIconAreaInner, animate);
animateShow(mCenteredIconArea, animate);
}
public void hideOperatorName(boolean animate) {
if (mOperatorNameFrame != null) {
animateHide(mOperatorNameFrame, animate);
}
}
public void showOperatorName(boolean animate) {
if (mOperatorNameFrame != null) {
animateShow(mOperatorNameFrame, animate);
}
④animateShow
private void animateShow(View v, boolean animate) {
v.animate().cancel();
//(设置透明度为1,visibility为VISIBLE)
v.setVisibility(View.VISIBLE);
if (!animate) {
v.setAlpha(1f);
return;
}
.....
}
⑤animateHiddenState
//将视图动画化为 INVISIBLE 或 GONE
private void animateHiddenState(final View v, int state, boolean animate) {
v.animate().cancel();
if (!animate) {
v.setAlpha(0f);
v.setVisibility(state);
return;
}
v.animate()
.alpha(0f)
.setDuration(160)
.setStartDelay(0)
.setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(() -> v.setVisibility(state));
}
3、status_bar.xml
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<!-- android:background="@drawable/status_bar_closed_default_background" -->
<com.android.systemui.statusbar.phone.PhoneStatusBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_height"
android:id="@+id/status_bar"
android:orientation="vertical"
android:focusable="false"
android:descendantFocusability="afterDescendants"
android:accessibilityPaneTitle="@string/status_bar"
>
<!-- add for KGDAANWIKFRA-135 -->
<View
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_height"
android:id="@+id/status_bar_dark_view"
android:background="#ff000000"
android:visibility="gone" />
//<!--通知灯,默认gone-->
<ImageView
android:id="@+id/notification_lights_out"
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="match_parent"
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingBottom="2dip"
android:src="@drawable/ic_sysbar_lights_out_dot_small"
android:scaleType="center"
android:visibility="gone"
/>
//<!--状态栏内容-->
<LinearLayout android:id="@+id/status_bar_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="@dimen/status_bar_padding_end"
android:paddingTop="@dimen/status_bar_padding_top"
android:orientation="horizontal"
>
<FrameLayout
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1">
<include layout="@layout/heads_up_status_bar_layout" />
<!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and the
individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK and
DISABLE_NOTIFICATION_ICONS, respectively -->
<LinearLayout
android:id="@+id/status_bar_left_side"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:clipChildren="false"
>
<ViewStub
android:id="@+id/operator_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout="@layout/operator_name" />
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:singleLine="true"
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
android:gravity="center_vertical|start"
/>
//<!--通知图标区域-->
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/notification_icon_area"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:clipChildren="false"/>
</LinearLayout>
</FrameLayout>
<!-- Space should cover the notch (if it exists) and let other views lay out around it -->
<android.widget.Space
android:id="@+id/cutout_space_view"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center_horizontal|center_vertical"
/>
//居中的图标区域
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/centered_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:clipChildren="false"
android:gravity="center_horizontal|center_vertical"/>
<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical|end"
>
//<!--系统图标-->
<include layout="@layout/system_icons" />
</com.android.keyguard.AlphaOptimizedLinearLayout>
</LinearLayout>
//<!--紧急密码管理员文本-->
<ViewStub
android:id="@+id/emergency_cryptkeeper_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout="@layout/emergency_cryptkeeper_text"
/>
</com.android.systemui.statusbar.phone.PhoneStatusBarView>
4、NotificationIconAreaController
①getNotificationInnerAreaView
/**
* Returns the view that represents the notification area.+
* 返回表示通知区域的视图。
*/
public View getNotificationInnerAreaView() {
return mNotificationIconArea;
}
②initializeNotificationAreaViews
/**
* Initializes the views that will represent the notification area.
* 初始化将表示通知区域的视图。
*/
protected void initializeNotificationAreaViews(Context context) {
reloadDimens(context);
LayoutInflater layoutInflater = LayoutInflater.from(context);
//通知图标区域布局
mNotificationIconArea = inflateIconArea(layoutInflater);
//通知图标
mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons);
//获取通知滚动布局
mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
//中心图标区域布局
mCenteredIconArea = layoutInflater.inflate(R.layout.center_icon_area, null);
//居中的图标
mCenteredIcon = mCenteredIconArea.findViewById(R.id.centeredIcon);
initAodIcons();
}
③inflateIconArea
protected View inflateIconArea(LayoutInflater inflater) {
return inflater.inflate(R.layout.notification_icon_area, null);
}
三、status icon加载流程
1、 status_bar.xml
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/centered_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:clipChildren="false"
android:gravity="center_horizontal|center_vertical"/>
<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical|end"
>
<!--系统图标-->
<include layout="@layout/system_icons" />
</com.android.keyguard.AlphaOptimizedLinearLayout>
2、system_icons.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/system_icons"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical">
//StatusIconContainer继承AlphaOptimizedLinearLayout
<com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:paddingEnd="@dimen/signal_cluster_battery_padding"
android:gravity="center_vertical"
android:orientation="horizontal"/>
<com.android.systemui.BatteryMeterView android:id="@+id/battery"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:clipToPadding="false"
android:clipChildren="false"
systemui:textAppearance="@style/TextAppearance.StatusBar.Clock" />
</LinearLayout>
3、AlphaOptimizedLinearLayout
//该方法用来标记当前view是否存在过度绘制,存在返回ture,不存在返回false,
//api里面默认返回为true,status icon不存在过度绘制。
@Override
public boolean hasOverlappingRendering() {
return false;
}
4、CollapsedStatusBarFragment
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mStatusBar = (PhoneStatusBarView) view;
if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
mStatusBar.restoreHierarchyState(
savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
}
// TINNO BEGIN, add for KGDAANWIKFRA-135
if (Utils.TINNO_NOTCH_SCREEN) {
mStatusBarDarkView = mStatusBar.findViewById(R.id.status_bar_dark_view);
toggleStatusBarDarkView();
mLastOrientation = getContext().getResources().getConfiguration().orientation;
}
// TINNO END
//DarkIconManager初始化
mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons),
Dependency.get(CommandQueue.class));
mDarkIconManager.setShouldLog(true);
Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
mClockView = mStatusBar.findViewById(R.id.clock);
showSystemIconArea(false);
showClock(false);
initEmergencyCryptkeeperText();
initOperatorName();
}
4、StatusBarIconController
①DarkIconManager
/**
* Version of ViewGroup that observes state from the DarkIconDispatcher.
*/
public static class DarkIconManager extends IconManager {
private final DarkIconDispatcher mDarkIconDispatcher;
private int mIconHPadding;
public DarkIconManager(LinearLayout linearLayout, CommandQueue commandQueue) {
super(linearLayout, commandQueue);
mIconHPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.status_bar_icon_padding);
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
}
//每个icon应该就是对应着代表顺序的index和数据类型为String的slot
@Override
protected void onIconAdded(int index, String slot, boolean blocked,
StatusBarIconHolder holder) {
StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view);
}
.....
//onSetIcon可能就是刷新icon状态的
@Override
public void onSetIcon(int viewIndex, StatusBarIcon icon) {
super.onSetIcon(viewIndex, icon);
mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex));
}
.....
}
②IconManager
public static class IconManager implements DemoMode {
.....
protected void onIconAdded(int index, String slot, boolean blocked,
StatusBarIconHolder holder) {
addHolder(index, slot, blocked, holder);
}
protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
StatusBarIconHolder holder) {
switch (holder.getType()) {
case TYPE_ICON:
return addIcon(index, slot, blocked, holder.getIcon());
case TYPE_WIFI:
return addSignalIcon(index, slot, holder.getWifiState());
case TYPE_MOBILE:
return addMobileIcon(index, slot, holder.getMobileState());
}
return null;
}
@VisibleForTesting
protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
StatusBarIcon icon) {
StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
view.set(icon);
mGroup.addView(view, index, onCreateLayoutParams());
return view;
}
.....
}
5、StatusBarIconControllerImpl
//继承StatusBarIconList
public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {
......
@Inject
public StatusBarIconControllerImpl(Context context, CommandQueue commandQueue) {
//config_statusBarIcons
super(context.getResources().getStringArray(
com.android.internal.R.array.config_statusBarIcons));
Dependency.get(ConfigurationController.class).addCallback(this);
.....
}
}
6、StatusBarIconList
在初始化的时候就已经定义好了所有的slots,然后从framework中加载出来,index就是string-array中的顺序。
public class StatusBarIconList {
private ArrayList<Slot> mSlots = new ArrayList<>();
public StatusBarIconList(String[] slots) {
final int N = slots.length;
for (int i=0; i < N; i++) {
mSlots.add(new Slot(slots[i], null));
}
}
7、config_statusBarIcons
<string-array name="config_statusBarIcons">
<item><xliff:g id="id">@string/status_bar_rotate</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_headset</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_data_saver</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_ime</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_sync_failing</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_sync_active</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_nfc</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_tty</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_speakerphone</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_zen</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_secure</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_clock</xliff:g></item>
</string-array>
<string translatable="false" name="status_bar_rotate">rotate</string>
<string translatable="false" name="status_bar_headset">headset</string>
<string translatable="false" name="status_bar_data_saver">data_saver</string>
<string translatable="false" name="status_bar_managed_profile">managed_profile</string>
<string translatable="false" name="status_bar_ime">ime</string>
<string translatable="false" name="status_bar_sync_failing">sync_failing</string>
<string translatable="false" name="status_bar_sync_active">sync_active</string>
<string translatable="false" name="status_bar_cast">cast</string>
<string translatable="false" name="status_bar_hotspot">hotspot</string>
<string translatable="false" name="status_bar_location">location</string>
<string translatable="false" name="status_bar_bluetooth">bluetooth</string>
<string translatable="false" name="status_bar_nfc">nfc</string>
<string translatable="false" name="status_bar_tty">tty</string>
<string translatable="false" name="status_bar_speakerphone">speakerphone</string>
<string translatable="false" name="status_bar_zen">zen</string>
<string translatable="false" name="status_bar_mute">mute</string>
<string translatable="false" name="status_bar_volume">volume</string>
<string translatable="false" name="status_bar_wifi">wifi</string>
<string translatable="false" name="status_bar_cdma_eri">cdma_eri</string>
<string translatable="false" name="status_bar_data_connection">data_connection</string>
<string translatable="false" name="status_bar_phone_evdo_signal">phone_evdo_signal</string>
<string translatable="false" name="status_bar_phone_signal">phone_signal</string>
<string translatable="false" name="status_bar_battery">battery</string>
<string translatable="false" name="status_bar_alarm_clock">alarm_clock</string>
<string translatable="false" name="status_bar_secure">secure</string>
<string translatable="false" name="status_bar_clock">clock</string>
<string translatable="false" name="status_bar_mobile">mobile</string>
<string translatable="false" name="status_bar_vpn">vpn</string>
<string translatable="false" name="status_bar_ethernet">ethernet</string>
<string translatable="false" name="status_bar_airplane">airplane</string>
好了,到这里我们的第一部分初始化流程就讲完了
四、状态显示流程
由上面的初始化流程我们可以知道,每个icon都对应了slot,slot数量比较多,我们就挑一个常见的Headset讲下,其他的流程都是大致一样的。
1、PhoneStatusBarPolicy
初始化注册了大量的监听
①init
// 初始化headset的slot
mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset);
/** Initialize the object after construction. */
public void init() {
// listen for broadcasts
IntentFilter filter = new IntentFilter();
// 注册headset状态变化的action
filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen);
mRingerModeTracker.getRingerMode().observeForever(observer);
mRingerModeTracker.getRingerModeInternal().observeForever(observer);
....
}
②BroadcastReceiver
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
case Intent.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
break;
}
break;
case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:
updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
TelecomManager.TTY_MODE_OFF));
break;
case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
case Intent.ACTION_MANAGED_PROFILE_REMOVED:
updateManagedProfile();
break;
//监听ACTION_HEADSET_PLUG
case AudioManager.ACTION_HEADSET_PLUG:
updateHeadsetPlug(context, intent);
break;
}
}
};
③updateHeadsetPlug
完成icon添加和状态监听,然后当收到对应的action变化的时候,更新headset icon
private void updateHeadsetPlug(Context context, Intent intent) {
boolean connected = intent.getIntExtra("state", 0) != 0;
boolean hasMic = intent.getIntExtra("microphone", 0) != 0;
if (connected) {
String contentDescription = mResources.getString(hasMic
? R.string.accessibility_status_bar_headset
: R.string.accessibility_status_bar_headphones);
//setIcon负责设置icon
mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.stat_sys_headset_mic
: R.drawable.stat_sys_headset, contentDescription);
//setIconVisibility则根据connected状态设置icon的可见性
mIconController.setIconVisibility(mSlotHeadset, true);
} else {
/*UNISOC: Add for bug 1130932 {@ */
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if (!audioManager.isWiredHeadsetOn()) {
//setIconVisibility则根据connected状态设置icon的可见性
mIconController.setIconVisibility(mSlotHeadset, false);
}
/* @} */
}
}
2、StatusBarIconControllerImpl
①setIcon
@Override
public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
//根据slot找到对应的index
int index = getSlotIndex(slot);
//用index取得对应的icon
StatusBarIconHolder holder = getIcon(index, 0);
if (holder == null) {
//第一次的时候,icon == null,所以通过new StatusBarIcon创建一个
StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
Icon.createWithResource(
mContext, resourceId), 0, 0, contentDescription);
holder = StatusBarIconHolder.fromIcon(icon);
//然后调用此方法
setIcon(index, holder);
} else {
holder.getIcon().icon = Icon.createWithResource(mContext, resourceId);
holder.getIcon().contentDescription = contentDescription;
handleSet(index, holder);
}
}
@Override
public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
boolean isNew = getIcon(index, holder.getTag()) == null;
super.setIcon(index, holder);
if (isNew) {
addSystemIcon(index, holder);
} else {
handleSet(index, holder);
}
}
private void addSystemIcon(int index, StatusBarIconHolder holder) {
String slot = getSlotName(index);
int viewIndex = getViewIndex(index, holder.getTag());
boolean blocked = mIconBlacklist.contains(slot);
//onIconAdded-》初始化流程里面StatusBarIconController中添加icon的入口
//到这里setIcon就添加完毕了
mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder));
}
②setIconVisibility
public void setIconVisibility(String slot, boolean visibility) {
int index = getSlotIndex(slot);
StatusBarIconHolder holder = getIcon(index, 0);
if (holder == null || holder.isVisible() == visibility) {
return;
}
holder.setVisible(visibility);
//icon已经创建成功了,icon非空,并且第一次是icon.visible != visibility的
//就会顺利的走到handleSet(index, icon)
handleSet(index, holder);
}
private void handleSet(int index, StatusBarIconHolder holder) {
int viewIndex = getViewIndex(index, holder.getTag());
//初始化流程里面StatusBarIconController中setIcon的地方
//到这里setIconVisibility也设置完毕了
mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
}
五、创建状态栏的窗口
1、StatusBarWindowController
mStatusBarWindowController.attach()
/**
* Adds the status bar view to the window manager.
*/
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.
mLp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
mBarHeight,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
mLp.setFitInsetsTypes(0 /* types */);
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
//WindowManager中添加view
//mStatusBarView = mSuperStatusBarViewFactory.getStatusBarWindowView();
//private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged.copyFrom(mLp);
}
2、SuperStatusBarViewFactory
/**
* Gets the inflated {@link StatusBarWindowView} from {@link R.layout#super_status_bar}.
* Returns a cached instance, if it has already been inflated.
*/
public StatusBarWindowView getStatusBarWindowView() {
if (mStatusBarWindowView != null) {
return mStatusBarWindowView;
}
//由其可知加载的布局来自于super_status_bar
mStatusBarWindowView =
(StatusBarWindowView) mInjectionInflationController.injectable(
LayoutInflater.from(mContext)).inflate(R.layout.super_status_bar,
/* root= */ null);
if (mStatusBarWindowView == null) {
throw new IllegalStateException(
"R.layout.super_status_bar could not be properly inflated");
}
return mStatusBarWindowView;
}
3、super_status_bar.xml
从前面可知这里会用CollapsedStatusBarFragment 替换 status_bar_container(状态栏通知显示区域),完成图标的显示
<!-- This is the status bar window. -->
<com.android.systemui.statusbar.phone.StatusBarWindowView
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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<FrameLayout
android:id="@+id/status_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
/>
<FrameLayout
android:id="@+id/car_top_navigation_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</com.android.systemui.statusbar.phone.StatusBarWindowView>



















