blob: 6953ffddaf334966883dec4d616db501b3667f53 [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;
Tony Mantlerab55f0f2017-06-16 10:50:00 -070028import android.content.pm.PackageManager;
Chris Wren333a61c2014-05-28 16:40:57 -040029import android.content.pm.PackageManager.NameNotFoundException;
30import android.content.res.Resources;
31import android.graphics.Bitmap;
Dan Sandlerd63f9322015-05-06 15:18:49 -040032import android.graphics.drawable.Icon;
John Spurlockbfa5dc42014-07-28 23:30:45 -040033import android.media.AudioAttributes;
Julia Reynolds0c299d42016-11-15 14:37:04 -050034import android.media.AudioSystem;
Chris Wren9eb5e102017-01-26 13:15:06 -050035import android.metrics.LogMaker;
Julia Reynolds0c299d42016-11-15 14:37:04 -050036import android.net.Uri;
37import android.os.Build;
Chris Wrenda4bd202014-09-04 15:53:52 -040038import android.os.UserHandle;
Julia Reynolds0c299d42016-11-15 14:37:04 -050039import android.provider.Settings;
Julia Reynolds5d25ee72015-11-20 15:38:20 -050040import android.service.notification.NotificationListenerService;
Julia Reynoldsc9842c12017-02-07 12:46:41 -050041import android.service.notification.NotificationRecordProto;
Julia Reynolds22f02b32016-12-01 15:05:13 -050042import android.service.notification.SnoozeCriterion;
Chris Wren333a61c2014-05-28 16:40:57 -040043import android.service.notification.StatusBarNotification;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -050044import android.text.TextUtils;
Julia Reynoldsf0f629f2016-02-25 09:34:04 -050045import android.util.Log;
Julia Reynolds0c299d42016-11-15 14:37:04 -050046import android.util.Slog;
Julia Reynolds2a128742016-11-28 14:29:25 -050047import android.util.TimeUtils;
Julia Reynoldsc9842c12017-02-07 12:46:41 -050048import android.util.proto.ProtoOutputStream;
Dan Sandler0a2308e2017-05-30 19:50:42 -040049import android.widget.RemoteViews;
John Spurlockbfa5dc42014-07-28 23:30:45 -040050
Chris Wren1031c972014-07-23 13:11:45 +000051import com.android.internal.annotations.VisibleForTesting;
Chris Wren9eb5e102017-01-26 13:15:06 -050052import com.android.internal.logging.MetricsLogger;
53import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Chris Wren6650e572015-05-15 17:19:25 -040054import com.android.server.EventLogTags;
Chris Wren333a61c2014-05-28 16:40:57 -040055
56import java.io.PrintWriter;
57import java.lang.reflect.Array;
Julia Reynolds22f02b32016-12-01 15:05:13 -050058import java.util.ArrayList;
Chris Wren333a61c2014-05-28 16:40:57 -040059import java.util.Arrays;
John Spurlock312d1d02014-07-08 10:24:57 -040060import java.util.Objects;
Chris Wren333a61c2014-05-28 16:40:57 -040061
62/**
63 * Holds data about notifications that should not be shared with the
64 * {@link android.service.notification.NotificationListenerService}s.
65 *
66 * <p>These objects should not be mutated unless the code is synchronized
Julia Reynolds88860ce2017-06-01 16:55:49 -040067 * on {@link NotificationManagerService#mNotificationLock}, and any
Chris Wren333a61c2014-05-28 16:40:57 -040068 * modification should be followed by a sorting of that list.</p>
69 *
70 * <p>Is sortable by {@link NotificationComparator}.</p>
71 *
72 * {@hide}
73 */
74public final class NotificationRecord {
Julia Reynoldsf0f629f2016-02-25 09:34:04 -050075 static final String TAG = "NotificationRecord";
76 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Chris Wrenb3921792017-06-01 13:34:46 -040077 private static final int MAX_LOGTAG_LENGTH = 35;
Chris Wren333a61c2014-05-28 16:40:57 -040078 final StatusBarNotification sbn;
Christoph Studer365e4c32014-09-18 20:35:36 +020079 final int mOriginalFlags;
Chris Wrenbdf33762015-12-04 15:50:51 -050080 private final Context mContext;
Christoph Studer365e4c32014-09-18 20:35:36 +020081
Chris Wren333a61c2014-05-28 16:40:57 -040082 NotificationUsageStats.SingleNotificationStats stats;
83 boolean isCanceled;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070084 /** Whether the notification was seen by the user via one of the notification listeners. */
85 boolean mIsSeen;
Chris Wren333a61c2014-05-28 16:40:57 -040086
87 // These members are used by NotificationSignalExtractors
88 // to communicate with the ranking module.
89 private float mContactAffinity;
90 private boolean mRecentlyIntrusive;
Julia Reynolds309d1c82017-05-03 16:00:20 -040091 private long mLastIntrusive;
Chris Wren333a61c2014-05-28 16:40:57 -040092
93 // is this notification currently being intercepted by Zen Mode?
94 private boolean mIntercept;
Chris Wren333a61c2014-05-28 16:40:57 -040095
Christoph Studer52b7a5a2014-06-06 16:09:15 +020096 // The timestamp used for ranking.
97 private long mRankingTimeMs;
98
Chris Wren640e3872015-04-21 13:23:18 -040099 // The first post time, stable across updates.
100 private long mCreationTimeMs;
101
Chris Wren6650e572015-05-15 17:19:25 -0400102 // The most recent visibility event.
103 private long mVisibleSinceMs;
104
105 // The most recent update time, or the creation time if no updates.
106 private long mUpdateTimeMs;
107
Chris Wrena3446562014-06-03 18:11:47 -0400108 // Is this record an update of an old record?
109 public boolean isUpdate;
Chris Wren54bbef42014-07-09 18:37:56 -0400110 private int mPackagePriority;
Chris Wrena3446562014-06-03 18:11:47 -0400111
Chris Wren1031c972014-07-23 13:11:45 +0000112 private int mAuthoritativeRank;
Christoph Studercd4adf82014-08-19 17:50:49 +0200113 private String mGlobalSortKey;
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400114 private int mPackageVisibility;
Julia Reynoldsef37f282016-02-12 09:11:27 -0500115 private int mUserImportance = IMPORTANCE_UNSPECIFIED;
Chris Wren47633422016-01-22 09:56:59 -0500116 private int mImportance = IMPORTANCE_UNSPECIFIED;
Chris Wrenbdf33762015-12-04 15:50:51 -0500117 private CharSequence mImportanceExplanation = null;
Chris Wren1031c972014-07-23 13:11:45 +0000118
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500119 private int mSuppressedVisualEffects = 0;
Julia Reynoldsef37f282016-02-12 09:11:27 -0500120 private String mUserExplanation;
Chris Wrenbdf33762015-12-04 15:50:51 -0500121 private String mPeopleExplanation;
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000122 private boolean mPreChannelsNotification = true;
Julia Reynolds0c299d42016-11-15 14:37:04 -0500123 private Uri mSound;
124 private long[] mVibration;
125 private AudioAttributes mAttributes;
Julia Reynolds924eed12017-01-19 09:52:07 -0500126 private NotificationChannel mChannel;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500127 private ArrayList<String> mPeopleOverride;
128 private ArrayList<SnoozeCriterion> mSnoozeCriteria;
Julia Reynolds924eed12017-01-19 09:52:07 -0500129 private boolean mShowBadge;
Chris Wren9eb5e102017-01-26 13:15:06 -0500130 private LogMaker mLogMaker;
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500131 private Light mLight;
Chris Wrenb3921792017-06-01 13:34:46 -0400132 private String mGroupLogTag;
133 private String mChannelIdLogTag;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500134
Chris Wren1031c972014-07-23 13:11:45 +0000135 @VisibleForTesting
Julia Reynolds924eed12017-01-19 09:52:07 -0500136 public NotificationRecord(Context context, StatusBarNotification sbn,
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000137 NotificationChannel channel)
Chris Wren333a61c2014-05-28 16:40:57 -0400138 {
139 this.sbn = sbn;
Christoph Studer365e4c32014-09-18 20:35:36 +0200140 mOriginalFlags = sbn.getNotification().flags;
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200141 mRankingTimeMs = calculateRankingTimeMs(0L);
Chris Wren640e3872015-04-21 13:23:18 -0400142 mCreationTimeMs = sbn.getPostTime();
Chris Wren6650e572015-05-15 17:19:25 -0400143 mUpdateTimeMs = mCreationTimeMs;
Chris Wrenbdf33762015-12-04 15:50:51 -0500144 mContext = context;
Chris Wrencdee8cd2016-01-25 17:10:30 -0500145 stats = new NotificationUsageStats.SingleNotificationStats();
Julia Reynolds924eed12017-01-19 09:52:07 -0500146 mChannel = channel;
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000147 mPreChannelsNotification = isPreChannelsNotification();
Julia Reynolds0c299d42016-11-15 14:37:04 -0500148 mSound = calculateSound();
149 mVibration = calculateVibration();
150 mAttributes = calculateAttributes();
151 mImportance = calculateImportance();
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500152 mLight = calculateLights();
Chris Wrenbdf33762015-12-04 15:50:51 -0500153 }
154
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000155 private boolean isPreChannelsNotification() {
156 try {
157 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
158 final ApplicationInfo applicationInfo =
159 mContext.getPackageManager().getApplicationInfoAsUser(sbn.getPackageName(),
160 0, UserHandle.getUserId(sbn.getUid()));
Dan Sandlere103b782017-05-17 16:07:56 -0700161 if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000162 return true;
163 }
164 }
165 } catch (NameNotFoundException e) {
166 Slog.e(TAG, "Can't find package", e);
167 }
168 return false;
169 }
170
Julia Reynolds0c299d42016-11-15 14:37:04 -0500171 private Uri calculateSound() {
Chris Wrenbdf33762015-12-04 15:50:51 -0500172 final Notification n = sbn.getNotification();
Julia Reynolds0c299d42016-11-15 14:37:04 -0500173
Tony Mantlerab55f0f2017-06-16 10:50:00 -0700174 // No notification sounds on tv
175 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
176 return null;
177 }
178
Julia Reynolds924eed12017-01-19 09:52:07 -0500179 Uri sound = mChannel.getSound();
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000180 if (mPreChannelsNotification && (getChannel().getUserLockedFields()
Julia Reynolds0c299d42016-11-15 14:37:04 -0500181 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
182
183 final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0;
184 if (useDefaultSound) {
185 sound = Settings.System.DEFAULT_NOTIFICATION_URI;
Julia Reynoldsb9e712e2017-04-17 10:31:03 -0400186 } else {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500187 sound = n.sound;
188 }
189 }
190 return sound;
191 }
192
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500193 private Light calculateLights() {
194 int defaultLightColor = mContext.getResources().getColor(
195 com.android.internal.R.color.config_defaultNotificationColor);
196 int defaultLightOn = mContext.getResources().getInteger(
197 com.android.internal.R.integer.config_defaultNotificationLedOn);
198 int defaultLightOff = mContext.getResources().getInteger(
199 com.android.internal.R.integer.config_defaultNotificationLedOff);
200
Julia Reynolds529e3322017-02-06 08:33:01 -0500201 int channelLightColor = getChannel().getLightColor() != 0 ? getChannel().getLightColor()
202 : defaultLightColor;
203 Light light = getChannel().shouldShowLights() ? new Light(channelLightColor,
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500204 defaultLightOn, defaultLightOff) : null;
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000205 if (mPreChannelsNotification
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500206 && (getChannel().getUserLockedFields()
207 & NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
208 final Notification notification = sbn.getNotification();
209 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
210 light = new Light(notification.ledARGB, notification.ledOnMS,
211 notification.ledOffMS);
212 if ((notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
213 light = new Light(defaultLightColor, defaultLightOn,
214 defaultLightOff);
215 }
216 } else {
217 light = null;
218 }
219 }
220 return light;
221 }
222
Julia Reynolds0c299d42016-11-15 14:37:04 -0500223 private long[] calculateVibration() {
224 long[] vibration;
225 final long[] defaultVibration = NotificationManagerService.getLongArray(
226 mContext.getResources(),
227 com.android.internal.R.array.config_defaultNotificationVibePattern,
228 NotificationManagerService.VIBRATE_PATTERN_MAXLEN,
229 NotificationManagerService.DEFAULT_VIBRATE_PATTERN);
230 if (getChannel().shouldVibrate()) {
Julia Reynoldsf57de462016-11-23 11:31:46 -0500231 vibration = getChannel().getVibrationPattern() == null
232 ? defaultVibration : getChannel().getVibrationPattern();
Julia Reynolds0c299d42016-11-15 14:37:04 -0500233 } else {
234 vibration = null;
235 }
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000236 if (mPreChannelsNotification
Julia Reynolds0c299d42016-11-15 14:37:04 -0500237 && (getChannel().getUserLockedFields()
238 & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
239 final Notification notification = sbn.getNotification();
240 final boolean useDefaultVibrate =
241 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
242 if (useDefaultVibrate) {
243 vibration = defaultVibration;
244 } else {
245 vibration = notification.vibrate;
246 }
247 }
248 return vibration;
249 }
250
251 private AudioAttributes calculateAttributes() {
252 final Notification n = sbn.getNotification();
Julia Reynolds619a69f2017-01-27 15:11:38 -0500253 AudioAttributes attributes = getChannel().getAudioAttributes();
254 if (attributes == null) {
255 attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
256 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500257
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000258 if (mPreChannelsNotification
Julia Reynolds619a69f2017-01-27 15:11:38 -0500259 && (getChannel().getUserLockedFields()
260 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
261 if (n.audioAttributes != null) {
262 // prefer audio attributes to stream type
263 attributes = n.audioAttributes;
264 } else if (n.audioStreamType >= 0
265 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
266 // the stream type is valid, use it
267 attributes = new AudioAttributes.Builder()
268 .setInternalLegacyStreamType(n.audioStreamType)
269 .build();
270 } else if (n.audioStreamType != AudioSystem.STREAM_DEFAULT) {
271 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
272 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500273 }
274 return attributes;
275 }
276
277 private int calculateImportance() {
278 final Notification n = sbn.getNotification();
279 int importance = getChannel().getImportance();
280 int requestedImportance = IMPORTANCE_DEFAULT;
Chris Wrenbdf33762015-12-04 15:50:51 -0500281
282 // Migrate notification flags to scores
283 if (0 != (n.flags & Notification.FLAG_HIGH_PRIORITY)) {
284 n.priority = Notification.PRIORITY_MAX;
285 }
286
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500287 n.priority = NotificationManagerService.clamp(n.priority, Notification.PRIORITY_MIN,
288 Notification.PRIORITY_MAX);
Chris Wrenbdf33762015-12-04 15:50:51 -0500289 switch (n.priority) {
290 case Notification.PRIORITY_MIN:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500291 requestedImportance = IMPORTANCE_MIN;
Julia Reynoldsf0f629f2016-02-25 09:34:04 -0500292 break;
Chris Wrenbdf33762015-12-04 15:50:51 -0500293 case Notification.PRIORITY_LOW:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500294 requestedImportance = IMPORTANCE_LOW;
Chris Wrenbdf33762015-12-04 15:50:51 -0500295 break;
296 case Notification.PRIORITY_DEFAULT:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500297 requestedImportance = IMPORTANCE_DEFAULT;
Chris Wrenbdf33762015-12-04 15:50:51 -0500298 break;
299 case Notification.PRIORITY_HIGH:
Chris Wrenbdf33762015-12-04 15:50:51 -0500300 case Notification.PRIORITY_MAX:
Julia Reynolds0c299d42016-11-15 14:37:04 -0500301 requestedImportance = IMPORTANCE_HIGH;
Chris Wrenbdf33762015-12-04 15:50:51 -0500302 break;
303 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500304 stats.requestedImportance = requestedImportance;
305 stats.isNoisy = mSound != null || mVibration != null;
Chris Wrenbdf33762015-12-04 15:50:51 -0500306
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000307 if (mPreChannelsNotification
Julia Reynoldsa917a112017-03-21 11:09:14 -0400308 && (importance == IMPORTANCE_UNSPECIFIED
309 || (getChannel().getUserLockedFields()
310 & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0)) {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500311 if (!stats.isNoisy && requestedImportance > IMPORTANCE_LOW) {
312 requestedImportance = IMPORTANCE_LOW;
Julia Reynolds83fa1072016-02-17 09:10:19 -0500313 }
Julia Reynolds83fa1072016-02-17 09:10:19 -0500314
Julia Reynolds0c299d42016-11-15 14:37:04 -0500315 if (stats.isNoisy) {
316 if (requestedImportance < IMPORTANCE_DEFAULT) {
317 requestedImportance = IMPORTANCE_DEFAULT;
318 }
319 }
320
321 if (n.fullScreenIntent != null) {
322 requestedImportance = IMPORTANCE_HIGH;
323 }
324 importance = requestedImportance;
Chris Wrenbdf33762015-12-04 15:50:51 -0500325 }
326
Chris Wrencdee8cd2016-01-25 17:10:30 -0500327 stats.naturalImportance = importance;
Chris Wrenbdf33762015-12-04 15:50:51 -0500328 return importance;
Chris Wren333a61c2014-05-28 16:40:57 -0400329 }
330
331 // copy any notes that the ranking system may have made before the update
332 public void copyRankingInformation(NotificationRecord previous) {
333 mContactAffinity = previous.mContactAffinity;
334 mRecentlyIntrusive = previous.mRecentlyIntrusive;
Chris Wren54bbef42014-07-09 18:37:56 -0400335 mPackagePriority = previous.mPackagePriority;
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400336 mPackageVisibility = previous.mPackageVisibility;
Chris Wren333a61c2014-05-28 16:40:57 -0400337 mIntercept = previous.mIntercept;
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200338 mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
Chris Wren640e3872015-04-21 13:23:18 -0400339 mCreationTimeMs = previous.mCreationTimeMs;
Chris Wren6650e572015-05-15 17:19:25 -0400340 mVisibleSinceMs = previous.mVisibleSinceMs;
Selim Cinek5b03ce92016-05-18 15:16:58 -0700341 if (previous.sbn.getOverrideGroupKey() != null && !sbn.isAppGroup()) {
Chris Wren8a1638f2016-05-02 16:19:14 -0400342 sbn.setOverrideGroupKey(previous.sbn.getOverrideGroupKey());
343 }
Chris Wren1f602dc2016-04-11 10:33:46 -0400344 // Don't copy importance information or mGlobalSortKey, recompute them.
Chris Wren333a61c2014-05-28 16:40:57 -0400345 }
346
347 public Notification getNotification() { return sbn.getNotification(); }
348 public int getFlags() { return sbn.getNotification().flags; }
Chris Wrenda4bd202014-09-04 15:53:52 -0400349 public UserHandle getUser() { return sbn.getUser(); }
Chris Wren333a61c2014-05-28 16:40:57 -0400350 public String getKey() { return sbn.getKey(); }
Chris Wrenda4bd202014-09-04 15:53:52 -0400351 /** @deprecated Use {@link #getUser()} instead. */
352 public int getUserId() { return sbn.getUserId(); }
Chris Wren333a61c2014-05-28 16:40:57 -0400353
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500354 void dump(ProtoOutputStream proto, boolean redact) {
355 proto.write(NotificationRecordProto.KEY, sbn.getKey());
356 if (getChannel() != null) {
357 proto.write(NotificationRecordProto.CHANNEL_ID, getChannel().getId());
358 }
359 proto.write(NotificationRecordProto.CAN_SHOW_LIGHT, getLight() != null);
360 proto.write(NotificationRecordProto.CAN_VIBRATE, getVibration() != null);
361 proto.write(NotificationRecordProto.FLAGS, sbn.getNotification().flags);
362 proto.write(NotificationRecordProto.GROUP_KEY, getGroupKey());
363 proto.write(NotificationRecordProto.IMPORTANCE, getImportance());
364 if (getSound() != null) {
365 proto.write(NotificationRecordProto.SOUND, getSound().toString());
366 }
367 if (getAudioAttributes() != null) {
368 proto.write(NotificationRecordProto.SOUND_USAGE, getAudioAttributes().getUsage());
369 }
370 }
371
Dan Sandler0a2308e2017-05-30 19:50:42 -0400372 String formatRemoteViews(RemoteViews rv) {
373 if (rv == null) return "null";
374 return String.format("%s/0x%08x (%d bytes): %s",
375 rv.getPackage(), rv.getLayoutId(), rv.estimateMemoryUsage(), rv.toString());
376 }
377
Dan Sandlera1770312015-07-10 13:59:29 -0400378 void dump(PrintWriter pw, String prefix, Context baseContext, boolean redact) {
Chris Wren333a61c2014-05-28 16:40:57 -0400379 final Notification notification = sbn.getNotification();
Dan Sandlerd63f9322015-05-06 15:18:49 -0400380 final Icon icon = notification.getSmallIcon();
381 String iconStr = String.valueOf(icon);
382 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
383 iconStr += " / " + idDebugString(baseContext, icon.getResPackage(), icon.getResId());
384 }
Chris Wren333a61c2014-05-28 16:40:57 -0400385 pw.println(prefix + this);
Dan Sandler0a2308e2017-05-30 19:50:42 -0400386 prefix = prefix + " ";
Julia Reynoldsa917a112017-03-21 11:09:14 -0400387 pw.println(prefix + "uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
388 pw.println(prefix + "icon=" + iconStr);
389 pw.println(prefix + "pri=" + notification.priority);
390 pw.println(prefix + "key=" + sbn.getKey());
391 pw.println(prefix + "seen=" + mIsSeen);
392 pw.println(prefix + "groupKey=" + getGroupKey());
393 pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
394 pw.println(prefix + "contentIntent=" + notification.contentIntent);
395 pw.println(prefix + "deleteIntent=" + notification.deleteIntent);
Dan Sandler0b4ceb32017-03-29 14:13:55 -0400396
397 pw.print(prefix + "tickerText=");
398 if (!TextUtils.isEmpty(notification.tickerText)) {
399 final String ticker = notification.tickerText.toString();
400 if (redact) {
401 // if the string is long enough, we allow ourselves a few bytes for debugging
402 pw.print(ticker.length() > 16 ? ticker.substring(0,8) : "");
403 pw.println("...");
404 } else {
405 pw.println(ticker);
406 }
407 } else {
408 pw.println("null");
409 }
Dan Sandler0a2308e2017-05-30 19:50:42 -0400410 pw.println(prefix + "contentView=" + formatRemoteViews(notification.contentView));
411 pw.println(prefix + "bigContentView=" + formatRemoteViews(notification.bigContentView));
412 pw.println(prefix + "headsUpContentView="
413 + formatRemoteViews(notification.headsUpContentView));
414 pw.print(prefix + String.format("color=0x%08x", notification.color));
Julia Reynoldsbad42972017-04-25 13:52:49 -0400415 pw.println(prefix + "timeout="
416 + TimeUtils.formatForLogging(notification.getTimeoutAfter()));
Chris Wren333a61c2014-05-28 16:40:57 -0400417 if (notification.actions != null && notification.actions.length > 0) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400418 pw.println(prefix + "actions={");
Chris Wren333a61c2014-05-28 16:40:57 -0400419 final int N = notification.actions.length;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500420 for (int i = 0; i < N; i++) {
Chris Wren333a61c2014-05-28 16:40:57 -0400421 final Notification.Action action = notification.actions[i];
Chris Wren1ac52a92016-02-24 14:54:52 -0500422 if (action != null) {
423 pw.println(String.format("%s [%d] \"%s\" -> %s",
424 prefix,
425 i,
426 action.title,
427 action.actionIntent == null ? "null" : action.actionIntent.toString()
428 ));
429 }
Chris Wren333a61c2014-05-28 16:40:57 -0400430 }
431 pw.println(prefix + " }");
432 }
433 if (notification.extras != null && notification.extras.size() > 0) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400434 pw.println(prefix + "extras={");
Chris Wren333a61c2014-05-28 16:40:57 -0400435 for (String key : notification.extras.keySet()) {
436 pw.print(prefix + " " + key + "=");
437 Object val = notification.extras.get(key);
438 if (val == null) {
439 pw.println("null");
440 } else {
441 pw.print(val.getClass().getSimpleName());
Dan Sandlera1770312015-07-10 13:59:29 -0400442 if (redact && (val instanceof CharSequence || val instanceof String)) {
Chris Wren333a61c2014-05-28 16:40:57 -0400443 // redact contents from bugreports
444 } else if (val instanceof Bitmap) {
445 pw.print(String.format(" (%dx%d)",
446 ((Bitmap) val).getWidth(),
447 ((Bitmap) val).getHeight()));
448 } else if (val.getClass().isArray()) {
449 final int N = Array.getLength(val);
Dan Sandlera1770312015-07-10 13:59:29 -0400450 pw.print(" (" + N + ")");
451 if (!redact) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500452 for (int j = 0; j < N; j++) {
Dan Sandlera1770312015-07-10 13:59:29 -0400453 pw.println();
454 pw.print(String.format("%s [%d] %s",
455 prefix, j, String.valueOf(Array.get(val, j))));
456 }
457 }
Chris Wren333a61c2014-05-28 16:40:57 -0400458 } else {
459 pw.print(" (" + String.valueOf(val) + ")");
460 }
461 pw.println();
462 }
463 }
Julia Reynoldsa917a112017-03-21 11:09:14 -0400464 pw.println(prefix + "}");
Chris Wren333a61c2014-05-28 16:40:57 -0400465 }
Julia Reynoldsa917a112017-03-21 11:09:14 -0400466 pw.println(prefix + "stats=" + stats.toString());
467 pw.println(prefix + "mContactAffinity=" + mContactAffinity);
468 pw.println(prefix + "mRecentlyIntrusive=" + mRecentlyIntrusive);
469 pw.println(prefix + "mPackagePriority=" + mPackagePriority);
470 pw.println(prefix + "mPackageVisibility=" + mPackageVisibility);
471 pw.println(prefix + "mUserImportance="
Julia Reynoldsef37f282016-02-12 09:11:27 -0500472 + NotificationListenerService.Ranking.importanceToString(mUserImportance));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400473 pw.println(prefix + "mImportance="
Chris Wrenbdf33762015-12-04 15:50:51 -0500474 + NotificationListenerService.Ranking.importanceToString(mImportance));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400475 pw.println(prefix + "mImportanceExplanation=" + mImportanceExplanation);
476 pw.println(prefix + "mIntercept=" + mIntercept);
477 pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
478 pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
479 pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
480 pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
481 pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
482 pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
Geoffrey Pitscha22f6442017-05-05 16:47:38 +0000483 if (mPreChannelsNotification) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400484 pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500485 notification.defaults, notification.flags));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400486 pw.println(prefix + "n.sound=" + notification.sound);
487 pw.println(prefix + "n.audioStreamType=" + notification.audioStreamType);
488 pw.println(prefix + "n.audioAttributes=" + notification.audioAttributes);
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500489 pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
490 notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
Julia Reynoldsa917a112017-03-21 11:09:14 -0400491 pw.println(prefix + "vibrate=" + Arrays.toString(notification.vibrate));
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500492 }
Julia Reynoldsa917a112017-03-21 11:09:14 -0400493 pw.println(prefix + "mSound= " + mSound);
494 pw.println(prefix + "mVibration= " + mVibration);
495 pw.println(prefix + "mAttributes= " + mAttributes);
496 pw.println(prefix + "mLight= " + mLight);
497 pw.println(prefix + "mShowBadge=" + mShowBadge);
498 pw.println(prefix + "effectiveNotificationChannel=" + getChannel());
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500499 if (getPeopleOverride() != null) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400500 pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride()));
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500501 }
502 if (getSnoozeCriteria() != null) {
Julia Reynoldsa917a112017-03-21 11:09:14 -0400503 pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500504 }
Chris Wren333a61c2014-05-28 16:40:57 -0400505 }
506
507
508 static String idDebugString(Context baseContext, String packageName, int id) {
509 Context c;
510
511 if (packageName != null) {
512 try {
513 c = baseContext.createPackageContext(packageName, 0);
514 } catch (NameNotFoundException e) {
515 c = baseContext;
516 }
517 } else {
518 c = baseContext;
519 }
520
521 Resources r = c.getResources();
522 try {
523 return r.getResourceName(id);
524 } catch (Resources.NotFoundException e) {
525 return "<name unknown>";
526 }
527 }
528
529 @Override
530 public final String toString() {
531 return String.format(
Julia Reynolds85769912016-10-25 09:08:57 -0400532 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" +
533 " channel=%s: %s)",
Chris Wren333a61c2014-05-28 16:40:57 -0400534 System.identityHashCode(this),
535 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
Julia Reynolds85769912016-10-25 09:08:57 -0400536 this.sbn.getTag(), this.mImportance, this.sbn.getKey(), this.getChannel().getId(),
Chris Wren333a61c2014-05-28 16:40:57 -0400537 this.sbn.getNotification());
538 }
539
540 public void setContactAffinity(float contactAffinity) {
541 mContactAffinity = contactAffinity;
Chris Wrenbdf33762015-12-04 15:50:51 -0500542 if (mImportance < IMPORTANCE_DEFAULT &&
Chris Wrenc977f812016-06-13 21:24:53 +0000543 mContactAffinity > ValidateNotificationPeople.VALID_CONTACT) {
Chris Wrenbdf33762015-12-04 15:50:51 -0500544 setImportance(IMPORTANCE_DEFAULT, getPeopleExplanation());
545 }
Chris Wren333a61c2014-05-28 16:40:57 -0400546 }
547
548 public float getContactAffinity() {
549 return mContactAffinity;
550 }
551
John Spurlock1d881a12015-03-18 19:21:54 -0400552 public void setRecentlyIntrusive(boolean recentlyIntrusive) {
Chris Wren333a61c2014-05-28 16:40:57 -0400553 mRecentlyIntrusive = recentlyIntrusive;
Julia Reynolds309d1c82017-05-03 16:00:20 -0400554 if (recentlyIntrusive) {
555 mLastIntrusive = System.currentTimeMillis();
556 }
Chris Wren333a61c2014-05-28 16:40:57 -0400557 }
558
559 public boolean isRecentlyIntrusive() {
560 return mRecentlyIntrusive;
561 }
562
Julia Reynolds309d1c82017-05-03 16:00:20 -0400563 public long getLastIntrusive() {
564 return mLastIntrusive;
565 }
566
Chris Wren54bbef42014-07-09 18:37:56 -0400567 public void setPackagePriority(int packagePriority) {
John Spurlock6ac5f8d2014-07-18 11:27:54 -0400568 mPackagePriority = packagePriority;
Chris Wren54bbef42014-07-09 18:37:56 -0400569 }
570
571 public int getPackagePriority() {
572 return mPackagePriority;
573 }
574
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400575 public void setPackageVisibilityOverride(int packageVisibility) {
576 mPackageVisibility = packageVisibility;
577 }
578
579 public int getPackageVisibilityOverride() {
580 return mPackageVisibility;
581 }
582
Julia Reynoldsef37f282016-02-12 09:11:27 -0500583 public void setUserImportance(int importance) {
584 mUserImportance = importance;
585 applyUserImportance();
Chris Wrenbdf33762015-12-04 15:50:51 -0500586 }
587
Julia Reynoldsef37f282016-02-12 09:11:27 -0500588 private String getUserExplanation() {
589 if (mUserExplanation == null) {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500590 mUserExplanation = mContext.getResources().getString(
591 com.android.internal.R.string.importance_from_user);
Chris Wrenbdf33762015-12-04 15:50:51 -0500592 }
Julia Reynoldsef37f282016-02-12 09:11:27 -0500593 return mUserExplanation;
Chris Wrenbdf33762015-12-04 15:50:51 -0500594 }
595
596 private String getPeopleExplanation() {
597 if (mPeopleExplanation == null) {
Julia Reynolds0c299d42016-11-15 14:37:04 -0500598 mPeopleExplanation = mContext.getResources().getString(
599 com.android.internal.R.string.importance_from_person);
Chris Wrenbdf33762015-12-04 15:50:51 -0500600 }
601 return mPeopleExplanation;
602 }
603
Julia Reynoldsef37f282016-02-12 09:11:27 -0500604 private void applyUserImportance() {
Julia Reynolds85769912016-10-25 09:08:57 -0400605 if (mUserImportance != IMPORTANCE_UNSPECIFIED) {
Julia Reynoldsef37f282016-02-12 09:11:27 -0500606 mImportance = mUserImportance;
607 mImportanceExplanation = getUserExplanation();
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500608 }
609 }
610
Julia Reynoldsef37f282016-02-12 09:11:27 -0500611 public int getUserImportance() {
612 return mUserImportance;
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500613 }
614
Chris Wrenbdf33762015-12-04 15:50:51 -0500615 public void setImportance(int importance, CharSequence explanation) {
Julia Reynolds85769912016-10-25 09:08:57 -0400616 if (importance != IMPORTANCE_UNSPECIFIED) {
Chris Wrenbdf33762015-12-04 15:50:51 -0500617 mImportance = importance;
618 mImportanceExplanation = explanation;
619 }
Julia Reynoldsef37f282016-02-12 09:11:27 -0500620 applyUserImportance();
Chris Wrenbdf33762015-12-04 15:50:51 -0500621 }
622
623 public int getImportance() {
624 return mImportance;
625 }
626
627 public CharSequence getImportanceExplanation() {
628 return mImportanceExplanation;
629 }
630
Chris Wren333a61c2014-05-28 16:40:57 -0400631 public boolean setIntercepted(boolean intercept) {
632 mIntercept = intercept;
633 return mIntercept;
634 }
635
636 public boolean isIntercepted() {
637 return mIntercept;
638 }
639
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500640 public void setSuppressedVisualEffects(int effects) {
641 mSuppressedVisualEffects = effects;
642 }
643
644 public int getSuppressedVisualEffects() {
645 return mSuppressedVisualEffects;
646 }
647
John Spurlock312d1d02014-07-08 10:24:57 -0400648 public boolean isCategory(String category) {
John Spurlockbfa5dc42014-07-28 23:30:45 -0400649 return Objects.equals(getNotification().category, category);
650 }
651
652 public boolean isAudioStream(int stream) {
653 return getNotification().audioStreamType == stream;
654 }
655
656 public boolean isAudioAttributesUsage(int usage) {
657 final AudioAttributes attributes = getNotification().audioAttributes;
658 return attributes != null && attributes.getUsage() == usage;
John Spurlock312d1d02014-07-08 10:24:57 -0400659 }
660
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200661 /**
662 * Returns the timestamp to use for time-based sorting in the ranker.
663 */
664 public long getRankingTimeMs() {
665 return mRankingTimeMs;
666 }
667
668 /**
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400669 * @param now this current time in milliseconds.
670 * @returns the number of milliseconds since the most recent update, or the post time if none.
Chris Wren6650e572015-05-15 17:19:25 -0400671 */
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400672 public int getFreshnessMs(long now) {
673 return (int) (now - mUpdateTimeMs);
Chris Wren6650e572015-05-15 17:19:25 -0400674 }
675
676 /**
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400677 * @param now this current time in milliseconds.
678 * @returns the number of milliseconds since the the first post, ignoring updates.
Chris Wren640e3872015-04-21 13:23:18 -0400679 */
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400680 public int getLifespanMs(long now) {
681 return (int) (now - mCreationTimeMs);
Chris Wren640e3872015-04-21 13:23:18 -0400682 }
683
684 /**
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400685 * @param now this current time in milliseconds.
686 * @returns the number of milliseconds since the most recent visibility event, or 0 if never.
Chris Wren6650e572015-05-15 17:19:25 -0400687 */
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400688 public int getExposureMs(long now) {
689 return mVisibleSinceMs == 0 ? 0 : (int) (now - mVisibleSinceMs);
Chris Wren6650e572015-05-15 17:19:25 -0400690 }
691
692 /**
693 * Set the visibility of the notification.
694 */
Chris Wrend1dbc922015-06-19 17:51:16 -0400695 public void setVisibility(boolean visible, int rank) {
Chris Wren6650e572015-05-15 17:19:25 -0400696 final long now = System.currentTimeMillis();
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400697 mVisibleSinceMs = visible ? now : mVisibleSinceMs;
Chris Wren6650e572015-05-15 17:19:25 -0400698 stats.onVisibilityChanged(visible);
Chris Wren9eb5e102017-01-26 13:15:06 -0500699 MetricsLogger.action(getLogMaker(now)
700 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
701 .setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
702 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank));
703 if (visible) {
704 MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now));
705 }
Chris Wren6650e572015-05-15 17:19:25 -0400706 EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0,
Chris Wren9eb5e102017-01-26 13:15:06 -0500707 getLifespanMs(now),
708 getFreshnessMs(now),
Chris Wrend1dbc922015-06-19 17:51:16 -0400709 0, // exposure time
710 rank);
Chris Wren6650e572015-05-15 17:19:25 -0400711 }
712
713 /**
Christoph Studer52b7a5a2014-06-06 16:09:15 +0200714 * @param previousRankingTimeMs for updated notifications, {@link #getRankingTimeMs()}
715 * of the previous notification record, 0 otherwise
716 */
717 private long calculateRankingTimeMs(long previousRankingTimeMs) {
718 Notification n = getNotification();
719 // Take developer provided 'when', unless it's in the future.
720 if (n.when != 0 && n.when <= sbn.getPostTime()) {
721 return n.when;
722 }
723 // If we've ranked a previous instance with a timestamp, inherit it. This case is
724 // important in order to have ranking stability for updating notifications.
725 if (previousRankingTimeMs > 0) {
726 return previousRankingTimeMs;
727 }
728 return sbn.getPostTime();
729 }
Chris Wren1031c972014-07-23 13:11:45 +0000730
Christoph Studercd4adf82014-08-19 17:50:49 +0200731 public void setGlobalSortKey(String globalSortKey) {
732 mGlobalSortKey = globalSortKey;
Chris Wren1031c972014-07-23 13:11:45 +0000733 }
734
Christoph Studercd4adf82014-08-19 17:50:49 +0200735 public String getGlobalSortKey() {
736 return mGlobalSortKey;
Chris Wren1031c972014-07-23 13:11:45 +0000737 }
738
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700739 /** Check if any of the listeners have marked this notification as seen by the user. */
740 public boolean isSeen() {
741 return mIsSeen;
742 }
743
744 /** Mark the notification as seen by the user. */
745 public void setSeen() {
746 mIsSeen = true;
747 }
748
Chris Wren1031c972014-07-23 13:11:45 +0000749 public void setAuthoritativeRank(int authoritativeRank) {
750 mAuthoritativeRank = authoritativeRank;
751 }
752
753 public int getAuthoritativeRank() {
754 return mAuthoritativeRank;
755 }
756
757 public String getGroupKey() {
758 return sbn.getGroupKey();
759 }
Chris Wren47633422016-01-22 09:56:59 -0500760
Chris Wrenb3921792017-06-01 13:34:46 -0400761 public void setOverrideGroupKey(String overrideGroupKey) {
762 sbn.setOverrideGroupKey(overrideGroupKey);
763 mGroupLogTag = null;
764 }
765
766 private String getGroupLogTag() {
767 if (mGroupLogTag == null) {
768 mGroupLogTag = shortenTag(sbn.getGroup());
769 }
770 return mGroupLogTag;
771 }
772
773 private String getChannelIdLogTag() {
774 if (mChannelIdLogTag == null) {
775 mChannelIdLogTag = shortenTag(mChannel.getId());
776 }
777 return mChannelIdLogTag;
778 }
779
780 private String shortenTag(String longTag) {
781 if (longTag == null) {
782 return null;
783 }
784 if (longTag.length() < MAX_LOGTAG_LENGTH) {
785 return longTag;
786 } else {
787 return longTag.substring(0, MAX_LOGTAG_LENGTH - 8) + "-" +
788 Integer.toHexString(longTag.hashCode());
789 }
790 }
791
Chris Wren47633422016-01-22 09:56:59 -0500792 public boolean isImportanceFromUser() {
Julia Reynoldsef37f282016-02-12 09:11:27 -0500793 return mImportance == mUserImportance;
Chris Wren47633422016-01-22 09:56:59 -0500794 }
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400795
796 public NotificationChannel getChannel() {
Julia Reynolds924eed12017-01-19 09:52:07 -0500797 return mChannel;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500798 }
799
Julia Reynolds924eed12017-01-19 09:52:07 -0500800 protected void updateNotificationChannel(NotificationChannel channel) {
801 if (channel != null) {
802 mChannel = channel;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500803 calculateImportance();
804 }
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400805 }
Julia Reynolds0c299d42016-11-15 14:37:04 -0500806
Julia Reynolds924eed12017-01-19 09:52:07 -0500807 public void setShowBadge(boolean showBadge) {
808 mShowBadge = showBadge;
809 }
810
811 public boolean canShowBadge() {
812 return mShowBadge;
813 }
814
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500815 public Light getLight() {
816 return mLight;
817 }
818
Julia Reynolds0c299d42016-11-15 14:37:04 -0500819 public Uri getSound() {
820 return mSound;
821 }
822
823 public long[] getVibration() {
824 return mVibration;
825 }
826
827 public AudioAttributes getAudioAttributes() {
828 return mAttributes;
829 }
Julia Reynolds22f02b32016-12-01 15:05:13 -0500830
831 public ArrayList<String> getPeopleOverride() {
832 return mPeopleOverride;
833 }
834
835 protected void setPeopleOverride(ArrayList<String> people) {
836 mPeopleOverride = people;
837 }
838
839 public ArrayList<SnoozeCriterion> getSnoozeCriteria() {
840 return mSnoozeCriteria;
841 }
842
843 protected void setSnoozeCriteria(ArrayList<SnoozeCriterion> snoozeCriteria) {
844 mSnoozeCriteria = snoozeCriteria;
845 }
Chris Wren9eb5e102017-01-26 13:15:06 -0500846
847 public LogMaker getLogMaker(long now) {
848 if (mLogMaker == null) {
Chris Wrenb3921792017-06-01 13:34:46 -0400849 // initialize fields that only change on update (so a new record)
Chris Wren9eb5e102017-01-26 13:15:06 -0500850 mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN)
851 .setPackageName(sbn.getPackageName())
852 .addTaggedData(MetricsEvent.NOTIFICATION_ID, sbn.getId())
Chris Wrenb3921792017-06-01 13:34:46 -0400853 .addTaggedData(MetricsEvent.NOTIFICATION_TAG, sbn.getTag())
854 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID, getChannelIdLogTag());
Chris Wren9eb5e102017-01-26 13:15:06 -0500855 }
Chris Wrenb3921792017-06-01 13:34:46 -0400856 // reset fields that can change between updates, or are used by multiple logs
Chris Wren9eb5e102017-01-26 13:15:06 -0500857 return mLogMaker
Chris Wrena7c1b802017-03-07 10:17:20 -0500858 .clearCategory()
859 .clearType()
860 .clearSubtype()
Chris Wren9eb5e102017-01-26 13:15:06 -0500861 .clearTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX)
Chris Wrenb3921792017-06-01 13:34:46 -0400862 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, mImportance)
863 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID, getGroupLogTag())
864 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_SUMMARY,
865 sbn.getNotification().isGroupSummary() ? 1 : 0)
Chris Wren9eb5e102017-01-26 13:15:06 -0500866 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
867 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
868 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now));
869 }
870
871 public LogMaker getLogMaker() {
872 return getLogMaker(System.currentTimeMillis());
873 }
Julia Reynoldsa33f5c42017-01-31 16:53:35 -0500874
875 @VisibleForTesting
876 static final class Light {
877 public final int color;
878 public final int onMs;
879 public final int offMs;
880
881 public Light(int color, int onMs, int offMs) {
882 this.color = color;
883 this.onMs = onMs;
884 this.offMs = offMs;
885 }
886
887 @Override
888 public boolean equals(Object o) {
889 if (this == o) return true;
890 if (o == null || getClass() != o.getClass()) return false;
891
892 Light light = (Light) o;
893
894 if (color != light.color) return false;
895 if (onMs != light.onMs) return false;
896 return offMs == light.offMs;
897
898 }
899
900 @Override
901 public int hashCode() {
902 int result = color;
903 result = 31 * result + onMs;
904 result = 31 * result + offMs;
905 return result;
906 }
907
908 @Override
909 public String toString() {
910 return "Light{" +
911 "color=" + color +
912 ", onMs=" + onMs +
913 ", offMs=" + offMs +
914 '}';
915 }
916 }
Chris Wren333a61c2014-05-28 16:40:57 -0400917}