1 /* 2 * Copyright (C) 2007 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.server.notification; 18 19 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS; 20 import static android.service.notification.NotificationListenerService.TRIM_FULL; 21 import static android.service.notification.NotificationListenerService.TRIM_LIGHT; 22 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 23 import static org.xmlpull.v1.XmlPullParser.END_TAG; 24 import static org.xmlpull.v1.XmlPullParser.START_TAG; 25 26 import android.app.ActivityManager; 27 import android.app.ActivityManagerNative; 28 import android.app.AppGlobals; 29 import android.app.AppOpsManager; 30 import android.app.IActivityManager; 31 import android.app.INotificationManager; 32 import android.app.ITransientNotification; 33 import android.app.Notification; 34 import android.app.NotificationManager; 35 import android.app.PendingIntent; 36 import android.app.StatusBarManager; 37 import android.content.BroadcastReceiver; 38 import android.content.ComponentName; 39 import android.content.ContentResolver; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.content.IntentFilter; 43 import android.content.pm.ApplicationInfo; 44 import android.content.pm.IPackageManager; 45 import android.content.pm.PackageInfo; 46 import android.content.pm.PackageManager; 47 import android.content.pm.PackageManager.NameNotFoundException; 48 import android.content.pm.ParceledListSlice; 49 import android.content.res.Resources; 50 import android.database.ContentObserver; 51 import android.media.AudioAttributes; 52 import android.media.AudioManager; 53 import android.media.AudioSystem; 54 import android.media.IRingtonePlayer; 55 import android.net.Uri; 56 import android.os.Binder; 57 import android.os.Bundle; 58 import android.os.Environment; 59 import android.os.Handler; 60 import android.os.HandlerThread; 61 import android.os.IBinder; 62 import android.os.IInterface; 63 import android.os.Looper; 64 import android.os.Message; 65 import android.os.Process; 66 import android.os.RemoteException; 67 import android.os.UserHandle; 68 import android.os.Vibrator; 69 import android.provider.Settings; 70 import android.service.notification.Condition; 71 import android.service.notification.IConditionListener; 72 import android.service.notification.IConditionProvider; 73 import android.service.notification.INotificationListener; 74 import android.service.notification.IStatusBarNotificationHolder; 75 import android.service.notification.NotificationListenerService; 76 import android.service.notification.NotificationRankingUpdate; 77 import android.service.notification.StatusBarNotification; 78 import android.service.notification.ZenModeConfig; 79 import android.telephony.PhoneStateListener; 80 import android.telephony.TelephonyManager; 81 import android.text.TextUtils; 82 import android.util.ArrayMap; 83 import android.util.ArraySet; 84 import android.util.AtomicFile; 85 import android.util.Log; 86 import android.util.Slog; 87 import android.util.Xml; 88 import android.view.accessibility.AccessibilityEvent; 89 import android.view.accessibility.AccessibilityManager; 90 import android.widget.Toast; 91 92 import com.android.internal.R; 93 import com.android.internal.util.FastXmlSerializer; 94 import com.android.server.EventLogTags; 95 import com.android.server.SystemService; 96 import com.android.server.lights.Light; 97 import com.android.server.lights.LightsManager; 98 import com.android.server.notification.ManagedServices.ManagedServiceInfo; 99 import com.android.server.notification.ManagedServices.UserProfiles; 100 import com.android.server.statusbar.StatusBarManagerInternal; 101 102 import libcore.io.IoUtils; 103 104 import org.xmlpull.v1.XmlPullParser; 105 import org.xmlpull.v1.XmlPullParserException; 106 import org.xmlpull.v1.XmlSerializer; 107 108 import java.io.File; 109 import java.io.FileDescriptor; 110 import java.io.FileInputStream; 111 import java.io.FileNotFoundException; 112 import java.io.FileOutputStream; 113 import java.io.IOException; 114 import java.io.PrintWriter; 115 import java.util.ArrayDeque; 116 import java.util.ArrayList; 117 import java.util.HashSet; 118 import java.util.Iterator; 119 import java.util.Map.Entry; 120 import java.util.NoSuchElementException; 121 import java.util.Objects; 122 123 /** {@hide} */ 124 public class NotificationManagerService extends SystemService { 125 static final String TAG = "NotificationService"; 126 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 127 128 static final int MAX_PACKAGE_NOTIFICATIONS = 50; 129 130 // message codes 131 static final int MESSAGE_TIMEOUT = 2; 132 static final int MESSAGE_SAVE_POLICY_FILE = 3; 133 static final int MESSAGE_RECONSIDER_RANKING = 4; 134 static final int MESSAGE_RANKING_CONFIG_CHANGE = 5; 135 static final int MESSAGE_SEND_RANKING_UPDATE = 6; 136 static final int MESSAGE_LISTENER_HINTS_CHANGED = 7; 137 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8; 138 139 static final int LONG_DELAY = 3500; // 3.5 seconds 140 static final int SHORT_DELAY = 2000; // 2 seconds 141 142 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; 143 144 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps 145 146 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; 147 static final boolean SCORE_ONGOING_HIGHER = false; 148 149 static final int JUNK_SCORE = -1000; 150 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; 151 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER; 152 153 // Notifications with scores below this will not interrupt the user, either via LED or 154 // sound or vibration 155 static final int SCORE_INTERRUPTION_THRESHOLD = 156 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; 157 158 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; 159 static final boolean ENABLE_BLOCKED_TOASTS = true; 160 161 // When #matchesCallFilter is called from the ringer, wait at most 162 // 3s to resolve the contacts. This timeout is required since 163 // ContactsProvider might take a long time to start up. 164 // 165 // Return STARRED_CONTACT when the timeout is hit in order to avoid 166 // missed calls in ZEN mode "Important". 167 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000; 168 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY = 169 ValidateNotificationPeople.STARRED_CONTACT; 170 171 /** notification_enqueue status value for a newly enqueued notification. */ 172 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0; 173 174 /** notification_enqueue status value for an existing notification. */ 175 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1; 176 177 /** notification_enqueue status value for an ignored notification. */ 178 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2; 179 180 private IActivityManager mAm; 181 AudioManager mAudioManager; 182 StatusBarManagerInternal mStatusBar; 183 Vibrator mVibrator; 184 185 final IBinder mForegroundToken = new Binder(); 186 private WorkerHandler mHandler; 187 private final HandlerThread mRankingThread = new HandlerThread("ranker", 188 Process.THREAD_PRIORITY_BACKGROUND); 189 190 private Light mNotificationLight; 191 Light mAttentionLight; 192 private int mDefaultNotificationColor; 193 private int mDefaultNotificationLedOn; 194 195 private int mDefaultNotificationLedOff; 196 private long[] mDefaultVibrationPattern; 197 198 private long[] mFallbackVibrationPattern; 199 private boolean mUseAttentionLight; 200 boolean mSystemReady; 201 202 private boolean mDisableNotificationEffects; 203 private int mCallState; 204 private String mSoundNotificationKey; 205 private String mVibrateNotificationKey; 206 207 private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>(); 208 private ComponentName mEffectsSuppressor; 209 private int mListenerHints; // right now, all hints are global 210 private int mInterruptionFilter; // current ZEN mode as communicated to listeners 211 212 // for enabling and disabling notification pulse behavior 213 private boolean mScreenOn = true; 214 private boolean mInCall = false; 215 private boolean mNotificationPulseEnabled; 216 217 // used as a mutex for access to all active notifications & listeners 218 final ArrayList<NotificationRecord> mNotificationList = 219 new ArrayList<NotificationRecord>(); 220 final ArrayMap<String, NotificationRecord> mNotificationsByKey = 221 new ArrayMap<String, NotificationRecord>(); 222 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); 223 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>(); 224 225 // The last key in this list owns the hardware. 226 ArrayList<String> mLights = new ArrayList<>(); 227 228 private AppOpsManager mAppOps; 229 230 private Archive mArchive; 231 232 // Notification control database. For now just contains disabled packages. 233 private AtomicFile mPolicyFile; 234 private HashSet<String> mBlockedPackages = new HashSet<String>(); 235 236 private static final int DB_VERSION = 1; 237 238 private static final String TAG_BODY = "notification-policy"; 239 private static final String ATTR_VERSION = "version"; 240 241 private static final String TAG_BLOCKED_PKGS = "blocked-packages"; 242 private static final String TAG_PACKAGE = "package"; 243 private static final String ATTR_NAME = "name"; 244 245 private RankingHelper mRankingHelper; 246 247 private final UserProfiles mUserProfiles = new UserProfiles(); 248 private NotificationListeners mListeners; 249 private ConditionProviders mConditionProviders; 250 private NotificationUsageStats mUsageStats; 251 252 private static final int MY_UID = Process.myUid(); 253 private static final int MY_PID = Process.myPid(); 254 private static final int REASON_DELEGATE_CLICK = 1; 255 private static final int REASON_DELEGATE_CANCEL = 2; 256 private static final int REASON_DELEGATE_CANCEL_ALL = 3; 257 private static final int REASON_DELEGATE_ERROR = 4; 258 private static final int REASON_PACKAGE_CHANGED = 5; 259 private static final int REASON_USER_STOPPED = 6; 260 private static final int REASON_PACKAGE_BANNED = 7; 261 private static final int REASON_NOMAN_CANCEL = 8; 262 private static final int REASON_NOMAN_CANCEL_ALL = 9; 263 private static final int REASON_LISTENER_CANCEL = 10; 264 private static final int REASON_LISTENER_CANCEL_ALL = 11; 265 private static final int REASON_GROUP_SUMMARY_CANCELED = 12; 266 private static final int REASON_GROUP_OPTIMIZATION = 13; 267 268 private static class Archive { 269 final int mBufferSize; 270 final ArrayDeque<StatusBarNotification> mBuffer; 271 272 public Archive(int size) { 273 mBufferSize = size; 274 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize); 275 } 276 277 public String toString() { 278 final StringBuilder sb = new StringBuilder(); 279 final int N = mBuffer.size(); 280 sb.append("Archive ("); 281 sb.append(N); 282 sb.append(" notification"); 283 sb.append((N==1)?")":"s)"); 284 return sb.toString(); 285 } 286 287 public void record(StatusBarNotification nr) { 288 if (mBuffer.size() == mBufferSize) { 289 mBuffer.removeFirst(); 290 } 291 292 // We don't want to store the heavy bits of the notification in the archive, 293 // but other clients in the system process might be using the object, so we 294 // store a (lightened) copy. 295 mBuffer.addLast(nr.cloneLight()); 296 } 297 298 public void clear() { 299 mBuffer.clear(); 300 } 301 302 public Iterator<StatusBarNotification> descendingIterator() { 303 return mBuffer.descendingIterator(); 304 } 305 public Iterator<StatusBarNotification> ascendingIterator() { 306 return mBuffer.iterator(); 307 } 308 public Iterator<StatusBarNotification> filter( 309 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) { 310 return new Iterator<StatusBarNotification>() { 311 StatusBarNotification mNext = findNext(); 312 313 private StatusBarNotification findNext() { 314 while (iter.hasNext()) { 315 StatusBarNotification nr = iter.next(); 316 if ((pkg == null || nr.getPackageName() == pkg) 317 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) { 318 return nr; 319 } 320 } 321 return null; 322 } 323 324 @Override 325 public boolean hasNext() { 326 return mNext == null; 327 } 328 329 @Override 330 public StatusBarNotification next() { 331 StatusBarNotification next = mNext; 332 if (next == null) { 333 throw new NoSuchElementException(); 334 } 335 mNext = findNext(); 336 return next; 337 } 338 339 @Override 340 public void remove() { 341 iter.remove(); 342 } 343 }; 344 } 345 346 public StatusBarNotification[] getArray(int count) { 347 if (count == 0) count = mBufferSize; 348 final StatusBarNotification[] a 349 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 350 Iterator<StatusBarNotification> iter = descendingIterator(); 351 int i=0; 352 while (iter.hasNext() && i < count) { 353 a[i++] = iter.next(); 354 } 355 return a; 356 } 357 358 public StatusBarNotification[] getArray(int count, String pkg, int userId) { 359 if (count == 0) count = mBufferSize; 360 final StatusBarNotification[] a 361 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 362 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId); 363 int i=0; 364 while (iter.hasNext() && i < count) { 365 a[i++] = iter.next(); 366 } 367 return a; 368 } 369 370 } 371 372 private void loadPolicyFile() { 373 synchronized(mPolicyFile) { 374 mBlockedPackages.clear(); 375 376 FileInputStream infile = null; 377 try { 378 infile = mPolicyFile.openRead(); 379 final XmlPullParser parser = Xml.newPullParser(); 380 parser.setInput(infile, null); 381 382 int type; 383 String tag; 384 int version = DB_VERSION; 385 while ((type = parser.next()) != END_DOCUMENT) { 386 tag = parser.getName(); 387 if (type == START_TAG) { 388 if (TAG_BODY.equals(tag)) { 389 version = Integer.parseInt( 390 parser.getAttributeValue(null, ATTR_VERSION)); 391 } else if (TAG_BLOCKED_PKGS.equals(tag)) { 392 while ((type = parser.next()) != END_DOCUMENT) { 393 tag = parser.getName(); 394 if (TAG_PACKAGE.equals(tag)) { 395 mBlockedPackages.add( 396 parser.getAttributeValue(null, ATTR_NAME)); 397 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { 398 break; 399 } 400 } 401 } 402 } 403 mZenModeHelper.readXml(parser); 404 mRankingHelper.readXml(parser); 405 } 406 } catch (FileNotFoundException e) { 407 // No data yet 408 } catch (IOException e) { 409 Log.wtf(TAG, "Unable to read notification policy", e); 410 } catch (NumberFormatException e) { 411 Log.wtf(TAG, "Unable to parse notification policy", e); 412 } catch (XmlPullParserException e) { 413 Log.wtf(TAG, "Unable to parse notification policy", e); 414 } finally { 415 IoUtils.closeQuietly(infile); 416 } 417 } 418 } 419 420 public void savePolicyFile() { 421 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE); 422 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE); 423 } 424 425 private void handleSavePolicyFile() { 426 Slog.d(TAG, "handleSavePolicyFile"); 427 synchronized (mPolicyFile) { 428 final FileOutputStream stream; 429 try { 430 stream = mPolicyFile.startWrite(); 431 } catch (IOException e) { 432 Slog.w(TAG, "Failed to save policy file", e); 433 return; 434 } 435 436 try { 437 final XmlSerializer out = new FastXmlSerializer(); 438 out.setOutput(stream, "utf-8"); 439 out.startDocument(null, true); 440 out.startTag(null, TAG_BODY); 441 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); 442 mZenModeHelper.writeXml(out); 443 mRankingHelper.writeXml(out); 444 out.endTag(null, TAG_BODY); 445 out.endDocument(); 446 mPolicyFile.finishWrite(stream); 447 } catch (IOException e) { 448 Slog.w(TAG, "Failed to save policy file, restoring backup", e); 449 mPolicyFile.failWrite(stream); 450 } 451 } 452 } 453 454 /** Use this when you actually want to post a notification or toast. 455 * 456 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*(). 457 */ 458 private boolean noteNotificationOp(String pkg, int uid) { 459 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 460 != AppOpsManager.MODE_ALLOWED) { 461 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg); 462 return false; 463 } 464 return true; 465 } 466 467 private static final class ToastRecord 468 { 469 final int pid; 470 final String pkg; 471 final ITransientNotification callback; 472 int duration; 473 474 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) 475 { 476 this.pid = pid; 477 this.pkg = pkg; 478 this.callback = callback; 479 this.duration = duration; 480 } 481 482 void update(int duration) { 483 this.duration = duration; 484 } 485 486 void dump(PrintWriter pw, String prefix, DumpFilter filter) { 487 if (filter != null && !filter.matches(pkg)) return; 488 pw.println(prefix + this); 489 } 490 491 @Override 492 public final String toString() 493 { 494 return "ToastRecord{" 495 + Integer.toHexString(System.identityHashCode(this)) 496 + " pkg=" + pkg 497 + " callback=" + callback 498 + " duration=" + duration; 499 } 500 } 501 502 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() { 503 504 @Override 505 public void onSetDisabled(int status) { 506 synchronized (mNotificationList) { 507 mDisableNotificationEffects = 508 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; 509 if (disableNotificationEffects(null) != null) { 510 // cancel whatever's going on 511 long identity = Binder.clearCallingIdentity(); 512 try { 513 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 514 if (player != null) { 515 player.stopAsync(); 516 } 517 } catch (RemoteException e) { 518 } finally { 519 Binder.restoreCallingIdentity(identity); 520 } 521 522 identity = Binder.clearCallingIdentity(); 523 try { 524 mVibrator.cancel(); 525 } finally { 526 Binder.restoreCallingIdentity(identity); 527 } 528 } 529 } 530 } 531 532 @Override 533 public void onClearAll(int callingUid, int callingPid, int userId) { 534 synchronized (mNotificationList) { 535 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null, 536 /*includeCurrentProfiles*/ true); 537 } 538 } 539 540 @Override 541 public void onNotificationClick(int callingUid, int callingPid, String key) { 542 synchronized (mNotificationList) { 543 EventLogTags.writeNotificationClicked(key); 544 NotificationRecord r = mNotificationsByKey.get(key); 545 if (r == null) { 546 Log.w(TAG, "No notification with key: " + key); 547 return; 548 } 549 StatusBarNotification sbn = r.sbn; 550 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(), 551 sbn.getId(), Notification.FLAG_AUTO_CANCEL, 552 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(), 553 REASON_DELEGATE_CLICK, null); 554 } 555 } 556 557 @Override 558 public void onNotificationActionClick(int callingUid, int callingPid, String key, 559 int actionIndex) { 560 synchronized (mNotificationList) { 561 EventLogTags.writeNotificationActionClicked(key, actionIndex); 562 NotificationRecord r = mNotificationsByKey.get(key); 563 if (r == null) { 564 Log.w(TAG, "No notification with key: " + key); 565 return; 566 } 567 // TODO: Log action click via UsageStats. 568 } 569 } 570 571 @Override 572 public void onNotificationClear(int callingUid, int callingPid, 573 String pkg, String tag, int id, int userId) { 574 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 575 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 576 true, userId, REASON_DELEGATE_CANCEL, null); 577 } 578 579 @Override 580 public void onPanelRevealed(boolean clearEffects) { 581 EventLogTags.writeNotificationPanelRevealed(); 582 if (clearEffects) { 583 clearEffects(); 584 } 585 } 586 587 @Override 588 public void onPanelHidden() { 589 EventLogTags.writeNotificationPanelHidden(); 590 } 591 592 @Override 593 public void clearEffects() { 594 synchronized (mNotificationList) { 595 if (DBG) Slog.d(TAG, "clearEffects"); 596 597 // sound 598 mSoundNotificationKey = null; 599 600 long identity = Binder.clearCallingIdentity(); 601 try { 602 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 603 if (player != null) { 604 player.stopAsync(); 605 } 606 } catch (RemoteException e) { 607 } finally { 608 Binder.restoreCallingIdentity(identity); 609 } 610 611 // vibrate 612 mVibrateNotificationKey = null; 613 identity = Binder.clearCallingIdentity(); 614 try { 615 mVibrator.cancel(); 616 } finally { 617 Binder.restoreCallingIdentity(identity); 618 } 619 620 // light 621 mLights.clear(); 622 updateLightsLocked(); 623 } 624 } 625 626 @Override 627 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id, 628 int uid, int initialPid, String message, int userId) { 629 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id 630 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")"); 631 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId, 632 REASON_DELEGATE_ERROR, null); 633 long ident = Binder.clearCallingIdentity(); 634 try { 635 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, 636 "Bad notification posted from package " + pkg 637 + ": " + message); 638 } catch (RemoteException e) { 639 } 640 Binder.restoreCallingIdentity(ident); 641 } 642 643 @Override 644 public void onNotificationVisibilityChanged( 645 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) { 646 // Using ';' as separator since eventlogs uses ',' to separate 647 // args. 648 EventLogTags.writeNotificationVisibilityChanged( 649 TextUtils.join(";", newlyVisibleKeys), 650 TextUtils.join(";", noLongerVisibleKeys)); 651 synchronized (mNotificationList) { 652 for (String key : newlyVisibleKeys) { 653 NotificationRecord r = mNotificationsByKey.get(key); 654 if (r == null) continue; 655 r.stats.onVisibilityChanged(true); 656 } 657 // Note that we might receive this event after notifications 658 // have already left the system, e.g. after dismissing from the 659 // shade. Hence not finding notifications in 660 // mNotificationsByKey is not an exceptional condition. 661 for (String key : noLongerVisibleKeys) { 662 NotificationRecord r = mNotificationsByKey.get(key); 663 if (r == null) continue; 664 r.stats.onVisibilityChanged(false); 665 } 666 } 667 } 668 669 @Override 670 public void onNotificationExpansionChanged(String key, 671 boolean userAction, boolean expanded) { 672 EventLogTags.writeNotificationExpansion(key, userAction ? 1 : 0, expanded ? 1 : 0); 673 synchronized (mNotificationList) { 674 NotificationRecord r = mNotificationsByKey.get(key); 675 if (r != null) { 676 r.stats.onExpansionChanged(userAction, expanded); 677 } 678 } 679 } 680 }; 681 682 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() { 683 @Override 684 public void onReceive(Context context, Intent intent) { 685 String action = intent.getAction(); 686 if (action == null) { 687 return; 688 } 689 690 boolean queryRestart = false; 691 boolean queryRemove = false; 692 boolean packageChanged = false; 693 boolean cancelNotifications = true; 694 695 if (action.equals(Intent.ACTION_PACKAGE_ADDED) 696 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED)) 697 || action.equals(Intent.ACTION_PACKAGE_RESTARTED) 698 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED)) 699 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) 700 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 701 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 702 UserHandle.USER_ALL); 703 String pkgList[] = null; 704 boolean queryReplace = queryRemove && 705 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 706 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace); 707 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 708 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 709 } else if (queryRestart) { 710 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 711 } else { 712 Uri uri = intent.getData(); 713 if (uri == null) { 714 return; 715 } 716 String pkgName = uri.getSchemeSpecificPart(); 717 if (pkgName == null) { 718 return; 719 } 720 if (packageChanged) { 721 // We cancel notifications for packages which have just been disabled 722 try { 723 final IPackageManager pm = AppGlobals.getPackageManager(); 724 final int enabled = pm.getApplicationEnabledSetting(pkgName, 725 changeUserId != UserHandle.USER_ALL ? changeUserId : 726 UserHandle.USER_OWNER); 727 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED 728 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { 729 cancelNotifications = false; 730 } 731 } catch (IllegalArgumentException e) { 732 // Package doesn't exist; probably racing with uninstall. 733 // cancelNotifications is already true, so nothing to do here. 734 if (DBG) { 735 Slog.i(TAG, "Exception trying to look up app enabled setting", e); 736 } 737 } catch (RemoteException e) { 738 // Failed to talk to PackageManagerService Should never happen! 739 } 740 } 741 pkgList = new String[]{pkgName}; 742 } 743 744 if (pkgList != null && (pkgList.length > 0)) { 745 for (String pkgName : pkgList) { 746 if (cancelNotifications) { 747 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart, 748 changeUserId, REASON_PACKAGE_CHANGED, null); 749 } 750 } 751 } 752 mListeners.onPackagesChanged(queryReplace, pkgList); 753 mConditionProviders.onPackagesChanged(queryReplace, pkgList); 754 } 755 } 756 }; 757 758 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 759 @Override 760 public void onReceive(Context context, Intent intent) { 761 String action = intent.getAction(); 762 763 if (action.equals(Intent.ACTION_SCREEN_ON)) { 764 // Keep track of screen on/off state, but do not turn off the notification light 765 // until user passes through the lock screen or views the notification. 766 mScreenOn = true; 767 updateNotificationPulse(); 768 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 769 mScreenOn = false; 770 updateNotificationPulse(); 771 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 772 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK 773 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE)); 774 updateNotificationPulse(); 775 } else if (action.equals(Intent.ACTION_USER_STOPPED)) { 776 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 777 if (userHandle >= 0) { 778 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle, 779 REASON_USER_STOPPED, null); 780 } 781 } else if (action.equals(Intent.ACTION_USER_PRESENT)) { 782 // turn off LED when user passes through lock screen 783 mNotificationLight.turnOff(); 784 mStatusBar.notificationLightOff(); 785 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { 786 // reload per-user settings 787 mSettingsObserver.update(null); 788 mUserProfiles.updateCache(context); 789 // Refresh managed services 790 mConditionProviders.onUserSwitched(); 791 mListeners.onUserSwitched(); 792 } else if (action.equals(Intent.ACTION_USER_ADDED)) { 793 mUserProfiles.updateCache(context); 794 } 795 } 796 }; 797 798 class SettingsObserver extends ContentObserver { 799 private final Uri NOTIFICATION_LIGHT_PULSE_URI 800 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); 801 802 SettingsObserver(Handler handler) { 803 super(handler); 804 } 805 806 void observe() { 807 ContentResolver resolver = getContext().getContentResolver(); 808 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, 809 false, this, UserHandle.USER_ALL); 810 update(null); 811 } 812 813 @Override public void onChange(boolean selfChange, Uri uri) { 814 update(uri); 815 } 816 817 public void update(Uri uri) { 818 ContentResolver resolver = getContext().getContentResolver(); 819 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) { 820 boolean pulseEnabled = Settings.System.getInt(resolver, 821 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; 822 if (mNotificationPulseEnabled != pulseEnabled) { 823 mNotificationPulseEnabled = pulseEnabled; 824 updateNotificationPulse(); 825 } 826 } 827 } 828 } 829 830 private SettingsObserver mSettingsObserver; 831 private ZenModeHelper mZenModeHelper; 832 833 private final Runnable mBuzzBeepBlinked = new Runnable() { 834 @Override 835 public void run() { 836 mStatusBar.buzzBeepBlinked(); 837 } 838 }; 839 840 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) { 841 int[] ar = r.getIntArray(resid); 842 if (ar == null) { 843 return def; 844 } 845 final int len = ar.length > maxlen ? maxlen : ar.length; 846 long[] out = new long[len]; 847 for (int i=0; i<len; i++) { 848 out[i] = ar[i]; 849 } 850 return out; 851 } 852 853 public NotificationManagerService(Context context) { 854 super(context); 855 } 856 857 @Override 858 public void onStart() { 859 Resources resources = getContext().getResources(); 860 861 mAm = ActivityManagerNative.getDefault(); 862 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); 863 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); 864 865 mHandler = new WorkerHandler(); 866 mRankingThread.start(); 867 String[] extractorNames; 868 try { 869 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors); 870 } catch (Resources.NotFoundException e) { 871 extractorNames = new String[0]; 872 } 873 mRankingHelper = new RankingHelper(getContext(), 874 new RankingWorkerHandler(mRankingThread.getLooper()), 875 extractorNames); 876 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper()); 877 mZenModeHelper.addCallback(new ZenModeHelper.Callback() { 878 @Override 879 public void onConfigChanged() { 880 savePolicyFile(); 881 } 882 883 @Override 884 void onZenModeChanged() { 885 synchronized(mNotificationList) { 886 updateInterruptionFilterLocked(); 887 } 888 } 889 }); 890 final File systemDir = new File(Environment.getDataDirectory(), "system"); 891 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml")); 892 mUsageStats = new NotificationUsageStats(getContext()); 893 894 importOldBlockDb(); 895 896 mListeners = new NotificationListeners(); 897 mConditionProviders = new ConditionProviders(getContext(), 898 mHandler, mUserProfiles, mZenModeHelper); 899 mStatusBar = getLocalService(StatusBarManagerInternal.class); 900 mStatusBar.setNotificationDelegate(mNotificationDelegate); 901 902 final LightsManager lights = getLocalService(LightsManager.class); 903 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS); 904 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION); 905 906 mDefaultNotificationColor = resources.getColor( 907 R.color.config_defaultNotificationColor); 908 mDefaultNotificationLedOn = resources.getInteger( 909 R.integer.config_defaultNotificationLedOn); 910 mDefaultNotificationLedOff = resources.getInteger( 911 R.integer.config_defaultNotificationLedOff); 912 913 mDefaultVibrationPattern = getLongArray(resources, 914 R.array.config_defaultNotificationVibePattern, 915 VIBRATE_PATTERN_MAXLEN, 916 DEFAULT_VIBRATE_PATTERN); 917 918 mFallbackVibrationPattern = getLongArray(resources, 919 R.array.config_notificationFallbackVibePattern, 920 VIBRATE_PATTERN_MAXLEN, 921 DEFAULT_VIBRATE_PATTERN); 922 923 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight); 924 925 // Don't start allowing notifications until the setup wizard has run once. 926 // After that, including subsequent boots, init with notifications turned on. 927 // This works on the first boot because the setup wizard will toggle this 928 // flag at least once and we'll go back to 0 after that. 929 if (0 == Settings.Global.getInt(getContext().getContentResolver(), 930 Settings.Global.DEVICE_PROVISIONED, 0)) { 931 mDisableNotificationEffects = true; 932 } 933 mZenModeHelper.readZenModeFromSetting(); 934 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter(); 935 936 mUserProfiles.updateCache(getContext()); 937 listenForCallState(); 938 939 // register for various Intents 940 IntentFilter filter = new IntentFilter(); 941 filter.addAction(Intent.ACTION_SCREEN_ON); 942 filter.addAction(Intent.ACTION_SCREEN_OFF); 943 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 944 filter.addAction(Intent.ACTION_USER_PRESENT); 945 filter.addAction(Intent.ACTION_USER_STOPPED); 946 filter.addAction(Intent.ACTION_USER_SWITCHED); 947 filter.addAction(Intent.ACTION_USER_ADDED); 948 getContext().registerReceiver(mIntentReceiver, filter); 949 950 IntentFilter pkgFilter = new IntentFilter(); 951 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 952 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 953 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 954 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 955 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 956 pkgFilter.addDataScheme("package"); 957 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null, 958 null); 959 960 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 961 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null, 962 null); 963 964 mSettingsObserver = new SettingsObserver(mHandler); 965 966 mArchive = new Archive(resources.getInteger( 967 R.integer.config_notificationServiceArchiveSize)); 968 969 publishBinderService(Context.NOTIFICATION_SERVICE, mService); 970 publishLocalService(NotificationManagerInternal.class, mInternalService); 971 } 972 973 /** 974 * Read the old XML-based app block database and import those blockages into the AppOps system. 975 */ 976 private void importOldBlockDb() { 977 loadPolicyFile(); 978 979 PackageManager pm = getContext().getPackageManager(); 980 for (String pkg : mBlockedPackages) { 981 PackageInfo info = null; 982 try { 983 info = pm.getPackageInfo(pkg, 0); 984 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false); 985 } catch (NameNotFoundException e) { 986 // forget you 987 } 988 } 989 mBlockedPackages.clear(); 990 } 991 992 @Override 993 public void onBootPhase(int phase) { 994 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 995 // no beeping until we're basically done booting 996 mSystemReady = true; 997 998 // Grab our optional AudioService 999 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 1000 mZenModeHelper.onSystemReady(); 1001 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 1002 // This observer will force an update when observe is called, causing us to 1003 // bind to listener services. 1004 mSettingsObserver.observe(); 1005 mListeners.onBootPhaseAppsCanStart(); 1006 mConditionProviders.onBootPhaseAppsCanStart(); 1007 } 1008 } 1009 1010 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) { 1011 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); 1012 1013 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, 1014 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 1015 1016 // Now, cancel any outstanding notifications that are part of a just-disabled app 1017 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { 1018 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid), 1019 REASON_PACKAGE_BANNED, null); 1020 } 1021 } 1022 1023 private void updateListenerHintsLocked() { 1024 final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS; 1025 if (hints == mListenerHints) return; 1026 mListenerHints = hints; 1027 scheduleListenerHintsChanged(hints); 1028 } 1029 1030 private void updateEffectsSuppressorLocked() { 1031 final ComponentName suppressor = !mListenersDisablingEffects.isEmpty() 1032 ? mListenersDisablingEffects.valueAt(0).component : null; 1033 if (Objects.equals(suppressor, mEffectsSuppressor)) return; 1034 mEffectsSuppressor = suppressor; 1035 mZenModeHelper.setEffectsSuppressed(suppressor != null); 1036 getContext().sendBroadcast(new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED) 1037 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)); 1038 } 1039 1040 private void updateInterruptionFilterLocked() { 1041 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter(); 1042 if (interruptionFilter == mInterruptionFilter) return; 1043 mInterruptionFilter = interruptionFilter; 1044 scheduleInterruptionFilterChanged(interruptionFilter); 1045 } 1046 1047 private final IBinder mService = new INotificationManager.Stub() { 1048 // Toasts 1049 // ============================================================================ 1050 1051 @Override 1052 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 1053 { 1054 if (DBG) { 1055 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback 1056 + " duration=" + duration); 1057 } 1058 1059 if (pkg == null || callback == null) { 1060 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 1061 return ; 1062 } 1063 1064 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); 1065 1066 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { 1067 if (!isSystemToast) { 1068 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 1069 return; 1070 } 1071 } 1072 1073 synchronized (mToastQueue) { 1074 int callingPid = Binder.getCallingPid(); 1075 long callingId = Binder.clearCallingIdentity(); 1076 try { 1077 ToastRecord record; 1078 int index = indexOfToastLocked(pkg, callback); 1079 // If it's already in the queue, we update it in place, we don't 1080 // move it to the end of the queue. 1081 if (index >= 0) { 1082 record = mToastQueue.get(index); 1083 record.update(duration); 1084 } else { 1085 // Limit the number of toasts that any given package except the android 1086 // package can enqueue. Prevents DOS attacks and deals with leaks. 1087 if (!isSystemToast) { 1088 int count = 0; 1089 final int N = mToastQueue.size(); 1090 for (int i=0; i<N; i++) { 1091 final ToastRecord r = mToastQueue.get(i); 1092 if (r.pkg.equals(pkg)) { 1093 count++; 1094 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1095 Slog.e(TAG, "Package has already posted " + count 1096 + " toasts. Not showing more. Package=" + pkg); 1097 return; 1098 } 1099 } 1100 } 1101 } 1102 1103 record = new ToastRecord(callingPid, pkg, callback, duration); 1104 mToastQueue.add(record); 1105 index = mToastQueue.size() - 1; 1106 keepProcessAliveLocked(callingPid); 1107 } 1108 // If it's at index 0, it's the current toast. It doesn't matter if it's 1109 // new or just been updated. Call back and tell it to show itself. 1110 // If the callback fails, this will remove it from the list, so don't 1111 // assume that it's valid after this. 1112 if (index == 0) { 1113 showNextToastLocked(); 1114 } 1115 } finally { 1116 Binder.restoreCallingIdentity(callingId); 1117 } 1118 } 1119 } 1120 1121 @Override 1122 public void cancelToast(String pkg, ITransientNotification callback) { 1123 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 1124 1125 if (pkg == null || callback == null) { 1126 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 1127 return ; 1128 } 1129 1130 synchronized (mToastQueue) { 1131 long callingId = Binder.clearCallingIdentity(); 1132 try { 1133 int index = indexOfToastLocked(pkg, callback); 1134 if (index >= 0) { 1135 cancelToastLocked(index); 1136 } else { 1137 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg 1138 + " callback=" + callback); 1139 } 1140 } finally { 1141 Binder.restoreCallingIdentity(callingId); 1142 } 1143 } 1144 } 1145 1146 @Override 1147 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, 1148 Notification notification, int[] idOut, int userId) throws RemoteException { 1149 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(), 1150 Binder.getCallingPid(), tag, id, notification, idOut, userId); 1151 } 1152 1153 @Override 1154 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 1155 checkCallerIsSystemOrSameApp(pkg); 1156 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1157 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 1158 // Don't allow client applications to cancel foreground service notis. 1159 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0, 1160 Binder.getCallingUid() == Process.SYSTEM_UID 1161 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL, 1162 null); 1163 } 1164 1165 @Override 1166 public void cancelAllNotifications(String pkg, int userId) { 1167 checkCallerIsSystemOrSameApp(pkg); 1168 1169 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1170 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 1171 1172 // Calling from user space, don't allow the canceling of actively 1173 // running foreground services. 1174 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(), 1175 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId, 1176 REASON_NOMAN_CANCEL_ALL, null); 1177 } 1178 1179 @Override 1180 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { 1181 checkCallerIsSystem(); 1182 1183 setNotificationsEnabledForPackageImpl(pkg, uid, enabled); 1184 } 1185 1186 /** 1187 * Use this when you just want to know if notifications are OK for this package. 1188 */ 1189 @Override 1190 public boolean areNotificationsEnabledForPackage(String pkg, int uid) { 1191 checkCallerIsSystem(); 1192 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 1193 == AppOpsManager.MODE_ALLOWED); 1194 } 1195 1196 @Override 1197 public void setPackagePriority(String pkg, int uid, int priority) { 1198 checkCallerIsSystem(); 1199 mRankingHelper.setPackagePriority(pkg, uid, priority); 1200 savePolicyFile(); 1201 } 1202 1203 @Override 1204 public int getPackagePriority(String pkg, int uid) { 1205 checkCallerIsSystem(); 1206 return mRankingHelper.getPackagePriority(pkg, uid); 1207 } 1208 1209 @Override 1210 public void setPackageVisibilityOverride(String pkg, int uid, int visibility) { 1211 checkCallerIsSystem(); 1212 mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility); 1213 savePolicyFile(); 1214 } 1215 1216 @Override 1217 public int getPackageVisibilityOverride(String pkg, int uid) { 1218 checkCallerIsSystem(); 1219 return mRankingHelper.getPackageVisibilityOverride(pkg, uid); 1220 } 1221 1222 /** 1223 * System-only API for getting a list of current (i.e. not cleared) notifications. 1224 * 1225 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1226 * @returns A list of all the notifications, in natural order. 1227 */ 1228 @Override 1229 public StatusBarNotification[] getActiveNotifications(String callingPkg) { 1230 // enforce() will ensure the calling uid has the correct permission 1231 getContext().enforceCallingOrSelfPermission( 1232 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1233 "NotificationManagerService.getActiveNotifications"); 1234 1235 StatusBarNotification[] tmp = null; 1236 int uid = Binder.getCallingUid(); 1237 1238 // noteOp will check to make sure the callingPkg matches the uid 1239 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1240 == AppOpsManager.MODE_ALLOWED) { 1241 synchronized (mNotificationList) { 1242 tmp = new StatusBarNotification[mNotificationList.size()]; 1243 final int N = mNotificationList.size(); 1244 for (int i=0; i<N; i++) { 1245 tmp[i] = mNotificationList.get(i).sbn; 1246 } 1247 } 1248 } 1249 return tmp; 1250 } 1251 1252 /** 1253 * System-only API for getting a list of recent (cleared, no longer shown) notifications. 1254 * 1255 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1256 */ 1257 @Override 1258 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { 1259 // enforce() will ensure the calling uid has the correct permission 1260 getContext().enforceCallingOrSelfPermission( 1261 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1262 "NotificationManagerService.getHistoricalNotifications"); 1263 1264 StatusBarNotification[] tmp = null; 1265 int uid = Binder.getCallingUid(); 1266 1267 // noteOp will check to make sure the callingPkg matches the uid 1268 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1269 == AppOpsManager.MODE_ALLOWED) { 1270 synchronized (mArchive) { 1271 tmp = mArchive.getArray(count); 1272 } 1273 } 1274 return tmp; 1275 } 1276 1277 /** 1278 * Register a listener binder directly with the notification manager. 1279 * 1280 * Only works with system callers. Apps should extend 1281 * {@link android.service.notification.NotificationListenerService}. 1282 */ 1283 @Override 1284 public void registerListener(final INotificationListener listener, 1285 final ComponentName component, final int userid) { 1286 enforceSystemOrSystemUI("INotificationManager.registerListener"); 1287 mListeners.registerService(listener, component, userid); 1288 } 1289 1290 /** 1291 * Remove a listener binder directly 1292 */ 1293 @Override 1294 public void unregisterListener(INotificationListener listener, int userid) { 1295 mListeners.unregisterService(listener, userid); 1296 } 1297 1298 /** 1299 * Allow an INotificationListener to simulate a "clear all" operation. 1300 * 1301 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} 1302 * 1303 * @param token The binder for the listener, to check that the caller is allowed 1304 */ 1305 @Override 1306 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) { 1307 final int callingUid = Binder.getCallingUid(); 1308 final int callingPid = Binder.getCallingPid(); 1309 long identity = Binder.clearCallingIdentity(); 1310 try { 1311 synchronized (mNotificationList) { 1312 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1313 if (keys != null) { 1314 final int N = keys.length; 1315 for (int i = 0; i < N; i++) { 1316 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1317 if (r == null) continue; 1318 final int userId = r.sbn.getUserId(); 1319 if (userId != info.userid && userId != UserHandle.USER_ALL && 1320 !mUserProfiles.isCurrentProfile(userId)) { 1321 throw new SecurityException("Disallowed call from listener: " 1322 + info.service); 1323 } 1324 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1325 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(), 1326 userId); 1327 } 1328 } else { 1329 cancelAllLocked(callingUid, callingPid, info.userid, 1330 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles()); 1331 } 1332 } 1333 } finally { 1334 Binder.restoreCallingIdentity(identity); 1335 } 1336 } 1337 1338 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info, 1339 int callingUid, int callingPid, String pkg, String tag, int id, int userId) { 1340 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 1341 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 1342 true, 1343 userId, REASON_LISTENER_CANCEL, info); 1344 } 1345 1346 /** 1347 * Allow an INotificationListener to simulate clearing (dismissing) a single notification. 1348 * 1349 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} 1350 * 1351 * @param token The binder for the listener, to check that the caller is allowed 1352 */ 1353 @Override 1354 public void cancelNotificationFromListener(INotificationListener token, String pkg, 1355 String tag, int id) { 1356 final int callingUid = Binder.getCallingUid(); 1357 final int callingPid = Binder.getCallingPid(); 1358 long identity = Binder.clearCallingIdentity(); 1359 try { 1360 synchronized (mNotificationList) { 1361 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1362 if (info.supportsProfiles()) { 1363 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) " 1364 + "from " + info.component 1365 + " use cancelNotification(key) instead."); 1366 } else { 1367 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1368 pkg, tag, id, info.userid); 1369 } 1370 } 1371 } finally { 1372 Binder.restoreCallingIdentity(identity); 1373 } 1374 } 1375 1376 /** 1377 * Allow an INotificationListener to request the list of outstanding notifications seen by 1378 * the current user. Useful when starting up, after which point the listener callbacks 1379 * should be used. 1380 * 1381 * @param token The binder for the listener, to check that the caller is allowed 1382 * @param keys An array of notification keys to fetch, or null to fetch everything 1383 * @returns The return value will contain the notifications specified in keys, in that 1384 * order, or if keys is null, all the notifications, in natural order. 1385 */ 1386 @Override 1387 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener( 1388 INotificationListener token, String[] keys, int trim) { 1389 synchronized (mNotificationList) { 1390 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1391 final boolean getKeys = keys != null; 1392 final int N = getKeys ? keys.length : mNotificationList.size(); 1393 final ArrayList<StatusBarNotification> list 1394 = new ArrayList<StatusBarNotification>(N); 1395 for (int i=0; i<N; i++) { 1396 final NotificationRecord r = getKeys 1397 ? mNotificationsByKey.get(keys[i]) 1398 : mNotificationList.get(i); 1399 if (r == null) continue; 1400 StatusBarNotification sbn = r.sbn; 1401 if (!isVisibleToListener(sbn, info)) continue; 1402 StatusBarNotification sbnToSend = 1403 (trim == TRIM_FULL) ? sbn : sbn.cloneLight(); 1404 list.add(sbnToSend); 1405 } 1406 return new ParceledListSlice<StatusBarNotification>(list); 1407 } 1408 } 1409 1410 @Override 1411 public void requestHintsFromListener(INotificationListener token, int hints) { 1412 final long identity = Binder.clearCallingIdentity(); 1413 try { 1414 synchronized (mNotificationList) { 1415 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1416 final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0; 1417 if (disableEffects) { 1418 mListenersDisablingEffects.add(info); 1419 } else { 1420 mListenersDisablingEffects.remove(info); 1421 } 1422 updateListenerHintsLocked(); 1423 updateEffectsSuppressorLocked(); 1424 } 1425 } finally { 1426 Binder.restoreCallingIdentity(identity); 1427 } 1428 } 1429 1430 @Override 1431 public int getHintsFromListener(INotificationListener token) { 1432 synchronized (mNotificationList) { 1433 return mListenerHints; 1434 } 1435 } 1436 1437 @Override 1438 public void requestInterruptionFilterFromListener(INotificationListener token, 1439 int interruptionFilter) throws RemoteException { 1440 final long identity = Binder.clearCallingIdentity(); 1441 try { 1442 synchronized (mNotificationList) { 1443 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1444 mZenModeHelper.requestFromListener(info.component, interruptionFilter); 1445 updateInterruptionFilterLocked(); 1446 } 1447 } finally { 1448 Binder.restoreCallingIdentity(identity); 1449 } 1450 } 1451 1452 @Override 1453 public int getInterruptionFilterFromListener(INotificationListener token) 1454 throws RemoteException { 1455 synchronized (mNotificationLight) { 1456 return mInterruptionFilter; 1457 } 1458 } 1459 1460 @Override 1461 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim) 1462 throws RemoteException { 1463 synchronized (mNotificationList) { 1464 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1465 if (info == null) return; 1466 mListeners.setOnNotificationPostedTrimLocked(info, trim); 1467 } 1468 } 1469 1470 @Override 1471 public ZenModeConfig getZenModeConfig() { 1472 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig"); 1473 return mZenModeHelper.getConfig(); 1474 } 1475 1476 @Override 1477 public boolean setZenModeConfig(ZenModeConfig config) { 1478 checkCallerIsSystem(); 1479 return mZenModeHelper.setConfig(config); 1480 } 1481 1482 @Override 1483 public void notifyConditions(String pkg, IConditionProvider provider, 1484 Condition[] conditions) { 1485 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider); 1486 checkCallerIsSystemOrSameApp(pkg); 1487 final long identity = Binder.clearCallingIdentity(); 1488 try { 1489 mConditionProviders.notifyConditions(pkg, info, conditions); 1490 } finally { 1491 Binder.restoreCallingIdentity(identity); 1492 } 1493 } 1494 1495 @Override 1496 public void requestZenModeConditions(IConditionListener callback, int relevance) { 1497 enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions"); 1498 mConditionProviders.requestZenModeConditions(callback, relevance); 1499 } 1500 1501 @Override 1502 public void setZenModeCondition(Condition condition) { 1503 enforceSystemOrSystemUI("INotificationManager.setZenModeCondition"); 1504 final long identity = Binder.clearCallingIdentity(); 1505 try { 1506 mConditionProviders.setZenModeCondition(condition, "binderCall"); 1507 } finally { 1508 Binder.restoreCallingIdentity(identity); 1509 } 1510 } 1511 1512 @Override 1513 public void setAutomaticZenModeConditions(Uri[] conditionIds) { 1514 enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions"); 1515 mConditionProviders.setAutomaticZenModeConditions(conditionIds); 1516 } 1517 1518 @Override 1519 public Condition[] getAutomaticZenModeConditions() { 1520 enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions"); 1521 return mConditionProviders.getAutomaticZenModeConditions(); 1522 } 1523 1524 private void enforceSystemOrSystemUI(String message) { 1525 if (isCallerSystem()) return; 1526 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 1527 message); 1528 } 1529 1530 @Override 1531 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1532 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1533 != PackageManager.PERMISSION_GRANTED) { 1534 pw.println("Permission Denial: can't dump NotificationManager from pid=" 1535 + Binder.getCallingPid() 1536 + ", uid=" + Binder.getCallingUid()); 1537 return; 1538 } 1539 1540 dumpImpl(pw, DumpFilter.parseFromArguments(args)); 1541 } 1542 1543 @Override 1544 public ComponentName getEffectsSuppressor() { 1545 enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor"); 1546 return mEffectsSuppressor; 1547 } 1548 1549 @Override 1550 public boolean matchesCallFilter(Bundle extras) { 1551 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter"); 1552 return mZenModeHelper.matchesCallFilter( 1553 UserHandle.getCallingUserHandle(), 1554 extras, 1555 mRankingHelper.findExtractor(ValidateNotificationPeople.class), 1556 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS, 1557 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY); 1558 } 1559 1560 @Override 1561 public boolean isSystemConditionProviderEnabled(String path) { 1562 enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled"); 1563 return mConditionProviders.isSystemConditionProviderEnabled(path); 1564 } 1565 }; 1566 1567 private String[] getActiveNotificationKeys(INotificationListener token) { 1568 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1569 final ArrayList<String> keys = new ArrayList<String>(); 1570 if (info.isEnabledForCurrentProfiles()) { 1571 synchronized (mNotificationList) { 1572 final int N = mNotificationList.size(); 1573 for (int i = 0; i < N; i++) { 1574 final StatusBarNotification sbn = mNotificationList.get(i).sbn; 1575 if (info.enabledAndUserMatches(sbn.getUserId())) { 1576 keys.add(sbn.getKey()); 1577 } 1578 } 1579 } 1580 } 1581 return keys.toArray(new String[keys.size()]); 1582 } 1583 1584 private String disableNotificationEffects(NotificationRecord record) { 1585 if (mDisableNotificationEffects) { 1586 return "booleanState"; 1587 } 1588 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) { 1589 return "listenerHints"; 1590 } 1591 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) { 1592 return "callState"; 1593 } 1594 return null; 1595 } 1596 1597 void dumpImpl(PrintWriter pw, DumpFilter filter) { 1598 pw.print("Current Notification Manager state"); 1599 if (filter != null) { 1600 pw.print(" (filtered to "); pw.print(filter); pw.print(")"); 1601 } 1602 pw.println(':'); 1603 int N; 1604 final boolean zenOnly = filter != null && filter.zen; 1605 1606 if (!zenOnly) { 1607 synchronized (mToastQueue) { 1608 N = mToastQueue.size(); 1609 if (N > 0) { 1610 pw.println(" Toast Queue:"); 1611 for (int i=0; i<N; i++) { 1612 mToastQueue.get(i).dump(pw, " ", filter); 1613 } 1614 pw.println(" "); 1615 } 1616 } 1617 } 1618 1619 synchronized (mNotificationList) { 1620 if (!zenOnly) { 1621 N = mNotificationList.size(); 1622 if (N > 0) { 1623 pw.println(" Notification List:"); 1624 for (int i=0; i<N; i++) { 1625 final NotificationRecord nr = mNotificationList.get(i); 1626 if (filter != null && !filter.matches(nr.sbn)) continue; 1627 nr.dump(pw, " ", getContext()); 1628 } 1629 pw.println(" "); 1630 } 1631 1632 if (filter == null) { 1633 N = mLights.size(); 1634 if (N > 0) { 1635 pw.println(" Lights List:"); 1636 for (int i=0; i<N; i++) { 1637 if (i == N - 1) { 1638 pw.print(" > "); 1639 } else { 1640 pw.print(" "); 1641 } 1642 pw.println(mLights.get(i)); 1643 } 1644 pw.println(" "); 1645 } 1646 pw.println(" mUseAttentionLight=" + mUseAttentionLight); 1647 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled); 1648 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey); 1649 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey); 1650 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects); 1651 pw.println(" mCallState=" + callStateToString(mCallState)); 1652 pw.println(" mSystemReady=" + mSystemReady); 1653 } 1654 pw.println(" mArchive=" + mArchive.toString()); 1655 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1656 int i=0; 1657 while (iter.hasNext()) { 1658 final StatusBarNotification sbn = iter.next(); 1659 if (filter != null && !filter.matches(sbn)) continue; 1660 pw.println(" " + sbn); 1661 if (++i >= 5) { 1662 if (iter.hasNext()) pw.println(" ..."); 1663 break; 1664 } 1665 } 1666 } 1667 1668 if (!zenOnly) { 1669 pw.println("\n Usage Stats:"); 1670 mUsageStats.dump(pw, " ", filter); 1671 } 1672 1673 if (filter == null || zenOnly) { 1674 pw.println("\n Zen Mode:"); 1675 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter); 1676 mZenModeHelper.dump(pw, " "); 1677 1678 pw.println("\n Zen Log:"); 1679 ZenLog.dump(pw, " "); 1680 } 1681 1682 if (!zenOnly) { 1683 pw.println("\n Ranking Config:"); 1684 mRankingHelper.dump(pw, " ", filter); 1685 1686 pw.println("\n Notification listeners:"); 1687 mListeners.dump(pw, filter); 1688 pw.print(" mListenerHints: "); pw.println(mListenerHints); 1689 pw.print(" mListenersDisablingEffects: ("); 1690 N = mListenersDisablingEffects.size(); 1691 for (int i = 0; i < N; i++) { 1692 final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i); 1693 if (i > 0) pw.print(','); 1694 pw.print(listener.component); 1695 } 1696 pw.println(')'); 1697 } 1698 1699 pw.println("\n Condition providers:"); 1700 mConditionProviders.dump(pw, filter); 1701 1702 pw.println("\n Group summaries:"); 1703 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) { 1704 NotificationRecord r = entry.getValue(); 1705 pw.println(" " + entry.getKey() + " -> " + r.getKey()); 1706 if (mNotificationsByKey.get(r.getKey()) != r) { 1707 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey."); 1708 r.dump(pw, " ", getContext()); 1709 } 1710 } 1711 } 1712 } 1713 1714 /** 1715 * The private API only accessible to the system process. 1716 */ 1717 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1718 @Override 1719 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, 1720 String tag, int id, Notification notification, int[] idReceived, int userId) { 1721 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 1722 idReceived, userId); 1723 } 1724 1725 @Override 1726 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId, 1727 int userId) { 1728 checkCallerIsSystem(); 1729 synchronized (mNotificationList) { 1730 int i = indexOfNotificationLocked(pkg, null, notificationId, userId); 1731 if (i < 0) { 1732 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with " 1733 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId); 1734 return; 1735 } 1736 NotificationRecord r = mNotificationList.get(i); 1737 StatusBarNotification sbn = r.sbn; 1738 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees 1739 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE, 1740 // we have to revert to the flags we received initially *and* force remove 1741 // FLAG_FOREGROUND_SERVICE. 1742 sbn.getNotification().flags = 1743 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE); 1744 mRankingHelper.sort(mNotificationList); 1745 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */); 1746 } 1747 } 1748 }; 1749 1750 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, 1751 final int callingPid, final String tag, final int id, final Notification notification, 1752 int[] idOut, int incomingUserId) { 1753 if (DBG) { 1754 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 1755 + " notification=" + notification); 1756 } 1757 checkCallerIsSystemOrSameApp(pkg); 1758 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 1759 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg); 1760 1761 final int userId = ActivityManager.handleIncomingUser(callingPid, 1762 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1763 final UserHandle user = new UserHandle(userId); 1764 1765 // Limit the number of notifications that any given package except the android 1766 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks. 1767 if (!isSystemNotification && !isNotificationFromListener) { 1768 synchronized (mNotificationList) { 1769 int count = 0; 1770 final int N = mNotificationList.size(); 1771 for (int i=0; i<N; i++) { 1772 final NotificationRecord r = mNotificationList.get(i); 1773 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1774 count++; 1775 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1776 Slog.e(TAG, "Package has already posted " + count 1777 + " notifications. Not showing more. package=" + pkg); 1778 return; 1779 } 1780 } 1781 } 1782 } 1783 } 1784 1785 if (pkg == null || notification == null) { 1786 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1787 + " id=" + id + " notification=" + notification); 1788 } 1789 if (notification.icon != 0) { 1790 if (!notification.isValid()) { 1791 throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg 1792 + " id=" + id + " notification=" + notification); 1793 } 1794 } 1795 1796 mHandler.post(new Runnable() { 1797 @Override 1798 public void run() { 1799 1800 synchronized (mNotificationList) { 1801 1802 // === Scoring === 1803 1804 // 0. Sanitize inputs 1805 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1806 Notification.PRIORITY_MAX); 1807 // Migrate notification flags to scores 1808 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1809 if (notification.priority < Notification.PRIORITY_MAX) { 1810 notification.priority = Notification.PRIORITY_MAX; 1811 } 1812 } else if (SCORE_ONGOING_HIGHER && 1813 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1814 if (notification.priority < Notification.PRIORITY_HIGH) { 1815 notification.priority = Notification.PRIORITY_HIGH; 1816 } 1817 } 1818 1819 // 1. initial score: buckets of 10, around the app [-20..20] 1820 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; 1821 1822 // 2. extract ranking signals from the notification data 1823 final StatusBarNotification n = new StatusBarNotification( 1824 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 1825 user); 1826 NotificationRecord r = new NotificationRecord(n, score); 1827 NotificationRecord old = mNotificationsByKey.get(n.getKey()); 1828 if (old != null) { 1829 // Retain ranking information from previous record 1830 r.copyRankingInformation(old); 1831 } 1832 1833 // Handle grouped notifications and bail out early if we 1834 // can to avoid extracting signals. 1835 handleGroupedNotificationLocked(r, old, callingUid, callingPid); 1836 boolean ignoreNotification = 1837 removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid); 1838 1839 // This conditional is a dirty hack to limit the logging done on 1840 // behalf of the download manager without affecting other apps. 1841 if (!pkg.equals("com.android.providers.downloads") 1842 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1843 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW; 1844 if (ignoreNotification) { 1845 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED; 1846 } else if (old != null) { 1847 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE; 1848 } 1849 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 1850 pkg, id, tag, userId, notification.toString(), 1851 enqueueStatus); 1852 } 1853 1854 if (ignoreNotification) { 1855 return; 1856 } 1857 1858 mRankingHelper.extractSignals(r); 1859 1860 // 3. Apply local rules 1861 1862 // blocked apps 1863 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1864 if (!isSystemNotification) { 1865 r.score = JUNK_SCORE; 1866 Slog.e(TAG, "Suppressing notification from package " + pkg 1867 + " by user request."); 1868 } 1869 } 1870 1871 if (r.score < SCORE_DISPLAY_THRESHOLD) { 1872 // Notification will be blocked because the score is too low. 1873 return; 1874 } 1875 1876 int index = indexOfNotificationLocked(n.getKey()); 1877 if (index < 0) { 1878 mNotificationList.add(r); 1879 mUsageStats.registerPostedByApp(r); 1880 } else { 1881 old = mNotificationList.get(index); 1882 mNotificationList.set(index, r); 1883 mUsageStats.registerUpdatedByApp(r, old); 1884 // Make sure we don't lose the foreground service state. 1885 notification.flags |= 1886 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1887 r.isUpdate = true; 1888 } 1889 1890 mNotificationsByKey.put(n.getKey(), r); 1891 1892 // Ensure if this is a foreground service that the proper additional 1893 // flags are set. 1894 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1895 notification.flags |= Notification.FLAG_ONGOING_EVENT 1896 | Notification.FLAG_NO_CLEAR; 1897 } 1898 1899 applyZenModeLocked(r); 1900 mRankingHelper.sort(mNotificationList); 1901 1902 if (notification.icon != 0) { 1903 StatusBarNotification oldSbn = (old != null) ? old.sbn : null; 1904 mListeners.notifyPostedLocked(n, oldSbn); 1905 } else { 1906 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1907 if (old != null && !old.isCanceled) { 1908 mListeners.notifyRemovedLocked(n); 1909 } 1910 // ATTENTION: in a future release we will bail out here 1911 // so that we do not play sounds, show lights, etc. for invalid 1912 // notifications 1913 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1914 + n.getPackageName()); 1915 } 1916 1917 buzzBeepBlinkLocked(r); 1918 } 1919 } 1920 }); 1921 1922 idOut[0] = id; 1923 } 1924 1925 /** 1926 * Ensures that grouped notification receive their special treatment. 1927 * 1928 * <p>Cancels group children if the new notification causes a group to lose 1929 * its summary.</p> 1930 * 1931 * <p>Updates mSummaryByGroupKey.</p> 1932 */ 1933 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old, 1934 int callingUid, int callingPid) { 1935 StatusBarNotification sbn = r.sbn; 1936 Notification n = sbn.getNotification(); 1937 String group = sbn.getGroupKey(); 1938 boolean isSummary = n.isGroupSummary(); 1939 1940 Notification oldN = old != null ? old.sbn.getNotification() : null; 1941 String oldGroup = old != null ? old.sbn.getGroupKey() : null; 1942 boolean oldIsSummary = old != null && oldN.isGroupSummary(); 1943 1944 if (oldIsSummary) { 1945 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup); 1946 if (removedSummary != old) { 1947 String removedKey = 1948 removedSummary != null ? removedSummary.getKey() : "<null>"; 1949 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() + 1950 ", removed=" + removedKey); 1951 } 1952 } 1953 if (isSummary) { 1954 mSummaryByGroupKey.put(group, r); 1955 } 1956 1957 // Clear out group children of the old notification if the update 1958 // causes the group summary to go away. This happens when the old 1959 // notification was a summary and the new one isn't, or when the old 1960 // notification was a summary and its group key changed. 1961 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) { 1962 cancelGroupChildrenLocked(old, callingUid, callingPid, null, 1963 REASON_GROUP_SUMMARY_CANCELED); 1964 } 1965 } 1966 1967 /** 1968 * Performs group notification optimizations if SysUI is the only active 1969 * notification listener and returns whether the given notification should 1970 * be ignored. 1971 * 1972 * <p>Returns true if the given notification is a child of a group with a 1973 * summary, which means that SysUI will never show it, and hence the new 1974 * notification can be safely ignored. Also cancels any previous instance 1975 * of the ignored notification.</p> 1976 * 1977 * <p>For summaries, cancels all children of that group, as SysUI will 1978 * never show them anymore.</p> 1979 * 1980 * @return true if the given notification can be ignored as an optimization 1981 */ 1982 private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r, 1983 NotificationRecord old, int callingUid, int callingPid) { 1984 // No optimizations are possible if listeners want groups. 1985 if (mListeners.notificationGroupsDesired()) { 1986 return false; 1987 } 1988 1989 StatusBarNotification sbn = r.sbn; 1990 String group = sbn.getGroupKey(); 1991 boolean isSummary = sbn.getNotification().isGroupSummary(); 1992 boolean isChild = sbn.getNotification().isGroupChild(); 1993 1994 NotificationRecord summary = mSummaryByGroupKey.get(group); 1995 if (isChild && summary != null) { 1996 // Child with an active summary -> ignore 1997 if (DBG) { 1998 Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary " 1999 + summary.getKey()); 2000 } 2001 // Make sure we don't leave an old version of the notification around. 2002 if (old != null) { 2003 if (DBG) { 2004 Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey()); 2005 } 2006 cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION); 2007 } 2008 return true; 2009 } else if (isSummary) { 2010 // Summary -> cancel children 2011 cancelGroupChildrenLocked(r, callingUid, callingPid, null, 2012 REASON_GROUP_OPTIMIZATION); 2013 } 2014 return false; 2015 } 2016 2017 private void buzzBeepBlinkLocked(NotificationRecord record) { 2018 boolean buzzBeepBlinked = false; 2019 final Notification notification = record.sbn.getNotification(); 2020 2021 // Should this notification make noise, vibe, or use the LED? 2022 final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD; 2023 final boolean canInterrupt = aboveThreshold && !record.isIntercepted(); 2024 if (DBG || record.isIntercepted()) 2025 Slog.v(TAG, 2026 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt + 2027 " intercept=" + record.isIntercepted() 2028 ); 2029 2030 final int currentUser; 2031 final long token = Binder.clearCallingIdentity(); 2032 try { 2033 currentUser = ActivityManager.getCurrentUser(); 2034 } finally { 2035 Binder.restoreCallingIdentity(token); 2036 } 2037 2038 // If we're not supposed to beep, vibrate, etc. then don't. 2039 final String disableEffects = disableNotificationEffects(record); 2040 if (disableEffects != null) { 2041 ZenLog.traceDisableEffects(record, disableEffects); 2042 } 2043 if (disableEffects == null 2044 && (!(record.isUpdate 2045 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 2046 && (record.getUserId() == UserHandle.USER_ALL || 2047 record.getUserId() == currentUser || 2048 mUserProfiles.isCurrentProfile(record.getUserId())) 2049 && canInterrupt 2050 && mSystemReady 2051 && mAudioManager != null) { 2052 if (DBG) Slog.v(TAG, "Interrupting!"); 2053 2054 sendAccessibilityEvent(notification, record.sbn.getPackageName()); 2055 2056 // sound 2057 2058 // should we use the default notification sound? (indicated either by 2059 // DEFAULT_SOUND or because notification.sound is pointing at 2060 // Settings.System.NOTIFICATION_SOUND) 2061 final boolean useDefaultSound = 2062 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 2063 Settings.System.DEFAULT_NOTIFICATION_URI 2064 .equals(notification.sound); 2065 2066 Uri soundUri = null; 2067 boolean hasValidSound = false; 2068 2069 if (useDefaultSound) { 2070 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 2071 2072 // check to see if the default notification sound is silent 2073 ContentResolver resolver = getContext().getContentResolver(); 2074 hasValidSound = Settings.System.getString(resolver, 2075 Settings.System.NOTIFICATION_SOUND) != null; 2076 } else if (notification.sound != null) { 2077 soundUri = notification.sound; 2078 hasValidSound = (soundUri != null); 2079 } 2080 2081 if (hasValidSound) { 2082 boolean looping = 2083 (notification.flags & Notification.FLAG_INSISTENT) != 0; 2084 AudioAttributes audioAttributes = audioAttributesForNotification(notification); 2085 mSoundNotificationKey = record.getKey(); 2086 // do not play notifications if stream volume is 0 (typically because 2087 // ringer mode is silent) or if there is a user of exclusive audio focus 2088 if ((mAudioManager.getStreamVolume( 2089 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0) 2090 && !mAudioManager.isAudioFocusExclusive()) { 2091 final long identity = Binder.clearCallingIdentity(); 2092 try { 2093 final IRingtonePlayer player = 2094 mAudioManager.getRingtonePlayer(); 2095 if (player != null) { 2096 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 2097 + " with attributes " + audioAttributes); 2098 player.playAsync(soundUri, record.sbn.getUser(), looping, 2099 audioAttributes); 2100 buzzBeepBlinked = true; 2101 } 2102 } catch (RemoteException e) { 2103 } finally { 2104 Binder.restoreCallingIdentity(identity); 2105 } 2106 } 2107 } 2108 2109 // vibrate 2110 // Does the notification want to specify its own vibration? 2111 final boolean hasCustomVibrate = notification.vibrate != null; 2112 2113 // new in 4.2: if there was supposed to be a sound and we're in vibrate 2114 // mode, and no other vibration is specified, we fall back to vibration 2115 final boolean convertSoundToVibration = 2116 !hasCustomVibrate 2117 && hasValidSound 2118 && (mAudioManager.getRingerModeInternal() 2119 == AudioManager.RINGER_MODE_VIBRATE); 2120 2121 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 2122 final boolean useDefaultVibrate = 2123 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 2124 2125 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 2126 && !(mAudioManager.getRingerModeInternal() 2127 == AudioManager.RINGER_MODE_SILENT)) { 2128 mVibrateNotificationKey = record.getKey(); 2129 2130 if (useDefaultVibrate || convertSoundToVibration) { 2131 // Escalate privileges so we can use the vibrator even if the 2132 // notifying app does not have the VIBRATE permission. 2133 long identity = Binder.clearCallingIdentity(); 2134 try { 2135 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 2136 useDefaultVibrate ? mDefaultVibrationPattern 2137 : mFallbackVibrationPattern, 2138 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 2139 ? 0: -1, audioAttributesForNotification(notification)); 2140 buzzBeepBlinked = true; 2141 } finally { 2142 Binder.restoreCallingIdentity(identity); 2143 } 2144 } else if (notification.vibrate.length > 1) { 2145 // If you want your own vibration pattern, you need the VIBRATE 2146 // permission 2147 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 2148 notification.vibrate, 2149 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 2150 ? 0: -1, audioAttributesForNotification(notification)); 2151 buzzBeepBlinked = true; 2152 } 2153 } 2154 } 2155 2156 // light 2157 // release the light 2158 boolean wasShowLights = mLights.remove(record.getKey()); 2159 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) { 2160 mLights.add(record.getKey()); 2161 updateLightsLocked(); 2162 if (mUseAttentionLight) { 2163 mAttentionLight.pulse(); 2164 } 2165 buzzBeepBlinked = true; 2166 } else if (wasShowLights) { 2167 updateLightsLocked(); 2168 } 2169 if (buzzBeepBlinked) { 2170 mHandler.post(mBuzzBeepBlinked); 2171 } 2172 } 2173 2174 private static AudioAttributes audioAttributesForNotification(Notification n) { 2175 if (n.audioAttributes != null 2176 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) { 2177 // the audio attributes are set and different from the default, use them 2178 return n.audioAttributes; 2179 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) { 2180 // the stream type is valid, use it 2181 return new AudioAttributes.Builder() 2182 .setInternalLegacyStreamType(n.audioStreamType) 2183 .build(); 2184 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) { 2185 return Notification.AUDIO_ATTRIBUTES_DEFAULT; 2186 } else { 2187 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType)); 2188 return Notification.AUDIO_ATTRIBUTES_DEFAULT; 2189 } 2190 } 2191 2192 void showNextToastLocked() { 2193 ToastRecord record = mToastQueue.get(0); 2194 while (record != null) { 2195 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 2196 try { 2197 record.callback.show(); 2198 scheduleTimeoutLocked(record); 2199 return; 2200 } catch (RemoteException e) { 2201 Slog.w(TAG, "Object died trying to show notification " + record.callback 2202 + " in package " + record.pkg); 2203 // remove it from the list and let the process die 2204 int index = mToastQueue.indexOf(record); 2205 if (index >= 0) { 2206 mToastQueue.remove(index); 2207 } 2208 keepProcessAliveLocked(record.pid); 2209 if (mToastQueue.size() > 0) { 2210 record = mToastQueue.get(0); 2211 } else { 2212 record = null; 2213 } 2214 } 2215 } 2216 } 2217 2218 void cancelToastLocked(int index) { 2219 ToastRecord record = mToastQueue.get(index); 2220 try { 2221 record.callback.hide(); 2222 } catch (RemoteException e) { 2223 Slog.w(TAG, "Object died trying to hide notification " + record.callback 2224 + " in package " + record.pkg); 2225 // don't worry about this, we're about to remove it from 2226 // the list anyway 2227 } 2228 mToastQueue.remove(index); 2229 keepProcessAliveLocked(record.pid); 2230 if (mToastQueue.size() > 0) { 2231 // Show the next one. If the callback fails, this will remove 2232 // it from the list, so don't assume that the list hasn't changed 2233 // after this point. 2234 showNextToastLocked(); 2235 } 2236 } 2237 2238 private void scheduleTimeoutLocked(ToastRecord r) 2239 { 2240 mHandler.removeCallbacksAndMessages(r); 2241 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 2242 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 2243 mHandler.sendMessageDelayed(m, delay); 2244 } 2245 2246 private void handleTimeout(ToastRecord record) 2247 { 2248 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 2249 synchronized (mToastQueue) { 2250 int index = indexOfToastLocked(record.pkg, record.callback); 2251 if (index >= 0) { 2252 cancelToastLocked(index); 2253 } 2254 } 2255 } 2256 2257 // lock on mToastQueue 2258 int indexOfToastLocked(String pkg, ITransientNotification callback) 2259 { 2260 IBinder cbak = callback.asBinder(); 2261 ArrayList<ToastRecord> list = mToastQueue; 2262 int len = list.size(); 2263 for (int i=0; i<len; i++) { 2264 ToastRecord r = list.get(i); 2265 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 2266 return i; 2267 } 2268 } 2269 return -1; 2270 } 2271 2272 // lock on mToastQueue 2273 void keepProcessAliveLocked(int pid) 2274 { 2275 int toastCount = 0; // toasts from this pid 2276 ArrayList<ToastRecord> list = mToastQueue; 2277 int N = list.size(); 2278 for (int i=0; i<N; i++) { 2279 ToastRecord r = list.get(i); 2280 if (r.pid == pid) { 2281 toastCount++; 2282 } 2283 } 2284 try { 2285 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 2286 } catch (RemoteException e) { 2287 // Shouldn't happen. 2288 } 2289 } 2290 2291 private void handleRankingReconsideration(Message message) { 2292 if (!(message.obj instanceof RankingReconsideration)) return; 2293 RankingReconsideration recon = (RankingReconsideration) message.obj; 2294 recon.run(); 2295 boolean changed; 2296 synchronized (mNotificationList) { 2297 final NotificationRecord record = mNotificationsByKey.get(recon.getKey()); 2298 if (record == null) { 2299 return; 2300 } 2301 int indexBefore = findNotificationRecordIndexLocked(record); 2302 boolean interceptBefore = record.isIntercepted(); 2303 int visibilityBefore = record.getPackageVisibilityOverride(); 2304 recon.applyChangesLocked(record); 2305 applyZenModeLocked(record); 2306 mRankingHelper.sort(mNotificationList); 2307 int indexAfter = findNotificationRecordIndexLocked(record); 2308 boolean interceptAfter = record.isIntercepted(); 2309 int visibilityAfter = record.getPackageVisibilityOverride(); 2310 changed = indexBefore != indexAfter || interceptBefore != interceptAfter 2311 || visibilityBefore != visibilityAfter; 2312 if (interceptBefore && !interceptAfter) { 2313 buzzBeepBlinkLocked(record); 2314 } 2315 } 2316 if (changed) { 2317 scheduleSendRankingUpdate(); 2318 } 2319 } 2320 2321 private void handleRankingConfigChange() { 2322 synchronized (mNotificationList) { 2323 final int N = mNotificationList.size(); 2324 ArrayList<String> orderBefore = new ArrayList<String>(N); 2325 int[] visibilities = new int[N]; 2326 for (int i = 0; i < N; i++) { 2327 final NotificationRecord r = mNotificationList.get(i); 2328 orderBefore.add(r.getKey()); 2329 visibilities[i] = r.getPackageVisibilityOverride(); 2330 mRankingHelper.extractSignals(r); 2331 } 2332 for (int i = 0; i < N; i++) { 2333 mRankingHelper.sort(mNotificationList); 2334 final NotificationRecord r = mNotificationList.get(i); 2335 if (!orderBefore.get(i).equals(r.getKey()) 2336 || visibilities[i] != r.getPackageVisibilityOverride()) { 2337 scheduleSendRankingUpdate(); 2338 return; 2339 } 2340 } 2341 } 2342 } 2343 2344 // let zen mode evaluate this record 2345 private void applyZenModeLocked(NotificationRecord record) { 2346 record.setIntercepted(mZenModeHelper.shouldIntercept(record)); 2347 } 2348 2349 // lock on mNotificationList 2350 private int findNotificationRecordIndexLocked(NotificationRecord target) { 2351 return mRankingHelper.indexOf(mNotificationList, target); 2352 } 2353 2354 private void scheduleSendRankingUpdate() { 2355 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 2356 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 2357 mHandler.sendMessage(m); 2358 } 2359 2360 private void handleSendRankingUpdate() { 2361 synchronized (mNotificationList) { 2362 mListeners.notifyRankingUpdateLocked(); 2363 } 2364 } 2365 2366 private void scheduleListenerHintsChanged(int state) { 2367 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED); 2368 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget(); 2369 } 2370 2371 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) { 2372 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED); 2373 mHandler.obtainMessage( 2374 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED, 2375 listenerInterruptionFilter, 2376 0).sendToTarget(); 2377 } 2378 2379 private void handleListenerHintsChanged(int hints) { 2380 synchronized (mNotificationList) { 2381 mListeners.notifyListenerHintsChangedLocked(hints); 2382 } 2383 } 2384 2385 private void handleListenerInterruptionFilterChanged(int interruptionFilter) { 2386 synchronized (mNotificationList) { 2387 mListeners.notifyInterruptionFilterChanged(interruptionFilter); 2388 } 2389 } 2390 2391 private final class WorkerHandler extends Handler 2392 { 2393 @Override 2394 public void handleMessage(Message msg) 2395 { 2396 switch (msg.what) 2397 { 2398 case MESSAGE_TIMEOUT: 2399 handleTimeout((ToastRecord)msg.obj); 2400 break; 2401 case MESSAGE_SAVE_POLICY_FILE: 2402 handleSavePolicyFile(); 2403 break; 2404 case MESSAGE_SEND_RANKING_UPDATE: 2405 handleSendRankingUpdate(); 2406 break; 2407 case MESSAGE_LISTENER_HINTS_CHANGED: 2408 handleListenerHintsChanged(msg.arg1); 2409 break; 2410 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED: 2411 handleListenerInterruptionFilterChanged(msg.arg1); 2412 break; 2413 } 2414 } 2415 2416 } 2417 2418 private final class RankingWorkerHandler extends Handler 2419 { 2420 public RankingWorkerHandler(Looper looper) { 2421 super(looper); 2422 } 2423 2424 @Override 2425 public void handleMessage(Message msg) { 2426 switch (msg.what) { 2427 case MESSAGE_RECONSIDER_RANKING: 2428 handleRankingReconsideration(msg); 2429 break; 2430 case MESSAGE_RANKING_CONFIG_CHANGE: 2431 handleRankingConfigChange(); 2432 break; 2433 } 2434 } 2435 } 2436 2437 // Notifications 2438 // ============================================================================ 2439 static int clamp(int x, int low, int high) { 2440 return (x < low) ? low : ((x > high) ? high : x); 2441 } 2442 2443 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 2444 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 2445 if (!manager.isEnabled()) { 2446 return; 2447 } 2448 2449 AccessibilityEvent event = 2450 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 2451 event.setPackageName(packageName); 2452 event.setClassName(Notification.class.getName()); 2453 event.setParcelableData(notification); 2454 CharSequence tickerText = notification.tickerText; 2455 if (!TextUtils.isEmpty(tickerText)) { 2456 event.getText().add(tickerText); 2457 } 2458 2459 manager.sendAccessibilityEvent(event); 2460 } 2461 2462 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 2463 // tell the app 2464 if (sendDelete) { 2465 if (r.getNotification().deleteIntent != null) { 2466 try { 2467 r.getNotification().deleteIntent.send(); 2468 } catch (PendingIntent.CanceledException ex) { 2469 // do nothing - there's no relevant way to recover, and 2470 // no reason to let this propagate 2471 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 2472 } 2473 } 2474 } 2475 2476 // status bar 2477 if (r.getNotification().icon != 0) { 2478 r.isCanceled = true; 2479 mListeners.notifyRemovedLocked(r.sbn); 2480 } 2481 2482 final String canceledKey = r.getKey(); 2483 2484 // sound 2485 if (canceledKey.equals(mSoundNotificationKey)) { 2486 mSoundNotificationKey = null; 2487 final long identity = Binder.clearCallingIdentity(); 2488 try { 2489 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2490 if (player != null) { 2491 player.stopAsync(); 2492 } 2493 } catch (RemoteException e) { 2494 } finally { 2495 Binder.restoreCallingIdentity(identity); 2496 } 2497 } 2498 2499 // vibrate 2500 if (canceledKey.equals(mVibrateNotificationKey)) { 2501 mVibrateNotificationKey = null; 2502 long identity = Binder.clearCallingIdentity(); 2503 try { 2504 mVibrator.cancel(); 2505 } 2506 finally { 2507 Binder.restoreCallingIdentity(identity); 2508 } 2509 } 2510 2511 // light 2512 mLights.remove(canceledKey); 2513 2514 // Record usage stats 2515 switch (reason) { 2516 case REASON_DELEGATE_CANCEL: 2517 case REASON_DELEGATE_CANCEL_ALL: 2518 case REASON_LISTENER_CANCEL: 2519 case REASON_LISTENER_CANCEL_ALL: 2520 mUsageStats.registerDismissedByUser(r); 2521 break; 2522 case REASON_NOMAN_CANCEL: 2523 case REASON_NOMAN_CANCEL_ALL: 2524 mUsageStats.registerRemovedByApp(r); 2525 break; 2526 case REASON_DELEGATE_CLICK: 2527 mUsageStats.registerCancelDueToClick(r); 2528 break; 2529 default: 2530 mUsageStats.registerCancelUnknown(r); 2531 break; 2532 } 2533 2534 mNotificationsByKey.remove(r.sbn.getKey()); 2535 String groupKey = r.getGroupKey(); 2536 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey); 2537 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) { 2538 mSummaryByGroupKey.remove(groupKey); 2539 } 2540 2541 // Save it for users of getHistoricalNotifications() 2542 mArchive.record(r.sbn); 2543 2544 EventLogTags.writeNotificationCanceled(canceledKey, reason); 2545 } 2546 2547 /** 2548 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2549 * and none of the {@code mustNotHaveFlags}. 2550 */ 2551 void cancelNotification(final int callingUid, final int callingPid, 2552 final String pkg, final String tag, final int id, 2553 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2554 final int userId, final int reason, final ManagedServiceInfo listener) { 2555 // In enqueueNotificationInternal notifications are added by scheduling the 2556 // work on the worker handler. Hence, we also schedule the cancel on this 2557 // handler to avoid a scenario where an add notification call followed by a 2558 // remove notification call ends up in not removing the notification. 2559 mHandler.post(new Runnable() { 2560 @Override 2561 public void run() { 2562 String listenerName = listener == null ? null : listener.component.toShortString(); 2563 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2564 mustHaveFlags, mustNotHaveFlags, reason, listenerName); 2565 2566 synchronized (mNotificationList) { 2567 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2568 if (index >= 0) { 2569 NotificationRecord r = mNotificationList.get(index); 2570 2571 // Ideally we'd do this in the caller of this method. However, that would 2572 // require the caller to also find the notification. 2573 if (reason == REASON_DELEGATE_CLICK) { 2574 mUsageStats.registerClickedByUser(r); 2575 } 2576 2577 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2578 return; 2579 } 2580 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2581 return; 2582 } 2583 2584 mNotificationList.remove(index); 2585 2586 cancelNotificationLocked(r, sendDelete, reason); 2587 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName, 2588 REASON_GROUP_SUMMARY_CANCELED); 2589 updateLightsLocked(); 2590 } 2591 } 2592 } 2593 }); 2594 } 2595 2596 /** 2597 * Determine whether the userId applies to the notification in question, either because 2598 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2599 */ 2600 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2601 return 2602 // looking for USER_ALL notifications? match everything 2603 userId == UserHandle.USER_ALL 2604 // a notification sent to USER_ALL matches any query 2605 || r.getUserId() == UserHandle.USER_ALL 2606 // an exact user match 2607 || r.getUserId() == userId; 2608 } 2609 2610 /** 2611 * Determine whether the userId applies to the notification in question, either because 2612 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2613 * because it matches one of the users profiles. 2614 */ 2615 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2616 return notificationMatchesUserId(r, userId) 2617 || mUserProfiles.isCurrentProfile(r.getUserId()); 2618 } 2619 2620 /** 2621 * Cancels all notifications from a given package that have all of the 2622 * {@code mustHaveFlags}. 2623 */ 2624 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2625 int mustNotHaveFlags, boolean doit, int userId, int reason, 2626 ManagedServiceInfo listener) { 2627 String listenerName = listener == null ? null : listener.component.toShortString(); 2628 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2629 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2630 listenerName); 2631 2632 synchronized (mNotificationList) { 2633 final int N = mNotificationList.size(); 2634 ArrayList<NotificationRecord> canceledNotifications = null; 2635 for (int i = N-1; i >= 0; --i) { 2636 NotificationRecord r = mNotificationList.get(i); 2637 if (!notificationMatchesUserId(r, userId)) { 2638 continue; 2639 } 2640 // Don't remove notifications to all, if there's no package name specified 2641 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2642 continue; 2643 } 2644 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2645 continue; 2646 } 2647 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2648 continue; 2649 } 2650 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2651 continue; 2652 } 2653 if (canceledNotifications == null) { 2654 canceledNotifications = new ArrayList<>(); 2655 } 2656 canceledNotifications.add(r); 2657 if (!doit) { 2658 return true; 2659 } 2660 mNotificationList.remove(i); 2661 cancelNotificationLocked(r, false, reason); 2662 } 2663 if (doit && canceledNotifications != null) { 2664 final int M = canceledNotifications.size(); 2665 for (int i = 0; i < M; i++) { 2666 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2667 listenerName, REASON_GROUP_SUMMARY_CANCELED); 2668 } 2669 } 2670 if (canceledNotifications != null) { 2671 updateLightsLocked(); 2672 } 2673 return canceledNotifications != null; 2674 } 2675 } 2676 2677 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2678 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2679 String listenerName = listener == null ? null : listener.component.toShortString(); 2680 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2681 null, userId, 0, 0, reason, listenerName); 2682 2683 ArrayList<NotificationRecord> canceledNotifications = null; 2684 final int N = mNotificationList.size(); 2685 for (int i=N-1; i>=0; i--) { 2686 NotificationRecord r = mNotificationList.get(i); 2687 if (includeCurrentProfiles) { 2688 if (!notificationMatchesCurrentProfiles(r, userId)) { 2689 continue; 2690 } 2691 } else { 2692 if (!notificationMatchesUserId(r, userId)) { 2693 continue; 2694 } 2695 } 2696 2697 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2698 | Notification.FLAG_NO_CLEAR)) == 0) { 2699 mNotificationList.remove(i); 2700 cancelNotificationLocked(r, true, reason); 2701 // Make a note so we can cancel children later. 2702 if (canceledNotifications == null) { 2703 canceledNotifications = new ArrayList<>(); 2704 } 2705 canceledNotifications.add(r); 2706 } 2707 } 2708 int M = canceledNotifications != null ? canceledNotifications.size() : 0; 2709 for (int i = 0; i < M; i++) { 2710 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2711 listenerName, REASON_GROUP_SUMMARY_CANCELED); 2712 } 2713 updateLightsLocked(); 2714 } 2715 2716 // Warning: The caller is responsible for invoking updateLightsLocked(). 2717 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid, 2718 String listenerName, int reason) { 2719 Notification n = r.getNotification(); 2720 if (!n.isGroupSummary()) { 2721 return; 2722 } 2723 2724 String pkg = r.sbn.getPackageName(); 2725 int userId = r.getUserId(); 2726 2727 if (pkg == null) { 2728 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey()); 2729 return; 2730 } 2731 2732 final int N = mNotificationList.size(); 2733 for (int i = N - 1; i >= 0; i--) { 2734 NotificationRecord childR = mNotificationList.get(i); 2735 StatusBarNotification childSbn = childR.sbn; 2736 if (childR.getNotification().isGroupChild() && 2737 childR.getGroupKey().equals(r.getGroupKey())) { 2738 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(), 2739 childSbn.getTag(), userId, 0, 0, reason, listenerName); 2740 mNotificationList.remove(i); 2741 cancelNotificationLocked(childR, false, reason); 2742 } 2743 } 2744 } 2745 2746 // lock on mNotificationList 2747 void updateLightsLocked() 2748 { 2749 // handle notification lights 2750 NotificationRecord ledNotification = null; 2751 while (ledNotification == null && !mLights.isEmpty()) { 2752 final String owner = mLights.get(mLights.size() - 1); 2753 ledNotification = mNotificationsByKey.get(owner); 2754 if (ledNotification == null) { 2755 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner); 2756 mLights.remove(owner); 2757 } 2758 } 2759 2760 // Don't flash while we are in a call or screen is on 2761 if (ledNotification == null || mInCall || mScreenOn) { 2762 mNotificationLight.turnOff(); 2763 mStatusBar.notificationLightOff(); 2764 } else { 2765 final Notification ledno = ledNotification.sbn.getNotification(); 2766 int ledARGB = ledno.ledARGB; 2767 int ledOnMS = ledno.ledOnMS; 2768 int ledOffMS = ledno.ledOffMS; 2769 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2770 ledARGB = mDefaultNotificationColor; 2771 ledOnMS = mDefaultNotificationLedOn; 2772 ledOffMS = mDefaultNotificationLedOff; 2773 } 2774 if (mNotificationPulseEnabled) { 2775 // pulse repeatedly 2776 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2777 ledOnMS, ledOffMS); 2778 } 2779 // let SystemUI make an independent decision 2780 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS); 2781 } 2782 } 2783 2784 // lock on mNotificationList 2785 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2786 { 2787 ArrayList<NotificationRecord> list = mNotificationList; 2788 final int len = list.size(); 2789 for (int i=0; i<len; i++) { 2790 NotificationRecord r = list.get(i); 2791 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2792 continue; 2793 } 2794 if (tag == null) { 2795 if (r.sbn.getTag() != null) { 2796 continue; 2797 } 2798 } else { 2799 if (!tag.equals(r.sbn.getTag())) { 2800 continue; 2801 } 2802 } 2803 if (r.sbn.getPackageName().equals(pkg)) { 2804 return i; 2805 } 2806 } 2807 return -1; 2808 } 2809 2810 // lock on mNotificationList 2811 int indexOfNotificationLocked(String key) { 2812 final int N = mNotificationList.size(); 2813 for (int i = 0; i < N; i++) { 2814 if (key.equals(mNotificationList.get(i).getKey())) { 2815 return i; 2816 } 2817 } 2818 return -1; 2819 } 2820 2821 private void updateNotificationPulse() { 2822 synchronized (mNotificationList) { 2823 updateLightsLocked(); 2824 } 2825 } 2826 2827 private static boolean isUidSystem(int uid) { 2828 final int appid = UserHandle.getAppId(uid); 2829 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2830 } 2831 2832 private static boolean isCallerSystem() { 2833 return isUidSystem(Binder.getCallingUid()); 2834 } 2835 2836 private static void checkCallerIsSystem() { 2837 if (isCallerSystem()) { 2838 return; 2839 } 2840 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2841 } 2842 2843 private static void checkCallerIsSystemOrSameApp(String pkg) { 2844 if (isCallerSystem()) { 2845 return; 2846 } 2847 final int uid = Binder.getCallingUid(); 2848 try { 2849 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2850 pkg, 0, UserHandle.getCallingUserId()); 2851 if (ai == null) { 2852 throw new SecurityException("Unknown package " + pkg); 2853 } 2854 if (!UserHandle.isSameApp(ai.uid, uid)) { 2855 throw new SecurityException("Calling uid " + uid + " gave package" 2856 + pkg + " which is owned by uid " + ai.uid); 2857 } 2858 } catch (RemoteException re) { 2859 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2860 } 2861 } 2862 2863 private static String callStateToString(int state) { 2864 switch (state) { 2865 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE"; 2866 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING"; 2867 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK"; 2868 default: return "CALL_STATE_UNKNOWN_" + state; 2869 } 2870 } 2871 2872 private void listenForCallState() { 2873 TelephonyManager.from(getContext()).listen(new PhoneStateListener() { 2874 @Override 2875 public void onCallStateChanged(int state, String incomingNumber) { 2876 if (mCallState == state) return; 2877 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state)); 2878 mCallState = state; 2879 } 2880 }, PhoneStateListener.LISTEN_CALL_STATE); 2881 } 2882 2883 /** 2884 * Generates a NotificationRankingUpdate from 'sbns', considering only 2885 * notifications visible to the given listener. 2886 * 2887 * <p>Caller must hold a lock on mNotificationList.</p> 2888 */ 2889 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { 2890 int speedBumpIndex = -1; 2891 final int N = mNotificationList.size(); 2892 ArrayList<String> keys = new ArrayList<String>(N); 2893 ArrayList<String> interceptedKeys = new ArrayList<String>(N); 2894 Bundle visibilityOverrides = new Bundle(); 2895 for (int i = 0; i < N; i++) { 2896 NotificationRecord record = mNotificationList.get(i); 2897 if (!isVisibleToListener(record.sbn, info)) { 2898 continue; 2899 } 2900 keys.add(record.sbn.getKey()); 2901 if (record.isIntercepted()) { 2902 interceptedKeys.add(record.sbn.getKey()); 2903 } 2904 if (record.getPackageVisibilityOverride() 2905 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) { 2906 visibilityOverrides.putInt(record.sbn.getKey(), 2907 record.getPackageVisibilityOverride()); 2908 } 2909 // Find first min-prio notification for speedbump placement. 2910 if (speedBumpIndex == -1 && 2911 // Intrusiveness trumps priority, hence ignore intrusives. 2912 !record.isRecentlyIntrusive() && 2913 // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so 2914 // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT 2915 // (or lower as a safeguard) is sufficient to find the speedbump index. 2916 // We'll have to revisit this when more package priority buckets are introduced. 2917 record.getPackagePriority() <= Notification.PRIORITY_DEFAULT && 2918 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) { 2919 speedBumpIndex = keys.size() - 1; 2920 } 2921 } 2922 String[] keysAr = keys.toArray(new String[keys.size()]); 2923 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]); 2924 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides, 2925 speedBumpIndex); 2926 } 2927 2928 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { 2929 if (!listener.enabledAndUserMatches(sbn.getUserId())) { 2930 return false; 2931 } 2932 // TODO: remove this for older listeners. 2933 return true; 2934 } 2935 2936 public class NotificationListeners extends ManagedServices { 2937 2938 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>(); 2939 private boolean mNotificationGroupsDesired; 2940 2941 public NotificationListeners() { 2942 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2943 } 2944 2945 @Override 2946 protected Config getConfig() { 2947 Config c = new Config(); 2948 c.caption = "notification listener"; 2949 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2950 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2951 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2952 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2953 c.clientLabel = R.string.notification_listener_binding_label; 2954 return c; 2955 } 2956 2957 @Override 2958 protected IInterface asInterface(IBinder binder) { 2959 return INotificationListener.Stub.asInterface(binder); 2960 } 2961 2962 @Override 2963 public void onServiceAdded(ManagedServiceInfo info) { 2964 final INotificationListener listener = (INotificationListener) info.service; 2965 final NotificationRankingUpdate update; 2966 synchronized (mNotificationList) { 2967 updateNotificationGroupsDesiredLocked(); 2968 update = makeRankingUpdateLocked(info); 2969 } 2970 try { 2971 listener.onListenerConnected(update); 2972 } catch (RemoteException e) { 2973 // we tried 2974 } 2975 } 2976 2977 @Override 2978 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 2979 if (mListenersDisablingEffects.remove(removed)) { 2980 updateListenerHintsLocked(); 2981 updateEffectsSuppressorLocked(); 2982 } 2983 mLightTrimListeners.remove(removed); 2984 updateNotificationGroupsDesiredLocked(); 2985 } 2986 2987 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) { 2988 if (trim == TRIM_LIGHT) { 2989 mLightTrimListeners.add(info); 2990 } else { 2991 mLightTrimListeners.remove(info); 2992 } 2993 } 2994 2995 public int getOnNotificationPostedTrim(ManagedServiceInfo info) { 2996 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL; 2997 2998 } 2999 3000 /** 3001 * asynchronously notify all listeners about a new notification 3002 * 3003 * <p> 3004 * Also takes care of removing a notification that has been visible to a listener before, 3005 * but isn't anymore. 3006 */ 3007 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) { 3008 // Lazily initialized snapshots of the notification. 3009 StatusBarNotification sbnClone = null; 3010 StatusBarNotification sbnCloneLight = null; 3011 3012 for (final ManagedServiceInfo info : mServices) { 3013 boolean sbnVisible = isVisibleToListener(sbn, info); 3014 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false; 3015 // This notification hasn't been and still isn't visible -> ignore. 3016 if (!oldSbnVisible && !sbnVisible) { 3017 continue; 3018 } 3019 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 3020 3021 // This notification became invisible -> remove the old one. 3022 if (oldSbnVisible && !sbnVisible) { 3023 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight(); 3024 mHandler.post(new Runnable() { 3025 @Override 3026 public void run() { 3027 notifyRemoved(info, oldSbnLightClone, update); 3028 } 3029 }); 3030 continue; 3031 } 3032 3033 final int trim = mListeners.getOnNotificationPostedTrim(info); 3034 3035 if (trim == TRIM_LIGHT && sbnCloneLight == null) { 3036 sbnCloneLight = sbn.cloneLight(); 3037 } else if (trim == TRIM_FULL && sbnClone == null) { 3038 sbnClone = sbn.clone(); 3039 } 3040 final StatusBarNotification sbnToPost = 3041 (trim == TRIM_FULL) ? sbnClone : sbnCloneLight; 3042 3043 mHandler.post(new Runnable() { 3044 @Override 3045 public void run() { 3046 notifyPosted(info, sbnToPost, update); 3047 } 3048 }); 3049 } 3050 } 3051 3052 /** 3053 * asynchronously notify all listeners about a removed notification 3054 */ 3055 public void notifyRemovedLocked(StatusBarNotification sbn) { 3056 // make a copy in case changes are made to the underlying Notification object 3057 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 3058 // notification 3059 final StatusBarNotification sbnLight = sbn.cloneLight(); 3060 for (final ManagedServiceInfo info : mServices) { 3061 if (!isVisibleToListener(sbn, info)) { 3062 continue; 3063 } 3064 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 3065 mHandler.post(new Runnable() { 3066 @Override 3067 public void run() { 3068 notifyRemoved(info, sbnLight, update); 3069 } 3070 }); 3071 } 3072 } 3073 3074 /** 3075 * asynchronously notify all listeners about a reordering of notifications 3076 */ 3077 public void notifyRankingUpdateLocked() { 3078 for (final ManagedServiceInfo serviceInfo : mServices) { 3079 if (!serviceInfo.isEnabledForCurrentProfiles()) { 3080 continue; 3081 } 3082 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo); 3083 mHandler.post(new Runnable() { 3084 @Override 3085 public void run() { 3086 notifyRankingUpdate(serviceInfo, update); 3087 } 3088 }); 3089 } 3090 } 3091 3092 public void notifyListenerHintsChangedLocked(final int hints) { 3093 for (final ManagedServiceInfo serviceInfo : mServices) { 3094 if (!serviceInfo.isEnabledForCurrentProfiles()) { 3095 continue; 3096 } 3097 mHandler.post(new Runnable() { 3098 @Override 3099 public void run() { 3100 notifyListenerHintsChanged(serviceInfo, hints); 3101 } 3102 }); 3103 } 3104 } 3105 3106 public void notifyInterruptionFilterChanged(final int interruptionFilter) { 3107 for (final ManagedServiceInfo serviceInfo : mServices) { 3108 if (!serviceInfo.isEnabledForCurrentProfiles()) { 3109 continue; 3110 } 3111 mHandler.post(new Runnable() { 3112 @Override 3113 public void run() { 3114 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter); 3115 } 3116 }); 3117 } 3118 } 3119 3120 private void notifyPosted(final ManagedServiceInfo info, 3121 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 3122 final INotificationListener listener = (INotificationListener)info.service; 3123 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); 3124 try { 3125 listener.onNotificationPosted(sbnHolder, rankingUpdate); 3126 } catch (RemoteException ex) { 3127 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 3128 } 3129 } 3130 3131 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn, 3132 NotificationRankingUpdate rankingUpdate) { 3133 if (!info.enabledAndUserMatches(sbn.getUserId())) { 3134 return; 3135 } 3136 final INotificationListener listener = (INotificationListener) info.service; 3137 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); 3138 try { 3139 listener.onNotificationRemoved(sbnHolder, rankingUpdate); 3140 } catch (RemoteException ex) { 3141 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 3142 } 3143 } 3144 3145 private void notifyRankingUpdate(ManagedServiceInfo info, 3146 NotificationRankingUpdate rankingUpdate) { 3147 final INotificationListener listener = (INotificationListener) info.service; 3148 try { 3149 listener.onNotificationRankingUpdate(rankingUpdate); 3150 } catch (RemoteException ex) { 3151 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 3152 } 3153 } 3154 3155 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) { 3156 final INotificationListener listener = (INotificationListener) info.service; 3157 try { 3158 listener.onListenerHintsChanged(hints); 3159 } catch (RemoteException ex) { 3160 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex); 3161 } 3162 } 3163 3164 private void notifyInterruptionFilterChanged(ManagedServiceInfo info, 3165 int interruptionFilter) { 3166 final INotificationListener listener = (INotificationListener) info.service; 3167 try { 3168 listener.onInterruptionFilterChanged(interruptionFilter); 3169 } catch (RemoteException ex) { 3170 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex); 3171 } 3172 } 3173 3174 private boolean isListenerPackage(String packageName) { 3175 if (packageName == null) { 3176 return false; 3177 } 3178 // TODO: clean up locking object later 3179 synchronized (mNotificationList) { 3180 for (final ManagedServiceInfo serviceInfo : mServices) { 3181 if (packageName.equals(serviceInfo.component.getPackageName())) { 3182 return true; 3183 } 3184 } 3185 } 3186 return false; 3187 } 3188 3189 /** 3190 * Returns whether any of the currently registered listeners wants to receive notification 3191 * groups. 3192 * 3193 * <p>Currently we assume groups are desired by non-SystemUI listeners.</p> 3194 */ 3195 public boolean notificationGroupsDesired() { 3196 return mNotificationGroupsDesired; 3197 } 3198 3199 private void updateNotificationGroupsDesiredLocked() { 3200 mNotificationGroupsDesired = true; 3201 // No listeners, no groups. 3202 if (mServices.isEmpty()) { 3203 mNotificationGroupsDesired = false; 3204 return; 3205 } 3206 // One listener: Check whether it's SysUI. 3207 if (mServices.size() == 1 && 3208 mServices.get(0).component.getPackageName().equals("com.android.systemui")) { 3209 mNotificationGroupsDesired = false; 3210 return; 3211 } 3212 } 3213 } 3214 3215 public static final class DumpFilter { 3216 public String pkgFilter; 3217 public boolean zen; 3218 3219 public static DumpFilter parseFromArguments(String[] args) { 3220 if (args != null && args.length == 2 && "p".equals(args[0]) 3221 && args[1] != null && !args[1].trim().isEmpty()) { 3222 final DumpFilter filter = new DumpFilter(); 3223 filter.pkgFilter = args[1].trim().toLowerCase(); 3224 return filter; 3225 } 3226 if (args != null && args.length == 1 && "zen".equals(args[0])) { 3227 final DumpFilter filter = new DumpFilter(); 3228 filter.zen = true; 3229 return filter; 3230 } 3231 return null; 3232 } 3233 3234 public boolean matches(StatusBarNotification sbn) { 3235 return zen ? true : sbn != null 3236 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg())); 3237 } 3238 3239 public boolean matches(ComponentName component) { 3240 return zen ? true : component != null && matches(component.getPackageName()); 3241 } 3242 3243 public boolean matches(String pkg) { 3244 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter); 3245 } 3246 3247 @Override 3248 public String toString() { 3249 return zen ? "zen" : ('\'' + pkgFilter + '\''); 3250 } 3251 } 3252 3253 /** 3254 * Wrapper for a StatusBarNotification object that allows transfer across a oneway 3255 * binder without sending large amounts of data over a oneway transaction. 3256 */ 3257 private static final class StatusBarNotificationHolder 3258 extends IStatusBarNotificationHolder.Stub { 3259 private StatusBarNotification mValue; 3260 3261 public StatusBarNotificationHolder(StatusBarNotification value) { 3262 mValue = value; 3263 } 3264 3265 /** Get the held value and clear it. This function should only be called once per holder */ 3266 @Override 3267 public StatusBarNotification get() { 3268 StatusBarNotification value = mValue; 3269 mValue = null; 3270 return value; 3271 } 3272 } 3273 } 3274