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