当前位置: 首页 > 工具软件 > book-ms-ui > 使用案例 >

SystemUI之USB2(Framework UEvent -> SystemUI)

向弘懿
2023-12-01

第二节  Framework UEvent -> SystemUI

逻辑主要在Framework里面的NotificationManagerService.java

UsbDeviceManager.java 到framework的NotificationManagerService.java,这里面涉及到通知的分类和排序,通知的显示风格等等,然后通过NotificationListenerService来通知SystemUI.

在SystemUI注册服务,然后在framework层回调,贯通SystemUI以及Framework。

UsbDeviceManager.java中——》

mNotificationManager.notifyAsUser(null, id, notification,                             UserHandle.ALL);

调用NotificationManager的notifyAsUser()往上上传notification,跟到方法里面看下,

路径:framework/base/core/java/android/app/NotificationManager.java

 INotificationManager service = getService();//获取IBinder对象

我们看下Notification的addFieldsFromContext方法做了哪些事情:
路径:frameworks/base/core/java/android/app/Notification.java
public static void addFieldsFromContext(Context context, Notification notification) {  
    addFieldsFromContext(context.getApplicationInfo(), context.getUserId(), notification);  
}  
  
/** 
 * @hide 
 */  
public static void addFieldsFromContext(ApplicationInfo ai, int userId,  
        Notification notification) {  
    notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);//"android.appinfo"  
    notification.extras.putInt(EXTRA_ORIGINATING_USERID, userId);//"android.originatingUserId"  
}  
addFieldsFromContext方法主要实现将当前应用的ApplicationInfo对象保存到“android.appinfo”字段,将当前的userId保存到“android.originatingUserId”字段中。
 
fixLegacySmalIcon,notify函数会判断notification是否有small icon,如果没有设icon或small icon,用notify方法时会抛出异常。fixLegacySmallIcon方法如下:
    private void fixLegacySmallIcon(Notification n, String pkg) {  
        if (n.getSmallIcon() == null && n.icon != 0) {  
            n.setSmallIcon(Icon.createWithResource(pkg, n.icon));  
        }  
    }  
完成上面的操作后,调用通知管理类NotificationManagerService的enqueueNotificationWithTag方法,

    @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);  
    } 
我们看到在enqueueNotificationWithTag方法中调用了enqueueNotificationInternal方法,这里就是通知处理的核心了。


[java] view plain copy
在CODE上查看代码片派生到我的代码片


    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) {  
        if (DBG) {  
            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id  
                    + " notification=" + notification);  
        }  
        checkCallerIsSystemOrSameApp(pkg);  
        // 校验UID  
        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));  
        final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);  
      
        final int userId = ActivityManager.handleIncomingUser(callingPid,  
                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);  
        final UserHandle user = new UserHandle(userId);  
      
        // Fix the notification as best we can.  
        try {  
            final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(  
                    pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,  
                    (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);  
            Notification.addFieldsFromContext(ai, userId, notification);  
        } catch (NameNotFoundException e) {  
            Slog.e(TAG, "Cannot create a context for sending app", e);  
            return;  
        }  
      
        mUsageStats.registerEnqueuedByApp(pkg);  
      
        // Limit the number of notifications that any given package except the android  
        // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.  
        // 这里会做一个限制,除了系统级别的应用之外,其他应用的notification数量会做限制,用来防止DOS攻击导致的泄露  
        if (!isSystemNotification && !isNotificationFromListener) {  
            synchronized (mNotificationList) {  
                final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);  
                if (appEnqueueRate > mMaxPackageEnqueueRate) {  
                    mUsageStats.registerOverRateQuota(pkg);  
                    final long now = SystemClock.elapsedRealtime();  
                    if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {  
                        Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate  
                                + ". Shedding events. package=" + pkg);  
                        mLastOverRateLogTime = now;  
                    }  
                    return;  
                }  
      
                int count = 0;  
                final int N = mNotificationList.size();  
                for (int i=0; i<N; i++) {  
                    final NotificationRecord r = mNotificationList.get(i);  
                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {  
                        if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {  
                            break;  // Allow updating existing notification  
                        }  
                        count++;  
                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {// 同一个应用发送notification数量不能超过50  
                            mUsageStats.registerOverCountQuota(pkg);  
                            Slog.e(TAG, "Package has already posted " + count  
                                    + " notifications.  Not showing more.  package=" + pkg);  
                            return;  
                        }  
                    }  
                }  
            }  
        }  
      
        if (pkg == null || notification == null) {//通知不能为空  
            throw new IllegalArgumentException("null not allowed: pkg=" + pkg  
                    + " id=" + id + " notification=" + notification);  
        }  
      
        // Whitelist pending intents.  
        if (notification.allPendingIntents != null) {  
            final int intentCount = notification.allPendingIntents.size();  
            if (intentCount > 0) {  
                final ActivityManagerInternal am = LocalServices  
                        .getService(ActivityManagerInternal.class);  
                final long duration = LocalServices.getService(  
                        DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();  
                for (int i = 0; i < intentCount; i++) {  
                    PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);  
                    if (pendingIntent != null) {  
                        am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);  
                    }  
                }  
            }  
        }  
      
        // Sanitize inputs  
        notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,  
                Notification.PRIORITY_MAX);  
      
        // setup local book-keeping  
        // 验证完条件后,将前面传递进来的Notification封装成一个StatusBarNotification对象,  
        final StatusBarNotification n = new StatusBarNotification(  
                pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,  
                user);  
        // 封装NotificationRecord对象  
        final NotificationRecord r = new NotificationRecord(getContext(), n);  
        mHandler.post(new EnqueueNotificationRunnable(userId, r));  
      
        idOut[0] = id;  
    }  


开启线程,异步处理。
    private class EnqueueNotificationRunnable implements Runnable {  
        private final NotificationRecord r;  
        private final int userId;  
      
        EnqueueNotificationRunnable(int userId, NotificationRecord r) {  
            this.userId = userId;  
            this.r = r;  
        };  
      
        @Override  
        public void run() {  
      
            synchronized (mNotificationList) {  
                final StatusBarNotification n = r.sbn;  
                if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());  
                NotificationRecord old = mNotificationsByKey.get(n.getKey());  
                if (old != null) {  
                    // Retain ranking information from previous record  
                    r.copyRankingInformation(old);  
                }  
      
                final int callingUid = n.getUid();  
                final int callingPid = n.getInitialPid();  
                final Notification notification = n.getNotification();  
                final String pkg = n.getPackageName();  
                final int id = n.getId();  
                final String tag = n.getTag();  
                final boolean isSystemNotification = isUidSystem(callingUid) ||  
                        ("android".equals(pkg));  
      
                // Handle grouped notifications and bail out early if we  
                // can to avoid extracting signals.  
                handleGroupedNotificationLocked(r, old, callingUid, callingPid);  
      
                // This conditional is a dirty hack to limit the logging done on  
                //     behalf of the download manager without affecting other apps.  
                if (!pkg.equals("com.android.providers.downloads")  
                        || Log.isLoggable("DownloadManager", Log.VERBOSE)) {  
                    int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;  
                    if (old != null) {  
                        enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;  
                    }  
                    EventLogTags.writeNotificationEnqueue(callingUid, callingPid,  
                            pkg, id, tag, userId, notification.toString(),  
                            enqueueStatus);  
                }  
      
                mRankingHelper.extractSignals(r);  
      
                final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);  
      
                // blocked apps 判断pkg是否可以显示通知  
                if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE  
                        || !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {  
                    if (!isSystemNotification) {//不拦截系统通知  
                        if (isPackageSuspended) {  
                            Slog.e(TAG, "Suppressing notification from package due to package "  
                                    + "suspended by administrator.");  
                            mUsageStats.registerSuspendedByAdmin(r);  
                        } else {  
                            Slog.e(TAG, "Suppressing notification from package by user request.");  
                            mUsageStats.registerBlocked(r);  
                        }  
                        return;  
                    }  
                }  
      
                // tell the ranker service about the notification  
                if (mRankerServices.isEnabled()) {  
                    mRankerServices.onNotificationEnqueued(r);  
                    // TODO delay the code below here for 100ms or until there is an answer  
                }  
      
                // 获取是否已经发送过此notification  
                int index = indexOfNotificationLocked(n.getKey());  
                if (index < 0) {  
                    // 如果是新发送的notification,就走新增流程.  
                    mNotificationList.add(r);  
                    mUsageStats.registerPostedByApp(r);  
                } else {  
                    //如果有发送过,就获取oldNtificationRecord,后面走更新流程 mStatusBar.updateNotification(r.statusBarKey, n)  
                    old = mNotificationList.get(index);  
                    mNotificationList.set(index, r);  
                    mUsageStats.registerUpdatedByApp(r, old);  
                    // Make sure we don't lose the foreground service state.  
                    notification.flags |=  
                            old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;  
                    r.isUpdate = true;  
                }  
      
                Slog.d(TAG, "NotificationRecord, r = "+r);  
                mNotificationsByKey.put(n.getKey(), r);  
      
                // Ensure if this is a foreground service that the proper additional  
                // flags are set.  
                if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {  
                    notification.flags |= Notification.FLAG_ONGOING_EVENT  
                            | Notification.FLAG_NO_CLEAR;  
                }  
      
                applyZenModeLocked(r);  
                mRankingHelper.sort(mNotificationList);//将mNotificationList排序  
      
                // 如果notification设置了smallIcon,调用所有NotificationListeners的notifyPostedLocked方法,  
                // 通知有新的notification,传入的参数为上面封装成的StatusBarNotification对象.  
                if (notification.getSmallIcon() != null) {  
                    StatusBarNotification oldSbn = (old != null) ? old.sbn : null;  
                    mListeners.notifyPostedLocked(n, oldSbn);  
                } else {  
                    Slog.e(TAG, "Not posting notification without small icon: " + notification);  
                    if (old != null && !old.isCanceled) {  
                        mListeners.notifyRemovedLocked(n);  
                    }  
                    // ATTENTION: in a future release we will bail out here  
                    // so that we do not play sounds, show lights, etc. for invalid  
                    // notifications  
                    Slog.e(TAG, "WARNING: In a future release this will crash the app: "  
                            + n.getPackageName());  
                }  
      
                // buzzBeepBlinkLocked方法负责对消息进行处理。  
                // 通知status bar显示该notification,确认是否需要声音,震动和闪光,如果需要,那么就发出声音,震动和闪光  
                buzzBeepBlinkLocked(r);  
            }  
        }  
    }  

到这里,就开始进入SystemUI了。

// 如果notification设置了smallIcon,调用所有NotificationListeners的notifyPostedLocked方法,               // 通知有新的notification,传入的参数为上面封装成的StatusBarNotification对象.               if (notification.getSmallIcon() != null) {                   StatusBarNotification oldSbn = (old != null) ? old.sbn : null;                   mListeners.notifyPostedLocked(n, oldSbn);               } else {                   Slog.e(TAG, "Not posting notification without small icon: " + notification);                   if (old != null && !old.isCanceled) {                       mListeners.notifyRemovedLocked(n);                   }                   // ATTENTION: in a future release we will bail out here                   // so that we do not play sounds, show lights, etc. for invalid                   // notifications                   Slog.e(TAG, "WARNING: In a future release this will crash the app: "                           + n.getPackageName());               }  

其中,mListeners为NotificationListeners,为内部类。查看方法notifyPostedLocked()

 /**          * asynchronously notify all listeners about a new notification          *          * <p>          * Also takes care of removing a notification that has been visible to a listener before,          * but isn't anymore.          */         public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {             // Lazily initialized snapshots of the notification.             TrimCache trimCache = new TrimCache(sbn); 通知所有的监听者关于这个新通知,             for (final ManagedServiceInfo info : mServices) {                 boolean sbnVisible = isVisibleToListener(sbn, info);                 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;                 // This notification hasn't been and still isn't visible -> ignore.                 if (!oldSbnVisible && !sbnVisible) {                     continue;                 }                 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);                 // This notification became invisible -> remove the old one.                 if (oldSbnVisible && !sbnVisible) {                     final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();                     mHandler.post(new Runnable() {                         @Override                         public void run() {                             notifyRemoved(info, oldSbnLightClone, update);                         }                     });                     continue;                 }                 final StatusBarNotification sbnToPost =  trimCache.ForListener(info);                 mHandler.post(new Runnable() {                     @Override                     public void run() {                         notifyPosted(info, sbnToPost, update);                     }                 });             }         }

然后在notifyPosted()方法里面推送通知,我们来看下这个方法:

private void notifyPosted(final ManagedServiceInfo info,                 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {             final INotificationListener listener = (INotificationListener)info.service;             StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);             try {                 listener.onNotificationPosted(sbnHolder, rankingUpdate);             } catch (RemoteException ex) {                 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);             }         }

然后监听者调用onNotificationPosted()继续往上推送

路径:frameworks/base/core/java/android/service/notification/INotificationListener.aidl

INotificationManager.aidl  BaseStatusBar.java  

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

在BaseStatusBar.java 里面注册NotificationListenerService。

try {
            mNotificationListener.registerAsSystemService(mContext,
                    new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
                    UserHandle.USER_ALL);
        } catch (RemoteException e) {
            Log.e(TAG, "Unable to register notification listener", e);
        }
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


在registerAsSystemService中调用registerListener方法。这个方法在INotificationManager.stub中声明了,而INotificationManager.stub 跟NotificationManagerService进行了绑定。so 跟进了NotificationManagerService.java

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

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

在NotificationManagerService.java里面

private final IBinder mService = new INotificationManager.Stub()

----> NotificationManagerService.java在实例化INotificationManager.stub 对象的时候,对registerListener 进行了override。在这个方法中,它首先去调用enforceSystemOrSystemUI,接着才去registerService。很显然,想要的答案就在enforceSystemOrSystemUI 中。so 跟进enforceSystemOrSystemUI。这个方法呢,也是在实例化INotificationManager.stub 对象mService的时候,对其 进行了override。

这样NotificationManagerService和NotificationListenerService连接上,可以吧消息传到systemUI进去。




 类似资料: