NotificationManagerService.java revision 008232acbdc9b6988f498b619b686efd02e614ae
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 android.service.notification.NotificationListenerService.TRIM_FULL; 21import static android.service.notification.NotificationListenerService.TRIM_LIGHT; 22import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 23import static org.xmlpull.v1.XmlPullParser.END_TAG; 24import static org.xmlpull.v1.XmlPullParser.START_TAG; 25 26import android.app.ActivityManager; 27import android.app.ActivityManagerNative; 28import android.app.AppGlobals; 29import android.app.AppOpsManager; 30import android.app.IActivityManager; 31import android.app.INotificationManager; 32import android.app.INotificationManagerCallback; 33import android.app.ITransientNotification; 34import android.app.Notification; 35import android.app.NotificationManager; 36import android.app.NotificationManager.Policy; 37import android.app.PendingIntent; 38import android.app.StatusBarManager; 39import android.app.usage.UsageEvents; 40import android.app.usage.UsageStatsManagerInternal; 41import android.content.BroadcastReceiver; 42import android.content.ComponentName; 43import android.content.ContentResolver; 44import android.content.Context; 45import android.content.Intent; 46import android.content.IntentFilter; 47import android.content.pm.ApplicationInfo; 48import android.content.pm.IPackageManager; 49import android.content.pm.PackageInfo; 50import android.content.pm.PackageManager; 51import android.content.pm.PackageManager.NameNotFoundException; 52import android.content.pm.ParceledListSlice; 53import android.content.res.Resources; 54import android.database.ContentObserver; 55import android.media.AudioAttributes; 56import android.media.AudioManager; 57import android.media.AudioManagerInternal; 58import android.media.AudioSystem; 59import android.media.IRingtonePlayer; 60import android.net.Uri; 61import android.os.Binder; 62import android.os.Build; 63import android.os.Bundle; 64import android.os.Environment; 65import android.os.Handler; 66import android.os.HandlerThread; 67import android.os.IBinder; 68import android.os.IInterface; 69import android.os.Looper; 70import android.os.Message; 71import android.os.Process; 72import android.os.RemoteException; 73import android.os.SystemProperties; 74import android.os.UserHandle; 75import android.os.Vibrator; 76import android.provider.Settings; 77import android.service.notification.Condition; 78import android.service.notification.IConditionListener; 79import android.service.notification.IConditionProvider; 80import android.service.notification.INotificationListener; 81import android.service.notification.IStatusBarNotificationHolder; 82import android.service.notification.NotificationListenerService; 83import android.service.notification.NotificationRankingUpdate; 84import android.service.notification.StatusBarNotification; 85import android.service.notification.ZenModeConfig; 86import android.telephony.PhoneStateListener; 87import android.telephony.TelephonyManager; 88import android.text.TextUtils; 89import android.util.ArrayMap; 90import android.util.ArraySet; 91import android.util.AtomicFile; 92import android.util.Log; 93import android.util.Slog; 94import android.util.Xml; 95import android.view.accessibility.AccessibilityEvent; 96import android.view.accessibility.AccessibilityManager; 97import android.widget.Toast; 98 99import com.android.internal.R; 100import com.android.internal.util.FastXmlSerializer; 101import com.android.server.EventLogTags; 102import com.android.server.LocalServices; 103import com.android.server.SystemService; 104import com.android.server.lights.Light; 105import com.android.server.lights.LightsManager; 106import com.android.server.notification.ManagedServices.ManagedServiceInfo; 107import com.android.server.notification.ManagedServices.UserProfiles; 108import com.android.server.statusbar.StatusBarManagerInternal; 109 110import libcore.io.IoUtils; 111 112import org.xmlpull.v1.XmlPullParser; 113import org.xmlpull.v1.XmlPullParserException; 114import org.xmlpull.v1.XmlSerializer; 115 116import java.io.File; 117import java.io.FileDescriptor; 118import java.io.FileInputStream; 119import java.io.FileNotFoundException; 120import java.io.FileOutputStream; 121import java.io.IOException; 122import java.io.PrintWriter; 123import java.nio.charset.StandardCharsets; 124import java.util.ArrayDeque; 125import java.util.ArrayList; 126import java.util.HashSet; 127import java.util.Iterator; 128import java.util.Map.Entry; 129import java.util.Objects; 130 131/** {@hide} */ 132public class NotificationManagerService extends SystemService { 133 static final String TAG = "NotificationService"; 134 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 135 public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE 136 && SystemProperties.getBoolean("debug.child_notifs", false); 137 138 static final int MAX_PACKAGE_NOTIFICATIONS = 50; 139 140 // message codes 141 static final int MESSAGE_TIMEOUT = 2; 142 static final int MESSAGE_SAVE_POLICY_FILE = 3; 143 static final int MESSAGE_RECONSIDER_RANKING = 4; 144 static final int MESSAGE_RANKING_CONFIG_CHANGE = 5; 145 static final int MESSAGE_SEND_RANKING_UPDATE = 6; 146 static final int MESSAGE_LISTENER_HINTS_CHANGED = 7; 147 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8; 148 149 static final int LONG_DELAY = 3500; // 3.5 seconds 150 static final int SHORT_DELAY = 2000; // 2 seconds 151 152 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; 153 154 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps 155 156 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; 157 static final boolean SCORE_ONGOING_HIGHER = false; 158 159 static final int JUNK_SCORE = -1000; 160 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; 161 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER; 162 163 // Notifications with scores below this will not interrupt the user, either via LED or 164 // sound or vibration 165 static final int SCORE_INTERRUPTION_THRESHOLD = 166 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; 167 168 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; 169 static final boolean ENABLE_BLOCKED_TOASTS = true; 170 171 // When #matchesCallFilter is called from the ringer, wait at most 172 // 3s to resolve the contacts. This timeout is required since 173 // ContactsProvider might take a long time to start up. 174 // 175 // Return STARRED_CONTACT when the timeout is hit in order to avoid 176 // missed calls in ZEN mode "Important". 177 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000; 178 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY = 179 ValidateNotificationPeople.STARRED_CONTACT; 180 181 /** notification_enqueue status value for a newly enqueued notification. */ 182 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0; 183 184 /** notification_enqueue status value for an existing notification. */ 185 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1; 186 187 /** notification_enqueue status value for an ignored notification. */ 188 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2; 189 190 private IActivityManager mAm; 191 AudioManager mAudioManager; 192 AudioManagerInternal mAudioManagerInternal; 193 StatusBarManagerInternal mStatusBar; 194 Vibrator mVibrator; 195 196 final IBinder mForegroundToken = new Binder(); 197 private WorkerHandler mHandler; 198 private final HandlerThread mRankingThread = new HandlerThread("ranker", 199 Process.THREAD_PRIORITY_BACKGROUND); 200 201 private Light mNotificationLight; 202 Light mAttentionLight; 203 private int mDefaultNotificationColor; 204 private int mDefaultNotificationLedOn; 205 206 private int mDefaultNotificationLedOff; 207 private long[] mDefaultVibrationPattern; 208 209 private long[] mFallbackVibrationPattern; 210 private boolean mUseAttentionLight; 211 boolean mSystemReady; 212 213 private boolean mDisableNotificationEffects; 214 private int mCallState; 215 private String mSoundNotificationKey; 216 private String mVibrateNotificationKey; 217 218 private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>(); 219 private ComponentName mEffectsSuppressor; 220 private int mListenerHints; // right now, all hints are global 221 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN; 222 223 // for enabling and disabling notification pulse behavior 224 private boolean mScreenOn = true; 225 private boolean mInCall = false; 226 private boolean mNotificationPulseEnabled; 227 228 // used as a mutex for access to all active notifications & listeners 229 final ArrayList<NotificationRecord> mNotificationList = 230 new ArrayList<NotificationRecord>(); 231 final ArrayMap<String, NotificationRecord> mNotificationsByKey = 232 new ArrayMap<String, NotificationRecord>(); 233 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); 234 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>(); 235 private final ArrayMap<String, Boolean> mPolicyAccess = new ArrayMap<>(); 236 237 238 // The last key in this list owns the hardware. 239 ArrayList<String> mLights = new ArrayList<>(); 240 241 private AppOpsManager mAppOps; 242 private UsageStatsManagerInternal mAppUsageStats; 243 244 private Archive mArchive; 245 246 // Persistent storage for notification policy 247 private AtomicFile mPolicyFile; 248 249 // Temporary holder for <blocked-packages> config coming from old policy files. 250 private HashSet<String> mBlockedPackages = new HashSet<String>(); 251 252 private static final int DB_VERSION = 1; 253 254 private static final String TAG_NOTIFICATION_POLICY = "notification-policy"; 255 private static final String ATTR_VERSION = "version"; 256 257 // Obsolete: converted if present, but not resaved to disk. 258 private static final String TAG_BLOCKED_PKGS = "blocked-packages"; 259 private static final String TAG_PACKAGE = "package"; 260 private static final String ATTR_NAME = "name"; 261 262 private RankingHelper mRankingHelper; 263 264 private final UserProfiles mUserProfiles = new UserProfiles(); 265 private NotificationListeners mListeners; 266 private ConditionProviders mConditionProviders; 267 private NotificationUsageStats mUsageStats; 268 269 private static final int MY_UID = Process.myUid(); 270 private static final int MY_PID = Process.myPid(); 271 private static final int REASON_DELEGATE_CLICK = 1; 272 private static final int REASON_DELEGATE_CANCEL = 2; 273 private static final int REASON_DELEGATE_CANCEL_ALL = 3; 274 private static final int REASON_DELEGATE_ERROR = 4; 275 private static final int REASON_PACKAGE_CHANGED = 5; 276 private static final int REASON_USER_STOPPED = 6; 277 private static final int REASON_PACKAGE_BANNED = 7; 278 private static final int REASON_NOMAN_CANCEL = 8; 279 private static final int REASON_NOMAN_CANCEL_ALL = 9; 280 private static final int REASON_LISTENER_CANCEL = 10; 281 private static final int REASON_LISTENER_CANCEL_ALL = 11; 282 private static final int REASON_GROUP_SUMMARY_CANCELED = 12; 283 private static final int REASON_GROUP_OPTIMIZATION = 13; 284 285 private static class Archive { 286 final int mBufferSize; 287 final ArrayDeque<StatusBarNotification> mBuffer; 288 289 public Archive(int size) { 290 mBufferSize = size; 291 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize); 292 } 293 294 public String toString() { 295 final StringBuilder sb = new StringBuilder(); 296 final int N = mBuffer.size(); 297 sb.append("Archive ("); 298 sb.append(N); 299 sb.append(" notification"); 300 sb.append((N==1)?")":"s)"); 301 return sb.toString(); 302 } 303 304 public void record(StatusBarNotification nr) { 305 if (mBuffer.size() == mBufferSize) { 306 mBuffer.removeFirst(); 307 } 308 309 // We don't want to store the heavy bits of the notification in the archive, 310 // but other clients in the system process might be using the object, so we 311 // store a (lightened) copy. 312 mBuffer.addLast(nr.cloneLight()); 313 } 314 315 public Iterator<StatusBarNotification> descendingIterator() { 316 return mBuffer.descendingIterator(); 317 } 318 319 public StatusBarNotification[] getArray(int count) { 320 if (count == 0) count = mBufferSize; 321 final StatusBarNotification[] a 322 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 323 Iterator<StatusBarNotification> iter = descendingIterator(); 324 int i=0; 325 while (iter.hasNext() && i < count) { 326 a[i++] = iter.next(); 327 } 328 return a; 329 } 330 331 } 332 333 private void loadPolicyFile() { 334 if (DBG) Slog.d(TAG, "loadPolicyFile"); 335 synchronized(mPolicyFile) { 336 mBlockedPackages.clear(); 337 338 FileInputStream infile = null; 339 try { 340 infile = mPolicyFile.openRead(); 341 final XmlPullParser parser = Xml.newPullParser(); 342 parser.setInput(infile, StandardCharsets.UTF_8.name()); 343 344 int type; 345 String tag; 346 int version = DB_VERSION; 347 while ((type = parser.next()) != END_DOCUMENT) { 348 tag = parser.getName(); 349 if (type == START_TAG) { 350 if (TAG_NOTIFICATION_POLICY.equals(tag)) { 351 version = Integer.parseInt( 352 parser.getAttributeValue(null, ATTR_VERSION)); 353 } else if (TAG_BLOCKED_PKGS.equals(tag)) { 354 while ((type = parser.next()) != END_DOCUMENT) { 355 tag = parser.getName(); 356 if (TAG_PACKAGE.equals(tag)) { 357 mBlockedPackages.add( 358 parser.getAttributeValue(null, ATTR_NAME)); 359 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { 360 break; 361 } 362 } 363 } 364 } 365 mZenModeHelper.readXml(parser); 366 mRankingHelper.readXml(parser); 367 } 368 } catch (FileNotFoundException e) { 369 // No data yet 370 } catch (IOException e) { 371 Log.wtf(TAG, "Unable to read notification policy", e); 372 } catch (NumberFormatException e) { 373 Log.wtf(TAG, "Unable to parse notification policy", e); 374 } catch (XmlPullParserException e) { 375 Log.wtf(TAG, "Unable to parse notification policy", e); 376 } finally { 377 IoUtils.closeQuietly(infile); 378 } 379 } 380 } 381 382 public void savePolicyFile() { 383 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE); 384 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE); 385 } 386 387 private void handleSavePolicyFile() { 388 if (DBG) Slog.d(TAG, "handleSavePolicyFile"); 389 synchronized (mPolicyFile) { 390 final FileOutputStream stream; 391 try { 392 stream = mPolicyFile.startWrite(); 393 } catch (IOException e) { 394 Slog.w(TAG, "Failed to save policy file", e); 395 return; 396 } 397 398 try { 399 final XmlSerializer out = new FastXmlSerializer(); 400 out.setOutput(stream, StandardCharsets.UTF_8.name()); 401 out.startDocument(null, true); 402 out.startTag(null, TAG_NOTIFICATION_POLICY); 403 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); 404 mZenModeHelper.writeXml(out); 405 mRankingHelper.writeXml(out); 406 out.endTag(null, TAG_NOTIFICATION_POLICY); 407 out.endDocument(); 408 mPolicyFile.finishWrite(stream); 409 } catch (IOException e) { 410 Slog.w(TAG, "Failed to save policy file, restoring backup", e); 411 mPolicyFile.failWrite(stream); 412 } 413 } 414 } 415 416 /** Use this when you actually want to post a notification or toast. 417 * 418 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*(). 419 */ 420 private boolean noteNotificationOp(String pkg, int uid) { 421 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 422 != AppOpsManager.MODE_ALLOWED) { 423 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg); 424 return false; 425 } 426 return true; 427 } 428 429 private static final class ToastRecord 430 { 431 final int pid; 432 final String pkg; 433 final ITransientNotification callback; 434 int duration; 435 436 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) 437 { 438 this.pid = pid; 439 this.pkg = pkg; 440 this.callback = callback; 441 this.duration = duration; 442 } 443 444 void update(int duration) { 445 this.duration = duration; 446 } 447 448 void dump(PrintWriter pw, String prefix, DumpFilter filter) { 449 if (filter != null && !filter.matches(pkg)) return; 450 pw.println(prefix + this); 451 } 452 453 @Override 454 public final String toString() 455 { 456 return "ToastRecord{" 457 + Integer.toHexString(System.identityHashCode(this)) 458 + " pkg=" + pkg 459 + " callback=" + callback 460 + " duration=" + duration; 461 } 462 } 463 464 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() { 465 466 @Override 467 public void onSetDisabled(int status) { 468 synchronized (mNotificationList) { 469 mDisableNotificationEffects = 470 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; 471 if (disableNotificationEffects(null) != null) { 472 // cancel whatever's going on 473 long identity = Binder.clearCallingIdentity(); 474 try { 475 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 476 if (player != null) { 477 player.stopAsync(); 478 } 479 } catch (RemoteException e) { 480 } finally { 481 Binder.restoreCallingIdentity(identity); 482 } 483 484 identity = Binder.clearCallingIdentity(); 485 try { 486 mVibrator.cancel(); 487 } finally { 488 Binder.restoreCallingIdentity(identity); 489 } 490 } 491 } 492 } 493 494 @Override 495 public void onClearAll(int callingUid, int callingPid, int userId) { 496 synchronized (mNotificationList) { 497 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null, 498 /*includeCurrentProfiles*/ true); 499 } 500 } 501 502 @Override 503 public void onNotificationClick(int callingUid, int callingPid, String key) { 504 synchronized (mNotificationList) { 505 NotificationRecord r = mNotificationsByKey.get(key); 506 if (r == null) { 507 Log.w(TAG, "No notification with key: " + key); 508 return; 509 } 510 final long now = System.currentTimeMillis(); 511 EventLogTags.writeNotificationClicked(key, 512 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now)); 513 514 StatusBarNotification sbn = r.sbn; 515 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(), 516 sbn.getId(), Notification.FLAG_AUTO_CANCEL, 517 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(), 518 REASON_DELEGATE_CLICK, null); 519 } 520 } 521 522 @Override 523 public void onNotificationActionClick(int callingUid, int callingPid, String key, 524 int actionIndex) { 525 synchronized (mNotificationList) { 526 NotificationRecord r = mNotificationsByKey.get(key); 527 if (r == null) { 528 Log.w(TAG, "No notification with key: " + key); 529 return; 530 } 531 final long now = System.currentTimeMillis(); 532 EventLogTags.writeNotificationActionClicked(key, actionIndex, 533 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now)); 534 // TODO: Log action click via UsageStats. 535 } 536 } 537 538 @Override 539 public void onNotificationClear(int callingUid, int callingPid, 540 String pkg, String tag, int id, int userId) { 541 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 542 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 543 true, userId, REASON_DELEGATE_CANCEL, null); 544 } 545 546 @Override 547 public void onPanelRevealed(boolean clearEffects) { 548 EventLogTags.writeNotificationPanelRevealed(); 549 if (clearEffects) { 550 clearEffects(); 551 } 552 } 553 554 @Override 555 public void onPanelHidden() { 556 EventLogTags.writeNotificationPanelHidden(); 557 } 558 559 @Override 560 public void clearEffects() { 561 synchronized (mNotificationList) { 562 if (DBG) Slog.d(TAG, "clearEffects"); 563 564 // sound 565 mSoundNotificationKey = null; 566 567 long identity = Binder.clearCallingIdentity(); 568 try { 569 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 570 if (player != null) { 571 player.stopAsync(); 572 } 573 } catch (RemoteException e) { 574 } finally { 575 Binder.restoreCallingIdentity(identity); 576 } 577 578 // vibrate 579 mVibrateNotificationKey = null; 580 identity = Binder.clearCallingIdentity(); 581 try { 582 mVibrator.cancel(); 583 } finally { 584 Binder.restoreCallingIdentity(identity); 585 } 586 587 // light 588 mLights.clear(); 589 updateLightsLocked(); 590 } 591 } 592 593 @Override 594 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id, 595 int uid, int initialPid, String message, int userId) { 596 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id 597 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")"); 598 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId, 599 REASON_DELEGATE_ERROR, null); 600 long ident = Binder.clearCallingIdentity(); 601 try { 602 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, 603 "Bad notification posted from package " + pkg 604 + ": " + message); 605 } catch (RemoteException e) { 606 } 607 Binder.restoreCallingIdentity(ident); 608 } 609 610 @Override 611 public void onNotificationVisibilityChanged( 612 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) { 613 // Using ';' as separator since eventlogs uses ',' to separate 614 // args. 615 // TODO remove this: b/21248682 616 EventLogTags.writeNotificationVisibilityChanged( 617 TextUtils.join(";", newlyVisibleKeys), 618 TextUtils.join(";", noLongerVisibleKeys)); 619 synchronized (mNotificationList) { 620 for (String key : newlyVisibleKeys) { 621 NotificationRecord r = mNotificationsByKey.get(key); 622 if (r == null) continue; 623 r.setVisibility(true); 624 } 625 // Note that we might receive this event after notifications 626 // have already left the system, e.g. after dismissing from the 627 // shade. Hence not finding notifications in 628 // mNotificationsByKey is not an exceptional condition. 629 for (String key : noLongerVisibleKeys) { 630 NotificationRecord r = mNotificationsByKey.get(key); 631 if (r == null) continue; 632 r.setVisibility(false); 633 } 634 } 635 } 636 637 @Override 638 public void onNotificationExpansionChanged(String key, 639 boolean userAction, boolean expanded) { 640 synchronized (mNotificationList) { 641 NotificationRecord r = mNotificationsByKey.get(key); 642 if (r != null) { 643 r.stats.onExpansionChanged(userAction, expanded); 644 final long now = System.currentTimeMillis(); 645 EventLogTags.writeNotificationExpansion(key, 646 userAction ? 1 : 0, expanded ? 1 : 0, 647 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now)); 648 } 649 } 650 } 651 }; 652 653 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() { 654 @Override 655 public void onReceive(Context context, Intent intent) { 656 String action = intent.getAction(); 657 if (action == null) { 658 return; 659 } 660 661 boolean queryRestart = false; 662 boolean queryRemove = false; 663 boolean packageChanged = false; 664 boolean cancelNotifications = true; 665 666 if (action.equals(Intent.ACTION_PACKAGE_ADDED) 667 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED)) 668 || action.equals(Intent.ACTION_PACKAGE_RESTARTED) 669 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED)) 670 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) 671 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 672 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 673 UserHandle.USER_ALL); 674 String pkgList[] = null; 675 boolean queryReplace = queryRemove && 676 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 677 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace); 678 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 679 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 680 } else if (queryRestart) { 681 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 682 } else { 683 Uri uri = intent.getData(); 684 if (uri == null) { 685 return; 686 } 687 String pkgName = uri.getSchemeSpecificPart(); 688 if (pkgName == null) { 689 return; 690 } 691 if (packageChanged) { 692 // We cancel notifications for packages which have just been disabled 693 try { 694 final IPackageManager pm = AppGlobals.getPackageManager(); 695 final int enabled = pm.getApplicationEnabledSetting(pkgName, 696 changeUserId != UserHandle.USER_ALL ? changeUserId : 697 UserHandle.USER_OWNER); 698 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED 699 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { 700 cancelNotifications = false; 701 } 702 } catch (IllegalArgumentException e) { 703 // Package doesn't exist; probably racing with uninstall. 704 // cancelNotifications is already true, so nothing to do here. 705 if (DBG) { 706 Slog.i(TAG, "Exception trying to look up app enabled setting", e); 707 } 708 } catch (RemoteException e) { 709 // Failed to talk to PackageManagerService Should never happen! 710 } 711 } 712 pkgList = new String[]{pkgName}; 713 } 714 715 if (pkgList != null && (pkgList.length > 0)) { 716 for (String pkgName : pkgList) { 717 if (cancelNotifications) { 718 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart, 719 changeUserId, REASON_PACKAGE_CHANGED, null); 720 } 721 } 722 } 723 mListeners.onPackagesChanged(queryReplace, pkgList); 724 mConditionProviders.onPackagesChanged(queryReplace, pkgList); 725 } 726 } 727 }; 728 729 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 730 @Override 731 public void onReceive(Context context, Intent intent) { 732 String action = intent.getAction(); 733 734 if (action.equals(Intent.ACTION_SCREEN_ON)) { 735 // Keep track of screen on/off state, but do not turn off the notification light 736 // until user passes through the lock screen or views the notification. 737 mScreenOn = true; 738 updateNotificationPulse(); 739 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 740 mScreenOn = false; 741 updateNotificationPulse(); 742 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 743 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK 744 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE)); 745 updateNotificationPulse(); 746 } else if (action.equals(Intent.ACTION_USER_STOPPED)) { 747 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 748 if (userHandle >= 0) { 749 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle, 750 REASON_USER_STOPPED, null); 751 } 752 } else if (action.equals(Intent.ACTION_USER_PRESENT)) { 753 // turn off LED when user passes through lock screen 754 mNotificationLight.turnOff(); 755 mStatusBar.notificationLightOff(); 756 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { 757 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 758 // reload per-user settings 759 mSettingsObserver.update(null); 760 mUserProfiles.updateCache(context); 761 // Refresh managed services 762 mConditionProviders.onUserSwitched(user); 763 mListeners.onUserSwitched(user); 764 mZenModeHelper.onUserSwitched(user); 765 } else if (action.equals(Intent.ACTION_USER_ADDED)) { 766 mUserProfiles.updateCache(context); 767 } else if (action.equals(Intent.ACTION_USER_REMOVED)) { 768 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 769 mZenModeHelper.onUserRemoved(user); 770 } 771 } 772 }; 773 774 class SettingsObserver extends ContentObserver { 775 private final Uri NOTIFICATION_LIGHT_PULSE_URI 776 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); 777 778 SettingsObserver(Handler handler) { 779 super(handler); 780 } 781 782 void observe() { 783 ContentResolver resolver = getContext().getContentResolver(); 784 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, 785 false, this, UserHandle.USER_ALL); 786 update(null); 787 } 788 789 @Override public void onChange(boolean selfChange, Uri uri) { 790 update(uri); 791 } 792 793 public void update(Uri uri) { 794 ContentResolver resolver = getContext().getContentResolver(); 795 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) { 796 boolean pulseEnabled = Settings.System.getInt(resolver, 797 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; 798 if (mNotificationPulseEnabled != pulseEnabled) { 799 mNotificationPulseEnabled = pulseEnabled; 800 updateNotificationPulse(); 801 } 802 } 803 } 804 } 805 806 private SettingsObserver mSettingsObserver; 807 private ZenModeHelper mZenModeHelper; 808 809 private final Runnable mBuzzBeepBlinked = new Runnable() { 810 @Override 811 public void run() { 812 mStatusBar.buzzBeepBlinked(); 813 } 814 }; 815 816 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) { 817 int[] ar = r.getIntArray(resid); 818 if (ar == null) { 819 return def; 820 } 821 final int len = ar.length > maxlen ? maxlen : ar.length; 822 long[] out = new long[len]; 823 for (int i=0; i<len; i++) { 824 out[i] = ar[i]; 825 } 826 return out; 827 } 828 829 public NotificationManagerService(Context context) { 830 super(context); 831 } 832 833 @Override 834 public void onStart() { 835 Resources resources = getContext().getResources(); 836 837 mAm = ActivityManagerNative.getDefault(); 838 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); 839 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); 840 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); 841 842 mHandler = new WorkerHandler(); 843 mRankingThread.start(); 844 String[] extractorNames; 845 try { 846 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors); 847 } catch (Resources.NotFoundException e) { 848 extractorNames = new String[0]; 849 } 850 mRankingHelper = new RankingHelper(getContext(), 851 new RankingWorkerHandler(mRankingThread.getLooper()), 852 extractorNames); 853 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles); 854 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders); 855 mZenModeHelper.addCallback(new ZenModeHelper.Callback() { 856 @Override 857 public void onConfigChanged() { 858 savePolicyFile(); 859 } 860 861 @Override 862 void onZenModeChanged() { 863 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED); 864 synchronized(mNotificationList) { 865 updateInterruptionFilterLocked(); 866 } 867 } 868 869 @Override 870 void onPolicyChanged() { 871 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED); 872 } 873 }); 874 final File systemDir = new File(Environment.getDataDirectory(), "system"); 875 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml")); 876 mUsageStats = new NotificationUsageStats(getContext()); 877 878 importOldBlockDb(); 879 880 mListeners = new NotificationListeners(); 881 mStatusBar = getLocalService(StatusBarManagerInternal.class); 882 mStatusBar.setNotificationDelegate(mNotificationDelegate); 883 884 final LightsManager lights = getLocalService(LightsManager.class); 885 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS); 886 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION); 887 888 mDefaultNotificationColor = resources.getColor( 889 R.color.config_defaultNotificationColor); 890 mDefaultNotificationLedOn = resources.getInteger( 891 R.integer.config_defaultNotificationLedOn); 892 mDefaultNotificationLedOff = resources.getInteger( 893 R.integer.config_defaultNotificationLedOff); 894 895 mDefaultVibrationPattern = getLongArray(resources, 896 R.array.config_defaultNotificationVibePattern, 897 VIBRATE_PATTERN_MAXLEN, 898 DEFAULT_VIBRATE_PATTERN); 899 900 mFallbackVibrationPattern = getLongArray(resources, 901 R.array.config_notificationFallbackVibePattern, 902 VIBRATE_PATTERN_MAXLEN, 903 DEFAULT_VIBRATE_PATTERN); 904 905 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight); 906 907 // Don't start allowing notifications until the setup wizard has run once. 908 // After that, including subsequent boots, init with notifications turned on. 909 // This works on the first boot because the setup wizard will toggle this 910 // flag at least once and we'll go back to 0 after that. 911 if (0 == Settings.Global.getInt(getContext().getContentResolver(), 912 Settings.Global.DEVICE_PROVISIONED, 0)) { 913 mDisableNotificationEffects = true; 914 } 915 mZenModeHelper.initZenMode(); 916 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter(); 917 918 mUserProfiles.updateCache(getContext()); 919 listenForCallState(); 920 921 // register for various Intents 922 IntentFilter filter = new IntentFilter(); 923 filter.addAction(Intent.ACTION_SCREEN_ON); 924 filter.addAction(Intent.ACTION_SCREEN_OFF); 925 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 926 filter.addAction(Intent.ACTION_USER_PRESENT); 927 filter.addAction(Intent.ACTION_USER_STOPPED); 928 filter.addAction(Intent.ACTION_USER_SWITCHED); 929 filter.addAction(Intent.ACTION_USER_ADDED); 930 filter.addAction(Intent.ACTION_USER_REMOVED); 931 getContext().registerReceiver(mIntentReceiver, filter); 932 933 IntentFilter pkgFilter = new IntentFilter(); 934 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 935 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 936 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 937 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 938 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 939 pkgFilter.addDataScheme("package"); 940 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null, 941 null); 942 943 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 944 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null, 945 null); 946 947 mSettingsObserver = new SettingsObserver(mHandler); 948 949 mArchive = new Archive(resources.getInteger( 950 R.integer.config_notificationServiceArchiveSize)); 951 952 publishBinderService(Context.NOTIFICATION_SERVICE, mService); 953 publishLocalService(NotificationManagerInternal.class, mInternalService); 954 } 955 956 private void sendRegisteredOnlyBroadcast(String action) { 957 getContext().sendBroadcastAsUser(new Intent(action) 958 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null); 959 } 960 961 /** 962 * Read the old XML-based app block database and import those blockages into the AppOps system. 963 */ 964 private void importOldBlockDb() { 965 loadPolicyFile(); 966 967 PackageManager pm = getContext().getPackageManager(); 968 for (String pkg : mBlockedPackages) { 969 PackageInfo info = null; 970 try { 971 info = pm.getPackageInfo(pkg, 0); 972 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false); 973 } catch (NameNotFoundException e) { 974 // forget you 975 } 976 } 977 mBlockedPackages.clear(); 978 } 979 980 @Override 981 public void onBootPhase(int phase) { 982 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 983 // no beeping until we're basically done booting 984 mSystemReady = true; 985 986 // Grab our optional AudioService 987 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 988 mAudioManagerInternal = getLocalService(AudioManagerInternal.class); 989 mZenModeHelper.onSystemReady(); 990 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 991 // This observer will force an update when observe is called, causing us to 992 // bind to listener services. 993 mSettingsObserver.observe(); 994 mListeners.onBootPhaseAppsCanStart(); 995 mConditionProviders.onBootPhaseAppsCanStart(); 996 } 997 } 998 999 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) { 1000 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); 1001 1002 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, 1003 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 1004 1005 // Now, cancel any outstanding notifications that are part of a just-disabled app 1006 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { 1007 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid), 1008 REASON_PACKAGE_BANNED, null); 1009 } 1010 } 1011 1012 private void updateListenerHintsLocked() { 1013 final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS; 1014 if (hints == mListenerHints) return; 1015 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mListenersDisablingEffects.size()); 1016 mListenerHints = hints; 1017 scheduleListenerHintsChanged(hints); 1018 } 1019 1020 private void updateEffectsSuppressorLocked() { 1021 final ComponentName suppressor = !mListenersDisablingEffects.isEmpty() 1022 ? mListenersDisablingEffects.valueAt(0).component : null; 1023 if (Objects.equals(suppressor, mEffectsSuppressor)) return; 1024 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressor, suppressor); 1025 mEffectsSuppressor = suppressor; 1026 mZenModeHelper.setEffectsSuppressed(suppressor != null); 1027 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED); 1028 } 1029 1030 private void updateInterruptionFilterLocked() { 1031 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter(); 1032 if (interruptionFilter == mInterruptionFilter) return; 1033 mInterruptionFilter = interruptionFilter; 1034 scheduleInterruptionFilterChanged(interruptionFilter); 1035 } 1036 1037 private final IBinder mService = new INotificationManager.Stub() { 1038 // Toasts 1039 // ============================================================================ 1040 1041 @Override 1042 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 1043 { 1044 if (DBG) { 1045 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback 1046 + " duration=" + duration); 1047 } 1048 1049 if (pkg == null || callback == null) { 1050 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 1051 return ; 1052 } 1053 1054 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); 1055 1056 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { 1057 if (!isSystemToast) { 1058 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 1059 return; 1060 } 1061 } 1062 1063 synchronized (mToastQueue) { 1064 int callingPid = Binder.getCallingPid(); 1065 long callingId = Binder.clearCallingIdentity(); 1066 try { 1067 ToastRecord record; 1068 int index = indexOfToastLocked(pkg, callback); 1069 // If it's already in the queue, we update it in place, we don't 1070 // move it to the end of the queue. 1071 if (index >= 0) { 1072 record = mToastQueue.get(index); 1073 record.update(duration); 1074 } else { 1075 // Limit the number of toasts that any given package except the android 1076 // package can enqueue. Prevents DOS attacks and deals with leaks. 1077 if (!isSystemToast) { 1078 int count = 0; 1079 final int N = mToastQueue.size(); 1080 for (int i=0; i<N; i++) { 1081 final ToastRecord r = mToastQueue.get(i); 1082 if (r.pkg.equals(pkg)) { 1083 count++; 1084 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1085 Slog.e(TAG, "Package has already posted " + count 1086 + " toasts. Not showing more. Package=" + pkg); 1087 return; 1088 } 1089 } 1090 } 1091 } 1092 1093 record = new ToastRecord(callingPid, pkg, callback, duration); 1094 mToastQueue.add(record); 1095 index = mToastQueue.size() - 1; 1096 keepProcessAliveLocked(callingPid); 1097 } 1098 // If it's at index 0, it's the current toast. It doesn't matter if it's 1099 // new or just been updated. Call back and tell it to show itself. 1100 // If the callback fails, this will remove it from the list, so don't 1101 // assume that it's valid after this. 1102 if (index == 0) { 1103 showNextToastLocked(); 1104 } 1105 } finally { 1106 Binder.restoreCallingIdentity(callingId); 1107 } 1108 } 1109 } 1110 1111 @Override 1112 public void cancelToast(String pkg, ITransientNotification callback) { 1113 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 1114 1115 if (pkg == null || callback == null) { 1116 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 1117 return ; 1118 } 1119 1120 synchronized (mToastQueue) { 1121 long callingId = Binder.clearCallingIdentity(); 1122 try { 1123 int index = indexOfToastLocked(pkg, callback); 1124 if (index >= 0) { 1125 cancelToastLocked(index); 1126 } else { 1127 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg 1128 + " callback=" + callback); 1129 } 1130 } finally { 1131 Binder.restoreCallingIdentity(callingId); 1132 } 1133 } 1134 } 1135 1136 @Override 1137 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, 1138 Notification notification, int[] idOut, int userId) throws RemoteException { 1139 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(), 1140 Binder.getCallingPid(), tag, id, notification, idOut, userId); 1141 } 1142 1143 @Override 1144 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 1145 checkCallerIsSystemOrSameApp(pkg); 1146 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1147 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 1148 // Don't allow client applications to cancel foreground service notis. 1149 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0, 1150 Binder.getCallingUid() == Process.SYSTEM_UID 1151 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL, 1152 null); 1153 } 1154 1155 @Override 1156 public void cancelAllNotifications(String pkg, int userId) { 1157 checkCallerIsSystemOrSameApp(pkg); 1158 1159 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1160 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 1161 1162 // Calling from user space, don't allow the canceling of actively 1163 // running foreground services. 1164 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(), 1165 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId, 1166 REASON_NOMAN_CANCEL_ALL, null); 1167 } 1168 1169 @Override 1170 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { 1171 checkCallerIsSystem(); 1172 1173 setNotificationsEnabledForPackageImpl(pkg, uid, enabled); 1174 } 1175 1176 /** 1177 * Use this when you just want to know if notifications are OK for this package. 1178 */ 1179 @Override 1180 public boolean areNotificationsEnabledForPackage(String pkg, int uid) { 1181 checkCallerIsSystem(); 1182 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 1183 == AppOpsManager.MODE_ALLOWED); 1184 } 1185 1186 @Override 1187 public void setPackagePriority(String pkg, int uid, int priority) { 1188 checkCallerIsSystem(); 1189 mRankingHelper.setPackagePriority(pkg, uid, priority); 1190 savePolicyFile(); 1191 } 1192 1193 @Override 1194 public int getPackagePriority(String pkg, int uid) { 1195 checkCallerIsSystem(); 1196 return mRankingHelper.getPackagePriority(pkg, uid); 1197 } 1198 1199 @Override 1200 public void setPackagePeekable(String pkg, int uid, boolean peekable) { 1201 checkCallerIsSystem(); 1202 1203 mRankingHelper.setPackagePeekable(pkg, uid, peekable); 1204 } 1205 1206 @Override 1207 public boolean getPackagePeekable(String pkg, int uid) { 1208 checkCallerIsSystem(); 1209 return mRankingHelper.getPackagePeekable(pkg, uid); 1210 } 1211 1212 @Override 1213 public void setPackageVisibilityOverride(String pkg, int uid, int visibility) { 1214 checkCallerIsSystem(); 1215 mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility); 1216 savePolicyFile(); 1217 } 1218 1219 @Override 1220 public int getPackageVisibilityOverride(String pkg, int uid) { 1221 checkCallerIsSystem(); 1222 return mRankingHelper.getPackageVisibilityOverride(pkg, uid); 1223 } 1224 1225 /** 1226 * System-only API for getting a list of current (i.e. not cleared) notifications. 1227 * 1228 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1229 * @returns A list of all the notifications, in natural order. 1230 */ 1231 @Override 1232 public StatusBarNotification[] getActiveNotifications(String callingPkg) { 1233 // enforce() will ensure the calling uid has the correct permission 1234 getContext().enforceCallingOrSelfPermission( 1235 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1236 "NotificationManagerService.getActiveNotifications"); 1237 1238 StatusBarNotification[] tmp = null; 1239 int uid = Binder.getCallingUid(); 1240 1241 // noteOp will check to make sure the callingPkg matches the uid 1242 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1243 == AppOpsManager.MODE_ALLOWED) { 1244 synchronized (mNotificationList) { 1245 tmp = new StatusBarNotification[mNotificationList.size()]; 1246 final int N = mNotificationList.size(); 1247 for (int i=0; i<N; i++) { 1248 tmp[i] = mNotificationList.get(i).sbn; 1249 } 1250 } 1251 } 1252 return tmp; 1253 } 1254 1255 /** 1256 * Public API for getting a list of current notifications for the calling package/uid. 1257 * 1258 * @returns A list of all the package's notifications, in natural order. 1259 */ 1260 @Override 1261 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg, 1262 int incomingUserId) { 1263 checkCallerIsSystemOrSameApp(pkg); 1264 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1265 Binder.getCallingUid(), incomingUserId, true, false, 1266 "getAppActiveNotifications", pkg); 1267 1268 final int N = mNotificationList.size(); 1269 final ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>(N); 1270 1271 synchronized (mNotificationList) { 1272 for (int i = 0; i < N; i++) { 1273 final StatusBarNotification sbn = mNotificationList.get(i).sbn; 1274 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) { 1275 // We could pass back a cloneLight() but clients might get confused and 1276 // try to send this thing back to notify() again, which would not work 1277 // very well. 1278 final StatusBarNotification sbnOut = new StatusBarNotification( 1279 sbn.getPackageName(), 1280 sbn.getOpPkg(), 1281 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), 1282 0, // hide score from apps 1283 sbn.getNotification().clone(), 1284 sbn.getUser(), sbn.getPostTime()); 1285 list.add(sbnOut); 1286 } 1287 } 1288 } 1289 1290 return new ParceledListSlice<StatusBarNotification>(list); 1291 } 1292 1293 /** 1294 * System-only API for getting a list of recent (cleared, no longer shown) notifications. 1295 * 1296 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1297 */ 1298 @Override 1299 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { 1300 // enforce() will ensure the calling uid has the correct permission 1301 getContext().enforceCallingOrSelfPermission( 1302 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1303 "NotificationManagerService.getHistoricalNotifications"); 1304 1305 StatusBarNotification[] tmp = null; 1306 int uid = Binder.getCallingUid(); 1307 1308 // noteOp will check to make sure the callingPkg matches the uid 1309 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1310 == AppOpsManager.MODE_ALLOWED) { 1311 synchronized (mArchive) { 1312 tmp = mArchive.getArray(count); 1313 } 1314 } 1315 return tmp; 1316 } 1317 1318 /** 1319 * Register a listener binder directly with the notification manager. 1320 * 1321 * Only works with system callers. Apps should extend 1322 * {@link android.service.notification.NotificationListenerService}. 1323 */ 1324 @Override 1325 public void registerListener(final INotificationListener listener, 1326 final ComponentName component, final int userid) { 1327 enforceSystemOrSystemUI("INotificationManager.registerListener"); 1328 mListeners.registerService(listener, component, userid); 1329 } 1330 1331 /** 1332 * Remove a listener binder directly 1333 */ 1334 @Override 1335 public void unregisterListener(INotificationListener listener, int userid) { 1336 mListeners.unregisterService(listener, userid); 1337 } 1338 1339 /** 1340 * Allow an INotificationListener to simulate a "clear all" operation. 1341 * 1342 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} 1343 * 1344 * @param token The binder for the listener, to check that the caller is allowed 1345 */ 1346 @Override 1347 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) { 1348 final int callingUid = Binder.getCallingUid(); 1349 final int callingPid = Binder.getCallingPid(); 1350 long identity = Binder.clearCallingIdentity(); 1351 try { 1352 synchronized (mNotificationList) { 1353 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1354 if (keys != null) { 1355 final int N = keys.length; 1356 for (int i = 0; i < N; i++) { 1357 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1358 if (r == null) continue; 1359 final int userId = r.sbn.getUserId(); 1360 if (userId != info.userid && userId != UserHandle.USER_ALL && 1361 !mUserProfiles.isCurrentProfile(userId)) { 1362 throw new SecurityException("Disallowed call from listener: " 1363 + info.service); 1364 } 1365 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1366 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(), 1367 userId); 1368 } 1369 } else { 1370 cancelAllLocked(callingUid, callingPid, info.userid, 1371 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles()); 1372 } 1373 } 1374 } finally { 1375 Binder.restoreCallingIdentity(identity); 1376 } 1377 } 1378 1379 @Override 1380 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) { 1381 long identity = Binder.clearCallingIdentity(); 1382 try { 1383 synchronized (mNotificationList) { 1384 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1385 if (keys != null) { 1386 final int N = keys.length; 1387 for (int i = 0; i < N; i++) { 1388 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1389 if (r == null) continue; 1390 final int userId = r.sbn.getUserId(); 1391 if (userId != info.userid && userId != UserHandle.USER_ALL && 1392 !mUserProfiles.isCurrentProfile(userId)) { 1393 throw new SecurityException("Disallowed call from listener: " 1394 + info.service); 1395 } 1396 if (!r.isSeen()) { 1397 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]); 1398 mAppUsageStats.reportEvent(r.sbn.getPackageName(), 1399 userId == UserHandle.USER_ALL ? UserHandle.USER_OWNER 1400 : userId, 1401 UsageEvents.Event.INTERACTION); 1402 r.setSeen(); 1403 } 1404 } 1405 } 1406 } 1407 } finally { 1408 Binder.restoreCallingIdentity(identity); 1409 } 1410 } 1411 1412 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info, 1413 int callingUid, int callingPid, String pkg, String tag, int id, int userId) { 1414 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 1415 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 1416 true, 1417 userId, REASON_LISTENER_CANCEL, info); 1418 } 1419 1420 /** 1421 * Allow an INotificationListener to simulate clearing (dismissing) a single notification. 1422 * 1423 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} 1424 * 1425 * @param token The binder for the listener, to check that the caller is allowed 1426 */ 1427 @Override 1428 public void cancelNotificationFromListener(INotificationListener token, String pkg, 1429 String tag, int id) { 1430 final int callingUid = Binder.getCallingUid(); 1431 final int callingPid = Binder.getCallingPid(); 1432 long identity = Binder.clearCallingIdentity(); 1433 try { 1434 synchronized (mNotificationList) { 1435 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1436 if (info.supportsProfiles()) { 1437 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) " 1438 + "from " + info.component 1439 + " use cancelNotification(key) instead."); 1440 } else { 1441 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1442 pkg, tag, id, info.userid); 1443 } 1444 } 1445 } finally { 1446 Binder.restoreCallingIdentity(identity); 1447 } 1448 } 1449 1450 /** 1451 * Allow an INotificationListener to request the list of outstanding notifications seen by 1452 * the current user. Useful when starting up, after which point the listener callbacks 1453 * should be used. 1454 * 1455 * @param token The binder for the listener, to check that the caller is allowed 1456 * @param keys An array of notification keys to fetch, or null to fetch everything 1457 * @returns The return value will contain the notifications specified in keys, in that 1458 * order, or if keys is null, all the notifications, in natural order. 1459 */ 1460 @Override 1461 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener( 1462 INotificationListener token, String[] keys, int trim) { 1463 synchronized (mNotificationList) { 1464 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1465 final boolean getKeys = keys != null; 1466 final int N = getKeys ? keys.length : mNotificationList.size(); 1467 final ArrayList<StatusBarNotification> list 1468 = new ArrayList<StatusBarNotification>(N); 1469 for (int i=0; i<N; i++) { 1470 final NotificationRecord r = getKeys 1471 ? mNotificationsByKey.get(keys[i]) 1472 : mNotificationList.get(i); 1473 if (r == null) continue; 1474 StatusBarNotification sbn = r.sbn; 1475 if (!isVisibleToListener(sbn, info)) continue; 1476 StatusBarNotification sbnToSend = 1477 (trim == TRIM_FULL) ? sbn : sbn.cloneLight(); 1478 list.add(sbnToSend); 1479 } 1480 return new ParceledListSlice<StatusBarNotification>(list); 1481 } 1482 } 1483 1484 @Override 1485 public void requestHintsFromListener(INotificationListener token, int hints) { 1486 final long identity = Binder.clearCallingIdentity(); 1487 try { 1488 synchronized (mNotificationList) { 1489 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1490 final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0; 1491 if (disableEffects) { 1492 mListenersDisablingEffects.add(info); 1493 } else { 1494 mListenersDisablingEffects.remove(info); 1495 } 1496 updateListenerHintsLocked(); 1497 updateEffectsSuppressorLocked(); 1498 } 1499 } finally { 1500 Binder.restoreCallingIdentity(identity); 1501 } 1502 } 1503 1504 @Override 1505 public int getHintsFromListener(INotificationListener token) { 1506 synchronized (mNotificationList) { 1507 return mListenerHints; 1508 } 1509 } 1510 1511 @Override 1512 public void requestInterruptionFilterFromListener(INotificationListener token, 1513 int interruptionFilter) throws RemoteException { 1514 final long identity = Binder.clearCallingIdentity(); 1515 try { 1516 synchronized (mNotificationList) { 1517 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1518 mZenModeHelper.requestFromListener(info.component, interruptionFilter); 1519 updateInterruptionFilterLocked(); 1520 } 1521 } finally { 1522 Binder.restoreCallingIdentity(identity); 1523 } 1524 } 1525 1526 @Override 1527 public int getInterruptionFilterFromListener(INotificationListener token) 1528 throws RemoteException { 1529 synchronized (mNotificationLight) { 1530 return mInterruptionFilter; 1531 } 1532 } 1533 1534 @Override 1535 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim) 1536 throws RemoteException { 1537 synchronized (mNotificationList) { 1538 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1539 if (info == null) return; 1540 mListeners.setOnNotificationPostedTrimLocked(info, trim); 1541 } 1542 } 1543 1544 @Override 1545 public int getZenMode() { 1546 return mZenModeHelper.getZenMode(); 1547 } 1548 1549 @Override 1550 public ZenModeConfig getZenModeConfig() { 1551 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig"); 1552 return mZenModeHelper.getConfig(); 1553 } 1554 1555 @Override 1556 public boolean setZenModeConfig(ZenModeConfig config, String reason) { 1557 checkCallerIsSystem(); 1558 return mZenModeHelper.setConfig(config, reason); 1559 } 1560 1561 @Override 1562 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException { 1563 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode"); 1564 final long identity = Binder.clearCallingIdentity(); 1565 try { 1566 mZenModeHelper.setManualZenMode(mode, conditionId, reason); 1567 } finally { 1568 Binder.restoreCallingIdentity(identity); 1569 } 1570 } 1571 1572 @Override 1573 public void setInterruptionFilter(String pkg, int filter) throws RemoteException { 1574 enforcePolicyAccess(pkg, "setInterruptionFilter"); 1575 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1); 1576 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter); 1577 final long identity = Binder.clearCallingIdentity(); 1578 try { 1579 mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter"); 1580 } finally { 1581 Binder.restoreCallingIdentity(identity); 1582 } 1583 } 1584 1585 @Override 1586 public void notifyConditions(final String pkg, IConditionProvider provider, 1587 final Condition[] conditions) { 1588 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider); 1589 checkCallerIsSystemOrSameApp(pkg); 1590 mHandler.post(new Runnable() { 1591 @Override 1592 public void run() { 1593 mConditionProviders.notifyConditions(pkg, info, conditions); 1594 } 1595 }); 1596 } 1597 1598 @Override 1599 public void requestZenModeConditions(IConditionListener callback, int relevance) { 1600 enforceSystemOrSystemUIOrVolume("INotificationManager.requestZenModeConditions"); 1601 mZenModeHelper.requestZenModeConditions(callback, relevance); 1602 } 1603 1604 private void enforceSystemOrSystemUIOrVolume(String message) { 1605 if (mAudioManagerInternal != null) { 1606 final int vcuid = mAudioManagerInternal.getVolumeControllerUid(); 1607 if (vcuid > 0 && Binder.getCallingUid() == vcuid) { 1608 return; 1609 } 1610 } 1611 enforceSystemOrSystemUI(message); 1612 } 1613 1614 private void enforceSystemOrSystemUI(String message) { 1615 if (isCallerSystem()) return; 1616 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 1617 message); 1618 } 1619 1620 private void enforcePolicyAccess(String pkg, String method) { 1621 if (!checkPolicyAccess(pkg)) { 1622 Slog.w(TAG, "Notification policy access denied calling " + method); 1623 throw new SecurityException("Notification policy access denied"); 1624 } 1625 } 1626 1627 private boolean checkPackagePolicyAccess(String pkg) { 1628 return Boolean.TRUE.equals(mPolicyAccess.get(pkg)); 1629 } 1630 1631 private boolean checkPolicyAccess(String pkg) { 1632 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg); 1633 } 1634 1635 @Override 1636 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1637 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1638 != PackageManager.PERMISSION_GRANTED) { 1639 pw.println("Permission Denial: can't dump NotificationManager from pid=" 1640 + Binder.getCallingPid() 1641 + ", uid=" + Binder.getCallingUid()); 1642 return; 1643 } 1644 1645 dumpImpl(pw, DumpFilter.parseFromArguments(args)); 1646 } 1647 1648 @Override 1649 public ComponentName getEffectsSuppressor() { 1650 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor"); 1651 return mEffectsSuppressor; 1652 } 1653 1654 @Override 1655 public boolean matchesCallFilter(Bundle extras) { 1656 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter"); 1657 return mZenModeHelper.matchesCallFilter( 1658 UserHandle.getCallingUserHandle(), 1659 extras, 1660 mRankingHelper.findExtractor(ValidateNotificationPeople.class), 1661 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS, 1662 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY); 1663 } 1664 1665 @Override 1666 public boolean isSystemConditionProviderEnabled(String path) { 1667 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled"); 1668 return mConditionProviders.isSystemProviderEnabled(path); 1669 } 1670 1671 // Backup/restore interface 1672 @Override 1673 public byte[] getBackupPayload(int user) { 1674 // TODO: build a payload of whatever is appropriate 1675 return null; 1676 } 1677 1678 @Override 1679 public void applyRestore(byte[] payload, int user) { 1680 // TODO: apply the restored payload as new current state 1681 } 1682 1683 @Override 1684 public void requestNotificationPolicyAccess(String pkg, 1685 INotificationManagerCallback callback) throws RemoteException { 1686 if (callback == null) { 1687 Slog.w(TAG, "requestNotificationPolicyAccess: no callback specified"); 1688 return; 1689 } 1690 if (pkg == null) { 1691 Slog.w(TAG, "requestNotificationPolicyAccess denied: no package specified"); 1692 callback.onPolicyRequestResult(false); 1693 return; 1694 } 1695 final long identity = Binder.clearCallingIdentity(); 1696 try { 1697 synchronized (mNotificationList) { 1698 // immediately grant for now 1699 mPolicyAccess.put(pkg, true); 1700 if (DBG) Slog.w(TAG, "requestNotificationPolicyAccess granted for " + pkg); 1701 } 1702 } finally { 1703 Binder.restoreCallingIdentity(identity); 1704 } 1705 callback.onPolicyRequestResult(true); 1706 } 1707 1708 @Override 1709 public boolean isNotificationPolicyAccessGranted(String pkg) { 1710 return checkPolicyAccess(pkg); 1711 } 1712 1713 @Override 1714 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) { 1715 enforceSystemOrSystemUI("request policy access status for another package"); 1716 return checkPackagePolicyAccess(pkg); 1717 } 1718 1719 @Override 1720 public String[] getPackagesRequestingNotificationPolicyAccess() 1721 throws RemoteException { 1722 enforceSystemOrSystemUI("request policy access packages"); 1723 final long identity = Binder.clearCallingIdentity(); 1724 try { 1725 synchronized (mNotificationList) { 1726 final String[] rt = new String[mPolicyAccess.size()]; 1727 for (int i = 0; i < mPolicyAccess.size(); i++) { 1728 rt[i] = mPolicyAccess.keyAt(i); 1729 } 1730 return rt; 1731 } 1732 } finally { 1733 Binder.restoreCallingIdentity(identity); 1734 } 1735 } 1736 1737 @Override 1738 public void setNotificationPolicyAccessGranted(String pkg, boolean granted) 1739 throws RemoteException { 1740 enforceSystemOrSystemUI("grant notification policy access"); 1741 final long identity = Binder.clearCallingIdentity(); 1742 try { 1743 synchronized (mNotificationList) { 1744 mPolicyAccess.put(pkg, granted); 1745 } 1746 } finally { 1747 Binder.restoreCallingIdentity(identity); 1748 } 1749 } 1750 1751 @Override 1752 public Policy getNotificationPolicy(String pkg) { 1753 enforcePolicyAccess(pkg, "getNotificationPolicy"); 1754 final long identity = Binder.clearCallingIdentity(); 1755 try { 1756 return mZenModeHelper.getNotificationPolicy(); 1757 } finally { 1758 Binder.restoreCallingIdentity(identity); 1759 } 1760 } 1761 1762 @Override 1763 public void setNotificationPolicy(String pkg, Policy policy) { 1764 enforcePolicyAccess(pkg, "setNotificationPolicy"); 1765 final long identity = Binder.clearCallingIdentity(); 1766 try { 1767 mZenModeHelper.setNotificationPolicy(policy); 1768 } finally { 1769 Binder.restoreCallingIdentity(identity); 1770 } 1771 } 1772 }; 1773 1774 private String disableNotificationEffects(NotificationRecord record) { 1775 if (mDisableNotificationEffects) { 1776 return "booleanState"; 1777 } 1778 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) { 1779 return "listenerHints"; 1780 } 1781 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) { 1782 return "callState"; 1783 } 1784 return null; 1785 } 1786 1787 void dumpImpl(PrintWriter pw, DumpFilter filter) { 1788 pw.print("Current Notification Manager state"); 1789 if (filter != null) { 1790 pw.print(" (filtered to "); pw.print(filter); pw.print(")"); 1791 } 1792 pw.println(':'); 1793 int N; 1794 final boolean zenOnly = filter != null && filter.zen; 1795 1796 if (!zenOnly) { 1797 synchronized (mToastQueue) { 1798 N = mToastQueue.size(); 1799 if (N > 0) { 1800 pw.println(" Toast Queue:"); 1801 for (int i=0; i<N; i++) { 1802 mToastQueue.get(i).dump(pw, " ", filter); 1803 } 1804 pw.println(" "); 1805 } 1806 } 1807 } 1808 1809 synchronized (mNotificationList) { 1810 if (!zenOnly) { 1811 N = mNotificationList.size(); 1812 if (N > 0) { 1813 pw.println(" Notification List:"); 1814 for (int i=0; i<N; i++) { 1815 final NotificationRecord nr = mNotificationList.get(i); 1816 if (filter != null && !filter.matches(nr.sbn)) continue; 1817 nr.dump(pw, " ", getContext()); 1818 } 1819 pw.println(" "); 1820 } 1821 1822 if (filter == null) { 1823 N = mLights.size(); 1824 if (N > 0) { 1825 pw.println(" Lights List:"); 1826 for (int i=0; i<N; i++) { 1827 if (i == N - 1) { 1828 pw.print(" > "); 1829 } else { 1830 pw.print(" "); 1831 } 1832 pw.println(mLights.get(i)); 1833 } 1834 pw.println(" "); 1835 } 1836 pw.println(" mUseAttentionLight=" + mUseAttentionLight); 1837 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled); 1838 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey); 1839 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey); 1840 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects); 1841 pw.println(" mCallState=" + callStateToString(mCallState)); 1842 pw.println(" mSystemReady=" + mSystemReady); 1843 } 1844 pw.println(" mArchive=" + mArchive.toString()); 1845 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1846 int i=0; 1847 while (iter.hasNext()) { 1848 final StatusBarNotification sbn = iter.next(); 1849 if (filter != null && !filter.matches(sbn)) continue; 1850 pw.println(" " + sbn); 1851 if (++i >= 5) { 1852 if (iter.hasNext()) pw.println(" ..."); 1853 break; 1854 } 1855 } 1856 } 1857 1858 if (!zenOnly) { 1859 pw.println("\n Usage Stats:"); 1860 mUsageStats.dump(pw, " ", filter); 1861 } 1862 1863 if (filter == null || zenOnly) { 1864 pw.println("\n Zen Mode:"); 1865 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter); 1866 mZenModeHelper.dump(pw, " "); 1867 1868 pw.println("\n Zen Log:"); 1869 ZenLog.dump(pw, " "); 1870 } 1871 1872 if (!zenOnly) { 1873 pw.println("\n Ranking Config:"); 1874 mRankingHelper.dump(pw, " ", filter); 1875 1876 pw.println("\n Notification listeners:"); 1877 mListeners.dump(pw, filter); 1878 pw.print(" mListenerHints: "); pw.println(mListenerHints); 1879 pw.print(" mListenersDisablingEffects: ("); 1880 N = mListenersDisablingEffects.size(); 1881 for (int i = 0; i < N; i++) { 1882 final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i); 1883 if (i > 0) pw.print(','); 1884 pw.print(listener.component); 1885 } 1886 pw.println(')'); 1887 } 1888 pw.println("\n Policy access:"); 1889 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess); 1890 1891 pw.println("\n Condition providers:"); 1892 mConditionProviders.dump(pw, filter); 1893 1894 pw.println("\n Group summaries:"); 1895 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) { 1896 NotificationRecord r = entry.getValue(); 1897 pw.println(" " + entry.getKey() + " -> " + r.getKey()); 1898 if (mNotificationsByKey.get(r.getKey()) != r) { 1899 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey."); 1900 r.dump(pw, " ", getContext()); 1901 } 1902 } 1903 } 1904 } 1905 1906 /** 1907 * The private API only accessible to the system process. 1908 */ 1909 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1910 @Override 1911 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, 1912 String tag, int id, Notification notification, int[] idReceived, int userId) { 1913 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 1914 idReceived, userId); 1915 } 1916 1917 @Override 1918 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId, 1919 int userId) { 1920 checkCallerIsSystem(); 1921 synchronized (mNotificationList) { 1922 int i = indexOfNotificationLocked(pkg, null, notificationId, userId); 1923 if (i < 0) { 1924 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with " 1925 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId); 1926 return; 1927 } 1928 NotificationRecord r = mNotificationList.get(i); 1929 StatusBarNotification sbn = r.sbn; 1930 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees 1931 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE, 1932 // we have to revert to the flags we received initially *and* force remove 1933 // FLAG_FOREGROUND_SERVICE. 1934 sbn.getNotification().flags = 1935 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE); 1936 mRankingHelper.sort(mNotificationList); 1937 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */); 1938 } 1939 } 1940 }; 1941 1942 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, 1943 final int callingPid, final String tag, final int id, final Notification notification, 1944 int[] idOut, int incomingUserId) { 1945 if (DBG) { 1946 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 1947 + " notification=" + notification); 1948 } 1949 checkCallerIsSystemOrSameApp(pkg); 1950 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 1951 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg); 1952 1953 final int userId = ActivityManager.handleIncomingUser(callingPid, 1954 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1955 final UserHandle user = new UserHandle(userId); 1956 1957 // Limit the number of notifications that any given package except the android 1958 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks. 1959 if (!isSystemNotification && !isNotificationFromListener) { 1960 synchronized (mNotificationList) { 1961 int count = 0; 1962 final int N = mNotificationList.size(); 1963 for (int i=0; i<N; i++) { 1964 final NotificationRecord r = mNotificationList.get(i); 1965 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1966 count++; 1967 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1968 Slog.e(TAG, "Package has already posted " + count 1969 + " notifications. Not showing more. package=" + pkg); 1970 return; 1971 } 1972 } 1973 } 1974 } 1975 } 1976 1977 if (pkg == null || notification == null) { 1978 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1979 + " id=" + id + " notification=" + notification); 1980 } 1981 1982 if (notification.getSmallIcon() != null) { 1983 if (!notification.isValid()) { 1984 throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg 1985 + " id=" + id + " notification=" + notification); 1986 } 1987 } 1988 1989 mHandler.post(new Runnable() { 1990 @Override 1991 public void run() { 1992 1993 synchronized (mNotificationList) { 1994 1995 // === Scoring === 1996 1997 // 0. Sanitize inputs 1998 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1999 Notification.PRIORITY_MAX); 2000 // Migrate notification flags to scores 2001 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 2002 if (notification.priority < Notification.PRIORITY_MAX) { 2003 notification.priority = Notification.PRIORITY_MAX; 2004 } 2005 } else if (SCORE_ONGOING_HIGHER && 2006 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 2007 if (notification.priority < Notification.PRIORITY_HIGH) { 2008 notification.priority = Notification.PRIORITY_HIGH; 2009 } 2010 } 2011 // force no heads up per package config 2012 if (!mRankingHelper.getPackagePeekable(pkg, callingUid)) { 2013 if (notification.extras == null) { 2014 notification.extras = new Bundle(); 2015 } 2016 notification.extras.putInt(Notification.EXTRA_AS_HEADS_UP, 2017 Notification.HEADS_UP_NEVER); 2018 } 2019 2020 // 1. initial score: buckets of 10, around the app [-20..20] 2021 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; 2022 2023 // 2. extract ranking signals from the notification data 2024 final StatusBarNotification n = new StatusBarNotification( 2025 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 2026 user); 2027 NotificationRecord r = new NotificationRecord(n, score); 2028 NotificationRecord old = mNotificationsByKey.get(n.getKey()); 2029 if (old != null) { 2030 // Retain ranking information from previous record 2031 r.copyRankingInformation(old); 2032 } 2033 2034 // Handle grouped notifications and bail out early if we 2035 // can to avoid extracting signals. 2036 handleGroupedNotificationLocked(r, old, callingUid, callingPid); 2037 boolean ignoreNotification = 2038 removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid); 2039 2040 // This conditional is a dirty hack to limit the logging done on 2041 // behalf of the download manager without affecting other apps. 2042 if (!pkg.equals("com.android.providers.downloads") 2043 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 2044 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW; 2045 if (ignoreNotification) { 2046 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED; 2047 } else if (old != null) { 2048 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE; 2049 } 2050 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 2051 pkg, id, tag, userId, notification.toString(), 2052 enqueueStatus); 2053 } 2054 2055 if (ignoreNotification) { 2056 return; 2057 } 2058 2059 mRankingHelper.extractSignals(r); 2060 2061 // 3. Apply local rules 2062 2063 // blocked apps 2064 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 2065 if (!isSystemNotification) { 2066 r.score = JUNK_SCORE; 2067 Slog.e(TAG, "Suppressing notification from package " + pkg 2068 + " by user request."); 2069 } 2070 } 2071 2072 if (r.score < SCORE_DISPLAY_THRESHOLD) { 2073 // Notification will be blocked because the score is too low. 2074 return; 2075 } 2076 2077 int index = indexOfNotificationLocked(n.getKey()); 2078 if (index < 0) { 2079 mNotificationList.add(r); 2080 mUsageStats.registerPostedByApp(r); 2081 } else { 2082 old = mNotificationList.get(index); 2083 mNotificationList.set(index, r); 2084 mUsageStats.registerUpdatedByApp(r, old); 2085 // Make sure we don't lose the foreground service state. 2086 notification.flags |= 2087 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 2088 r.isUpdate = true; 2089 } 2090 2091 mNotificationsByKey.put(n.getKey(), r); 2092 2093 // Ensure if this is a foreground service that the proper additional 2094 // flags are set. 2095 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { 2096 notification.flags |= Notification.FLAG_ONGOING_EVENT 2097 | Notification.FLAG_NO_CLEAR; 2098 } 2099 2100 applyZenModeLocked(r); 2101 mRankingHelper.sort(mNotificationList); 2102 2103 if (notification.getSmallIcon() != null) { 2104 StatusBarNotification oldSbn = (old != null) ? old.sbn : null; 2105 mListeners.notifyPostedLocked(n, oldSbn); 2106 } else { 2107 Slog.e(TAG, "Not posting notification without small icon: " + notification); 2108 if (old != null && !old.isCanceled) { 2109 mListeners.notifyRemovedLocked(n); 2110 } 2111 // ATTENTION: in a future release we will bail out here 2112 // so that we do not play sounds, show lights, etc. for invalid 2113 // notifications 2114 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 2115 + n.getPackageName()); 2116 } 2117 2118 buzzBeepBlinkLocked(r); 2119 } 2120 } 2121 }); 2122 2123 idOut[0] = id; 2124 } 2125 2126 /** 2127 * Ensures that grouped notification receive their special treatment. 2128 * 2129 * <p>Cancels group children if the new notification causes a group to lose 2130 * its summary.</p> 2131 * 2132 * <p>Updates mSummaryByGroupKey.</p> 2133 */ 2134 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old, 2135 int callingUid, int callingPid) { 2136 StatusBarNotification sbn = r.sbn; 2137 Notification n = sbn.getNotification(); 2138 String group = sbn.getGroupKey(); 2139 boolean isSummary = n.isGroupSummary(); 2140 2141 Notification oldN = old != null ? old.sbn.getNotification() : null; 2142 String oldGroup = old != null ? old.sbn.getGroupKey() : null; 2143 boolean oldIsSummary = old != null && oldN.isGroupSummary(); 2144 2145 if (oldIsSummary) { 2146 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup); 2147 if (removedSummary != old) { 2148 String removedKey = 2149 removedSummary != null ? removedSummary.getKey() : "<null>"; 2150 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() + 2151 ", removed=" + removedKey); 2152 } 2153 } 2154 if (isSummary) { 2155 mSummaryByGroupKey.put(group, r); 2156 } 2157 2158 // Clear out group children of the old notification if the update 2159 // causes the group summary to go away. This happens when the old 2160 // notification was a summary and the new one isn't, or when the old 2161 // notification was a summary and its group key changed. 2162 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) { 2163 cancelGroupChildrenLocked(old, callingUid, callingPid, null, 2164 REASON_GROUP_SUMMARY_CANCELED); 2165 } 2166 } 2167 2168 /** 2169 * Performs group notification optimizations if SysUI is the only active 2170 * notification listener and returns whether the given notification should 2171 * be ignored. 2172 * 2173 * <p>Returns true if the given notification is a child of a group with a 2174 * summary, which means that SysUI will never show it, and hence the new 2175 * notification can be safely ignored. Also cancels any previous instance 2176 * of the ignored notification.</p> 2177 * 2178 * <p>For summaries, cancels all children of that group, as SysUI will 2179 * never show them anymore.</p> 2180 * 2181 * @return true if the given notification can be ignored as an optimization 2182 */ 2183 private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r, 2184 NotificationRecord old, int callingUid, int callingPid) { 2185 if (!ENABLE_CHILD_NOTIFICATIONS) { 2186 // No optimizations are possible if listeners want groups. 2187 if (mListeners.notificationGroupsDesired()) { 2188 return false; 2189 } 2190 2191 StatusBarNotification sbn = r.sbn; 2192 String group = sbn.getGroupKey(); 2193 boolean isSummary = sbn.getNotification().isGroupSummary(); 2194 boolean isChild = sbn.getNotification().isGroupChild(); 2195 2196 NotificationRecord summary = mSummaryByGroupKey.get(group); 2197 if (isChild && summary != null) { 2198 // Child with an active summary -> ignore 2199 if (DBG) { 2200 Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary " 2201 + summary.getKey()); 2202 } 2203 // Make sure we don't leave an old version of the notification around. 2204 if (old != null) { 2205 if (DBG) { 2206 Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey()); 2207 } 2208 cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION); 2209 } 2210 return true; 2211 } else if (isSummary) { 2212 // Summary -> cancel children 2213 cancelGroupChildrenLocked(r, callingUid, callingPid, null, 2214 REASON_GROUP_OPTIMIZATION); 2215 } 2216 } 2217 return false; 2218 } 2219 2220 private void buzzBeepBlinkLocked(NotificationRecord record) { 2221 boolean buzzBeepBlinked = false; 2222 final Notification notification = record.sbn.getNotification(); 2223 2224 // Should this notification make noise, vibe, or use the LED? 2225 final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD; 2226 final boolean canInterrupt = aboveThreshold && !record.isIntercepted(); 2227 if (DBG || record.isIntercepted()) 2228 Slog.v(TAG, 2229 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt + 2230 " intercept=" + record.isIntercepted() 2231 ); 2232 2233 final int currentUser; 2234 final long token = Binder.clearCallingIdentity(); 2235 try { 2236 currentUser = ActivityManager.getCurrentUser(); 2237 } finally { 2238 Binder.restoreCallingIdentity(token); 2239 } 2240 2241 // If we're not supposed to beep, vibrate, etc. then don't. 2242 final String disableEffects = disableNotificationEffects(record); 2243 if (disableEffects != null) { 2244 ZenLog.traceDisableEffects(record, disableEffects); 2245 } 2246 if (disableEffects == null 2247 && (!(record.isUpdate 2248 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 2249 && (record.getUserId() == UserHandle.USER_ALL || 2250 record.getUserId() == currentUser || 2251 mUserProfiles.isCurrentProfile(record.getUserId())) 2252 && canInterrupt 2253 && mSystemReady 2254 && mAudioManager != null) { 2255 if (DBG) Slog.v(TAG, "Interrupting!"); 2256 2257 sendAccessibilityEvent(notification, record.sbn.getPackageName()); 2258 2259 // sound 2260 2261 // should we use the default notification sound? (indicated either by 2262 // DEFAULT_SOUND or because notification.sound is pointing at 2263 // Settings.System.NOTIFICATION_SOUND) 2264 final boolean useDefaultSound = 2265 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 2266 Settings.System.DEFAULT_NOTIFICATION_URI 2267 .equals(notification.sound); 2268 2269 Uri soundUri = null; 2270 boolean hasValidSound = false; 2271 2272 if (useDefaultSound) { 2273 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 2274 2275 // check to see if the default notification sound is silent 2276 ContentResolver resolver = getContext().getContentResolver(); 2277 hasValidSound = Settings.System.getString(resolver, 2278 Settings.System.NOTIFICATION_SOUND) != null; 2279 } else if (notification.sound != null) { 2280 soundUri = notification.sound; 2281 hasValidSound = (soundUri != null); 2282 } 2283 2284 if (hasValidSound) { 2285 boolean looping = 2286 (notification.flags & Notification.FLAG_INSISTENT) != 0; 2287 AudioAttributes audioAttributes = audioAttributesForNotification(notification); 2288 mSoundNotificationKey = record.getKey(); 2289 // do not play notifications if stream volume is 0 (typically because 2290 // ringer mode is silent) or if there is a user of exclusive audio focus 2291 if ((mAudioManager.getStreamVolume( 2292 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0) 2293 && !mAudioManager.isAudioFocusExclusive()) { 2294 final long identity = Binder.clearCallingIdentity(); 2295 try { 2296 final IRingtonePlayer player = 2297 mAudioManager.getRingtonePlayer(); 2298 if (player != null) { 2299 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 2300 + " with attributes " + audioAttributes); 2301 player.playAsync(soundUri, record.sbn.getUser(), looping, 2302 audioAttributes); 2303 buzzBeepBlinked = true; 2304 } 2305 } catch (RemoteException e) { 2306 } finally { 2307 Binder.restoreCallingIdentity(identity); 2308 } 2309 } 2310 } 2311 2312 // vibrate 2313 // Does the notification want to specify its own vibration? 2314 final boolean hasCustomVibrate = notification.vibrate != null; 2315 2316 // new in 4.2: if there was supposed to be a sound and we're in vibrate 2317 // mode, and no other vibration is specified, we fall back to vibration 2318 final boolean convertSoundToVibration = 2319 !hasCustomVibrate 2320 && hasValidSound 2321 && (mAudioManager.getRingerModeInternal() 2322 == AudioManager.RINGER_MODE_VIBRATE); 2323 2324 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 2325 final boolean useDefaultVibrate = 2326 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 2327 2328 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 2329 && !(mAudioManager.getRingerModeInternal() 2330 == AudioManager.RINGER_MODE_SILENT)) { 2331 mVibrateNotificationKey = record.getKey(); 2332 2333 if (useDefaultVibrate || convertSoundToVibration) { 2334 // Escalate privileges so we can use the vibrator even if the 2335 // notifying app does not have the VIBRATE permission. 2336 long identity = Binder.clearCallingIdentity(); 2337 try { 2338 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 2339 useDefaultVibrate ? mDefaultVibrationPattern 2340 : mFallbackVibrationPattern, 2341 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 2342 ? 0: -1, audioAttributesForNotification(notification)); 2343 buzzBeepBlinked = true; 2344 } finally { 2345 Binder.restoreCallingIdentity(identity); 2346 } 2347 } else if (notification.vibrate.length > 1) { 2348 // If you want your own vibration pattern, you need the VIBRATE 2349 // permission 2350 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 2351 notification.vibrate, 2352 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 2353 ? 0: -1, audioAttributesForNotification(notification)); 2354 buzzBeepBlinked = true; 2355 } 2356 } 2357 } 2358 2359 // light 2360 // release the light 2361 boolean wasShowLights = mLights.remove(record.getKey()); 2362 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) { 2363 mLights.add(record.getKey()); 2364 updateLightsLocked(); 2365 if (mUseAttentionLight) { 2366 mAttentionLight.pulse(); 2367 } 2368 buzzBeepBlinked = true; 2369 } else if (wasShowLights) { 2370 updateLightsLocked(); 2371 } 2372 if (buzzBeepBlinked) { 2373 mHandler.post(mBuzzBeepBlinked); 2374 } 2375 } 2376 2377 private static AudioAttributes audioAttributesForNotification(Notification n) { 2378 if (n.audioAttributes != null 2379 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) { 2380 // the audio attributes are set and different from the default, use them 2381 return n.audioAttributes; 2382 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) { 2383 // the stream type is valid, use it 2384 return new AudioAttributes.Builder() 2385 .setInternalLegacyStreamType(n.audioStreamType) 2386 .build(); 2387 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) { 2388 return Notification.AUDIO_ATTRIBUTES_DEFAULT; 2389 } else { 2390 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType)); 2391 return Notification.AUDIO_ATTRIBUTES_DEFAULT; 2392 } 2393 } 2394 2395 void showNextToastLocked() { 2396 ToastRecord record = mToastQueue.get(0); 2397 while (record != null) { 2398 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 2399 try { 2400 record.callback.show(); 2401 scheduleTimeoutLocked(record); 2402 return; 2403 } catch (RemoteException e) { 2404 Slog.w(TAG, "Object died trying to show notification " + record.callback 2405 + " in package " + record.pkg); 2406 // remove it from the list and let the process die 2407 int index = mToastQueue.indexOf(record); 2408 if (index >= 0) { 2409 mToastQueue.remove(index); 2410 } 2411 keepProcessAliveLocked(record.pid); 2412 if (mToastQueue.size() > 0) { 2413 record = mToastQueue.get(0); 2414 } else { 2415 record = null; 2416 } 2417 } 2418 } 2419 } 2420 2421 void cancelToastLocked(int index) { 2422 ToastRecord record = mToastQueue.get(index); 2423 try { 2424 record.callback.hide(); 2425 } catch (RemoteException e) { 2426 Slog.w(TAG, "Object died trying to hide notification " + record.callback 2427 + " in package " + record.pkg); 2428 // don't worry about this, we're about to remove it from 2429 // the list anyway 2430 } 2431 mToastQueue.remove(index); 2432 keepProcessAliveLocked(record.pid); 2433 if (mToastQueue.size() > 0) { 2434 // Show the next one. If the callback fails, this will remove 2435 // it from the list, so don't assume that the list hasn't changed 2436 // after this point. 2437 showNextToastLocked(); 2438 } 2439 } 2440 2441 private void scheduleTimeoutLocked(ToastRecord r) 2442 { 2443 mHandler.removeCallbacksAndMessages(r); 2444 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 2445 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 2446 mHandler.sendMessageDelayed(m, delay); 2447 } 2448 2449 private void handleTimeout(ToastRecord record) 2450 { 2451 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 2452 synchronized (mToastQueue) { 2453 int index = indexOfToastLocked(record.pkg, record.callback); 2454 if (index >= 0) { 2455 cancelToastLocked(index); 2456 } 2457 } 2458 } 2459 2460 // lock on mToastQueue 2461 int indexOfToastLocked(String pkg, ITransientNotification callback) 2462 { 2463 IBinder cbak = callback.asBinder(); 2464 ArrayList<ToastRecord> list = mToastQueue; 2465 int len = list.size(); 2466 for (int i=0; i<len; i++) { 2467 ToastRecord r = list.get(i); 2468 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 2469 return i; 2470 } 2471 } 2472 return -1; 2473 } 2474 2475 // lock on mToastQueue 2476 void keepProcessAliveLocked(int pid) 2477 { 2478 int toastCount = 0; // toasts from this pid 2479 ArrayList<ToastRecord> list = mToastQueue; 2480 int N = list.size(); 2481 for (int i=0; i<N; i++) { 2482 ToastRecord r = list.get(i); 2483 if (r.pid == pid) { 2484 toastCount++; 2485 } 2486 } 2487 try { 2488 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 2489 } catch (RemoteException e) { 2490 // Shouldn't happen. 2491 } 2492 } 2493 2494 private void handleRankingReconsideration(Message message) { 2495 if (!(message.obj instanceof RankingReconsideration)) return; 2496 RankingReconsideration recon = (RankingReconsideration) message.obj; 2497 recon.run(); 2498 boolean changed; 2499 synchronized (mNotificationList) { 2500 final NotificationRecord record = mNotificationsByKey.get(recon.getKey()); 2501 if (record == null) { 2502 return; 2503 } 2504 int indexBefore = findNotificationRecordIndexLocked(record); 2505 boolean interceptBefore = record.isIntercepted(); 2506 int visibilityBefore = record.getPackageVisibilityOverride(); 2507 recon.applyChangesLocked(record); 2508 applyZenModeLocked(record); 2509 mRankingHelper.sort(mNotificationList); 2510 int indexAfter = findNotificationRecordIndexLocked(record); 2511 boolean interceptAfter = record.isIntercepted(); 2512 int visibilityAfter = record.getPackageVisibilityOverride(); 2513 changed = indexBefore != indexAfter || interceptBefore != interceptAfter 2514 || visibilityBefore != visibilityAfter; 2515 if (interceptBefore && !interceptAfter) { 2516 buzzBeepBlinkLocked(record); 2517 } 2518 } 2519 if (changed) { 2520 scheduleSendRankingUpdate(); 2521 } 2522 } 2523 2524 private void handleRankingConfigChange() { 2525 synchronized (mNotificationList) { 2526 final int N = mNotificationList.size(); 2527 ArrayList<String> orderBefore = new ArrayList<String>(N); 2528 int[] visibilities = new int[N]; 2529 for (int i = 0; i < N; i++) { 2530 final NotificationRecord r = mNotificationList.get(i); 2531 orderBefore.add(r.getKey()); 2532 visibilities[i] = r.getPackageVisibilityOverride(); 2533 mRankingHelper.extractSignals(r); 2534 } 2535 for (int i = 0; i < N; i++) { 2536 mRankingHelper.sort(mNotificationList); 2537 final NotificationRecord r = mNotificationList.get(i); 2538 if (!orderBefore.get(i).equals(r.getKey()) 2539 || visibilities[i] != r.getPackageVisibilityOverride()) { 2540 scheduleSendRankingUpdate(); 2541 return; 2542 } 2543 } 2544 } 2545 } 2546 2547 // let zen mode evaluate this record 2548 private void applyZenModeLocked(NotificationRecord record) { 2549 record.setIntercepted(mZenModeHelper.shouldIntercept(record)); 2550 } 2551 2552 // lock on mNotificationList 2553 private int findNotificationRecordIndexLocked(NotificationRecord target) { 2554 return mRankingHelper.indexOf(mNotificationList, target); 2555 } 2556 2557 private void scheduleSendRankingUpdate() { 2558 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 2559 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 2560 mHandler.sendMessage(m); 2561 } 2562 2563 private void handleSendRankingUpdate() { 2564 synchronized (mNotificationList) { 2565 mListeners.notifyRankingUpdateLocked(); 2566 } 2567 } 2568 2569 private void scheduleListenerHintsChanged(int state) { 2570 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED); 2571 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget(); 2572 } 2573 2574 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) { 2575 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED); 2576 mHandler.obtainMessage( 2577 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED, 2578 listenerInterruptionFilter, 2579 0).sendToTarget(); 2580 } 2581 2582 private void handleListenerHintsChanged(int hints) { 2583 synchronized (mNotificationList) { 2584 mListeners.notifyListenerHintsChangedLocked(hints); 2585 } 2586 } 2587 2588 private void handleListenerInterruptionFilterChanged(int interruptionFilter) { 2589 synchronized (mNotificationList) { 2590 mListeners.notifyInterruptionFilterChanged(interruptionFilter); 2591 } 2592 } 2593 2594 private final class WorkerHandler extends Handler 2595 { 2596 @Override 2597 public void handleMessage(Message msg) 2598 { 2599 switch (msg.what) 2600 { 2601 case MESSAGE_TIMEOUT: 2602 handleTimeout((ToastRecord)msg.obj); 2603 break; 2604 case MESSAGE_SAVE_POLICY_FILE: 2605 handleSavePolicyFile(); 2606 break; 2607 case MESSAGE_SEND_RANKING_UPDATE: 2608 handleSendRankingUpdate(); 2609 break; 2610 case MESSAGE_LISTENER_HINTS_CHANGED: 2611 handleListenerHintsChanged(msg.arg1); 2612 break; 2613 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED: 2614 handleListenerInterruptionFilterChanged(msg.arg1); 2615 break; 2616 } 2617 } 2618 2619 } 2620 2621 private final class RankingWorkerHandler extends Handler 2622 { 2623 public RankingWorkerHandler(Looper looper) { 2624 super(looper); 2625 } 2626 2627 @Override 2628 public void handleMessage(Message msg) { 2629 switch (msg.what) { 2630 case MESSAGE_RECONSIDER_RANKING: 2631 handleRankingReconsideration(msg); 2632 break; 2633 case MESSAGE_RANKING_CONFIG_CHANGE: 2634 handleRankingConfigChange(); 2635 break; 2636 } 2637 } 2638 } 2639 2640 // Notifications 2641 // ============================================================================ 2642 static int clamp(int x, int low, int high) { 2643 return (x < low) ? low : ((x > high) ? high : x); 2644 } 2645 2646 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 2647 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 2648 if (!manager.isEnabled()) { 2649 return; 2650 } 2651 2652 AccessibilityEvent event = 2653 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 2654 event.setPackageName(packageName); 2655 event.setClassName(Notification.class.getName()); 2656 event.setParcelableData(notification); 2657 CharSequence tickerText = notification.tickerText; 2658 if (!TextUtils.isEmpty(tickerText)) { 2659 event.getText().add(tickerText); 2660 } 2661 2662 manager.sendAccessibilityEvent(event); 2663 } 2664 2665 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 2666 // tell the app 2667 if (sendDelete) { 2668 if (r.getNotification().deleteIntent != null) { 2669 try { 2670 r.getNotification().deleteIntent.send(); 2671 } catch (PendingIntent.CanceledException ex) { 2672 // do nothing - there's no relevant way to recover, and 2673 // no reason to let this propagate 2674 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 2675 } 2676 } 2677 } 2678 2679 // status bar 2680 if (r.getNotification().getSmallIcon() != null) { 2681 r.isCanceled = true; 2682 mListeners.notifyRemovedLocked(r.sbn); 2683 } 2684 2685 final String canceledKey = r.getKey(); 2686 2687 // sound 2688 if (canceledKey.equals(mSoundNotificationKey)) { 2689 mSoundNotificationKey = null; 2690 final long identity = Binder.clearCallingIdentity(); 2691 try { 2692 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2693 if (player != null) { 2694 player.stopAsync(); 2695 } 2696 } catch (RemoteException e) { 2697 } finally { 2698 Binder.restoreCallingIdentity(identity); 2699 } 2700 } 2701 2702 // vibrate 2703 if (canceledKey.equals(mVibrateNotificationKey)) { 2704 mVibrateNotificationKey = null; 2705 long identity = Binder.clearCallingIdentity(); 2706 try { 2707 mVibrator.cancel(); 2708 } 2709 finally { 2710 Binder.restoreCallingIdentity(identity); 2711 } 2712 } 2713 2714 // light 2715 mLights.remove(canceledKey); 2716 2717 // Record usage stats 2718 switch (reason) { 2719 case REASON_DELEGATE_CANCEL: 2720 case REASON_DELEGATE_CANCEL_ALL: 2721 case REASON_LISTENER_CANCEL: 2722 case REASON_LISTENER_CANCEL_ALL: 2723 mUsageStats.registerDismissedByUser(r); 2724 break; 2725 case REASON_NOMAN_CANCEL: 2726 case REASON_NOMAN_CANCEL_ALL: 2727 mUsageStats.registerRemovedByApp(r); 2728 break; 2729 case REASON_DELEGATE_CLICK: 2730 mUsageStats.registerCancelDueToClick(r); 2731 break; 2732 default: 2733 mUsageStats.registerCancelUnknown(r); 2734 break; 2735 } 2736 2737 mNotificationsByKey.remove(r.sbn.getKey()); 2738 String groupKey = r.getGroupKey(); 2739 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey); 2740 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) { 2741 mSummaryByGroupKey.remove(groupKey); 2742 } 2743 2744 // Save it for users of getHistoricalNotifications() 2745 mArchive.record(r.sbn); 2746 2747 final long now = System.currentTimeMillis(); 2748 EventLogTags.writeNotificationCanceled(canceledKey, reason, 2749 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now)); 2750 } 2751 2752 /** 2753 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2754 * and none of the {@code mustNotHaveFlags}. 2755 */ 2756 void cancelNotification(final int callingUid, final int callingPid, 2757 final String pkg, final String tag, final int id, 2758 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2759 final int userId, final int reason, final ManagedServiceInfo listener) { 2760 // In enqueueNotificationInternal notifications are added by scheduling the 2761 // work on the worker handler. Hence, we also schedule the cancel on this 2762 // handler to avoid a scenario where an add notification call followed by a 2763 // remove notification call ends up in not removing the notification. 2764 mHandler.post(new Runnable() { 2765 @Override 2766 public void run() { 2767 String listenerName = listener == null ? null : listener.component.toShortString(); 2768 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, 2769 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName); 2770 2771 synchronized (mNotificationList) { 2772 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2773 if (index >= 0) { 2774 NotificationRecord r = mNotificationList.get(index); 2775 2776 // Ideally we'd do this in the caller of this method. However, that would 2777 // require the caller to also find the notification. 2778 if (reason == REASON_DELEGATE_CLICK) { 2779 mUsageStats.registerClickedByUser(r); 2780 } 2781 2782 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2783 return; 2784 } 2785 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2786 return; 2787 } 2788 2789 mNotificationList.remove(index); 2790 2791 cancelNotificationLocked(r, sendDelete, reason); 2792 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName, 2793 REASON_GROUP_SUMMARY_CANCELED); 2794 updateLightsLocked(); 2795 } 2796 } 2797 } 2798 }); 2799 } 2800 2801 /** 2802 * Determine whether the userId applies to the notification in question, either because 2803 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2804 */ 2805 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2806 return 2807 // looking for USER_ALL notifications? match everything 2808 userId == UserHandle.USER_ALL 2809 // a notification sent to USER_ALL matches any query 2810 || r.getUserId() == UserHandle.USER_ALL 2811 // an exact user match 2812 || r.getUserId() == userId; 2813 } 2814 2815 /** 2816 * Determine whether the userId applies to the notification in question, either because 2817 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2818 * because it matches one of the users profiles. 2819 */ 2820 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2821 return notificationMatchesUserId(r, userId) 2822 || mUserProfiles.isCurrentProfile(r.getUserId()); 2823 } 2824 2825 /** 2826 * Cancels all notifications from a given package that have all of the 2827 * {@code mustHaveFlags}. 2828 */ 2829 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2830 int mustNotHaveFlags, boolean doit, int userId, int reason, 2831 ManagedServiceInfo listener) { 2832 String listenerName = listener == null ? null : listener.component.toShortString(); 2833 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2834 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2835 listenerName); 2836 2837 synchronized (mNotificationList) { 2838 final int N = mNotificationList.size(); 2839 ArrayList<NotificationRecord> canceledNotifications = null; 2840 for (int i = N-1; i >= 0; --i) { 2841 NotificationRecord r = mNotificationList.get(i); 2842 if (!notificationMatchesUserId(r, userId)) { 2843 continue; 2844 } 2845 // Don't remove notifications to all, if there's no package name specified 2846 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2847 continue; 2848 } 2849 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2850 continue; 2851 } 2852 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2853 continue; 2854 } 2855 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2856 continue; 2857 } 2858 if (canceledNotifications == null) { 2859 canceledNotifications = new ArrayList<>(); 2860 } 2861 canceledNotifications.add(r); 2862 if (!doit) { 2863 return true; 2864 } 2865 mNotificationList.remove(i); 2866 cancelNotificationLocked(r, false, reason); 2867 } 2868 if (doit && canceledNotifications != null) { 2869 final int M = canceledNotifications.size(); 2870 for (int i = 0; i < M; i++) { 2871 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2872 listenerName, REASON_GROUP_SUMMARY_CANCELED); 2873 } 2874 } 2875 if (canceledNotifications != null) { 2876 updateLightsLocked(); 2877 } 2878 return canceledNotifications != null; 2879 } 2880 } 2881 2882 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2883 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2884 String listenerName = listener == null ? null : listener.component.toShortString(); 2885 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2886 null, userId, 0, 0, reason, listenerName); 2887 2888 ArrayList<NotificationRecord> canceledNotifications = null; 2889 final int N = mNotificationList.size(); 2890 for (int i=N-1; i>=0; i--) { 2891 NotificationRecord r = mNotificationList.get(i); 2892 if (includeCurrentProfiles) { 2893 if (!notificationMatchesCurrentProfiles(r, userId)) { 2894 continue; 2895 } 2896 } else { 2897 if (!notificationMatchesUserId(r, userId)) { 2898 continue; 2899 } 2900 } 2901 2902 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2903 | Notification.FLAG_NO_CLEAR)) == 0) { 2904 mNotificationList.remove(i); 2905 cancelNotificationLocked(r, true, reason); 2906 // Make a note so we can cancel children later. 2907 if (canceledNotifications == null) { 2908 canceledNotifications = new ArrayList<>(); 2909 } 2910 canceledNotifications.add(r); 2911 } 2912 } 2913 int M = canceledNotifications != null ? canceledNotifications.size() : 0; 2914 for (int i = 0; i < M; i++) { 2915 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2916 listenerName, REASON_GROUP_SUMMARY_CANCELED); 2917 } 2918 updateLightsLocked(); 2919 } 2920 2921 // Warning: The caller is responsible for invoking updateLightsLocked(). 2922 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid, 2923 String listenerName, int reason) { 2924 Notification n = r.getNotification(); 2925 if (!n.isGroupSummary()) { 2926 return; 2927 } 2928 2929 String pkg = r.sbn.getPackageName(); 2930 int userId = r.getUserId(); 2931 2932 if (pkg == null) { 2933 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey()); 2934 return; 2935 } 2936 2937 final int N = mNotificationList.size(); 2938 for (int i = N - 1; i >= 0; i--) { 2939 NotificationRecord childR = mNotificationList.get(i); 2940 StatusBarNotification childSbn = childR.sbn; 2941 if (childR.getNotification().isGroupChild() && 2942 childR.getGroupKey().equals(r.getGroupKey())) { 2943 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(), 2944 childSbn.getTag(), userId, 0, 0, reason, listenerName); 2945 mNotificationList.remove(i); 2946 cancelNotificationLocked(childR, false, reason); 2947 } 2948 } 2949 } 2950 2951 // lock on mNotificationList 2952 void updateLightsLocked() 2953 { 2954 // handle notification lights 2955 NotificationRecord ledNotification = null; 2956 while (ledNotification == null && !mLights.isEmpty()) { 2957 final String owner = mLights.get(mLights.size() - 1); 2958 ledNotification = mNotificationsByKey.get(owner); 2959 if (ledNotification == null) { 2960 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner); 2961 mLights.remove(owner); 2962 } 2963 } 2964 2965 // Don't flash while we are in a call or screen is on 2966 if (ledNotification == null || mInCall || mScreenOn) { 2967 mNotificationLight.turnOff(); 2968 mStatusBar.notificationLightOff(); 2969 } else { 2970 final Notification ledno = ledNotification.sbn.getNotification(); 2971 int ledARGB = ledno.ledARGB; 2972 int ledOnMS = ledno.ledOnMS; 2973 int ledOffMS = ledno.ledOffMS; 2974 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2975 ledARGB = mDefaultNotificationColor; 2976 ledOnMS = mDefaultNotificationLedOn; 2977 ledOffMS = mDefaultNotificationLedOff; 2978 } 2979 if (mNotificationPulseEnabled) { 2980 // pulse repeatedly 2981 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2982 ledOnMS, ledOffMS); 2983 } 2984 // let SystemUI make an independent decision 2985 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS); 2986 } 2987 } 2988 2989 // lock on mNotificationList 2990 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2991 { 2992 ArrayList<NotificationRecord> list = mNotificationList; 2993 final int len = list.size(); 2994 for (int i=0; i<len; i++) { 2995 NotificationRecord r = list.get(i); 2996 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2997 continue; 2998 } 2999 if (tag == null) { 3000 if (r.sbn.getTag() != null) { 3001 continue; 3002 } 3003 } else { 3004 if (!tag.equals(r.sbn.getTag())) { 3005 continue; 3006 } 3007 } 3008 if (r.sbn.getPackageName().equals(pkg)) { 3009 return i; 3010 } 3011 } 3012 return -1; 3013 } 3014 3015 // lock on mNotificationList 3016 int indexOfNotificationLocked(String key) { 3017 final int N = mNotificationList.size(); 3018 for (int i = 0; i < N; i++) { 3019 if (key.equals(mNotificationList.get(i).getKey())) { 3020 return i; 3021 } 3022 } 3023 return -1; 3024 } 3025 3026 private void updateNotificationPulse() { 3027 synchronized (mNotificationList) { 3028 updateLightsLocked(); 3029 } 3030 } 3031 3032 private static boolean isUidSystem(int uid) { 3033 final int appid = UserHandle.getAppId(uid); 3034 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 3035 } 3036 3037 private static boolean isCallerSystem() { 3038 return isUidSystem(Binder.getCallingUid()); 3039 } 3040 3041 private static void checkCallerIsSystem() { 3042 if (isCallerSystem()) { 3043 return; 3044 } 3045 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 3046 } 3047 3048 private static void checkCallerIsSystemOrSameApp(String pkg) { 3049 if (isCallerSystem()) { 3050 return; 3051 } 3052 final int uid = Binder.getCallingUid(); 3053 try { 3054 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 3055 pkg, 0, UserHandle.getCallingUserId()); 3056 if (ai == null) { 3057 throw new SecurityException("Unknown package " + pkg); 3058 } 3059 if (!UserHandle.isSameApp(ai.uid, uid)) { 3060 throw new SecurityException("Calling uid " + uid + " gave package" 3061 + pkg + " which is owned by uid " + ai.uid); 3062 } 3063 } catch (RemoteException re) { 3064 throw new SecurityException("Unknown package " + pkg + "\n" + re); 3065 } 3066 } 3067 3068 private static String callStateToString(int state) { 3069 switch (state) { 3070 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE"; 3071 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING"; 3072 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK"; 3073 default: return "CALL_STATE_UNKNOWN_" + state; 3074 } 3075 } 3076 3077 private void listenForCallState() { 3078 TelephonyManager.from(getContext()).listen(new PhoneStateListener() { 3079 @Override 3080 public void onCallStateChanged(int state, String incomingNumber) { 3081 if (mCallState == state) return; 3082 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state)); 3083 mCallState = state; 3084 } 3085 }, PhoneStateListener.LISTEN_CALL_STATE); 3086 } 3087 3088 /** 3089 * Generates a NotificationRankingUpdate from 'sbns', considering only 3090 * notifications visible to the given listener. 3091 * 3092 * <p>Caller must hold a lock on mNotificationList.</p> 3093 */ 3094 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { 3095 int speedBumpIndex = -1; 3096 final int N = mNotificationList.size(); 3097 ArrayList<String> keys = new ArrayList<String>(N); 3098 ArrayList<String> interceptedKeys = new ArrayList<String>(N); 3099 Bundle visibilityOverrides = new Bundle(); 3100 for (int i = 0; i < N; i++) { 3101 NotificationRecord record = mNotificationList.get(i); 3102 if (!isVisibleToListener(record.sbn, info)) { 3103 continue; 3104 } 3105 keys.add(record.sbn.getKey()); 3106 if (record.isIntercepted()) { 3107 interceptedKeys.add(record.sbn.getKey()); 3108 } 3109 if (record.getPackageVisibilityOverride() 3110 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) { 3111 visibilityOverrides.putInt(record.sbn.getKey(), 3112 record.getPackageVisibilityOverride()); 3113 } 3114 // Find first min-prio notification for speedbump placement. 3115 if (speedBumpIndex == -1 && 3116 // Intrusiveness trumps priority, hence ignore intrusives. 3117 !record.isRecentlyIntrusive() && 3118 // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so 3119 // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT 3120 // (or lower as a safeguard) is sufficient to find the speedbump index. 3121 // We'll have to revisit this when more package priority buckets are introduced. 3122 record.getPackagePriority() <= Notification.PRIORITY_DEFAULT && 3123 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) { 3124 speedBumpIndex = keys.size() - 1; 3125 } 3126 } 3127 String[] keysAr = keys.toArray(new String[keys.size()]); 3128 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]); 3129 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides, 3130 speedBumpIndex); 3131 } 3132 3133 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { 3134 if (!listener.enabledAndUserMatches(sbn.getUserId())) { 3135 return false; 3136 } 3137 // TODO: remove this for older listeners. 3138 return true; 3139 } 3140 3141 public class NotificationListeners extends ManagedServices { 3142 3143 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>(); 3144 private boolean mNotificationGroupsDesired; 3145 3146 public NotificationListeners() { 3147 super(getContext(), mHandler, mNotificationList, mUserProfiles); 3148 } 3149 3150 @Override 3151 protected Config getConfig() { 3152 Config c = new Config(); 3153 c.caption = "notification listener"; 3154 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 3155 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 3156 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 3157 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 3158 c.clientLabel = R.string.notification_listener_binding_label; 3159 return c; 3160 } 3161 3162 @Override 3163 protected IInterface asInterface(IBinder binder) { 3164 return INotificationListener.Stub.asInterface(binder); 3165 } 3166 3167 @Override 3168 public void onServiceAdded(ManagedServiceInfo info) { 3169 final INotificationListener listener = (INotificationListener) info.service; 3170 final NotificationRankingUpdate update; 3171 synchronized (mNotificationList) { 3172 updateNotificationGroupsDesiredLocked(); 3173 update = makeRankingUpdateLocked(info); 3174 } 3175 try { 3176 listener.onListenerConnected(update); 3177 } catch (RemoteException e) { 3178 // we tried 3179 } 3180 } 3181 3182 @Override 3183 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 3184 if (mListenersDisablingEffects.remove(removed)) { 3185 updateListenerHintsLocked(); 3186 updateEffectsSuppressorLocked(); 3187 } 3188 mLightTrimListeners.remove(removed); 3189 updateNotificationGroupsDesiredLocked(); 3190 } 3191 3192 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) { 3193 if (trim == TRIM_LIGHT) { 3194 mLightTrimListeners.add(info); 3195 } else { 3196 mLightTrimListeners.remove(info); 3197 } 3198 } 3199 3200 public int getOnNotificationPostedTrim(ManagedServiceInfo info) { 3201 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL; 3202 3203 } 3204 3205 /** 3206 * asynchronously notify all listeners about a new notification 3207 * 3208 * <p> 3209 * Also takes care of removing a notification that has been visible to a listener before, 3210 * but isn't anymore. 3211 */ 3212 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) { 3213 // Lazily initialized snapshots of the notification. 3214 StatusBarNotification sbnClone = null; 3215 StatusBarNotification sbnCloneLight = null; 3216 3217 for (final ManagedServiceInfo info : mServices) { 3218 boolean sbnVisible = isVisibleToListener(sbn, info); 3219 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false; 3220 // This notification hasn't been and still isn't visible -> ignore. 3221 if (!oldSbnVisible && !sbnVisible) { 3222 continue; 3223 } 3224 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 3225 3226 // This notification became invisible -> remove the old one. 3227 if (oldSbnVisible && !sbnVisible) { 3228 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight(); 3229 mHandler.post(new Runnable() { 3230 @Override 3231 public void run() { 3232 notifyRemoved(info, oldSbnLightClone, update); 3233 } 3234 }); 3235 continue; 3236 } 3237 3238 final int trim = mListeners.getOnNotificationPostedTrim(info); 3239 3240 if (trim == TRIM_LIGHT && sbnCloneLight == null) { 3241 sbnCloneLight = sbn.cloneLight(); 3242 } else if (trim == TRIM_FULL && sbnClone == null) { 3243 sbnClone = sbn.clone(); 3244 } 3245 final StatusBarNotification sbnToPost = 3246 (trim == TRIM_FULL) ? sbnClone : sbnCloneLight; 3247 3248 mHandler.post(new Runnable() { 3249 @Override 3250 public void run() { 3251 notifyPosted(info, sbnToPost, update); 3252 } 3253 }); 3254 } 3255 } 3256 3257 /** 3258 * asynchronously notify all listeners about a removed notification 3259 */ 3260 public void notifyRemovedLocked(StatusBarNotification sbn) { 3261 // make a copy in case changes are made to the underlying Notification object 3262 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 3263 // notification 3264 final StatusBarNotification sbnLight = sbn.cloneLight(); 3265 for (final ManagedServiceInfo info : mServices) { 3266 if (!isVisibleToListener(sbn, info)) { 3267 continue; 3268 } 3269 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 3270 mHandler.post(new Runnable() { 3271 @Override 3272 public void run() { 3273 notifyRemoved(info, sbnLight, update); 3274 } 3275 }); 3276 } 3277 } 3278 3279 /** 3280 * asynchronously notify all listeners about a reordering of notifications 3281 */ 3282 public void notifyRankingUpdateLocked() { 3283 for (final ManagedServiceInfo serviceInfo : mServices) { 3284 if (!serviceInfo.isEnabledForCurrentProfiles()) { 3285 continue; 3286 } 3287 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo); 3288 mHandler.post(new Runnable() { 3289 @Override 3290 public void run() { 3291 notifyRankingUpdate(serviceInfo, update); 3292 } 3293 }); 3294 } 3295 } 3296 3297 public void notifyListenerHintsChangedLocked(final int hints) { 3298 for (final ManagedServiceInfo serviceInfo : mServices) { 3299 if (!serviceInfo.isEnabledForCurrentProfiles()) { 3300 continue; 3301 } 3302 mHandler.post(new Runnable() { 3303 @Override 3304 public void run() { 3305 notifyListenerHintsChanged(serviceInfo, hints); 3306 } 3307 }); 3308 } 3309 } 3310 3311 public void notifyInterruptionFilterChanged(final int interruptionFilter) { 3312 for (final ManagedServiceInfo serviceInfo : mServices) { 3313 if (!serviceInfo.isEnabledForCurrentProfiles()) { 3314 continue; 3315 } 3316 mHandler.post(new Runnable() { 3317 @Override 3318 public void run() { 3319 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter); 3320 } 3321 }); 3322 } 3323 } 3324 3325 private void notifyPosted(final ManagedServiceInfo info, 3326 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 3327 final INotificationListener listener = (INotificationListener)info.service; 3328 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); 3329 try { 3330 listener.onNotificationPosted(sbnHolder, rankingUpdate); 3331 } catch (RemoteException ex) { 3332 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 3333 } 3334 } 3335 3336 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn, 3337 NotificationRankingUpdate rankingUpdate) { 3338 if (!info.enabledAndUserMatches(sbn.getUserId())) { 3339 return; 3340 } 3341 final INotificationListener listener = (INotificationListener) info.service; 3342 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); 3343 try { 3344 listener.onNotificationRemoved(sbnHolder, rankingUpdate); 3345 } catch (RemoteException ex) { 3346 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 3347 } 3348 } 3349 3350 private void notifyRankingUpdate(ManagedServiceInfo info, 3351 NotificationRankingUpdate rankingUpdate) { 3352 final INotificationListener listener = (INotificationListener) info.service; 3353 try { 3354 listener.onNotificationRankingUpdate(rankingUpdate); 3355 } catch (RemoteException ex) { 3356 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 3357 } 3358 } 3359 3360 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) { 3361 final INotificationListener listener = (INotificationListener) info.service; 3362 try { 3363 listener.onListenerHintsChanged(hints); 3364 } catch (RemoteException ex) { 3365 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex); 3366 } 3367 } 3368 3369 private void notifyInterruptionFilterChanged(ManagedServiceInfo info, 3370 int interruptionFilter) { 3371 final INotificationListener listener = (INotificationListener) info.service; 3372 try { 3373 listener.onInterruptionFilterChanged(interruptionFilter); 3374 } catch (RemoteException ex) { 3375 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex); 3376 } 3377 } 3378 3379 private boolean isListenerPackage(String packageName) { 3380 if (packageName == null) { 3381 return false; 3382 } 3383 // TODO: clean up locking object later 3384 synchronized (mNotificationList) { 3385 for (final ManagedServiceInfo serviceInfo : mServices) { 3386 if (packageName.equals(serviceInfo.component.getPackageName())) { 3387 return true; 3388 } 3389 } 3390 } 3391 return false; 3392 } 3393 3394 /** 3395 * Returns whether any of the currently registered listeners wants to receive notification 3396 * groups. 3397 * 3398 * <p>Currently we assume groups are desired by non-SystemUI listeners.</p> 3399 */ 3400 public boolean notificationGroupsDesired() { 3401 return mNotificationGroupsDesired; 3402 } 3403 3404 private void updateNotificationGroupsDesiredLocked() { 3405 mNotificationGroupsDesired = true; 3406 // No listeners, no groups. 3407 if (mServices.isEmpty()) { 3408 mNotificationGroupsDesired = false; 3409 return; 3410 } 3411 // One listener: Check whether it's SysUI. 3412 if (mServices.size() == 1 && 3413 mServices.get(0).component.getPackageName().equals("com.android.systemui")) { 3414 mNotificationGroupsDesired = false; 3415 return; 3416 } 3417 } 3418 } 3419 3420 public static final class DumpFilter { 3421 public String pkgFilter; 3422 public boolean zen; 3423 3424 public static DumpFilter parseFromArguments(String[] args) { 3425 if (args != null && args.length == 2 && "p".equals(args[0]) 3426 && args[1] != null && !args[1].trim().isEmpty()) { 3427 final DumpFilter filter = new DumpFilter(); 3428 filter.pkgFilter = args[1].trim().toLowerCase(); 3429 return filter; 3430 } 3431 if (args != null && args.length == 1 && "zen".equals(args[0])) { 3432 final DumpFilter filter = new DumpFilter(); 3433 filter.zen = true; 3434 return filter; 3435 } 3436 return null; 3437 } 3438 3439 public boolean matches(StatusBarNotification sbn) { 3440 return zen ? true : sbn != null 3441 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg())); 3442 } 3443 3444 public boolean matches(ComponentName component) { 3445 return zen ? true : component != null && matches(component.getPackageName()); 3446 } 3447 3448 public boolean matches(String pkg) { 3449 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter); 3450 } 3451 3452 @Override 3453 public String toString() { 3454 return zen ? "zen" : ('\'' + pkgFilter + '\''); 3455 } 3456 } 3457 3458 /** 3459 * Wrapper for a StatusBarNotification object that allows transfer across a oneway 3460 * binder without sending large amounts of data over a oneway transaction. 3461 */ 3462 private static final class StatusBarNotificationHolder 3463 extends IStatusBarNotificationHolder.Stub { 3464 private StatusBarNotification mValue; 3465 3466 public StatusBarNotificationHolder(StatusBarNotification value) { 3467 mValue = value; 3468 } 3469 3470 /** Get the held value and clear it. This function should only be called once per holder */ 3471 @Override 3472 public StatusBarNotification get() { 3473 StatusBarNotification value = mValue; 3474 mValue = null; 3475 return value; 3476 } 3477 } 3478} 3479