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