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