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