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