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