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