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