1 /* 2 * Copyright (C) 2008 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 17 package com.android.systemui.statusbar; 18 19 import static android.app.Notification.CATEGORY_ALARM; 20 import static android.app.Notification.CATEGORY_CALL; 21 import static android.app.Notification.CATEGORY_EVENT; 22 import static android.app.Notification.CATEGORY_MESSAGE; 23 import static android.app.Notification.CATEGORY_REMINDER; 24 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; 25 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 26 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; 27 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; 28 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; 29 30 import android.Manifest; 31 import android.app.AppGlobals; 32 import android.app.Notification; 33 import android.app.NotificationChannel; 34 import android.app.NotificationManager; 35 import android.app.Person; 36 import android.content.Context; 37 import android.content.pm.IPackageManager; 38 import android.content.pm.PackageManager; 39 import android.graphics.drawable.Icon; 40 import android.os.Bundle; 41 import android.os.Parcelable; 42 import android.os.RemoteException; 43 import android.os.SystemClock; 44 import android.service.notification.NotificationListenerService.Ranking; 45 import android.service.notification.NotificationListenerService.RankingMap; 46 import android.service.notification.SnoozeCriterion; 47 import android.service.notification.StatusBarNotification; 48 import android.util.ArrayMap; 49 import android.util.ArraySet; 50 import android.view.View; 51 import android.widget.ImageView; 52 import android.widget.RemoteViews; 53 54 import com.android.internal.annotations.VisibleForTesting; 55 import com.android.internal.statusbar.StatusBarIcon; 56 import com.android.internal.util.ArrayUtils; 57 import com.android.internal.util.NotificationColorUtil; 58 import com.android.systemui.Dependency; 59 import com.android.systemui.ForegroundServiceController; 60 import com.android.systemui.statusbar.notification.InflationException; 61 import com.android.systemui.statusbar.phone.NotificationGroupManager; 62 import com.android.systemui.statusbar.phone.StatusBar; 63 import com.android.systemui.statusbar.policy.HeadsUpManager; 64 import com.android.systemui.statusbar.policy.ZenModeController; 65 66 import java.io.PrintWriter; 67 import java.util.ArrayList; 68 import java.util.Collections; 69 import java.util.Comparator; 70 import java.util.List; 71 import java.util.Objects; 72 73 /** 74 * The list of currently displaying notifications. 75 */ 76 public class NotificationData { 77 78 private final Environment mEnvironment; 79 private HeadsUpManager mHeadsUpManager; 80 81 final ZenModeController mZen = Dependency.get(ZenModeController.class); 82 final ForegroundServiceController mFsc = Dependency.get(ForegroundServiceController.class); 83 84 public static final class Entry { 85 private static final long LAUNCH_COOLDOWN = 2000; 86 private static final long REMOTE_INPUT_COOLDOWN = 500; 87 private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN; 88 private static final int COLOR_INVALID = 1; 89 public String key; 90 public StatusBarNotification notification; 91 public NotificationChannel channel; 92 public StatusBarIconView icon; 93 public StatusBarIconView expandedIcon; 94 public ExpandableNotificationRow row; // the outer expanded view 95 private boolean interruption; 96 public boolean autoRedacted; // whether the redacted notification was generated by us 97 public int targetSdk; 98 private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET; 99 public RemoteViews cachedContentView; 100 public RemoteViews cachedBigContentView; 101 public RemoteViews cachedHeadsUpContentView; 102 public RemoteViews cachedPublicContentView; 103 public RemoteViews cachedAmbientContentView; 104 public CharSequence remoteInputText; 105 public List<SnoozeCriterion> snoozeCriteria; 106 public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL; 107 108 private int mCachedContrastColor = COLOR_INVALID; 109 private int mCachedContrastColorIsFor = COLOR_INVALID; 110 private InflationTask mRunningTask = null; 111 private Throwable mDebugThrowable; 112 public CharSequence remoteInputTextWhenReset; 113 public long lastRemoteInputSent = NOT_LAUNCHED_YET; 114 public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3); 115 public CharSequence headsUpStatusBarText; 116 public CharSequence headsUpStatusBarTextPublic; 117 /** 118 * Whether or not this row represents a system notification. Note that if this is 119 * {@code null}, that means we were either unable to retrieve the info or have yet to 120 * retrieve the info. 121 */ 122 public Boolean mIsSystemNotification; 123 124 /** 125 * Has the user sent a reply through this Notification. 126 */ 127 private boolean hasSentReply; 128 129 public Entry(StatusBarNotification n) { 130 this.key = n.getKey(); 131 this.notification = n; 132 } 133 134 public void setInterruption() { 135 interruption = true; 136 } 137 138 public boolean hasInterrupted() { 139 return interruption; 140 } 141 142 /** 143 * Resets the notification entry to be re-used. 144 */ 145 public void reset() { 146 if (row != null) { 147 row.reset(); 148 } 149 } 150 151 public View getExpandedContentView() { 152 return row.getPrivateLayout().getExpandedChild(); 153 } 154 155 public View getPublicContentView() { 156 return row.getPublicLayout().getContractedChild(); 157 } 158 159 public void notifyFullScreenIntentLaunched() { 160 setInterruption(); 161 lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime(); 162 } 163 164 public boolean hasJustLaunchedFullScreenIntent() { 165 return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN; 166 } 167 168 public boolean hasJustSentRemoteInput() { 169 return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN; 170 } 171 172 /** 173 * Create the icons for a notification 174 * @param context the context to create the icons with 175 * @param sbn the notification 176 * @throws InflationException 177 */ 178 public void createIcons(Context context, StatusBarNotification sbn) 179 throws InflationException { 180 Notification n = sbn.getNotification(); 181 final Icon smallIcon = n.getSmallIcon(); 182 if (smallIcon == null) { 183 throw new InflationException("No small icon in notification from " 184 + sbn.getPackageName()); 185 } 186 187 // Construct the icon. 188 icon = new StatusBarIconView(context, 189 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); 190 icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 191 192 // Construct the expanded icon. 193 expandedIcon = new StatusBarIconView(context, 194 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); 195 expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 196 final StatusBarIcon ic = new StatusBarIcon( 197 sbn.getUser(), 198 sbn.getPackageName(), 199 smallIcon, 200 n.iconLevel, 201 n.number, 202 StatusBarIconView.contentDescForNotification(context, n)); 203 if (!icon.set(ic) || !expandedIcon.set(ic)) { 204 icon = null; 205 expandedIcon = null; 206 throw new InflationException("Couldn't create icon: " + ic); 207 } 208 expandedIcon.setVisibility(View.INVISIBLE); 209 expandedIcon.setOnVisibilityChangedListener( 210 newVisibility -> { 211 if (row != null) { 212 row.setIconsVisible(newVisibility != View.VISIBLE); 213 } 214 }); 215 } 216 217 public void setIconTag(int key, Object tag) { 218 if (icon != null) { 219 icon.setTag(key, tag); 220 expandedIcon.setTag(key, tag); 221 } 222 } 223 224 /** 225 * Update the notification icons. 226 * @param context the context to create the icons with. 227 * @param sbn the notification to read the icon from. 228 * @throws InflationException 229 */ 230 public void updateIcons(Context context, StatusBarNotification sbn) 231 throws InflationException { 232 if (icon != null) { 233 // Update the icon 234 Notification n = sbn.getNotification(); 235 final StatusBarIcon ic = new StatusBarIcon( 236 notification.getUser(), 237 notification.getPackageName(), 238 n.getSmallIcon(), 239 n.iconLevel, 240 n.number, 241 StatusBarIconView.contentDescForNotification(context, n)); 242 icon.setNotification(sbn); 243 expandedIcon.setNotification(sbn); 244 if (!icon.set(ic) || !expandedIcon.set(ic)) { 245 throw new InflationException("Couldn't update icon: " + ic); 246 } 247 } 248 } 249 250 public int getContrastedColor(Context context, boolean isLowPriority, 251 int backgroundColor) { 252 int rawColor = isLowPriority ? Notification.COLOR_DEFAULT : 253 notification.getNotification().color; 254 if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) { 255 return mCachedContrastColor; 256 } 257 final int contrasted = NotificationColorUtil.resolveContrastColor(context, rawColor, 258 backgroundColor); 259 mCachedContrastColorIsFor = rawColor; 260 mCachedContrastColor = contrasted; 261 return mCachedContrastColor; 262 } 263 264 /** 265 * Abort all existing inflation tasks 266 */ 267 public void abortTask() { 268 if (mRunningTask != null) { 269 mRunningTask.abort(); 270 mRunningTask = null; 271 } 272 } 273 274 public void setInflationTask(InflationTask abortableTask) { 275 // abort any existing inflation 276 InflationTask existing = mRunningTask; 277 abortTask(); 278 mRunningTask = abortableTask; 279 if (existing != null && mRunningTask != null) { 280 mRunningTask.supersedeTask(existing); 281 } 282 } 283 284 public void onInflationTaskFinished() { 285 mRunningTask = null; 286 } 287 288 @VisibleForTesting 289 public InflationTask getRunningTask() { 290 return mRunningTask; 291 } 292 293 /** 294 * Set a throwable that is used for debugging 295 * 296 * @param debugThrowable the throwable to save 297 */ 298 public void setDebugThrowable(Throwable debugThrowable) { 299 mDebugThrowable = debugThrowable; 300 } 301 302 public Throwable getDebugThrowable() { 303 return mDebugThrowable; 304 } 305 306 public void onRemoteInputInserted() { 307 lastRemoteInputSent = NOT_LAUNCHED_YET; 308 remoteInputTextWhenReset = null; 309 } 310 311 public void setHasSentReply() { 312 hasSentReply = true; 313 } 314 315 public boolean isLastMessageFromReply() { 316 if (!hasSentReply) { 317 return false; 318 } 319 Bundle extras = notification.getNotification().extras; 320 CharSequence[] replyTexts = extras.getCharSequenceArray( 321 Notification.EXTRA_REMOTE_INPUT_HISTORY); 322 if (!ArrayUtils.isEmpty(replyTexts)) { 323 return true; 324 } 325 Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES); 326 if (messages != null && messages.length > 0) { 327 Parcelable message = messages[messages.length - 1]; 328 if (message instanceof Bundle) { 329 Notification.MessagingStyle.Message lastMessage = 330 Notification.MessagingStyle.Message.getMessageFromBundle( 331 (Bundle) message); 332 if (lastMessage != null) { 333 Person senderPerson = lastMessage.getSenderPerson(); 334 if (senderPerson == null) { 335 return true; 336 } 337 Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON); 338 return Objects.equals(user, senderPerson); 339 } 340 } 341 } 342 return false; 343 } 344 } 345 346 private final ArrayMap<String, Entry> mEntries = new ArrayMap<>(); 347 private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>(); 348 private final ArrayList<Entry> mFilteredForUser = new ArrayList<>(); 349 350 private NotificationGroupManager mGroupManager; 351 352 private RankingMap mRankingMap; 353 private final Ranking mTmpRanking = new Ranking(); 354 355 public void setHeadsUpManager(HeadsUpManager headsUpManager) { 356 mHeadsUpManager = headsUpManager; 357 } 358 359 private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() { 360 private final Ranking mRankingA = new Ranking(); 361 private final Ranking mRankingB = new Ranking(); 362 363 @Override 364 public int compare(Entry a, Entry b) { 365 final StatusBarNotification na = a.notification; 366 final StatusBarNotification nb = b.notification; 367 int aImportance = NotificationManager.IMPORTANCE_DEFAULT; 368 int bImportance = NotificationManager.IMPORTANCE_DEFAULT; 369 int aRank = 0; 370 int bRank = 0; 371 372 if (mRankingMap != null) { 373 // RankingMap as received from NoMan 374 getRanking(a.key, mRankingA); 375 getRanking(b.key, mRankingB); 376 aImportance = mRankingA.getImportance(); 377 bImportance = mRankingB.getImportance(); 378 aRank = mRankingA.getRank(); 379 bRank = mRankingB.getRank(); 380 } 381 382 String mediaNotification = mEnvironment.getCurrentMediaNotificationKey(); 383 384 // IMPORTANCE_MIN media streams are allowed to drift to the bottom 385 final boolean aMedia = a.key.equals(mediaNotification) 386 && aImportance > NotificationManager.IMPORTANCE_MIN; 387 final boolean bMedia = b.key.equals(mediaNotification) 388 && bImportance > NotificationManager.IMPORTANCE_MIN; 389 390 boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH && 391 isSystemNotification(na); 392 boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH && 393 isSystemNotification(nb); 394 395 boolean isHeadsUp = a.row.isHeadsUp(); 396 if (isHeadsUp != b.row.isHeadsUp()) { 397 return isHeadsUp ? -1 : 1; 398 } else if (isHeadsUp) { 399 // Provide consistent ranking with headsUpManager 400 return mHeadsUpManager.compare(a, b); 401 } else if (aMedia != bMedia) { 402 // Upsort current media notification. 403 return aMedia ? -1 : 1; 404 } else if (aSystemMax != bSystemMax) { 405 // Upsort PRIORITY_MAX system notifications 406 return aSystemMax ? -1 : 1; 407 } else if (aRank != bRank) { 408 return aRank - bRank; 409 } else { 410 return Long.compare(nb.getNotification().when, na.getNotification().when); 411 } 412 } 413 }; 414 415 public NotificationData(Environment environment) { 416 mEnvironment = environment; 417 mGroupManager = environment.getGroupManager(); 418 } 419 420 /** 421 * Returns the sorted list of active notifications (depending on {@link Environment} 422 * 423 * <p> 424 * This call doesn't update the list of active notifications. Call {@link #filterAndSort()} 425 * when the environment changes. 426 * <p> 427 * Don't hold on to or modify the returned list. 428 */ 429 public ArrayList<Entry> getActiveNotifications() { 430 return mSortedAndFiltered; 431 } 432 433 public ArrayList<Entry> getNotificationsForCurrentUser() { 434 mFilteredForUser.clear(); 435 436 synchronized (mEntries) { 437 final int N = mEntries.size(); 438 for (int i = 0; i < N; i++) { 439 Entry entry = mEntries.valueAt(i); 440 final StatusBarNotification sbn = entry.notification; 441 if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) { 442 continue; 443 } 444 mFilteredForUser.add(entry); 445 } 446 } 447 return mFilteredForUser; 448 } 449 450 public Entry get(String key) { 451 return mEntries.get(key); 452 } 453 454 public void add(Entry entry) { 455 synchronized (mEntries) { 456 mEntries.put(entry.notification.getKey(), entry); 457 } 458 mGroupManager.onEntryAdded(entry); 459 460 updateRankingAndSort(mRankingMap); 461 } 462 463 public Entry remove(String key, RankingMap ranking) { 464 Entry removed = null; 465 synchronized (mEntries) { 466 removed = mEntries.remove(key); 467 } 468 if (removed == null) return null; 469 mGroupManager.onEntryRemoved(removed); 470 updateRankingAndSort(ranking); 471 return removed; 472 } 473 474 public void updateRanking(RankingMap ranking) { 475 updateRankingAndSort(ranking); 476 } 477 478 public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) { 479 synchronized (mEntries) { 480 final int N = mEntries.size(); 481 for (int i = 0; i < N; i++) { 482 Entry entry = mEntries.valueAt(i); 483 if (uid == entry.notification.getUid() 484 && pkg.equals(entry.notification.getPackageName()) 485 && key.equals(entry.key)) { 486 if (showIcon) { 487 entry.mActiveAppOps.add(appOp); 488 } else { 489 entry.mActiveAppOps.remove(appOp); 490 } 491 } 492 } 493 } 494 } 495 496 public boolean isAmbient(String key) { 497 if (mRankingMap != null) { 498 getRanking(key, mTmpRanking); 499 return mTmpRanking.isAmbient(); 500 } 501 return false; 502 } 503 504 public int getVisibilityOverride(String key) { 505 if (mRankingMap != null) { 506 getRanking(key, mTmpRanking); 507 return mTmpRanking.getVisibilityOverride(); 508 } 509 return Ranking.VISIBILITY_NO_OVERRIDE; 510 } 511 512 public boolean shouldSuppressFullScreenIntent(Entry entry) { 513 return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_FULL_SCREEN_INTENT); 514 } 515 516 public boolean shouldSuppressPeek(Entry entry) { 517 return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_PEEK); 518 } 519 520 public boolean shouldSuppressStatusBar(Entry entry) { 521 return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_STATUS_BAR); 522 } 523 524 public boolean shouldSuppressAmbient(Entry entry) { 525 return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_AMBIENT); 526 } 527 528 public boolean shouldSuppressNotificationList(Entry entry) { 529 return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_NOTIFICATION_LIST); 530 } 531 532 private boolean shouldSuppressVisualEffect(Entry entry, int effect) { 533 if (isExemptFromDndVisualSuppression(entry)) { 534 return false; 535 } 536 String key = entry.key; 537 if (mRankingMap != null) { 538 getRanking(key, mTmpRanking); 539 return (mTmpRanking.getSuppressedVisualEffects() & effect) != 0; 540 } 541 return false; 542 } 543 544 protected boolean isExemptFromDndVisualSuppression(Entry entry) { 545 if (isNotificationBlockedByPolicy(entry.notification.getNotification())) { 546 return false; 547 } 548 549 if ((entry.notification.getNotification().flags 550 & Notification.FLAG_FOREGROUND_SERVICE) != 0) { 551 return true; 552 } 553 if (entry.notification.getNotification().isMediaNotification()) { 554 return true; 555 } 556 if (entry.mIsSystemNotification != null && entry.mIsSystemNotification) { 557 return true; 558 } 559 return false; 560 } 561 562 /** 563 * Categories that are explicitly called out on DND settings screens are always blocked, if 564 * DND has flagged them, even if they are foreground or system notifications that might 565 * otherwise visually bypass DND. 566 */ 567 protected boolean isNotificationBlockedByPolicy(Notification n) { 568 if (isCategory(CATEGORY_CALL, n) 569 || isCategory(CATEGORY_MESSAGE, n) 570 || isCategory(CATEGORY_ALARM, n) 571 || isCategory(CATEGORY_EVENT, n) 572 || isCategory(CATEGORY_REMINDER, n)) { 573 return true; 574 } 575 return false; 576 } 577 578 private boolean isCategory(String category, Notification n) { 579 return Objects.equals(n.category, category); 580 } 581 582 public int getImportance(String key) { 583 if (mRankingMap != null) { 584 getRanking(key, mTmpRanking); 585 return mTmpRanking.getImportance(); 586 } 587 return NotificationManager.IMPORTANCE_UNSPECIFIED; 588 } 589 590 public String getOverrideGroupKey(String key) { 591 if (mRankingMap != null) { 592 getRanking(key, mTmpRanking); 593 return mTmpRanking.getOverrideGroupKey(); 594 } 595 return null; 596 } 597 598 public List<SnoozeCriterion> getSnoozeCriteria(String key) { 599 if (mRankingMap != null) { 600 getRanking(key, mTmpRanking); 601 return mTmpRanking.getSnoozeCriteria(); 602 } 603 return null; 604 } 605 606 public NotificationChannel getChannel(String key) { 607 if (mRankingMap != null) { 608 getRanking(key, mTmpRanking); 609 return mTmpRanking.getChannel(); 610 } 611 return null; 612 } 613 614 public int getRank(String key) { 615 if (mRankingMap != null) { 616 getRanking(key, mTmpRanking); 617 return mTmpRanking.getRank(); 618 } 619 return 0; 620 } 621 622 public boolean shouldHide(String key) { 623 if (mRankingMap != null) { 624 getRanking(key, mTmpRanking); 625 return mTmpRanking.isSuspended(); 626 } 627 return false; 628 } 629 630 private void updateRankingAndSort(RankingMap ranking) { 631 if (ranking != null) { 632 mRankingMap = ranking; 633 synchronized (mEntries) { 634 final int N = mEntries.size(); 635 for (int i = 0; i < N; i++) { 636 Entry entry = mEntries.valueAt(i); 637 if (!getRanking(entry.key, mTmpRanking)) { 638 continue; 639 } 640 final StatusBarNotification oldSbn = entry.notification.cloneLight(); 641 final String overrideGroupKey = getOverrideGroupKey(entry.key); 642 if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) { 643 entry.notification.setOverrideGroupKey(overrideGroupKey); 644 mGroupManager.onEntryUpdated(entry, oldSbn); 645 } 646 entry.channel = getChannel(entry.key); 647 entry.snoozeCriteria = getSnoozeCriteria(entry.key); 648 entry.userSentiment = mTmpRanking.getUserSentiment(); 649 } 650 } 651 } 652 filterAndSort(); 653 } 654 655 /** 656 * Get the ranking from the current ranking map. 657 * 658 * @param key the key to look up 659 * @param outRanking the ranking to populate 660 * 661 * @return {@code true} if the ranking was properly obtained. 662 */ 663 @VisibleForTesting 664 protected boolean getRanking(String key, Ranking outRanking) { 665 return mRankingMap.getRanking(key, outRanking); 666 } 667 668 // TODO: This should not be public. Instead the Environment should notify this class when 669 // anything changed, and this class should call back the UI so it updates itself. 670 public void filterAndSort() { 671 mSortedAndFiltered.clear(); 672 673 synchronized (mEntries) { 674 final int N = mEntries.size(); 675 for (int i = 0; i < N; i++) { 676 Entry entry = mEntries.valueAt(i); 677 678 if (shouldFilterOut(entry)) { 679 continue; 680 } 681 682 mSortedAndFiltered.add(entry); 683 } 684 } 685 686 Collections.sort(mSortedAndFiltered, mRankingComparator); 687 } 688 689 /** 690 * @return true if this notification should NOT be shown right now 691 */ 692 public boolean shouldFilterOut(Entry entry) { 693 final StatusBarNotification sbn = entry.notification; 694 if (!(mEnvironment.isDeviceProvisioned() || 695 showNotificationEvenIfUnprovisioned(sbn))) { 696 return true; 697 } 698 699 if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) { 700 return true; 701 } 702 703 if (mEnvironment.isSecurelyLocked(sbn.getUserId()) && 704 (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET 705 || mEnvironment.shouldHideNotifications(sbn.getUserId()) 706 || mEnvironment.shouldHideNotifications(sbn.getKey()))) { 707 return true; 708 } 709 710 if (mEnvironment.isDozing() && shouldSuppressAmbient(entry)) { 711 return true; 712 } 713 714 if (!mEnvironment.isDozing() && shouldSuppressNotificationList(entry)) { 715 return true; 716 } 717 718 if (shouldHide(sbn.getKey())) { 719 return true; 720 } 721 722 if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS 723 && mGroupManager.isChildInGroupWithSummary(sbn)) { 724 return true; 725 } 726 727 if (mFsc.isDungeonNotification(sbn) && !mFsc.isDungeonNeededForUser(sbn.getUserId())) { 728 // this is a foreground-service disclosure for a user that does not need to show one 729 return true; 730 } 731 if (mFsc.isSystemAlertNotification(sbn)) { 732 final String[] apps = sbn.getNotification().extras.getStringArray( 733 Notification.EXTRA_FOREGROUND_APPS); 734 if (apps != null && apps.length >= 1) { 735 if (!mFsc.isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) { 736 return true; 737 } 738 } 739 } 740 741 return false; 742 } 743 744 // Q: What kinds of notifications should show during setup? 745 // A: Almost none! Only things coming from packages with permission 746 // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them 747 // as relevant for setup (see below). 748 public static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) { 749 return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn); 750 } 751 752 @VisibleForTesting 753 static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager, 754 StatusBarNotification sbn) { 755 return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP, 756 sbn.getUid()) == PackageManager.PERMISSION_GRANTED 757 && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP); 758 } 759 760 private static int checkUidPermission(IPackageManager packageManager, String permission, 761 int uid) { 762 try { 763 return packageManager.checkUidPermission(permission, uid); 764 } catch (RemoteException e) { 765 throw e.rethrowFromSystemServer(); 766 } 767 } 768 769 public void dump(PrintWriter pw, String indent) { 770 int N = mSortedAndFiltered.size(); 771 pw.print(indent); 772 pw.println("active notifications: " + N); 773 int active; 774 for (active = 0; active < N; active++) { 775 NotificationData.Entry e = mSortedAndFiltered.get(active); 776 dumpEntry(pw, indent, active, e); 777 } 778 synchronized (mEntries) { 779 int M = mEntries.size(); 780 pw.print(indent); 781 pw.println("inactive notifications: " + (M - active)); 782 int inactiveCount = 0; 783 for (int i = 0; i < M; i++) { 784 Entry entry = mEntries.valueAt(i); 785 if (!mSortedAndFiltered.contains(entry)) { 786 dumpEntry(pw, indent, inactiveCount, entry); 787 inactiveCount++; 788 } 789 } 790 } 791 } 792 793 private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) { 794 getRanking(e.key, mTmpRanking); 795 pw.print(indent); 796 pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon); 797 StatusBarNotification n = e.notification; 798 pw.print(indent); 799 pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance=" + 800 mTmpRanking.getImportance()); 801 pw.print(indent); 802 pw.println(" notification=" + n.getNotification()); 803 } 804 805 private static boolean isSystemNotification(StatusBarNotification sbn) { 806 String sbnPackage = sbn.getPackageName(); 807 return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage); 808 } 809 810 /** 811 * Provides access to keyguard state and user settings dependent data. 812 */ 813 public interface Environment { 814 public boolean isSecurelyLocked(int userId); 815 public boolean shouldHideNotifications(int userid); 816 public boolean shouldHideNotifications(String key); 817 public boolean isDeviceProvisioned(); 818 public boolean isNotificationForCurrentProfiles(StatusBarNotification sbn); 819 public String getCurrentMediaNotificationKey(); 820 public NotificationGroupManager getGroupManager(); 821 /** 822 * @return true iff the device is dozing 823 */ 824 boolean isDozing(); 825 } 826 } 827