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