NotificationManagerService.java revision eeb397b77f249045b02fe3014479a9c1bf001d0d
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 1246 // spin up NotificationScorers 1247 String[] notificationScorerNames = resources.getStringArray( 1248 R.array.config_notificationScorers); 1249 for (String scorerName : notificationScorerNames) { 1250 try { 1251 Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName); 1252 NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance(); 1253 scorer.initialize(getContext()); 1254 mScorers.add(scorer); 1255 } catch (ClassNotFoundException e) { 1256 Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e); 1257 } catch (InstantiationException e) { 1258 Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e); 1259 } catch (IllegalAccessException e) { 1260 Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e); 1261 } 1262 } 1263 1264 publishBinderService(Context.NOTIFICATION_SERVICE, mService); 1265 publishLocalService(NotificationManagerInternal.class, mInternalService); 1266 } 1267 1268 /** 1269 * Read the old XML-based app block database and import those blockages into the AppOps system. 1270 */ 1271 private void importOldBlockDb() { 1272 loadBlockDb(); 1273 1274 PackageManager pm = getContext().getPackageManager(); 1275 for (String pkg : mBlockedPackages) { 1276 PackageInfo info = null; 1277 try { 1278 info = pm.getPackageInfo(pkg, 0); 1279 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false); 1280 } catch (NameNotFoundException e) { 1281 // forget you 1282 } 1283 } 1284 mBlockedPackages.clear(); 1285 if (mPolicyFile != null) { 1286 mPolicyFile.delete(); 1287 } 1288 } 1289 1290 @Override 1291 public void onBootPhase(int phase) { 1292 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 1293 // no beeping until we're basically done booting 1294 mSystemReady = true; 1295 1296 // Grab our optional AudioService 1297 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 1298 1299 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 1300 // This observer will force an update when observe is called, causing us to 1301 // bind to listener services. 1302 mSettingsObserver.observe(); 1303 } 1304 } 1305 1306 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) { 1307 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); 1308 1309 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, 1310 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 1311 1312 // Now, cancel any outstanding notifications that are part of a just-disabled app 1313 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { 1314 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid), 1315 REASON_PACKAGE_BANNED, null); 1316 } 1317 } 1318 1319 private final IBinder mService = new INotificationManager.Stub() { 1320 // Toasts 1321 // ============================================================================ 1322 1323 @Override 1324 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 1325 { 1326 if (DBG) { 1327 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback 1328 + " duration=" + duration); 1329 } 1330 1331 if (pkg == null || callback == null) { 1332 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 1333 return ; 1334 } 1335 1336 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); 1337 1338 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { 1339 if (!isSystemToast) { 1340 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 1341 return; 1342 } 1343 } 1344 1345 synchronized (mToastQueue) { 1346 int callingPid = Binder.getCallingPid(); 1347 long callingId = Binder.clearCallingIdentity(); 1348 try { 1349 ToastRecord record; 1350 int index = indexOfToastLocked(pkg, callback); 1351 // If it's already in the queue, we update it in place, we don't 1352 // move it to the end of the queue. 1353 if (index >= 0) { 1354 record = mToastQueue.get(index); 1355 record.update(duration); 1356 } else { 1357 // Limit the number of toasts that any given package except the android 1358 // package can enqueue. Prevents DOS attacks and deals with leaks. 1359 if (!isSystemToast) { 1360 int count = 0; 1361 final int N = mToastQueue.size(); 1362 for (int i=0; i<N; i++) { 1363 final ToastRecord r = mToastQueue.get(i); 1364 if (r.pkg.equals(pkg)) { 1365 count++; 1366 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1367 Slog.e(TAG, "Package has already posted " + count 1368 + " toasts. Not showing more. Package=" + pkg); 1369 return; 1370 } 1371 } 1372 } 1373 } 1374 1375 record = new ToastRecord(callingPid, pkg, callback, duration); 1376 mToastQueue.add(record); 1377 index = mToastQueue.size() - 1; 1378 keepProcessAliveLocked(callingPid); 1379 } 1380 // If it's at index 0, it's the current toast. It doesn't matter if it's 1381 // new or just been updated. Call back and tell it to show itself. 1382 // If the callback fails, this will remove it from the list, so don't 1383 // assume that it's valid after this. 1384 if (index == 0) { 1385 showNextToastLocked(); 1386 } 1387 } finally { 1388 Binder.restoreCallingIdentity(callingId); 1389 } 1390 } 1391 } 1392 1393 @Override 1394 public void cancelToast(String pkg, ITransientNotification callback) { 1395 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 1396 1397 if (pkg == null || callback == null) { 1398 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 1399 return ; 1400 } 1401 1402 synchronized (mToastQueue) { 1403 long callingId = Binder.clearCallingIdentity(); 1404 try { 1405 int index = indexOfToastLocked(pkg, callback); 1406 if (index >= 0) { 1407 cancelToastLocked(index); 1408 } else { 1409 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg 1410 + " callback=" + callback); 1411 } 1412 } finally { 1413 Binder.restoreCallingIdentity(callingId); 1414 } 1415 } 1416 } 1417 1418 @Override 1419 public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id, 1420 Notification notification, int[] idOut, int userId) throws RemoteException { 1421 enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(), 1422 Binder.getCallingPid(), tag, id, notification, idOut, userId); 1423 } 1424 1425 @Override 1426 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 1427 checkCallerIsSystemOrSameApp(pkg); 1428 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1429 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 1430 // Don't allow client applications to cancel foreground service notis. 1431 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0, 1432 Binder.getCallingUid() == Process.SYSTEM_UID 1433 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL, 1434 null); 1435 } 1436 1437 @Override 1438 public void cancelAllNotifications(String pkg, int userId) { 1439 checkCallerIsSystemOrSameApp(pkg); 1440 1441 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1442 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 1443 1444 // Calling from user space, don't allow the canceling of actively 1445 // running foreground services. 1446 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(), 1447 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId, 1448 REASON_NOMAN_CANCEL_ALL, null); 1449 } 1450 1451 @Override 1452 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { 1453 checkCallerIsSystem(); 1454 1455 setNotificationsEnabledForPackageImpl(pkg, uid, enabled); 1456 } 1457 1458 /** 1459 * Use this when you just want to know if notifications are OK for this package. 1460 */ 1461 @Override 1462 public boolean areNotificationsEnabledForPackage(String pkg, int uid) { 1463 checkCallerIsSystem(); 1464 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 1465 == AppOpsManager.MODE_ALLOWED); 1466 } 1467 1468 /** 1469 * System-only API for getting a list of current (i.e. not cleared) notifications. 1470 * 1471 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1472 */ 1473 @Override 1474 public StatusBarNotification[] getActiveNotifications(String callingPkg) { 1475 // enforce() will ensure the calling uid has the correct permission 1476 getContext().enforceCallingOrSelfPermission( 1477 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1478 "NotificationManagerService.getActiveNotifications"); 1479 1480 StatusBarNotification[] tmp = null; 1481 int uid = Binder.getCallingUid(); 1482 1483 // noteOp will check to make sure the callingPkg matches the uid 1484 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1485 == AppOpsManager.MODE_ALLOWED) { 1486 synchronized (mNotificationList) { 1487 tmp = new StatusBarNotification[mNotificationList.size()]; 1488 final int N = mNotificationList.size(); 1489 for (int i=0; i<N; i++) { 1490 tmp[i] = mNotificationList.get(i).sbn; 1491 } 1492 } 1493 } 1494 return tmp; 1495 } 1496 1497 /** 1498 * System-only API for getting a list of recent (cleared, no longer shown) notifications. 1499 * 1500 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1501 */ 1502 @Override 1503 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { 1504 // enforce() will ensure the calling uid has the correct permission 1505 getContext().enforceCallingOrSelfPermission( 1506 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1507 "NotificationManagerService.getHistoricalNotifications"); 1508 1509 StatusBarNotification[] tmp = null; 1510 int uid = Binder.getCallingUid(); 1511 1512 // noteOp will check to make sure the callingPkg matches the uid 1513 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1514 == AppOpsManager.MODE_ALLOWED) { 1515 synchronized (mArchive) { 1516 tmp = mArchive.getArray(count); 1517 } 1518 } 1519 return tmp; 1520 } 1521 1522 /** 1523 * Register a listener binder directly with the notification manager. 1524 * 1525 * Only works with system callers. Apps should extend 1526 * {@link android.service.notification.NotificationListenerService}. 1527 */ 1528 @Override 1529 public void registerListener(final INotificationListener listener, 1530 final ComponentName component, final int userid) { 1531 checkCallerIsSystem(); 1532 checkNullListener(listener); 1533 registerListenerImpl(listener, component, userid); 1534 } 1535 1536 /** 1537 * Remove a listener binder directly 1538 */ 1539 @Override 1540 public void unregisterListener(INotificationListener listener, int userid) { 1541 checkNullListener(listener); 1542 // no need to check permissions; if your listener binder is in the list, 1543 // that's proof that you had permission to add it in the first place 1544 unregisterListenerImpl(listener, userid); 1545 } 1546 1547 /** 1548 * Allow an INotificationListener to simulate a "clear all" operation. 1549 * 1550 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} 1551 * 1552 * @param token The binder for the listener, to check that the caller is allowed 1553 */ 1554 @Override 1555 public void cancelAllNotificationsFromListener(INotificationListener token) { 1556 NotificationListenerInfo info = checkListenerToken(token); 1557 final int callingUid = Binder.getCallingUid(); 1558 final int callingPid = Binder.getCallingPid(); 1559 long identity = Binder.clearCallingIdentity(); 1560 try { 1561 cancelAll(callingUid, callingPid, info.userid, 1562 REASON_LISTENER_CANCEL_ALL, info); 1563 } finally { 1564 Binder.restoreCallingIdentity(identity); 1565 } 1566 } 1567 1568 /** 1569 * Allow an INotificationListener to simulate clearing (dismissing) a single notification. 1570 * 1571 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} 1572 * 1573 * @param token The binder for the listener, to check that the caller is allowed 1574 */ 1575 @Override 1576 public void cancelNotificationFromListener(INotificationListener token, String pkg, 1577 String tag, int id) { 1578 NotificationListenerInfo info = checkListenerToken(token); 1579 final int callingUid = Binder.getCallingUid(); 1580 final int callingPid = Binder.getCallingPid(); 1581 long identity = Binder.clearCallingIdentity(); 1582 try { 1583 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 1584 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 1585 true, 1586 info.userid, REASON_LISTENER_CANCEL, info); 1587 } finally { 1588 Binder.restoreCallingIdentity(identity); 1589 } 1590 } 1591 1592 /** 1593 * Allow an INotificationListener to request the list of outstanding notifications seen by 1594 * the current user. Useful when starting up, after which point the listener callbacks 1595 * should be used. 1596 * 1597 * @param token The binder for the listener, to check that the caller is allowed 1598 */ 1599 @Override 1600 public StatusBarNotification[] getActiveNotificationsFromListener( 1601 INotificationListener token) { 1602 NotificationListenerInfo info = checkListenerToken(token); 1603 1604 StatusBarNotification[] result = new StatusBarNotification[0]; 1605 ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>(); 1606 synchronized (mNotificationList) { 1607 final int N = mNotificationList.size(); 1608 for (int i=0; i<N; i++) { 1609 StatusBarNotification sbn = mNotificationList.get(i).sbn; 1610 if (info.enabledAndUserMatches(sbn)) { 1611 list.add(sbn); 1612 } 1613 } 1614 } 1615 return list.toArray(result); 1616 } 1617 1618 @Override 1619 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1620 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1621 != PackageManager.PERMISSION_GRANTED) { 1622 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1623 + Binder.getCallingPid() 1624 + ", uid=" + Binder.getCallingUid()); 1625 return; 1626 } 1627 1628 dumpImpl(pw); 1629 } 1630 }; 1631 1632 void dumpImpl(PrintWriter pw) { 1633 pw.println("Current Notification Manager state:"); 1634 1635 pw.println(" Listeners (" + mEnabledListenersForCurrentUser.size() 1636 + ") enabled for current user:"); 1637 for (ComponentName cmpt : mEnabledListenersForCurrentUser) { 1638 pw.println(" " + cmpt); 1639 } 1640 1641 pw.println(" Live listeners (" + mListeners.size() + "):"); 1642 for (NotificationListenerInfo info : mListeners) { 1643 pw.println(" " + info.component 1644 + " (user " + info.userid + "): " + info.listener 1645 + (info.isSystem?" SYSTEM":"")); 1646 } 1647 1648 int N; 1649 1650 synchronized (mToastQueue) { 1651 N = mToastQueue.size(); 1652 if (N > 0) { 1653 pw.println(" Toast Queue:"); 1654 for (int i=0; i<N; i++) { 1655 mToastQueue.get(i).dump(pw, " "); 1656 } 1657 pw.println(" "); 1658 } 1659 1660 } 1661 1662 synchronized (mNotificationList) { 1663 N = mNotificationList.size(); 1664 if (N > 0) { 1665 pw.println(" Notification List:"); 1666 for (int i=0; i<N; i++) { 1667 mNotificationList.get(i).dump(pw, " ", getContext()); 1668 } 1669 pw.println(" "); 1670 } 1671 1672 N = mLights.size(); 1673 if (N > 0) { 1674 pw.println(" Lights List:"); 1675 for (int i=0; i<N; i++) { 1676 pw.println(" " + mLights.get(i)); 1677 } 1678 pw.println(" "); 1679 } 1680 1681 pw.println(" mSoundNotification=" + mSoundNotification); 1682 pw.println(" mVibrateNotification=" + mVibrateNotification); 1683 pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts); 1684 pw.println(" mZenMode=" + Settings.Global.zenModeToString(mZenMode)); 1685 pw.println(" mSystemReady=" + mSystemReady); 1686 pw.println(" mArchive=" + mArchive.toString()); 1687 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1688 int i=0; 1689 while (iter.hasNext()) { 1690 pw.println(" " + iter.next()); 1691 if (++i >= 5) { 1692 if (iter.hasNext()) pw.println(" ..."); 1693 break; 1694 } 1695 } 1696 1697 } 1698 } 1699 1700 /** 1701 * The private API only accessible to the system process. 1702 */ 1703 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1704 @Override 1705 public void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid, 1706 String tag, int id, Notification notification, int[] idReceived, int userId) { 1707 enqueueNotificationInternal(pkg, basePkg, callingUid, callingPid, tag, id, notification, 1708 idReceived, userId); 1709 } 1710 }; 1711 1712 void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid, 1713 final int callingPid, final String tag, final int id, final Notification notification, 1714 int[] idOut, int incomingUserId) { 1715 if (DBG) { 1716 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 1717 + " notification=" + notification); 1718 } 1719 checkCallerIsSystemOrSameApp(pkg); 1720 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 1721 1722 final int userId = ActivityManager.handleIncomingUser(callingPid, 1723 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1724 final UserHandle user = new UserHandle(userId); 1725 1726 // Limit the number of notifications that any given package except the android 1727 // package can enqueue. Prevents DOS attacks and deals with leaks. 1728 if (!isSystemNotification) { 1729 synchronized (mNotificationList) { 1730 int count = 0; 1731 final int N = mNotificationList.size(); 1732 for (int i=0; i<N; i++) { 1733 final NotificationRecord r = mNotificationList.get(i); 1734 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1735 count++; 1736 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1737 Slog.e(TAG, "Package has already posted " + count 1738 + " notifications. Not showing more. package=" + pkg); 1739 return; 1740 } 1741 } 1742 } 1743 } 1744 } 1745 1746 // This conditional is a dirty hack to limit the logging done on 1747 // behalf of the download manager without affecting other apps. 1748 if (!pkg.equals("com.android.providers.downloads") 1749 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1750 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 1751 pkg, id, tag, userId, notification.toString()); 1752 } 1753 1754 if (pkg == null || notification == null) { 1755 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1756 + " id=" + id + " notification=" + notification); 1757 } 1758 if (notification.icon != 0) { 1759 if (notification.contentView == null) { 1760 throw new IllegalArgumentException("contentView required: pkg=" + pkg 1761 + " id=" + id + " notification=" + notification); 1762 } 1763 } 1764 1765 mHandler.post(new Runnable() { 1766 @Override 1767 public void run() { 1768 1769 // === Scoring === 1770 1771 // 0. Sanitize inputs 1772 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1773 Notification.PRIORITY_MAX); 1774 // Migrate notification flags to scores 1775 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1776 if (notification.priority < Notification.PRIORITY_MAX) { 1777 notification.priority = Notification.PRIORITY_MAX; 1778 } 1779 } else if (SCORE_ONGOING_HIGHER && 1780 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1781 if (notification.priority < Notification.PRIORITY_HIGH) { 1782 notification.priority = Notification.PRIORITY_HIGH; 1783 } 1784 } 1785 1786 // 1. initial score: buckets of 10, around the app 1787 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] 1788 1789 // 2. Consult external heuristics (TBD) 1790 1791 // 3. Apply local rules 1792 1793 int initialScore = score; 1794 if (!mScorers.isEmpty()) { 1795 if (DBG) Slog.v(TAG, "Initial score is " + score + "."); 1796 for (NotificationScorer scorer : mScorers) { 1797 try { 1798 score = scorer.getScore(notification, score); 1799 } catch (Throwable t) { 1800 Slog.w(TAG, "Scorer threw on .getScore.", t); 1801 } 1802 } 1803 if (DBG) Slog.v(TAG, "Final score is " + score + "."); 1804 } 1805 1806 // add extra to indicate score modified by NotificationScorer 1807 notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED, 1808 score != initialScore); 1809 1810 // blocked apps 1811 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1812 if (!isSystemNotification) { 1813 score = JUNK_SCORE; 1814 Slog.e(TAG, "Suppressing notification from package " + pkg 1815 + " by user request."); 1816 } 1817 } 1818 1819 if (DBG) { 1820 Slog.v(TAG, "Assigned score=" + score + " to " + notification); 1821 } 1822 1823 if (score < SCORE_DISPLAY_THRESHOLD) { 1824 // Notification will be blocked because the score is too low. 1825 return; 1826 } 1827 1828 // Is this notification intercepted by zen mode? 1829 final boolean intercept = shouldIntercept(pkg, notification); 1830 notification.extras.putBoolean(EXTRA_INTERCEPT, intercept); 1831 1832 // Should this notification make noise, vibe, or use the LED? 1833 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept; 1834 if (DBG) Slog.v(TAG, "canInterrupt=" + canInterrupt + " intercept=" + intercept); 1835 synchronized (mNotificationList) { 1836 final StatusBarNotification n = new StatusBarNotification( 1837 pkg, id, tag, callingUid, callingPid, score, notification, user); 1838 NotificationRecord r = new NotificationRecord(n); 1839 NotificationRecord old = null; 1840 1841 int index = indexOfNotificationLocked(pkg, tag, id, userId); 1842 if (index < 0) { 1843 mNotificationList.add(r); 1844 } else { 1845 old = mNotificationList.remove(index); 1846 mNotificationList.add(index, r); 1847 // Make sure we don't lose the foreground service state. 1848 if (old != null) { 1849 notification.flags |= 1850 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1851 } 1852 } 1853 1854 // Ensure if this is a foreground service that the proper additional 1855 // flags are set. 1856 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1857 notification.flags |= Notification.FLAG_ONGOING_EVENT 1858 | Notification.FLAG_NO_CLEAR; 1859 } 1860 1861 final int currentUser; 1862 final long token = Binder.clearCallingIdentity(); 1863 try { 1864 currentUser = ActivityManager.getCurrentUser(); 1865 } finally { 1866 Binder.restoreCallingIdentity(token); 1867 } 1868 1869 if (notification.icon != 0) { 1870 if (old != null && old.statusBarKey != null) { 1871 r.statusBarKey = old.statusBarKey; 1872 final long identity = Binder.clearCallingIdentity(); 1873 try { 1874 mStatusBar.updateNotification(r.statusBarKey, n); 1875 } finally { 1876 Binder.restoreCallingIdentity(identity); 1877 } 1878 } else { 1879 final long identity = Binder.clearCallingIdentity(); 1880 try { 1881 r.statusBarKey = mStatusBar.addNotification(n); 1882 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0 1883 && canInterrupt) { 1884 mAttentionLight.pulse(); 1885 } 1886 } finally { 1887 Binder.restoreCallingIdentity(identity); 1888 } 1889 } 1890 // Send accessibility events only for the current user. 1891 if (currentUser == userId) { 1892 sendAccessibilityEvent(notification, pkg); 1893 } 1894 1895 notifyPostedLocked(r); 1896 } else { 1897 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1898 if (old != null && old.statusBarKey != null) { 1899 final long identity = Binder.clearCallingIdentity(); 1900 try { 1901 mStatusBar.removeNotification(old.statusBarKey); 1902 } finally { 1903 Binder.restoreCallingIdentity(identity); 1904 } 1905 1906 notifyRemovedLocked(r); 1907 } 1908 // ATTENTION: in a future release we will bail out here 1909 // so that we do not play sounds, show lights, etc. for invalid 1910 // notifications 1911 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1912 + n.getPackageName()); 1913 } 1914 1915 // If we're not supposed to beep, vibrate, etc. then don't. 1916 if (!mDisableNotificationAlerts 1917 && (!(old != null 1918 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1919 && (r.getUserId() == UserHandle.USER_ALL || 1920 (r.getUserId() == userId && r.getUserId() == currentUser)) 1921 && canInterrupt 1922 && mSystemReady 1923 && mAudioManager != null) { 1924 if (DBG) Slog.v(TAG, "Interrupting!"); 1925 // sound 1926 1927 // should we use the default notification sound? (indicated either by 1928 // DEFAULT_SOUND or because notification.sound is pointing at 1929 // Settings.System.NOTIFICATION_SOUND) 1930 final boolean useDefaultSound = 1931 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1932 Settings.System.DEFAULT_NOTIFICATION_URI 1933 .equals(notification.sound); 1934 1935 Uri soundUri = null; 1936 boolean hasValidSound = false; 1937 1938 if (useDefaultSound) { 1939 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1940 1941 // check to see if the default notification sound is silent 1942 ContentResolver resolver = getContext().getContentResolver(); 1943 hasValidSound = Settings.System.getString(resolver, 1944 Settings.System.NOTIFICATION_SOUND) != null; 1945 } else if (notification.sound != null) { 1946 soundUri = notification.sound; 1947 hasValidSound = (soundUri != null); 1948 } 1949 1950 if (hasValidSound) { 1951 boolean looping = 1952 (notification.flags & Notification.FLAG_INSISTENT) != 0; 1953 int audioStreamType; 1954 if (notification.audioStreamType >= 0) { 1955 audioStreamType = notification.audioStreamType; 1956 } else { 1957 audioStreamType = DEFAULT_STREAM_TYPE; 1958 } 1959 mSoundNotification = r; 1960 // do not play notifications if stream volume is 0 (typically because 1961 // ringer mode is silent) or if there is a user of exclusive audio focus 1962 if ((mAudioManager.getStreamVolume(audioStreamType) != 0) 1963 && !mAudioManager.isAudioFocusExclusive()) { 1964 final long identity = Binder.clearCallingIdentity(); 1965 try { 1966 final IRingtonePlayer player = 1967 mAudioManager.getRingtonePlayer(); 1968 if (player != null) { 1969 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 1970 + " on stream " + audioStreamType); 1971 player.playAsync(soundUri, user, looping, audioStreamType); 1972 } 1973 } catch (RemoteException e) { 1974 } finally { 1975 Binder.restoreCallingIdentity(identity); 1976 } 1977 } 1978 } 1979 1980 // vibrate 1981 // Does the notification want to specify its own vibration? 1982 final boolean hasCustomVibrate = notification.vibrate != null; 1983 1984 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1985 // mode, and no other vibration is specified, we fall back to vibration 1986 final boolean convertSoundToVibration = 1987 !hasCustomVibrate 1988 && hasValidSound 1989 && (mAudioManager.getRingerMode() 1990 == AudioManager.RINGER_MODE_VIBRATE); 1991 1992 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1993 final boolean useDefaultVibrate = 1994 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1995 1996 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1997 && !(mAudioManager.getRingerMode() 1998 == AudioManager.RINGER_MODE_SILENT)) { 1999 mVibrateNotification = r; 2000 2001 if (useDefaultVibrate || convertSoundToVibration) { 2002 // Escalate privileges so we can use the vibrator even if the 2003 // notifying app does not have the VIBRATE permission. 2004 long identity = Binder.clearCallingIdentity(); 2005 try { 2006 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(), 2007 useDefaultVibrate ? mDefaultVibrationPattern 2008 : mFallbackVibrationPattern, 2009 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 2010 ? 0: -1, notification.audioStreamType); 2011 } finally { 2012 Binder.restoreCallingIdentity(identity); 2013 } 2014 } else if (notification.vibrate.length > 1) { 2015 // If you want your own vibration pattern, you need the VIBRATE 2016 // permission 2017 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(), 2018 notification.vibrate, 2019 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 2020 ? 0: -1, notification.audioStreamType); 2021 } 2022 } 2023 } 2024 2025 // light 2026 // the most recent thing gets the light 2027 mLights.remove(old); 2028 if (mLedNotification == old) { 2029 mLedNotification = null; 2030 } 2031 //Slog.i(TAG, "notification.lights=" 2032 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) 2033 // != 0)); 2034 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 2035 && canInterrupt) { 2036 mLights.add(r); 2037 updateLightsLocked(); 2038 } else { 2039 if (old != null 2040 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) { 2041 updateLightsLocked(); 2042 } 2043 } 2044 } 2045 } 2046 }); 2047 2048 idOut[0] = id; 2049 } 2050 2051 void registerListenerImpl(final INotificationListener listener, 2052 final ComponentName component, final int userid) { 2053 synchronized (mNotificationList) { 2054 try { 2055 NotificationListenerInfo info 2056 = new NotificationListenerInfo(listener, component, userid, true); 2057 listener.asBinder().linkToDeath(info, 0); 2058 mListeners.add(info); 2059 } catch (RemoteException e) { 2060 // already dead 2061 } 2062 } 2063 } 2064 2065 /** 2066 * Removes a listener from the list and unbinds from its service. 2067 */ 2068 void unregisterListenerImpl(final INotificationListener listener, final int userid) { 2069 NotificationListenerInfo info = removeListenerImpl(listener, userid); 2070 if (info != null && info.connection != null) { 2071 getContext().unbindService(info.connection); 2072 } 2073 } 2074 2075 /** 2076 * Removes a listener from the list but does not unbind from the listener's service. 2077 * 2078 * @return the removed listener. 2079 */ 2080 NotificationListenerInfo removeListenerImpl( 2081 final INotificationListener listener, final int userid) { 2082 NotificationListenerInfo listenerInfo = null; 2083 synchronized (mNotificationList) { 2084 final int N = mListeners.size(); 2085 for (int i=N-1; i>=0; i--) { 2086 final NotificationListenerInfo info = mListeners.get(i); 2087 if (info.listener.asBinder() == listener.asBinder() 2088 && info.userid == userid) { 2089 listenerInfo = mListeners.remove(i); 2090 } 2091 } 2092 } 2093 return listenerInfo; 2094 } 2095 2096 void showNextToastLocked() { 2097 ToastRecord record = mToastQueue.get(0); 2098 while (record != null) { 2099 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 2100 try { 2101 record.callback.show(); 2102 scheduleTimeoutLocked(record); 2103 return; 2104 } catch (RemoteException e) { 2105 Slog.w(TAG, "Object died trying to show notification " + record.callback 2106 + " in package " + record.pkg); 2107 // remove it from the list and let the process die 2108 int index = mToastQueue.indexOf(record); 2109 if (index >= 0) { 2110 mToastQueue.remove(index); 2111 } 2112 keepProcessAliveLocked(record.pid); 2113 if (mToastQueue.size() > 0) { 2114 record = mToastQueue.get(0); 2115 } else { 2116 record = null; 2117 } 2118 } 2119 } 2120 } 2121 2122 void cancelToastLocked(int index) { 2123 ToastRecord record = mToastQueue.get(index); 2124 try { 2125 record.callback.hide(); 2126 } catch (RemoteException e) { 2127 Slog.w(TAG, "Object died trying to hide notification " + record.callback 2128 + " in package " + record.pkg); 2129 // don't worry about this, we're about to remove it from 2130 // the list anyway 2131 } 2132 mToastQueue.remove(index); 2133 keepProcessAliveLocked(record.pid); 2134 if (mToastQueue.size() > 0) { 2135 // Show the next one. If the callback fails, this will remove 2136 // it from the list, so don't assume that the list hasn't changed 2137 // after this point. 2138 showNextToastLocked(); 2139 } 2140 } 2141 2142 private void scheduleTimeoutLocked(ToastRecord r) 2143 { 2144 mHandler.removeCallbacksAndMessages(r); 2145 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 2146 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 2147 mHandler.sendMessageDelayed(m, delay); 2148 } 2149 2150 private void handleTimeout(ToastRecord record) 2151 { 2152 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 2153 synchronized (mToastQueue) { 2154 int index = indexOfToastLocked(record.pkg, record.callback); 2155 if (index >= 0) { 2156 cancelToastLocked(index); 2157 } 2158 } 2159 } 2160 2161 // lock on mToastQueue 2162 int indexOfToastLocked(String pkg, ITransientNotification callback) 2163 { 2164 IBinder cbak = callback.asBinder(); 2165 ArrayList<ToastRecord> list = mToastQueue; 2166 int len = list.size(); 2167 for (int i=0; i<len; i++) { 2168 ToastRecord r = list.get(i); 2169 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 2170 return i; 2171 } 2172 } 2173 return -1; 2174 } 2175 2176 // lock on mToastQueue 2177 void keepProcessAliveLocked(int pid) 2178 { 2179 int toastCount = 0; // toasts from this pid 2180 ArrayList<ToastRecord> list = mToastQueue; 2181 int N = list.size(); 2182 for (int i=0; i<N; i++) { 2183 ToastRecord r = list.get(i); 2184 if (r.pid == pid) { 2185 toastCount++; 2186 } 2187 } 2188 try { 2189 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 2190 } catch (RemoteException e) { 2191 // Shouldn't happen. 2192 } 2193 } 2194 2195 private final class WorkerHandler extends Handler 2196 { 2197 @Override 2198 public void handleMessage(Message msg) 2199 { 2200 switch (msg.what) 2201 { 2202 case MESSAGE_TIMEOUT: 2203 handleTimeout((ToastRecord)msg.obj); 2204 break; 2205 } 2206 } 2207 } 2208 2209 2210 // Notifications 2211 // ============================================================================ 2212 static int clamp(int x, int low, int high) { 2213 return (x < low) ? low : ((x > high) ? high : x); 2214 } 2215 2216 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 2217 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 2218 if (!manager.isEnabled()) { 2219 return; 2220 } 2221 2222 AccessibilityEvent event = 2223 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 2224 event.setPackageName(packageName); 2225 event.setClassName(Notification.class.getName()); 2226 event.setParcelableData(notification); 2227 CharSequence tickerText = notification.tickerText; 2228 if (!TextUtils.isEmpty(tickerText)) { 2229 event.getText().add(tickerText); 2230 } 2231 2232 manager.sendAccessibilityEvent(event); 2233 } 2234 2235 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) { 2236 // tell the app 2237 if (sendDelete) { 2238 if (r.getNotification().deleteIntent != null) { 2239 try { 2240 r.getNotification().deleteIntent.send(); 2241 } catch (PendingIntent.CanceledException ex) { 2242 // do nothing - there's no relevant way to recover, and 2243 // no reason to let this propagate 2244 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 2245 } 2246 } 2247 } 2248 2249 // status bar 2250 if (r.getNotification().icon != 0) { 2251 final long identity = Binder.clearCallingIdentity(); 2252 try { 2253 mStatusBar.removeNotification(r.statusBarKey); 2254 } finally { 2255 Binder.restoreCallingIdentity(identity); 2256 } 2257 r.statusBarKey = null; 2258 notifyRemovedLocked(r); 2259 } 2260 2261 // sound 2262 if (mSoundNotification == r) { 2263 mSoundNotification = null; 2264 final long identity = Binder.clearCallingIdentity(); 2265 try { 2266 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2267 if (player != null) { 2268 player.stopAsync(); 2269 } 2270 } catch (RemoteException e) { 2271 } finally { 2272 Binder.restoreCallingIdentity(identity); 2273 } 2274 } 2275 2276 // vibrate 2277 if (mVibrateNotification == r) { 2278 mVibrateNotification = null; 2279 long identity = Binder.clearCallingIdentity(); 2280 try { 2281 mVibrator.cancel(); 2282 } 2283 finally { 2284 Binder.restoreCallingIdentity(identity); 2285 } 2286 } 2287 2288 // light 2289 mLights.remove(r); 2290 if (mLedNotification == r) { 2291 mLedNotification = null; 2292 } 2293 2294 // Save it for users of getHistoricalNotifications() 2295 mArchive.record(r.sbn); 2296 } 2297 2298 /** 2299 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2300 * and none of the {@code mustNotHaveFlags}. 2301 */ 2302 void cancelNotification(final int callingUid, final int callingPid, 2303 final String pkg, final String tag, final int id, 2304 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2305 final int userId, final int reason, final NotificationListenerInfo listener) { 2306 // In enqueueNotificationInternal notifications are added by scheduling the 2307 // work on the worker handler. Hence, we also schedule the cancel on this 2308 // handler to avoid a scenario where an add notification call followed by a 2309 // remove notification call ends up in not removing the notification. 2310 mHandler.post(new Runnable() { 2311 @Override 2312 public void run() { 2313 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2314 mustHaveFlags, mustNotHaveFlags, reason, 2315 listener == null ? null : listener.component.toShortString()); 2316 2317 synchronized (mNotificationList) { 2318 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2319 if (index >= 0) { 2320 NotificationRecord r = mNotificationList.get(index); 2321 2322 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2323 return; 2324 } 2325 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2326 return; 2327 } 2328 2329 mNotificationList.remove(index); 2330 2331 cancelNotificationLocked(r, sendDelete); 2332 updateLightsLocked(); 2333 } 2334 } 2335 } 2336 }); 2337 } 2338 2339 /** 2340 * Determine whether the userId applies to the notification in question, either because 2341 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2342 */ 2343 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2344 return 2345 // looking for USER_ALL notifications? match everything 2346 userId == UserHandle.USER_ALL 2347 // a notification sent to USER_ALL matches any query 2348 || r.getUserId() == UserHandle.USER_ALL 2349 // an exact user match 2350 || r.getUserId() == userId; 2351 } 2352 2353 /** 2354 * Determine whether the userId applies to the notification in question, either because 2355 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2356 * because it matches a related user. 2357 */ 2358 private boolean notificationMatchesUserIdOrRelated(NotificationRecord r, int userId) { 2359 synchronized (mRelatedUsers) { 2360 return notificationMatchesUserId(r, userId) 2361 || mRelatedUsers.get(r.getUserId()) != null; 2362 } 2363 } 2364 2365 /** 2366 * Cancels all notifications from a given package that have all of the 2367 * {@code mustHaveFlags}. 2368 */ 2369 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2370 int mustNotHaveFlags, boolean doit, int userId, int reason, 2371 NotificationListenerInfo listener) { 2372 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2373 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2374 listener == null ? null : listener.component.toShortString()); 2375 2376 synchronized (mNotificationList) { 2377 final int N = mNotificationList.size(); 2378 boolean canceledSomething = false; 2379 for (int i = N-1; i >= 0; --i) { 2380 NotificationRecord r = mNotificationList.get(i); 2381 if (!notificationMatchesUserId(r, userId)) { 2382 continue; 2383 } 2384 // Don't remove notifications to all, if there's no package name specified 2385 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2386 continue; 2387 } 2388 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2389 continue; 2390 } 2391 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2392 continue; 2393 } 2394 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2395 continue; 2396 } 2397 canceledSomething = true; 2398 if (!doit) { 2399 return true; 2400 } 2401 mNotificationList.remove(i); 2402 cancelNotificationLocked(r, false); 2403 } 2404 if (canceledSomething) { 2405 updateLightsLocked(); 2406 } 2407 return canceledSomething; 2408 } 2409 } 2410 2411 2412 2413 // Return true if the UID is a system or phone UID and therefore should not have 2414 // any notifications or toasts blocked. 2415 boolean isUidSystem(int uid) { 2416 final int appid = UserHandle.getAppId(uid); 2417 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2418 } 2419 2420 // same as isUidSystem(int, int) for the Binder caller's UID. 2421 boolean isCallerSystem() { 2422 return isUidSystem(Binder.getCallingUid()); 2423 } 2424 2425 void checkCallerIsSystem() { 2426 if (isCallerSystem()) { 2427 return; 2428 } 2429 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2430 } 2431 2432 void checkCallerIsSystemOrSameApp(String pkg) { 2433 if (isCallerSystem()) { 2434 return; 2435 } 2436 final int uid = Binder.getCallingUid(); 2437 try { 2438 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2439 pkg, 0, UserHandle.getCallingUserId()); 2440 if (!UserHandle.isSameApp(ai.uid, uid)) { 2441 throw new SecurityException("Calling uid " + uid + " gave package" 2442 + pkg + " which is owned by uid " + ai.uid); 2443 } 2444 } catch (RemoteException re) { 2445 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2446 } 2447 } 2448 2449 void cancelAll(int callingUid, int callingPid, int userId, int reason, 2450 NotificationListenerInfo listener) { 2451 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2452 null, userId, 0, 0, reason, 2453 listener == null ? null : listener.component.toShortString()); 2454 synchronized (mNotificationList) { 2455 final int N = mNotificationList.size(); 2456 for (int i=N-1; i>=0; i--) { 2457 NotificationRecord r = mNotificationList.get(i); 2458 2459 if (!notificationMatchesUserIdOrRelated(r, userId)) { 2460 continue; 2461 } 2462 2463 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2464 | Notification.FLAG_NO_CLEAR)) == 0) { 2465 mNotificationList.remove(i); 2466 cancelNotificationLocked(r, true); 2467 } 2468 } 2469 2470 updateLightsLocked(); 2471 } 2472 } 2473 2474 // lock on mNotificationList 2475 void updateLightsLocked() 2476 { 2477 // handle notification lights 2478 if (mLedNotification == null) { 2479 // get next notification, if any 2480 int n = mLights.size(); 2481 if (n > 0) { 2482 mLedNotification = mLights.get(n-1); 2483 } 2484 } 2485 2486 // Don't flash while we are in a call or screen is on 2487 if (mLedNotification == null || mInCall || mScreenOn) { 2488 mNotificationLight.turnOff(); 2489 } else { 2490 final Notification ledno = mLedNotification.sbn.getNotification(); 2491 int ledARGB = ledno.ledARGB; 2492 int ledOnMS = ledno.ledOnMS; 2493 int ledOffMS = ledno.ledOffMS; 2494 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2495 ledARGB = mDefaultNotificationColor; 2496 ledOnMS = mDefaultNotificationLedOn; 2497 ledOffMS = mDefaultNotificationLedOff; 2498 } 2499 if (mNotificationPulseEnabled) { 2500 // pulse repeatedly 2501 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2502 ledOnMS, ledOffMS); 2503 } 2504 } 2505 } 2506 2507 // lock on mNotificationList 2508 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2509 { 2510 ArrayList<NotificationRecord> list = mNotificationList; 2511 final int len = list.size(); 2512 for (int i=0; i<len; i++) { 2513 NotificationRecord r = list.get(i); 2514 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2515 continue; 2516 } 2517 if (tag == null) { 2518 if (r.sbn.getTag() != null) { 2519 continue; 2520 } 2521 } else { 2522 if (!tag.equals(r.sbn.getTag())) { 2523 continue; 2524 } 2525 } 2526 if (r.sbn.getPackageName().equals(pkg)) { 2527 return i; 2528 } 2529 } 2530 return -1; 2531 } 2532 2533 private void updateNotificationPulse() { 2534 synchronized (mNotificationList) { 2535 updateLightsLocked(); 2536 } 2537 } 2538 2539 private void updateZenMode() { 2540 final int mode = Settings.Global.getInt(getContext().getContentResolver(), 2541 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); 2542 if (mode != mZenMode) { 2543 Slog.d(TAG, String.format("updateZenMode: %s -> %s", 2544 Settings.Global.zenModeToString(mZenMode), 2545 Settings.Global.zenModeToString(mode))); 2546 } 2547 mZenMode = mode; 2548 2549 final String[] exceptionPackages = null; // none (for now) 2550 2551 // call restrictions 2552 final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF; 2553 mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING, 2554 muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 2555 exceptionPackages); 2556 mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING, 2557 muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 2558 exceptionPackages); 2559 2560 // alarm restrictions 2561 final boolean muteAlarms = false; // TODO until we save user config 2562 mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM, 2563 muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 2564 exceptionPackages); 2565 mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM, 2566 muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 2567 exceptionPackages); 2568 2569 // restrict vibrations with no hints 2570 mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE, 2571 (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 2572 exceptionPackages); 2573 } 2574 2575 private void updateRelatedUserCache(Context context) { 2576 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 2577 int currentUserId = ActivityManager.getCurrentUser(); 2578 if (userManager != null) { 2579 List<UserInfo> relatedUsers = userManager.getRelatedUsers(currentUserId); 2580 synchronized (mRelatedUsers) { 2581 mRelatedUsers.clear(); 2582 for (UserInfo related : relatedUsers) { 2583 mRelatedUsers.put(related.id, related); 2584 } 2585 } 2586 } 2587 } 2588 2589 private boolean isCall(String pkg, Notification n) { 2590 return CALL_PACKAGES.contains(pkg); 2591 } 2592 2593 private boolean isAlarm(String pkg, Notification n) { 2594 return ALARM_PACKAGES.contains(pkg); 2595 } 2596 2597 private boolean shouldIntercept(String pkg, Notification n) { 2598 if (mZenMode != Settings.Global.ZEN_MODE_OFF) { 2599 return !isAlarm(pkg, n); 2600 } 2601 return false; 2602 } 2603} 2604