1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.notification; 17 18 import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE; 19 import static android.app.NotificationManager.IMPORTANCE_DEFAULT; 20 import static android.app.NotificationManager.IMPORTANCE_HIGH; 21 import static android.app.NotificationManager.IMPORTANCE_LOW; 22 import static android.app.NotificationManager.IMPORTANCE_MIN; 23 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; 24 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; 25 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE; 26 27 import android.annotation.Nullable; 28 import android.app.ActivityManager; 29 import android.app.IActivityManager; 30 import android.app.Notification; 31 import android.app.NotificationChannel; 32 import android.content.ContentProvider; 33 import android.content.ContentResolver; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.pm.PackageManager; 37 import android.content.pm.PackageManager.NameNotFoundException; 38 import android.content.pm.PackageManagerInternal; 39 import android.content.res.Resources; 40 import android.graphics.Bitmap; 41 import android.graphics.drawable.Icon; 42 import android.media.AudioAttributes; 43 import android.media.AudioSystem; 44 import android.metrics.LogMaker; 45 import android.net.Uri; 46 import android.os.Binder; 47 import android.os.Build; 48 import android.os.Bundle; 49 import android.os.IBinder; 50 import android.os.UserHandle; 51 import android.provider.Settings; 52 import android.service.notification.Adjustment; 53 import android.service.notification.NotificationListenerService; 54 import android.service.notification.NotificationRecordProto; 55 import android.service.notification.NotificationStats; 56 import android.service.notification.SnoozeCriterion; 57 import android.service.notification.StatusBarNotification; 58 import android.text.TextUtils; 59 import android.util.ArraySet; 60 import android.util.Log; 61 import android.util.TimeUtils; 62 import android.util.proto.ProtoOutputStream; 63 import android.widget.RemoteViews; 64 65 import com.android.internal.annotations.VisibleForTesting; 66 import com.android.internal.logging.MetricsLogger; 67 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 68 import com.android.server.EventLogTags; 69 import com.android.server.LocalServices; 70 import com.android.server.uri.UriGrantsManagerInternal; 71 72 import java.io.PrintWriter; 73 import java.lang.reflect.Array; 74 import java.util.ArrayList; 75 import java.util.Arrays; 76 import java.util.List; 77 import java.util.Objects; 78 79 /** 80 * Holds data about notifications that should not be shared with the 81 * {@link android.service.notification.NotificationListenerService}s. 82 * 83 * <p>These objects should not be mutated unless the code is synchronized 84 * on {@link NotificationManagerService#mNotificationLock}, and any 85 * modification should be followed by a sorting of that list.</p> 86 * 87 * <p>Is sortable by {@link NotificationComparator}.</p> 88 * 89 * {@hide} 90 */ 91 public final class NotificationRecord { 92 static final String TAG = "NotificationRecord"; 93 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 94 // the period after which a notification is updated where it can make sound 95 private static final int MAX_SOUND_DELAY_MS = 2000; 96 final StatusBarNotification sbn; 97 IActivityManager mAm; 98 UriGrantsManagerInternal mUgmInternal; 99 final int mTargetSdkVersion; 100 final int mOriginalFlags; 101 private final Context mContext; 102 103 NotificationUsageStats.SingleNotificationStats stats; 104 boolean isCanceled; 105 IBinder permissionOwner; 106 107 // These members are used by NotificationSignalExtractors 108 // to communicate with the ranking module. 109 private float mContactAffinity; 110 private boolean mRecentlyIntrusive; 111 private long mLastIntrusive; 112 113 // is this notification currently being intercepted by Zen Mode? 114 private boolean mIntercept; 115 116 // is this notification hidden since the app pkg is suspended? 117 private boolean mHidden; 118 119 // The timestamp used for ranking. 120 private long mRankingTimeMs; 121 122 // The first post time, stable across updates. 123 private long mCreationTimeMs; 124 125 // The most recent visibility event. 126 private long mVisibleSinceMs; 127 128 // The most recent update time, or the creation time if no updates. 129 @VisibleForTesting 130 final long mUpdateTimeMs; 131 132 // The most recent interruption time, or the creation time if no updates. Differs from the 133 // above value because updates are filtered based on whether they actually interrupted the 134 // user 135 private long mInterruptionTimeMs; 136 137 // The most recent time the notification made noise or buzzed the device, or -1 if it did not. 138 private long mLastAudiblyAlertedMs; 139 140 // Is this record an update of an old record? 141 public boolean isUpdate; 142 private int mPackagePriority; 143 144 private int mAuthoritativeRank; 145 private String mGlobalSortKey; 146 private int mPackageVisibility; 147 private int mSystemImportance = IMPORTANCE_UNSPECIFIED; 148 private int mAssistantImportance = IMPORTANCE_UNSPECIFIED; 149 private int mImportance = IMPORTANCE_UNSPECIFIED; 150 // Field used in global sort key to bypass normal notifications 151 private int mCriticality = CriticalNotificationExtractor.NORMAL; 152 // A MetricsEvent.NotificationImportanceExplanation, tracking source of mImportance. 153 private int mImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN; 154 // A MetricsEvent.NotificationImportanceExplanation for initial importance. 155 private int mInitialImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN; 156 157 private int mSuppressedVisualEffects = 0; 158 private String mUserExplanation; 159 private boolean mPreChannelsNotification = true; 160 private Uri mSound; 161 private long[] mVibration; 162 private AudioAttributes mAttributes; 163 private NotificationChannel mChannel; 164 private ArrayList<String> mPeopleOverride; 165 private ArrayList<SnoozeCriterion> mSnoozeCriteria; 166 private boolean mShowBadge; 167 private boolean mAllowBubble; 168 private Light mLight; 169 /** 170 * This list contains system generated smart actions from NAS, app-generated smart actions are 171 * stored in Notification.actions with isContextual() set to true. 172 */ 173 private ArrayList<Notification.Action> mSystemGeneratedSmartActions; 174 private ArrayList<CharSequence> mSmartReplies; 175 176 private final List<Adjustment> mAdjustments; 177 private String mAdjustmentIssuer; 178 private final NotificationStats mStats; 179 private int mUserSentiment; 180 private boolean mIsInterruptive; 181 private boolean mTextChanged; 182 private boolean mRecordedInterruption; 183 private int mNumberOfSmartRepliesAdded; 184 private int mNumberOfSmartActionsAdded; 185 private boolean mSuggestionsGeneratedByAssistant; 186 private boolean mEditChoicesBeforeSending; 187 private boolean mHasSeenSmartReplies; 188 /** 189 * Whether this notification (and its channels) should be considered user locked. Used in 190 * conjunction with user sentiment calculation. 191 */ 192 private boolean mIsAppImportanceLocked; 193 private ArraySet<Uri> mGrantableUris; 194 NotificationRecord(Context context, StatusBarNotification sbn, NotificationChannel channel)195 public NotificationRecord(Context context, StatusBarNotification sbn, 196 NotificationChannel channel) { 197 this.sbn = sbn; 198 mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class) 199 .getPackageTargetSdkVersion(sbn.getPackageName()); 200 mAm = ActivityManager.getService(); 201 mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); 202 mOriginalFlags = sbn.getNotification().flags; 203 mRankingTimeMs = calculateRankingTimeMs(0L); 204 mCreationTimeMs = sbn.getPostTime(); 205 mUpdateTimeMs = mCreationTimeMs; 206 mInterruptionTimeMs = mCreationTimeMs; 207 mContext = context; 208 stats = new NotificationUsageStats.SingleNotificationStats(); 209 mChannel = channel; 210 mPreChannelsNotification = isPreChannelsNotification(); 211 mSound = calculateSound(); 212 mVibration = calculateVibration(); 213 mAttributes = calculateAttributes(); 214 mImportance = calculateInitialImportance(); 215 mLight = calculateLights(); 216 mAdjustments = new ArrayList<>(); 217 mStats = new NotificationStats(); 218 calculateUserSentiment(); 219 calculateGrantableUris(); 220 } 221 isPreChannelsNotification()222 private boolean isPreChannelsNotification() { 223 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) { 224 if (mTargetSdkVersion < Build.VERSION_CODES.O) { 225 return true; 226 } 227 } 228 return false; 229 } 230 calculateSound()231 private Uri calculateSound() { 232 final Notification n = sbn.getNotification(); 233 234 // No notification sounds on tv 235 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { 236 return null; 237 } 238 239 Uri sound = mChannel.getSound(); 240 if (mPreChannelsNotification && (getChannel().getUserLockedFields() 241 & NotificationChannel.USER_LOCKED_SOUND) == 0) { 242 243 final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0; 244 if (useDefaultSound) { 245 sound = Settings.System.DEFAULT_NOTIFICATION_URI; 246 } else { 247 sound = n.sound; 248 } 249 } 250 return sound; 251 } 252 calculateLights()253 private Light calculateLights() { 254 int defaultLightColor = mContext.getResources().getColor( 255 com.android.internal.R.color.config_defaultNotificationColor); 256 int defaultLightOn = mContext.getResources().getInteger( 257 com.android.internal.R.integer.config_defaultNotificationLedOn); 258 int defaultLightOff = mContext.getResources().getInteger( 259 com.android.internal.R.integer.config_defaultNotificationLedOff); 260 261 int channelLightColor = getChannel().getLightColor() != 0 ? getChannel().getLightColor() 262 : defaultLightColor; 263 Light light = getChannel().shouldShowLights() ? new Light(channelLightColor, 264 defaultLightOn, defaultLightOff) : null; 265 if (mPreChannelsNotification 266 && (getChannel().getUserLockedFields() 267 & NotificationChannel.USER_LOCKED_LIGHTS) == 0) { 268 final Notification notification = sbn.getNotification(); 269 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { 270 light = new Light(notification.ledARGB, notification.ledOnMS, 271 notification.ledOffMS); 272 if ((notification.defaults & Notification.DEFAULT_LIGHTS) != 0) { 273 light = new Light(defaultLightColor, defaultLightOn, 274 defaultLightOff); 275 } 276 } else { 277 light = null; 278 } 279 } 280 return light; 281 } 282 calculateVibration()283 private long[] calculateVibration() { 284 long[] vibration; 285 final long[] defaultVibration = NotificationManagerService.getLongArray( 286 mContext.getResources(), 287 com.android.internal.R.array.config_defaultNotificationVibePattern, 288 NotificationManagerService.VIBRATE_PATTERN_MAXLEN, 289 NotificationManagerService.DEFAULT_VIBRATE_PATTERN); 290 if (getChannel().shouldVibrate()) { 291 vibration = getChannel().getVibrationPattern() == null 292 ? defaultVibration : getChannel().getVibrationPattern(); 293 } else { 294 vibration = null; 295 } 296 if (mPreChannelsNotification 297 && (getChannel().getUserLockedFields() 298 & NotificationChannel.USER_LOCKED_VIBRATION) == 0) { 299 final Notification notification = sbn.getNotification(); 300 final boolean useDefaultVibrate = 301 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 302 if (useDefaultVibrate) { 303 vibration = defaultVibration; 304 } else { 305 vibration = notification.vibrate; 306 } 307 } 308 return vibration; 309 } 310 calculateAttributes()311 private AudioAttributes calculateAttributes() { 312 final Notification n = sbn.getNotification(); 313 AudioAttributes attributes = getChannel().getAudioAttributes(); 314 if (attributes == null) { 315 attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT; 316 } 317 318 if (mPreChannelsNotification 319 && (getChannel().getUserLockedFields() 320 & NotificationChannel.USER_LOCKED_SOUND) == 0) { 321 if (n.audioAttributes != null) { 322 // prefer audio attributes to stream type 323 attributes = n.audioAttributes; 324 } else if (n.audioStreamType >= 0 325 && n.audioStreamType < AudioSystem.getNumStreamTypes()) { 326 // the stream type is valid, use it 327 attributes = new AudioAttributes.Builder() 328 .setInternalLegacyStreamType(n.audioStreamType) 329 .build(); 330 } else if (n.audioStreamType != AudioSystem.STREAM_DEFAULT) { 331 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType)); 332 } 333 } 334 return attributes; 335 } 336 calculateInitialImportance()337 private int calculateInitialImportance() { 338 final Notification n = sbn.getNotification(); 339 int importance = getChannel().getImportance(); // Post-channels notifications use this 340 mInitialImportanceExplanationCode = getChannel().hasUserSetImportance() 341 ? MetricsEvent.IMPORTANCE_EXPLANATION_USER 342 : MetricsEvent.IMPORTANCE_EXPLANATION_APP; 343 344 // Migrate notification priority flag to a priority value. 345 if (0 != (n.flags & Notification.FLAG_HIGH_PRIORITY)) { 346 n.priority = Notification.PRIORITY_MAX; 347 } 348 349 // Convert priority value to an importance value, used only for pre-channels notifications. 350 int requestedImportance = IMPORTANCE_DEFAULT; 351 n.priority = NotificationManagerService.clamp(n.priority, Notification.PRIORITY_MIN, 352 Notification.PRIORITY_MAX); 353 switch (n.priority) { 354 case Notification.PRIORITY_MIN: 355 requestedImportance = IMPORTANCE_MIN; 356 break; 357 case Notification.PRIORITY_LOW: 358 requestedImportance = IMPORTANCE_LOW; 359 break; 360 case Notification.PRIORITY_DEFAULT: 361 requestedImportance = IMPORTANCE_DEFAULT; 362 break; 363 case Notification.PRIORITY_HIGH: 364 case Notification.PRIORITY_MAX: 365 requestedImportance = IMPORTANCE_HIGH; 366 break; 367 } 368 stats.requestedImportance = requestedImportance; 369 stats.isNoisy = mSound != null || mVibration != null; 370 371 // For pre-channels notifications, apply system overrides and then use requestedImportance 372 // as importance. 373 if (mPreChannelsNotification 374 && (importance == IMPORTANCE_UNSPECIFIED 375 || (!getChannel().hasUserSetImportance()))) { 376 if (!stats.isNoisy && requestedImportance > IMPORTANCE_LOW) { 377 requestedImportance = IMPORTANCE_LOW; 378 } 379 380 if (stats.isNoisy) { 381 if (requestedImportance < IMPORTANCE_DEFAULT) { 382 requestedImportance = IMPORTANCE_DEFAULT; 383 } 384 } 385 386 if (n.fullScreenIntent != null) { 387 requestedImportance = IMPORTANCE_HIGH; 388 } 389 importance = requestedImportance; 390 mInitialImportanceExplanationCode = 391 MetricsEvent.IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS; 392 } 393 394 stats.naturalImportance = importance; 395 return importance; 396 } 397 398 // copy any notes that the ranking system may have made before the update copyRankingInformation(NotificationRecord previous)399 public void copyRankingInformation(NotificationRecord previous) { 400 mContactAffinity = previous.mContactAffinity; 401 mRecentlyIntrusive = previous.mRecentlyIntrusive; 402 mPackagePriority = previous.mPackagePriority; 403 mPackageVisibility = previous.mPackageVisibility; 404 mIntercept = previous.mIntercept; 405 mHidden = previous.mHidden; 406 mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs()); 407 mCreationTimeMs = previous.mCreationTimeMs; 408 mVisibleSinceMs = previous.mVisibleSinceMs; 409 if (previous.sbn.getOverrideGroupKey() != null && !sbn.isAppGroup()) { 410 sbn.setOverrideGroupKey(previous.sbn.getOverrideGroupKey()); 411 } 412 // Don't copy importance information or mGlobalSortKey, recompute them. 413 } 414 getNotification()415 public Notification getNotification() { return sbn.getNotification(); } getFlags()416 public int getFlags() { return sbn.getNotification().flags; } getUser()417 public UserHandle getUser() { return sbn.getUser(); } getKey()418 public String getKey() { return sbn.getKey(); } 419 /** @deprecated Use {@link #getUser()} instead. */ getUserId()420 public int getUserId() { return sbn.getUserId(); } getUid()421 public int getUid() { return sbn.getUid(); } 422 dump(ProtoOutputStream proto, long fieldId, boolean redact, int state)423 void dump(ProtoOutputStream proto, long fieldId, boolean redact, int state) { 424 final long token = proto.start(fieldId); 425 426 proto.write(NotificationRecordProto.KEY, sbn.getKey()); 427 proto.write(NotificationRecordProto.STATE, state); 428 if (getChannel() != null) { 429 proto.write(NotificationRecordProto.CHANNEL_ID, getChannel().getId()); 430 } 431 proto.write(NotificationRecordProto.CAN_SHOW_LIGHT, getLight() != null); 432 proto.write(NotificationRecordProto.CAN_VIBRATE, getVibration() != null); 433 proto.write(NotificationRecordProto.FLAGS, sbn.getNotification().flags); 434 proto.write(NotificationRecordProto.GROUP_KEY, getGroupKey()); 435 proto.write(NotificationRecordProto.IMPORTANCE, getImportance()); 436 if (getSound() != null) { 437 proto.write(NotificationRecordProto.SOUND, getSound().toString()); 438 } 439 if (getAudioAttributes() != null) { 440 getAudioAttributes().writeToProto(proto, NotificationRecordProto.AUDIO_ATTRIBUTES); 441 } 442 proto.write(NotificationRecordProto.PACKAGE, sbn.getPackageName()); 443 proto.write(NotificationRecordProto.DELEGATE_PACKAGE, sbn.getOpPkg()); 444 445 proto.end(token); 446 } 447 formatRemoteViews(RemoteViews rv)448 String formatRemoteViews(RemoteViews rv) { 449 if (rv == null) return "null"; 450 return String.format("%s/0x%08x (%d bytes): %s", 451 rv.getPackage(), rv.getLayoutId(), rv.estimateMemoryUsage(), rv.toString()); 452 } 453 dump(PrintWriter pw, String prefix, Context baseContext, boolean redact)454 void dump(PrintWriter pw, String prefix, Context baseContext, boolean redact) { 455 final Notification notification = sbn.getNotification(); 456 final Icon icon = notification.getSmallIcon(); 457 String iconStr = String.valueOf(icon); 458 if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) { 459 iconStr += " / " + idDebugString(baseContext, icon.getResPackage(), icon.getResId()); 460 } 461 pw.println(prefix + this); 462 prefix = prefix + " "; 463 pw.println(prefix + "uid=" + sbn.getUid() + " userId=" + sbn.getUserId()); 464 pw.println(prefix + "opPkg=" + sbn.getOpPkg()); 465 pw.println(prefix + "icon=" + iconStr); 466 pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags)); 467 pw.println(prefix + "pri=" + notification.priority); 468 pw.println(prefix + "key=" + sbn.getKey()); 469 pw.println(prefix + "seen=" + mStats.hasSeen()); 470 pw.println(prefix + "groupKey=" + getGroupKey()); 471 pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent); 472 pw.println(prefix + "contentIntent=" + notification.contentIntent); 473 pw.println(prefix + "deleteIntent=" + notification.deleteIntent); 474 pw.println(prefix + "number=" + notification.number); 475 pw.println(prefix + "groupAlertBehavior=" + notification.getGroupAlertBehavior()); 476 477 pw.print(prefix + "tickerText="); 478 if (!TextUtils.isEmpty(notification.tickerText)) { 479 final String ticker = notification.tickerText.toString(); 480 if (redact) { 481 // if the string is long enough, we allow ourselves a few bytes for debugging 482 pw.print(ticker.length() > 16 ? ticker.substring(0,8) : ""); 483 pw.println("..."); 484 } else { 485 pw.println(ticker); 486 } 487 } else { 488 pw.println("null"); 489 } 490 pw.println(prefix + "contentView=" + formatRemoteViews(notification.contentView)); 491 pw.println(prefix + "bigContentView=" + formatRemoteViews(notification.bigContentView)); 492 pw.println(prefix + "headsUpContentView=" 493 + formatRemoteViews(notification.headsUpContentView)); 494 pw.print(prefix + String.format("color=0x%08x", notification.color)); 495 pw.println(prefix + "timeout=" 496 + TimeUtils.formatForLogging(notification.getTimeoutAfter())); 497 if (notification.actions != null && notification.actions.length > 0) { 498 pw.println(prefix + "actions={"); 499 final int N = notification.actions.length; 500 for (int i = 0; i < N; i++) { 501 final Notification.Action action = notification.actions[i]; 502 if (action != null) { 503 pw.println(String.format("%s [%d] \"%s\" -> %s", 504 prefix, 505 i, 506 action.title, 507 action.actionIntent == null ? "null" : action.actionIntent.toString() 508 )); 509 } 510 } 511 pw.println(prefix + " }"); 512 } 513 if (notification.extras != null && notification.extras.size() > 0) { 514 pw.println(prefix + "extras={"); 515 for (String key : notification.extras.keySet()) { 516 pw.print(prefix + " " + key + "="); 517 Object val = notification.extras.get(key); 518 if (val == null) { 519 pw.println("null"); 520 } else { 521 pw.print(val.getClass().getSimpleName()); 522 if (redact && (val instanceof CharSequence || val instanceof String)) { 523 // redact contents from bugreports 524 } else if (val instanceof Bitmap) { 525 pw.print(String.format(" (%dx%d)", 526 ((Bitmap) val).getWidth(), 527 ((Bitmap) val).getHeight())); 528 } else if (val.getClass().isArray()) { 529 final int N = Array.getLength(val); 530 pw.print(" (" + N + ")"); 531 if (!redact) { 532 for (int j = 0; j < N; j++) { 533 pw.println(); 534 pw.print(String.format("%s [%d] %s", 535 prefix, j, String.valueOf(Array.get(val, j)))); 536 } 537 } 538 } else { 539 pw.print(" (" + String.valueOf(val) + ")"); 540 } 541 pw.println(); 542 } 543 } 544 pw.println(prefix + "}"); 545 } 546 pw.println(prefix + "stats=" + stats.toString()); 547 pw.println(prefix + "mContactAffinity=" + mContactAffinity); 548 pw.println(prefix + "mRecentlyIntrusive=" + mRecentlyIntrusive); 549 pw.println(prefix + "mPackagePriority=" + mPackagePriority); 550 pw.println(prefix + "mPackageVisibility=" + mPackageVisibility); 551 pw.println(prefix + "mSystemImportance=" 552 + NotificationListenerService.Ranking.importanceToString(mSystemImportance)); 553 pw.println(prefix + "mAsstImportance=" 554 + NotificationListenerService.Ranking.importanceToString(mAssistantImportance)); 555 pw.println(prefix + "mImportance=" 556 + NotificationListenerService.Ranking.importanceToString(mImportance)); 557 pw.println(prefix + "mImportanceExplanation=" + getImportanceExplanation()); 558 pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked); 559 pw.println(prefix + "mIntercept=" + mIntercept); 560 pw.println(prefix + "mHidden==" + mHidden); 561 pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey); 562 pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs); 563 pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs); 564 pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs); 565 pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs); 566 pw.println(prefix + "mInterruptionTimeMs=" + mInterruptionTimeMs); 567 pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects); 568 if (mPreChannelsNotification) { 569 pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x", 570 notification.defaults, notification.flags)); 571 pw.println(prefix + "n.sound=" + notification.sound); 572 pw.println(prefix + "n.audioStreamType=" + notification.audioStreamType); 573 pw.println(prefix + "n.audioAttributes=" + notification.audioAttributes); 574 pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d", 575 notification.ledARGB, notification.ledOnMS, notification.ledOffMS)); 576 pw.println(prefix + "vibrate=" + Arrays.toString(notification.vibrate)); 577 } 578 pw.println(prefix + "mSound= " + mSound); 579 pw.println(prefix + "mVibration= " + mVibration); 580 pw.println(prefix + "mAttributes= " + mAttributes); 581 pw.println(prefix + "mLight= " + mLight); 582 pw.println(prefix + "mShowBadge=" + mShowBadge); 583 pw.println(prefix + "mColorized=" + notification.isColorized()); 584 pw.println(prefix + "mIsInterruptive=" + mIsInterruptive); 585 pw.println(prefix + "effectiveNotificationChannel=" + getChannel()); 586 if (getPeopleOverride() != null) { 587 pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride())); 588 } 589 if (getSnoozeCriteria() != null) { 590 pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria())); 591 } 592 pw.println(prefix + "mAdjustments=" + mAdjustments); 593 } 594 595 idDebugString(Context baseContext, String packageName, int id)596 static String idDebugString(Context baseContext, String packageName, int id) { 597 Context c; 598 599 if (packageName != null) { 600 try { 601 c = baseContext.createPackageContext(packageName, 0); 602 } catch (NameNotFoundException e) { 603 c = baseContext; 604 } 605 } else { 606 c = baseContext; 607 } 608 609 Resources r = c.getResources(); 610 try { 611 return r.getResourceName(id); 612 } catch (Resources.NotFoundException e) { 613 return "<name unknown>"; 614 } 615 } 616 617 @Override toString()618 public final String toString() { 619 return String.format( 620 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" + 621 "appImportanceLocked=%s: %s)", 622 System.identityHashCode(this), 623 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(), 624 this.sbn.getTag(), this.mImportance, this.sbn.getKey(), 625 mIsAppImportanceLocked, this.sbn.getNotification()); 626 } 627 hasAdjustment(String key)628 public boolean hasAdjustment(String key) { 629 synchronized (mAdjustments) { 630 for (Adjustment adjustment : mAdjustments) { 631 if (adjustment.getSignals().containsKey(key)) { 632 return true; 633 } 634 } 635 } 636 return false; 637 } 638 addAdjustment(Adjustment adjustment)639 public void addAdjustment(Adjustment adjustment) { 640 synchronized (mAdjustments) { 641 mAdjustments.add(adjustment); 642 } 643 } 644 applyAdjustments()645 public void applyAdjustments() { 646 long now = System.currentTimeMillis(); 647 synchronized (mAdjustments) { 648 for (Adjustment adjustment: mAdjustments) { 649 Bundle signals = adjustment.getSignals(); 650 if (signals.containsKey(Adjustment.KEY_PEOPLE)) { 651 final ArrayList<String> people = 652 adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE); 653 setPeopleOverride(people); 654 } 655 if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) { 656 final ArrayList<SnoozeCriterion> snoozeCriterionList = 657 adjustment.getSignals().getParcelableArrayList( 658 Adjustment.KEY_SNOOZE_CRITERIA); 659 setSnoozeCriteria(snoozeCriterionList); 660 } 661 if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) { 662 final String groupOverrideKey = 663 adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY); 664 setOverrideGroupKey(groupOverrideKey); 665 } 666 if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) { 667 // Only allow user sentiment update from assistant if user hasn't already 668 // expressed a preference for this channel 669 if (!mIsAppImportanceLocked 670 && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) { 671 setUserSentiment(adjustment.getSignals().getInt( 672 Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL)); 673 } 674 } 675 if (signals.containsKey(Adjustment.KEY_CONTEXTUAL_ACTIONS)) { 676 setSystemGeneratedSmartActions( 677 signals.getParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS)); 678 } 679 if (signals.containsKey(Adjustment.KEY_TEXT_REPLIES)) { 680 setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES)); 681 } 682 if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) { 683 int importance = signals.getInt(Adjustment.KEY_IMPORTANCE); 684 importance = Math.max(IMPORTANCE_UNSPECIFIED, importance); 685 importance = Math.min(IMPORTANCE_HIGH, importance); 686 setAssistantImportance(importance); 687 } 688 if (!signals.isEmpty() && adjustment.getIssuer() != null) { 689 mAdjustmentIssuer = adjustment.getIssuer(); 690 } 691 } 692 // We have now gotten all the information out of the adjustments and can forget them. 693 mAdjustments.clear(); 694 } 695 } 696 setIsAppImportanceLocked(boolean isAppImportanceLocked)697 public void setIsAppImportanceLocked(boolean isAppImportanceLocked) { 698 mIsAppImportanceLocked = isAppImportanceLocked; 699 calculateUserSentiment(); 700 } 701 setContactAffinity(float contactAffinity)702 public void setContactAffinity(float contactAffinity) { 703 mContactAffinity = contactAffinity; 704 } 705 getContactAffinity()706 public float getContactAffinity() { 707 return mContactAffinity; 708 } 709 setRecentlyIntrusive(boolean recentlyIntrusive)710 public void setRecentlyIntrusive(boolean recentlyIntrusive) { 711 mRecentlyIntrusive = recentlyIntrusive; 712 if (recentlyIntrusive) { 713 mLastIntrusive = System.currentTimeMillis(); 714 } 715 } 716 isRecentlyIntrusive()717 public boolean isRecentlyIntrusive() { 718 return mRecentlyIntrusive; 719 } 720 getLastIntrusive()721 public long getLastIntrusive() { 722 return mLastIntrusive; 723 } 724 setPackagePriority(int packagePriority)725 public void setPackagePriority(int packagePriority) { 726 mPackagePriority = packagePriority; 727 } 728 getPackagePriority()729 public int getPackagePriority() { 730 return mPackagePriority; 731 } 732 setPackageVisibilityOverride(int packageVisibility)733 public void setPackageVisibilityOverride(int packageVisibility) { 734 mPackageVisibility = packageVisibility; 735 } 736 getPackageVisibilityOverride()737 public int getPackageVisibilityOverride() { 738 return mPackageVisibility; 739 } 740 getUserExplanation()741 private String getUserExplanation() { 742 if (mUserExplanation == null) { 743 mUserExplanation = mContext.getResources().getString( 744 com.android.internal.R.string.importance_from_user); 745 } 746 return mUserExplanation; 747 } 748 749 /** 750 * Sets the importance value the system thinks the record should have. 751 * e.g. bumping up foreground service notifications or people to people notifications. 752 */ setSystemImportance(int importance)753 public void setSystemImportance(int importance) { 754 mSystemImportance = importance; 755 // System importance is only changed in enqueue, so it's ok for us to calculate the 756 // importance directly instead of waiting for signal extractor. 757 calculateImportance(); 758 } 759 760 /** 761 * Sets the importance value the 762 * {@link android.service.notification.NotificationAssistantService} thinks the record should 763 * have. 764 */ setAssistantImportance(int importance)765 public void setAssistantImportance(int importance) { 766 mAssistantImportance = importance; 767 // Unlike the system importance, the assistant importance can change on posted 768 // notifications, so don't calculateImportance() here, but wait for the signal extractors. 769 } 770 771 /** 772 * Returns the importance set by the assistant, or IMPORTANCE_UNSPECIFIED if the assistant 773 * hasn't set it. 774 */ getAssistantImportance()775 public int getAssistantImportance() { 776 return mAssistantImportance; 777 } 778 779 /** 780 * Recalculates the importance of the record after fields affecting importance have changed, 781 * and records an explanation. 782 */ calculateImportance()783 protected void calculateImportance() { 784 mImportance = calculateInitialImportance(); 785 mImportanceExplanationCode = mInitialImportanceExplanationCode; 786 787 // Consider Notification Assistant and system overrides to importance. If both, system wins. 788 if (!getChannel().hasUserSetImportance() 789 && mAssistantImportance != IMPORTANCE_UNSPECIFIED 790 && !getChannel().isImportanceLockedByOEM() 791 && !getChannel().isImportanceLockedByCriticalDeviceFunction()) { 792 mImportance = mAssistantImportance; 793 mImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_ASST; 794 } 795 if (mSystemImportance != IMPORTANCE_UNSPECIFIED) { 796 mImportance = mSystemImportance; 797 mImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_SYSTEM; 798 } 799 } 800 getImportance()801 public int getImportance() { 802 return mImportance; 803 } 804 getImportanceExplanation()805 public CharSequence getImportanceExplanation() { 806 switch (mImportanceExplanationCode) { 807 case MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN: 808 return null; 809 case MetricsEvent.IMPORTANCE_EXPLANATION_APP: 810 case MetricsEvent.IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS: 811 return "app"; 812 case MetricsEvent.IMPORTANCE_EXPLANATION_USER: 813 return "user"; 814 case MetricsEvent.IMPORTANCE_EXPLANATION_ASST: 815 return "asst"; 816 case MetricsEvent.IMPORTANCE_EXPLANATION_SYSTEM: 817 return "system"; 818 } 819 return null; 820 } 821 setIntercepted(boolean intercept)822 public boolean setIntercepted(boolean intercept) { 823 mIntercept = intercept; 824 return mIntercept; 825 } 826 827 /** 828 * Set to affect global sort key. 829 * 830 * @param criticality used in a string based sort thus 0 is the most critical 831 */ setCriticality(int criticality)832 public void setCriticality(int criticality) { 833 mCriticality = criticality; 834 } 835 getCriticality()836 public int getCriticality() { 837 return mCriticality; 838 } 839 isIntercepted()840 public boolean isIntercepted() { 841 return mIntercept; 842 } 843 isNewEnoughForAlerting(long now)844 public boolean isNewEnoughForAlerting(long now) { 845 return getFreshnessMs(now) <= MAX_SOUND_DELAY_MS; 846 } 847 setHidden(boolean hidden)848 public void setHidden(boolean hidden) { 849 mHidden = hidden; 850 } 851 isHidden()852 public boolean isHidden() { 853 return mHidden; 854 } 855 856 setSuppressedVisualEffects(int effects)857 public void setSuppressedVisualEffects(int effects) { 858 mSuppressedVisualEffects = effects; 859 } 860 getSuppressedVisualEffects()861 public int getSuppressedVisualEffects() { 862 return mSuppressedVisualEffects; 863 } 864 isCategory(String category)865 public boolean isCategory(String category) { 866 return Objects.equals(getNotification().category, category); 867 } 868 isAudioAttributesUsage(int usage)869 public boolean isAudioAttributesUsage(int usage) { 870 return mAttributes != null && mAttributes.getUsage() == usage; 871 } 872 873 /** 874 * Returns the timestamp to use for time-based sorting in the ranker. 875 */ getRankingTimeMs()876 public long getRankingTimeMs() { 877 return mRankingTimeMs; 878 } 879 880 /** 881 * @param now this current time in milliseconds. 882 * @returns the number of milliseconds since the most recent update, or the post time if none. 883 */ getFreshnessMs(long now)884 public int getFreshnessMs(long now) { 885 return (int) (now - mUpdateTimeMs); 886 } 887 888 /** 889 * @param now this current time in milliseconds. 890 * @returns the number of milliseconds since the the first post, ignoring updates. 891 */ getLifespanMs(long now)892 public int getLifespanMs(long now) { 893 return (int) (now - mCreationTimeMs); 894 } 895 896 /** 897 * @param now this current time in milliseconds. 898 * @returns the number of milliseconds since the most recent visibility event, or 0 if never. 899 */ getExposureMs(long now)900 public int getExposureMs(long now) { 901 return mVisibleSinceMs == 0 ? 0 : (int) (now - mVisibleSinceMs); 902 } 903 getInterruptionMs(long now)904 public int getInterruptionMs(long now) { 905 return (int) (now - mInterruptionTimeMs); 906 } 907 908 /** 909 * Set the visibility of the notification. 910 */ setVisibility(boolean visible, int rank, int count)911 public void setVisibility(boolean visible, int rank, int count) { 912 final long now = System.currentTimeMillis(); 913 mVisibleSinceMs = visible ? now : mVisibleSinceMs; 914 stats.onVisibilityChanged(visible); 915 MetricsLogger.action(getLogMaker(now) 916 .setCategory(MetricsEvent.NOTIFICATION_ITEM) 917 .setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE) 918 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank) 919 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, count)); 920 if (visible) { 921 setSeen(); 922 MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now)); 923 } 924 EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0, 925 getLifespanMs(now), 926 getFreshnessMs(now), 927 0, // exposure time 928 rank); 929 } 930 931 /** 932 * @param previousRankingTimeMs for updated notifications, {@link #getRankingTimeMs()} 933 * of the previous notification record, 0 otherwise 934 */ calculateRankingTimeMs(long previousRankingTimeMs)935 private long calculateRankingTimeMs(long previousRankingTimeMs) { 936 Notification n = getNotification(); 937 // Take developer provided 'when', unless it's in the future. 938 if (n.when != 0 && n.when <= sbn.getPostTime()) { 939 return n.when; 940 } 941 // If we've ranked a previous instance with a timestamp, inherit it. This case is 942 // important in order to have ranking stability for updating notifications. 943 if (previousRankingTimeMs > 0) { 944 return previousRankingTimeMs; 945 } 946 return sbn.getPostTime(); 947 } 948 setGlobalSortKey(String globalSortKey)949 public void setGlobalSortKey(String globalSortKey) { 950 mGlobalSortKey = globalSortKey; 951 } 952 getGlobalSortKey()953 public String getGlobalSortKey() { 954 return mGlobalSortKey; 955 } 956 957 /** Check if any of the listeners have marked this notification as seen by the user. */ isSeen()958 public boolean isSeen() { 959 return mStats.hasSeen(); 960 } 961 962 /** Mark the notification as seen by the user. */ setSeen()963 public void setSeen() { 964 mStats.setSeen(); 965 if (mTextChanged) { 966 setInterruptive(true); 967 } 968 } 969 setAuthoritativeRank(int authoritativeRank)970 public void setAuthoritativeRank(int authoritativeRank) { 971 mAuthoritativeRank = authoritativeRank; 972 } 973 getAuthoritativeRank()974 public int getAuthoritativeRank() { 975 return mAuthoritativeRank; 976 } 977 getGroupKey()978 public String getGroupKey() { 979 return sbn.getGroupKey(); 980 } 981 setOverrideGroupKey(String overrideGroupKey)982 public void setOverrideGroupKey(String overrideGroupKey) { 983 sbn.setOverrideGroupKey(overrideGroupKey); 984 } 985 getChannel()986 public NotificationChannel getChannel() { 987 return mChannel; 988 } 989 990 /** 991 * @see PreferencesHelper#getIsAppImportanceLocked(String, int) 992 */ getIsAppImportanceLocked()993 public boolean getIsAppImportanceLocked() { 994 return mIsAppImportanceLocked; 995 } 996 updateNotificationChannel(NotificationChannel channel)997 protected void updateNotificationChannel(NotificationChannel channel) { 998 if (channel != null) { 999 mChannel = channel; 1000 calculateImportance(); 1001 calculateUserSentiment(); 1002 } 1003 } 1004 setShowBadge(boolean showBadge)1005 public void setShowBadge(boolean showBadge) { 1006 mShowBadge = showBadge; 1007 } 1008 canBubble()1009 public boolean canBubble() { 1010 return mAllowBubble; 1011 } 1012 setAllowBubble(boolean allow)1013 public void setAllowBubble(boolean allow) { 1014 mAllowBubble = allow; 1015 } 1016 canShowBadge()1017 public boolean canShowBadge() { 1018 return mShowBadge; 1019 } 1020 getLight()1021 public Light getLight() { 1022 return mLight; 1023 } 1024 getSound()1025 public Uri getSound() { 1026 return mSound; 1027 } 1028 getVibration()1029 public long[] getVibration() { 1030 return mVibration; 1031 } 1032 getAudioAttributes()1033 public AudioAttributes getAudioAttributes() { 1034 return mAttributes; 1035 } 1036 getPeopleOverride()1037 public ArrayList<String> getPeopleOverride() { 1038 return mPeopleOverride; 1039 } 1040 setInterruptive(boolean interruptive)1041 public void setInterruptive(boolean interruptive) { 1042 mIsInterruptive = interruptive; 1043 final long now = System.currentTimeMillis(); 1044 mInterruptionTimeMs = interruptive ? now : mInterruptionTimeMs; 1045 1046 if (interruptive) { 1047 MetricsLogger.action(getLogMaker() 1048 .setCategory(MetricsEvent.NOTIFICATION_INTERRUPTION) 1049 .setType(MetricsEvent.TYPE_OPEN) 1050 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS, 1051 getInterruptionMs(now))); 1052 MetricsLogger.histogram(mContext, "note_interruptive", getInterruptionMs(now)); 1053 } 1054 } 1055 setAudiblyAlerted(boolean audiblyAlerted)1056 public void setAudiblyAlerted(boolean audiblyAlerted) { 1057 mLastAudiblyAlertedMs = audiblyAlerted ? System.currentTimeMillis() : -1; 1058 } 1059 setTextChanged(boolean textChanged)1060 public void setTextChanged(boolean textChanged) { 1061 mTextChanged = textChanged; 1062 } 1063 setRecordedInterruption(boolean recorded)1064 public void setRecordedInterruption(boolean recorded) { 1065 mRecordedInterruption = recorded; 1066 } 1067 hasRecordedInterruption()1068 public boolean hasRecordedInterruption() { 1069 return mRecordedInterruption; 1070 } 1071 isInterruptive()1072 public boolean isInterruptive() { 1073 return mIsInterruptive; 1074 } 1075 1076 /** Returns the time the notification audibly alerted the user. */ getLastAudiblyAlertedMs()1077 public long getLastAudiblyAlertedMs() { 1078 return mLastAudiblyAlertedMs; 1079 } 1080 setPeopleOverride(ArrayList<String> people)1081 protected void setPeopleOverride(ArrayList<String> people) { 1082 mPeopleOverride = people; 1083 } 1084 getSnoozeCriteria()1085 public ArrayList<SnoozeCriterion> getSnoozeCriteria() { 1086 return mSnoozeCriteria; 1087 } 1088 setSnoozeCriteria(ArrayList<SnoozeCriterion> snoozeCriteria)1089 protected void setSnoozeCriteria(ArrayList<SnoozeCriterion> snoozeCriteria) { 1090 mSnoozeCriteria = snoozeCriteria; 1091 } 1092 calculateUserSentiment()1093 private void calculateUserSentiment() { 1094 if ((getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0 1095 || mIsAppImportanceLocked) { 1096 mUserSentiment = USER_SENTIMENT_POSITIVE; 1097 } 1098 } 1099 setUserSentiment(int userSentiment)1100 private void setUserSentiment(int userSentiment) { 1101 mUserSentiment = userSentiment; 1102 } 1103 getUserSentiment()1104 public int getUserSentiment() { 1105 return mUserSentiment; 1106 } 1107 getStats()1108 public NotificationStats getStats() { 1109 return mStats; 1110 } 1111 recordExpanded()1112 public void recordExpanded() { 1113 mStats.setExpanded(); 1114 } 1115 recordDirectReplied()1116 public void recordDirectReplied() { 1117 mStats.setDirectReplied(); 1118 } 1119 recordDismissalSurface(@otificationStats.DismissalSurface int surface)1120 public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) { 1121 mStats.setDismissalSurface(surface); 1122 } 1123 recordDismissalSentiment(@otificationStats.DismissalSentiment int sentiment)1124 public void recordDismissalSentiment(@NotificationStats.DismissalSentiment int sentiment) { 1125 mStats.setDismissalSentiment(sentiment); 1126 } 1127 recordSnoozed()1128 public void recordSnoozed() { 1129 mStats.setSnoozed(); 1130 } 1131 recordViewedSettings()1132 public void recordViewedSettings() { 1133 mStats.setViewedSettings(); 1134 } 1135 setNumSmartRepliesAdded(int noReplies)1136 public void setNumSmartRepliesAdded(int noReplies) { 1137 mNumberOfSmartRepliesAdded = noReplies; 1138 } 1139 getNumSmartRepliesAdded()1140 public int getNumSmartRepliesAdded() { 1141 return mNumberOfSmartRepliesAdded; 1142 } 1143 setNumSmartActionsAdded(int noActions)1144 public void setNumSmartActionsAdded(int noActions) { 1145 mNumberOfSmartActionsAdded = noActions; 1146 } 1147 getNumSmartActionsAdded()1148 public int getNumSmartActionsAdded() { 1149 return mNumberOfSmartActionsAdded; 1150 } 1151 setSuggestionsGeneratedByAssistant(boolean generatedByAssistant)1152 public void setSuggestionsGeneratedByAssistant(boolean generatedByAssistant) { 1153 mSuggestionsGeneratedByAssistant = generatedByAssistant; 1154 } 1155 getSuggestionsGeneratedByAssistant()1156 public boolean getSuggestionsGeneratedByAssistant() { 1157 return mSuggestionsGeneratedByAssistant; 1158 } 1159 getEditChoicesBeforeSending()1160 public boolean getEditChoicesBeforeSending() { 1161 return mEditChoicesBeforeSending; 1162 } 1163 setEditChoicesBeforeSending(boolean editChoicesBeforeSending)1164 public void setEditChoicesBeforeSending(boolean editChoicesBeforeSending) { 1165 mEditChoicesBeforeSending = editChoicesBeforeSending; 1166 } 1167 hasSeenSmartReplies()1168 public boolean hasSeenSmartReplies() { 1169 return mHasSeenSmartReplies; 1170 } 1171 setSeenSmartReplies(boolean hasSeenSmartReplies)1172 public void setSeenSmartReplies(boolean hasSeenSmartReplies) { 1173 mHasSeenSmartReplies = hasSeenSmartReplies; 1174 } 1175 1176 /** 1177 * Returns whether this notification has been visible and expanded at the same time. 1178 */ hasBeenVisiblyExpanded()1179 public boolean hasBeenVisiblyExpanded() { 1180 return stats.hasBeenVisiblyExpanded(); 1181 } 1182 setSystemGeneratedSmartActions( ArrayList<Notification.Action> systemGeneratedSmartActions)1183 public void setSystemGeneratedSmartActions( 1184 ArrayList<Notification.Action> systemGeneratedSmartActions) { 1185 mSystemGeneratedSmartActions = systemGeneratedSmartActions; 1186 } 1187 getSystemGeneratedSmartActions()1188 public ArrayList<Notification.Action> getSystemGeneratedSmartActions() { 1189 return mSystemGeneratedSmartActions; 1190 } 1191 setSmartReplies(ArrayList<CharSequence> smartReplies)1192 public void setSmartReplies(ArrayList<CharSequence> smartReplies) { 1193 mSmartReplies = smartReplies; 1194 } 1195 getSmartReplies()1196 public ArrayList<CharSequence> getSmartReplies() { 1197 return mSmartReplies; 1198 } 1199 1200 /** 1201 * Returns whether this notification was posted by a secondary app 1202 */ isProxied()1203 public boolean isProxied() { 1204 return !Objects.equals(sbn.getPackageName(), sbn.getOpPkg()); 1205 } 1206 1207 /** 1208 * @return all {@link Uri} that should have permission granted to whoever 1209 * will be rendering it. This list has already been vetted to only 1210 * include {@link Uri} that the enqueuing app can grant. 1211 */ getGrantableUris()1212 public @Nullable ArraySet<Uri> getGrantableUris() { 1213 return mGrantableUris; 1214 } 1215 1216 /** 1217 * Collect all {@link Uri} that should have permission granted to whoever 1218 * will be rendering it. 1219 */ calculateGrantableUris()1220 protected void calculateGrantableUris() { 1221 final Notification notification = getNotification(); 1222 notification.visitUris((uri) -> { 1223 visitGrantableUri(uri, false); 1224 }); 1225 1226 if (notification.getChannelId() != null) { 1227 NotificationChannel channel = getChannel(); 1228 if (channel != null) { 1229 visitGrantableUri(channel.getSound(), (channel.getUserLockedFields() 1230 & NotificationChannel.USER_LOCKED_SOUND) != 0); 1231 } 1232 } 1233 } 1234 1235 /** 1236 * Note the presence of a {@link Uri} that should have permission granted to 1237 * whoever will be rendering it. 1238 * <p> 1239 * If the enqueuing app has the ability to grant access, it will be added to 1240 * {@link #mGrantableUris}. Otherwise, this will either log or throw 1241 * {@link SecurityException} depending on target SDK of enqueuing app. 1242 */ visitGrantableUri(Uri uri, boolean userOverriddenUri)1243 private void visitGrantableUri(Uri uri, boolean userOverriddenUri) { 1244 if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return; 1245 1246 // We can't grant Uri permissions from system 1247 final int sourceUid = sbn.getUid(); 1248 if (sourceUid == android.os.Process.SYSTEM_UID) return; 1249 1250 final long ident = Binder.clearCallingIdentity(); 1251 try { 1252 // This will throw SecurityException if caller can't grant 1253 mUgmInternal.checkGrantUriPermission(sourceUid, null, 1254 ContentProvider.getUriWithoutUserId(uri), 1255 Intent.FLAG_GRANT_READ_URI_PERMISSION, 1256 ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid))); 1257 1258 if (mGrantableUris == null) { 1259 mGrantableUris = new ArraySet<>(); 1260 } 1261 mGrantableUris.add(uri); 1262 } catch (SecurityException e) { 1263 if (!userOverriddenUri) { 1264 if (mTargetSdkVersion >= Build.VERSION_CODES.P) { 1265 throw e; 1266 } else { 1267 Log.w(TAG, "Ignoring " + uri + " from " + sourceUid + ": " + e.getMessage()); 1268 } 1269 } 1270 } finally { 1271 Binder.restoreCallingIdentity(ident); 1272 } 1273 } 1274 getLogMaker(long now)1275 public LogMaker getLogMaker(long now) { 1276 LogMaker lm = sbn.getLogMaker() 1277 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, mImportance) 1278 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now)) 1279 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now)) 1280 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now)) 1281 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS, 1282 getInterruptionMs(now)); 1283 // Record results of the calculateImportance() calculation if available. 1284 if (mImportanceExplanationCode != MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN) { 1285 lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_EXPLANATION, 1286 mImportanceExplanationCode); 1287 // To avoid redundancy, we log the initial importance information only if it was 1288 // overridden. 1289 if (((mImportanceExplanationCode == MetricsEvent.IMPORTANCE_EXPLANATION_ASST) 1290 || (mImportanceExplanationCode == MetricsEvent.IMPORTANCE_EXPLANATION_SYSTEM)) 1291 && (stats.naturalImportance != IMPORTANCE_UNSPECIFIED)) { 1292 // stats.naturalImportance is due to one of the 3 sources of initial importance. 1293 lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_INITIAL_EXPLANATION, 1294 mInitialImportanceExplanationCode); 1295 lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_INITIAL, 1296 stats.naturalImportance); 1297 } 1298 } 1299 // Log Assistant override if present, whether or not importance calculation is complete. 1300 if (mAssistantImportance != IMPORTANCE_UNSPECIFIED) { 1301 lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST, 1302 mAssistantImportance); 1303 } 1304 // Log the issuer of any adjustments that may have affected this notification. We only log 1305 // the hash here as NotificationItem events are frequent, and the number of NAS 1306 // implementations (and hence the chance of collisions) is low. 1307 if (mAdjustmentIssuer != null) { 1308 lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_ASSISTANT_SERVICE_HASH, 1309 mAdjustmentIssuer.hashCode()); 1310 } 1311 return lm; 1312 } 1313 getLogMaker()1314 public LogMaker getLogMaker() { 1315 return getLogMaker(System.currentTimeMillis()); 1316 } 1317 getItemLogMaker()1318 public LogMaker getItemLogMaker() { 1319 return getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM); 1320 } 1321 1322 @VisibleForTesting 1323 static final class Light { 1324 public final int color; 1325 public final int onMs; 1326 public final int offMs; 1327 Light(int color, int onMs, int offMs)1328 public Light(int color, int onMs, int offMs) { 1329 this.color = color; 1330 this.onMs = onMs; 1331 this.offMs = offMs; 1332 } 1333 1334 @Override equals(Object o)1335 public boolean equals(Object o) { 1336 if (this == o) return true; 1337 if (o == null || getClass() != o.getClass()) return false; 1338 1339 Light light = (Light) o; 1340 1341 if (color != light.color) return false; 1342 if (onMs != light.onMs) return false; 1343 return offMs == light.offMs; 1344 1345 } 1346 1347 @Override hashCode()1348 public int hashCode() { 1349 int result = color; 1350 result = 31 * result + onMs; 1351 result = 31 * result + offMs; 1352 return result; 1353 } 1354 1355 @Override toString()1356 public String toString() { 1357 return "Light{" + 1358 "color=" + color + 1359 ", onMs=" + onMs + 1360 ", offMs=" + offMs + 1361 '}'; 1362 } 1363 } 1364 } 1365