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