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