NotificationManagerService.java revision 1fa865f396e94913cfbbd32ce7799c2e8aeb7a08
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 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) { 744 int[] ar = r.getIntArray(resid); 745 if (ar == null) { 746 return def; 747 } 748 final int len = ar.length > maxlen ? maxlen : ar.length; 749 long[] out = new long[len]; 750 for (int i=0; i<len; i++) { 751 out[i] = ar[i]; 752 } 753 return out; 754 } 755 756 public NotificationManagerService(Context context) { 757 super(context); 758 } 759 760 @Override 761 public void onStart() { 762 Resources resources = getContext().getResources(); 763 764 mAm = ActivityManagerNative.getDefault(); 765 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); 766 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); 767 768 mHandler = new WorkerHandler(); 769 mRankingThread.start(); 770 String[] extractorNames; 771 try { 772 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors); 773 } catch (Resources.NotFoundException e) { 774 extractorNames = new String[0]; 775 } 776 mRankingHelper = new RankingHelper(getContext(), 777 new RankingWorkerHandler(mRankingThread.getLooper()), 778 extractorNames); 779 mZenModeHelper = new ZenModeHelper(getContext(), mHandler); 780 mZenModeHelper.addCallback(new ZenModeHelper.Callback() { 781 @Override 782 public void onConfigChanged() { 783 savePolicyFile(); 784 } 785 }); 786 final File systemDir = new File(Environment.getDataDirectory(), "system"); 787 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml")); 788 mUsageStats = new NotificationUsageStats(getContext()); 789 790 importOldBlockDb(); 791 792 mListeners = new NotificationListeners(); 793 mConditionProviders = new ConditionProviders(getContext(), 794 mHandler, mUserProfiles, mZenModeHelper); 795 mStatusBar = getLocalService(StatusBarManagerInternal.class); 796 mStatusBar.setNotificationDelegate(mNotificationDelegate); 797 798 final LightsManager lights = getLocalService(LightsManager.class); 799 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS); 800 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION); 801 802 mDefaultNotificationColor = resources.getColor( 803 R.color.config_defaultNotificationColor); 804 mDefaultNotificationLedOn = resources.getInteger( 805 R.integer.config_defaultNotificationLedOn); 806 mDefaultNotificationLedOff = resources.getInteger( 807 R.integer.config_defaultNotificationLedOff); 808 809 mDefaultVibrationPattern = getLongArray(resources, 810 R.array.config_defaultNotificationVibePattern, 811 VIBRATE_PATTERN_MAXLEN, 812 DEFAULT_VIBRATE_PATTERN); 813 814 mFallbackVibrationPattern = getLongArray(resources, 815 R.array.config_notificationFallbackVibePattern, 816 VIBRATE_PATTERN_MAXLEN, 817 DEFAULT_VIBRATE_PATTERN); 818 819 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight); 820 821 // Don't start allowing notifications until the setup wizard has run once. 822 // After that, including subsequent boots, init with notifications turned on. 823 // This works on the first boot because the setup wizard will toggle this 824 // flag at least once and we'll go back to 0 after that. 825 if (0 == Settings.Global.getInt(getContext().getContentResolver(), 826 Settings.Global.DEVICE_PROVISIONED, 0)) { 827 mDisableNotificationAlerts = true; 828 } 829 mZenModeHelper.updateZenMode(); 830 831 mUserProfiles.updateCache(getContext()); 832 833 // register for various Intents 834 IntentFilter filter = new IntentFilter(); 835 filter.addAction(Intent.ACTION_SCREEN_ON); 836 filter.addAction(Intent.ACTION_SCREEN_OFF); 837 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 838 filter.addAction(Intent.ACTION_USER_PRESENT); 839 filter.addAction(Intent.ACTION_USER_STOPPED); 840 filter.addAction(Intent.ACTION_USER_SWITCHED); 841 filter.addAction(Intent.ACTION_USER_ADDED); 842 getContext().registerReceiver(mIntentReceiver, filter); 843 IntentFilter pkgFilter = new IntentFilter(); 844 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 845 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 846 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 847 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 848 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 849 pkgFilter.addDataScheme("package"); 850 getContext().registerReceiver(mIntentReceiver, pkgFilter); 851 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 852 getContext().registerReceiver(mIntentReceiver, sdFilter); 853 854 mSettingsObserver = new SettingsObserver(mHandler); 855 856 mArchive = new Archive(resources.getInteger( 857 R.integer.config_notificationServiceArchiveSize)); 858 859 publishBinderService(Context.NOTIFICATION_SERVICE, mService); 860 publishLocalService(NotificationManagerInternal.class, mInternalService); 861 } 862 863 /** 864 * Read the old XML-based app block database and import those blockages into the AppOps system. 865 */ 866 private void importOldBlockDb() { 867 loadPolicyFile(); 868 869 PackageManager pm = getContext().getPackageManager(); 870 for (String pkg : mBlockedPackages) { 871 PackageInfo info = null; 872 try { 873 info = pm.getPackageInfo(pkg, 0); 874 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false); 875 } catch (NameNotFoundException e) { 876 // forget you 877 } 878 } 879 mBlockedPackages.clear(); 880 } 881 882 @Override 883 public void onBootPhase(int phase) { 884 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 885 // no beeping until we're basically done booting 886 mSystemReady = true; 887 888 // Grab our optional AudioService 889 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 890 mZenModeHelper.setAudioManager(mAudioManager); 891 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 892 // This observer will force an update when observe is called, causing us to 893 // bind to listener services. 894 mSettingsObserver.observe(); 895 mListeners.onBootPhaseAppsCanStart(); 896 mConditionProviders.onBootPhaseAppsCanStart(); 897 } 898 } 899 900 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) { 901 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); 902 903 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, 904 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 905 906 // Now, cancel any outstanding notifications that are part of a just-disabled app 907 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { 908 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid), 909 REASON_PACKAGE_BANNED, null); 910 } 911 } 912 913 private void updateListenerFlagsLocked() { 914 final int flags = mListenersDisablingAlerts.isEmpty() ? 0 : FLAG_DISABLE_HOST_ALERTS; 915 if (flags == mListenerFlags) return; 916 mListenerFlags = flags; 917 scheduleListenerFlagsChanged(flags); 918 } 919 920 private final IBinder mService = new INotificationManager.Stub() { 921 // Toasts 922 // ============================================================================ 923 924 @Override 925 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 926 { 927 if (DBG) { 928 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback 929 + " duration=" + duration); 930 } 931 932 if (pkg == null || callback == null) { 933 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 934 return ; 935 } 936 937 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); 938 939 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { 940 if (!isSystemToast) { 941 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 942 return; 943 } 944 } 945 946 synchronized (mToastQueue) { 947 int callingPid = Binder.getCallingPid(); 948 long callingId = Binder.clearCallingIdentity(); 949 try { 950 ToastRecord record; 951 int index = indexOfToastLocked(pkg, callback); 952 // If it's already in the queue, we update it in place, we don't 953 // move it to the end of the queue. 954 if (index >= 0) { 955 record = mToastQueue.get(index); 956 record.update(duration); 957 } else { 958 // Limit the number of toasts that any given package except the android 959 // package can enqueue. Prevents DOS attacks and deals with leaks. 960 if (!isSystemToast) { 961 int count = 0; 962 final int N = mToastQueue.size(); 963 for (int i=0; i<N; i++) { 964 final ToastRecord r = mToastQueue.get(i); 965 if (r.pkg.equals(pkg)) { 966 count++; 967 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 968 Slog.e(TAG, "Package has already posted " + count 969 + " toasts. Not showing more. Package=" + pkg); 970 return; 971 } 972 } 973 } 974 } 975 976 record = new ToastRecord(callingPid, pkg, callback, duration); 977 mToastQueue.add(record); 978 index = mToastQueue.size() - 1; 979 keepProcessAliveLocked(callingPid); 980 } 981 // If it's at index 0, it's the current toast. It doesn't matter if it's 982 // new or just been updated. Call back and tell it to show itself. 983 // If the callback fails, this will remove it from the list, so don't 984 // assume that it's valid after this. 985 if (index == 0) { 986 showNextToastLocked(); 987 } 988 } finally { 989 Binder.restoreCallingIdentity(callingId); 990 } 991 } 992 } 993 994 @Override 995 public void cancelToast(String pkg, ITransientNotification callback) { 996 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 997 998 if (pkg == null || callback == null) { 999 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 1000 return ; 1001 } 1002 1003 synchronized (mToastQueue) { 1004 long callingId = Binder.clearCallingIdentity(); 1005 try { 1006 int index = indexOfToastLocked(pkg, callback); 1007 if (index >= 0) { 1008 cancelToastLocked(index); 1009 } else { 1010 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg 1011 + " callback=" + callback); 1012 } 1013 } finally { 1014 Binder.restoreCallingIdentity(callingId); 1015 } 1016 } 1017 } 1018 1019 @Override 1020 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, 1021 Notification notification, int[] idOut, int userId) throws RemoteException { 1022 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(), 1023 Binder.getCallingPid(), tag, id, notification, idOut, userId); 1024 } 1025 1026 @Override 1027 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 1028 checkCallerIsSystemOrSameApp(pkg); 1029 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1030 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 1031 // Don't allow client applications to cancel foreground service notis. 1032 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0, 1033 Binder.getCallingUid() == Process.SYSTEM_UID 1034 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL, 1035 null); 1036 } 1037 1038 @Override 1039 public void cancelAllNotifications(String pkg, int userId) { 1040 checkCallerIsSystemOrSameApp(pkg); 1041 1042 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1043 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 1044 1045 // Calling from user space, don't allow the canceling of actively 1046 // running foreground services. 1047 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(), 1048 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId, 1049 REASON_NOMAN_CANCEL_ALL, null); 1050 } 1051 1052 @Override 1053 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { 1054 checkCallerIsSystem(); 1055 1056 setNotificationsEnabledForPackageImpl(pkg, uid, enabled); 1057 } 1058 1059 /** 1060 * Use this when you just want to know if notifications are OK for this package. 1061 */ 1062 @Override 1063 public boolean areNotificationsEnabledForPackage(String pkg, int uid) { 1064 checkCallerIsSystem(); 1065 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 1066 == AppOpsManager.MODE_ALLOWED); 1067 } 1068 1069 @Override 1070 public void setPackagePriority(String pkg, int uid, int priority) { 1071 checkCallerIsSystem(); 1072 mRankingHelper.setPackagePriority(pkg, uid, priority); 1073 savePolicyFile(); 1074 } 1075 1076 @Override 1077 public int getPackagePriority(String pkg, int uid) { 1078 checkCallerIsSystem(); 1079 return mRankingHelper.getPackagePriority(pkg, uid); 1080 } 1081 1082 /** 1083 * System-only API for getting a list of current (i.e. not cleared) notifications. 1084 * 1085 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1086 * @returns A list of all the notifications, in natural order. 1087 */ 1088 @Override 1089 public StatusBarNotification[] getActiveNotifications(String callingPkg) { 1090 // enforce() will ensure the calling uid has the correct permission 1091 getContext().enforceCallingOrSelfPermission( 1092 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1093 "NotificationManagerService.getActiveNotifications"); 1094 1095 StatusBarNotification[] tmp = null; 1096 int uid = Binder.getCallingUid(); 1097 1098 // noteOp will check to make sure the callingPkg matches the uid 1099 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1100 == AppOpsManager.MODE_ALLOWED) { 1101 synchronized (mNotificationList) { 1102 tmp = new StatusBarNotification[mNotificationList.size()]; 1103 final int N = mNotificationList.size(); 1104 for (int i=0; i<N; i++) { 1105 tmp[i] = mNotificationList.get(i).sbn; 1106 } 1107 } 1108 } 1109 return tmp; 1110 } 1111 1112 /** 1113 * System-only API for getting a list of recent (cleared, no longer shown) notifications. 1114 * 1115 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1116 */ 1117 @Override 1118 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { 1119 // enforce() will ensure the calling uid has the correct permission 1120 getContext().enforceCallingOrSelfPermission( 1121 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1122 "NotificationManagerService.getHistoricalNotifications"); 1123 1124 StatusBarNotification[] tmp = null; 1125 int uid = Binder.getCallingUid(); 1126 1127 // noteOp will check to make sure the callingPkg matches the uid 1128 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1129 == AppOpsManager.MODE_ALLOWED) { 1130 synchronized (mArchive) { 1131 tmp = mArchive.getArray(count); 1132 } 1133 } 1134 return tmp; 1135 } 1136 1137 /** 1138 * Register a listener binder directly with the notification manager. 1139 * 1140 * Only works with system callers. Apps should extend 1141 * {@link android.service.notification.NotificationListenerService}. 1142 */ 1143 @Override 1144 public void registerListener(final INotificationListener listener, 1145 final ComponentName component, final int userid) { 1146 enforceSystemOrSystemUI("INotificationManager.registerListener"); 1147 mListeners.registerService(listener, component, userid); 1148 } 1149 1150 /** 1151 * Remove a listener binder directly 1152 */ 1153 @Override 1154 public void unregisterListener(INotificationListener listener, int userid) { 1155 mListeners.unregisterService(listener, userid); 1156 } 1157 1158 /** 1159 * Allow an INotificationListener to simulate a "clear all" operation. 1160 * 1161 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} 1162 * 1163 * @param token The binder for the listener, to check that the caller is allowed 1164 */ 1165 @Override 1166 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) { 1167 final int callingUid = Binder.getCallingUid(); 1168 final int callingPid = Binder.getCallingPid(); 1169 long identity = Binder.clearCallingIdentity(); 1170 try { 1171 synchronized (mNotificationList) { 1172 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1173 if (keys != null) { 1174 final int N = keys.length; 1175 for (int i = 0; i < N; i++) { 1176 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1177 final int userId = r.sbn.getUserId(); 1178 if (userId != info.userid && userId != UserHandle.USER_ALL && 1179 !mUserProfiles.isCurrentProfile(userId)) { 1180 throw new SecurityException("Disallowed call from listener: " 1181 + info.service); 1182 } 1183 if (r != null) { 1184 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1185 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(), 1186 userId); 1187 } 1188 } 1189 } else { 1190 cancelAllLocked(callingUid, callingPid, info.userid, 1191 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles()); 1192 } 1193 } 1194 } finally { 1195 Binder.restoreCallingIdentity(identity); 1196 } 1197 } 1198 1199 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info, 1200 int callingUid, int callingPid, String pkg, String tag, int id, int userId) { 1201 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 1202 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 1203 true, 1204 userId, REASON_LISTENER_CANCEL, info); 1205 } 1206 1207 /** 1208 * Allow an INotificationListener to simulate clearing (dismissing) a single notification. 1209 * 1210 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} 1211 * 1212 * @param token The binder for the listener, to check that the caller is allowed 1213 */ 1214 @Override 1215 public void cancelNotificationFromListener(INotificationListener token, String pkg, 1216 String tag, int id) { 1217 final int callingUid = Binder.getCallingUid(); 1218 final int callingPid = Binder.getCallingPid(); 1219 long identity = Binder.clearCallingIdentity(); 1220 try { 1221 synchronized (mNotificationList) { 1222 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1223 if (info.supportsProfiles()) { 1224 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) " 1225 + "from " + info.component 1226 + " use cancelNotification(key) instead."); 1227 } else { 1228 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1229 pkg, tag, id, info.userid); 1230 } 1231 } 1232 } finally { 1233 Binder.restoreCallingIdentity(identity); 1234 } 1235 } 1236 1237 /** 1238 * Allow an INotificationListener to request the list of outstanding notifications seen by 1239 * the current user. Useful when starting up, after which point the listener callbacks 1240 * should be used. 1241 * 1242 * @param token The binder for the listener, to check that the caller is allowed 1243 * @returns The return value will contain the notifications specified in keys, in that 1244 * order, or if keys is null, all the notifications, in natural order. 1245 */ 1246 @Override 1247 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener( 1248 INotificationListener token) { 1249 synchronized (mNotificationList) { 1250 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1251 final ArrayList<StatusBarNotification> list 1252 = new ArrayList<StatusBarNotification>(); 1253 final int N = mNotificationList.size(); 1254 for (int i=0; i<N; i++) { 1255 StatusBarNotification sbn = mNotificationList.get(i).sbn; 1256 if (info.enabledAndUserMatches(sbn.getUserId())) { 1257 list.add(sbn); 1258 } 1259 } 1260 return new ParceledListSlice<StatusBarNotification>(list); 1261 } 1262 } 1263 1264 @Override 1265 public void requestFlagsFromListener(INotificationListener token, int flags) { 1266 synchronized (mNotificationList) { 1267 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1268 final boolean disableAlerts = (flags & FLAG_DISABLE_HOST_ALERTS) != 0; 1269 if (disableAlerts) { 1270 mListenersDisablingAlerts.add(info); 1271 } else { 1272 mListenersDisablingAlerts.remove(info); 1273 } 1274 updateListenerFlagsLocked(); 1275 } 1276 } 1277 1278 @Override 1279 public int getFlagsFromListener(INotificationListener token) { 1280 synchronized (mNotificationList) { 1281 return mListenerFlags; 1282 } 1283 } 1284 1285 @Override 1286 public ZenModeConfig getZenModeConfig() { 1287 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig"); 1288 return mZenModeHelper.getConfig(); 1289 } 1290 1291 @Override 1292 public boolean setZenModeConfig(ZenModeConfig config) { 1293 checkCallerIsSystem(); 1294 return mZenModeHelper.setConfig(config); 1295 } 1296 1297 @Override 1298 public void notifyConditions(String pkg, IConditionProvider provider, 1299 Condition[] conditions) { 1300 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider); 1301 checkCallerIsSystemOrSameApp(pkg); 1302 final long identity = Binder.clearCallingIdentity(); 1303 try { 1304 mConditionProviders.notifyConditions(pkg, info, conditions); 1305 } finally { 1306 Binder.restoreCallingIdentity(identity); 1307 } 1308 } 1309 1310 @Override 1311 public void requestZenModeConditions(IConditionListener callback, int relevance) { 1312 enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions"); 1313 mConditionProviders.requestZenModeConditions(callback, relevance); 1314 } 1315 1316 @Override 1317 public void setZenModeCondition(Uri conditionId) { 1318 enforceSystemOrSystemUI("INotificationManager.setZenModeCondition"); 1319 final long identity = Binder.clearCallingIdentity(); 1320 try { 1321 mConditionProviders.setZenModeCondition(conditionId, "binderCall"); 1322 } finally { 1323 Binder.restoreCallingIdentity(identity); 1324 } 1325 } 1326 1327 @Override 1328 public void setAutomaticZenModeConditions(Uri[] conditionIds) { 1329 enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions"); 1330 mConditionProviders.setAutomaticZenModeConditions(conditionIds); 1331 } 1332 1333 @Override 1334 public Condition[] getAutomaticZenModeConditions() { 1335 enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions"); 1336 return mConditionProviders.getAutomaticZenModeConditions(); 1337 } 1338 1339 private void enforceSystemOrSystemUI(String message) { 1340 if (isCallerSystem()) return; 1341 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 1342 message); 1343 } 1344 1345 @Override 1346 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1347 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1348 != PackageManager.PERMISSION_GRANTED) { 1349 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1350 + Binder.getCallingPid() 1351 + ", uid=" + Binder.getCallingUid()); 1352 return; 1353 } 1354 1355 dumpImpl(pw, DumpFilter.parseFromArguments(args)); 1356 } 1357 }; 1358 1359 private String[] getActiveNotificationKeys(INotificationListener token) { 1360 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1361 final ArrayList<String> keys = new ArrayList<String>(); 1362 if (info.isEnabledForCurrentProfiles()) { 1363 synchronized (mNotificationList) { 1364 final int N = mNotificationList.size(); 1365 for (int i = 0; i < N; i++) { 1366 final StatusBarNotification sbn = mNotificationList.get(i).sbn; 1367 if (info.enabledAndUserMatches(sbn.getUserId())) { 1368 keys.add(sbn.getKey()); 1369 } 1370 } 1371 } 1372 } 1373 return keys.toArray(new String[keys.size()]); 1374 } 1375 1376 private boolean disableNotificationAlerts() { 1377 return mDisableNotificationAlerts || (mListenerFlags & FLAG_DISABLE_HOST_ALERTS) != 0; 1378 } 1379 1380 void dumpImpl(PrintWriter pw, DumpFilter filter) { 1381 pw.print("Current Notification Manager state"); 1382 if (filter != null) { 1383 pw.print(" (filtered to "); pw.print(filter); pw.print(")"); 1384 } 1385 pw.println(':'); 1386 int N; 1387 final boolean zenOnly = filter != null && filter.zen; 1388 1389 if (!zenOnly) { 1390 synchronized (mToastQueue) { 1391 N = mToastQueue.size(); 1392 if (N > 0) { 1393 pw.println(" Toast Queue:"); 1394 for (int i=0; i<N; i++) { 1395 mToastQueue.get(i).dump(pw, " ", filter); 1396 } 1397 pw.println(" "); 1398 } 1399 } 1400 } 1401 1402 synchronized (mNotificationList) { 1403 if (!zenOnly) { 1404 N = mNotificationList.size(); 1405 if (N > 0) { 1406 pw.println(" Notification List:"); 1407 for (int i=0; i<N; i++) { 1408 final NotificationRecord nr = mNotificationList.get(i); 1409 if (filter != null && !filter.matches(nr.sbn)) continue; 1410 nr.dump(pw, " ", getContext()); 1411 } 1412 pw.println(" "); 1413 } 1414 1415 if (filter == null) { 1416 N = mLights.size(); 1417 if (N > 0) { 1418 pw.println(" Lights List:"); 1419 for (int i=0; i<N; i++) { 1420 pw.println(" " + mLights.get(i)); 1421 } 1422 pw.println(" "); 1423 } 1424 1425 pw.println(" mSoundNotification=" + mSoundNotification); 1426 pw.println(" mVibrateNotification=" + mVibrateNotification); 1427 pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts); 1428 pw.println(" mSystemReady=" + mSystemReady); 1429 } 1430 pw.println(" mArchive=" + mArchive.toString()); 1431 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1432 int i=0; 1433 while (iter.hasNext()) { 1434 final StatusBarNotification sbn = iter.next(); 1435 if (filter != null && !filter.matches(sbn)) continue; 1436 pw.println(" " + sbn); 1437 if (++i >= 5) { 1438 if (iter.hasNext()) pw.println(" ..."); 1439 break; 1440 } 1441 } 1442 } 1443 1444 if (!zenOnly) { 1445 pw.println("\n Usage Stats:"); 1446 mUsageStats.dump(pw, " ", filter); 1447 } 1448 1449 if (filter == null || zenOnly) { 1450 pw.println("\n Zen Mode:"); 1451 mZenModeHelper.dump(pw, " "); 1452 1453 pw.println("\n Zen Log:"); 1454 ZenLog.dump(pw, " "); 1455 } 1456 1457 if (!zenOnly) { 1458 pw.println("\n Ranking Config:"); 1459 mRankingHelper.dump(pw, " ", filter); 1460 1461 pw.println("\n Notification listeners:"); 1462 mListeners.dump(pw, filter); 1463 pw.print(" mListenerFlags: "); pw.println(mListenerFlags); 1464 pw.print(" mListenersDisablingAlerts: ("); 1465 N = mListenersDisablingAlerts.size(); 1466 for (int i = 0; i < N; i++) { 1467 final ManagedServiceInfo listener = mListenersDisablingAlerts.valueAt(i); 1468 if (i > 0) pw.print(','); 1469 pw.print(listener.component); 1470 } 1471 pw.println(')'); 1472 } 1473 1474 pw.println("\n Condition providers:"); 1475 mConditionProviders.dump(pw, filter); 1476 } 1477 } 1478 1479 /** 1480 * The private API only accessible to the system process. 1481 */ 1482 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1483 @Override 1484 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, 1485 String tag, int id, Notification notification, int[] idReceived, int userId) { 1486 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 1487 idReceived, userId); 1488 } 1489 }; 1490 1491 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, 1492 final int callingPid, final String tag, final int id, final Notification notification, 1493 int[] idOut, int incomingUserId) { 1494 if (DBG) { 1495 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 1496 + " notification=" + notification); 1497 } 1498 checkCallerIsSystemOrSameApp(pkg); 1499 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 1500 1501 final int userId = ActivityManager.handleIncomingUser(callingPid, 1502 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1503 final UserHandle user = new UserHandle(userId); 1504 1505 // Limit the number of notifications that any given package except the android 1506 // package can enqueue. Prevents DOS attacks and deals with leaks. 1507 if (!isSystemNotification) { 1508 synchronized (mNotificationList) { 1509 int count = 0; 1510 final int N = mNotificationList.size(); 1511 for (int i=0; i<N; i++) { 1512 final NotificationRecord r = mNotificationList.get(i); 1513 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1514 count++; 1515 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1516 Slog.e(TAG, "Package has already posted " + count 1517 + " notifications. Not showing more. package=" + pkg); 1518 return; 1519 } 1520 } 1521 } 1522 } 1523 } 1524 1525 // This conditional is a dirty hack to limit the logging done on 1526 // behalf of the download manager without affecting other apps. 1527 if (!pkg.equals("com.android.providers.downloads") 1528 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1529 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 1530 pkg, id, tag, userId, notification.toString()); 1531 } 1532 1533 if (pkg == null || notification == null) { 1534 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1535 + " id=" + id + " notification=" + notification); 1536 } 1537 if (notification.icon != 0) { 1538 if (notification.contentView == null) { 1539 throw new IllegalArgumentException("contentView required: pkg=" + pkg 1540 + " id=" + id + " notification=" + notification); 1541 } 1542 } 1543 1544 mHandler.post(new Runnable() { 1545 @Override 1546 public void run() { 1547 1548 // === Scoring === 1549 1550 // 0. Sanitize inputs 1551 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1552 Notification.PRIORITY_MAX); 1553 // Migrate notification flags to scores 1554 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1555 if (notification.priority < Notification.PRIORITY_MAX) { 1556 notification.priority = Notification.PRIORITY_MAX; 1557 } 1558 } else if (SCORE_ONGOING_HIGHER && 1559 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1560 if (notification.priority < Notification.PRIORITY_HIGH) { 1561 notification.priority = Notification.PRIORITY_HIGH; 1562 } 1563 } 1564 1565 // 1. initial score: buckets of 10, around the app [-20..20] 1566 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; 1567 1568 // 2. extract ranking signals from the notification data 1569 final StatusBarNotification n = new StatusBarNotification( 1570 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 1571 user); 1572 NotificationRecord r = new NotificationRecord(n, score); 1573 NotificationRecord old = mNotificationsByKey.get(n.getKey()); 1574 if (old != null) { 1575 // Retain ranking information from previous record 1576 r.copyRankingInformation(old); 1577 } 1578 mRankingHelper.extractSignals(r); 1579 1580 // 3. Apply local rules 1581 1582 // blocked apps 1583 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1584 if (!isSystemNotification) { 1585 r.score = JUNK_SCORE; 1586 Slog.e(TAG, "Suppressing notification from package " + pkg 1587 + " by user request."); 1588 } 1589 } 1590 1591 if (r.score < SCORE_DISPLAY_THRESHOLD) { 1592 // Notification will be blocked because the score is too low. 1593 return; 1594 } 1595 1596 synchronized (mNotificationList) { 1597 int index = indexOfNotificationLocked(n.getKey()); 1598 if (index < 0) { 1599 mNotificationList.add(r); 1600 mUsageStats.registerPostedByApp(r); 1601 } else { 1602 old = mNotificationList.get(index); 1603 mNotificationList.set(index, r); 1604 mUsageStats.registerUpdatedByApp(r, old); 1605 // Make sure we don't lose the foreground service state. 1606 notification.flags |= 1607 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1608 mNotificationsByKey.remove(old.sbn.getKey()); 1609 r.isUpdate = true; 1610 } 1611 1612 mNotificationsByKey.put(n.getKey(), r); 1613 1614 // Ensure if this is a foreground service that the proper additional 1615 // flags are set. 1616 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1617 notification.flags |= Notification.FLAG_ONGOING_EVENT 1618 | Notification.FLAG_NO_CLEAR; 1619 } 1620 1621 applyZenModeLocked(r); 1622 1623 mRankingHelper.sort(mNotificationList); 1624 1625 if (notification.icon != 0) { 1626 mListeners.notifyPostedLocked(n); 1627 } else { 1628 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1629 if (old != null && !old.isCanceled) { 1630 mListeners.notifyRemovedLocked(n); 1631 } 1632 // ATTENTION: in a future release we will bail out here 1633 // so that we do not play sounds, show lights, etc. for invalid 1634 // notifications 1635 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1636 + n.getPackageName()); 1637 } 1638 1639 buzzBeepBlinkLocked(r); 1640 } 1641 } 1642 }); 1643 1644 idOut[0] = id; 1645 } 1646 1647 private void buzzBeepBlinkLocked(NotificationRecord record) { 1648 final Notification notification = record.sbn.getNotification(); 1649 1650 // Should this notification make noise, vibe, or use the LED? 1651 final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) && 1652 !record.isIntercepted(); 1653 if (DBG || record.isIntercepted()) 1654 Slog.v(TAG, 1655 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt + 1656 " intercept=" + record.isIntercepted() 1657 ); 1658 1659 final int currentUser; 1660 final long token = Binder.clearCallingIdentity(); 1661 try { 1662 currentUser = ActivityManager.getCurrentUser(); 1663 } finally { 1664 Binder.restoreCallingIdentity(token); 1665 } 1666 1667 // If we're not supposed to beep, vibrate, etc. then don't. 1668 if (!disableNotificationAlerts() 1669 && (!(record.isUpdate 1670 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1671 && (record.getUserId() == UserHandle.USER_ALL || 1672 record.getUserId() == currentUser || 1673 mUserProfiles.isCurrentProfile(record.getUserId())) 1674 && canInterrupt 1675 && mSystemReady 1676 && mAudioManager != null) { 1677 if (DBG) Slog.v(TAG, "Interrupting!"); 1678 1679 sendAccessibilityEvent(notification, record.sbn.getPackageName()); 1680 1681 // sound 1682 1683 // should we use the default notification sound? (indicated either by 1684 // DEFAULT_SOUND or because notification.sound is pointing at 1685 // Settings.System.NOTIFICATION_SOUND) 1686 final boolean useDefaultSound = 1687 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1688 Settings.System.DEFAULT_NOTIFICATION_URI 1689 .equals(notification.sound); 1690 1691 Uri soundUri = null; 1692 boolean hasValidSound = false; 1693 1694 if (useDefaultSound) { 1695 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1696 1697 // check to see if the default notification sound is silent 1698 ContentResolver resolver = getContext().getContentResolver(); 1699 hasValidSound = Settings.System.getString(resolver, 1700 Settings.System.NOTIFICATION_SOUND) != null; 1701 } else if (notification.sound != null) { 1702 soundUri = notification.sound; 1703 hasValidSound = (soundUri != null); 1704 } 1705 1706 if (hasValidSound) { 1707 boolean looping = 1708 (notification.flags & Notification.FLAG_INSISTENT) != 0; 1709 int audioStreamType; 1710 if (notification.audioStreamType >= 0) { 1711 audioStreamType = notification.audioStreamType; 1712 } else { 1713 audioStreamType = DEFAULT_STREAM_TYPE; 1714 } 1715 mSoundNotification = record; 1716 // do not play notifications if stream volume is 0 (typically because 1717 // ringer mode is silent) or if there is a user of exclusive audio focus 1718 if ((mAudioManager.getStreamVolume(audioStreamType) != 0) 1719 && !mAudioManager.isAudioFocusExclusive()) { 1720 final long identity = Binder.clearCallingIdentity(); 1721 try { 1722 final IRingtonePlayer player = 1723 mAudioManager.getRingtonePlayer(); 1724 if (player != null) { 1725 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 1726 + " on stream " + audioStreamType); 1727 player.playAsync(soundUri, record.sbn.getUser(), looping, 1728 audioStreamType); 1729 } 1730 } catch (RemoteException e) { 1731 } finally { 1732 Binder.restoreCallingIdentity(identity); 1733 } 1734 } 1735 } 1736 1737 // vibrate 1738 // Does the notification want to specify its own vibration? 1739 final boolean hasCustomVibrate = notification.vibrate != null; 1740 1741 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1742 // mode, and no other vibration is specified, we fall back to vibration 1743 final boolean convertSoundToVibration = 1744 !hasCustomVibrate 1745 && hasValidSound 1746 && (mAudioManager.getRingerMode() 1747 == AudioManager.RINGER_MODE_VIBRATE); 1748 1749 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1750 final boolean useDefaultVibrate = 1751 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1752 1753 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1754 && !(mAudioManager.getRingerMode() 1755 == AudioManager.RINGER_MODE_SILENT)) { 1756 mVibrateNotification = record; 1757 1758 if (useDefaultVibrate || convertSoundToVibration) { 1759 // Escalate privileges so we can use the vibrator even if the 1760 // notifying app does not have the VIBRATE permission. 1761 long identity = Binder.clearCallingIdentity(); 1762 try { 1763 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 1764 useDefaultVibrate ? mDefaultVibrationPattern 1765 : mFallbackVibrationPattern, 1766 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1767 ? 0: -1, audioAttributesForNotification(notification)); 1768 } finally { 1769 Binder.restoreCallingIdentity(identity); 1770 } 1771 } else if (notification.vibrate.length > 1) { 1772 // If you want your own vibration pattern, you need the VIBRATE 1773 // permission 1774 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 1775 notification.vibrate, 1776 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1777 ? 0: -1, audioAttributesForNotification(notification)); 1778 } 1779 } 1780 } 1781 1782 // light 1783 // release the light 1784 boolean wasShowLights = mLights.remove(record.getKey()); 1785 if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) { 1786 mLedNotification = null; 1787 } 1788 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) { 1789 mLights.add(record.getKey()); 1790 updateLightsLocked(); 1791 if (mUseAttentionLight) { 1792 mAttentionLight.pulse(); 1793 } 1794 } else if (wasShowLights) { 1795 updateLightsLocked(); 1796 } 1797 } 1798 1799 private static AudioAttributes audioAttributesForNotification(Notification n) { 1800 return new AudioAttributes.Builder().setLegacyStreamType(n.audioStreamType).build(); 1801 } 1802 1803 void showNextToastLocked() { 1804 ToastRecord record = mToastQueue.get(0); 1805 while (record != null) { 1806 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1807 try { 1808 record.callback.show(); 1809 scheduleTimeoutLocked(record); 1810 return; 1811 } catch (RemoteException e) { 1812 Slog.w(TAG, "Object died trying to show notification " + record.callback 1813 + " in package " + record.pkg); 1814 // remove it from the list and let the process die 1815 int index = mToastQueue.indexOf(record); 1816 if (index >= 0) { 1817 mToastQueue.remove(index); 1818 } 1819 keepProcessAliveLocked(record.pid); 1820 if (mToastQueue.size() > 0) { 1821 record = mToastQueue.get(0); 1822 } else { 1823 record = null; 1824 } 1825 } 1826 } 1827 } 1828 1829 void cancelToastLocked(int index) { 1830 ToastRecord record = mToastQueue.get(index); 1831 try { 1832 record.callback.hide(); 1833 } catch (RemoteException e) { 1834 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1835 + " in package " + record.pkg); 1836 // don't worry about this, we're about to remove it from 1837 // the list anyway 1838 } 1839 mToastQueue.remove(index); 1840 keepProcessAliveLocked(record.pid); 1841 if (mToastQueue.size() > 0) { 1842 // Show the next one. If the callback fails, this will remove 1843 // it from the list, so don't assume that the list hasn't changed 1844 // after this point. 1845 showNextToastLocked(); 1846 } 1847 } 1848 1849 private void scheduleTimeoutLocked(ToastRecord r) 1850 { 1851 mHandler.removeCallbacksAndMessages(r); 1852 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1853 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 1854 mHandler.sendMessageDelayed(m, delay); 1855 } 1856 1857 private void handleTimeout(ToastRecord record) 1858 { 1859 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1860 synchronized (mToastQueue) { 1861 int index = indexOfToastLocked(record.pkg, record.callback); 1862 if (index >= 0) { 1863 cancelToastLocked(index); 1864 } 1865 } 1866 } 1867 1868 // lock on mToastQueue 1869 int indexOfToastLocked(String pkg, ITransientNotification callback) 1870 { 1871 IBinder cbak = callback.asBinder(); 1872 ArrayList<ToastRecord> list = mToastQueue; 1873 int len = list.size(); 1874 for (int i=0; i<len; i++) { 1875 ToastRecord r = list.get(i); 1876 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1877 return i; 1878 } 1879 } 1880 return -1; 1881 } 1882 1883 // lock on mToastQueue 1884 void keepProcessAliveLocked(int pid) 1885 { 1886 int toastCount = 0; // toasts from this pid 1887 ArrayList<ToastRecord> list = mToastQueue; 1888 int N = list.size(); 1889 for (int i=0; i<N; i++) { 1890 ToastRecord r = list.get(i); 1891 if (r.pid == pid) { 1892 toastCount++; 1893 } 1894 } 1895 try { 1896 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 1897 } catch (RemoteException e) { 1898 // Shouldn't happen. 1899 } 1900 } 1901 1902 private void handleRankingReconsideration(Message message) { 1903 if (!(message.obj instanceof RankingReconsideration)) return; 1904 RankingReconsideration recon = (RankingReconsideration) message.obj; 1905 recon.run(); 1906 boolean changed; 1907 synchronized (mNotificationList) { 1908 final NotificationRecord record = mNotificationsByKey.get(recon.getKey()); 1909 if (record == null) { 1910 return; 1911 } 1912 int indexBefore = findNotificationRecordIndexLocked(record); 1913 boolean interceptBefore = record.isIntercepted(); 1914 recon.applyChangesLocked(record); 1915 applyZenModeLocked(record); 1916 mRankingHelper.sort(mNotificationList); 1917 int indexAfter = findNotificationRecordIndexLocked(record); 1918 boolean interceptAfter = record.isIntercepted(); 1919 changed = indexBefore != indexAfter || interceptBefore != interceptAfter; 1920 if (interceptBefore && !interceptAfter) { 1921 buzzBeepBlinkLocked(record); 1922 } 1923 } 1924 if (changed) { 1925 scheduleSendRankingUpdate(); 1926 } 1927 } 1928 1929 private void handleRankingConfigChange() { 1930 synchronized (mNotificationList) { 1931 final int N = mNotificationList.size(); 1932 ArrayList<String> orderBefore = new ArrayList<String>(N); 1933 for (int i = 0; i < N; i++) { 1934 final NotificationRecord r = mNotificationList.get(i); 1935 orderBefore.add(r.getKey()); 1936 mRankingHelper.extractSignals(r); 1937 } 1938 mRankingHelper.sort(mNotificationList); 1939 for (int i = 0; i < N; i++) { 1940 if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) { 1941 scheduleSendRankingUpdate(); 1942 return; 1943 } 1944 } 1945 } 1946 } 1947 1948 // let zen mode evaluate this record 1949 private void applyZenModeLocked(NotificationRecord record) { 1950 record.setIntercepted(mZenModeHelper.shouldIntercept(record)); 1951 } 1952 1953 // lock on mNotificationList 1954 private int findNotificationRecordIndexLocked(NotificationRecord target) { 1955 return mRankingHelper.indexOf(mNotificationList, target); 1956 } 1957 1958 private void scheduleSendRankingUpdate() { 1959 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 1960 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 1961 mHandler.sendMessage(m); 1962 } 1963 1964 private void handleSendRankingUpdate() { 1965 synchronized (mNotificationList) { 1966 mListeners.notifyRankingUpdateLocked(); 1967 } 1968 } 1969 1970 private void scheduleListenerFlagsChanged(int state) { 1971 mHandler.removeMessages(MESSAGE_LISTENER_FLAGS_CHANGED); 1972 mHandler.obtainMessage(MESSAGE_LISTENER_FLAGS_CHANGED, state, 0).sendToTarget(); 1973 } 1974 1975 private void handleListenerFlagsChanged(int state) { 1976 synchronized (mNotificationList) { 1977 mListeners.notifyListenerFlagsChangedLocked(state); 1978 } 1979 } 1980 1981 private final class WorkerHandler extends Handler 1982 { 1983 @Override 1984 public void handleMessage(Message msg) 1985 { 1986 switch (msg.what) 1987 { 1988 case MESSAGE_TIMEOUT: 1989 handleTimeout((ToastRecord)msg.obj); 1990 break; 1991 case MESSAGE_SAVE_POLICY_FILE: 1992 handleSavePolicyFile(); 1993 break; 1994 case MESSAGE_SEND_RANKING_UPDATE: 1995 handleSendRankingUpdate(); 1996 break; 1997 case MESSAGE_LISTENER_FLAGS_CHANGED: 1998 handleListenerFlagsChanged(msg.arg1); 1999 break; 2000 } 2001 } 2002 2003 } 2004 2005 private final class RankingWorkerHandler extends Handler 2006 { 2007 public RankingWorkerHandler(Looper looper) { 2008 super(looper); 2009 } 2010 2011 @Override 2012 public void handleMessage(Message msg) { 2013 switch (msg.what) { 2014 case MESSAGE_RECONSIDER_RANKING: 2015 handleRankingReconsideration(msg); 2016 break; 2017 case MESSAGE_RANKING_CONFIG_CHANGE: 2018 handleRankingConfigChange(); 2019 break; 2020 } 2021 } 2022 } 2023 2024 // Notifications 2025 // ============================================================================ 2026 static int clamp(int x, int low, int high) { 2027 return (x < low) ? low : ((x > high) ? high : x); 2028 } 2029 2030 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 2031 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 2032 if (!manager.isEnabled()) { 2033 return; 2034 } 2035 2036 AccessibilityEvent event = 2037 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 2038 event.setPackageName(packageName); 2039 event.setClassName(Notification.class.getName()); 2040 event.setParcelableData(notification); 2041 CharSequence tickerText = notification.tickerText; 2042 if (!TextUtils.isEmpty(tickerText)) { 2043 event.getText().add(tickerText); 2044 } 2045 2046 manager.sendAccessibilityEvent(event); 2047 } 2048 2049 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 2050 // tell the app 2051 if (sendDelete) { 2052 if (r.getNotification().deleteIntent != null) { 2053 try { 2054 r.getNotification().deleteIntent.send(); 2055 } catch (PendingIntent.CanceledException ex) { 2056 // do nothing - there's no relevant way to recover, and 2057 // no reason to let this propagate 2058 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 2059 } 2060 } 2061 } 2062 2063 // status bar 2064 if (r.getNotification().icon != 0) { 2065 r.isCanceled = true; 2066 mListeners.notifyRemovedLocked(r.sbn); 2067 } 2068 2069 // sound 2070 if (mSoundNotification == r) { 2071 mSoundNotification = null; 2072 final long identity = Binder.clearCallingIdentity(); 2073 try { 2074 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2075 if (player != null) { 2076 player.stopAsync(); 2077 } 2078 } catch (RemoteException e) { 2079 } finally { 2080 Binder.restoreCallingIdentity(identity); 2081 } 2082 } 2083 2084 // vibrate 2085 if (mVibrateNotification == r) { 2086 mVibrateNotification = null; 2087 long identity = Binder.clearCallingIdentity(); 2088 try { 2089 mVibrator.cancel(); 2090 } 2091 finally { 2092 Binder.restoreCallingIdentity(identity); 2093 } 2094 } 2095 2096 // light 2097 mLights.remove(r.getKey()); 2098 if (mLedNotification == r) { 2099 mLedNotification = null; 2100 } 2101 2102 // Record usage stats 2103 switch (reason) { 2104 case REASON_DELEGATE_CANCEL: 2105 case REASON_DELEGATE_CANCEL_ALL: 2106 case REASON_LISTENER_CANCEL: 2107 case REASON_LISTENER_CANCEL_ALL: 2108 mUsageStats.registerDismissedByUser(r); 2109 break; 2110 case REASON_NOMAN_CANCEL: 2111 case REASON_NOMAN_CANCEL_ALL: 2112 mUsageStats.registerRemovedByApp(r); 2113 break; 2114 case REASON_DELEGATE_CLICK: 2115 mUsageStats.registerCancelDueToClick(r); 2116 break; 2117 default: 2118 mUsageStats.registerCancelUnknown(r); 2119 break; 2120 } 2121 2122 // Save it for users of getHistoricalNotifications() 2123 mArchive.record(r.sbn); 2124 } 2125 2126 /** 2127 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2128 * and none of the {@code mustNotHaveFlags}. 2129 */ 2130 void cancelNotification(final int callingUid, final int callingPid, 2131 final String pkg, final String tag, final int id, 2132 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2133 final int userId, final int reason, final ManagedServiceInfo listener) { 2134 // In enqueueNotificationInternal notifications are added by scheduling the 2135 // work on the worker handler. Hence, we also schedule the cancel on this 2136 // handler to avoid a scenario where an add notification call followed by a 2137 // remove notification call ends up in not removing the notification. 2138 mHandler.post(new Runnable() { 2139 @Override 2140 public void run() { 2141 String listenerName = listener == null ? null : listener.component.toShortString(); 2142 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2143 mustHaveFlags, mustNotHaveFlags, reason, listenerName); 2144 2145 synchronized (mNotificationList) { 2146 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2147 if (index >= 0) { 2148 NotificationRecord r = mNotificationList.get(index); 2149 2150 // Ideally we'd do this in the caller of this method. However, that would 2151 // require the caller to also find the notification. 2152 if (reason == REASON_DELEGATE_CLICK) { 2153 mUsageStats.registerClickedByUser(r); 2154 } 2155 2156 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2157 return; 2158 } 2159 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2160 return; 2161 } 2162 2163 mNotificationList.remove(index); 2164 mNotificationsByKey.remove(r.sbn.getKey()); 2165 2166 cancelNotificationLocked(r, sendDelete, reason); 2167 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName); 2168 updateLightsLocked(); 2169 } 2170 } 2171 } 2172 }); 2173 } 2174 2175 /** 2176 * Determine whether the userId applies to the notification in question, either because 2177 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2178 */ 2179 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2180 return 2181 // looking for USER_ALL notifications? match everything 2182 userId == UserHandle.USER_ALL 2183 // a notification sent to USER_ALL matches any query 2184 || r.getUserId() == UserHandle.USER_ALL 2185 // an exact user match 2186 || r.getUserId() == userId; 2187 } 2188 2189 /** 2190 * Determine whether the userId applies to the notification in question, either because 2191 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2192 * because it matches one of the users profiles. 2193 */ 2194 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2195 return notificationMatchesUserId(r, userId) 2196 || mUserProfiles.isCurrentProfile(r.getUserId()); 2197 } 2198 2199 /** 2200 * Cancels all notifications from a given package that have all of the 2201 * {@code mustHaveFlags}. 2202 */ 2203 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2204 int mustNotHaveFlags, boolean doit, int userId, int reason, 2205 ManagedServiceInfo listener) { 2206 String listenerName = listener == null ? null : listener.component.toShortString(); 2207 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2208 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2209 listenerName); 2210 2211 synchronized (mNotificationList) { 2212 final int N = mNotificationList.size(); 2213 ArrayList<NotificationRecord> canceledNotifications = null; 2214 for (int i = N-1; i >= 0; --i) { 2215 NotificationRecord r = mNotificationList.get(i); 2216 if (!notificationMatchesUserId(r, userId)) { 2217 continue; 2218 } 2219 // Don't remove notifications to all, if there's no package name specified 2220 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2221 continue; 2222 } 2223 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2224 continue; 2225 } 2226 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2227 continue; 2228 } 2229 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2230 continue; 2231 } 2232 if (canceledNotifications == null) { 2233 canceledNotifications = new ArrayList<>(); 2234 } 2235 canceledNotifications.add(r); 2236 if (!doit) { 2237 return true; 2238 } 2239 mNotificationList.remove(i); 2240 mNotificationsByKey.remove(r.sbn.getKey()); 2241 cancelNotificationLocked(r, false, reason); 2242 } 2243 if (doit && canceledNotifications != null) { 2244 final int M = canceledNotifications.size(); 2245 for (int i = 0; i < M; i++) { 2246 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2247 listenerName); 2248 } 2249 } 2250 if (canceledNotifications != null) { 2251 updateLightsLocked(); 2252 } 2253 return canceledNotifications != null; 2254 } 2255 } 2256 2257 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2258 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2259 String listenerName = listener == null ? null : listener.component.toShortString(); 2260 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2261 null, userId, 0, 0, reason, listenerName); 2262 2263 ArrayList<NotificationRecord> canceledNotifications = null; 2264 final int N = mNotificationList.size(); 2265 for (int i=N-1; i>=0; i--) { 2266 NotificationRecord r = mNotificationList.get(i); 2267 if (includeCurrentProfiles) { 2268 if (!notificationMatchesCurrentProfiles(r, userId)) { 2269 continue; 2270 } 2271 } else { 2272 if (!notificationMatchesUserId(r, userId)) { 2273 continue; 2274 } 2275 } 2276 2277 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2278 | Notification.FLAG_NO_CLEAR)) == 0) { 2279 mNotificationList.remove(i); 2280 mNotificationsByKey.remove(r.sbn.getKey()); 2281 cancelNotificationLocked(r, true, reason); 2282 // Make a note so we can cancel children later. 2283 if (canceledNotifications == null) { 2284 canceledNotifications = new ArrayList<>(); 2285 } 2286 canceledNotifications.add(r); 2287 } 2288 } 2289 int M = canceledNotifications != null ? canceledNotifications.size() : 0; 2290 for (int i = 0; i < M; i++) { 2291 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2292 listenerName); 2293 } 2294 updateLightsLocked(); 2295 } 2296 2297 // Warning: The caller is responsible for invoking updateLightsLocked(). 2298 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid, 2299 String listenerName) { 2300 Notification n = r.getNotification(); 2301 if (n.getGroup() == null || (n.flags & Notification.FLAG_GROUP_SUMMARY) == 0) { 2302 return; 2303 } 2304 2305 String pkg = r.sbn.getPackageName(); 2306 int userId = r.getUserId(); 2307 2308 if (pkg == null) { 2309 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey()); 2310 return; 2311 } 2312 2313 final int N = mNotificationList.size(); 2314 for (int i = N - 1; i >= 0; i--) { 2315 NotificationRecord childR = mNotificationList.get(i); 2316 Notification childN = childR.getNotification(); 2317 StatusBarNotification childSbn = childR.sbn; 2318 if (childR.getUserId() == userId && pkg.equals(childSbn.getPackageName()) && 2319 n.getGroup().equals(childN.getGroup())) { 2320 EventLogTags.writeNotificationCancel(callingUid, callingPid, 2321 pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0, 2322 REASON_GROUP_SUMMARY_CANCELED, listenerName); 2323 mNotificationList.remove(i); 2324 mNotificationsByKey.remove(childR.getKey()); 2325 cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED); 2326 } 2327 } 2328 } 2329 2330 // lock on mNotificationList 2331 void updateLightsLocked() 2332 { 2333 // handle notification lights 2334 if (mLedNotification == null) { 2335 // get next notification, if any 2336 int n = mLights.size(); 2337 if (n > 0) { 2338 mLedNotification = mNotificationsByKey.get(mLights.get(n-1)); 2339 } 2340 } 2341 2342 // Don't flash while we are in a call or screen is on 2343 if (mLedNotification == null || mInCall || mScreenOn) { 2344 mNotificationLight.turnOff(); 2345 } else { 2346 final Notification ledno = mLedNotification.sbn.getNotification(); 2347 int ledARGB = ledno.ledARGB; 2348 int ledOnMS = ledno.ledOnMS; 2349 int ledOffMS = ledno.ledOffMS; 2350 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2351 ledARGB = mDefaultNotificationColor; 2352 ledOnMS = mDefaultNotificationLedOn; 2353 ledOffMS = mDefaultNotificationLedOff; 2354 } 2355 if (mNotificationPulseEnabled) { 2356 // pulse repeatedly 2357 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2358 ledOnMS, ledOffMS); 2359 } 2360 } 2361 } 2362 2363 // lock on mNotificationList 2364 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2365 { 2366 ArrayList<NotificationRecord> list = mNotificationList; 2367 final int len = list.size(); 2368 for (int i=0; i<len; i++) { 2369 NotificationRecord r = list.get(i); 2370 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2371 continue; 2372 } 2373 if (tag == null) { 2374 if (r.sbn.getTag() != null) { 2375 continue; 2376 } 2377 } else { 2378 if (!tag.equals(r.sbn.getTag())) { 2379 continue; 2380 } 2381 } 2382 if (r.sbn.getPackageName().equals(pkg)) { 2383 return i; 2384 } 2385 } 2386 return -1; 2387 } 2388 2389 // lock on mNotificationList 2390 int indexOfNotificationLocked(String key) { 2391 final int N = mNotificationList.size(); 2392 for (int i = 0; i < N; i++) { 2393 if (key.equals(mNotificationList.get(i).getKey())) { 2394 return i; 2395 } 2396 } 2397 return -1; 2398 } 2399 2400 private void updateNotificationPulse() { 2401 synchronized (mNotificationList) { 2402 updateLightsLocked(); 2403 } 2404 } 2405 2406 private static boolean isUidSystem(int uid) { 2407 final int appid = UserHandle.getAppId(uid); 2408 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2409 } 2410 2411 private static boolean isCallerSystem() { 2412 return isUidSystem(Binder.getCallingUid()); 2413 } 2414 2415 private static void checkCallerIsSystem() { 2416 if (isCallerSystem()) { 2417 return; 2418 } 2419 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2420 } 2421 2422 private static void checkCallerIsSystemOrSameApp(String pkg) { 2423 if (isCallerSystem()) { 2424 return; 2425 } 2426 final int uid = Binder.getCallingUid(); 2427 try { 2428 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2429 pkg, 0, UserHandle.getCallingUserId()); 2430 if (ai == null) { 2431 throw new SecurityException("Unknown package " + pkg); 2432 } 2433 if (!UserHandle.isSameApp(ai.uid, uid)) { 2434 throw new SecurityException("Calling uid " + uid + " gave package" 2435 + pkg + " which is owned by uid " + ai.uid); 2436 } 2437 } catch (RemoteException re) { 2438 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2439 } 2440 } 2441 2442 /** 2443 * Generates a NotificationRankingUpdate from 'sbns', considering only 2444 * notifications visible to the given listener. 2445 * 2446 * <p>Caller must hold a lock on mNotificationList.</p> 2447 */ 2448 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { 2449 int speedBumpIndex = -1; 2450 final int N = mNotificationList.size(); 2451 ArrayList<String> keys = new ArrayList<String>(N); 2452 ArrayList<String> interceptedKeys = new ArrayList<String>(N); 2453 for (int i = 0; i < N; i++) { 2454 NotificationRecord record = mNotificationList.get(i); 2455 if (!info.enabledAndUserMatches(record.sbn.getUserId())) { 2456 continue; 2457 } 2458 keys.add(record.sbn.getKey()); 2459 if (record.isIntercepted()) { 2460 interceptedKeys.add(record.sbn.getKey()); 2461 } 2462 if (speedBumpIndex == -1 && 2463 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) { 2464 speedBumpIndex = keys.size() - 1; 2465 } 2466 } 2467 String[] keysAr = keys.toArray(new String[keys.size()]); 2468 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]); 2469 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, speedBumpIndex); 2470 } 2471 2472 public class NotificationListeners extends ManagedServices { 2473 2474 public NotificationListeners() { 2475 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2476 } 2477 2478 @Override 2479 protected Config getConfig() { 2480 Config c = new Config(); 2481 c.caption = "notification listener"; 2482 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2483 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2484 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2485 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2486 c.clientLabel = R.string.notification_listener_binding_label; 2487 return c; 2488 } 2489 2490 @Override 2491 protected IInterface asInterface(IBinder binder) { 2492 return INotificationListener.Stub.asInterface(binder); 2493 } 2494 2495 @Override 2496 public void onServiceAdded(ManagedServiceInfo info) { 2497 final INotificationListener listener = (INotificationListener) info.service; 2498 final NotificationRankingUpdate update; 2499 synchronized (mNotificationList) { 2500 update = makeRankingUpdateLocked(info); 2501 } 2502 try { 2503 listener.onListenerConnected(update); 2504 } catch (RemoteException e) { 2505 // we tried 2506 } 2507 } 2508 2509 @Override 2510 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 2511 if (mListenersDisablingAlerts.remove(removed)) { 2512 updateListenerFlagsLocked(); 2513 } 2514 } 2515 2516 /** 2517 * asynchronously notify all listeners about a new notification 2518 */ 2519 public void notifyPostedLocked(StatusBarNotification sbn) { 2520 // make a copy in case changes are made to the underlying Notification object 2521 final StatusBarNotification sbnClone = sbn.clone(); 2522 for (final ManagedServiceInfo info : mServices) { 2523 if (!info.isEnabledForCurrentProfiles()) { 2524 continue; 2525 } 2526 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2527 if (update.getOrderedKeys().length == 0) { 2528 continue; 2529 } 2530 mHandler.post(new Runnable() { 2531 @Override 2532 public void run() { 2533 notifyPostedIfUserMatch(info, sbnClone, update); 2534 } 2535 }); 2536 } 2537 } 2538 2539 /** 2540 * asynchronously notify all listeners about a removed notification 2541 */ 2542 public void notifyRemovedLocked(StatusBarNotification sbn) { 2543 // make a copy in case changes are made to the underlying Notification object 2544 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 2545 // notification 2546 final StatusBarNotification sbnLight = sbn.cloneLight(); 2547 for (final ManagedServiceInfo info : mServices) { 2548 if (!info.isEnabledForCurrentProfiles()) { 2549 continue; 2550 } 2551 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2552 mHandler.post(new Runnable() { 2553 @Override 2554 public void run() { 2555 notifyRemovedIfUserMatch(info, sbnLight, update); 2556 } 2557 }); 2558 } 2559 } 2560 2561 /** 2562 * asynchronously notify all listeners about a reordering of notifications 2563 */ 2564 public void notifyRankingUpdateLocked() { 2565 for (final ManagedServiceInfo serviceInfo : mServices) { 2566 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2567 continue; 2568 } 2569 final NotificationRankingUpdate update = 2570 makeRankingUpdateLocked(serviceInfo); 2571 mHandler.post(new Runnable() { 2572 @Override 2573 public void run() { 2574 notifyRankingUpdate(serviceInfo, update); 2575 } 2576 }); 2577 } 2578 } 2579 2580 public void notifyListenerFlagsChangedLocked(final int flags) { 2581 for (final ManagedServiceInfo serviceInfo : mServices) { 2582 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2583 continue; 2584 } 2585 mHandler.post(new Runnable() { 2586 @Override 2587 public void run() { 2588 notifyListenerFlagsChanged(serviceInfo, flags); 2589 } 2590 }); 2591 } 2592 } 2593 2594 private void notifyPostedIfUserMatch(final ManagedServiceInfo info, 2595 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 2596 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2597 return; 2598 } 2599 final INotificationListener listener = (INotificationListener)info.service; 2600 try { 2601 listener.onNotificationPosted(sbn, rankingUpdate); 2602 } catch (RemoteException ex) { 2603 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 2604 } 2605 } 2606 2607 private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn, 2608 NotificationRankingUpdate rankingUpdate) { 2609 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2610 return; 2611 } 2612 final INotificationListener listener = (INotificationListener) info.service; 2613 try { 2614 listener.onNotificationRemoved(sbn, rankingUpdate); 2615 } catch (RemoteException ex) { 2616 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 2617 } 2618 } 2619 2620 private void notifyRankingUpdate(ManagedServiceInfo info, 2621 NotificationRankingUpdate rankingUpdate) { 2622 final INotificationListener listener = (INotificationListener) info.service; 2623 try { 2624 listener.onNotificationRankingUpdate(rankingUpdate); 2625 } catch (RemoteException ex) { 2626 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 2627 } 2628 } 2629 2630 private void notifyListenerFlagsChanged(ManagedServiceInfo info, int state) { 2631 final INotificationListener listener = (INotificationListener) info.service; 2632 try { 2633 listener.onListenerFlagsChanged(state); 2634 } catch (RemoteException ex) { 2635 Log.e(TAG, "unable to notify listener (listener flags): " + listener, ex); 2636 } 2637 } 2638 } 2639 2640 public static final class DumpFilter { 2641 public String pkgFilter; 2642 public boolean zen; 2643 2644 public static DumpFilter parseFromArguments(String[] args) { 2645 if (args != null && args.length == 2 && "p".equals(args[0]) 2646 && args[1] != null && !args[1].trim().isEmpty()) { 2647 final DumpFilter filter = new DumpFilter(); 2648 filter.pkgFilter = args[1].trim().toLowerCase(); 2649 return filter; 2650 } 2651 if (args != null && args.length == 1 && "zen".equals(args[0])) { 2652 final DumpFilter filter = new DumpFilter(); 2653 filter.zen = true; 2654 return filter; 2655 } 2656 return null; 2657 } 2658 2659 public boolean matches(StatusBarNotification sbn) { 2660 return zen ? true : sbn != null 2661 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg())); 2662 } 2663 2664 public boolean matches(ComponentName component) { 2665 return zen ? true : component != null && matches(component.getPackageName()); 2666 } 2667 2668 public boolean matches(String pkg) { 2669 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter); 2670 } 2671 2672 @Override 2673 public String toString() { 2674 return zen ? "zen" : ('\'' + pkgFilter + '\''); 2675 } 2676 } 2677} 2678