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