NotificationManagerService.java revision 5feceebb892d4cb5777cea3c6174b206705d456b
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; 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.ServiceConnection; 40import android.content.pm.ApplicationInfo; 41import android.content.pm.PackageInfo; 42import android.content.pm.PackageManager; 43import android.content.pm.PackageManager.NameNotFoundException; 44import android.content.res.Resources; 45import android.database.ContentObserver; 46import android.media.AudioManager; 47import android.media.IAudioService; 48import android.media.IRingtonePlayer; 49import android.net.Uri; 50import android.os.Binder; 51import android.os.Handler; 52import android.os.IBinder; 53import android.os.Message; 54import android.os.Process; 55import android.os.RemoteException; 56import android.os.ServiceManager; 57import android.os.UserHandle; 58import android.os.UserManager; 59import android.os.Vibrator; 60import android.provider.Settings; 61import android.service.notification.INotificationListener; 62import android.service.notification.NotificationListenerService; 63import android.service.notification.StatusBarNotification; 64import android.telephony.TelephonyManager; 65import android.text.TextUtils; 66import android.util.AtomicFile; 67import android.util.EventLog; 68import android.util.Log; 69import android.util.Slog; 70import android.util.Xml; 71import android.view.accessibility.AccessibilityEvent; 72import android.view.accessibility.AccessibilityManager; 73import android.widget.Toast; 74 75import org.xmlpull.v1.XmlPullParser; 76import org.xmlpull.v1.XmlPullParserException; 77 78import java.io.File; 79import java.io.FileDescriptor; 80import java.io.FileInputStream; 81import java.io.FileNotFoundException; 82import java.io.IOException; 83import java.io.PrintWriter; 84import java.util.ArrayDeque; 85import java.util.ArrayList; 86import java.util.Arrays; 87import java.util.HashSet; 88import java.util.Iterator; 89import java.util.NoSuchElementException; 90 91import libcore.io.IoUtils; 92 93 94/** {@hide} */ 95public class NotificationManagerService extends INotificationManager.Stub 96{ 97 private static final String TAG = "NotificationService"; 98 private static final boolean DBG = false; 99 100 private static final int MAX_PACKAGE_NOTIFICATIONS = 50; 101 102 // message codes 103 private static final int MESSAGE_TIMEOUT = 2; 104 105 private static final int LONG_DELAY = 3500; // 3.5 seconds 106 private static final int SHORT_DELAY = 2000; // 2 seconds 107 108 private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; 109 private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps 110 111 private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; 112 private static final boolean SCORE_ONGOING_HIGHER = false; 113 114 private static final int JUNK_SCORE = -1000; 115 private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; 116 private static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER; 117 118 // Notifications with scores below this will not interrupt the user, either via LED or 119 // sound or vibration 120 private static final int SCORE_INTERRUPTION_THRESHOLD = 121 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; 122 123 private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; 124 private static final boolean ENABLE_BLOCKED_TOASTS = true; 125 126 private static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":"; 127 128 final Context mContext; 129 final IActivityManager mAm; 130 final UserManager mUserManager; 131 final IBinder mForegroundToken = new Binder(); 132 133 private WorkerHandler mHandler; 134 private StatusBarManagerService mStatusBar; 135 private LightsService.Light mNotificationLight; 136 private LightsService.Light mAttentionLight; 137 138 private int mDefaultNotificationColor; 139 private int mDefaultNotificationLedOn; 140 private int mDefaultNotificationLedOff; 141 142 private long[] mDefaultVibrationPattern; 143 private long[] mFallbackVibrationPattern; 144 145 private boolean mSystemReady; 146 private int mDisabledNotifications; 147 148 private NotificationRecord mSoundNotification; 149 private NotificationRecord mVibrateNotification; 150 151 private IAudioService mAudioService; 152 private Vibrator mVibrator; 153 154 // for enabling and disabling notification pulse behavior 155 private boolean mScreenOn = true; 156 private boolean mInCall = false; 157 private boolean mNotificationPulseEnabled; 158 159 // used as a mutex for access to all active notifications & listeners 160 private final ArrayList<NotificationRecord> mNotificationList = 161 new ArrayList<NotificationRecord>(); 162 163 private ArrayList<ToastRecord> mToastQueue; 164 165 private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); 166 private NotificationRecord mLedNotification; 167 168 private final AppOpsManager mAppOps; 169 170 // contains connections to all connected listeners, including app services 171 // and system listeners 172 private ArrayList<NotificationListenerInfo> mListeners 173 = new ArrayList<NotificationListenerInfo>(); 174 // things that will be put into mListeners as soon as they're ready 175 private ArrayList<String> mServicesBinding = new ArrayList<String>(); 176 // lists the component names of all enabled (and therefore connected) listener 177 // app services for the current user only 178 private HashSet<ComponentName> mEnabledListenersForCurrentUser 179 = new HashSet<ComponentName>(); 180 // Just the packages from mEnabledListenersForCurrentUser 181 private HashSet<String> mEnabledListenerPackageNames = new HashSet<String>(); 182 183 // Notification control database. For now just contains disabled packages. 184 private AtomicFile mPolicyFile; 185 private HashSet<String> mBlockedPackages = new HashSet<String>(); 186 187 private static final int DB_VERSION = 1; 188 189 private static final String TAG_BODY = "notification-policy"; 190 private static final String ATTR_VERSION = "version"; 191 192 private static final String TAG_BLOCKED_PKGS = "blocked-packages"; 193 private static final String TAG_PACKAGE = "package"; 194 private static final String ATTR_NAME = "name"; 195 196 private class NotificationListenerInfo implements DeathRecipient { 197 INotificationListener listener; 198 ComponentName component; 199 int userid; 200 boolean isSystem; 201 ServiceConnection connection; 202 203 public NotificationListenerInfo(INotificationListener listener, ComponentName component, 204 int userid, boolean isSystem) { 205 this.listener = listener; 206 this.component = component; 207 this.userid = userid; 208 this.isSystem = isSystem; 209 this.connection = null; 210 } 211 212 public NotificationListenerInfo(INotificationListener listener, ComponentName component, 213 int userid, ServiceConnection connection) { 214 this.listener = listener; 215 this.component = component; 216 this.userid = userid; 217 this.isSystem = false; 218 this.connection = connection; 219 } 220 221 boolean enabledAndUserMatches(StatusBarNotification sbn) { 222 final int nid = sbn.getUserId(); 223 if (!isEnabledForCurrentUser()) { 224 return false; 225 } 226 if (this.userid == UserHandle.USER_ALL) return true; 227 return (nid == UserHandle.USER_ALL || nid == this.userid); 228 } 229 230 public void notifyPostedIfUserMatch(StatusBarNotification sbn) { 231 if (!enabledAndUserMatches(sbn)) { 232 return; 233 } 234 try { 235 listener.onNotificationPosted(sbn); 236 } catch (RemoteException ex) { 237 // not there? 238 } 239 } 240 241 public void notifyRemovedIfUserMatch(StatusBarNotification sbn) { 242 if (!enabledAndUserMatches(sbn)) return; 243 try { 244 listener.onNotificationRemoved(sbn); 245 } catch (RemoteException ex) { 246 // not there? 247 } 248 } 249 250 @Override 251 public void binderDied() { 252 if (connection == null) { 253 // This is not a service; it won't be recreated. We can give up this connection. 254 unregisterListener(this.listener, this.userid); 255 } 256 } 257 258 /** convenience method for looking in mEnabledListenersForCurrentUser */ 259 public boolean isEnabledForCurrentUser() { 260 if (this.isSystem) return true; 261 if (this.connection == null) return false; 262 return mEnabledListenersForCurrentUser.contains(this.component); 263 } 264 } 265 266 private static class Archive { 267 static final int BUFFER_SIZE = 1000; 268 ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE); 269 270 public Archive() { 271 } 272 273 public void record(StatusBarNotification nr) { 274 // Nuke heavy parts of notification before storing in archive 275 nr.notification.tickerView = null; 276 nr.notification.contentView = null; 277 nr.notification.bigContentView = null; 278 nr.notification.largeIcon = null; 279 280 if (mBuffer.size() == BUFFER_SIZE) { 281 mBuffer.removeFirst(); 282 } 283 mBuffer.addLast(nr); 284 } 285 286 public void clear() { 287 mBuffer.clear(); 288 } 289 290 public Iterator<StatusBarNotification> descendingIterator() { 291 return mBuffer.descendingIterator(); 292 } 293 public Iterator<StatusBarNotification> ascendingIterator() { 294 return mBuffer.iterator(); 295 } 296 public Iterator<StatusBarNotification> filter( 297 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) { 298 return new Iterator<StatusBarNotification>() { 299 StatusBarNotification mNext = findNext(); 300 301 private StatusBarNotification findNext() { 302 while (iter.hasNext()) { 303 StatusBarNotification nr = iter.next(); 304 if ((pkg == null || nr.pkg == pkg) 305 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) { 306 return nr; 307 } 308 } 309 return null; 310 } 311 312 @Override 313 public boolean hasNext() { 314 return mNext == null; 315 } 316 317 @Override 318 public StatusBarNotification next() { 319 StatusBarNotification next = mNext; 320 if (next == null) { 321 throw new NoSuchElementException(); 322 } 323 mNext = findNext(); 324 return next; 325 } 326 327 @Override 328 public void remove() { 329 iter.remove(); 330 } 331 }; 332 } 333 334 public StatusBarNotification[] getArray(int count) { 335 if (count == 0) count = Archive.BUFFER_SIZE; 336 final StatusBarNotification[] a 337 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 338 Iterator<StatusBarNotification> iter = descendingIterator(); 339 int i=0; 340 while (iter.hasNext() && i < count) { 341 a[i++] = iter.next(); 342 } 343 return a; 344 } 345 346 public StatusBarNotification[] getArray(int count, String pkg, int userId) { 347 if (count == 0) count = Archive.BUFFER_SIZE; 348 final StatusBarNotification[] a 349 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 350 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId); 351 int i=0; 352 while (iter.hasNext() && i < count) { 353 a[i++] = iter.next(); 354 } 355 return a; 356 } 357 358 } 359 360 Archive mArchive = new Archive(); 361 362 private void loadBlockDb() { 363 synchronized(mBlockedPackages) { 364 if (mPolicyFile == null) { 365 File dir = new File("/data/system"); 366 mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml")); 367 368 mBlockedPackages.clear(); 369 370 FileInputStream infile = null; 371 try { 372 infile = mPolicyFile.openRead(); 373 final XmlPullParser parser = Xml.newPullParser(); 374 parser.setInput(infile, null); 375 376 int type; 377 String tag; 378 int version = DB_VERSION; 379 while ((type = parser.next()) != END_DOCUMENT) { 380 tag = parser.getName(); 381 if (type == START_TAG) { 382 if (TAG_BODY.equals(tag)) { 383 version = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION)); 384 } else if (TAG_BLOCKED_PKGS.equals(tag)) { 385 while ((type = parser.next()) != END_DOCUMENT) { 386 tag = parser.getName(); 387 if (TAG_PACKAGE.equals(tag)) { 388 mBlockedPackages.add(parser.getAttributeValue(null, ATTR_NAME)); 389 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { 390 break; 391 } 392 } 393 } 394 } 395 } 396 } catch (FileNotFoundException e) { 397 // No data yet 398 } catch (IOException e) { 399 Log.wtf(TAG, "Unable to read blocked notifications database", e); 400 } catch (NumberFormatException e) { 401 Log.wtf(TAG, "Unable to parse blocked notifications database", e); 402 } catch (XmlPullParserException e) { 403 Log.wtf(TAG, "Unable to parse blocked notifications database", e); 404 } finally { 405 IoUtils.closeQuietly(infile); 406 } 407 } 408 } 409 } 410 411 /** 412 * Use this when you just want to know if notifications are OK for this package. 413 */ 414 public boolean areNotificationsEnabledForPackage(String pkg, int uid) { 415 checkCallerIsSystem(); 416 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 417 == AppOpsManager.MODE_ALLOWED); 418 } 419 420 /** Use this when you actually want to post a notification or toast. 421 * 422 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*(). 423 */ 424 private boolean noteNotificationOp(String pkg, int uid) { 425 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 426 != AppOpsManager.MODE_ALLOWED) { 427 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg); 428 return false; 429 } 430 return true; 431 } 432 433 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { 434 checkCallerIsSystem(); 435 if (true||DBG) { 436 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); 437 } 438 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, 439 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 440 } 441 442 443 private static String idDebugString(Context baseContext, String packageName, int id) { 444 Context c = null; 445 446 if (packageName != null) { 447 try { 448 c = baseContext.createPackageContext(packageName, 0); 449 } catch (NameNotFoundException e) { 450 c = baseContext; 451 } 452 } else { 453 c = baseContext; 454 } 455 456 String pkg; 457 String type; 458 String name; 459 460 Resources r = c.getResources(); 461 try { 462 return r.getResourceName(id); 463 } catch (Resources.NotFoundException e) { 464 return "<name unknown>"; 465 } 466 } 467 468 /** 469 * System-only API for getting a list of current (i.e. not cleared) notifications. 470 * 471 * Requires ACCESS_NOTIFICATIONS which is signature|system. 472 */ 473 @Override 474 public StatusBarNotification[] getActiveNotifications(String callingPkg) { 475 // enforce() will ensure the calling uid has the correct permission 476 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS, 477 "NotificationManagerService.getActiveNotifications"); 478 479 StatusBarNotification[] tmp = null; 480 int uid = Binder.getCallingUid(); 481 482 // noteOp will check to make sure the callingPkg matches the uid 483 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 484 == AppOpsManager.MODE_ALLOWED) { 485 synchronized (mNotificationList) { 486 tmp = new StatusBarNotification[mNotificationList.size()]; 487 final int N = mNotificationList.size(); 488 for (int i=0; i<N; i++) { 489 tmp[i] = mNotificationList.get(i).sbn; 490 } 491 } 492 } 493 return tmp; 494 } 495 496 /** 497 * System-only API for getting a list of recent (cleared, no longer shown) notifications. 498 * 499 * Requires ACCESS_NOTIFICATIONS which is signature|system. 500 */ 501 @Override 502 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { 503 // enforce() will ensure the calling uid has the correct permission 504 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS, 505 "NotificationManagerService.getHistoricalNotifications"); 506 507 StatusBarNotification[] tmp = null; 508 int uid = Binder.getCallingUid(); 509 510 // noteOp will check to make sure the callingPkg matches the uid 511 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 512 == AppOpsManager.MODE_ALLOWED) { 513 synchronized (mArchive) { 514 tmp = mArchive.getArray(count); 515 } 516 } 517 return tmp; 518 } 519 520 /** 521 * Called whenever packages change, the user switches, or ENABLED_NOTIFICATION_LISTENERS 522 * is altered. (For example in response to USER_SWITCHED in our broadcast receiver) 523 */ 524 void rebindListenerServices() { 525 String flat = Settings.Secure.getString( 526 mContext.getContentResolver(), 527 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); 528 529 NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()]; 530 final ArrayList<ComponentName> toAdd; 531 final int currentUser = ActivityManager.getCurrentUser(); 532 533 synchronized (mNotificationList) { 534 // unbind and remove all existing listeners 535 toRemove = mListeners.toArray(toRemove); 536 537 toAdd = new ArrayList<ComponentName>(); 538 final HashSet<ComponentName> newEnabled = new HashSet<ComponentName>(); 539 final HashSet<String> newPackages = new HashSet<String>(); 540 541 // decode the list of components 542 if (flat != null) { 543 String[] components = flat.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR); 544 for (int i=0; i<components.length; i++) { 545 final ComponentName component 546 = ComponentName.unflattenFromString(components[i]); 547 if (component != null) { 548 newEnabled.add(component); 549 toAdd.add(component); 550 newPackages.add(component.getPackageName()); 551 } 552 } 553 554 mEnabledListenersForCurrentUser = newEnabled; 555 mEnabledListenerPackageNames = newPackages; 556 } 557 } 558 559 for (NotificationListenerInfo info : toRemove) { 560 final ComponentName component = info.component; 561 final int oldUser = info.userid; 562 Slog.v(TAG, "disabling notification listener for user " + oldUser + ": " + component); 563 unregisterListenerService(component, info.userid); 564 } 565 566 final int N = toAdd.size(); 567 for (int i=0; i<N; i++) { 568 final ComponentName component = toAdd.get(i); 569 Slog.v(TAG, "enabling notification listener for user " + currentUser + ": " 570 + component); 571 registerListenerService(component, currentUser); 572 } 573 } 574 575 /** 576 * Register a listener binder directly with the notification manager. 577 * 578 * Only works with system callers. Apps should extend 579 * {@link android.service.notification.NotificationListenerService}. 580 */ 581 @Override 582 public void registerListener(final INotificationListener listener, 583 final ComponentName component, final int userid) { 584 checkCallerIsSystem(); 585 586 synchronized (mNotificationList) { 587 try { 588 NotificationListenerInfo info 589 = new NotificationListenerInfo(listener, component, userid, true); 590 listener.asBinder().linkToDeath(info, 0); 591 mListeners.add(info); 592 } catch (RemoteException e) { 593 // already dead 594 } 595 } 596 } 597 598 /** 599 * Version of registerListener that takes the name of a 600 * {@link android.service.notification.NotificationListenerService} to bind to. 601 * 602 * This is the mechanism by which third parties may subscribe to notifications. 603 */ 604 private void registerListenerService(final ComponentName name, final int userid) { 605 checkCallerIsSystem(); 606 607 if (DBG) Slog.v(TAG, "registerListenerService: " + name + " u=" + userid); 608 609 synchronized (mNotificationList) { 610 final String servicesBindingTag = name.toString() + "/" + userid; 611 if (mServicesBinding.contains(servicesBindingTag)) { 612 // stop registering this thing already! we're working on it 613 return; 614 } 615 mServicesBinding.add(servicesBindingTag); 616 617 final int N = mListeners.size(); 618 for (int i=N-1; i>=0; i--) { 619 final NotificationListenerInfo info = mListeners.get(i); 620 if (name.equals(info.component) 621 && info.userid == userid) { 622 // cut old connections 623 if (DBG) Slog.v(TAG, " disconnecting old listener: " + info.listener); 624 mListeners.remove(i); 625 if (info.connection != null) { 626 mContext.unbindService(info.connection); 627 } 628 } 629 } 630 631 Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE); 632 intent.setComponent(name); 633 634 intent.putExtra(Intent.EXTRA_CLIENT_LABEL, 635 com.android.internal.R.string.notification_listener_binding_label); 636 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( 637 mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0)); 638 639 try { 640 if (DBG) Slog.v(TAG, "binding: " + intent); 641 if (!mContext.bindServiceAsUser(intent, 642 new ServiceConnection() { 643 INotificationListener mListener; 644 @Override 645 public void onServiceConnected(ComponentName name, IBinder service) { 646 synchronized (mNotificationList) { 647 mServicesBinding.remove(servicesBindingTag); 648 try { 649 mListener = INotificationListener.Stub.asInterface(service); 650 NotificationListenerInfo info = new NotificationListenerInfo( 651 mListener, name, userid, this); 652 service.linkToDeath(info, 0); 653 mListeners.add(info); 654 } catch (RemoteException e) { 655 // already dead 656 } 657 } 658 } 659 660 @Override 661 public void onServiceDisconnected(ComponentName name) { 662 Slog.v(TAG, "notification listener connection lost: " + name); 663 } 664 }, 665 Context.BIND_AUTO_CREATE, 666 new UserHandle(userid))) 667 { 668 mServicesBinding.remove(servicesBindingTag); 669 Slog.w(TAG, "Unable to bind listener service: " + intent); 670 return; 671 } 672 } catch (SecurityException ex) { 673 Slog.e(TAG, "Unable to bind listener service: " + intent, ex); 674 return; 675 } 676 } 677 } 678 679 /** 680 * Remove a listener binder directly 681 */ 682 @Override 683 public void unregisterListener(INotificationListener listener, int userid) { 684 // no need to check permissions; if your listener binder is in the list, 685 // that's proof that you had permission to add it in the first place 686 687 synchronized (mNotificationList) { 688 final int N = mListeners.size(); 689 for (int i=N-1; i>=0; i--) { 690 final NotificationListenerInfo info = mListeners.get(i); 691 if (info.listener == listener && info.userid == userid) { 692 mListeners.remove(i); 693 if (info.connection != null) { 694 mContext.unbindService(info.connection); 695 } 696 } 697 } 698 } 699 } 700 701 /** 702 * Remove a listener service for the given user by ComponentName 703 */ 704 private void unregisterListenerService(ComponentName name, int userid) { 705 checkCallerIsSystem(); 706 707 synchronized (mNotificationList) { 708 final int N = mListeners.size(); 709 for (int i=N-1; i>=0; i--) { 710 final NotificationListenerInfo info = mListeners.get(i); 711 if (name.equals(info.component) 712 && info.userid == userid) { 713 mListeners.remove(i); 714 if (info.connection != null) { 715 mContext.unbindService(info.connection); 716 } 717 } 718 } 719 } 720 } 721 722 /** 723 * asynchronously notify all listeners about a new notification 724 */ 725 private void notifyPostedLocked(NotificationRecord n) { 726 final StatusBarNotification sbn = n.sbn; 727 for (final NotificationListenerInfo info : mListeners) { 728 mHandler.post(new Runnable() { 729 @Override 730 public void run() { 731 info.notifyPostedIfUserMatch(sbn); 732 }}); 733 } 734 } 735 736 /** 737 * asynchronously notify all listeners about a removed notification 738 */ 739 private void notifyRemovedLocked(NotificationRecord n) { 740 final StatusBarNotification sbn = n.sbn; 741 for (final NotificationListenerInfo info : mListeners) { 742 mHandler.post(new Runnable() { 743 @Override 744 public void run() { 745 info.notifyRemovedIfUserMatch(sbn); 746 }}); 747 } 748 } 749 750 // -- APIs to support listeners clicking/clearing notifications -- 751 752 private NotificationListenerInfo checkListenerToken(INotificationListener listener) { 753 final IBinder token = listener.asBinder(); 754 final int N = mListeners.size(); 755 for (int i=0; i<N; i++) { 756 final NotificationListenerInfo info = mListeners.get(i); 757 if (info.listener.asBinder() == token) return info; 758 } 759 throw new SecurityException("Disallowed call from unknown listener: " + listener); 760 } 761 762 /** 763 * Allow an INotificationListener to simulate a "clear all" operation. 764 * 765 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} 766 * 767 * @param token The binder for the listener, to check that the caller is allowed 768 */ 769 public void clearAllNotificationsFromListener(INotificationListener token) { 770 NotificationListenerInfo info = checkListenerToken(token); 771 long identity = Binder.clearCallingIdentity(); 772 try { 773 cancelAll(info.userid); 774 } finally { 775 Binder.restoreCallingIdentity(identity); 776 } 777 } 778 779 /** 780 * Allow an INotificationListener to simulate clearing (dismissing) a single notification. 781 * 782 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} 783 * 784 * @param token The binder for the listener, to check that the caller is allowed 785 */ 786 public void clearNotificationFromListener(INotificationListener token, String pkg, String tag, int id) { 787 NotificationListenerInfo info = checkListenerToken(token); 788 long identity = Binder.clearCallingIdentity(); 789 try { 790 cancelNotification(pkg, tag, id, 0, 791 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 792 true, 793 info.userid); 794 } finally { 795 Binder.restoreCallingIdentity(identity); 796 } 797 } 798 799 // -- end of listener APIs -- 800 801 public static final class NotificationRecord 802 { 803 final StatusBarNotification sbn; 804 IBinder statusBarKey; 805 806 NotificationRecord(StatusBarNotification sbn) 807 { 808 this.sbn = sbn; 809 } 810 811 public Notification getNotification() { return sbn.notification; } 812 public int getFlags() { return sbn.notification.flags; } 813 public int getUserId() { return sbn.getUserId(); } 814 815 void dump(PrintWriter pw, String prefix, Context baseContext) { 816 final Notification notification = sbn.notification; 817 pw.println(prefix + this); 818 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon) 819 + " / " + idDebugString(baseContext, this.sbn.pkg, notification.icon)); 820 pw.println(prefix + " pri=" + notification.priority); 821 pw.println(prefix + " score=" + this.sbn.score); 822 pw.println(prefix + " contentIntent=" + notification.contentIntent); 823 pw.println(prefix + " deleteIntent=" + notification.deleteIntent); 824 pw.println(prefix + " tickerText=" + notification.tickerText); 825 pw.println(prefix + " contentView=" + notification.contentView); 826 pw.println(prefix + " uid=" + this.sbn.uid + " userId=" + this.sbn.getUserId()); 827 pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults)); 828 pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags)); 829 pw.println(prefix + " sound=" + notification.sound); 830 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); 831 pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB) 832 + " ledOnMS=" + notification.ledOnMS 833 + " ledOffMS=" + notification.ledOffMS); 834 } 835 836 @Override 837 public final String toString() { 838 return String.format( 839 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d: %s)", 840 System.identityHashCode(this), 841 this.sbn.pkg, this.sbn.user, this.sbn.id, this.sbn.tag, 842 this.sbn.score, this.sbn.notification); 843 } 844 } 845 846 private static final class ToastRecord 847 { 848 final int pid; 849 final String pkg; 850 final ITransientNotification callback; 851 int duration; 852 853 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) 854 { 855 this.pid = pid; 856 this.pkg = pkg; 857 this.callback = callback; 858 this.duration = duration; 859 } 860 861 void update(int duration) { 862 this.duration = duration; 863 } 864 865 void dump(PrintWriter pw, String prefix) { 866 pw.println(prefix + this); 867 } 868 869 @Override 870 public final String toString() 871 { 872 return "ToastRecord{" 873 + Integer.toHexString(System.identityHashCode(this)) 874 + " pkg=" + pkg 875 + " callback=" + callback 876 + " duration=" + duration; 877 } 878 } 879 880 private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks 881 = new StatusBarManagerService.NotificationCallbacks() { 882 883 public void onSetDisabled(int status) { 884 synchronized (mNotificationList) { 885 mDisabledNotifications = status; 886 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { 887 // cancel whatever's going on 888 long identity = Binder.clearCallingIdentity(); 889 try { 890 final IRingtonePlayer player = mAudioService.getRingtonePlayer(); 891 if (player != null) { 892 player.stopAsync(); 893 } 894 } catch (RemoteException e) { 895 } finally { 896 Binder.restoreCallingIdentity(identity); 897 } 898 899 identity = Binder.clearCallingIdentity(); 900 try { 901 mVibrator.cancel(); 902 } finally { 903 Binder.restoreCallingIdentity(identity); 904 } 905 } 906 } 907 } 908 909 public void onClearAll() { 910 // XXX to be totally correct, the caller should tell us which user 911 // this is for. 912 cancelAll(ActivityManager.getCurrentUser()); 913 } 914 915 public void onNotificationClick(String pkg, String tag, int id) { 916 // XXX to be totally correct, the caller should tell us which user 917 // this is for. 918 cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL, 919 Notification.FLAG_FOREGROUND_SERVICE, false, 920 ActivityManager.getCurrentUser()); 921 } 922 923 public void onNotificationClear(String pkg, String tag, int id) { 924 // XXX to be totally correct, the caller should tell us which user 925 // this is for. 926 cancelNotification(pkg, tag, id, 0, 927 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 928 true, ActivityManager.getCurrentUser()); 929 } 930 931 public void onPanelRevealed() { 932 synchronized (mNotificationList) { 933 // sound 934 mSoundNotification = null; 935 936 long identity = Binder.clearCallingIdentity(); 937 try { 938 final IRingtonePlayer player = mAudioService.getRingtonePlayer(); 939 if (player != null) { 940 player.stopAsync(); 941 } 942 } catch (RemoteException e) { 943 } finally { 944 Binder.restoreCallingIdentity(identity); 945 } 946 947 // vibrate 948 mVibrateNotification = null; 949 identity = Binder.clearCallingIdentity(); 950 try { 951 mVibrator.cancel(); 952 } finally { 953 Binder.restoreCallingIdentity(identity); 954 } 955 956 // light 957 mLights.clear(); 958 mLedNotification = null; 959 updateLightsLocked(); 960 } 961 } 962 963 public void onNotificationError(String pkg, String tag, int id, 964 int uid, int initialPid, String message) { 965 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id 966 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")"); 967 // XXX to be totally correct, the caller should tell us which user 968 // this is for. 969 cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid)); 970 long ident = Binder.clearCallingIdentity(); 971 try { 972 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, 973 "Bad notification posted from package " + pkg 974 + ": " + message); 975 } catch (RemoteException e) { 976 } 977 Binder.restoreCallingIdentity(ident); 978 } 979 }; 980 981 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 982 @Override 983 public void onReceive(Context context, Intent intent) { 984 String action = intent.getAction(); 985 986 boolean queryRestart = false; 987 boolean packageChanged = false; 988 989 if (action.equals(Intent.ACTION_PACKAGE_REMOVED) 990 || action.equals(Intent.ACTION_PACKAGE_RESTARTED) 991 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED)) 992 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) 993 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 994 String pkgList[] = null; 995 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 996 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 997 } else if (queryRestart) { 998 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 999 } else { 1000 Uri uri = intent.getData(); 1001 if (uri == null) { 1002 return; 1003 } 1004 String pkgName = uri.getSchemeSpecificPart(); 1005 if (pkgName == null) { 1006 return; 1007 } 1008 if (packageChanged) { 1009 // We cancel notifications for packages which have just been disabled 1010 final int enabled = mContext.getPackageManager() 1011 .getApplicationEnabledSetting(pkgName); 1012 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED 1013 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { 1014 return; 1015 } 1016 } 1017 pkgList = new String[]{pkgName}; 1018 } 1019 1020 boolean anyListenersInvolved = false; 1021 if (pkgList != null && (pkgList.length > 0)) { 1022 for (String pkgName : pkgList) { 1023 cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart, 1024 UserHandle.USER_ALL); 1025 if (mEnabledListenerPackageNames.contains(pkgName)) { 1026 anyListenersInvolved = true; 1027 } 1028 } 1029 } 1030 1031 if (anyListenersInvolved) { 1032 // make sure we're still bound to any of our 1033 // listeners who may have just upgraded 1034 rebindListenerServices(); 1035 } 1036 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 1037 // Keep track of screen on/off state, but do not turn off the notification light 1038 // until user passes through the lock screen or views the notification. 1039 mScreenOn = true; 1040 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 1041 mScreenOn = false; 1042 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 1043 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals( 1044 TelephonyManager.EXTRA_STATE_OFFHOOK)); 1045 updateNotificationPulse(); 1046 } else if (action.equals(Intent.ACTION_USER_STOPPED)) { 1047 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 1048 if (userHandle >= 0) { 1049 cancelAllNotificationsInt(null, 0, 0, true, userHandle); 1050 } 1051 } else if (action.equals(Intent.ACTION_USER_PRESENT)) { 1052 // turn off LED when user passes through lock screen 1053 mNotificationLight.turnOff(); 1054 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { 1055 // reload per-user settings 1056 mSettingsObserver.update(null); 1057 } 1058 } 1059 }; 1060 1061 class SettingsObserver extends ContentObserver { 1062 private final Uri NOTIFICATION_LIGHT_PULSE_URI 1063 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); 1064 1065 private final Uri ENABLED_NOTIFICATION_LISTENERS_URI 1066 = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); 1067 1068 SettingsObserver(Handler handler) { 1069 super(handler); 1070 } 1071 1072 void observe() { 1073 ContentResolver resolver = mContext.getContentResolver(); 1074 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, 1075 false, this, UserHandle.USER_ALL); 1076 resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI, 1077 false, this, UserHandle.USER_ALL); 1078 update(null); 1079 } 1080 1081 @Override public void onChange(boolean selfChange, Uri uri) { 1082 update(uri); 1083 } 1084 1085 public void update(Uri uri) { 1086 ContentResolver resolver = mContext.getContentResolver(); 1087 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) { 1088 boolean pulseEnabled = Settings.System.getInt(resolver, 1089 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; 1090 if (mNotificationPulseEnabled != pulseEnabled) { 1091 mNotificationPulseEnabled = pulseEnabled; 1092 updateNotificationPulse(); 1093 } 1094 } 1095 if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) { 1096 rebindListenerServices(); 1097 } 1098 } 1099 } 1100 1101 private SettingsObserver mSettingsObserver; 1102 1103 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) { 1104 int[] ar = r.getIntArray(resid); 1105 if (ar == null) { 1106 return def; 1107 } 1108 final int len = ar.length > maxlen ? maxlen : ar.length; 1109 long[] out = new long[len]; 1110 for (int i=0; i<len; i++) { 1111 out[i] = ar[i]; 1112 } 1113 return out; 1114 } 1115 1116 NotificationManagerService(Context context, StatusBarManagerService statusBar, 1117 LightsService lights) 1118 { 1119 super(); 1120 mContext = context; 1121 mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); 1122 mAm = ActivityManagerNative.getDefault(); 1123 mUserManager = (UserManager)context.getSystemService(Context.USER_SERVICE); 1124 mToastQueue = new ArrayList<ToastRecord>(); 1125 mHandler = new WorkerHandler(); 1126 1127 mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); 1128 1129 importOldBlockDb(); 1130 1131 mStatusBar = statusBar; 1132 statusBar.setNotificationCallbacks(mNotificationCallbacks); 1133 1134 mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS); 1135 mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION); 1136 1137 Resources resources = mContext.getResources(); 1138 mDefaultNotificationColor = resources.getColor( 1139 com.android.internal.R.color.config_defaultNotificationColor); 1140 mDefaultNotificationLedOn = resources.getInteger( 1141 com.android.internal.R.integer.config_defaultNotificationLedOn); 1142 mDefaultNotificationLedOff = resources.getInteger( 1143 com.android.internal.R.integer.config_defaultNotificationLedOff); 1144 1145 mDefaultVibrationPattern = getLongArray(resources, 1146 com.android.internal.R.array.config_defaultNotificationVibePattern, 1147 VIBRATE_PATTERN_MAXLEN, 1148 DEFAULT_VIBRATE_PATTERN); 1149 1150 mFallbackVibrationPattern = getLongArray(resources, 1151 com.android.internal.R.array.config_notificationFallbackVibePattern, 1152 VIBRATE_PATTERN_MAXLEN, 1153 DEFAULT_VIBRATE_PATTERN); 1154 1155 // Don't start allowing notifications until the setup wizard has run once. 1156 // After that, including subsequent boots, init with notifications turned on. 1157 // This works on the first boot because the setup wizard will toggle this 1158 // flag at least once and we'll go back to 0 after that. 1159 if (0 == Settings.Global.getInt(mContext.getContentResolver(), 1160 Settings.Global.DEVICE_PROVISIONED, 0)) { 1161 mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS; 1162 } 1163 1164 // register for various Intents 1165 IntentFilter filter = new IntentFilter(); 1166 filter.addAction(Intent.ACTION_SCREEN_ON); 1167 filter.addAction(Intent.ACTION_SCREEN_OFF); 1168 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 1169 filter.addAction(Intent.ACTION_USER_PRESENT); 1170 filter.addAction(Intent.ACTION_USER_STOPPED); 1171 filter.addAction(Intent.ACTION_USER_SWITCHED); 1172 mContext.registerReceiver(mIntentReceiver, filter); 1173 IntentFilter pkgFilter = new IntentFilter(); 1174 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 1175 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 1176 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 1177 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 1178 pkgFilter.addDataScheme("package"); 1179 mContext.registerReceiver(mIntentReceiver, pkgFilter); 1180 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 1181 mContext.registerReceiver(mIntentReceiver, sdFilter); 1182 1183 mSettingsObserver = new SettingsObserver(mHandler); 1184 mSettingsObserver.observe(); 1185 } 1186 1187 /** 1188 * Read the old XML-based app block database and import those blockages into the AppOps system. 1189 */ 1190 private void importOldBlockDb() { 1191 loadBlockDb(); 1192 1193 PackageManager pm = mContext.getPackageManager(); 1194 for (String pkg : mBlockedPackages) { 1195 PackageInfo info = null; 1196 try { 1197 info = pm.getPackageInfo(pkg, 0); 1198 setNotificationsEnabledForPackage(pkg, info.applicationInfo.uid, false); 1199 } catch (NameNotFoundException e) { 1200 // forget you 1201 } 1202 } 1203 mBlockedPackages.clear(); 1204 if (mPolicyFile != null) { 1205 mPolicyFile.delete(); 1206 } 1207 } 1208 1209 void systemReady() { 1210 mAudioService = IAudioService.Stub.asInterface( 1211 ServiceManager.getService(Context.AUDIO_SERVICE)); 1212 1213 // no beeping until we're basically done booting 1214 mSystemReady = true; 1215 1216 // make sure our listener services are properly bound 1217 rebindListenerServices(); 1218 } 1219 1220 // Toasts 1221 // ============================================================================ 1222 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 1223 { 1224 if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration); 1225 1226 if (pkg == null || callback == null) { 1227 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 1228 return ; 1229 } 1230 1231 final boolean isSystemToast = ("android".equals(pkg)); 1232 1233 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { 1234 if (!isSystemToast) { 1235 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 1236 return; 1237 } 1238 } 1239 1240 synchronized (mToastQueue) { 1241 int callingPid = Binder.getCallingPid(); 1242 long callingId = Binder.clearCallingIdentity(); 1243 try { 1244 ToastRecord record; 1245 int index = indexOfToastLocked(pkg, callback); 1246 // If it's already in the queue, we update it in place, we don't 1247 // move it to the end of the queue. 1248 if (index >= 0) { 1249 record = mToastQueue.get(index); 1250 record.update(duration); 1251 } else { 1252 // Limit the number of toasts that any given package except the android 1253 // package can enqueue. Prevents DOS attacks and deals with leaks. 1254 if (!isSystemToast) { 1255 int count = 0; 1256 final int N = mToastQueue.size(); 1257 for (int i=0; i<N; i++) { 1258 final ToastRecord r = mToastQueue.get(i); 1259 if (r.pkg.equals(pkg)) { 1260 count++; 1261 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1262 Slog.e(TAG, "Package has already posted " + count 1263 + " toasts. Not showing more. Package=" + pkg); 1264 return; 1265 } 1266 } 1267 } 1268 } 1269 1270 record = new ToastRecord(callingPid, pkg, callback, duration); 1271 mToastQueue.add(record); 1272 index = mToastQueue.size() - 1; 1273 keepProcessAliveLocked(callingPid); 1274 } 1275 // If it's at index 0, it's the current toast. It doesn't matter if it's 1276 // new or just been updated. Call back and tell it to show itself. 1277 // If the callback fails, this will remove it from the list, so don't 1278 // assume that it's valid after this. 1279 if (index == 0) { 1280 showNextToastLocked(); 1281 } 1282 } finally { 1283 Binder.restoreCallingIdentity(callingId); 1284 } 1285 } 1286 } 1287 1288 public void cancelToast(String pkg, ITransientNotification callback) { 1289 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 1290 1291 if (pkg == null || callback == null) { 1292 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 1293 return ; 1294 } 1295 1296 synchronized (mToastQueue) { 1297 long callingId = Binder.clearCallingIdentity(); 1298 try { 1299 int index = indexOfToastLocked(pkg, callback); 1300 if (index >= 0) { 1301 cancelToastLocked(index); 1302 } else { 1303 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback); 1304 } 1305 } finally { 1306 Binder.restoreCallingIdentity(callingId); 1307 } 1308 } 1309 } 1310 1311 private void showNextToastLocked() { 1312 ToastRecord record = mToastQueue.get(0); 1313 while (record != null) { 1314 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1315 try { 1316 record.callback.show(); 1317 scheduleTimeoutLocked(record, false); 1318 return; 1319 } catch (RemoteException e) { 1320 Slog.w(TAG, "Object died trying to show notification " + record.callback 1321 + " in package " + record.pkg); 1322 // remove it from the list and let the process die 1323 int index = mToastQueue.indexOf(record); 1324 if (index >= 0) { 1325 mToastQueue.remove(index); 1326 } 1327 keepProcessAliveLocked(record.pid); 1328 if (mToastQueue.size() > 0) { 1329 record = mToastQueue.get(0); 1330 } else { 1331 record = null; 1332 } 1333 } 1334 } 1335 } 1336 1337 private void cancelToastLocked(int index) { 1338 ToastRecord record = mToastQueue.get(index); 1339 try { 1340 record.callback.hide(); 1341 } catch (RemoteException e) { 1342 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1343 + " in package " + record.pkg); 1344 // don't worry about this, we're about to remove it from 1345 // the list anyway 1346 } 1347 mToastQueue.remove(index); 1348 keepProcessAliveLocked(record.pid); 1349 if (mToastQueue.size() > 0) { 1350 // Show the next one. If the callback fails, this will remove 1351 // it from the list, so don't assume that the list hasn't changed 1352 // after this point. 1353 showNextToastLocked(); 1354 } 1355 } 1356 1357 private void scheduleTimeoutLocked(ToastRecord r, boolean immediate) 1358 { 1359 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1360 long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY); 1361 mHandler.removeCallbacksAndMessages(r); 1362 mHandler.sendMessageDelayed(m, delay); 1363 } 1364 1365 private void handleTimeout(ToastRecord record) 1366 { 1367 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1368 synchronized (mToastQueue) { 1369 int index = indexOfToastLocked(record.pkg, record.callback); 1370 if (index >= 0) { 1371 cancelToastLocked(index); 1372 } 1373 } 1374 } 1375 1376 // lock on mToastQueue 1377 private int indexOfToastLocked(String pkg, ITransientNotification callback) 1378 { 1379 IBinder cbak = callback.asBinder(); 1380 ArrayList<ToastRecord> list = mToastQueue; 1381 int len = list.size(); 1382 for (int i=0; i<len; i++) { 1383 ToastRecord r = list.get(i); 1384 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1385 return i; 1386 } 1387 } 1388 return -1; 1389 } 1390 1391 // lock on mToastQueue 1392 private void keepProcessAliveLocked(int pid) 1393 { 1394 int toastCount = 0; // toasts from this pid 1395 ArrayList<ToastRecord> list = mToastQueue; 1396 int N = list.size(); 1397 for (int i=0; i<N; i++) { 1398 ToastRecord r = list.get(i); 1399 if (r.pid == pid) { 1400 toastCount++; 1401 } 1402 } 1403 try { 1404 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 1405 } catch (RemoteException e) { 1406 // Shouldn't happen. 1407 } 1408 } 1409 1410 private final class WorkerHandler extends Handler 1411 { 1412 @Override 1413 public void handleMessage(Message msg) 1414 { 1415 switch (msg.what) 1416 { 1417 case MESSAGE_TIMEOUT: 1418 handleTimeout((ToastRecord)msg.obj); 1419 break; 1420 } 1421 } 1422 } 1423 1424 1425 // Notifications 1426 // ============================================================================ 1427 public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id, 1428 Notification notification, int[] idOut, int userId) 1429 { 1430 enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(), Binder.getCallingPid(), 1431 tag, id, notification, idOut, userId); 1432 } 1433 1434 private final static int clamp(int x, int low, int high) { 1435 return (x < low) ? low : ((x > high) ? high : x); 1436 } 1437 1438 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the 1439 // uid/pid of another application) 1440 public void enqueueNotificationInternal(String pkg, String basePkg, int callingUid, 1441 int callingPid, String tag, int id, Notification notification, int[] idOut, int userId) 1442 { 1443 if (DBG) { 1444 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification); 1445 } 1446 checkCallerIsSystemOrSameApp(pkg); 1447 final boolean isSystemNotification = ("android".equals(pkg)); 1448 1449 userId = ActivityManager.handleIncomingUser(callingPid, 1450 callingUid, userId, true, false, "enqueueNotification", pkg); 1451 final UserHandle user = new UserHandle(userId); 1452 1453 // Limit the number of notifications that any given package except the android 1454 // package can enqueue. Prevents DOS attacks and deals with leaks. 1455 if (!isSystemNotification) { 1456 synchronized (mNotificationList) { 1457 int count = 0; 1458 final int N = mNotificationList.size(); 1459 for (int i=0; i<N; i++) { 1460 final NotificationRecord r = mNotificationList.get(i); 1461 if (r.sbn.pkg.equals(pkg) && r.sbn.getUserId() == userId) { 1462 count++; 1463 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1464 Slog.e(TAG, "Package has already posted " + count 1465 + " notifications. Not showing more. package=" + pkg); 1466 return; 1467 } 1468 } 1469 } 1470 } 1471 } 1472 1473 // This conditional is a dirty hack to limit the logging done on 1474 // behalf of the download manager without affecting other apps. 1475 if (!pkg.equals("com.android.providers.downloads") 1476 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1477 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId, 1478 notification.toString()); 1479 } 1480 1481 if (pkg == null || notification == null) { 1482 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1483 + " id=" + id + " notification=" + notification); 1484 } 1485 if (notification.icon != 0) { 1486 if (notification.contentView == null) { 1487 throw new IllegalArgumentException("contentView required: pkg=" + pkg 1488 + " id=" + id + " notification=" + notification); 1489 } 1490 } 1491 1492 // === Scoring === 1493 1494 // 0. Sanitize inputs 1495 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX); 1496 // Migrate notification flags to scores 1497 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1498 if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX; 1499 } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1500 if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH; 1501 } 1502 1503 // 1. initial score: buckets of 10, around the app 1504 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] 1505 1506 // 2. Consult external heuristics (TBD) 1507 1508 // 3. Apply local rules 1509 1510 // blocked apps 1511 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1512 if (!isSystemNotification) { 1513 score = JUNK_SCORE; 1514 Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request."); 1515 } 1516 } 1517 1518 if (DBG) { 1519 Slog.v(TAG, "Assigned score=" + score + " to " + notification); 1520 } 1521 1522 if (score < SCORE_DISPLAY_THRESHOLD) { 1523 // Notification will be blocked because the score is too low. 1524 return; 1525 } 1526 1527 // Should this notification make noise, vibe, or use the LED? 1528 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD); 1529 1530 synchronized (mNotificationList) { 1531 final StatusBarNotification n = new StatusBarNotification( 1532 pkg, id, tag, callingUid, callingPid, score, notification, user); 1533 NotificationRecord r = new NotificationRecord(n); 1534 NotificationRecord old = null; 1535 1536 int index = indexOfNotificationLocked(pkg, tag, id, userId); 1537 if (index < 0) { 1538 mNotificationList.add(r); 1539 } else { 1540 old = mNotificationList.remove(index); 1541 mNotificationList.add(index, r); 1542 // Make sure we don't lose the foreground service state. 1543 if (old != null) { 1544 notification.flags |= 1545 old.getNotification().flags&Notification.FLAG_FOREGROUND_SERVICE; 1546 } 1547 } 1548 1549 // Ensure if this is a foreground service that the proper additional 1550 // flags are set. 1551 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1552 notification.flags |= Notification.FLAG_ONGOING_EVENT 1553 | Notification.FLAG_NO_CLEAR; 1554 } 1555 1556 final int currentUser; 1557 final long token = Binder.clearCallingIdentity(); 1558 try { 1559 currentUser = ActivityManager.getCurrentUser(); 1560 } finally { 1561 Binder.restoreCallingIdentity(token); 1562 } 1563 1564 if (notification.icon != 0) { 1565 if (old != null && old.statusBarKey != null) { 1566 r.statusBarKey = old.statusBarKey; 1567 long identity = Binder.clearCallingIdentity(); 1568 try { 1569 mStatusBar.updateNotification(r.statusBarKey, n); 1570 } 1571 finally { 1572 Binder.restoreCallingIdentity(identity); 1573 } 1574 } else { 1575 long identity = Binder.clearCallingIdentity(); 1576 try { 1577 r.statusBarKey = mStatusBar.addNotification(n); 1578 if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 1579 && canInterrupt) { 1580 mAttentionLight.pulse(); 1581 } 1582 } 1583 finally { 1584 Binder.restoreCallingIdentity(identity); 1585 } 1586 } 1587 // Send accessibility events only for the current user. 1588 if (currentUser == userId) { 1589 sendAccessibilityEvent(notification, pkg); 1590 } 1591 1592 notifyPostedLocked(r); 1593 } else { 1594 Slog.e(TAG, "Ignoring notification with icon==0: " + notification); 1595 if (old != null && old.statusBarKey != null) { 1596 long identity = Binder.clearCallingIdentity(); 1597 try { 1598 mStatusBar.removeNotification(old.statusBarKey); 1599 } 1600 finally { 1601 Binder.restoreCallingIdentity(identity); 1602 } 1603 1604 notifyRemovedLocked(r); 1605 } 1606 return; // do not play sounds, show lights, etc. for invalid notifications 1607 } 1608 1609 // If we're not supposed to beep, vibrate, etc. then don't. 1610 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) 1611 && (!(old != null 1612 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1613 && (r.getUserId() == UserHandle.USER_ALL || 1614 (r.getUserId() == userId && r.getUserId() == currentUser)) 1615 && canInterrupt 1616 && mSystemReady) { 1617 1618 final AudioManager audioManager = (AudioManager) mContext 1619 .getSystemService(Context.AUDIO_SERVICE); 1620 1621 // sound 1622 final boolean useDefaultSound = 1623 (notification.defaults & Notification.DEFAULT_SOUND) != 0; 1624 1625 Uri soundUri = null; 1626 boolean hasValidSound = false; 1627 1628 if (useDefaultSound) { 1629 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1630 1631 // check to see if the default notification sound is silent 1632 ContentResolver resolver = mContext.getContentResolver(); 1633 hasValidSound = Settings.System.getString(resolver, 1634 Settings.System.NOTIFICATION_SOUND) != null; 1635 } else if (notification.sound != null) { 1636 soundUri = notification.sound; 1637 hasValidSound = (soundUri != null); 1638 } 1639 1640 if (hasValidSound) { 1641 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; 1642 int audioStreamType; 1643 if (notification.audioStreamType >= 0) { 1644 audioStreamType = notification.audioStreamType; 1645 } else { 1646 audioStreamType = DEFAULT_STREAM_TYPE; 1647 } 1648 mSoundNotification = r; 1649 // do not play notifications if stream volume is 0 1650 // (typically because ringer mode is silent) or if speech recognition is active. 1651 if ((audioManager.getStreamVolume(audioStreamType) != 0) 1652 && !audioManager.isSpeechRecognitionActive()) { 1653 final long identity = Binder.clearCallingIdentity(); 1654 try { 1655 final IRingtonePlayer player = mAudioService.getRingtonePlayer(); 1656 if (player != null) { 1657 player.playAsync(soundUri, user, looping, audioStreamType); 1658 } 1659 } catch (RemoteException e) { 1660 } finally { 1661 Binder.restoreCallingIdentity(identity); 1662 } 1663 } 1664 } 1665 1666 // vibrate 1667 // Does the notification want to specify its own vibration? 1668 final boolean hasCustomVibrate = notification.vibrate != null; 1669 1670 // new in 4.2: if there was supposed to be a sound and we're in vibrate mode, 1671 // and no other vibration is specified, we fall back to vibration 1672 final boolean convertSoundToVibration = 1673 !hasCustomVibrate 1674 && hasValidSound 1675 && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE); 1676 1677 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1678 final boolean useDefaultVibrate = 1679 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1680 1681 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1682 && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) { 1683 mVibrateNotification = r; 1684 1685 if (useDefaultVibrate || convertSoundToVibration) { 1686 // Escalate privileges so we can use the vibrator even if the notifying app 1687 // does not have the VIBRATE permission. 1688 long identity = Binder.clearCallingIdentity(); 1689 try { 1690 mVibrator.vibrate(r.sbn.uid, r.sbn.basePkg, 1691 useDefaultVibrate ? mDefaultVibrationPattern 1692 : mFallbackVibrationPattern, 1693 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); 1694 } finally { 1695 Binder.restoreCallingIdentity(identity); 1696 } 1697 } else if (notification.vibrate.length > 1) { 1698 // If you want your own vibration pattern, you need the VIBRATE permission 1699 mVibrator.vibrate(r.sbn.uid, r.sbn.basePkg, notification.vibrate, 1700 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); 1701 } 1702 } 1703 } 1704 1705 // light 1706 // the most recent thing gets the light 1707 mLights.remove(old); 1708 if (mLedNotification == old) { 1709 mLedNotification = null; 1710 } 1711 //Slog.i(TAG, "notification.lights=" 1712 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0)); 1713 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 1714 && canInterrupt) { 1715 mLights.add(r); 1716 updateLightsLocked(); 1717 } else { 1718 if (old != null 1719 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) { 1720 updateLightsLocked(); 1721 } 1722 } 1723 } 1724 1725 idOut[0] = id; 1726 } 1727 1728 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 1729 AccessibilityManager manager = AccessibilityManager.getInstance(mContext); 1730 if (!manager.isEnabled()) { 1731 return; 1732 } 1733 1734 AccessibilityEvent event = 1735 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 1736 event.setPackageName(packageName); 1737 event.setClassName(Notification.class.getName()); 1738 event.setParcelableData(notification); 1739 CharSequence tickerText = notification.tickerText; 1740 if (!TextUtils.isEmpty(tickerText)) { 1741 event.getText().add(tickerText); 1742 } 1743 1744 manager.sendAccessibilityEvent(event); 1745 } 1746 1747 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) { 1748 // tell the app 1749 if (sendDelete) { 1750 if (r.getNotification().deleteIntent != null) { 1751 try { 1752 r.getNotification().deleteIntent.send(); 1753 } catch (PendingIntent.CanceledException ex) { 1754 // do nothing - there's no relevant way to recover, and 1755 // no reason to let this propagate 1756 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.pkg, ex); 1757 } 1758 } 1759 } 1760 1761 // status bar 1762 if (r.getNotification().icon != 0) { 1763 long identity = Binder.clearCallingIdentity(); 1764 try { 1765 mStatusBar.removeNotification(r.statusBarKey); 1766 } 1767 finally { 1768 Binder.restoreCallingIdentity(identity); 1769 } 1770 r.statusBarKey = null; 1771 notifyRemovedLocked(r); 1772 } 1773 1774 // sound 1775 if (mSoundNotification == r) { 1776 mSoundNotification = null; 1777 final long identity = Binder.clearCallingIdentity(); 1778 try { 1779 final IRingtonePlayer player = mAudioService.getRingtonePlayer(); 1780 if (player != null) { 1781 player.stopAsync(); 1782 } 1783 } catch (RemoteException e) { 1784 } finally { 1785 Binder.restoreCallingIdentity(identity); 1786 } 1787 } 1788 1789 // vibrate 1790 if (mVibrateNotification == r) { 1791 mVibrateNotification = null; 1792 long identity = Binder.clearCallingIdentity(); 1793 try { 1794 mVibrator.cancel(); 1795 } 1796 finally { 1797 Binder.restoreCallingIdentity(identity); 1798 } 1799 } 1800 1801 // light 1802 mLights.remove(r); 1803 if (mLedNotification == r) { 1804 mLedNotification = null; 1805 } 1806 1807 // Save it for users of getHistoricalNotifications() 1808 mArchive.record(r.sbn); 1809 } 1810 1811 /** 1812 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 1813 * and none of the {@code mustNotHaveFlags}. 1814 */ 1815 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags, 1816 int mustNotHaveFlags, boolean sendDelete, int userId) { 1817 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId, 1818 mustHaveFlags, mustNotHaveFlags); 1819 1820 synchronized (mNotificationList) { 1821 int index = indexOfNotificationLocked(pkg, tag, id, userId); 1822 if (index >= 0) { 1823 NotificationRecord r = mNotificationList.get(index); 1824 1825 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 1826 return; 1827 } 1828 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 1829 return; 1830 } 1831 1832 mNotificationList.remove(index); 1833 1834 cancelNotificationLocked(r, sendDelete); 1835 updateLightsLocked(); 1836 } 1837 } 1838 } 1839 1840 /** 1841 * Determine whether the userId applies to the notification in question, either because 1842 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 1843 */ 1844 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 1845 return 1846 // looking for USER_ALL notifications? match everything 1847 userId == UserHandle.USER_ALL 1848 // a notification sent to USER_ALL matches any query 1849 || r.getUserId() == UserHandle.USER_ALL 1850 // an exact user match 1851 || r.getUserId() == userId; 1852 } 1853 1854 /** 1855 * Cancels all notifications from a given package that have all of the 1856 * {@code mustHaveFlags}. 1857 */ 1858 boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags, 1859 int mustNotHaveFlags, boolean doit, int userId) { 1860 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, userId, 1861 mustHaveFlags, mustNotHaveFlags); 1862 1863 synchronized (mNotificationList) { 1864 final int N = mNotificationList.size(); 1865 boolean canceledSomething = false; 1866 for (int i = N-1; i >= 0; --i) { 1867 NotificationRecord r = mNotificationList.get(i); 1868 if (!notificationMatchesUserId(r, userId)) { 1869 continue; 1870 } 1871 // Don't remove notifications to all, if there's no package name specified 1872 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 1873 continue; 1874 } 1875 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 1876 continue; 1877 } 1878 if ((r.getFlags() & mustNotHaveFlags) != 0) { 1879 continue; 1880 } 1881 if (pkg != null && !r.sbn.pkg.equals(pkg)) { 1882 continue; 1883 } 1884 canceledSomething = true; 1885 if (!doit) { 1886 return true; 1887 } 1888 mNotificationList.remove(i); 1889 cancelNotificationLocked(r, false); 1890 } 1891 if (canceledSomething) { 1892 updateLightsLocked(); 1893 } 1894 return canceledSomething; 1895 } 1896 } 1897 1898 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 1899 checkCallerIsSystemOrSameApp(pkg); 1900 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1901 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 1902 // Don't allow client applications to cancel foreground service notis. 1903 cancelNotification(pkg, tag, id, 0, 1904 Binder.getCallingUid() == Process.SYSTEM_UID 1905 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId); 1906 } 1907 1908 public void cancelAllNotifications(String pkg, int userId) { 1909 checkCallerIsSystemOrSameApp(pkg); 1910 1911 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1912 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 1913 1914 // Calling from user space, don't allow the canceling of actively 1915 // running foreground services. 1916 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId); 1917 } 1918 1919 void checkCallerIsSystem() { 1920 int uid = Binder.getCallingUid(); 1921 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) { 1922 return; 1923 } 1924 throw new SecurityException("Disallowed call for uid " + uid); 1925 } 1926 1927 void checkCallerIsSystemOrSameApp(String pkg) { 1928 int uid = Binder.getCallingUid(); 1929 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) { 1930 return; 1931 } 1932 try { 1933 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 1934 pkg, 0, UserHandle.getCallingUserId()); 1935 if (!UserHandle.isSameApp(ai.uid, uid)) { 1936 throw new SecurityException("Calling uid " + uid + " gave package" 1937 + pkg + " which is owned by uid " + ai.uid); 1938 } 1939 } catch (RemoteException re) { 1940 throw new SecurityException("Unknown package " + pkg + "\n" + re); 1941 } 1942 } 1943 1944 void cancelAll(int userId) { 1945 synchronized (mNotificationList) { 1946 final int N = mNotificationList.size(); 1947 for (int i=N-1; i>=0; i--) { 1948 NotificationRecord r = mNotificationList.get(i); 1949 1950 if (!notificationMatchesUserId(r, userId)) { 1951 continue; 1952 } 1953 1954 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 1955 | Notification.FLAG_NO_CLEAR)) == 0) { 1956 mNotificationList.remove(i); 1957 cancelNotificationLocked(r, true); 1958 } 1959 } 1960 1961 updateLightsLocked(); 1962 } 1963 } 1964 1965 // lock on mNotificationList 1966 private void updateLightsLocked() 1967 { 1968 // handle notification lights 1969 if (mLedNotification == null) { 1970 // get next notification, if any 1971 int n = mLights.size(); 1972 if (n > 0) { 1973 mLedNotification = mLights.get(n-1); 1974 } 1975 } 1976 1977 // Don't flash while we are in a call or screen is on 1978 if (mLedNotification == null || mInCall || mScreenOn) { 1979 mNotificationLight.turnOff(); 1980 } else { 1981 final Notification ledno = mLedNotification.sbn.notification; 1982 int ledARGB = ledno.ledARGB; 1983 int ledOnMS = ledno.ledOnMS; 1984 int ledOffMS = ledno.ledOffMS; 1985 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 1986 ledARGB = mDefaultNotificationColor; 1987 ledOnMS = mDefaultNotificationLedOn; 1988 ledOffMS = mDefaultNotificationLedOff; 1989 } 1990 if (mNotificationPulseEnabled) { 1991 // pulse repeatedly 1992 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, 1993 ledOnMS, ledOffMS); 1994 } 1995 } 1996 } 1997 1998 // lock on mNotificationList 1999 private int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2000 { 2001 ArrayList<NotificationRecord> list = mNotificationList; 2002 final int len = list.size(); 2003 for (int i=0; i<len; i++) { 2004 NotificationRecord r = list.get(i); 2005 if (!notificationMatchesUserId(r, userId) || r.sbn.id != id) { 2006 continue; 2007 } 2008 if (tag == null) { 2009 if (r.sbn.tag != null) { 2010 continue; 2011 } 2012 } else { 2013 if (!tag.equals(r.sbn.tag)) { 2014 continue; 2015 } 2016 } 2017 if (r.sbn.pkg.equals(pkg)) { 2018 return i; 2019 } 2020 } 2021 return -1; 2022 } 2023 2024 private void updateNotificationPulse() { 2025 synchronized (mNotificationList) { 2026 updateLightsLocked(); 2027 } 2028 } 2029 2030 // ====================================================================== 2031 @Override 2032 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2033 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 2034 != PackageManager.PERMISSION_GRANTED) { 2035 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 2036 + Binder.getCallingPid() 2037 + ", uid=" + Binder.getCallingUid()); 2038 return; 2039 } 2040 2041 pw.println("Current Notification Manager state:"); 2042 2043 pw.println(" Listeners (" + mEnabledListenersForCurrentUser.size() 2044 + ") enabled for current user:"); 2045 for (ComponentName cmpt : mEnabledListenersForCurrentUser) { 2046 pw.println(" " + cmpt); 2047 } 2048 2049 pw.println(" Live listeners (" + mListeners.size() + "):"); 2050 for (NotificationListenerInfo info : mListeners) { 2051 pw.println(" " + info.component 2052 + " (user " + info.userid + "): " + info.listener 2053 + (info.isSystem?" SYSTEM":"")); 2054 } 2055 2056 int N; 2057 2058 synchronized (mToastQueue) { 2059 N = mToastQueue.size(); 2060 if (N > 0) { 2061 pw.println(" Toast Queue:"); 2062 for (int i=0; i<N; i++) { 2063 mToastQueue.get(i).dump(pw, " "); 2064 } 2065 pw.println(" "); 2066 } 2067 2068 } 2069 2070 synchronized (mNotificationList) { 2071 N = mNotificationList.size(); 2072 if (N > 0) { 2073 pw.println(" Notification List:"); 2074 for (int i=0; i<N; i++) { 2075 mNotificationList.get(i).dump(pw, " ", mContext); 2076 } 2077 pw.println(" "); 2078 } 2079 2080 N = mLights.size(); 2081 if (N > 0) { 2082 pw.println(" Lights List:"); 2083 for (int i=0; i<N; i++) { 2084 mLights.get(i).dump(pw, " ", mContext); 2085 } 2086 pw.println(" "); 2087 } 2088 2089 pw.println(" mSoundNotification=" + mSoundNotification); 2090 pw.println(" mVibrateNotification=" + mVibrateNotification); 2091 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications)); 2092 pw.println(" mSystemReady=" + mSystemReady); 2093 } 2094 } 2095} 2096