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