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