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