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