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