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