NotificationManagerService.java revision 41da5ff98f68a575fc46085e8714c8a3e5e5c17a
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 try { 1646 mRankingHelper.sort(mNotificationList); 1647 } catch (RuntimeException ex) { 1648 // Don't crash the system server if something bad happened. 1649 Log.e(TAG, "Extreme badness during notification sort", ex); 1650 Log.e(TAG, "Current notification list: "); 1651 for (int ii=0; ii < mNotificationList.size(); ii++) { 1652 NotificationRecord nr = mNotificationList.get(ii); 1653 Log.e(TAG, String.format( 1654 " [%d] %s (group %s, rank %d, sortkey %s, proxy %s)", 1655 ii, nr, nr.getGroupKey(), nr.getAuthoritativeRank(), 1656 nr.getNotification().getSortKey(), 1657 nr.getRankingProxy())); 1658 } 1659 // STOPSHIP: remove once b/16626175 is found 1660 throw ex; 1661 } 1662 1663 if (notification.icon != 0) { 1664 StatusBarNotification oldSbn = (old != null) ? old.sbn : null; 1665 mListeners.notifyPostedLocked(n, oldSbn); 1666 } else { 1667 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1668 if (old != null && !old.isCanceled) { 1669 mListeners.notifyRemovedLocked(n); 1670 } 1671 // ATTENTION: in a future release we will bail out here 1672 // so that we do not play sounds, show lights, etc. for invalid 1673 // notifications 1674 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1675 + n.getPackageName()); 1676 } 1677 1678 buzzBeepBlinkLocked(r); 1679 } 1680 } 1681 }); 1682 1683 idOut[0] = id; 1684 } 1685 1686 private void buzzBeepBlinkLocked(NotificationRecord record) { 1687 boolean buzzBeepBlinked = false; 1688 final Notification notification = record.sbn.getNotification(); 1689 1690 // Should this notification make noise, vibe, or use the LED? 1691 final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) && 1692 !record.isIntercepted(); 1693 if (DBG || record.isIntercepted()) 1694 Slog.v(TAG, 1695 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt + 1696 " intercept=" + record.isIntercepted() 1697 ); 1698 1699 final int currentUser; 1700 final long token = Binder.clearCallingIdentity(); 1701 try { 1702 currentUser = ActivityManager.getCurrentUser(); 1703 } finally { 1704 Binder.restoreCallingIdentity(token); 1705 } 1706 1707 // If we're not supposed to beep, vibrate, etc. then don't. 1708 if (!disableNotificationAlerts() 1709 && (!(record.isUpdate 1710 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1711 && (record.getUserId() == UserHandle.USER_ALL || 1712 record.getUserId() == currentUser || 1713 mUserProfiles.isCurrentProfile(record.getUserId())) 1714 && canInterrupt 1715 && mSystemReady 1716 && mAudioManager != null) { 1717 if (DBG) Slog.v(TAG, "Interrupting!"); 1718 1719 sendAccessibilityEvent(notification, record.sbn.getPackageName()); 1720 1721 // sound 1722 1723 // should we use the default notification sound? (indicated either by 1724 // DEFAULT_SOUND or because notification.sound is pointing at 1725 // Settings.System.NOTIFICATION_SOUND) 1726 final boolean useDefaultSound = 1727 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1728 Settings.System.DEFAULT_NOTIFICATION_URI 1729 .equals(notification.sound); 1730 1731 Uri soundUri = null; 1732 boolean hasValidSound = false; 1733 1734 if (useDefaultSound) { 1735 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1736 1737 // check to see if the default notification sound is silent 1738 ContentResolver resolver = getContext().getContentResolver(); 1739 hasValidSound = Settings.System.getString(resolver, 1740 Settings.System.NOTIFICATION_SOUND) != null; 1741 } else if (notification.sound != null) { 1742 soundUri = notification.sound; 1743 hasValidSound = (soundUri != null); 1744 } 1745 1746 if (hasValidSound) { 1747 boolean looping = 1748 (notification.flags & Notification.FLAG_INSISTENT) != 0; 1749 int audioStreamType; 1750 if (notification.audioStreamType >= 0) { 1751 audioStreamType = notification.audioStreamType; 1752 } else { 1753 audioStreamType = DEFAULT_STREAM_TYPE; 1754 } 1755 mSoundNotification = record; 1756 // do not play notifications if stream volume is 0 (typically because 1757 // ringer mode is silent) or if there is a user of exclusive audio focus 1758 if ((mAudioManager.getStreamVolume(audioStreamType) != 0) 1759 && !mAudioManager.isAudioFocusExclusive()) { 1760 final long identity = Binder.clearCallingIdentity(); 1761 try { 1762 final IRingtonePlayer player = 1763 mAudioManager.getRingtonePlayer(); 1764 if (player != null) { 1765 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 1766 + " on stream " + audioStreamType); 1767 player.playAsync(soundUri, record.sbn.getUser(), looping, 1768 audioStreamType); 1769 buzzBeepBlinked = true; 1770 } 1771 } catch (RemoteException e) { 1772 } finally { 1773 Binder.restoreCallingIdentity(identity); 1774 } 1775 } 1776 } 1777 1778 // vibrate 1779 // Does the notification want to specify its own vibration? 1780 final boolean hasCustomVibrate = notification.vibrate != null; 1781 1782 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1783 // mode, and no other vibration is specified, we fall back to vibration 1784 final boolean convertSoundToVibration = 1785 !hasCustomVibrate 1786 && hasValidSound 1787 && (mAudioManager.getRingerMode() 1788 == AudioManager.RINGER_MODE_VIBRATE); 1789 1790 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1791 final boolean useDefaultVibrate = 1792 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1793 1794 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1795 && !(mAudioManager.getRingerMode() 1796 == AudioManager.RINGER_MODE_SILENT)) { 1797 mVibrateNotification = record; 1798 1799 if (useDefaultVibrate || convertSoundToVibration) { 1800 // Escalate privileges so we can use the vibrator even if the 1801 // notifying app does not have the VIBRATE permission. 1802 long identity = Binder.clearCallingIdentity(); 1803 try { 1804 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 1805 useDefaultVibrate ? mDefaultVibrationPattern 1806 : mFallbackVibrationPattern, 1807 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1808 ? 0: -1, audioAttributesForNotification(notification)); 1809 buzzBeepBlinked = true; 1810 } finally { 1811 Binder.restoreCallingIdentity(identity); 1812 } 1813 } else if (notification.vibrate.length > 1) { 1814 // If you want your own vibration pattern, you need the VIBRATE 1815 // permission 1816 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 1817 notification.vibrate, 1818 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1819 ? 0: -1, audioAttributesForNotification(notification)); 1820 buzzBeepBlinked = true; 1821 } 1822 } 1823 } 1824 1825 // light 1826 // release the light 1827 boolean wasShowLights = mLights.remove(record.getKey()); 1828 if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) { 1829 mLedNotification = null; 1830 } 1831 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) { 1832 mLights.add(record.getKey()); 1833 updateLightsLocked(); 1834 if (mUseAttentionLight) { 1835 mAttentionLight.pulse(); 1836 } 1837 buzzBeepBlinked = true; 1838 } else if (wasShowLights) { 1839 updateLightsLocked(); 1840 } 1841 if (buzzBeepBlinked) { 1842 mHandler.post(mBuzzBeepBlinked); 1843 } 1844 } 1845 1846 private static AudioAttributes audioAttributesForNotification(Notification n) { 1847 if (n.audioAttributes != null 1848 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) { 1849 return n.audioAttributes; 1850 } 1851 return new AudioAttributes.Builder() 1852 .setLegacyStreamType(n.audioStreamType) 1853 .setUsage(AudioAttributes.usageForLegacyStreamType(n.audioStreamType)) 1854 .build(); 1855 } 1856 1857 void showNextToastLocked() { 1858 ToastRecord record = mToastQueue.get(0); 1859 while (record != null) { 1860 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1861 try { 1862 record.callback.show(); 1863 scheduleTimeoutLocked(record); 1864 return; 1865 } catch (RemoteException e) { 1866 Slog.w(TAG, "Object died trying to show notification " + record.callback 1867 + " in package " + record.pkg); 1868 // remove it from the list and let the process die 1869 int index = mToastQueue.indexOf(record); 1870 if (index >= 0) { 1871 mToastQueue.remove(index); 1872 } 1873 keepProcessAliveLocked(record.pid); 1874 if (mToastQueue.size() > 0) { 1875 record = mToastQueue.get(0); 1876 } else { 1877 record = null; 1878 } 1879 } 1880 } 1881 } 1882 1883 void cancelToastLocked(int index) { 1884 ToastRecord record = mToastQueue.get(index); 1885 try { 1886 record.callback.hide(); 1887 } catch (RemoteException e) { 1888 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1889 + " in package " + record.pkg); 1890 // don't worry about this, we're about to remove it from 1891 // the list anyway 1892 } 1893 mToastQueue.remove(index); 1894 keepProcessAliveLocked(record.pid); 1895 if (mToastQueue.size() > 0) { 1896 // Show the next one. If the callback fails, this will remove 1897 // it from the list, so don't assume that the list hasn't changed 1898 // after this point. 1899 showNextToastLocked(); 1900 } 1901 } 1902 1903 private void scheduleTimeoutLocked(ToastRecord r) 1904 { 1905 mHandler.removeCallbacksAndMessages(r); 1906 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1907 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 1908 mHandler.sendMessageDelayed(m, delay); 1909 } 1910 1911 private void handleTimeout(ToastRecord record) 1912 { 1913 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1914 synchronized (mToastQueue) { 1915 int index = indexOfToastLocked(record.pkg, record.callback); 1916 if (index >= 0) { 1917 cancelToastLocked(index); 1918 } 1919 } 1920 } 1921 1922 // lock on mToastQueue 1923 int indexOfToastLocked(String pkg, ITransientNotification callback) 1924 { 1925 IBinder cbak = callback.asBinder(); 1926 ArrayList<ToastRecord> list = mToastQueue; 1927 int len = list.size(); 1928 for (int i=0; i<len; i++) { 1929 ToastRecord r = list.get(i); 1930 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1931 return i; 1932 } 1933 } 1934 return -1; 1935 } 1936 1937 // lock on mToastQueue 1938 void keepProcessAliveLocked(int pid) 1939 { 1940 int toastCount = 0; // toasts from this pid 1941 ArrayList<ToastRecord> list = mToastQueue; 1942 int N = list.size(); 1943 for (int i=0; i<N; i++) { 1944 ToastRecord r = list.get(i); 1945 if (r.pid == pid) { 1946 toastCount++; 1947 } 1948 } 1949 try { 1950 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 1951 } catch (RemoteException e) { 1952 // Shouldn't happen. 1953 } 1954 } 1955 1956 private void handleRankingReconsideration(Message message) { 1957 if (!(message.obj instanceof RankingReconsideration)) return; 1958 RankingReconsideration recon = (RankingReconsideration) message.obj; 1959 recon.run(); 1960 boolean changed; 1961 synchronized (mNotificationList) { 1962 final NotificationRecord record = mNotificationsByKey.get(recon.getKey()); 1963 if (record == null) { 1964 return; 1965 } 1966 int indexBefore = findNotificationRecordIndexLocked(record); 1967 boolean interceptBefore = record.isIntercepted(); 1968 recon.applyChangesLocked(record); 1969 applyZenModeLocked(record); 1970 mRankingHelper.sort(mNotificationList); 1971 int indexAfter = findNotificationRecordIndexLocked(record); 1972 boolean interceptAfter = record.isIntercepted(); 1973 changed = indexBefore != indexAfter || interceptBefore != interceptAfter; 1974 if (interceptBefore && !interceptAfter) { 1975 buzzBeepBlinkLocked(record); 1976 } 1977 } 1978 if (changed) { 1979 scheduleSendRankingUpdate(); 1980 } 1981 } 1982 1983 private void handleRankingConfigChange() { 1984 synchronized (mNotificationList) { 1985 final int N = mNotificationList.size(); 1986 ArrayList<String> orderBefore = new ArrayList<String>(N); 1987 for (int i = 0; i < N; i++) { 1988 final NotificationRecord r = mNotificationList.get(i); 1989 orderBefore.add(r.getKey()); 1990 mRankingHelper.extractSignals(r); 1991 } 1992 mRankingHelper.sort(mNotificationList); 1993 for (int i = 0; i < N; i++) { 1994 if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) { 1995 scheduleSendRankingUpdate(); 1996 return; 1997 } 1998 } 1999 } 2000 } 2001 2002 // let zen mode evaluate this record 2003 private void applyZenModeLocked(NotificationRecord record) { 2004 record.setIntercepted(mZenModeHelper.shouldIntercept(record)); 2005 } 2006 2007 // lock on mNotificationList 2008 private int findNotificationRecordIndexLocked(NotificationRecord target) { 2009 return mRankingHelper.indexOf(mNotificationList, target); 2010 } 2011 2012 private void scheduleSendRankingUpdate() { 2013 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 2014 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 2015 mHandler.sendMessage(m); 2016 } 2017 2018 private void handleSendRankingUpdate() { 2019 synchronized (mNotificationList) { 2020 mListeners.notifyRankingUpdateLocked(); 2021 } 2022 } 2023 2024 private void scheduleListenerFlagsChanged(int state) { 2025 mHandler.removeMessages(MESSAGE_LISTENER_FLAGS_CHANGED); 2026 mHandler.obtainMessage(MESSAGE_LISTENER_FLAGS_CHANGED, state, 0).sendToTarget(); 2027 } 2028 2029 private void handleListenerFlagsChanged(int state) { 2030 synchronized (mNotificationList) { 2031 mListeners.notifyListenerFlagsChangedLocked(state); 2032 } 2033 } 2034 2035 private final class WorkerHandler extends Handler 2036 { 2037 @Override 2038 public void handleMessage(Message msg) 2039 { 2040 switch (msg.what) 2041 { 2042 case MESSAGE_TIMEOUT: 2043 handleTimeout((ToastRecord)msg.obj); 2044 break; 2045 case MESSAGE_SAVE_POLICY_FILE: 2046 handleSavePolicyFile(); 2047 break; 2048 case MESSAGE_SEND_RANKING_UPDATE: 2049 handleSendRankingUpdate(); 2050 break; 2051 case MESSAGE_LISTENER_FLAGS_CHANGED: 2052 handleListenerFlagsChanged(msg.arg1); 2053 break; 2054 } 2055 } 2056 2057 } 2058 2059 private final class RankingWorkerHandler extends Handler 2060 { 2061 public RankingWorkerHandler(Looper looper) { 2062 super(looper); 2063 } 2064 2065 @Override 2066 public void handleMessage(Message msg) { 2067 switch (msg.what) { 2068 case MESSAGE_RECONSIDER_RANKING: 2069 handleRankingReconsideration(msg); 2070 break; 2071 case MESSAGE_RANKING_CONFIG_CHANGE: 2072 handleRankingConfigChange(); 2073 break; 2074 } 2075 } 2076 } 2077 2078 // Notifications 2079 // ============================================================================ 2080 static int clamp(int x, int low, int high) { 2081 return (x < low) ? low : ((x > high) ? high : x); 2082 } 2083 2084 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 2085 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 2086 if (!manager.isEnabled()) { 2087 return; 2088 } 2089 2090 AccessibilityEvent event = 2091 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 2092 event.setPackageName(packageName); 2093 event.setClassName(Notification.class.getName()); 2094 event.setParcelableData(notification); 2095 CharSequence tickerText = notification.tickerText; 2096 if (!TextUtils.isEmpty(tickerText)) { 2097 event.getText().add(tickerText); 2098 } 2099 2100 manager.sendAccessibilityEvent(event); 2101 } 2102 2103 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 2104 // tell the app 2105 if (sendDelete) { 2106 if (r.getNotification().deleteIntent != null) { 2107 try { 2108 r.getNotification().deleteIntent.send(); 2109 } catch (PendingIntent.CanceledException ex) { 2110 // do nothing - there's no relevant way to recover, and 2111 // no reason to let this propagate 2112 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 2113 } 2114 } 2115 } 2116 2117 // status bar 2118 if (r.getNotification().icon != 0) { 2119 r.isCanceled = true; 2120 mListeners.notifyRemovedLocked(r.sbn); 2121 } 2122 2123 // sound 2124 if (mSoundNotification == r) { 2125 mSoundNotification = null; 2126 final long identity = Binder.clearCallingIdentity(); 2127 try { 2128 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2129 if (player != null) { 2130 player.stopAsync(); 2131 } 2132 } catch (RemoteException e) { 2133 } finally { 2134 Binder.restoreCallingIdentity(identity); 2135 } 2136 } 2137 2138 // vibrate 2139 if (mVibrateNotification == r) { 2140 mVibrateNotification = null; 2141 long identity = Binder.clearCallingIdentity(); 2142 try { 2143 mVibrator.cancel(); 2144 } 2145 finally { 2146 Binder.restoreCallingIdentity(identity); 2147 } 2148 } 2149 2150 // light 2151 mLights.remove(r.getKey()); 2152 if (mLedNotification == r) { 2153 mLedNotification = null; 2154 } 2155 2156 // Record usage stats 2157 switch (reason) { 2158 case REASON_DELEGATE_CANCEL: 2159 case REASON_DELEGATE_CANCEL_ALL: 2160 case REASON_LISTENER_CANCEL: 2161 case REASON_LISTENER_CANCEL_ALL: 2162 mUsageStats.registerDismissedByUser(r); 2163 break; 2164 case REASON_NOMAN_CANCEL: 2165 case REASON_NOMAN_CANCEL_ALL: 2166 mUsageStats.registerRemovedByApp(r); 2167 break; 2168 case REASON_DELEGATE_CLICK: 2169 mUsageStats.registerCancelDueToClick(r); 2170 break; 2171 default: 2172 mUsageStats.registerCancelUnknown(r); 2173 break; 2174 } 2175 2176 mNotificationsByKey.remove(r.sbn.getKey()); 2177 2178 // Save it for users of getHistoricalNotifications() 2179 mArchive.record(r.sbn); 2180 } 2181 2182 /** 2183 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2184 * and none of the {@code mustNotHaveFlags}. 2185 */ 2186 void cancelNotification(final int callingUid, final int callingPid, 2187 final String pkg, final String tag, final int id, 2188 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2189 final int userId, final int reason, final ManagedServiceInfo listener) { 2190 // In enqueueNotificationInternal notifications are added by scheduling the 2191 // work on the worker handler. Hence, we also schedule the cancel on this 2192 // handler to avoid a scenario where an add notification call followed by a 2193 // remove notification call ends up in not removing the notification. 2194 mHandler.post(new Runnable() { 2195 @Override 2196 public void run() { 2197 String listenerName = listener == null ? null : listener.component.toShortString(); 2198 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2199 mustHaveFlags, mustNotHaveFlags, reason, listenerName); 2200 2201 synchronized (mNotificationList) { 2202 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2203 if (index >= 0) { 2204 NotificationRecord r = mNotificationList.get(index); 2205 2206 // Ideally we'd do this in the caller of this method. However, that would 2207 // require the caller to also find the notification. 2208 if (reason == REASON_DELEGATE_CLICK) { 2209 mUsageStats.registerClickedByUser(r); 2210 } 2211 2212 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2213 return; 2214 } 2215 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2216 return; 2217 } 2218 2219 mNotificationList.remove(index); 2220 2221 cancelNotificationLocked(r, sendDelete, reason); 2222 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName); 2223 updateLightsLocked(); 2224 } 2225 } 2226 } 2227 }); 2228 } 2229 2230 /** 2231 * Determine whether the userId applies to the notification in question, either because 2232 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2233 */ 2234 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2235 return 2236 // looking for USER_ALL notifications? match everything 2237 userId == UserHandle.USER_ALL 2238 // a notification sent to USER_ALL matches any query 2239 || r.getUserId() == UserHandle.USER_ALL 2240 // an exact user match 2241 || r.getUserId() == userId; 2242 } 2243 2244 /** 2245 * Determine whether the userId applies to the notification in question, either because 2246 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2247 * because it matches one of the users profiles. 2248 */ 2249 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2250 return notificationMatchesUserId(r, userId) 2251 || mUserProfiles.isCurrentProfile(r.getUserId()); 2252 } 2253 2254 /** 2255 * Cancels all notifications from a given package that have all of the 2256 * {@code mustHaveFlags}. 2257 */ 2258 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2259 int mustNotHaveFlags, boolean doit, int userId, int reason, 2260 ManagedServiceInfo listener) { 2261 String listenerName = listener == null ? null : listener.component.toShortString(); 2262 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2263 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2264 listenerName); 2265 2266 synchronized (mNotificationList) { 2267 final int N = mNotificationList.size(); 2268 ArrayList<NotificationRecord> canceledNotifications = null; 2269 for (int i = N-1; i >= 0; --i) { 2270 NotificationRecord r = mNotificationList.get(i); 2271 if (!notificationMatchesUserId(r, userId)) { 2272 continue; 2273 } 2274 // Don't remove notifications to all, if there's no package name specified 2275 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2276 continue; 2277 } 2278 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2279 continue; 2280 } 2281 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2282 continue; 2283 } 2284 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2285 continue; 2286 } 2287 if (canceledNotifications == null) { 2288 canceledNotifications = new ArrayList<>(); 2289 } 2290 canceledNotifications.add(r); 2291 if (!doit) { 2292 return true; 2293 } 2294 mNotificationList.remove(i); 2295 cancelNotificationLocked(r, false, reason); 2296 } 2297 if (doit && canceledNotifications != null) { 2298 final int M = canceledNotifications.size(); 2299 for (int i = 0; i < M; i++) { 2300 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2301 listenerName); 2302 } 2303 } 2304 if (canceledNotifications != null) { 2305 updateLightsLocked(); 2306 } 2307 return canceledNotifications != null; 2308 } 2309 } 2310 2311 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2312 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2313 String listenerName = listener == null ? null : listener.component.toShortString(); 2314 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2315 null, userId, 0, 0, reason, listenerName); 2316 2317 ArrayList<NotificationRecord> canceledNotifications = null; 2318 final int N = mNotificationList.size(); 2319 for (int i=N-1; i>=0; i--) { 2320 NotificationRecord r = mNotificationList.get(i); 2321 if (includeCurrentProfiles) { 2322 if (!notificationMatchesCurrentProfiles(r, userId)) { 2323 continue; 2324 } 2325 } else { 2326 if (!notificationMatchesUserId(r, userId)) { 2327 continue; 2328 } 2329 } 2330 2331 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2332 | Notification.FLAG_NO_CLEAR)) == 0) { 2333 mNotificationList.remove(i); 2334 cancelNotificationLocked(r, true, reason); 2335 // Make a note so we can cancel children later. 2336 if (canceledNotifications == null) { 2337 canceledNotifications = new ArrayList<>(); 2338 } 2339 canceledNotifications.add(r); 2340 } 2341 } 2342 int M = canceledNotifications != null ? canceledNotifications.size() : 0; 2343 for (int i = 0; i < M; i++) { 2344 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2345 listenerName); 2346 } 2347 updateLightsLocked(); 2348 } 2349 2350 // Warning: The caller is responsible for invoking updateLightsLocked(). 2351 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid, 2352 String listenerName) { 2353 Notification n = r.getNotification(); 2354 if (!n.isGroupSummary()) { 2355 return; 2356 } 2357 2358 String pkg = r.sbn.getPackageName(); 2359 int userId = r.getUserId(); 2360 2361 if (pkg == null) { 2362 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey()); 2363 return; 2364 } 2365 2366 final int N = mNotificationList.size(); 2367 for (int i = N - 1; i >= 0; i--) { 2368 NotificationRecord childR = mNotificationList.get(i); 2369 Notification childN = childR.getNotification(); 2370 StatusBarNotification childSbn = childR.sbn; 2371 if (childR.getUserId() == userId && pkg.equals(childSbn.getPackageName()) && 2372 n.getGroup().equals(childN.getGroup())) { 2373 EventLogTags.writeNotificationCancel(callingUid, callingPid, 2374 pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0, 2375 REASON_GROUP_SUMMARY_CANCELED, listenerName); 2376 mNotificationList.remove(i); 2377 cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED); 2378 } 2379 } 2380 } 2381 2382 // lock on mNotificationList 2383 void updateLightsLocked() 2384 { 2385 // handle notification lights 2386 if (mLedNotification == null) { 2387 // get next notification, if any 2388 int n = mLights.size(); 2389 if (n > 0) { 2390 mLedNotification = mNotificationsByKey.get(mLights.get(n-1)); 2391 } 2392 } 2393 2394 // Don't flash while we are in a call or screen is on 2395 if (mLedNotification == null || mInCall || mScreenOn) { 2396 mNotificationLight.turnOff(); 2397 mStatusBar.notificationLightOff(); 2398 } else { 2399 final Notification ledno = mLedNotification.sbn.getNotification(); 2400 int ledARGB = ledno.ledARGB; 2401 int ledOnMS = ledno.ledOnMS; 2402 int ledOffMS = ledno.ledOffMS; 2403 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2404 ledARGB = mDefaultNotificationColor; 2405 ledOnMS = mDefaultNotificationLedOn; 2406 ledOffMS = mDefaultNotificationLedOff; 2407 } 2408 if (mNotificationPulseEnabled) { 2409 // pulse repeatedly 2410 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2411 ledOnMS, ledOffMS); 2412 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS); 2413 } 2414 } 2415 } 2416 2417 // lock on mNotificationList 2418 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2419 { 2420 ArrayList<NotificationRecord> list = mNotificationList; 2421 final int len = list.size(); 2422 for (int i=0; i<len; i++) { 2423 NotificationRecord r = list.get(i); 2424 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2425 continue; 2426 } 2427 if (tag == null) { 2428 if (r.sbn.getTag() != null) { 2429 continue; 2430 } 2431 } else { 2432 if (!tag.equals(r.sbn.getTag())) { 2433 continue; 2434 } 2435 } 2436 if (r.sbn.getPackageName().equals(pkg)) { 2437 return i; 2438 } 2439 } 2440 return -1; 2441 } 2442 2443 // lock on mNotificationList 2444 int indexOfNotificationLocked(String key) { 2445 final int N = mNotificationList.size(); 2446 for (int i = 0; i < N; i++) { 2447 if (key.equals(mNotificationList.get(i).getKey())) { 2448 return i; 2449 } 2450 } 2451 return -1; 2452 } 2453 2454 private void updateNotificationPulse() { 2455 synchronized (mNotificationList) { 2456 updateLightsLocked(); 2457 } 2458 } 2459 2460 private static boolean isUidSystem(int uid) { 2461 final int appid = UserHandle.getAppId(uid); 2462 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2463 } 2464 2465 private static boolean isCallerSystem() { 2466 return isUidSystem(Binder.getCallingUid()); 2467 } 2468 2469 private static void checkCallerIsSystem() { 2470 if (isCallerSystem()) { 2471 return; 2472 } 2473 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2474 } 2475 2476 private static void checkCallerIsSystemOrSameApp(String pkg) { 2477 if (isCallerSystem()) { 2478 return; 2479 } 2480 final int uid = Binder.getCallingUid(); 2481 try { 2482 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2483 pkg, 0, UserHandle.getCallingUserId()); 2484 if (ai == null) { 2485 throw new SecurityException("Unknown package " + pkg); 2486 } 2487 if (!UserHandle.isSameApp(ai.uid, uid)) { 2488 throw new SecurityException("Calling uid " + uid + " gave package" 2489 + pkg + " which is owned by uid " + ai.uid); 2490 } 2491 } catch (RemoteException re) { 2492 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2493 } 2494 } 2495 2496 /** 2497 * Generates a NotificationRankingUpdate from 'sbns', considering only 2498 * notifications visible to the given listener. 2499 * 2500 * <p>Caller must hold a lock on mNotificationList.</p> 2501 */ 2502 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { 2503 int speedBumpIndex = -1; 2504 final int N = mNotificationList.size(); 2505 ArrayList<String> keys = new ArrayList<String>(N); 2506 ArrayList<String> interceptedKeys = new ArrayList<String>(N); 2507 for (int i = 0; i < N; i++) { 2508 NotificationRecord record = mNotificationList.get(i); 2509 if (!isVisibleToListener(record.sbn, info)) { 2510 continue; 2511 } 2512 keys.add(record.sbn.getKey()); 2513 if (record.isIntercepted()) { 2514 interceptedKeys.add(record.sbn.getKey()); 2515 } 2516 if (speedBumpIndex == -1 && 2517 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) { 2518 speedBumpIndex = keys.size() - 1; 2519 } 2520 } 2521 String[] keysAr = keys.toArray(new String[keys.size()]); 2522 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]); 2523 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, speedBumpIndex); 2524 } 2525 2526 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { 2527 if (!listener.enabledAndUserMatches(sbn.getUserId())) { 2528 return false; 2529 } 2530 // TODO: remove this for older listeners. 2531 return true; 2532 } 2533 2534 public class NotificationListeners extends ManagedServices { 2535 2536 public NotificationListeners() { 2537 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2538 } 2539 2540 @Override 2541 protected Config getConfig() { 2542 Config c = new Config(); 2543 c.caption = "notification listener"; 2544 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2545 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2546 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2547 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2548 c.clientLabel = R.string.notification_listener_binding_label; 2549 return c; 2550 } 2551 2552 @Override 2553 protected IInterface asInterface(IBinder binder) { 2554 return INotificationListener.Stub.asInterface(binder); 2555 } 2556 2557 @Override 2558 public void onServiceAdded(ManagedServiceInfo info) { 2559 final INotificationListener listener = (INotificationListener) info.service; 2560 final NotificationRankingUpdate update; 2561 synchronized (mNotificationList) { 2562 update = makeRankingUpdateLocked(info); 2563 } 2564 try { 2565 listener.onListenerConnected(update); 2566 } catch (RemoteException e) { 2567 // we tried 2568 } 2569 } 2570 2571 @Override 2572 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 2573 if (mListenersDisablingAlerts.remove(removed)) { 2574 updateListenerFlagsLocked(); 2575 } 2576 } 2577 2578 /** 2579 * asynchronously notify all listeners about a new notification 2580 * 2581 * <p> 2582 * Also takes care of removing a notification that has been visible to a listener before, 2583 * but isn't anymore. 2584 */ 2585 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) { 2586 // make a copy in case changes are made to the underlying Notification object 2587 final StatusBarNotification sbnClone = sbn.clone(); 2588 for (final ManagedServiceInfo info : mServices) { 2589 boolean sbnVisible = isVisibleToListener(sbn, info); 2590 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false; 2591 // This notification hasn't been and still isn't visible -> ignore. 2592 if (!oldSbnVisible && !sbnVisible) { 2593 continue; 2594 } 2595 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2596 2597 // This notification became invisible -> remove the old one. 2598 if (oldSbnVisible && !sbnVisible) { 2599 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight(); 2600 mHandler.post(new Runnable() { 2601 @Override 2602 public void run() { 2603 notifyRemoved(info, oldSbnLightClone, update); 2604 } 2605 }); 2606 continue; 2607 } 2608 2609 mHandler.post(new Runnable() { 2610 @Override 2611 public void run() { 2612 notifyPosted(info, sbnClone, update); 2613 } 2614 }); 2615 } 2616 } 2617 2618 /** 2619 * asynchronously notify all listeners about a removed notification 2620 */ 2621 public void notifyRemovedLocked(StatusBarNotification sbn) { 2622 // make a copy in case changes are made to the underlying Notification object 2623 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 2624 // notification 2625 final StatusBarNotification sbnLight = sbn.cloneLight(); 2626 for (final ManagedServiceInfo info : mServices) { 2627 if (!isVisibleToListener(sbn, info)) { 2628 continue; 2629 } 2630 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2631 mHandler.post(new Runnable() { 2632 @Override 2633 public void run() { 2634 notifyRemoved(info, sbnLight, update); 2635 } 2636 }); 2637 } 2638 } 2639 2640 /** 2641 * asynchronously notify all listeners about a reordering of notifications 2642 */ 2643 public void notifyRankingUpdateLocked() { 2644 for (final ManagedServiceInfo serviceInfo : mServices) { 2645 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2646 continue; 2647 } 2648 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo); 2649 mHandler.post(new Runnable() { 2650 @Override 2651 public void run() { 2652 notifyRankingUpdate(serviceInfo, update); 2653 } 2654 }); 2655 } 2656 } 2657 2658 public void notifyListenerFlagsChangedLocked(final int flags) { 2659 for (final ManagedServiceInfo serviceInfo : mServices) { 2660 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2661 continue; 2662 } 2663 mHandler.post(new Runnable() { 2664 @Override 2665 public void run() { 2666 notifyListenerFlagsChanged(serviceInfo, flags); 2667 } 2668 }); 2669 } 2670 } 2671 2672 private void notifyPosted(final ManagedServiceInfo info, 2673 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 2674 final INotificationListener listener = (INotificationListener)info.service; 2675 try { 2676 listener.onNotificationPosted(sbn, rankingUpdate); 2677 } catch (RemoteException ex) { 2678 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 2679 } 2680 } 2681 2682 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn, 2683 NotificationRankingUpdate rankingUpdate) { 2684 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2685 return; 2686 } 2687 final INotificationListener listener = (INotificationListener) info.service; 2688 try { 2689 listener.onNotificationRemoved(sbn, rankingUpdate); 2690 } catch (RemoteException ex) { 2691 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 2692 } 2693 } 2694 2695 private void notifyRankingUpdate(ManagedServiceInfo info, 2696 NotificationRankingUpdate rankingUpdate) { 2697 final INotificationListener listener = (INotificationListener) info.service; 2698 try { 2699 listener.onNotificationRankingUpdate(rankingUpdate); 2700 } catch (RemoteException ex) { 2701 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 2702 } 2703 } 2704 2705 private void notifyListenerFlagsChanged(ManagedServiceInfo info, int state) { 2706 final INotificationListener listener = (INotificationListener) info.service; 2707 try { 2708 listener.onListenerFlagsChanged(state); 2709 } catch (RemoteException ex) { 2710 Log.e(TAG, "unable to notify listener (listener flags): " + listener, ex); 2711 } 2712 } 2713 } 2714 2715 public static final class DumpFilter { 2716 public String pkgFilter; 2717 public boolean zen; 2718 2719 public static DumpFilter parseFromArguments(String[] args) { 2720 if (args != null && args.length == 2 && "p".equals(args[0]) 2721 && args[1] != null && !args[1].trim().isEmpty()) { 2722 final DumpFilter filter = new DumpFilter(); 2723 filter.pkgFilter = args[1].trim().toLowerCase(); 2724 return filter; 2725 } 2726 if (args != null && args.length == 1 && "zen".equals(args[0])) { 2727 final DumpFilter filter = new DumpFilter(); 2728 filter.zen = true; 2729 return filter; 2730 } 2731 return null; 2732 } 2733 2734 public boolean matches(StatusBarNotification sbn) { 2735 return zen ? true : sbn != null 2736 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg())); 2737 } 2738 2739 public boolean matches(ComponentName component) { 2740 return zen ? true : component != null && matches(component.getPackageName()); 2741 } 2742 2743 public boolean matches(String pkg) { 2744 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter); 2745 } 2746 2747 @Override 2748 public String toString() { 2749 return zen ? "zen" : ('\'' + pkgFilter + '\''); 2750 } 2751 } 2752} 2753