工作需要更改SystemUI,记录一下Android4.0的SystemUI分析

Android的启动是从内核启动–>Android System启动–>Launcher启动。

SystemUI的代码位置,路径:Source/frameworks/base/packages/SystemUI

先从AndroidManifest.xml开始看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.systemui"
coreApp="true"
android:sharedUserId="android.uid.system"
android:process="system"
>
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.MANAGE_USB" />
<application
android:persistent="true"
android:allowClearUserData="false"
android:allowBackup="false"
android:hardwareAccelerated="true"
android:label="@string/app_label"
android:icon="@drawable/ic_launcher_settings">
<!-- 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"
/>
<!-- started from PhoneWindowManager
TODO: Should have an android:permission attribute -->
<service android:name=".screenshot.TakeScreenshotService"
android:process=":screenshot"
android:exported="false" />
<service android:name=".LoadAverageService"
android:exported="true" />
<service android:name=".ImageWallpaper"
android:permission="android.permission.BIND_WALLPAPER"
android:exported="true" />
<receiver android:name=".BootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
... ...
</application>
</manifest>

截取一部分,主要看SystemUIService,在SystemServer.java找到关键代码:

1
2
3
4
5
6
7
static final void startSystemUi(Context context) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
Slog.d(TAG, "Starting service: " + intent);
context.startService(intent);
}

SystemServer启动大致流程图:

init->ServiceManager->Zygote->SystemServer->SystemUI->...

SystemUIService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public void onCreate() {
// Pick status bar or system bar.
IWindowManager wm = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
try {
SERVICES[0] = wm.canStatusBarHide()
? R.string.config_statusBarComponent
: R.string.config_systemBarComponent;
} catch (RemoteException e) {
Slog.w(TAG, "Failing checking whether status bar can hide", e);
}
final int N = SERVICES.length;
mServices = new SystemUI[N];
for (int i=0; i<N; i++) {
Class cl = chooseClass(SERVICES[i]);
Slog.d(TAG, "loading: " + cl);
try {
mServices[i] = (SystemUI)cl.newInstance();
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
}
mServices[i].mContext = this;
Slog.d(TAG, "running: " + mServices[i]);
mServices[i].start();
}
}

通过AIDL的方式获取了WindowManager的对象wm,并调用其方法canStatusBarHide()来判断当前设备的类型,也就是说如果我们使用的Phone那么后续就会加载StatusBar和NivagationBar;而如果我们设备类型是Tablet(TV)之类的(可以在配置文档里面配置),就会加载CombiedBar。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Determine whether the status bar can hide based on the size
// of the screen. We assume sizes > 600dp are tablets where we
// will use the system bar.
int shortSizeDp = shortSize
* DisplayMetrics.DENSITY_DEFAULT
/ DisplayMetrics.DENSITY_DEVICE;
mStatusBarCanHide = shortSizeDp < 600;
mStatusBarHeight = mContext.getResources().getDimensionPixelSize(
mStatusBarCanHide
? com.android.internal.R.dimen.status_bar_height
: com.android.internal.R.dimen.system_bar_height);
mHasNavigationBar = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_showNavigationBar);

通过shortSizeDp来判断当前设备的类型,如果当前屏幕的shortSizeDp<600dp,则系统会认为该设备是phone反之则认为是tablet。 这样就得知,service[0]根据canstatusbarhide()的判断,将存放r.string.config_statusbarcomponent或者r.string.config_systembarcomponent。=""

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
final int N = SERVICES.length;
mServices = new SystemUI[N];
for (int i=0; i<N; i++) {
Class cl = chooseClass(SERVICES[i]);
Slog.d(TAG, "loading: " + cl);
try {
mServices[i] = (SystemUI)cl.newInstance();
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
}
mServices[i].mContext = this;
Slog.d(TAG, "running: " + mServices[i]);
mServices[i].start();
}

这时候就启动PhoneStatusBar.start()和PowerUI.start()。关注PhoneStatusBar.start()方法。
PhoneStatusBar.java

1
2
3
4
5
6
7
8
9
10
11
@Override
public void start() {
mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
super.start(); // calls makeStatusBarView()
addNavigationBar();
//addIntruderView();
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy = new PhoneStatusBarPolicy(mContext);
}

跟踪super.start()->StatusBar.java的start()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public void start() {
// First set up our views and stuff.
View sb = makeStatusBarView();
// Connect in to the status bar manager service
StatusBarIconList iconList = new StatusBarIconList();
ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
mCommandQueue = new CommandQueue(this, iconList);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
int[] switches = new int[7];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
try {
mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
switches, binders);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
disable(switches[0]);
setSystemUiVisibility(switches[1]);
topAppWindowChanged(switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(binders.get(0), switches[3], switches[4]);
setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
// Set up the initial icon state
int N = iconList.size();
int viewIndex = 0;
for (int i=0; i<N; i++) {
StatusBarIcon icon = iconList.getIcon(i);
if (icon != null) {
addIcon(iconList.getSlot(i), i, viewIndex, icon);
viewIndex++;
}
}
// Set up the initial notification state
N = notificationKeys.size();
if (N == notifications.size()) {
for (int i=0; i<N; i++) {
addNotification(notificationKeys.get(i), notifications.get(i));
}
} else {
Log.wtf(TAG, "Notification list length mismatch: keys=" + N
+ " notifications=" + notifications.size());
}
// Put up the view
final int height = getStatusBarHeight();
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
// We use a pixel format of RGB565 for the status bar to save memory bandwidth and
// to ensure that the layer can be handled by HWComposer. On some devices the
// HWComposer is unable to handle SW-rendered RGBX_8888 layers.
PixelFormat.RGB_565);
// the status bar should be in an overlay if possible
final Display defaultDisplay
= ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
// We explicitly leave FLAG_HARDWARE_ACCELERATED out of the flags. The status bar occupies
// very little screen real-estate and is updated fairly frequently. By using CPU rendering
// for the status bar, we prevent the GPU from having to wake up just to do these small
// updates, which should help keep power consumption down.
lp.gravity = getStatusBarGravity();
lp.setTitle("StatusBar");
lp.packageName = mContext.getPackageName();
lp.windowAnimations = R.style.Animation_StatusBar;
WindowManagerImpl.getDefault().addView(sb, lp);
if (SPEW) {
Slog.d(TAG, "Added status bar view: gravity=0x" + Integer.toHexString(lp.gravity)
+ " icons=" + iconList.size()
+ " disabled=0x" + Integer.toHexString(switches[0])
+ " lights=" + switches[1]
+ " menu=" + switches[2]
+ " imeButton=" + switches[3]
);
}
mDoNotDisturb = new DoNotDisturb(mContext);
}

在这里,完成了SystemUI的整个初始化以及设置过程:首先获取需要在StatusBar上显示的各种icons。然后初始化一些属性,最后通过WindowManager的addView方法将StatusBar显示出来。

时序图:

workflow