Android System Server大綱之StatusBarManagerService
狀態欄管理服務
下拉通知面板管理服務
前言
StatusBar即狀態欄,也就是下拉通知欄和快速設置共同組成。StatusBarManagerService也就是通知欄和快速設置的管理服務。StatusBar如下圖所示:
StatusBarManagerService啓動過程
在文章《Android系統之System Server大綱 》一文中的服務啓動過程可知,啓動StatusBarManagerService的代碼如下:
if (!disableSystemUI) {
traceBeginAndSlog("StartStatusBarManagerService");
try {
statusBar = new StatusBarManagerService(context, wm);
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
.....
}
這些代碼定義在文件frameworks/base/services/java/com/android/server/SystemServer.java中。
如上面的代碼,啓動StatusBarManagerService是由條件disableSystemUI來控制的,如果關閉SystemUI,那麼將不會啓動StatusBarManagerService。StatusBar是SystemUI組成的一部分,讀者對SystemUI不熟悉的可以參閱文章《 SystemUI架構分析》。
在文章《Android系統之System Server大綱 》一文中的服務啓動方式可知,StatusBarManagerService通過ServiceManager.addService()的方式啓動,那麼StatusBarManagerService將是實現了AIDL,本身支持Binder通信。如下:
public class StatusBarManagerService extends IStatusBarService.Stub {
}
這個類定義在文件frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java中。
還有一點是值得注意的,在實例化StatusBarManagerService的時候,還添加了一個LocalServices,代碼如下:
public StatusBarManagerService(Context context, WindowManagerService windowManager) {
mContext = context;
mWindowManager = windowManager;
LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
}
這個方法定義在文件frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java中。
在文章《Android系統之System Server大綱 》一文中有提到LocalServices的服務的特徵特點,LocalServices是進程內調用的服務,那麼StatusBarManagerInternal也就是在system_process進程中使用,StatusBarManagerInternal定義如下:
/**
* Private API used by NotificationManagerService.
*/
private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() {
}
這個變量定義在文件frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java中。
StatusBarManagerService的上層接口
在文章《Android System Server大綱之VibratorService》一文中有詳細參數瞭如果去獲取一個系統服務的上層接口,以及查看獲取到的上層接口的本質。獲取StatusBarManagerService的上層接口代碼:
StatusBarManager statusBarManager = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);
參閱文章《Android System Server大綱之VibratorService》,getSystemService(Context.STATUS_BAR_SERVICE)的調用過程如下:
registerService(Context.STATUS_BAR_SERVICE, StatusBarManager.class,
new CachedServiceFetcher<StatusBarManager>() {
@Override
public StatusBarManager createService(ContextImpl ctx) {
return new StatusBarManager(ctx.getOuterContext());
}});
這個方法定義在文件frameworks/base/core/java/android/app/SystemServiceRegistry.java中。
registerService()中的第一個參數Context.STATUS_BAR_SERVICE剛好和StatusBarManagerService啓動過程中的ServiceManager.addService()的第一參數Context.STATUS_BAR_SERVICE一致,所以getSystemService(Context.STATUS_BAR_SERVICE)所獲取到的對象實例就是StatusBarManager。
上層接口StatusBarManager概覽
注意:StatusBarManager類不向外暴露,也就是說第三方APP無法使用這個類的功能。
StatusBarManager定義如下:
public class StatusBarManager {
/**
* Collapse the notifications and settings panels.
*/
public void collapsePanels() {
......
}
/**
* Disable some features in the status bar.
*/
public void disable(int what) {
......
}
/**
* Expand the notifications panel.
*/
public void expandNotificationsPanel() {
......
}
/**
* Expand the settings panel.
*/
public void expandSettingsPanel() {
......
}
/**
* Expand the settings panel and open a subPanel, pass null to just open the settings panel.
*/
public void expandSettingsPanel(String subPanel) {
......
}
/**
* 移除StatusBar的icon.
*/
public void removeIcon(String slot) {
......
}
/**
* 設置StatusBar的icon.
*/
public void setIcon(String slot, int iconId, int iconLevel, String contentDescription) {
......
}
}
/**
* 設置StatusBar的icon的可見性.
*/
public void setIconVisibility(String slot, boolean visible) {
......
}
}
這個類定義在文件frameworks/base/core/java/android/app/StatusBarManager.java中。
StatusBarManager提供的功能不多,從上面的代碼的註釋,基本可以瞭解StatusBarManager所能提供的功能。
StatusBarManager的使用
上一章節中,對StatusBarManager的功能已經基本瞭解,下面以expandSettingsPanel()和setIcon()爲例,闡述這兩個功能的實現過程。
Demo演示和代碼
我們描繪一個界面,有兩個按鈕,一個是調用expandSettingsPanel()方法,一個是調用setIcon()方法,如下:
點擊按鈕“展開面板”時,下拉通知欄自動往下彈出到快速設置面板,如下圖所示:
點擊按鈕“設置ICON”時,顯示如下時鐘圖片顯示在status bar(狀態欄):
效果如下(請注意狀態欄圖標的變化):
demo的主要代碼如下:
public class MainActivity extends Activity {
private StatusBarManager mStatusBarManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲取StatusBarManager對象實例
mStatusBarManager = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);
}
public void buttonClick(View v) {
switch (v.getId()){
case R.id.btn1:
//展開快速設置面板
mStatusBarManager.expandSettingsPanel();
break;
case R.id.btn2:
//設置時鐘圖標
mStatusBarManager.setIcon("clock", R.drawable.clock, 0, null);
break;
}
}
}
深入實現過程
展開面板的過程
在上文中我們已經知道,StatusBarManager是StatusBarManagerService上層接口的封裝,因此,mStatusBarManager.expandSettingsPanel()實質調用的是StatusBarManagerService的expandSettingsPanel()方法,如下:
public void expandSettingsPanel(String subPanel) {
enforceExpandStatusBar();
if (mBar != null) {
try {
mBar.animateExpandSettingsPanel(subPanel);
} catch (RemoteException ex) {
}
}
}
這個方法定義在文件frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java中。
首先是調用了enforceExpandStatusBar()方法,該方法會檢測調用者是否有展開下拉麪板的權限,因此,調用者需要在Manifest文件中聲明權限android.permission.EXPAND_STATUS_BAR。
接着通過mBar調用了animateExpandSettingsPanel()方法,mBar是AIDL IStatusBar的實例,mBar在手機啓動的時候啓動SystemUI時被實例化,mBar的實質是IStatusBar的子類CommandQueue的實例,CommandQueue定製在文件frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java中。讀者如果不瞭解這個過程,可以參閱文章《SystemUI架構分析 》。animateExpandSettingsPanel()的實現如下:
public void animateExpandSettingsPanel(String subPanel) {
synchronized (mLock) {
mHandler.removeMessages(MSG_EXPAND_SETTINGS);
mHandler.obtainMessage(MSG_EXPAND_SETTINGS, subPanel).sendToTarget();
}
}
這個方法定義在文件frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java中。
animateExpandSettingsPanel()只是發送了一個消息,繼續跟蹤如下:
private final class H extends Handler {
public void handleMessage(Message msg) {
case MSG_EXPAND_SETTINGS:
mCallbacks.animateExpandSettingsPanel((String) msg.obj);
break;
}
}
這個方法定義在文件frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java中。
在處理消息的地方通過mCallbacks又調用了animateExpandSettingsPanel(),mCallbacks實質是PhoneStatusBar的實例,讀者如果不瞭解這個過程,可以參閱文章《SystemUI架構分析 》。因此mCallbacks.animateExpandSettingsPanel()的定義如下:
public void animateExpandSettingsPanel(String subPanel) {
......
if (subPanel != null) {
mQSPanel.openDetails(subPanel);
}
mNotificationPanel.expandWithQs();
if (false) postStartTracing();
}
這個方法定義在文件frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java中。
這裏調用了mNotificationPanel的expandWithQs()方法,mNotificationPanel是NotificationPanelView的實例對象,NotificationPanelView是View的間接子類,也就是通知面板。到此,本文就不再往下跟蹤這個過程了,已經超出本文所要闡述的內容。
設置StatusBar Icon的過程
StatusBarManager的setIcon()方法和展開面板的類似,在StatusBarManagerService中定義如下:
public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
String contentDescription) {
enforceStatusBar();
synchronized (mIcons) {
StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,
iconLevel, 0, contentDescription);
//Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
mIcons.put(slot, icon);
if (mBar != null) {
try {
mBar.setIcon(slot, icon);
} catch (RemoteException ex) {
}
}
}
}
這個方法定義在文件frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java中。
和展開面板類似,首先調用enforceStatusBar()檢測調用者是否有對應的權限,這裏需要檢測的權限是android.permission.STATUS_BAR。
接着把相關的數據封裝到StatusBarIcon對象中,在某些設備中,不允許自定義Status Bar Icon,也就是說參數slot必須要配置在config.xml文件中,如下:
<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_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_clock</xliff:g></item>
</string-array>
這些數據定義在文件frameworks/base/core/res/res/values/config.xml中。
也就是說,在某些設備,參數slot的值必須匹配上面數組中的某個item的值,才能正常顯示在狀態欄中。繼續往下看,mBar.setIcon(),和展開面板一樣,mBar.setIcon()的定義如下:
public void setIcon(String slot, StatusBarIcon icon) {
mIconController.setIcon(slot, icon);
}
這個方法定義在文件frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java中。
這裏通過mIconController繼續調用了setIcon()方法。到此,本文就不再往下闡述這個過程了。
總結
本文介紹了StatusBarManagerService以及其上層接口StatusBarManager的使用,但是,StatusBarManager對於第三方APP卻沒有公開這個接口。本文以展開設置面板和設置狀態欄圖標爲例,詳細闡述了從上層接口StatusBarManager到SystemUI的過程。除StatusBarManager外,StatusBarManagerService是持有SystemUI的遠程句柄mBar,所以系統還有其它需要和SystemUI“打交道”的功能,也會通過StatusBarManagerService。