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