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