Android系統通知服務的啓動流程 和 功能實現(基於Android 10)

原創不易,轉載請標明出處。若閱讀過程當中發現問題,請不吝指教,比心~java

前言

這是一個基於 Android 10 源碼,全面分析 Android通知系統實現原理 的系列,這是第二篇,全系列將覆蓋:android

寫在前面

爲了讓讀者更全面地瞭解 系統通知服務總體流程,這一篇咱們會先簡單介紹 手機是如何在啓動的過程當中拉起NMS服務的,涉及到 Zygote進程的啓動 和 System進程啓動;而後介紹 NMS服務的功能實現 以及 如何獲取到NMS服務數組

簡單說明:下文出現的簡寫bash

NM -> NotificationManager
NMS -> NotificationManagerService
Sysui -> SystemUI
複製代碼

NMS系統通知服務的啓動流程

相關類:app

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
frameworks/base/services/java/com/android/server/SystemServer.java
複製代碼

咱們知道(不知道就僞裝知道吧),手機啓動的過程當中,init進程 是第一個啓動的進程,該進程在啓動的過程當中會去啓動一個叫 Zygote的進程,Zygote進程在啓動的過程當中會去建立一個虛擬機對象,後續其餘進程的啓動則是直接從Zygote進程fork,從而達到每一個進程都擁有一個獨立虛擬機的目的,這是題外話了。其中 System進程 就是 Zygote進程在啓動的過程當中fork出來的一個進程,這是一個系統進程,負責在開機的時候啓動各類核心系統服務,例如AMS、PMS、NMS等常見的服務。框架

下面來看看Zygote是如何一步步啓動 NMS服務的吧:socket

Zygote進程在啓動過程當中fork出了System進程

/*frameworks/base/core/java/com/android/internal/os/ZygoteInit.java*/
    public static void main(String argv[]) {
        ......
        try {
            ......
            boolean startSystemServer = false;
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) {
                    startSystemServer = true;
                }
                ......
            }
            if (startSystemServer) {
                Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
                ......
            }
            ......
    }
複製代碼

ZygoteInit.main函數中的argv數組會帶有start-system-server字段,代表Zygote進程須要啓動System進程,上面startSystemServer=true,也就是接下來執行的是forkSystemServer方法:ide

/*frameworks/base/core/java/com/android/internal/os/ZygoteInit.java*/
    // Prepare the arguments and forks for the system server process.
    private static Runnable forkSystemServer(String abiList, String socketName,ZygoteServer zygoteServer) {
        ......
        String args[] = {
                "--setuid=1000",
                "--setgid=1000",
                "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
                        + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
                "--capabilities=" + capabilities + "," + capabilities,
                "--nice-name=system_server",
                "--runtime-args",
                "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
                "com.android.server.SystemServer", // 步驟1:指定 System進程 的入口類爲 com.android.server.SystemServer 的 main 方法
        };
        ZygoteArguments parsedArgs = null;
        int pid;
        try {
            ......
            // 步驟2:經過 Zygote 去 fork System 進程
            pid = Zygote.forkSystemServer(
                    parsedArgs.mUid, parsedArgs.mGid,
                    parsedArgs.mGids,
                    parsedArgs.mRuntimeFlags,
                    null,
                    parsedArgs.mPermittedCapabilities,
                    parsedArgs.mEffectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }

        // 步驟3:pid = 0 表示子進程fork成功
        if (pid == 0) {
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }
            zygoteServer.closeServerSocket();
            return handleSystemServerProcess(parsedArgs);
        }
        return null;
    }
複製代碼

有三個主要的步驟:函數

  • 1.指定com.android.server.SystemServer 的 main函數System進程的程序入口
  • 2.經過 Zygote 去 fork System 進程,並返回一個進程id,咱們知道,當fork返回的id爲0時表示子進程fork成功,也就是步驟3要執行的
  • 3.當pid=0時,會執行步驟3中的handleSystemServerProcess函數,該函數會進一步完成System進程的啓動工做,往下走的主要工做就是去到native層了,這裏就再也不跟蹤了(不行了)

System進程的啓動:SystemServer的main函數

上面分析了,Zygote進程啓建立了System進程並指定了程序入口爲SystemServer.main(),咱們接下來看看這個入口函數作了什麼事:post

  • 直接調用SystemServer().run()
/*frameworks/base/services/java/com/android/server/SystemServer.java*/
    // The main entry point from zygote.
    public static void main(String[] args) {
        new SystemServer().run();
    }
複製代碼
  • run方法在作好各類準備工做以後,就開始開啓各類系統服務,NMS的啓動在startOtherServices()函數中執行
/*frameworks/base/services/java/com/android/server/SystemServer.java*/
    private void run() {
        try {
            ......
        // Start services.
        try {
            traceBeginAndSlog("StartServices");
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
            SystemServerInitThreadPool.shutdown();
        }
        ......
    }
複製代碼

經過SystemServiceManager.startService()啓動NMS服務,SystemServiceManager是一個專門用於建立、啓動以及管理各系統服務生命週期事件的管理類

/*frameworks/base/services/java/com/android/server/SystemServer.java*/
    private void startOtherServices() {
        ......
        mSystemServiceManager.startService(NotificationManagerService.class);
        ......
    }
複製代碼

startService函數的內容很簡單,最終直接執行到NMS服務的onStart函數。

/*frameworks/base/services/core/java/com/android/server/SystemServiceManager.java*/
    public void startService(@NonNull final SystemService service) {
        mServices.add(service);
        try {
            service.onStart();
        }
        ......
    }
複製代碼

到這裏,NMS服務的啓動流程和啓動時機就分析完了,稍微總結下:NMS服務是一個常駐在System進程中的系統服務,在手機系統啓動的過程當中被拉起。


NMS服務的功能實現 以及 如何獲取到NMS服務

這一節咱們會從框架上來看看NMS服務的大致功能與實現,先簡單說明下:

  • 1.INotificationManager.aidl定義了系統通知服務指望暴露給其餘客戶端的各類接口;
  • 2.NotificationManagerService實現了INotificationManager.aidl這個接口,並將Binder代理對象註冊到了ServiceManager中去,以便其餘服務與應用調用,如NotificationManager

相關類以下:

1. frameworks/base/core/java/android/app/NotificationManager.java
2. frameworks/base/core/java/android/app/INotificationManager.aidl
3. frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
複製代碼

下面開始分析,咱們上面講過,系統在啓動NMS服務的時候,調用了 NotificationManagerService.onStart() 函數,這是系統通知服務的入口,該函數主要完成了:

  • 1.NotificationManagerService 中一系列成員變量的賦值(經過init(...)方法),如AMS、PMS類的代理對象的獲取,各類輔助類實例的建立等等
  • 2.註冊各類廣播,監聽所需狀態,如亮滅屏廣播、用戶切換廣播、應用添加刪除廣播等等
  • 3.將一個實現了INotificationManager.aidl接口的Binder代理對象mService註冊到ServiceManager

上面屢次提到INotificationManager.aidl這個接口文件,咱們看下這個接口:該文件主要定義了這麼幾種類型的接口:

/*frameworks/base/core/java/android/app/INotificationManager.aidl*/
- 通知的添加(包括更新)、刪除操做,即咱們前面講到的 `notify、cancel` 等接口的實現,如`enqueueNotificationWithTag(...)`
- 通知屬性的設置和判斷,如是否容許顯示某應用的通知、是否容許顯示通知圓點(桌面圖標右上角上的角標)等,如`setShowBadge(String pkg, int uid, boolean showBadge)`
- 通知`channel`的增刪改查,如`createNotificationChannels(...)`
- 通知列表的獲取如`StatusBarNotification[] getActiveNotifications(String callingPkg)`
- 通知狀態監聽相關接口,如`registerListener(in INotificationListener listener, in ComponentName component, int userid)`
- ......
複製代碼

一、2點的內容不展開講,感興趣的童鞋直接看下NMS的onStart方法便可,這裏咱們直接看第3點,咱們來看看INotificationManager.aidl的實現和註冊過程:

NMS 實現 INotificationManager.aidl

/*frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java*/
    final IBinder mService = new INotificationManager.Stub() {
        @Override
        public void enqueueToast(String pkg, ITransientNotification callback, int duration, int displayId) {......}
        
        @Override
        ......
    }
複製代碼

Binder代理對象註冊到ServiceManager

/*frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java*/
    @Override
    public void onStart() {
        // 各成員變量的初始化 與 註冊各類廣播監聽器
        ......
        // 註冊到ServiceManager
        publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
                DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
    }

    protected final void publishBinderService(String name, IBinder service,
            boolean allowIsolated, int dumpPriority) {
        ServiceManager.addService(name, service, allowIsolated, dumpPriority);
    }
複製代碼

其中 NOTIFICATION_SERVICE 的值爲"notification", 這樣其餘服務或應用就能夠經過ServiceManager來獲取到 name 爲 notificationINotificationManager Binder代理對象了, 從而達到與其交互的目的了,例如咱們上面講到的,開發者操做通知的入口類NotificationManager,look look:

/*frameworks/base/core/java/android/app/NotificationManager.java*/
    private static INotificationManager sService;
    static public INotificationManager getService()
    {
        if (sService != null) {
            return sService;
        }
        IBinder b = ServiceManager.getService("notification");
        sService = INotificationManager.Stub.asInterface(b);
        return sService;
    }

複製代碼

能夠看到NotificationManager直接經過查詢得到ServiceManager中 name 爲 notification 的 Binder對象,並經過asInterface方法將這個服務端的Binder對象轉換成客戶端所需的 AIDL 接口類型的對象,而後保存到成員變量sService中,以供後續調用。

須要注意的是,ServiceManager中註冊的服務是不支持普通應用獲取的,咱們知道,當咱們須要發送通知的時候,發送的入口以下:

NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(notiTag, notiId, notification);
複製代碼

這裏getSystemService(String name)Activity的方法,並非ServiceManager提供的,Android爲了不這些系統服務直接與用戶打交道,統一提供了代理類供用戶獲取服務。

NotificationManagerService面向用戶的代理類爲NotificationManagerActivityManagerService面向用戶的代理類爲ActivityManager,這些代理類均被註冊在一個叫SystemServiceRegistry的管理類中(代碼路徑frameworks/base/core/java/android/app/SystemServiceRegistry.java),當咱們調用ActivitygetSystemService(String name)方法去獲取系統服務時,最終會獲取到SystemServiceRegistry中的對應代理類,從而咱們就能夠利用這些代理類來間接與各類系統服務交互了。

相關文章
相關標籤/搜索