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