目录
3.EnqueueNotificationRunnable:
5.PostNotificationRunnable的处理:
1.时序图:
从时序图上看执行流程很简单,接下来就一起跟随源码看看这些流程中都做了什么事情。
2.NotificationManger:
1):对smallicon的非空检验(Build.VERSION_CODES.LOLLIPOP_MR1以上版本),校验不通过报IllegalArgumentException("Invalid notification (no valid small icon),日志上有明显的提示,可以通过此条打印定位crash原因。
2)对LargeIcon压缩(若超过最大宽高MediaStyle(H:140dp W:280dp) BigPictureStyle(H:284dp W:416dp) 其他40dp),若未达到阀值则居中显示。
附上主要源码:
292 /**
293 * @hide
294 */
295 public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
296 {
297 INotificationManager service = getService();
298 String pkg = mContext.getPackageName();
299 // Fix the notification as best we can.
300 Notification.addFieldsFromContext(mContext, notification);
301 if (notification.sound != null) {
302 notification.sound = notification.sound.getCanonicalUri();
303 if (StrictMode.vmFileUriExposureEnabled()) {
304 notification.sound.checkFileUriExposed("Notification.sound");
305 }
306 }
307 fixLegacySmallIcon(notification, pkg);
308 if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
309 if (notification.getSmallIcon() == null) {
310 throw new IllegalArgumentException("Invalid notification (no valid small icon): "
311 + notification);
312 }
313 }
314 if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
315 notification.reduceImageSizes(mContext);
316 ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
317 boolean isLowRam = am.isLowRamDevice();
318 final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam);
319 try {
320 service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
321 copy, user.getIdentifier());
322 } catch (RemoteException e) {
323 throw e.rethrowFromSystemServer();
324 }
325 }
3.NotificationManagerService:
- 检验Caller是否合法(主要是针对多用户的非管理用户的限制)
- 参数是否合法(对nofiticaiton和packageName进行非空校验)
- 是否能被渲染(猜测是沉浸式状态栏相关设置,只有系统APP才可以设置)
- 保存通知状态(分为全局和根据Pakcagename区分的应用程序的)
- 校验ChannelId是否合法(ChannelId是否成功注册了)
- 封装Notification的相关信息到StatusBarNotification(实现Parcelable接口,用来给SystemUI传输数据)
- 创建NotificationRecord,用来保存framework层私有数据(也就是不发送给SystemUI的数据)和StatusBarNotification。简单理解就是所有的通知数据;
- 通知限制处理:
1):即时应用不允许发送通知;
2):通知的刷新频率不能太快,并且进度条通知的进度不能大于或等于最大值;
3):每个应用最多能同时存在50个通知;
- 将通知的PenddingIntent加入到白名单中
- 新建线程发送此条通知
附上主要源码:
3439 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
3440 final int callingPid, final String tag, final int id, final Notification notification,
3441 int incomingUserId) {
3442 if (DBG) {
3443 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
3444 + " notification=" + notification);
3445 }
3446 checkCallerIsSystemOrSameApp(pkg);
3447
3448 final int userId = ActivityManager.handleIncomingUser(callingPid,
3449 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
3450 final UserHandle user = new UserHandle(userId);
3451
3452 if (pkg == null || notification == null) {
3453 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
3454 + " id=" + id + " notification=" + notification);
3455 }
3456
3457 // The system can post notifications for any package, let us resolve that.
3458 final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
3459
3460 // Fix the notification as best we can.
3461 try {
3462 final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
3463 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3464 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
3465 Notification.addFieldsFromContext(ai, notification);
3466
3467 int canColorize = mPackageManagerClient.checkPermission(
3468 android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
3469 if (canColorize == PERMISSION_GRANTED) {
3470 notification.flags |= Notification.FLAG_CAN_COLORIZE;
3471 } else {
3472 notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
3473 }
3474
3475 } catch (NameNotFoundException e) {
3476 Slog.e(TAG, "Cannot create a context for sending app", e);
3477 return;
3478 }
3479
3480 mUsageStats.registerEnqueuedByApp(pkg);
3481
3482 // setup local book-keeping
3483 String channelId = notification.getChannelId();
3484 if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
3485 channelId = (new Notification.TvExtender(notification)).getChannelId();
3486 }
3487 final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
3488 notificationUid, channelId, false /* includeDeleted */);
3489 if (channel == null) {
3490 final String noChannelStr = "No Channel found for "
3491 + "pkg=" + pkg
3492 + ", channelId=" + channelId
3493 + ", id=" + id
3494 + ", tag=" + tag
3495 + ", opPkg=" + opPkg
3496 + ", callingUid=" + callingUid
3497 + ", userId=" + userId
3498 + ", incomingUserId=" + incomingUserId
3499 + ", notificationUid=" + notificationUid
3500 + ", notification=" + notification;
3501 Log.e(TAG, noChannelStr);
3502 doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
3503 "Failed to post notification on channel \"" + channelId + "\"\n" +
3504 "See log for more details");
3505 return;
3506 }
3507
3508 final StatusBarNotification n = new StatusBarNotification(
3509 pkg, opPkg, id, tag, notificationUid, callingPid, notification,
3510 user, null, System.currentTimeMillis());
3511 final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
3512
3513 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0
3514 && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
3515 && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) {
3516 // Increase the importance of foreground service notifications unless the user had an
3517 // opinion otherwise
3518 if (TextUtils.isEmpty(channelId)
3519 || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
3520 r.setImportance(IMPORTANCE_LOW, "Bumped for foreground service");
3521 } else {
3522 channel.setImportance(IMPORTANCE_LOW);
3523 mRankingHelper.updateNotificationChannel(pkg, notificationUid, channel, false);
3524 r.updateNotificationChannel(channel);
3525 }
3526 }
3527
3528 if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
3529 r.sbn.getOverrideGroupKey() != null)) {
3530 return;
3531 }
3532
3533 // Whitelist pending intents.
3534 if (notification.allPendingIntents != null) {
3535 final int intentCount = notification.allPendingIntents.size();
3536 if (intentCount > 0) {
3537 final ActivityManagerInternal am = LocalServices
3538 .getService(ActivityManagerInternal.class);
3539 final long duration = LocalServices.getService(
3540 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
3541 for (int i = 0; i < intentCount; i++) {
3542 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
3543 if (pendingIntent != null) {
3544 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
3545 WHITELIST_TOKEN, duration);
3546 }
3547 }
3548 }
3549 }
3550
3551 mHandler.post(new EnqueueNotificationRunnable(userId, r));
3552 }
3.EnqueueNotificationRunnable:
- 加入mEnqueuedNotifications队列中
- 定时取消功能
- 对NotificationGroup的处理
- 调用助理服务
- 启动PostNotificationRunnable线程
主要源码如下:
3780 protected class EnqueueNotificationRunnable implements Runnable {
3781 private final NotificationRecord r;
3782 private final int userId;
3783
3784 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
3785 this.userId = userId;
3786 this.r = r;
3787 };
3788
3789 @Override
3790 public void run() {
3791 synchronized (mNotificationLock) {
3792 mEnqueuedNotifications.add(r);
3793 scheduleTimeoutLocked(r);
3794
3795 final StatusBarNotification n = r.sbn;
3796 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
3797 NotificationRecord old = mNotificationsByKey.get(n.getKey());
3798 if (old != null) {
3799 // Retain ranking information from previous record
3800 r.copyRankingInformation(old);
3801 }
3802
3803 final int callingUid = n.getUid();
3804 final int callingPid = n.getInitialPid();
3805 final Notification notification = n.getNotification();
3806 final String pkg = n.getPackageName();
3807 final int id = n.getId();
3808 final String tag = n.getTag();
3809
3810 // Handle grouped notifications and bail out early if we
3811 // can to avoid extracting signals.
3812 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
3813
3814 // if this is a group child, unsnooze parent summary
3815 if (n.isGroup() && notification.isGroupChild()) {
3816 mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
3817 }
3818
3819 // This conditional is a dirty hack to limit the logging done on
3820 // behalf of the download manager without affecting other apps.
3821 if (!pkg.equals("com.android.providers.downloads")
3822 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
3823 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
3824 if (old != null) {
3825 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
3826 }
3827 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
3828 pkg, id, tag, userId, notification.toString(),
3829 enqueueStatus);
3830 }
3831
3832 mRankingHelper.extractSignals(r);
3833
3834 // tell the assistant service about the notification
3835 if (mAssistants.isEnabled()) {
3836 mAssistants.onNotificationEnqueued(r);
3837 mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
3838 DELAY_FOR_ASSISTANT_TIME);
3839 } else {
3840 mHandler.post(new PostNotificationRunnable(r.getKey()));
3841 }
3842 }
3843 }
3844 }
5.PostNotificationRunnable的处理:
- 判断通知新增还是刷新
- 刷新NotificationGroup
- 判断是否支持Zen Mode(勿扰模式)
- 调用NotificationListeners的notifyPostedLocked方法
源码如下:
3846 protected class PostNotificationRunnable implements Runnable {
3847 private final String key;
3848
3849 PostNotificationRunnable(String key) {
3850 this.key = key;
3851 }
3852
3853 @Override
3854 public void run() {
3855 synchronized (mNotificationLock) {
3856 try {
3857 NotificationRecord r = null;
3858 int N = mEnqueuedNotifications.size();
3859 for (int i = 0; i < N; i++) {
3860 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3861 if (Objects.equals(key, enqueued.getKey())) {
3862 r = enqueued;
3863 break;
3864 }
3865 }
3866 if (r == null) {
3867 Slog.i(TAG, "Cannot find enqueued record for key: " + key);
3868 return;
3869 }
3870 NotificationRecord old = mNotificationsByKey.get(key);
3871 final StatusBarNotification n = r.sbn;
3872 final Notification notification = n.getNotification();
3873 int index = indexOfNotificationLocked(n.getKey());
3874 if (index < 0) {
3875 mNotificationList.add(r);
3876 mUsageStats.registerPostedByApp(r);
3877 } else {
3878 old = mNotificationList.get(index);
3879 mNotificationList.set(index, r);
3880 mUsageStats.registerUpdatedByApp(r, old);
3881 // Make sure we don't lose the foreground service state.
3882 notification.flags |=
3883 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
3884 r.isUpdate = true;
3885 }
3886
3887 mNotificationsByKey.put(n.getKey(), r);
3888
3889 // Ensure if this is a foreground service that the proper additional
3890 // flags are set.
3891 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
3892 notification.flags |= Notification.FLAG_ONGOING_EVENT
3893 | Notification.FLAG_NO_CLEAR;
3894 }
3895
3896 applyZenModeLocked(r);
3897 mRankingHelper.sort(mNotificationList);
3898
3899 if (notification.getSmallIcon() != null) {
3900 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
3901 mListeners.notifyPostedLocked(n, oldSbn);
3902 if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
3903 mHandler.post(new Runnable() {
3904 @Override
3905 public void run() {
3906 mGroupHelper.onNotificationPosted(
3907 n, hasAutoGroupSummaryLocked(n));
3908 }
3909 });
3910 }
3911 } else {
3912 Slog.e(TAG, "Not posting notification without small icon: " + notification);
3913 if (old != null && !old.isCanceled) {
3914 mListeners.notifyRemovedLocked(n,
3915 NotificationListenerService.REASON_ERROR);
3916 mHandler.post(new Runnable() {
3917 @Override
3918 public void run() {
3919 mGroupHelper.onNotificationRemoved(n);
3920 }
3921 });
3922 }
3923 // ATTENTION: in a future release we will bail out here
3924 // so that we do not play sounds, show lights, etc. for invalid
3925 // notifications
3926 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
3927 + n.getPackageName());
3928 }
3929
3930 buzzBeepBlinkLocked(r);//提示音
3931 } finally {
3932 int N = mEnqueuedNotifications.size();
3933 for (int i = 0; i < N; i++) {
3934 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3935 if (Objects.equals(key, enqueued.getKey())) {
3936 mEnqueuedNotifications.remove(i);
3937 break;
3938 }
3939 }
3940 }
3941 }
3942 }
3943 }
6.NotificationListeners:
- 寻找匹配的ManagedService
- 通知services移除不在显示的Notification
- 向services发送新通知的信息
5534 /**
5535 * asynchronously notify all listeners about a new notification
5536 *
5537 * <p>
5538 * Also takes care of removing a notification that has been visible to a listener before,
5539 * but isn't anymore.
5540 */
5541 @GuardedBy("mNotificationLock")
5542 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
5543 // Lazily initialized snapshots of the notification.
5544 TrimCache trimCache = new TrimCache(sbn);
5545
5546 for (final ManagedServiceInfo info : getServices()) {
5547 boolean sbnVisible = isVisibleToListener(sbn, info);
5548 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
5549 // This notification hasn't been and still isn't visible -> ignore.
5550 if (!oldSbnVisible && !sbnVisible) {
5551 continue;
5552 }
5553 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5554
5555 // This notification became invisible -> remove the old one.
5556 if (oldSbnVisible && !sbnVisible) {
5557 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
5558 mHandler.post(new Runnable() {
5559 @Override
5560 public void run() {
5561 notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
5562 }
5563 });
5564 continue;
5565 }
5566
5567 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5568 mHandler.post(new Runnable() {
5569 @Override
5570 public void run() {
5571 notifyPosted(info, sbnToPost, update);
5572 }
5573 });
5574 }
5575 }
5687 private void notifyPosted(final ManagedServiceInfo info,
5688 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
5689 final INotificationListener listener = (INotificationListener) info.service;
5690 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5691 try {
5692 listener.onNotificationPosted(sbnHolder, rankingUpdate);
5693 } catch (RemoteException ex) {
5694 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
5695 }
5696 }
到此为止,通知的相关信息就发送到SystemUI中的Services中了,之后就是SystemUI的显示流程,下节在续
欢迎您来斧正。