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