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