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