源码网站:http://androidxref.com/9.0.0_r3
可以参照: Android O 8.0 Notification 源码分析(一)
Android O 8.0 Notification 源码分析(二)
link:
Android P Notification(1) 之 removeNotification
Android P Notification(2) 之 发送通知慢
Android P Notification(3) 之 Fullscreen intent被拦截
NotificationManagerService原理分析
拦截原因
1): No Fullscreen intent: suppressed by DND(do not disturb) 被勿扰模式拦截
2).No Fullscreen intent: not important enough 通知优先级不够
incallUI发送通知的代码如下
@/packages/apps/Dialer/java/com/android/incallui/StatusBarNotifier.java
private void buildAndSendNotification(
LogUtil.i("StatusBarNotifier.buildAndSendNotification", "notificationType=" + notificationType); //2 log会打印2,表示incallUI发送通知开始
switch (notificationType) {
case NOTIFICATION_INCOMING_CALL: //2
if (BuildCompat.isAtLeastO()) {
builder.setChannelId(NotificationChannelId.INCOMING_CALL); //channel id : INCOMING_CALL
}
// Set the intent as a full screen intent as well if a call is incoming
configureFullScreenIntent(builder, createLaunchPendingIntent(true /* isFullScreen */)); //发送全屏通知
// Set the notification category and bump the priority for incoming calls
builder.setCategory(Notification.CATEGORY_CALL);
// This will be ignored on O+ and handled by the channel
builder.setPriority(Notification.PRIORITY_MAX); //优先级 PRIORITY_MAX
if (currentNotification != NOTIFICATION_INCOMING_CALL) {
LogUtil.i(
"StatusBarNotifier.buildAndSendNotification",
"Canceling old notification so this one can be noisy");
// Moving from a non-interuptive notification (or none) to a noisy one. Cancel the old
// notification (if there is one) so the fullScreenIntent or HUN will show
TelecomAdapter.getInstance().stopForegroundNotification();
}
break;
LogUtil.i(
"StatusBarNotifier.buildAndSendNotification",
"displaying notification for " + notificationType);
// If a notification exists, this will only update it.
TelecomAdapter.getInstance().startForegroundNotification(NOTIFICATION_ID, notification); //notify通知
}
需要设置setFullScreenIntent为true
http://androidxref.com/9.0.0_r3
packages/apps/Dialer/java/com/android/incallui/StatusBarNotifier.java
983 private void configureFullScreenIntent(Notification.Builder builder, PendingIntent intent) {
984 // Ok, we actually want to launch the incoming call
985 // UI at this point (in addition to simply posting a notification
986 // to the status bar). Setting fullScreenIntent will cause
987 // the InCallScreen to be launched immediately *unless* the
988 // current foreground activity is marked as "immersive".
989 LogUtil.d("StatusBarNotifier.configureFullScreenIntent", "setting fullScreenIntent: " + intent);
990 builder.setFullScreenIntent(intent, true);
991 }
在addNotificationInternal回去拦截,看了几个Android 版本位置都有点差异,不过代码逻辑都是一样的
@/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
743 protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
744 throws InflationException {
745 if (DEBUG) {
746 Log.d(TAG, "createNotificationViews(notification=" + sbn);
747 }
748 NotificationData.Entry entry = new NotificationData.Entry(sbn);
749 Dependency.get(LeakDetector.class).trackInstance(entry);
750 entry.createIcons(mContext, sbn);
751 // Construct the expanded view.
752 inflateViews(entry, mListContainer.getViewParentForNotification(entry));
753 return entry;
754 }
755
756 private void addNotificationInternal(StatusBarNotification notification,
757 NotificationListenerService.RankingMap ranking) throws InflationException {
758 String key = notification.getKey();
759 if (DEBUG) Log.d(TAG, "addNotification key=" + key);
760
761 mNotificationData.updateRanking(ranking);
762 NotificationData.Entry shadeEntry = createNotificationViews(notification);
763 boolean isHeadsUped = shouldPeek(shadeEntry);
764 if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
765 if (shouldSuppressFullScreenIntent(shadeEntry)) {
766 if (DEBUG) {
767 Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
768 }
769 } else if (mNotificationData.getImportance(key)
770 < NotificationManager.IMPORTANCE_HIGH) {
771 if (DEBUG) {
772 Log.d(TAG, "No Fullscreen intent: not important enough: "
773 + key);
774 }
775 } else {
776 // Stop screensaver if the notification has a fullscreen intent.
777 // (like an incoming phone call)
778 SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
779
780 // not immersive & a fullscreen alert should be shown
781 if (DEBUG)
782 Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
783 try {
784 EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
785 key);
786 notification.getNotification().fullScreenIntent.send();
787 shadeEntry.notifyFullScreenIntentLaunched();
788 mMetricsLogger.count("note_fullscreen", 1);
789 } catch (PendingIntent.CanceledException e) {
790 }
791 }
792 }
793 abortExistingInflation(key);
794
795 mForegroundServiceController.addNotification(notification,
796 mNotificationData.getImportance(key));
797
798 mPendingNotifications.put(key, shadeEntry);
799 mGroupManager.onPendingEntryAdded(shadeEntry);
800 }
由于app没有setimportance()方法,因此Importance是在这里赋值。
如果出现优先级低导致fullscreen initent被拦截,可以添加下详细LOG来DEBUG。
@frameworks/base/services/core/java/com/android/server/notification/NotificationRecord.java
@VisibleForTesting
public NotificationRecord(Context context, StatusBarNotification sbn,
NotificationChannel channel)
{
this.sbn = sbn;
mOriginalFlags = sbn.getNotification().flags;
mRankingTimeMs = calculateRankingTimeMs(0L);
mCreationTimeMs = sbn.getPostTime();
mUpdateTimeMs = mCreationTimeMs;
mContext = context;
stats = new NotificationUsageStats.SingleNotificationStats();
mChannel = channel;
mPreChannelsNotification = isPreChannelsNotification();
mSound = calculateSound();
mVibration = calculateVibration();
mAttributes = calculateAttributes();
mImportance = calculateImportance(); //app没有setimprotance方法,是在这里赋值
mLight = calculateLights();
}
private int calculateImportance() {
final Notification n = sbn.getNotification();
int importance = getChannel().getImportance();
int requestedImportance = IMPORTANCE_DEFAULT;
// Migrate notification flags to scores
if (0 != (n.flags & Notification.FLAG_HIGH_PRIORITY)) {
n.priority = Notification.PRIORITY_MAX;
}
n.priority = NotificationManagerService.clamp(n.priority, Notification.PRIORITY_MIN,
Notification.PRIORITY_MAX);
switch (n.priority) {
case Notification.PRIORITY_MIN:
requestedImportance = IMPORTANCE_MIN;
break;
case Notification.PRIORITY_LOW:
requestedImportance = IMPORTANCE_LOW;
break;
case Notification.PRIORITY_DEFAULT:
requestedImportance = IMPORTANCE_DEFAULT;
break;
case Notification.PRIORITY_HIGH:
case Notification.PRIORITY_MAX:
requestedImportance = IMPORTANCE_HIGH;
break;
}
stats.requestedImportance = requestedImportance;
stats.isNoisy = mSound != null || mVibration != null; //isNoisy可能优先级会和这个有关
if (mPreChannelsNotification
&& (importance == IMPORTANCE_UNSPECIFIED
|| (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_IMPORTANCE) == 0)) {
if (!stats.isNoisy && requestedImportance > IMPORTANCE_LOW) {
requestedImportance = IMPORTANCE_LOW;
}
if (stats.isNoisy) {
if (requestedImportance < IMPORTANCE_DEFAULT) {
requestedImportance = IMPORTANCE_DEFAULT;
}
}
if (n.fullScreenIntent != null) {
requestedImportance = IMPORTANCE_HIGH;
}
importance = requestedImportance;
}
stats.naturalImportance = importance;
return importance;
}