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