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