blob: 57af2ceef594c2ad9867e2e748d3d16ef3c81a26 [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_MIN;
20import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
21import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
22import static android.app.NotificationManager.IMPORTANCE_HIGH;
23import static android.app.NotificationManager.IMPORTANCE_LOW;
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
Chris Wren333a61c2014-05-28 16:40:57 -040029import android.app.Notification;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -040030import android.app.NotificationChannel;
Chris Wren333a61c2014-05-28 16:40:57 -040031import android.content.Context;
Julia Reynolds0c299d42016-11-15 14:37:04 -050032import android.content.pm.ApplicationInfo;
Tony Mantlerab55f0f2017-06-16 10:50:00 -070033import android.content.pm.PackageManager;
Chris Wren333a61c2014-05-28 16:40:57 -040034import android.content.pm.PackageManager.NameNotFoundException;
35import android.content.res.Resources;
36import android.graphics.Bitmap;
Dan Sandlerd63f9322015-05-06 15:18:49 -040037import android.graphics.drawable.Icon;
John Spurlockbfa5dc42014-07-28 23:30:45 -040038import android.media.AudioAttributes;
Julia Reynolds0c299d42016-11-15 14:37:04 -050039import android.media.AudioSystem;
Chris Wren9eb5e102017-01-26 13:15:06 -050040import android.metrics.LogMaker;
Julia Reynolds0c299d42016-11-15 14:37:04 -050041import android.net.Uri;
42import android.os.Build;
Julia Reynoldseb3dca72017-07-11 10:39:58 -040043import android.os.Bundle;
Julia Reynoldse0d711f2017-09-01 08:50:47 -040044import android.os.Parcelable;
Chris Wrenda4bd202014-09-04 15:53:52 -040045import android.os.UserHandle;
Julia Reynolds0c299d42016-11-15 14:37:04 -050046import android.provider.Settings;
Julia Reynoldseb3dca72017-07-11 10:39:58 -040047import android.service.notification.Adjustment;
Julia Reynolds5d25ee72015-11-20 15:38:20 -050048import android.service.notification.NotificationListenerService;
Julia Reynoldsc9842c12017-02-07 12:46:41 -050049import android.service.notification.NotificationRecordProto;
Julia Reynolds503ed942017-10-04 16:04:56 -040050import android.service.notification.NotificationStats;
Julia Reynolds22f02b32016-12-01 15:05:13 -050051import android.service.notification.SnoozeCriterion;
Chris Wren333a61c2014-05-28 16:40:57 -040052import android.service.notification.StatusBarNotification;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -050053import android.text.TextUtils;
Julia Reynoldse0d711f2017-09-01 08:50:47 -040054import android.util.ArraySet;
Julia Reynoldsf0f629f2016-02-25 09:34:04 -050055import android.util.Log;
Julia Reynolds0c299d42016-11-15 14:37:04 -050056import android.util.Slog;
Julia Reynolds2a128742016-11-28 14:29:25 -050057import android.util.TimeUtils;
Julia Reynoldsc9842c12017-02-07 12:46:41 -050058import android.util.proto.ProtoOutputStream;
Dan Sandler0a2308e2017-05-30 19:50:42 -040059import android.widget.RemoteViews;
John Spurlockbfa5dc42014-07-28 23:30:45 -040060
Chris Wren1031c972014-07-23 13:11:45 +000061import com.android.internal.annotations.VisibleForTesting;
Chris Wren9eb5e102017-01-26 13:15:06 -050062import com.android.internal.logging.MetricsLogger;
63import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Chris Wren6650e572015-05-15 17:19:25 -040064import com.android.server.EventLogTags;
Chris Wren333a61c2014-05-28 16:40:57 -040065
66import java.io.PrintWriter;
67import java.lang.reflect.Array;
Julia Reynolds22f02b32016-12-01 15:05:13 -050068import java.util.ArrayList;
Chris Wren333a61c2014-05-28 16:40:57 -040069import java.util.Arrays;
Julia Reynoldseb3dca72017-07-11 10:39:58 -040070import java.util.List;
John Spurlock312d1d02014-07-08 10:24:57 -040071import java.util.Objects;
Julia Reynoldse0d711f2017-09-01 08:50:47 -040072import java.util.Set;
Chris Wren333a61c2014-05-28 16:40:57 -040073
74/**
75 * Holds data about notifications that should not be shared with the
76 * {@link android.service.notification.NotificationListenerService}s.
77 *
78 * <p>These objects should not be mutated unless the code is synchronized
Julia Reynolds88860ce2017-06-01 16:55:49 -040079 * on {@link NotificationManagerService#mNotificationLock}, and any
Chris Wren333a61c2014-05-28 16:40:57 -040080 * modification should be followed by a sorting of that list.</p>
81 *
82 * <p>Is sortable by {@link NotificationComparator}.</p>
83 *
84 * {@hide}
85 */
86public final class NotificationRecord {
Julia Reynoldsf0f629f2016-02-25 09:34:04 -050087 static final String TAG = "NotificationRecord";
88 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Chris Wrenb3921792017-06-01 13:34:46 -040089 private static final int MAX_LOGTAG_LENGTH = 35;
Chris Wren333a61c2014-05-28 16:40:57 -040090 final StatusBarNotification sbn;
Christoph Studer365e4c32014-09-18 20:35:36 +020091 final int mOriginalFlags;
Chris Wrenbdf33762015-12-04 15:50:51 -050092 private final Context mContext;
Christoph Studer365e4c32014-09-18 20:35:36 +020093
Chris Wren333a61c2014-05-28 16:40:57 -040094 NotificationUsageStats.SingleNotificationStats stats;
95 boolean isCanceled;
96
97 // These members are used by NotificationSignalExtractors
98 // to communicate with the ranking module.
99 private float mContactAffinity;
100 private boolean mRecentlyIntrusive;
Julia Reynolds309d1c82017-05-03 16:00:20 -0400101 private long mLastIntrusive;
Chris Wren333a61c2014-05-28 16:40:57 -0400102
103 // is this notification currently being intercepted by Zen Mode?
104 private boolean mIntercept;
Chris Wren333a61c2014-05-28 16:40:57 -0400105
Beverly5a20a5e2018-03-06 15:02:44 -0500106 // is this notification hidden since the app pkg is suspended?
107 private boolean mHidden;
108
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200109 // The timestamp used for ranking.
110 private long mRankingTimeMs;
111
Chris Wren640e3872015-04-21 13:23:18 -0400112 // The first post time, stable across updates.
113 private long mCreationTimeMs;
114
Chris Wren6650e572015-05-15 17:19:25 -0400115 // The most recent visibility event.
116 private long mVisibleSinceMs;
117
118 // The most recent update time, or the creation time if no updates.
119 private long mUpdateTimeMs;
120
Chris Wrena3446562014-06-03 18:11:47 -0400121 // Is this record an update of an old record?
122 public boolean isUpdate;
Chris Wren54bbef42014-07-09 18:37:56 -0400123 private int mPackagePriority;
Chris Wrena3446562014-06-03 18:11:47 -0400124
Chris Wren1031c972014-07-23 13:11:45 +0000125 private int mAuthoritativeRank;
Christoph Studercd4adf82014-08-19 17:50:49 +0200126 private String mGlobalSortKey;
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400127 private int mPackageVisibility;
Julia Reynoldsef37f282016-02-12 09:11:27 -0500128 private int mUserImportance = IMPORTANCE_UNSPECIFIED;
Chris Wren47633422016-01-22 09:56:59 -0500129 private int mImportance = IMPORTANCE_UNSPECIFIED;
Chris Wrenbdf33762015-12-04 15:50:51 -0500130 private CharSequence mImportanceExplanation = null;
Chris Wren1031c972014-07-23 13:11:45 +0000131
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500132 private int mSuppressedVisualEffects = 0;
Julia Reynoldsef37f282016-02-12 09:11:27 -0500133 private String mUserExplanation;
Chris Wrenbdf33762015-12-04 15:50:51 -0500134 private String mPeopleExplanation;
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000135 private boolean mPreChannelsNotification = true;
Julia Reynolds0c299d42016-11-15 14:37:04 -0500136 private Uri mSound;
137 private long[] mVibration;
138 private AudioAttributes mAttributes;
Julia Reynolds924eed12017-01-19 09:52:07 -0500139 private NotificationChannel mChannel;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500140 private ArrayList<String> mPeopleOverride;
141 private ArrayList<SnoozeCriterion> mSnoozeCriteria;
Julia Reynolds924eed12017-01-19 09:52:07 -0500142 private boolean mShowBadge;
Chris Wren9eb5e102017-01-26 13:15:06 -0500143 private LogMaker mLogMaker;
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500144 private Light mLight;
Chris Wrenb3921792017-06-01 13:34:46 -0400145 private String mGroupLogTag;
146 private String mChannelIdLogTag;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500147
Julia Reynoldseb3dca72017-07-11 10:39:58 -0400148 private final List<Adjustment> mAdjustments;
Julia Reynolds503ed942017-10-04 16:04:56 -0400149 private final NotificationStats mStats;
150 private int mUserSentiment;
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500151 private boolean mIsInterruptive;
Kenny Guy23991102018-04-05 21:18:38 +0100152 private int mNumberOfSmartRepliesAdded;
153 private boolean mHasSeenSmartReplies;
Rohan Shah590e1b22018-04-10 23:48:47 -0400154 /**
155 * Whether this notification (and its channels) should be considered user locked. Used in
156 * conjunction with user sentiment calculation.
157 */
158 private boolean mIsAppImportanceLocked;
Julia Reynoldseb3dca72017-07-11 10:39:58 -0400159
Julia Reynolds924eed12017-01-19 09:52:07 -0500160 public NotificationRecord(Context context, StatusBarNotification sbn,
Rohan Shah590e1b22018-04-10 23:48:47 -0400161 NotificationChannel channel) {
Chris Wren333a61c2014-05-28 16:40:57 -0400162 this.sbn = sbn;
Christoph Studer365e4c32014-09-18 20:35:36 +0200163 mOriginalFlags = sbn.getNotification().flags;
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200164 mRankingTimeMs = calculateRankingTimeMs(0L);
Chris Wren640e3872015-04-21 13:23:18 -0400165 mCreationTimeMs = sbn.getPostTime();
Chris Wren6650e572015-05-15 17:19:25 -0400166 mUpdateTimeMs = mCreationTimeMs;
Chris Wrenbdf33762015-12-04 15:50:51 -0500167 mContext = context;
Chris Wrencdee8cd2016-01-25 17:10:30 -0500168 stats = new NotificationUsageStats.SingleNotificationStats();
Julia Reynolds924eed12017-01-19 09:52:07 -0500169 mChannel = channel;
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000170 mPreChannelsNotification = isPreChannelsNotification();
Julia Reynolds0c299d42016-11-15 14:37:04 -0500171 mSound = calculateSound();
172 mVibration = calculateVibration();
173 mAttributes = calculateAttributes();
174 mImportance = calculateImportance();
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500175 mLight = calculateLights();
Julia Reynoldseb3dca72017-07-11 10:39:58 -0400176 mAdjustments = new ArrayList<>();
Julia Reynolds503ed942017-10-04 16:04:56 -0400177 mStats = new NotificationStats();
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500178 calculateUserSentiment();
Chris Wrenbdf33762015-12-04 15:50:51 -0500179 }
180
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000181 private boolean isPreChannelsNotification() {
182 try {
183 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
184 final ApplicationInfo applicationInfo =
185 mContext.getPackageManager().getApplicationInfoAsUser(sbn.getPackageName(),
186 0, UserHandle.getUserId(sbn.getUid()));
Dan Sandlere103b782017-05-17 16:07:56 -0700187 if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000188 return true;
189 }
190 }
191 } catch (NameNotFoundException e) {
192 Slog.e(TAG, "Can't find package", e);
193 }
194 return false;
195 }
196
Julia Reynolds0c299d42016-11-15 14:37:04 -0500197 private Uri calculateSound() {
Chris Wrenbdf33762015-12-04 15:50:51 -0500198 final Notification n = sbn.getNotification();
Julia Reynolds0c299d42016-11-15 14:37:04 -0500199
Tony Mantlerab55f0f2017-06-16 10:50:00 -0700200 // No notification sounds on tv
201 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
202 return null;
203 }
204
Julia Reynolds924eed12017-01-19 09:52:07 -0500205 Uri sound = mChannel.getSound();
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000206 if (mPreChannelsNotification && (getChannel().getUserLockedFields()
Julia Reynolds0c299d42016-11-15 14:37:04 -0500207 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
208
209 final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0;
210 if (useDefaultSound) {
211 sound = Settings.System.DEFAULT_NOTIFICATION_URI;
Julia Reynoldsb9e712e2017-04-17 10:31:03 -0400212 } else {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500213 sound = n.sound;
214 }
215 }
216 return sound;
217 }
218
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500219 private Light calculateLights() {
220 int defaultLightColor = mContext.getResources().getColor(
221 com.android.internal.R.color.config_defaultNotificationColor);
222 int defaultLightOn = mContext.getResources().getInteger(
223 com.android.internal.R.integer.config_defaultNotificationLedOn);
224 int defaultLightOff = mContext.getResources().getInteger(
225 com.android.internal.R.integer.config_defaultNotificationLedOff);
226
Julia Reynolds529e3322017-02-06 08:33:01 -0500227 int channelLightColor = getChannel().getLightColor() != 0 ? getChannel().getLightColor()
228 : defaultLightColor;
229 Light light = getChannel().shouldShowLights() ? new Light(channelLightColor,
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500230 defaultLightOn, defaultLightOff) : null;
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000231 if (mPreChannelsNotification
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500232 && (getChannel().getUserLockedFields()
233 & NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
234 final Notification notification = sbn.getNotification();
235 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
236 light = new Light(notification.ledARGB, notification.ledOnMS,
237 notification.ledOffMS);
238 if ((notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
239 light = new Light(defaultLightColor, defaultLightOn,
240 defaultLightOff);
241 }
242 } else {
243 light = null;
244 }
245 }
246 return light;
247 }
248
Julia Reynolds0c299d42016-11-15 14:37:04 -0500249 private long[] calculateVibration() {
250 long[] vibration;
251 final long[] defaultVibration = NotificationManagerService.getLongArray(
252 mContext.getResources(),
253 com.android.internal.R.array.config_defaultNotificationVibePattern,
254 NotificationManagerService.VIBRATE_PATTERN_MAXLEN,
255 NotificationManagerService.DEFAULT_VIBRATE_PATTERN);
256 if (getChannel().shouldVibrate()) {
Julia Reynoldsf57de462016-11-23 11:31:46 -0500257 vibration = getChannel().getVibrationPattern() == null
258 ? defaultVibration : getChannel().getVibrationPattern();
Julia Reynolds0c299d42016-11-15 14:37:04 -0500259 } else {
260 vibration = null;
261 }
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000262 if (mPreChannelsNotification
Julia Reynolds0c299d42016-11-15 14:37:04 -0500263 && (getChannel().getUserLockedFields()
264 & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
265 final Notification notification = sbn.getNotification();
266 final boolean useDefaultVibrate =
267 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
268 if (useDefaultVibrate) {
269 vibration = defaultVibration;
270 } else {
271 vibration = notification.vibrate;
272 }
273 }
274 return vibration;
275 }
276
277 private AudioAttributes calculateAttributes() {
278 final Notification n = sbn.getNotification();
Julia Reynolds619a69f2017-01-27 15:11:38 -0500279 AudioAttributes attributes = getChannel().getAudioAttributes();
280 if (attributes == null) {
281 attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
282 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500283
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000284 if (mPreChannelsNotification
Julia Reynolds619a69f2017-01-27 15:11:38 -0500285 && (getChannel().getUserLockedFields()
286 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
287 if (n.audioAttributes != null) {
288 // prefer audio attributes to stream type
289 attributes = n.audioAttributes;
290 } else if (n.audioStreamType >= 0
291 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
292 // the stream type is valid, use it
293 attributes = new AudioAttributes.Builder()
294 .setInternalLegacyStreamType(n.audioStreamType)
295 .build();
296 } else if (n.audioStreamType != AudioSystem.STREAM_DEFAULT) {
297 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
298 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500299 }
300 return attributes;
301 }
302
303 private int calculateImportance() {
304 final Notification n = sbn.getNotification();
305 int importance = getChannel().getImportance();
306 int requestedImportance = IMPORTANCE_DEFAULT;
Chris Wrenbdf33762015-12-04 15:50:51 -0500307
308 // Migrate notification flags to scores
309 if (0 != (n.flags & Notification.FLAG_HIGH_PRIORITY)) {
310 n.priority = Notification.PRIORITY_MAX;
311 }
312
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500313 n.priority = NotificationManagerService.clamp(n.priority, Notification.PRIORITY_MIN,
314 Notification.PRIORITY_MAX);
Chris Wrenbdf33762015-12-04 15:50:51 -0500315 switch (n.priority) {
316 case Notification.PRIORITY_MIN:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500317 requestedImportance = IMPORTANCE_MIN;
Julia Reynoldsf0f629f2016-02-25 09:34:04 -0500318 break;
Chris Wrenbdf33762015-12-04 15:50:51 -0500319 case Notification.PRIORITY_LOW:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500320 requestedImportance = IMPORTANCE_LOW;
Chris Wrenbdf33762015-12-04 15:50:51 -0500321 break;
322 case Notification.PRIORITY_DEFAULT:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500323 requestedImportance = IMPORTANCE_DEFAULT;
Chris Wrenbdf33762015-12-04 15:50:51 -0500324 break;
325 case Notification.PRIORITY_HIGH:
Chris Wrenbdf33762015-12-04 15:50:51 -0500326 case Notification.PRIORITY_MAX:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500327 requestedImportance = IMPORTANCE_HIGH;
Chris Wrenbdf33762015-12-04 15:50:51 -0500328 break;
329 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500330 stats.requestedImportance = requestedImportance;
331 stats.isNoisy = mSound != null || mVibration != null;
Chris Wrenbdf33762015-12-04 15:50:51 -0500332
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000333 if (mPreChannelsNotification
Julia Reynoldsa917a112017-03-21 11:09:14 -0400334 && (importance == IMPORTANCE_UNSPECIFIED
335 || (getChannel().getUserLockedFields()
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500336 & USER_LOCKED_IMPORTANCE) == 0)) {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500337 if (!stats.isNoisy && requestedImportance > IMPORTANCE_LOW) {
338 requestedImportance = IMPORTANCE_LOW;
Julia Reynolds83fa1072016-02-17 09:10:19 -0500339 }
Julia Reynolds83fa1072016-02-17 09:10:19 -0500340
Julia Reynolds0c299d42016-11-15 14:37:04 -0500341 if (stats.isNoisy) {
342 if (requestedImportance < IMPORTANCE_DEFAULT) {
343 requestedImportance = IMPORTANCE_DEFAULT;
344 }
345 }
346
347 if (n.fullScreenIntent != null) {
348 requestedImportance = IMPORTANCE_HIGH;
349 }
350 importance = requestedImportance;
Chris Wrenbdf33762015-12-04 15:50:51 -0500351 }
352
Chris Wrencdee8cd2016-01-25 17:10:30 -0500353 stats.naturalImportance = importance;
Chris Wrenbdf33762015-12-04 15:50:51 -0500354 return importance;
Chris Wren333a61c2014-05-28 16:40:57 -0400355 }
356
357 // copy any notes that the ranking system may have made before the update
358 public void copyRankingInformation(NotificationRecord previous) {
359 mContactAffinity = previous.mContactAffinity;
360 mRecentlyIntrusive = previous.mRecentlyIntrusive;
Chris Wren54bbef42014-07-09 18:37:56 -0400361 mPackagePriority = previous.mPackagePriority;
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400362 mPackageVisibility = previous.mPackageVisibility;
Chris Wren333a61c2014-05-28 16:40:57 -0400363 mIntercept = previous.mIntercept;
Beverly5a20a5e2018-03-06 15:02:44 -0500364 mHidden = previous.mHidden;
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200365 mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
Chris Wren640e3872015-04-21 13:23:18 -0400366 mCreationTimeMs = previous.mCreationTimeMs;
Chris Wren6650e572015-05-15 17:19:25 -0400367 mVisibleSinceMs = previous.mVisibleSinceMs;
Selim Cinek5b03ce92016-05-18 15:16:58 -0700368 if (previous.sbn.getOverrideGroupKey() != null && !sbn.isAppGroup()) {
Chris Wren8a1638f2016-05-02 16:19:14 -0400369 sbn.setOverrideGroupKey(previous.sbn.getOverrideGroupKey());
370 }
Chris Wren1f602dc2016-04-11 10:33:46 -0400371 // Don't copy importance information or mGlobalSortKey, recompute them.
Chris Wren333a61c2014-05-28 16:40:57 -0400372 }
373
374 public Notification getNotification() { return sbn.getNotification(); }
375 public int getFlags() { return sbn.getNotification().flags; }
Chris Wrenda4bd202014-09-04 15:53:52 -0400376 public UserHandle getUser() { return sbn.getUser(); }
Chris Wren333a61c2014-05-28 16:40:57 -0400377 public String getKey() { return sbn.getKey(); }
Chris Wrenda4bd202014-09-04 15:53:52 -0400378 /** @deprecated Use {@link #getUser()} instead. */
379 public int getUserId() { return sbn.getUserId(); }
Chris Wren333a61c2014-05-28 16:40:57 -0400380
Kweku Adamsbc84aec2018-01-23 13:33:12 -0800381 void dump(ProtoOutputStream proto, long fieldId, boolean redact, int state) {
382 final long token = proto.start(fieldId);
383
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500384 proto.write(NotificationRecordProto.KEY, sbn.getKey());
Kweku Adamsbc84aec2018-01-23 13:33:12 -0800385 proto.write(NotificationRecordProto.STATE, state);
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500386 if (getChannel() != null) {
387 proto.write(NotificationRecordProto.CHANNEL_ID, getChannel().getId());
388 }
389 proto.write(NotificationRecordProto.CAN_SHOW_LIGHT, getLight() != null);
390 proto.write(NotificationRecordProto.CAN_VIBRATE, getVibration() != null);
391 proto.write(NotificationRecordProto.FLAGS, sbn.getNotification().flags);
392 proto.write(NotificationRecordProto.GROUP_KEY, getGroupKey());
393 proto.write(NotificationRecordProto.IMPORTANCE, getImportance());
394 if (getSound() != null) {
395 proto.write(NotificationRecordProto.SOUND, getSound().toString());
396 }
397 if (getAudioAttributes() != null) {
Kweku Adamsbc84aec2018-01-23 13:33:12 -0800398 getAudioAttributes().writeToProto(proto, NotificationRecordProto.AUDIO_ATTRIBUTES);
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500399 }
Kweku Adamsbc84aec2018-01-23 13:33:12 -0800400
401 proto.end(token);
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500402 }
403
Dan Sandler0a2308e2017-05-30 19:50:42 -0400404 String formatRemoteViews(RemoteViews rv) {
405 if (rv == null) return "null";
406 return String.format("%s/0x%08x (%d bytes): %s",
407 rv.getPackage(), rv.getLayoutId(), rv.estimateMemoryUsage(), rv.toString());
408 }
409
Dan Sandlera1770312015-07-10 13:59:29 -0400410 void dump(PrintWriter pw, String prefix, Context baseContext, boolean redact) {
Chris Wren333a61c2014-05-28 16:40:57 -0400411 final Notification notification = sbn.getNotification();
Dan Sandlerd63f9322015-05-06 15:18:49 -0400412 final Icon icon = notification.getSmallIcon();
413 String iconStr = String.valueOf(icon);
414 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
415 iconStr += " / " + idDebugString(baseContext, icon.getResPackage(), icon.getResId());
416 }
Chris Wren333a61c2014-05-28 16:40:57 -0400417 pw.println(prefix + this);
Dan Sandler0a2308e2017-05-30 19:50:42 -0400418 prefix = prefix + " ";
Julia Reynoldsa917a112017-03-21 11:09:14 -0400419 pw.println(prefix + "uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
420 pw.println(prefix + "icon=" + iconStr);
Julia Reynolds4db59552017-06-30 13:34:01 -0400421 pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400422 pw.println(prefix + "pri=" + notification.priority);
423 pw.println(prefix + "key=" + sbn.getKey());
Julia Reynolds503ed942017-10-04 16:04:56 -0400424 pw.println(prefix + "seen=" + mStats.hasSeen());
Julia Reynoldsa917a112017-03-21 11:09:14 -0400425 pw.println(prefix + "groupKey=" + getGroupKey());
426 pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
427 pw.println(prefix + "contentIntent=" + notification.contentIntent);
428 pw.println(prefix + "deleteIntent=" + notification.deleteIntent);
Dan Sandler0b4ceb32017-03-29 14:13:55 -0400429
430 pw.print(prefix + "tickerText=");
431 if (!TextUtils.isEmpty(notification.tickerText)) {
432 final String ticker = notification.tickerText.toString();
433 if (redact) {
434 // if the string is long enough, we allow ourselves a few bytes for debugging
435 pw.print(ticker.length() > 16 ? ticker.substring(0,8) : "");
436 pw.println("...");
437 } else {
438 pw.println(ticker);
439 }
440 } else {
441 pw.println("null");
442 }
Dan Sandler0a2308e2017-05-30 19:50:42 -0400443 pw.println(prefix + "contentView=" + formatRemoteViews(notification.contentView));
444 pw.println(prefix + "bigContentView=" + formatRemoteViews(notification.bigContentView));
445 pw.println(prefix + "headsUpContentView="
446 + formatRemoteViews(notification.headsUpContentView));
447 pw.print(prefix + String.format("color=0x%08x", notification.color));
Julia Reynoldsbad42972017-04-25 13:52:49 -0400448 pw.println(prefix + "timeout="
449 + TimeUtils.formatForLogging(notification.getTimeoutAfter()));
Chris Wren333a61c2014-05-28 16:40:57 -0400450 if (notification.actions != null && notification.actions.length > 0) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400451 pw.println(prefix + "actions={");
Chris Wren333a61c2014-05-28 16:40:57 -0400452 final int N = notification.actions.length;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500453 for (int i = 0; i < N; i++) {
Chris Wren333a61c2014-05-28 16:40:57 -0400454 final Notification.Action action = notification.actions[i];
Chris Wren1ac52a92016-02-24 14:54:52 -0500455 if (action != null) {
456 pw.println(String.format("%s [%d] \"%s\" -> %s",
457 prefix,
458 i,
459 action.title,
460 action.actionIntent == null ? "null" : action.actionIntent.toString()
461 ));
462 }
Chris Wren333a61c2014-05-28 16:40:57 -0400463 }
464 pw.println(prefix + " }");
465 }
466 if (notification.extras != null && notification.extras.size() > 0) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400467 pw.println(prefix + "extras={");
Chris Wren333a61c2014-05-28 16:40:57 -0400468 for (String key : notification.extras.keySet()) {
469 pw.print(prefix + " " + key + "=");
470 Object val = notification.extras.get(key);
471 if (val == null) {
472 pw.println("null");
473 } else {
474 pw.print(val.getClass().getSimpleName());
Dan Sandlera1770312015-07-10 13:59:29 -0400475 if (redact && (val instanceof CharSequence || val instanceof String)) {
Chris Wren333a61c2014-05-28 16:40:57 -0400476 // redact contents from bugreports
477 } else if (val instanceof Bitmap) {
478 pw.print(String.format(" (%dx%d)",
479 ((Bitmap) val).getWidth(),
480 ((Bitmap) val).getHeight()));
481 } else if (val.getClass().isArray()) {
482 final int N = Array.getLength(val);
Dan Sandlera1770312015-07-10 13:59:29 -0400483 pw.print(" (" + N + ")");
484 if (!redact) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500485 for (int j = 0; j < N; j++) {
Dan Sandlera1770312015-07-10 13:59:29 -0400486 pw.println();
487 pw.print(String.format("%s [%d] %s",
488 prefix, j, String.valueOf(Array.get(val, j))));
489 }
490 }
Chris Wren333a61c2014-05-28 16:40:57 -0400491 } else {
492 pw.print(" (" + String.valueOf(val) + ")");
493 }
494 pw.println();
495 }
496 }
Julia Reynoldsa917a112017-03-21 11:09:14 -0400497 pw.println(prefix + "}");
Chris Wren333a61c2014-05-28 16:40:57 -0400498 }
Julia Reynoldsa917a112017-03-21 11:09:14 -0400499 pw.println(prefix + "stats=" + stats.toString());
500 pw.println(prefix + "mContactAffinity=" + mContactAffinity);
501 pw.println(prefix + "mRecentlyIntrusive=" + mRecentlyIntrusive);
502 pw.println(prefix + "mPackagePriority=" + mPackagePriority);
503 pw.println(prefix + "mPackageVisibility=" + mPackageVisibility);
504 pw.println(prefix + "mUserImportance="
Julia Reynoldsef37f282016-02-12 09:11:27 -0500505 + NotificationListenerService.Ranking.importanceToString(mUserImportance));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400506 pw.println(prefix + "mImportance="
Chris Wrenbdf33762015-12-04 15:50:51 -0500507 + NotificationListenerService.Ranking.importanceToString(mImportance));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400508 pw.println(prefix + "mImportanceExplanation=" + mImportanceExplanation);
Rohan Shah590e1b22018-04-10 23:48:47 -0400509 pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked);
Julia Reynoldsa917a112017-03-21 11:09:14 -0400510 pw.println(prefix + "mIntercept=" + mIntercept);
Beverly5a20a5e2018-03-06 15:02:44 -0500511 pw.println(prefix + "mHidden==" + mHidden);
Julia Reynoldsa917a112017-03-21 11:09:14 -0400512 pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
513 pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
514 pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
515 pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
516 pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
517 pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000518 if (mPreChannelsNotification) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400519 pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500520 notification.defaults, notification.flags));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400521 pw.println(prefix + "n.sound=" + notification.sound);
522 pw.println(prefix + "n.audioStreamType=" + notification.audioStreamType);
523 pw.println(prefix + "n.audioAttributes=" + notification.audioAttributes);
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500524 pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
525 notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400526 pw.println(prefix + "vibrate=" + Arrays.toString(notification.vibrate));
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500527 }
Julia Reynoldsa917a112017-03-21 11:09:14 -0400528 pw.println(prefix + "mSound= " + mSound);
529 pw.println(prefix + "mVibration= " + mVibration);
530 pw.println(prefix + "mAttributes= " + mAttributes);
531 pw.println(prefix + "mLight= " + mLight);
532 pw.println(prefix + "mShowBadge=" + mShowBadge);
Julia Reynolds4db59552017-06-30 13:34:01 -0400533 pw.println(prefix + "mColorized=" + notification.isColorized());
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500534 pw.println(prefix + "mIsInterruptive=" + mIsInterruptive);
Julia Reynoldsa917a112017-03-21 11:09:14 -0400535 pw.println(prefix + "effectiveNotificationChannel=" + getChannel());
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500536 if (getPeopleOverride() != null) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400537 pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride()));
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500538 }
539 if (getSnoozeCriteria() != null) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400540 pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500541 }
Julia Reynoldseb3dca72017-07-11 10:39:58 -0400542 pw.println(prefix + "mAdjustments=" + mAdjustments);
Chris Wren333a61c2014-05-28 16:40:57 -0400543 }
544
545
546 static String idDebugString(Context baseContext, String packageName, int id) {
547 Context c;
548
549 if (packageName != null) {
550 try {
551 c = baseContext.createPackageContext(packageName, 0);
552 } catch (NameNotFoundException e) {
553 c = baseContext;
554 }
555 } else {
556 c = baseContext;
557 }
558
559 Resources r = c.getResources();
560 try {
561 return r.getResourceName(id);
562 } catch (Resources.NotFoundException e) {
563 return "<name unknown>";
564 }
565 }
566
567 @Override
568 public final String toString() {
569 return String.format(
Julia Reynolds85769912016-10-25 09:08:57 -0400570 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" +
Rohan Shah590e1b22018-04-10 23:48:47 -0400571 "appImportanceLocked=%s: %s)",
Chris Wren333a61c2014-05-28 16:40:57 -0400572 System.identityHashCode(this),
573 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
Julia Reynolds4db59552017-06-30 13:34:01 -0400574 this.sbn.getTag(), this.mImportance, this.sbn.getKey(),
Rohan Shah590e1b22018-04-10 23:48:47 -0400575 mIsAppImportanceLocked, this.sbn.getNotification());
Chris Wren333a61c2014-05-28 16:40:57 -0400576 }
577
Julia Reynoldseb3dca72017-07-11 10:39:58 -0400578 public void addAdjustment(Adjustment adjustment) {
579 synchronized (mAdjustments) {
580 mAdjustments.add(adjustment);
581 }
582 }
583
584 public void applyAdjustments() {
585 synchronized (mAdjustments) {
586 for (Adjustment adjustment: mAdjustments) {
587 Bundle signals = adjustment.getSignals();
588 if (signals.containsKey(Adjustment.KEY_PEOPLE)) {
589 final ArrayList<String> people =
590 adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
591 setPeopleOverride(people);
592 }
593 if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
594 final ArrayList<SnoozeCriterion> snoozeCriterionList =
595 adjustment.getSignals().getParcelableArrayList(
596 Adjustment.KEY_SNOOZE_CRITERIA);
597 setSnoozeCriteria(snoozeCriterionList);
598 }
599 if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
600 final String groupOverrideKey =
601 adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
602 setOverrideGroupKey(groupOverrideKey);
603 }
Julia Reynolds503ed942017-10-04 16:04:56 -0400604 if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500605 // Only allow user sentiment update from assistant if user hasn't already
606 // expressed a preference for this channel
Rohan Shah590e1b22018-04-10 23:48:47 -0400607 if (!mIsAppImportanceLocked
608 && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) {
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500609 setUserSentiment(adjustment.getSignals().getInt(
610 Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
611 }
Julia Reynolds503ed942017-10-04 16:04:56 -0400612 }
Julia Reynoldseb3dca72017-07-11 10:39:58 -0400613 }
614 }
615 }
616
Rohan Shah590e1b22018-04-10 23:48:47 -0400617 public void setIsAppImportanceLocked(boolean isAppImportanceLocked) {
618 mIsAppImportanceLocked = isAppImportanceLocked;
619 calculateUserSentiment();
620 }
621
Chris Wren333a61c2014-05-28 16:40:57 -0400622 public void setContactAffinity(float contactAffinity) {
623 mContactAffinity = contactAffinity;
Chris Wrenbdf33762015-12-04 15:50:51 -0500624 if (mImportance < IMPORTANCE_DEFAULT &&
Chris Wrenc977f812016-06-13 21:24:53 +0000625 mContactAffinity > ValidateNotificationPeople.VALID_CONTACT) {
Chris Wrenbdf33762015-12-04 15:50:51 -0500626 setImportance(IMPORTANCE_DEFAULT, getPeopleExplanation());
627 }
Chris Wren333a61c2014-05-28 16:40:57 -0400628 }
629
630 public float getContactAffinity() {
631 return mContactAffinity;
632 }
633
John Spurlock1d881a12015-03-18 19:21:54 -0400634 public void setRecentlyIntrusive(boolean recentlyIntrusive) {
Chris Wren333a61c2014-05-28 16:40:57 -0400635 mRecentlyIntrusive = recentlyIntrusive;
Julia Reynolds309d1c82017-05-03 16:00:20 -0400636 if (recentlyIntrusive) {
637 mLastIntrusive = System.currentTimeMillis();
638 }
Chris Wren333a61c2014-05-28 16:40:57 -0400639 }
640
641 public boolean isRecentlyIntrusive() {
642 return mRecentlyIntrusive;
643 }
644
Julia Reynolds309d1c82017-05-03 16:00:20 -0400645 public long getLastIntrusive() {
646 return mLastIntrusive;
647 }
648
Chris Wren54bbef42014-07-09 18:37:56 -0400649 public void setPackagePriority(int packagePriority) {
John Spurlock6ac5f8d2014-07-18 11:27:54 -0400650 mPackagePriority = packagePriority;
Chris Wren54bbef42014-07-09 18:37:56 -0400651 }
652
653 public int getPackagePriority() {
654 return mPackagePriority;
655 }
656
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400657 public void setPackageVisibilityOverride(int packageVisibility) {
658 mPackageVisibility = packageVisibility;
659 }
660
661 public int getPackageVisibilityOverride() {
662 return mPackageVisibility;
663 }
664
Julia Reynoldsef37f282016-02-12 09:11:27 -0500665 public void setUserImportance(int importance) {
666 mUserImportance = importance;
667 applyUserImportance();
Chris Wrenbdf33762015-12-04 15:50:51 -0500668 }
669
Julia Reynoldsef37f282016-02-12 09:11:27 -0500670 private String getUserExplanation() {
671 if (mUserExplanation == null) {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500672 mUserExplanation = mContext.getResources().getString(
673 com.android.internal.R.string.importance_from_user);
Chris Wrenbdf33762015-12-04 15:50:51 -0500674 }
Julia Reynoldsef37f282016-02-12 09:11:27 -0500675 return mUserExplanation;
Chris Wrenbdf33762015-12-04 15:50:51 -0500676 }
677
678 private String getPeopleExplanation() {
679 if (mPeopleExplanation == null) {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500680 mPeopleExplanation = mContext.getResources().getString(
681 com.android.internal.R.string.importance_from_person);
Chris Wrenbdf33762015-12-04 15:50:51 -0500682 }
683 return mPeopleExplanation;
684 }
685
Julia Reynoldsef37f282016-02-12 09:11:27 -0500686 private void applyUserImportance() {
Julia Reynolds85769912016-10-25 09:08:57 -0400687 if (mUserImportance != IMPORTANCE_UNSPECIFIED) {
Julia Reynoldsef37f282016-02-12 09:11:27 -0500688 mImportance = mUserImportance;
689 mImportanceExplanation = getUserExplanation();
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500690 }
691 }
692
Julia Reynoldsef37f282016-02-12 09:11:27 -0500693 public int getUserImportance() {
694 return mUserImportance;
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500695 }
696
Chris Wrenbdf33762015-12-04 15:50:51 -0500697 public void setImportance(int importance, CharSequence explanation) {
Julia Reynolds85769912016-10-25 09:08:57 -0400698 if (importance != IMPORTANCE_UNSPECIFIED) {
Chris Wrenbdf33762015-12-04 15:50:51 -0500699 mImportance = importance;
700 mImportanceExplanation = explanation;
701 }
Julia Reynoldsef37f282016-02-12 09:11:27 -0500702 applyUserImportance();
Chris Wrenbdf33762015-12-04 15:50:51 -0500703 }
704
705 public int getImportance() {
706 return mImportance;
707 }
708
709 public CharSequence getImportanceExplanation() {
710 return mImportanceExplanation;
711 }
712
Chris Wren333a61c2014-05-28 16:40:57 -0400713 public boolean setIntercepted(boolean intercept) {
714 mIntercept = intercept;
715 return mIntercept;
716 }
717
718 public boolean isIntercepted() {
719 return mIntercept;
720 }
721
Beverly5a20a5e2018-03-06 15:02:44 -0500722 public void setHidden(boolean hidden) {
723 mHidden = hidden;
724 }
725
726 public boolean isHidden() {
727 return mHidden;
728 }
729
730
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500731 public void setSuppressedVisualEffects(int effects) {
732 mSuppressedVisualEffects = effects;
733 }
734
735 public int getSuppressedVisualEffects() {
736 return mSuppressedVisualEffects;
737 }
738
John Spurlock312d1d02014-07-08 10:24:57 -0400739 public boolean isCategory(String category) {
John Spurlockbfa5dc42014-07-28 23:30:45 -0400740 return Objects.equals(getNotification().category, category);
741 }
742
John Spurlockbfa5dc42014-07-28 23:30:45 -0400743 public boolean isAudioAttributesUsage(int usage) {
Julia Reynolds51eb78f82018-03-07 07:35:21 -0500744 return mAttributes != null && mAttributes.getUsage() == usage;
John Spurlock312d1d02014-07-08 10:24:57 -0400745 }
746
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200747 /**
748 * Returns the timestamp to use for time-based sorting in the ranker.
749 */
750 public long getRankingTimeMs() {
751 return mRankingTimeMs;
752 }
753
754 /**
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400755 * @param now this current time in milliseconds.
756 * @returns the number of milliseconds since the most recent update, or the post time if none.
Chris Wren6650e572015-05-15 17:19:25 -0400757 */
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400758 public int getFreshnessMs(long now) {
759 return (int) (now - mUpdateTimeMs);
Chris Wren6650e572015-05-15 17:19:25 -0400760 }
761
762 /**
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400763 * @param now this current time in milliseconds.
764 * @returns the number of milliseconds since the the first post, ignoring updates.
Chris Wren640e3872015-04-21 13:23:18 -0400765 */
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400766 public int getLifespanMs(long now) {
767 return (int) (now - mCreationTimeMs);
Chris Wren640e3872015-04-21 13:23:18 -0400768 }
769
770 /**
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400771 * @param now this current time in milliseconds.
772 * @returns the number of milliseconds since the most recent visibility event, or 0 if never.
Chris Wren6650e572015-05-15 17:19:25 -0400773 */
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400774 public int getExposureMs(long now) {
775 return mVisibleSinceMs == 0 ? 0 : (int) (now - mVisibleSinceMs);
Chris Wren6650e572015-05-15 17:19:25 -0400776 }
777
778 /**
779 * Set the visibility of the notification.
780 */
Dieter Hsud39f0d52018-04-14 02:08:30 +0800781 public void setVisibility(boolean visible, int rank, int count) {
Chris Wren6650e572015-05-15 17:19:25 -0400782 final long now = System.currentTimeMillis();
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400783 mVisibleSinceMs = visible ? now : mVisibleSinceMs;
Chris Wren6650e572015-05-15 17:19:25 -0400784 stats.onVisibilityChanged(visible);
Chris Wren9eb5e102017-01-26 13:15:06 -0500785 MetricsLogger.action(getLogMaker(now)
786 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
787 .setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
Dieter Hsud39f0d52018-04-14 02:08:30 +0800788 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank)
789 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, count));
Chris Wren9eb5e102017-01-26 13:15:06 -0500790 if (visible) {
Julia Reynolds503ed942017-10-04 16:04:56 -0400791 setSeen();
Chris Wren9eb5e102017-01-26 13:15:06 -0500792 MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now));
793 }
Chris Wren6650e572015-05-15 17:19:25 -0400794 EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0,
Chris Wren9eb5e102017-01-26 13:15:06 -0500795 getLifespanMs(now),
796 getFreshnessMs(now),
Chris Wrend1dbc922015-06-19 17:51:16 -0400797 0, // exposure time
798 rank);
Chris Wren6650e572015-05-15 17:19:25 -0400799 }
800
801 /**
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200802 * @param previousRankingTimeMs for updated notifications, {@link #getRankingTimeMs()}
803 * of the previous notification record, 0 otherwise
804 */
805 private long calculateRankingTimeMs(long previousRankingTimeMs) {
806 Notification n = getNotification();
807 // Take developer provided 'when', unless it's in the future.
808 if (n.when != 0 && n.when <= sbn.getPostTime()) {
809 return n.when;
810 }
811 // If we've ranked a previous instance with a timestamp, inherit it. This case is
812 // important in order to have ranking stability for updating notifications.
813 if (previousRankingTimeMs > 0) {
814 return previousRankingTimeMs;
815 }
816 return sbn.getPostTime();
817 }
Chris Wren1031c972014-07-23 13:11:45 +0000818
Christoph Studercd4adf82014-08-19 17:50:49 +0200819 public void setGlobalSortKey(String globalSortKey) {
820 mGlobalSortKey = globalSortKey;
Chris Wren1031c972014-07-23 13:11:45 +0000821 }
822
Christoph Studercd4adf82014-08-19 17:50:49 +0200823 public String getGlobalSortKey() {
824 return mGlobalSortKey;
Chris Wren1031c972014-07-23 13:11:45 +0000825 }
826
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700827 /** Check if any of the listeners have marked this notification as seen by the user. */
828 public boolean isSeen() {
Julia Reynolds503ed942017-10-04 16:04:56 -0400829 return mStats.hasSeen();
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700830 }
831
832 /** Mark the notification as seen by the user. */
833 public void setSeen() {
Julia Reynolds503ed942017-10-04 16:04:56 -0400834 mStats.setSeen();
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700835 }
836
Chris Wren1031c972014-07-23 13:11:45 +0000837 public void setAuthoritativeRank(int authoritativeRank) {
838 mAuthoritativeRank = authoritativeRank;
839 }
840
841 public int getAuthoritativeRank() {
842 return mAuthoritativeRank;
843 }
844
845 public String getGroupKey() {
846 return sbn.getGroupKey();
847 }
Chris Wren47633422016-01-22 09:56:59 -0500848
Chris Wrenb3921792017-06-01 13:34:46 -0400849 public void setOverrideGroupKey(String overrideGroupKey) {
850 sbn.setOverrideGroupKey(overrideGroupKey);
851 mGroupLogTag = null;
852 }
853
854 private String getGroupLogTag() {
855 if (mGroupLogTag == null) {
856 mGroupLogTag = shortenTag(sbn.getGroup());
857 }
858 return mGroupLogTag;
859 }
860
861 private String getChannelIdLogTag() {
862 if (mChannelIdLogTag == null) {
863 mChannelIdLogTag = shortenTag(mChannel.getId());
864 }
865 return mChannelIdLogTag;
866 }
867
868 private String shortenTag(String longTag) {
869 if (longTag == null) {
870 return null;
871 }
872 if (longTag.length() < MAX_LOGTAG_LENGTH) {
873 return longTag;
874 } else {
875 return longTag.substring(0, MAX_LOGTAG_LENGTH - 8) + "-" +
876 Integer.toHexString(longTag.hashCode());
877 }
878 }
879
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400880 public NotificationChannel getChannel() {
Julia Reynolds924eed12017-01-19 09:52:07 -0500881 return mChannel;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500882 }
883
Rohan Shah590e1b22018-04-10 23:48:47 -0400884 /**
885 * @see RankingHelper#getIsAppImportanceLocked(String, int)
886 */
887 public boolean getIsAppImportanceLocked() {
888 return mIsAppImportanceLocked;
889 }
890
Julia Reynolds924eed12017-01-19 09:52:07 -0500891 protected void updateNotificationChannel(NotificationChannel channel) {
892 if (channel != null) {
893 mChannel = channel;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500894 calculateImportance();
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500895 calculateUserSentiment();
Julia Reynolds22f02b32016-12-01 15:05:13 -0500896 }
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400897 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500898
Julia Reynolds924eed12017-01-19 09:52:07 -0500899 public void setShowBadge(boolean showBadge) {
900 mShowBadge = showBadge;
901 }
902
903 public boolean canShowBadge() {
904 return mShowBadge;
905 }
906
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500907 public Light getLight() {
908 return mLight;
909 }
910
Julia Reynolds0c299d42016-11-15 14:37:04 -0500911 public Uri getSound() {
912 return mSound;
913 }
914
915 public long[] getVibration() {
916 return mVibration;
917 }
918
919 public AudioAttributes getAudioAttributes() {
920 return mAttributes;
921 }
Julia Reynolds22f02b32016-12-01 15:05:13 -0500922
923 public ArrayList<String> getPeopleOverride() {
924 return mPeopleOverride;
925 }
926
Julia Reynolds1fac86e2018-03-07 08:30:37 -0500927 public void setInterruptive(boolean interruptive) {
928 mIsInterruptive = interruptive;
929 }
930
931 public boolean isInterruptive() {
932 return mIsInterruptive;
933 }
934
Julia Reynolds22f02b32016-12-01 15:05:13 -0500935 protected void setPeopleOverride(ArrayList<String> people) {
936 mPeopleOverride = people;
937 }
938
939 public ArrayList<SnoozeCriterion> getSnoozeCriteria() {
940 return mSnoozeCriteria;
941 }
942
943 protected void setSnoozeCriteria(ArrayList<SnoozeCriterion> snoozeCriteria) {
944 mSnoozeCriteria = snoozeCriteria;
945 }
Chris Wren9eb5e102017-01-26 13:15:06 -0500946
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500947 private void calculateUserSentiment() {
Rohan Shah590e1b22018-04-10 23:48:47 -0400948 if ((getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0
949 || mIsAppImportanceLocked) {
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500950 mUserSentiment = USER_SENTIMENT_POSITIVE;
951 }
952 }
953
Julia Reynolds503ed942017-10-04 16:04:56 -0400954 private void setUserSentiment(int userSentiment) {
955 mUserSentiment = userSentiment;
956 }
957
958 public int getUserSentiment() {
959 return mUserSentiment;
960 }
961
962 public NotificationStats getStats() {
963 return mStats;
964 }
965
966 public void recordExpanded() {
967 mStats.setExpanded();
968 }
969
970 public void recordDirectReplied() {
971 mStats.setDirectReplied();
972 }
973
974 public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) {
975 mStats.setDismissalSurface(surface);
976 }
977
978 public void recordSnoozed() {
979 mStats.setSnoozed();
980 }
981
982 public void recordViewedSettings() {
983 mStats.setViewedSettings();
984 }
985
Kenny Guy23991102018-04-05 21:18:38 +0100986 public void setNumSmartRepliesAdded(int noReplies) {
987 mNumberOfSmartRepliesAdded = noReplies;
988 }
989
990 public int getNumSmartRepliesAdded() {
991 return mNumberOfSmartRepliesAdded;
992 }
993
994 public boolean hasSeenSmartReplies() {
995 return mHasSeenSmartReplies;
996 }
997
998 public void setSeenSmartReplies(boolean hasSeenSmartReplies) {
999 mHasSeenSmartReplies = hasSeenSmartReplies;
1000 }
1001
Julia Reynoldse0d711f2017-09-01 08:50:47 -04001002 public Set<Uri> getNotificationUris() {
1003 Notification notification = getNotification();
1004 Set<Uri> uris = new ArraySet<>();
1005
1006 if (notification.sound != null) {
1007 uris.add(notification.sound);
1008 }
1009 if (notification.getChannelId() != null) {
1010 NotificationChannel channel = getChannel();
1011 if (channel != null && channel.getSound() != null) {
1012 uris.add(channel.getSound());
1013 }
1014 }
1015 if (notification.extras.containsKey(Notification.EXTRA_AUDIO_CONTENTS_URI)) {
1016 uris.add(notification.extras.getParcelable(Notification.EXTRA_AUDIO_CONTENTS_URI));
1017 }
1018 if (notification.extras.containsKey(Notification.EXTRA_BACKGROUND_IMAGE_URI)) {
1019 uris.add(notification.extras.getParcelable(Notification.EXTRA_BACKGROUND_IMAGE_URI));
1020 }
1021 if (Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
1022 Parcelable[] newMessages =
1023 notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
1024 List<Notification.MessagingStyle.Message> messages
1025 = Notification.MessagingStyle.Message.getMessagesFromBundleArray(newMessages);
1026 Parcelable[] histMessages =
1027 notification.extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
1028 messages.addAll(
1029 Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages));
1030 for (Notification.MessagingStyle.Message message : messages) {
1031 uris.add(message.getDataUri());
1032 }
1033 }
1034
1035 return uris;
1036 }
1037
Chris Wren9eb5e102017-01-26 13:15:06 -05001038 public LogMaker getLogMaker(long now) {
1039 if (mLogMaker == null) {
Chris Wrenb3921792017-06-01 13:34:46 -04001040 // initialize fields that only change on update (so a new record)
Chris Wren9eb5e102017-01-26 13:15:06 -05001041 mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN)
1042 .setPackageName(sbn.getPackageName())
1043 .addTaggedData(MetricsEvent.NOTIFICATION_ID, sbn.getId())
Chris Wrenb3921792017-06-01 13:34:46 -04001044 .addTaggedData(MetricsEvent.NOTIFICATION_TAG, sbn.getTag())
1045 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID, getChannelIdLogTag());
Chris Wren9eb5e102017-01-26 13:15:06 -05001046 }
Chris Wrenb3921792017-06-01 13:34:46 -04001047 // reset fields that can change between updates, or are used by multiple logs
Chris Wren9eb5e102017-01-26 13:15:06 -05001048 return mLogMaker
Chris Wrena7c1b802017-03-07 10:17:20 -05001049 .clearCategory()
1050 .clearType()
1051 .clearSubtype()
Chris Wren9eb5e102017-01-26 13:15:06 -05001052 .clearTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX)
Chris Wrenb3921792017-06-01 13:34:46 -04001053 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, mImportance)
1054 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID, getGroupLogTag())
1055 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_SUMMARY,
1056 sbn.getNotification().isGroupSummary() ? 1 : 0)
Chris Wren9eb5e102017-01-26 13:15:06 -05001057 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
1058 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
1059 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now));
1060 }
1061
1062 public LogMaker getLogMaker() {
1063 return getLogMaker(System.currentTimeMillis());
1064 }
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05001065
1066 @VisibleForTesting
1067 static final class Light {
1068 public final int color;
1069 public final int onMs;
1070 public final int offMs;
1071
1072 public Light(int color, int onMs, int offMs) {
1073 this.color = color;
1074 this.onMs = onMs;
1075 this.offMs = offMs;
1076 }
1077
1078 @Override
1079 public boolean equals(Object o) {
1080 if (this == o) return true;
1081 if (o == null || getClass() != o.getClass()) return false;
1082
1083 Light light = (Light) o;
1084
1085 if (color != light.color) return false;
1086 if (onMs != light.onMs) return false;
1087 return offMs == light.offMs;
1088
1089 }
1090
1091 @Override
1092 public int hashCode() {
1093 int result = color;
1094 result = 31 * result + onMs;
1095 result = 31 * result + offMs;
1096 return result;
1097 }
1098
1099 @Override
1100 public String toString() {
1101 return "Light{" +
1102 "color=" + color +
1103 ", onMs=" + onMs +
1104 ", offMs=" + offMs +
1105 '}';
1106 }
1107 }
Chris Wren333a61c2014-05-28 16:40:57 -04001108}