前言:上节粗浅地分析了通知栏相关的服务和controller是如何被初始化和启动的,这节就重点讲系统是如何将通知发送(显示)出来的。
- 源码浅析,分支为android-12.1.0_r8
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));
}
注意到有一个概念叫
AppForeground
和IMPORTANCE_FOREGROUND
,指的就是前台活动,这是在Android11
新添加的一个概念,Android11
之前并没有对Activity
和Service
等做这个概念归类。在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()
。mListeners
是NotificationManagerService
的私有全局变量(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
继承NotificationListenerWithPlugins
,NotificationListenerWithPlugins
继承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 从其它进程拿到信息)、通知的分类排序,就不做分析,后续有机会在深究。感谢阅读。