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