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