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