android12 SystemUI之通知

创建一个通知,然后调用notify发送:

scss 复制代码
//frameworks/base/core/java/android/app/NotificationManager.java
public void notify(String tag, int id, Notification notification){
   notifyAsUser(tag, id, notification, mContext.getUser());
}

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();
       }
 }

调用NotificationManagerService的enqueueNotificationWithTag

arduino 复制代码
//frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
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);
}
  

调用NotificationManagerService的enqueueNotificationInternal

scss 复制代码
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 notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
        //如果 UID 不匹配(`INVALID_UID`),直接抛 `SecurityException`,通知被丢弃
         if (notificationUid == INVALID_UID) {
             throw new SecurityException("Caller " + opPkg + ":" + callingUid
                      + " trying to post for invalid pkg " + pkg + " in user " + incomingUserId);
         }
  
         ...
  
        final ServiceNotificationPolicy policy = mAmi.applyForegroundServiceNotification(
                 notification, tag, id, pkg, userId);
          //如果通知只是"更新"且尚未显示
       if (policy == ServiceNotificationPolicy.UPDATE_ONLY) {
            
             //丢弃
            if (!isNotificationShownInternal(pkg, tag, id, userId)) {
              reportForegroundServiceUpdate(false, notification, id, pkg, userId);
              return;
            }
       }
  
       mUsageStats.registerEnqueuedByApp(pkg);
  
      //创建StatusBarNotification
       final StatusBarNotification n = new StatusBarNotification(
                  pkg, opPkg, id, tag, notificationUid, callingPid, notification,
                  user, null, System.currentTimeMillis());
  
          // setup local book-keeping
          String channelId = notification.getChannelId();
          if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
              channelId = (new Notification.TvExtender(notification)).getChannelId();
          }
          String shortcutId = n.getShortcutId();
          //获取通知渠道,如果渠道为空,直接 `return`(丢弃通知)
          final NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel(
                  pkg, notificationUid, channelId, shortcutId,
                  true /* parent ok */, false /* includeDeleted */);
          if (channel == null) {
              final String noChannelStr = "No Channel found for "
                      + "pkg=" + pkg
                      + ", channelId=" + channelId
                      + ", id=" + id
                      + ", tag=" + tag
                      + ", opPkg=" + opPkg
                      + ", callingUid=" + callingUid
                      + ", userId=" + userId
                      + ", incomingUserId=" + incomingUserId
                      + ", notificationUid=" + notificationUid
                      + ", notification=" + notification;
              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));
  
          //如果通知带有 `FLAG_FOREGROUND_SERVICE` 标志
          //系统会强制将重要性(`Importance`)提至 `IMPORTANCE_LOW`(如果用户未锁定),并调用 `updateNotificationChannel`
          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));
  
     //检查应用是否被系统挂起(`suspended`)、是否处于"勿扰模式"下被禁止、或者该渠道是否被屏蔽
      if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
                  r.getSbn().getOverrideGroupKey() != null)) {
              return;
          }
  
          if (info != null) {
              //缓存快捷方式信息
              mShortcutHelper.cacheShortcut(info, user);
          }
  
          // temporarily allow apps to perform extra work when their pending intents are launched
          //如果通知包含 `PendingIntent`,系统会授予接收者临时的电池优化白名单(`setPendingIntentAllowlistDuration`)和后台活动启动权限
          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;
          } finally {
              Binder.restoreCallingIdentity(token);
          }
          //Post消息
          mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground));
      }
      


//EnqueueNotificationRunnable
public void run() {
       synchronized (mNotificationLock) {
          //处理"稍后提醒"
          final Long snoozeAt =
                 mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
                                  r.getUser().getIdentifier(),
                                  r.getSbn().getPackageName(), r.getSbn().getKey());
          final long currentTime = System.currentTimeMillis();
          //如果将来提醒,创建一个 `SnoozeNotificationRunnable` 并 `return`
          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
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;
                          }
                      }
                      if (r == null) {
                          Slog.i(TAG, "Cannot find enqueued record for key: " + key);
                          return;
                      }
  
                      if (isBlocked(r)) {
                          Slog.i(TAG, "notification blocked by assistant request");
                          return;
                      }
  
                      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());
                      }
  
  
                     //获取通知在mNotificationList中的索引
                      int index = indexOfNotificationLocked(n.getKey());
                      if (index < 0) {
                      //将通知记录NotificationRecord添加到mNotificationList
                          mNotificationList.add(r);
                          mUsageStats.registerPostedByApp(r);
                          r.setInterruptive(isVisuallyInterruptive(null, r));
                      } else {
                      //倘若已存在则直接更新为新的NotificationRecord
                          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);
                          r.setInterruptive(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);
                      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;
                          //通知SystemUI显示图标
                          //NotificationListeners mListeners
                          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 {
                          Slog.e(TAG, "Not posting notification without small icon: " + notification);
                          if (old != null && !old.isCanceled) {
                          // 移除该通知并通知SystemUI移除它
                          //NotificationListeners mListeners
                              mListeners.notifyRemovedLocked(r,
                                      NotificationListenerService.REASON_ERROR, r.getStats());
                              mHandler.post(new Runnable() {
                                  @Override
                                  public void run() {
                                      mGroupHelper.onNotificationRemoved(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());
                      }
  
                      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 {
                      int N = mEnqueuedNotifications.size();
                      for (int i = 0; i < N; i++) {
                          final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
                          if (Objects.equals(key, enqueued.getKey())) {
                              mEnqueuedNotifications.remove(i);
                              break;
                          }
                      }
                  }
              }
          }
 }
      
      

通知SystemUI显示图标

java 复制代码
//frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java)
private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
                  boolean notifyAllListeners) {
              try {
                  // Lazily initialized snapshots of the notification.
                  StatusBarNotification sbn = r.getSbn();
                  ...
                  mHandler.post(() -> notifyPosted(info, sbnToPost, update));
                  }
              } catch (Exception e) {
                  Slog.e(TAG, "Could not notify listeners for " + r.getKey(), e);
              }
          }
          
          
          
 
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);
         }
   }
  

通知SystemUI移除通知

java 复制代码
//frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java)
public void notifyRemovedLocked(NotificationRecord r, int reason,
                  NotificationStats notificationStats) {
        ...
  
        mHandler.post(() -> notifyRemoved(info, sbnLight, update, stats, reason));
              }
        ...
          }
          
          
          
private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
                  NotificationRankingUpdate rankingUpdate, NotificationStats stats, int reason) {
              final INotificationListener listener = (INotificationListener) info.service;
              StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
              try {
                  if (!CompatChanges.isChangeEnabled(NOTIFICATION_CANCELLATION_REASONS, info.uid)
                          && (reason == REASON_CHANNEL_REMOVED || reason == REASON_CLEAR_DATA)) {
                      reason = REASON_CHANNEL_BANNED;
                  }
                  listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason);
              } catch (RemoteException ex) {
                  Slog.e(TAG, "unable to notify listener (removed): " + info, ex);
              }
          }
相关推荐
summerkissyou19872 小时前
android - 性能 - Perfetto - cpu 分析教程及例子
android·性能
AFinalStone3 小时前
Android 7系统休眠唤醒(七)内核层—wakelock与autosleep机制
android·休眠唤醒
starvapour3 小时前
在关机键损坏的情况下让手机关机
android·adb·手机
AFinalStone4 小时前
Android 7系统休眠唤醒(五)休眠全链路
android·电源管理·休眠唤醒
zzq77975 小时前
Android 风险识别机制实测:从录屏悬浮窗到远程控制的边界评估
android·安全·安卓·app加固·御盾安全
码云数智-园园5 小时前
MySQL 慢查询排查完整流程
android
码云骑士6 小时前
【Android Performance】进程关联启动治理详解——从唤醒链分析到自启动管控的完整方案
android
心念枕惊7 小时前
PHP 在领域驱动(DDD)设计中的核心实践
android·开发语言·php
蜡台7 小时前
Android WebView 设计指南
android·java·kotlin