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