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