Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ddd2347 authored by Yuri Lin's avatar Yuri Lin Committed by Automerger Merge Worker
Browse files

Merge "Log a notification update when fields such as importance or ranking...

Merge "Log a notification update when fields such as importance or ranking score are updated." into sc-qpr1-dev am: e97b0e8f

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15890028

Change-Id: I0f7077a265a409626eb2606474c77c88e685902b
parents aba82c26 e97b0e8f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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
+133 −45
Original line number Diff line number Diff line
@@ -5397,6 +5397,7 @@ public class NotificationManagerService extends SystemService {
                                    == IMPORTANCE_NONE) {
                                cancelNotificationsFromListener(token, new String[]{r.getKey()});
                            } else {
                                r.setPendingLogUpdate(true);
                                needsSort = true;
                            }
                        }
@@ -8057,64 +8058,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);
                }
            }
        }
+43 −0
Original line number Diff line number Diff line
@@ -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;
@@ -648,17 +652,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
@@ -667,27 +677,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();
@@ -1478,6 +1503,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;
+19 −2
Original line number Diff line number Diff line
@@ -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).
@@ -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) {
@@ -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()));
        }

        /**
+29 −11
Original line number Diff line number Diff line
@@ -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;
@@ -37,33 +39,49 @@ 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()
                /* float assistant_ranking_score = 21 */ p.r.getRankingScore()
        );
    }

Loading