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