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.content.ContentProvider;
33 import android.content.ContentResolver;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.pm.PackageManager;
37 import android.content.pm.PackageManagerInternal;
38 import android.graphics.Bitmap;
39 import android.media.AudioAttributes;
40 import android.media.AudioSystem;
41 import android.metrics.LogMaker;
42 import android.net.Uri;
43 import android.os.Binder;
44 import android.os.Build;
45 import android.os.Bundle;
46 import android.os.IBinder;
47 import android.os.UserHandle;
48 import android.provider.Settings;
49 import android.service.notification.Adjustment;
50 import android.service.notification.NotificationListenerService;
51 import android.service.notification.NotificationRecordProto;
52 import android.service.notification.NotificationStats;
53 import android.service.notification.SnoozeCriterion;
54 import android.service.notification.StatusBarNotification;
55 import android.text.TextUtils;
56 import android.util.ArraySet;
57 import android.util.Log;
58 import android.util.TimeUtils;
59 import android.util.proto.ProtoOutputStream;
60 import android.widget.RemoteViews;
61 
62 import com.android.internal.annotations.VisibleForTesting;
63 import com.android.internal.logging.MetricsLogger;
64 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
65 import com.android.server.EventLogTags;
66 import com.android.server.LocalServices;
67 import com.android.server.uri.UriGrantsManagerInternal;
68 
69 import java.io.PrintWriter;
70 import java.lang.reflect.Array;
71 import java.util.ArrayList;
72 import java.util.Arrays;
73 import java.util.List;
74 import java.util.Objects;
75 
76 /**
77  * Holds data about notifications that should not be shared with the
78  * {@link android.service.notification.NotificationListenerService}s.
79  *
80  * <p>These objects should not be mutated unless the code is synchronized
81  * on {@link NotificationManagerService#mNotificationLock}, and any
82  * modification should be followed by a sorting of that list.</p>
83  *
84  * <p>Is sortable by {@link NotificationComparator}.</p>
85  *
86  * {@hide}
87  */
88 public final class NotificationRecord {
89     static final String TAG = "NotificationRecord";
90     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
91     // the period after which a notification is updated where it can make sound
92     private static final int MAX_SOUND_DELAY_MS = 2000;
93     final StatusBarNotification sbn;
94     IActivityManager mAm;
95     UriGrantsManagerInternal mUgmInternal;
96     final int mTargetSdkVersion;
97     final int mOriginalFlags;
98     private final Context mContext;
99 
100     NotificationUsageStats.SingleNotificationStats stats;
101     boolean isCanceled;
102     IBinder permissionOwner;
103 
104     // These members are used by NotificationSignalExtractors
105     // to communicate with the ranking module.
106     private float mContactAffinity;
107     private boolean mRecentlyIntrusive;
108     private long mLastIntrusive;
109 
110     // is this notification currently being intercepted by Zen Mode?
111     private boolean mIntercept;
112 
113     // is this notification hidden since the app pkg is suspended?
114     private boolean mHidden;
115 
116     // The timestamp used for ranking.
117     private long mRankingTimeMs;
118 
119     // The first post time, stable across updates.
120     private long mCreationTimeMs;
121 
122     // The most recent visibility event.
123     private long mVisibleSinceMs;
124 
125     // The most recent update time, or the creation time if no updates.
126     @VisibleForTesting
127     final long mUpdateTimeMs;
128 
129     // The most recent interruption time, or the creation time if no updates. Differs from the
130     // above value because updates are filtered based on whether they actually interrupted the
131     // user
132     private long mInterruptionTimeMs;
133 
134     // The most recent time the notification made noise or buzzed the device, or -1 if it did not.
135     private long mLastAudiblyAlertedMs;
136 
137     // Is this record an update of an old record?
138     public boolean isUpdate;
139     private int mPackagePriority;
140 
141     private int mAuthoritativeRank;
142     private String mGlobalSortKey;
143     private int mPackageVisibility;
144     private int mSystemImportance = IMPORTANCE_UNSPECIFIED;
145     private int mAssistantImportance = IMPORTANCE_UNSPECIFIED;
146     private int mImportance = IMPORTANCE_UNSPECIFIED;
147     // Field used in global sort key to bypass normal notifications
148     private int mCriticality = CriticalNotificationExtractor.NORMAL;
149     // A MetricsEvent.NotificationImportanceExplanation, tracking source of mImportance.
150     private int mImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN;
151     // A MetricsEvent.NotificationImportanceExplanation for initial importance.
152     private int mInitialImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN;
153 
154     private int mSuppressedVisualEffects = 0;
155     private String mUserExplanation;
156     private boolean mPreChannelsNotification = true;
157     private Uri mSound;
158     private long[] mVibration;
159     private AudioAttributes mAttributes;
160     private NotificationChannel mChannel;
161     private ArrayList<String> mPeopleOverride;
162     private ArrayList<SnoozeCriterion> mSnoozeCriteria;
163     private boolean mShowBadge;
164     private boolean mAllowBubble;
165     private Light mLight;
166     /**
167      * This list contains system generated smart actions from NAS, app-generated smart actions are
168      * stored in Notification.actions with isContextual() set to true.
169      */
170     private ArrayList<Notification.Action> mSystemGeneratedSmartActions;
171     private ArrayList<CharSequence> mSmartReplies;
172 
173     private final List<Adjustment> mAdjustments;
174     private String mAdjustmentIssuer;
175     private final NotificationStats mStats;
176     private int mUserSentiment;
177     private boolean mIsInterruptive;
178     private boolean mTextChanged;
179     private boolean mRecordedInterruption;
180     private int mNumberOfSmartRepliesAdded;
181     private int mNumberOfSmartActionsAdded;
182     private boolean mSuggestionsGeneratedByAssistant;
183     private boolean mEditChoicesBeforeSending;
184     private boolean mHasSeenSmartReplies;
185     /**
186      * Whether this notification (and its channels) should be considered user locked. Used in
187      * conjunction with user sentiment calculation.
188      */
189     private boolean mIsAppImportanceLocked;
190     private ArraySet<Uri> mGrantableUris;
191 
NotificationRecord(Context context, StatusBarNotification sbn, NotificationChannel channel)192     public NotificationRecord(Context context, StatusBarNotification sbn,
193             NotificationChannel channel) {
194         this.sbn = sbn;
195         mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class)
196                 .getPackageTargetSdkVersion(sbn.getPackageName());
197         mAm = ActivityManager.getService();
198         mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
199         mOriginalFlags = sbn.getNotification().flags;
200         mRankingTimeMs = calculateRankingTimeMs(0L);
201         mCreationTimeMs = sbn.getPostTime();
202         mUpdateTimeMs = mCreationTimeMs;
203         mInterruptionTimeMs = mCreationTimeMs;
204         mContext = context;
205         stats = new NotificationUsageStats.SingleNotificationStats();
206         mChannel = channel;
207         mPreChannelsNotification = isPreChannelsNotification();
208         mSound = calculateSound();
209         mVibration = calculateVibration();
210         mAttributes = calculateAttributes();
211         mImportance = calculateInitialImportance();
212         mLight = calculateLights();
213         mAdjustments = new ArrayList<>();
214         mStats = new NotificationStats();
215         calculateUserSentiment();
216         calculateGrantableUris();
217     }
218 
isPreChannelsNotification()219     private boolean isPreChannelsNotification() {
220         if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
221             if (mTargetSdkVersion < Build.VERSION_CODES.O) {
222                 return true;
223             }
224         }
225         return false;
226     }
227 
calculateSound()228     private Uri calculateSound() {
229         final Notification n = sbn.getNotification();
230 
231         // No notification sounds on tv
232         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
233             return null;
234         }
235 
236         Uri sound = mChannel.getSound();
237         if (mPreChannelsNotification && (getChannel().getUserLockedFields()
238                 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
239 
240             final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0;
241             if (useDefaultSound) {
242                 sound = Settings.System.DEFAULT_NOTIFICATION_URI;
243             } else {
244                 sound = n.sound;
245             }
246         }
247         return sound;
248     }
249 
calculateLights()250     private Light calculateLights() {
251         int defaultLightColor = mContext.getResources().getColor(
252                 com.android.internal.R.color.config_defaultNotificationColor);
253         int defaultLightOn = mContext.getResources().getInteger(
254                 com.android.internal.R.integer.config_defaultNotificationLedOn);
255         int defaultLightOff = mContext.getResources().getInteger(
256                 com.android.internal.R.integer.config_defaultNotificationLedOff);
257 
258         int channelLightColor = getChannel().getLightColor() != 0 ? getChannel().getLightColor()
259                 : defaultLightColor;
260         Light light = getChannel().shouldShowLights() ? new Light(channelLightColor,
261                 defaultLightOn, defaultLightOff) : null;
262         if (mPreChannelsNotification
263                 && (getChannel().getUserLockedFields()
264                 & NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
265             final Notification notification = sbn.getNotification();
266             if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
267                 light = new Light(notification.ledARGB, notification.ledOnMS,
268                         notification.ledOffMS);
269                 if ((notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
270                     light = new Light(defaultLightColor, defaultLightOn,
271                             defaultLightOff);
272                 }
273             } else {
274                 light = null;
275             }
276         }
277         return light;
278     }
279 
calculateVibration()280     private long[] calculateVibration() {
281         long[] vibration;
282         final long[] defaultVibration =  NotificationManagerService.getLongArray(
283                 mContext.getResources(),
284                 com.android.internal.R.array.config_defaultNotificationVibePattern,
285                 NotificationManagerService.VIBRATE_PATTERN_MAXLEN,
286                 NotificationManagerService.DEFAULT_VIBRATE_PATTERN);
287         if (getChannel().shouldVibrate()) {
288             vibration = getChannel().getVibrationPattern() == null
289                     ? defaultVibration : getChannel().getVibrationPattern();
290         } else {
291             vibration = null;
292         }
293         if (mPreChannelsNotification
294                 && (getChannel().getUserLockedFields()
295                 & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
296             final Notification notification = sbn.getNotification();
297             final boolean useDefaultVibrate =
298                     (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
299             if (useDefaultVibrate) {
300                 vibration = defaultVibration;
301             } else {
302                 vibration = notification.vibrate;
303             }
304         }
305         return vibration;
306     }
307 
calculateAttributes()308     private AudioAttributes calculateAttributes() {
309         final Notification n = sbn.getNotification();
310         AudioAttributes attributes = getChannel().getAudioAttributes();
311         if (attributes == null) {
312             attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
313         }
314 
315         if (mPreChannelsNotification
316                 && (getChannel().getUserLockedFields()
317                 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
318             if (n.audioAttributes != null) {
319                 // prefer audio attributes to stream type
320                 attributes = n.audioAttributes;
321             } else if (n.audioStreamType >= 0
322                     && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
323                 // the stream type is valid, use it
324                 attributes = new AudioAttributes.Builder()
325                         .setInternalLegacyStreamType(n.audioStreamType)
326                         .build();
327             } else if (n.audioStreamType != AudioSystem.STREAM_DEFAULT) {
328                 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
329             }
330         }
331         return attributes;
332     }
333 
calculateInitialImportance()334     private int calculateInitialImportance() {
335         final Notification n = sbn.getNotification();
336         int importance = getChannel().getImportance();  // Post-channels notifications use this
337         mInitialImportanceExplanationCode = getChannel().hasUserSetImportance()
338                 ? MetricsEvent.IMPORTANCE_EXPLANATION_USER
339                 : MetricsEvent.IMPORTANCE_EXPLANATION_APP;
340 
341         // Migrate notification priority flag to a priority value.
342         if (0 != (n.flags & Notification.FLAG_HIGH_PRIORITY)) {
343             n.priority = Notification.PRIORITY_MAX;
344         }
345 
346         // Convert priority value to an importance value, used only for pre-channels notifications.
347         int requestedImportance = IMPORTANCE_DEFAULT;
348         n.priority = NotificationManagerService.clamp(n.priority, Notification.PRIORITY_MIN,
349                 Notification.PRIORITY_MAX);
350         switch (n.priority) {
351             case Notification.PRIORITY_MIN:
352                 requestedImportance = IMPORTANCE_MIN;
353                 break;
354             case Notification.PRIORITY_LOW:
355                 requestedImportance = IMPORTANCE_LOW;
356                 break;
357             case Notification.PRIORITY_DEFAULT:
358                 requestedImportance = IMPORTANCE_DEFAULT;
359                 break;
360             case Notification.PRIORITY_HIGH:
361             case Notification.PRIORITY_MAX:
362                 requestedImportance = IMPORTANCE_HIGH;
363                 break;
364         }
365         stats.requestedImportance = requestedImportance;
366         stats.isNoisy = mSound != null || mVibration != null;
367 
368         // For pre-channels notifications, apply system overrides and then use requestedImportance
369         // as importance.
370         if (mPreChannelsNotification
371                 && (importance == IMPORTANCE_UNSPECIFIED
372                 || (!getChannel().hasUserSetImportance()))) {
373             if (!stats.isNoisy && requestedImportance > IMPORTANCE_LOW) {
374                 requestedImportance = IMPORTANCE_LOW;
375             }
376 
377             if (stats.isNoisy) {
378                 if (requestedImportance < IMPORTANCE_DEFAULT) {
379                     requestedImportance = IMPORTANCE_DEFAULT;
380                 }
381             }
382 
383             if (n.fullScreenIntent != null) {
384                 requestedImportance = IMPORTANCE_HIGH;
385             }
386             importance = requestedImportance;
387             mInitialImportanceExplanationCode =
388                     MetricsEvent.IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS;
389         }
390 
391         stats.naturalImportance = importance;
392         return importance;
393     }
394 
395     // copy any notes that the ranking system may have made before the update
copyRankingInformation(NotificationRecord previous)396     public void copyRankingInformation(NotificationRecord previous) {
397         mContactAffinity = previous.mContactAffinity;
398         mRecentlyIntrusive = previous.mRecentlyIntrusive;
399         mPackagePriority = previous.mPackagePriority;
400         mPackageVisibility = previous.mPackageVisibility;
401         mIntercept = previous.mIntercept;
402         mHidden = previous.mHidden;
403         mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
404         mCreationTimeMs = previous.mCreationTimeMs;
405         mVisibleSinceMs = previous.mVisibleSinceMs;
406         if (previous.sbn.getOverrideGroupKey() != null && !sbn.isAppGroup()) {
407             sbn.setOverrideGroupKey(previous.sbn.getOverrideGroupKey());
408         }
409         // Don't copy importance information or mGlobalSortKey, recompute them.
410     }
411 
getNotification()412     public Notification getNotification() { return sbn.getNotification(); }
getFlags()413     public int getFlags() { return sbn.getNotification().flags; }
getUser()414     public UserHandle getUser() { return sbn.getUser(); }
getKey()415     public String getKey() { return sbn.getKey(); }
416     /** @deprecated Use {@link #getUser()} instead. */
getUserId()417     public int getUserId() { return sbn.getUserId(); }
getUid()418     public int getUid() { return sbn.getUid(); }
419 
dump(ProtoOutputStream proto, long fieldId, boolean redact, int state)420     void dump(ProtoOutputStream proto, long fieldId, boolean redact, int state) {
421         final long token = proto.start(fieldId);
422 
423         proto.write(NotificationRecordProto.KEY, sbn.getKey());
424         proto.write(NotificationRecordProto.STATE, state);
425         if (getChannel() != null) {
426             proto.write(NotificationRecordProto.CHANNEL_ID, getChannel().getId());
427         }
428         proto.write(NotificationRecordProto.CAN_SHOW_LIGHT, getLight() != null);
429         proto.write(NotificationRecordProto.CAN_VIBRATE, getVibration() != null);
430         proto.write(NotificationRecordProto.FLAGS, sbn.getNotification().flags);
431         proto.write(NotificationRecordProto.GROUP_KEY, getGroupKey());
432         proto.write(NotificationRecordProto.IMPORTANCE, getImportance());
433         if (getSound() != null) {
434             proto.write(NotificationRecordProto.SOUND, getSound().toString());
435         }
436         if (getAudioAttributes() != null) {
437             getAudioAttributes().writeToProto(proto, NotificationRecordProto.AUDIO_ATTRIBUTES);
438         }
439         proto.write(NotificationRecordProto.PACKAGE, sbn.getPackageName());
440         proto.write(NotificationRecordProto.DELEGATE_PACKAGE, sbn.getOpPkg());
441 
442         proto.end(token);
443     }
444 
formatRemoteViews(RemoteViews rv)445     String formatRemoteViews(RemoteViews rv) {
446         if (rv == null) return "null";
447         return String.format("%s/0x%08x (%d bytes): %s",
448             rv.getPackage(), rv.getLayoutId(), rv.estimateMemoryUsage(), rv.toString());
449     }
450 
dump(PrintWriter pw, String prefix, Context baseContext, boolean redact)451     void dump(PrintWriter pw, String prefix, Context baseContext, boolean redact) {
452         final Notification notification = sbn.getNotification();
453         pw.println(prefix + this);
454         prefix = prefix + "  ";
455         pw.println(prefix + "uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
456         pw.println(prefix + "opPkg=" + sbn.getOpPkg());
457         pw.println(prefix + "icon=" + notification.getSmallIcon());
458         pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
459         pw.println(prefix + "pri=" + notification.priority);
460         pw.println(prefix + "key=" + sbn.getKey());
461         pw.println(prefix + "seen=" + mStats.hasSeen());
462         pw.println(prefix + "groupKey=" + getGroupKey());
463         pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
464         pw.println(prefix + "contentIntent=" + notification.contentIntent);
465         pw.println(prefix + "deleteIntent=" + notification.deleteIntent);
466         pw.println(prefix + "number=" + notification.number);
467         pw.println(prefix + "groupAlertBehavior=" + notification.getGroupAlertBehavior());
468 
469         pw.print(prefix + "tickerText=");
470         if (!TextUtils.isEmpty(notification.tickerText)) {
471             final String ticker = notification.tickerText.toString();
472             if (redact) {
473                 // if the string is long enough, we allow ourselves a few bytes for debugging
474                 pw.print(ticker.length() > 16 ? ticker.substring(0,8) : "");
475                 pw.println("...");
476             } else {
477                 pw.println(ticker);
478             }
479         } else {
480             pw.println("null");
481         }
482         pw.println(prefix + "contentView=" + formatRemoteViews(notification.contentView));
483         pw.println(prefix + "bigContentView=" + formatRemoteViews(notification.bigContentView));
484         pw.println(prefix + "headsUpContentView="
485                 + formatRemoteViews(notification.headsUpContentView));
486         pw.print(prefix + String.format("color=0x%08x", notification.color));
487         pw.println(prefix + "timeout="
488                 + TimeUtils.formatForLogging(notification.getTimeoutAfter()));
489         if (notification.actions != null && notification.actions.length > 0) {
490             pw.println(prefix + "actions={");
491             final int N = notification.actions.length;
492             for (int i = 0; i < N; i++) {
493                 final Notification.Action action = notification.actions[i];
494                 if (action != null) {
495                     pw.println(String.format("%s    [%d] \"%s\" -> %s",
496                             prefix,
497                             i,
498                             action.title,
499                             action.actionIntent == null ? "null" : action.actionIntent.toString()
500                     ));
501                 }
502             }
503             pw.println(prefix + "  }");
504         }
505         if (notification.extras != null && notification.extras.size() > 0) {
506             pw.println(prefix + "extras={");
507             for (String key : notification.extras.keySet()) {
508                 pw.print(prefix + "    " + key + "=");
509                 Object val = notification.extras.get(key);
510                 if (val == null) {
511                     pw.println("null");
512                 } else {
513                     pw.print(val.getClass().getSimpleName());
514                     if (redact && (val instanceof CharSequence || val instanceof String)) {
515                         // redact contents from bugreports
516                     } else if (val instanceof Bitmap) {
517                         pw.print(String.format(" (%dx%d)",
518                                 ((Bitmap) val).getWidth(),
519                                 ((Bitmap) val).getHeight()));
520                     } else if (val.getClass().isArray()) {
521                         final int N = Array.getLength(val);
522                         pw.print(" (" + N + ")");
523                         if (!redact) {
524                             for (int j = 0; j < N; j++) {
525                                 pw.println();
526                                 pw.print(String.format("%s      [%d] %s",
527                                         prefix, j, String.valueOf(Array.get(val, j))));
528                             }
529                         }
530                     } else {
531                         pw.print(" (" + String.valueOf(val) + ")");
532                     }
533                     pw.println();
534                 }
535             }
536             pw.println(prefix + "}");
537         }
538         pw.println(prefix + "stats=" + stats.toString());
539         pw.println(prefix + "mContactAffinity=" + mContactAffinity);
540         pw.println(prefix + "mRecentlyIntrusive=" + mRecentlyIntrusive);
541         pw.println(prefix + "mPackagePriority=" + mPackagePriority);
542         pw.println(prefix + "mPackageVisibility=" + mPackageVisibility);
543         pw.println(prefix + "mSystemImportance="
544                 + NotificationListenerService.Ranking.importanceToString(mSystemImportance));
545         pw.println(prefix + "mAsstImportance="
546                 + NotificationListenerService.Ranking.importanceToString(mAssistantImportance));
547         pw.println(prefix + "mImportance="
548                 + NotificationListenerService.Ranking.importanceToString(mImportance));
549         pw.println(prefix + "mImportanceExplanation=" + getImportanceExplanation());
550         pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked);
551         pw.println(prefix + "mIntercept=" + mIntercept);
552         pw.println(prefix + "mHidden==" + mHidden);
553         pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
554         pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
555         pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
556         pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
557         pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
558         pw.println(prefix + "mInterruptionTimeMs=" + mInterruptionTimeMs);
559         pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
560         if (mPreChannelsNotification) {
561             pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
562                     notification.defaults, notification.flags));
563             pw.println(prefix + "n.sound=" + notification.sound);
564             pw.println(prefix + "n.audioStreamType=" + notification.audioStreamType);
565             pw.println(prefix + "n.audioAttributes=" + notification.audioAttributes);
566             pw.println(prefix + String.format("  led=0x%08x onMs=%d offMs=%d",
567                     notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
568             pw.println(prefix + "vibrate=" + Arrays.toString(notification.vibrate));
569         }
570         pw.println(prefix + "mSound= " + mSound);
571         pw.println(prefix + "mVibration= " + mVibration);
572         pw.println(prefix + "mAttributes= " + mAttributes);
573         pw.println(prefix + "mLight= " + mLight);
574         pw.println(prefix + "mShowBadge=" + mShowBadge);
575         pw.println(prefix + "mColorized=" + notification.isColorized());
576         pw.println(prefix + "mIsInterruptive=" + mIsInterruptive);
577         pw.println(prefix + "effectiveNotificationChannel=" + getChannel());
578         if (getPeopleOverride() != null) {
579             pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride()));
580         }
581         if (getSnoozeCriteria() != null) {
582             pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
583         }
584         pw.println(prefix + "mAdjustments=" + mAdjustments);
585     }
586 
587     @Override
toString()588     public final String toString() {
589         return String.format(
590                 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" +
591                         "appImportanceLocked=%s: %s)",
592                 System.identityHashCode(this),
593                 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
594                 this.sbn.getTag(), this.mImportance, this.sbn.getKey(),
595                 mIsAppImportanceLocked, this.sbn.getNotification());
596     }
597 
hasAdjustment(String key)598     public boolean hasAdjustment(String key) {
599         synchronized (mAdjustments) {
600             for (Adjustment adjustment : mAdjustments) {
601                 if (adjustment.getSignals().containsKey(key)) {
602                     return true;
603                 }
604             }
605         }
606         return false;
607     }
608 
addAdjustment(Adjustment adjustment)609     public void addAdjustment(Adjustment adjustment) {
610         synchronized (mAdjustments) {
611             mAdjustments.add(adjustment);
612         }
613     }
614 
applyAdjustments()615     public void applyAdjustments() {
616         long now = System.currentTimeMillis();
617         synchronized (mAdjustments) {
618             for (Adjustment adjustment: mAdjustments) {
619                 Bundle signals = adjustment.getSignals();
620                 if (signals.containsKey(Adjustment.KEY_PEOPLE)) {
621                     final ArrayList<String> people =
622                             adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
623                     setPeopleOverride(people);
624                 }
625                 if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
626                     final ArrayList<SnoozeCriterion> snoozeCriterionList =
627                             adjustment.getSignals().getParcelableArrayList(
628                                     Adjustment.KEY_SNOOZE_CRITERIA);
629                     setSnoozeCriteria(snoozeCriterionList);
630                 }
631                 if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
632                     final String groupOverrideKey =
633                             adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
634                     setOverrideGroupKey(groupOverrideKey);
635                 }
636                 if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
637                     // Only allow user sentiment update from assistant if user hasn't already
638                     // expressed a preference for this channel
639                     if (!mIsAppImportanceLocked
640                             && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) {
641                         setUserSentiment(adjustment.getSignals().getInt(
642                                 Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
643                     }
644                 }
645                 if (signals.containsKey(Adjustment.KEY_CONTEXTUAL_ACTIONS)) {
646                     setSystemGeneratedSmartActions(
647                             signals.getParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS));
648                 }
649                 if (signals.containsKey(Adjustment.KEY_TEXT_REPLIES)) {
650                     setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES));
651                 }
652                 if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
653                     int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
654                     importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
655                     importance = Math.min(IMPORTANCE_HIGH, importance);
656                     setAssistantImportance(importance);
657                 }
658                 if (!signals.isEmpty() && adjustment.getIssuer() != null) {
659                     mAdjustmentIssuer = adjustment.getIssuer();
660                 }
661             }
662             // We have now gotten all the information out of the adjustments and can forget them.
663             mAdjustments.clear();
664         }
665     }
666 
setIsAppImportanceLocked(boolean isAppImportanceLocked)667     public void setIsAppImportanceLocked(boolean isAppImportanceLocked) {
668         mIsAppImportanceLocked = isAppImportanceLocked;
669         calculateUserSentiment();
670     }
671 
setContactAffinity(float contactAffinity)672     public void setContactAffinity(float contactAffinity) {
673         mContactAffinity = contactAffinity;
674     }
675 
getContactAffinity()676     public float getContactAffinity() {
677         return mContactAffinity;
678     }
679 
setRecentlyIntrusive(boolean recentlyIntrusive)680     public void setRecentlyIntrusive(boolean recentlyIntrusive) {
681         mRecentlyIntrusive = recentlyIntrusive;
682         if (recentlyIntrusive) {
683             mLastIntrusive = System.currentTimeMillis();
684         }
685     }
686 
isRecentlyIntrusive()687     public boolean isRecentlyIntrusive() {
688         return mRecentlyIntrusive;
689     }
690 
getLastIntrusive()691     public long getLastIntrusive() {
692         return mLastIntrusive;
693     }
694 
setPackagePriority(int packagePriority)695     public void setPackagePriority(int packagePriority) {
696         mPackagePriority = packagePriority;
697     }
698 
getPackagePriority()699     public int getPackagePriority() {
700         return mPackagePriority;
701     }
702 
setPackageVisibilityOverride(int packageVisibility)703     public void setPackageVisibilityOverride(int packageVisibility) {
704         mPackageVisibility = packageVisibility;
705     }
706 
getPackageVisibilityOverride()707     public int getPackageVisibilityOverride() {
708         return mPackageVisibility;
709     }
710 
getUserExplanation()711     private String getUserExplanation() {
712         if (mUserExplanation == null) {
713             mUserExplanation = mContext.getResources().getString(
714                     com.android.internal.R.string.importance_from_user);
715         }
716         return mUserExplanation;
717     }
718 
719     /**
720      * Sets the importance value the system thinks the record should have.
721      * e.g. bumping up foreground service notifications or people to people notifications.
722      */
setSystemImportance(int importance)723     public void setSystemImportance(int importance) {
724         mSystemImportance = importance;
725         // System importance is only changed in enqueue, so it's ok for us to calculate the
726         // importance directly instead of waiting for signal extractor.
727         calculateImportance();
728     }
729 
730     /**
731      * Sets the importance value the
732      * {@link android.service.notification.NotificationAssistantService} thinks the record should
733      * have.
734      */
setAssistantImportance(int importance)735     public void setAssistantImportance(int importance) {
736         mAssistantImportance = importance;
737         // Unlike the system importance, the assistant importance can change on posted
738         // notifications, so don't calculateImportance() here, but wait for the signal extractors.
739     }
740 
741     /**
742      * Returns the importance set by the assistant, or IMPORTANCE_UNSPECIFIED if the assistant
743      * hasn't set it.
744      */
getAssistantImportance()745     public int getAssistantImportance() {
746         return mAssistantImportance;
747     }
748 
749     /**
750      * Recalculates the importance of the record after fields affecting importance have changed,
751      * and records an explanation.
752      */
calculateImportance()753     protected void calculateImportance() {
754         mImportance = calculateInitialImportance();
755         mImportanceExplanationCode = mInitialImportanceExplanationCode;
756 
757         // Consider Notification Assistant and system overrides to importance. If both, system wins.
758         if (!getChannel().hasUserSetImportance()
759                 && mAssistantImportance != IMPORTANCE_UNSPECIFIED
760                 && !getChannel().isImportanceLockedByOEM()
761                 && !getChannel().isImportanceLockedByCriticalDeviceFunction()) {
762             mImportance = mAssistantImportance;
763             mImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_ASST;
764         }
765         if (mSystemImportance != IMPORTANCE_UNSPECIFIED) {
766             mImportance = mSystemImportance;
767             mImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_SYSTEM;
768         }
769     }
770 
getImportance()771     public int getImportance() {
772         return mImportance;
773     }
774 
getImportanceExplanation()775     public CharSequence getImportanceExplanation() {
776         switch (mImportanceExplanationCode) {
777             case MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN:
778                 return null;
779             case MetricsEvent.IMPORTANCE_EXPLANATION_APP:
780             case MetricsEvent.IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS:
781                 return "app";
782             case MetricsEvent.IMPORTANCE_EXPLANATION_USER:
783                 return "user";
784             case MetricsEvent.IMPORTANCE_EXPLANATION_ASST:
785                 return "asst";
786             case MetricsEvent.IMPORTANCE_EXPLANATION_SYSTEM:
787                 return "system";
788         }
789         return null;
790     }
791 
setIntercepted(boolean intercept)792     public boolean setIntercepted(boolean intercept) {
793         mIntercept = intercept;
794         return mIntercept;
795     }
796 
797     /**
798      * Set to affect global sort key.
799      *
800      * @param criticality used in a string based sort thus 0 is the most critical
801      */
setCriticality(int criticality)802     public void setCriticality(int criticality) {
803         mCriticality = criticality;
804     }
805 
getCriticality()806     public int getCriticality() {
807         return mCriticality;
808     }
809 
isIntercepted()810     public boolean isIntercepted() {
811         return mIntercept;
812     }
813 
isNewEnoughForAlerting(long now)814     public boolean isNewEnoughForAlerting(long now) {
815         return getFreshnessMs(now) <= MAX_SOUND_DELAY_MS;
816     }
817 
setHidden(boolean hidden)818     public void setHidden(boolean hidden) {
819         mHidden = hidden;
820     }
821 
isHidden()822     public boolean isHidden() {
823         return mHidden;
824     }
825 
826 
setSuppressedVisualEffects(int effects)827     public void setSuppressedVisualEffects(int effects) {
828         mSuppressedVisualEffects = effects;
829     }
830 
getSuppressedVisualEffects()831     public int getSuppressedVisualEffects() {
832         return mSuppressedVisualEffects;
833     }
834 
isCategory(String category)835     public boolean isCategory(String category) {
836         return Objects.equals(getNotification().category, category);
837     }
838 
isAudioAttributesUsage(int usage)839     public boolean isAudioAttributesUsage(int usage) {
840         return mAttributes != null && mAttributes.getUsage() == usage;
841     }
842 
843     /**
844      * Returns the timestamp to use for time-based sorting in the ranker.
845      */
getRankingTimeMs()846     public long getRankingTimeMs() {
847         return mRankingTimeMs;
848     }
849 
850     /**
851      * @param now this current time in milliseconds.
852      * @returns the number of milliseconds since the most recent update, or the post time if none.
853      */
getFreshnessMs(long now)854     public int getFreshnessMs(long now) {
855         return (int) (now - mUpdateTimeMs);
856     }
857 
858     /**
859      * @param now this current time in milliseconds.
860      * @returns the number of milliseconds since the the first post, ignoring updates.
861      */
getLifespanMs(long now)862     public int getLifespanMs(long now) {
863         return (int) (now - mCreationTimeMs);
864     }
865 
866     /**
867      * @param now this current time in milliseconds.
868      * @returns the number of milliseconds since the most recent visibility event, or 0 if never.
869      */
getExposureMs(long now)870     public int getExposureMs(long now) {
871         return mVisibleSinceMs == 0 ? 0 : (int) (now - mVisibleSinceMs);
872     }
873 
getInterruptionMs(long now)874     public int getInterruptionMs(long now) {
875         return (int) (now - mInterruptionTimeMs);
876     }
877 
878     /**
879      * Set the visibility of the notification.
880      */
setVisibility(boolean visible, int rank, int count)881     public void setVisibility(boolean visible, int rank, int count) {
882         final long now = System.currentTimeMillis();
883         mVisibleSinceMs = visible ? now : mVisibleSinceMs;
884         stats.onVisibilityChanged(visible);
885         MetricsLogger.action(getLogMaker(now)
886                 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
887                 .setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
888                 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank)
889                 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, count));
890         if (visible) {
891             setSeen();
892             MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now));
893         }
894         EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0,
895                 getLifespanMs(now),
896                 getFreshnessMs(now),
897                 0, // exposure time
898                 rank);
899     }
900 
901     /**
902      * @param previousRankingTimeMs for updated notifications, {@link #getRankingTimeMs()}
903      *     of the previous notification record, 0 otherwise
904      */
calculateRankingTimeMs(long previousRankingTimeMs)905     private long calculateRankingTimeMs(long previousRankingTimeMs) {
906         Notification n = getNotification();
907         // Take developer provided 'when', unless it's in the future.
908         if (n.when != 0 && n.when <= sbn.getPostTime()) {
909             return n.when;
910         }
911         // If we've ranked a previous instance with a timestamp, inherit it. This case is
912         // important in order to have ranking stability for updating notifications.
913         if (previousRankingTimeMs > 0) {
914             return previousRankingTimeMs;
915         }
916         return sbn.getPostTime();
917     }
918 
setGlobalSortKey(String globalSortKey)919     public void setGlobalSortKey(String globalSortKey) {
920         mGlobalSortKey = globalSortKey;
921     }
922 
getGlobalSortKey()923     public String getGlobalSortKey() {
924         return mGlobalSortKey;
925     }
926 
927     /** Check if any of the listeners have marked this notification as seen by the user. */
isSeen()928     public boolean isSeen() {
929         return mStats.hasSeen();
930     }
931 
932     /** Mark the notification as seen by the user. */
setSeen()933     public void setSeen() {
934         mStats.setSeen();
935         if (mTextChanged) {
936             setInterruptive(true);
937         }
938     }
939 
setAuthoritativeRank(int authoritativeRank)940     public void setAuthoritativeRank(int authoritativeRank) {
941         mAuthoritativeRank = authoritativeRank;
942     }
943 
getAuthoritativeRank()944     public int getAuthoritativeRank() {
945         return mAuthoritativeRank;
946     }
947 
getGroupKey()948     public String getGroupKey() {
949         return sbn.getGroupKey();
950     }
951 
setOverrideGroupKey(String overrideGroupKey)952     public void setOverrideGroupKey(String overrideGroupKey) {
953         sbn.setOverrideGroupKey(overrideGroupKey);
954     }
955 
getChannel()956     public NotificationChannel getChannel() {
957         return mChannel;
958     }
959 
960     /**
961      * @see PreferencesHelper#getIsAppImportanceLocked(String, int)
962      */
getIsAppImportanceLocked()963     public boolean getIsAppImportanceLocked() {
964         return mIsAppImportanceLocked;
965     }
966 
updateNotificationChannel(NotificationChannel channel)967     protected void updateNotificationChannel(NotificationChannel channel) {
968         if (channel != null) {
969             mChannel = channel;
970             calculateImportance();
971             calculateUserSentiment();
972         }
973     }
974 
setShowBadge(boolean showBadge)975     public void setShowBadge(boolean showBadge) {
976         mShowBadge = showBadge;
977     }
978 
canBubble()979     public boolean canBubble() {
980         return mAllowBubble;
981     }
982 
setAllowBubble(boolean allow)983     public void setAllowBubble(boolean allow) {
984         mAllowBubble = allow;
985     }
986 
canShowBadge()987     public boolean canShowBadge() {
988         return mShowBadge;
989     }
990 
getLight()991     public Light getLight() {
992         return mLight;
993     }
994 
getSound()995     public Uri getSound() {
996         return mSound;
997     }
998 
getVibration()999     public long[] getVibration() {
1000         return mVibration;
1001     }
1002 
getAudioAttributes()1003     public AudioAttributes getAudioAttributes() {
1004         return mAttributes;
1005     }
1006 
getPeopleOverride()1007     public ArrayList<String> getPeopleOverride() {
1008         return mPeopleOverride;
1009     }
1010 
setInterruptive(boolean interruptive)1011     public void setInterruptive(boolean interruptive) {
1012         mIsInterruptive = interruptive;
1013         final long now = System.currentTimeMillis();
1014         mInterruptionTimeMs = interruptive ? now : mInterruptionTimeMs;
1015 
1016         if (interruptive) {
1017             MetricsLogger.action(getLogMaker()
1018                     .setCategory(MetricsEvent.NOTIFICATION_INTERRUPTION)
1019                     .setType(MetricsEvent.TYPE_OPEN)
1020                     .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS,
1021                             getInterruptionMs(now)));
1022             MetricsLogger.histogram(mContext, "note_interruptive", getInterruptionMs(now));
1023         }
1024     }
1025 
setAudiblyAlerted(boolean audiblyAlerted)1026     public void setAudiblyAlerted(boolean audiblyAlerted) {
1027         mLastAudiblyAlertedMs = audiblyAlerted ? System.currentTimeMillis() : -1;
1028     }
1029 
setTextChanged(boolean textChanged)1030     public void setTextChanged(boolean textChanged) {
1031         mTextChanged = textChanged;
1032     }
1033 
setRecordedInterruption(boolean recorded)1034     public void setRecordedInterruption(boolean recorded) {
1035         mRecordedInterruption = recorded;
1036     }
1037 
hasRecordedInterruption()1038     public boolean hasRecordedInterruption() {
1039         return mRecordedInterruption;
1040     }
1041 
isInterruptive()1042     public boolean isInterruptive() {
1043         return mIsInterruptive;
1044     }
1045 
1046     /** Returns the time the notification audibly alerted the user. */
getLastAudiblyAlertedMs()1047     public long getLastAudiblyAlertedMs() {
1048         return mLastAudiblyAlertedMs;
1049     }
1050 
setPeopleOverride(ArrayList<String> people)1051     protected void setPeopleOverride(ArrayList<String> people) {
1052         mPeopleOverride = people;
1053     }
1054 
getSnoozeCriteria()1055     public ArrayList<SnoozeCriterion> getSnoozeCriteria() {
1056         return mSnoozeCriteria;
1057     }
1058 
setSnoozeCriteria(ArrayList<SnoozeCriterion> snoozeCriteria)1059     protected void setSnoozeCriteria(ArrayList<SnoozeCriterion> snoozeCriteria) {
1060         mSnoozeCriteria = snoozeCriteria;
1061     }
1062 
calculateUserSentiment()1063     private void calculateUserSentiment() {
1064         if ((getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0
1065                 || mIsAppImportanceLocked) {
1066             mUserSentiment = USER_SENTIMENT_POSITIVE;
1067         }
1068     }
1069 
setUserSentiment(int userSentiment)1070     private void setUserSentiment(int userSentiment) {
1071         mUserSentiment = userSentiment;
1072     }
1073 
getUserSentiment()1074     public int getUserSentiment() {
1075         return mUserSentiment;
1076     }
1077 
getStats()1078     public NotificationStats getStats() {
1079         return mStats;
1080     }
1081 
recordExpanded()1082     public void recordExpanded() {
1083         mStats.setExpanded();
1084     }
1085 
recordDirectReplied()1086     public void recordDirectReplied() {
1087         mStats.setDirectReplied();
1088     }
1089 
recordDismissalSurface(@otificationStats.DismissalSurface int surface)1090     public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) {
1091         mStats.setDismissalSurface(surface);
1092     }
1093 
recordDismissalSentiment(@otificationStats.DismissalSentiment int sentiment)1094     public void recordDismissalSentiment(@NotificationStats.DismissalSentiment int sentiment) {
1095         mStats.setDismissalSentiment(sentiment);
1096     }
1097 
recordSnoozed()1098     public void recordSnoozed() {
1099         mStats.setSnoozed();
1100     }
1101 
recordViewedSettings()1102     public void recordViewedSettings() {
1103         mStats.setViewedSettings();
1104     }
1105 
setNumSmartRepliesAdded(int noReplies)1106     public void setNumSmartRepliesAdded(int noReplies) {
1107         mNumberOfSmartRepliesAdded = noReplies;
1108     }
1109 
getNumSmartRepliesAdded()1110     public int getNumSmartRepliesAdded() {
1111         return mNumberOfSmartRepliesAdded;
1112     }
1113 
setNumSmartActionsAdded(int noActions)1114     public void setNumSmartActionsAdded(int noActions) {
1115         mNumberOfSmartActionsAdded = noActions;
1116     }
1117 
getNumSmartActionsAdded()1118     public int getNumSmartActionsAdded() {
1119         return mNumberOfSmartActionsAdded;
1120     }
1121 
setSuggestionsGeneratedByAssistant(boolean generatedByAssistant)1122     public void setSuggestionsGeneratedByAssistant(boolean generatedByAssistant) {
1123         mSuggestionsGeneratedByAssistant = generatedByAssistant;
1124     }
1125 
getSuggestionsGeneratedByAssistant()1126     public boolean getSuggestionsGeneratedByAssistant() {
1127         return mSuggestionsGeneratedByAssistant;
1128     }
1129 
getEditChoicesBeforeSending()1130     public boolean getEditChoicesBeforeSending() {
1131         return mEditChoicesBeforeSending;
1132     }
1133 
setEditChoicesBeforeSending(boolean editChoicesBeforeSending)1134     public void setEditChoicesBeforeSending(boolean editChoicesBeforeSending) {
1135         mEditChoicesBeforeSending = editChoicesBeforeSending;
1136     }
1137 
hasSeenSmartReplies()1138     public boolean hasSeenSmartReplies() {
1139         return mHasSeenSmartReplies;
1140     }
1141 
setSeenSmartReplies(boolean hasSeenSmartReplies)1142     public void setSeenSmartReplies(boolean hasSeenSmartReplies) {
1143         mHasSeenSmartReplies = hasSeenSmartReplies;
1144     }
1145 
1146     /**
1147      * Returns whether this notification has been visible and expanded at the same time.
1148      */
hasBeenVisiblyExpanded()1149     public boolean hasBeenVisiblyExpanded() {
1150         return stats.hasBeenVisiblyExpanded();
1151     }
1152 
setSystemGeneratedSmartActions( ArrayList<Notification.Action> systemGeneratedSmartActions)1153     public void setSystemGeneratedSmartActions(
1154             ArrayList<Notification.Action> systemGeneratedSmartActions) {
1155         mSystemGeneratedSmartActions = systemGeneratedSmartActions;
1156     }
1157 
getSystemGeneratedSmartActions()1158     public ArrayList<Notification.Action> getSystemGeneratedSmartActions() {
1159         return mSystemGeneratedSmartActions;
1160     }
1161 
setSmartReplies(ArrayList<CharSequence> smartReplies)1162     public void setSmartReplies(ArrayList<CharSequence> smartReplies) {
1163         mSmartReplies = smartReplies;
1164     }
1165 
getSmartReplies()1166     public ArrayList<CharSequence> getSmartReplies() {
1167         return mSmartReplies;
1168     }
1169 
1170     /**
1171      * Returns whether this notification was posted by a secondary app
1172      */
isProxied()1173     public boolean isProxied() {
1174         return !Objects.equals(sbn.getPackageName(), sbn.getOpPkg());
1175     }
1176 
1177     /**
1178      * @return all {@link Uri} that should have permission granted to whoever
1179      *         will be rendering it. This list has already been vetted to only
1180      *         include {@link Uri} that the enqueuing app can grant.
1181      */
getGrantableUris()1182     public @Nullable ArraySet<Uri> getGrantableUris() {
1183         return mGrantableUris;
1184     }
1185 
1186     /**
1187      * Collect all {@link Uri} that should have permission granted to whoever
1188      * will be rendering it.
1189      */
calculateGrantableUris()1190     protected void calculateGrantableUris() {
1191         final Notification notification = getNotification();
1192         notification.visitUris((uri) -> {
1193             visitGrantableUri(uri, false);
1194         });
1195 
1196         if (notification.getChannelId() != null) {
1197             NotificationChannel channel = getChannel();
1198             if (channel != null) {
1199                 visitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
1200                         & NotificationChannel.USER_LOCKED_SOUND) != 0);
1201             }
1202         }
1203     }
1204 
1205     /**
1206      * Note the presence of a {@link Uri} that should have permission granted to
1207      * whoever will be rendering it.
1208      * <p>
1209      * If the enqueuing app has the ability to grant access, it will be added to
1210      * {@link #mGrantableUris}. Otherwise, this will either log or throw
1211      * {@link SecurityException} depending on target SDK of enqueuing app.
1212      */
visitGrantableUri(Uri uri, boolean userOverriddenUri)1213     private void visitGrantableUri(Uri uri, boolean userOverriddenUri) {
1214         if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
1215 
1216         // We can't grant Uri permissions from system
1217         final int sourceUid = sbn.getUid();
1218         if (sourceUid == android.os.Process.SYSTEM_UID) return;
1219 
1220         final long ident = Binder.clearCallingIdentity();
1221         try {
1222             // This will throw SecurityException if caller can't grant
1223             mUgmInternal.checkGrantUriPermission(sourceUid, null,
1224                     ContentProvider.getUriWithoutUserId(uri),
1225                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
1226                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
1227 
1228             if (mGrantableUris == null) {
1229                 mGrantableUris = new ArraySet<>();
1230             }
1231             mGrantableUris.add(uri);
1232         } catch (SecurityException e) {
1233             if (!userOverriddenUri) {
1234                 if (mTargetSdkVersion >= Build.VERSION_CODES.P) {
1235                     throw e;
1236                 } else {
1237                     Log.w(TAG, "Ignoring " + uri + " from " + sourceUid + ": " + e.getMessage());
1238                 }
1239             }
1240         } finally {
1241             Binder.restoreCallingIdentity(ident);
1242         }
1243     }
1244 
getLogMaker(long now)1245     public LogMaker getLogMaker(long now) {
1246         LogMaker lm = sbn.getLogMaker()
1247                 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, mImportance)
1248                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
1249                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
1250                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now))
1251                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS,
1252                         getInterruptionMs(now));
1253         // Record results of the calculateImportance() calculation if available.
1254         if (mImportanceExplanationCode != MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN) {
1255             lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_EXPLANATION,
1256                     mImportanceExplanationCode);
1257             // To avoid redundancy, we log the initial importance information only if it was
1258             // overridden.
1259             if (((mImportanceExplanationCode == MetricsEvent.IMPORTANCE_EXPLANATION_ASST)
1260                     || (mImportanceExplanationCode == MetricsEvent.IMPORTANCE_EXPLANATION_SYSTEM))
1261                     && (stats.naturalImportance != IMPORTANCE_UNSPECIFIED)) {
1262                 // stats.naturalImportance is due to one of the 3 sources of initial importance.
1263                 lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_INITIAL_EXPLANATION,
1264                         mInitialImportanceExplanationCode);
1265                 lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_INITIAL,
1266                         stats.naturalImportance);
1267             }
1268         }
1269         // Log Assistant override if present, whether or not importance calculation is complete.
1270         if (mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
1271             lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST,
1272                         mAssistantImportance);
1273         }
1274         // Log the issuer of any adjustments that may have affected this notification. We only log
1275         // the hash here as NotificationItem events are frequent, and the number of NAS
1276         // implementations (and hence the chance of collisions) is low.
1277         if (mAdjustmentIssuer != null) {
1278             lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_ASSISTANT_SERVICE_HASH,
1279                     mAdjustmentIssuer.hashCode());
1280         }
1281         return lm;
1282     }
1283 
getLogMaker()1284     public LogMaker getLogMaker() {
1285         return getLogMaker(System.currentTimeMillis());
1286     }
1287 
getItemLogMaker()1288     public LogMaker getItemLogMaker() {
1289         return getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM);
1290     }
1291 
hasUndecoratedRemoteView()1292     public boolean hasUndecoratedRemoteView() {
1293         Notification notification = getNotification();
1294         Class<? extends Notification.Style> style = notification.getNotificationStyle();
1295         boolean hasDecoratedStyle = style != null
1296                 && (Notification.DecoratedCustomViewStyle.class.equals(style)
1297                 || Notification.DecoratedMediaCustomViewStyle.class.equals(style));
1298         boolean hasCustomRemoteView = notification.contentView != null
1299                 || notification.bigContentView != null
1300                 || notification.headsUpContentView != null;
1301         return hasCustomRemoteView && !hasDecoratedStyle;
1302     }
1303 
1304     @VisibleForTesting
1305     static final class Light {
1306         public final int color;
1307         public final int onMs;
1308         public final int offMs;
1309 
Light(int color, int onMs, int offMs)1310         public Light(int color, int onMs, int offMs) {
1311             this.color = color;
1312             this.onMs = onMs;
1313             this.offMs = offMs;
1314         }
1315 
1316         @Override
equals(Object o)1317         public boolean equals(Object o) {
1318             if (this == o) return true;
1319             if (o == null || getClass() != o.getClass()) return false;
1320 
1321             Light light = (Light) o;
1322 
1323             if (color != light.color) return false;
1324             if (onMs != light.onMs) return false;
1325             return offMs == light.offMs;
1326 
1327         }
1328 
1329         @Override
hashCode()1330         public int hashCode() {
1331             int result = color;
1332             result = 31 * result + onMs;
1333             result = 31 * result + offMs;
1334             return result;
1335         }
1336 
1337         @Override
toString()1338         public String toString() {
1339             return "Light{" +
1340                     "color=" + color +
1341                     ", onMs=" + onMs +
1342                     ", offMs=" + offMs +
1343                     '}';
1344         }
1345     }
1346 }
1347