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 try { 1171 final int enabled = mContext.getPackageManager() 1172 .getApplicationEnabledSetting(pkgName); 1173 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED 1174 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { 1175 cancelNotifications = false; 1176 } 1177 } catch (IllegalArgumentException e) { 1178 // Package doesn't exist; probably racing with uninstall. 1179 // cancelNotifications is already true, so nothing to do here. 1180 if (DBG) { 1181 Slog.i(TAG, "Exception trying to look up app enabled setting", e); 1182 } 1183 } 1184 } 1185 pkgList = new String[]{pkgName}; 1186 } 1187 1188 boolean anyListenersInvolved = false; 1189 if (pkgList != null && (pkgList.length > 0)) { 1190 for (String pkgName : pkgList) { 1191 if (cancelNotifications) { 1192 cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart, 1193 UserHandle.USER_ALL); 1194 } 1195 if (mEnabledListenerPackageNames.contains(pkgName)) { 1196 anyListenersInvolved = true; 1197 } 1198 } 1199 } 1200 1201 if (anyListenersInvolved) { 1202 // if we're not replacing a package, clean up orphaned bits 1203 if (!queryReplace) { 1204 disableNonexistentListeners(); 1205 } 1206 // make sure we're still bound to any of our 1207 // listeners who may have just upgraded 1208 rebindListenerServices(); 1209 } 1210 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 1211 // Keep track of screen on/off state, but do not turn off the notification light 1212 // until user passes through the lock screen or views the notification. 1213 mScreenOn = true; 1214 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 1215 mScreenOn = false; 1216 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 1217 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals( 1218 TelephonyManager.EXTRA_STATE_OFFHOOK)); 1219 updateNotificationPulse(); 1220 } else if (action.equals(Intent.ACTION_USER_STOPPED)) { 1221 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 1222 if (userHandle >= 0) { 1223 cancelAllNotificationsInt(null, 0, 0, true, userHandle); 1224 } 1225 } else if (action.equals(Intent.ACTION_USER_PRESENT)) { 1226 // turn off LED when user passes through lock screen 1227 mNotificationLight.turnOff(); 1228 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { 1229 // reload per-user settings 1230 mSettingsObserver.update(null); 1231 } 1232 } 1233 }; 1234 1235 class SettingsObserver extends ContentObserver { 1236 private final Uri NOTIFICATION_LIGHT_PULSE_URI 1237 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); 1238 1239 private final Uri ENABLED_NOTIFICATION_LISTENERS_URI 1240 = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); 1241 1242 SettingsObserver(Handler handler) { 1243 super(handler); 1244 } 1245 1246 void observe() { 1247 ContentResolver resolver = mContext.getContentResolver(); 1248 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, 1249 false, this, UserHandle.USER_ALL); 1250 resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI, 1251 false, this, UserHandle.USER_ALL); 1252 update(null); 1253 } 1254 1255 @Override public void onChange(boolean selfChange, Uri uri) { 1256 update(uri); 1257 } 1258 1259 public void update(Uri uri) { 1260 ContentResolver resolver = mContext.getContentResolver(); 1261 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) { 1262 boolean pulseEnabled = Settings.System.getInt(resolver, 1263 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; 1264 if (mNotificationPulseEnabled != pulseEnabled) { 1265 mNotificationPulseEnabled = pulseEnabled; 1266 updateNotificationPulse(); 1267 } 1268 } 1269 if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) { 1270 rebindListenerServices(); 1271 } 1272 } 1273 } 1274 1275 private SettingsObserver mSettingsObserver; 1276 1277 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) { 1278 int[] ar = r.getIntArray(resid); 1279 if (ar == null) { 1280 return def; 1281 } 1282 final int len = ar.length > maxlen ? maxlen : ar.length; 1283 long[] out = new long[len]; 1284 for (int i=0; i<len; i++) { 1285 out[i] = ar[i]; 1286 } 1287 return out; 1288 } 1289 1290 NotificationManagerService(Context context, StatusBarManagerService statusBar, 1291 LightsService lights) 1292 { 1293 super(); 1294 mContext = context; 1295 mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); 1296 mAm = ActivityManagerNative.getDefault(); 1297 mUserManager = (UserManager)context.getSystemService(Context.USER_SERVICE); 1298 mToastQueue = new ArrayList<ToastRecord>(); 1299 mHandler = new WorkerHandler(); 1300 1301 mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); 1302 1303 importOldBlockDb(); 1304 1305 mStatusBar = statusBar; 1306 statusBar.setNotificationCallbacks(mNotificationCallbacks); 1307 1308 mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS); 1309 mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION); 1310 1311 Resources resources = mContext.getResources(); 1312 mDefaultNotificationColor = resources.getColor( 1313 R.color.config_defaultNotificationColor); 1314 mDefaultNotificationLedOn = resources.getInteger( 1315 R.integer.config_defaultNotificationLedOn); 1316 mDefaultNotificationLedOff = resources.getInteger( 1317 R.integer.config_defaultNotificationLedOff); 1318 1319 mDefaultVibrationPattern = getLongArray(resources, 1320 R.array.config_defaultNotificationVibePattern, 1321 VIBRATE_PATTERN_MAXLEN, 1322 DEFAULT_VIBRATE_PATTERN); 1323 1324 mFallbackVibrationPattern = getLongArray(resources, 1325 R.array.config_notificationFallbackVibePattern, 1326 VIBRATE_PATTERN_MAXLEN, 1327 DEFAULT_VIBRATE_PATTERN); 1328 1329 // Don't start allowing notifications until the setup wizard has run once. 1330 // After that, including subsequent boots, init with notifications turned on. 1331 // This works on the first boot because the setup wizard will toggle this 1332 // flag at least once and we'll go back to 0 after that. 1333 if (0 == Settings.Global.getInt(mContext.getContentResolver(), 1334 Settings.Global.DEVICE_PROVISIONED, 0)) { 1335 mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS; 1336 } 1337 1338 // register for various Intents 1339 IntentFilter filter = new IntentFilter(); 1340 filter.addAction(Intent.ACTION_SCREEN_ON); 1341 filter.addAction(Intent.ACTION_SCREEN_OFF); 1342 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 1343 filter.addAction(Intent.ACTION_USER_PRESENT); 1344 filter.addAction(Intent.ACTION_USER_STOPPED); 1345 filter.addAction(Intent.ACTION_USER_SWITCHED); 1346 mContext.registerReceiver(mIntentReceiver, filter); 1347 IntentFilter pkgFilter = new IntentFilter(); 1348 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 1349 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 1350 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 1351 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 1352 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 1353 pkgFilter.addDataScheme("package"); 1354 mContext.registerReceiver(mIntentReceiver, pkgFilter); 1355 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 1356 mContext.registerReceiver(mIntentReceiver, sdFilter); 1357 1358 mSettingsObserver = new SettingsObserver(mHandler); 1359 mSettingsObserver.observe(); 1360 1361 // spin up NotificationScorers 1362 String[] notificationScorerNames = resources.getStringArray( 1363 R.array.config_notificationScorers); 1364 for (String scorerName : notificationScorerNames) { 1365 try { 1366 Class<?> scorerClass = mContext.getClassLoader().loadClass(scorerName); 1367 NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance(); 1368 scorer.initialize(mContext); 1369 mScorers.add(scorer); 1370 } catch (ClassNotFoundException e) { 1371 Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e); 1372 } catch (InstantiationException e) { 1373 Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e); 1374 } catch (IllegalAccessException e) { 1375 Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e); 1376 } 1377 } 1378 } 1379 1380 /** 1381 * Read the old XML-based app block database and import those blockages into the AppOps system. 1382 */ 1383 private void importOldBlockDb() { 1384 loadBlockDb(); 1385 1386 PackageManager pm = mContext.getPackageManager(); 1387 for (String pkg : mBlockedPackages) { 1388 PackageInfo info = null; 1389 try { 1390 info = pm.getPackageInfo(pkg, 0); 1391 setNotificationsEnabledForPackage(pkg, info.applicationInfo.uid, false); 1392 } catch (NameNotFoundException e) { 1393 // forget you 1394 } 1395 } 1396 mBlockedPackages.clear(); 1397 if (mPolicyFile != null) { 1398 mPolicyFile.delete(); 1399 } 1400 } 1401 1402 void systemReady() { 1403 mAudioService = IAudioService.Stub.asInterface( 1404 ServiceManager.getService(Context.AUDIO_SERVICE)); 1405 1406 // no beeping until we're basically done booting 1407 mSystemReady = true; 1408 1409 // make sure our listener services are properly bound 1410 rebindListenerServices(); 1411 } 1412 1413 // Toasts 1414 // ============================================================================ 1415 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 1416 { 1417 if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration); 1418 1419 if (pkg == null || callback == null) { 1420 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 1421 return ; 1422 } 1423 1424 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); 1425 1426 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { 1427 if (!isSystemToast) { 1428 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 1429 return; 1430 } 1431 } 1432 1433 synchronized (mToastQueue) { 1434 int callingPid = Binder.getCallingPid(); 1435 long callingId = Binder.clearCallingIdentity(); 1436 try { 1437 ToastRecord record; 1438 int index = indexOfToastLocked(pkg, callback); 1439 // If it's already in the queue, we update it in place, we don't 1440 // move it to the end of the queue. 1441 if (index >= 0) { 1442 record = mToastQueue.get(index); 1443 record.update(duration); 1444 } else { 1445 // Limit the number of toasts that any given package except the android 1446 // package can enqueue. Prevents DOS attacks and deals with leaks. 1447 if (!isSystemToast) { 1448 int count = 0; 1449 final int N = mToastQueue.size(); 1450 for (int i=0; i<N; i++) { 1451 final ToastRecord r = mToastQueue.get(i); 1452 if (r.pkg.equals(pkg)) { 1453 count++; 1454 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1455 Slog.e(TAG, "Package has already posted " + count 1456 + " toasts. Not showing more. Package=" + pkg); 1457 return; 1458 } 1459 } 1460 } 1461 } 1462 1463 record = new ToastRecord(callingPid, pkg, callback, duration); 1464 mToastQueue.add(record); 1465 index = mToastQueue.size() - 1; 1466 keepProcessAliveLocked(callingPid); 1467 } 1468 // If it's at index 0, it's the current toast. It doesn't matter if it's 1469 // new or just been updated. Call back and tell it to show itself. 1470 // If the callback fails, this will remove it from the list, so don't 1471 // assume that it's valid after this. 1472 if (index == 0) { 1473 showNextToastLocked(); 1474 } 1475 } finally { 1476 Binder.restoreCallingIdentity(callingId); 1477 } 1478 } 1479 } 1480 1481 public void cancelToast(String pkg, ITransientNotification callback) { 1482 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 1483 1484 if (pkg == null || callback == null) { 1485 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 1486 return ; 1487 } 1488 1489 synchronized (mToastQueue) { 1490 long callingId = Binder.clearCallingIdentity(); 1491 try { 1492 int index = indexOfToastLocked(pkg, callback); 1493 if (index >= 0) { 1494 cancelToastLocked(index); 1495 } else { 1496 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback); 1497 } 1498 } finally { 1499 Binder.restoreCallingIdentity(callingId); 1500 } 1501 } 1502 } 1503 1504 private void showNextToastLocked() { 1505 ToastRecord record = mToastQueue.get(0); 1506 while (record != null) { 1507 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1508 try { 1509 record.callback.show(); 1510 scheduleTimeoutLocked(record); 1511 return; 1512 } catch (RemoteException e) { 1513 Slog.w(TAG, "Object died trying to show notification " + record.callback 1514 + " in package " + record.pkg); 1515 // remove it from the list and let the process die 1516 int index = mToastQueue.indexOf(record); 1517 if (index >= 0) { 1518 mToastQueue.remove(index); 1519 } 1520 keepProcessAliveLocked(record.pid); 1521 if (mToastQueue.size() > 0) { 1522 record = mToastQueue.get(0); 1523 } else { 1524 record = null; 1525 } 1526 } 1527 } 1528 } 1529 1530 private void cancelToastLocked(int index) { 1531 ToastRecord record = mToastQueue.get(index); 1532 try { 1533 record.callback.hide(); 1534 } catch (RemoteException e) { 1535 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1536 + " in package " + record.pkg); 1537 // don't worry about this, we're about to remove it from 1538 // the list anyway 1539 } 1540 mToastQueue.remove(index); 1541 keepProcessAliveLocked(record.pid); 1542 if (mToastQueue.size() > 0) { 1543 // Show the next one. If the callback fails, this will remove 1544 // it from the list, so don't assume that the list hasn't changed 1545 // after this point. 1546 showNextToastLocked(); 1547 } 1548 } 1549 1550 private void scheduleTimeoutLocked(ToastRecord r) 1551 { 1552 mHandler.removeCallbacksAndMessages(r); 1553 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1554 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 1555 mHandler.sendMessageDelayed(m, delay); 1556 } 1557 1558 private void handleTimeout(ToastRecord record) 1559 { 1560 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1561 synchronized (mToastQueue) { 1562 int index = indexOfToastLocked(record.pkg, record.callback); 1563 if (index >= 0) { 1564 cancelToastLocked(index); 1565 } 1566 } 1567 } 1568 1569 // lock on mToastQueue 1570 private int indexOfToastLocked(String pkg, ITransientNotification callback) 1571 { 1572 IBinder cbak = callback.asBinder(); 1573 ArrayList<ToastRecord> list = mToastQueue; 1574 int len = list.size(); 1575 for (int i=0; i<len; i++) { 1576 ToastRecord r = list.get(i); 1577 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1578 return i; 1579 } 1580 } 1581 return -1; 1582 } 1583 1584 // lock on mToastQueue 1585 private void keepProcessAliveLocked(int pid) 1586 { 1587 int toastCount = 0; // toasts from this pid 1588 ArrayList<ToastRecord> list = mToastQueue; 1589 int N = list.size(); 1590 for (int i=0; i<N; i++) { 1591 ToastRecord r = list.get(i); 1592 if (r.pid == pid) { 1593 toastCount++; 1594 } 1595 } 1596 try { 1597 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 1598 } catch (RemoteException e) { 1599 // Shouldn't happen. 1600 } 1601 } 1602 1603 private final class WorkerHandler extends Handler 1604 { 1605 @Override 1606 public void handleMessage(Message msg) 1607 { 1608 switch (msg.what) 1609 { 1610 case MESSAGE_TIMEOUT: 1611 handleTimeout((ToastRecord)msg.obj); 1612 break; 1613 } 1614 } 1615 } 1616 1617 1618 // Notifications 1619 // ============================================================================ 1620 public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id, 1621 Notification notification, int[] idOut, int userId) 1622 { 1623 enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(), Binder.getCallingPid(), 1624 tag, id, notification, idOut, userId); 1625 } 1626 1627 private final static int clamp(int x, int low, int high) { 1628 return (x < low) ? low : ((x > high) ? high : x); 1629 } 1630 1631 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the 1632 // uid/pid of another application) 1633 1634 public void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid, 1635 final int callingPid, final String tag, final int id, final Notification notification, 1636 int[] idOut, int incomingUserId) 1637 { 1638 if (DBG) { 1639 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification); 1640 } 1641 checkCallerIsSystemOrSameApp(pkg); 1642 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 1643 1644 final int userId = ActivityManager.handleIncomingUser(callingPid, 1645 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1646 final UserHandle user = new UserHandle(userId); 1647 1648 // Limit the number of notifications that any given package except the android 1649 // package can enqueue. Prevents DOS attacks and deals with leaks. 1650 if (!isSystemNotification) { 1651 synchronized (mNotificationList) { 1652 int count = 0; 1653 final int N = mNotificationList.size(); 1654 for (int i=0; i<N; i++) { 1655 final NotificationRecord r = mNotificationList.get(i); 1656 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1657 count++; 1658 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1659 Slog.e(TAG, "Package has already posted " + count 1660 + " notifications. Not showing more. package=" + pkg); 1661 return; 1662 } 1663 } 1664 } 1665 } 1666 } 1667 1668 // This conditional is a dirty hack to limit the logging done on 1669 // behalf of the download manager without affecting other apps. 1670 if (!pkg.equals("com.android.providers.downloads") 1671 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1672 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId, 1673 notification.toString()); 1674 } 1675 1676 if (pkg == null || notification == null) { 1677 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1678 + " id=" + id + " notification=" + notification); 1679 } 1680 if (notification.icon != 0) { 1681 if (notification.contentView == null) { 1682 throw new IllegalArgumentException("contentView required: pkg=" + pkg 1683 + " id=" + id + " notification=" + notification); 1684 } 1685 } 1686 1687 mHandler.post(new Runnable() { 1688 @Override 1689 public void run() { 1690 1691 // === Scoring === 1692 1693 // 0. Sanitize inputs 1694 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1695 Notification.PRIORITY_MAX); 1696 // Migrate notification flags to scores 1697 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1698 if (notification.priority < Notification.PRIORITY_MAX) { 1699 notification.priority = Notification.PRIORITY_MAX; 1700 } 1701 } else if (SCORE_ONGOING_HIGHER && 1702 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1703 if (notification.priority < Notification.PRIORITY_HIGH) { 1704 notification.priority = Notification.PRIORITY_HIGH; 1705 } 1706 } 1707 1708 // 1. initial score: buckets of 10, around the app 1709 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] 1710 1711 // 2. Consult external heuristics (TBD) 1712 1713 // 3. Apply local rules 1714 1715 int initialScore = score; 1716 if (!mScorers.isEmpty()) { 1717 if (DBG) Slog.v(TAG, "Initial score is " + score + "."); 1718 for (NotificationScorer scorer : mScorers) { 1719 try { 1720 score = scorer.getScore(notification, score); 1721 } catch (Throwable t) { 1722 Slog.w(TAG, "Scorer threw on .getScore.", t); 1723 } 1724 } 1725 if (DBG) Slog.v(TAG, "Final score is " + score + "."); 1726 } 1727 1728 // add extra to indicate score modified by NotificationScorer 1729 notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED, 1730 score != initialScore); 1731 1732 // blocked apps 1733 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1734 if (!isSystemNotification) { 1735 score = JUNK_SCORE; 1736 Slog.e(TAG, "Suppressing notification from package " + pkg 1737 + " by user request."); 1738 } 1739 } 1740 1741 if (DBG) { 1742 Slog.v(TAG, "Assigned score=" + score + " to " + notification); 1743 } 1744 1745 if (score < SCORE_DISPLAY_THRESHOLD) { 1746 // Notification will be blocked because the score is too low. 1747 return; 1748 } 1749 1750 // Should this notification make noise, vibe, or use the LED? 1751 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD); 1752 1753 synchronized (mNotificationList) { 1754 final StatusBarNotification n = new StatusBarNotification( 1755 pkg, id, tag, callingUid, callingPid, score, notification, user); 1756 NotificationRecord r = new NotificationRecord(n); 1757 NotificationRecord old = null; 1758 1759 int index = indexOfNotificationLocked(pkg, tag, id, userId); 1760 if (index < 0) { 1761 mNotificationList.add(r); 1762 } else { 1763 old = mNotificationList.remove(index); 1764 mNotificationList.add(index, r); 1765 // Make sure we don't lose the foreground service state. 1766 if (old != null) { 1767 notification.flags |= 1768 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1769 } 1770 } 1771 1772 // Ensure if this is a foreground service that the proper additional 1773 // flags are set. 1774 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1775 notification.flags |= Notification.FLAG_ONGOING_EVENT 1776 | Notification.FLAG_NO_CLEAR; 1777 } 1778 1779 final int currentUser; 1780 final long token = Binder.clearCallingIdentity(); 1781 try { 1782 currentUser = ActivityManager.getCurrentUser(); 1783 } finally { 1784 Binder.restoreCallingIdentity(token); 1785 } 1786 1787 if (notification.icon != 0) { 1788 if (old != null && old.statusBarKey != null) { 1789 r.statusBarKey = old.statusBarKey; 1790 long identity = Binder.clearCallingIdentity(); 1791 try { 1792 mStatusBar.updateNotification(r.statusBarKey, n); 1793 } 1794 finally { 1795 Binder.restoreCallingIdentity(identity); 1796 } 1797 } else { 1798 long identity = Binder.clearCallingIdentity(); 1799 try { 1800 r.statusBarKey = mStatusBar.addNotification(n); 1801 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0 1802 && canInterrupt) { 1803 mAttentionLight.pulse(); 1804 } 1805 } 1806 finally { 1807 Binder.restoreCallingIdentity(identity); 1808 } 1809 } 1810 // Send accessibility events only for the current user. 1811 if (currentUser == userId) { 1812 sendAccessibilityEvent(notification, pkg); 1813 } 1814 1815 notifyPostedLocked(r); 1816 } else { 1817 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1818 if (old != null && old.statusBarKey != null) { 1819 long identity = Binder.clearCallingIdentity(); 1820 try { 1821 mStatusBar.removeNotification(old.statusBarKey); 1822 } 1823 finally { 1824 Binder.restoreCallingIdentity(identity); 1825 } 1826 1827 notifyRemovedLocked(r); 1828 } 1829 // ATTENTION: in a future release we will bail out here 1830 // so that we do not play sounds, show lights, etc. for invalid notifications 1831 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1832 + n.getPackageName()); 1833 } 1834 1835 // If we're not supposed to beep, vibrate, etc. then don't. 1836 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) 1837 && (!(old != null 1838 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1839 && (r.getUserId() == UserHandle.USER_ALL || 1840 (r.getUserId() == userId && r.getUserId() == currentUser)) 1841 && canInterrupt 1842 && mSystemReady) { 1843 1844 final AudioManager audioManager = (AudioManager) mContext 1845 .getSystemService(Context.AUDIO_SERVICE); 1846 1847 // sound 1848 1849 // should we use the default notification sound? (indicated either by 1850 // DEFAULT_SOUND or because notification.sound is pointing at 1851 // Settings.System.NOTIFICATION_SOUND) 1852 final boolean useDefaultSound = 1853 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1854 Settings.System.DEFAULT_NOTIFICATION_URI 1855 .equals(notification.sound); 1856 1857 Uri soundUri = null; 1858 boolean hasValidSound = false; 1859 1860 if (useDefaultSound) { 1861 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1862 1863 // check to see if the default notification sound is silent 1864 ContentResolver resolver = mContext.getContentResolver(); 1865 hasValidSound = Settings.System.getString(resolver, 1866 Settings.System.NOTIFICATION_SOUND) != null; 1867 } else if (notification.sound != null) { 1868 soundUri = notification.sound; 1869 hasValidSound = (soundUri != null); 1870 } 1871 1872 if (hasValidSound) { 1873 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; 1874 int audioStreamType; 1875 if (notification.audioStreamType >= 0) { 1876 audioStreamType = notification.audioStreamType; 1877 } else { 1878 audioStreamType = DEFAULT_STREAM_TYPE; 1879 } 1880 mSoundNotification = r; 1881 // do not play notifications if stream volume is 0 (typically because 1882 // ringer mode is silent) or if there is a user of exclusive audio focus 1883 if ((audioManager.getStreamVolume(audioStreamType) != 0) 1884 && !audioManager.isAudioFocusExclusive()) { 1885 final long identity = Binder.clearCallingIdentity(); 1886 try { 1887 final IRingtonePlayer player = mAudioService.getRingtonePlayer(); 1888 if (player != null) { 1889 player.playAsync(soundUri, user, looping, audioStreamType); 1890 } 1891 } catch (RemoteException e) { 1892 } finally { 1893 Binder.restoreCallingIdentity(identity); 1894 } 1895 } 1896 } 1897 1898 // vibrate 1899 // Does the notification want to specify its own vibration? 1900 final boolean hasCustomVibrate = notification.vibrate != null; 1901 1902 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1903 // mode, and no other vibration is specified, we fall back to vibration 1904 final boolean convertSoundToVibration = 1905 !hasCustomVibrate 1906 && hasValidSound 1907 && (audioManager.getRingerMode() 1908 == AudioManager.RINGER_MODE_VIBRATE); 1909 1910 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1911 final boolean useDefaultVibrate = 1912 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1913 1914 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1915 && !(audioManager.getRingerMode() 1916 == AudioManager.RINGER_MODE_SILENT)) { 1917 mVibrateNotification = r; 1918 1919 if (useDefaultVibrate || convertSoundToVibration) { 1920 // Escalate privileges so we can use the vibrator even if the 1921 // notifying app does not have the VIBRATE permission. 1922 long identity = Binder.clearCallingIdentity(); 1923 try { 1924 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(), 1925 useDefaultVibrate ? mDefaultVibrationPattern 1926 : mFallbackVibrationPattern, 1927 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1928 ? 0: -1); 1929 } finally { 1930 Binder.restoreCallingIdentity(identity); 1931 } 1932 } else if (notification.vibrate.length > 1) { 1933 // If you want your own vibration pattern, you need the VIBRATE 1934 // permission 1935 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(), 1936 notification.vibrate, 1937 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1938 ? 0: -1); 1939 } 1940 } 1941 } 1942 1943 // light 1944 // the most recent thing gets the light 1945 mLights.remove(old); 1946 if (mLedNotification == old) { 1947 mLedNotification = null; 1948 } 1949 //Slog.i(TAG, "notification.lights=" 1950 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) 1951 // != 0)); 1952 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 1953 && canInterrupt) { 1954 mLights.add(r); 1955 updateLightsLocked(); 1956 } else { 1957 if (old != null 1958 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) { 1959 updateLightsLocked(); 1960 } 1961 } 1962 } 1963 } 1964 }); 1965 1966 idOut[0] = id; 1967 } 1968 1969 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 1970 AccessibilityManager manager = AccessibilityManager.getInstance(mContext); 1971 if (!manager.isEnabled()) { 1972 return; 1973 } 1974 1975 AccessibilityEvent event = 1976 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 1977 event.setPackageName(packageName); 1978 event.setClassName(Notification.class.getName()); 1979 event.setParcelableData(notification); 1980 CharSequence tickerText = notification.tickerText; 1981 if (!TextUtils.isEmpty(tickerText)) { 1982 event.getText().add(tickerText); 1983 } 1984 1985 manager.sendAccessibilityEvent(event); 1986 } 1987 1988 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) { 1989 // tell the app 1990 if (sendDelete) { 1991 if (r.getNotification().deleteIntent != null) { 1992 try { 1993 r.getNotification().deleteIntent.send(); 1994 } catch (PendingIntent.CanceledException ex) { 1995 // do nothing - there's no relevant way to recover, and 1996 // no reason to let this propagate 1997 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 1998 } 1999 } 2000 } 2001 2002 // status bar 2003 if (r.getNotification().icon != 0) { 2004 long identity = Binder.clearCallingIdentity(); 2005 try { 2006 mStatusBar.removeNotification(r.statusBarKey); 2007 } 2008 finally { 2009 Binder.restoreCallingIdentity(identity); 2010 } 2011 r.statusBarKey = null; 2012 notifyRemovedLocked(r); 2013 } 2014 2015 // sound 2016 if (mSoundNotification == r) { 2017 mSoundNotification = null; 2018 final long identity = Binder.clearCallingIdentity(); 2019 try { 2020 final IRingtonePlayer player = mAudioService.getRingtonePlayer(); 2021 if (player != null) { 2022 player.stopAsync(); 2023 } 2024 } catch (RemoteException e) { 2025 } finally { 2026 Binder.restoreCallingIdentity(identity); 2027 } 2028 } 2029 2030 // vibrate 2031 if (mVibrateNotification == r) { 2032 mVibrateNotification = null; 2033 long identity = Binder.clearCallingIdentity(); 2034 try { 2035 mVibrator.cancel(); 2036 } 2037 finally { 2038 Binder.restoreCallingIdentity(identity); 2039 } 2040 } 2041 2042 // light 2043 mLights.remove(r); 2044 if (mLedNotification == r) { 2045 mLedNotification = null; 2046 } 2047 2048 // Save it for users of getHistoricalNotifications() 2049 mArchive.record(r.sbn); 2050 } 2051 2052 /** 2053 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2054 * and none of the {@code mustNotHaveFlags}. 2055 */ 2056 private void cancelNotification(final String pkg, final String tag, final int id, 2057 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2058 final int userId) { 2059 // In enqueueNotificationInternal notifications are added by scheduling the 2060 // work on the worker handler. Hence, we also schedule the cancel on this 2061 // handler to avoid a scenario where an add notification call followed by a 2062 // remove notification call ends up in not removing the notification. 2063 mHandler.post(new Runnable() { 2064 @Override 2065 public void run() { 2066 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId, 2067 mustHaveFlags, mustNotHaveFlags); 2068 2069 synchronized (mNotificationList) { 2070 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2071 if (index >= 0) { 2072 NotificationRecord r = mNotificationList.get(index); 2073 2074 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2075 return; 2076 } 2077 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2078 return; 2079 } 2080 2081 mNotificationList.remove(index); 2082 2083 cancelNotificationLocked(r, sendDelete); 2084 updateLightsLocked(); 2085 } 2086 } 2087 } 2088 }); 2089 } 2090 2091 /** 2092 * Determine whether the userId applies to the notification in question, either because 2093 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2094 */ 2095 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2096 return 2097 // looking for USER_ALL notifications? match everything 2098 userId == UserHandle.USER_ALL 2099 // a notification sent to USER_ALL matches any query 2100 || r.getUserId() == UserHandle.USER_ALL 2101 // an exact user match 2102 || r.getUserId() == userId; 2103 } 2104 2105 /** 2106 * Cancels all notifications from a given package that have all of the 2107 * {@code mustHaveFlags}. 2108 */ 2109 boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags, 2110 int mustNotHaveFlags, boolean doit, int userId) { 2111 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, userId, 2112 mustHaveFlags, mustNotHaveFlags); 2113 2114 synchronized (mNotificationList) { 2115 final int N = mNotificationList.size(); 2116 boolean canceledSomething = false; 2117 for (int i = N-1; i >= 0; --i) { 2118 NotificationRecord r = mNotificationList.get(i); 2119 if (!notificationMatchesUserId(r, userId)) { 2120 continue; 2121 } 2122 // Don't remove notifications to all, if there's no package name specified 2123 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2124 continue; 2125 } 2126 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2127 continue; 2128 } 2129 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2130 continue; 2131 } 2132 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2133 continue; 2134 } 2135 canceledSomething = true; 2136 if (!doit) { 2137 return true; 2138 } 2139 mNotificationList.remove(i); 2140 cancelNotificationLocked(r, false); 2141 } 2142 if (canceledSomething) { 2143 updateLightsLocked(); 2144 } 2145 return canceledSomething; 2146 } 2147 } 2148 2149 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 2150 checkCallerIsSystemOrSameApp(pkg); 2151 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 2152 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 2153 // Don't allow client applications to cancel foreground service notis. 2154 cancelNotification(pkg, tag, id, 0, 2155 Binder.getCallingUid() == Process.SYSTEM_UID 2156 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId); 2157 } 2158 2159 public void cancelAllNotifications(String pkg, int userId) { 2160 checkCallerIsSystemOrSameApp(pkg); 2161 2162 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 2163 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 2164 2165 // Calling from user space, don't allow the canceling of actively 2166 // running foreground services. 2167 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId); 2168 } 2169 2170 // Return true if the UID is a system or phone UID and therefore should not have 2171 // any notifications or toasts blocked. 2172 boolean isUidSystem(int uid) { 2173 final int appid = UserHandle.getAppId(uid); 2174 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2175 } 2176 2177 // same as isUidSystem(int, int) for the Binder caller's UID. 2178 boolean isCallerSystem() { 2179 return isUidSystem(Binder.getCallingUid()); 2180 } 2181 2182 void checkCallerIsSystem() { 2183 if (isCallerSystem()) { 2184 return; 2185 } 2186 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2187 } 2188 2189 void checkCallerIsSystemOrSameApp(String pkg) { 2190 if (isCallerSystem()) { 2191 return; 2192 } 2193 final int uid = Binder.getCallingUid(); 2194 try { 2195 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2196 pkg, 0, UserHandle.getCallingUserId()); 2197 if (!UserHandle.isSameApp(ai.uid, uid)) { 2198 throw new SecurityException("Calling uid " + uid + " gave package" 2199 + pkg + " which is owned by uid " + ai.uid); 2200 } 2201 } catch (RemoteException re) { 2202 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2203 } 2204 } 2205 2206 void cancelAll(int userId) { 2207 synchronized (mNotificationList) { 2208 final int N = mNotificationList.size(); 2209 for (int i=N-1; i>=0; i--) { 2210 NotificationRecord r = mNotificationList.get(i); 2211 2212 if (!notificationMatchesUserId(r, userId)) { 2213 continue; 2214 } 2215 2216 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2217 | Notification.FLAG_NO_CLEAR)) == 0) { 2218 mNotificationList.remove(i); 2219 cancelNotificationLocked(r, true); 2220 } 2221 } 2222 2223 updateLightsLocked(); 2224 } 2225 } 2226 2227 // lock on mNotificationList 2228 private void updateLightsLocked() 2229 { 2230 // handle notification lights 2231 if (mLedNotification == null) { 2232 // get next notification, if any 2233 int n = mLights.size(); 2234 if (n > 0) { 2235 mLedNotification = mLights.get(n-1); 2236 } 2237 } 2238 2239 // Don't flash while we are in a call or screen is on 2240 if (mLedNotification == null || mInCall || mScreenOn) { 2241 mNotificationLight.turnOff(); 2242 } else { 2243 final Notification ledno = mLedNotification.sbn.getNotification(); 2244 int ledARGB = ledno.ledARGB; 2245 int ledOnMS = ledno.ledOnMS; 2246 int ledOffMS = ledno.ledOffMS; 2247 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2248 ledARGB = mDefaultNotificationColor; 2249 ledOnMS = mDefaultNotificationLedOn; 2250 ledOffMS = mDefaultNotificationLedOff; 2251 } 2252 if (mNotificationPulseEnabled) { 2253 // pulse repeatedly 2254 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, 2255 ledOnMS, ledOffMS); 2256 } 2257 } 2258 } 2259 2260 // lock on mNotificationList 2261 private int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2262 { 2263 ArrayList<NotificationRecord> list = mNotificationList; 2264 final int len = list.size(); 2265 for (int i=0; i<len; i++) { 2266 NotificationRecord r = list.get(i); 2267 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2268 continue; 2269 } 2270 if (tag == null) { 2271 if (r.sbn.getTag() != null) { 2272 continue; 2273 } 2274 } else { 2275 if (!tag.equals(r.sbn.getTag())) { 2276 continue; 2277 } 2278 } 2279 if (r.sbn.getPackageName().equals(pkg)) { 2280 return i; 2281 } 2282 } 2283 return -1; 2284 } 2285 2286 private void updateNotificationPulse() { 2287 synchronized (mNotificationList) { 2288 updateLightsLocked(); 2289 } 2290 } 2291 2292 // ====================================================================== 2293 @Override 2294 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2295 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 2296 != PackageManager.PERMISSION_GRANTED) { 2297 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 2298 + Binder.getCallingPid() 2299 + ", uid=" + Binder.getCallingUid()); 2300 return; 2301 } 2302 2303 pw.println("Current Notification Manager state:"); 2304 2305 pw.println(" Listeners (" + mEnabledListenersForCurrentUser.size() 2306 + ") enabled for current user:"); 2307 for (ComponentName cmpt : mEnabledListenersForCurrentUser) { 2308 pw.println(" " + cmpt); 2309 } 2310 2311 pw.println(" Live listeners (" + mListeners.size() + "):"); 2312 for (NotificationListenerInfo info : mListeners) { 2313 pw.println(" " + info.component 2314 + " (user " + info.userid + "): " + info.listener 2315 + (info.isSystem?" SYSTEM":"")); 2316 } 2317 2318 int N; 2319 2320 synchronized (mToastQueue) { 2321 N = mToastQueue.size(); 2322 if (N > 0) { 2323 pw.println(" Toast Queue:"); 2324 for (int i=0; i<N; i++) { 2325 mToastQueue.get(i).dump(pw, " "); 2326 } 2327 pw.println(" "); 2328 } 2329 2330 } 2331 2332 synchronized (mNotificationList) { 2333 N = mNotificationList.size(); 2334 if (N > 0) { 2335 pw.println(" Notification List:"); 2336 for (int i=0; i<N; i++) { 2337 mNotificationList.get(i).dump(pw, " ", mContext); 2338 } 2339 pw.println(" "); 2340 } 2341 2342 N = mLights.size(); 2343 if (N > 0) { 2344 pw.println(" Lights List:"); 2345 for (int i=0; i<N; i++) { 2346 pw.println(" " + mLights.get(i)); 2347 } 2348 pw.println(" "); 2349 } 2350 2351 pw.println(" mSoundNotification=" + mSoundNotification); 2352 pw.println(" mVibrateNotification=" + mVibrateNotification); 2353 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications)); 2354 pw.println(" mSystemReady=" + mSystemReady); 2355 pw.println(" mArchive=" + mArchive.toString()); 2356 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 2357 int i=0; 2358 while (iter.hasNext()) { 2359 pw.println(" " + iter.next()); 2360 if (++i >= 5) { 2361 if (iter.hasNext()) pw.println(" ..."); 2362 break; 2363 } 2364 } 2365 2366 } 2367 } 2368} 2369