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