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