blob: 75b9f1311b752c68156e9c9ed39f7d034262df86 [file] [log] [blame]
Chris Wren333a61c2014-05-28 16:40:57 -04001/*
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 */
16package com.android.server.notification;
17
Julia Reynoldsc65656a2018-02-12 09:55:14 -050018import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
Julia Reynolds85769912016-10-25 09:08:57 -040019import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
20import static android.app.NotificationManager.IMPORTANCE_HIGH;
21import static android.app.NotificationManager.IMPORTANCE_LOW;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -060022import static android.app.NotificationManager.IMPORTANCE_MIN;
23import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
Julia Reynolds503ed942017-10-04 16:04:56 -040024import static android.service.notification.NotificationListenerService.Ranking
25 .USER_SENTIMENT_NEUTRAL;
Julia Reynoldsc65656a2018-02-12 09:55:14 -050026import static android.service.notification.NotificationListenerService.Ranking
27 .USER_SENTIMENT_POSITIVE;
Chris Wrenbdf33762015-12-04 15:50:51 -050028
Jeff Sharkey6a97cc32018-04-17 12:16:20 -060029import android.annotation.Nullable;
30import android.app.ActivityManager;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -060031import android.app.IActivityManager;
Chris Wren333a61c2014-05-28 16:40:57 -040032import android.app.Notification;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -040033import android.app.NotificationChannel;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -060034import android.content.ContentProvider;
35import android.content.ContentResolver;
Chris Wren333a61c2014-05-28 16:40:57 -040036import android.content.Context;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -060037import android.content.Intent;
Tony Mantlerab55f0f2017-06-16 10:50:00 -070038import android.content.pm.PackageManager;
Chris Wren333a61c2014-05-28 16:40:57 -040039import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -060040import android.content.pm.PackageManagerInternal;
Chris Wren333a61c2014-05-28 16:40:57 -040041import android.content.res.Resources;
42import android.graphics.Bitmap;
Dan Sandlerd63f9322015-05-06 15:18:49 -040043import android.graphics.drawable.Icon;
John Spurlockbfa5dc42014-07-28 23:30:45 -040044import android.media.AudioAttributes;
Julia Reynolds0c299d42016-11-15 14:37:04 -050045import android.media.AudioSystem;
Chris Wren9eb5e102017-01-26 13:15:06 -050046import android.metrics.LogMaker;
Julia Reynolds0c299d42016-11-15 14:37:04 -050047import android.net.Uri;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -060048import android.os.Binder;
Julia Reynolds0c299d42016-11-15 14:37:04 -050049import android.os.Build;
Julia Reynoldseb3dca72017-07-11 10:39:58 -040050import android.os.Bundle;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -060051import android.os.IBinder;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -060052import android.os.RemoteException;
Chris Wrenda4bd202014-09-04 15:53:52 -040053import android.os.UserHandle;
Julia Reynolds0c299d42016-11-15 14:37:04 -050054import android.provider.Settings;
Julia Reynoldseb3dca72017-07-11 10:39:58 -040055import android.service.notification.Adjustment;
Julia Reynolds5d25ee72015-11-20 15:38:20 -050056import android.service.notification.NotificationListenerService;
Julia Reynoldsc9842c12017-02-07 12:46:41 -050057import android.service.notification.NotificationRecordProto;
Julia Reynolds503ed942017-10-04 16:04:56 -040058import android.service.notification.NotificationStats;
Julia Reynolds22f02b32016-12-01 15:05:13 -050059import android.service.notification.SnoozeCriterion;
Chris Wren333a61c2014-05-28 16:40:57 -040060import android.service.notification.StatusBarNotification;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -050061import android.text.TextUtils;
Julia Reynoldse0d711f2017-09-01 08:50:47 -040062import android.util.ArraySet;
Julia Reynoldsf0f629f2016-02-25 09:34:04 -050063import android.util.Log;
Julia Reynolds2a128742016-11-28 14:29:25 -050064import android.util.TimeUtils;
Julia Reynoldsc9842c12017-02-07 12:46:41 -050065import android.util.proto.ProtoOutputStream;
Dan Sandler0a2308e2017-05-30 19:50:42 -040066import android.widget.RemoteViews;
John Spurlockbfa5dc42014-07-28 23:30:45 -040067
Chris Wren1031c972014-07-23 13:11:45 +000068import com.android.internal.annotations.VisibleForTesting;
Chris Wren9eb5e102017-01-26 13:15:06 -050069import com.android.internal.logging.MetricsLogger;
70import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Chris Wren6650e572015-05-15 17:19:25 -040071import com.android.server.EventLogTags;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -060072import com.android.server.LocalServices;
Chris Wren333a61c2014-05-28 16:40:57 -040073
74import java.io.PrintWriter;
75import java.lang.reflect.Array;
Julia Reynolds22f02b32016-12-01 15:05:13 -050076import java.util.ArrayList;
Chris Wren333a61c2014-05-28 16:40:57 -040077import java.util.Arrays;
Julia Reynoldseb3dca72017-07-11 10:39:58 -040078import java.util.List;
John Spurlock312d1d02014-07-08 10:24:57 -040079import java.util.Objects;
Chris Wren333a61c2014-05-28 16:40:57 -040080
81/**
82 * Holds data about notifications that should not be shared with the
83 * {@link android.service.notification.NotificationListenerService}s.
84 *
85 * <p>These objects should not be mutated unless the code is synchronized
Julia Reynolds88860ce2017-06-01 16:55:49 -040086 * on {@link NotificationManagerService#mNotificationLock}, and any
Chris Wren333a61c2014-05-28 16:40:57 -040087 * modification should be followed by a sorting of that list.</p>
88 *
89 * <p>Is sortable by {@link NotificationComparator}.</p>
90 *
91 * {@hide}
92 */
93public final class NotificationRecord {
Julia Reynoldsf0f629f2016-02-25 09:34:04 -050094 static final String TAG = "NotificationRecord";
95 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Chris Wrenb3921792017-06-01 13:34:46 -040096 private static final int MAX_LOGTAG_LENGTH = 35;
Chris Wren333a61c2014-05-28 16:40:57 -040097 final StatusBarNotification sbn;
Julia Reynolds89945c52018-06-13 10:45:21 -040098 IActivityManager mAm;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -060099 final int mTargetSdkVersion;
Christoph Studer365e4c32014-09-18 20:35:36 +0200100 final int mOriginalFlags;
Chris Wrenbdf33762015-12-04 15:50:51 -0500101 private final Context mContext;
Christoph Studer365e4c32014-09-18 20:35:36 +0200102
Chris Wren333a61c2014-05-28 16:40:57 -0400103 NotificationUsageStats.SingleNotificationStats stats;
104 boolean isCanceled;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -0600105 IBinder permissionOwner;
Chris Wren333a61c2014-05-28 16:40:57 -0400106
107 // These members are used by NotificationSignalExtractors
108 // to communicate with the ranking module.
109 private float mContactAffinity;
110 private boolean mRecentlyIntrusive;
Julia Reynolds309d1c82017-05-03 16:00:20 -0400111 private long mLastIntrusive;
Chris Wren333a61c2014-05-28 16:40:57 -0400112
113 // is this notification currently being intercepted by Zen Mode?
114 private boolean mIntercept;
Chris Wren333a61c2014-05-28 16:40:57 -0400115
Beverly5a20a5e2018-03-06 15:02:44 -0500116 // is this notification hidden since the app pkg is suspended?
117 private boolean mHidden;
118
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200119 // The timestamp used for ranking.
120 private long mRankingTimeMs;
121
Chris Wren640e3872015-04-21 13:23:18 -0400122 // The first post time, stable across updates.
123 private long mCreationTimeMs;
124
Chris Wren6650e572015-05-15 17:19:25 -0400125 // The most recent visibility event.
126 private long mVisibleSinceMs;
127
128 // The most recent update time, or the creation time if no updates.
129 private long mUpdateTimeMs;
130
Julia Reynoldsfdaa6492018-06-18 09:29:19 -0400131 // The most recent interruption time, or the creation time if no updates. Differs from the
132 // above value because updates are filtered based on whether they actually interrupted the
133 // user
134 private long mInterruptionTimeMs;
135
Chris Wrena3446562014-06-03 18:11:47 -0400136 // Is this record an update of an old record?
137 public boolean isUpdate;
Chris Wren54bbef42014-07-09 18:37:56 -0400138 private int mPackagePriority;
Chris Wrena3446562014-06-03 18:11:47 -0400139
Chris Wren1031c972014-07-23 13:11:45 +0000140 private int mAuthoritativeRank;
Christoph Studercd4adf82014-08-19 17:50:49 +0200141 private String mGlobalSortKey;
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400142 private int mPackageVisibility;
Julia Reynoldsef37f282016-02-12 09:11:27 -0500143 private int mUserImportance = IMPORTANCE_UNSPECIFIED;
Chris Wren47633422016-01-22 09:56:59 -0500144 private int mImportance = IMPORTANCE_UNSPECIFIED;
Chris Wrenbdf33762015-12-04 15:50:51 -0500145 private CharSequence mImportanceExplanation = null;
Chris Wren1031c972014-07-23 13:11:45 +0000146
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500147 private int mSuppressedVisualEffects = 0;
Julia Reynoldsef37f282016-02-12 09:11:27 -0500148 private String mUserExplanation;
Chris Wrenbdf33762015-12-04 15:50:51 -0500149 private String mPeopleExplanation;
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000150 private boolean mPreChannelsNotification = true;
Julia Reynolds0c299d42016-11-15 14:37:04 -0500151 private Uri mSound;
152 private long[] mVibration;
153 private AudioAttributes mAttributes;
Julia Reynolds924eed12017-01-19 09:52:07 -0500154 private NotificationChannel mChannel;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500155 private ArrayList<String> mPeopleOverride;
156 private ArrayList<SnoozeCriterion> mSnoozeCriteria;
Julia Reynolds924eed12017-01-19 09:52:07 -0500157 private boolean mShowBadge;
Chris Wren9eb5e102017-01-26 13:15:06 -0500158 private LogMaker mLogMaker;
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500159 private Light mLight;
Chris Wrenb3921792017-06-01 13:34:46 -0400160 private String mGroupLogTag;
161 private String mChannelIdLogTag;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500162
Julia Reynoldseb3dca72017-07-11 10:39:58 -0400163 private final List<Adjustment> mAdjustments;
Julia Reynolds503ed942017-10-04 16:04:56 -0400164 private final NotificationStats mStats;
165 private int mUserSentiment;
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500166 private boolean mIsInterruptive;
Julia Reynoldsb3c68ff2018-05-22 14:58:39 -0400167 private boolean mTextChanged;
168 private boolean mRecordedInterruption;
Kenny Guy23991102018-04-05 21:18:38 +0100169 private int mNumberOfSmartRepliesAdded;
170 private boolean mHasSeenSmartReplies;
Rohan Shah590e1b22018-04-10 23:48:47 -0400171 /**
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;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -0600176 private ArraySet<Uri> mGrantableUris;
Julia Reynoldseb3dca72017-07-11 10:39:58 -0400177
Julia Reynolds924eed12017-01-19 09:52:07 -0500178 public NotificationRecord(Context context, StatusBarNotification sbn,
Rohan Shah590e1b22018-04-10 23:48:47 -0400179 NotificationChannel channel) {
Chris Wren333a61c2014-05-28 16:40:57 -0400180 this.sbn = sbn;
Jeff Sharkey6a97cc32018-04-17 12:16:20 -0600181 mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class)
182 .getPackageTargetSdkVersion(sbn.getPackageName());
Julia Reynolds89945c52018-06-13 10:45:21 -0400183 mAm = ActivityManager.getService();
Christoph Studer365e4c32014-09-18 20:35:36 +0200184 mOriginalFlags = sbn.getNotification().flags;
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200185 mRankingTimeMs = calculateRankingTimeMs(0L);
Chris Wren640e3872015-04-21 13:23:18 -0400186 mCreationTimeMs = sbn.getPostTime();
Chris Wren6650e572015-05-15 17:19:25 -0400187 mUpdateTimeMs = mCreationTimeMs;
Julia Reynoldsfdaa6492018-06-18 09:29:19 -0400188 mInterruptionTimeMs = mCreationTimeMs;
Chris Wrenbdf33762015-12-04 15:50:51 -0500189 mContext = context;
Chris Wrencdee8cd2016-01-25 17:10:30 -0500190 stats = new NotificationUsageStats.SingleNotificationStats();
Julia Reynolds924eed12017-01-19 09:52:07 -0500191 mChannel = channel;
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000192 mPreChannelsNotification = isPreChannelsNotification();
Julia Reynolds0c299d42016-11-15 14:37:04 -0500193 mSound = calculateSound();
194 mVibration = calculateVibration();
195 mAttributes = calculateAttributes();
196 mImportance = calculateImportance();
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500197 mLight = calculateLights();
Julia Reynoldseb3dca72017-07-11 10:39:58 -0400198 mAdjustments = new ArrayList<>();
Julia Reynolds503ed942017-10-04 16:04:56 -0400199 mStats = new NotificationStats();
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500200 calculateUserSentiment();
Jeff Sharkey6a97cc32018-04-17 12:16:20 -0600201 calculateGrantableUris();
Chris Wrenbdf33762015-12-04 15:50:51 -0500202 }
203
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000204 private boolean isPreChannelsNotification() {
Jeff Sharkey6a97cc32018-04-17 12:16:20 -0600205 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
206 if (mTargetSdkVersion < Build.VERSION_CODES.O) {
207 return true;
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000208 }
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000209 }
210 return false;
211 }
212
Julia Reynolds0c299d42016-11-15 14:37:04 -0500213 private Uri calculateSound() {
Chris Wrenbdf33762015-12-04 15:50:51 -0500214 final Notification n = sbn.getNotification();
Julia Reynolds0c299d42016-11-15 14:37:04 -0500215
Tony Mantlerab55f0f2017-06-16 10:50:00 -0700216 // No notification sounds on tv
217 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
218 return null;
219 }
220
Julia Reynolds924eed12017-01-19 09:52:07 -0500221 Uri sound = mChannel.getSound();
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000222 if (mPreChannelsNotification && (getChannel().getUserLockedFields()
Julia Reynolds0c299d42016-11-15 14:37:04 -0500223 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
224
225 final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0;
226 if (useDefaultSound) {
227 sound = Settings.System.DEFAULT_NOTIFICATION_URI;
Julia Reynoldsb9e712e2017-04-17 10:31:03 -0400228 } else {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500229 sound = n.sound;
230 }
231 }
232 return sound;
233 }
234
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500235 private Light calculateLights() {
236 int defaultLightColor = mContext.getResources().getColor(
237 com.android.internal.R.color.config_defaultNotificationColor);
238 int defaultLightOn = mContext.getResources().getInteger(
239 com.android.internal.R.integer.config_defaultNotificationLedOn);
240 int defaultLightOff = mContext.getResources().getInteger(
241 com.android.internal.R.integer.config_defaultNotificationLedOff);
242
Julia Reynolds529e3322017-02-06 08:33:01 -0500243 int channelLightColor = getChannel().getLightColor() != 0 ? getChannel().getLightColor()
244 : defaultLightColor;
245 Light light = getChannel().shouldShowLights() ? new Light(channelLightColor,
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500246 defaultLightOn, defaultLightOff) : null;
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000247 if (mPreChannelsNotification
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500248 && (getChannel().getUserLockedFields()
249 & NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
250 final Notification notification = sbn.getNotification();
251 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
252 light = new Light(notification.ledARGB, notification.ledOnMS,
253 notification.ledOffMS);
254 if ((notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
255 light = new Light(defaultLightColor, defaultLightOn,
256 defaultLightOff);
257 }
258 } else {
259 light = null;
260 }
261 }
262 return light;
263 }
264
Julia Reynolds0c299d42016-11-15 14:37:04 -0500265 private long[] calculateVibration() {
266 long[] vibration;
267 final long[] defaultVibration = NotificationManagerService.getLongArray(
268 mContext.getResources(),
269 com.android.internal.R.array.config_defaultNotificationVibePattern,
270 NotificationManagerService.VIBRATE_PATTERN_MAXLEN,
271 NotificationManagerService.DEFAULT_VIBRATE_PATTERN);
272 if (getChannel().shouldVibrate()) {
Julia Reynoldsf57de462016-11-23 11:31:46 -0500273 vibration = getChannel().getVibrationPattern() == null
274 ? defaultVibration : getChannel().getVibrationPattern();
Julia Reynolds0c299d42016-11-15 14:37:04 -0500275 } else {
276 vibration = null;
277 }
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000278 if (mPreChannelsNotification
Julia Reynolds0c299d42016-11-15 14:37:04 -0500279 && (getChannel().getUserLockedFields()
280 & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
281 final Notification notification = sbn.getNotification();
282 final boolean useDefaultVibrate =
283 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
284 if (useDefaultVibrate) {
285 vibration = defaultVibration;
286 } else {
287 vibration = notification.vibrate;
288 }
289 }
290 return vibration;
291 }
292
293 private AudioAttributes calculateAttributes() {
294 final Notification n = sbn.getNotification();
Julia Reynolds619a69f2017-01-27 15:11:38 -0500295 AudioAttributes attributes = getChannel().getAudioAttributes();
296 if (attributes == null) {
297 attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
298 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500299
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000300 if (mPreChannelsNotification
Julia Reynolds619a69f2017-01-27 15:11:38 -0500301 && (getChannel().getUserLockedFields()
302 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
303 if (n.audioAttributes != null) {
304 // prefer audio attributes to stream type
305 attributes = n.audioAttributes;
306 } else if (n.audioStreamType >= 0
307 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
308 // the stream type is valid, use it
309 attributes = new AudioAttributes.Builder()
310 .setInternalLegacyStreamType(n.audioStreamType)
311 .build();
312 } else if (n.audioStreamType != AudioSystem.STREAM_DEFAULT) {
313 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
314 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500315 }
316 return attributes;
317 }
318
319 private int calculateImportance() {
320 final Notification n = sbn.getNotification();
321 int importance = getChannel().getImportance();
322 int requestedImportance = IMPORTANCE_DEFAULT;
Chris Wrenbdf33762015-12-04 15:50:51 -0500323
324 // Migrate notification flags to scores
325 if (0 != (n.flags & Notification.FLAG_HIGH_PRIORITY)) {
326 n.priority = Notification.PRIORITY_MAX;
327 }
328
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500329 n.priority = NotificationManagerService.clamp(n.priority, Notification.PRIORITY_MIN,
330 Notification.PRIORITY_MAX);
Chris Wrenbdf33762015-12-04 15:50:51 -0500331 switch (n.priority) {
332 case Notification.PRIORITY_MIN:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500333 requestedImportance = IMPORTANCE_MIN;
Julia Reynoldsf0f629f2016-02-25 09:34:04 -0500334 break;
Chris Wrenbdf33762015-12-04 15:50:51 -0500335 case Notification.PRIORITY_LOW:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500336 requestedImportance = IMPORTANCE_LOW;
Chris Wrenbdf33762015-12-04 15:50:51 -0500337 break;
338 case Notification.PRIORITY_DEFAULT:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500339 requestedImportance = IMPORTANCE_DEFAULT;
Chris Wrenbdf33762015-12-04 15:50:51 -0500340 break;
341 case Notification.PRIORITY_HIGH:
Chris Wrenbdf33762015-12-04 15:50:51 -0500342 case Notification.PRIORITY_MAX:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500343 requestedImportance = IMPORTANCE_HIGH;
Chris Wrenbdf33762015-12-04 15:50:51 -0500344 break;
345 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500346 stats.requestedImportance = requestedImportance;
347 stats.isNoisy = mSound != null || mVibration != null;
Chris Wrenbdf33762015-12-04 15:50:51 -0500348
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000349 if (mPreChannelsNotification
Julia Reynoldsa917a112017-03-21 11:09:14 -0400350 && (importance == IMPORTANCE_UNSPECIFIED
351 || (getChannel().getUserLockedFields()
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500352 & USER_LOCKED_IMPORTANCE) == 0)) {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500353 if (!stats.isNoisy && requestedImportance > IMPORTANCE_LOW) {
354 requestedImportance = IMPORTANCE_LOW;
Julia Reynolds83fa1072016-02-17 09:10:19 -0500355 }
Julia Reynolds83fa1072016-02-17 09:10:19 -0500356
Julia Reynolds0c299d42016-11-15 14:37:04 -0500357 if (stats.isNoisy) {
358 if (requestedImportance < IMPORTANCE_DEFAULT) {
359 requestedImportance = IMPORTANCE_DEFAULT;
360 }
361 }
362
363 if (n.fullScreenIntent != null) {
364 requestedImportance = IMPORTANCE_HIGH;
365 }
366 importance = requestedImportance;
Chris Wrenbdf33762015-12-04 15:50:51 -0500367 }
368
Chris Wrencdee8cd2016-01-25 17:10:30 -0500369 stats.naturalImportance = importance;
Chris Wrenbdf33762015-12-04 15:50:51 -0500370 return importance;
Chris Wren333a61c2014-05-28 16:40:57 -0400371 }
372
373 // copy any notes that the ranking system may have made before the update
374 public void copyRankingInformation(NotificationRecord previous) {
375 mContactAffinity = previous.mContactAffinity;
376 mRecentlyIntrusive = previous.mRecentlyIntrusive;
Chris Wren54bbef42014-07-09 18:37:56 -0400377 mPackagePriority = previous.mPackagePriority;
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400378 mPackageVisibility = previous.mPackageVisibility;
Chris Wren333a61c2014-05-28 16:40:57 -0400379 mIntercept = previous.mIntercept;
Beverly5a20a5e2018-03-06 15:02:44 -0500380 mHidden = previous.mHidden;
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200381 mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
Chris Wren640e3872015-04-21 13:23:18 -0400382 mCreationTimeMs = previous.mCreationTimeMs;
Chris Wren6650e572015-05-15 17:19:25 -0400383 mVisibleSinceMs = previous.mVisibleSinceMs;
Selim Cinek5b03ce92016-05-18 15:16:58 -0700384 if (previous.sbn.getOverrideGroupKey() != null && !sbn.isAppGroup()) {
Chris Wren8a1638f2016-05-02 16:19:14 -0400385 sbn.setOverrideGroupKey(previous.sbn.getOverrideGroupKey());
386 }
Chris Wren1f602dc2016-04-11 10:33:46 -0400387 // Don't copy importance information or mGlobalSortKey, recompute them.
Chris Wren333a61c2014-05-28 16:40:57 -0400388 }
389
390 public Notification getNotification() { return sbn.getNotification(); }
391 public int getFlags() { return sbn.getNotification().flags; }
Chris Wrenda4bd202014-09-04 15:53:52 -0400392 public UserHandle getUser() { return sbn.getUser(); }
Chris Wren333a61c2014-05-28 16:40:57 -0400393 public String getKey() { return sbn.getKey(); }
Chris Wrenda4bd202014-09-04 15:53:52 -0400394 /** @deprecated Use {@link #getUser()} instead. */
395 public int getUserId() { return sbn.getUserId(); }
Jeff Sharkey6a97cc32018-04-17 12:16:20 -0600396 public int getUid() { return sbn.getUid(); }
Chris Wren333a61c2014-05-28 16:40:57 -0400397
Kweku Adamsbc84aec2018-01-23 13:33:12 -0800398 void dump(ProtoOutputStream proto, long fieldId, boolean redact, int state) {
399 final long token = proto.start(fieldId);
400
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500401 proto.write(NotificationRecordProto.KEY, sbn.getKey());
Kweku Adamsbc84aec2018-01-23 13:33:12 -0800402 proto.write(NotificationRecordProto.STATE, state);
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500403 if (getChannel() != null) {
404 proto.write(NotificationRecordProto.CHANNEL_ID, getChannel().getId());
405 }
406 proto.write(NotificationRecordProto.CAN_SHOW_LIGHT, getLight() != null);
407 proto.write(NotificationRecordProto.CAN_VIBRATE, getVibration() != null);
408 proto.write(NotificationRecordProto.FLAGS, sbn.getNotification().flags);
409 proto.write(NotificationRecordProto.GROUP_KEY, getGroupKey());
410 proto.write(NotificationRecordProto.IMPORTANCE, getImportance());
411 if (getSound() != null) {
412 proto.write(NotificationRecordProto.SOUND, getSound().toString());
413 }
414 if (getAudioAttributes() != null) {
Kweku Adamsbc84aec2018-01-23 13:33:12 -0800415 getAudioAttributes().writeToProto(proto, NotificationRecordProto.AUDIO_ATTRIBUTES);
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500416 }
Kweku Adamsbc84aec2018-01-23 13:33:12 -0800417
418 proto.end(token);
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500419 }
420
Dan Sandler0a2308e2017-05-30 19:50:42 -0400421 String formatRemoteViews(RemoteViews rv) {
422 if (rv == null) return "null";
423 return String.format("%s/0x%08x (%d bytes): %s",
424 rv.getPackage(), rv.getLayoutId(), rv.estimateMemoryUsage(), rv.toString());
425 }
426
Dan Sandlera1770312015-07-10 13:59:29 -0400427 void dump(PrintWriter pw, String prefix, Context baseContext, boolean redact) {
Chris Wren333a61c2014-05-28 16:40:57 -0400428 final Notification notification = sbn.getNotification();
Dan Sandlerd63f9322015-05-06 15:18:49 -0400429 final Icon icon = notification.getSmallIcon();
430 String iconStr = String.valueOf(icon);
431 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
432 iconStr += " / " + idDebugString(baseContext, icon.getResPackage(), icon.getResId());
433 }
Chris Wren333a61c2014-05-28 16:40:57 -0400434 pw.println(prefix + this);
Dan Sandler0a2308e2017-05-30 19:50:42 -0400435 prefix = prefix + " ";
Julia Reynoldsa917a112017-03-21 11:09:14 -0400436 pw.println(prefix + "uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
437 pw.println(prefix + "icon=" + iconStr);
Julia Reynolds4db59552017-06-30 13:34:01 -0400438 pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400439 pw.println(prefix + "pri=" + notification.priority);
440 pw.println(prefix + "key=" + sbn.getKey());
Julia Reynolds503ed942017-10-04 16:04:56 -0400441 pw.println(prefix + "seen=" + mStats.hasSeen());
Julia Reynoldsa917a112017-03-21 11:09:14 -0400442 pw.println(prefix + "groupKey=" + getGroupKey());
443 pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
444 pw.println(prefix + "contentIntent=" + notification.contentIntent);
445 pw.println(prefix + "deleteIntent=" + notification.deleteIntent);
Dan Sandler0b4ceb32017-03-29 14:13:55 -0400446
447 pw.print(prefix + "tickerText=");
448 if (!TextUtils.isEmpty(notification.tickerText)) {
449 final String ticker = notification.tickerText.toString();
450 if (redact) {
451 // if the string is long enough, we allow ourselves a few bytes for debugging
452 pw.print(ticker.length() > 16 ? ticker.substring(0,8) : "");
453 pw.println("...");
454 } else {
455 pw.println(ticker);
456 }
457 } else {
458 pw.println("null");
459 }
Dan Sandler0a2308e2017-05-30 19:50:42 -0400460 pw.println(prefix + "contentView=" + formatRemoteViews(notification.contentView));
461 pw.println(prefix + "bigContentView=" + formatRemoteViews(notification.bigContentView));
462 pw.println(prefix + "headsUpContentView="
463 + formatRemoteViews(notification.headsUpContentView));
464 pw.print(prefix + String.format("color=0x%08x", notification.color));
Julia Reynoldsbad42972017-04-25 13:52:49 -0400465 pw.println(prefix + "timeout="
466 + TimeUtils.formatForLogging(notification.getTimeoutAfter()));
Chris Wren333a61c2014-05-28 16:40:57 -0400467 if (notification.actions != null && notification.actions.length > 0) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400468 pw.println(prefix + "actions={");
Chris Wren333a61c2014-05-28 16:40:57 -0400469 final int N = notification.actions.length;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500470 for (int i = 0; i < N; i++) {
Chris Wren333a61c2014-05-28 16:40:57 -0400471 final Notification.Action action = notification.actions[i];
Chris Wren1ac52a92016-02-24 14:54:52 -0500472 if (action != null) {
473 pw.println(String.format("%s [%d] \"%s\" -> %s",
474 prefix,
475 i,
476 action.title,
477 action.actionIntent == null ? "null" : action.actionIntent.toString()
478 ));
479 }
Chris Wren333a61c2014-05-28 16:40:57 -0400480 }
481 pw.println(prefix + " }");
482 }
483 if (notification.extras != null && notification.extras.size() > 0) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400484 pw.println(prefix + "extras={");
Chris Wren333a61c2014-05-28 16:40:57 -0400485 for (String key : notification.extras.keySet()) {
486 pw.print(prefix + " " + key + "=");
487 Object val = notification.extras.get(key);
488 if (val == null) {
489 pw.println("null");
490 } else {
491 pw.print(val.getClass().getSimpleName());
Dan Sandlera1770312015-07-10 13:59:29 -0400492 if (redact && (val instanceof CharSequence || val instanceof String)) {
Chris Wren333a61c2014-05-28 16:40:57 -0400493 // redact contents from bugreports
494 } else if (val instanceof Bitmap) {
495 pw.print(String.format(" (%dx%d)",
496 ((Bitmap) val).getWidth(),
497 ((Bitmap) val).getHeight()));
498 } else if (val.getClass().isArray()) {
499 final int N = Array.getLength(val);
Dan Sandlera1770312015-07-10 13:59:29 -0400500 pw.print(" (" + N + ")");
501 if (!redact) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500502 for (int j = 0; j < N; j++) {
Dan Sandlera1770312015-07-10 13:59:29 -0400503 pw.println();
504 pw.print(String.format("%s [%d] %s",
505 prefix, j, String.valueOf(Array.get(val, j))));
506 }
507 }
Chris Wren333a61c2014-05-28 16:40:57 -0400508 } else {
509 pw.print(" (" + String.valueOf(val) + ")");
510 }
511 pw.println();
512 }
513 }
Julia Reynoldsa917a112017-03-21 11:09:14 -0400514 pw.println(prefix + "}");
Chris Wren333a61c2014-05-28 16:40:57 -0400515 }
Julia Reynoldsa917a112017-03-21 11:09:14 -0400516 pw.println(prefix + "stats=" + stats.toString());
517 pw.println(prefix + "mContactAffinity=" + mContactAffinity);
518 pw.println(prefix + "mRecentlyIntrusive=" + mRecentlyIntrusive);
519 pw.println(prefix + "mPackagePriority=" + mPackagePriority);
520 pw.println(prefix + "mPackageVisibility=" + mPackageVisibility);
521 pw.println(prefix + "mUserImportance="
Julia Reynoldsef37f282016-02-12 09:11:27 -0500522 + NotificationListenerService.Ranking.importanceToString(mUserImportance));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400523 pw.println(prefix + "mImportance="
Chris Wrenbdf33762015-12-04 15:50:51 -0500524 + NotificationListenerService.Ranking.importanceToString(mImportance));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400525 pw.println(prefix + "mImportanceExplanation=" + mImportanceExplanation);
Rohan Shah590e1b22018-04-10 23:48:47 -0400526 pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked);
Julia Reynoldsa917a112017-03-21 11:09:14 -0400527 pw.println(prefix + "mIntercept=" + mIntercept);
Beverly5a20a5e2018-03-06 15:02:44 -0500528 pw.println(prefix + "mHidden==" + mHidden);
Julia Reynoldsa917a112017-03-21 11:09:14 -0400529 pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
530 pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
531 pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
532 pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
533 pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
Julia Reynoldsfdaa6492018-06-18 09:29:19 -0400534 pw.println(prefix + "mInterruptionTimeMs=" + mInterruptionTimeMs);
Julia Reynoldsa917a112017-03-21 11:09:14 -0400535 pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000536 if (mPreChannelsNotification) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400537 pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500538 notification.defaults, notification.flags));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400539 pw.println(prefix + "n.sound=" + notification.sound);
540 pw.println(prefix + "n.audioStreamType=" + notification.audioStreamType);
541 pw.println(prefix + "n.audioAttributes=" + notification.audioAttributes);
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500542 pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
543 notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400544 pw.println(prefix + "vibrate=" + Arrays.toString(notification.vibrate));
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500545 }
Julia Reynoldsa917a112017-03-21 11:09:14 -0400546 pw.println(prefix + "mSound= " + mSound);
547 pw.println(prefix + "mVibration= " + mVibration);
548 pw.println(prefix + "mAttributes= " + mAttributes);
549 pw.println(prefix + "mLight= " + mLight);
550 pw.println(prefix + "mShowBadge=" + mShowBadge);
Julia Reynolds4db59552017-06-30 13:34:01 -0400551 pw.println(prefix + "mColorized=" + notification.isColorized());
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500552 pw.println(prefix + "mIsInterruptive=" + mIsInterruptive);
Julia Reynoldsa917a112017-03-21 11:09:14 -0400553 pw.println(prefix + "effectiveNotificationChannel=" + getChannel());
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500554 if (getPeopleOverride() != null) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400555 pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride()));
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500556 }
557 if (getSnoozeCriteria() != null) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400558 pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500559 }
Julia Reynoldseb3dca72017-07-11 10:39:58 -0400560 pw.println(prefix + "mAdjustments=" + mAdjustments);
Chris Wren333a61c2014-05-28 16:40:57 -0400561 }
562
563
564 static String idDebugString(Context baseContext, String packageName, int id) {
565 Context c;
566
567 if (packageName != null) {
568 try {
569 c = baseContext.createPackageContext(packageName, 0);
570 } catch (NameNotFoundException e) {
571 c = baseContext;
572 }
573 } else {
574 c = baseContext;
575 }
576
577 Resources r = c.getResources();
578 try {
579 return r.getResourceName(id);
580 } catch (Resources.NotFoundException e) {
581 return "<name unknown>";
582 }
583 }
584
585 @Override
586 public final String toString() {
587 return String.format(
Julia Reynolds85769912016-10-25 09:08:57 -0400588 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" +
Rohan Shah590e1b22018-04-10 23:48:47 -0400589 "appImportanceLocked=%s: %s)",
Chris Wren333a61c2014-05-28 16:40:57 -0400590 System.identityHashCode(this),
591 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
Julia Reynolds4db59552017-06-30 13:34:01 -0400592 this.sbn.getTag(), this.mImportance, this.sbn.getKey(),
Rohan Shah590e1b22018-04-10 23:48:47 -0400593 mIsAppImportanceLocked, this.sbn.getNotification());
Chris Wren333a61c2014-05-28 16:40:57 -0400594 }
595
Julia Reynoldseb3dca72017-07-11 10:39:58 -0400596 public void addAdjustment(Adjustment adjustment) {
597 synchronized (mAdjustments) {
598 mAdjustments.add(adjustment);
599 }
600 }
601
602 public void applyAdjustments() {
603 synchronized (mAdjustments) {
604 for (Adjustment adjustment: mAdjustments) {
605 Bundle signals = adjustment.getSignals();
606 if (signals.containsKey(Adjustment.KEY_PEOPLE)) {
607 final ArrayList<String> people =
608 adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
609 setPeopleOverride(people);
610 }
611 if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
612 final ArrayList<SnoozeCriterion> snoozeCriterionList =
613 adjustment.getSignals().getParcelableArrayList(
614 Adjustment.KEY_SNOOZE_CRITERIA);
615 setSnoozeCriteria(snoozeCriterionList);
616 }
617 if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
618 final String groupOverrideKey =
619 adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
620 setOverrideGroupKey(groupOverrideKey);
621 }
Julia Reynolds503ed942017-10-04 16:04:56 -0400622 if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500623 // Only allow user sentiment update from assistant if user hasn't already
624 // expressed a preference for this channel
Rohan Shah590e1b22018-04-10 23:48:47 -0400625 if (!mIsAppImportanceLocked
626 && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) {
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500627 setUserSentiment(adjustment.getSignals().getInt(
628 Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
629 }
Julia Reynolds503ed942017-10-04 16:04:56 -0400630 }
Julia Reynoldseb3dca72017-07-11 10:39:58 -0400631 }
632 }
633 }
634
Rohan Shah590e1b22018-04-10 23:48:47 -0400635 public void setIsAppImportanceLocked(boolean isAppImportanceLocked) {
636 mIsAppImportanceLocked = isAppImportanceLocked;
637 calculateUserSentiment();
638 }
639
Chris Wren333a61c2014-05-28 16:40:57 -0400640 public void setContactAffinity(float contactAffinity) {
641 mContactAffinity = contactAffinity;
Chris Wrenbdf33762015-12-04 15:50:51 -0500642 if (mImportance < IMPORTANCE_DEFAULT &&
Chris Wrenc977f812016-06-13 21:24:53 +0000643 mContactAffinity > ValidateNotificationPeople.VALID_CONTACT) {
Chris Wrenbdf33762015-12-04 15:50:51 -0500644 setImportance(IMPORTANCE_DEFAULT, getPeopleExplanation());
645 }
Chris Wren333a61c2014-05-28 16:40:57 -0400646 }
647
648 public float getContactAffinity() {
649 return mContactAffinity;
650 }
651
John Spurlock1d881a12015-03-18 19:21:54 -0400652 public void setRecentlyIntrusive(boolean recentlyIntrusive) {
Chris Wren333a61c2014-05-28 16:40:57 -0400653 mRecentlyIntrusive = recentlyIntrusive;
Julia Reynolds309d1c82017-05-03 16:00:20 -0400654 if (recentlyIntrusive) {
655 mLastIntrusive = System.currentTimeMillis();
656 }
Chris Wren333a61c2014-05-28 16:40:57 -0400657 }
658
659 public boolean isRecentlyIntrusive() {
660 return mRecentlyIntrusive;
661 }
662
Julia Reynolds309d1c82017-05-03 16:00:20 -0400663 public long getLastIntrusive() {
664 return mLastIntrusive;
665 }
666
Chris Wren54bbef42014-07-09 18:37:56 -0400667 public void setPackagePriority(int packagePriority) {
John Spurlock6ac5f8d2014-07-18 11:27:54 -0400668 mPackagePriority = packagePriority;
Chris Wren54bbef42014-07-09 18:37:56 -0400669 }
670
671 public int getPackagePriority() {
672 return mPackagePriority;
673 }
674
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400675 public void setPackageVisibilityOverride(int packageVisibility) {
676 mPackageVisibility = packageVisibility;
677 }
678
679 public int getPackageVisibilityOverride() {
680 return mPackageVisibility;
681 }
682
Julia Reynoldsef37f282016-02-12 09:11:27 -0500683 public void setUserImportance(int importance) {
684 mUserImportance = importance;
685 applyUserImportance();
Chris Wrenbdf33762015-12-04 15:50:51 -0500686 }
687
Julia Reynoldsef37f282016-02-12 09:11:27 -0500688 private String getUserExplanation() {
689 if (mUserExplanation == null) {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500690 mUserExplanation = mContext.getResources().getString(
691 com.android.internal.R.string.importance_from_user);
Chris Wrenbdf33762015-12-04 15:50:51 -0500692 }
Julia Reynoldsef37f282016-02-12 09:11:27 -0500693 return mUserExplanation;
Chris Wrenbdf33762015-12-04 15:50:51 -0500694 }
695
696 private String getPeopleExplanation() {
697 if (mPeopleExplanation == null) {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500698 mPeopleExplanation = mContext.getResources().getString(
699 com.android.internal.R.string.importance_from_person);
Chris Wrenbdf33762015-12-04 15:50:51 -0500700 }
701 return mPeopleExplanation;
702 }
703
Julia Reynoldsef37f282016-02-12 09:11:27 -0500704 private void applyUserImportance() {
Julia Reynolds85769912016-10-25 09:08:57 -0400705 if (mUserImportance != IMPORTANCE_UNSPECIFIED) {
Julia Reynoldsef37f282016-02-12 09:11:27 -0500706 mImportance = mUserImportance;
707 mImportanceExplanation = getUserExplanation();
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500708 }
709 }
710
Julia Reynoldsef37f282016-02-12 09:11:27 -0500711 public int getUserImportance() {
712 return mUserImportance;
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500713 }
714
Chris Wrenbdf33762015-12-04 15:50:51 -0500715 public void setImportance(int importance, CharSequence explanation) {
Julia Reynolds85769912016-10-25 09:08:57 -0400716 if (importance != IMPORTANCE_UNSPECIFIED) {
Chris Wrenbdf33762015-12-04 15:50:51 -0500717 mImportance = importance;
718 mImportanceExplanation = explanation;
719 }
Julia Reynoldsef37f282016-02-12 09:11:27 -0500720 applyUserImportance();
Chris Wrenbdf33762015-12-04 15:50:51 -0500721 }
722
723 public int getImportance() {
724 return mImportance;
725 }
726
727 public CharSequence getImportanceExplanation() {
728 return mImportanceExplanation;
729 }
730
Chris Wren333a61c2014-05-28 16:40:57 -0400731 public boolean setIntercepted(boolean intercept) {
732 mIntercept = intercept;
733 return mIntercept;
734 }
735
736 public boolean isIntercepted() {
737 return mIntercept;
738 }
739
Beverly5a20a5e2018-03-06 15:02:44 -0500740 public void setHidden(boolean hidden) {
741 mHidden = hidden;
742 }
743
744 public boolean isHidden() {
745 return mHidden;
746 }
747
748
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500749 public void setSuppressedVisualEffects(int effects) {
750 mSuppressedVisualEffects = effects;
751 }
752
753 public int getSuppressedVisualEffects() {
754 return mSuppressedVisualEffects;
755 }
756
John Spurlock312d1d02014-07-08 10:24:57 -0400757 public boolean isCategory(String category) {
John Spurlockbfa5dc42014-07-28 23:30:45 -0400758 return Objects.equals(getNotification().category, category);
759 }
760
John Spurlockbfa5dc42014-07-28 23:30:45 -0400761 public boolean isAudioAttributesUsage(int usage) {
Julia Reynolds51eb78f82018-03-07 07:35:21 -0500762 return mAttributes != null && mAttributes.getUsage() == usage;
John Spurlock312d1d02014-07-08 10:24:57 -0400763 }
764
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200765 /**
766 * Returns the timestamp to use for time-based sorting in the ranker.
767 */
768 public long getRankingTimeMs() {
769 return mRankingTimeMs;
770 }
771
772 /**
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400773 * @param now this current time in milliseconds.
774 * @returns the number of milliseconds since the most recent update, or the post time if none.
Chris Wren6650e572015-05-15 17:19:25 -0400775 */
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400776 public int getFreshnessMs(long now) {
777 return (int) (now - mUpdateTimeMs);
Chris Wren6650e572015-05-15 17:19:25 -0400778 }
779
780 /**
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400781 * @param now this current time in milliseconds.
782 * @returns the number of milliseconds since the the first post, ignoring updates.
Chris Wren640e3872015-04-21 13:23:18 -0400783 */
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400784 public int getLifespanMs(long now) {
785 return (int) (now - mCreationTimeMs);
Chris Wren640e3872015-04-21 13:23:18 -0400786 }
787
788 /**
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400789 * @param now this current time in milliseconds.
790 * @returns the number of milliseconds since the most recent visibility event, or 0 if never.
Chris Wren6650e572015-05-15 17:19:25 -0400791 */
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400792 public int getExposureMs(long now) {
793 return mVisibleSinceMs == 0 ? 0 : (int) (now - mVisibleSinceMs);
Chris Wren6650e572015-05-15 17:19:25 -0400794 }
795
Julia Reynoldsfdaa6492018-06-18 09:29:19 -0400796 public int getInterruptionMs(long now) {
797 return (int) (now - mInterruptionTimeMs);
798 }
799
Chris Wren6650e572015-05-15 17:19:25 -0400800 /**
801 * Set the visibility of the notification.
802 */
Dieter Hsud39f0d52018-04-14 02:08:30 +0800803 public void setVisibility(boolean visible, int rank, int count) {
Chris Wren6650e572015-05-15 17:19:25 -0400804 final long now = System.currentTimeMillis();
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400805 mVisibleSinceMs = visible ? now : mVisibleSinceMs;
Chris Wren6650e572015-05-15 17:19:25 -0400806 stats.onVisibilityChanged(visible);
Chris Wren9eb5e102017-01-26 13:15:06 -0500807 MetricsLogger.action(getLogMaker(now)
808 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
809 .setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
Dieter Hsud39f0d52018-04-14 02:08:30 +0800810 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank)
811 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, count));
Chris Wren9eb5e102017-01-26 13:15:06 -0500812 if (visible) {
Julia Reynolds503ed942017-10-04 16:04:56 -0400813 setSeen();
Chris Wren9eb5e102017-01-26 13:15:06 -0500814 MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now));
815 }
Chris Wren6650e572015-05-15 17:19:25 -0400816 EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0,
Chris Wren9eb5e102017-01-26 13:15:06 -0500817 getLifespanMs(now),
818 getFreshnessMs(now),
Chris Wrend1dbc922015-06-19 17:51:16 -0400819 0, // exposure time
820 rank);
Chris Wren6650e572015-05-15 17:19:25 -0400821 }
822
823 /**
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200824 * @param previousRankingTimeMs for updated notifications, {@link #getRankingTimeMs()}
825 * of the previous notification record, 0 otherwise
826 */
827 private long calculateRankingTimeMs(long previousRankingTimeMs) {
828 Notification n = getNotification();
829 // Take developer provided 'when', unless it's in the future.
830 if (n.when != 0 && n.when <= sbn.getPostTime()) {
831 return n.when;
832 }
833 // If we've ranked a previous instance with a timestamp, inherit it. This case is
834 // important in order to have ranking stability for updating notifications.
835 if (previousRankingTimeMs > 0) {
836 return previousRankingTimeMs;
837 }
838 return sbn.getPostTime();
839 }
Chris Wren1031c972014-07-23 13:11:45 +0000840
Christoph Studercd4adf82014-08-19 17:50:49 +0200841 public void setGlobalSortKey(String globalSortKey) {
842 mGlobalSortKey = globalSortKey;
Chris Wren1031c972014-07-23 13:11:45 +0000843 }
844
Christoph Studercd4adf82014-08-19 17:50:49 +0200845 public String getGlobalSortKey() {
846 return mGlobalSortKey;
Chris Wren1031c972014-07-23 13:11:45 +0000847 }
848
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700849 /** Check if any of the listeners have marked this notification as seen by the user. */
850 public boolean isSeen() {
Julia Reynolds503ed942017-10-04 16:04:56 -0400851 return mStats.hasSeen();
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700852 }
853
854 /** Mark the notification as seen by the user. */
855 public void setSeen() {
Julia Reynolds503ed942017-10-04 16:04:56 -0400856 mStats.setSeen();
Julia Reynoldsb3c68ff2018-05-22 14:58:39 -0400857 if (mTextChanged) {
Julia Reynoldsfdaa6492018-06-18 09:29:19 -0400858 setInterruptive(true);
Julia Reynoldsb3c68ff2018-05-22 14:58:39 -0400859 }
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700860 }
861
Chris Wren1031c972014-07-23 13:11:45 +0000862 public void setAuthoritativeRank(int authoritativeRank) {
863 mAuthoritativeRank = authoritativeRank;
864 }
865
866 public int getAuthoritativeRank() {
867 return mAuthoritativeRank;
868 }
869
870 public String getGroupKey() {
871 return sbn.getGroupKey();
872 }
Chris Wren47633422016-01-22 09:56:59 -0500873
Chris Wrenb3921792017-06-01 13:34:46 -0400874 public void setOverrideGroupKey(String overrideGroupKey) {
875 sbn.setOverrideGroupKey(overrideGroupKey);
876 mGroupLogTag = null;
877 }
878
879 private String getGroupLogTag() {
880 if (mGroupLogTag == null) {
881 mGroupLogTag = shortenTag(sbn.getGroup());
882 }
883 return mGroupLogTag;
884 }
885
886 private String getChannelIdLogTag() {
887 if (mChannelIdLogTag == null) {
888 mChannelIdLogTag = shortenTag(mChannel.getId());
889 }
890 return mChannelIdLogTag;
891 }
892
893 private String shortenTag(String longTag) {
894 if (longTag == null) {
895 return null;
896 }
897 if (longTag.length() < MAX_LOGTAG_LENGTH) {
898 return longTag;
899 } else {
900 return longTag.substring(0, MAX_LOGTAG_LENGTH - 8) + "-" +
901 Integer.toHexString(longTag.hashCode());
902 }
903 }
904
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400905 public NotificationChannel getChannel() {
Julia Reynolds924eed12017-01-19 09:52:07 -0500906 return mChannel;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500907 }
908
Rohan Shah590e1b22018-04-10 23:48:47 -0400909 /**
910 * @see RankingHelper#getIsAppImportanceLocked(String, int)
911 */
912 public boolean getIsAppImportanceLocked() {
913 return mIsAppImportanceLocked;
914 }
915
Julia Reynolds924eed12017-01-19 09:52:07 -0500916 protected void updateNotificationChannel(NotificationChannel channel) {
917 if (channel != null) {
918 mChannel = channel;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500919 calculateImportance();
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500920 calculateUserSentiment();
Julia Reynolds22f02b32016-12-01 15:05:13 -0500921 }
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400922 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500923
Julia Reynolds924eed12017-01-19 09:52:07 -0500924 public void setShowBadge(boolean showBadge) {
925 mShowBadge = showBadge;
926 }
927
928 public boolean canShowBadge() {
929 return mShowBadge;
930 }
931
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500932 public Light getLight() {
933 return mLight;
934 }
935
Julia Reynolds0c299d42016-11-15 14:37:04 -0500936 public Uri getSound() {
937 return mSound;
938 }
939
940 public long[] getVibration() {
941 return mVibration;
942 }
943
944 public AudioAttributes getAudioAttributes() {
945 return mAttributes;
946 }
Julia Reynolds22f02b32016-12-01 15:05:13 -0500947
948 public ArrayList<String> getPeopleOverride() {
949 return mPeopleOverride;
950 }
951
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500952 public void setInterruptive(boolean interruptive) {
953 mIsInterruptive = interruptive;
Julia Reynoldsfdaa6492018-06-18 09:29:19 -0400954 final long now = System.currentTimeMillis();
955 mInterruptionTimeMs = interruptive ? now : mInterruptionTimeMs;
956
957 if (interruptive) {
958 MetricsLogger.action(getLogMaker()
959 .setCategory(MetricsEvent.NOTIFICATION_INTERRUPTION)
960 .setType(MetricsEvent.TYPE_OPEN)
961 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS,
962 getInterruptionMs(now)));
963 MetricsLogger.histogram(mContext, "note_interruptive", getInterruptionMs(now));
964 }
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500965 }
966
Julia Reynoldsb3c68ff2018-05-22 14:58:39 -0400967 public void setTextChanged(boolean textChanged) {
968 mTextChanged = textChanged;
969 }
970
971 public void setRecordedInterruption(boolean recorded) {
972 mRecordedInterruption = recorded;
973 }
974
975 public boolean hasRecordedInterruption() {
976 return mRecordedInterruption;
977 }
978
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500979 public boolean isInterruptive() {
980 return mIsInterruptive;
981 }
982
Julia Reynolds22f02b32016-12-01 15:05:13 -0500983 protected void setPeopleOverride(ArrayList<String> people) {
984 mPeopleOverride = people;
985 }
986
987 public ArrayList<SnoozeCriterion> getSnoozeCriteria() {
988 return mSnoozeCriteria;
989 }
990
991 protected void setSnoozeCriteria(ArrayList<SnoozeCriterion> snoozeCriteria) {
992 mSnoozeCriteria = snoozeCriteria;
993 }
Chris Wren9eb5e102017-01-26 13:15:06 -0500994
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500995 private void calculateUserSentiment() {
Rohan Shah590e1b22018-04-10 23:48:47 -0400996 if ((getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0
997 || mIsAppImportanceLocked) {
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500998 mUserSentiment = USER_SENTIMENT_POSITIVE;
999 }
1000 }
1001
Julia Reynolds503ed942017-10-04 16:04:56 -04001002 private void setUserSentiment(int userSentiment) {
1003 mUserSentiment = userSentiment;
1004 }
1005
1006 public int getUserSentiment() {
1007 return mUserSentiment;
1008 }
1009
1010 public NotificationStats getStats() {
1011 return mStats;
1012 }
1013
1014 public void recordExpanded() {
1015 mStats.setExpanded();
1016 }
1017
1018 public void recordDirectReplied() {
1019 mStats.setDirectReplied();
1020 }
1021
1022 public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) {
1023 mStats.setDismissalSurface(surface);
1024 }
1025
1026 public void recordSnoozed() {
1027 mStats.setSnoozed();
1028 }
1029
1030 public void recordViewedSettings() {
1031 mStats.setViewedSettings();
1032 }
1033
Kenny Guy23991102018-04-05 21:18:38 +01001034 public void setNumSmartRepliesAdded(int noReplies) {
1035 mNumberOfSmartRepliesAdded = noReplies;
1036 }
1037
1038 public int getNumSmartRepliesAdded() {
1039 return mNumberOfSmartRepliesAdded;
1040 }
1041
1042 public boolean hasSeenSmartReplies() {
1043 return mHasSeenSmartReplies;
1044 }
1045
1046 public void setSeenSmartReplies(boolean hasSeenSmartReplies) {
1047 mHasSeenSmartReplies = hasSeenSmartReplies;
1048 }
1049
Jeff Sharkey6a97cc32018-04-17 12:16:20 -06001050 /**
1051 * @return all {@link Uri} that should have permission granted to whoever
1052 * will be rendering it. This list has already been vetted to only
1053 * include {@link Uri} that the enqueuing app can grant.
1054 */
1055 public @Nullable ArraySet<Uri> getGrantableUris() {
1056 return mGrantableUris;
1057 }
Julia Reynoldse0d711f2017-09-01 08:50:47 -04001058
Jeff Sharkey6a97cc32018-04-17 12:16:20 -06001059 /**
1060 * Collect all {@link Uri} that should have permission granted to whoever
1061 * will be rendering it.
1062 */
Julia Reynolds89945c52018-06-13 10:45:21 -04001063 protected void calculateGrantableUris() {
Jeff Sharkey6a97cc32018-04-17 12:16:20 -06001064 final Notification notification = getNotification();
Jeff Sharkey23b31182018-04-18 21:32:12 -06001065 notification.visitUris((uri) -> {
Julia Reynolds89945c52018-06-13 10:45:21 -04001066 visitGrantableUri(uri, false);
Jeff Sharkey23b31182018-04-18 21:32:12 -06001067 });
Jeff Sharkey6a97cc32018-04-17 12:16:20 -06001068
Julia Reynoldse0d711f2017-09-01 08:50:47 -04001069 if (notification.getChannelId() != null) {
1070 NotificationChannel channel = getChannel();
Jeff Sharkey6a97cc32018-04-17 12:16:20 -06001071 if (channel != null) {
Julia Reynolds89945c52018-06-13 10:45:21 -04001072 visitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
1073 & NotificationChannel.USER_LOCKED_SOUND) != 0);
Jeff Sharkey6a97cc32018-04-17 12:16:20 -06001074 }
1075 }
1076 }
1077
1078 /**
1079 * Note the presence of a {@link Uri} that should have permission granted to
1080 * whoever will be rendering it.
1081 * <p>
1082 * If the enqueuing app has the ability to grant access, it will be added to
1083 * {@link #mGrantableUris}. Otherwise, this will either log or throw
1084 * {@link SecurityException} depending on target SDK of enqueuing app.
1085 */
Julia Reynolds89945c52018-06-13 10:45:21 -04001086 private void visitGrantableUri(Uri uri, boolean userOverriddenUri) {
Jeff Sharkey6a97cc32018-04-17 12:16:20 -06001087 if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
1088
1089 // We can't grant Uri permissions from system
1090 final int sourceUid = sbn.getUid();
1091 if (sourceUid == android.os.Process.SYSTEM_UID) return;
1092
Jeff Sharkey6a97cc32018-04-17 12:16:20 -06001093 final long ident = Binder.clearCallingIdentity();
1094 try {
1095 // This will throw SecurityException if caller can't grant
Julia Reynolds89945c52018-06-13 10:45:21 -04001096 mAm.checkGrantUriPermission(sourceUid, null,
Jeff Sharkey6a97cc32018-04-17 12:16:20 -06001097 ContentProvider.getUriWithoutUserId(uri),
1098 Intent.FLAG_GRANT_READ_URI_PERMISSION,
1099 ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
1100
1101 if (mGrantableUris == null) {
1102 mGrantableUris = new ArraySet<>();
1103 }
1104 mGrantableUris.add(uri);
1105 } catch (RemoteException ignored) {
1106 // Ignored because we're in same process
1107 } catch (SecurityException e) {
Julia Reynolds89945c52018-06-13 10:45:21 -04001108 if (!userOverriddenUri) {
1109 if (mTargetSdkVersion >= Build.VERSION_CODES.P) {
1110 throw e;
1111 } else {
1112 Log.w(TAG, "Ignoring " + uri + " from " + sourceUid + ": " + e.getMessage());
1113 }
Jeff Sharkey6a97cc32018-04-17 12:16:20 -06001114 }
1115 } finally {
1116 Binder.restoreCallingIdentity(ident);
1117 }
Julia Reynoldse0d711f2017-09-01 08:50:47 -04001118 }
1119
Chris Wren9eb5e102017-01-26 13:15:06 -05001120 public LogMaker getLogMaker(long now) {
1121 if (mLogMaker == null) {
Chris Wrenb3921792017-06-01 13:34:46 -04001122 // initialize fields that only change on update (so a new record)
Chris Wren9eb5e102017-01-26 13:15:06 -05001123 mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN)
1124 .setPackageName(sbn.getPackageName())
1125 .addTaggedData(MetricsEvent.NOTIFICATION_ID, sbn.getId())
Chris Wrenb3921792017-06-01 13:34:46 -04001126 .addTaggedData(MetricsEvent.NOTIFICATION_TAG, sbn.getTag())
1127 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID, getChannelIdLogTag());
Chris Wren9eb5e102017-01-26 13:15:06 -05001128 }
Chris Wrenb3921792017-06-01 13:34:46 -04001129 // reset fields that can change between updates, or are used by multiple logs
Chris Wren9eb5e102017-01-26 13:15:06 -05001130 return mLogMaker
Chris Wrena7c1b802017-03-07 10:17:20 -05001131 .clearCategory()
1132 .clearType()
1133 .clearSubtype()
Chris Wren9eb5e102017-01-26 13:15:06 -05001134 .clearTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX)
Chris Wrenb3921792017-06-01 13:34:46 -04001135 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, mImportance)
1136 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID, getGroupLogTag())
1137 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_SUMMARY,
1138 sbn.getNotification().isGroupSummary() ? 1 : 0)
Chris Wren9eb5e102017-01-26 13:15:06 -05001139 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
1140 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
Julia Reynoldsfdaa6492018-06-18 09:29:19 -04001141 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now))
1142 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS,
1143 getInterruptionMs(now));
Chris Wren9eb5e102017-01-26 13:15:06 -05001144 }
1145
1146 public LogMaker getLogMaker() {
1147 return getLogMaker(System.currentTimeMillis());
1148 }
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05001149
1150 @VisibleForTesting
1151 static final class Light {
1152 public final int color;
1153 public final int onMs;
1154 public final int offMs;
1155
1156 public Light(int color, int onMs, int offMs) {
1157 this.color = color;
1158 this.onMs = onMs;
1159 this.offMs = offMs;
1160 }
1161
1162 @Override
1163 public boolean equals(Object o) {
1164 if (this == o) return true;
1165 if (o == null || getClass() != o.getClass()) return false;
1166
1167 Light light = (Light) o;
1168
1169 if (color != light.color) return false;
1170 if (onMs != light.onMs) return false;
1171 return offMs == light.offMs;
1172
1173 }
1174
1175 @Override
1176 public int hashCode() {
1177 int result = color;
1178 result = 31 * result + onMs;
1179 result = 31 * result + offMs;
1180 return result;
1181 }
1182
1183 @Override
1184 public String toString() {
1185 return "Light{" +
1186 "color=" + color +
1187 ", onMs=" + onMs +
1188 ", offMs=" + offMs +
1189 '}';
1190 }
1191 }
Chris Wren333a61c2014-05-28 16:40:57 -04001192}