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