NotificationManagerService.java revision cdb57aeb0e2c83a887c86da0ca2a890df7f02f41
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; // current ZEN mode as communicated to listeners 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 937 mUserProfiles.updateCache(getContext()); 938 listenForCallState(); 939 940 // register for various Intents 941 IntentFilter filter = new IntentFilter(); 942 filter.addAction(Intent.ACTION_SCREEN_ON); 943 filter.addAction(Intent.ACTION_SCREEN_OFF); 944 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 945 filter.addAction(Intent.ACTION_USER_PRESENT); 946 filter.addAction(Intent.ACTION_USER_STOPPED); 947 filter.addAction(Intent.ACTION_USER_SWITCHED); 948 filter.addAction(Intent.ACTION_USER_ADDED); 949 getContext().registerReceiver(mIntentReceiver, filter); 950 951 IntentFilter pkgFilter = new IntentFilter(); 952 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 953 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 954 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 955 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 956 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 957 pkgFilter.addDataScheme("package"); 958 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null, 959 null); 960 961 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 962 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null, 963 null); 964 965 mSettingsObserver = new SettingsObserver(mHandler); 966 967 mArchive = new Archive(resources.getInteger( 968 R.integer.config_notificationServiceArchiveSize)); 969 970 publishBinderService(Context.NOTIFICATION_SERVICE, mService); 971 publishLocalService(NotificationManagerInternal.class, mInternalService); 972 } 973 974 /** 975 * Read the old XML-based app block database and import those blockages into the AppOps system. 976 */ 977 private void importOldBlockDb() { 978 loadPolicyFile(); 979 980 PackageManager pm = getContext().getPackageManager(); 981 for (String pkg : mBlockedPackages) { 982 PackageInfo info = null; 983 try { 984 info = pm.getPackageInfo(pkg, 0); 985 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false); 986 } catch (NameNotFoundException e) { 987 // forget you 988 } 989 } 990 mBlockedPackages.clear(); 991 } 992 993 @Override 994 public void onBootPhase(int phase) { 995 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 996 // no beeping until we're basically done booting 997 mSystemReady = true; 998 999 // Grab our optional AudioService 1000 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 1001 mAudioManagerInternal = getLocalService(AudioManagerInternal.class); 1002 mZenModeHelper.onSystemReady(); 1003 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 1004 // This observer will force an update when observe is called, causing us to 1005 // bind to listener services. 1006 mSettingsObserver.observe(); 1007 mListeners.onBootPhaseAppsCanStart(); 1008 mConditionProviders.onBootPhaseAppsCanStart(); 1009 } 1010 } 1011 1012 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) { 1013 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); 1014 1015 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, 1016 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 1017 1018 // Now, cancel any outstanding notifications that are part of a just-disabled app 1019 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { 1020 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid), 1021 REASON_PACKAGE_BANNED, null); 1022 } 1023 } 1024 1025 private void updateListenerHintsLocked() { 1026 final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS; 1027 if (hints == mListenerHints) return; 1028 mListenerHints = hints; 1029 scheduleListenerHintsChanged(hints); 1030 } 1031 1032 private void updateEffectsSuppressorLocked() { 1033 final ComponentName suppressor = !mListenersDisablingEffects.isEmpty() 1034 ? mListenersDisablingEffects.valueAt(0).component : null; 1035 if (Objects.equals(suppressor, mEffectsSuppressor)) return; 1036 mEffectsSuppressor = suppressor; 1037 mZenModeHelper.setEffectsSuppressed(suppressor != null); 1038 getContext().sendBroadcast(new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED) 1039 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)); 1040 } 1041 1042 private void updateInterruptionFilterLocked() { 1043 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter(); 1044 if (interruptionFilter == mInterruptionFilter) return; 1045 mInterruptionFilter = interruptionFilter; 1046 scheduleInterruptionFilterChanged(interruptionFilter); 1047 } 1048 1049 private final IBinder mService = new INotificationManager.Stub() { 1050 // Toasts 1051 // ============================================================================ 1052 1053 @Override 1054 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 1055 { 1056 if (DBG) { 1057 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback 1058 + " duration=" + duration); 1059 } 1060 1061 if (pkg == null || callback == null) { 1062 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 1063 return ; 1064 } 1065 1066 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); 1067 1068 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { 1069 if (!isSystemToast) { 1070 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 1071 return; 1072 } 1073 } 1074 1075 synchronized (mToastQueue) { 1076 int callingPid = Binder.getCallingPid(); 1077 long callingId = Binder.clearCallingIdentity(); 1078 try { 1079 ToastRecord record; 1080 int index = indexOfToastLocked(pkg, callback); 1081 // If it's already in the queue, we update it in place, we don't 1082 // move it to the end of the queue. 1083 if (index >= 0) { 1084 record = mToastQueue.get(index); 1085 record.update(duration); 1086 } else { 1087 // Limit the number of toasts that any given package except the android 1088 // package can enqueue. Prevents DOS attacks and deals with leaks. 1089 if (!isSystemToast) { 1090 int count = 0; 1091 final int N = mToastQueue.size(); 1092 for (int i=0; i<N; i++) { 1093 final ToastRecord r = mToastQueue.get(i); 1094 if (r.pkg.equals(pkg)) { 1095 count++; 1096 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1097 Slog.e(TAG, "Package has already posted " + count 1098 + " toasts. Not showing more. Package=" + pkg); 1099 return; 1100 } 1101 } 1102 } 1103 } 1104 1105 record = new ToastRecord(callingPid, pkg, callback, duration); 1106 mToastQueue.add(record); 1107 index = mToastQueue.size() - 1; 1108 keepProcessAliveLocked(callingPid); 1109 } 1110 // If it's at index 0, it's the current toast. It doesn't matter if it's 1111 // new or just been updated. Call back and tell it to show itself. 1112 // If the callback fails, this will remove it from the list, so don't 1113 // assume that it's valid after this. 1114 if (index == 0) { 1115 showNextToastLocked(); 1116 } 1117 } finally { 1118 Binder.restoreCallingIdentity(callingId); 1119 } 1120 } 1121 } 1122 1123 @Override 1124 public void cancelToast(String pkg, ITransientNotification callback) { 1125 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 1126 1127 if (pkg == null || callback == null) { 1128 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 1129 return ; 1130 } 1131 1132 synchronized (mToastQueue) { 1133 long callingId = Binder.clearCallingIdentity(); 1134 try { 1135 int index = indexOfToastLocked(pkg, callback); 1136 if (index >= 0) { 1137 cancelToastLocked(index); 1138 } else { 1139 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg 1140 + " callback=" + callback); 1141 } 1142 } finally { 1143 Binder.restoreCallingIdentity(callingId); 1144 } 1145 } 1146 } 1147 1148 @Override 1149 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, 1150 Notification notification, int[] idOut, int userId) throws RemoteException { 1151 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(), 1152 Binder.getCallingPid(), tag, id, notification, idOut, userId); 1153 } 1154 1155 @Override 1156 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 1157 checkCallerIsSystemOrSameApp(pkg); 1158 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1159 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 1160 // Don't allow client applications to cancel foreground service notis. 1161 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0, 1162 Binder.getCallingUid() == Process.SYSTEM_UID 1163 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL, 1164 null); 1165 } 1166 1167 @Override 1168 public void cancelAllNotifications(String pkg, int userId) { 1169 checkCallerIsSystemOrSameApp(pkg); 1170 1171 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1172 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 1173 1174 // Calling from user space, don't allow the canceling of actively 1175 // running foreground services. 1176 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(), 1177 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId, 1178 REASON_NOMAN_CANCEL_ALL, null); 1179 } 1180 1181 @Override 1182 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { 1183 checkCallerIsSystem(); 1184 1185 setNotificationsEnabledForPackageImpl(pkg, uid, enabled); 1186 } 1187 1188 /** 1189 * Use this when you just want to know if notifications are OK for this package. 1190 */ 1191 @Override 1192 public boolean areNotificationsEnabledForPackage(String pkg, int uid) { 1193 checkCallerIsSystem(); 1194 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 1195 == AppOpsManager.MODE_ALLOWED); 1196 } 1197 1198 @Override 1199 public void setPackagePriority(String pkg, int uid, int priority) { 1200 checkCallerIsSystem(); 1201 mRankingHelper.setPackagePriority(pkg, uid, priority); 1202 savePolicyFile(); 1203 } 1204 1205 @Override 1206 public int getPackagePriority(String pkg, int uid) { 1207 checkCallerIsSystem(); 1208 return mRankingHelper.getPackagePriority(pkg, uid); 1209 } 1210 1211 @Override 1212 public void setPackageVisibilityOverride(String pkg, int uid, int visibility) { 1213 checkCallerIsSystem(); 1214 mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility); 1215 savePolicyFile(); 1216 } 1217 1218 @Override 1219 public int getPackageVisibilityOverride(String pkg, int uid) { 1220 checkCallerIsSystem(); 1221 return mRankingHelper.getPackageVisibilityOverride(pkg, uid); 1222 } 1223 1224 /** 1225 * System-only API for getting a list of current (i.e. not cleared) notifications. 1226 * 1227 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1228 * @returns A list of all the notifications, in natural order. 1229 */ 1230 @Override 1231 public StatusBarNotification[] getActiveNotifications(String callingPkg) { 1232 // enforce() will ensure the calling uid has the correct permission 1233 getContext().enforceCallingOrSelfPermission( 1234 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1235 "NotificationManagerService.getActiveNotifications"); 1236 1237 StatusBarNotification[] tmp = null; 1238 int uid = Binder.getCallingUid(); 1239 1240 // noteOp will check to make sure the callingPkg matches the uid 1241 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1242 == AppOpsManager.MODE_ALLOWED) { 1243 synchronized (mNotificationList) { 1244 tmp = new StatusBarNotification[mNotificationList.size()]; 1245 final int N = mNotificationList.size(); 1246 for (int i=0; i<N; i++) { 1247 tmp[i] = mNotificationList.get(i).sbn; 1248 } 1249 } 1250 } 1251 return tmp; 1252 } 1253 1254 /** 1255 * System-only API for getting a list of recent (cleared, no longer shown) notifications. 1256 * 1257 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1258 */ 1259 @Override 1260 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { 1261 // enforce() will ensure the calling uid has the correct permission 1262 getContext().enforceCallingOrSelfPermission( 1263 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1264 "NotificationManagerService.getHistoricalNotifications"); 1265 1266 StatusBarNotification[] tmp = null; 1267 int uid = Binder.getCallingUid(); 1268 1269 // noteOp will check to make sure the callingPkg matches the uid 1270 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1271 == AppOpsManager.MODE_ALLOWED) { 1272 synchronized (mArchive) { 1273 tmp = mArchive.getArray(count); 1274 } 1275 } 1276 return tmp; 1277 } 1278 1279 /** 1280 * Register a listener binder directly with the notification manager. 1281 * 1282 * Only works with system callers. Apps should extend 1283 * {@link android.service.notification.NotificationListenerService}. 1284 */ 1285 @Override 1286 public void registerListener(final INotificationListener listener, 1287 final ComponentName component, final int userid) { 1288 enforceSystemOrSystemUI("INotificationManager.registerListener"); 1289 mListeners.registerService(listener, component, userid); 1290 } 1291 1292 /** 1293 * Remove a listener binder directly 1294 */ 1295 @Override 1296 public void unregisterListener(INotificationListener listener, int userid) { 1297 mListeners.unregisterService(listener, userid); 1298 } 1299 1300 /** 1301 * Allow an INotificationListener to simulate a "clear all" operation. 1302 * 1303 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} 1304 * 1305 * @param token The binder for the listener, to check that the caller is allowed 1306 */ 1307 @Override 1308 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) { 1309 final int callingUid = Binder.getCallingUid(); 1310 final int callingPid = Binder.getCallingPid(); 1311 long identity = Binder.clearCallingIdentity(); 1312 try { 1313 synchronized (mNotificationList) { 1314 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1315 if (keys != null) { 1316 final int N = keys.length; 1317 for (int i = 0; i < N; i++) { 1318 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1319 if (r == null) continue; 1320 final int userId = r.sbn.getUserId(); 1321 if (userId != info.userid && userId != UserHandle.USER_ALL && 1322 !mUserProfiles.isCurrentProfile(userId)) { 1323 throw new SecurityException("Disallowed call from listener: " 1324 + info.service); 1325 } 1326 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1327 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(), 1328 userId); 1329 } 1330 } else { 1331 cancelAllLocked(callingUid, callingPid, info.userid, 1332 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles()); 1333 } 1334 } 1335 } finally { 1336 Binder.restoreCallingIdentity(identity); 1337 } 1338 } 1339 1340 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info, 1341 int callingUid, int callingPid, String pkg, String tag, int id, int userId) { 1342 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 1343 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 1344 true, 1345 userId, REASON_LISTENER_CANCEL, info); 1346 } 1347 1348 /** 1349 * Allow an INotificationListener to simulate clearing (dismissing) a single notification. 1350 * 1351 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} 1352 * 1353 * @param token The binder for the listener, to check that the caller is allowed 1354 */ 1355 @Override 1356 public void cancelNotificationFromListener(INotificationListener token, String pkg, 1357 String tag, int id) { 1358 final int callingUid = Binder.getCallingUid(); 1359 final int callingPid = Binder.getCallingPid(); 1360 long identity = Binder.clearCallingIdentity(); 1361 try { 1362 synchronized (mNotificationList) { 1363 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1364 if (info.supportsProfiles()) { 1365 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) " 1366 + "from " + info.component 1367 + " use cancelNotification(key) instead."); 1368 } else { 1369 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1370 pkg, tag, id, info.userid); 1371 } 1372 } 1373 } finally { 1374 Binder.restoreCallingIdentity(identity); 1375 } 1376 } 1377 1378 /** 1379 * Allow an INotificationListener to request the list of outstanding notifications seen by 1380 * the current user. Useful when starting up, after which point the listener callbacks 1381 * should be used. 1382 * 1383 * @param token The binder for the listener, to check that the caller is allowed 1384 * @param keys An array of notification keys to fetch, or null to fetch everything 1385 * @returns The return value will contain the notifications specified in keys, in that 1386 * order, or if keys is null, all the notifications, in natural order. 1387 */ 1388 @Override 1389 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener( 1390 INotificationListener token, String[] keys, int trim) { 1391 synchronized (mNotificationList) { 1392 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1393 final boolean getKeys = keys != null; 1394 final int N = getKeys ? keys.length : mNotificationList.size(); 1395 final ArrayList<StatusBarNotification> list 1396 = new ArrayList<StatusBarNotification>(N); 1397 for (int i=0; i<N; i++) { 1398 final NotificationRecord r = getKeys 1399 ? mNotificationsByKey.get(keys[i]) 1400 : mNotificationList.get(i); 1401 if (r == null) continue; 1402 StatusBarNotification sbn = r.sbn; 1403 if (!isVisibleToListener(sbn, info)) continue; 1404 StatusBarNotification sbnToSend = 1405 (trim == TRIM_FULL) ? sbn : sbn.cloneLight(); 1406 list.add(sbnToSend); 1407 } 1408 return new ParceledListSlice<StatusBarNotification>(list); 1409 } 1410 } 1411 1412 @Override 1413 public void requestHintsFromListener(INotificationListener token, int hints) { 1414 final long identity = Binder.clearCallingIdentity(); 1415 try { 1416 synchronized (mNotificationList) { 1417 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1418 final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0; 1419 if (disableEffects) { 1420 mListenersDisablingEffects.add(info); 1421 } else { 1422 mListenersDisablingEffects.remove(info); 1423 } 1424 updateListenerHintsLocked(); 1425 updateEffectsSuppressorLocked(); 1426 } 1427 } finally { 1428 Binder.restoreCallingIdentity(identity); 1429 } 1430 } 1431 1432 @Override 1433 public int getHintsFromListener(INotificationListener token) { 1434 synchronized (mNotificationList) { 1435 return mListenerHints; 1436 } 1437 } 1438 1439 @Override 1440 public void requestInterruptionFilterFromListener(INotificationListener token, 1441 int interruptionFilter) throws RemoteException { 1442 final long identity = Binder.clearCallingIdentity(); 1443 try { 1444 synchronized (mNotificationList) { 1445 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1446 mZenModeHelper.requestFromListener(info.component, interruptionFilter); 1447 updateInterruptionFilterLocked(); 1448 } 1449 } finally { 1450 Binder.restoreCallingIdentity(identity); 1451 } 1452 } 1453 1454 @Override 1455 public int getInterruptionFilterFromListener(INotificationListener token) 1456 throws RemoteException { 1457 synchronized (mNotificationLight) { 1458 return mInterruptionFilter; 1459 } 1460 } 1461 1462 @Override 1463 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim) 1464 throws RemoteException { 1465 synchronized (mNotificationList) { 1466 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1467 if (info == null) return; 1468 mListeners.setOnNotificationPostedTrimLocked(info, trim); 1469 } 1470 } 1471 1472 @Override 1473 public ZenModeConfig getZenModeConfig() { 1474 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig"); 1475 return mZenModeHelper.getConfig(); 1476 } 1477 1478 @Override 1479 public boolean setZenModeConfig(ZenModeConfig config) { 1480 checkCallerIsSystem(); 1481 return mZenModeHelper.setConfig(config); 1482 } 1483 1484 @Override 1485 public void setZenMode(int mode) throws RemoteException { 1486 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode"); 1487 final long identity = Binder.clearCallingIdentity(); 1488 try { 1489 mZenModeHelper.setZenMode(mode, "NotificationManager"); 1490 } finally { 1491 Binder.restoreCallingIdentity(identity); 1492 } 1493 } 1494 1495 @Override 1496 public void notifyConditions(String pkg, IConditionProvider provider, 1497 Condition[] conditions) { 1498 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider); 1499 checkCallerIsSystemOrSameApp(pkg); 1500 final long identity = Binder.clearCallingIdentity(); 1501 try { 1502 mConditionProviders.notifyConditions(pkg, info, conditions); 1503 } finally { 1504 Binder.restoreCallingIdentity(identity); 1505 } 1506 } 1507 1508 @Override 1509 public void requestZenModeConditions(IConditionListener callback, int relevance) { 1510 enforceSystemOrSystemUIOrVolume("INotificationManager.requestZenModeConditions"); 1511 mConditionProviders.requestZenModeConditions(callback, relevance); 1512 } 1513 1514 @Override 1515 public void setZenModeCondition(Condition condition) { 1516 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenModeCondition"); 1517 final long identity = Binder.clearCallingIdentity(); 1518 try { 1519 mConditionProviders.setZenModeCondition(condition, "binderCall"); 1520 } finally { 1521 Binder.restoreCallingIdentity(identity); 1522 } 1523 } 1524 1525 @Override 1526 public void setAutomaticZenModeConditions(Uri[] conditionIds) { 1527 enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions"); 1528 mConditionProviders.setAutomaticZenModeConditions(conditionIds); 1529 } 1530 1531 @Override 1532 public Condition[] getAutomaticZenModeConditions() { 1533 enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions"); 1534 return mConditionProviders.getAutomaticZenModeConditions(); 1535 } 1536 1537 private void enforceSystemOrSystemUIOrVolume(String message) { 1538 if (mAudioManagerInternal != null) { 1539 final int vcuid = mAudioManagerInternal.getVolumeControllerUid(); 1540 if (vcuid > 0 && Binder.getCallingUid() == vcuid) { 1541 return; 1542 } 1543 } 1544 enforceSystemOrSystemUI(message); 1545 } 1546 1547 private void enforceSystemOrSystemUI(String message) { 1548 if (isCallerSystem()) return; 1549 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 1550 message); 1551 } 1552 1553 @Override 1554 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1555 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1556 != PackageManager.PERMISSION_GRANTED) { 1557 pw.println("Permission Denial: can't dump NotificationManager from pid=" 1558 + Binder.getCallingPid() 1559 + ", uid=" + Binder.getCallingUid()); 1560 return; 1561 } 1562 1563 dumpImpl(pw, DumpFilter.parseFromArguments(args)); 1564 } 1565 1566 @Override 1567 public ComponentName getEffectsSuppressor() { 1568 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor"); 1569 return mEffectsSuppressor; 1570 } 1571 1572 @Override 1573 public boolean matchesCallFilter(Bundle extras) { 1574 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter"); 1575 return mZenModeHelper.matchesCallFilter( 1576 UserHandle.getCallingUserHandle(), 1577 extras, 1578 mRankingHelper.findExtractor(ValidateNotificationPeople.class), 1579 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS, 1580 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY); 1581 } 1582 1583 @Override 1584 public boolean isSystemConditionProviderEnabled(String path) { 1585 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled"); 1586 return mConditionProviders.isSystemConditionProviderEnabled(path); 1587 } 1588 }; 1589 1590 private String[] getActiveNotificationKeys(INotificationListener token) { 1591 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1592 final ArrayList<String> keys = new ArrayList<String>(); 1593 if (info.isEnabledForCurrentProfiles()) { 1594 synchronized (mNotificationList) { 1595 final int N = mNotificationList.size(); 1596 for (int i = 0; i < N; i++) { 1597 final StatusBarNotification sbn = mNotificationList.get(i).sbn; 1598 if (info.enabledAndUserMatches(sbn.getUserId())) { 1599 keys.add(sbn.getKey()); 1600 } 1601 } 1602 } 1603 } 1604 return keys.toArray(new String[keys.size()]); 1605 } 1606 1607 private String disableNotificationEffects(NotificationRecord record) { 1608 if (mDisableNotificationEffects) { 1609 return "booleanState"; 1610 } 1611 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) { 1612 return "listenerHints"; 1613 } 1614 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) { 1615 return "callState"; 1616 } 1617 return null; 1618 } 1619 1620 void dumpImpl(PrintWriter pw, DumpFilter filter) { 1621 pw.print("Current Notification Manager state"); 1622 if (filter != null) { 1623 pw.print(" (filtered to "); pw.print(filter); pw.print(")"); 1624 } 1625 pw.println(':'); 1626 int N; 1627 final boolean zenOnly = filter != null && filter.zen; 1628 1629 if (!zenOnly) { 1630 synchronized (mToastQueue) { 1631 N = mToastQueue.size(); 1632 if (N > 0) { 1633 pw.println(" Toast Queue:"); 1634 for (int i=0; i<N; i++) { 1635 mToastQueue.get(i).dump(pw, " ", filter); 1636 } 1637 pw.println(" "); 1638 } 1639 } 1640 } 1641 1642 synchronized (mNotificationList) { 1643 if (!zenOnly) { 1644 N = mNotificationList.size(); 1645 if (N > 0) { 1646 pw.println(" Notification List:"); 1647 for (int i=0; i<N; i++) { 1648 final NotificationRecord nr = mNotificationList.get(i); 1649 if (filter != null && !filter.matches(nr.sbn)) continue; 1650 nr.dump(pw, " ", getContext()); 1651 } 1652 pw.println(" "); 1653 } 1654 1655 if (filter == null) { 1656 N = mLights.size(); 1657 if (N > 0) { 1658 pw.println(" Lights List:"); 1659 for (int i=0; i<N; i++) { 1660 if (i == N - 1) { 1661 pw.print(" > "); 1662 } else { 1663 pw.print(" "); 1664 } 1665 pw.println(mLights.get(i)); 1666 } 1667 pw.println(" "); 1668 } 1669 pw.println(" mUseAttentionLight=" + mUseAttentionLight); 1670 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled); 1671 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey); 1672 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey); 1673 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects); 1674 pw.println(" mCallState=" + callStateToString(mCallState)); 1675 pw.println(" mSystemReady=" + mSystemReady); 1676 } 1677 pw.println(" mArchive=" + mArchive.toString()); 1678 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1679 int i=0; 1680 while (iter.hasNext()) { 1681 final StatusBarNotification sbn = iter.next(); 1682 if (filter != null && !filter.matches(sbn)) continue; 1683 pw.println(" " + sbn); 1684 if (++i >= 5) { 1685 if (iter.hasNext()) pw.println(" ..."); 1686 break; 1687 } 1688 } 1689 } 1690 1691 if (!zenOnly) { 1692 pw.println("\n Usage Stats:"); 1693 mUsageStats.dump(pw, " ", filter); 1694 } 1695 1696 if (filter == null || zenOnly) { 1697 pw.println("\n Zen Mode:"); 1698 mZenModeHelper.dump(pw, " "); 1699 1700 pw.println("\n Zen Log:"); 1701 ZenLog.dump(pw, " "); 1702 } 1703 1704 if (!zenOnly) { 1705 pw.println("\n Ranking Config:"); 1706 mRankingHelper.dump(pw, " ", filter); 1707 1708 pw.println("\n Notification listeners:"); 1709 mListeners.dump(pw, filter); 1710 pw.print(" mListenerHints: "); pw.println(mListenerHints); 1711 pw.print(" mListenersDisablingEffects: ("); 1712 N = mListenersDisablingEffects.size(); 1713 for (int i = 0; i < N; i++) { 1714 final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i); 1715 if (i > 0) pw.print(','); 1716 pw.print(listener.component); 1717 } 1718 pw.println(')'); 1719 } 1720 1721 pw.println("\n Condition providers:"); 1722 mConditionProviders.dump(pw, filter); 1723 1724 pw.println("\n Group summaries:"); 1725 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) { 1726 NotificationRecord r = entry.getValue(); 1727 pw.println(" " + entry.getKey() + " -> " + r.getKey()); 1728 if (mNotificationsByKey.get(r.getKey()) != r) { 1729 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey."); 1730 r.dump(pw, " ", getContext()); 1731 } 1732 } 1733 } 1734 } 1735 1736 /** 1737 * The private API only accessible to the system process. 1738 */ 1739 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1740 @Override 1741 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, 1742 String tag, int id, Notification notification, int[] idReceived, int userId) { 1743 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 1744 idReceived, userId); 1745 } 1746 1747 @Override 1748 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId, 1749 int userId) { 1750 checkCallerIsSystem(); 1751 synchronized (mNotificationList) { 1752 int i = indexOfNotificationLocked(pkg, null, notificationId, userId); 1753 if (i < 0) { 1754 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with " 1755 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId); 1756 return; 1757 } 1758 NotificationRecord r = mNotificationList.get(i); 1759 StatusBarNotification sbn = r.sbn; 1760 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees 1761 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE, 1762 // we have to revert to the flags we received initially *and* force remove 1763 // FLAG_FOREGROUND_SERVICE. 1764 sbn.getNotification().flags = 1765 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE); 1766 mRankingHelper.sort(mNotificationList); 1767 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */); 1768 } 1769 } 1770 }; 1771 1772 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, 1773 final int callingPid, final String tag, final int id, final Notification notification, 1774 int[] idOut, int incomingUserId) { 1775 if (DBG) { 1776 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 1777 + " notification=" + notification); 1778 } 1779 checkCallerIsSystemOrSameApp(pkg); 1780 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 1781 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg); 1782 1783 final int userId = ActivityManager.handleIncomingUser(callingPid, 1784 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1785 final UserHandle user = new UserHandle(userId); 1786 1787 // Limit the number of notifications that any given package except the android 1788 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks. 1789 if (!isSystemNotification && !isNotificationFromListener) { 1790 synchronized (mNotificationList) { 1791 int count = 0; 1792 final int N = mNotificationList.size(); 1793 for (int i=0; i<N; i++) { 1794 final NotificationRecord r = mNotificationList.get(i); 1795 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1796 count++; 1797 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1798 Slog.e(TAG, "Package has already posted " + count 1799 + " notifications. Not showing more. package=" + pkg); 1800 return; 1801 } 1802 } 1803 } 1804 } 1805 } 1806 1807 if (pkg == null || notification == null) { 1808 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1809 + " id=" + id + " notification=" + notification); 1810 } 1811 if (notification.icon != 0) { 1812 if (!notification.isValid()) { 1813 throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg 1814 + " id=" + id + " notification=" + notification); 1815 } 1816 } 1817 1818 mHandler.post(new Runnable() { 1819 @Override 1820 public void run() { 1821 1822 synchronized (mNotificationList) { 1823 1824 // === Scoring === 1825 1826 // 0. Sanitize inputs 1827 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1828 Notification.PRIORITY_MAX); 1829 // Migrate notification flags to scores 1830 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1831 if (notification.priority < Notification.PRIORITY_MAX) { 1832 notification.priority = Notification.PRIORITY_MAX; 1833 } 1834 } else if (SCORE_ONGOING_HIGHER && 1835 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1836 if (notification.priority < Notification.PRIORITY_HIGH) { 1837 notification.priority = Notification.PRIORITY_HIGH; 1838 } 1839 } 1840 1841 // 1. initial score: buckets of 10, around the app [-20..20] 1842 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; 1843 1844 // 2. extract ranking signals from the notification data 1845 final StatusBarNotification n = new StatusBarNotification( 1846 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 1847 user); 1848 NotificationRecord r = new NotificationRecord(n, score); 1849 NotificationRecord old = mNotificationsByKey.get(n.getKey()); 1850 if (old != null) { 1851 // Retain ranking information from previous record 1852 r.copyRankingInformation(old); 1853 } 1854 1855 // Handle grouped notifications and bail out early if we 1856 // can to avoid extracting signals. 1857 handleGroupedNotificationLocked(r, old, callingUid, callingPid); 1858 boolean ignoreNotification = 1859 removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid); 1860 1861 // This conditional is a dirty hack to limit the logging done on 1862 // behalf of the download manager without affecting other apps. 1863 if (!pkg.equals("com.android.providers.downloads") 1864 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1865 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW; 1866 if (ignoreNotification) { 1867 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED; 1868 } else if (old != null) { 1869 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE; 1870 } 1871 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 1872 pkg, id, tag, userId, notification.toString(), 1873 enqueueStatus); 1874 } 1875 1876 if (ignoreNotification) { 1877 return; 1878 } 1879 1880 mRankingHelper.extractSignals(r); 1881 1882 // 3. Apply local rules 1883 1884 // blocked apps 1885 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1886 if (!isSystemNotification) { 1887 r.score = JUNK_SCORE; 1888 Slog.e(TAG, "Suppressing notification from package " + pkg 1889 + " by user request."); 1890 } 1891 } 1892 1893 if (r.score < SCORE_DISPLAY_THRESHOLD) { 1894 // Notification will be blocked because the score is too low. 1895 return; 1896 } 1897 1898 int index = indexOfNotificationLocked(n.getKey()); 1899 if (index < 0) { 1900 mNotificationList.add(r); 1901 mUsageStats.registerPostedByApp(r); 1902 } else { 1903 old = mNotificationList.get(index); 1904 mNotificationList.set(index, r); 1905 mUsageStats.registerUpdatedByApp(r, old); 1906 // Make sure we don't lose the foreground service state. 1907 notification.flags |= 1908 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1909 r.isUpdate = true; 1910 } 1911 1912 mNotificationsByKey.put(n.getKey(), r); 1913 1914 // Ensure if this is a foreground service that the proper additional 1915 // flags are set. 1916 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1917 notification.flags |= Notification.FLAG_ONGOING_EVENT 1918 | Notification.FLAG_NO_CLEAR; 1919 } 1920 1921 applyZenModeLocked(r); 1922 mRankingHelper.sort(mNotificationList); 1923 1924 if (notification.icon != 0) { 1925 StatusBarNotification oldSbn = (old != null) ? old.sbn : null; 1926 mListeners.notifyPostedLocked(n, oldSbn); 1927 } else { 1928 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1929 if (old != null && !old.isCanceled) { 1930 mListeners.notifyRemovedLocked(n); 1931 } 1932 // ATTENTION: in a future release we will bail out here 1933 // so that we do not play sounds, show lights, etc. for invalid 1934 // notifications 1935 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1936 + n.getPackageName()); 1937 } 1938 1939 buzzBeepBlinkLocked(r); 1940 } 1941 } 1942 }); 1943 1944 idOut[0] = id; 1945 } 1946 1947 /** 1948 * Ensures that grouped notification receive their special treatment. 1949 * 1950 * <p>Cancels group children if the new notification causes a group to lose 1951 * its summary.</p> 1952 * 1953 * <p>Updates mSummaryByGroupKey.</p> 1954 */ 1955 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old, 1956 int callingUid, int callingPid) { 1957 StatusBarNotification sbn = r.sbn; 1958 Notification n = sbn.getNotification(); 1959 String group = sbn.getGroupKey(); 1960 boolean isSummary = n.isGroupSummary(); 1961 1962 Notification oldN = old != null ? old.sbn.getNotification() : null; 1963 String oldGroup = old != null ? old.sbn.getGroupKey() : null; 1964 boolean oldIsSummary = old != null && oldN.isGroupSummary(); 1965 1966 if (oldIsSummary) { 1967 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup); 1968 if (removedSummary != old) { 1969 String removedKey = 1970 removedSummary != null ? removedSummary.getKey() : "<null>"; 1971 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() + 1972 ", removed=" + removedKey); 1973 } 1974 } 1975 if (isSummary) { 1976 mSummaryByGroupKey.put(group, r); 1977 } 1978 1979 // Clear out group children of the old notification if the update 1980 // causes the group summary to go away. This happens when the old 1981 // notification was a summary and the new one isn't, or when the old 1982 // notification was a summary and its group key changed. 1983 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) { 1984 cancelGroupChildrenLocked(old, callingUid, callingPid, null, 1985 REASON_GROUP_SUMMARY_CANCELED); 1986 } 1987 } 1988 1989 /** 1990 * Performs group notification optimizations if SysUI is the only active 1991 * notification listener and returns whether the given notification should 1992 * be ignored. 1993 * 1994 * <p>Returns true if the given notification is a child of a group with a 1995 * summary, which means that SysUI will never show it, and hence the new 1996 * notification can be safely ignored. Also cancels any previous instance 1997 * of the ignored notification.</p> 1998 * 1999 * <p>For summaries, cancels all children of that group, as SysUI will 2000 * never show them anymore.</p> 2001 * 2002 * @return true if the given notification can be ignored as an optimization 2003 */ 2004 private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r, 2005 NotificationRecord old, int callingUid, int callingPid) { 2006 // No optimizations are possible if listeners want groups. 2007 if (mListeners.notificationGroupsDesired()) { 2008 return false; 2009 } 2010 2011 StatusBarNotification sbn = r.sbn; 2012 String group = sbn.getGroupKey(); 2013 boolean isSummary = sbn.getNotification().isGroupSummary(); 2014 boolean isChild = sbn.getNotification().isGroupChild(); 2015 2016 NotificationRecord summary = mSummaryByGroupKey.get(group); 2017 if (isChild && summary != null) { 2018 // Child with an active summary -> ignore 2019 if (DBG) { 2020 Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary " 2021 + summary.getKey()); 2022 } 2023 // Make sure we don't leave an old version of the notification around. 2024 if (old != null) { 2025 if (DBG) { 2026 Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey()); 2027 } 2028 cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION); 2029 } 2030 return true; 2031 } else if (isSummary) { 2032 // Summary -> cancel children 2033 cancelGroupChildrenLocked(r, callingUid, callingPid, null, 2034 REASON_GROUP_OPTIMIZATION); 2035 } 2036 return false; 2037 } 2038 2039 private void buzzBeepBlinkLocked(NotificationRecord record) { 2040 boolean buzzBeepBlinked = false; 2041 final Notification notification = record.sbn.getNotification(); 2042 2043 // Should this notification make noise, vibe, or use the LED? 2044 final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD; 2045 final boolean canInterrupt = aboveThreshold && !record.isIntercepted(); 2046 if (DBG || record.isIntercepted()) 2047 Slog.v(TAG, 2048 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt + 2049 " intercept=" + record.isIntercepted() 2050 ); 2051 2052 final int currentUser; 2053 final long token = Binder.clearCallingIdentity(); 2054 try { 2055 currentUser = ActivityManager.getCurrentUser(); 2056 } finally { 2057 Binder.restoreCallingIdentity(token); 2058 } 2059 2060 // If we're not supposed to beep, vibrate, etc. then don't. 2061 final String disableEffects = disableNotificationEffects(record); 2062 if (disableEffects != null) { 2063 ZenLog.traceDisableEffects(record, disableEffects); 2064 } 2065 if (disableEffects == null 2066 && (!(record.isUpdate 2067 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 2068 && (record.getUserId() == UserHandle.USER_ALL || 2069 record.getUserId() == currentUser || 2070 mUserProfiles.isCurrentProfile(record.getUserId())) 2071 && canInterrupt 2072 && mSystemReady 2073 && mAudioManager != null) { 2074 if (DBG) Slog.v(TAG, "Interrupting!"); 2075 2076 sendAccessibilityEvent(notification, record.sbn.getPackageName()); 2077 2078 // sound 2079 2080 // should we use the default notification sound? (indicated either by 2081 // DEFAULT_SOUND or because notification.sound is pointing at 2082 // Settings.System.NOTIFICATION_SOUND) 2083 final boolean useDefaultSound = 2084 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 2085 Settings.System.DEFAULT_NOTIFICATION_URI 2086 .equals(notification.sound); 2087 2088 Uri soundUri = null; 2089 boolean hasValidSound = false; 2090 2091 if (useDefaultSound) { 2092 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 2093 2094 // check to see if the default notification sound is silent 2095 ContentResolver resolver = getContext().getContentResolver(); 2096 hasValidSound = Settings.System.getString(resolver, 2097 Settings.System.NOTIFICATION_SOUND) != null; 2098 } else if (notification.sound != null) { 2099 soundUri = notification.sound; 2100 hasValidSound = (soundUri != null); 2101 } 2102 2103 if (hasValidSound) { 2104 boolean looping = 2105 (notification.flags & Notification.FLAG_INSISTENT) != 0; 2106 AudioAttributes audioAttributes = audioAttributesForNotification(notification); 2107 mSoundNotificationKey = record.getKey(); 2108 // do not play notifications if stream volume is 0 (typically because 2109 // ringer mode is silent) or if there is a user of exclusive audio focus 2110 if ((mAudioManager.getStreamVolume( 2111 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0) 2112 && !mAudioManager.isAudioFocusExclusive()) { 2113 final long identity = Binder.clearCallingIdentity(); 2114 try { 2115 final IRingtonePlayer player = 2116 mAudioManager.getRingtonePlayer(); 2117 if (player != null) { 2118 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 2119 + " with attributes " + audioAttributes); 2120 player.playAsync(soundUri, record.sbn.getUser(), looping, 2121 audioAttributes); 2122 buzzBeepBlinked = true; 2123 } 2124 } catch (RemoteException e) { 2125 } finally { 2126 Binder.restoreCallingIdentity(identity); 2127 } 2128 } 2129 } 2130 2131 // vibrate 2132 // Does the notification want to specify its own vibration? 2133 final boolean hasCustomVibrate = notification.vibrate != null; 2134 2135 // new in 4.2: if there was supposed to be a sound and we're in vibrate 2136 // mode, and no other vibration is specified, we fall back to vibration 2137 final boolean convertSoundToVibration = 2138 !hasCustomVibrate 2139 && hasValidSound 2140 && (mAudioManager.getRingerModeInternal() 2141 == AudioManager.RINGER_MODE_VIBRATE); 2142 2143 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 2144 final boolean useDefaultVibrate = 2145 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 2146 2147 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 2148 && !(mAudioManager.getRingerModeInternal() 2149 == AudioManager.RINGER_MODE_SILENT)) { 2150 mVibrateNotificationKey = record.getKey(); 2151 2152 if (useDefaultVibrate || convertSoundToVibration) { 2153 // Escalate privileges so we can use the vibrator even if the 2154 // notifying app does not have the VIBRATE permission. 2155 long identity = Binder.clearCallingIdentity(); 2156 try { 2157 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 2158 useDefaultVibrate ? mDefaultVibrationPattern 2159 : mFallbackVibrationPattern, 2160 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 2161 ? 0: -1, audioAttributesForNotification(notification)); 2162 buzzBeepBlinked = true; 2163 } finally { 2164 Binder.restoreCallingIdentity(identity); 2165 } 2166 } else if (notification.vibrate.length > 1) { 2167 // If you want your own vibration pattern, you need the VIBRATE 2168 // permission 2169 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 2170 notification.vibrate, 2171 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 2172 ? 0: -1, audioAttributesForNotification(notification)); 2173 buzzBeepBlinked = true; 2174 } 2175 } 2176 } 2177 2178 // light 2179 // release the light 2180 boolean wasShowLights = mLights.remove(record.getKey()); 2181 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) { 2182 mLights.add(record.getKey()); 2183 updateLightsLocked(); 2184 if (mUseAttentionLight) { 2185 mAttentionLight.pulse(); 2186 } 2187 buzzBeepBlinked = true; 2188 } else if (wasShowLights) { 2189 updateLightsLocked(); 2190 } 2191 if (buzzBeepBlinked) { 2192 mHandler.post(mBuzzBeepBlinked); 2193 } 2194 } 2195 2196 private static AudioAttributes audioAttributesForNotification(Notification n) { 2197 if (n.audioAttributes != null 2198 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) { 2199 // the audio attributes are set and different from the default, use them 2200 return n.audioAttributes; 2201 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) { 2202 // the stream type is valid, use it 2203 return new AudioAttributes.Builder() 2204 .setInternalLegacyStreamType(n.audioStreamType) 2205 .build(); 2206 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) { 2207 return Notification.AUDIO_ATTRIBUTES_DEFAULT; 2208 } else { 2209 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType)); 2210 return Notification.AUDIO_ATTRIBUTES_DEFAULT; 2211 } 2212 } 2213 2214 void showNextToastLocked() { 2215 ToastRecord record = mToastQueue.get(0); 2216 while (record != null) { 2217 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 2218 try { 2219 record.callback.show(); 2220 scheduleTimeoutLocked(record); 2221 return; 2222 } catch (RemoteException e) { 2223 Slog.w(TAG, "Object died trying to show notification " + record.callback 2224 + " in package " + record.pkg); 2225 // remove it from the list and let the process die 2226 int index = mToastQueue.indexOf(record); 2227 if (index >= 0) { 2228 mToastQueue.remove(index); 2229 } 2230 keepProcessAliveLocked(record.pid); 2231 if (mToastQueue.size() > 0) { 2232 record = mToastQueue.get(0); 2233 } else { 2234 record = null; 2235 } 2236 } 2237 } 2238 } 2239 2240 void cancelToastLocked(int index) { 2241 ToastRecord record = mToastQueue.get(index); 2242 try { 2243 record.callback.hide(); 2244 } catch (RemoteException e) { 2245 Slog.w(TAG, "Object died trying to hide notification " + record.callback 2246 + " in package " + record.pkg); 2247 // don't worry about this, we're about to remove it from 2248 // the list anyway 2249 } 2250 mToastQueue.remove(index); 2251 keepProcessAliveLocked(record.pid); 2252 if (mToastQueue.size() > 0) { 2253 // Show the next one. If the callback fails, this will remove 2254 // it from the list, so don't assume that the list hasn't changed 2255 // after this point. 2256 showNextToastLocked(); 2257 } 2258 } 2259 2260 private void scheduleTimeoutLocked(ToastRecord r) 2261 { 2262 mHandler.removeCallbacksAndMessages(r); 2263 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 2264 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 2265 mHandler.sendMessageDelayed(m, delay); 2266 } 2267 2268 private void handleTimeout(ToastRecord record) 2269 { 2270 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 2271 synchronized (mToastQueue) { 2272 int index = indexOfToastLocked(record.pkg, record.callback); 2273 if (index >= 0) { 2274 cancelToastLocked(index); 2275 } 2276 } 2277 } 2278 2279 // lock on mToastQueue 2280 int indexOfToastLocked(String pkg, ITransientNotification callback) 2281 { 2282 IBinder cbak = callback.asBinder(); 2283 ArrayList<ToastRecord> list = mToastQueue; 2284 int len = list.size(); 2285 for (int i=0; i<len; i++) { 2286 ToastRecord r = list.get(i); 2287 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 2288 return i; 2289 } 2290 } 2291 return -1; 2292 } 2293 2294 // lock on mToastQueue 2295 void keepProcessAliveLocked(int pid) 2296 { 2297 int toastCount = 0; // toasts from this pid 2298 ArrayList<ToastRecord> list = mToastQueue; 2299 int N = list.size(); 2300 for (int i=0; i<N; i++) { 2301 ToastRecord r = list.get(i); 2302 if (r.pid == pid) { 2303 toastCount++; 2304 } 2305 } 2306 try { 2307 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 2308 } catch (RemoteException e) { 2309 // Shouldn't happen. 2310 } 2311 } 2312 2313 private void handleRankingReconsideration(Message message) { 2314 if (!(message.obj instanceof RankingReconsideration)) return; 2315 RankingReconsideration recon = (RankingReconsideration) message.obj; 2316 recon.run(); 2317 boolean changed; 2318 synchronized (mNotificationList) { 2319 final NotificationRecord record = mNotificationsByKey.get(recon.getKey()); 2320 if (record == null) { 2321 return; 2322 } 2323 int indexBefore = findNotificationRecordIndexLocked(record); 2324 boolean interceptBefore = record.isIntercepted(); 2325 int visibilityBefore = record.getPackageVisibilityOverride(); 2326 recon.applyChangesLocked(record); 2327 applyZenModeLocked(record); 2328 mRankingHelper.sort(mNotificationList); 2329 int indexAfter = findNotificationRecordIndexLocked(record); 2330 boolean interceptAfter = record.isIntercepted(); 2331 int visibilityAfter = record.getPackageVisibilityOverride(); 2332 changed = indexBefore != indexAfter || interceptBefore != interceptAfter 2333 || visibilityBefore != visibilityAfter; 2334 if (interceptBefore && !interceptAfter) { 2335 buzzBeepBlinkLocked(record); 2336 } 2337 } 2338 if (changed) { 2339 scheduleSendRankingUpdate(); 2340 } 2341 } 2342 2343 private void handleRankingConfigChange() { 2344 synchronized (mNotificationList) { 2345 final int N = mNotificationList.size(); 2346 ArrayList<String> orderBefore = new ArrayList<String>(N); 2347 int[] visibilities = new int[N]; 2348 for (int i = 0; i < N; i++) { 2349 final NotificationRecord r = mNotificationList.get(i); 2350 orderBefore.add(r.getKey()); 2351 visibilities[i] = r.getPackageVisibilityOverride(); 2352 mRankingHelper.extractSignals(r); 2353 } 2354 for (int i = 0; i < N; i++) { 2355 mRankingHelper.sort(mNotificationList); 2356 final NotificationRecord r = mNotificationList.get(i); 2357 if (!orderBefore.get(i).equals(r.getKey()) 2358 || visibilities[i] != r.getPackageVisibilityOverride()) { 2359 scheduleSendRankingUpdate(); 2360 return; 2361 } 2362 } 2363 } 2364 } 2365 2366 // let zen mode evaluate this record 2367 private void applyZenModeLocked(NotificationRecord record) { 2368 record.setIntercepted(mZenModeHelper.shouldIntercept(record)); 2369 } 2370 2371 // lock on mNotificationList 2372 private int findNotificationRecordIndexLocked(NotificationRecord target) { 2373 return mRankingHelper.indexOf(mNotificationList, target); 2374 } 2375 2376 private void scheduleSendRankingUpdate() { 2377 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 2378 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 2379 mHandler.sendMessage(m); 2380 } 2381 2382 private void handleSendRankingUpdate() { 2383 synchronized (mNotificationList) { 2384 mListeners.notifyRankingUpdateLocked(); 2385 } 2386 } 2387 2388 private void scheduleListenerHintsChanged(int state) { 2389 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED); 2390 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget(); 2391 } 2392 2393 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) { 2394 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED); 2395 mHandler.obtainMessage( 2396 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED, 2397 listenerInterruptionFilter, 2398 0).sendToTarget(); 2399 } 2400 2401 private void handleListenerHintsChanged(int hints) { 2402 synchronized (mNotificationList) { 2403 mListeners.notifyListenerHintsChangedLocked(hints); 2404 } 2405 } 2406 2407 private void handleListenerInterruptionFilterChanged(int interruptionFilter) { 2408 synchronized (mNotificationList) { 2409 mListeners.notifyInterruptionFilterChanged(interruptionFilter); 2410 } 2411 } 2412 2413 private final class WorkerHandler extends Handler 2414 { 2415 @Override 2416 public void handleMessage(Message msg) 2417 { 2418 switch (msg.what) 2419 { 2420 case MESSAGE_TIMEOUT: 2421 handleTimeout((ToastRecord)msg.obj); 2422 break; 2423 case MESSAGE_SAVE_POLICY_FILE: 2424 handleSavePolicyFile(); 2425 break; 2426 case MESSAGE_SEND_RANKING_UPDATE: 2427 handleSendRankingUpdate(); 2428 break; 2429 case MESSAGE_LISTENER_HINTS_CHANGED: 2430 handleListenerHintsChanged(msg.arg1); 2431 break; 2432 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED: 2433 handleListenerInterruptionFilterChanged(msg.arg1); 2434 break; 2435 } 2436 } 2437 2438 } 2439 2440 private final class RankingWorkerHandler extends Handler 2441 { 2442 public RankingWorkerHandler(Looper looper) { 2443 super(looper); 2444 } 2445 2446 @Override 2447 public void handleMessage(Message msg) { 2448 switch (msg.what) { 2449 case MESSAGE_RECONSIDER_RANKING: 2450 handleRankingReconsideration(msg); 2451 break; 2452 case MESSAGE_RANKING_CONFIG_CHANGE: 2453 handleRankingConfigChange(); 2454 break; 2455 } 2456 } 2457 } 2458 2459 // Notifications 2460 // ============================================================================ 2461 static int clamp(int x, int low, int high) { 2462 return (x < low) ? low : ((x > high) ? high : x); 2463 } 2464 2465 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 2466 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 2467 if (!manager.isEnabled()) { 2468 return; 2469 } 2470 2471 AccessibilityEvent event = 2472 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 2473 event.setPackageName(packageName); 2474 event.setClassName(Notification.class.getName()); 2475 event.setParcelableData(notification); 2476 CharSequence tickerText = notification.tickerText; 2477 if (!TextUtils.isEmpty(tickerText)) { 2478 event.getText().add(tickerText); 2479 } 2480 2481 manager.sendAccessibilityEvent(event); 2482 } 2483 2484 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 2485 // tell the app 2486 if (sendDelete) { 2487 if (r.getNotification().deleteIntent != null) { 2488 try { 2489 r.getNotification().deleteIntent.send(); 2490 } catch (PendingIntent.CanceledException ex) { 2491 // do nothing - there's no relevant way to recover, and 2492 // no reason to let this propagate 2493 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 2494 } 2495 } 2496 } 2497 2498 // status bar 2499 if (r.getNotification().icon != 0) { 2500 r.isCanceled = true; 2501 mListeners.notifyRemovedLocked(r.sbn); 2502 } 2503 2504 final String canceledKey = r.getKey(); 2505 2506 // sound 2507 if (canceledKey.equals(mSoundNotificationKey)) { 2508 mSoundNotificationKey = null; 2509 final long identity = Binder.clearCallingIdentity(); 2510 try { 2511 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2512 if (player != null) { 2513 player.stopAsync(); 2514 } 2515 } catch (RemoteException e) { 2516 } finally { 2517 Binder.restoreCallingIdentity(identity); 2518 } 2519 } 2520 2521 // vibrate 2522 if (canceledKey.equals(mVibrateNotificationKey)) { 2523 mVibrateNotificationKey = null; 2524 long identity = Binder.clearCallingIdentity(); 2525 try { 2526 mVibrator.cancel(); 2527 } 2528 finally { 2529 Binder.restoreCallingIdentity(identity); 2530 } 2531 } 2532 2533 // light 2534 mLights.remove(canceledKey); 2535 2536 // Record usage stats 2537 switch (reason) { 2538 case REASON_DELEGATE_CANCEL: 2539 case REASON_DELEGATE_CANCEL_ALL: 2540 case REASON_LISTENER_CANCEL: 2541 case REASON_LISTENER_CANCEL_ALL: 2542 mUsageStats.registerDismissedByUser(r); 2543 break; 2544 case REASON_NOMAN_CANCEL: 2545 case REASON_NOMAN_CANCEL_ALL: 2546 mUsageStats.registerRemovedByApp(r); 2547 break; 2548 case REASON_DELEGATE_CLICK: 2549 mUsageStats.registerCancelDueToClick(r); 2550 break; 2551 default: 2552 mUsageStats.registerCancelUnknown(r); 2553 break; 2554 } 2555 2556 mNotificationsByKey.remove(r.sbn.getKey()); 2557 String groupKey = r.getGroupKey(); 2558 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey); 2559 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) { 2560 mSummaryByGroupKey.remove(groupKey); 2561 } 2562 2563 // Save it for users of getHistoricalNotifications() 2564 mArchive.record(r.sbn); 2565 2566 EventLogTags.writeNotificationCanceled(canceledKey, reason); 2567 } 2568 2569 /** 2570 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2571 * and none of the {@code mustNotHaveFlags}. 2572 */ 2573 void cancelNotification(final int callingUid, final int callingPid, 2574 final String pkg, final String tag, final int id, 2575 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2576 final int userId, final int reason, final ManagedServiceInfo listener) { 2577 // In enqueueNotificationInternal notifications are added by scheduling the 2578 // work on the worker handler. Hence, we also schedule the cancel on this 2579 // handler to avoid a scenario where an add notification call followed by a 2580 // remove notification call ends up in not removing the notification. 2581 mHandler.post(new Runnable() { 2582 @Override 2583 public void run() { 2584 String listenerName = listener == null ? null : listener.component.toShortString(); 2585 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2586 mustHaveFlags, mustNotHaveFlags, reason, listenerName); 2587 2588 synchronized (mNotificationList) { 2589 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2590 if (index >= 0) { 2591 NotificationRecord r = mNotificationList.get(index); 2592 2593 // Ideally we'd do this in the caller of this method. However, that would 2594 // require the caller to also find the notification. 2595 if (reason == REASON_DELEGATE_CLICK) { 2596 mUsageStats.registerClickedByUser(r); 2597 } 2598 2599 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2600 return; 2601 } 2602 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2603 return; 2604 } 2605 2606 mNotificationList.remove(index); 2607 2608 cancelNotificationLocked(r, sendDelete, reason); 2609 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName, 2610 REASON_GROUP_SUMMARY_CANCELED); 2611 updateLightsLocked(); 2612 } 2613 } 2614 } 2615 }); 2616 } 2617 2618 /** 2619 * Determine whether the userId applies to the notification in question, either because 2620 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2621 */ 2622 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2623 return 2624 // looking for USER_ALL notifications? match everything 2625 userId == UserHandle.USER_ALL 2626 // a notification sent to USER_ALL matches any query 2627 || r.getUserId() == UserHandle.USER_ALL 2628 // an exact user match 2629 || r.getUserId() == userId; 2630 } 2631 2632 /** 2633 * Determine whether the userId applies to the notification in question, either because 2634 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2635 * because it matches one of the users profiles. 2636 */ 2637 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2638 return notificationMatchesUserId(r, userId) 2639 || mUserProfiles.isCurrentProfile(r.getUserId()); 2640 } 2641 2642 /** 2643 * Cancels all notifications from a given package that have all of the 2644 * {@code mustHaveFlags}. 2645 */ 2646 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2647 int mustNotHaveFlags, boolean doit, int userId, int reason, 2648 ManagedServiceInfo listener) { 2649 String listenerName = listener == null ? null : listener.component.toShortString(); 2650 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2651 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2652 listenerName); 2653 2654 synchronized (mNotificationList) { 2655 final int N = mNotificationList.size(); 2656 ArrayList<NotificationRecord> canceledNotifications = null; 2657 for (int i = N-1; i >= 0; --i) { 2658 NotificationRecord r = mNotificationList.get(i); 2659 if (!notificationMatchesUserId(r, userId)) { 2660 continue; 2661 } 2662 // Don't remove notifications to all, if there's no package name specified 2663 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2664 continue; 2665 } 2666 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2667 continue; 2668 } 2669 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2670 continue; 2671 } 2672 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2673 continue; 2674 } 2675 if (canceledNotifications == null) { 2676 canceledNotifications = new ArrayList<>(); 2677 } 2678 canceledNotifications.add(r); 2679 if (!doit) { 2680 return true; 2681 } 2682 mNotificationList.remove(i); 2683 cancelNotificationLocked(r, false, reason); 2684 } 2685 if (doit && canceledNotifications != null) { 2686 final int M = canceledNotifications.size(); 2687 for (int i = 0; i < M; i++) { 2688 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2689 listenerName, REASON_GROUP_SUMMARY_CANCELED); 2690 } 2691 } 2692 if (canceledNotifications != null) { 2693 updateLightsLocked(); 2694 } 2695 return canceledNotifications != null; 2696 } 2697 } 2698 2699 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2700 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2701 String listenerName = listener == null ? null : listener.component.toShortString(); 2702 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2703 null, userId, 0, 0, reason, listenerName); 2704 2705 ArrayList<NotificationRecord> canceledNotifications = null; 2706 final int N = mNotificationList.size(); 2707 for (int i=N-1; i>=0; i--) { 2708 NotificationRecord r = mNotificationList.get(i); 2709 if (includeCurrentProfiles) { 2710 if (!notificationMatchesCurrentProfiles(r, userId)) { 2711 continue; 2712 } 2713 } else { 2714 if (!notificationMatchesUserId(r, userId)) { 2715 continue; 2716 } 2717 } 2718 2719 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2720 | Notification.FLAG_NO_CLEAR)) == 0) { 2721 mNotificationList.remove(i); 2722 cancelNotificationLocked(r, true, reason); 2723 // Make a note so we can cancel children later. 2724 if (canceledNotifications == null) { 2725 canceledNotifications = new ArrayList<>(); 2726 } 2727 canceledNotifications.add(r); 2728 } 2729 } 2730 int M = canceledNotifications != null ? canceledNotifications.size() : 0; 2731 for (int i = 0; i < M; i++) { 2732 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2733 listenerName, REASON_GROUP_SUMMARY_CANCELED); 2734 } 2735 updateLightsLocked(); 2736 } 2737 2738 // Warning: The caller is responsible for invoking updateLightsLocked(). 2739 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid, 2740 String listenerName, int reason) { 2741 Notification n = r.getNotification(); 2742 if (!n.isGroupSummary()) { 2743 return; 2744 } 2745 2746 String pkg = r.sbn.getPackageName(); 2747 int userId = r.getUserId(); 2748 2749 if (pkg == null) { 2750 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey()); 2751 return; 2752 } 2753 2754 final int N = mNotificationList.size(); 2755 for (int i = N - 1; i >= 0; i--) { 2756 NotificationRecord childR = mNotificationList.get(i); 2757 StatusBarNotification childSbn = childR.sbn; 2758 if (childR.getNotification().isGroupChild() && 2759 childR.getGroupKey().equals(r.getGroupKey())) { 2760 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(), 2761 childSbn.getTag(), userId, 0, 0, reason, listenerName); 2762 mNotificationList.remove(i); 2763 cancelNotificationLocked(childR, false, reason); 2764 } 2765 } 2766 } 2767 2768 // lock on mNotificationList 2769 void updateLightsLocked() 2770 { 2771 // handle notification lights 2772 NotificationRecord ledNotification = null; 2773 while (ledNotification == null && !mLights.isEmpty()) { 2774 final String owner = mLights.get(mLights.size() - 1); 2775 ledNotification = mNotificationsByKey.get(owner); 2776 if (ledNotification == null) { 2777 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner); 2778 mLights.remove(owner); 2779 } 2780 } 2781 2782 // Don't flash while we are in a call or screen is on 2783 if (ledNotification == null || mInCall || mScreenOn) { 2784 mNotificationLight.turnOff(); 2785 mStatusBar.notificationLightOff(); 2786 } else { 2787 final Notification ledno = ledNotification.sbn.getNotification(); 2788 int ledARGB = ledno.ledARGB; 2789 int ledOnMS = ledno.ledOnMS; 2790 int ledOffMS = ledno.ledOffMS; 2791 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2792 ledARGB = mDefaultNotificationColor; 2793 ledOnMS = mDefaultNotificationLedOn; 2794 ledOffMS = mDefaultNotificationLedOff; 2795 } 2796 if (mNotificationPulseEnabled) { 2797 // pulse repeatedly 2798 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2799 ledOnMS, ledOffMS); 2800 } 2801 // let SystemUI make an independent decision 2802 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS); 2803 } 2804 } 2805 2806 // lock on mNotificationList 2807 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2808 { 2809 ArrayList<NotificationRecord> list = mNotificationList; 2810 final int len = list.size(); 2811 for (int i=0; i<len; i++) { 2812 NotificationRecord r = list.get(i); 2813 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2814 continue; 2815 } 2816 if (tag == null) { 2817 if (r.sbn.getTag() != null) { 2818 continue; 2819 } 2820 } else { 2821 if (!tag.equals(r.sbn.getTag())) { 2822 continue; 2823 } 2824 } 2825 if (r.sbn.getPackageName().equals(pkg)) { 2826 return i; 2827 } 2828 } 2829 return -1; 2830 } 2831 2832 // lock on mNotificationList 2833 int indexOfNotificationLocked(String key) { 2834 final int N = mNotificationList.size(); 2835 for (int i = 0; i < N; i++) { 2836 if (key.equals(mNotificationList.get(i).getKey())) { 2837 return i; 2838 } 2839 } 2840 return -1; 2841 } 2842 2843 private void updateNotificationPulse() { 2844 synchronized (mNotificationList) { 2845 updateLightsLocked(); 2846 } 2847 } 2848 2849 private static boolean isUidSystem(int uid) { 2850 final int appid = UserHandle.getAppId(uid); 2851 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2852 } 2853 2854 private static boolean isCallerSystem() { 2855 return isUidSystem(Binder.getCallingUid()); 2856 } 2857 2858 private static void checkCallerIsSystem() { 2859 if (isCallerSystem()) { 2860 return; 2861 } 2862 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2863 } 2864 2865 private static void checkCallerIsSystemOrSameApp(String pkg) { 2866 if (isCallerSystem()) { 2867 return; 2868 } 2869 final int uid = Binder.getCallingUid(); 2870 try { 2871 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2872 pkg, 0, UserHandle.getCallingUserId()); 2873 if (ai == null) { 2874 throw new SecurityException("Unknown package " + pkg); 2875 } 2876 if (!UserHandle.isSameApp(ai.uid, uid)) { 2877 throw new SecurityException("Calling uid " + uid + " gave package" 2878 + pkg + " which is owned by uid " + ai.uid); 2879 } 2880 } catch (RemoteException re) { 2881 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2882 } 2883 } 2884 2885 private static String callStateToString(int state) { 2886 switch (state) { 2887 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE"; 2888 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING"; 2889 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK"; 2890 default: return "CALL_STATE_UNKNOWN_" + state; 2891 } 2892 } 2893 2894 private void listenForCallState() { 2895 TelephonyManager.from(getContext()).listen(new PhoneStateListener() { 2896 @Override 2897 public void onCallStateChanged(int state, String incomingNumber) { 2898 if (mCallState == state) return; 2899 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state)); 2900 mCallState = state; 2901 } 2902 }, PhoneStateListener.LISTEN_CALL_STATE); 2903 } 2904 2905 /** 2906 * Generates a NotificationRankingUpdate from 'sbns', considering only 2907 * notifications visible to the given listener. 2908 * 2909 * <p>Caller must hold a lock on mNotificationList.</p> 2910 */ 2911 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { 2912 int speedBumpIndex = -1; 2913 final int N = mNotificationList.size(); 2914 ArrayList<String> keys = new ArrayList<String>(N); 2915 ArrayList<String> interceptedKeys = new ArrayList<String>(N); 2916 Bundle visibilityOverrides = new Bundle(); 2917 for (int i = 0; i < N; i++) { 2918 NotificationRecord record = mNotificationList.get(i); 2919 if (!isVisibleToListener(record.sbn, info)) { 2920 continue; 2921 } 2922 keys.add(record.sbn.getKey()); 2923 if (record.isIntercepted()) { 2924 interceptedKeys.add(record.sbn.getKey()); 2925 } 2926 if (record.getPackageVisibilityOverride() 2927 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) { 2928 visibilityOverrides.putInt(record.sbn.getKey(), 2929 record.getPackageVisibilityOverride()); 2930 } 2931 // Find first min-prio notification for speedbump placement. 2932 if (speedBumpIndex == -1 && 2933 // Intrusiveness trumps priority, hence ignore intrusives. 2934 !record.isRecentlyIntrusive() && 2935 // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so 2936 // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT 2937 // (or lower as a safeguard) is sufficient to find the speedbump index. 2938 // We'll have to revisit this when more package priority buckets are introduced. 2939 record.getPackagePriority() <= Notification.PRIORITY_DEFAULT && 2940 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) { 2941 speedBumpIndex = keys.size() - 1; 2942 } 2943 } 2944 String[] keysAr = keys.toArray(new String[keys.size()]); 2945 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]); 2946 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides, 2947 speedBumpIndex); 2948 } 2949 2950 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { 2951 if (!listener.enabledAndUserMatches(sbn.getUserId())) { 2952 return false; 2953 } 2954 // TODO: remove this for older listeners. 2955 return true; 2956 } 2957 2958 public class NotificationListeners extends ManagedServices { 2959 2960 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>(); 2961 private boolean mNotificationGroupsDesired; 2962 2963 public NotificationListeners() { 2964 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2965 } 2966 2967 @Override 2968 protected Config getConfig() { 2969 Config c = new Config(); 2970 c.caption = "notification listener"; 2971 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2972 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2973 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2974 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2975 c.clientLabel = R.string.notification_listener_binding_label; 2976 return c; 2977 } 2978 2979 @Override 2980 protected IInterface asInterface(IBinder binder) { 2981 return INotificationListener.Stub.asInterface(binder); 2982 } 2983 2984 @Override 2985 public void onServiceAdded(ManagedServiceInfo info) { 2986 final INotificationListener listener = (INotificationListener) info.service; 2987 final NotificationRankingUpdate update; 2988 synchronized (mNotificationList) { 2989 updateNotificationGroupsDesiredLocked(); 2990 update = makeRankingUpdateLocked(info); 2991 } 2992 try { 2993 listener.onListenerConnected(update); 2994 } catch (RemoteException e) { 2995 // we tried 2996 } 2997 } 2998 2999 @Override 3000 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 3001 if (mListenersDisablingEffects.remove(removed)) { 3002 updateListenerHintsLocked(); 3003 updateEffectsSuppressorLocked(); 3004 } 3005 mLightTrimListeners.remove(removed); 3006 updateNotificationGroupsDesiredLocked(); 3007 } 3008 3009 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) { 3010 if (trim == TRIM_LIGHT) { 3011 mLightTrimListeners.add(info); 3012 } else { 3013 mLightTrimListeners.remove(info); 3014 } 3015 } 3016 3017 public int getOnNotificationPostedTrim(ManagedServiceInfo info) { 3018 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL; 3019 3020 } 3021 3022 /** 3023 * asynchronously notify all listeners about a new notification 3024 * 3025 * <p> 3026 * Also takes care of removing a notification that has been visible to a listener before, 3027 * but isn't anymore. 3028 */ 3029 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) { 3030 // Lazily initialized snapshots of the notification. 3031 StatusBarNotification sbnClone = null; 3032 StatusBarNotification sbnCloneLight = null; 3033 3034 for (final ManagedServiceInfo info : mServices) { 3035 boolean sbnVisible = isVisibleToListener(sbn, info); 3036 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false; 3037 // This notification hasn't been and still isn't visible -> ignore. 3038 if (!oldSbnVisible && !sbnVisible) { 3039 continue; 3040 } 3041 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 3042 3043 // This notification became invisible -> remove the old one. 3044 if (oldSbnVisible && !sbnVisible) { 3045 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight(); 3046 mHandler.post(new Runnable() { 3047 @Override 3048 public void run() { 3049 notifyRemoved(info, oldSbnLightClone, update); 3050 } 3051 }); 3052 continue; 3053 } 3054 3055 final int trim = mListeners.getOnNotificationPostedTrim(info); 3056 3057 if (trim == TRIM_LIGHT && sbnCloneLight == null) { 3058 sbnCloneLight = sbn.cloneLight(); 3059 } else if (trim == TRIM_FULL && sbnClone == null) { 3060 sbnClone = sbn.clone(); 3061 } 3062 final StatusBarNotification sbnToPost = 3063 (trim == TRIM_FULL) ? sbnClone : sbnCloneLight; 3064 3065 mHandler.post(new Runnable() { 3066 @Override 3067 public void run() { 3068 notifyPosted(info, sbnToPost, update); 3069 } 3070 }); 3071 } 3072 } 3073 3074 /** 3075 * asynchronously notify all listeners about a removed notification 3076 */ 3077 public void notifyRemovedLocked(StatusBarNotification sbn) { 3078 // make a copy in case changes are made to the underlying Notification object 3079 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 3080 // notification 3081 final StatusBarNotification sbnLight = sbn.cloneLight(); 3082 for (final ManagedServiceInfo info : mServices) { 3083 if (!isVisibleToListener(sbn, info)) { 3084 continue; 3085 } 3086 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 3087 mHandler.post(new Runnable() { 3088 @Override 3089 public void run() { 3090 notifyRemoved(info, sbnLight, update); 3091 } 3092 }); 3093 } 3094 } 3095 3096 /** 3097 * asynchronously notify all listeners about a reordering of notifications 3098 */ 3099 public void notifyRankingUpdateLocked() { 3100 for (final ManagedServiceInfo serviceInfo : mServices) { 3101 if (!serviceInfo.isEnabledForCurrentProfiles()) { 3102 continue; 3103 } 3104 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo); 3105 mHandler.post(new Runnable() { 3106 @Override 3107 public void run() { 3108 notifyRankingUpdate(serviceInfo, update); 3109 } 3110 }); 3111 } 3112 } 3113 3114 public void notifyListenerHintsChangedLocked(final int hints) { 3115 for (final ManagedServiceInfo serviceInfo : mServices) { 3116 if (!serviceInfo.isEnabledForCurrentProfiles()) { 3117 continue; 3118 } 3119 mHandler.post(new Runnable() { 3120 @Override 3121 public void run() { 3122 notifyListenerHintsChanged(serviceInfo, hints); 3123 } 3124 }); 3125 } 3126 } 3127 3128 public void notifyInterruptionFilterChanged(final int interruptionFilter) { 3129 for (final ManagedServiceInfo serviceInfo : mServices) { 3130 if (!serviceInfo.isEnabledForCurrentProfiles()) { 3131 continue; 3132 } 3133 mHandler.post(new Runnable() { 3134 @Override 3135 public void run() { 3136 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter); 3137 } 3138 }); 3139 } 3140 } 3141 3142 private void notifyPosted(final ManagedServiceInfo info, 3143 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 3144 final INotificationListener listener = (INotificationListener)info.service; 3145 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); 3146 try { 3147 listener.onNotificationPosted(sbnHolder, rankingUpdate); 3148 } catch (RemoteException ex) { 3149 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 3150 } 3151 } 3152 3153 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn, 3154 NotificationRankingUpdate rankingUpdate) { 3155 if (!info.enabledAndUserMatches(sbn.getUserId())) { 3156 return; 3157 } 3158 final INotificationListener listener = (INotificationListener) info.service; 3159 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); 3160 try { 3161 listener.onNotificationRemoved(sbnHolder, rankingUpdate); 3162 } catch (RemoteException ex) { 3163 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 3164 } 3165 } 3166 3167 private void notifyRankingUpdate(ManagedServiceInfo info, 3168 NotificationRankingUpdate rankingUpdate) { 3169 final INotificationListener listener = (INotificationListener) info.service; 3170 try { 3171 listener.onNotificationRankingUpdate(rankingUpdate); 3172 } catch (RemoteException ex) { 3173 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 3174 } 3175 } 3176 3177 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) { 3178 final INotificationListener listener = (INotificationListener) info.service; 3179 try { 3180 listener.onListenerHintsChanged(hints); 3181 } catch (RemoteException ex) { 3182 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex); 3183 } 3184 } 3185 3186 private void notifyInterruptionFilterChanged(ManagedServiceInfo info, 3187 int interruptionFilter) { 3188 final INotificationListener listener = (INotificationListener) info.service; 3189 try { 3190 listener.onInterruptionFilterChanged(interruptionFilter); 3191 } catch (RemoteException ex) { 3192 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex); 3193 } 3194 } 3195 3196 private boolean isListenerPackage(String packageName) { 3197 if (packageName == null) { 3198 return false; 3199 } 3200 // TODO: clean up locking object later 3201 synchronized (mNotificationList) { 3202 for (final ManagedServiceInfo serviceInfo : mServices) { 3203 if (packageName.equals(serviceInfo.component.getPackageName())) { 3204 return true; 3205 } 3206 } 3207 } 3208 return false; 3209 } 3210 3211 /** 3212 * Returns whether any of the currently registered listeners wants to receive notification 3213 * groups. 3214 * 3215 * <p>Currently we assume groups are desired by non-SystemUI listeners.</p> 3216 */ 3217 public boolean notificationGroupsDesired() { 3218 return mNotificationGroupsDesired; 3219 } 3220 3221 private void updateNotificationGroupsDesiredLocked() { 3222 mNotificationGroupsDesired = true; 3223 // No listeners, no groups. 3224 if (mServices.isEmpty()) { 3225 mNotificationGroupsDesired = false; 3226 return; 3227 } 3228 // One listener: Check whether it's SysUI. 3229 if (mServices.size() == 1 && 3230 mServices.get(0).component.getPackageName().equals("com.android.systemui")) { 3231 mNotificationGroupsDesired = false; 3232 return; 3233 } 3234 } 3235 } 3236 3237 public static final class DumpFilter { 3238 public String pkgFilter; 3239 public boolean zen; 3240 3241 public static DumpFilter parseFromArguments(String[] args) { 3242 if (args != null && args.length == 2 && "p".equals(args[0]) 3243 && args[1] != null && !args[1].trim().isEmpty()) { 3244 final DumpFilter filter = new DumpFilter(); 3245 filter.pkgFilter = args[1].trim().toLowerCase(); 3246 return filter; 3247 } 3248 if (args != null && args.length == 1 && "zen".equals(args[0])) { 3249 final DumpFilter filter = new DumpFilter(); 3250 filter.zen = true; 3251 return filter; 3252 } 3253 return null; 3254 } 3255 3256 public boolean matches(StatusBarNotification sbn) { 3257 return zen ? true : sbn != null 3258 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg())); 3259 } 3260 3261 public boolean matches(ComponentName component) { 3262 return zen ? true : component != null && matches(component.getPackageName()); 3263 } 3264 3265 public boolean matches(String pkg) { 3266 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter); 3267 } 3268 3269 @Override 3270 public String toString() { 3271 return zen ? "zen" : ('\'' + pkgFilter + '\''); 3272 } 3273 } 3274 3275 /** 3276 * Wrapper for a StatusBarNotification object that allows transfer across a oneway 3277 * binder without sending large amounts of data over a oneway transaction. 3278 */ 3279 private static final class StatusBarNotificationHolder 3280 extends IStatusBarNotificationHolder.Stub { 3281 private StatusBarNotification mValue; 3282 3283 public StatusBarNotificationHolder(StatusBarNotification value) { 3284 mValue = value; 3285 } 3286 3287 /** Get the held value and clear it. This function should only be called once per holder */ 3288 @Override 3289 public StatusBarNotification get() { 3290 StatusBarNotification value = mValue; 3291 mValue = null; 3292 return value; 3293 } 3294 } 3295} 3296