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