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