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