Loading services/core/java/com/android/server/EventLogTags.logtags +2 −0 Original line number Diff line number Diff line Loading @@ -92,6 +92,8 @@ option java_package com.android.server 27533 notification_autogrouped (key|3) # notification was removed from an autogroup 275534 notification_unautogrouped (key|3) # when a notification is adjusted via assistant 27535 notification_adjusted (key|3),(adjustment_type|3),(new_value|3) # --------------------------- # Watchdog.java Loading services/core/java/com/android/server/notification/NotificationManagerService.java +133 −45 Original line number Diff line number Diff line Loading @@ -5399,6 +5399,7 @@ public class NotificationManagerService extends SystemService { == IMPORTANCE_NONE) { cancelNotificationsFromListener(token, new String[]{r.getKey()}); } else { r.setPendingLogUpdate(true); needsSort = true; } } Loading Loading @@ -8059,64 +8060,151 @@ public class NotificationManagerService extends SystemService { } } static class NotificationRecordExtractorData { // Class that stores any field in a NotificationRecord that can change via an extractor. // Used to cache previous data used in a sort. int mPosition; int mVisibility; boolean mShowBadge; boolean mAllowBubble; boolean mIsBubble; NotificationChannel mChannel; String mGroupKey; ArrayList<String> mOverridePeople; ArrayList<SnoozeCriterion> mSnoozeCriteria; Integer mUserSentiment; Integer mSuppressVisually; ArrayList<Notification.Action> mSystemSmartActions; ArrayList<CharSequence> mSmartReplies; int mImportance; // These fields may not trigger a reranking but diffs here may be logged. float mRankingScore; boolean mIsConversation; NotificationRecordExtractorData(int position, int visibility, boolean showBadge, boolean allowBubble, boolean isBubble, NotificationChannel channel, String groupKey, ArrayList<String> overridePeople, ArrayList<SnoozeCriterion> snoozeCriteria, Integer userSentiment, Integer suppressVisually, ArrayList<Notification.Action> systemSmartActions, ArrayList<CharSequence> smartReplies, int importance, float rankingScore, boolean isConversation) { mPosition = position; mVisibility = visibility; mShowBadge = showBadge; mAllowBubble = allowBubble; mIsBubble = isBubble; mChannel = channel; mGroupKey = groupKey; mOverridePeople = overridePeople; mSnoozeCriteria = snoozeCriteria; mUserSentiment = userSentiment; mSuppressVisually = suppressVisually; mSystemSmartActions = systemSmartActions; mSmartReplies = smartReplies; mImportance = importance; mRankingScore = rankingScore; mIsConversation = isConversation; } // Returns whether the provided NotificationRecord differs from the cached data in any way. // Should be guarded by mNotificationLock; not annotated here as this class is static. boolean hasDiffForRankingLocked(NotificationRecord r, int newPosition) { return mPosition != newPosition || mVisibility != r.getPackageVisibilityOverride() || mShowBadge != r.canShowBadge() || mAllowBubble != r.canBubble() || mIsBubble != r.getNotification().isBubbleNotification() || !Objects.equals(mChannel, r.getChannel()) || !Objects.equals(mGroupKey, r.getGroupKey()) || !Objects.equals(mOverridePeople, r.getPeopleOverride()) || !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria()) || !Objects.equals(mUserSentiment, r.getUserSentiment()) || !Objects.equals(mSuppressVisually, r.getSuppressedVisualEffects()) || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions()) || !Objects.equals(mSmartReplies, r.getSmartReplies()) || mImportance != r.getImportance(); } // Returns whether the NotificationRecord has a change from this data for which we should // log an update. This method specifically targets fields that may be changed via // adjustments from the assistant. // // Fields here are the union of things in NotificationRecordLogger.shouldLogReported // and NotificationRecord.applyAdjustments. // // Should be guarded by mNotificationLock; not annotated here as this class is static. boolean hasDiffForLoggingLocked(NotificationRecord r, int newPosition) { return mPosition != newPosition || !Objects.equals(mChannel, r.getChannel()) || !Objects.equals(mGroupKey, r.getGroupKey()) || !Objects.equals(mOverridePeople, r.getPeopleOverride()) || !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria()) || !Objects.equals(mUserSentiment, r.getUserSentiment()) || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions()) || !Objects.equals(mSmartReplies, r.getSmartReplies()) || mImportance != r.getImportance() || !r.rankingScoreMatches(mRankingScore) || mIsConversation != r.isConversation(); } } void handleRankingSort() { if (mRankingHelper == null) return; synchronized (mNotificationLock) { final int N = mNotificationList.size(); // Any field that can change via one of the extractors needs to be added here. ArrayList<String> orderBefore = new ArrayList<>(N); int[] visibilities = new int[N]; boolean[] showBadges = new boolean[N]; boolean[] allowBubbles = new boolean[N]; boolean[] isBubble = new boolean[N]; ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N); ArrayList<String> groupKeyBefore = new ArrayList<>(N); ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N); ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N); ArrayList<Integer> userSentimentBefore = new ArrayList<>(N); ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N); ArrayList<ArrayList<Notification.Action>> systemSmartActionsBefore = new ArrayList<>(N); ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N); int[] importancesBefore = new int[N]; ArrayMap<String, NotificationRecordExtractorData> extractorDataBefore = new ArrayMap<>(N); for (int i = 0; i < N; i++) { final NotificationRecord r = mNotificationList.get(i); orderBefore.add(r.getKey()); visibilities[i] = r.getPackageVisibilityOverride(); showBadges[i] = r.canShowBadge(); allowBubbles[i] = r.canBubble(); isBubble[i] = r.getNotification().isBubbleNotification(); channelBefore.add(r.getChannel()); groupKeyBefore.add(r.getGroupKey()); overridePeopleBefore.add(r.getPeopleOverride()); snoozeCriteriaBefore.add(r.getSnoozeCriteria()); userSentimentBefore.add(r.getUserSentiment()); suppressVisuallyBefore.add(r.getSuppressedVisualEffects()); systemSmartActionsBefore.add(r.getSystemGeneratedSmartActions()); smartRepliesBefore.add(r.getSmartReplies()); importancesBefore[i] = r.getImportance(); NotificationRecordExtractorData extractorData = new NotificationRecordExtractorData( i, r.getPackageVisibilityOverride(), r.canShowBadge(), r.canBubble(), r.getNotification().isBubbleNotification(), r.getChannel(), r.getGroupKey(), r.getPeopleOverride(), r.getSnoozeCriteria(), r.getUserSentiment(), r.getSuppressedVisualEffects(), r.getSystemGeneratedSmartActions(), r.getSmartReplies(), r.getImportance(), r.getRankingScore(), r.isConversation()); extractorDataBefore.put(r.getKey(), extractorData); mRankingHelper.extractSignals(r); } mRankingHelper.sort(mNotificationList); for (int i = 0; i < N; i++) { final NotificationRecord r = mNotificationList.get(i); if (!orderBefore.get(i).equals(r.getKey()) || visibilities[i] != r.getPackageVisibilityOverride() || showBadges[i] != r.canShowBadge() || allowBubbles[i] != r.canBubble() || isBubble[i] != r.getNotification().isBubbleNotification() || !Objects.equals(channelBefore.get(i), r.getChannel()) || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey()) || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride()) || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria()) || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment()) || !Objects.equals(suppressVisuallyBefore.get(i), r.getSuppressedVisualEffects()) || !Objects.equals(systemSmartActionsBefore.get(i), r.getSystemGeneratedSmartActions()) || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies()) || importancesBefore[i] != r.getImportance()) { if (!extractorDataBefore.containsKey(r.getKey())) { // This shouldn't happen given that we just built this with all the // notifications, but check just to be safe. continue; } if (extractorDataBefore.get(r.getKey()).hasDiffForRankingLocked(r, i)) { mHandler.scheduleSendRankingUpdate(); return; } // If this notification is one for which we wanted to log an update, and // sufficient relevant bits are different, log update. if (r.hasPendingLogUpdate()) { // We need to acquire the previous data associated with this specific // notification, as the one at the current index may be unrelated if // notification order has changed. NotificationRecordExtractorData prevData = extractorDataBefore.get(r.getKey()); if (prevData.hasDiffForLoggingLocked(r, i)) { mNotificationRecordLogger.logNotificationAdjusted(r, i, 0, getGroupInstanceId(r.getSbn().getGroupKey())); } // Remove whether there was a diff or not; we've sorted the key, so if it // turns out there was nothing to log, that's fine too. r.setPendingLogUpdate(false); } } } Loading services/core/java/com/android/server/notification/NotificationRecord.java +43 −0 Original line number Diff line number Diff line Loading @@ -200,6 +200,10 @@ public final class NotificationRecord { private boolean mIsAppImportanceLocked; private ArraySet<Uri> mGrantableUris; // Whether this notification record should have an update logged the next time notifications // are sorted. private boolean mPendingLogUpdate = false; public NotificationRecord(Context context, StatusBarNotification sbn, NotificationChannel channel) { this.sbn = sbn; Loading Loading @@ -662,17 +666,23 @@ public final class NotificationRecord { final ArrayList<String> people = adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE); setPeopleOverride(people); EventLogTags.writeNotificationAdjusted( getKey(), Adjustment.KEY_PEOPLE, people.toString()); } if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) { final ArrayList<SnoozeCriterion> snoozeCriterionList = adjustment.getSignals().getParcelableArrayList( Adjustment.KEY_SNOOZE_CRITERIA); setSnoozeCriteria(snoozeCriterionList); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_SNOOZE_CRITERIA, snoozeCriterionList.toString()); } if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) { final String groupOverrideKey = adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY); setOverrideGroupKey(groupOverrideKey); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_GROUP_KEY, groupOverrideKey); } if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) { // Only allow user sentiment update from assistant if user hasn't already Loading @@ -681,27 +691,42 @@ public final class NotificationRecord { && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) { setUserSentiment(adjustment.getSignals().getInt( Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL)); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_USER_SENTIMENT, Integer.toString(getUserSentiment())); } } if (signals.containsKey(Adjustment.KEY_CONTEXTUAL_ACTIONS)) { setSystemGeneratedSmartActions( signals.getParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS)); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_CONTEXTUAL_ACTIONS, getSystemGeneratedSmartActions().toString()); } if (signals.containsKey(Adjustment.KEY_TEXT_REPLIES)) { setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES)); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_TEXT_REPLIES, getSmartReplies().toString()); } if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) { int importance = signals.getInt(Adjustment.KEY_IMPORTANCE); importance = Math.max(IMPORTANCE_UNSPECIFIED, importance); importance = Math.min(IMPORTANCE_HIGH, importance); setAssistantImportance(importance); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_IMPORTANCE, Integer.toString(importance)); } if (signals.containsKey(Adjustment.KEY_RANKING_SCORE)) { mRankingScore = signals.getFloat(Adjustment.KEY_RANKING_SCORE); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_RANKING_SCORE, Float.toString(mRankingScore)); } if (signals.containsKey(Adjustment.KEY_NOT_CONVERSATION)) { mIsNotConversationOverride = signals.getBoolean( Adjustment.KEY_NOT_CONVERSATION); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_NOT_CONVERSATION, Boolean.toString(mIsNotConversationOverride)); } if (!signals.isEmpty() && adjustment.getIssuer() != null) { mAdjustmentIssuer = adjustment.getIssuer(); Loading Loading @@ -1492,6 +1517,24 @@ public final class NotificationRecord { return sbn; } /** * Returns whether this record's ranking score is approximately equal to otherScore * (the difference must be within 0.0001). */ public boolean rankingScoreMatches(float otherScore) { return Math.abs(mRankingScore - otherScore) < 0.0001; } protected void setPendingLogUpdate(boolean pendingLogUpdate) { mPendingLogUpdate = pendingLogUpdate; } // If a caller of this function subsequently logs the update, they should also call // setPendingLogUpdate to false to make sure other callers don't also do so. protected boolean hasPendingLogUpdate() { return mPendingLogUpdate; } @VisibleForTesting static final class Light { public final int color; Loading services/core/java/com/android/server/notification/NotificationRecordLogger.java +19 −2 Original line number Diff line number Diff line Loading @@ -58,6 +58,20 @@ public interface NotificationRecordLogger { int position, int buzzBeepBlink, InstanceId groupId); /** * Logs a NotificationReported atom reflecting an adjustment to a notification. * Unlike maybeLogNotificationPosted, this method is guaranteed to log a notification update, * so the caller must take responsibility for checking that that logging update is necessary, * and that the notification is meaningfully changed. * @param r The NotificationRecord. If null, no action is taken. * @param position The position at which this notification is ranked. * @param buzzBeepBlink Logging code reflecting whether this notification alerted the user. * @param groupId The instance Id of the group summary notification, or null. */ void logNotificationAdjusted(@Nullable NotificationRecord r, int position, int buzzBeepBlink, InstanceId groupId); /** * Logs a notification cancel / dismiss event using UiEventReported (event ids from the * NotificationCancelledEvents enum). Loading Loading @@ -96,7 +110,9 @@ public interface NotificationRecordLogger { @UiEvent(doc = "New notification enqueued to post") NOTIFICATION_POSTED(162), @UiEvent(doc = "Notification substantially updated, or alerted again.") NOTIFICATION_UPDATED(163); NOTIFICATION_UPDATED(163), @UiEvent(doc = "Notification adjusted by assistant.") NOTIFICATION_ADJUSTED(908); private final int mId; NotificationReportedEvent(int id) { Loading Loading @@ -349,7 +365,8 @@ public interface NotificationRecordLogger { && Objects.equals(r.getSbn().getNotification().category, old.getSbn().getNotification().category) && (r.getImportance() == old.getImportance()) && (getLoggingImportance(r) == getLoggingImportance(old))); && (getLoggingImportance(r) == getLoggingImportance(old)) && r.rankingScoreMatches(old.getRankingScore())); } /** Loading services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java +32 −14 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.notification; import android.annotation.Nullable; import com.android.internal.logging.InstanceId; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; Loading @@ -37,38 +39,54 @@ public class NotificationRecordLoggerImpl implements NotificationRecordLogger { if (!p.shouldLogReported(buzzBeepBlink)) { return; } writeNotificationReportedAtom(p, NotificationReportedEvent.fromRecordPair(p), position, buzzBeepBlink, groupId); } @Override public void logNotificationAdjusted(@Nullable NotificationRecord r, int position, int buzzBeepBlink, InstanceId groupId) { NotificationRecordPair p = new NotificationRecordPair(r, null); writeNotificationReportedAtom(p, NotificationReportedEvent.NOTIFICATION_ADJUSTED, position, buzzBeepBlink, groupId); } private void writeNotificationReportedAtom(NotificationRecordPair p, NotificationReportedEvent eventType, int position, int buzzBeepBlink, InstanceId groupId) { FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED, /* int32 event_id = 1 */ NotificationReportedEvent.fromRecordPair(p).getId(), /* int32 uid = 2 */ r.getUid(), /* string package_name = 3 */ r.getSbn().getPackageName(), /* int32 event_id = 1 */ eventType.getId(), /* int32 uid = 2 */ p.r.getUid(), /* string package_name = 3 */ p.r.getSbn().getPackageName(), /* int32 instance_id = 4 */ p.getInstanceId(), /* int32 notification_id_hash = 5 */ p.getNotificationIdHash(), /* int32 channel_id_hash = 6 */ p.getChannelIdHash(), /* string group_id_hash = 7 */ p.getGroupIdHash(), /* int32 group_instance_id = 8 */ (groupId == null) ? 0 : groupId.getId(), /* bool is_group_summary = 9 */ r.getSbn().getNotification().isGroupSummary(), /* string category = 10 */ r.getSbn().getNotification().category, /* bool is_group_summary = 9 */ p.r.getSbn().getNotification().isGroupSummary(), /* string category = 10 */ p.r.getSbn().getNotification().category, /* int32 style = 11 */ p.getStyle(), /* int32 num_people = 12 */ p.getNumPeople(), /* int32 position = 13 */ position, /* android.stats.sysui.NotificationImportance importance = 14 */ NotificationRecordLogger.getLoggingImportance(r), NotificationRecordLogger.getLoggingImportance(p.r), /* int32 alerting = 15 */ buzzBeepBlink, /* NotificationImportanceExplanation importance_source = 16 */ r.getImportanceExplanationCode(), p.r.getImportanceExplanationCode(), /* android.stats.sysui.NotificationImportance importance_initial = 17 */ r.getInitialImportance(), p.r.getInitialImportance(), /* NotificationImportanceExplanation importance_initial_source = 18 */ r.getInitialImportanceExplanationCode(), p.r.getInitialImportanceExplanationCode(), /* android.stats.sysui.NotificationImportance importance_asst = 19 */ r.getAssistantImportance(), p.r.getAssistantImportance(), /* int32 assistant_hash = 20 */ p.getAssistantHash(), /* float assistant_ranking_score = 21 */ r.getRankingScore(), /* bool is_ongoing = 22 */ r.getSbn().isOngoing(), /* float assistant_ranking_score = 21 */ p.r.getRankingScore(), /* bool is_ongoing = 22 */ p.r.getSbn().isOngoing(), /* bool is_foreground_service = 23 */ NotificationRecordLogger.isForegroundService(r), NotificationRecordLogger.isForegroundService(p.r), /* optional int64 timeout_millis = 24 */ r.getSbn().getNotification().getTimeoutAfter() p.r.getSbn().getNotification().getTimeoutAfter() ); } Loading Loading
services/core/java/com/android/server/EventLogTags.logtags +2 −0 Original line number Diff line number Diff line Loading @@ -92,6 +92,8 @@ option java_package com.android.server 27533 notification_autogrouped (key|3) # notification was removed from an autogroup 275534 notification_unautogrouped (key|3) # when a notification is adjusted via assistant 27535 notification_adjusted (key|3),(adjustment_type|3),(new_value|3) # --------------------------- # Watchdog.java Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +133 −45 Original line number Diff line number Diff line Loading @@ -5399,6 +5399,7 @@ public class NotificationManagerService extends SystemService { == IMPORTANCE_NONE) { cancelNotificationsFromListener(token, new String[]{r.getKey()}); } else { r.setPendingLogUpdate(true); needsSort = true; } } Loading Loading @@ -8059,64 +8060,151 @@ public class NotificationManagerService extends SystemService { } } static class NotificationRecordExtractorData { // Class that stores any field in a NotificationRecord that can change via an extractor. // Used to cache previous data used in a sort. int mPosition; int mVisibility; boolean mShowBadge; boolean mAllowBubble; boolean mIsBubble; NotificationChannel mChannel; String mGroupKey; ArrayList<String> mOverridePeople; ArrayList<SnoozeCriterion> mSnoozeCriteria; Integer mUserSentiment; Integer mSuppressVisually; ArrayList<Notification.Action> mSystemSmartActions; ArrayList<CharSequence> mSmartReplies; int mImportance; // These fields may not trigger a reranking but diffs here may be logged. float mRankingScore; boolean mIsConversation; NotificationRecordExtractorData(int position, int visibility, boolean showBadge, boolean allowBubble, boolean isBubble, NotificationChannel channel, String groupKey, ArrayList<String> overridePeople, ArrayList<SnoozeCriterion> snoozeCriteria, Integer userSentiment, Integer suppressVisually, ArrayList<Notification.Action> systemSmartActions, ArrayList<CharSequence> smartReplies, int importance, float rankingScore, boolean isConversation) { mPosition = position; mVisibility = visibility; mShowBadge = showBadge; mAllowBubble = allowBubble; mIsBubble = isBubble; mChannel = channel; mGroupKey = groupKey; mOverridePeople = overridePeople; mSnoozeCriteria = snoozeCriteria; mUserSentiment = userSentiment; mSuppressVisually = suppressVisually; mSystemSmartActions = systemSmartActions; mSmartReplies = smartReplies; mImportance = importance; mRankingScore = rankingScore; mIsConversation = isConversation; } // Returns whether the provided NotificationRecord differs from the cached data in any way. // Should be guarded by mNotificationLock; not annotated here as this class is static. boolean hasDiffForRankingLocked(NotificationRecord r, int newPosition) { return mPosition != newPosition || mVisibility != r.getPackageVisibilityOverride() || mShowBadge != r.canShowBadge() || mAllowBubble != r.canBubble() || mIsBubble != r.getNotification().isBubbleNotification() || !Objects.equals(mChannel, r.getChannel()) || !Objects.equals(mGroupKey, r.getGroupKey()) || !Objects.equals(mOverridePeople, r.getPeopleOverride()) || !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria()) || !Objects.equals(mUserSentiment, r.getUserSentiment()) || !Objects.equals(mSuppressVisually, r.getSuppressedVisualEffects()) || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions()) || !Objects.equals(mSmartReplies, r.getSmartReplies()) || mImportance != r.getImportance(); } // Returns whether the NotificationRecord has a change from this data for which we should // log an update. This method specifically targets fields that may be changed via // adjustments from the assistant. // // Fields here are the union of things in NotificationRecordLogger.shouldLogReported // and NotificationRecord.applyAdjustments. // // Should be guarded by mNotificationLock; not annotated here as this class is static. boolean hasDiffForLoggingLocked(NotificationRecord r, int newPosition) { return mPosition != newPosition || !Objects.equals(mChannel, r.getChannel()) || !Objects.equals(mGroupKey, r.getGroupKey()) || !Objects.equals(mOverridePeople, r.getPeopleOverride()) || !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria()) || !Objects.equals(mUserSentiment, r.getUserSentiment()) || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions()) || !Objects.equals(mSmartReplies, r.getSmartReplies()) || mImportance != r.getImportance() || !r.rankingScoreMatches(mRankingScore) || mIsConversation != r.isConversation(); } } void handleRankingSort() { if (mRankingHelper == null) return; synchronized (mNotificationLock) { final int N = mNotificationList.size(); // Any field that can change via one of the extractors needs to be added here. ArrayList<String> orderBefore = new ArrayList<>(N); int[] visibilities = new int[N]; boolean[] showBadges = new boolean[N]; boolean[] allowBubbles = new boolean[N]; boolean[] isBubble = new boolean[N]; ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N); ArrayList<String> groupKeyBefore = new ArrayList<>(N); ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N); ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N); ArrayList<Integer> userSentimentBefore = new ArrayList<>(N); ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N); ArrayList<ArrayList<Notification.Action>> systemSmartActionsBefore = new ArrayList<>(N); ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N); int[] importancesBefore = new int[N]; ArrayMap<String, NotificationRecordExtractorData> extractorDataBefore = new ArrayMap<>(N); for (int i = 0; i < N; i++) { final NotificationRecord r = mNotificationList.get(i); orderBefore.add(r.getKey()); visibilities[i] = r.getPackageVisibilityOverride(); showBadges[i] = r.canShowBadge(); allowBubbles[i] = r.canBubble(); isBubble[i] = r.getNotification().isBubbleNotification(); channelBefore.add(r.getChannel()); groupKeyBefore.add(r.getGroupKey()); overridePeopleBefore.add(r.getPeopleOverride()); snoozeCriteriaBefore.add(r.getSnoozeCriteria()); userSentimentBefore.add(r.getUserSentiment()); suppressVisuallyBefore.add(r.getSuppressedVisualEffects()); systemSmartActionsBefore.add(r.getSystemGeneratedSmartActions()); smartRepliesBefore.add(r.getSmartReplies()); importancesBefore[i] = r.getImportance(); NotificationRecordExtractorData extractorData = new NotificationRecordExtractorData( i, r.getPackageVisibilityOverride(), r.canShowBadge(), r.canBubble(), r.getNotification().isBubbleNotification(), r.getChannel(), r.getGroupKey(), r.getPeopleOverride(), r.getSnoozeCriteria(), r.getUserSentiment(), r.getSuppressedVisualEffects(), r.getSystemGeneratedSmartActions(), r.getSmartReplies(), r.getImportance(), r.getRankingScore(), r.isConversation()); extractorDataBefore.put(r.getKey(), extractorData); mRankingHelper.extractSignals(r); } mRankingHelper.sort(mNotificationList); for (int i = 0; i < N; i++) { final NotificationRecord r = mNotificationList.get(i); if (!orderBefore.get(i).equals(r.getKey()) || visibilities[i] != r.getPackageVisibilityOverride() || showBadges[i] != r.canShowBadge() || allowBubbles[i] != r.canBubble() || isBubble[i] != r.getNotification().isBubbleNotification() || !Objects.equals(channelBefore.get(i), r.getChannel()) || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey()) || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride()) || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria()) || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment()) || !Objects.equals(suppressVisuallyBefore.get(i), r.getSuppressedVisualEffects()) || !Objects.equals(systemSmartActionsBefore.get(i), r.getSystemGeneratedSmartActions()) || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies()) || importancesBefore[i] != r.getImportance()) { if (!extractorDataBefore.containsKey(r.getKey())) { // This shouldn't happen given that we just built this with all the // notifications, but check just to be safe. continue; } if (extractorDataBefore.get(r.getKey()).hasDiffForRankingLocked(r, i)) { mHandler.scheduleSendRankingUpdate(); return; } // If this notification is one for which we wanted to log an update, and // sufficient relevant bits are different, log update. if (r.hasPendingLogUpdate()) { // We need to acquire the previous data associated with this specific // notification, as the one at the current index may be unrelated if // notification order has changed. NotificationRecordExtractorData prevData = extractorDataBefore.get(r.getKey()); if (prevData.hasDiffForLoggingLocked(r, i)) { mNotificationRecordLogger.logNotificationAdjusted(r, i, 0, getGroupInstanceId(r.getSbn().getGroupKey())); } // Remove whether there was a diff or not; we've sorted the key, so if it // turns out there was nothing to log, that's fine too. r.setPendingLogUpdate(false); } } } Loading
services/core/java/com/android/server/notification/NotificationRecord.java +43 −0 Original line number Diff line number Diff line Loading @@ -200,6 +200,10 @@ public final class NotificationRecord { private boolean mIsAppImportanceLocked; private ArraySet<Uri> mGrantableUris; // Whether this notification record should have an update logged the next time notifications // are sorted. private boolean mPendingLogUpdate = false; public NotificationRecord(Context context, StatusBarNotification sbn, NotificationChannel channel) { this.sbn = sbn; Loading Loading @@ -662,17 +666,23 @@ public final class NotificationRecord { final ArrayList<String> people = adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE); setPeopleOverride(people); EventLogTags.writeNotificationAdjusted( getKey(), Adjustment.KEY_PEOPLE, people.toString()); } if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) { final ArrayList<SnoozeCriterion> snoozeCriterionList = adjustment.getSignals().getParcelableArrayList( Adjustment.KEY_SNOOZE_CRITERIA); setSnoozeCriteria(snoozeCriterionList); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_SNOOZE_CRITERIA, snoozeCriterionList.toString()); } if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) { final String groupOverrideKey = adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY); setOverrideGroupKey(groupOverrideKey); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_GROUP_KEY, groupOverrideKey); } if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) { // Only allow user sentiment update from assistant if user hasn't already Loading @@ -681,27 +691,42 @@ public final class NotificationRecord { && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) { setUserSentiment(adjustment.getSignals().getInt( Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL)); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_USER_SENTIMENT, Integer.toString(getUserSentiment())); } } if (signals.containsKey(Adjustment.KEY_CONTEXTUAL_ACTIONS)) { setSystemGeneratedSmartActions( signals.getParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS)); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_CONTEXTUAL_ACTIONS, getSystemGeneratedSmartActions().toString()); } if (signals.containsKey(Adjustment.KEY_TEXT_REPLIES)) { setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES)); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_TEXT_REPLIES, getSmartReplies().toString()); } if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) { int importance = signals.getInt(Adjustment.KEY_IMPORTANCE); importance = Math.max(IMPORTANCE_UNSPECIFIED, importance); importance = Math.min(IMPORTANCE_HIGH, importance); setAssistantImportance(importance); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_IMPORTANCE, Integer.toString(importance)); } if (signals.containsKey(Adjustment.KEY_RANKING_SCORE)) { mRankingScore = signals.getFloat(Adjustment.KEY_RANKING_SCORE); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_RANKING_SCORE, Float.toString(mRankingScore)); } if (signals.containsKey(Adjustment.KEY_NOT_CONVERSATION)) { mIsNotConversationOverride = signals.getBoolean( Adjustment.KEY_NOT_CONVERSATION); EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_NOT_CONVERSATION, Boolean.toString(mIsNotConversationOverride)); } if (!signals.isEmpty() && adjustment.getIssuer() != null) { mAdjustmentIssuer = adjustment.getIssuer(); Loading Loading @@ -1492,6 +1517,24 @@ public final class NotificationRecord { return sbn; } /** * Returns whether this record's ranking score is approximately equal to otherScore * (the difference must be within 0.0001). */ public boolean rankingScoreMatches(float otherScore) { return Math.abs(mRankingScore - otherScore) < 0.0001; } protected void setPendingLogUpdate(boolean pendingLogUpdate) { mPendingLogUpdate = pendingLogUpdate; } // If a caller of this function subsequently logs the update, they should also call // setPendingLogUpdate to false to make sure other callers don't also do so. protected boolean hasPendingLogUpdate() { return mPendingLogUpdate; } @VisibleForTesting static final class Light { public final int color; Loading
services/core/java/com/android/server/notification/NotificationRecordLogger.java +19 −2 Original line number Diff line number Diff line Loading @@ -58,6 +58,20 @@ public interface NotificationRecordLogger { int position, int buzzBeepBlink, InstanceId groupId); /** * Logs a NotificationReported atom reflecting an adjustment to a notification. * Unlike maybeLogNotificationPosted, this method is guaranteed to log a notification update, * so the caller must take responsibility for checking that that logging update is necessary, * and that the notification is meaningfully changed. * @param r The NotificationRecord. If null, no action is taken. * @param position The position at which this notification is ranked. * @param buzzBeepBlink Logging code reflecting whether this notification alerted the user. * @param groupId The instance Id of the group summary notification, or null. */ void logNotificationAdjusted(@Nullable NotificationRecord r, int position, int buzzBeepBlink, InstanceId groupId); /** * Logs a notification cancel / dismiss event using UiEventReported (event ids from the * NotificationCancelledEvents enum). Loading Loading @@ -96,7 +110,9 @@ public interface NotificationRecordLogger { @UiEvent(doc = "New notification enqueued to post") NOTIFICATION_POSTED(162), @UiEvent(doc = "Notification substantially updated, or alerted again.") NOTIFICATION_UPDATED(163); NOTIFICATION_UPDATED(163), @UiEvent(doc = "Notification adjusted by assistant.") NOTIFICATION_ADJUSTED(908); private final int mId; NotificationReportedEvent(int id) { Loading Loading @@ -349,7 +365,8 @@ public interface NotificationRecordLogger { && Objects.equals(r.getSbn().getNotification().category, old.getSbn().getNotification().category) && (r.getImportance() == old.getImportance()) && (getLoggingImportance(r) == getLoggingImportance(old))); && (getLoggingImportance(r) == getLoggingImportance(old)) && r.rankingScoreMatches(old.getRankingScore())); } /** Loading
services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java +32 −14 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.notification; import android.annotation.Nullable; import com.android.internal.logging.InstanceId; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; Loading @@ -37,38 +39,54 @@ public class NotificationRecordLoggerImpl implements NotificationRecordLogger { if (!p.shouldLogReported(buzzBeepBlink)) { return; } writeNotificationReportedAtom(p, NotificationReportedEvent.fromRecordPair(p), position, buzzBeepBlink, groupId); } @Override public void logNotificationAdjusted(@Nullable NotificationRecord r, int position, int buzzBeepBlink, InstanceId groupId) { NotificationRecordPair p = new NotificationRecordPair(r, null); writeNotificationReportedAtom(p, NotificationReportedEvent.NOTIFICATION_ADJUSTED, position, buzzBeepBlink, groupId); } private void writeNotificationReportedAtom(NotificationRecordPair p, NotificationReportedEvent eventType, int position, int buzzBeepBlink, InstanceId groupId) { FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED, /* int32 event_id = 1 */ NotificationReportedEvent.fromRecordPair(p).getId(), /* int32 uid = 2 */ r.getUid(), /* string package_name = 3 */ r.getSbn().getPackageName(), /* int32 event_id = 1 */ eventType.getId(), /* int32 uid = 2 */ p.r.getUid(), /* string package_name = 3 */ p.r.getSbn().getPackageName(), /* int32 instance_id = 4 */ p.getInstanceId(), /* int32 notification_id_hash = 5 */ p.getNotificationIdHash(), /* int32 channel_id_hash = 6 */ p.getChannelIdHash(), /* string group_id_hash = 7 */ p.getGroupIdHash(), /* int32 group_instance_id = 8 */ (groupId == null) ? 0 : groupId.getId(), /* bool is_group_summary = 9 */ r.getSbn().getNotification().isGroupSummary(), /* string category = 10 */ r.getSbn().getNotification().category, /* bool is_group_summary = 9 */ p.r.getSbn().getNotification().isGroupSummary(), /* string category = 10 */ p.r.getSbn().getNotification().category, /* int32 style = 11 */ p.getStyle(), /* int32 num_people = 12 */ p.getNumPeople(), /* int32 position = 13 */ position, /* android.stats.sysui.NotificationImportance importance = 14 */ NotificationRecordLogger.getLoggingImportance(r), NotificationRecordLogger.getLoggingImportance(p.r), /* int32 alerting = 15 */ buzzBeepBlink, /* NotificationImportanceExplanation importance_source = 16 */ r.getImportanceExplanationCode(), p.r.getImportanceExplanationCode(), /* android.stats.sysui.NotificationImportance importance_initial = 17 */ r.getInitialImportance(), p.r.getInitialImportance(), /* NotificationImportanceExplanation importance_initial_source = 18 */ r.getInitialImportanceExplanationCode(), p.r.getInitialImportanceExplanationCode(), /* android.stats.sysui.NotificationImportance importance_asst = 19 */ r.getAssistantImportance(), p.r.getAssistantImportance(), /* int32 assistant_hash = 20 */ p.getAssistantHash(), /* float assistant_ranking_score = 21 */ r.getRankingScore(), /* bool is_ongoing = 22 */ r.getSbn().isOngoing(), /* float assistant_ranking_score = 21 */ p.r.getRankingScore(), /* bool is_ongoing = 22 */ p.r.getSbn().isOngoing(), /* bool is_foreground_service = 23 */ NotificationRecordLogger.isForegroundService(r), NotificationRecordLogger.isForegroundService(p.r), /* optional int64 timeout_millis = 24 */ r.getSbn().getNotification().getTimeoutAfter() p.r.getSbn().getNotification().getTimeoutAfter() ); } Loading