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