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