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