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