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