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