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