• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.server.notification;
17 
18 import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
19 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
20 import static android.app.NotificationManager.IMPORTANCE_HIGH;
21 import static android.app.NotificationManager.IMPORTANCE_LOW;
22 import static android.app.NotificationManager.IMPORTANCE_MIN;
23 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
24 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
25 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
26 
27 import android.annotation.Nullable;
28 import android.app.ActivityManager;
29 import android.app.IActivityManager;
30 import android.app.Notification;
31 import android.app.NotificationChannel;
32 import android.app.Person;
33 import android.content.ContentProvider;
34 import android.content.ContentResolver;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.pm.PackageManager;
38 import android.content.pm.PackageManagerInternal;
39 import android.content.pm.ShortcutInfo;
40 import android.graphics.Bitmap;
41 import android.media.AudioAttributes;
42 import android.media.AudioSystem;
43 import android.metrics.LogMaker;
44 import android.net.Uri;
45 import android.os.Binder;
46 import android.os.Build;
47 import android.os.Bundle;
48 import android.os.IBinder;
49 import android.os.UserHandle;
50 import android.os.VibrationEffect;
51 import android.provider.Settings;
52 import android.service.notification.Adjustment;
53 import android.service.notification.NotificationListenerService;
54 import android.service.notification.NotificationRecordProto;
55 import android.service.notification.NotificationStats;
56 import android.service.notification.SnoozeCriterion;
57 import android.service.notification.StatusBarNotification;
58 import android.text.TextUtils;
59 import android.util.ArraySet;
60 import android.util.Log;
61 import android.util.TimeUtils;
62 import android.util.proto.ProtoOutputStream;
63 import android.widget.RemoteViews;
64 
65 import com.android.internal.annotations.VisibleForTesting;
66 import com.android.internal.logging.MetricsLogger;
67 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
68 import com.android.server.EventLogTags;
69 import com.android.server.LocalServices;
70 import com.android.server.uri.UriGrantsManagerInternal;
71 
72 import dalvik.annotation.optimization.NeverCompile;
73 
74 import java.io.PrintWriter;
75 import java.lang.reflect.Array;
76 import java.util.ArrayList;
77 import java.util.Arrays;
78 import java.util.List;
79 import java.util.Objects;
80 
81 /**
82  * Holds data about notifications that should not be shared with the
83  * {@link android.service.notification.NotificationListenerService}s.
84  *
85  * <p>These objects should not be mutated unless the code is synchronized
86  * on {@link NotificationManagerService#mNotificationLock}, and any
87  * modification should be followed by a sorting of that list.</p>
88  *
89  * <p>Is sortable by {@link NotificationComparator}.</p>
90  *
91  * {@hide}
92  */
93 public final class NotificationRecord {
94     static final String TAG = "NotificationRecord";
95     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
96     // the period after which a notification is updated where it can make sound
97     private static final int MAX_SOUND_DELAY_MS = 2000;
98     private final StatusBarNotification sbn;
99     IActivityManager mAm;
100     UriGrantsManagerInternal mUgmInternal;
101     final int mTargetSdkVersion;
102     final int mOriginalFlags;
103     private final Context mContext;
104 
105     NotificationUsageStats.SingleNotificationStats stats;
106     boolean isCanceled;
107     IBinder permissionOwner;
108 
109     // These members are used by NotificationSignalExtractors
110     // to communicate with the ranking module.
111     private float mContactAffinity;
112     private boolean mRecentlyIntrusive;
113     private long mLastIntrusive;
114 
115     // is this notification currently being intercepted by Zen Mode?
116     private boolean mIntercept;
117     // has the intercept value been set explicitly? we only want to log it if new or changed
118     private boolean mInterceptSet;
119 
120     // is this notification hidden since the app pkg is suspended?
121     private boolean mHidden;
122 
123     // The timestamp used for ranking.
124     private long mRankingTimeMs;
125 
126     // The first post time, stable across updates.
127     private long mCreationTimeMs;
128 
129     // The most recent visibility event.
130     private long mVisibleSinceMs;
131 
132     // The most recent update time, or the creation time if no updates.
133     @VisibleForTesting
134     final long mUpdateTimeMs;
135 
136     // The most recent interruption time, or the creation time if no updates. Differs from the
137     // above value because updates are filtered based on whether they actually interrupted the
138     // user
139     private long mInterruptionTimeMs;
140 
141     // The most recent time the notification made noise or buzzed the device, or -1 if it did not.
142     private long mLastAudiblyAlertedMs;
143 
144     // Is this record an update of an old record?
145     public boolean isUpdate;
146     private int mPackagePriority;
147 
148     private int mAuthoritativeRank;
149     private String mGlobalSortKey;
150     private int mPackageVisibility;
151     private int mSystemImportance = IMPORTANCE_UNSPECIFIED;
152     private int mAssistantImportance = IMPORTANCE_UNSPECIFIED;
153     private int mImportance = IMPORTANCE_UNSPECIFIED;
154     private float mRankingScore = 0f;
155     // Field used in global sort key to bypass normal notifications
156     private int mCriticality = CriticalNotificationExtractor.NORMAL;
157     // A MetricsEvent.NotificationImportanceExplanation, tracking source of mImportance.
158     private int mImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN;
159     // A MetricsEvent.NotificationImportanceExplanation for initial importance.
160     private int mInitialImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN;
161 
162     private int mSuppressedVisualEffects = 0;
163     private String mUserExplanation;
164     private boolean mPreChannelsNotification = true;
165     private Uri mSound;
166     private VibrationEffect mVibration;
167     private AudioAttributes mAttributes;
168     private NotificationChannel mChannel;
169     private ArrayList<String> mPeopleOverride;
170     private ArrayList<SnoozeCriterion> mSnoozeCriteria;
171     private boolean mShowBadge;
172     private boolean mAllowBubble;
173     private Light mLight;
174     private boolean mIsNotConversationOverride;
175     private ShortcutInfo mShortcutInfo;
176     /**
177      * This list contains system generated smart actions from NAS, app-generated smart actions are
178      * stored in Notification.actions with isContextual() set to true.
179      */
180     private ArrayList<Notification.Action> mSystemGeneratedSmartActions;
181     private ArrayList<CharSequence> mSmartReplies;
182 
183     private final List<Adjustment> mAdjustments;
184     private String mAdjustmentIssuer;
185     private final NotificationStats mStats;
186     private int mUserSentiment;
187     private boolean mIsInterruptive;
188     private boolean mTextChanged;
189     private boolean mRecordedInterruption;
190     private int mNumberOfSmartRepliesAdded;
191     private int mNumberOfSmartActionsAdded;
192     private boolean mSuggestionsGeneratedByAssistant;
193     private boolean mEditChoicesBeforeSending;
194     private boolean mHasSeenSmartReplies;
195     private boolean mFlagBubbleRemoved;
196     private boolean mPostSilently;
197     private boolean mHasSentValidMsg;
198     private boolean mAppDemotedFromConvo;
199     private boolean mPkgAllowedAsConvo;
200     private boolean mImportanceFixed;
201     /**
202      * Whether this notification (and its channels) should be considered user locked. Used in
203      * conjunction with user sentiment calculation.
204      */
205     private boolean mIsAppImportanceLocked;
206     private ArraySet<Uri> mGrantableUris;
207 
208     // Storage for phone numbers that were found to be associated with
209     // contacts in this notification.
210     private ArraySet<String> mPhoneNumbers;
211 
212     // Whether this notification record should have an update logged the next time notifications
213     // are sorted.
214     private boolean mPendingLogUpdate = false;
215     private int mProposedImportance = IMPORTANCE_UNSPECIFIED;
216 
NotificationRecord(Context context, StatusBarNotification sbn, NotificationChannel channel)217     public NotificationRecord(Context context, StatusBarNotification sbn,
218             NotificationChannel channel) {
219         this.sbn = sbn;
220         mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class)
221                 .getPackageTargetSdkVersion(sbn.getPackageName());
222         mAm = ActivityManager.getService();
223         mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
224         mOriginalFlags = sbn.getNotification().flags;
225         mRankingTimeMs = calculateRankingTimeMs(0L);
226         mCreationTimeMs = sbn.getPostTime();
227         mUpdateTimeMs = mCreationTimeMs;
228         mInterruptionTimeMs = mCreationTimeMs;
229         mContext = context;
230         stats = new NotificationUsageStats.SingleNotificationStats();
231         mChannel = channel;
232         mPreChannelsNotification = isPreChannelsNotification();
233         mSound = calculateSound();
234         mVibration = calculateVibration();
235         mAttributes = calculateAttributes();
236         mImportance = calculateInitialImportance();
237         mLight = calculateLights();
238         mAdjustments = new ArrayList<>();
239         mStats = new NotificationStats();
240         calculateUserSentiment();
241         calculateGrantableUris();
242     }
243 
isPreChannelsNotification()244     private boolean isPreChannelsNotification() {
245         if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
246             if (mTargetSdkVersion < Build.VERSION_CODES.O) {
247                 return true;
248             }
249         }
250         return false;
251     }
252 
calculateSound()253     private Uri calculateSound() {
254         final Notification n = getSbn().getNotification();
255 
256         // No notification sounds on tv
257         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
258             return null;
259         }
260 
261         Uri sound = mChannel.getSound();
262         if (mPreChannelsNotification && (getChannel().getUserLockedFields()
263                 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
264 
265             final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0;
266             if (useDefaultSound) {
267                 sound = Settings.System.DEFAULT_NOTIFICATION_URI;
268             } else {
269                 sound = n.sound;
270             }
271         }
272         return sound;
273     }
274 
calculateLights()275     private Light calculateLights() {
276         int defaultLightColor = mContext.getResources().getColor(
277                 com.android.internal.R.color.config_defaultNotificationColor);
278         int defaultLightOn = mContext.getResources().getInteger(
279                 com.android.internal.R.integer.config_defaultNotificationLedOn);
280         int defaultLightOff = mContext.getResources().getInteger(
281                 com.android.internal.R.integer.config_defaultNotificationLedOff);
282 
283         int channelLightColor = getChannel().getLightColor() != 0 ? getChannel().getLightColor()
284                 : defaultLightColor;
285         Light light = getChannel().shouldShowLights() ? new Light(channelLightColor,
286                 defaultLightOn, defaultLightOff) : null;
287         if (mPreChannelsNotification
288                 && (getChannel().getUserLockedFields()
289                 & NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
290             final Notification notification = getSbn().getNotification();
291             if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
292                 light = new Light(notification.ledARGB, notification.ledOnMS,
293                         notification.ledOffMS);
294                 if ((notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
295                     light = new Light(defaultLightColor, defaultLightOn,
296                             defaultLightOff);
297                 }
298             } else {
299                 light = null;
300             }
301         }
302         return light;
303     }
304 
calculateVibration()305     private VibrationEffect calculateVibration() {
306         VibratorHelper helper = new VibratorHelper(mContext);
307         final Notification notification = getSbn().getNotification();
308         final boolean insistent = (notification.flags & Notification.FLAG_INSISTENT) != 0;
309         VibrationEffect defaultVibration = helper.createDefaultVibration(insistent);
310         VibrationEffect vibration;
311         if (getChannel().shouldVibrate()) {
312             vibration = getChannel().getVibrationPattern() == null
313                     ? defaultVibration
314                     : helper.createWaveformVibration(getChannel().getVibrationPattern(), insistent);
315         } else {
316             vibration = null;
317         }
318         if (mPreChannelsNotification
319                 && (getChannel().getUserLockedFields()
320                 & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
321             final boolean useDefaultVibrate =
322                     (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
323             if (useDefaultVibrate) {
324                 vibration = defaultVibration;
325             } else {
326                 vibration = helper.createWaveformVibration(notification.vibrate, insistent);
327             }
328         }
329         return vibration;
330     }
331 
calculateAttributes()332     private AudioAttributes calculateAttributes() {
333         final Notification n = getSbn().getNotification();
334         AudioAttributes attributes = getChannel().getAudioAttributes();
335         if (attributes == null) {
336             attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
337         }
338 
339         if (mPreChannelsNotification
340                 && (getChannel().getUserLockedFields()
341                 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
342             if (n.audioAttributes != null) {
343                 // prefer audio attributes to stream type
344                 attributes = n.audioAttributes;
345             } else if (n.audioStreamType >= 0
346                     && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
347                 // the stream type is valid, use it
348                 attributes = new AudioAttributes.Builder()
349                         .setInternalLegacyStreamType(n.audioStreamType)
350                         .build();
351             } else if (n.audioStreamType != AudioSystem.STREAM_DEFAULT) {
352                 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
353             }
354         }
355         return attributes;
356     }
357 
calculateInitialImportance()358     private int calculateInitialImportance() {
359         final Notification n = getSbn().getNotification();
360         int importance = getChannel().getImportance();  // Post-channels notifications use this
361         mInitialImportanceExplanationCode = getChannel().hasUserSetImportance()
362                 ? MetricsEvent.IMPORTANCE_EXPLANATION_USER
363                 : MetricsEvent.IMPORTANCE_EXPLANATION_APP;
364 
365         // Migrate notification priority flag to a priority value.
366         if (0 != (n.flags & Notification.FLAG_HIGH_PRIORITY)) {
367             n.priority = Notification.PRIORITY_MAX;
368         }
369 
370         // Convert priority value to an importance value, used only for pre-channels notifications.
371         int requestedImportance = IMPORTANCE_DEFAULT;
372         n.priority = NotificationManagerService.clamp(n.priority, Notification.PRIORITY_MIN,
373                 Notification.PRIORITY_MAX);
374         switch (n.priority) {
375             case Notification.PRIORITY_MIN:
376                 requestedImportance = IMPORTANCE_MIN;
377                 break;
378             case Notification.PRIORITY_LOW:
379                 requestedImportance = IMPORTANCE_LOW;
380                 break;
381             case Notification.PRIORITY_DEFAULT:
382                 requestedImportance = IMPORTANCE_DEFAULT;
383                 break;
384             case Notification.PRIORITY_HIGH:
385             case Notification.PRIORITY_MAX:
386                 requestedImportance = IMPORTANCE_HIGH;
387                 break;
388         }
389         stats.requestedImportance = requestedImportance;
390         stats.isNoisy = mSound != null || mVibration != null;
391 
392         // For pre-channels notifications, apply system overrides and then use requestedImportance
393         // as importance.
394         if (mPreChannelsNotification
395                 && (importance == IMPORTANCE_UNSPECIFIED
396                 || (!getChannel().hasUserSetImportance()))) {
397             if (!stats.isNoisy && requestedImportance > IMPORTANCE_LOW) {
398                 requestedImportance = IMPORTANCE_LOW;
399             }
400 
401             if (stats.isNoisy) {
402                 if (requestedImportance < IMPORTANCE_DEFAULT) {
403                     requestedImportance = IMPORTANCE_DEFAULT;
404                 }
405             }
406 
407             if (n.fullScreenIntent != null) {
408                 requestedImportance = IMPORTANCE_HIGH;
409             }
410             importance = requestedImportance;
411             mInitialImportanceExplanationCode =
412                     MetricsEvent.IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS;
413         }
414 
415         stats.naturalImportance = importance;
416         return importance;
417     }
418 
419     // copy any notes that the ranking system may have made before the update
copyRankingInformation(NotificationRecord previous)420     public void copyRankingInformation(NotificationRecord previous) {
421         mContactAffinity = previous.mContactAffinity;
422         mRecentlyIntrusive = previous.mRecentlyIntrusive;
423         mPackagePriority = previous.mPackagePriority;
424         mPackageVisibility = previous.mPackageVisibility;
425         mIntercept = previous.mIntercept;
426         mHidden = previous.mHidden;
427         mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
428         mCreationTimeMs = previous.mCreationTimeMs;
429         mVisibleSinceMs = previous.mVisibleSinceMs;
430         if (previous.getSbn().getOverrideGroupKey() != null && !getSbn().isAppGroup()) {
431             getSbn().setOverrideGroupKey(previous.getSbn().getOverrideGroupKey());
432         }
433         // Don't copy importance information or mGlobalSortKey, recompute them.
434     }
435 
getNotification()436     public Notification getNotification() { return getSbn().getNotification(); }
getFlags()437     public int getFlags() { return getSbn().getNotification().flags; }
getUser()438     public UserHandle getUser() { return getSbn().getUser(); }
getKey()439     public String getKey() { return getSbn().getKey(); }
440     /** @deprecated Use {@link #getUser()} instead. */
getUserId()441     public int getUserId() { return getSbn().getUserId(); }
getUid()442     public int getUid() { return getSbn().getUid(); }
443 
dump(ProtoOutputStream proto, long fieldId, boolean redact, int state)444     void dump(ProtoOutputStream proto, long fieldId, boolean redact, int state) {
445         final long token = proto.start(fieldId);
446 
447         proto.write(NotificationRecordProto.KEY, getSbn().getKey());
448         proto.write(NotificationRecordProto.STATE, state);
449         if (getChannel() != null) {
450             proto.write(NotificationRecordProto.CHANNEL_ID, getChannel().getId());
451         }
452         proto.write(NotificationRecordProto.CAN_SHOW_LIGHT, getLight() != null);
453         proto.write(NotificationRecordProto.CAN_VIBRATE, getVibration() != null);
454         proto.write(NotificationRecordProto.FLAGS, getSbn().getNotification().flags);
455         proto.write(NotificationRecordProto.GROUP_KEY, getGroupKey());
456         proto.write(NotificationRecordProto.IMPORTANCE, getImportance());
457         if (getSound() != null) {
458             proto.write(NotificationRecordProto.SOUND, getSound().toString());
459         }
460         if (getAudioAttributes() != null) {
461             getAudioAttributes().dumpDebug(proto, NotificationRecordProto.AUDIO_ATTRIBUTES);
462         }
463         proto.write(NotificationRecordProto.PACKAGE, getSbn().getPackageName());
464         proto.write(NotificationRecordProto.DELEGATE_PACKAGE, getSbn().getOpPkg());
465 
466         proto.end(token);
467     }
468 
formatRemoteViews(RemoteViews rv)469     String formatRemoteViews(RemoteViews rv) {
470         if (rv == null) return "null";
471         return String.format("%s/0x%08x (%d bytes): %s",
472             rv.getPackage(), rv.getLayoutId(), rv.estimateMemoryUsage(), rv.toString());
473     }
474 
475     @NeverCompile // Avoid size overhead of debugging code.
dump(PrintWriter pw, String prefix, Context baseContext, boolean redact)476     void dump(PrintWriter pw, String prefix, Context baseContext, boolean redact) {
477         final Notification notification = getSbn().getNotification();
478         pw.println(prefix + this);
479         prefix = prefix + "  ";
480         pw.println(prefix + "uid=" + getSbn().getUid() + " userId=" + getSbn().getUserId());
481         pw.println(prefix + "opPkg=" + getSbn().getOpPkg());
482         pw.println(prefix + "icon=" + notification.getSmallIcon());
483         pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
484         pw.println(prefix + "originalFlags=0x" + Integer.toHexString(mOriginalFlags));
485         pw.println(prefix + "pri=" + notification.priority);
486         pw.println(prefix + "key=" + getSbn().getKey());
487         pw.println(prefix + "seen=" + mStats.hasSeen());
488         pw.println(prefix + "groupKey=" + getGroupKey());
489         pw.println(prefix + "notification=");
490         dumpNotification(pw, prefix + prefix, notification, redact);
491         pw.println(prefix + "publicNotification=");
492         dumpNotification(pw, prefix + prefix, notification.publicVersion, redact);
493         pw.println(prefix + "stats=" + stats.toString());
494         pw.println(prefix + "mContactAffinity=" + mContactAffinity);
495         pw.println(prefix + "mRecentlyIntrusive=" + mRecentlyIntrusive);
496         pw.println(prefix + "mPackagePriority=" + mPackagePriority);
497         pw.println(prefix + "mPackageVisibility=" + mPackageVisibility);
498         pw.println(prefix + "mSystemImportance="
499                 + NotificationListenerService.Ranking.importanceToString(mSystemImportance));
500         pw.println(prefix + "mAsstImportance="
501                 + NotificationListenerService.Ranking.importanceToString(mAssistantImportance));
502         pw.println(prefix + "mImportance="
503                 + NotificationListenerService.Ranking.importanceToString(mImportance));
504         pw.println(prefix + "mImportanceExplanation=" + getImportanceExplanation());
505         pw.println(prefix + "mProposedImportance="
506                 + NotificationListenerService.Ranking.importanceToString(mProposedImportance));
507         pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked);
508         pw.println(prefix + "mIntercept=" + mIntercept);
509         pw.println(prefix + "mHidden==" + mHidden);
510         pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
511         pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
512         pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
513         pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
514         pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
515         pw.println(prefix + "mInterruptionTimeMs=" + mInterruptionTimeMs);
516         pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
517         if (mPreChannelsNotification) {
518             pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
519                     notification.defaults, notification.flags));
520             pw.println(prefix + "n.sound=" + notification.sound);
521             pw.println(prefix + "n.audioStreamType=" + notification.audioStreamType);
522             pw.println(prefix + "n.audioAttributes=" + notification.audioAttributes);
523             pw.println(prefix + String.format("  led=0x%08x onMs=%d offMs=%d",
524                     notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
525             pw.println(prefix + "vibrate=" + Arrays.toString(notification.vibrate));
526         }
527         pw.println(prefix + "mSound= " + mSound);
528         pw.println(prefix + "mVibration= " + mVibration);
529         pw.println(prefix + "mAttributes= " + mAttributes);
530         pw.println(prefix + "mLight= " + mLight);
531         pw.println(prefix + "mShowBadge=" + mShowBadge);
532         pw.println(prefix + "mColorized=" + notification.isColorized());
533         pw.println(prefix + "mAllowBubble=" + mAllowBubble);
534         pw.println(prefix + "isBubble=" + notification.isBubbleNotification());
535         pw.println(prefix + "mIsInterruptive=" + mIsInterruptive);
536         pw.println(prefix + "effectiveNotificationChannel=" + getChannel());
537         if (getPeopleOverride() != null) {
538             pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride()));
539         }
540         if (getSnoozeCriteria() != null) {
541             pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
542         }
543         pw.println(prefix + "mAdjustments=" + mAdjustments);
544         pw.println(prefix + "shortcut=" + notification.getShortcutId()
545                 + " found valid? " + (mShortcutInfo != null));
546     }
547 
dumpNotification(PrintWriter pw, String prefix, Notification notification, boolean redact)548     private void dumpNotification(PrintWriter pw, String prefix, Notification notification,
549             boolean redact) {
550         if (notification == null) {
551             pw.println(prefix + "None");
552             return;
553 
554         }
555         pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
556         pw.println(prefix + "contentIntent=" + notification.contentIntent);
557         pw.println(prefix + "deleteIntent=" + notification.deleteIntent);
558         pw.println(prefix + "number=" + notification.number);
559         pw.println(prefix + "groupAlertBehavior=" + notification.getGroupAlertBehavior());
560         pw.println(prefix + "when=" + notification.when);
561 
562         pw.print(prefix + "tickerText=");
563         if (!TextUtils.isEmpty(notification.tickerText)) {
564             final String ticker = notification.tickerText.toString();
565             if (redact) {
566                 // if the string is long enough, we allow ourselves a few bytes for debugging
567                 pw.print(ticker.length() > 16 ? ticker.substring(0,8) : "");
568                 pw.println("...");
569             } else {
570                 pw.println(ticker);
571             }
572         } else {
573             pw.println("null");
574         }
575         pw.println(prefix + "contentView=" + formatRemoteViews(notification.contentView));
576         pw.println(prefix + "bigContentView=" + formatRemoteViews(notification.bigContentView));
577         pw.println(prefix + "headsUpContentView="
578                 + formatRemoteViews(notification.headsUpContentView));
579         pw.println(prefix + String.format("color=0x%08x", notification.color));
580         pw.println(prefix + "timeout="
581                 + TimeUtils.formatForLogging(notification.getTimeoutAfter()));
582         if (notification.actions != null && notification.actions.length > 0) {
583             pw.println(prefix + "actions={");
584             final int N = notification.actions.length;
585             for (int i = 0; i < N; i++) {
586                 final Notification.Action action = notification.actions[i];
587                 if (action != null) {
588                     pw.println(String.format("%s    [%d] \"%s\" -> %s",
589                             prefix,
590                             i,
591                             action.title,
592                             action.actionIntent == null ? "null" : action.actionIntent.toString()
593                     ));
594                 }
595             }
596             pw.println(prefix + "  }");
597         }
598         if (notification.extras != null && notification.extras.size() > 0) {
599             pw.println(prefix + "extras={");
600             for (String key : notification.extras.keySet()) {
601                 pw.print(prefix + "    " + key + "=");
602                 Object val = notification.extras.get(key);
603                 if (val == null) {
604                     pw.println("null");
605                 } else {
606                     pw.print(val.getClass().getSimpleName());
607                     if (redact && (val instanceof CharSequence) && shouldRedactStringExtra(key)) {
608                         pw.print(String.format(" [length=%d]", ((CharSequence) val).length()));
609                         // redact contents from bugreports
610                     } else if (val instanceof Bitmap) {
611                         pw.print(String.format(" (%dx%d)",
612                                 ((Bitmap) val).getWidth(),
613                                 ((Bitmap) val).getHeight()));
614                     } else if (val.getClass().isArray()) {
615                         final int N = Array.getLength(val);
616                         pw.print(" (" + N + ")");
617                         if (!redact) {
618                             for (int j = 0; j < N; j++) {
619                                 pw.println();
620                                 pw.print(String.format("%s      [%d] %s",
621                                         prefix, j, String.valueOf(Array.get(val, j))));
622                             }
623                         }
624                     } else {
625                         pw.print(" (" + String.valueOf(val) + ")");
626                     }
627                     pw.println();
628                 }
629             }
630             pw.println(prefix + "}");
631         }
632     }
633 
shouldRedactStringExtra(String key)634     private boolean shouldRedactStringExtra(String key) {
635         if (key == null) return true;
636         switch (key) {
637             // none of these keys contain user-related information; they do not need to be redacted
638             case Notification.EXTRA_SUBSTITUTE_APP_NAME:
639             case Notification.EXTRA_TEMPLATE:
640             case "android.support.v4.app.extra.COMPAT_TEMPLATE":
641                 return false;
642             default:
643                 return true;
644         }
645     }
646 
647     @Override
toString()648     public final String toString() {
649         return String.format(
650                 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" +
651                         ": %s)",
652                 System.identityHashCode(this),
653                 this.getSbn().getPackageName(), this.getSbn().getUser(), this.getSbn().getId(),
654                 this.getSbn().getTag(), this.mImportance, this.getSbn().getKey(),
655                 this.getSbn().getNotification());
656     }
657 
hasAdjustment(String key)658     public boolean hasAdjustment(String key) {
659         synchronized (mAdjustments) {
660             for (Adjustment adjustment : mAdjustments) {
661                 if (adjustment.getSignals().containsKey(key)) {
662                     return true;
663                 }
664             }
665         }
666         return false;
667     }
668 
addAdjustment(Adjustment adjustment)669     public void addAdjustment(Adjustment adjustment) {
670         synchronized (mAdjustments) {
671             mAdjustments.add(adjustment);
672         }
673     }
674 
applyAdjustments()675     public void applyAdjustments() {
676         long now = System.currentTimeMillis();
677         synchronized (mAdjustments) {
678             for (Adjustment adjustment: mAdjustments) {
679                 Bundle signals = adjustment.getSignals();
680                 if (signals.containsKey(Adjustment.KEY_PEOPLE)) {
681                     final ArrayList<String> people =
682                             adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
683                     setPeopleOverride(people);
684                     EventLogTags.writeNotificationAdjusted(
685                             getKey(), Adjustment.KEY_PEOPLE, people.toString());
686                 }
687                 if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
688                     final ArrayList<SnoozeCriterion> snoozeCriterionList =
689                             adjustment.getSignals().getParcelableArrayList(
690                                     Adjustment.KEY_SNOOZE_CRITERIA);
691                     setSnoozeCriteria(snoozeCriterionList);
692                     EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_SNOOZE_CRITERIA,
693                             snoozeCriterionList.toString());
694                 }
695                 if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
696                     final String groupOverrideKey =
697                             adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
698                     setOverrideGroupKey(groupOverrideKey);
699                     EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_GROUP_KEY,
700                             groupOverrideKey);
701                 }
702                 if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
703                     // Only allow user sentiment update from assistant if user hasn't already
704                     // expressed a preference for this channel
705                     if (!mIsAppImportanceLocked
706                             && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) {
707                         setUserSentiment(adjustment.getSignals().getInt(
708                                 Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
709                         EventLogTags.writeNotificationAdjusted(getKey(),
710                                 Adjustment.KEY_USER_SENTIMENT,
711                                 Integer.toString(getUserSentiment()));
712                     }
713                 }
714                 if (signals.containsKey(Adjustment.KEY_CONTEXTUAL_ACTIONS)) {
715                     setSystemGeneratedSmartActions(
716                             signals.getParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS));
717                     EventLogTags.writeNotificationAdjusted(getKey(),
718                             Adjustment.KEY_CONTEXTUAL_ACTIONS,
719                             getSystemGeneratedSmartActions().toString());
720                 }
721                 if (signals.containsKey(Adjustment.KEY_TEXT_REPLIES)) {
722                     setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES));
723                     EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_TEXT_REPLIES,
724                             getSmartReplies().toString());
725                 }
726                 if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
727                     int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
728                     importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
729                     importance = Math.min(IMPORTANCE_HIGH, importance);
730                     setAssistantImportance(importance);
731                     EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_IMPORTANCE,
732                             Integer.toString(importance));
733                 }
734                 if (signals.containsKey(Adjustment.KEY_RANKING_SCORE)) {
735                     mRankingScore = signals.getFloat(Adjustment.KEY_RANKING_SCORE);
736                     EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_RANKING_SCORE,
737                             Float.toString(mRankingScore));
738                 }
739                 if (signals.containsKey(Adjustment.KEY_NOT_CONVERSATION)) {
740                     mIsNotConversationOverride = signals.getBoolean(
741                             Adjustment.KEY_NOT_CONVERSATION);
742                     EventLogTags.writeNotificationAdjusted(getKey(),
743                             Adjustment.KEY_NOT_CONVERSATION,
744                             Boolean.toString(mIsNotConversationOverride));
745                 }
746                 if (signals.containsKey(Adjustment.KEY_IMPORTANCE_PROPOSAL)) {
747                     mProposedImportance = signals.getInt(Adjustment.KEY_IMPORTANCE_PROPOSAL);
748                     EventLogTags.writeNotificationAdjusted(getKey(),
749                             Adjustment.KEY_IMPORTANCE_PROPOSAL,
750                             Integer.toString(mProposedImportance));
751                 }
752                 if (!signals.isEmpty() && adjustment.getIssuer() != null) {
753                     mAdjustmentIssuer = adjustment.getIssuer();
754                 }
755             }
756             // We have now gotten all the information out of the adjustments and can forget them.
757             mAdjustments.clear();
758         }
759     }
760 
getAdjustmentIssuer()761     String getAdjustmentIssuer() {
762         return mAdjustmentIssuer;
763     }
764 
setIsAppImportanceLocked(boolean isAppImportanceLocked)765     public void setIsAppImportanceLocked(boolean isAppImportanceLocked) {
766         mIsAppImportanceLocked = isAppImportanceLocked;
767         calculateUserSentiment();
768     }
769 
setContactAffinity(float contactAffinity)770     public void setContactAffinity(float contactAffinity) {
771         mContactAffinity = contactAffinity;
772     }
773 
getContactAffinity()774     public float getContactAffinity() {
775         return mContactAffinity;
776     }
777 
setRecentlyIntrusive(boolean recentlyIntrusive)778     public void setRecentlyIntrusive(boolean recentlyIntrusive) {
779         mRecentlyIntrusive = recentlyIntrusive;
780         if (recentlyIntrusive) {
781             mLastIntrusive = System.currentTimeMillis();
782         }
783     }
784 
isRecentlyIntrusive()785     public boolean isRecentlyIntrusive() {
786         return mRecentlyIntrusive;
787     }
788 
getLastIntrusive()789     public long getLastIntrusive() {
790         return mLastIntrusive;
791     }
792 
setPackagePriority(int packagePriority)793     public void setPackagePriority(int packagePriority) {
794         mPackagePriority = packagePriority;
795     }
796 
getPackagePriority()797     public int getPackagePriority() {
798         return mPackagePriority;
799     }
800 
setPackageVisibilityOverride(int packageVisibility)801     public void setPackageVisibilityOverride(int packageVisibility) {
802         mPackageVisibility = packageVisibility;
803     }
804 
getPackageVisibilityOverride()805     public int getPackageVisibilityOverride() {
806         return mPackageVisibility;
807     }
808 
getUserExplanation()809     private String getUserExplanation() {
810         if (mUserExplanation == null) {
811             mUserExplanation = mContext.getResources().getString(
812                     com.android.internal.R.string.importance_from_user);
813         }
814         return mUserExplanation;
815     }
816 
817     /**
818      * Sets the importance value the system thinks the record should have.
819      * e.g. bumping up foreground service notifications or people to people notifications.
820      */
setSystemImportance(int importance)821     public void setSystemImportance(int importance) {
822         mSystemImportance = importance;
823         // System importance is only changed in enqueue, so it's ok for us to calculate the
824         // importance directly instead of waiting for signal extractor.
825         calculateImportance();
826     }
827 
828     /**
829      * Sets the importance value the
830      * {@link android.service.notification.NotificationAssistantService} thinks the record should
831      * have.
832      */
setAssistantImportance(int importance)833     public void setAssistantImportance(int importance) {
834         mAssistantImportance = importance;
835         // Unlike the system importance, the assistant importance can change on posted
836         // notifications, so don't calculateImportance() here, but wait for the signal extractors.
837     }
838 
839     /**
840      * Returns the importance set by the assistant, or IMPORTANCE_UNSPECIFIED if the assistant
841      * hasn't set it.
842      */
getAssistantImportance()843     public int getAssistantImportance() {
844         return mAssistantImportance;
845     }
846 
setImportanceFixed(boolean fixed)847     public void setImportanceFixed(boolean fixed) {
848         mImportanceFixed = fixed;
849     }
850 
isImportanceFixed()851     public boolean isImportanceFixed() {
852         return mImportanceFixed;
853     }
854 
855     /**
856      * Recalculates the importance of the record after fields affecting importance have changed,
857      * and records an explanation.
858      */
calculateImportance()859     protected void calculateImportance() {
860         mImportance = calculateInitialImportance();
861         mImportanceExplanationCode = mInitialImportanceExplanationCode;
862 
863         // Consider Notification Assistant and system overrides to importance. If both, system wins.
864         if (!getChannel().hasUserSetImportance()
865                 && mAssistantImportance != IMPORTANCE_UNSPECIFIED
866                 && !mImportanceFixed) {
867             mImportance = mAssistantImportance;
868             mImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_ASST;
869         }
870         if (mSystemImportance != IMPORTANCE_UNSPECIFIED) {
871             mImportance = mSystemImportance;
872             mImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_SYSTEM;
873         }
874     }
875 
getImportance()876     public int getImportance() {
877         return mImportance;
878     }
879 
getInitialImportance()880     int getInitialImportance() {
881         return stats.naturalImportance;
882     }
883 
getProposedImportance()884     public int getProposedImportance() {
885         return mProposedImportance;
886     }
887 
getRankingScore()888     public float getRankingScore() {
889         return mRankingScore;
890     }
891 
getImportanceExplanationCode()892     int getImportanceExplanationCode() {
893         return mImportanceExplanationCode;
894     }
895 
getInitialImportanceExplanationCode()896     int getInitialImportanceExplanationCode() {
897         return mInitialImportanceExplanationCode;
898     }
899 
getImportanceExplanation()900     public CharSequence getImportanceExplanation() {
901         switch (mImportanceExplanationCode) {
902             case MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN:
903                 return null;
904             case MetricsEvent.IMPORTANCE_EXPLANATION_APP:
905             case MetricsEvent.IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS:
906                 return "app";
907             case MetricsEvent.IMPORTANCE_EXPLANATION_USER:
908                 return "user";
909             case MetricsEvent.IMPORTANCE_EXPLANATION_ASST:
910                 return "asst";
911             case MetricsEvent.IMPORTANCE_EXPLANATION_SYSTEM:
912                 return "system";
913         }
914         return null;
915     }
916 
setIntercepted(boolean intercept)917     public boolean setIntercepted(boolean intercept) {
918         mIntercept = intercept;
919         mInterceptSet = true;
920         return mIntercept;
921     }
922 
923     /**
924      * Set to affect global sort key.
925      *
926      * @param criticality used in a string based sort thus 0 is the most critical
927      */
setCriticality(int criticality)928     public void setCriticality(int criticality) {
929         mCriticality = criticality;
930     }
931 
getCriticality()932     public int getCriticality() {
933         return mCriticality;
934     }
935 
isIntercepted()936     public boolean isIntercepted() {
937         return mIntercept;
938     }
939 
hasInterceptBeenSet()940     public boolean hasInterceptBeenSet() {
941         return mInterceptSet;
942     }
943 
isNewEnoughForAlerting(long now)944     public boolean isNewEnoughForAlerting(long now) {
945         return getFreshnessMs(now) <= MAX_SOUND_DELAY_MS;
946     }
947 
setHidden(boolean hidden)948     public void setHidden(boolean hidden) {
949         mHidden = hidden;
950     }
951 
isHidden()952     public boolean isHidden() {
953         return mHidden;
954     }
955 
isForegroundService()956     public boolean isForegroundService() {
957         return 0 != (getFlags() & Notification.FLAG_FOREGROUND_SERVICE);
958     }
959 
960     /**
961      * Override of all alerting information on the channel and notification. Used when notifications
962      * are reposted in response to direct user action and thus don't need to alert.
963      */
setPostSilently(boolean postSilently)964     public void setPostSilently(boolean postSilently) {
965         mPostSilently = postSilently;
966     }
967 
shouldPostSilently()968     public boolean shouldPostSilently() {
969         return mPostSilently;
970     }
971 
setSuppressedVisualEffects(int effects)972     public void setSuppressedVisualEffects(int effects) {
973         mSuppressedVisualEffects = effects;
974     }
975 
getSuppressedVisualEffects()976     public int getSuppressedVisualEffects() {
977         return mSuppressedVisualEffects;
978     }
979 
isCategory(String category)980     public boolean isCategory(String category) {
981         return Objects.equals(getNotification().category, category);
982     }
983 
isAudioAttributesUsage(int usage)984     public boolean isAudioAttributesUsage(int usage) {
985         return mAttributes != null && mAttributes.getUsage() == usage;
986     }
987 
988     /**
989      * Returns the timestamp to use for time-based sorting in the ranker.
990      */
getRankingTimeMs()991     public long getRankingTimeMs() {
992         return mRankingTimeMs;
993     }
994 
995     /**
996      * @param now this current time in milliseconds.
997      * @returns the number of milliseconds since the most recent update, or the post time if none.
998      */
getFreshnessMs(long now)999     public int getFreshnessMs(long now) {
1000         return (int) (now - mUpdateTimeMs);
1001     }
1002 
1003     /**
1004      * @param now this current time in milliseconds.
1005      * @returns the number of milliseconds since the the first post, ignoring updates.
1006      */
getLifespanMs(long now)1007     public int getLifespanMs(long now) {
1008         return (int) (now - mCreationTimeMs);
1009     }
1010 
1011     /**
1012      * @param now this current time in milliseconds.
1013      * @returns the number of milliseconds since the most recent visibility event, or 0 if never.
1014      */
getExposureMs(long now)1015     public int getExposureMs(long now) {
1016         return mVisibleSinceMs == 0 ? 0 : (int) (now - mVisibleSinceMs);
1017     }
1018 
getInterruptionMs(long now)1019     public int getInterruptionMs(long now) {
1020         return (int) (now - mInterruptionTimeMs);
1021     }
1022 
getUpdateTimeMs()1023     public long getUpdateTimeMs() {
1024         return mUpdateTimeMs;
1025     }
1026 
1027     /**
1028      * Set the visibility of the notification.
1029      */
setVisibility(boolean visible, int rank, int count, NotificationRecordLogger notificationRecordLogger)1030     public void setVisibility(boolean visible, int rank, int count,
1031             NotificationRecordLogger notificationRecordLogger) {
1032         final long now = System.currentTimeMillis();
1033         mVisibleSinceMs = visible ? now : mVisibleSinceMs;
1034         stats.onVisibilityChanged(visible);
1035         MetricsLogger.action(getLogMaker(now)
1036                 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
1037                 .setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
1038                 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank)
1039                 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, count));
1040         if (visible) {
1041             setSeen();
1042             MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now));
1043         }
1044         EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0,
1045                 getLifespanMs(now),
1046                 getFreshnessMs(now),
1047                 0, // exposure time
1048                 rank);
1049         notificationRecordLogger.logNotificationVisibility(this, visible);
1050     }
1051 
1052     /**
1053      * @param previousRankingTimeMs for updated notifications, {@link #getRankingTimeMs()}
1054      *     of the previous notification record, 0 otherwise
1055      */
calculateRankingTimeMs(long previousRankingTimeMs)1056     private long calculateRankingTimeMs(long previousRankingTimeMs) {
1057         Notification n = getNotification();
1058         // Take developer provided 'when', unless it's in the future.
1059         if (n.when != 0 && n.when <= getSbn().getPostTime()) {
1060             return n.when;
1061         }
1062         // If we've ranked a previous instance with a timestamp, inherit it. This case is
1063         // important in order to have ranking stability for updating notifications.
1064         if (previousRankingTimeMs > 0) {
1065             return previousRankingTimeMs;
1066         }
1067         return getSbn().getPostTime();
1068     }
1069 
setGlobalSortKey(String globalSortKey)1070     public void setGlobalSortKey(String globalSortKey) {
1071         mGlobalSortKey = globalSortKey;
1072     }
1073 
getGlobalSortKey()1074     public String getGlobalSortKey() {
1075         return mGlobalSortKey;
1076     }
1077 
1078     /** Check if any of the listeners have marked this notification as seen by the user. */
isSeen()1079     public boolean isSeen() {
1080         return mStats.hasSeen();
1081     }
1082 
1083     /** Mark the notification as seen by the user. */
setSeen()1084     public void setSeen() {
1085         mStats.setSeen();
1086         if (mTextChanged) {
1087             setInterruptive(true);
1088         }
1089     }
1090 
setAuthoritativeRank(int authoritativeRank)1091     public void setAuthoritativeRank(int authoritativeRank) {
1092         mAuthoritativeRank = authoritativeRank;
1093     }
1094 
getAuthoritativeRank()1095     public int getAuthoritativeRank() {
1096         return mAuthoritativeRank;
1097     }
1098 
getGroupKey()1099     public String getGroupKey() {
1100         return getSbn().getGroupKey();
1101     }
1102 
setOverrideGroupKey(String overrideGroupKey)1103     public void setOverrideGroupKey(String overrideGroupKey) {
1104         getSbn().setOverrideGroupKey(overrideGroupKey);
1105     }
1106 
getChannel()1107     public NotificationChannel getChannel() {
1108         return mChannel;
1109     }
1110 
1111     /**
1112      * @see PermissionHelper#isPermissionUserSet(String, int)
1113      */
getIsAppImportanceLocked()1114     public boolean getIsAppImportanceLocked() {
1115         return mIsAppImportanceLocked;
1116     }
1117 
updateNotificationChannel(NotificationChannel channel)1118     protected void updateNotificationChannel(NotificationChannel channel) {
1119         if (channel != null) {
1120             mChannel = channel;
1121             calculateImportance();
1122             calculateUserSentiment();
1123         }
1124     }
1125 
setShowBadge(boolean showBadge)1126     public void setShowBadge(boolean showBadge) {
1127         mShowBadge = showBadge;
1128     }
1129 
canBubble()1130     public boolean canBubble() {
1131         return mAllowBubble;
1132     }
1133 
setAllowBubble(boolean allow)1134     public void setAllowBubble(boolean allow) {
1135         mAllowBubble = allow;
1136     }
1137 
canShowBadge()1138     public boolean canShowBadge() {
1139         return mShowBadge;
1140     }
1141 
getLight()1142     public Light getLight() {
1143         return mLight;
1144     }
1145 
getSound()1146     public Uri getSound() {
1147         return mSound;
1148     }
1149 
getVibration()1150     public VibrationEffect getVibration() {
1151         return mVibration;
1152     }
1153 
getAudioAttributes()1154     public AudioAttributes getAudioAttributes() {
1155         return mAttributes;
1156     }
1157 
getPeopleOverride()1158     public ArrayList<String> getPeopleOverride() {
1159         return mPeopleOverride;
1160     }
1161 
setInterruptive(boolean interruptive)1162     public void setInterruptive(boolean interruptive) {
1163         mIsInterruptive = interruptive;
1164         final long now = System.currentTimeMillis();
1165         mInterruptionTimeMs = interruptive ? now : mInterruptionTimeMs;
1166 
1167         if (interruptive) {
1168             MetricsLogger.action(getLogMaker()
1169                     .setCategory(MetricsEvent.NOTIFICATION_INTERRUPTION)
1170                     .setType(MetricsEvent.TYPE_OPEN)
1171                     .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS,
1172                             getInterruptionMs(now)));
1173             MetricsLogger.histogram(mContext, "note_interruptive", getInterruptionMs(now));
1174         }
1175     }
1176 
setAudiblyAlerted(boolean audiblyAlerted)1177     public void setAudiblyAlerted(boolean audiblyAlerted) {
1178         mLastAudiblyAlertedMs = audiblyAlerted ? System.currentTimeMillis() : -1;
1179     }
1180 
setTextChanged(boolean textChanged)1181     public void setTextChanged(boolean textChanged) {
1182         mTextChanged = textChanged;
1183     }
1184 
setRecordedInterruption(boolean recorded)1185     public void setRecordedInterruption(boolean recorded) {
1186         mRecordedInterruption = recorded;
1187     }
1188 
hasRecordedInterruption()1189     public boolean hasRecordedInterruption() {
1190         return mRecordedInterruption;
1191     }
1192 
isInterruptive()1193     public boolean isInterruptive() {
1194         return mIsInterruptive;
1195     }
1196 
isTextChanged()1197     public boolean isTextChanged() {
1198         return mTextChanged;
1199     }
1200 
1201     /** Returns the time the notification audibly alerted the user. */
getLastAudiblyAlertedMs()1202     public long getLastAudiblyAlertedMs() {
1203         return mLastAudiblyAlertedMs;
1204     }
1205 
setPeopleOverride(ArrayList<String> people)1206     protected void setPeopleOverride(ArrayList<String> people) {
1207         mPeopleOverride = people;
1208     }
1209 
getSnoozeCriteria()1210     public ArrayList<SnoozeCriterion> getSnoozeCriteria() {
1211         return mSnoozeCriteria;
1212     }
1213 
setSnoozeCriteria(ArrayList<SnoozeCriterion> snoozeCriteria)1214     protected void setSnoozeCriteria(ArrayList<SnoozeCriterion> snoozeCriteria) {
1215         mSnoozeCriteria = snoozeCriteria;
1216     }
1217 
calculateUserSentiment()1218     private void calculateUserSentiment() {
1219         if ((getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0
1220                 || mIsAppImportanceLocked) {
1221             mUserSentiment = USER_SENTIMENT_POSITIVE;
1222         }
1223     }
1224 
setUserSentiment(int userSentiment)1225     private void setUserSentiment(int userSentiment) {
1226         mUserSentiment = userSentiment;
1227     }
1228 
getUserSentiment()1229     public int getUserSentiment() {
1230         return mUserSentiment;
1231     }
1232 
getStats()1233     public NotificationStats getStats() {
1234         return mStats;
1235     }
1236 
recordExpanded()1237     public void recordExpanded() {
1238         mStats.setExpanded();
1239     }
1240 
recordDirectReplied()1241     public void recordDirectReplied() {
1242         mStats.setDirectReplied();
1243     }
1244 
recordDismissalSurface(@otificationStats.DismissalSurface int surface)1245     public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) {
1246         mStats.setDismissalSurface(surface);
1247     }
1248 
recordDismissalSentiment(@otificationStats.DismissalSentiment int sentiment)1249     public void recordDismissalSentiment(@NotificationStats.DismissalSentiment int sentiment) {
1250         mStats.setDismissalSentiment(sentiment);
1251     }
1252 
recordSnoozed()1253     public void recordSnoozed() {
1254         mStats.setSnoozed();
1255     }
1256 
recordViewedSettings()1257     public void recordViewedSettings() {
1258         mStats.setViewedSettings();
1259     }
1260 
setNumSmartRepliesAdded(int noReplies)1261     public void setNumSmartRepliesAdded(int noReplies) {
1262         mNumberOfSmartRepliesAdded = noReplies;
1263     }
1264 
getNumSmartRepliesAdded()1265     public int getNumSmartRepliesAdded() {
1266         return mNumberOfSmartRepliesAdded;
1267     }
1268 
setNumSmartActionsAdded(int noActions)1269     public void setNumSmartActionsAdded(int noActions) {
1270         mNumberOfSmartActionsAdded = noActions;
1271     }
1272 
getNumSmartActionsAdded()1273     public int getNumSmartActionsAdded() {
1274         return mNumberOfSmartActionsAdded;
1275     }
1276 
setSuggestionsGeneratedByAssistant(boolean generatedByAssistant)1277     public void setSuggestionsGeneratedByAssistant(boolean generatedByAssistant) {
1278         mSuggestionsGeneratedByAssistant = generatedByAssistant;
1279     }
1280 
getSuggestionsGeneratedByAssistant()1281     public boolean getSuggestionsGeneratedByAssistant() {
1282         return mSuggestionsGeneratedByAssistant;
1283     }
1284 
getEditChoicesBeforeSending()1285     public boolean getEditChoicesBeforeSending() {
1286         return mEditChoicesBeforeSending;
1287     }
1288 
setEditChoicesBeforeSending(boolean editChoicesBeforeSending)1289     public void setEditChoicesBeforeSending(boolean editChoicesBeforeSending) {
1290         mEditChoicesBeforeSending = editChoicesBeforeSending;
1291     }
1292 
hasSeenSmartReplies()1293     public boolean hasSeenSmartReplies() {
1294         return mHasSeenSmartReplies;
1295     }
1296 
setSeenSmartReplies(boolean hasSeenSmartReplies)1297     public void setSeenSmartReplies(boolean hasSeenSmartReplies) {
1298         mHasSeenSmartReplies = hasSeenSmartReplies;
1299     }
1300 
1301     /**
1302      * Returns whether this notification has been visible and expanded at the same time.
1303      */
hasBeenVisiblyExpanded()1304     public boolean hasBeenVisiblyExpanded() {
1305         return stats.hasBeenVisiblyExpanded();
1306     }
1307 
1308     /**
1309      * When the bubble state on a notif changes due to user action (e.g. dismiss a bubble) then
1310      * this value is set until an update or bubble change event due to user action (e.g. create
1311      * bubble from sysui)
1312      **/
isFlagBubbleRemoved()1313     public boolean isFlagBubbleRemoved() {
1314         return mFlagBubbleRemoved;
1315     }
1316 
setFlagBubbleRemoved(boolean flagBubbleRemoved)1317     public void setFlagBubbleRemoved(boolean flagBubbleRemoved) {
1318         mFlagBubbleRemoved = flagBubbleRemoved;
1319     }
1320 
setSystemGeneratedSmartActions( ArrayList<Notification.Action> systemGeneratedSmartActions)1321     public void setSystemGeneratedSmartActions(
1322             ArrayList<Notification.Action> systemGeneratedSmartActions) {
1323         mSystemGeneratedSmartActions = systemGeneratedSmartActions;
1324     }
1325 
getSystemGeneratedSmartActions()1326     public ArrayList<Notification.Action> getSystemGeneratedSmartActions() {
1327         return mSystemGeneratedSmartActions;
1328     }
1329 
setSmartReplies(ArrayList<CharSequence> smartReplies)1330     public void setSmartReplies(ArrayList<CharSequence> smartReplies) {
1331         mSmartReplies = smartReplies;
1332     }
1333 
getSmartReplies()1334     public ArrayList<CharSequence> getSmartReplies() {
1335         return mSmartReplies;
1336     }
1337 
1338     /**
1339      * Returns whether this notification was posted by a secondary app
1340      */
isProxied()1341     public boolean isProxied() {
1342         return !Objects.equals(getSbn().getPackageName(), getSbn().getOpPkg());
1343     }
1344 
getNotificationType()1345     public int getNotificationType() {
1346         if (isConversation()) {
1347             return NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
1348         } else if (getImportance() >= IMPORTANCE_DEFAULT) {
1349             return NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
1350         } else {
1351             return NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
1352         }
1353     }
1354 
1355     /**
1356      * @return all {@link Uri} that should have permission granted to whoever
1357      *         will be rendering it. This list has already been vetted to only
1358      *         include {@link Uri} that the enqueuing app can grant.
1359      */
getGrantableUris()1360     public @Nullable ArraySet<Uri> getGrantableUris() {
1361         return mGrantableUris;
1362     }
1363 
1364     /**
1365      * Collect all {@link Uri} that should have permission granted to whoever
1366      * will be rendering it.
1367      */
calculateGrantableUris()1368     protected void calculateGrantableUris() {
1369         final Notification notification = getNotification();
1370         notification.visitUris((uri) -> {
1371             visitGrantableUri(uri, false, false);
1372         });
1373 
1374         if (notification.getChannelId() != null) {
1375             NotificationChannel channel = getChannel();
1376             if (channel != null) {
1377                 visitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
1378                         & NotificationChannel.USER_LOCKED_SOUND) != 0, true);
1379             }
1380         }
1381     }
1382 
1383     /**
1384      * Note the presence of a {@link Uri} that should have permission granted to
1385      * whoever will be rendering it.
1386      * <p>
1387      * If the enqueuing app has the ability to grant access, it will be added to
1388      * {@link #mGrantableUris}. Otherwise, this will either log or throw
1389      * {@link SecurityException} depending on target SDK of enqueuing app.
1390      */
visitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound)1391     private void visitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound) {
1392         if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
1393 
1394         // We can't grant Uri permissions from system
1395         final int sourceUid = getSbn().getUid();
1396         if (sourceUid == android.os.Process.SYSTEM_UID) return;
1397 
1398         final long ident = Binder.clearCallingIdentity();
1399         try {
1400             // This will throw SecurityException if caller can't grant
1401             mUgmInternal.checkGrantUriPermission(sourceUid, null,
1402                     ContentProvider.getUriWithoutUserId(uri),
1403                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
1404                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
1405 
1406             if (mGrantableUris == null) {
1407                 mGrantableUris = new ArraySet<>();
1408             }
1409             mGrantableUris.add(uri);
1410         } catch (SecurityException e) {
1411             if (!userOverriddenUri) {
1412                 if (isSound) {
1413                     mSound = Settings.System.DEFAULT_NOTIFICATION_URI;
1414                     Log.w(TAG, "Replacing " + uri + " from " + sourceUid + ": " + e.getMessage());
1415                 } else {
1416                     if (mTargetSdkVersion >= Build.VERSION_CODES.P) {
1417                         throw e;
1418                     } else {
1419                         Log.w(TAG,
1420                                 "Ignoring " + uri + " from " + sourceUid + ": " + e.getMessage());
1421                     }
1422                 }
1423             }
1424         } finally {
1425             Binder.restoreCallingIdentity(ident);
1426         }
1427     }
1428 
getLogMaker(long now)1429     public LogMaker getLogMaker(long now) {
1430         LogMaker lm = getSbn().getLogMaker()
1431                 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, mImportance)
1432                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
1433                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
1434                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now))
1435                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS,
1436                         getInterruptionMs(now));
1437         // Record results of the calculateImportance() calculation if available.
1438         if (mImportanceExplanationCode != MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN) {
1439             lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_EXPLANATION,
1440                     mImportanceExplanationCode);
1441             // To avoid redundancy, we log the initial importance information only if it was
1442             // overridden.
1443             if (((mImportanceExplanationCode == MetricsEvent.IMPORTANCE_EXPLANATION_ASST)
1444                     || (mImportanceExplanationCode == MetricsEvent.IMPORTANCE_EXPLANATION_SYSTEM))
1445                     && (stats.naturalImportance != IMPORTANCE_UNSPECIFIED)) {
1446                 // stats.naturalImportance is due to one of the 3 sources of initial importance.
1447                 lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_INITIAL_EXPLANATION,
1448                         mInitialImportanceExplanationCode);
1449                 lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_INITIAL,
1450                         stats.naturalImportance);
1451             }
1452         }
1453         // Log Assistant override if present, whether or not importance calculation is complete.
1454         if (mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
1455             lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST,
1456                         mAssistantImportance);
1457         }
1458         // Log the issuer of any adjustments that may have affected this notification. We only log
1459         // the hash here as NotificationItem events are frequent, and the number of NAS
1460         // implementations (and hence the chance of collisions) is low.
1461         if (mAdjustmentIssuer != null) {
1462             lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_ASSISTANT_SERVICE_HASH,
1463                     mAdjustmentIssuer.hashCode());
1464         }
1465         return lm;
1466     }
1467 
getLogMaker()1468     public LogMaker getLogMaker() {
1469         return getLogMaker(System.currentTimeMillis());
1470     }
1471 
getItemLogMaker()1472     public LogMaker getItemLogMaker() {
1473         return getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM);
1474     }
1475 
hasUndecoratedRemoteView()1476     public boolean hasUndecoratedRemoteView() {
1477         Notification notification = getNotification();
1478         boolean hasDecoratedStyle =
1479                 notification.isStyle(Notification.DecoratedCustomViewStyle.class)
1480                 || notification.isStyle(Notification.DecoratedMediaCustomViewStyle.class);
1481         boolean hasCustomRemoteView = notification.contentView != null
1482                 || notification.bigContentView != null
1483                 || notification.headsUpContentView != null;
1484         return hasCustomRemoteView && !hasDecoratedStyle;
1485     }
1486 
setShortcutInfo(ShortcutInfo shortcutInfo)1487     public void setShortcutInfo(ShortcutInfo shortcutInfo) {
1488         mShortcutInfo = shortcutInfo;
1489     }
1490 
getShortcutInfo()1491     public ShortcutInfo getShortcutInfo() {
1492         return mShortcutInfo;
1493     }
1494 
setHasSentValidMsg(boolean hasSentValidMsg)1495     public void setHasSentValidMsg(boolean hasSentValidMsg) {
1496         mHasSentValidMsg = hasSentValidMsg;
1497     }
1498 
userDemotedAppFromConvoSpace(boolean userDemoted)1499     public void userDemotedAppFromConvoSpace(boolean userDemoted) {
1500         mAppDemotedFromConvo = userDemoted;
1501     }
1502 
setPkgAllowedAsConvo(boolean allowedAsConvo)1503     public void setPkgAllowedAsConvo(boolean allowedAsConvo) {
1504         mPkgAllowedAsConvo = allowedAsConvo;
1505     }
1506 
1507     /**
1508      * Whether this notification is a conversation notification.
1509      */
isConversation()1510     public boolean isConversation() {
1511         Notification notification = getNotification();
1512         // user kicked it out of convo space
1513         if (mChannel.isDemoted() || mAppDemotedFromConvo) {
1514             return false;
1515         }
1516         // NAS kicked it out of notification space
1517         if (mIsNotConversationOverride) {
1518             return false;
1519         }
1520         if (!notification.isStyle(Notification.MessagingStyle.class)) {
1521             // some non-msgStyle notifs can temporarily appear in the conversation space if category
1522             // is right
1523             if (mPkgAllowedAsConvo && mTargetSdkVersion < Build.VERSION_CODES.R
1524                 && Notification.CATEGORY_MESSAGE.equals(getNotification().category)) {
1525                 return true;
1526             }
1527             return false;
1528         }
1529 
1530         if (mTargetSdkVersion >= Build.VERSION_CODES.R
1531                 && notification.isStyle(Notification.MessagingStyle.class)
1532                 && (mShortcutInfo == null || isOnlyBots(mShortcutInfo.getPersons()))) {
1533             return false;
1534         }
1535         if (mHasSentValidMsg && mShortcutInfo == null) {
1536             return false;
1537         }
1538         return true;
1539     }
1540 
1541     /**
1542      * Determines if the {@link ShortcutInfo#getPersons()} array includes only bots, for the purpose
1543      * of excluding that shortcut from the "conversations" section of the notification shade.  If
1544      * the shortcut has no people, this returns false to allow the conversation into the shade, and
1545      * if there is any non-bot person we allow it as well.  Otherwise, this is only bots and will
1546      * not count as a conversation.
1547      */
isOnlyBots(Person[] persons)1548     private boolean isOnlyBots(Person[] persons) {
1549         // Return false if there are no persons at all
1550         if (persons == null || persons.length == 0) {
1551             return false;
1552         }
1553         // Return false if there are any non-bot persons
1554         for (Person person : persons) {
1555             if (!person.isBot()) {
1556                 return false;
1557             }
1558         }
1559         // Return true otherwise
1560         return true;
1561     }
1562 
getSbn()1563     StatusBarNotification getSbn() {
1564         return sbn;
1565     }
1566 
1567     /**
1568      * Returns whether this record's ranking score is approximately equal to otherScore
1569      * (the difference must be within 0.0001).
1570      */
rankingScoreMatches(float otherScore)1571     public boolean rankingScoreMatches(float otherScore) {
1572         return Math.abs(mRankingScore - otherScore) < 0.0001;
1573     }
1574 
setPendingLogUpdate(boolean pendingLogUpdate)1575     protected void setPendingLogUpdate(boolean pendingLogUpdate) {
1576         mPendingLogUpdate = pendingLogUpdate;
1577     }
1578 
1579     // If a caller of this function subsequently logs the update, they should also call
1580     // setPendingLogUpdate to false to make sure other callers don't also do so.
hasPendingLogUpdate()1581     protected boolean hasPendingLogUpdate() {
1582         return mPendingLogUpdate;
1583     }
1584 
1585     /**
1586      * Merge the given set of phone numbers into the list of phone numbers that
1587      * are cached on this notification record.
1588      */
mergePhoneNumbers(ArraySet<String> phoneNumbers)1589     public void mergePhoneNumbers(ArraySet<String> phoneNumbers) {
1590         // if the given phone numbers are null or empty then don't do anything
1591         if (phoneNumbers == null || phoneNumbers.size() == 0) {
1592             return;
1593         }
1594         // initialize if not already
1595         if (mPhoneNumbers == null) {
1596             mPhoneNumbers = new ArraySet<>();
1597         }
1598         mPhoneNumbers.addAll(phoneNumbers);
1599     }
1600 
getPhoneNumbers()1601     public ArraySet<String> getPhoneNumbers() {
1602         return mPhoneNumbers;
1603     }
1604 
1605     @VisibleForTesting
1606     static final class Light {
1607         public final int color;
1608         public final int onMs;
1609         public final int offMs;
1610 
Light(int color, int onMs, int offMs)1611         public Light(int color, int onMs, int offMs) {
1612             this.color = color;
1613             this.onMs = onMs;
1614             this.offMs = offMs;
1615         }
1616 
1617         @Override
equals(Object o)1618         public boolean equals(Object o) {
1619             if (this == o) return true;
1620             if (o == null || getClass() != o.getClass()) return false;
1621 
1622             Light light = (Light) o;
1623 
1624             if (color != light.color) return false;
1625             if (onMs != light.onMs) return false;
1626             return offMs == light.offMs;
1627 
1628         }
1629 
1630         @Override
hashCode()1631         public int hashCode() {
1632             int result = color;
1633             result = 31 * result + onMs;
1634             result = 31 * result + offMs;
1635             return result;
1636         }
1637 
1638         @Override
toString()1639         public String toString() {
1640             return "Light{" +
1641                     "color=" + color +
1642                     ", onMs=" + onMs +
1643                     ", offMs=" + offMs +
1644                     '}';
1645         }
1646     }
1647 }
1648