Android System Server大綱之StatusBarManagerService

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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章