NotificationManagerService.java revision 8fd7f1ed7c11d35b3f2a97878e68ee38a551dd15
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 opPkg, String tag, int id, 1501 Notification notification, int[] idOut, int userId) throws RemoteException { 1502 enqueueNotificationInternal(pkg, opPkg, 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 opPkg, int callingUid, int callingPid, 1853 String tag, int id, Notification notification, int[] idReceived, int userId) { 1854 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 1855 idReceived, userId); 1856 } 1857 }; 1858 1859 void enqueueNotificationInternal(final String pkg, final String opPkg, 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, opPkg, id, tag, callingUid, callingPid, score, notification, 1985 user); 1986 NotificationRecord r = new NotificationRecord(n); 1987 NotificationRecord old = null; 1988 1989 int index = indexOfNotificationLocked(pkg, tag, id, userId); 1990 if (index < 0) { 1991 mNotificationList.add(r); 1992 mUsageStats.registerPostedByApp(r); 1993 } else { 1994 old = mNotificationList.get(index); 1995 mNotificationList.set(index, r); 1996 mUsageStats.registerUpdatedByApp(r); 1997 // Make sure we don't lose the foreground service state. 1998 if (old != null) { 1999 notification.flags |= 2000 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 2001 } 2002 } 2003 if (old != null) { 2004 mNotificationsByKey.remove(old.sbn.getKey()); 2005 } 2006 mNotificationsByKey.put(n.getKey(), r); 2007 2008 // Ensure if this is a foreground service that the proper additional 2009 // flags are set. 2010 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 2011 notification.flags |= Notification.FLAG_ONGOING_EVENT 2012 | Notification.FLAG_NO_CLEAR; 2013 } 2014 2015 final int currentUser; 2016 final long token = Binder.clearCallingIdentity(); 2017 try { 2018 currentUser = ActivityManager.getCurrentUser(); 2019 } finally { 2020 Binder.restoreCallingIdentity(token); 2021 } 2022 2023 if (notification.icon != 0) { 2024 if (old != null && old.statusBarKey != null) { 2025 r.statusBarKey = old.statusBarKey; 2026 final long identity = Binder.clearCallingIdentity(); 2027 try { 2028 mStatusBar.updateNotification(r.statusBarKey, n); 2029 } finally { 2030 Binder.restoreCallingIdentity(identity); 2031 } 2032 } else { 2033 final long identity = Binder.clearCallingIdentity(); 2034 try { 2035 r.statusBarKey = mStatusBar.addNotification(n); 2036 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0 2037 && canInterrupt) { 2038 mAttentionLight.pulse(); 2039 } 2040 } finally { 2041 Binder.restoreCallingIdentity(identity); 2042 } 2043 } 2044 // Send accessibility events only for the current user. 2045 if (currentUser == userId) { 2046 sendAccessibilityEvent(notification, pkg); 2047 } 2048 2049 notifyPostedLocked(r); 2050 } else { 2051 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 2052 if (old != null && old.statusBarKey != null) { 2053 final long identity = Binder.clearCallingIdentity(); 2054 try { 2055 mStatusBar.removeNotification(old.statusBarKey); 2056 } finally { 2057 Binder.restoreCallingIdentity(identity); 2058 } 2059 2060 notifyRemovedLocked(r); 2061 } 2062 // ATTENTION: in a future release we will bail out here 2063 // so that we do not play sounds, show lights, etc. for invalid 2064 // notifications 2065 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 2066 + n.getPackageName()); 2067 } 2068 2069 // If we're not supposed to beep, vibrate, etc. then don't. 2070 if (!mDisableNotificationAlerts 2071 && (!(old != null 2072 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 2073 && (r.getUserId() == UserHandle.USER_ALL || 2074 (r.getUserId() == userId && r.getUserId() == currentUser) || 2075 isCurrentProfile(r.getUserId())) 2076 && canInterrupt 2077 && mSystemReady 2078 && mAudioManager != null) { 2079 if (DBG) Slog.v(TAG, "Interrupting!"); 2080 // sound 2081 2082 // should we use the default notification sound? (indicated either by 2083 // DEFAULT_SOUND or because notification.sound is pointing at 2084 // Settings.System.NOTIFICATION_SOUND) 2085 final boolean useDefaultSound = 2086 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 2087 Settings.System.DEFAULT_NOTIFICATION_URI 2088 .equals(notification.sound); 2089 2090 Uri soundUri = null; 2091 boolean hasValidSound = false; 2092 2093 if (useDefaultSound) { 2094 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 2095 2096 // check to see if the default notification sound is silent 2097 ContentResolver resolver = getContext().getContentResolver(); 2098 hasValidSound = Settings.System.getString(resolver, 2099 Settings.System.NOTIFICATION_SOUND) != null; 2100 } else if (notification.sound != null) { 2101 soundUri = notification.sound; 2102 hasValidSound = (soundUri != null); 2103 } 2104 2105 if (hasValidSound) { 2106 boolean looping = 2107 (notification.flags & Notification.FLAG_INSISTENT) != 0; 2108 int audioStreamType; 2109 if (notification.audioStreamType >= 0) { 2110 audioStreamType = notification.audioStreamType; 2111 } else { 2112 audioStreamType = DEFAULT_STREAM_TYPE; 2113 } 2114 mSoundNotification = r; 2115 // do not play notifications if stream volume is 0 (typically because 2116 // ringer mode is silent) or if there is a user of exclusive audio focus 2117 if ((mAudioManager.getStreamVolume(audioStreamType) != 0) 2118 && !mAudioManager.isAudioFocusExclusive()) { 2119 final long identity = Binder.clearCallingIdentity(); 2120 try { 2121 final IRingtonePlayer player = 2122 mAudioManager.getRingtonePlayer(); 2123 if (player != null) { 2124 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 2125 + " on stream " + audioStreamType); 2126 player.playAsync(soundUri, user, looping, audioStreamType); 2127 } 2128 } catch (RemoteException e) { 2129 } finally { 2130 Binder.restoreCallingIdentity(identity); 2131 } 2132 } 2133 } 2134 2135 // vibrate 2136 // Does the notification want to specify its own vibration? 2137 final boolean hasCustomVibrate = notification.vibrate != null; 2138 2139 // new in 4.2: if there was supposed to be a sound and we're in vibrate 2140 // mode, and no other vibration is specified, we fall back to vibration 2141 final boolean convertSoundToVibration = 2142 !hasCustomVibrate 2143 && hasValidSound 2144 && (mAudioManager.getRingerMode() 2145 == AudioManager.RINGER_MODE_VIBRATE); 2146 2147 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 2148 final boolean useDefaultVibrate = 2149 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 2150 2151 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 2152 && !(mAudioManager.getRingerMode() 2153 == AudioManager.RINGER_MODE_SILENT)) { 2154 mVibrateNotification = r; 2155 2156 if (useDefaultVibrate || convertSoundToVibration) { 2157 // Escalate privileges so we can use the vibrator even if the 2158 // notifying app does not have the VIBRATE permission. 2159 long identity = Binder.clearCallingIdentity(); 2160 try { 2161 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 2162 useDefaultVibrate ? mDefaultVibrationPattern 2163 : mFallbackVibrationPattern, 2164 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 2165 ? 0: -1, notification.audioStreamType); 2166 } finally { 2167 Binder.restoreCallingIdentity(identity); 2168 } 2169 } else if (notification.vibrate.length > 1) { 2170 // If you want your own vibration pattern, you need the VIBRATE 2171 // permission 2172 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 2173 notification.vibrate, 2174 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 2175 ? 0: -1, notification.audioStreamType); 2176 } 2177 } 2178 } 2179 2180 // light 2181 // the most recent thing gets the light 2182 mLights.remove(old); 2183 if (mLedNotification == old) { 2184 mLedNotification = null; 2185 } 2186 //Slog.i(TAG, "notification.lights=" 2187 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) 2188 // != 0)); 2189 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 2190 && canInterrupt) { 2191 mLights.add(r); 2192 updateLightsLocked(); 2193 } else { 2194 if (old != null 2195 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) { 2196 updateLightsLocked(); 2197 } 2198 } 2199 } 2200 } 2201 }); 2202 2203 idOut[0] = id; 2204 } 2205 2206 void registerListenerImpl(final INotificationListener listener, 2207 final ComponentName component, final int userid) { 2208 synchronized (mNotificationList) { 2209 try { 2210 NotificationListenerInfo info 2211 = new NotificationListenerInfo(listener, component, userid, 2212 /*isSystem*/ true, Build.VERSION_CODES.L); 2213 listener.asBinder().linkToDeath(info, 0); 2214 mListeners.add(info); 2215 } catch (RemoteException e) { 2216 // already dead 2217 } 2218 } 2219 } 2220 2221 /** 2222 * Removes a listener from the list and unbinds from its service. 2223 */ 2224 void unregisterListenerImpl(final INotificationListener listener, final int userid) { 2225 NotificationListenerInfo info = removeListenerImpl(listener, userid); 2226 if (info != null && info.connection != null) { 2227 getContext().unbindService(info.connection); 2228 } 2229 } 2230 2231 /** 2232 * Removes a listener from the list but does not unbind from the listener's service. 2233 * 2234 * @return the removed listener. 2235 */ 2236 NotificationListenerInfo removeListenerImpl( 2237 final INotificationListener listener, final int userid) { 2238 NotificationListenerInfo listenerInfo = null; 2239 synchronized (mNotificationList) { 2240 final int N = mListeners.size(); 2241 for (int i=N-1; i>=0; i--) { 2242 final NotificationListenerInfo info = mListeners.get(i); 2243 if (info.listener.asBinder() == listener.asBinder() 2244 && info.userid == userid) { 2245 listenerInfo = mListeners.remove(i); 2246 } 2247 } 2248 } 2249 return listenerInfo; 2250 } 2251 2252 void showNextToastLocked() { 2253 ToastRecord record = mToastQueue.get(0); 2254 while (record != null) { 2255 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 2256 try { 2257 record.callback.show(); 2258 scheduleTimeoutLocked(record); 2259 return; 2260 } catch (RemoteException e) { 2261 Slog.w(TAG, "Object died trying to show notification " + record.callback 2262 + " in package " + record.pkg); 2263 // remove it from the list and let the process die 2264 int index = mToastQueue.indexOf(record); 2265 if (index >= 0) { 2266 mToastQueue.remove(index); 2267 } 2268 keepProcessAliveLocked(record.pid); 2269 if (mToastQueue.size() > 0) { 2270 record = mToastQueue.get(0); 2271 } else { 2272 record = null; 2273 } 2274 } 2275 } 2276 } 2277 2278 void cancelToastLocked(int index) { 2279 ToastRecord record = mToastQueue.get(index); 2280 try { 2281 record.callback.hide(); 2282 } catch (RemoteException e) { 2283 Slog.w(TAG, "Object died trying to hide notification " + record.callback 2284 + " in package " + record.pkg); 2285 // don't worry about this, we're about to remove it from 2286 // the list anyway 2287 } 2288 mToastQueue.remove(index); 2289 keepProcessAliveLocked(record.pid); 2290 if (mToastQueue.size() > 0) { 2291 // Show the next one. If the callback fails, this will remove 2292 // it from the list, so don't assume that the list hasn't changed 2293 // after this point. 2294 showNextToastLocked(); 2295 } 2296 } 2297 2298 private void scheduleTimeoutLocked(ToastRecord r) 2299 { 2300 mHandler.removeCallbacksAndMessages(r); 2301 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 2302 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 2303 mHandler.sendMessageDelayed(m, delay); 2304 } 2305 2306 private void handleTimeout(ToastRecord record) 2307 { 2308 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 2309 synchronized (mToastQueue) { 2310 int index = indexOfToastLocked(record.pkg, record.callback); 2311 if (index >= 0) { 2312 cancelToastLocked(index); 2313 } 2314 } 2315 } 2316 2317 // lock on mToastQueue 2318 int indexOfToastLocked(String pkg, ITransientNotification callback) 2319 { 2320 IBinder cbak = callback.asBinder(); 2321 ArrayList<ToastRecord> list = mToastQueue; 2322 int len = list.size(); 2323 for (int i=0; i<len; i++) { 2324 ToastRecord r = list.get(i); 2325 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 2326 return i; 2327 } 2328 } 2329 return -1; 2330 } 2331 2332 // lock on mToastQueue 2333 void keepProcessAliveLocked(int pid) 2334 { 2335 int toastCount = 0; // toasts from this pid 2336 ArrayList<ToastRecord> list = mToastQueue; 2337 int N = list.size(); 2338 for (int i=0; i<N; i++) { 2339 ToastRecord r = list.get(i); 2340 if (r.pid == pid) { 2341 toastCount++; 2342 } 2343 } 2344 try { 2345 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 2346 } catch (RemoteException e) { 2347 // Shouldn't happen. 2348 } 2349 } 2350 2351 private final class WorkerHandler extends Handler 2352 { 2353 @Override 2354 public void handleMessage(Message msg) 2355 { 2356 switch (msg.what) 2357 { 2358 case MESSAGE_TIMEOUT: 2359 handleTimeout((ToastRecord)msg.obj); 2360 break; 2361 } 2362 } 2363 } 2364 2365 2366 // Notifications 2367 // ============================================================================ 2368 static int clamp(int x, int low, int high) { 2369 return (x < low) ? low : ((x > high) ? high : x); 2370 } 2371 2372 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 2373 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 2374 if (!manager.isEnabled()) { 2375 return; 2376 } 2377 2378 AccessibilityEvent event = 2379 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 2380 event.setPackageName(packageName); 2381 event.setClassName(Notification.class.getName()); 2382 event.setParcelableData(notification); 2383 CharSequence tickerText = notification.tickerText; 2384 if (!TextUtils.isEmpty(tickerText)) { 2385 event.getText().add(tickerText); 2386 } 2387 2388 manager.sendAccessibilityEvent(event); 2389 } 2390 2391 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 2392 // tell the app 2393 if (sendDelete) { 2394 if (r.getNotification().deleteIntent != null) { 2395 try { 2396 r.getNotification().deleteIntent.send(); 2397 } catch (PendingIntent.CanceledException ex) { 2398 // do nothing - there's no relevant way to recover, and 2399 // no reason to let this propagate 2400 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 2401 } 2402 } 2403 } 2404 2405 // status bar 2406 if (r.getNotification().icon != 0) { 2407 final long identity = Binder.clearCallingIdentity(); 2408 try { 2409 mStatusBar.removeNotification(r.statusBarKey); 2410 } finally { 2411 Binder.restoreCallingIdentity(identity); 2412 } 2413 r.statusBarKey = null; 2414 notifyRemovedLocked(r); 2415 } 2416 2417 // sound 2418 if (mSoundNotification == r) { 2419 mSoundNotification = null; 2420 final long identity = Binder.clearCallingIdentity(); 2421 try { 2422 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2423 if (player != null) { 2424 player.stopAsync(); 2425 } 2426 } catch (RemoteException e) { 2427 } finally { 2428 Binder.restoreCallingIdentity(identity); 2429 } 2430 } 2431 2432 // vibrate 2433 if (mVibrateNotification == r) { 2434 mVibrateNotification = null; 2435 long identity = Binder.clearCallingIdentity(); 2436 try { 2437 mVibrator.cancel(); 2438 } 2439 finally { 2440 Binder.restoreCallingIdentity(identity); 2441 } 2442 } 2443 2444 // light 2445 mLights.remove(r); 2446 if (mLedNotification == r) { 2447 mLedNotification = null; 2448 } 2449 2450 // Record usage stats 2451 switch (reason) { 2452 case REASON_DELEGATE_CANCEL: 2453 case REASON_DELEGATE_CANCEL_ALL: 2454 case REASON_LISTENER_CANCEL: 2455 case REASON_LISTENER_CANCEL_ALL: 2456 mUsageStats.registerDismissedByUser(r); 2457 break; 2458 case REASON_NOMAN_CANCEL: 2459 case REASON_NOMAN_CANCEL_ALL: 2460 mUsageStats.registerRemovedByApp(r); 2461 break; 2462 case REASON_DELEGATE_CLICK: 2463 mUsageStats.registerCancelDueToClick(r); 2464 break; 2465 default: 2466 mUsageStats.registerCancelUnknown(r); 2467 break; 2468 } 2469 2470 // Save it for users of getHistoricalNotifications() 2471 mArchive.record(r.sbn); 2472 } 2473 2474 /** 2475 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2476 * and none of the {@code mustNotHaveFlags}. 2477 */ 2478 void cancelNotification(final int callingUid, final int callingPid, 2479 final String pkg, final String tag, final int id, 2480 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2481 final int userId, final int reason, final NotificationListenerInfo listener) { 2482 // In enqueueNotificationInternal notifications are added by scheduling the 2483 // work on the worker handler. Hence, we also schedule the cancel on this 2484 // handler to avoid a scenario where an add notification call followed by a 2485 // remove notification call ends up in not removing the notification. 2486 mHandler.post(new Runnable() { 2487 @Override 2488 public void run() { 2489 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2490 mustHaveFlags, mustNotHaveFlags, reason, 2491 listener == null ? null : listener.component.toShortString()); 2492 2493 synchronized (mNotificationList) { 2494 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2495 if (index >= 0) { 2496 NotificationRecord r = mNotificationList.get(index); 2497 2498 // Ideally we'd do this in the caller of this method. However, that would 2499 // require the caller to also find the notification. 2500 if (reason == REASON_DELEGATE_CLICK) { 2501 mUsageStats.registerClickedByUser(r); 2502 } 2503 2504 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2505 return; 2506 } 2507 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2508 return; 2509 } 2510 2511 mNotificationList.remove(index); 2512 mNotificationsByKey.remove(r.sbn.getKey()); 2513 2514 cancelNotificationLocked(r, sendDelete, reason); 2515 updateLightsLocked(); 2516 } 2517 } 2518 } 2519 }); 2520 } 2521 2522 /** 2523 * Determine whether the userId applies to the notification in question, either because 2524 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2525 */ 2526 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2527 return 2528 // looking for USER_ALL notifications? match everything 2529 userId == UserHandle.USER_ALL 2530 // a notification sent to USER_ALL matches any query 2531 || r.getUserId() == UserHandle.USER_ALL 2532 // an exact user match 2533 || r.getUserId() == userId; 2534 } 2535 2536 /** 2537 * Determine whether the userId applies to the notification in question, either because 2538 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2539 * because it matches one of the users profiles. 2540 */ 2541 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2542 return notificationMatchesUserId(r, userId) 2543 || isCurrentProfile(r.getUserId()); 2544 } 2545 2546 /** 2547 * Cancels all notifications from a given package that have all of the 2548 * {@code mustHaveFlags}. 2549 */ 2550 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2551 int mustNotHaveFlags, boolean doit, int userId, int reason, 2552 NotificationListenerInfo listener) { 2553 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2554 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2555 listener == null ? null : listener.component.toShortString()); 2556 2557 synchronized (mNotificationList) { 2558 final int N = mNotificationList.size(); 2559 boolean canceledSomething = false; 2560 for (int i = N-1; i >= 0; --i) { 2561 NotificationRecord r = mNotificationList.get(i); 2562 if (!notificationMatchesUserId(r, userId)) { 2563 continue; 2564 } 2565 // Don't remove notifications to all, if there's no package name specified 2566 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2567 continue; 2568 } 2569 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2570 continue; 2571 } 2572 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2573 continue; 2574 } 2575 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2576 continue; 2577 } 2578 canceledSomething = true; 2579 if (!doit) { 2580 return true; 2581 } 2582 mNotificationList.remove(i); 2583 mNotificationsByKey.remove(r.sbn.getKey()); 2584 cancelNotificationLocked(r, false, reason); 2585 } 2586 if (canceledSomething) { 2587 updateLightsLocked(); 2588 } 2589 return canceledSomething; 2590 } 2591 } 2592 2593 2594 2595 // Return true if the UID is a system or phone UID and therefore should not have 2596 // any notifications or toasts blocked. 2597 boolean isUidSystem(int uid) { 2598 final int appid = UserHandle.getAppId(uid); 2599 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2600 } 2601 2602 // same as isUidSystem(int, int) for the Binder caller's UID. 2603 boolean isCallerSystem() { 2604 return isUidSystem(Binder.getCallingUid()); 2605 } 2606 2607 void checkCallerIsSystem() { 2608 if (isCallerSystem()) { 2609 return; 2610 } 2611 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2612 } 2613 2614 void checkCallerIsSystemOrSameApp(String pkg) { 2615 if (isCallerSystem()) { 2616 return; 2617 } 2618 final int uid = Binder.getCallingUid(); 2619 try { 2620 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2621 pkg, 0, UserHandle.getCallingUserId()); 2622 if (!UserHandle.isSameApp(ai.uid, uid)) { 2623 throw new SecurityException("Calling uid " + uid + " gave package" 2624 + pkg + " which is owned by uid " + ai.uid); 2625 } 2626 } catch (RemoteException re) { 2627 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2628 } 2629 } 2630 2631 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2632 NotificationListenerInfo listener, boolean includeCurrentProfiles) { 2633 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2634 null, userId, 0, 0, reason, 2635 listener == null ? null : listener.component.toShortString()); 2636 2637 final int N = mNotificationList.size(); 2638 for (int i=N-1; i>=0; i--) { 2639 NotificationRecord r = mNotificationList.get(i); 2640 if (includeCurrentProfiles) { 2641 if (!notificationMatchesCurrentProfiles(r, userId)) { 2642 continue; 2643 } 2644 } else { 2645 if (!notificationMatchesUserId(r, userId)) { 2646 continue; 2647 } 2648 } 2649 2650 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2651 | Notification.FLAG_NO_CLEAR)) == 0) { 2652 mNotificationList.remove(i); 2653 mNotificationsByKey.remove(r.sbn.getKey()); 2654 cancelNotificationLocked(r, true, reason); 2655 } 2656 } 2657 updateLightsLocked(); 2658 } 2659 2660 // lock on mNotificationList 2661 void updateLightsLocked() 2662 { 2663 // handle notification lights 2664 if (mLedNotification == null) { 2665 // get next notification, if any 2666 int n = mLights.size(); 2667 if (n > 0) { 2668 mLedNotification = mLights.get(n-1); 2669 } 2670 } 2671 2672 // Don't flash while we are in a call or screen is on 2673 if (mLedNotification == null || mInCall || mScreenOn) { 2674 mNotificationLight.turnOff(); 2675 } else { 2676 final Notification ledno = mLedNotification.sbn.getNotification(); 2677 int ledARGB = ledno.ledARGB; 2678 int ledOnMS = ledno.ledOnMS; 2679 int ledOffMS = ledno.ledOffMS; 2680 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2681 ledARGB = mDefaultNotificationColor; 2682 ledOnMS = mDefaultNotificationLedOn; 2683 ledOffMS = mDefaultNotificationLedOff; 2684 } 2685 if (mNotificationPulseEnabled) { 2686 // pulse repeatedly 2687 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2688 ledOnMS, ledOffMS); 2689 } 2690 } 2691 } 2692 2693 // lock on mNotificationList 2694 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2695 { 2696 ArrayList<NotificationRecord> list = mNotificationList; 2697 final int len = list.size(); 2698 for (int i=0; i<len; i++) { 2699 NotificationRecord r = list.get(i); 2700 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2701 continue; 2702 } 2703 if (tag == null) { 2704 if (r.sbn.getTag() != null) { 2705 continue; 2706 } 2707 } else { 2708 if (!tag.equals(r.sbn.getTag())) { 2709 continue; 2710 } 2711 } 2712 if (r.sbn.getPackageName().equals(pkg)) { 2713 return i; 2714 } 2715 } 2716 return -1; 2717 } 2718 2719 private void updateNotificationPulse() { 2720 synchronized (mNotificationList) { 2721 updateLightsLocked(); 2722 } 2723 } 2724 2725 private void updateZenMode() { 2726 final int mode = Settings.Global.getInt(getContext().getContentResolver(), 2727 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); 2728 if (mode != mZenMode) { 2729 Slog.d(TAG, String.format("updateZenMode: %s -> %s", 2730 Settings.Global.zenModeToString(mZenMode), 2731 Settings.Global.zenModeToString(mode))); 2732 } 2733 mZenMode = mode; 2734 2735 final String[] exceptionPackages = null; // none (for now) 2736 2737 // call restrictions 2738 final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF; 2739 mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING, 2740 muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 2741 exceptionPackages); 2742 mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING, 2743 muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 2744 exceptionPackages); 2745 2746 // alarm restrictions 2747 final boolean muteAlarms = false; // TODO until we save user config 2748 mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM, 2749 muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 2750 exceptionPackages); 2751 mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM, 2752 muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 2753 exceptionPackages); 2754 2755 // restrict vibrations with no hints 2756 mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE, 2757 (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 2758 exceptionPackages); 2759 } 2760 2761 private void updateCurrentProfilesCache(Context context) { 2762 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 2763 if (userManager != null) { 2764 int currentUserId = ActivityManager.getCurrentUser(); 2765 List<UserInfo> profiles = userManager.getProfiles(currentUserId); 2766 synchronized (mCurrentProfiles) { 2767 mCurrentProfiles.clear(); 2768 for (UserInfo user : profiles) { 2769 mCurrentProfiles.put(user.id, user); 2770 } 2771 } 2772 } 2773 } 2774 2775 private int[] getCurrentProfileIds() { 2776 synchronized (mCurrentProfiles) { 2777 int[] users = new int[mCurrentProfiles.size()]; 2778 final int N = mCurrentProfiles.size(); 2779 for (int i = 0; i < N; ++i) { 2780 users[i] = mCurrentProfiles.keyAt(i); 2781 } 2782 return users; 2783 } 2784 } 2785 2786 private boolean isCurrentProfile(int userId) { 2787 synchronized (mCurrentProfiles) { 2788 return mCurrentProfiles.get(userId) != null; 2789 } 2790 } 2791 2792 private boolean isCall(String pkg, Notification n) { 2793 return CALL_PACKAGES.contains(pkg); 2794 } 2795 2796 private boolean isAlarm(String pkg, Notification n) { 2797 return ALARM_PACKAGES.contains(pkg); 2798 } 2799 2800 private boolean shouldIntercept(String pkg, Notification n) { 2801 if (mZenMode != Settings.Global.ZEN_MODE_OFF) { 2802 return !isAlarm(pkg, n); 2803 } 2804 return false; 2805 } 2806} 2807