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