Notification框架簡(jiǎn)介

目錄

  • Notification介紹
  • Notification框架原理
  • Notification框架服務(wù)端啟動(dòng)過程
  • SystemUI進(jìn)程啟動(dòng)和綁定NotificationManagerService服務(wù)端過程
  • Notification調(diào)用過程
  • Notification通知提示音響起過程
  • 總結(jié)

Notification介紹

功能作用

  1. 顯示接收到短息、即時(shí)消息等信息 (如QQ、微信、新浪、短信)
  2. 顯示客戶端的推送消息(如有新版本發(fā)布,廣告,推薦新聞等)
  3. 顯示正在進(jìn)行的事務(wù)(例如:后臺(tái)運(yùn)行的程序)(如音樂播放器、版本更新時(shí)候的下載進(jìn)度等)

通知的基本組成

Notification.png
1. 標(biāo)題
2. 大圖標(biāo)
3. 內(nèi)容文字 
4. 內(nèi)容信息
5. 小圖標(biāo)
6. 通知的時(shí)間(Timestamp,默認(rèn)為系統(tǒng)發(fā)出通知的時(shí)間,也可通過setWhen()來設(shè)置)

Notification框架原理

通知欄框架(Notificaiton),它適用于Android系統(tǒng)中交互事件的通知。它主要由三部分組成:系統(tǒng)服務(wù)端NotificationManagerService,通知顯示端SystemUI,還有創(chuàng)建和更新通知的App端。
NotificationManagerService作為框架的服務(wù)端,在系統(tǒng)啟動(dòng)時(shí)就會(huì)啟動(dòng)并在后臺(tái)運(yùn)行,顯示端SystemUI作為系統(tǒng)UI進(jìn)程,也是Notification框架的客戶端,在系統(tǒng)啟動(dòng)時(shí)也會(huì)啟動(dòng)并一直運(yùn)行。
其它模塊需調(diào)用Notification時(shí),只需要調(diào)用NotificationManager.notify(int,Notification)就可以發(fā)出通知。

模塊關(guān)系圖.png

根據(jù)Notification框架的原理,我們就分別按以下幾點(diǎn)來分析:

1. Notification框架服務(wù)端啟動(dòng)過程
2. SystemUI進(jìn)程啟動(dòng)和綁定NotificationManagerService服務(wù)端過程
3. Notification調(diào)用過程
4. Notification通知提示音響起過程

Notification框架相關(guān)的包

android/frameworks/base/services/java/com/android/server/SystemServer.java      //系統(tǒng)服務(wù)類啟動(dòng)的地方
android/frameworks/base/core/java/com/android/server/LocalServices.java         //系統(tǒng)服務(wù)類通信的輔助類
android/frameworks/base/core/java/android/service/notification/                 //Notification服務(wù)的接口類和監(jiān)聽類
android/frameworks/base/services/core/java/com/android/server/notification/     //NotificationManagerService服務(wù)相關(guān)的類
android/frameworks/base/core/java/android/app/      //NotificationManager、Notification類和INotificationManager.aidl的包
android/frameworks/base/packages/SystemUI/                                      //Notification顯示的UI進(jìn)程

Notification框架的關(guān)系類圖

類圖.jpg

Notification框架服務(wù)端啟動(dòng)過程

SystemServer啟動(dòng)的Notification管理服務(wù)類是NotificationManagerService,保存到SystemServiceManager的是NotificationManagerService服務(wù)對(duì)象中的INotificationManager.Stub(),但是綁定到ServiceManager中Context.NOTIFICATION_ SERVICE的服務(wù)類是NotificationManager,所有開發(fā)者通過Context.getSystemService(Context.NOTIFICATION_SERVICE)獲取回來的服務(wù)類不是NotificationManagerServiced服務(wù)對(duì)象,而是NotificationManager對(duì)象,需要再通過NotificationManager對(duì)象中的getService()方法,獲取SystemServiceManager系統(tǒng)服務(wù)管理對(duì)象中保存的INotificationManager.Stub()對(duì)象。這樣NotificationManager就能通過INotificationManager.Stub()對(duì)象和NotificationManagerService服務(wù)對(duì)象進(jìn)行遠(yuǎn)程通信了

系統(tǒng)啟動(dòng)時(shí),SystemServiceRegistry類中會(huì)把NotificationManager注冊(cè)為系統(tǒng)服務(wù)提供給其它服務(wù)或者應(yīng)用獲取系統(tǒng)服務(wù)使用,

/android/frameworks/base/core/java/android/app/SystemServiceRegistry.java 

private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
        new HashMap<Class<?>, String>();
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
        new HashMap<String, ServiceFetcher<?>>();

static {
    //...
    registerService(Context.NOTIFICATION_SERVICE, NotificationManager.class,
            new CachedServiceFetcher<NotificationManager>() {
        @Override
        public NotificationManager createService(ContextImpl ctx) {
            final Context outerContext = ctx.getOuterContext();
            return new NotificationManager(
                new ContextThemeWrapper(outerContext,
                        Resources.selectSystemTheme(0,
                                outerContext.getApplicationInfo().targetSdkVersion,
                                com.android.internal.R.style.Theme_Dialog,
                                com.android.internal.R.style.Theme_Holo_Dialog,
                                com.android.internal.R.style.Theme_DeviceDefault_Dialog,
                                com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog)),
                ctx.mMainThread.getHandler());
        }});
    //...
}

private static <T> void registerService(String serviceName, Class<T> serviceClass,
        ServiceFetcher<T> serviceFetcher) {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);   //把注冊(cè)的服務(wù)保存到列表對(duì)象中
}

public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

在Activity或者Service中,可以直接通過Context.getSystemService(Context.NOTIFICATION_SERVICE)就可以獲取系統(tǒng)服務(wù)使用。ActivityThread.performLaunchActivity()方法中創(chuàng)建ContextImpl對(duì)象并通過activity.attach()傳遞給Activity對(duì)象,再通過attachBaseContext()方法賦值給父類ContextWrapper中Context mBase對(duì)象,在Activity或者Service中調(diào)用getSystemService()方法,最終是調(diào)用ContextImpl中的getSystemService()方法。

//android/frameworks/base/core/java/android/app/ContextImpl.java 

@Override
public Object getSystemService(String name) {                   //從注冊(cè)到系統(tǒng)的服務(wù)列表中獲取對(duì)應(yīng)的服務(wù)
    //...
    Object registrySystemService = SystemServiceRegistry.getSystemService(this, name);
    return registrySystemService;
}

SystemServiceRegistry中注冊(cè)的NotificationManager對(duì)象,其實(shí)不是真正的Notification服務(wù),它只是一個(gè)調(diào)用接口對(duì)象,需要通過遠(yuǎn)程調(diào)用來實(shí)現(xiàn)和NotificationManagerService服務(wù)對(duì)象進(jìn)行通信,真正實(shí)現(xiàn)相應(yīng)的操作。以下是NotificationManagerService服務(wù)的啟動(dòng)流程。系統(tǒng)啟動(dòng)時(shí)會(huì)調(diào)用SystemServer來啟動(dòng)相應(yīng)的系統(tǒng)服務(wù)對(duì)象。

/android/frameworks/base/services/java/com/android/server/SystemServer.java 

private SystemServiceManager mSystemServiceManager;                                     //系統(tǒng)服務(wù)管理類

private void run() {
    //...
    //創(chuàng)建系統(tǒng)服務(wù)管理對(duì)象
    mSystemServiceManager = new SystemServiceManager(mSystemContext);
    //把系統(tǒng)服務(wù)管理對(duì)象保存到本地服務(wù)列表對(duì)象中,這樣在系統(tǒng)進(jìn)程中就可以通過LocalServices.getService(Class<T> type)
    //直接返回本地服務(wù)列表中的服務(wù)對(duì)象進(jìn)行使用
    LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
    //...
}

private void startOtherServices() {
    //...
    INotificationManager notification = null;
    //...
    mSystemServiceManager.startService(NotificationManagerService.class);               //啟動(dòng)NotificationManagerService服務(wù)對(duì)象
    //獲取NotificationManagerService服務(wù)對(duì)象的樁,用于進(jìn)行遠(yuǎn)程調(diào)用
    notification = INotificationManager.Stub.asInterface(
            ServiceManager.getService(Context.NOTIFICATION_SERVICE));
    //...
}


/android/frameworks/base/services/core/java/com/android/server/SystemServiceManager.java //系統(tǒng)服務(wù)管理類

public <T extends SystemService> T startService(Class<T> serviceClass) {
    final String name = serviceClass.getName();
    final T service;

    Constructor<T> constructor = serviceClass.getConstructor(Context.class);
    service = constructor.newInstance(mContext);    //實(shí)例化服務(wù)

    // Register it.
    mServices.add(service);     //注冊(cè)服務(wù)

    // Start it.
    service.onStart();          //啟動(dòng)服務(wù)

    return service;
}


//系統(tǒng)服務(wù)類,NotificationManagerService的父類
/android/frameworks/base/services/core/java/com/android/server/SystemService.java       

public abstract void onStart();     //抽象方法,在子類中實(shí)現(xiàn)

protected final void publishBinderService(String name, IBinder service) {
    publishBinderService(name, service, false);
}

protected final void publishBinderService(String name, IBinder service,
        boolean allowIsolated) {
    ServiceManager.addService(name, service, allowIsolated);    
}   

//綁定和發(fā)布key為Context.NOTIFICATION_SERVICE的NotificationManager系統(tǒng)服務(wù)類
protected final <T> void publishLocalService(Class<T> type, T service) {
    LocalServices.addService(type, service);
}   

//添加到內(nèi)部系統(tǒng)服務(wù)類的集合類LocalServices中
private SystemServiceManager getManager() {
    return LocalServices.getService(SystemServiceManager.class);
}

SystemServer中調(diào)用NotificationManagerService的onStart()方法來啟動(dòng)NotificationManagerService服務(wù)。

/android/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java 

public class NotificationManagerService extends SystemService {

    @Override
    public void onStart() {

        mListeners = new NotificationListeners();
    
        //綁定和發(fā)布key為Context.NOTIFICATION_SERVICE的NotificationManager系統(tǒng)服務(wù)類
        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
        //添加到內(nèi)部系統(tǒng)服務(wù)類的集合類LocalServices中
        publishLocalService(NotificationManagerInternal.class, mInternalService);
    }

    //只開放給系統(tǒng)內(nèi)部調(diào)用的API
    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
        @Override
        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
                String tag, int id, Notification notification, int[] idReceived, int userId) {
            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
                    idReceived, userId);
        }

        @Override
        public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
                int userId) {
            checkCallerIsSystem();
            synchronized (mNotificationList) {
                int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
                if (i < 0) {
                    return;
                }
                NotificationRecord r = mNotificationList.get(i);
                StatusBarNotification sbn = r.sbn;
                sbn.getNotification().flags =
                        (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
                mRankingHelper.sort(mNotificationList);
                mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
            }
        }
    };

    //INotificationManager.Stub用于與NotificationManager類和SystemUI進(jìn)程進(jìn)行遠(yuǎn)程通信的樁 (或者其它模塊)
    private final IBinder mService = new INotificationManager.Stub() {

        @Override
        public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
                Notification notification, int[] idOut, int userId) throws RemoteException {
            enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
                    Binder.getCallingPid(), tag, id, notification, idOut, userId);  //添加或更新Notification
        }

        @Override
        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
            checkCallerIsSystemOrSameApp(pkg);
            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
                    Binder.getCallingUid() == Process.SYSTEM_UID
                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
                    null);                                                          //刪除Notification
        }

        @Override
        public void cancelAllNotifications(String pkg, int userId) {
            checkCallerIsSystemOrSameApp(pkg);
            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
                    REASON_NOMAN_CANCEL_ALL, null);                                 //刪除所有Notification
        }

        @Override
        public void registerListener(final INotificationListener listener,
                final ComponentName component, final int userid) {
            enforceSystemOrSystemUI("INotificationManager.registerListener");
            //把NotificationListenerService對(duì)象注冊(cè)到ManagedServices服務(wù)管理子類NotificationListeners對(duì)象中
            mListeners.registerService(listener, component, userid);
        }

        @Override
        public void unregisterListener(INotificationListener listener, int userid) {
            //把NotificationListenerService對(duì)象從ManagedServices服務(wù)管理子類NotificationListeners對(duì)象中解除
            mListeners.unregisterService(listener, userid);
        }
    };

    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
            final int callingPid, final String tag, final int id, final Notification notification,
            int[] idOut, int incomingUserId) {
        //...
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                synchronized (mNotificationList) {
                    //...
                    //調(diào)用服務(wù)管理對(duì)象mListeners來更新所有注冊(cè)到mListeners中的NotificationListenerService對(duì)象
                    mListeners.notifyPostedLocked(n, oldSbn);
                    //實(shí)現(xiàn)播放notification的鈴聲,使led燈亮起來或者震動(dòng)等操作。buzz:嗡嗡叫,beep: 嘟嘟響,blink: 閃爍
                    buzzBeepBlinkLocked(r);
                }
            }
        });
        idOut[0] = id;
    }

    //ManagedServices服務(wù)管理類,就是用于一類服務(wù)的管理對(duì)象,例如:如需要管理幾個(gè)同一類的服務(wù)對(duì)象NotificationListenerService
    //只需要把相關(guān)的NotificationListenerService對(duì)象注冊(cè)到ManagedServices服務(wù)管理對(duì)象中,需要更新的時(shí)候,只需要調(diào)用
    //ManagedServices服務(wù)管理對(duì)象對(duì)注冊(cè)的NotificationListenerService對(duì)象進(jìn)行更新即可
    public class NotificationListeners extends ManagedServices {    

        public NotificationListeners() {
            super(getContext(), mHandler, mNotificationList, mUserProfiles);
        }

        @Override
        protected IInterface asInterface(IBinder binder) {
            return INotificationListener.Stub.asInterface(binder);
        }

        //新添加的方法,調(diào)用這個(gè)方法,就會(huì)更新所有注冊(cè)進(jìn)來的NotificationListenerService對(duì)象來更新
        //調(diào)用服務(wù)管理對(duì)象mListeners來更新所有注冊(cè)到mListeners中的NotificationListenerService對(duì)象
        public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
            StatusBarNotification sbnClone = null;
            StatusBarNotification sbnCloneLight = null;

            for (final ManagedServiceInfo info : mServices) {
                //...
                if (trim == TRIM_LIGHT && sbnCloneLight == null) {
                    sbnCloneLight = sbn.cloneLight();
                } else if (trim == TRIM_FULL) {
                    sbnClone = sbn.clone();
                }
                final StatusBarNotification sbnToPost =
                        (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;

                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyPosted(info, sbnToPost, update);  //調(diào)用更新通知方法
                    }
                });
            }
        }

        public void notifyRemovedLocked(StatusBarNotification sbn) {
            final StatusBarNotification sbnLight = sbn.cloneLight();
            for (final ManagedServiceInfo info : mServices) {
                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyRemoved(info, sbnLight, update);
                    }
                });
            }
        }
        
        //調(diào)用更新通知方法
        private void notifyPosted(final ManagedServiceInfo info,
                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
            final INotificationListener listener = (INotificationListener)info.service;
            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
            try {
                //回調(diào)NotificationListenerService對(duì)象中的方法onNotificationPosted(),在SystemUI中顯示Notification
                listener.onNotificationPosted(sbnHolder, rankingUpdate);
            } catch (RemoteException ex) {
                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
            }
        }

        private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
                NotificationRankingUpdate rankingUpdate) {
            if (!info.enabledAndUserMatches(sbn.getUserId())) {
                return;
            }
            final INotificationListener listener = (INotificationListener) info.service;
            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
            try {
                listener.onNotificationRemoved(sbnHolder, rankingUpdate);
            } catch (RemoteException ex) {
                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
            }
        }
        //...
    }
}

//系統(tǒng)服務(wù)類,NotificationManagerService的父類
/android/frameworks/base/services/core/java/com/android/server/notification/ManagedServices.java        

abstract protected IInterface asInterface(IBinder binder);

    protected final ArrayList<ManagedServiceInfo> mServices = new ArrayList<ManagedServiceInfo>();

    abstract public class ManagedServices {
    abstract protected void onServiceAdded(ManagedServiceInfo info);
    protected void onServiceRemovedLocked(ManagedServiceInfo removed) { }

    //把Service對(duì)象從ManagedServices服務(wù)管理類對(duì)象中刪除
    public void unregisterService(IInterface service, int userid) {
        checkNotNull(service);
        unregisterServiceImpl(service, userid);
    }

    //把Service對(duì)象注冊(cè)到ManagedServices服務(wù)管理類對(duì)象中
    public void registerService(IInterface service, ComponentName component, int userid) {
        checkNotNull(service);
        ManagedServiceInfo info = registerServiceImpl(service, component, userid);
        if (info != null) {
            onServiceAdded(info);
        }
    }
    
    private ManagedServiceInfo registerServiceImpl(final IInterface service,
            final ComponentName component, final int userid) {
        synchronized (mMutex) {
            try {
                ManagedServiceInfo info = newServiceInfo(service, component, userid,
                        true /*isSystem*/, null, Build.VERSION_CODES.LOLLIPOP);
                service.asBinder().linkToDeath(info, 0);
                mServices.add(info);
                return info;
            } catch (RemoteException e) {
            }
        }
        return null;
    }

    private void unregisterServiceImpl(IInterface service, int userid) {
        ManagedServiceInfo info = removeServiceImpl(service, userid);
        if (info != null && info.connection != null) {
            mContext.unbindService(info.connection);
        }
    }
}

SystemUI進(jìn)程啟動(dòng)和綁定NotificationManagerService服務(wù)端過程:

至此,Notification框架的服務(wù)端就已經(jīng)啟動(dòng)完畢,NotificationManagerService類只是管理Notification的邏輯,顯示端是在SystemUI進(jìn)程中實(shí)現(xiàn)的,那么NotificationManagerService服務(wù)對(duì)象和SystemUI進(jìn)程間是怎么通信的呢??jī)蓚€(gè)不同進(jìn)程間通信,很多同學(xué)可能就會(huì)想到Android的遠(yuǎn)程過程調(diào)用(Remote Procedure Call,RPC)方式來實(shí)現(xiàn),這種猜測(cè)是合理的,而且這里也的確是這么實(shí)現(xiàn)的。與很多其他的基于RPC的解決方案一樣,Android使用一種接口定義語(yǔ)言(Interface Definition Language,IDL)來公開服務(wù)的接口。所以我們先來看下NotificationManagerService服務(wù)和SystemUI進(jìn)程通信的服務(wù)接口文件:

/android/frameworks/base/core/java/android/app/INotificationManager.aidl 

interface INotificationManager
{
    void cancelAllNotifications(String pkg, int userId);

    void enqueueToast(String pkg, ITransientNotification callback, int duration);
    void cancelToast(String pkg, ITransientNotification callback);
    void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
            in Notification notification, inout int[] idReceived, int userId);                          //發(fā)出通知的方法
    void cancelNotificationWithTag(String pkg, String tag, int id, int userId);                         //取消通知的方法

    void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);                       //Settings中關(guān)閉應(yīng)用通知
    boolean areNotificationsEnabledForPackage(String pkg, int uid);                                     //判斷應(yīng)用是否可發(fā)通知

    void setPackagePriority(String pkg, int uid, int priority);
    int getPackagePriority(String pkg, int uid);

    void setPackagePeekable(String pkg, int uid, boolean peekable);
    boolean getPackagePeekable(String pkg, int uid);

    void setPackageVisibilityOverride(String pkg, int uid, int visibility);
    int getPackageVisibilityOverride(String pkg, int uid);

    // TODO: Remove this when callers have been migrated to the equivalent
    // INotificationListener method.
    StatusBarNotification[] getActiveNotifications(String callingPkg);
    StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count);

    //注冊(cè)INotificationListener服務(wù)的方法
    void registerListener(in INotificationListener listener, in ComponentName component, int userid);   
    //解除INotificationListener服務(wù)的方法
    void unregisterListener(in INotificationListener listener, int userid);                             

    void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
    void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);

    void setNotificationsShownFromListener(in INotificationListener token, in String[] keys);

    ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim);
    void requestHintsFromListener(in INotificationListener token, int hints);
    int getHintsFromListener(in INotificationListener token);
    void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
    int getInterruptionFilterFromListener(in INotificationListener token);
    void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
    void setInterruptionFilter(String pkg, int interruptionFilter);

    ComponentName getEffectsSuppressor();
    boolean matchesCallFilter(in Bundle extras);
    boolean matchesMessageFilter(in Bundle extras);
    boolean isSystemConditionProviderEnabled(String path);

    int getZenMode();
    ZenModeConfig getZenModeConfig();
    boolean setZenModeConfig(in ZenModeConfig config, String reason);
    oneway void setZenMode(int mode, in Uri conditionId, String reason);
    oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
    oneway void requestZenModeConditions(in IConditionListener callback, int relevance);
    boolean isNotificationPolicyAccessGranted(String pkg);
    NotificationManager.Policy getNotificationPolicy(String pkg);
    void setNotificationPolicy(String pkg, in NotificationManager.Policy policy);
    String[] getPackagesRequestingNotificationPolicyAccess();
    boolean isNotificationPolicyAccessGrantedForPackage(String pkg);
    void setNotificationPolicyAccessGranted(String pkg, boolean granted);

    byte[] getBackupPayload(int user);
    void applyRestore(in byte[] payload, int user);

    ParceledListSlice getAppActiveNotifications(String callingPkg, int userId);

    //***************************************************************//
    // ++ @noti.sysui [START] NotificationManager APIs for SEC ONLY  //
    //***************************************************************//
    boolean isNotificationInterruptable(String pkg, String opPkg, String tag, int id, 
                in Notification notification, in long time, int userId);
    void cancelSummaryNotificationWithTag(String pkg, String tag, int id, int userId);
    //***************************************************************///
    // -- @noti.sysui [START] NotificationManager APIs for SEC ONLY  //
    //***************************************************************//
        
    //WTL_EDM_START
    void clearAllNotificationsAsUser(int userId);
    //WTL_EDM_END

    void enqueueEdgeNotification(String pkg, String opPkg, int id, in Bundle extras, int userId);   //發(fā)側(cè)屏通知的方法
    void removeEdgeNotification(String pkg, int id, in Bundle extras, int userId);                  //刪除側(cè)屏通知的方法
}


/android/frameworks/base/core/java/android/service/notification/INotificationListener.aidl 

oneway interface INotificationListener
{
    void onListenerConnected(in NotificationRankingUpdate update);
    void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
            in NotificationRankingUpdate update);
    void onNotificationRemoved(in IStatusBarNotificationHolder notificationHolder,
            in NotificationRankingUpdate update);
    void onNotificationRankingUpdate(in NotificationRankingUpdate update);
    void onListenerHintsChanged(int hints);
    void onInterruptionFilterChanged(int interruptionFilter);

    void onEdgeNotificationPosted(String pkg, int id, in Bundle extra);
    void onEdgeNotificationRemoved(String pkg, int id, in Bundle extra);
}

這個(gè)接口類的服務(wù)端就是NotificationManagerService服務(wù)對(duì)象中的INotificationManager.Stub對(duì)象mService

private final IBinder mService = new INotificationManager.Stub(){};

客戶端可以通過以下方式來獲取和服務(wù)端通信的樁對(duì)象:

IBinder b = ServiceManager.getService(Context.NOTIFICATION_SERVICE);
INotificationManager service = INotificationManager.Stub.asInterface(b);

SystemUI進(jìn)程在初始化過程中,會(huì)創(chuàng)建一個(gè)NotificationListenerService服務(wù)類,服務(wù)對(duì)象中創(chuàng)建一個(gè)INotificationListener對(duì)象并通過遠(yuǎn)程過程調(diào)用把這個(gè)INotificationListener對(duì)象注冊(cè)到NotificationManagerService服務(wù)對(duì)象的服務(wù)管理類子類NotificationListeners對(duì)象mListeners中

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java 

private final NotificationListenerService mNotificationListener =
        new NotificationListenerService() {
    //...
    @Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
        if (sbn != null) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    String key = sbn.getKey();
                    boolean isUpdate = mNotificationData.get(key) != null;
                    if (isUpdate) {
                        updateNotification(sbn, rankingMap);                    //更新Notification
                    } else {
                        addNotification(sbn, rankingMap, null /* oldEntry */);  //添加Notification
                    }
                }
            });
        }
    }

    @Override
    public void onNotificationRemoved(StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
        if (sbn != null) {
            final String key = sbn.getKey();
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    removeNotification(key, rankingMap);                        //刪除Notification
                }
            });
        }
    }
    //...
};

public void start() {
    //...
    mNotificationListener.registerAsSystemService(mContext,
            new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
            UserHandle.USER_ALL);   //把NotificationListenerService對(duì)象注冊(cè)為系統(tǒng)服務(wù)并通過和NotificationManagerService服務(wù)遠(yuǎn)程通信
    //...
}


/android/frameworks/base/core/java/android/service/notification/NotificationListenerService.java 

public void registerAsSystemService(Context context, ComponentName componentName,
        int currentUser) throws RemoteException {
    mSystemContext = context;
    if (mWrapper == null) {
        mWrapper = new INotificationListenerWrapper();
    }
    INotificationManager noMan = getNotificationInterface();
    //通過遠(yuǎn)程過程把INotificationListener注冊(cè)到NotificationManagerService服務(wù)對(duì)象中,
    //這樣NotificationManagerService對(duì)象就可以通過INotificationListener通信對(duì)象
    //直接回調(diào)SystemUI進(jìn)程中的NotificationListenerService對(duì)象來操作顯示UI
    noMan.registerListener(mWrapper, componentName, currentUser);
    mCurrentUser = currentUser;
}

private final INotificationManager getNotificationInterface() {
    if (mNoMan == null) {
        mNoMan = INotificationManager.Stub.asInterface(
                ServiceManager.getService(Context.NOTIFICATION_SERVICE));   //這里就是上面客戶端可以獲取和服務(wù)端通信的樁對(duì)象的過程
    }
    return mNoMan;
}

public void onNotificationPosted(StatusBarNotification sbn) {
    // optional     //在SystemUI中BaseStatusBar的NotificationListenerService重寫了這個(gè)方法
}

private class INotificationListenerWrapper extends INotificationListener.Stub {
    @Override
    public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
            NotificationRankingUpdate update) {
        StatusBarNotification sbn;
        try {
            sbn = sbnHolder.get();
        } catch (RemoteException e) {
            return;
        }
        synchronized (mWrapper) {
            applyUpdate(update);
            try {
                if (sbn != null) {
                    NotificationListenerService.this.onNotificationPosted(sbn, mRankingMap);
                } else {
                    NotificationListenerService.this.onNotificationRankingUpdate(mRankingMap);
                }
            } catch (Throwable t) {
                Log.w(TAG, "Error running onNotificationPosted", t);
            }
        }
    }

    @Override
    public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,NotificationRankingUpdate update) {}

    @Override
    public void onListenerConnected(NotificationRankingUpdate update) {}

    @Override
    public void onNotificationRankingUpdate(NotificationRankingUpdate update) throws RemoteException {}

    @Override
    public void onListenerHintsChanged(int hints) throws RemoteException {}

    @Override
    public void onInterruptionFilterChanged(int interruptionFilter) throws RemoteException {}

    @Override
    public void onEdgeNotificationPosted(String pkg, int id, Bundle extra) {}

    @Override
    public void onEdgeNotificationRemoved(String pkg, int id, Bundle extra) {}
}

Notification調(diào)用過程

Notification調(diào)用過程可以從應(yīng)用開始,通過NotificationManager.notify()來發(fā)出通知,NotificationManager通過和NotificationManagerService服務(wù)對(duì)象通信,NotificationManagerService服務(wù)對(duì)象再利用通過NotificationListeners中監(jiān)聽的服務(wù)列表與SystemUI進(jìn)程啟動(dòng)的系統(tǒng)服務(wù)NotificationListenerService中的INotificationListener對(duì)象通信,就可以調(diào)用SystemUI進(jìn)程進(jìn)行顯示。

調(diào)用過程如下圖所示:

時(shí)序圖3.jpg

對(duì)應(yīng)的代碼,如以下所示:

應(yīng)用程序要發(fā)通知或取消通知,只需要獲取系統(tǒng)的通知管理服務(wù),調(diào)用notify或者cancel來操作通知即可。

NotificationManager nm = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);   //獲取通知管理服務(wù)
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.drawable.smallicon)
        .setContentTitle("This is Title")
        .setContentText("This is Content");
Notification n = b.build();                                                                     //創(chuàng)建Notification
nm.notify(ID, n);                                                                               //發(fā)通知

NotificationManager是管理通知的服務(wù)類,它負(fù)責(zé)與NotificationManagerService服務(wù)對(duì)象通信,并通過調(diào)用NotificationManagerService服務(wù)對(duì)象添加、更新、刪除通知等等,所支持的功能可以參照遠(yuǎn)程通信服務(wù)接口INotificationManager.aidl中公開的方法。

/android/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");  //獲取系統(tǒng)服務(wù)的樁對(duì)象
    sService = INotificationManager.Stub.asInterface(b);    //把樁對(duì)象轉(zhuǎn)化成遠(yuǎn)程通信對(duì)象
    return sService;
}

public void notify(int id, Notification notification)
{
    notify(null, id, notification);
}

public void notify(String tag, int id, Notification notification)
{
    int[] idOut = new int[1];
    INotificationManager service = getService();
    String pkg = mContext.getPackageName();
    //...
    Notification stripped = notification.clone();
    Builder.stripForDelivery(stripped);
    try {
        //調(diào)用遠(yuǎn)程服務(wù)對(duì)象的enqueueNotificationWithTag()方法來調(diào)用NotificationManagerService對(duì)象發(fā)出通知
        service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                stripped, idOut, UserHandle.myUserId());
    } catch (RemoteException e) {
    }
}

NotificationManagerService服務(wù)對(duì)象只是處理邏輯,顯示的邏輯是放在系統(tǒng)UI的進(jìn)程SystemUI中去的,所以NotificationManagerService服務(wù)對(duì)象處理完邏輯后,還需要遠(yuǎn)程調(diào)用SystemUI進(jìn)程去更新顯示。所以SystemUI進(jìn)程需要把INotificationListener服務(wù)對(duì)象注冊(cè)到NotificationManagerService服務(wù)對(duì)象中來,當(dāng)需要更新UI是,就可以通過INotificationListener服務(wù)對(duì)象回調(diào)SystemUI進(jìn)程中的方法來更新。

/android/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java 

    @Override
public void onStart() {
    mListeners = new NotificationListeners();
}

private final IBinder mService = new INotificationManager.Stub() {
    @Override
    public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
            Notification notification, int[] idOut, int userId) throws RemoteException {
        enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
                Binder.getCallingPid(), tag, id, notification, idOut, userId);  //添加或更新Notification
    }
}

void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
        final int callingPid, final String tag, final int id, final Notification notification,
        int[] idOut, int incomingUserId) {
    //...
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            synchronized (mNotificationList) {
                //...
                //調(diào)用服務(wù)管理對(duì)象mListeners來更新所有注冊(cè)到mListeners中的NotificationListenerService對(duì)象
                mListeners.notifyPostedLocked(n, oldSbn);
                //實(shí)現(xiàn)播放notification的鈴聲,使led燈亮起來或者震動(dòng)等操作。buzz:嗡嗡叫,beep: 嘟嘟響,blink: 閃爍
                buzzBeepBlinkLocked(r);
            }
        }
    });
    idOut[0] = id;
}

public class NotificationListeners extends ManagedServices {    

    public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
        for (final ManagedServiceInfo info : mServices) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    notifyPosted(info, sbnToPost, update);  //調(diào)用更新通知方法
                }
            });
        }
    }

    private void notifyPosted(final ManagedServiceInfo info,
            final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
        final INotificationListener listener = (INotificationListener)info.service;
        StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
        try {
            //回調(diào)NotificationListenerService對(duì)象中的方法onNotificationPosted(),在SystemUI中顯示Notification
            listener.onNotificationPosted(sbnHolder, rankingUpdate);
        } catch (RemoteException ex) {
            Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
        }
    }

INotificationListener服務(wù)對(duì)象是從BaseStatusBar對(duì)象中啟動(dòng)的系統(tǒng)服務(wù)BNotificationListenerService注冊(cè)到NotificationManagerService對(duì)象中的,當(dāng)NotificationManagerService服務(wù)對(duì)象通過INotificationListener服務(wù)對(duì)象回調(diào)SystemUI進(jìn)程中的方法時(shí),就可以調(diào)用BaseStatusBar對(duì)象中的方法來更新UI顯示了。

/android/frameworks/base/core/java/android/service/notification/NotificationListenerService.java    

private class INotificationListenerWrapper extends INotificationListener.Stub {
    @Override
    public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
            NotificationRankingUpdate update) {
        StatusBarNotification sbn;
        try {
            sbn = sbnHolder.get();
        } catch (RemoteException e) {
            return;
        }
        synchronized (mWrapper) {
            applyUpdate(update);
            try {
                if (sbn != null) {
                    NotificationListenerService.this.onNotificationPosted(sbn, mRankingMap);
                } else {
                    NotificationListenerService.this.onNotificationRankingUpdate(mRankingMap);
                }
            } catch (Throwable t) {
                Log.w(TAG, "Error running onNotificationPosted", t);
            }
        }
    }
}

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java 

private final NotificationListenerService mNotificationListener =
        new NotificationListenerService() {
    @Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (sbn != null) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    String key = sbn.getKey();
                    boolean isUpdate = mNotificationData.get(key) != null;
                    if (isUpdate) {
                        updateNotification(sbn, rankingMap);                    //更新Notification,
                    } else {
                        addNotification(sbn, rankingMap, null /* oldEntry */);  //添加Notification
                    }
                }
            });
        }
    }
};

PhoneStatusBar是BaseStatusBar的子類,實(shí)現(xiàn)了BaseStatusBar中的相關(guān)方法,addNotification()就是其中之一,這個(gè)方法是用來添加Notification和狀態(tài)欄通知圖標(biāo)的。

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java 

public void addNotification(StatusBarNotification notification, RankingMap ranking,
        Entry oldEntry) {
    //...
    Entry shadeEntry = createNotificationViews(notification);   //創(chuàng)建狀態(tài)欄通知圖標(biāo)和通知列表行布局
    //...
    //把創(chuàng)建的狀態(tài)欄通知圖標(biāo)和通知列表行布局分別添加到狀態(tài)欄通知欄和通知列表NotificatioStackScrollLayout中
    addNotificationViews(shadeEntry, ranking);
    setAreThereNotifications();
    //...
}

protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {   //創(chuàng)建狀態(tài)欄通知圖標(biāo)和通知列表行布局
    final StatusBarIconView iconView = createIcon(sbn);         //創(chuàng)建狀態(tài)欄通知圖標(biāo)
    if (iconView == null) {
        return null;
    }

    NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
    if (!inflateViews(entry, mStackScroller)) {                 //創(chuàng)建展開通知布局列表中的通知一行的布局
        handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
        return null;
    }
    return entry;
}

protected StatusBarIconView createIcon(StatusBarNotification sbn) {                 //創(chuàng)建狀態(tài)欄通知圖標(biāo)
    Notification n = sbn.getNotification();
    final StatusBarIconView iconView = new StatusBarIconView(mContext,
            sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);    //創(chuàng)建狀態(tài)欄通知圖標(biāo)布局View
    iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);

    final Icon smallIcon = n.getSmallIcon();
    if (smallIcon == null) {
        handleNotificationError(sbn,
                "No small icon in notification from " + sbn.getPackageName());
        return null;
    }
    final StatusBarIcon ic = new StatusBarIcon(
            sbn.getUser(),
            sbn.getPackageName(),
            smallIcon,
            n.iconLevel,
            n.number,
            n.tickerText);
    if (!iconView.set(ic)) {                                    //把StatusBarIcon傳到狀態(tài)欄通知圖標(biāo)布局View中
        handleNotificationError(sbn, "Couldn't create icon: " + ic);
        return null;
    }
    return iconView;
}

protected boolean inflateViews(Entry entry, ViewGroup parent) { //創(chuàng)建展開通知布局列表中的通知一行的布局
    final StatusBarNotification sbn = entry.notification;

    RemoteViews contentView = sbn.getNotification().contentView;              //通知布局的contentView布局
    RemoteViews bigContentView = sbn.getNotification().bigContentView;        //通知布局的bigContentView布局
    RemoteViews headsUpContentView = sbn.getNotification().headsUpContentView;//通知布局的headsUpContentView布局

    Notification publicNotification = sbn.getNotification().publicVersion;

    ExpandableNotificationRow row;                              //創(chuàng)建通知布局列表中的一行的布局
    if (entry.row != null) {
    } else {
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
        row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
                parent, false);
        row.setExpansionLogger(this, entry.notification.getKey());
        row.setGroupManager(mGroupManager);
    }
  
    NotificationContentView contentContainer = row.getPrivateLayout();
    NotificationContentView contentContainerPublic = row.getPublicLayout();
    NotificationContentView expandedKnox = row.getKnoxLayout();

    mNotificationClicker.register(row, sbn);

    View contentViewLocal = null;
    View bigContentViewLocal = null;
    View headsUpContentViewLocal = null;
    try {
        contentViewLocal = contentView.apply(
                sbn.getPackageContext(mContext),
                contentContainer,
                mOnClickHandler);                               //把contentView添加到通知布局列表中通知行容器中
        if (bigContentView != null) {
            bigContentViewLocal = bigContentView.apply(
                    sbn.getPackageContext(mContext),
                    contentContainer,
                    mOnClickHandler);                           //把bigContentView添加到通知布局列表中通知行容器中
        }
        if (headsUpContentView != null) {
            headsUpContentViewLocal = headsUpContentView.apply(
                    sbn.getPackageContext(mContext),
                    contentContainer,
                    mOnClickHandler);                           //把headsUpContentView添加到通知布局列表中通知行容器中
        }
    } catch (RuntimeException e) {
        return false;
    }

    View publicViewLocal = null;
    if (publicNotification != null) {
        try {
            publicViewLocal = publicNotification.contentView.apply(
                    sbn.getPackageContext(mContext),
                    contentContainerPublic, mOnClickHandler);
        } catch (RuntimeException e) {
            publicViewLocal = null;
        }
    }
    //...
    return true;
}

//把創(chuàng)建的狀態(tài)欄通知圖標(biāo)和通知列表行布局分別添加到狀態(tài)欄通知欄和通知列表NotificatioStackScrollLayout中
protected void addNotificationViews(Entry entry, RankingMap ranking) {
    if (entry == null) {
        return;
    }
    mNotificationData.add(entry, ranking);  //先把通知添加到NotificationData中去
    updateNotifications();//根據(jù)更新后的NotificationData數(shù)據(jù)更新狀態(tài)欄通知圖標(biāo)和通知列表NotificationStackScrollLayout布局
}

@Override
protected void updateNotifications() {
    mNotificationData.filterAndSort();      //過濾和排序通知的順序

    updateNotificationShade();              //添加或者更新NotificationStackScrollLayout中的Notification
    mIconController.updateNotificationIcons(mNotificationData); //添加或者更新狀態(tài)欄左上角通知欄圖標(biāo)

    mNotificationPanel.updateCarrierAndClearLayout();           //更新通信類型和清除布局
}

private void updateNotificationShade() {
    ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
    ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
    
    //把所有需要顯示的Notification添加到toShow列表中
    final int N = activeNotifications.size();
    for (int i=0; i<N; i++) {
        Entry ent = activeNotifications.get(i);
        toShow.add(ent.row);
    }

    //通過判斷mStackScroller中各個(gè)child是否在toShow列表;不在的話,就添加toRemove列表中,待會(huì)一起刪除
    ArrayList<View> toRemove = new ArrayList<>();
    for (int i=0; i< mStackScroller.getChildCount(); i++) {
        View child = mStackScroller.getChildAt(i);
        if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {//child是否在toShow列表
            toRemove.add(child);
        }
    }

    //把toRemove列表中需要?jiǎng)h除的child,都刪除掉
    for (View remove : toRemove) {
        mStackScroller.removeView(remove);
    }
    
    //判斷toShow列表中,有哪些是新添加的通知(可以通過通知View是否有父容器來判斷),新的通知就添加到mStackScroller中
    for (int i=0; i<toShow.size(); i++) {
        View v = toShow.get(i);
        if (v.getParent() == null) {    //這里通過通知View是否有父容器來判斷這條通知是否是新的
            mStackScroller.addView(v);  //新通知就添加到mStackScroller中
        }
    }

    //以上已經(jīng)把需要顯示或者刪除的通知都處理完了,但是還需要重新調(diào)整順序
    //從mStackScroller中順序判斷每個(gè)child的順序是否與toShow列表中的順序一樣
    //不一樣的,就把順序調(diào)整下
    int j = 0;
    for (int i = 0; i < mStackScroller.getChildCount(); i++) {
        View child = mStackScroller.getChildAt(i);
        ExpandableNotificationRow targetChild = toShow.get(j);
        if (child != targetChild) {     //順序不對(duì)
            mStackScroller.changeViewPosition(targetChild, i);  //調(diào)整順序
        }
        j++;
    }

    updateNotificationShadeForChildren();   //更新每個(gè)Group Notification中的child Notification
    updateRowStates();                      //更新Notification布局中的Item展開情況、dim情況和鎖屏狀態(tài)下的通知布局情況,很重要的一個(gè)方法
    updateClearAll();                       //更新清楚所有按鈕布局
    updateEmptyShadeView();                 //隱藏"No Notification"
    updateQsExpansionEnabled();             //關(guān)閉QS功能
    mShadeUpdates.check();
}


/android/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java 

public void filterAndSort() {
    mSortedAndFiltered.clear();         //清除mSortedAndFiltered列表

    final int N = mEntries.size();
    for (int i = 0; i < N; i++) {
        Entry entry = mEntries.valueAt(i);
        StatusBarNotification sbn = entry.notification;

        if (shouldFilterOut(sbn)) {     //判斷是否需要過濾掉
            continue;
        }

        mSortedAndFiltered.add(entry);  //添加到mSortedAndFiltered列表中
    }

    Collections.sort(mSortedAndFiltered, mRankingComparator);   //重新排序mSortedAndFiltered中的選項(xiàng)
}

StatusBarIconController是狀態(tài)欄圖標(biāo)控制的類,用來控制狀態(tài)欄通知圖標(biāo)顯示和系統(tǒng)圖標(biāo)顯示等等。

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java 

private IconMerger mNotificationIcons;      //狀態(tài)欄通知圖標(biāo)容器對(duì)象

public StatusBarIconController(Context context, View statusBar, View keyguardStatusBar,
        PhoneStatusBar phoneStatusBar) {
    //...
    mNotificationIcons = (IconMerger) statusBar.findViewById(R.id.notificationIcons);   //獲取狀態(tài)欄圖標(biāo)的容器類
    //...
}

public void updateNotificationIcons(NotificationData notificationData) {
    int iconSize = mContext.getResources().getDimensionPixelSize(R.dimen.notification_icon_view_width); //圖標(biāo)寬度
    final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
            iconSize + 2*mIconHPadding, mPhoneStatusBar.getStatusBarHeight());  //狀態(tài)欄圖標(biāo)的布局參數(shù)(寬度x高度)

    ArrayList<NotificationData.Entry> activeNotifications =
            notificationData.getActiveNotifications();
    final int N = activeNotifications.size();
    ArrayList<StatusBarIconView> toShow = new ArrayList<>(N);

    //把所有需要顯示的Notification添加到toShow列表中
    for (int i = 0; i < N; i++) {
        NotificationData.Entry ent = activeNotifications.get(i);
        //過濾環(huán)境通知,例如:插USB或者充電線時(shí)的通知,是不需要顯示狀態(tài)欄中的圖標(biāo)的
        if (notificationData.isAmbient(ent.key)
                && !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
            continue;
        }
        if (!PhoneStatusBar.isTopLevelChild(ent)) {                         //過濾分組的組圖標(biāo)
            continue;
        }
        if ((ent.notification.getNotification().secFlags
                & Notification.SEC_FLAG_HIDE_NOTIFICATION_ICON) !=0) {      //過濾掉設(shè)置了隱藏狀態(tài)欄圖標(biāo)的通知
            continue;
        }
        if(!mPhoneStatusBar.shouldShowOnIndicator(ent.notification.getKey())) { //過濾設(shè)置了隱藏圖標(biāo)的包的通知
            continue;
        }
        toShow.add(ent.icon);
    }

    //通過判斷mNotificationIcons中各個(gè)child是否在toShow列表;不在的話,就添加toRemove列表中,待會(huì)一起刪除
    ArrayList<View> toRemove = new ArrayList<>();
    for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
        View child = mNotificationIcons.getChildAt(i);
        if (!toShow.contains(child)) {                                      //child是否在toShow列表
            toRemove.add(child);
        }
    }

    //把toRemove列表中需要?jiǎng)h除的child,都刪除掉
    final int toRemoveCount = toRemove.size();
    for (int i = 0; i < toRemoveCount; i++) {
        mNotificationIcons.removeView(toRemove.get(i));
    }

    //判斷toShow列表中,有哪些是新添加的通知(可以通過通知View是否有父容器來判斷),新的通知就添加到mNotificationIcons中
    for (int i=0; i<toShow.size(); i++) {
        View v = toShow.get(i);
        if (v.getParent() == null) {                            //這里通過通知View是否有父容器來判斷這條通知是否是新的
            mNotificationIcons.addView(v, i, params);           //新通知就添加到mStackScroller中
        }
    }

    //以上已經(jīng)把需要顯示或者刪除的通知都處理完了,但是還需要重新調(diào)整順序
    //從mNotificationIcons中順序判斷每個(gè)child的順序是否與toShow列表中的順序一樣
    //不一樣的,就把順序調(diào)整下
    final int childCount = mNotificationIcons.getChildCount();
    for (int i = 0; i < childCount; i++) {
        View actual = mNotificationIcons.getChildAt(i);
        StatusBarIconView expected = toShow.get(i);
        if (actual == expected) {   //順序正確的就不處理
            continue;
        }
        mNotificationIcons.removeView(expected);    //把順序錯(cuò)誤的View先刪除
        mNotificationIcons.addView(expected, i);    //再添加到正確的順序位置上
    }

    applyNotificationIconsTint();                   //更新狀態(tài)欄圖標(biāo)的顏色
}


/android/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java 

public class IconMerger extends LinearLayout{       //狀態(tài)欄通知圖標(biāo)容器類

}

把狀態(tài)欄通知圖標(biāo)和通知行布局都分別添加到相應(yīng)的容器類后,顯示部分的邏輯也就完成了。

Notification通知提示音響起過程

SystemUI的RingtonePlayer服務(wù)通過IAudioService.setRingtonePlayer(IRingtonePlayer)把IRingtonePlayer實(shí)現(xiàn)的回調(diào)接口對(duì)象注冊(cè)到AudioService服務(wù)中,第三方App調(diào)用NotificationManager.notify()時(shí),會(huì)調(diào)用NotificationManagerService中的enqueueNotificationInternal方法中的buzzBeepBlinkLocked()方法,這個(gè)方法會(huì)通過IAudioService.getRingtonePlayer()獲取AudioServoce中的IRingtonePlayer對(duì)象,并調(diào)用回調(diào)方法來調(diào)用SystenUI.RingtonePlayer.mCallback的playAsync()來實(shí)現(xiàn)播放notification鈴聲、震動(dòng)、閃爍燈功能。

調(diào)用過程如下圖所示:

時(shí)序圖2.jpg

對(duì)應(yīng)的代碼,如以下所示:

啟動(dòng)AudioService服務(wù)過程,

/android/frameworks/base/services/java/com/android/server/SystemServer.java 

private void startOtherServices() {
    AudioService audioService = null;
    audioService = new AudioService(context);
    ServiceManager.addService(Context.AUDIO_SERVICE, audioService);
}

/android/frameworks/base/services/core/java/com/android/server/audio/AudioService.java 

public class AudioService extends IAudioService.Stub {

    private volatile IRingtonePlayer mRingtonePlayer;

    @Override
    public void setRingtonePlayer(IRingtonePlayer player) {
        mContext.enforceCallingOrSelfPermission(REMOTE_AUDIO_PLAYBACK, null);
        mRingtonePlayer = player;
    }

    @Override
    public IRingtonePlayer getRingtonePlayer() {
        return mRingtonePlayer;
    }
}

啟動(dòng)SystemUI的RingtonePlayer服務(wù)過程,

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java 

@Override
public void onCreate() {
    super.onCreate();
    ((SystemUIApplication) getApplication()).startServicesIfNeeded();
}


/android/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java 

private final Class<?>[] SERVICES = new Class[] {
        com.android.systemui.tuner.TunerService.class,
        com.android.systemui.keyguard.KeyguardViewMediator.class,
        com.android.systemui.recents.Recents.class,
        com.android.systemui.volume.VolumeUI.class,
        com.android.systemui.statusbar.SystemBars.class,
        com.android.systemui.usb.StorageNotification.class,
        com.android.systemui.power.PowerUI.class,
        com.android.systemui.media.RingtonePlayer.class,        //RingtinePlayer服務(wù)
        com.android.systemui.keyboard.KeyboardUI.class,
};

public void startServicesIfNeeded() {
    //...
    final int N = SERVICES.length;
    for (int i=0; i<N; i++) {
        Class<?> cl = SERVICES[i];
        mServices[i] = (SystemUI)cl.newInstance();
        mServices[i].mContext = this;
        mServices[i].mComponents = mComponents;
        mServices[i].start();                                   //啟動(dòng)RingtinePlayer服務(wù)
        if (mBootCompleted) {
            mServices[i].onBootCompleted();
        }
    }
    //...
}

RingtonePlayer是運(yùn)行在SystemUI進(jìn)程的服務(wù),RingtonePlayer服務(wù)會(huì)獲取AudioService服務(wù)對(duì)象,并把IRingtonePlayer對(duì)象傳給AudioService服務(wù)對(duì)象中去,其它模塊通過AudioService.getRingtonePlayer()來控制RingtonePlayer服務(wù)播放提示音的功能。

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java 

//相關(guān)的遠(yuǎn)程過程調(diào)用接口:
/android/frameworks/base/media/java/android/media/IRingtonePlayer.aidl
/android/frameworks/base/media/java/android/media/IAudioService.aidl

private IAudioService mAudioService;

private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);
private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>();

@Override
public void start() {
    mAsyncPlayer.setUsesWakeLock(mContext);

    mAudioService = IAudioService.Stub.asInterface(
            ServiceManager.getService(Context.AUDIO_SERVICE));  //獲取AudioService服務(wù)的遠(yuǎn)程對(duì)象

    mAudioService.setRingtonePlayer(mCallback); //把IRingtonePlayer傳到AudioService服務(wù)對(duì)象中去
}

//每個(gè)Client代表一個(gè)播放的任務(wù)
private class Client implements IBinder.DeathRecipient {
    private final IBinder mToken;
    private final Ringtone mRingtone;

    public Client(IBinder token, Uri uri, UserHandle user, AudioAttributes aa) {
        mToken = token;

        mRingtone = new Ringtone(getContextForUser(user), false);   //創(chuàng)建一個(gè)Ringtone對(duì)象
        mRingtone.setAudioAttributes(aa);
        mRingtone.setUri(uri);
    }

    @Override
    public void binderDied() {
        if (LOGD) Log.d(TAG, "binderDied() token=" + mToken);
        synchronized (mClients) {
            mClients.remove(mToken);
        }
        mRingtone.stop();
    }
}

private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() {
    @Override
    public void play(IBinder token, Uri uri, AudioAttributes aa, float volume, boolean looping)
            throws RemoteException {
        Client client;
        synchronized (mClients) {
            client = mClients.get(token);
            if (client == null) {
                final UserHandle user = Binder.getCallingUserHandle();
                client = new Client(token, uri, user, aa);
                token.linkToDeath(client, 0);
                mClients.put(token, client);
            }
        }
        client.mRingtone.setLooping(looping);
        client.mRingtone.setVolume(volume);
        client.mRingtone.play();                                            //同步播放提示音
    }

    @Override
    public void stop(IBinder token) {
        Client client;
        synchronized (mClients) {
            client = mClients.remove(token);
        }
        if (client != null) {
            client.mToken.unlinkToDeath(client, 0);
            client.mRingtone.stop();
        }
    }

    @Override
    public boolean isPlaying(IBinder token) {
        Client client;
        synchronized (mClients) {
            client = mClients.get(token);
        }
        if (client != null) {
            return client.mRingtone.isPlaying();
        } else {
            return false;
        }
    }

    @Override
    public void setPlaybackProperties(IBinder token, float volume, boolean looping) {
        Client client;
        synchronized (mClients) {
            client = mClients.get(token);
        }
        if (client != null) {
            client.mRingtone.setVolume(volume);
            client.mRingtone.setLooping(looping);
        }
    }

    @Override
    public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa) {
        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
            throw new SecurityException("Async playback only available from system UID.");
        }
        mAsyncPlayer.play(getContextForUser(user), uri, looping, aa);   //把播放任務(wù)放到異步隊(duì)列中
    }

    @Override
    public void stopAsync() {
        if (LOGD) Log.d(TAG, "stopAsync()");
        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
            throw new SecurityException("Async playback only available from system UID.");
        }
        mAsyncPlayer.stop();
    }

    @Override
    public String getTitle(Uri uri) {
        final UserHandle user = Binder.getCallingUserHandle();
        return Ringtone.getTitle(getContextForUser(user), uri,
                false /*followSettingsUri*/, false /*allowRemote*/);
    }

    @Override
    public IBinder setOnCompletionListener(INotificationPlayerOnCompletionListener l) {
        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
            throw new SecurityException(
                    "setOnCompletionListener only available from system UID.");
        }
        return mAsyncPlayer.setOnCompletionListener(l);
    }
};

如果需要調(diào)用異步方式來播放提示音,就需要用到NotificationPlayer這個(gè)類,它會(huì)把播放任務(wù)保存到隊(duì)列中,通過線程一個(gè)一個(gè)為隊(duì)列中每個(gè)提示音播放任務(wù)創(chuàng)建一個(gè)播放線程并執(zhí)行。

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java 

private static final class Command {    //一個(gè)異步任務(wù)對(duì)應(yīng)一個(gè)Command對(duì)象
    int code;
    Context context;
    Uri uri;
    boolean looping;
    AudioAttributes attributes;
    long requestTime;
}

private LinkedList<Command> mCmdQueue = new LinkedList();       //異步任務(wù)隊(duì)列對(duì)象

public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
    Command cmd = new Command();
    cmd.requestTime = SystemClock.uptimeMillis();
    cmd.code = PLAY;
    cmd.context = context;
    cmd.uri = uri;
    cmd.looping = looping;
    cmd.attributes = attributes;
    synchronized (mCmdQueue) {
        enqueueLocked(cmd);             //把異步任務(wù)加入隊(duì)列中
        mState = PLAY;
    }
}

private void enqueueLocked(Command cmd) {
    mCmdQueue.add(cmd);                 //把異步任務(wù)加入隊(duì)列對(duì)象mCmdQueue中
    if (mThread == null) {              //如果執(zhí)行任務(wù)線程已經(jīng)停止,創(chuàng)建線程并開始執(zhí)行
        acquireWakeLock();
        mThread = new CmdThread();      //創(chuàng)建執(zhí)行任務(wù)的線程
        mThread.start();                //啟動(dòng)線程
    }
}

private final class CmdThread extends java.lang.Thread {
    CmdThread() {
        super("NotificationPlayer-" + mTag);
   }

    public void run() {
        while (true) {
            Command cmd = null;

            synchronized (mCmdQueue) {
                cmd = mCmdQueue.removeFirst();  //取出隊(duì)列中第一個(gè)任務(wù)
            }

            switch (cmd.code) {                 //任務(wù)類型
            case PLAY:                          //播放提示音任務(wù)
                startSound(cmd);                //播放提示音
                break;
            case STOP:                          //停止提示音任務(wù)
                if (mPlayer != null) {
                    long delay = SystemClock.uptimeMillis() - cmd.requestTime;
                    if (delay > 1000) {         //如果異步時(shí)間超過1s,打印出來,方便調(diào)試
                        Log.w(mTag, "Notification stop delayed by " + delay + "msecs");
                    }
                    mPlayer.stop();
                    mPlayer.release();
                    mPlayer = null;
                    synchronized(mQueueAudioFocusLock) {
                        if (mAudioManagerWithAudioFocus != null) {
                            mAudioManagerWithAudioFocus.abandonAudioFocus(null);
                            mAudioManagerWithAudioFocus = null;
                        }
                    }
                    if((mLooper != null)
                            && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
                        mLooper.quit();
                    }
                } else {
                    Log.w(mTag, "STOP command without a player");
                }
                break;
            }

            synchronized (mCmdQueue) {
                if (mCmdQueue.size() == 0) {
                    mThread = null;
                    releaseWakeLock();
                    return;
                }
            }
        }
    }
}

private void startSound(Command cmd) {
    try {
        synchronized(mCompletionHandlingLock) {
            if((mLooper != null)
                    && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
                mLooper.quit();
            }
            //為每個(gè)播放任務(wù)創(chuàng)建一個(gè)播放提示音線程
            mCompletionThread = new CreationAndCompletionThread(cmd);
            synchronized(mCompletionThread) {
                mCompletionThread.start();  //開始執(zhí)行線程
                mCompletionThread.wait();   //等待線程執(zhí)行完
            }
        }
        long delay = SystemClock.uptimeMillis() - cmd.requestTime;
        if (delay > 1000) {                 //如果異步時(shí)間超過1s,打印出來,方便調(diào)試
            Log.w(mTag, "Notification sound delayed by " + delay + "msecs");
        }
    }
    catch (Exception e) {
        Log.w(mTag, "error loading sound for " + cmd.uri, e);
    }
}

private final class CreationAndCompletionThread extends Thread {
    public Command mCmd;
    public CreationAndCompletionThread(Command cmd) {
        super();
        mCmd = cmd;
    }
    public void run() {
        Looper.prepare();
        mLooper = Looper.myLooper();
        synchronized(this) {
            AudioManager audioManager =
                (AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
            try {
                MediaPlayer player = new MediaPlayer();         //創(chuàng)建一個(gè)MediaPlayer對(duì)象
                player.setAudioAttributes(mCmd.attributes);
                player.setDataSource(mCmd.context, mCmd.uri);
                player.setLooping(mCmd.looping);
                player.prepare();
                if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)
                        && (mCmd.uri.getEncodedPath().length() > 0)) {
                    if (!audioManager.isMusicActiveRemotely()) {
                        synchronized(mQueueAudioFocusLock) {
                            if (mAudioManagerWithAudioFocus == null) {
                                if (mDebug) Log.d(mTag, "requesting AudioFocus");
                                if (mCmd.looping) {             //獲取長(zhǎng)時(shí)間音頻焦點(diǎn)
                                    audioManager.requestAudioFocus(null,
                                            AudioAttributes.toLegacyStreamType(mCmd.attributes),
                                            AudioManager.AUDIOFOCUS_GAIN);  
                                } else {                        //獲取臨時(shí)音頻焦點(diǎn)
                                    audioManager.requestAudioFocus(null,
                                            AudioAttributes.toLegacyStreamType(mCmd.attributes),
                                            AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
                                }
                                mAudioManagerWithAudioFocus = audioManager;
                            } else {
                                if (mDebug) Log.d(mTag, "AudioFocus was previously requested");
                            }
                        }
                    }
                }
                player.setOnCompletionListener(NotificationPlayer.this);
                player.setOnErrorListener(NotificationPlayer.this);
                player.start();                             //開始播放提示音
                if (mPlayer != null) {
                    mPlayer.release();
                }
                mPlayer = player;
            }
            catch (Exception e) {
                Log.w(mTag, "error loading sound for " + mCmd.uri, e);
            }
            this.notify();
        }
        Looper.loop();
    }
};

應(yīng)用或者服務(wù)通過NotificationManager調(diào)用notify()發(fā)出通過,NotificationManager通過和NotificationManagerService服務(wù)通信,調(diào)用發(fā)出通知,并調(diào)用buzzBeepBlinkLocked()方法來觸發(fā)通知提示音、震動(dòng)或者led閃爍。

/android/frameworks/base/core/java/android/app/NotificationManager.java

public void notify(int id, Notification notification)
{
    notify(null, id, notification);
}

public void notify(String tag, int id, Notification notification)
{
    INotificationManager service = getService();
    service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
            stripped, idOut, UserHandle.myUserId());
}


/android/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java 

private final IBinder mService = new INotificationManager.Stub() {
    @Override
    public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
            Notification notification, int[] idOut, int userId) throws RemoteException {

        enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
                Binder.getCallingPid(), tag, id, notification, idOut, userId);
    }
}

void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
        final int callingPid, final String tag, final int id, final Notification notification,
        int[] idOut, int incomingUserId) {
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            synchronized (mNotificationList) {
                final StatusBarNotification n = new StatusBarNotification(
                        pkg, opPkg, id, tag, callingUid, callingPid, score, notification, user);
                NotificationRecord r = new NotificationRecord(n, score);
                NotificationRecord old = mNotificationsByKey.get(n.getKey());
                //調(diào)用服務(wù)管理對(duì)象mListeners來更新所有注冊(cè)到mListeners中的NotificationListenerService對(duì)象
                mListeners.notifyPostedLocked(n, oldSbn);
                //實(shí)現(xiàn)播放notification的提示音,使led燈亮起來或者震動(dòng)等操作。buzz:嗡嗡叫,beep: 嘟嘟響,blink: 閃爍
                buzzBeepBlinkLocked(r);
            }
        }
    });
}

//實(shí)現(xiàn)播放notification的提示音,使led燈亮起來或者震動(dòng)等操作。buzz:嗡嗡叫,beep: 嘟嘟響,blink: 閃爍
private void buzzBeepBlinkLocked(NotificationRecord record) {
    boolean buzz = false;   //震動(dòng)
    boolean beep = false;   //提示音
    boolean blink = false;  //閃爍
    final Notification notification = record.sbn.getNotification();
    // Should this notification make noise, vibe, or use the LED?
    final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
    final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
    // If we're not supposed to beep, vibrate, etc. then don't.
    final String disableEffects = disableNotificationEffects(record);
    if (disableEffects != null) {
        ZenLog.traceDisableEffects(record, disableEffects);
    }
    boolean smsRingtone = getContext().getResources().getBoolean(
            com.android.internal.R.bool.config_sms_ringtone_incall);
    if ((disableEffects == null || (smsRingtone && mInCall))    //通話期間來短信鈴聲
            && (!(record.isUpdate
                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
            && (record.getUserId() == UserHandle.USER_ALL ||
                record.getUserId() == currentUser ||
                mUserProfiles.isCurrentProfile(record.getUserId()))
            && canInterrupt
            && mSystemReady
            && mAudioManager != null) {     //判斷是否需要提示音或者震動(dòng)

        //這里會(huì)發(fā)出一個(gè)Notification的AccessibilityEvent,這樣在輔助服務(wù)中才能收到這個(gè)事件,
        //微信搶紅包的功能就是通過這個(gè)輔助事件才得以實(shí)現(xiàn)的
        sendAccessibilityEvent(notification, record.sbn.getPackageName());

        //提示音相關(guān)
        if(!isPrayModeNotiOn(mContext)) {       //判斷是否是在祈禱模式下
            //判斷Notification是否設(shè)置了使用默認(rèn)提示音或者Notification設(shè)置的提示音文件剛好是默認(rèn)提示音文件
            final boolean useDefaultSound =
                   (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
                           Settings.System.DEFAULT_NOTIFICATION_URI
                                   .equals(notification.sound);
            Uri soundUri = null;
            boolean hasValidSound = false;
            if (useDefaultSound) {  //判斷是否使用默認(rèn)Notification提示音
                soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;    //獲取默認(rèn)提示音Uri
                ContentResolver resolver = getContext().getContentResolver();
                hasValidSound = Settings.System.getString(resolver,
                       Settings.System.NOTIFICATION_SOUND) != null;     //默認(rèn)提示音文件是否有效
            } else if (notification.sound != null) {
                soundUri = notification.sound;                          //獲取Notification的提示音Uri
                hasValidSound = (soundUri != null);                     //提示音文件是否有效
            }

            if (hasValidSound) {    //判斷是否是有效提示音
                boolean looping =
                        (notification.flags & Notification.FLAG_INSISTENT) != 0;  //是否設(shè)置了循環(huán)
                AudioAttributes audioAttributes;
                if (notification.audioAttributes != null) {
                    audioAttributes = notification.audioAttributes;
                } else {
                    audioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
                }
                mSoundNotificationKey = record.getKey();
                //Notification提示音音量是否為0和音頻焦點(diǎn)是否可用
                if ((mAudioManager.getStreamVolume(
                        AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
                            && !mAudioManager.isAudioFocusExclusive()) {
                    final long identity = Binder.clearCallingIdentity();
                    try {
                        //-----這里或通過AudioService來獲取IRingtonePlayer對(duì)象,最終會(huì)調(diào)用SystemUI進(jìn)程中的RingtonePlayer來播放提示音
                        final IRingtonePlayer player = mAudioManager.getRingtonePlayer();   
                        if (player != null) {
                            player.playAsync(soundUri, record.sbn.getUser(), looping,
                                    audioAttributes);                                       //異步方式播放提示音
                            beep = true;
                            mIsPlaying = true;

                                if (mMethodRingtonePlayer != null) {                        //翻轉(zhuǎn)停止播放提示音功能的相關(guān)邏輯
                                if (mOverTurnPlayer != null && mOverTurnPlayer.isEnable() && !mOverTurnPlayer.isRegister()) {
                                    mOverTurnPlayer.register();
                                    //...                   //翻轉(zhuǎn)停止播放提示音功能的相關(guān)邏輯,這里先不贅述
                                }
                            }
                        }
                    } catch (RemoteException e) {
                    } finally {
                        Binder.restoreCallingIdentity(identity);
                    }
                }
            }

            //震動(dòng)相關(guān)
            final boolean hasCustomVibrate = notification.vibrate != null;              //Notification是否設(shè)置震動(dòng)
            final boolean convertSoundToVibration =
                       !hasCustomVibrate
                    && hasValidSound
                    && (mAudioManager.getRingerMode()
                               == AudioManager.RINGER_MODE_VIBRATE);                    //震動(dòng)模式下,需要把通知提示音變成震動(dòng)

            final boolean useDefaultVibrate =
                    (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;        //Notification是否設(shè)置了默認(rèn)震動(dòng)

            final boolean useHaptic = doesItUseHaptic(notification.haptic);             //Notification是否設(shè)置了觸屏反饋

            if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate ||useHaptic)
                    && !(mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) {   //判斷是否需要震動(dòng)
                mVibrateNotificationKey = record.getKey();
                buzz = true;    
                doVibrate((useDefaultVibrate || convertSoundToVibration), useHaptic, notification); //執(zhí)行震動(dòng)
            }
        }

        if(beep || buzz) {
            AccessibilityManager accManager = AccessibilityManager.getInstance(getContext());
            accManager.onFlashNotification(record.getNotification().category);
        }
    }

    //led燈相關(guān)
    boolean wasShowLights = mLights.remove(record.getKey());
    if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
        mLights.add(record.getKey());
        updateLightsLocked();       //更新led燈閃爍
        if (mUseAttentionLight) {
            mAttentionLight.pulse();
        }
        blink = true;
    } else if (wasShowLights) {
        updateLightsLocked();       //更新led燈閃爍
    }

    if ((buzz || beep || blink) && !isPrayModeNotiOn(mContext)) {
        EventLogTags.writeNotificationAlert(record.getKey(),
                buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
        mHandler.post(mBuzzBeepBlinked);
    }
}

private void doVibrate(boolean useDefaultVibrate, boolean useHaptic, Notification n) {
    if (useHaptic) {
        mVibrator.vibrate(n.haptic, -1, null,
               Vibrator.MagnitudeTypes.NotificationMagnitude);          //執(zhí)行震動(dòng)
    } else if (useDefaultVibrate) {
        long identity = Binder.clearCallingIdentity();
        try {
            mVibrator.vibrate(HapticFeedbackConstants.VIBE_NOTIFICATION, -1, null,
                    Vibrator.MagnitudeTypes.NotificationMagnitude);     //執(zhí)行震動(dòng)
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    } else {
        long identity2 = Binder.clearCallingIdentity();
        try {
            mVibrator.vibrate(n.vibrate, ((n.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1, null,
                    Vibrator.MagnitudeTypes.NotificationMagnitude);     //執(zhí)行震動(dòng)
        } finally {
            Binder.restoreCallingIdentity(identity2);
        }
    }
}

NotificationManagerService服務(wù)中就是通過AudioService服務(wù)獲取IRingtonePlayer對(duì)象來控制SystemUI進(jìn)程進(jìn)行播放提示音的,

final IRingtonePlayer player = mAudioManager.getRingtonePlayer();   
player.playAsync(soundUri, record.sbn.getUser(), looping, audioAttributes); 

SystemUI進(jìn)程播放提示音的流程如下:

/android/frameworks/base/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java   

private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);

private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() {
    @Override
    public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa) {
        mAsyncPlayer.play(getContextForUser(user), uri, looping, aa);   //異步播放提示音
    }
}


/android/frameworks/base/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java   

public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
    Command cmd = new Command();
    cmd.requestTime = SystemClock.uptimeMillis();
    cmd.code = PLAY;
    cmd.context = context;
    cmd.uri = uri;
    cmd.looping = looping;
    cmd.attributes = attributes;
    synchronized (mCmdQueue) {
        enqueueLocked(cmd);                         //把異步播放提示音放到播放任務(wù)隊(duì)列中
        mState = PLAY;
    }
}

private void enqueueLocked(Command cmd) {
    mCmdQueue.add(cmd);
    if (mThread == null) {
        acquireWakeLock();
        mThread = new CmdThread();                  //創(chuàng)建處理隊(duì)列任務(wù)的線程
        mThread.start();                            //啟動(dòng)線程開始處理隊(duì)列中的任務(wù)
    }
}

private final class CmdThread extends java.lang.Thread {
    public void run() {
        while (true) {
            Command cmd = null;
            synchronized (mCmdQueue) {
                cmd = mCmdQueue.removeFirst();      //取隊(duì)列中第一條播放任務(wù)
            }
            switch (cmd.code) {
            case PLAY:
                startSound(cmd);                    //開始處理播放提示音任務(wù)
                break;
        }
    }
}

private void startSound(Command cmd) {
    synchronized(mCompletionHandlingLock) {
        mCompletionThread = new CreationAndCompletionThread(cmd);
        synchronized(mCompletionThread) {
            mCompletionThread.start();              //執(zhí)行播放提示音線程
            mCompletionThread.wait();
        }
    }          
}

private final class CreationAndCompletionThread extends Thread {
    public Command mCmd;
    public CreationAndCompletionThread(Command cmd) {
        super();
        mCmd = cmd;
    }
    public void run() {
        Looper.prepare();
        mLooper = Looper.myLooper();
        synchronized(this) {
            try {
                MediaPlayer player = new MediaPlayer();
                player.setAudioAttributes(mCmd.attributes);
                player.setDataSource(mCmd.context, mCmd.uri);
                player.setLooping(mCmd.looping);
                player.prepare();
                player.start();                     //播放提示音
                if (mPlayer != null) {
                    mPlayer.release();
                }
            catch (Exception e) {
                Log.w(mTag, "error loading sound for " + mCmd.uri, e);
            }
            this.notify();
        }
        Looper.loop();
    }
};

總結(jié)

要理解Notification框架的原理,需要理清NotificationManager和NotificationManagerService之間是怎么通信的,NotificationManagerService和SystemUI之間是怎么通信的。INotificationManager.Stub不僅作為NotificationManagerService和NotificationManager的遠(yuǎn)程通信方式,也是NotificationManagerService和SystemUI的遠(yuǎn)程通信方式,不過SystemUI進(jìn)程會(huì)創(chuàng)建和啟動(dòng)一個(gè)系統(tǒng)服務(wù)NotificationListenerService,這個(gè)系統(tǒng)服務(wù)通過INotificationManager.Stub把INotificationListener.Stub對(duì)象遠(yuǎn)程傳給NotificationListenerService服務(wù)中,讓NotificationListenerService服務(wù)通過INotificationListener.Stub對(duì)象和系統(tǒng)服務(wù)NotificationListenerService通信,系統(tǒng)服務(wù)NotificationListenerService再調(diào)用SystemUI進(jìn)程來更新UI。

通信圖.jpg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 原文出處: http://www.androidchina.net/6174.html Notification在...
    木木00閱讀 12,530評(píng)論 3 32
  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說閱讀 12,321評(píng)論 6 13
  • 為深入學(xué)習(xí)貫徹黨的十八大精神,進(jìn)一步明確就業(yè)創(chuàng)業(yè)工作重點(diǎn),切實(shí)抓好學(xué)院學(xué)生就業(yè)創(chuàng)業(yè)工作,幫助學(xué)生樹立創(chuàng)業(yè)意識(shí),提升...
    LD丹哥閱讀 299評(píng)論 0 0
  • 天氣說冷就冷,不過,還算沒有冷得猝不及防。 每天清早天微亮出門送小煙,也會(huì)常遇一團(tuán)團(tuán)的霧。尤其行至橋上的時(shí)候,霧氣...
    煙煙閱讀 423評(píng)論 0 1
  • 最近項(xiàng)目中需要獲取一些權(quán)限,于是就大致整理搜集了下,都是基于系統(tǒng)最基本的 這些都是不需要獲取用戶權(quán)限的 手機(jī)型號(hào) ...
    iSongWei閱讀 2,737評(píng)論 0 5

友情鏈接更多精彩內(nèi)容