Android12 源码分析 SystemUI 学习(4) --- Notification(2/2)

前言:上节粗浅地分析了通知栏相关的服务和controller是如何被初始化和启动的,这节就重点讲系统是如何将通知发送(显示)出来的。

NotificationManager

我们在应用程序中,需要发送通知时就会用到NotificationManager的notify()方法。在NotificationManager中这个方法的嵌套有很多,对应的是不同情况的处理方案。

源码路径: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)
    {
        notifyAsUser(tag, id, notification, mContext.getUser());
    }
    public void notifyAsPackage(@NonNull String targetPackage, @Nullable String tag, int id,
            @NonNull Notification notification) {
        INotificationManager service = getService();
        String sender = mContext.getPackageName();
​
        try {
            if (localLOGV) Log.v(TAG, sender + ": notify(" + id + ", " + notification + ")");
            service.enqueueNotificationWithTag(targetPackage, sender, tag, id,
                    fixNotification(notification), mContext.getUser().getIdentifier());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    @UnsupportedAppUsage
    public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
    {
        INotificationManager service = getService();
        String pkg = mContext.getPackageName();
​
        try {
            if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                    fixNotification(notification), user.getIdentifier());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    /** @hide */
    @UnsupportedAppUsage
    static public INotificationManager getService()
    {
        if (sService != null) {
            return sService;
        }
        IBinder b = ServiceManager.getService("notification");
        sService = INotificationManager.Stub.asInterface(b);
        return sService;
    }

重点关注 notifyAsUser() 方法,获取NotificationManagerService服务,执行 enqueueNotificationWithTag() 方法。

  • 这里用到了Binder,IPC(进程间通信)的常用机制,具体查看IPC通信的相关文章。
NotificationManagerService
enqueueNotificationWithTag

源码路径:frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

    @VisibleForTesting
    final IBinder mService = new INotificationManager.Stub() {
        // Toasts
        // ============================================================================
        //...省略...
        @Override
        public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
                Notification notification, int userId) throws RemoteException {
            enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
                    Binder.getCallingPid(), tag, id, notification, userId);
        }
        //...省略...
    }
enqueueNotificationInternal
    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
            final int callingPid, final String tag, final int id, final Notification notification,
            int incomingUserId) {
        enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
                incomingUserId, false);
    }
​
    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
            final int callingPid, final String tag, final int id, final Notification notification,
            int incomingUserId, boolean postSilently) {
        //...省略...
​
        final int userId = ActivityManager.handleIncomingUser(callingPid,
                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
        final UserHandle user = UserHandle.of(userId);
​
        // Can throw a SecurityException if the calling uid doesn't have permission to post
        // as "pkg"
        final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
​
        if (notificationUid == INVALID_UID) {
            throw new SecurityException("Caller " + opPkg + ":" + callingUid
                    + " trying to post for invalid pkg " + pkg + " in user " + incomingUserId);
        }
​
        checkRestrictedCategories(notification);
​
        // Fix the notification as best we can.
        try {//不是修复,而是判断获取到的 通知 是否异常和能否进行加工操作
            fixNotification(notification, pkg, tag, id, userId);
        } catch (Exception e) {
            if (notification.isForegroundService()) {
                throw new SecurityException("Invalid FGS notification", e);
            }
            Slog.e(TAG, "Cannot fix notification", e);
            return;
        }
​
        // Notifications passed to setForegroundService() have FLAG_FOREGROUND_SERVICE,
        // but it's also possible that the app has called notify() with an update to an
        // FGS notification that hasn't yet been displayed.  Make sure we check for any
        // FGS-related situation up front, outside of any locks so it's safe to call into
        // the Activity Manager.
        final ServiceNotificationPolicy policy = mAmi.applyForegroundServiceNotification(
                notification, tag, id, pkg, userId);
        if (policy == ServiceNotificationPolicy.UPDATE_ONLY) {
            // Proceed if the notification is already showing/known, otherwise ignore
            // because the service lifecycle logic has retained responsibility for its
            // handling.
            if (!isNotificationShownInternal(pkg, tag, id, userId)) {
                reportForegroundServiceUpdate(false, notification, id, pkg, userId);
                return;
            }
        }
​
        mUsageStats.registerEnqueuedByApp(pkg);
​
        final StatusBarNotification n = new StatusBarNotification(
                pkg, opPkg, id, tag, notificationUid, callingPid, notification,
                user, null, System.currentTimeMillis());
​
        // setup local book-keeping
        String channelId = notification.getChannelId();
        //...省略 TV相关...
        String shortcutId = n.getShortcutId();
        final NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel(
                pkg, notificationUid, channelId, shortcutId,
                true /* parent ok */, false /* includeDeleted */);
        if (channel == null) {
            final String noChannelStr = "No Channel found for "
                    + /*...LogString省略...*/;
            Slog.e(TAG, noChannelStr);
            boolean appNotificationsOff = mPreferencesHelper.getImportance(pkg, notificationUid)
                    == NotificationManager.IMPORTANCE_NONE;
​
            if (!appNotificationsOff) {
                doChannelWarningToast("Developer warning for package "" + pkg + ""\n" +
                        "Failed to post notification on channel "" + channelId + ""\n" +
                        "See log for more details");
            }
            return;
        }
​
        final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
        r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid));
        r.setPostSilently(postSilently);
        r.setFlagBubbleRemoved(false);
        r.setPkgAllowedAsConvo(mMsgPkgsAllowedAsConvos.contains(pkg));
​
        if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
            final boolean fgServiceShown = channel.isFgServiceShown();
            if (((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
                        || !fgServiceShown)
                    && (r.getImportance() == IMPORTANCE_MIN
                            || r.getImportance() == IMPORTANCE_NONE)) {
                // Increase the importance of foreground service notifications unless the user had
                // an opinion otherwise (and the channel hasn't yet shown a fg service).
                if (TextUtils.isEmpty(channelId)
                        || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
                    r.setSystemImportance(IMPORTANCE_LOW);
                } else {
                    channel.setImportance(IMPORTANCE_LOW);
                    r.setSystemImportance(IMPORTANCE_LOW);
                    if (!fgServiceShown) {
                        channel.unlockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
                        channel.setFgServiceShown(true);
                    }
                    mPreferencesHelper.updateNotificationChannel(
                            pkg, notificationUid, channel, false);
                    r.updateNotificationChannel(channel);
                }
            } else if (!fgServiceShown && !TextUtils.isEmpty(channelId)
                    && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
                channel.setFgServiceShown(true);
                r.updateNotificationChannel(channel);
            }
        }
​
        ShortcutInfo info = mShortcutHelper != null
                ? mShortcutHelper.getValidShortcutInfo(notification.getShortcutId(), pkg, user)
                : null;
        if (notification.getShortcutId() != null && info == null) {
            Slog.w(TAG, "notification " + r.getKey() + " added an invalid shortcut");
        }
        r.setShortcutInfo(info);
        r.setHasSentValidMsg(mPreferencesHelper.hasSentValidMsg(pkg, notificationUid));
        r.userDemotedAppFromConvoSpace(
                mPreferencesHelper.hasUserDemotedInvalidMsgApp(pkg, notificationUid));
​
        if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
                r.getSbn().getOverrideGroupKey() != null)) {
            return;
        }
​
        if (info != null) {
            // Cache the shortcut synchronously after the associated notification is posted in case
            // the app unpublishes this shortcut immediately after posting the notification. If the
            // user does not modify the notification settings on this conversation, the shortcut
            // will be uncached by People Service when all the associated notifications are removed.
            mShortcutHelper.cacheShortcut(info, user);
        }
​
        // temporarily allow apps to perform extra work when their pending intents are launched
        if (notification.allPendingIntents != null) {
            final int intentCount = notification.allPendingIntents.size();
            if (intentCount > 0) {
                final long duration = LocalServices.getService(
                        DeviceIdleInternal.class).getNotificationAllowlistDuration();
                for (int i = 0; i < intentCount; i++) {
                    PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
                    if (pendingIntent != null) {
                        mAmi.setPendingIntentAllowlistDuration(pendingIntent.getTarget(),
                                ALLOWLIST_TOKEN, duration,
                                TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
                                REASON_NOTIFICATION_SERVICE,
                                "NotificationManagerService");
                        mAmi.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(),
                                ALLOWLIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
                                        | FLAG_SERVICE_SENDER));
                    }
                }
            }
        }
​
        // Need escalated privileges to get package importance
        final long token = Binder.clearCallingIdentity();
        boolean isAppForeground;
        try {
            isAppForeground = mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;//获取app是否在前台运行
        } finally {
            Binder.restoreCallingIdentity(token);
        }
        mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground));
    }

注意到有一个概念叫AppForegroundIMPORTANCE_FOREGROUND,指的就是前台活动,这是在Android11新添加的一个概念,Android11之前并没有对ActivityService等做这个概念归类。在Android11引进这个概念主要是因为增强系统安全和显示程序负担。在Android11后,所有的前台服务都需要再通知栏中显示以让用户知晓。这样用户就可以根据需求决定是否需要开启或关闭这个服务,例如现在使用位置、录音、摄像头等权限时也会在通知栏上显示,都是Google为了加强用户隐私所做的一些改动。[之所以不说活动(Activity),是因为前台活动一般是直接显示在手机屏上了]

这里post新建了一个线程EnqueueNotificationRunnable,代码就在NotificationManagerService里。

EnqueueNotificationRunnable
    protected class EnqueueNotificationRunnable implements Runnable {
        private final NotificationRecord r;
        private final int userId;
        private final boolean isAppForeground;
​
        EnqueueNotificationRunnable(int userId, NotificationRecord r, boolean foreground) {
            this.userId = userId;
            this.r = r;
            this.isAppForeground = foreground;
        }
​
        @Override
        public void run() {
            synchronized (mNotificationLock) {
                final Long snoozeAt =
                        mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
                                r.getUser().getIdentifier(),
                                r.getSbn().getPackageName(), r.getSbn().getKey());
                final long currentTime = System.currentTimeMillis();
                if (snoozeAt.longValue() > currentTime) {
                    (new SnoozeNotificationRunnable(r.getSbn().getKey(),
                            snoozeAt.longValue() - currentTime, null)).snoozeLocked(r);
                    return;
                }
​
                final String contextId =
                        mSnoozeHelper.getSnoozeContextForUnpostedNotification(
                                r.getUser().getIdentifier(),
                                r.getSbn().getPackageName(), r.getSbn().getKey());
                if (contextId != null) {
                    (new SnoozeNotificationRunnable(r.getSbn().getKey(),
                            0, contextId)).snoozeLocked(r);
                    return;
                }
​
                mEnqueuedNotifications.add(r);
                scheduleTimeoutLocked(r);
​
                final StatusBarNotification n = r.getSbn();
                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();
​
                // We need to fix the notification up a little for bubbles
                updateNotificationBubbleFlags(r, isAppForeground);
​
                // Handle grouped notifications and bail out early if we
                // can to avoid extracting signals.
                handleGroupedNotificationLocked(r, old, callingUid, callingPid);
​
                // if this is a group child, unsnooze parent summary
                if (n.isGroup() && notification.isGroupChild()) {
                    mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
                }
​
                // 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);
                }
​
                // tell the assistant service about the notification
                if (mAssistants.isEnabled()) {
                    mAssistants.onNotificationEnqueuedLocked(r);
                    mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
                            DELAY_FOR_ASSISTANT_TIME);
                } else {
                    mHandler.post(new PostNotificationRunnable(r.getKey()));
                }
            }
        }
    }
PostNotificationRunnable

同上直接转到PostNotificationRunnable

    protected class PostNotificationRunnable implements Runnable {
        private final String key;
​
        PostNotificationRunnable(String key) {
            this.key = key;
        }
​
        @Override
        public void run() {
            synchronized (mNotificationLock) {
                try {
                    NotificationRecord r = null;
                    int N = mEnqueuedNotifications.size();
                    for (int i = 0; i < N; i++) {
                        final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
                        if (Objects.equals(key, enqueued.getKey())) {
                            r = enqueued;
                            break;
                        }
                    }
                    //...省略...
​
                    final boolean isPackageSuspended =
                            isPackagePausedOrSuspended(r.getSbn().getPackageName(), r.getUid());
                    r.setHidden(isPackageSuspended);
                    if (isPackageSuspended) {
                        mUsageStats.registerSuspendedByAdmin(r);
                    }
                    NotificationRecord old = mNotificationsByKey.get(key);
                    final StatusBarNotification n = r.getSbn();
                    final Notification notification = n.getNotification();
​
                    // Make sure the SBN has an instance ID for statsd logging.
                    if (old == null || old.getSbn().getInstanceId() == null) {
                        n.setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
                    } else {
                        n.setInstanceId(old.getSbn().getInstanceId());
                    }
​
                    int index = indexOfNotificationLocked(n.getKey());
                    if (index < 0) {
                        mNotificationList.add(r);
                        mUsageStats.registerPostedByApp(r);
                        final boolean isInterruptive = isVisuallyInterruptive(null, r);
                        r.setInterruptive(isInterruptive);
                        r.setTextChanged(isInterruptive);
                    } else {
                        old = mNotificationList.get(index);  // Potentially *changes* old
                        mNotificationList.set(index, r);
                        mUsageStats.registerUpdatedByApp(r, old);
                        // Make sure we don't lose the foreground service state.
                        notification.flags |=
                                old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
                        r.isUpdate = true;
                        final boolean isInterruptive = isVisuallyInterruptive(old, r);
                        r.setTextChanged(isInterruptive);
                    }
​
                    mNotificationsByKey.put(n.getKey(), r);
​
                    // Ensure if this is a foreground service that the proper additional
                    // flags are set.
                    if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {
                        notification.flags |= FLAG_ONGOING_EVENT
                                | FLAG_NO_CLEAR;
                    }
​
                    mRankingHelper.extractSignals(r);
                    mRankingHelper.sort(mNotificationList);//排序 消息list
                    final int position = mRankingHelper.indexOf(mNotificationList, r);
​
                    int buzzBeepBlinkLoggingCode = 0;
                    if (!r.isHidden()) {
                        buzzBeepBlinkLoggingCode = buzzBeepBlinkLocked(r);
                    }
​
                    if (notification.getSmallIcon() != null) {//判断小图标不为空
                        StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
                        mListeners.notifyPostedLocked(r, old);
                        if ((oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup()))
                                && !isCritical(r)) {
                            mHandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    mGroupHelper.onNotificationPosted(
                                            n, hasAutoGroupSummaryLocked(n));
                                }
                            });
                        } else if (oldSbn != null) {
                            final NotificationRecord finalRecord = r;
                            mHandler.post(() -> mGroupHelper.onNotificationUpdated(
                                    finalRecord.getSbn(), hasAutoGroupSummaryLocked(n)));
                        }
                    } else {
                        //...省略...
                    }
​
                    if (mShortcutHelper != null) {
                        mShortcutHelper.maybeListenForShortcutChangesForBubbles(r,
                                false /* isRemoved */,
                                mHandler);
                    }
​
                    maybeRecordInterruptionLocked(r);
                    maybeRegisterMessageSent(r);
                    maybeReportForegroundServiceUpdate(r, true);
​
                    // Log event to statsd
                    mNotificationRecordLogger.maybeLogNotificationPosted(r, old, position,
                            buzzBeepBlinkLoggingCode, getGroupInstanceId(r.getSbn().getGroupKey()));
                } finally {
                    //...省略...
                }
            }
        }
    }

重点在mListeners.notifyPostedLocked()mListenersNotificationManagerService的私有全局变量(private NotificationListeners mListeners;),它是NotificationManagerService的一个内部类。

NotificationListeners

查看NotificationListeners的 notifyPostedLocked() 方法。

public class NotificationListeners extends ManagedServices {
    //...省略...
    @GuardedBy("mNotificationLock")
    private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
                boolean notifyAllListeners) {
        try {
            // Lazily initialized snapshots of the notification.
            StatusBarNotification sbn = r.getSbn();
            StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
            TrimCache trimCache = new TrimCache(sbn);
            for (final ManagedServiceInfo info : getServices()) {
                boolean sbnVisible = isVisibleToListener(sbn, r. getNotificationType(), info);
                boolean oldSbnVisible = (oldSbn != null)
                            && isVisibleToListener(oldSbn, old.getNotificationType(), info);
                // This notification hasn't been and still isn't visible -> ignore.
                if (!oldSbnVisible && !sbnVisible) {
                    continue;
                }
                // If the notification is hidden, don't notifyPosted listeners targeting < P.
                // Instead, those listeners will receive notifyPosted when the notification is
                // unhidden.
                if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) {
                    continue;
                }
​
                // If we shouldn't notify all listeners, this means the hidden state of
                // a notification was changed.  Don't notifyPosted listeners targeting >= P.
                // Instead, those listeners will receive notifyRankingUpdate.
                if (!notifyAllListeners && info.targetSdkVersion >= Build.VERSION_CODES.P) {
                    continue;
                }
​
                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
                // This notification became invisible -> remove the old one.
                if (oldSbnVisible && !sbnVisible) {
                    final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
                    mHandler.post(() -> notifyRemoved(
                        info, oldSbnLightClone, update, null, REASON_USER_STOPPED));
                    continue;
                }
​
                // Grant access before listener is notified
                final int targetUserId = (info.userid == UserHandle.USER_ALL)
                    ? UserHandle.USER_SYSTEM : info.userid;
                updateUriPermissions(r, old, info.component.getPackageName(), targetUserId);
​
                final StatusBarNotification sbnToPost = trimCache.ForListener(info);
                mHandler.post(() -> notifyPosted(info, sbnToPost, update));
            }
        } catch (Exception e) {
            Slog.e(TAG, "Could not notify listeners for " + r.getKey(), e);
        }
    }
    //...省略...
}

注意到Handler.post()里的notifyPosted(info, sbnToPost, update)。这里info内容来自NotificationListeners父类(ManagedServices)的方法 getServices() 。

ManagedServices

先看看这个 getServices() 方法是怎么拿到info的。

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

abstract public class ManagedServices { 
    private final ArrayList<ManagedServiceInfo> mServices = new ArrayList<>();  
    //...省略...
    protected List<ManagedServiceInfo> getServices() {
        synchronized (mMutex) {
            List<ManagedServiceInfo> services = new ArrayList<>(mServices);
            return services;
        }
    }
    //...省略...
    private ManagedServiceInfo registerServiceImpl(final IInterface service,
            final ComponentName component, final int userid, int targetSdk, int uid) {
        ManagedServiceInfo info = newServiceInfo(service, component, userid,
                true /*isSystem*/, null /*connection*/, targetSdk, uid);
        return registerServiceImpl(info);
    }
​
    private ManagedServiceInfo registerServiceImpl(ManagedServiceInfo info) {
        synchronized (mMutex) {
            try {
                info.service.asBinder().linkToDeath(info, 0);
                mServices.add(info);
                return info;
            } catch (RemoteException e) {
                // already dead
            }
        }
        return null;
    }
    //...省略...
}

在两个 registerServiceImpl() 方法里实现了info的创建,然后再 getService() 方法中返回这些服务信息(info)

notifyPosted

再回头看 notifyPosted() 方法。

public class NotificationListeners extends ManagedServices {
    //...省略...
    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) {
            Slog.e(TAG, "unable to notify listener (posted): " + info, ex);
        }
    }
    //...省略...
}
NotificationListenerService

onNotificationPosted() 方法是INotificationListener的,这是一个接口类,它的具体实现是NotificationListenerWrapper类,它是Binder的服务端,还是NotificationListenerService的内部类

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

protected class NotificationListenerWrapper extends INotificationListener.Stub {
        @Override
        public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
                NotificationRankingUpdate update) {
            StatusBarNotification sbn;
            try {
                sbn = sbnHolder.get();
            } catch (RemoteException e) {
                //...省略...
            }
            if (sbn == null) {
                //...省略...
            }
​
            try {
                // convert icon metadata to legacy format for older clients
                createLegacyIconExtras(sbn.getNotification());
                maybePopulateRemoteViews(sbn.getNotification());
                maybePopulatePeople(sbn.getNotification());
            } catch (IllegalArgumentException e) {
                //...省略...
            }
​
            // protect subclass from concurrent modifications of (@link mNotificationKeys}.
            synchronized (mLock) {
                applyUpdateLocked(update);
                if (sbn != null) {
                    SomeArgs args = SomeArgs.obtain();
                    args.arg1 = sbn;
                    args.arg2 = mRankingMap;
                    mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,
                            args).sendToTarget();
                } else {
                    // still pass along the ranking map, it may contain other information
                    mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,
                            mRankingMap).sendToTarget();
                }
            }
​
        }
        //...省略...
    }

这里实现了INotificationListener中的各个接口。在 onNotificationPosted() 方法中使用了handler,因此我们就在NotificationListenerService中的Handler找到对应的MSG_ON_NOTIFICATION_POSTED相关操作。

    private final class MyHandler extends Handler {
        public static final int MSG_ON_NOTIFICATION_POSTED = 1;
        public static final int MSG_ON_NOTIFICATION_REMOVED = 2;
        public static final int MSG_ON_LISTENER_CONNECTED = 3;
        public static final int MSG_ON_NOTIFICATION_RANKING_UPDATE = 4;
        public static final int MSG_ON_LISTENER_HINTS_CHANGED = 5;
        public static final int MSG_ON_INTERRUPTION_FILTER_CHANGED = 6;
        public static final int MSG_ON_NOTIFICATION_CHANNEL_MODIFIED = 7;
        public static final int MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED = 8;
        public static final int MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED = 9;
​
        public MyHandler(Looper looper) {
            super(looper, null, false);
        }
​
        @Override
        public void handleMessage(Message msg) {
            if (!isConnected) {
                return;
            }
            switch (msg.what) {
                case onNotificationPosted: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    StatusBarNotification sbn = (StatusBarNotification) args.arg1;
                    RankingMap rankingMap = (RankingMap) args.arg2;
                    args.recycle();
                    onNotificationPosted(sbn, rankingMap);
                } break;
​
                //...省略...
​
                case MSG_ON_NOTIFICATION_RANKING_UPDATE: {
                    RankingMap rankingMap = (RankingMap) msg.obj;
                    onNotificationRankingUpdate(rankingMap);
                } break;
​
                case MSG_ON_LISTENER_HINTS_CHANGED: {
                    final int hints = msg.arg1;
                    onListenerHintsChanged(hints);
                } break;
​
                //...省略...
            }
        }
    }

onNotificationPosted对应的代码中,调用了 onNotificationPosted() 方法。注意这个是NotificationListenerService的方法,而不是它的内部类的NotificationListenerWrapper的方法。

但是在NotificationListenerService中被没有实现这个方法,具体实现则由其子类实现。

通过查找代码,具体实现是在NotificationListener中。NotificationListener继承NotificationListenerWithPluginsNotificationListenerWithPlugins继承NotificationListenerService

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

@SuppressLint("OverrideAbstract")
public class NotificationListener extends NotificationListenerWithPlugins {
    //...省略...
    @Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
        if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
            mMainHandler.post(() -> {
                processForRemoteInput(sbn.getNotification(), mContext);
​
                for (NotificationHandler handler : mNotificationHandlers) {
                    handler.onNotificationPosted(sbn, rankingMap);
                }
            });
        }
    }
    //...省略...
}

依旧使用了handler,查看相关的NotificationHandler发现是一个接口,定位到NotificationEntryManager

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

相关实现代码如下

    private final NotificationHandler mNotifListener = new NotificationHandler() {
        @Override
        public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
            final boolean isUpdateToInflatedNotif = mActiveNotifications.containsKey(sbn.getKey());
            if (isUpdateToInflatedNotif) {
                updateNotification(sbn, rankingMap);
            } else {
                addNotification(sbn, rankingMap);
            }
        }
    }
​
    private void addNotificationInternal(
            StatusBarNotification notification,
            RankingMap rankingMap) throws InflationException {
        String key = notification.getKey();
        //...省略...
        updateRankingAndSort(rankingMap, "addNotificationInternal");
​
        Ranking ranking = new Ranking();
        rankingMap.getRanking(key, ranking);
​
        NotificationEntry entry = mPendingNotifications.get(key);
        if (entry != null) {
            entry.setSbn(notification);
            entry.setRanking(ranking);
        } else {
            entry = new NotificationEntry(
                    notification,
                    ranking,
                    mFgsFeatureController.isForegroundServiceDismissalEnabled(),
                    SystemClock.uptimeMillis());
            mAllNotifications.add(entry);
            mLeakDetector.trackInstance(entry);
​
            for (NotifCollectionListener listener : mNotifCollectionListeners) {
                listener.onEntryInit(entry);
            }
        }
​
        for (NotifCollectionListener listener : mNotifCollectionListeners) {
            listener.onEntryBind(entry, notification);
        }
​
        // Construct the expanded view.
        if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
            mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
        }
​
        mPendingNotifications.put(key, entry);
        mLogger.logNotifAdded(entry.getKey());
        for (NotificationEntryListener listener : mNotificationEntryListeners) {
            listener.onPendingEntryAdded(entry);
        }
        for (NotifCollectionListener listener : mNotifCollectionListeners) {
            listener.onEntryAdded(entry);
        }
        for (NotifCollectionListener listener : mNotifCollectionListeners) {
            listener.onRankingApplied();
        }
    }
​
    public void addNotification(StatusBarNotification notification, RankingMap ranking) {
        try {
            addNotificationInternal(notification, ranking);
        } catch (InflationException e) {
            handleInflationException(notification, e);
        }
    }
        
    private void updateNotificationInternal(StatusBarNotification notification,
            RankingMap ranking) throws InflationException {
        if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
​
        final String key = notification.getKey();
        abortExistingInflation(key, "updateNotification");
        final NotificationEntry entry = getActiveNotificationUnfiltered(key);
        if (entry == null) {
            return;
        }
​
        // Notification is updated so it is essentially re-added and thus alive again.  Don't need
        // to keep its lifetime extended.
        cancelLifetimeExtension(entry);
​
        updateRankingAndSort(ranking, "updateNotificationInternal");
        StatusBarNotification oldSbn = entry.getSbn();
        entry.setSbn(notification);
        for (NotifCollectionListener listener : mNotifCollectionListeners) {
            listener.onEntryBind(entry, notification);
        }
        mGroupManager.onEntryUpdated(entry, oldSbn);
​
        mLogger.logNotifUpdated(entry.getKey());
        for (NotificationEntryListener listener : mNotificationEntryListeners) {
            listener.onPreEntryUpdated(entry);
        }
        final boolean fromSystem = ranking != null;
        for (NotifCollectionListener listener : mNotifCollectionListeners) {
            listener.onEntryUpdated(entry, fromSystem);
        }
​
        if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
            mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
        }
​
        updateNotifications("updateNotificationInternal");
​
        for (NotificationEntryListener listener : mNotificationEntryListeners) {
            listener.onPostEntryUpdated(entry);
        }
        for (NotifCollectionListener listener : mNotifCollectionListeners) {
            listener.onRankingApplied();
        }
    }
​
    public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
        try {
            updateNotificationInternal(notification, ranking);
        } catch (InflationException e) {
            handleInflationException(notification, e);
        }
    }

注意到,不管是 add 还是 update 中都有一个inflateViews的操作,这个操作就是构造通知视图的操作。

总结

通知和状态栏的启动流程和一些数据上的操作在Android10、11、12上有些比较大的改动。因此在分析这些代码时,不能完全依赖网络上的文章来总结源码的结构。

android-12.1.0_r8分支上,通知分析从 notify 方法开始,使用到服务、线程通信、进程通信等,最终在NotificationEntryManager中进行了视图的填充。这篇文章只是浅析了一下通知的显示,结合前一篇文章通知栏组件的创建,对于通知相关的组件有了一定的了解。篇幅问题,通知的获取(通过 Binder 从其它进程拿到信息)、通知的分类排序,就不做分析,后续有机会在深究。感谢阅读。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-情绪零碎-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值