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