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