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