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