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