NotificationManagerService.java revision 71f18fd1b64071e486bafff237b1f87a56d4aead
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 org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 20import static org.xmlpull.v1.XmlPullParser.END_TAG; 21import static org.xmlpull.v1.XmlPullParser.START_TAG; 22 23import android.app.ActivityManager; 24import android.app.ActivityManagerNative; 25import android.app.AppGlobals; 26import android.app.AppOpsManager; 27import android.app.IActivityManager; 28import android.app.INotificationManager; 29import android.app.ITransientNotification; 30import android.app.Notification; 31import android.app.PendingIntent; 32import android.app.StatusBarManager; 33import android.content.BroadcastReceiver; 34import android.content.ComponentName; 35import android.content.ContentResolver; 36import android.content.Context; 37import android.content.Intent; 38import android.content.IntentFilter; 39import android.content.pm.ApplicationInfo; 40import android.content.pm.PackageInfo; 41import android.content.pm.PackageManager; 42import android.content.pm.PackageManager.NameNotFoundException; 43import android.content.pm.ParceledListSlice; 44import android.content.res.Resources; 45import android.database.ContentObserver; 46import android.graphics.Bitmap; 47import android.media.AudioManager; 48import android.media.IRingtonePlayer; 49import android.net.Uri; 50import android.os.Binder; 51import android.os.Environment; 52import android.os.Handler; 53import android.os.HandlerThread; 54import android.os.IBinder; 55import android.os.IInterface; 56import android.os.Looper; 57import android.os.Message; 58import android.os.Process; 59import android.os.RemoteException; 60import android.os.UserHandle; 61import android.os.Vibrator; 62import android.provider.Settings; 63import android.service.notification.INotificationListener; 64import android.service.notification.IConditionListener; 65import android.service.notification.IConditionProvider; 66import android.service.notification.NotificationListenerService; 67import android.service.notification.NotificationRankingUpdate; 68import android.service.notification.StatusBarNotification; 69import android.service.notification.Condition; 70import android.service.notification.ZenModeConfig; 71import android.telephony.TelephonyManager; 72import android.text.TextUtils; 73import android.util.ArrayMap; 74import android.util.AtomicFile; 75import android.util.Log; 76import android.util.Slog; 77import android.util.Xml; 78import android.view.accessibility.AccessibilityEvent; 79import android.view.accessibility.AccessibilityManager; 80import android.widget.Toast; 81 82import com.android.internal.R; 83import com.android.internal.util.FastXmlSerializer; 84import com.android.server.EventLogTags; 85import com.android.server.SystemService; 86import com.android.server.lights.Light; 87import com.android.server.lights.LightsManager; 88import com.android.server.notification.ManagedServices.ManagedServiceInfo; 89import com.android.server.notification.ManagedServices.UserProfiles; 90import com.android.server.notification.NotificationUsageStats.SingleNotificationStats; 91import com.android.server.statusbar.StatusBarManagerInternal; 92 93import libcore.io.IoUtils; 94 95import org.xmlpull.v1.XmlPullParser; 96import org.xmlpull.v1.XmlPullParserException; 97import org.xmlpull.v1.XmlSerializer; 98 99import java.io.File; 100import java.io.FileDescriptor; 101import java.io.FileInputStream; 102import java.io.FileNotFoundException; 103import java.io.FileOutputStream; 104import java.io.IOException; 105import java.io.PrintWriter; 106import java.lang.reflect.Array; 107import java.util.ArrayDeque; 108import java.util.ArrayList; 109import java.util.Arrays; 110import java.util.Collections; 111import java.util.HashSet; 112import java.util.Iterator; 113import java.util.NoSuchElementException; 114import java.util.concurrent.ExecutionException; 115import java.util.concurrent.TimeUnit; 116 117/** {@hide} */ 118public class NotificationManagerService extends SystemService { 119 static final String TAG = "NotificationService"; 120 static final boolean DBG = false; 121 122 static final int MAX_PACKAGE_NOTIFICATIONS = 50; 123 124 // message codes 125 static final int MESSAGE_TIMEOUT = 2; 126 static final int MESSAGE_SAVE_POLICY_FILE = 3; 127 static final int MESSAGE_RECONSIDER_RANKING = 4; 128 static final int MESSAGE_SEND_RANKING_UPDATE = 5; 129 130 static final int LONG_DELAY = 3500; // 3.5 seconds 131 static final int SHORT_DELAY = 2000; // 2 seconds 132 133 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; 134 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps 135 136 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; 137 static final boolean SCORE_ONGOING_HIGHER = false; 138 139 static final int JUNK_SCORE = -1000; 140 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; 141 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER; 142 143 // Notifications with scores below this will not interrupt the user, either via LED or 144 // sound or vibration 145 static final int SCORE_INTERRUPTION_THRESHOLD = 146 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; 147 148 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; 149 static final boolean ENABLE_BLOCKED_TOASTS = true; 150 151 private IActivityManager mAm; 152 AudioManager mAudioManager; 153 StatusBarManagerInternal mStatusBar; 154 Vibrator mVibrator; 155 156 final IBinder mForegroundToken = new Binder(); 157 private WorkerHandler mHandler; 158 private final HandlerThread mRankingThread = new HandlerThread("ranker", 159 Process.THREAD_PRIORITY_BACKGROUND); 160 private Handler mRankingHandler = null; 161 162 private Light mNotificationLight; 163 Light mAttentionLight; 164 private int mDefaultNotificationColor; 165 private int mDefaultNotificationLedOn; 166 167 private int mDefaultNotificationLedOff; 168 private long[] mDefaultVibrationPattern; 169 170 private long[] mFallbackVibrationPattern; 171 boolean mSystemReady; 172 173 private boolean mDisableNotificationAlerts; 174 NotificationRecord mSoundNotification; 175 NotificationRecord mVibrateNotification; 176 177 // for enabling and disabling notification pulse behavior 178 private boolean mScreenOn = true; 179 private boolean mInCall = false; 180 private boolean mNotificationPulseEnabled; 181 182 // used as a mutex for access to all active notifications & listeners 183 final ArrayList<NotificationRecord> mNotificationList = 184 new ArrayList<NotificationRecord>(); 185 final NotificationComparator mRankingComparator = new NotificationComparator(); 186 final ArrayMap<String, NotificationRecord> mNotificationsByKey = 187 new ArrayMap<String, NotificationRecord>(); 188 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); 189 190 ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); 191 NotificationRecord mLedNotification; 192 193 private AppOpsManager mAppOps; 194 195 // Notification control database. For now just contains disabled packages. 196 private AtomicFile mPolicyFile; 197 private HashSet<String> mBlockedPackages = new HashSet<String>(); 198 199 private static final int DB_VERSION = 1; 200 201 private static final String TAG_BODY = "notification-policy"; 202 private static final String ATTR_VERSION = "version"; 203 204 private static final String TAG_BLOCKED_PKGS = "blocked-packages"; 205 private static final String TAG_PACKAGE = "package"; 206 private static final String ATTR_NAME = "name"; 207 208 final ArrayList<NotificationSignalExtractor> mSignalExtractors = new ArrayList<NotificationSignalExtractor>(); 209 210 private final UserProfiles mUserProfiles = new UserProfiles(); 211 private NotificationListeners mListeners; 212 private ConditionProviders mConditionProviders; 213 private NotificationUsageStats mUsageStats; 214 215 private static final String EXTRA_INTERCEPT = "android.intercept"; 216 217 private static final int MY_UID = Process.myUid(); 218 private static final int MY_PID = Process.myPid(); 219 private static final int REASON_DELEGATE_CLICK = 1; 220 private static final int REASON_DELEGATE_CANCEL = 2; 221 private static final int REASON_DELEGATE_CANCEL_ALL = 3; 222 private static final int REASON_DELEGATE_ERROR = 4; 223 private static final int REASON_PACKAGE_CHANGED = 5; 224 private static final int REASON_USER_STOPPED = 6; 225 private static final int REASON_PACKAGE_BANNED = 7; 226 private static final int REASON_NOMAN_CANCEL = 8; 227 private static final int REASON_NOMAN_CANCEL_ALL = 9; 228 private static final int REASON_LISTENER_CANCEL = 10; 229 private static final int REASON_LISTENER_CANCEL_ALL = 11; 230 231 private static class Archive { 232 static final int BUFFER_SIZE = 250; 233 ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE); 234 235 public Archive() { 236 } 237 238 public String toString() { 239 final StringBuilder sb = new StringBuilder(); 240 final int N = mBuffer.size(); 241 sb.append("Archive ("); 242 sb.append(N); 243 sb.append(" notification"); 244 sb.append((N==1)?")":"s)"); 245 return sb.toString(); 246 } 247 248 public void record(StatusBarNotification nr) { 249 if (mBuffer.size() == BUFFER_SIZE) { 250 mBuffer.removeFirst(); 251 } 252 253 // We don't want to store the heavy bits of the notification in the archive, 254 // but other clients in the system process might be using the object, so we 255 // store a (lightened) copy. 256 mBuffer.addLast(nr.cloneLight()); 257 } 258 259 260 public void clear() { 261 mBuffer.clear(); 262 } 263 264 public Iterator<StatusBarNotification> descendingIterator() { 265 return mBuffer.descendingIterator(); 266 } 267 public Iterator<StatusBarNotification> ascendingIterator() { 268 return mBuffer.iterator(); 269 } 270 public Iterator<StatusBarNotification> filter( 271 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) { 272 return new Iterator<StatusBarNotification>() { 273 StatusBarNotification mNext = findNext(); 274 275 private StatusBarNotification findNext() { 276 while (iter.hasNext()) { 277 StatusBarNotification nr = iter.next(); 278 if ((pkg == null || nr.getPackageName() == pkg) 279 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) { 280 return nr; 281 } 282 } 283 return null; 284 } 285 286 @Override 287 public boolean hasNext() { 288 return mNext == null; 289 } 290 291 @Override 292 public StatusBarNotification next() { 293 StatusBarNotification next = mNext; 294 if (next == null) { 295 throw new NoSuchElementException(); 296 } 297 mNext = findNext(); 298 return next; 299 } 300 301 @Override 302 public void remove() { 303 iter.remove(); 304 } 305 }; 306 } 307 308 public StatusBarNotification[] getArray(int count) { 309 if (count == 0) count = Archive.BUFFER_SIZE; 310 final StatusBarNotification[] a 311 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 312 Iterator<StatusBarNotification> iter = descendingIterator(); 313 int i=0; 314 while (iter.hasNext() && i < count) { 315 a[i++] = iter.next(); 316 } 317 return a; 318 } 319 320 public StatusBarNotification[] getArray(int count, String pkg, int userId) { 321 if (count == 0) count = Archive.BUFFER_SIZE; 322 final StatusBarNotification[] a 323 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 324 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId); 325 int i=0; 326 while (iter.hasNext() && i < count) { 327 a[i++] = iter.next(); 328 } 329 return a; 330 } 331 332 } 333 334 Archive mArchive = new Archive(); 335 336 private void loadPolicyFile() { 337 synchronized(mPolicyFile) { 338 mBlockedPackages.clear(); 339 340 FileInputStream infile = null; 341 try { 342 infile = mPolicyFile.openRead(); 343 final XmlPullParser parser = Xml.newPullParser(); 344 parser.setInput(infile, null); 345 346 int type; 347 String tag; 348 int version = DB_VERSION; 349 while ((type = parser.next()) != END_DOCUMENT) { 350 tag = parser.getName(); 351 if (type == START_TAG) { 352 if (TAG_BODY.equals(tag)) { 353 version = Integer.parseInt( 354 parser.getAttributeValue(null, ATTR_VERSION)); 355 } else if (TAG_BLOCKED_PKGS.equals(tag)) { 356 while ((type = parser.next()) != END_DOCUMENT) { 357 tag = parser.getName(); 358 if (TAG_PACKAGE.equals(tag)) { 359 mBlockedPackages.add( 360 parser.getAttributeValue(null, ATTR_NAME)); 361 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { 362 break; 363 } 364 } 365 } 366 } 367 mZenModeHelper.readXml(parser); 368 } 369 } catch (FileNotFoundException e) { 370 // No data yet 371 } catch (IOException e) { 372 Log.wtf(TAG, "Unable to read notification policy", e); 373 } catch (NumberFormatException e) { 374 Log.wtf(TAG, "Unable to parse notification policy", e); 375 } catch (XmlPullParserException e) { 376 Log.wtf(TAG, "Unable to parse notification policy", e); 377 } finally { 378 IoUtils.closeQuietly(infile); 379 } 380 } 381 } 382 383 public void savePolicyFile() { 384 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE); 385 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE); 386 } 387 388 private void handleSavePolicyFile() { 389 Slog.d(TAG, "handleSavePolicyFile"); 390 synchronized (mPolicyFile) { 391 final FileOutputStream stream; 392 try { 393 stream = mPolicyFile.startWrite(); 394 } catch (IOException e) { 395 Slog.w(TAG, "Failed to save policy file", e); 396 return; 397 } 398 399 try { 400 final XmlSerializer out = new FastXmlSerializer(); 401 out.setOutput(stream, "utf-8"); 402 out.startDocument(null, true); 403 out.startTag(null, TAG_BODY); 404 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); 405 mZenModeHelper.writeXml(out); 406 out.endTag(null, TAG_BODY); 407 out.endDocument(); 408 mPolicyFile.finishWrite(stream); 409 } catch (IOException e) { 410 Slog.w(TAG, "Failed to save policy file, restoring backup", e); 411 mPolicyFile.failWrite(stream); 412 } 413 } 414 } 415 416 /** Use this when you actually want to post a notification or toast. 417 * 418 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*(). 419 */ 420 private boolean noteNotificationOp(String pkg, int uid) { 421 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 422 != AppOpsManager.MODE_ALLOWED) { 423 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg); 424 return false; 425 } 426 return true; 427 } 428 429 private static String idDebugString(Context baseContext, String packageName, int id) { 430 Context c = null; 431 432 if (packageName != null) { 433 try { 434 c = baseContext.createPackageContext(packageName, 0); 435 } catch (NameNotFoundException e) { 436 c = baseContext; 437 } 438 } else { 439 c = baseContext; 440 } 441 442 String pkg; 443 String type; 444 String name; 445 446 Resources r = c.getResources(); 447 try { 448 return r.getResourceName(id); 449 } catch (Resources.NotFoundException e) { 450 return "<name unknown>"; 451 } 452 } 453 454 455 456 public static final class NotificationRecord 457 { 458 final StatusBarNotification sbn; 459 SingleNotificationStats stats; 460 boolean isCanceled; 461 462 // These members are used by NotificationSignalExtractors 463 // to communicate with the ranking module. 464 private float mContactAffinity; 465 private boolean mRecentlyIntrusive; 466 467 NotificationRecord(StatusBarNotification sbn) 468 { 469 this.sbn = sbn; 470 } 471 472 public Notification getNotification() { return sbn.getNotification(); } 473 public int getFlags() { return sbn.getNotification().flags; } 474 public int getUserId() { return sbn.getUserId(); } 475 public String getKey() { return sbn.getKey(); } 476 477 void dump(PrintWriter pw, String prefix, Context baseContext) { 478 final Notification notification = sbn.getNotification(); 479 pw.println(prefix + this); 480 pw.println(prefix + " uid=" + sbn.getUid() + " userId=" + sbn.getUserId()); 481 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon) 482 + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon)); 483 pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore()); 484 pw.println(prefix + " key=" + sbn.getKey()); 485 pw.println(prefix + " contentIntent=" + notification.contentIntent); 486 pw.println(prefix + " deleteIntent=" + notification.deleteIntent); 487 pw.println(prefix + " tickerText=" + notification.tickerText); 488 pw.println(prefix + " contentView=" + notification.contentView); 489 pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x", 490 notification.defaults, notification.flags)); 491 pw.println(prefix + " sound=" + notification.sound); 492 pw.println(prefix + String.format(" color=0x%08x", notification.color)); 493 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); 494 pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d", 495 notification.ledARGB, notification.ledOnMS, notification.ledOffMS)); 496 if (notification.actions != null && notification.actions.length > 0) { 497 pw.println(prefix + " actions={"); 498 final int N = notification.actions.length; 499 for (int i=0; i<N; i++) { 500 final Notification.Action action = notification.actions[i]; 501 pw.println(String.format("%s [%d] \"%s\" -> %s", 502 prefix, 503 i, 504 action.title, 505 action.actionIntent.toString() 506 )); 507 } 508 pw.println(prefix + " }"); 509 } 510 if (notification.extras != null && notification.extras.size() > 0) { 511 pw.println(prefix + " extras={"); 512 for (String key : notification.extras.keySet()) { 513 pw.print(prefix + " " + key + "="); 514 Object val = notification.extras.get(key); 515 if (val == null) { 516 pw.println("null"); 517 } else { 518 pw.print(val.getClass().getSimpleName()); 519 if (val instanceof CharSequence || val instanceof String) { 520 // redact contents from bugreports 521 } else if (val instanceof Bitmap) { 522 pw.print(String.format(" (%dx%d)", 523 ((Bitmap) val).getWidth(), 524 ((Bitmap) val).getHeight())); 525 } else if (val.getClass().isArray()) { 526 final int N = Array.getLength(val); 527 pw.println(" (" + N + ")"); 528 } else { 529 pw.print(" (" + String.valueOf(val) + ")"); 530 } 531 pw.println(); 532 } 533 } 534 pw.println(prefix + " }"); 535 } 536 pw.println(prefix + " stats=" + stats.toString()); 537 pw.println(prefix + " mContactAffinity=" + mContactAffinity); 538 pw.println(prefix + " mRecentlyIntrusive=" + mRecentlyIntrusive); 539 } 540 541 @Override 542 public final String toString() { 543 return String.format( 544 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)", 545 System.identityHashCode(this), 546 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(), 547 this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(), 548 this.sbn.getNotification()); 549 } 550 551 public void setContactAffinity(float contactAffinity) { 552 mContactAffinity = contactAffinity; 553 } 554 555 public float getContactAffinity() { 556 return mContactAffinity; 557 } 558 559 public boolean isRecentlyIntrusive() { 560 return mRecentlyIntrusive; 561 } 562 563 public void setRecentlyIntusive(boolean recentlyIntrusive) { 564 mRecentlyIntrusive = recentlyIntrusive; 565 } 566 } 567 568 private static final class ToastRecord 569 { 570 final int pid; 571 final String pkg; 572 final ITransientNotification callback; 573 int duration; 574 575 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) 576 { 577 this.pid = pid; 578 this.pkg = pkg; 579 this.callback = callback; 580 this.duration = duration; 581 } 582 583 void update(int duration) { 584 this.duration = duration; 585 } 586 587 void dump(PrintWriter pw, String prefix) { 588 pw.println(prefix + this); 589 } 590 591 @Override 592 public final String toString() 593 { 594 return "ToastRecord{" 595 + Integer.toHexString(System.identityHashCode(this)) 596 + " pkg=" + pkg 597 + " callback=" + callback 598 + " duration=" + duration; 599 } 600 } 601 602 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() { 603 604 @Override 605 public void onSetDisabled(int status) { 606 synchronized (mNotificationList) { 607 mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; 608 if (mDisableNotificationAlerts) { 609 // cancel whatever's going on 610 long identity = Binder.clearCallingIdentity(); 611 try { 612 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 613 if (player != null) { 614 player.stopAsync(); 615 } 616 } catch (RemoteException e) { 617 } finally { 618 Binder.restoreCallingIdentity(identity); 619 } 620 621 identity = Binder.clearCallingIdentity(); 622 try { 623 mVibrator.cancel(); 624 } finally { 625 Binder.restoreCallingIdentity(identity); 626 } 627 } 628 } 629 } 630 631 @Override 632 public void onClearAll(int callingUid, int callingPid, int userId) { 633 synchronized (mNotificationList) { 634 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null, 635 /*includeCurrentProfiles*/ true); 636 } 637 } 638 639 @Override 640 public void onNotificationClick(int callingUid, int callingPid, String key) { 641 synchronized (mNotificationList) { 642 EventLogTags.writeNotificationClicked(key); 643 NotificationRecord r = mNotificationsByKey.get(key); 644 if (r == null) { 645 Log.w(TAG, "No notification with key: " + key); 646 return; 647 } 648 StatusBarNotification sbn = r.sbn; 649 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(), 650 sbn.getId(), Notification.FLAG_AUTO_CANCEL, 651 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(), 652 REASON_DELEGATE_CLICK, null); 653 } 654 } 655 656 @Override 657 public void onNotificationClear(int callingUid, int callingPid, 658 String pkg, String tag, int id, int userId) { 659 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 660 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 661 true, userId, REASON_DELEGATE_CANCEL, null); 662 } 663 664 @Override 665 public void onPanelRevealed() { 666 EventLogTags.writeNotificationPanelRevealed(); 667 synchronized (mNotificationList) { 668 // sound 669 mSoundNotification = null; 670 671 long identity = Binder.clearCallingIdentity(); 672 try { 673 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 674 if (player != null) { 675 player.stopAsync(); 676 } 677 } catch (RemoteException e) { 678 } finally { 679 Binder.restoreCallingIdentity(identity); 680 } 681 682 // vibrate 683 mVibrateNotification = null; 684 identity = Binder.clearCallingIdentity(); 685 try { 686 mVibrator.cancel(); 687 } finally { 688 Binder.restoreCallingIdentity(identity); 689 } 690 691 // light 692 mLights.clear(); 693 mLedNotification = null; 694 updateLightsLocked(); 695 } 696 } 697 698 @Override 699 public void onPanelHidden() { 700 EventLogTags.writeNotificationPanelHidden(); 701 } 702 703 @Override 704 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id, 705 int uid, int initialPid, String message, int userId) { 706 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id 707 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")"); 708 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId, 709 REASON_DELEGATE_ERROR, null); 710 long ident = Binder.clearCallingIdentity(); 711 try { 712 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, 713 "Bad notification posted from package " + pkg 714 + ": " + message); 715 } catch (RemoteException e) { 716 } 717 Binder.restoreCallingIdentity(ident); 718 } 719 720 @Override 721 public boolean allowDisable(int what, IBinder token, String pkg) { 722 return mZenModeHelper.allowDisable(what, token, pkg); 723 } 724 725 @Override 726 public void onNotificationVisibilityChanged( 727 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) { 728 // Using ';' as separator since eventlogs uses ',' to separate 729 // args. 730 EventLogTags.writeNotificationVisibilityChanged( 731 TextUtils.join(";", newlyVisibleKeys), 732 TextUtils.join(";", noLongerVisibleKeys)); 733 synchronized (mNotificationList) { 734 for (String key : newlyVisibleKeys) { 735 NotificationRecord r = mNotificationsByKey.get(key); 736 if (r == null) continue; 737 r.stats.onVisibilityChanged(true); 738 } 739 // Note that we might receive this event after notifications 740 // have already left the system, e.g. after dismissing from the 741 // shade. Hence not finding notifications in 742 // mNotificationsByKey is not an exceptional condition. 743 for (String key : noLongerVisibleKeys) { 744 NotificationRecord r = mNotificationsByKey.get(key); 745 if (r == null) continue; 746 r.stats.onVisibilityChanged(false); 747 } 748 } 749 } 750 }; 751 752 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 753 @Override 754 public void onReceive(Context context, Intent intent) { 755 String action = intent.getAction(); 756 757 boolean queryRestart = false; 758 boolean queryRemove = false; 759 boolean packageChanged = false; 760 boolean cancelNotifications = true; 761 762 if (action.equals(Intent.ACTION_PACKAGE_ADDED) 763 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED)) 764 || action.equals(Intent.ACTION_PACKAGE_RESTARTED) 765 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED)) 766 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) 767 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 768 String pkgList[] = null; 769 boolean queryReplace = queryRemove && 770 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 771 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace); 772 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 773 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 774 } else if (queryRestart) { 775 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 776 } else { 777 Uri uri = intent.getData(); 778 if (uri == null) { 779 return; 780 } 781 String pkgName = uri.getSchemeSpecificPart(); 782 if (pkgName == null) { 783 return; 784 } 785 if (packageChanged) { 786 // We cancel notifications for packages which have just been disabled 787 try { 788 final int enabled = getContext().getPackageManager() 789 .getApplicationEnabledSetting(pkgName); 790 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED 791 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { 792 cancelNotifications = false; 793 } 794 } catch (IllegalArgumentException e) { 795 // Package doesn't exist; probably racing with uninstall. 796 // cancelNotifications is already true, so nothing to do here. 797 if (DBG) { 798 Slog.i(TAG, "Exception trying to look up app enabled setting", e); 799 } 800 } 801 } 802 pkgList = new String[]{pkgName}; 803 } 804 805 if (pkgList != null && (pkgList.length > 0)) { 806 for (String pkgName : pkgList) { 807 if (cancelNotifications) { 808 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart, 809 UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null); 810 } 811 } 812 } 813 mListeners.onPackagesChanged(queryReplace, pkgList); 814 mConditionProviders.onPackagesChanged(queryReplace, pkgList); 815 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 816 // Keep track of screen on/off state, but do not turn off the notification light 817 // until user passes through the lock screen or views the notification. 818 mScreenOn = true; 819 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 820 mScreenOn = false; 821 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 822 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK 823 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE)); 824 updateNotificationPulse(); 825 } else if (action.equals(Intent.ACTION_USER_STOPPED)) { 826 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 827 if (userHandle >= 0) { 828 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle, 829 REASON_USER_STOPPED, null); 830 } 831 } else if (action.equals(Intent.ACTION_USER_PRESENT)) { 832 // turn off LED when user passes through lock screen 833 mNotificationLight.turnOff(); 834 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { 835 // reload per-user settings 836 mSettingsObserver.update(null); 837 mUserProfiles.updateCache(context); 838 } else if (action.equals(Intent.ACTION_USER_ADDED)) { 839 mUserProfiles.updateCache(context); 840 } 841 } 842 }; 843 844 class SettingsObserver extends ContentObserver { 845 private final Uri NOTIFICATION_LIGHT_PULSE_URI 846 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); 847 848 SettingsObserver(Handler handler) { 849 super(handler); 850 } 851 852 void observe() { 853 ContentResolver resolver = getContext().getContentResolver(); 854 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, 855 false, this, UserHandle.USER_ALL); 856 update(null); 857 } 858 859 @Override public void onChange(boolean selfChange, Uri uri) { 860 update(uri); 861 } 862 863 public void update(Uri uri) { 864 ContentResolver resolver = getContext().getContentResolver(); 865 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) { 866 boolean pulseEnabled = Settings.System.getInt(resolver, 867 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; 868 if (mNotificationPulseEnabled != pulseEnabled) { 869 mNotificationPulseEnabled = pulseEnabled; 870 updateNotificationPulse(); 871 } 872 } 873 } 874 } 875 876 private SettingsObserver mSettingsObserver; 877 private ZenModeHelper mZenModeHelper; 878 879 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) { 880 int[] ar = r.getIntArray(resid); 881 if (ar == null) { 882 return def; 883 } 884 final int len = ar.length > maxlen ? maxlen : ar.length; 885 long[] out = new long[len]; 886 for (int i=0; i<len; i++) { 887 out[i] = ar[i]; 888 } 889 return out; 890 } 891 892 public NotificationManagerService(Context context) { 893 super(context); 894 } 895 896 @Override 897 public void onStart() { 898 mAm = ActivityManagerNative.getDefault(); 899 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); 900 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); 901 902 mHandler = new WorkerHandler(); 903 mRankingThread.start(); 904 mRankingHandler = new RankingWorkerHandler(mRankingThread.getLooper()); 905 mZenModeHelper = new ZenModeHelper(getContext(), mHandler); 906 mZenModeHelper.addCallback(new ZenModeHelper.Callback() { 907 @Override 908 public void onConfigChanged() { 909 savePolicyFile(); 910 } 911 }); 912 final File systemDir = new File(Environment.getDataDirectory(), "system"); 913 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml")); 914 mUsageStats = new NotificationUsageStats(getContext()); 915 916 importOldBlockDb(); 917 918 mListeners = new NotificationListeners(); 919 mConditionProviders = new ConditionProviders(getContext(), 920 mHandler, mUserProfiles, mZenModeHelper); 921 mStatusBar = getLocalService(StatusBarManagerInternal.class); 922 mStatusBar.setNotificationDelegate(mNotificationDelegate); 923 924 final LightsManager lights = getLocalService(LightsManager.class); 925 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS); 926 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION); 927 928 Resources resources = getContext().getResources(); 929 mDefaultNotificationColor = resources.getColor( 930 R.color.config_defaultNotificationColor); 931 mDefaultNotificationLedOn = resources.getInteger( 932 R.integer.config_defaultNotificationLedOn); 933 mDefaultNotificationLedOff = resources.getInteger( 934 R.integer.config_defaultNotificationLedOff); 935 936 mDefaultVibrationPattern = getLongArray(resources, 937 R.array.config_defaultNotificationVibePattern, 938 VIBRATE_PATTERN_MAXLEN, 939 DEFAULT_VIBRATE_PATTERN); 940 941 mFallbackVibrationPattern = getLongArray(resources, 942 R.array.config_notificationFallbackVibePattern, 943 VIBRATE_PATTERN_MAXLEN, 944 DEFAULT_VIBRATE_PATTERN); 945 946 // Don't start allowing notifications until the setup wizard has run once. 947 // After that, including subsequent boots, init with notifications turned on. 948 // This works on the first boot because the setup wizard will toggle this 949 // flag at least once and we'll go back to 0 after that. 950 if (0 == Settings.Global.getInt(getContext().getContentResolver(), 951 Settings.Global.DEVICE_PROVISIONED, 0)) { 952 mDisableNotificationAlerts = true; 953 } 954 mZenModeHelper.updateZenMode(); 955 956 mUserProfiles.updateCache(getContext()); 957 958 // register for various Intents 959 IntentFilter filter = new IntentFilter(); 960 filter.addAction(Intent.ACTION_SCREEN_ON); 961 filter.addAction(Intent.ACTION_SCREEN_OFF); 962 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 963 filter.addAction(Intent.ACTION_USER_PRESENT); 964 filter.addAction(Intent.ACTION_USER_STOPPED); 965 filter.addAction(Intent.ACTION_USER_SWITCHED); 966 filter.addAction(Intent.ACTION_USER_ADDED); 967 getContext().registerReceiver(mIntentReceiver, filter); 968 IntentFilter pkgFilter = new IntentFilter(); 969 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 970 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 971 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 972 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 973 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 974 pkgFilter.addDataScheme("package"); 975 getContext().registerReceiver(mIntentReceiver, pkgFilter); 976 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 977 getContext().registerReceiver(mIntentReceiver, sdFilter); 978 979 mSettingsObserver = new SettingsObserver(mHandler); 980 981 // spin up NotificationSignalExtractors 982 String[] extractorNames = resources.getStringArray( 983 R.array.config_notificationSignalExtractors); 984 for (String extractorName : extractorNames) { 985 try { 986 Class<?> extractorClass = getContext().getClassLoader().loadClass(extractorName); 987 NotificationSignalExtractor extractor = 988 (NotificationSignalExtractor) extractorClass.newInstance(); 989 extractor.initialize(getContext()); 990 mSignalExtractors.add(extractor); 991 } catch (ClassNotFoundException e) { 992 Slog.w(TAG, "Couldn't find extractor " + extractorName + ".", e); 993 } catch (InstantiationException e) { 994 Slog.w(TAG, "Couldn't instantiate extractor " + extractorName + ".", e); 995 } catch (IllegalAccessException e) { 996 Slog.w(TAG, "Problem accessing extractor " + extractorName + ".", e); 997 } 998 } 999 1000 publishBinderService(Context.NOTIFICATION_SERVICE, mService); 1001 publishLocalService(NotificationManagerInternal.class, mInternalService); 1002 } 1003 1004 /** 1005 * Read the old XML-based app block database and import those blockages into the AppOps system. 1006 */ 1007 private void importOldBlockDb() { 1008 loadPolicyFile(); 1009 1010 PackageManager pm = getContext().getPackageManager(); 1011 for (String pkg : mBlockedPackages) { 1012 PackageInfo info = null; 1013 try { 1014 info = pm.getPackageInfo(pkg, 0); 1015 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false); 1016 } catch (NameNotFoundException e) { 1017 // forget you 1018 } 1019 } 1020 mBlockedPackages.clear(); 1021 } 1022 1023 @Override 1024 public void onBootPhase(int phase) { 1025 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 1026 // no beeping until we're basically done booting 1027 mSystemReady = true; 1028 1029 // Grab our optional AudioService 1030 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 1031 1032 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 1033 // This observer will force an update when observe is called, causing us to 1034 // bind to listener services. 1035 mSettingsObserver.observe(); 1036 mListeners.onBootPhaseAppsCanStart(); 1037 mConditionProviders.onBootPhaseAppsCanStart(); 1038 } 1039 } 1040 1041 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) { 1042 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); 1043 1044 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, 1045 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 1046 1047 // Now, cancel any outstanding notifications that are part of a just-disabled app 1048 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { 1049 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid), 1050 REASON_PACKAGE_BANNED, null); 1051 } 1052 } 1053 1054 private final IBinder mService = new INotificationManager.Stub() { 1055 // Toasts 1056 // ============================================================================ 1057 1058 @Override 1059 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 1060 { 1061 if (DBG) { 1062 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback 1063 + " duration=" + duration); 1064 } 1065 1066 if (pkg == null || callback == null) { 1067 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 1068 return ; 1069 } 1070 1071 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); 1072 1073 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { 1074 if (!isSystemToast) { 1075 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 1076 return; 1077 } 1078 } 1079 1080 synchronized (mToastQueue) { 1081 int callingPid = Binder.getCallingPid(); 1082 long callingId = Binder.clearCallingIdentity(); 1083 try { 1084 ToastRecord record; 1085 int index = indexOfToastLocked(pkg, callback); 1086 // If it's already in the queue, we update it in place, we don't 1087 // move it to the end of the queue. 1088 if (index >= 0) { 1089 record = mToastQueue.get(index); 1090 record.update(duration); 1091 } else { 1092 // Limit the number of toasts that any given package except the android 1093 // package can enqueue. Prevents DOS attacks and deals with leaks. 1094 if (!isSystemToast) { 1095 int count = 0; 1096 final int N = mToastQueue.size(); 1097 for (int i=0; i<N; i++) { 1098 final ToastRecord r = mToastQueue.get(i); 1099 if (r.pkg.equals(pkg)) { 1100 count++; 1101 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1102 Slog.e(TAG, "Package has already posted " + count 1103 + " toasts. Not showing more. Package=" + pkg); 1104 return; 1105 } 1106 } 1107 } 1108 } 1109 1110 record = new ToastRecord(callingPid, pkg, callback, duration); 1111 mToastQueue.add(record); 1112 index = mToastQueue.size() - 1; 1113 keepProcessAliveLocked(callingPid); 1114 } 1115 // If it's at index 0, it's the current toast. It doesn't matter if it's 1116 // new or just been updated. Call back and tell it to show itself. 1117 // If the callback fails, this will remove it from the list, so don't 1118 // assume that it's valid after this. 1119 if (index == 0) { 1120 showNextToastLocked(); 1121 } 1122 } finally { 1123 Binder.restoreCallingIdentity(callingId); 1124 } 1125 } 1126 } 1127 1128 @Override 1129 public void cancelToast(String pkg, ITransientNotification callback) { 1130 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 1131 1132 if (pkg == null || callback == null) { 1133 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 1134 return ; 1135 } 1136 1137 synchronized (mToastQueue) { 1138 long callingId = Binder.clearCallingIdentity(); 1139 try { 1140 int index = indexOfToastLocked(pkg, callback); 1141 if (index >= 0) { 1142 cancelToastLocked(index); 1143 } else { 1144 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg 1145 + " callback=" + callback); 1146 } 1147 } finally { 1148 Binder.restoreCallingIdentity(callingId); 1149 } 1150 } 1151 } 1152 1153 @Override 1154 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, 1155 Notification notification, int[] idOut, int userId) throws RemoteException { 1156 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(), 1157 Binder.getCallingPid(), tag, id, notification, idOut, userId); 1158 } 1159 1160 @Override 1161 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 1162 checkCallerIsSystemOrSameApp(pkg); 1163 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1164 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 1165 // Don't allow client applications to cancel foreground service notis. 1166 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0, 1167 Binder.getCallingUid() == Process.SYSTEM_UID 1168 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL, 1169 null); 1170 } 1171 1172 @Override 1173 public void cancelAllNotifications(String pkg, int userId) { 1174 checkCallerIsSystemOrSameApp(pkg); 1175 1176 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1177 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 1178 1179 // Calling from user space, don't allow the canceling of actively 1180 // running foreground services. 1181 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(), 1182 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId, 1183 REASON_NOMAN_CANCEL_ALL, null); 1184 } 1185 1186 @Override 1187 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { 1188 checkCallerIsSystem(); 1189 1190 setNotificationsEnabledForPackageImpl(pkg, uid, enabled); 1191 } 1192 1193 /** 1194 * Use this when you just want to know if notifications are OK for this package. 1195 */ 1196 @Override 1197 public boolean areNotificationsEnabledForPackage(String pkg, int uid) { 1198 checkCallerIsSystem(); 1199 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 1200 == AppOpsManager.MODE_ALLOWED); 1201 } 1202 1203 /** 1204 * System-only API for getting a list of current (i.e. not cleared) notifications. 1205 * 1206 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1207 * @returns A list of all the notifications, in natural order. 1208 */ 1209 @Override 1210 public StatusBarNotification[] getActiveNotifications(String callingPkg) { 1211 // enforce() will ensure the calling uid has the correct permission 1212 getContext().enforceCallingOrSelfPermission( 1213 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1214 "NotificationManagerService.getActiveNotifications"); 1215 1216 StatusBarNotification[] tmp = null; 1217 int uid = Binder.getCallingUid(); 1218 1219 // noteOp will check to make sure the callingPkg matches the uid 1220 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1221 == AppOpsManager.MODE_ALLOWED) { 1222 synchronized (mNotificationList) { 1223 tmp = new StatusBarNotification[mNotificationList.size()]; 1224 final int N = mNotificationList.size(); 1225 for (int i=0; i<N; i++) { 1226 tmp[i] = mNotificationList.get(i).sbn; 1227 } 1228 } 1229 } 1230 return tmp; 1231 } 1232 1233 /** 1234 * System-only API for getting a list of recent (cleared, no longer shown) notifications. 1235 * 1236 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1237 */ 1238 @Override 1239 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { 1240 // enforce() will ensure the calling uid has the correct permission 1241 getContext().enforceCallingOrSelfPermission( 1242 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1243 "NotificationManagerService.getHistoricalNotifications"); 1244 1245 StatusBarNotification[] tmp = null; 1246 int uid = Binder.getCallingUid(); 1247 1248 // noteOp will check to make sure the callingPkg matches the uid 1249 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1250 == AppOpsManager.MODE_ALLOWED) { 1251 synchronized (mArchive) { 1252 tmp = mArchive.getArray(count); 1253 } 1254 } 1255 return tmp; 1256 } 1257 1258 /** 1259 * Register a listener binder directly with the notification manager. 1260 * 1261 * Only works with system callers. Apps should extend 1262 * {@link android.service.notification.NotificationListenerService}. 1263 */ 1264 @Override 1265 public void registerListener(final INotificationListener listener, 1266 final ComponentName component, final int userid) { 1267 checkCallerIsSystem(); 1268 mListeners.registerService(listener, component, userid); 1269 } 1270 1271 /** 1272 * Remove a listener binder directly 1273 */ 1274 @Override 1275 public void unregisterListener(INotificationListener listener, int userid) { 1276 mListeners.unregisterService(listener, userid); 1277 } 1278 1279 /** 1280 * Allow an INotificationListener to simulate a "clear all" operation. 1281 * 1282 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} 1283 * 1284 * @param token The binder for the listener, to check that the caller is allowed 1285 */ 1286 @Override 1287 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) { 1288 final int callingUid = Binder.getCallingUid(); 1289 final int callingPid = Binder.getCallingPid(); 1290 long identity = Binder.clearCallingIdentity(); 1291 try { 1292 synchronized (mNotificationList) { 1293 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1294 if (keys != null) { 1295 final int N = keys.length; 1296 for (int i = 0; i < N; i++) { 1297 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1298 final int userId = r.sbn.getUserId(); 1299 if (userId != info.userid && userId != UserHandle.USER_ALL && 1300 !mUserProfiles.isCurrentProfile(userId)) { 1301 throw new SecurityException("Disallowed call from listener: " 1302 + info.service); 1303 } 1304 if (r != null) { 1305 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1306 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(), 1307 userId); 1308 } 1309 } 1310 } else { 1311 cancelAllLocked(callingUid, callingPid, info.userid, 1312 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles()); 1313 } 1314 } 1315 } finally { 1316 Binder.restoreCallingIdentity(identity); 1317 } 1318 } 1319 1320 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info, 1321 int callingUid, int callingPid, String pkg, String tag, int id, int userId) { 1322 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 1323 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 1324 true, 1325 userId, REASON_LISTENER_CANCEL, info); 1326 } 1327 1328 /** 1329 * Allow an INotificationListener to simulate clearing (dismissing) a single notification. 1330 * 1331 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} 1332 * 1333 * @param token The binder for the listener, to check that the caller is allowed 1334 */ 1335 @Override 1336 public void cancelNotificationFromListener(INotificationListener token, String pkg, 1337 String tag, int id) { 1338 final int callingUid = Binder.getCallingUid(); 1339 final int callingPid = Binder.getCallingPid(); 1340 long identity = Binder.clearCallingIdentity(); 1341 try { 1342 synchronized (mNotificationList) { 1343 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1344 if (info.supportsProfiles()) { 1345 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) " 1346 + "from " + info.component 1347 + " use cancelNotification(key) instead."); 1348 } else { 1349 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1350 pkg, tag, id, info.userid); 1351 } 1352 } 1353 } finally { 1354 Binder.restoreCallingIdentity(identity); 1355 } 1356 } 1357 1358 /** 1359 * Allow an INotificationListener to request the list of outstanding notifications seen by 1360 * the current user. Useful when starting up, after which point the listener callbacks 1361 * should be used. 1362 * 1363 * @param token The binder for the listener, to check that the caller is allowed 1364 * @returns The return value will contain the notifications specified in keys, in that 1365 * order, or if keys is null, all the notifications, in natural order. 1366 */ 1367 @Override 1368 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener( 1369 INotificationListener token) { 1370 synchronized (mNotificationList) { 1371 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1372 final ArrayList<StatusBarNotification> list 1373 = new ArrayList<StatusBarNotification>(); 1374 final int N = mNotificationList.size(); 1375 for (int i=0; i<N; i++) { 1376 StatusBarNotification sbn = mNotificationList.get(i).sbn; 1377 if (info.enabledAndUserMatches(sbn.getUserId())) { 1378 list.add(sbn); 1379 } 1380 } 1381 return new ParceledListSlice<StatusBarNotification>(list); 1382 } 1383 } 1384 1385 @Override 1386 public ZenModeConfig getZenModeConfig() { 1387 checkCallerIsSystem(); 1388 return mZenModeHelper.getConfig(); 1389 } 1390 1391 @Override 1392 public boolean setZenModeConfig(ZenModeConfig config) { 1393 checkCallerIsSystem(); 1394 return mZenModeHelper.setConfig(config); 1395 } 1396 1397 @Override 1398 public void notifyConditions(String pkg, IConditionProvider provider, 1399 Condition[] conditions) { 1400 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider); 1401 checkCallerIsSystemOrSameApp(pkg); 1402 final long identity = Binder.clearCallingIdentity(); 1403 try { 1404 mConditionProviders.notifyConditions(pkg, info, conditions); 1405 } finally { 1406 Binder.restoreCallingIdentity(identity); 1407 } 1408 } 1409 1410 @Override 1411 public void requestZenModeConditions(IConditionListener callback, int relevance) { 1412 enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions"); 1413 mConditionProviders.requestZenModeConditions(callback, relevance); 1414 } 1415 1416 @Override 1417 public void setZenModeCondition(Uri conditionId) { 1418 enforceSystemOrSystemUI("INotificationManager.setZenModeCondition"); 1419 final long identity = Binder.clearCallingIdentity(); 1420 try { 1421 mConditionProviders.setZenModeCondition(conditionId); 1422 } finally { 1423 Binder.restoreCallingIdentity(identity); 1424 } 1425 } 1426 1427 @Override 1428 public void setAutomaticZenModeConditions(Uri[] conditionIds) { 1429 enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions"); 1430 mConditionProviders.setAutomaticZenModeConditions(conditionIds); 1431 } 1432 1433 @Override 1434 public Condition[] getAutomaticZenModeConditions() { 1435 enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions"); 1436 return mConditionProviders.getAutomaticZenModeConditions(); 1437 } 1438 1439 private void enforceSystemOrSystemUI(String message) { 1440 if (isCallerSystem()) return; 1441 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 1442 message); 1443 } 1444 1445 @Override 1446 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1447 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1448 != PackageManager.PERMISSION_GRANTED) { 1449 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1450 + Binder.getCallingPid() 1451 + ", uid=" + Binder.getCallingUid()); 1452 return; 1453 } 1454 1455 dumpImpl(pw); 1456 } 1457 }; 1458 1459 private String[] getActiveNotificationKeys(INotificationListener token) { 1460 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1461 final ArrayList<String> keys = new ArrayList<String>(); 1462 if (info.isEnabledForCurrentProfiles()) { 1463 synchronized (mNotificationList) { 1464 final int N = mNotificationList.size(); 1465 for (int i = 0; i < N; i++) { 1466 final StatusBarNotification sbn = mNotificationList.get(i).sbn; 1467 if (info.enabledAndUserMatches(sbn.getUserId())) { 1468 keys.add(sbn.getKey()); 1469 } 1470 } 1471 } 1472 } 1473 return keys.toArray(new String[keys.size()]); 1474 } 1475 1476 void dumpImpl(PrintWriter pw) { 1477 pw.println("Current Notification Manager state:"); 1478 1479 int N; 1480 1481 synchronized (mToastQueue) { 1482 N = mToastQueue.size(); 1483 if (N > 0) { 1484 pw.println(" Toast Queue:"); 1485 for (int i=0; i<N; i++) { 1486 mToastQueue.get(i).dump(pw, " "); 1487 } 1488 pw.println(" "); 1489 } 1490 1491 } 1492 1493 synchronized (mNotificationList) { 1494 N = mNotificationList.size(); 1495 if (N > 0) { 1496 pw.println(" Notification List:"); 1497 for (int i=0; i<N; i++) { 1498 mNotificationList.get(i).dump(pw, " ", getContext()); 1499 } 1500 pw.println(" "); 1501 } 1502 1503 N = mLights.size(); 1504 if (N > 0) { 1505 pw.println(" Lights List:"); 1506 for (int i=0; i<N; i++) { 1507 pw.println(" " + mLights.get(i)); 1508 } 1509 pw.println(" "); 1510 } 1511 1512 pw.println(" mSoundNotification=" + mSoundNotification); 1513 pw.println(" mVibrateNotification=" + mVibrateNotification); 1514 pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts); 1515 pw.println(" mSystemReady=" + mSystemReady); 1516 pw.println(" mArchive=" + mArchive.toString()); 1517 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1518 int i=0; 1519 while (iter.hasNext()) { 1520 pw.println(" " + iter.next()); 1521 if (++i >= 5) { 1522 if (iter.hasNext()) pw.println(" ..."); 1523 break; 1524 } 1525 } 1526 1527 pw.println("\n Usage Stats:"); 1528 mUsageStats.dump(pw, " "); 1529 1530 pw.println("\n Zen Mode:"); 1531 mZenModeHelper.dump(pw, " "); 1532 1533 pw.println("\n Notification listeners:"); 1534 mListeners.dump(pw); 1535 1536 pw.println("\n Condition providers:"); 1537 mConditionProviders.dump(pw); 1538 } 1539 } 1540 1541 /** 1542 * The private API only accessible to the system process. 1543 */ 1544 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1545 @Override 1546 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, 1547 String tag, int id, Notification notification, int[] idReceived, int userId) { 1548 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 1549 idReceived, userId); 1550 } 1551 }; 1552 1553 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, 1554 final int callingPid, final String tag, final int id, final Notification notification, 1555 int[] idOut, int incomingUserId) { 1556 if (DBG) { 1557 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 1558 + " notification=" + notification); 1559 } 1560 checkCallerIsSystemOrSameApp(pkg); 1561 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 1562 1563 final int userId = ActivityManager.handleIncomingUser(callingPid, 1564 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1565 final UserHandle user = new UserHandle(userId); 1566 1567 // Limit the number of notifications that any given package except the android 1568 // package can enqueue. Prevents DOS attacks and deals with leaks. 1569 if (!isSystemNotification) { 1570 synchronized (mNotificationList) { 1571 int count = 0; 1572 final int N = mNotificationList.size(); 1573 for (int i=0; i<N; i++) { 1574 final NotificationRecord r = mNotificationList.get(i); 1575 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1576 count++; 1577 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1578 Slog.e(TAG, "Package has already posted " + count 1579 + " notifications. Not showing more. package=" + pkg); 1580 return; 1581 } 1582 } 1583 } 1584 } 1585 } 1586 1587 // This conditional is a dirty hack to limit the logging done on 1588 // behalf of the download manager without affecting other apps. 1589 if (!pkg.equals("com.android.providers.downloads") 1590 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1591 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 1592 pkg, id, tag, userId, notification.toString()); 1593 } 1594 1595 if (pkg == null || notification == null) { 1596 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1597 + " id=" + id + " notification=" + notification); 1598 } 1599 if (notification.icon != 0) { 1600 if (notification.contentView == null) { 1601 throw new IllegalArgumentException("contentView required: pkg=" + pkg 1602 + " id=" + id + " notification=" + notification); 1603 } 1604 } 1605 1606 mHandler.post(new Runnable() { 1607 @Override 1608 public void run() { 1609 1610 // === Scoring === 1611 1612 // 0. Sanitize inputs 1613 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1614 Notification.PRIORITY_MAX); 1615 // Migrate notification flags to scores 1616 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1617 if (notification.priority < Notification.PRIORITY_MAX) { 1618 notification.priority = Notification.PRIORITY_MAX; 1619 } 1620 } else if (SCORE_ONGOING_HIGHER && 1621 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1622 if (notification.priority < Notification.PRIORITY_HIGH) { 1623 notification.priority = Notification.PRIORITY_HIGH; 1624 } 1625 } 1626 1627 // 1. initial score: buckets of 10, around the app 1628 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] 1629 1630 // 2. extract ranking signals from the notification data 1631 final StatusBarNotification n = new StatusBarNotification( 1632 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 1633 user); 1634 NotificationRecord r = new NotificationRecord(n); 1635 if (!mSignalExtractors.isEmpty()) { 1636 for (NotificationSignalExtractor extractor : mSignalExtractors) { 1637 try { 1638 RankingFuture future = extractor.process(r); 1639 scheduleRankingReconsideration(future); 1640 } catch (Throwable t) { 1641 Slog.w(TAG, "NotificationSignalExtractor failed.", t); 1642 } 1643 } 1644 } 1645 1646 // 3. Apply local rules 1647 1648 // blocked apps 1649 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1650 if (!isSystemNotification) { 1651 score = JUNK_SCORE; 1652 Slog.e(TAG, "Suppressing notification from package " + pkg 1653 + " by user request."); 1654 } 1655 } 1656 1657 if (score < SCORE_DISPLAY_THRESHOLD) { 1658 // Notification will be blocked because the score is too low. 1659 return; 1660 } 1661 1662 // Is this notification intercepted by zen mode? 1663 final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification); 1664 notification.extras.putBoolean(EXTRA_INTERCEPT, intercept); 1665 1666 // Should this notification make noise, vibe, or use the LED? 1667 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept; 1668 if (DBG || intercept) Slog.v(TAG, 1669 "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept); 1670 synchronized (mNotificationList) { 1671 NotificationRecord old = null; 1672 int index = indexOfNotificationLocked(n.getKey()); 1673 if (index < 0) { 1674 mNotificationList.add(r); 1675 mUsageStats.registerPostedByApp(r); 1676 } else { 1677 old = mNotificationList.get(index); 1678 mNotificationList.set(index, r); 1679 mUsageStats.registerUpdatedByApp(r, old); 1680 // Make sure we don't lose the foreground service state. 1681 notification.flags |= 1682 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1683 mNotificationsByKey.remove(old.sbn.getKey()); 1684 } 1685 mNotificationsByKey.put(n.getKey(), r); 1686 1687 Collections.sort(mNotificationList, mRankingComparator); 1688 1689 // Ensure if this is a foreground service that the proper additional 1690 // flags are set. 1691 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1692 notification.flags |= Notification.FLAG_ONGOING_EVENT 1693 | Notification.FLAG_NO_CLEAR; 1694 } 1695 1696 final int currentUser; 1697 final long token = Binder.clearCallingIdentity(); 1698 try { 1699 currentUser = ActivityManager.getCurrentUser(); 1700 } finally { 1701 Binder.restoreCallingIdentity(token); 1702 } 1703 1704 if (notification.icon != 0) { 1705 if (old != null && !old.isCanceled) { 1706 final long identity = Binder.clearCallingIdentity(); 1707 try { 1708 mStatusBar.updateNotification(n); 1709 } finally { 1710 Binder.restoreCallingIdentity(identity); 1711 } 1712 } else { 1713 final long identity = Binder.clearCallingIdentity(); 1714 try { 1715 mStatusBar.addNotification(n); 1716 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0 1717 && canInterrupt) { 1718 mAttentionLight.pulse(); 1719 } 1720 } finally { 1721 Binder.restoreCallingIdentity(identity); 1722 } 1723 } 1724 // Send accessibility events only for the current user. 1725 if (currentUser == userId) { 1726 sendAccessibilityEvent(notification, pkg); 1727 } 1728 1729 mListeners.notifyPostedLocked(r.sbn, cloneNotificationListLocked()); 1730 } else { 1731 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1732 if (old != null && !old.isCanceled) { 1733 final long identity = Binder.clearCallingIdentity(); 1734 try { 1735 mStatusBar.removeNotification(r.getKey()); 1736 } finally { 1737 Binder.restoreCallingIdentity(identity); 1738 } 1739 1740 mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked()); 1741 } 1742 // ATTENTION: in a future release we will bail out here 1743 // so that we do not play sounds, show lights, etc. for invalid 1744 // notifications 1745 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1746 + n.getPackageName()); 1747 } 1748 1749 // If we're not supposed to beep, vibrate, etc. then don't. 1750 if (!mDisableNotificationAlerts 1751 && (!(old != null 1752 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1753 && (r.getUserId() == UserHandle.USER_ALL || 1754 (r.getUserId() == userId && r.getUserId() == currentUser) || 1755 mUserProfiles.isCurrentProfile(r.getUserId())) 1756 && canInterrupt 1757 && mSystemReady 1758 && mAudioManager != null) { 1759 if (DBG) Slog.v(TAG, "Interrupting!"); 1760 // sound 1761 1762 // should we use the default notification sound? (indicated either by 1763 // DEFAULT_SOUND or because notification.sound is pointing at 1764 // Settings.System.NOTIFICATION_SOUND) 1765 final boolean useDefaultSound = 1766 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1767 Settings.System.DEFAULT_NOTIFICATION_URI 1768 .equals(notification.sound); 1769 1770 Uri soundUri = null; 1771 boolean hasValidSound = false; 1772 1773 if (useDefaultSound) { 1774 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1775 1776 // check to see if the default notification sound is silent 1777 ContentResolver resolver = getContext().getContentResolver(); 1778 hasValidSound = Settings.System.getString(resolver, 1779 Settings.System.NOTIFICATION_SOUND) != null; 1780 } else if (notification.sound != null) { 1781 soundUri = notification.sound; 1782 hasValidSound = (soundUri != null); 1783 } 1784 1785 if (hasValidSound) { 1786 boolean looping = 1787 (notification.flags & Notification.FLAG_INSISTENT) != 0; 1788 int audioStreamType; 1789 if (notification.audioStreamType >= 0) { 1790 audioStreamType = notification.audioStreamType; 1791 } else { 1792 audioStreamType = DEFAULT_STREAM_TYPE; 1793 } 1794 mSoundNotification = r; 1795 // do not play notifications if stream volume is 0 (typically because 1796 // ringer mode is silent) or if there is a user of exclusive audio focus 1797 if ((mAudioManager.getStreamVolume(audioStreamType) != 0) 1798 && !mAudioManager.isAudioFocusExclusive()) { 1799 final long identity = Binder.clearCallingIdentity(); 1800 try { 1801 final IRingtonePlayer player = 1802 mAudioManager.getRingtonePlayer(); 1803 if (player != null) { 1804 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 1805 + " on stream " + audioStreamType); 1806 player.playAsync(soundUri, user, looping, audioStreamType); 1807 } 1808 } catch (RemoteException e) { 1809 } finally { 1810 Binder.restoreCallingIdentity(identity); 1811 } 1812 } 1813 } 1814 1815 // vibrate 1816 // Does the notification want to specify its own vibration? 1817 final boolean hasCustomVibrate = notification.vibrate != null; 1818 1819 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1820 // mode, and no other vibration is specified, we fall back to vibration 1821 final boolean convertSoundToVibration = 1822 !hasCustomVibrate 1823 && hasValidSound 1824 && (mAudioManager.getRingerMode() 1825 == AudioManager.RINGER_MODE_VIBRATE); 1826 1827 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1828 final boolean useDefaultVibrate = 1829 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1830 1831 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1832 && !(mAudioManager.getRingerMode() 1833 == AudioManager.RINGER_MODE_SILENT)) { 1834 mVibrateNotification = r; 1835 1836 if (useDefaultVibrate || convertSoundToVibration) { 1837 // Escalate privileges so we can use the vibrator even if the 1838 // notifying app does not have the VIBRATE permission. 1839 long identity = Binder.clearCallingIdentity(); 1840 try { 1841 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1842 useDefaultVibrate ? mDefaultVibrationPattern 1843 : mFallbackVibrationPattern, 1844 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1845 ? 0: -1, notification.audioStreamType); 1846 } finally { 1847 Binder.restoreCallingIdentity(identity); 1848 } 1849 } else if (notification.vibrate.length > 1) { 1850 // If you want your own vibration pattern, you need the VIBRATE 1851 // permission 1852 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1853 notification.vibrate, 1854 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1855 ? 0: -1, notification.audioStreamType); 1856 } 1857 } 1858 } 1859 1860 // light 1861 // the most recent thing gets the light 1862 mLights.remove(old); 1863 if (mLedNotification == old) { 1864 mLedNotification = null; 1865 } 1866 //Slog.i(TAG, "notification.lights=" 1867 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) 1868 // != 0)); 1869 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 1870 && canInterrupt) { 1871 mLights.add(r); 1872 updateLightsLocked(); 1873 } else { 1874 if (old != null 1875 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) { 1876 updateLightsLocked(); 1877 } 1878 } 1879 } 1880 } 1881 }); 1882 1883 idOut[0] = id; 1884 } 1885 1886 void showNextToastLocked() { 1887 ToastRecord record = mToastQueue.get(0); 1888 while (record != null) { 1889 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1890 try { 1891 record.callback.show(); 1892 scheduleTimeoutLocked(record); 1893 return; 1894 } catch (RemoteException e) { 1895 Slog.w(TAG, "Object died trying to show notification " + record.callback 1896 + " in package " + record.pkg); 1897 // remove it from the list and let the process die 1898 int index = mToastQueue.indexOf(record); 1899 if (index >= 0) { 1900 mToastQueue.remove(index); 1901 } 1902 keepProcessAliveLocked(record.pid); 1903 if (mToastQueue.size() > 0) { 1904 record = mToastQueue.get(0); 1905 } else { 1906 record = null; 1907 } 1908 } 1909 } 1910 } 1911 1912 void cancelToastLocked(int index) { 1913 ToastRecord record = mToastQueue.get(index); 1914 try { 1915 record.callback.hide(); 1916 } catch (RemoteException e) { 1917 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1918 + " in package " + record.pkg); 1919 // don't worry about this, we're about to remove it from 1920 // the list anyway 1921 } 1922 mToastQueue.remove(index); 1923 keepProcessAliveLocked(record.pid); 1924 if (mToastQueue.size() > 0) { 1925 // Show the next one. If the callback fails, this will remove 1926 // it from the list, so don't assume that the list hasn't changed 1927 // after this point. 1928 showNextToastLocked(); 1929 } 1930 } 1931 1932 private void scheduleTimeoutLocked(ToastRecord r) 1933 { 1934 mHandler.removeCallbacksAndMessages(r); 1935 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1936 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 1937 mHandler.sendMessageDelayed(m, delay); 1938 } 1939 1940 private void handleTimeout(ToastRecord record) 1941 { 1942 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1943 synchronized (mToastQueue) { 1944 int index = indexOfToastLocked(record.pkg, record.callback); 1945 if (index >= 0) { 1946 cancelToastLocked(index); 1947 } 1948 } 1949 } 1950 1951 // lock on mToastQueue 1952 int indexOfToastLocked(String pkg, ITransientNotification callback) 1953 { 1954 IBinder cbak = callback.asBinder(); 1955 ArrayList<ToastRecord> list = mToastQueue; 1956 int len = list.size(); 1957 for (int i=0; i<len; i++) { 1958 ToastRecord r = list.get(i); 1959 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1960 return i; 1961 } 1962 } 1963 return -1; 1964 } 1965 1966 // lock on mToastQueue 1967 void keepProcessAliveLocked(int pid) 1968 { 1969 int toastCount = 0; // toasts from this pid 1970 ArrayList<ToastRecord> list = mToastQueue; 1971 int N = list.size(); 1972 for (int i=0; i<N; i++) { 1973 ToastRecord r = list.get(i); 1974 if (r.pid == pid) { 1975 toastCount++; 1976 } 1977 } 1978 try { 1979 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 1980 } catch (RemoteException e) { 1981 // Shouldn't happen. 1982 } 1983 } 1984 1985 private void scheduleRankingReconsideration(RankingFuture future) { 1986 if (future != null) { 1987 Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, future); 1988 long delay = future.getDelay(TimeUnit.MILLISECONDS); 1989 mRankingHandler.sendMessageDelayed(m, delay); 1990 } 1991 } 1992 1993 private void handleRankingReconsideration(Message message) { 1994 if (!(message.obj instanceof RankingFuture)) return; 1995 1996 RankingFuture future = (RankingFuture) message.obj; 1997 future.run(); 1998 try { 1999 NotificationRecord record = future.get(); 2000 synchronized (mNotificationList) { 2001 int before = mNotificationList.indexOf(record); 2002 if (before != -1) { 2003 Collections.sort(mNotificationList, mRankingComparator); 2004 int after = mNotificationList.indexOf(record); 2005 2006 if (before != after) { 2007 scheduleSendRankingUpdate(); 2008 } 2009 } 2010 } 2011 } catch (InterruptedException e) { 2012 // we're running the future explicitly, so this should never happen 2013 } catch (ExecutionException e) { 2014 // we're running the future explicitly, so this should never happen 2015 } 2016 } 2017 2018 private void scheduleSendRankingUpdate() { 2019 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 2020 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 2021 mHandler.sendMessage(m); 2022 } 2023 2024 private void handleSendRankingUpdate() { 2025 synchronized (mNotificationList) { 2026 mListeners.notifyRankingUpdateLocked(cloneNotificationListLocked()); 2027 } 2028 } 2029 2030 private ArrayList<StatusBarNotification> cloneNotificationListLocked() { 2031 final int N = mNotificationList.size(); 2032 ArrayList<StatusBarNotification> sbns = new ArrayList<StatusBarNotification>(N); 2033 for (int i = 0; i < N; i++) { 2034 sbns.add(mNotificationList.get(i).sbn); 2035 } 2036 return sbns; 2037 } 2038 2039 private final class WorkerHandler extends Handler 2040 { 2041 @Override 2042 public void handleMessage(Message msg) 2043 { 2044 switch (msg.what) 2045 { 2046 case MESSAGE_TIMEOUT: 2047 handleTimeout((ToastRecord)msg.obj); 2048 break; 2049 case MESSAGE_SAVE_POLICY_FILE: 2050 handleSavePolicyFile(); 2051 break; 2052 case MESSAGE_SEND_RANKING_UPDATE: 2053 handleSendRankingUpdate(); 2054 break; 2055 } 2056 } 2057 2058 } 2059 2060 private final class RankingWorkerHandler extends Handler 2061 { 2062 public RankingWorkerHandler(Looper looper) { 2063 super(looper); 2064 } 2065 2066 @Override 2067 public void handleMessage(Message msg) { 2068 switch (msg.what) { 2069 case MESSAGE_RECONSIDER_RANKING: 2070 handleRankingReconsideration(msg); 2071 break; 2072 } 2073 } 2074 } 2075 2076 // Notifications 2077 // ============================================================================ 2078 static int clamp(int x, int low, int high) { 2079 return (x < low) ? low : ((x > high) ? high : x); 2080 } 2081 2082 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 2083 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 2084 if (!manager.isEnabled()) { 2085 return; 2086 } 2087 2088 AccessibilityEvent event = 2089 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 2090 event.setPackageName(packageName); 2091 event.setClassName(Notification.class.getName()); 2092 event.setParcelableData(notification); 2093 CharSequence tickerText = notification.tickerText; 2094 if (!TextUtils.isEmpty(tickerText)) { 2095 event.getText().add(tickerText); 2096 } 2097 2098 manager.sendAccessibilityEvent(event); 2099 } 2100 2101 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 2102 // tell the app 2103 if (sendDelete) { 2104 if (r.getNotification().deleteIntent != null) { 2105 try { 2106 r.getNotification().deleteIntent.send(); 2107 } catch (PendingIntent.CanceledException ex) { 2108 // do nothing - there's no relevant way to recover, and 2109 // no reason to let this propagate 2110 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 2111 } 2112 } 2113 } 2114 2115 // status bar 2116 if (r.getNotification().icon != 0) { 2117 final long identity = Binder.clearCallingIdentity(); 2118 try { 2119 mStatusBar.removeNotification(r.getKey()); 2120 } finally { 2121 Binder.restoreCallingIdentity(identity); 2122 } 2123 r.isCanceled = true; 2124 mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked()); 2125 } 2126 2127 // sound 2128 if (mSoundNotification == r) { 2129 mSoundNotification = null; 2130 final long identity = Binder.clearCallingIdentity(); 2131 try { 2132 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2133 if (player != null) { 2134 player.stopAsync(); 2135 } 2136 } catch (RemoteException e) { 2137 } finally { 2138 Binder.restoreCallingIdentity(identity); 2139 } 2140 } 2141 2142 // vibrate 2143 if (mVibrateNotification == r) { 2144 mVibrateNotification = null; 2145 long identity = Binder.clearCallingIdentity(); 2146 try { 2147 mVibrator.cancel(); 2148 } 2149 finally { 2150 Binder.restoreCallingIdentity(identity); 2151 } 2152 } 2153 2154 // light 2155 mLights.remove(r); 2156 if (mLedNotification == r) { 2157 mLedNotification = null; 2158 } 2159 2160 // Record usage stats 2161 switch (reason) { 2162 case REASON_DELEGATE_CANCEL: 2163 case REASON_DELEGATE_CANCEL_ALL: 2164 case REASON_LISTENER_CANCEL: 2165 case REASON_LISTENER_CANCEL_ALL: 2166 mUsageStats.registerDismissedByUser(r); 2167 break; 2168 case REASON_NOMAN_CANCEL: 2169 case REASON_NOMAN_CANCEL_ALL: 2170 mUsageStats.registerRemovedByApp(r); 2171 break; 2172 case REASON_DELEGATE_CLICK: 2173 mUsageStats.registerCancelDueToClick(r); 2174 break; 2175 default: 2176 mUsageStats.registerCancelUnknown(r); 2177 break; 2178 } 2179 2180 // Save it for users of getHistoricalNotifications() 2181 mArchive.record(r.sbn); 2182 } 2183 2184 /** 2185 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2186 * and none of the {@code mustNotHaveFlags}. 2187 */ 2188 void cancelNotification(final int callingUid, final int callingPid, 2189 final String pkg, final String tag, final int id, 2190 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2191 final int userId, final int reason, final ManagedServiceInfo listener) { 2192 // In enqueueNotificationInternal notifications are added by scheduling the 2193 // work on the worker handler. Hence, we also schedule the cancel on this 2194 // handler to avoid a scenario where an add notification call followed by a 2195 // remove notification call ends up in not removing the notification. 2196 mHandler.post(new Runnable() { 2197 @Override 2198 public void run() { 2199 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2200 mustHaveFlags, mustNotHaveFlags, reason, 2201 listener == null ? null : listener.component.toShortString()); 2202 2203 synchronized (mNotificationList) { 2204 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2205 if (index >= 0) { 2206 NotificationRecord r = mNotificationList.get(index); 2207 2208 // Ideally we'd do this in the caller of this method. However, that would 2209 // require the caller to also find the notification. 2210 if (reason == REASON_DELEGATE_CLICK) { 2211 mUsageStats.registerClickedByUser(r); 2212 } 2213 2214 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2215 return; 2216 } 2217 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2218 return; 2219 } 2220 2221 mNotificationList.remove(index); 2222 mNotificationsByKey.remove(r.sbn.getKey()); 2223 2224 cancelNotificationLocked(r, sendDelete, reason); 2225 updateLightsLocked(); 2226 } 2227 } 2228 } 2229 }); 2230 } 2231 2232 /** 2233 * Determine whether the userId applies to the notification in question, either because 2234 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2235 */ 2236 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2237 return 2238 // looking for USER_ALL notifications? match everything 2239 userId == UserHandle.USER_ALL 2240 // a notification sent to USER_ALL matches any query 2241 || r.getUserId() == UserHandle.USER_ALL 2242 // an exact user match 2243 || r.getUserId() == userId; 2244 } 2245 2246 /** 2247 * Determine whether the userId applies to the notification in question, either because 2248 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2249 * because it matches one of the users profiles. 2250 */ 2251 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2252 return notificationMatchesUserId(r, userId) 2253 || mUserProfiles.isCurrentProfile(r.getUserId()); 2254 } 2255 2256 /** 2257 * Cancels all notifications from a given package that have all of the 2258 * {@code mustHaveFlags}. 2259 */ 2260 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2261 int mustNotHaveFlags, boolean doit, int userId, int reason, 2262 ManagedServiceInfo listener) { 2263 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2264 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2265 listener == null ? null : listener.component.toShortString()); 2266 2267 synchronized (mNotificationList) { 2268 final int N = mNotificationList.size(); 2269 boolean canceledSomething = false; 2270 for (int i = N-1; i >= 0; --i) { 2271 NotificationRecord r = mNotificationList.get(i); 2272 if (!notificationMatchesUserId(r, userId)) { 2273 continue; 2274 } 2275 // Don't remove notifications to all, if there's no package name specified 2276 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2277 continue; 2278 } 2279 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2280 continue; 2281 } 2282 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2283 continue; 2284 } 2285 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2286 continue; 2287 } 2288 canceledSomething = true; 2289 if (!doit) { 2290 return true; 2291 } 2292 mNotificationList.remove(i); 2293 mNotificationsByKey.remove(r.sbn.getKey()); 2294 cancelNotificationLocked(r, false, reason); 2295 } 2296 if (canceledSomething) { 2297 updateLightsLocked(); 2298 } 2299 return canceledSomething; 2300 } 2301 } 2302 2303 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2304 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2305 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2306 null, userId, 0, 0, reason, 2307 listener == null ? null : listener.component.toShortString()); 2308 2309 final int N = mNotificationList.size(); 2310 for (int i=N-1; i>=0; i--) { 2311 NotificationRecord r = mNotificationList.get(i); 2312 if (includeCurrentProfiles) { 2313 if (!notificationMatchesCurrentProfiles(r, userId)) { 2314 continue; 2315 } 2316 } else { 2317 if (!notificationMatchesUserId(r, userId)) { 2318 continue; 2319 } 2320 } 2321 2322 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2323 | Notification.FLAG_NO_CLEAR)) == 0) { 2324 mNotificationList.remove(i); 2325 mNotificationsByKey.remove(r.sbn.getKey()); 2326 cancelNotificationLocked(r, true, reason); 2327 } 2328 } 2329 updateLightsLocked(); 2330 } 2331 2332 // lock on mNotificationList 2333 void updateLightsLocked() 2334 { 2335 // handle notification lights 2336 if (mLedNotification == null) { 2337 // get next notification, if any 2338 int n = mLights.size(); 2339 if (n > 0) { 2340 mLedNotification = mLights.get(n-1); 2341 } 2342 } 2343 2344 // Don't flash while we are in a call or screen is on 2345 if (mLedNotification == null || mInCall || mScreenOn) { 2346 mNotificationLight.turnOff(); 2347 } else { 2348 final Notification ledno = mLedNotification.sbn.getNotification(); 2349 int ledARGB = ledno.ledARGB; 2350 int ledOnMS = ledno.ledOnMS; 2351 int ledOffMS = ledno.ledOffMS; 2352 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2353 ledARGB = mDefaultNotificationColor; 2354 ledOnMS = mDefaultNotificationLedOn; 2355 ledOffMS = mDefaultNotificationLedOff; 2356 } 2357 if (mNotificationPulseEnabled) { 2358 // pulse repeatedly 2359 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2360 ledOnMS, ledOffMS); 2361 } 2362 } 2363 } 2364 2365 // lock on mNotificationList 2366 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2367 { 2368 ArrayList<NotificationRecord> list = mNotificationList; 2369 final int len = list.size(); 2370 for (int i=0; i<len; i++) { 2371 NotificationRecord r = list.get(i); 2372 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2373 continue; 2374 } 2375 if (tag == null) { 2376 if (r.sbn.getTag() != null) { 2377 continue; 2378 } 2379 } else { 2380 if (!tag.equals(r.sbn.getTag())) { 2381 continue; 2382 } 2383 } 2384 if (r.sbn.getPackageName().equals(pkg)) { 2385 return i; 2386 } 2387 } 2388 return -1; 2389 } 2390 2391 // lock on mNotificationList 2392 int indexOfNotificationLocked(String key) { 2393 NotificationRecord r = mNotificationsByKey.get(key); 2394 if (r == null) { 2395 return -1; 2396 } 2397 int index = Collections.binarySearch(mNotificationList, r, mRankingComparator); 2398 // Guarantee to return -1 when not found. 2399 return (index >= 0) ? index : -1; 2400 } 2401 2402 2403 private void updateNotificationPulse() { 2404 synchronized (mNotificationList) { 2405 updateLightsLocked(); 2406 } 2407 } 2408 2409 private static boolean isUidSystem(int uid) { 2410 final int appid = UserHandle.getAppId(uid); 2411 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2412 } 2413 2414 private static boolean isCallerSystem() { 2415 return isUidSystem(Binder.getCallingUid()); 2416 } 2417 2418 private static void checkCallerIsSystem() { 2419 if (isCallerSystem()) { 2420 return; 2421 } 2422 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2423 } 2424 2425 private static void checkCallerIsSystemOrSameApp(String pkg) { 2426 if (isCallerSystem()) { 2427 return; 2428 } 2429 final int uid = Binder.getCallingUid(); 2430 try { 2431 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2432 pkg, 0, UserHandle.getCallingUserId()); 2433 if (!UserHandle.isSameApp(ai.uid, uid)) { 2434 throw new SecurityException("Calling uid " + uid + " gave package" 2435 + pkg + " which is owned by uid " + ai.uid); 2436 } 2437 } catch (RemoteException re) { 2438 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2439 } 2440 } 2441 2442 /** 2443 * Generates a NotificationRankingUpdate from 'sbns', considering only 2444 * notifications visible to the given listener. 2445 */ 2446 private static NotificationRankingUpdate makeRankingUpdateForListener(ManagedServiceInfo info, 2447 ArrayList<StatusBarNotification> sbns) { 2448 int speedBumpIndex = -1; 2449 ArrayList<String> keys = new ArrayList<String>(sbns.size()); 2450 ArrayList<String> dndKeys = new ArrayList<String>(sbns.size()); 2451 for (StatusBarNotification sbn: sbns) { 2452 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2453 continue; 2454 } 2455 keys.add(sbn.getKey()); 2456 if (sbn.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) { 2457 dndKeys.add(sbn.getKey()); 2458 } 2459 if (speedBumpIndex == -1 && 2460 sbn.getNotification().priority == Notification.PRIORITY_MIN) { 2461 speedBumpIndex = keys.size() - 1; 2462 } 2463 } 2464 String[] keysAr = keys.toArray(new String[keys.size()]); 2465 String[] dndKeysAr = dndKeys.toArray(new String[dndKeys.size()]); 2466 return new NotificationRankingUpdate(keysAr, dndKeysAr, speedBumpIndex); 2467 } 2468 2469 public class NotificationListeners extends ManagedServices { 2470 2471 public NotificationListeners() { 2472 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2473 } 2474 2475 @Override 2476 protected Config getConfig() { 2477 Config c = new Config(); 2478 c.caption = "notification listener"; 2479 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2480 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2481 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2482 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2483 c.clientLabel = R.string.notification_listener_binding_label; 2484 return c; 2485 } 2486 2487 @Override 2488 protected IInterface asInterface(IBinder binder) { 2489 return INotificationListener.Stub.asInterface(binder); 2490 } 2491 2492 @Override 2493 public void onServiceAdded(ManagedServiceInfo info) { 2494 final INotificationListener listener = (INotificationListener) info.service; 2495 final ArrayList<StatusBarNotification> sbns; 2496 synchronized (mNotificationList) { 2497 sbns = cloneNotificationListLocked(); 2498 } 2499 try { 2500 listener.onListenerConnected(makeRankingUpdateForListener(info, sbns)); 2501 } catch (RemoteException e) { 2502 // we tried 2503 } 2504 } 2505 2506 /** 2507 * asynchronously notify all listeners about a new notification 2508 */ 2509 public void notifyPostedLocked(StatusBarNotification sbn, 2510 final ArrayList<StatusBarNotification> sbns) { 2511 // make a copy in case changes are made to the underlying Notification object 2512 final StatusBarNotification sbnClone = sbn.clone(); 2513 for (final ManagedServiceInfo info : mServices) { 2514 if (!info.isEnabledForCurrentProfiles()) { 2515 continue; 2516 } 2517 final NotificationRankingUpdate update = makeRankingUpdateForListener(info, sbns); 2518 if (update.getOrderedKeys().length == 0) { 2519 continue; 2520 } 2521 mHandler.post(new Runnable() { 2522 @Override 2523 public void run() { 2524 notifyPostedIfUserMatch(info, sbnClone, update); 2525 } 2526 }); 2527 } 2528 } 2529 2530 /** 2531 * asynchronously notify all listeners about a removed notification 2532 */ 2533 public void notifyRemovedLocked(StatusBarNotification sbn, 2534 final ArrayList<StatusBarNotification> sbns) { 2535 // make a copy in case changes are made to the underlying Notification object 2536 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 2537 // notification 2538 final StatusBarNotification sbnLight = sbn.cloneLight(); 2539 for (final ManagedServiceInfo info : mServices) { 2540 if (!info.isEnabledForCurrentProfiles()) { 2541 continue; 2542 } 2543 mHandler.post(new Runnable() { 2544 @Override 2545 public void run() { 2546 notifyRemovedIfUserMatch(info, sbnLight, 2547 makeRankingUpdateForListener(info, sbns)); 2548 } 2549 }); 2550 } 2551 } 2552 2553 /** 2554 * asynchronously notify all listeners about a reordering of notifications 2555 * @param sbns an array of {@link StatusBarNotification}s to consider. This code 2556 * must not rely on mutable members of these objects, such as the 2557 * {@link Notification}. 2558 */ 2559 public void notifyRankingUpdateLocked(final ArrayList<StatusBarNotification> sbns) { 2560 for (final ManagedServiceInfo serviceInfo : mServices) { 2561 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2562 continue; 2563 } 2564 mHandler.post(new Runnable() { 2565 @Override 2566 public void run() { 2567 notifyRankingUpdate(serviceInfo, 2568 makeRankingUpdateForListener(serviceInfo, sbns)); 2569 } 2570 }); 2571 } 2572 } 2573 2574 private void notifyPostedIfUserMatch(final ManagedServiceInfo info, 2575 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 2576 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2577 return; 2578 } 2579 final INotificationListener listener = (INotificationListener)info.service; 2580 try { 2581 listener.onNotificationPosted(sbn, rankingUpdate); 2582 } catch (RemoteException ex) { 2583 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 2584 } 2585 } 2586 2587 private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn, 2588 NotificationRankingUpdate rankingUpdate) { 2589 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2590 return; 2591 } 2592 final INotificationListener listener = (INotificationListener) info.service; 2593 try { 2594 listener.onNotificationRemoved(sbn, rankingUpdate); 2595 } catch (RemoteException ex) { 2596 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 2597 } 2598 } 2599 2600 private void notifyRankingUpdate(ManagedServiceInfo info, 2601 NotificationRankingUpdate rankingUpdate) { 2602 final INotificationListener listener = (INotificationListener) info.service; 2603 try { 2604 listener.onNotificationRankingUpdate(rankingUpdate); 2605 } catch (RemoteException ex) { 2606 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 2607 } 2608 } 2609 } 2610} 2611