OSDN Git Service

Merge "Grant notification Uri permissions as sending app." into pi-dev
[android-x86/frameworks-base.git] / services / core / java / com / android / server / notification / NotificationRecord.java
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.Notification.EXTRA_AUDIO_CONTENTS_URI;
19 import static android.app.Notification.EXTRA_BACKGROUND_IMAGE_URI;
20 import static android.app.Notification.EXTRA_HISTORIC_MESSAGES;
21 import static android.app.Notification.EXTRA_MESSAGES;
22 import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
23 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
24 import static android.app.NotificationManager.IMPORTANCE_HIGH;
25 import static android.app.NotificationManager.IMPORTANCE_LOW;
26 import static android.app.NotificationManager.IMPORTANCE_MIN;
27 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
28 import static android.service.notification.NotificationListenerService.Ranking
29         .USER_SENTIMENT_NEUTRAL;
30 import static android.service.notification.NotificationListenerService.Ranking
31         .USER_SENTIMENT_POSITIVE;
32
33 import android.annotation.Nullable;
34 import android.app.ActivityManager;
35 import android.app.ActivityManagerInternal;
36 import android.app.IActivityManager;
37 import android.app.Notification;
38 import android.app.Notification.MessagingStyle;
39 import android.app.NotificationChannel;
40 import android.content.ContentProvider;
41 import android.content.ContentResolver;
42 import android.content.Context;
43 import android.content.Intent;
44 import android.content.pm.PackageManager;
45 import android.content.pm.PackageManager.NameNotFoundException;
46 import android.content.pm.PackageManagerInternal;
47 import android.content.res.Resources;
48 import android.graphics.Bitmap;
49 import android.graphics.drawable.Icon;
50 import android.media.AudioAttributes;
51 import android.media.AudioSystem;
52 import android.metrics.LogMaker;
53 import android.net.Uri;
54 import android.os.Binder;
55 import android.os.Build;
56 import android.os.Bundle;
57 import android.os.IBinder;
58 import android.os.Parcelable;
59 import android.os.RemoteException;
60 import android.os.UserHandle;
61 import android.provider.Settings;
62 import android.service.notification.Adjustment;
63 import android.service.notification.NotificationListenerService;
64 import android.service.notification.NotificationRecordProto;
65 import android.service.notification.NotificationStats;
66 import android.service.notification.SnoozeCriterion;
67 import android.service.notification.StatusBarNotification;
68 import android.text.TextUtils;
69 import android.util.ArraySet;
70 import android.util.Log;
71 import android.util.TimeUtils;
72 import android.util.proto.ProtoOutputStream;
73 import android.widget.RemoteViews;
74
75 import com.android.internal.annotations.VisibleForTesting;
76 import com.android.internal.logging.MetricsLogger;
77 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
78 import com.android.internal.util.ArrayUtils;
79 import com.android.server.EventLogTags;
80 import com.android.server.LocalServices;
81
82 import java.io.PrintWriter;
83 import java.lang.reflect.Array;
84 import java.util.ArrayList;
85 import java.util.Arrays;
86 import java.util.List;
87 import java.util.Objects;
88
89 /**
90  * Holds data about notifications that should not be shared with the
91  * {@link android.service.notification.NotificationListenerService}s.
92  *
93  * <p>These objects should not be mutated unless the code is synchronized
94  * on {@link NotificationManagerService#mNotificationLock}, and any
95  * modification should be followed by a sorting of that list.</p>
96  *
97  * <p>Is sortable by {@link NotificationComparator}.</p>
98  *
99  * {@hide}
100  */
101 public final class NotificationRecord {
102     static final String TAG = "NotificationRecord";
103     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
104     private static final int MAX_LOGTAG_LENGTH = 35;
105     final StatusBarNotification sbn;
106     final int mTargetSdkVersion;
107     final int mOriginalFlags;
108     private final Context mContext;
109
110     NotificationUsageStats.SingleNotificationStats stats;
111     boolean isCanceled;
112     IBinder permissionOwner;
113
114     // These members are used by NotificationSignalExtractors
115     // to communicate with the ranking module.
116     private float mContactAffinity;
117     private boolean mRecentlyIntrusive;
118     private long mLastIntrusive;
119
120     // is this notification currently being intercepted by Zen Mode?
121     private boolean mIntercept;
122
123     // is this notification hidden since the app pkg is suspended?
124     private boolean mHidden;
125
126     // The timestamp used for ranking.
127     private long mRankingTimeMs;
128
129     // The first post time, stable across updates.
130     private long mCreationTimeMs;
131
132     // The most recent visibility event.
133     private long mVisibleSinceMs;
134
135     // The most recent update time, or the creation time if no updates.
136     private long mUpdateTimeMs;
137
138     // Is this record an update of an old record?
139     public boolean isUpdate;
140     private int mPackagePriority;
141
142     private int mAuthoritativeRank;
143     private String mGlobalSortKey;
144     private int mPackageVisibility;
145     private int mUserImportance = IMPORTANCE_UNSPECIFIED;
146     private int mImportance = IMPORTANCE_UNSPECIFIED;
147     private CharSequence mImportanceExplanation = null;
148
149     private int mSuppressedVisualEffects = 0;
150     private String mUserExplanation;
151     private String mPeopleExplanation;
152     private boolean mPreChannelsNotification = true;
153     private Uri mSound;
154     private long[] mVibration;
155     private AudioAttributes mAttributes;
156     private NotificationChannel mChannel;
157     private ArrayList<String> mPeopleOverride;
158     private ArrayList<SnoozeCriterion> mSnoozeCriteria;
159     private boolean mShowBadge;
160     private LogMaker mLogMaker;
161     private Light mLight;
162     private String mGroupLogTag;
163     private String mChannelIdLogTag;
164
165     private final List<Adjustment> mAdjustments;
166     private final NotificationStats mStats;
167     private int mUserSentiment;
168     private boolean mIsInterruptive;
169     private int mNumberOfSmartRepliesAdded;
170     private boolean mHasSeenSmartReplies;
171     /**
172      * Whether this notification (and its channels) should be considered user locked. Used in
173      * conjunction with user sentiment calculation.
174      */
175     private boolean mIsAppImportanceLocked;
176     private ArraySet<Uri> mGrantableUris;
177
178     public NotificationRecord(Context context, StatusBarNotification sbn,
179             NotificationChannel channel) {
180         this.sbn = sbn;
181         mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class)
182                 .getPackageTargetSdkVersion(sbn.getPackageName());
183         mOriginalFlags = sbn.getNotification().flags;
184         mRankingTimeMs = calculateRankingTimeMs(0L);
185         mCreationTimeMs = sbn.getPostTime();
186         mUpdateTimeMs = mCreationTimeMs;
187         mContext = context;
188         stats = new NotificationUsageStats.SingleNotificationStats();
189         mChannel = channel;
190         mPreChannelsNotification = isPreChannelsNotification();
191         mSound = calculateSound();
192         mVibration = calculateVibration();
193         mAttributes = calculateAttributes();
194         mImportance = calculateImportance();
195         mLight = calculateLights();
196         mAdjustments = new ArrayList<>();
197         mStats = new NotificationStats();
198         calculateUserSentiment();
199         calculateGrantableUris();
200     }
201
202     private boolean isPreChannelsNotification() {
203         if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
204             if (mTargetSdkVersion < Build.VERSION_CODES.O) {
205                 return true;
206             }
207         }
208         return false;
209     }
210
211     private Uri calculateSound() {
212         final Notification n = sbn.getNotification();
213
214         // No notification sounds on tv
215         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
216             return null;
217         }
218
219         Uri sound = mChannel.getSound();
220         if (mPreChannelsNotification && (getChannel().getUserLockedFields()
221                 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
222
223             final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0;
224             if (useDefaultSound) {
225                 sound = Settings.System.DEFAULT_NOTIFICATION_URI;
226             } else {
227                 sound = n.sound;
228             }
229         }
230         return sound;
231     }
232
233     private Light calculateLights() {
234         int defaultLightColor = mContext.getResources().getColor(
235                 com.android.internal.R.color.config_defaultNotificationColor);
236         int defaultLightOn = mContext.getResources().getInteger(
237                 com.android.internal.R.integer.config_defaultNotificationLedOn);
238         int defaultLightOff = mContext.getResources().getInteger(
239                 com.android.internal.R.integer.config_defaultNotificationLedOff);
240
241         int channelLightColor = getChannel().getLightColor() != 0 ? getChannel().getLightColor()
242                 : defaultLightColor;
243         Light light = getChannel().shouldShowLights() ? new Light(channelLightColor,
244                 defaultLightOn, defaultLightOff) : null;
245         if (mPreChannelsNotification
246                 && (getChannel().getUserLockedFields()
247                 & NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
248             final Notification notification = sbn.getNotification();
249             if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
250                 light = new Light(notification.ledARGB, notification.ledOnMS,
251                         notification.ledOffMS);
252                 if ((notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
253                     light = new Light(defaultLightColor, defaultLightOn,
254                             defaultLightOff);
255                 }
256             } else {
257                 light = null;
258             }
259         }
260         return light;
261     }
262
263     private long[] calculateVibration() {
264         long[] vibration;
265         final long[] defaultVibration =  NotificationManagerService.getLongArray(
266                 mContext.getResources(),
267                 com.android.internal.R.array.config_defaultNotificationVibePattern,
268                 NotificationManagerService.VIBRATE_PATTERN_MAXLEN,
269                 NotificationManagerService.DEFAULT_VIBRATE_PATTERN);
270         if (getChannel().shouldVibrate()) {
271             vibration = getChannel().getVibrationPattern() == null
272                     ? defaultVibration : getChannel().getVibrationPattern();
273         } else {
274             vibration = null;
275         }
276         if (mPreChannelsNotification
277                 && (getChannel().getUserLockedFields()
278                 & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
279             final Notification notification = sbn.getNotification();
280             final boolean useDefaultVibrate =
281                     (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
282             if (useDefaultVibrate) {
283                 vibration = defaultVibration;
284             } else {
285                 vibration = notification.vibrate;
286             }
287         }
288         return vibration;
289     }
290
291     private AudioAttributes calculateAttributes() {
292         final Notification n = sbn.getNotification();
293         AudioAttributes attributes = getChannel().getAudioAttributes();
294         if (attributes == null) {
295             attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
296         }
297
298         if (mPreChannelsNotification
299                 && (getChannel().getUserLockedFields()
300                 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
301             if (n.audioAttributes != null) {
302                 // prefer audio attributes to stream type
303                 attributes = n.audioAttributes;
304             } else if (n.audioStreamType >= 0
305                     && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
306                 // the stream type is valid, use it
307                 attributes = new AudioAttributes.Builder()
308                         .setInternalLegacyStreamType(n.audioStreamType)
309                         .build();
310             } else if (n.audioStreamType != AudioSystem.STREAM_DEFAULT) {
311                 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
312             }
313         }
314         return attributes;
315     }
316
317     private int calculateImportance() {
318         final Notification n = sbn.getNotification();
319         int importance = getChannel().getImportance();
320         int requestedImportance = IMPORTANCE_DEFAULT;
321
322         // Migrate notification flags to scores
323         if (0 != (n.flags & Notification.FLAG_HIGH_PRIORITY)) {
324             n.priority = Notification.PRIORITY_MAX;
325         }
326
327         n.priority = NotificationManagerService.clamp(n.priority, Notification.PRIORITY_MIN,
328                 Notification.PRIORITY_MAX);
329         switch (n.priority) {
330             case Notification.PRIORITY_MIN:
331                 requestedImportance = IMPORTANCE_MIN;
332                 break;
333             case Notification.PRIORITY_LOW:
334                 requestedImportance = IMPORTANCE_LOW;
335                 break;
336             case Notification.PRIORITY_DEFAULT:
337                 requestedImportance = IMPORTANCE_DEFAULT;
338                 break;
339             case Notification.PRIORITY_HIGH:
340             case Notification.PRIORITY_MAX:
341                 requestedImportance = IMPORTANCE_HIGH;
342                 break;
343         }
344         stats.requestedImportance = requestedImportance;
345         stats.isNoisy = mSound != null || mVibration != null;
346
347         if (mPreChannelsNotification
348                 && (importance == IMPORTANCE_UNSPECIFIED
349                 || (getChannel().getUserLockedFields()
350                 & USER_LOCKED_IMPORTANCE) == 0)) {
351             if (!stats.isNoisy && requestedImportance > IMPORTANCE_LOW) {
352                 requestedImportance = IMPORTANCE_LOW;
353             }
354
355             if (stats.isNoisy) {
356                 if (requestedImportance < IMPORTANCE_DEFAULT) {
357                     requestedImportance = IMPORTANCE_DEFAULT;
358                 }
359             }
360
361             if (n.fullScreenIntent != null) {
362                 requestedImportance = IMPORTANCE_HIGH;
363             }
364             importance = requestedImportance;
365         }
366
367         stats.naturalImportance = importance;
368         return importance;
369     }
370
371     // copy any notes that the ranking system may have made before the update
372     public void copyRankingInformation(NotificationRecord previous) {
373         mContactAffinity = previous.mContactAffinity;
374         mRecentlyIntrusive = previous.mRecentlyIntrusive;
375         mPackagePriority = previous.mPackagePriority;
376         mPackageVisibility = previous.mPackageVisibility;
377         mIntercept = previous.mIntercept;
378         mHidden = previous.mHidden;
379         mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
380         mCreationTimeMs = previous.mCreationTimeMs;
381         mVisibleSinceMs = previous.mVisibleSinceMs;
382         if (previous.sbn.getOverrideGroupKey() != null && !sbn.isAppGroup()) {
383             sbn.setOverrideGroupKey(previous.sbn.getOverrideGroupKey());
384         }
385         // Don't copy importance information or mGlobalSortKey, recompute them.
386     }
387
388     public Notification getNotification() { return sbn.getNotification(); }
389     public int getFlags() { return sbn.getNotification().flags; }
390     public UserHandle getUser() { return sbn.getUser(); }
391     public String getKey() { return sbn.getKey(); }
392     /** @deprecated Use {@link #getUser()} instead. */
393     public int getUserId() { return sbn.getUserId(); }
394     public int getUid() { return sbn.getUid(); }
395
396     void dump(ProtoOutputStream proto, long fieldId, boolean redact, int state) {
397         final long token = proto.start(fieldId);
398
399         proto.write(NotificationRecordProto.KEY, sbn.getKey());
400         proto.write(NotificationRecordProto.STATE, state);
401         if (getChannel() != null) {
402             proto.write(NotificationRecordProto.CHANNEL_ID, getChannel().getId());
403         }
404         proto.write(NotificationRecordProto.CAN_SHOW_LIGHT, getLight() != null);
405         proto.write(NotificationRecordProto.CAN_VIBRATE, getVibration() != null);
406         proto.write(NotificationRecordProto.FLAGS, sbn.getNotification().flags);
407         proto.write(NotificationRecordProto.GROUP_KEY, getGroupKey());
408         proto.write(NotificationRecordProto.IMPORTANCE, getImportance());
409         if (getSound() != null) {
410             proto.write(NotificationRecordProto.SOUND, getSound().toString());
411         }
412         if (getAudioAttributes() != null) {
413             getAudioAttributes().writeToProto(proto, NotificationRecordProto.AUDIO_ATTRIBUTES);
414         }
415
416         proto.end(token);
417     }
418
419     String formatRemoteViews(RemoteViews rv) {
420         if (rv == null) return "null";
421         return String.format("%s/0x%08x (%d bytes): %s",
422             rv.getPackage(), rv.getLayoutId(), rv.estimateMemoryUsage(), rv.toString());
423     }
424
425     void dump(PrintWriter pw, String prefix, Context baseContext, boolean redact) {
426         final Notification notification = sbn.getNotification();
427         final Icon icon = notification.getSmallIcon();
428         String iconStr = String.valueOf(icon);
429         if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
430             iconStr += " / " + idDebugString(baseContext, icon.getResPackage(), icon.getResId());
431         }
432         pw.println(prefix + this);
433         prefix = prefix + "  ";
434         pw.println(prefix + "uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
435         pw.println(prefix + "icon=" + iconStr);
436         pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
437         pw.println(prefix + "pri=" + notification.priority);
438         pw.println(prefix + "key=" + sbn.getKey());
439         pw.println(prefix + "seen=" + mStats.hasSeen());
440         pw.println(prefix + "groupKey=" + getGroupKey());
441         pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
442         pw.println(prefix + "contentIntent=" + notification.contentIntent);
443         pw.println(prefix + "deleteIntent=" + notification.deleteIntent);
444
445         pw.print(prefix + "tickerText=");
446         if (!TextUtils.isEmpty(notification.tickerText)) {
447             final String ticker = notification.tickerText.toString();
448             if (redact) {
449                 // if the string is long enough, we allow ourselves a few bytes for debugging
450                 pw.print(ticker.length() > 16 ? ticker.substring(0,8) : "");
451                 pw.println("...");
452             } else {
453                 pw.println(ticker);
454             }
455         } else {
456             pw.println("null");
457         }
458         pw.println(prefix + "contentView=" + formatRemoteViews(notification.contentView));
459         pw.println(prefix + "bigContentView=" + formatRemoteViews(notification.bigContentView));
460         pw.println(prefix + "headsUpContentView="
461                 + formatRemoteViews(notification.headsUpContentView));
462         pw.print(prefix + String.format("color=0x%08x", notification.color));
463         pw.println(prefix + "timeout="
464                 + TimeUtils.formatForLogging(notification.getTimeoutAfter()));
465         if (notification.actions != null && notification.actions.length > 0) {
466             pw.println(prefix + "actions={");
467             final int N = notification.actions.length;
468             for (int i = 0; i < N; i++) {
469                 final Notification.Action action = notification.actions[i];
470                 if (action != null) {
471                     pw.println(String.format("%s    [%d] \"%s\" -> %s",
472                             prefix,
473                             i,
474                             action.title,
475                             action.actionIntent == null ? "null" : action.actionIntent.toString()
476                     ));
477                 }
478             }
479             pw.println(prefix + "  }");
480         }
481         if (notification.extras != null && notification.extras.size() > 0) {
482             pw.println(prefix + "extras={");
483             for (String key : notification.extras.keySet()) {
484                 pw.print(prefix + "    " + key + "=");
485                 Object val = notification.extras.get(key);
486                 if (val == null) {
487                     pw.println("null");
488                 } else {
489                     pw.print(val.getClass().getSimpleName());
490                     if (redact && (val instanceof CharSequence || val instanceof String)) {
491                         // redact contents from bugreports
492                     } else if (val instanceof Bitmap) {
493                         pw.print(String.format(" (%dx%d)",
494                                 ((Bitmap) val).getWidth(),
495                                 ((Bitmap) val).getHeight()));
496                     } else if (val.getClass().isArray()) {
497                         final int N = Array.getLength(val);
498                         pw.print(" (" + N + ")");
499                         if (!redact) {
500                             for (int j = 0; j < N; j++) {
501                                 pw.println();
502                                 pw.print(String.format("%s      [%d] %s",
503                                         prefix, j, String.valueOf(Array.get(val, j))));
504                             }
505                         }
506                     } else {
507                         pw.print(" (" + String.valueOf(val) + ")");
508                     }
509                     pw.println();
510                 }
511             }
512             pw.println(prefix + "}");
513         }
514         pw.println(prefix + "stats=" + stats.toString());
515         pw.println(prefix + "mContactAffinity=" + mContactAffinity);
516         pw.println(prefix + "mRecentlyIntrusive=" + mRecentlyIntrusive);
517         pw.println(prefix + "mPackagePriority=" + mPackagePriority);
518         pw.println(prefix + "mPackageVisibility=" + mPackageVisibility);
519         pw.println(prefix + "mUserImportance="
520                 + NotificationListenerService.Ranking.importanceToString(mUserImportance));
521         pw.println(prefix + "mImportance="
522                 + NotificationListenerService.Ranking.importanceToString(mImportance));
523         pw.println(prefix + "mImportanceExplanation=" + mImportanceExplanation);
524         pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked);
525         pw.println(prefix + "mIntercept=" + mIntercept);
526         pw.println(prefix + "mHidden==" + mHidden);
527         pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
528         pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
529         pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
530         pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
531         pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
532         pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
533         if (mPreChannelsNotification) {
534             pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
535                     notification.defaults, notification.flags));
536             pw.println(prefix + "n.sound=" + notification.sound);
537             pw.println(prefix + "n.audioStreamType=" + notification.audioStreamType);
538             pw.println(prefix + "n.audioAttributes=" + notification.audioAttributes);
539             pw.println(prefix + String.format("  led=0x%08x onMs=%d offMs=%d",
540                     notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
541             pw.println(prefix + "vibrate=" + Arrays.toString(notification.vibrate));
542         }
543         pw.println(prefix + "mSound= " + mSound);
544         pw.println(prefix + "mVibration= " + mVibration);
545         pw.println(prefix + "mAttributes= " + mAttributes);
546         pw.println(prefix + "mLight= " + mLight);
547         pw.println(prefix + "mShowBadge=" + mShowBadge);
548         pw.println(prefix + "mColorized=" + notification.isColorized());
549         pw.println(prefix + "mIsInterruptive=" + mIsInterruptive);
550         pw.println(prefix + "effectiveNotificationChannel=" + getChannel());
551         if (getPeopleOverride() != null) {
552             pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride()));
553         }
554         if (getSnoozeCriteria() != null) {
555             pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
556         }
557         pw.println(prefix + "mAdjustments=" + mAdjustments);
558     }
559
560
561     static String idDebugString(Context baseContext, String packageName, int id) {
562         Context c;
563
564         if (packageName != null) {
565             try {
566                 c = baseContext.createPackageContext(packageName, 0);
567             } catch (NameNotFoundException e) {
568                 c = baseContext;
569             }
570         } else {
571             c = baseContext;
572         }
573
574         Resources r = c.getResources();
575         try {
576             return r.getResourceName(id);
577         } catch (Resources.NotFoundException e) {
578             return "<name unknown>";
579         }
580     }
581
582     @Override
583     public final String toString() {
584         return String.format(
585                 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" +
586                         "appImportanceLocked=%s: %s)",
587                 System.identityHashCode(this),
588                 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
589                 this.sbn.getTag(), this.mImportance, this.sbn.getKey(),
590                 mIsAppImportanceLocked, this.sbn.getNotification());
591     }
592
593     public void addAdjustment(Adjustment adjustment) {
594         synchronized (mAdjustments) {
595             mAdjustments.add(adjustment);
596         }
597     }
598
599     public void applyAdjustments() {
600         synchronized (mAdjustments) {
601             for (Adjustment adjustment: mAdjustments) {
602                 Bundle signals = adjustment.getSignals();
603                 if (signals.containsKey(Adjustment.KEY_PEOPLE)) {
604                     final ArrayList<String> people =
605                             adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
606                     setPeopleOverride(people);
607                 }
608                 if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
609                     final ArrayList<SnoozeCriterion> snoozeCriterionList =
610                             adjustment.getSignals().getParcelableArrayList(
611                                     Adjustment.KEY_SNOOZE_CRITERIA);
612                     setSnoozeCriteria(snoozeCriterionList);
613                 }
614                 if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
615                     final String groupOverrideKey =
616                             adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
617                     setOverrideGroupKey(groupOverrideKey);
618                 }
619                 if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
620                     // Only allow user sentiment update from assistant if user hasn't already
621                     // expressed a preference for this channel
622                     if (!mIsAppImportanceLocked
623                             && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) {
624                         setUserSentiment(adjustment.getSignals().getInt(
625                                 Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
626                     }
627                 }
628             }
629         }
630     }
631
632     public void setIsAppImportanceLocked(boolean isAppImportanceLocked) {
633         mIsAppImportanceLocked = isAppImportanceLocked;
634         calculateUserSentiment();
635     }
636
637     public void setContactAffinity(float contactAffinity) {
638         mContactAffinity = contactAffinity;
639         if (mImportance < IMPORTANCE_DEFAULT &&
640                 mContactAffinity > ValidateNotificationPeople.VALID_CONTACT) {
641             setImportance(IMPORTANCE_DEFAULT, getPeopleExplanation());
642         }
643     }
644
645     public float getContactAffinity() {
646         return mContactAffinity;
647     }
648
649     public void setRecentlyIntrusive(boolean recentlyIntrusive) {
650         mRecentlyIntrusive = recentlyIntrusive;
651         if (recentlyIntrusive) {
652             mLastIntrusive = System.currentTimeMillis();
653         }
654     }
655
656     public boolean isRecentlyIntrusive() {
657         return mRecentlyIntrusive;
658     }
659
660     public long getLastIntrusive() {
661         return mLastIntrusive;
662     }
663
664     public void setPackagePriority(int packagePriority) {
665         mPackagePriority = packagePriority;
666     }
667
668     public int getPackagePriority() {
669         return mPackagePriority;
670     }
671
672     public void setPackageVisibilityOverride(int packageVisibility) {
673         mPackageVisibility = packageVisibility;
674     }
675
676     public int getPackageVisibilityOverride() {
677         return mPackageVisibility;
678     }
679
680     public void setUserImportance(int importance) {
681         mUserImportance = importance;
682         applyUserImportance();
683     }
684
685     private String getUserExplanation() {
686         if (mUserExplanation == null) {
687             mUserExplanation = mContext.getResources().getString(
688                     com.android.internal.R.string.importance_from_user);
689         }
690         return mUserExplanation;
691     }
692
693     private String getPeopleExplanation() {
694         if (mPeopleExplanation == null) {
695             mPeopleExplanation = mContext.getResources().getString(
696                     com.android.internal.R.string.importance_from_person);
697         }
698         return mPeopleExplanation;
699     }
700
701     private void applyUserImportance() {
702         if (mUserImportance != IMPORTANCE_UNSPECIFIED) {
703             mImportance = mUserImportance;
704             mImportanceExplanation = getUserExplanation();
705         }
706     }
707
708     public int getUserImportance() {
709         return mUserImportance;
710     }
711
712     public void setImportance(int importance, CharSequence explanation) {
713         if (importance != IMPORTANCE_UNSPECIFIED) {
714             mImportance = importance;
715             mImportanceExplanation = explanation;
716         }
717         applyUserImportance();
718     }
719
720     public int getImportance() {
721         return mImportance;
722     }
723
724     public CharSequence getImportanceExplanation() {
725         return mImportanceExplanation;
726     }
727
728     public boolean setIntercepted(boolean intercept) {
729         mIntercept = intercept;
730         return mIntercept;
731     }
732
733     public boolean isIntercepted() {
734         return mIntercept;
735     }
736
737     public void setHidden(boolean hidden) {
738         mHidden = hidden;
739     }
740
741     public boolean isHidden() {
742         return mHidden;
743     }
744
745
746     public void setSuppressedVisualEffects(int effects) {
747         mSuppressedVisualEffects = effects;
748     }
749
750     public int getSuppressedVisualEffects() {
751         return mSuppressedVisualEffects;
752     }
753
754     public boolean isCategory(String category) {
755         return Objects.equals(getNotification().category, category);
756     }
757
758     public boolean isAudioAttributesUsage(int usage) {
759         return mAttributes != null && mAttributes.getUsage() == usage;
760     }
761
762     /**
763      * Returns the timestamp to use for time-based sorting in the ranker.
764      */
765     public long getRankingTimeMs() {
766         return mRankingTimeMs;
767     }
768
769     /**
770      * @param now this current time in milliseconds.
771      * @returns the number of milliseconds since the most recent update, or the post time if none.
772      */
773     public int getFreshnessMs(long now) {
774         return (int) (now - mUpdateTimeMs);
775     }
776
777     /**
778      * @param now this current time in milliseconds.
779      * @returns the number of milliseconds since the the first post, ignoring updates.
780      */
781     public int getLifespanMs(long now) {
782         return (int) (now - mCreationTimeMs);
783     }
784
785     /**
786      * @param now this current time in milliseconds.
787      * @returns the number of milliseconds since the most recent visibility event, or 0 if never.
788      */
789     public int getExposureMs(long now) {
790         return mVisibleSinceMs == 0 ? 0 : (int) (now - mVisibleSinceMs);
791     }
792
793     /**
794      * Set the visibility of the notification.
795      */
796     public void setVisibility(boolean visible, int rank, int count) {
797         final long now = System.currentTimeMillis();
798         mVisibleSinceMs = visible ? now : mVisibleSinceMs;
799         stats.onVisibilityChanged(visible);
800         MetricsLogger.action(getLogMaker(now)
801                 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
802                 .setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
803                 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank)
804                 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, count));
805         if (visible) {
806             setSeen();
807             MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now));
808         }
809         EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0,
810                 getLifespanMs(now),
811                 getFreshnessMs(now),
812                 0, // exposure time
813                 rank);
814     }
815
816     /**
817      * @param previousRankingTimeMs for updated notifications, {@link #getRankingTimeMs()}
818      *     of the previous notification record, 0 otherwise
819      */
820     private long calculateRankingTimeMs(long previousRankingTimeMs) {
821         Notification n = getNotification();
822         // Take developer provided 'when', unless it's in the future.
823         if (n.when != 0 && n.when <= sbn.getPostTime()) {
824             return n.when;
825         }
826         // If we've ranked a previous instance with a timestamp, inherit it. This case is
827         // important in order to have ranking stability for updating notifications.
828         if (previousRankingTimeMs > 0) {
829             return previousRankingTimeMs;
830         }
831         return sbn.getPostTime();
832     }
833
834     public void setGlobalSortKey(String globalSortKey) {
835         mGlobalSortKey = globalSortKey;
836     }
837
838     public String getGlobalSortKey() {
839         return mGlobalSortKey;
840     }
841
842     /** Check if any of the listeners have marked this notification as seen by the user. */
843     public boolean isSeen() {
844         return mStats.hasSeen();
845     }
846
847     /** Mark the notification as seen by the user. */
848     public void setSeen() {
849         mStats.setSeen();
850     }
851
852     public void setAuthoritativeRank(int authoritativeRank) {
853         mAuthoritativeRank = authoritativeRank;
854     }
855
856     public int getAuthoritativeRank() {
857         return mAuthoritativeRank;
858     }
859
860     public String getGroupKey() {
861         return sbn.getGroupKey();
862     }
863
864     public void setOverrideGroupKey(String overrideGroupKey) {
865         sbn.setOverrideGroupKey(overrideGroupKey);
866         mGroupLogTag = null;
867     }
868
869     private String getGroupLogTag() {
870         if (mGroupLogTag == null) {
871             mGroupLogTag = shortenTag(sbn.getGroup());
872         }
873         return mGroupLogTag;
874     }
875
876     private String getChannelIdLogTag() {
877         if (mChannelIdLogTag == null) {
878             mChannelIdLogTag = shortenTag(mChannel.getId());
879         }
880         return mChannelIdLogTag;
881     }
882
883     private String shortenTag(String longTag) {
884         if (longTag == null) {
885             return null;
886         }
887         if (longTag.length() < MAX_LOGTAG_LENGTH) {
888             return longTag;
889         } else {
890             return longTag.substring(0, MAX_LOGTAG_LENGTH - 8) + "-" +
891                     Integer.toHexString(longTag.hashCode());
892         }
893     }
894
895     public NotificationChannel getChannel() {
896         return mChannel;
897     }
898
899     /**
900      * @see RankingHelper#getIsAppImportanceLocked(String, int)
901      */
902     public boolean getIsAppImportanceLocked() {
903         return mIsAppImportanceLocked;
904     }
905
906     protected void updateNotificationChannel(NotificationChannel channel) {
907         if (channel != null) {
908             mChannel = channel;
909             calculateImportance();
910             calculateUserSentiment();
911         }
912     }
913
914     public void setShowBadge(boolean showBadge) {
915         mShowBadge = showBadge;
916     }
917
918     public boolean canShowBadge() {
919         return mShowBadge;
920     }
921
922     public Light getLight() {
923         return mLight;
924     }
925
926     public Uri getSound() {
927         return mSound;
928     }
929
930     public long[] getVibration() {
931         return mVibration;
932     }
933
934     public AudioAttributes getAudioAttributes() {
935         return mAttributes;
936     }
937
938     public ArrayList<String> getPeopleOverride() {
939         return mPeopleOverride;
940     }
941
942     public void setInterruptive(boolean interruptive) {
943         mIsInterruptive = interruptive;
944     }
945
946     public boolean isInterruptive() {
947         return mIsInterruptive;
948     }
949
950     protected void setPeopleOverride(ArrayList<String> people) {
951         mPeopleOverride = people;
952     }
953
954     public ArrayList<SnoozeCriterion> getSnoozeCriteria() {
955         return mSnoozeCriteria;
956     }
957
958     protected void setSnoozeCriteria(ArrayList<SnoozeCriterion> snoozeCriteria) {
959         mSnoozeCriteria = snoozeCriteria;
960     }
961
962     private void calculateUserSentiment() {
963         if ((getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0
964                 || mIsAppImportanceLocked) {
965             mUserSentiment = USER_SENTIMENT_POSITIVE;
966         }
967     }
968
969     private void setUserSentiment(int userSentiment) {
970         mUserSentiment = userSentiment;
971     }
972
973     public int getUserSentiment() {
974         return mUserSentiment;
975     }
976
977     public NotificationStats getStats() {
978         return mStats;
979     }
980
981     public void recordExpanded() {
982         mStats.setExpanded();
983     }
984
985     public void recordDirectReplied() {
986         mStats.setDirectReplied();
987     }
988
989     public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) {
990         mStats.setDismissalSurface(surface);
991     }
992
993     public void recordSnoozed() {
994         mStats.setSnoozed();
995     }
996
997     public void recordViewedSettings() {
998         mStats.setViewedSettings();
999     }
1000
1001     public void setNumSmartRepliesAdded(int noReplies) {
1002         mNumberOfSmartRepliesAdded = noReplies;
1003     }
1004
1005     public int getNumSmartRepliesAdded() {
1006         return mNumberOfSmartRepliesAdded;
1007     }
1008
1009     public boolean hasSeenSmartReplies() {
1010         return mHasSeenSmartReplies;
1011     }
1012
1013     public void setSeenSmartReplies(boolean hasSeenSmartReplies) {
1014         mHasSeenSmartReplies = hasSeenSmartReplies;
1015     }
1016
1017     /**
1018      * @return all {@link Uri} that should have permission granted to whoever
1019      *         will be rendering it. This list has already been vetted to only
1020      *         include {@link Uri} that the enqueuing app can grant.
1021      */
1022     public @Nullable ArraySet<Uri> getGrantableUris() {
1023         return mGrantableUris;
1024     }
1025
1026     /**
1027      * Collect all {@link Uri} that should have permission granted to whoever
1028      * will be rendering it.
1029      */
1030     private void calculateGrantableUris() {
1031         final Notification notification = getNotification();
1032
1033         noteGrantableUri(notification.sound);
1034         if (notification.getChannelId() != null) {
1035             NotificationChannel channel = getChannel();
1036             if (channel != null) {
1037                 noteGrantableUri(channel.getSound());
1038             }
1039         }
1040
1041         final Bundle extras = notification.extras;
1042         if (extras != null) {
1043             noteGrantableUri(extras.getParcelable(EXTRA_AUDIO_CONTENTS_URI));
1044             noteGrantableUri(extras.getParcelable(EXTRA_BACKGROUND_IMAGE_URI));
1045         }
1046
1047         if (MessagingStyle.class.equals(notification.getNotificationStyle()) && extras != null) {
1048             final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
1049             if (!ArrayUtils.isEmpty(messages)) {
1050                 for (MessagingStyle.Message message : MessagingStyle.Message
1051                         .getMessagesFromBundleArray(messages)) {
1052                     noteGrantableUri(message.getDataUri());
1053                 }
1054             }
1055
1056             final Parcelable[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
1057             if (!ArrayUtils.isEmpty(historic)) {
1058                 for (MessagingStyle.Message message : MessagingStyle.Message
1059                         .getMessagesFromBundleArray(historic)) {
1060                     noteGrantableUri(message.getDataUri());
1061                 }
1062             }
1063         }
1064     }
1065
1066     /**
1067      * Note the presence of a {@link Uri} that should have permission granted to
1068      * whoever will be rendering it.
1069      * <p>
1070      * If the enqueuing app has the ability to grant access, it will be added to
1071      * {@link #mGrantableUris}. Otherwise, this will either log or throw
1072      * {@link SecurityException} depending on target SDK of enqueuing app.
1073      */
1074     private void noteGrantableUri(Uri uri) {
1075         if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
1076
1077         // We can't grant Uri permissions from system
1078         final int sourceUid = sbn.getUid();
1079         if (sourceUid == android.os.Process.SYSTEM_UID) return;
1080
1081         final IActivityManager am = ActivityManager.getService();
1082         final long ident = Binder.clearCallingIdentity();
1083         try {
1084             // This will throw SecurityException if caller can't grant
1085             am.checkGrantUriPermission(sourceUid, null,
1086                     ContentProvider.getUriWithoutUserId(uri),
1087                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
1088                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
1089
1090             if (mGrantableUris == null) {
1091                 mGrantableUris = new ArraySet<>();
1092             }
1093             mGrantableUris.add(uri);
1094         } catch (RemoteException ignored) {
1095             // Ignored because we're in same process
1096         } catch (SecurityException e) {
1097             if (mTargetSdkVersion >= Build.VERSION_CODES.P) {
1098                 throw e;
1099             } else {
1100                 Log.w(TAG, "Ignoring " + uri + " from " + sourceUid + ": " + e.getMessage());
1101             }
1102         } finally {
1103             Binder.restoreCallingIdentity(ident);
1104         }
1105     }
1106
1107     public LogMaker getLogMaker(long now) {
1108         if (mLogMaker == null) {
1109             // initialize fields that only change on update (so a new record)
1110             mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN)
1111                     .setPackageName(sbn.getPackageName())
1112                     .addTaggedData(MetricsEvent.NOTIFICATION_ID, sbn.getId())
1113                     .addTaggedData(MetricsEvent.NOTIFICATION_TAG, sbn.getTag())
1114                     .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID, getChannelIdLogTag());
1115         }
1116         // reset fields that can change between updates, or are used by multiple logs
1117         return mLogMaker
1118                 .clearCategory()
1119                 .clearType()
1120                 .clearSubtype()
1121                 .clearTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX)
1122                 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, mImportance)
1123                 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID, getGroupLogTag())
1124                 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_SUMMARY,
1125                         sbn.getNotification().isGroupSummary() ? 1 : 0)
1126                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
1127                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
1128                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now));
1129     }
1130
1131     public LogMaker getLogMaker() {
1132         return getLogMaker(System.currentTimeMillis());
1133     }
1134
1135     @VisibleForTesting
1136     static final class Light {
1137         public final int color;
1138         public final int onMs;
1139         public final int offMs;
1140
1141         public Light(int color, int onMs, int offMs) {
1142             this.color = color;
1143             this.onMs = onMs;
1144             this.offMs = offMs;
1145         }
1146
1147         @Override
1148         public boolean equals(Object o) {
1149             if (this == o) return true;
1150             if (o == null || getClass() != o.getClass()) return false;
1151
1152             Light light = (Light) o;
1153
1154             if (color != light.color) return false;
1155             if (onMs != light.onMs) return false;
1156             return offMs == light.offMs;
1157
1158         }
1159
1160         @Override
1161         public int hashCode() {
1162             int result = color;
1163             result = 31 * result + onMs;
1164             result = 31 * result + offMs;
1165             return result;
1166         }
1167
1168         @Override
1169         public String toString() {
1170             return "Light{" +
1171                     "color=" + color +
1172                     ", onMs=" + onMs +
1173                     ", offMs=" + offMs +
1174                     '}';
1175         }
1176     }
1177 }