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