blob: d751a2258dc6664b397a9f2d8dda4f4b87f01b58 [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 Reynolds85769912016-10-25 09:08:57 -040018import static android.app.NotificationManager.IMPORTANCE_MIN;
19import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
20import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
21import static android.app.NotificationManager.IMPORTANCE_HIGH;
22import static android.app.NotificationManager.IMPORTANCE_LOW;
Chris Wrenbdf33762015-12-04 15:50:51 -050023
Chris Wren333a61c2014-05-28 16:40:57 -040024import android.app.Notification;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -040025import android.app.NotificationChannel;
Chris Wren333a61c2014-05-28 16:40:57 -040026import android.content.Context;
Julia Reynolds0c299d42016-11-15 14:37:04 -050027import android.content.pm.ApplicationInfo;
Chris Wren333a61c2014-05-28 16:40:57 -040028import android.content.pm.PackageManager.NameNotFoundException;
29import android.content.res.Resources;
30import android.graphics.Bitmap;
Dan Sandlerd63f9322015-05-06 15:18:49 -040031import android.graphics.drawable.Icon;
John Spurlockbfa5dc42014-07-28 23:30:45 -040032import android.media.AudioAttributes;
Julia Reynolds0c299d42016-11-15 14:37:04 -050033import android.media.AudioSystem;
Chris Wren9eb5e102017-01-26 13:15:06 -050034import android.metrics.LogMaker;
Julia Reynolds0c299d42016-11-15 14:37:04 -050035import android.net.Uri;
36import android.os.Build;
Chris Wrenda4bd202014-09-04 15:53:52 -040037import android.os.UserHandle;
Julia Reynolds0c299d42016-11-15 14:37:04 -050038import android.provider.Settings;
Julia Reynolds5d25ee72015-11-20 15:38:20 -050039import android.service.notification.NotificationListenerService;
Julia Reynoldsc9842c12017-02-07 12:46:41 -050040import android.service.notification.NotificationRecordProto;
Julia Reynolds22f02b32016-12-01 15:05:13 -050041import android.service.notification.SnoozeCriterion;
Chris Wren333a61c2014-05-28 16:40:57 -040042import android.service.notification.StatusBarNotification;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -050043import android.text.TextUtils;
Julia Reynoldsf0f629f2016-02-25 09:34:04 -050044import android.util.Log;
Julia Reynolds0c299d42016-11-15 14:37:04 -050045import android.util.Slog;
Julia Reynolds2a128742016-11-28 14:29:25 -050046import android.util.TimeUtils;
Julia Reynoldsc9842c12017-02-07 12:46:41 -050047import android.util.proto.ProtoOutputStream;
John Spurlockbfa5dc42014-07-28 23:30:45 -040048
Chris Wren1031c972014-07-23 13:11:45 +000049import com.android.internal.annotations.VisibleForTesting;
Chris Wren9eb5e102017-01-26 13:15:06 -050050import com.android.internal.logging.MetricsLogger;
51import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Chris Wren6650e572015-05-15 17:19:25 -040052import com.android.server.EventLogTags;
Chris Wren333a61c2014-05-28 16:40:57 -040053
54import java.io.PrintWriter;
55import java.lang.reflect.Array;
Julia Reynolds22f02b32016-12-01 15:05:13 -050056import java.util.ArrayList;
Chris Wren333a61c2014-05-28 16:40:57 -040057import java.util.Arrays;
John Spurlock312d1d02014-07-08 10:24:57 -040058import java.util.Objects;
Chris Wren333a61c2014-05-28 16:40:57 -040059
60/**
61 * Holds data about notifications that should not be shared with the
62 * {@link android.service.notification.NotificationListenerService}s.
63 *
64 * <p>These objects should not be mutated unless the code is synchronized
65 * on {@link NotificationManagerService#mNotificationList}, and any
66 * modification should be followed by a sorting of that list.</p>
67 *
68 * <p>Is sortable by {@link NotificationComparator}.</p>
69 *
70 * {@hide}
71 */
72public final class NotificationRecord {
Julia Reynoldsf0f629f2016-02-25 09:34:04 -050073 static final String TAG = "NotificationRecord";
74 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Chris Wren333a61c2014-05-28 16:40:57 -040075 final StatusBarNotification sbn;
Christoph Studer365e4c32014-09-18 20:35:36 +020076 final int mOriginalFlags;
Chris Wrenbdf33762015-12-04 15:50:51 -050077 private final Context mContext;
Christoph Studer365e4c32014-09-18 20:35:36 +020078
Chris Wren333a61c2014-05-28 16:40:57 -040079 NotificationUsageStats.SingleNotificationStats stats;
80 boolean isCanceled;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070081 /** Whether the notification was seen by the user via one of the notification listeners. */
82 boolean mIsSeen;
Chris Wren333a61c2014-05-28 16:40:57 -040083
84 // These members are used by NotificationSignalExtractors
85 // to communicate with the ranking module.
86 private float mContactAffinity;
87 private boolean mRecentlyIntrusive;
88
89 // is this notification currently being intercepted by Zen Mode?
90 private boolean mIntercept;
Chris Wren333a61c2014-05-28 16:40:57 -040091
Christoph Studer52b7a5a2014-06-06 16:09:15 +020092 // The timestamp used for ranking.
93 private long mRankingTimeMs;
94
Chris Wren640e3872015-04-21 13:23:18 -040095 // The first post time, stable across updates.
96 private long mCreationTimeMs;
97
Chris Wren6650e572015-05-15 17:19:25 -040098 // The most recent visibility event.
99 private long mVisibleSinceMs;
100
101 // The most recent update time, or the creation time if no updates.
102 private long mUpdateTimeMs;
103
Chris Wrena3446562014-06-03 18:11:47 -0400104 // Is this record an update of an old record?
105 public boolean isUpdate;
Chris Wren54bbef42014-07-09 18:37:56 -0400106 private int mPackagePriority;
Chris Wrena3446562014-06-03 18:11:47 -0400107
Chris Wren1031c972014-07-23 13:11:45 +0000108 private int mAuthoritativeRank;
Christoph Studercd4adf82014-08-19 17:50:49 +0200109 private String mGlobalSortKey;
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400110 private int mPackageVisibility;
Julia Reynoldsef37f282016-02-12 09:11:27 -0500111 private int mUserImportance = IMPORTANCE_UNSPECIFIED;
Chris Wren47633422016-01-22 09:56:59 -0500112 private int mImportance = IMPORTANCE_UNSPECIFIED;
Chris Wrenbdf33762015-12-04 15:50:51 -0500113 private CharSequence mImportanceExplanation = null;
Chris Wren1031c972014-07-23 13:11:45 +0000114
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500115 private int mSuppressedVisualEffects = 0;
Julia Reynoldsef37f282016-02-12 09:11:27 -0500116 private String mUserExplanation;
Chris Wrenbdf33762015-12-04 15:50:51 -0500117 private String mPeopleExplanation;
Julia Reynolds0c299d42016-11-15 14:37:04 -0500118 private boolean mPreChannelsNotification = true;
119 private Uri mSound;
120 private long[] mVibration;
121 private AudioAttributes mAttributes;
Julia Reynolds924eed12017-01-19 09:52:07 -0500122 private NotificationChannel mChannel;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500123 private ArrayList<String> mPeopleOverride;
124 private ArrayList<SnoozeCriterion> mSnoozeCriteria;
Julia Reynolds924eed12017-01-19 09:52:07 -0500125 private boolean mShowBadge;
Chris Wren9eb5e102017-01-26 13:15:06 -0500126 private LogMaker mLogMaker;
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500127 private Light mLight;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500128
Chris Wren1031c972014-07-23 13:11:45 +0000129 @VisibleForTesting
Julia Reynolds924eed12017-01-19 09:52:07 -0500130 public NotificationRecord(Context context, StatusBarNotification sbn,
131 NotificationChannel channel)
Chris Wren333a61c2014-05-28 16:40:57 -0400132 {
133 this.sbn = sbn;
Christoph Studer365e4c32014-09-18 20:35:36 +0200134 mOriginalFlags = sbn.getNotification().flags;
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200135 mRankingTimeMs = calculateRankingTimeMs(0L);
Chris Wren640e3872015-04-21 13:23:18 -0400136 mCreationTimeMs = sbn.getPostTime();
Chris Wren6650e572015-05-15 17:19:25 -0400137 mUpdateTimeMs = mCreationTimeMs;
Chris Wrenbdf33762015-12-04 15:50:51 -0500138 mContext = context;
Chris Wrencdee8cd2016-01-25 17:10:30 -0500139 stats = new NotificationUsageStats.SingleNotificationStats();
Julia Reynolds924eed12017-01-19 09:52:07 -0500140 mChannel = channel;
Julia Reynolds0c299d42016-11-15 14:37:04 -0500141 mPreChannelsNotification = isPreChannelsNotification();
142 mSound = calculateSound();
143 mVibration = calculateVibration();
144 mAttributes = calculateAttributes();
145 mImportance = calculateImportance();
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500146 mLight = calculateLights();
Chris Wrenbdf33762015-12-04 15:50:51 -0500147 }
148
Julia Reynolds0c299d42016-11-15 14:37:04 -0500149 private boolean isPreChannelsNotification() {
150 try {
151 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
Julia Reynoldsd5261e12016-12-19 14:14:25 -0500152 final ApplicationInfo applicationInfo =
Julia Reynolds0c299d42016-11-15 14:37:04 -0500153 mContext.getPackageManager().getApplicationInfoAsUser(sbn.getPackageName(),
Julia Reynoldsd5261e12016-12-19 14:14:25 -0500154 0, UserHandle.getUserId(sbn.getUid()));
Julia Reynolds0c299d42016-11-15 14:37:04 -0500155 if (applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
156 return true;
157 }
158 }
159 } catch (NameNotFoundException e) {
160 Slog.e(TAG, "Can't find package", e);
161 }
162 return false;
163 }
164
165 private Uri calculateSound() {
Chris Wrenbdf33762015-12-04 15:50:51 -0500166 final Notification n = sbn.getNotification();
Julia Reynolds0c299d42016-11-15 14:37:04 -0500167
Julia Reynolds924eed12017-01-19 09:52:07 -0500168 Uri sound = mChannel.getSound();
Julia Reynolds0c299d42016-11-15 14:37:04 -0500169 if (mPreChannelsNotification && (getChannel().getUserLockedFields()
170 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
171
172 final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0;
173 if (useDefaultSound) {
174 sound = Settings.System.DEFAULT_NOTIFICATION_URI;
175 } else if (n.sound != null) {
176 sound = n.sound;
177 }
178 }
179 return sound;
180 }
181
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500182 private Light calculateLights() {
183 int defaultLightColor = mContext.getResources().getColor(
184 com.android.internal.R.color.config_defaultNotificationColor);
185 int defaultLightOn = mContext.getResources().getInteger(
186 com.android.internal.R.integer.config_defaultNotificationLedOn);
187 int defaultLightOff = mContext.getResources().getInteger(
188 com.android.internal.R.integer.config_defaultNotificationLedOff);
189
Julia Reynolds529e3322017-02-06 08:33:01 -0500190 int channelLightColor = getChannel().getLightColor() != 0 ? getChannel().getLightColor()
191 : defaultLightColor;
192 Light light = getChannel().shouldShowLights() ? new Light(channelLightColor,
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500193 defaultLightOn, defaultLightOff) : null;
194 if (mPreChannelsNotification
195 && (getChannel().getUserLockedFields()
196 & NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
197 final Notification notification = sbn.getNotification();
198 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
199 light = new Light(notification.ledARGB, notification.ledOnMS,
200 notification.ledOffMS);
201 if ((notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
202 light = new Light(defaultLightColor, defaultLightOn,
203 defaultLightOff);
204 }
205 } else {
206 light = null;
207 }
208 }
209 return light;
210 }
211
Julia Reynolds0c299d42016-11-15 14:37:04 -0500212 private long[] calculateVibration() {
213 long[] vibration;
214 final long[] defaultVibration = NotificationManagerService.getLongArray(
215 mContext.getResources(),
216 com.android.internal.R.array.config_defaultNotificationVibePattern,
217 NotificationManagerService.VIBRATE_PATTERN_MAXLEN,
218 NotificationManagerService.DEFAULT_VIBRATE_PATTERN);
219 if (getChannel().shouldVibrate()) {
Julia Reynoldsf57de462016-11-23 11:31:46 -0500220 vibration = getChannel().getVibrationPattern() == null
221 ? defaultVibration : getChannel().getVibrationPattern();
Julia Reynolds0c299d42016-11-15 14:37:04 -0500222 } else {
223 vibration = null;
224 }
225 if (mPreChannelsNotification
226 && (getChannel().getUserLockedFields()
227 & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
228 final Notification notification = sbn.getNotification();
229 final boolean useDefaultVibrate =
230 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
231 if (useDefaultVibrate) {
232 vibration = defaultVibration;
233 } else {
234 vibration = notification.vibrate;
235 }
236 }
237 return vibration;
238 }
239
240 private AudioAttributes calculateAttributes() {
241 final Notification n = sbn.getNotification();
Julia Reynolds619a69f2017-01-27 15:11:38 -0500242 AudioAttributes attributes = getChannel().getAudioAttributes();
243 if (attributes == null) {
244 attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
245 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500246
Julia Reynolds619a69f2017-01-27 15:11:38 -0500247 if (mPreChannelsNotification
248 && (getChannel().getUserLockedFields()
249 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
250 if (n.audioAttributes != null) {
251 // prefer audio attributes to stream type
252 attributes = n.audioAttributes;
253 } else if (n.audioStreamType >= 0
254 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
255 // the stream type is valid, use it
256 attributes = new AudioAttributes.Builder()
257 .setInternalLegacyStreamType(n.audioStreamType)
258 .build();
259 } else if (n.audioStreamType != AudioSystem.STREAM_DEFAULT) {
260 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
261 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500262 }
263 return attributes;
264 }
265
266 private int calculateImportance() {
267 final Notification n = sbn.getNotification();
268 int importance = getChannel().getImportance();
269 int requestedImportance = IMPORTANCE_DEFAULT;
Chris Wrenbdf33762015-12-04 15:50:51 -0500270
271 // Migrate notification flags to scores
272 if (0 != (n.flags & Notification.FLAG_HIGH_PRIORITY)) {
273 n.priority = Notification.PRIORITY_MAX;
274 }
275
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500276 n.priority = NotificationManagerService.clamp(n.priority, Notification.PRIORITY_MIN,
277 Notification.PRIORITY_MAX);
Chris Wrenbdf33762015-12-04 15:50:51 -0500278 switch (n.priority) {
279 case Notification.PRIORITY_MIN:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500280 requestedImportance = IMPORTANCE_MIN;
Julia Reynoldsf0f629f2016-02-25 09:34:04 -0500281 break;
Chris Wrenbdf33762015-12-04 15:50:51 -0500282 case Notification.PRIORITY_LOW:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500283 requestedImportance = IMPORTANCE_LOW;
Chris Wrenbdf33762015-12-04 15:50:51 -0500284 break;
285 case Notification.PRIORITY_DEFAULT:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500286 requestedImportance = IMPORTANCE_DEFAULT;
Chris Wrenbdf33762015-12-04 15:50:51 -0500287 break;
288 case Notification.PRIORITY_HIGH:
Chris Wrenbdf33762015-12-04 15:50:51 -0500289 case Notification.PRIORITY_MAX:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500290 requestedImportance = IMPORTANCE_HIGH;
Chris Wrenbdf33762015-12-04 15:50:51 -0500291 break;
292 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500293 stats.requestedImportance = requestedImportance;
294 stats.isNoisy = mSound != null || mVibration != null;
Chris Wrenbdf33762015-12-04 15:50:51 -0500295
Julia Reynolds0c299d42016-11-15 14:37:04 -0500296 if (mPreChannelsNotification
297 && (getChannel().getUserLockedFields()
298 & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0) {
299 if (!stats.isNoisy && requestedImportance > IMPORTANCE_LOW) {
300 requestedImportance = IMPORTANCE_LOW;
Julia Reynolds83fa1072016-02-17 09:10:19 -0500301 }
Julia Reynolds83fa1072016-02-17 09:10:19 -0500302
Julia Reynolds0c299d42016-11-15 14:37:04 -0500303 if (stats.isNoisy) {
304 if (requestedImportance < IMPORTANCE_DEFAULT) {
305 requestedImportance = IMPORTANCE_DEFAULT;
306 }
307 }
308
309 if (n.fullScreenIntent != null) {
310 requestedImportance = IMPORTANCE_HIGH;
311 }
312 importance = requestedImportance;
Chris Wrenbdf33762015-12-04 15:50:51 -0500313 }
314
Chris Wrencdee8cd2016-01-25 17:10:30 -0500315 stats.naturalImportance = importance;
Chris Wrenbdf33762015-12-04 15:50:51 -0500316 return importance;
Chris Wren333a61c2014-05-28 16:40:57 -0400317 }
318
319 // copy any notes that the ranking system may have made before the update
320 public void copyRankingInformation(NotificationRecord previous) {
321 mContactAffinity = previous.mContactAffinity;
322 mRecentlyIntrusive = previous.mRecentlyIntrusive;
Chris Wren54bbef42014-07-09 18:37:56 -0400323 mPackagePriority = previous.mPackagePriority;
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400324 mPackageVisibility = previous.mPackageVisibility;
Chris Wren333a61c2014-05-28 16:40:57 -0400325 mIntercept = previous.mIntercept;
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200326 mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
Chris Wren640e3872015-04-21 13:23:18 -0400327 mCreationTimeMs = previous.mCreationTimeMs;
Chris Wren6650e572015-05-15 17:19:25 -0400328 mVisibleSinceMs = previous.mVisibleSinceMs;
Selim Cinek5b03ce92016-05-18 15:16:58 -0700329 if (previous.sbn.getOverrideGroupKey() != null && !sbn.isAppGroup()) {
Chris Wren8a1638f2016-05-02 16:19:14 -0400330 sbn.setOverrideGroupKey(previous.sbn.getOverrideGroupKey());
331 }
Chris Wren1f602dc2016-04-11 10:33:46 -0400332 // Don't copy importance information or mGlobalSortKey, recompute them.
Chris Wren333a61c2014-05-28 16:40:57 -0400333 }
334
335 public Notification getNotification() { return sbn.getNotification(); }
336 public int getFlags() { return sbn.getNotification().flags; }
Chris Wrenda4bd202014-09-04 15:53:52 -0400337 public UserHandle getUser() { return sbn.getUser(); }
Chris Wren333a61c2014-05-28 16:40:57 -0400338 public String getKey() { return sbn.getKey(); }
Chris Wrenda4bd202014-09-04 15:53:52 -0400339 /** @deprecated Use {@link #getUser()} instead. */
340 public int getUserId() { return sbn.getUserId(); }
Chris Wren333a61c2014-05-28 16:40:57 -0400341
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500342 void dump(ProtoOutputStream proto, boolean redact) {
343 proto.write(NotificationRecordProto.KEY, sbn.getKey());
344 if (getChannel() != null) {
345 proto.write(NotificationRecordProto.CHANNEL_ID, getChannel().getId());
346 }
347 proto.write(NotificationRecordProto.CAN_SHOW_LIGHT, getLight() != null);
348 proto.write(NotificationRecordProto.CAN_VIBRATE, getVibration() != null);
349 proto.write(NotificationRecordProto.FLAGS, sbn.getNotification().flags);
350 proto.write(NotificationRecordProto.GROUP_KEY, getGroupKey());
351 proto.write(NotificationRecordProto.IMPORTANCE, getImportance());
352 if (getSound() != null) {
353 proto.write(NotificationRecordProto.SOUND, getSound().toString());
354 }
355 if (getAudioAttributes() != null) {
356 proto.write(NotificationRecordProto.SOUND_USAGE, getAudioAttributes().getUsage());
357 }
358 }
359
Dan Sandlera1770312015-07-10 13:59:29 -0400360 void dump(PrintWriter pw, String prefix, Context baseContext, boolean redact) {
Chris Wren333a61c2014-05-28 16:40:57 -0400361 final Notification notification = sbn.getNotification();
Dan Sandlerd63f9322015-05-06 15:18:49 -0400362 final Icon icon = notification.getSmallIcon();
363 String iconStr = String.valueOf(icon);
364 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
365 iconStr += " / " + idDebugString(baseContext, icon.getResPackage(), icon.getResId());
366 }
Chris Wren333a61c2014-05-28 16:40:57 -0400367 pw.println(prefix + this);
368 pw.println(prefix + " uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
Dan Sandlerd63f9322015-05-06 15:18:49 -0400369 pw.println(prefix + " icon=" + iconStr);
Chris Wrenbdf33762015-12-04 15:50:51 -0500370 pw.println(prefix + " pri=" + notification.priority);
Chris Wren333a61c2014-05-28 16:40:57 -0400371 pw.println(prefix + " key=" + sbn.getKey());
Amith Yamasani24bd0be2015-04-28 10:28:09 -0700372 pw.println(prefix + " seen=" + mIsSeen);
Chris Wren1031c972014-07-23 13:11:45 +0000373 pw.println(prefix + " groupKey=" + getGroupKey());
Julia Reynolds969ad142016-08-22 08:43:01 -0400374 pw.println(prefix + " fullscreenIntent=" + notification.fullScreenIntent);
Chris Wren333a61c2014-05-28 16:40:57 -0400375 pw.println(prefix + " contentIntent=" + notification.contentIntent);
376 pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
377 pw.println(prefix + " tickerText=" + notification.tickerText);
378 pw.println(prefix + " contentView=" + notification.contentView);
Chris Wren333a61c2014-05-28 16:40:57 -0400379 pw.println(prefix + String.format(" color=0x%08x", notification.color));
Julia Reynolds2a128742016-11-28 14:29:25 -0500380 pw.println(prefix + " timeout=" + TimeUtils.formatForLogging(notification.getTimeout()));
Chris Wren333a61c2014-05-28 16:40:57 -0400381 if (notification.actions != null && notification.actions.length > 0) {
382 pw.println(prefix + " actions={");
383 final int N = notification.actions.length;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500384 for (int i = 0; i < N; i++) {
Chris Wren333a61c2014-05-28 16:40:57 -0400385 final Notification.Action action = notification.actions[i];
Chris Wren1ac52a92016-02-24 14:54:52 -0500386 if (action != null) {
387 pw.println(String.format("%s [%d] \"%s\" -> %s",
388 prefix,
389 i,
390 action.title,
391 action.actionIntent == null ? "null" : action.actionIntent.toString()
392 ));
393 }
Chris Wren333a61c2014-05-28 16:40:57 -0400394 }
395 pw.println(prefix + " }");
396 }
397 if (notification.extras != null && notification.extras.size() > 0) {
398 pw.println(prefix + " extras={");
399 for (String key : notification.extras.keySet()) {
400 pw.print(prefix + " " + key + "=");
401 Object val = notification.extras.get(key);
402 if (val == null) {
403 pw.println("null");
404 } else {
405 pw.print(val.getClass().getSimpleName());
Dan Sandlera1770312015-07-10 13:59:29 -0400406 if (redact && (val instanceof CharSequence || val instanceof String)) {
Chris Wren333a61c2014-05-28 16:40:57 -0400407 // redact contents from bugreports
408 } else if (val instanceof Bitmap) {
409 pw.print(String.format(" (%dx%d)",
410 ((Bitmap) val).getWidth(),
411 ((Bitmap) val).getHeight()));
412 } else if (val.getClass().isArray()) {
413 final int N = Array.getLength(val);
Dan Sandlera1770312015-07-10 13:59:29 -0400414 pw.print(" (" + N + ")");
415 if (!redact) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500416 for (int j = 0; j < N; j++) {
Dan Sandlera1770312015-07-10 13:59:29 -0400417 pw.println();
418 pw.print(String.format("%s [%d] %s",
419 prefix, j, String.valueOf(Array.get(val, j))));
420 }
421 }
Chris Wren333a61c2014-05-28 16:40:57 -0400422 } else {
423 pw.print(" (" + String.valueOf(val) + ")");
424 }
425 pw.println();
426 }
427 }
428 pw.println(prefix + " }");
429 }
430 pw.println(prefix + " stats=" + stats.toString());
431 pw.println(prefix + " mContactAffinity=" + mContactAffinity);
432 pw.println(prefix + " mRecentlyIntrusive=" + mRecentlyIntrusive);
Chris Wren54bbef42014-07-09 18:37:56 -0400433 pw.println(prefix + " mPackagePriority=" + mPackagePriority);
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400434 pw.println(prefix + " mPackageVisibility=" + mPackageVisibility);
Julia Reynoldsef37f282016-02-12 09:11:27 -0500435 pw.println(prefix + " mUserImportance="
436 + NotificationListenerService.Ranking.importanceToString(mUserImportance));
Chris Wrenbdf33762015-12-04 15:50:51 -0500437 pw.println(prefix + " mImportance="
438 + NotificationListenerService.Ranking.importanceToString(mImportance));
439 pw.println(prefix + " mImportanceExplanation=" + mImportanceExplanation);
Chris Wren333a61c2014-05-28 16:40:57 -0400440 pw.println(prefix + " mIntercept=" + mIntercept);
Christoph Studercd4adf82014-08-19 17:50:49 +0200441 pw.println(prefix + " mGlobalSortKey=" + mGlobalSortKey);
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200442 pw.println(prefix + " mRankingTimeMs=" + mRankingTimeMs);
Chris Wren640e3872015-04-21 13:23:18 -0400443 pw.println(prefix + " mCreationTimeMs=" + mCreationTimeMs);
Chris Wren6650e572015-05-15 17:19:25 -0400444 pw.println(prefix + " mVisibleSinceMs=" + mVisibleSinceMs);
445 pw.println(prefix + " mUpdateTimeMs=" + mUpdateTimeMs);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500446 pw.println(prefix + " mSuppressedVisualEffects= " + mSuppressedVisualEffects);
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500447 if (mPreChannelsNotification) {
448 pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x",
449 notification.defaults, notification.flags));
450 pw.println(prefix + " n.sound=" + notification.sound);
451 pw.println(prefix + " n.audioStreamType=" + notification.audioStreamType);
452 pw.println(prefix + " n.audioAttributes=" + notification.audioAttributes);
453 pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
454 notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
455 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
456 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500457 pw.println(prefix + " mSound= " + mSound);
458 pw.println(prefix + " mVibration= " + mVibration);
459 pw.println(prefix + " mAttributes= " + mAttributes);
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500460 pw.println(prefix + " mLight= " + mLight);
Julia Reynolds924eed12017-01-19 09:52:07 -0500461 pw.println(prefix + " mShowBadge=" + mShowBadge);
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500462 pw.println(prefix + " effectiveNotificationChannel=" + getChannel());
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500463 if (getPeopleOverride() != null) {
464 pw.println(prefix + " overridePeople= " + TextUtils.join(",", getPeopleOverride()));
465 }
466 if (getSnoozeCriteria() != null) {
467 pw.println(prefix + " snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
468 }
Chris Wren333a61c2014-05-28 16:40:57 -0400469 }
470
471
472 static String idDebugString(Context baseContext, String packageName, int id) {
473 Context c;
474
475 if (packageName != null) {
476 try {
477 c = baseContext.createPackageContext(packageName, 0);
478 } catch (NameNotFoundException e) {
479 c = baseContext;
480 }
481 } else {
482 c = baseContext;
483 }
484
485 Resources r = c.getResources();
486 try {
487 return r.getResourceName(id);
488 } catch (Resources.NotFoundException e) {
489 return "<name unknown>";
490 }
491 }
492
493 @Override
494 public final String toString() {
495 return String.format(
Julia Reynolds85769912016-10-25 09:08:57 -0400496 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" +
497 " channel=%s: %s)",
Chris Wren333a61c2014-05-28 16:40:57 -0400498 System.identityHashCode(this),
499 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
Julia Reynolds85769912016-10-25 09:08:57 -0400500 this.sbn.getTag(), this.mImportance, this.sbn.getKey(), this.getChannel().getId(),
Chris Wren333a61c2014-05-28 16:40:57 -0400501 this.sbn.getNotification());
502 }
503
504 public void setContactAffinity(float contactAffinity) {
505 mContactAffinity = contactAffinity;
Chris Wrenbdf33762015-12-04 15:50:51 -0500506 if (mImportance < IMPORTANCE_DEFAULT &&
Chris Wrenc977f812016-06-13 21:24:53 +0000507 mContactAffinity > ValidateNotificationPeople.VALID_CONTACT) {
Chris Wrenbdf33762015-12-04 15:50:51 -0500508 setImportance(IMPORTANCE_DEFAULT, getPeopleExplanation());
509 }
Chris Wren333a61c2014-05-28 16:40:57 -0400510 }
511
512 public float getContactAffinity() {
513 return mContactAffinity;
514 }
515
John Spurlock1d881a12015-03-18 19:21:54 -0400516 public void setRecentlyIntrusive(boolean recentlyIntrusive) {
Chris Wren333a61c2014-05-28 16:40:57 -0400517 mRecentlyIntrusive = recentlyIntrusive;
518 }
519
520 public boolean isRecentlyIntrusive() {
521 return mRecentlyIntrusive;
522 }
523
Chris Wren54bbef42014-07-09 18:37:56 -0400524 public void setPackagePriority(int packagePriority) {
John Spurlock6ac5f8d2014-07-18 11:27:54 -0400525 mPackagePriority = packagePriority;
Chris Wren54bbef42014-07-09 18:37:56 -0400526 }
527
528 public int getPackagePriority() {
529 return mPackagePriority;
530 }
531
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400532 public void setPackageVisibilityOverride(int packageVisibility) {
533 mPackageVisibility = packageVisibility;
534 }
535
536 public int getPackageVisibilityOverride() {
537 return mPackageVisibility;
538 }
539
Julia Reynoldsef37f282016-02-12 09:11:27 -0500540 public void setUserImportance(int importance) {
541 mUserImportance = importance;
542 applyUserImportance();
Chris Wrenbdf33762015-12-04 15:50:51 -0500543 }
544
Julia Reynoldsef37f282016-02-12 09:11:27 -0500545 private String getUserExplanation() {
546 if (mUserExplanation == null) {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500547 mUserExplanation = mContext.getResources().getString(
548 com.android.internal.R.string.importance_from_user);
Chris Wrenbdf33762015-12-04 15:50:51 -0500549 }
Julia Reynoldsef37f282016-02-12 09:11:27 -0500550 return mUserExplanation;
Chris Wrenbdf33762015-12-04 15:50:51 -0500551 }
552
553 private String getPeopleExplanation() {
554 if (mPeopleExplanation == null) {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500555 mPeopleExplanation = mContext.getResources().getString(
556 com.android.internal.R.string.importance_from_person);
Chris Wrenbdf33762015-12-04 15:50:51 -0500557 }
558 return mPeopleExplanation;
559 }
560
Julia Reynoldsef37f282016-02-12 09:11:27 -0500561 private void applyUserImportance() {
Julia Reynolds85769912016-10-25 09:08:57 -0400562 if (mUserImportance != IMPORTANCE_UNSPECIFIED) {
Julia Reynoldsef37f282016-02-12 09:11:27 -0500563 mImportance = mUserImportance;
564 mImportanceExplanation = getUserExplanation();
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500565 }
566 }
567
Julia Reynoldsef37f282016-02-12 09:11:27 -0500568 public int getUserImportance() {
569 return mUserImportance;
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500570 }
571
Chris Wrenbdf33762015-12-04 15:50:51 -0500572 public void setImportance(int importance, CharSequence explanation) {
Julia Reynolds85769912016-10-25 09:08:57 -0400573 if (importance != IMPORTANCE_UNSPECIFIED) {
Chris Wrenbdf33762015-12-04 15:50:51 -0500574 mImportance = importance;
575 mImportanceExplanation = explanation;
576 }
Julia Reynoldsef37f282016-02-12 09:11:27 -0500577 applyUserImportance();
Chris Wrenbdf33762015-12-04 15:50:51 -0500578 }
579
580 public int getImportance() {
581 return mImportance;
582 }
583
584 public CharSequence getImportanceExplanation() {
585 return mImportanceExplanation;
586 }
587
Chris Wren333a61c2014-05-28 16:40:57 -0400588 public boolean setIntercepted(boolean intercept) {
589 mIntercept = intercept;
590 return mIntercept;
591 }
592
593 public boolean isIntercepted() {
594 return mIntercept;
595 }
596
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500597 public void setSuppressedVisualEffects(int effects) {
598 mSuppressedVisualEffects = effects;
599 }
600
601 public int getSuppressedVisualEffects() {
602 return mSuppressedVisualEffects;
603 }
604
John Spurlock312d1d02014-07-08 10:24:57 -0400605 public boolean isCategory(String category) {
John Spurlockbfa5dc42014-07-28 23:30:45 -0400606 return Objects.equals(getNotification().category, category);
607 }
608
609 public boolean isAudioStream(int stream) {
610 return getNotification().audioStreamType == stream;
611 }
612
613 public boolean isAudioAttributesUsage(int usage) {
614 final AudioAttributes attributes = getNotification().audioAttributes;
615 return attributes != null && attributes.getUsage() == usage;
John Spurlock312d1d02014-07-08 10:24:57 -0400616 }
617
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200618 /**
619 * Returns the timestamp to use for time-based sorting in the ranker.
620 */
621 public long getRankingTimeMs() {
622 return mRankingTimeMs;
623 }
624
625 /**
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400626 * @param now this current time in milliseconds.
627 * @returns the number of milliseconds since the most recent update, or the post time if none.
Chris Wren6650e572015-05-15 17:19:25 -0400628 */
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400629 public int getFreshnessMs(long now) {
630 return (int) (now - mUpdateTimeMs);
Chris Wren6650e572015-05-15 17:19:25 -0400631 }
632
633 /**
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400634 * @param now this current time in milliseconds.
635 * @returns the number of milliseconds since the the first post, ignoring updates.
Chris Wren640e3872015-04-21 13:23:18 -0400636 */
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400637 public int getLifespanMs(long now) {
638 return (int) (now - mCreationTimeMs);
Chris Wren640e3872015-04-21 13:23:18 -0400639 }
640
641 /**
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400642 * @param now this current time in milliseconds.
643 * @returns the number of milliseconds since the most recent visibility event, or 0 if never.
Chris Wren6650e572015-05-15 17:19:25 -0400644 */
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400645 public int getExposureMs(long now) {
646 return mVisibleSinceMs == 0 ? 0 : (int) (now - mVisibleSinceMs);
Chris Wren6650e572015-05-15 17:19:25 -0400647 }
648
649 /**
650 * Set the visibility of the notification.
651 */
Chris Wrend1dbc922015-06-19 17:51:16 -0400652 public void setVisibility(boolean visible, int rank) {
Chris Wren6650e572015-05-15 17:19:25 -0400653 final long now = System.currentTimeMillis();
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400654 mVisibleSinceMs = visible ? now : mVisibleSinceMs;
Chris Wren6650e572015-05-15 17:19:25 -0400655 stats.onVisibilityChanged(visible);
Chris Wren9eb5e102017-01-26 13:15:06 -0500656 MetricsLogger.action(getLogMaker(now)
657 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
658 .setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
659 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank));
660 if (visible) {
661 MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now));
662 }
Chris Wren6650e572015-05-15 17:19:25 -0400663 EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0,
Chris Wren9eb5e102017-01-26 13:15:06 -0500664 getLifespanMs(now),
665 getFreshnessMs(now),
Chris Wrend1dbc922015-06-19 17:51:16 -0400666 0, // exposure time
667 rank);
Chris Wren6650e572015-05-15 17:19:25 -0400668 }
669
670 /**
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200671 * @param previousRankingTimeMs for updated notifications, {@link #getRankingTimeMs()}
672 * of the previous notification record, 0 otherwise
673 */
674 private long calculateRankingTimeMs(long previousRankingTimeMs) {
675 Notification n = getNotification();
676 // Take developer provided 'when', unless it's in the future.
677 if (n.when != 0 && n.when <= sbn.getPostTime()) {
678 return n.when;
679 }
680 // If we've ranked a previous instance with a timestamp, inherit it. This case is
681 // important in order to have ranking stability for updating notifications.
682 if (previousRankingTimeMs > 0) {
683 return previousRankingTimeMs;
684 }
685 return sbn.getPostTime();
686 }
Chris Wren1031c972014-07-23 13:11:45 +0000687
Christoph Studercd4adf82014-08-19 17:50:49 +0200688 public void setGlobalSortKey(String globalSortKey) {
689 mGlobalSortKey = globalSortKey;
Chris Wren1031c972014-07-23 13:11:45 +0000690 }
691
Christoph Studercd4adf82014-08-19 17:50:49 +0200692 public String getGlobalSortKey() {
693 return mGlobalSortKey;
Chris Wren1031c972014-07-23 13:11:45 +0000694 }
695
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700696 /** Check if any of the listeners have marked this notification as seen by the user. */
697 public boolean isSeen() {
698 return mIsSeen;
699 }
700
701 /** Mark the notification as seen by the user. */
702 public void setSeen() {
703 mIsSeen = true;
704 }
705
Chris Wren1031c972014-07-23 13:11:45 +0000706 public void setAuthoritativeRank(int authoritativeRank) {
707 mAuthoritativeRank = authoritativeRank;
708 }
709
710 public int getAuthoritativeRank() {
711 return mAuthoritativeRank;
712 }
713
714 public String getGroupKey() {
715 return sbn.getGroupKey();
716 }
Chris Wren47633422016-01-22 09:56:59 -0500717
718 public boolean isImportanceFromUser() {
Julia Reynoldsef37f282016-02-12 09:11:27 -0500719 return mImportance == mUserImportance;
Chris Wren47633422016-01-22 09:56:59 -0500720 }
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400721
722 public NotificationChannel getChannel() {
Julia Reynolds924eed12017-01-19 09:52:07 -0500723 return mChannel;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500724 }
725
Julia Reynolds924eed12017-01-19 09:52:07 -0500726 protected void updateNotificationChannel(NotificationChannel channel) {
727 if (channel != null) {
728 mChannel = channel;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500729 calculateImportance();
730 }
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400731 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500732
Julia Reynolds924eed12017-01-19 09:52:07 -0500733 public void setShowBadge(boolean showBadge) {
734 mShowBadge = showBadge;
735 }
736
737 public boolean canShowBadge() {
738 return mShowBadge;
739 }
740
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500741 public Light getLight() {
742 return mLight;
743 }
744
Julia Reynolds0c299d42016-11-15 14:37:04 -0500745 public Uri getSound() {
746 return mSound;
747 }
748
749 public long[] getVibration() {
750 return mVibration;
751 }
752
753 public AudioAttributes getAudioAttributes() {
754 return mAttributes;
755 }
Julia Reynolds22f02b32016-12-01 15:05:13 -0500756
757 public ArrayList<String> getPeopleOverride() {
758 return mPeopleOverride;
759 }
760
761 protected void setPeopleOverride(ArrayList<String> people) {
762 mPeopleOverride = people;
763 }
764
765 public ArrayList<SnoozeCriterion> getSnoozeCriteria() {
766 return mSnoozeCriteria;
767 }
768
769 protected void setSnoozeCriteria(ArrayList<SnoozeCriterion> snoozeCriteria) {
770 mSnoozeCriteria = snoozeCriteria;
771 }
Chris Wren9eb5e102017-01-26 13:15:06 -0500772
773 public LogMaker getLogMaker(long now) {
774 if (mLogMaker == null) {
775 mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN)
776 .setPackageName(sbn.getPackageName())
777 .addTaggedData(MetricsEvent.NOTIFICATION_ID, sbn.getId())
778 .addTaggedData(MetricsEvent.NOTIFICATION_TAG, sbn.getTag());
779 }
780 return mLogMaker
Chris Wrena7c1b802017-03-07 10:17:20 -0500781 .clearCategory()
782 .clearType()
783 .clearSubtype()
Chris Wren9eb5e102017-01-26 13:15:06 -0500784 .clearTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX)
785 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
786 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
787 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now));
788 }
789
790 public LogMaker getLogMaker() {
791 return getLogMaker(System.currentTimeMillis());
792 }
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500793
794 @VisibleForTesting
795 static final class Light {
796 public final int color;
797 public final int onMs;
798 public final int offMs;
799
800 public Light(int color, int onMs, int offMs) {
801 this.color = color;
802 this.onMs = onMs;
803 this.offMs = offMs;
804 }
805
806 @Override
807 public boolean equals(Object o) {
808 if (this == o) return true;
809 if (o == null || getClass() != o.getClass()) return false;
810
811 Light light = (Light) o;
812
813 if (color != light.color) return false;
814 if (onMs != light.onMs) return false;
815 return offMs == light.offMs;
816
817 }
818
819 @Override
820 public int hashCode() {
821 int result = color;
822 result = 31 * result + onMs;
823 result = 31 * result + offMs;
824 return result;
825 }
826
827 @Override
828 public String toString() {
829 return "Light{" +
830 "color=" + color +
831 ", onMs=" + onMs +
832 ", offMs=" + offMs +
833 '}';
834 }
835 }
Chris Wren333a61c2014-05-28 16:40:57 -0400836}