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