NotificationManagerService.java revision d1dd0277552b8729154ffe67c14e7dcfb833b04a
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 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 * @param keys the notification keys to fetch, or null for all active notifications. 1364 * @returns The return value will contain the notifications specified in keys, in that 1365 * order, or if keys is null, all the notifications, in natural order. 1366 */ 1367 @Override 1368 public StatusBarNotification[] getActiveNotificationsFromListener( 1369 INotificationListener token, String[] keys) { 1370 synchronized (mNotificationList) { 1371 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1372 final ArrayList<StatusBarNotification> list 1373 = new ArrayList<StatusBarNotification>(); 1374 if (keys == null) { 1375 final int N = mNotificationList.size(); 1376 for (int i=0; i<N; i++) { 1377 StatusBarNotification sbn = mNotificationList.get(i).sbn; 1378 if (info.enabledAndUserMatches(sbn.getUserId())) { 1379 list.add(sbn); 1380 } 1381 } 1382 } else { 1383 final int N = keys.length; 1384 for (int i=0; i<N; i++) { 1385 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1386 if (r != null && info.enabledAndUserMatches(r.sbn.getUserId())) { 1387 list.add(r.sbn); 1388 } 1389 } 1390 } 1391 return list.toArray(new StatusBarNotification[list.size()]); 1392 } 1393 } 1394 1395 @Override 1396 public String[] getActiveNotificationKeysFromListener(INotificationListener token) { 1397 return NotificationManagerService.this.getActiveNotificationKeys(token); 1398 } 1399 1400 @Override 1401 public ZenModeConfig getZenModeConfig() { 1402 checkCallerIsSystem(); 1403 return mZenModeHelper.getConfig(); 1404 } 1405 1406 @Override 1407 public boolean setZenModeConfig(ZenModeConfig config) { 1408 checkCallerIsSystem(); 1409 return mZenModeHelper.setConfig(config); 1410 } 1411 1412 @Override 1413 public void notifyConditions(String pkg, IConditionProvider provider, 1414 Condition[] conditions) { 1415 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider); 1416 checkCallerIsSystemOrSameApp(pkg); 1417 final long identity = Binder.clearCallingIdentity(); 1418 try { 1419 mConditionProviders.notifyConditions(pkg, info, conditions); 1420 } finally { 1421 Binder.restoreCallingIdentity(identity); 1422 } 1423 } 1424 1425 @Override 1426 public void requestZenModeConditions(IConditionListener callback, int relevance) { 1427 enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions"); 1428 mConditionProviders.requestZenModeConditions(callback, relevance); 1429 } 1430 1431 @Override 1432 public void setZenModeCondition(Uri conditionId) { 1433 enforceSystemOrSystemUI("INotificationManager.setZenModeCondition"); 1434 final long identity = Binder.clearCallingIdentity(); 1435 try { 1436 mConditionProviders.setZenModeCondition(conditionId); 1437 } finally { 1438 Binder.restoreCallingIdentity(identity); 1439 } 1440 } 1441 1442 @Override 1443 public void setAutomaticZenModeConditions(Uri[] conditionIds) { 1444 enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions"); 1445 mConditionProviders.setAutomaticZenModeConditions(conditionIds); 1446 } 1447 1448 @Override 1449 public Condition[] getAutomaticZenModeConditions() { 1450 enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions"); 1451 return mConditionProviders.getAutomaticZenModeConditions(); 1452 } 1453 1454 private void enforceSystemOrSystemUI(String message) { 1455 if (isCallerSystem()) return; 1456 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 1457 message); 1458 } 1459 1460 @Override 1461 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1462 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1463 != PackageManager.PERMISSION_GRANTED) { 1464 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1465 + Binder.getCallingPid() 1466 + ", uid=" + Binder.getCallingUid()); 1467 return; 1468 } 1469 1470 dumpImpl(pw); 1471 } 1472 }; 1473 1474 private String[] getActiveNotificationKeys(INotificationListener token) { 1475 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1476 final ArrayList<String> keys = new ArrayList<String>(); 1477 if (info.isEnabledForCurrentProfiles()) { 1478 synchronized (mNotificationList) { 1479 final int N = mNotificationList.size(); 1480 for (int i = 0; i < N; i++) { 1481 final StatusBarNotification sbn = mNotificationList.get(i).sbn; 1482 if (info.enabledAndUserMatches(sbn.getUserId())) { 1483 keys.add(sbn.getKey()); 1484 } 1485 } 1486 } 1487 } 1488 return keys.toArray(new String[keys.size()]); 1489 } 1490 1491 void dumpImpl(PrintWriter pw) { 1492 pw.println("Current Notification Manager state:"); 1493 1494 int N; 1495 1496 synchronized (mToastQueue) { 1497 N = mToastQueue.size(); 1498 if (N > 0) { 1499 pw.println(" Toast Queue:"); 1500 for (int i=0; i<N; i++) { 1501 mToastQueue.get(i).dump(pw, " "); 1502 } 1503 pw.println(" "); 1504 } 1505 1506 } 1507 1508 synchronized (mNotificationList) { 1509 N = mNotificationList.size(); 1510 if (N > 0) { 1511 pw.println(" Notification List:"); 1512 for (int i=0; i<N; i++) { 1513 mNotificationList.get(i).dump(pw, " ", getContext()); 1514 } 1515 pw.println(" "); 1516 } 1517 1518 N = mLights.size(); 1519 if (N > 0) { 1520 pw.println(" Lights List:"); 1521 for (int i=0; i<N; i++) { 1522 pw.println(" " + mLights.get(i)); 1523 } 1524 pw.println(" "); 1525 } 1526 1527 pw.println(" mSoundNotification=" + mSoundNotification); 1528 pw.println(" mVibrateNotification=" + mVibrateNotification); 1529 pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts); 1530 pw.println(" mSystemReady=" + mSystemReady); 1531 pw.println(" mArchive=" + mArchive.toString()); 1532 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1533 int i=0; 1534 while (iter.hasNext()) { 1535 pw.println(" " + iter.next()); 1536 if (++i >= 5) { 1537 if (iter.hasNext()) pw.println(" ..."); 1538 break; 1539 } 1540 } 1541 1542 pw.println("\n Usage Stats:"); 1543 mUsageStats.dump(pw, " "); 1544 1545 pw.println("\n Zen Mode:"); 1546 mZenModeHelper.dump(pw, " "); 1547 1548 pw.println("\n Notification listeners:"); 1549 mListeners.dump(pw); 1550 1551 pw.println("\n Condition providers:"); 1552 mConditionProviders.dump(pw); 1553 } 1554 } 1555 1556 /** 1557 * The private API only accessible to the system process. 1558 */ 1559 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1560 @Override 1561 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, 1562 String tag, int id, Notification notification, int[] idReceived, int userId) { 1563 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 1564 idReceived, userId); 1565 } 1566 }; 1567 1568 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, 1569 final int callingPid, final String tag, final int id, final Notification notification, 1570 int[] idOut, int incomingUserId) { 1571 if (DBG) { 1572 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 1573 + " notification=" + notification); 1574 } 1575 checkCallerIsSystemOrSameApp(pkg); 1576 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 1577 1578 final int userId = ActivityManager.handleIncomingUser(callingPid, 1579 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1580 final UserHandle user = new UserHandle(userId); 1581 1582 // Limit the number of notifications that any given package except the android 1583 // package can enqueue. Prevents DOS attacks and deals with leaks. 1584 if (!isSystemNotification) { 1585 synchronized (mNotificationList) { 1586 int count = 0; 1587 final int N = mNotificationList.size(); 1588 for (int i=0; i<N; i++) { 1589 final NotificationRecord r = mNotificationList.get(i); 1590 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1591 count++; 1592 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1593 Slog.e(TAG, "Package has already posted " + count 1594 + " notifications. Not showing more. package=" + pkg); 1595 return; 1596 } 1597 } 1598 } 1599 } 1600 } 1601 1602 // This conditional is a dirty hack to limit the logging done on 1603 // behalf of the download manager without affecting other apps. 1604 if (!pkg.equals("com.android.providers.downloads") 1605 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1606 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 1607 pkg, id, tag, userId, notification.toString()); 1608 } 1609 1610 if (pkg == null || notification == null) { 1611 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1612 + " id=" + id + " notification=" + notification); 1613 } 1614 if (notification.icon != 0) { 1615 if (notification.contentView == null) { 1616 throw new IllegalArgumentException("contentView required: pkg=" + pkg 1617 + " id=" + id + " notification=" + notification); 1618 } 1619 } 1620 1621 mHandler.post(new Runnable() { 1622 @Override 1623 public void run() { 1624 1625 // === Scoring === 1626 1627 // 0. Sanitize inputs 1628 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1629 Notification.PRIORITY_MAX); 1630 // Migrate notification flags to scores 1631 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1632 if (notification.priority < Notification.PRIORITY_MAX) { 1633 notification.priority = Notification.PRIORITY_MAX; 1634 } 1635 } else if (SCORE_ONGOING_HIGHER && 1636 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1637 if (notification.priority < Notification.PRIORITY_HIGH) { 1638 notification.priority = Notification.PRIORITY_HIGH; 1639 } 1640 } 1641 1642 // 1. initial score: buckets of 10, around the app 1643 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] 1644 1645 // 2. extract ranking signals from the notification data 1646 final StatusBarNotification n = new StatusBarNotification( 1647 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 1648 user); 1649 NotificationRecord r = new NotificationRecord(n); 1650 if (!mSignalExtractors.isEmpty()) { 1651 for (NotificationSignalExtractor extractor : mSignalExtractors) { 1652 try { 1653 RankingFuture future = extractor.process(r); 1654 scheduleRankingReconsideration(future); 1655 } catch (Throwable t) { 1656 Slog.w(TAG, "NotificationSignalExtractor failed.", t); 1657 } 1658 } 1659 } 1660 1661 // 3. Apply local rules 1662 1663 // blocked apps 1664 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1665 if (!isSystemNotification) { 1666 score = JUNK_SCORE; 1667 Slog.e(TAG, "Suppressing notification from package " + pkg 1668 + " by user request."); 1669 } 1670 } 1671 1672 if (score < SCORE_DISPLAY_THRESHOLD) { 1673 // Notification will be blocked because the score is too low. 1674 return; 1675 } 1676 1677 // Is this notification intercepted by zen mode? 1678 final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification); 1679 notification.extras.putBoolean(EXTRA_INTERCEPT, intercept); 1680 1681 // Should this notification make noise, vibe, or use the LED? 1682 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept; 1683 if (DBG || intercept) Slog.v(TAG, 1684 "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept); 1685 synchronized (mNotificationList) { 1686 NotificationRecord old = null; 1687 int index = indexOfNotificationLocked(pkg, tag, id, userId); 1688 if (index < 0) { 1689 mNotificationList.add(r); 1690 mUsageStats.registerPostedByApp(r); 1691 } else { 1692 old = mNotificationList.get(index); 1693 mNotificationList.set(index, r); 1694 mUsageStats.registerUpdatedByApp(r, old); 1695 // Make sure we don't lose the foreground service state. 1696 if (old != null) { 1697 notification.flags |= 1698 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1699 } 1700 } 1701 if (old != null) { 1702 mNotificationsByKey.remove(old.sbn.getKey()); 1703 } 1704 mNotificationsByKey.put(n.getKey(), r); 1705 1706 Collections.sort(mNotificationList, mRankingComparator); 1707 1708 // Ensure if this is a foreground service that the proper additional 1709 // flags are set. 1710 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1711 notification.flags |= Notification.FLAG_ONGOING_EVENT 1712 | Notification.FLAG_NO_CLEAR; 1713 } 1714 1715 final int currentUser; 1716 final long token = Binder.clearCallingIdentity(); 1717 try { 1718 currentUser = ActivityManager.getCurrentUser(); 1719 } finally { 1720 Binder.restoreCallingIdentity(token); 1721 } 1722 1723 if (notification.icon != 0) { 1724 if (old != null && old.statusBarKey != null) { 1725 r.statusBarKey = old.statusBarKey; 1726 final long identity = Binder.clearCallingIdentity(); 1727 try { 1728 mStatusBar.updateNotification(r.statusBarKey, n); 1729 } finally { 1730 Binder.restoreCallingIdentity(identity); 1731 } 1732 } else { 1733 final long identity = Binder.clearCallingIdentity(); 1734 try { 1735 r.statusBarKey = mStatusBar.addNotification(n); 1736 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0 1737 && canInterrupt) { 1738 mAttentionLight.pulse(); 1739 } 1740 } finally { 1741 Binder.restoreCallingIdentity(identity); 1742 } 1743 } 1744 // Send accessibility events only for the current user. 1745 if (currentUser == userId) { 1746 sendAccessibilityEvent(notification, pkg); 1747 } 1748 1749 mListeners.notifyPostedLocked(r.sbn, cloneNotificationListLocked()); 1750 } else { 1751 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1752 if (old != null && old.statusBarKey != null) { 1753 final long identity = Binder.clearCallingIdentity(); 1754 try { 1755 mStatusBar.removeNotification(old.statusBarKey); 1756 } finally { 1757 Binder.restoreCallingIdentity(identity); 1758 } 1759 1760 mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked()); 1761 } 1762 // ATTENTION: in a future release we will bail out here 1763 // so that we do not play sounds, show lights, etc. for invalid 1764 // notifications 1765 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1766 + n.getPackageName()); 1767 } 1768 1769 // If we're not supposed to beep, vibrate, etc. then don't. 1770 if (!mDisableNotificationAlerts 1771 && (!(old != null 1772 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1773 && (r.getUserId() == UserHandle.USER_ALL || 1774 (r.getUserId() == userId && r.getUserId() == currentUser) || 1775 mUserProfiles.isCurrentProfile(r.getUserId())) 1776 && canInterrupt 1777 && mSystemReady 1778 && mAudioManager != null) { 1779 if (DBG) Slog.v(TAG, "Interrupting!"); 1780 // sound 1781 1782 // should we use the default notification sound? (indicated either by 1783 // DEFAULT_SOUND or because notification.sound is pointing at 1784 // Settings.System.NOTIFICATION_SOUND) 1785 final boolean useDefaultSound = 1786 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1787 Settings.System.DEFAULT_NOTIFICATION_URI 1788 .equals(notification.sound); 1789 1790 Uri soundUri = null; 1791 boolean hasValidSound = false; 1792 1793 if (useDefaultSound) { 1794 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1795 1796 // check to see if the default notification sound is silent 1797 ContentResolver resolver = getContext().getContentResolver(); 1798 hasValidSound = Settings.System.getString(resolver, 1799 Settings.System.NOTIFICATION_SOUND) != null; 1800 } else if (notification.sound != null) { 1801 soundUri = notification.sound; 1802 hasValidSound = (soundUri != null); 1803 } 1804 1805 if (hasValidSound) { 1806 boolean looping = 1807 (notification.flags & Notification.FLAG_INSISTENT) != 0; 1808 int audioStreamType; 1809 if (notification.audioStreamType >= 0) { 1810 audioStreamType = notification.audioStreamType; 1811 } else { 1812 audioStreamType = DEFAULT_STREAM_TYPE; 1813 } 1814 mSoundNotification = r; 1815 // do not play notifications if stream volume is 0 (typically because 1816 // ringer mode is silent) or if there is a user of exclusive audio focus 1817 if ((mAudioManager.getStreamVolume(audioStreamType) != 0) 1818 && !mAudioManager.isAudioFocusExclusive()) { 1819 final long identity = Binder.clearCallingIdentity(); 1820 try { 1821 final IRingtonePlayer player = 1822 mAudioManager.getRingtonePlayer(); 1823 if (player != null) { 1824 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 1825 + " on stream " + audioStreamType); 1826 player.playAsync(soundUri, user, looping, audioStreamType); 1827 } 1828 } catch (RemoteException e) { 1829 } finally { 1830 Binder.restoreCallingIdentity(identity); 1831 } 1832 } 1833 } 1834 1835 // vibrate 1836 // Does the notification want to specify its own vibration? 1837 final boolean hasCustomVibrate = notification.vibrate != null; 1838 1839 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1840 // mode, and no other vibration is specified, we fall back to vibration 1841 final boolean convertSoundToVibration = 1842 !hasCustomVibrate 1843 && hasValidSound 1844 && (mAudioManager.getRingerMode() 1845 == AudioManager.RINGER_MODE_VIBRATE); 1846 1847 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1848 final boolean useDefaultVibrate = 1849 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1850 1851 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1852 && !(mAudioManager.getRingerMode() 1853 == AudioManager.RINGER_MODE_SILENT)) { 1854 mVibrateNotification = r; 1855 1856 if (useDefaultVibrate || convertSoundToVibration) { 1857 // Escalate privileges so we can use the vibrator even if the 1858 // notifying app does not have the VIBRATE permission. 1859 long identity = Binder.clearCallingIdentity(); 1860 try { 1861 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1862 useDefaultVibrate ? mDefaultVibrationPattern 1863 : mFallbackVibrationPattern, 1864 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1865 ? 0: -1, notification.audioStreamType); 1866 } finally { 1867 Binder.restoreCallingIdentity(identity); 1868 } 1869 } else if (notification.vibrate.length > 1) { 1870 // If you want your own vibration pattern, you need the VIBRATE 1871 // permission 1872 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1873 notification.vibrate, 1874 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1875 ? 0: -1, notification.audioStreamType); 1876 } 1877 } 1878 } 1879 1880 // light 1881 // the most recent thing gets the light 1882 mLights.remove(old); 1883 if (mLedNotification == old) { 1884 mLedNotification = null; 1885 } 1886 //Slog.i(TAG, "notification.lights=" 1887 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) 1888 // != 0)); 1889 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 1890 && canInterrupt) { 1891 mLights.add(r); 1892 updateLightsLocked(); 1893 } else { 1894 if (old != null 1895 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) { 1896 updateLightsLocked(); 1897 } 1898 } 1899 } 1900 } 1901 }); 1902 1903 idOut[0] = id; 1904 } 1905 1906 void showNextToastLocked() { 1907 ToastRecord record = mToastQueue.get(0); 1908 while (record != null) { 1909 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1910 try { 1911 record.callback.show(); 1912 scheduleTimeoutLocked(record); 1913 return; 1914 } catch (RemoteException e) { 1915 Slog.w(TAG, "Object died trying to show notification " + record.callback 1916 + " in package " + record.pkg); 1917 // remove it from the list and let the process die 1918 int index = mToastQueue.indexOf(record); 1919 if (index >= 0) { 1920 mToastQueue.remove(index); 1921 } 1922 keepProcessAliveLocked(record.pid); 1923 if (mToastQueue.size() > 0) { 1924 record = mToastQueue.get(0); 1925 } else { 1926 record = null; 1927 } 1928 } 1929 } 1930 } 1931 1932 void cancelToastLocked(int index) { 1933 ToastRecord record = mToastQueue.get(index); 1934 try { 1935 record.callback.hide(); 1936 } catch (RemoteException e) { 1937 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1938 + " in package " + record.pkg); 1939 // don't worry about this, we're about to remove it from 1940 // the list anyway 1941 } 1942 mToastQueue.remove(index); 1943 keepProcessAliveLocked(record.pid); 1944 if (mToastQueue.size() > 0) { 1945 // Show the next one. If the callback fails, this will remove 1946 // it from the list, so don't assume that the list hasn't changed 1947 // after this point. 1948 showNextToastLocked(); 1949 } 1950 } 1951 1952 private void scheduleTimeoutLocked(ToastRecord r) 1953 { 1954 mHandler.removeCallbacksAndMessages(r); 1955 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1956 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 1957 mHandler.sendMessageDelayed(m, delay); 1958 } 1959 1960 private void handleTimeout(ToastRecord record) 1961 { 1962 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1963 synchronized (mToastQueue) { 1964 int index = indexOfToastLocked(record.pkg, record.callback); 1965 if (index >= 0) { 1966 cancelToastLocked(index); 1967 } 1968 } 1969 } 1970 1971 // lock on mToastQueue 1972 int indexOfToastLocked(String pkg, ITransientNotification callback) 1973 { 1974 IBinder cbak = callback.asBinder(); 1975 ArrayList<ToastRecord> list = mToastQueue; 1976 int len = list.size(); 1977 for (int i=0; i<len; i++) { 1978 ToastRecord r = list.get(i); 1979 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1980 return i; 1981 } 1982 } 1983 return -1; 1984 } 1985 1986 // lock on mToastQueue 1987 void keepProcessAliveLocked(int pid) 1988 { 1989 int toastCount = 0; // toasts from this pid 1990 ArrayList<ToastRecord> list = mToastQueue; 1991 int N = list.size(); 1992 for (int i=0; i<N; i++) { 1993 ToastRecord r = list.get(i); 1994 if (r.pid == pid) { 1995 toastCount++; 1996 } 1997 } 1998 try { 1999 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 2000 } catch (RemoteException e) { 2001 // Shouldn't happen. 2002 } 2003 } 2004 2005 private void scheduleRankingReconsideration(RankingFuture future) { 2006 if (future != null) { 2007 Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, future); 2008 long delay = future.getDelay(TimeUnit.MILLISECONDS); 2009 mRankingHandler.sendMessageDelayed(m, delay); 2010 } 2011 } 2012 2013 private void handleRankingReconsideration(Message message) { 2014 if (!(message.obj instanceof RankingFuture)) return; 2015 2016 RankingFuture future = (RankingFuture) message.obj; 2017 future.run(); 2018 try { 2019 NotificationRecord record = future.get(); 2020 synchronized (mNotificationList) { 2021 int before = mNotificationList.indexOf(record); 2022 if (before != -1) { 2023 Collections.sort(mNotificationList, mRankingComparator); 2024 int after = mNotificationList.indexOf(record); 2025 2026 if (before != after) { 2027 scheduleSendRankingUpdate(); 2028 } 2029 } 2030 } 2031 } catch (InterruptedException e) { 2032 // we're running the future explicitly, so this should never happen 2033 } catch (ExecutionException e) { 2034 // we're running the future explicitly, so this should never happen 2035 } 2036 } 2037 2038 private void scheduleSendRankingUpdate() { 2039 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 2040 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 2041 mHandler.sendMessage(m); 2042 } 2043 2044 private void handleSendRankingUpdate() { 2045 synchronized (mNotificationList) { 2046 mListeners.notifyRankingUpdateLocked(cloneNotificationListLocked()); 2047 } 2048 } 2049 2050 private ArrayList<StatusBarNotification> cloneNotificationListLocked() { 2051 final int N = mNotificationList.size(); 2052 ArrayList<StatusBarNotification> sbns = new ArrayList<StatusBarNotification>(N); 2053 for (int i = 0; i < N; i++) { 2054 sbns.add(mNotificationList.get(i).sbn); 2055 } 2056 return sbns; 2057 } 2058 2059 private final class WorkerHandler extends Handler 2060 { 2061 @Override 2062 public void handleMessage(Message msg) 2063 { 2064 switch (msg.what) 2065 { 2066 case MESSAGE_TIMEOUT: 2067 handleTimeout((ToastRecord)msg.obj); 2068 break; 2069 case MESSAGE_SAVE_POLICY_FILE: 2070 handleSavePolicyFile(); 2071 break; 2072 case MESSAGE_SEND_RANKING_UPDATE: 2073 handleSendRankingUpdate(); 2074 break; 2075 } 2076 } 2077 2078 } 2079 2080 private final class RankingWorkerHandler extends Handler 2081 { 2082 public RankingWorkerHandler(Looper looper) { 2083 super(looper); 2084 } 2085 2086 @Override 2087 public void handleMessage(Message msg) { 2088 switch (msg.what) { 2089 case MESSAGE_RECONSIDER_RANKING: 2090 handleRankingReconsideration(msg); 2091 break; 2092 } 2093 } 2094 } 2095 2096 // Notifications 2097 // ============================================================================ 2098 static int clamp(int x, int low, int high) { 2099 return (x < low) ? low : ((x > high) ? high : x); 2100 } 2101 2102 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 2103 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 2104 if (!manager.isEnabled()) { 2105 return; 2106 } 2107 2108 AccessibilityEvent event = 2109 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 2110 event.setPackageName(packageName); 2111 event.setClassName(Notification.class.getName()); 2112 event.setParcelableData(notification); 2113 CharSequence tickerText = notification.tickerText; 2114 if (!TextUtils.isEmpty(tickerText)) { 2115 event.getText().add(tickerText); 2116 } 2117 2118 manager.sendAccessibilityEvent(event); 2119 } 2120 2121 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 2122 // tell the app 2123 if (sendDelete) { 2124 if (r.getNotification().deleteIntent != null) { 2125 try { 2126 r.getNotification().deleteIntent.send(); 2127 } catch (PendingIntent.CanceledException ex) { 2128 // do nothing - there's no relevant way to recover, and 2129 // no reason to let this propagate 2130 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 2131 } 2132 } 2133 } 2134 2135 // status bar 2136 if (r.getNotification().icon != 0) { 2137 final long identity = Binder.clearCallingIdentity(); 2138 try { 2139 mStatusBar.removeNotification(r.statusBarKey); 2140 } finally { 2141 Binder.restoreCallingIdentity(identity); 2142 } 2143 r.statusBarKey = null; 2144 mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked()); 2145 } 2146 2147 // sound 2148 if (mSoundNotification == r) { 2149 mSoundNotification = null; 2150 final long identity = Binder.clearCallingIdentity(); 2151 try { 2152 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2153 if (player != null) { 2154 player.stopAsync(); 2155 } 2156 } catch (RemoteException e) { 2157 } finally { 2158 Binder.restoreCallingIdentity(identity); 2159 } 2160 } 2161 2162 // vibrate 2163 if (mVibrateNotification == r) { 2164 mVibrateNotification = null; 2165 long identity = Binder.clearCallingIdentity(); 2166 try { 2167 mVibrator.cancel(); 2168 } 2169 finally { 2170 Binder.restoreCallingIdentity(identity); 2171 } 2172 } 2173 2174 // light 2175 mLights.remove(r); 2176 if (mLedNotification == r) { 2177 mLedNotification = null; 2178 } 2179 2180 // Record usage stats 2181 switch (reason) { 2182 case REASON_DELEGATE_CANCEL: 2183 case REASON_DELEGATE_CANCEL_ALL: 2184 case REASON_LISTENER_CANCEL: 2185 case REASON_LISTENER_CANCEL_ALL: 2186 mUsageStats.registerDismissedByUser(r); 2187 break; 2188 case REASON_NOMAN_CANCEL: 2189 case REASON_NOMAN_CANCEL_ALL: 2190 mUsageStats.registerRemovedByApp(r); 2191 break; 2192 case REASON_DELEGATE_CLICK: 2193 mUsageStats.registerCancelDueToClick(r); 2194 break; 2195 default: 2196 mUsageStats.registerCancelUnknown(r); 2197 break; 2198 } 2199 2200 // Save it for users of getHistoricalNotifications() 2201 mArchive.record(r.sbn); 2202 } 2203 2204 /** 2205 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2206 * and none of the {@code mustNotHaveFlags}. 2207 */ 2208 void cancelNotification(final int callingUid, final int callingPid, 2209 final String pkg, final String tag, final int id, 2210 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2211 final int userId, final int reason, final ManagedServiceInfo listener) { 2212 // In enqueueNotificationInternal notifications are added by scheduling the 2213 // work on the worker handler. Hence, we also schedule the cancel on this 2214 // handler to avoid a scenario where an add notification call followed by a 2215 // remove notification call ends up in not removing the notification. 2216 mHandler.post(new Runnable() { 2217 @Override 2218 public void run() { 2219 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2220 mustHaveFlags, mustNotHaveFlags, reason, 2221 listener == null ? null : listener.component.toShortString()); 2222 2223 synchronized (mNotificationList) { 2224 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2225 if (index >= 0) { 2226 NotificationRecord r = mNotificationList.get(index); 2227 2228 // Ideally we'd do this in the caller of this method. However, that would 2229 // require the caller to also find the notification. 2230 if (reason == REASON_DELEGATE_CLICK) { 2231 mUsageStats.registerClickedByUser(r); 2232 } 2233 2234 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2235 return; 2236 } 2237 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2238 return; 2239 } 2240 2241 mNotificationList.remove(index); 2242 mNotificationsByKey.remove(r.sbn.getKey()); 2243 2244 cancelNotificationLocked(r, sendDelete, reason); 2245 updateLightsLocked(); 2246 } 2247 } 2248 } 2249 }); 2250 } 2251 2252 /** 2253 * Determine whether the userId applies to the notification in question, either because 2254 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2255 */ 2256 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2257 return 2258 // looking for USER_ALL notifications? match everything 2259 userId == UserHandle.USER_ALL 2260 // a notification sent to USER_ALL matches any query 2261 || r.getUserId() == UserHandle.USER_ALL 2262 // an exact user match 2263 || r.getUserId() == userId; 2264 } 2265 2266 /** 2267 * Determine whether the userId applies to the notification in question, either because 2268 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2269 * because it matches one of the users profiles. 2270 */ 2271 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2272 return notificationMatchesUserId(r, userId) 2273 || mUserProfiles.isCurrentProfile(r.getUserId()); 2274 } 2275 2276 /** 2277 * Cancels all notifications from a given package that have all of the 2278 * {@code mustHaveFlags}. 2279 */ 2280 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2281 int mustNotHaveFlags, boolean doit, int userId, int reason, 2282 ManagedServiceInfo listener) { 2283 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2284 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2285 listener == null ? null : listener.component.toShortString()); 2286 2287 synchronized (mNotificationList) { 2288 final int N = mNotificationList.size(); 2289 boolean canceledSomething = false; 2290 for (int i = N-1; i >= 0; --i) { 2291 NotificationRecord r = mNotificationList.get(i); 2292 if (!notificationMatchesUserId(r, userId)) { 2293 continue; 2294 } 2295 // Don't remove notifications to all, if there's no package name specified 2296 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2297 continue; 2298 } 2299 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2300 continue; 2301 } 2302 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2303 continue; 2304 } 2305 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2306 continue; 2307 } 2308 canceledSomething = true; 2309 if (!doit) { 2310 return true; 2311 } 2312 mNotificationList.remove(i); 2313 mNotificationsByKey.remove(r.sbn.getKey()); 2314 cancelNotificationLocked(r, false, reason); 2315 } 2316 if (canceledSomething) { 2317 updateLightsLocked(); 2318 } 2319 return canceledSomething; 2320 } 2321 } 2322 2323 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2324 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2325 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2326 null, userId, 0, 0, reason, 2327 listener == null ? null : listener.component.toShortString()); 2328 2329 final int N = mNotificationList.size(); 2330 for (int i=N-1; i>=0; i--) { 2331 NotificationRecord r = mNotificationList.get(i); 2332 if (includeCurrentProfiles) { 2333 if (!notificationMatchesCurrentProfiles(r, userId)) { 2334 continue; 2335 } 2336 } else { 2337 if (!notificationMatchesUserId(r, userId)) { 2338 continue; 2339 } 2340 } 2341 2342 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2343 | Notification.FLAG_NO_CLEAR)) == 0) { 2344 mNotificationList.remove(i); 2345 mNotificationsByKey.remove(r.sbn.getKey()); 2346 cancelNotificationLocked(r, true, reason); 2347 } 2348 } 2349 updateLightsLocked(); 2350 } 2351 2352 // lock on mNotificationList 2353 void updateLightsLocked() 2354 { 2355 // handle notification lights 2356 if (mLedNotification == null) { 2357 // get next notification, if any 2358 int n = mLights.size(); 2359 if (n > 0) { 2360 mLedNotification = mLights.get(n-1); 2361 } 2362 } 2363 2364 // Don't flash while we are in a call or screen is on 2365 if (mLedNotification == null || mInCall || mScreenOn) { 2366 mNotificationLight.turnOff(); 2367 } else { 2368 final Notification ledno = mLedNotification.sbn.getNotification(); 2369 int ledARGB = ledno.ledARGB; 2370 int ledOnMS = ledno.ledOnMS; 2371 int ledOffMS = ledno.ledOffMS; 2372 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2373 ledARGB = mDefaultNotificationColor; 2374 ledOnMS = mDefaultNotificationLedOn; 2375 ledOffMS = mDefaultNotificationLedOff; 2376 } 2377 if (mNotificationPulseEnabled) { 2378 // pulse repeatedly 2379 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2380 ledOnMS, ledOffMS); 2381 } 2382 } 2383 } 2384 2385 // lock on mNotificationList 2386 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2387 { 2388 ArrayList<NotificationRecord> list = mNotificationList; 2389 final int len = list.size(); 2390 for (int i=0; i<len; i++) { 2391 NotificationRecord r = list.get(i); 2392 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2393 continue; 2394 } 2395 if (tag == null) { 2396 if (r.sbn.getTag() != null) { 2397 continue; 2398 } 2399 } else { 2400 if (!tag.equals(r.sbn.getTag())) { 2401 continue; 2402 } 2403 } 2404 if (r.sbn.getPackageName().equals(pkg)) { 2405 return i; 2406 } 2407 } 2408 return -1; 2409 } 2410 2411 private void updateNotificationPulse() { 2412 synchronized (mNotificationList) { 2413 updateLightsLocked(); 2414 } 2415 } 2416 2417 private static boolean isUidSystem(int uid) { 2418 final int appid = UserHandle.getAppId(uid); 2419 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2420 } 2421 2422 private static boolean isCallerSystem() { 2423 return isUidSystem(Binder.getCallingUid()); 2424 } 2425 2426 private static void checkCallerIsSystem() { 2427 if (isCallerSystem()) { 2428 return; 2429 } 2430 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2431 } 2432 2433 private static void checkCallerIsSystemOrSameApp(String pkg) { 2434 if (isCallerSystem()) { 2435 return; 2436 } 2437 final int uid = Binder.getCallingUid(); 2438 try { 2439 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2440 pkg, 0, UserHandle.getCallingUserId()); 2441 if (!UserHandle.isSameApp(ai.uid, uid)) { 2442 throw new SecurityException("Calling uid " + uid + " gave package" 2443 + pkg + " which is owned by uid " + ai.uid); 2444 } 2445 } catch (RemoteException re) { 2446 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2447 } 2448 } 2449 2450 /** 2451 * Generates a NotificationRankingUpdate from 'sbns', considering only 2452 * notifications visible to the given listener. 2453 */ 2454 private static NotificationRankingUpdate makeRankingUpdateForListener(ManagedServiceInfo info, 2455 ArrayList<StatusBarNotification> sbns) { 2456 int speedBumpIndex = -1; 2457 ArrayList<String> keys = new ArrayList<String>(sbns.size()); 2458 ArrayList<String> dndKeys = new ArrayList<String>(sbns.size()); 2459 for (StatusBarNotification sbn: sbns) { 2460 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2461 continue; 2462 } 2463 keys.add(sbn.getKey()); 2464 if (sbn.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) { 2465 dndKeys.add(sbn.getKey()); 2466 } 2467 if (speedBumpIndex == -1 && 2468 sbn.getNotification().priority == Notification.PRIORITY_MIN) { 2469 speedBumpIndex = keys.size() - 1; 2470 } 2471 } 2472 String[] keysAr = keys.toArray(new String[keys.size()]); 2473 String[] dndKeysAr = dndKeys.toArray(new String[dndKeys.size()]); 2474 return new NotificationRankingUpdate(keysAr, dndKeysAr, speedBumpIndex); 2475 } 2476 2477 public class NotificationListeners extends ManagedServices { 2478 2479 public NotificationListeners() { 2480 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2481 } 2482 2483 @Override 2484 protected Config getConfig() { 2485 Config c = new Config(); 2486 c.caption = "notification listener"; 2487 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2488 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2489 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2490 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2491 c.clientLabel = R.string.notification_listener_binding_label; 2492 return c; 2493 } 2494 2495 @Override 2496 protected IInterface asInterface(IBinder binder) { 2497 return INotificationListener.Stub.asInterface(binder); 2498 } 2499 2500 @Override 2501 public void onServiceAdded(ManagedServiceInfo info) { 2502 final INotificationListener listener = (INotificationListener) info.service; 2503 final ArrayList<StatusBarNotification> sbns; 2504 synchronized (mNotificationList) { 2505 sbns = cloneNotificationListLocked(); 2506 } 2507 try { 2508 listener.onListenerConnected(makeRankingUpdateForListener(info, sbns)); 2509 } catch (RemoteException e) { 2510 // we tried 2511 } 2512 } 2513 2514 /** 2515 * asynchronously notify all listeners about a new notification 2516 */ 2517 public void notifyPostedLocked(StatusBarNotification sbn, 2518 final ArrayList<StatusBarNotification> sbns) { 2519 // make a copy in case changes are made to the underlying Notification object 2520 final StatusBarNotification sbnClone = sbn.clone(); 2521 for (final ManagedServiceInfo info : mServices) { 2522 if (!info.isEnabledForCurrentProfiles()) { 2523 continue; 2524 } 2525 final NotificationRankingUpdate update = makeRankingUpdateForListener(info, sbns); 2526 if (update.getOrderedKeys().length == 0) { 2527 continue; 2528 } 2529 mHandler.post(new Runnable() { 2530 @Override 2531 public void run() { 2532 notifyPostedIfUserMatch(info, sbnClone, update); 2533 } 2534 }); 2535 } 2536 } 2537 2538 /** 2539 * asynchronously notify all listeners about a removed notification 2540 */ 2541 public void notifyRemovedLocked(StatusBarNotification sbn, 2542 final ArrayList<StatusBarNotification> sbns) { 2543 // make a copy in case changes are made to the underlying Notification object 2544 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 2545 // notification 2546 final StatusBarNotification sbnLight = sbn.cloneLight(); 2547 for (final ManagedServiceInfo info : mServices) { 2548 if (!info.isEnabledForCurrentProfiles()) { 2549 continue; 2550 } 2551 mHandler.post(new Runnable() { 2552 @Override 2553 public void run() { 2554 notifyRemovedIfUserMatch(info, sbnLight, 2555 makeRankingUpdateForListener(info, sbns)); 2556 } 2557 }); 2558 } 2559 } 2560 2561 /** 2562 * asynchronously notify all listeners about a reordering of notifications 2563 * @param sbns an array of {@link StatusBarNotification}s to consider. This code 2564 * must not rely on mutable members of these objects, such as the 2565 * {@link Notification}. 2566 */ 2567 public void notifyRankingUpdateLocked(final ArrayList<StatusBarNotification> sbns) { 2568 for (final ManagedServiceInfo serviceInfo : mServices) { 2569 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2570 continue; 2571 } 2572 mHandler.post(new Runnable() { 2573 @Override 2574 public void run() { 2575 notifyRankingUpdate(serviceInfo, 2576 makeRankingUpdateForListener(serviceInfo, sbns)); 2577 } 2578 }); 2579 } 2580 } 2581 2582 private void notifyPostedIfUserMatch(final ManagedServiceInfo info, 2583 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 2584 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2585 return; 2586 } 2587 final INotificationListener listener = (INotificationListener)info.service; 2588 try { 2589 listener.onNotificationPosted(sbn, rankingUpdate); 2590 } catch (RemoteException ex) { 2591 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 2592 } 2593 } 2594 2595 private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn, 2596 NotificationRankingUpdate rankingUpdate) { 2597 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2598 return; 2599 } 2600 final INotificationListener listener = (INotificationListener) info.service; 2601 try { 2602 listener.onNotificationRemoved(sbn, rankingUpdate); 2603 } catch (RemoteException ex) { 2604 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 2605 } 2606 } 2607 2608 private void notifyRankingUpdate(ManagedServiceInfo info, 2609 NotificationRankingUpdate rankingUpdate) { 2610 final INotificationListener listener = (INotificationListener) info.service; 2611 try { 2612 listener.onNotificationRankingUpdate(rankingUpdate); 2613 } catch (RemoteException ex) { 2614 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 2615 } 2616 } 2617 } 2618} 2619