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