NotificationManagerService.java revision 335e1f03df933bb5e4e4d9c00811192b1b19a92e
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.notification; 18 19import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS; 20import static android.service.notification.NotificationListenerService.TRIM_FULL; 21import static android.service.notification.NotificationListenerService.TRIM_LIGHT; 22import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 23import static org.xmlpull.v1.XmlPullParser.END_TAG; 24import static org.xmlpull.v1.XmlPullParser.START_TAG; 25 26import android.app.ActivityManager; 27import android.app.ActivityManagerNative; 28import android.app.AppGlobals; 29import android.app.AppOpsManager; 30import android.app.IActivityManager; 31import android.app.INotificationManager; 32import android.app.ITransientNotification; 33import android.app.Notification; 34import android.app.NotificationManager; 35import android.app.PendingIntent; 36import android.app.StatusBarManager; 37import android.content.BroadcastReceiver; 38import android.content.ComponentName; 39import android.content.ContentResolver; 40import android.content.Context; 41import android.content.Intent; 42import android.content.IntentFilter; 43import android.content.pm.ApplicationInfo; 44import android.content.pm.PackageInfo; 45import android.content.pm.PackageManager; 46import android.content.pm.PackageManager.NameNotFoundException; 47import android.content.pm.ParceledListSlice; 48import android.content.res.Resources; 49import android.database.ContentObserver; 50import android.media.AudioAttributes; 51import android.media.AudioManager; 52import android.media.AudioSystem; 53import android.media.IRingtonePlayer; 54import android.net.Uri; 55import android.os.Binder; 56import android.os.Bundle; 57import android.os.Environment; 58import android.os.Handler; 59import android.os.HandlerThread; 60import android.os.IBinder; 61import android.os.IInterface; 62import android.os.Looper; 63import android.os.Message; 64import android.os.Process; 65import android.os.RemoteException; 66import android.os.UserHandle; 67import android.os.Vibrator; 68import android.provider.Settings; 69import android.service.notification.Condition; 70import android.service.notification.IConditionListener; 71import android.service.notification.IConditionProvider; 72import android.service.notification.INotificationListener; 73import android.service.notification.IStatusBarNotificationHolder; 74import android.service.notification.NotificationListenerService; 75import android.service.notification.NotificationRankingUpdate; 76import android.service.notification.StatusBarNotification; 77import android.service.notification.ZenModeConfig; 78import android.telephony.TelephonyManager; 79import android.text.TextUtils; 80import android.util.ArrayMap; 81import android.util.ArraySet; 82import android.util.AtomicFile; 83import android.util.Log; 84import android.util.Slog; 85import android.util.Xml; 86import android.view.accessibility.AccessibilityEvent; 87import android.view.accessibility.AccessibilityManager; 88import android.widget.Toast; 89 90import com.android.internal.R; 91import com.android.internal.util.FastXmlSerializer; 92import com.android.server.EventLogTags; 93import com.android.server.SystemService; 94import com.android.server.lights.Light; 95import com.android.server.lights.LightsManager; 96import com.android.server.notification.ManagedServices.ManagedServiceInfo; 97import com.android.server.notification.ManagedServices.UserProfiles; 98import com.android.server.statusbar.StatusBarManagerInternal; 99 100import libcore.io.IoUtils; 101 102import org.xmlpull.v1.XmlPullParser; 103import org.xmlpull.v1.XmlPullParserException; 104import org.xmlpull.v1.XmlSerializer; 105 106import java.io.File; 107import java.io.FileDescriptor; 108import java.io.FileInputStream; 109import java.io.FileNotFoundException; 110import java.io.FileOutputStream; 111import java.io.IOException; 112import java.io.PrintWriter; 113import java.util.ArrayDeque; 114import java.util.ArrayList; 115import java.util.HashSet; 116import java.util.Iterator; 117import java.util.NoSuchElementException; 118import java.util.Objects; 119 120/** {@hide} */ 121public class NotificationManagerService extends SystemService { 122 static final String TAG = "NotificationService"; 123 static final boolean DBG = false; 124 125 static final int MAX_PACKAGE_NOTIFICATIONS = 50; 126 127 // message codes 128 static final int MESSAGE_TIMEOUT = 2; 129 static final int MESSAGE_SAVE_POLICY_FILE = 3; 130 static final int MESSAGE_RECONSIDER_RANKING = 4; 131 static final int MESSAGE_RANKING_CONFIG_CHANGE = 5; 132 static final int MESSAGE_SEND_RANKING_UPDATE = 6; 133 static final int MESSAGE_LISTENER_HINTS_CHANGED = 7; 134 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8; 135 136 static final int LONG_DELAY = 3500; // 3.5 seconds 137 static final int SHORT_DELAY = 2000; // 2 seconds 138 139 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; 140 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps 141 142 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; 143 static final boolean SCORE_ONGOING_HIGHER = false; 144 145 static final int JUNK_SCORE = -1000; 146 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; 147 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER; 148 149 // Notifications with scores below this will not interrupt the user, either via LED or 150 // sound or vibration 151 static final int SCORE_INTERRUPTION_THRESHOLD = 152 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; 153 154 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; 155 static final boolean ENABLE_BLOCKED_TOASTS = true; 156 157 private IActivityManager mAm; 158 AudioManager mAudioManager; 159 StatusBarManagerInternal mStatusBar; 160 Vibrator mVibrator; 161 162 final IBinder mForegroundToken = new Binder(); 163 private WorkerHandler mHandler; 164 private final HandlerThread mRankingThread = new HandlerThread("ranker", 165 Process.THREAD_PRIORITY_BACKGROUND); 166 167 private Light mNotificationLight; 168 Light mAttentionLight; 169 private int mDefaultNotificationColor; 170 private int mDefaultNotificationLedOn; 171 172 private int mDefaultNotificationLedOff; 173 private long[] mDefaultVibrationPattern; 174 175 private long[] mFallbackVibrationPattern; 176 private boolean mUseAttentionLight; 177 boolean mSystemReady; 178 179 private boolean mDisableNotificationEffects; 180 NotificationRecord mSoundNotification; 181 NotificationRecord mVibrateNotification; 182 183 private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>(); 184 private ComponentName mEffectsSuppressor; 185 private int mListenerHints; // right now, all hints are global 186 private int mInterruptionFilter; // current ZEN mode as communicated to listeners 187 188 // for enabling and disabling notification pulse behavior 189 private boolean mScreenOn = true; 190 private boolean mInCall = false; 191 private boolean mNotificationPulseEnabled; 192 193 // used as a mutex for access to all active notifications & listeners 194 final ArrayList<NotificationRecord> mNotificationList = 195 new ArrayList<NotificationRecord>(); 196 final ArrayMap<String, NotificationRecord> mNotificationsByKey = 197 new ArrayMap<String, NotificationRecord>(); 198 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); 199 200 ArrayList<String> mLights = new ArrayList<String>(); 201 NotificationRecord mLedNotification; 202 203 private AppOpsManager mAppOps; 204 205 private Archive mArchive; 206 207 // Notification control database. For now just contains disabled packages. 208 private AtomicFile mPolicyFile; 209 private HashSet<String> mBlockedPackages = new HashSet<String>(); 210 211 private static final int DB_VERSION = 1; 212 213 private static final String TAG_BODY = "notification-policy"; 214 private static final String ATTR_VERSION = "version"; 215 216 private static final String TAG_BLOCKED_PKGS = "blocked-packages"; 217 private static final String TAG_PACKAGE = "package"; 218 private static final String ATTR_NAME = "name"; 219 220 private RankingHelper mRankingHelper; 221 222 private final UserProfiles mUserProfiles = new UserProfiles(); 223 private NotificationListeners mListeners; 224 private ConditionProviders mConditionProviders; 225 private NotificationUsageStats mUsageStats; 226 227 private static final int MY_UID = Process.myUid(); 228 private static final int MY_PID = Process.myPid(); 229 private static final int REASON_DELEGATE_CLICK = 1; 230 private static final int REASON_DELEGATE_CANCEL = 2; 231 private static final int REASON_DELEGATE_CANCEL_ALL = 3; 232 private static final int REASON_DELEGATE_ERROR = 4; 233 private static final int REASON_PACKAGE_CHANGED = 5; 234 private static final int REASON_USER_STOPPED = 6; 235 private static final int REASON_PACKAGE_BANNED = 7; 236 private static final int REASON_NOMAN_CANCEL = 8; 237 private static final int REASON_NOMAN_CANCEL_ALL = 9; 238 private static final int REASON_LISTENER_CANCEL = 10; 239 private static final int REASON_LISTENER_CANCEL_ALL = 11; 240 private static final int REASON_GROUP_SUMMARY_CANCELED = 12; 241 242 private static class Archive { 243 final int mBufferSize; 244 final ArrayDeque<StatusBarNotification> mBuffer; 245 246 public Archive(int size) { 247 mBufferSize = size; 248 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize); 249 } 250 251 public String toString() { 252 final StringBuilder sb = new StringBuilder(); 253 final int N = mBuffer.size(); 254 sb.append("Archive ("); 255 sb.append(N); 256 sb.append(" notification"); 257 sb.append((N==1)?")":"s)"); 258 return sb.toString(); 259 } 260 261 public void record(StatusBarNotification nr) { 262 if (mBuffer.size() == mBufferSize) { 263 mBuffer.removeFirst(); 264 } 265 266 // We don't want to store the heavy bits of the notification in the archive, 267 // but other clients in the system process might be using the object, so we 268 // store a (lightened) copy. 269 mBuffer.addLast(nr.cloneLight()); 270 } 271 272 public void clear() { 273 mBuffer.clear(); 274 } 275 276 public Iterator<StatusBarNotification> descendingIterator() { 277 return mBuffer.descendingIterator(); 278 } 279 public Iterator<StatusBarNotification> ascendingIterator() { 280 return mBuffer.iterator(); 281 } 282 public Iterator<StatusBarNotification> filter( 283 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) { 284 return new Iterator<StatusBarNotification>() { 285 StatusBarNotification mNext = findNext(); 286 287 private StatusBarNotification findNext() { 288 while (iter.hasNext()) { 289 StatusBarNotification nr = iter.next(); 290 if ((pkg == null || nr.getPackageName() == pkg) 291 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) { 292 return nr; 293 } 294 } 295 return null; 296 } 297 298 @Override 299 public boolean hasNext() { 300 return mNext == null; 301 } 302 303 @Override 304 public StatusBarNotification next() { 305 StatusBarNotification next = mNext; 306 if (next == null) { 307 throw new NoSuchElementException(); 308 } 309 mNext = findNext(); 310 return next; 311 } 312 313 @Override 314 public void remove() { 315 iter.remove(); 316 } 317 }; 318 } 319 320 public StatusBarNotification[] getArray(int count) { 321 if (count == 0) count = mBufferSize; 322 final StatusBarNotification[] a 323 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 324 Iterator<StatusBarNotification> iter = descendingIterator(); 325 int i=0; 326 while (iter.hasNext() && i < count) { 327 a[i++] = iter.next(); 328 } 329 return a; 330 } 331 332 public StatusBarNotification[] getArray(int count, String pkg, int userId) { 333 if (count == 0) count = mBufferSize; 334 final StatusBarNotification[] a 335 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 336 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId); 337 int i=0; 338 while (iter.hasNext() && i < count) { 339 a[i++] = iter.next(); 340 } 341 return a; 342 } 343 344 } 345 346 private void loadPolicyFile() { 347 synchronized(mPolicyFile) { 348 mBlockedPackages.clear(); 349 350 FileInputStream infile = null; 351 try { 352 infile = mPolicyFile.openRead(); 353 final XmlPullParser parser = Xml.newPullParser(); 354 parser.setInput(infile, null); 355 356 int type; 357 String tag; 358 int version = DB_VERSION; 359 while ((type = parser.next()) != END_DOCUMENT) { 360 tag = parser.getName(); 361 if (type == START_TAG) { 362 if (TAG_BODY.equals(tag)) { 363 version = Integer.parseInt( 364 parser.getAttributeValue(null, ATTR_VERSION)); 365 } else if (TAG_BLOCKED_PKGS.equals(tag)) { 366 while ((type = parser.next()) != END_DOCUMENT) { 367 tag = parser.getName(); 368 if (TAG_PACKAGE.equals(tag)) { 369 mBlockedPackages.add( 370 parser.getAttributeValue(null, ATTR_NAME)); 371 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { 372 break; 373 } 374 } 375 } 376 } 377 mZenModeHelper.readXml(parser); 378 mRankingHelper.readXml(parser); 379 } 380 } catch (FileNotFoundException e) { 381 // No data yet 382 } catch (IOException e) { 383 Log.wtf(TAG, "Unable to read notification policy", e); 384 } catch (NumberFormatException e) { 385 Log.wtf(TAG, "Unable to parse notification policy", e); 386 } catch (XmlPullParserException e) { 387 Log.wtf(TAG, "Unable to parse notification policy", e); 388 } finally { 389 IoUtils.closeQuietly(infile); 390 } 391 } 392 } 393 394 public void savePolicyFile() { 395 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE); 396 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE); 397 } 398 399 private void handleSavePolicyFile() { 400 Slog.d(TAG, "handleSavePolicyFile"); 401 synchronized (mPolicyFile) { 402 final FileOutputStream stream; 403 try { 404 stream = mPolicyFile.startWrite(); 405 } catch (IOException e) { 406 Slog.w(TAG, "Failed to save policy file", e); 407 return; 408 } 409 410 try { 411 final XmlSerializer out = new FastXmlSerializer(); 412 out.setOutput(stream, "utf-8"); 413 out.startDocument(null, true); 414 out.startTag(null, TAG_BODY); 415 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); 416 mZenModeHelper.writeXml(out); 417 mRankingHelper.writeXml(out); 418 out.endTag(null, TAG_BODY); 419 out.endDocument(); 420 mPolicyFile.finishWrite(stream); 421 } catch (IOException e) { 422 Slog.w(TAG, "Failed to save policy file, restoring backup", e); 423 mPolicyFile.failWrite(stream); 424 } 425 } 426 } 427 428 /** Use this when you actually want to post a notification or toast. 429 * 430 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*(). 431 */ 432 private boolean noteNotificationOp(String pkg, int uid) { 433 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 434 != AppOpsManager.MODE_ALLOWED) { 435 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg); 436 return false; 437 } 438 return true; 439 } 440 441 private static final class ToastRecord 442 { 443 final int pid; 444 final String pkg; 445 final ITransientNotification callback; 446 int duration; 447 448 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) 449 { 450 this.pid = pid; 451 this.pkg = pkg; 452 this.callback = callback; 453 this.duration = duration; 454 } 455 456 void update(int duration) { 457 this.duration = duration; 458 } 459 460 void dump(PrintWriter pw, String prefix, DumpFilter filter) { 461 if (filter != null && !filter.matches(pkg)) return; 462 pw.println(prefix + this); 463 } 464 465 @Override 466 public final String toString() 467 { 468 return "ToastRecord{" 469 + Integer.toHexString(System.identityHashCode(this)) 470 + " pkg=" + pkg 471 + " callback=" + callback 472 + " duration=" + duration; 473 } 474 } 475 476 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() { 477 478 @Override 479 public void onSetDisabled(int status) { 480 synchronized (mNotificationList) { 481 mDisableNotificationEffects = 482 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; 483 if (disableNotificationEffects()) { 484 // cancel whatever's going on 485 long identity = Binder.clearCallingIdentity(); 486 try { 487 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 488 if (player != null) { 489 player.stopAsync(); 490 } 491 } catch (RemoteException e) { 492 } finally { 493 Binder.restoreCallingIdentity(identity); 494 } 495 496 identity = Binder.clearCallingIdentity(); 497 try { 498 mVibrator.cancel(); 499 } finally { 500 Binder.restoreCallingIdentity(identity); 501 } 502 } 503 } 504 } 505 506 @Override 507 public void onClearAll(int callingUid, int callingPid, int userId) { 508 synchronized (mNotificationList) { 509 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null, 510 /*includeCurrentProfiles*/ true); 511 } 512 } 513 514 @Override 515 public void onNotificationClick(int callingUid, int callingPid, String key) { 516 synchronized (mNotificationList) { 517 EventLogTags.writeNotificationClicked(key); 518 NotificationRecord r = mNotificationsByKey.get(key); 519 if (r == null) { 520 Log.w(TAG, "No notification with key: " + key); 521 return; 522 } 523 StatusBarNotification sbn = r.sbn; 524 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(), 525 sbn.getId(), Notification.FLAG_AUTO_CANCEL, 526 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(), 527 REASON_DELEGATE_CLICK, null); 528 } 529 } 530 531 @Override 532 public void onNotificationClear(int callingUid, int callingPid, 533 String pkg, String tag, int id, int userId) { 534 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 535 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 536 true, userId, REASON_DELEGATE_CANCEL, null); 537 } 538 539 @Override 540 public void onPanelRevealed() { 541 EventLogTags.writeNotificationPanelRevealed(); 542 synchronized (mNotificationList) { 543 // sound 544 mSoundNotification = null; 545 546 long identity = Binder.clearCallingIdentity(); 547 try { 548 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 549 if (player != null) { 550 player.stopAsync(); 551 } 552 } catch (RemoteException e) { 553 } finally { 554 Binder.restoreCallingIdentity(identity); 555 } 556 557 // vibrate 558 mVibrateNotification = null; 559 identity = Binder.clearCallingIdentity(); 560 try { 561 mVibrator.cancel(); 562 } finally { 563 Binder.restoreCallingIdentity(identity); 564 } 565 566 // light 567 mLights.clear(); 568 mLedNotification = null; 569 updateLightsLocked(); 570 } 571 } 572 573 @Override 574 public void onPanelHidden() { 575 EventLogTags.writeNotificationPanelHidden(); 576 } 577 578 @Override 579 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id, 580 int uid, int initialPid, String message, int userId) { 581 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id 582 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")"); 583 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId, 584 REASON_DELEGATE_ERROR, null); 585 long ident = Binder.clearCallingIdentity(); 586 try { 587 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, 588 "Bad notification posted from package " + pkg 589 + ": " + message); 590 } catch (RemoteException e) { 591 } 592 Binder.restoreCallingIdentity(ident); 593 } 594 595 @Override 596 public void onNotificationVisibilityChanged( 597 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) { 598 // Using ';' as separator since eventlogs uses ',' to separate 599 // args. 600 EventLogTags.writeNotificationVisibilityChanged( 601 TextUtils.join(";", newlyVisibleKeys), 602 TextUtils.join(";", noLongerVisibleKeys)); 603 synchronized (mNotificationList) { 604 for (String key : newlyVisibleKeys) { 605 NotificationRecord r = mNotificationsByKey.get(key); 606 if (r == null) continue; 607 r.stats.onVisibilityChanged(true); 608 } 609 // Note that we might receive this event after notifications 610 // have already left the system, e.g. after dismissing from the 611 // shade. Hence not finding notifications in 612 // mNotificationsByKey is not an exceptional condition. 613 for (String key : noLongerVisibleKeys) { 614 NotificationRecord r = mNotificationsByKey.get(key); 615 if (r == null) continue; 616 r.stats.onVisibilityChanged(false); 617 } 618 } 619 } 620 621 @Override 622 public void onNotificationExpansionChanged(String key, 623 boolean userAction, boolean expanded) { 624 EventLogTags.writeNotificationExpansion(key, userAction ? 1 : 0, expanded ? 1 : 0); 625 synchronized (mNotificationList) { 626 NotificationRecord r = mNotificationsByKey.get(key); 627 if (r != null) { 628 r.stats.onExpansionChanged(userAction, expanded); 629 } 630 } 631 } 632 }; 633 634 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 635 @Override 636 public void onReceive(Context context, Intent intent) { 637 String action = intent.getAction(); 638 639 boolean queryRestart = false; 640 boolean queryRemove = false; 641 boolean packageChanged = false; 642 boolean cancelNotifications = true; 643 644 if (action.equals(Intent.ACTION_PACKAGE_ADDED) 645 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED)) 646 || action.equals(Intent.ACTION_PACKAGE_RESTARTED) 647 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED)) 648 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) 649 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 650 String pkgList[] = null; 651 boolean queryReplace = queryRemove && 652 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 653 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace); 654 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 655 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 656 } else if (queryRestart) { 657 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 658 } else { 659 Uri uri = intent.getData(); 660 if (uri == null) { 661 return; 662 } 663 String pkgName = uri.getSchemeSpecificPart(); 664 if (pkgName == null) { 665 return; 666 } 667 if (packageChanged) { 668 // We cancel notifications for packages which have just been disabled 669 try { 670 final int enabled = getContext().getPackageManager() 671 .getApplicationEnabledSetting(pkgName); 672 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED 673 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { 674 cancelNotifications = false; 675 } 676 } catch (IllegalArgumentException e) { 677 // Package doesn't exist; probably racing with uninstall. 678 // cancelNotifications is already true, so nothing to do here. 679 if (DBG) { 680 Slog.i(TAG, "Exception trying to look up app enabled setting", e); 681 } 682 } 683 } 684 pkgList = new String[]{pkgName}; 685 } 686 687 if (pkgList != null && (pkgList.length > 0)) { 688 for (String pkgName : pkgList) { 689 if (cancelNotifications) { 690 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart, 691 UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null); 692 } 693 } 694 } 695 mListeners.onPackagesChanged(queryReplace, pkgList); 696 mConditionProviders.onPackagesChanged(queryReplace, pkgList); 697 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 698 // Keep track of screen on/off state, but do not turn off the notification light 699 // until user passes through the lock screen or views the notification. 700 mScreenOn = true; 701 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 702 mScreenOn = false; 703 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 704 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK 705 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE)); 706 updateNotificationPulse(); 707 } else if (action.equals(Intent.ACTION_USER_STOPPED)) { 708 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 709 if (userHandle >= 0) { 710 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle, 711 REASON_USER_STOPPED, null); 712 } 713 } else if (action.equals(Intent.ACTION_USER_PRESENT)) { 714 // turn off LED when user passes through lock screen 715 mNotificationLight.turnOff(); 716 mStatusBar.notificationLightOff(); 717 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { 718 // reload per-user settings 719 mSettingsObserver.update(null); 720 mUserProfiles.updateCache(context); 721 } else if (action.equals(Intent.ACTION_USER_ADDED)) { 722 mUserProfiles.updateCache(context); 723 } 724 } 725 }; 726 727 class SettingsObserver extends ContentObserver { 728 private final Uri NOTIFICATION_LIGHT_PULSE_URI 729 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); 730 731 SettingsObserver(Handler handler) { 732 super(handler); 733 } 734 735 void observe() { 736 ContentResolver resolver = getContext().getContentResolver(); 737 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, 738 false, this, UserHandle.USER_ALL); 739 update(null); 740 } 741 742 @Override public void onChange(boolean selfChange, Uri uri) { 743 update(uri); 744 } 745 746 public void update(Uri uri) { 747 ContentResolver resolver = getContext().getContentResolver(); 748 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) { 749 boolean pulseEnabled = Settings.System.getInt(resolver, 750 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; 751 if (mNotificationPulseEnabled != pulseEnabled) { 752 mNotificationPulseEnabled = pulseEnabled; 753 updateNotificationPulse(); 754 } 755 } 756 } 757 } 758 759 private SettingsObserver mSettingsObserver; 760 private ZenModeHelper mZenModeHelper; 761 762 private final Runnable mBuzzBeepBlinked = new Runnable() { 763 @Override 764 public void run() { 765 mStatusBar.buzzBeepBlinked(); 766 } 767 }; 768 769 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) { 770 int[] ar = r.getIntArray(resid); 771 if (ar == null) { 772 return def; 773 } 774 final int len = ar.length > maxlen ? maxlen : ar.length; 775 long[] out = new long[len]; 776 for (int i=0; i<len; i++) { 777 out[i] = ar[i]; 778 } 779 return out; 780 } 781 782 public NotificationManagerService(Context context) { 783 super(context); 784 } 785 786 @Override 787 public void onStart() { 788 Resources resources = getContext().getResources(); 789 790 mAm = ActivityManagerNative.getDefault(); 791 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); 792 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); 793 794 mHandler = new WorkerHandler(); 795 mRankingThread.start(); 796 String[] extractorNames; 797 try { 798 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors); 799 } catch (Resources.NotFoundException e) { 800 extractorNames = new String[0]; 801 } 802 mRankingHelper = new RankingHelper(getContext(), 803 new RankingWorkerHandler(mRankingThread.getLooper()), 804 extractorNames); 805 mZenModeHelper = new ZenModeHelper(getContext(), mHandler); 806 mZenModeHelper.addCallback(new ZenModeHelper.Callback() { 807 @Override 808 public void onConfigChanged() { 809 savePolicyFile(); 810 } 811 812 @Override 813 void onZenModeChanged() { 814 synchronized(mNotificationList) { 815 updateInterruptionFilterLocked(); 816 } 817 } 818 }); 819 final File systemDir = new File(Environment.getDataDirectory(), "system"); 820 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml")); 821 mUsageStats = new NotificationUsageStats(getContext()); 822 823 importOldBlockDb(); 824 825 mListeners = new NotificationListeners(); 826 mConditionProviders = new ConditionProviders(getContext(), 827 mHandler, mUserProfiles, mZenModeHelper); 828 mStatusBar = getLocalService(StatusBarManagerInternal.class); 829 mStatusBar.setNotificationDelegate(mNotificationDelegate); 830 831 final LightsManager lights = getLocalService(LightsManager.class); 832 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS); 833 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION); 834 835 mDefaultNotificationColor = resources.getColor( 836 R.color.config_defaultNotificationColor); 837 mDefaultNotificationLedOn = resources.getInteger( 838 R.integer.config_defaultNotificationLedOn); 839 mDefaultNotificationLedOff = resources.getInteger( 840 R.integer.config_defaultNotificationLedOff); 841 842 mDefaultVibrationPattern = getLongArray(resources, 843 R.array.config_defaultNotificationVibePattern, 844 VIBRATE_PATTERN_MAXLEN, 845 DEFAULT_VIBRATE_PATTERN); 846 847 mFallbackVibrationPattern = getLongArray(resources, 848 R.array.config_notificationFallbackVibePattern, 849 VIBRATE_PATTERN_MAXLEN, 850 DEFAULT_VIBRATE_PATTERN); 851 852 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight); 853 854 // Don't start allowing notifications until the setup wizard has run once. 855 // After that, including subsequent boots, init with notifications turned on. 856 // This works on the first boot because the setup wizard will toggle this 857 // flag at least once and we'll go back to 0 after that. 858 if (0 == Settings.Global.getInt(getContext().getContentResolver(), 859 Settings.Global.DEVICE_PROVISIONED, 0)) { 860 mDisableNotificationEffects = true; 861 } 862 mZenModeHelper.updateZenMode(); 863 864 mUserProfiles.updateCache(getContext()); 865 866 // register for various Intents 867 IntentFilter filter = new IntentFilter(); 868 filter.addAction(Intent.ACTION_SCREEN_ON); 869 filter.addAction(Intent.ACTION_SCREEN_OFF); 870 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 871 filter.addAction(Intent.ACTION_USER_PRESENT); 872 filter.addAction(Intent.ACTION_USER_STOPPED); 873 filter.addAction(Intent.ACTION_USER_SWITCHED); 874 filter.addAction(Intent.ACTION_USER_ADDED); 875 getContext().registerReceiver(mIntentReceiver, filter); 876 IntentFilter pkgFilter = new IntentFilter(); 877 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 878 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 879 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 880 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 881 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 882 pkgFilter.addDataScheme("package"); 883 getContext().registerReceiver(mIntentReceiver, pkgFilter); 884 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 885 getContext().registerReceiver(mIntentReceiver, sdFilter); 886 887 mSettingsObserver = new SettingsObserver(mHandler); 888 889 mArchive = new Archive(resources.getInteger( 890 R.integer.config_notificationServiceArchiveSize)); 891 892 publishBinderService(Context.NOTIFICATION_SERVICE, mService); 893 publishLocalService(NotificationManagerInternal.class, mInternalService); 894 } 895 896 /** 897 * Read the old XML-based app block database and import those blockages into the AppOps system. 898 */ 899 private void importOldBlockDb() { 900 loadPolicyFile(); 901 902 PackageManager pm = getContext().getPackageManager(); 903 for (String pkg : mBlockedPackages) { 904 PackageInfo info = null; 905 try { 906 info = pm.getPackageInfo(pkg, 0); 907 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false); 908 } catch (NameNotFoundException e) { 909 // forget you 910 } 911 } 912 mBlockedPackages.clear(); 913 } 914 915 @Override 916 public void onBootPhase(int phase) { 917 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 918 // no beeping until we're basically done booting 919 mSystemReady = true; 920 921 // Grab our optional AudioService 922 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 923 mZenModeHelper.setAudioManager(mAudioManager); 924 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 925 // This observer will force an update when observe is called, causing us to 926 // bind to listener services. 927 mSettingsObserver.observe(); 928 mListeners.onBootPhaseAppsCanStart(); 929 mConditionProviders.onBootPhaseAppsCanStart(); 930 } 931 } 932 933 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) { 934 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); 935 936 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, 937 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 938 939 // Now, cancel any outstanding notifications that are part of a just-disabled app 940 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { 941 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid), 942 REASON_PACKAGE_BANNED, null); 943 } 944 } 945 946 private void updateListenerHintsLocked() { 947 final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS; 948 if (hints == mListenerHints) return; 949 mListenerHints = hints; 950 scheduleListenerHintsChanged(hints); 951 } 952 953 private void updateEffectsSuppressorLocked() { 954 final ComponentName suppressor = !mListenersDisablingEffects.isEmpty() 955 ? mListenersDisablingEffects.valueAt(0).component : null; 956 if (Objects.equals(suppressor, mEffectsSuppressor)) return; 957 mEffectsSuppressor = suppressor; 958 getContext().sendBroadcast(new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED) 959 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)); 960 } 961 962 private void updateInterruptionFilterLocked() { 963 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter(); 964 if (interruptionFilter == mInterruptionFilter) return; 965 mInterruptionFilter = interruptionFilter; 966 scheduleInterruptionFilterChanged(interruptionFilter); 967 } 968 969 private final IBinder mService = new INotificationManager.Stub() { 970 // Toasts 971 // ============================================================================ 972 973 @Override 974 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 975 { 976 if (DBG) { 977 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback 978 + " duration=" + duration); 979 } 980 981 if (pkg == null || callback == null) { 982 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 983 return ; 984 } 985 986 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); 987 988 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { 989 if (!isSystemToast) { 990 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 991 return; 992 } 993 } 994 995 synchronized (mToastQueue) { 996 int callingPid = Binder.getCallingPid(); 997 long callingId = Binder.clearCallingIdentity(); 998 try { 999 ToastRecord record; 1000 int index = indexOfToastLocked(pkg, callback); 1001 // If it's already in the queue, we update it in place, we don't 1002 // move it to the end of the queue. 1003 if (index >= 0) { 1004 record = mToastQueue.get(index); 1005 record.update(duration); 1006 } else { 1007 // Limit the number of toasts that any given package except the android 1008 // package can enqueue. Prevents DOS attacks and deals with leaks. 1009 if (!isSystemToast) { 1010 int count = 0; 1011 final int N = mToastQueue.size(); 1012 for (int i=0; i<N; i++) { 1013 final ToastRecord r = mToastQueue.get(i); 1014 if (r.pkg.equals(pkg)) { 1015 count++; 1016 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1017 Slog.e(TAG, "Package has already posted " + count 1018 + " toasts. Not showing more. Package=" + pkg); 1019 return; 1020 } 1021 } 1022 } 1023 } 1024 1025 record = new ToastRecord(callingPid, pkg, callback, duration); 1026 mToastQueue.add(record); 1027 index = mToastQueue.size() - 1; 1028 keepProcessAliveLocked(callingPid); 1029 } 1030 // If it's at index 0, it's the current toast. It doesn't matter if it's 1031 // new or just been updated. Call back and tell it to show itself. 1032 // If the callback fails, this will remove it from the list, so don't 1033 // assume that it's valid after this. 1034 if (index == 0) { 1035 showNextToastLocked(); 1036 } 1037 } finally { 1038 Binder.restoreCallingIdentity(callingId); 1039 } 1040 } 1041 } 1042 1043 @Override 1044 public void cancelToast(String pkg, ITransientNotification callback) { 1045 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 1046 1047 if (pkg == null || callback == null) { 1048 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 1049 return ; 1050 } 1051 1052 synchronized (mToastQueue) { 1053 long callingId = Binder.clearCallingIdentity(); 1054 try { 1055 int index = indexOfToastLocked(pkg, callback); 1056 if (index >= 0) { 1057 cancelToastLocked(index); 1058 } else { 1059 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg 1060 + " callback=" + callback); 1061 } 1062 } finally { 1063 Binder.restoreCallingIdentity(callingId); 1064 } 1065 } 1066 } 1067 1068 @Override 1069 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, 1070 Notification notification, int[] idOut, int userId) throws RemoteException { 1071 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(), 1072 Binder.getCallingPid(), tag, id, notification, idOut, userId); 1073 } 1074 1075 @Override 1076 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 1077 checkCallerIsSystemOrSameApp(pkg); 1078 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1079 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 1080 // Don't allow client applications to cancel foreground service notis. 1081 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0, 1082 Binder.getCallingUid() == Process.SYSTEM_UID 1083 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL, 1084 null); 1085 } 1086 1087 @Override 1088 public void cancelAllNotifications(String pkg, int userId) { 1089 checkCallerIsSystemOrSameApp(pkg); 1090 1091 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1092 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 1093 1094 // Calling from user space, don't allow the canceling of actively 1095 // running foreground services. 1096 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(), 1097 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId, 1098 REASON_NOMAN_CANCEL_ALL, null); 1099 } 1100 1101 @Override 1102 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { 1103 checkCallerIsSystem(); 1104 1105 setNotificationsEnabledForPackageImpl(pkg, uid, enabled); 1106 } 1107 1108 /** 1109 * Use this when you just want to know if notifications are OK for this package. 1110 */ 1111 @Override 1112 public boolean areNotificationsEnabledForPackage(String pkg, int uid) { 1113 checkCallerIsSystem(); 1114 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 1115 == AppOpsManager.MODE_ALLOWED); 1116 } 1117 1118 @Override 1119 public void setPackagePriority(String pkg, int uid, int priority) { 1120 checkCallerIsSystem(); 1121 mRankingHelper.setPackagePriority(pkg, uid, priority); 1122 savePolicyFile(); 1123 } 1124 1125 @Override 1126 public int getPackagePriority(String pkg, int uid) { 1127 checkCallerIsSystem(); 1128 return mRankingHelper.getPackagePriority(pkg, uid); 1129 } 1130 1131 @Override 1132 public void setPackageVisibilityOverride(String pkg, int uid, int visibility) { 1133 checkCallerIsSystem(); 1134 mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility); 1135 savePolicyFile(); 1136 } 1137 1138 @Override 1139 public int getPackageVisibilityOverride(String pkg, int uid) { 1140 checkCallerIsSystem(); 1141 return mRankingHelper.getPackageVisibilityOverride(pkg, uid); 1142 } 1143 1144 /** 1145 * System-only API for getting a list of current (i.e. not cleared) notifications. 1146 * 1147 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1148 * @returns A list of all the notifications, in natural order. 1149 */ 1150 @Override 1151 public StatusBarNotification[] getActiveNotifications(String callingPkg) { 1152 // enforce() will ensure the calling uid has the correct permission 1153 getContext().enforceCallingOrSelfPermission( 1154 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1155 "NotificationManagerService.getActiveNotifications"); 1156 1157 StatusBarNotification[] tmp = null; 1158 int uid = Binder.getCallingUid(); 1159 1160 // noteOp will check to make sure the callingPkg matches the uid 1161 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1162 == AppOpsManager.MODE_ALLOWED) { 1163 synchronized (mNotificationList) { 1164 tmp = new StatusBarNotification[mNotificationList.size()]; 1165 final int N = mNotificationList.size(); 1166 for (int i=0; i<N; i++) { 1167 tmp[i] = mNotificationList.get(i).sbn; 1168 } 1169 } 1170 } 1171 return tmp; 1172 } 1173 1174 /** 1175 * System-only API for getting a list of recent (cleared, no longer shown) notifications. 1176 * 1177 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1178 */ 1179 @Override 1180 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { 1181 // enforce() will ensure the calling uid has the correct permission 1182 getContext().enforceCallingOrSelfPermission( 1183 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1184 "NotificationManagerService.getHistoricalNotifications"); 1185 1186 StatusBarNotification[] tmp = null; 1187 int uid = Binder.getCallingUid(); 1188 1189 // noteOp will check to make sure the callingPkg matches the uid 1190 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1191 == AppOpsManager.MODE_ALLOWED) { 1192 synchronized (mArchive) { 1193 tmp = mArchive.getArray(count); 1194 } 1195 } 1196 return tmp; 1197 } 1198 1199 /** 1200 * Register a listener binder directly with the notification manager. 1201 * 1202 * Only works with system callers. Apps should extend 1203 * {@link android.service.notification.NotificationListenerService}. 1204 */ 1205 @Override 1206 public void registerListener(final INotificationListener listener, 1207 final ComponentName component, final int userid) { 1208 enforceSystemOrSystemUI("INotificationManager.registerListener"); 1209 mListeners.registerService(listener, component, userid); 1210 } 1211 1212 /** 1213 * Remove a listener binder directly 1214 */ 1215 @Override 1216 public void unregisterListener(INotificationListener listener, int userid) { 1217 mListeners.unregisterService(listener, userid); 1218 } 1219 1220 /** 1221 * Allow an INotificationListener to simulate a "clear all" operation. 1222 * 1223 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} 1224 * 1225 * @param token The binder for the listener, to check that the caller is allowed 1226 */ 1227 @Override 1228 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) { 1229 final int callingUid = Binder.getCallingUid(); 1230 final int callingPid = Binder.getCallingPid(); 1231 long identity = Binder.clearCallingIdentity(); 1232 try { 1233 synchronized (mNotificationList) { 1234 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1235 if (keys != null) { 1236 final int N = keys.length; 1237 for (int i = 0; i < N; i++) { 1238 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1239 if (r == null) continue; 1240 final int userId = r.sbn.getUserId(); 1241 if (userId != info.userid && userId != UserHandle.USER_ALL && 1242 !mUserProfiles.isCurrentProfile(userId)) { 1243 throw new SecurityException("Disallowed call from listener: " 1244 + info.service); 1245 } 1246 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1247 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(), 1248 userId); 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 return n.audioAttributes; 1950 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) { 1951 // the stream type is valid, use it 1952 return new AudioAttributes.Builder() 1953 .setInternalLegacyStreamType(n.audioStreamType) 1954 .build(); 1955 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) { 1956 return Notification.AUDIO_ATTRIBUTES_DEFAULT; 1957 } else { 1958 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType)); 1959 return Notification.AUDIO_ATTRIBUTES_DEFAULT; 1960 } 1961 } 1962 1963 void showNextToastLocked() { 1964 ToastRecord record = mToastQueue.get(0); 1965 while (record != null) { 1966 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1967 try { 1968 record.callback.show(); 1969 scheduleTimeoutLocked(record); 1970 return; 1971 } catch (RemoteException e) { 1972 Slog.w(TAG, "Object died trying to show notification " + record.callback 1973 + " in package " + record.pkg); 1974 // remove it from the list and let the process die 1975 int index = mToastQueue.indexOf(record); 1976 if (index >= 0) { 1977 mToastQueue.remove(index); 1978 } 1979 keepProcessAliveLocked(record.pid); 1980 if (mToastQueue.size() > 0) { 1981 record = mToastQueue.get(0); 1982 } else { 1983 record = null; 1984 } 1985 } 1986 } 1987 } 1988 1989 void cancelToastLocked(int index) { 1990 ToastRecord record = mToastQueue.get(index); 1991 try { 1992 record.callback.hide(); 1993 } catch (RemoteException e) { 1994 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1995 + " in package " + record.pkg); 1996 // don't worry about this, we're about to remove it from 1997 // the list anyway 1998 } 1999 mToastQueue.remove(index); 2000 keepProcessAliveLocked(record.pid); 2001 if (mToastQueue.size() > 0) { 2002 // Show the next one. If the callback fails, this will remove 2003 // it from the list, so don't assume that the list hasn't changed 2004 // after this point. 2005 showNextToastLocked(); 2006 } 2007 } 2008 2009 private void scheduleTimeoutLocked(ToastRecord r) 2010 { 2011 mHandler.removeCallbacksAndMessages(r); 2012 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 2013 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 2014 mHandler.sendMessageDelayed(m, delay); 2015 } 2016 2017 private void handleTimeout(ToastRecord record) 2018 { 2019 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 2020 synchronized (mToastQueue) { 2021 int index = indexOfToastLocked(record.pkg, record.callback); 2022 if (index >= 0) { 2023 cancelToastLocked(index); 2024 } 2025 } 2026 } 2027 2028 // lock on mToastQueue 2029 int indexOfToastLocked(String pkg, ITransientNotification callback) 2030 { 2031 IBinder cbak = callback.asBinder(); 2032 ArrayList<ToastRecord> list = mToastQueue; 2033 int len = list.size(); 2034 for (int i=0; i<len; i++) { 2035 ToastRecord r = list.get(i); 2036 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 2037 return i; 2038 } 2039 } 2040 return -1; 2041 } 2042 2043 // lock on mToastQueue 2044 void keepProcessAliveLocked(int pid) 2045 { 2046 int toastCount = 0; // toasts from this pid 2047 ArrayList<ToastRecord> list = mToastQueue; 2048 int N = list.size(); 2049 for (int i=0; i<N; i++) { 2050 ToastRecord r = list.get(i); 2051 if (r.pid == pid) { 2052 toastCount++; 2053 } 2054 } 2055 try { 2056 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 2057 } catch (RemoteException e) { 2058 // Shouldn't happen. 2059 } 2060 } 2061 2062 private void handleRankingReconsideration(Message message) { 2063 if (!(message.obj instanceof RankingReconsideration)) return; 2064 RankingReconsideration recon = (RankingReconsideration) message.obj; 2065 recon.run(); 2066 boolean changed; 2067 synchronized (mNotificationList) { 2068 final NotificationRecord record = mNotificationsByKey.get(recon.getKey()); 2069 if (record == null) { 2070 return; 2071 } 2072 int indexBefore = findNotificationRecordIndexLocked(record); 2073 boolean interceptBefore = record.isIntercepted(); 2074 int visibilityBefore = record.getPackageVisibilityOverride(); 2075 recon.applyChangesLocked(record); 2076 applyZenModeLocked(record); 2077 mRankingHelper.sort(mNotificationList); 2078 int indexAfter = findNotificationRecordIndexLocked(record); 2079 boolean interceptAfter = record.isIntercepted(); 2080 int visibilityAfter = record.getPackageVisibilityOverride(); 2081 changed = indexBefore != indexAfter || interceptBefore != interceptAfter 2082 || visibilityBefore != visibilityAfter; 2083 if (interceptBefore && !interceptAfter) { 2084 buzzBeepBlinkLocked(record); 2085 } 2086 } 2087 if (changed) { 2088 scheduleSendRankingUpdate(); 2089 } 2090 } 2091 2092 private void handleRankingConfigChange() { 2093 synchronized (mNotificationList) { 2094 final int N = mNotificationList.size(); 2095 ArrayList<String> orderBefore = new ArrayList<String>(N); 2096 int[] visibilities = new int[N]; 2097 for (int i = 0; i < N; i++) { 2098 final NotificationRecord r = mNotificationList.get(i); 2099 orderBefore.add(r.getKey()); 2100 visibilities[i] = r.getPackageVisibilityOverride(); 2101 mRankingHelper.extractSignals(r); 2102 } 2103 for (int i = 0; i < N; i++) { 2104 mRankingHelper.sort(mNotificationList); 2105 final NotificationRecord r = mNotificationList.get(i); 2106 if (!orderBefore.get(i).equals(r.getKey()) 2107 || visibilities[i] != r.getPackageVisibilityOverride()) { 2108 scheduleSendRankingUpdate(); 2109 return; 2110 } 2111 } 2112 } 2113 } 2114 2115 // let zen mode evaluate this record 2116 private void applyZenModeLocked(NotificationRecord record) { 2117 record.setIntercepted(mZenModeHelper.shouldIntercept(record)); 2118 } 2119 2120 // lock on mNotificationList 2121 private int findNotificationRecordIndexLocked(NotificationRecord target) { 2122 return mRankingHelper.indexOf(mNotificationList, target); 2123 } 2124 2125 private void scheduleSendRankingUpdate() { 2126 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 2127 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 2128 mHandler.sendMessage(m); 2129 } 2130 2131 private void handleSendRankingUpdate() { 2132 synchronized (mNotificationList) { 2133 mListeners.notifyRankingUpdateLocked(); 2134 } 2135 } 2136 2137 private void scheduleListenerHintsChanged(int state) { 2138 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED); 2139 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget(); 2140 } 2141 2142 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) { 2143 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED); 2144 mHandler.obtainMessage( 2145 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED, 2146 listenerInterruptionFilter, 2147 0).sendToTarget(); 2148 } 2149 2150 private void handleListenerHintsChanged(int hints) { 2151 synchronized (mNotificationList) { 2152 mListeners.notifyListenerHintsChangedLocked(hints); 2153 } 2154 } 2155 2156 private void handleListenerInterruptionFilterChanged(int interruptionFilter) { 2157 synchronized (mNotificationList) { 2158 mListeners.notifyInterruptionFilterChanged(interruptionFilter); 2159 } 2160 } 2161 2162 private final class WorkerHandler extends Handler 2163 { 2164 @Override 2165 public void handleMessage(Message msg) 2166 { 2167 switch (msg.what) 2168 { 2169 case MESSAGE_TIMEOUT: 2170 handleTimeout((ToastRecord)msg.obj); 2171 break; 2172 case MESSAGE_SAVE_POLICY_FILE: 2173 handleSavePolicyFile(); 2174 break; 2175 case MESSAGE_SEND_RANKING_UPDATE: 2176 handleSendRankingUpdate(); 2177 break; 2178 case MESSAGE_LISTENER_HINTS_CHANGED: 2179 handleListenerHintsChanged(msg.arg1); 2180 break; 2181 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED: 2182 handleListenerInterruptionFilterChanged(msg.arg1); 2183 break; 2184 } 2185 } 2186 2187 } 2188 2189 private final class RankingWorkerHandler extends Handler 2190 { 2191 public RankingWorkerHandler(Looper looper) { 2192 super(looper); 2193 } 2194 2195 @Override 2196 public void handleMessage(Message msg) { 2197 switch (msg.what) { 2198 case MESSAGE_RECONSIDER_RANKING: 2199 handleRankingReconsideration(msg); 2200 break; 2201 case MESSAGE_RANKING_CONFIG_CHANGE: 2202 handleRankingConfigChange(); 2203 break; 2204 } 2205 } 2206 } 2207 2208 // Notifications 2209 // ============================================================================ 2210 static int clamp(int x, int low, int high) { 2211 return (x < low) ? low : ((x > high) ? high : x); 2212 } 2213 2214 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 2215 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 2216 if (!manager.isEnabled()) { 2217 return; 2218 } 2219 2220 AccessibilityEvent event = 2221 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 2222 event.setPackageName(packageName); 2223 event.setClassName(Notification.class.getName()); 2224 event.setParcelableData(notification); 2225 CharSequence tickerText = notification.tickerText; 2226 if (!TextUtils.isEmpty(tickerText)) { 2227 event.getText().add(tickerText); 2228 } 2229 2230 manager.sendAccessibilityEvent(event); 2231 } 2232 2233 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 2234 // tell the app 2235 if (sendDelete) { 2236 if (r.getNotification().deleteIntent != null) { 2237 try { 2238 r.getNotification().deleteIntent.send(); 2239 } catch (PendingIntent.CanceledException ex) { 2240 // do nothing - there's no relevant way to recover, and 2241 // no reason to let this propagate 2242 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 2243 } 2244 } 2245 } 2246 2247 // status bar 2248 if (r.getNotification().icon != 0) { 2249 r.isCanceled = true; 2250 mListeners.notifyRemovedLocked(r.sbn); 2251 } 2252 2253 // sound 2254 if (mSoundNotification == r) { 2255 mSoundNotification = null; 2256 final long identity = Binder.clearCallingIdentity(); 2257 try { 2258 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2259 if (player != null) { 2260 player.stopAsync(); 2261 } 2262 } catch (RemoteException e) { 2263 } finally { 2264 Binder.restoreCallingIdentity(identity); 2265 } 2266 } 2267 2268 // vibrate 2269 if (mVibrateNotification == r) { 2270 mVibrateNotification = null; 2271 long identity = Binder.clearCallingIdentity(); 2272 try { 2273 mVibrator.cancel(); 2274 } 2275 finally { 2276 Binder.restoreCallingIdentity(identity); 2277 } 2278 } 2279 2280 // light 2281 mLights.remove(r.getKey()); 2282 if (mLedNotification == r) { 2283 mLedNotification = null; 2284 } 2285 2286 // Record usage stats 2287 switch (reason) { 2288 case REASON_DELEGATE_CANCEL: 2289 case REASON_DELEGATE_CANCEL_ALL: 2290 case REASON_LISTENER_CANCEL: 2291 case REASON_LISTENER_CANCEL_ALL: 2292 mUsageStats.registerDismissedByUser(r); 2293 break; 2294 case REASON_NOMAN_CANCEL: 2295 case REASON_NOMAN_CANCEL_ALL: 2296 mUsageStats.registerRemovedByApp(r); 2297 break; 2298 case REASON_DELEGATE_CLICK: 2299 mUsageStats.registerCancelDueToClick(r); 2300 break; 2301 default: 2302 mUsageStats.registerCancelUnknown(r); 2303 break; 2304 } 2305 2306 mNotificationsByKey.remove(r.sbn.getKey()); 2307 2308 // Save it for users of getHistoricalNotifications() 2309 mArchive.record(r.sbn); 2310 } 2311 2312 /** 2313 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2314 * and none of the {@code mustNotHaveFlags}. 2315 */ 2316 void cancelNotification(final int callingUid, final int callingPid, 2317 final String pkg, final String tag, final int id, 2318 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2319 final int userId, final int reason, final ManagedServiceInfo listener) { 2320 // In enqueueNotificationInternal notifications are added by scheduling the 2321 // work on the worker handler. Hence, we also schedule the cancel on this 2322 // handler to avoid a scenario where an add notification call followed by a 2323 // remove notification call ends up in not removing the notification. 2324 mHandler.post(new Runnable() { 2325 @Override 2326 public void run() { 2327 String listenerName = listener == null ? null : listener.component.toShortString(); 2328 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2329 mustHaveFlags, mustNotHaveFlags, reason, listenerName); 2330 2331 synchronized (mNotificationList) { 2332 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2333 if (index >= 0) { 2334 NotificationRecord r = mNotificationList.get(index); 2335 2336 // Ideally we'd do this in the caller of this method. However, that would 2337 // require the caller to also find the notification. 2338 if (reason == REASON_DELEGATE_CLICK) { 2339 mUsageStats.registerClickedByUser(r); 2340 } 2341 2342 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2343 return; 2344 } 2345 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2346 return; 2347 } 2348 2349 mNotificationList.remove(index); 2350 2351 cancelNotificationLocked(r, sendDelete, reason); 2352 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName); 2353 updateLightsLocked(); 2354 } 2355 } 2356 } 2357 }); 2358 } 2359 2360 /** 2361 * Determine whether the userId applies to the notification in question, either because 2362 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2363 */ 2364 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2365 return 2366 // looking for USER_ALL notifications? match everything 2367 userId == UserHandle.USER_ALL 2368 // a notification sent to USER_ALL matches any query 2369 || r.getUserId() == UserHandle.USER_ALL 2370 // an exact user match 2371 || r.getUserId() == userId; 2372 } 2373 2374 /** 2375 * Determine whether the userId applies to the notification in question, either because 2376 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2377 * because it matches one of the users profiles. 2378 */ 2379 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2380 return notificationMatchesUserId(r, userId) 2381 || mUserProfiles.isCurrentProfile(r.getUserId()); 2382 } 2383 2384 /** 2385 * Cancels all notifications from a given package that have all of the 2386 * {@code mustHaveFlags}. 2387 */ 2388 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2389 int mustNotHaveFlags, boolean doit, int userId, int reason, 2390 ManagedServiceInfo listener) { 2391 String listenerName = listener == null ? null : listener.component.toShortString(); 2392 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2393 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2394 listenerName); 2395 2396 synchronized (mNotificationList) { 2397 final int N = mNotificationList.size(); 2398 ArrayList<NotificationRecord> canceledNotifications = null; 2399 for (int i = N-1; i >= 0; --i) { 2400 NotificationRecord r = mNotificationList.get(i); 2401 if (!notificationMatchesUserId(r, userId)) { 2402 continue; 2403 } 2404 // Don't remove notifications to all, if there's no package name specified 2405 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2406 continue; 2407 } 2408 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2409 continue; 2410 } 2411 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2412 continue; 2413 } 2414 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2415 continue; 2416 } 2417 if (canceledNotifications == null) { 2418 canceledNotifications = new ArrayList<>(); 2419 } 2420 canceledNotifications.add(r); 2421 if (!doit) { 2422 return true; 2423 } 2424 mNotificationList.remove(i); 2425 cancelNotificationLocked(r, false, reason); 2426 } 2427 if (doit && canceledNotifications != null) { 2428 final int M = canceledNotifications.size(); 2429 for (int i = 0; i < M; i++) { 2430 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2431 listenerName); 2432 } 2433 } 2434 if (canceledNotifications != null) { 2435 updateLightsLocked(); 2436 } 2437 return canceledNotifications != null; 2438 } 2439 } 2440 2441 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2442 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2443 String listenerName = listener == null ? null : listener.component.toShortString(); 2444 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2445 null, userId, 0, 0, reason, listenerName); 2446 2447 ArrayList<NotificationRecord> canceledNotifications = null; 2448 final int N = mNotificationList.size(); 2449 for (int i=N-1; i>=0; i--) { 2450 NotificationRecord r = mNotificationList.get(i); 2451 if (includeCurrentProfiles) { 2452 if (!notificationMatchesCurrentProfiles(r, userId)) { 2453 continue; 2454 } 2455 } else { 2456 if (!notificationMatchesUserId(r, userId)) { 2457 continue; 2458 } 2459 } 2460 2461 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2462 | Notification.FLAG_NO_CLEAR)) == 0) { 2463 mNotificationList.remove(i); 2464 cancelNotificationLocked(r, true, reason); 2465 // Make a note so we can cancel children later. 2466 if (canceledNotifications == null) { 2467 canceledNotifications = new ArrayList<>(); 2468 } 2469 canceledNotifications.add(r); 2470 } 2471 } 2472 int M = canceledNotifications != null ? canceledNotifications.size() : 0; 2473 for (int i = 0; i < M; i++) { 2474 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2475 listenerName); 2476 } 2477 updateLightsLocked(); 2478 } 2479 2480 // Warning: The caller is responsible for invoking updateLightsLocked(). 2481 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid, 2482 String listenerName) { 2483 Notification n = r.getNotification(); 2484 if (!n.isGroupSummary()) { 2485 return; 2486 } 2487 2488 String pkg = r.sbn.getPackageName(); 2489 int userId = r.getUserId(); 2490 2491 if (pkg == null) { 2492 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey()); 2493 return; 2494 } 2495 2496 final int N = mNotificationList.size(); 2497 for (int i = N - 1; i >= 0; i--) { 2498 NotificationRecord childR = mNotificationList.get(i); 2499 StatusBarNotification childSbn = childR.sbn; 2500 if (childR.getNotification().isGroupChild() && 2501 childR.getGroupKey().equals(r.getGroupKey())) { 2502 EventLogTags.writeNotificationCancel(callingUid, callingPid, 2503 pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0, 2504 REASON_GROUP_SUMMARY_CANCELED, listenerName); 2505 mNotificationList.remove(i); 2506 cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED); 2507 } 2508 } 2509 } 2510 2511 // lock on mNotificationList 2512 void updateLightsLocked() 2513 { 2514 // handle notification lights 2515 if (mLedNotification == null) { 2516 // get next notification, if any 2517 int n = mLights.size(); 2518 if (n > 0) { 2519 mLedNotification = mNotificationsByKey.get(mLights.get(n-1)); 2520 } 2521 } 2522 2523 // Don't flash while we are in a call or screen is on 2524 if (mLedNotification == null || mInCall || mScreenOn) { 2525 mNotificationLight.turnOff(); 2526 mStatusBar.notificationLightOff(); 2527 } else { 2528 final Notification ledno = mLedNotification.sbn.getNotification(); 2529 int ledARGB = ledno.ledARGB; 2530 int ledOnMS = ledno.ledOnMS; 2531 int ledOffMS = ledno.ledOffMS; 2532 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2533 ledARGB = mDefaultNotificationColor; 2534 ledOnMS = mDefaultNotificationLedOn; 2535 ledOffMS = mDefaultNotificationLedOff; 2536 } 2537 if (mNotificationPulseEnabled) { 2538 // pulse repeatedly 2539 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2540 ledOnMS, ledOffMS); 2541 } 2542 // let SystemUI make an independent decision 2543 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS); 2544 } 2545 } 2546 2547 // lock on mNotificationList 2548 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2549 { 2550 ArrayList<NotificationRecord> list = mNotificationList; 2551 final int len = list.size(); 2552 for (int i=0; i<len; i++) { 2553 NotificationRecord r = list.get(i); 2554 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2555 continue; 2556 } 2557 if (tag == null) { 2558 if (r.sbn.getTag() != null) { 2559 continue; 2560 } 2561 } else { 2562 if (!tag.equals(r.sbn.getTag())) { 2563 continue; 2564 } 2565 } 2566 if (r.sbn.getPackageName().equals(pkg)) { 2567 return i; 2568 } 2569 } 2570 return -1; 2571 } 2572 2573 // lock on mNotificationList 2574 int indexOfNotificationLocked(String key) { 2575 final int N = mNotificationList.size(); 2576 for (int i = 0; i < N; i++) { 2577 if (key.equals(mNotificationList.get(i).getKey())) { 2578 return i; 2579 } 2580 } 2581 return -1; 2582 } 2583 2584 private void updateNotificationPulse() { 2585 synchronized (mNotificationList) { 2586 updateLightsLocked(); 2587 } 2588 } 2589 2590 private static boolean isUidSystem(int uid) { 2591 final int appid = UserHandle.getAppId(uid); 2592 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2593 } 2594 2595 private static boolean isCallerSystem() { 2596 return isUidSystem(Binder.getCallingUid()); 2597 } 2598 2599 private static void checkCallerIsSystem() { 2600 if (isCallerSystem()) { 2601 return; 2602 } 2603 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2604 } 2605 2606 private static void checkCallerIsSystemOrSameApp(String pkg) { 2607 if (isCallerSystem()) { 2608 return; 2609 } 2610 final int uid = Binder.getCallingUid(); 2611 try { 2612 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2613 pkg, 0, UserHandle.getCallingUserId()); 2614 if (ai == null) { 2615 throw new SecurityException("Unknown package " + pkg); 2616 } 2617 if (!UserHandle.isSameApp(ai.uid, uid)) { 2618 throw new SecurityException("Calling uid " + uid + " gave package" 2619 + pkg + " which is owned by uid " + ai.uid); 2620 } 2621 } catch (RemoteException re) { 2622 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2623 } 2624 } 2625 2626 /** 2627 * Generates a NotificationRankingUpdate from 'sbns', considering only 2628 * notifications visible to the given listener. 2629 * 2630 * <p>Caller must hold a lock on mNotificationList.</p> 2631 */ 2632 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { 2633 int speedBumpIndex = -1; 2634 final int N = mNotificationList.size(); 2635 ArrayList<String> keys = new ArrayList<String>(N); 2636 ArrayList<String> interceptedKeys = new ArrayList<String>(N); 2637 Bundle visibilityOverrides = new Bundle(); 2638 for (int i = 0; i < N; i++) { 2639 NotificationRecord record = mNotificationList.get(i); 2640 if (!isVisibleToListener(record.sbn, info)) { 2641 continue; 2642 } 2643 keys.add(record.sbn.getKey()); 2644 if (record.isIntercepted()) { 2645 interceptedKeys.add(record.sbn.getKey()); 2646 } 2647 if (record.getPackageVisibilityOverride() 2648 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) { 2649 visibilityOverrides.putInt(record.sbn.getKey(), 2650 record.getPackageVisibilityOverride()); 2651 } 2652 // Find first min-prio notification for speedbump placement. 2653 if (speedBumpIndex == -1 && 2654 // Intrusiveness trumps priority, hence ignore intrusives. 2655 !record.isRecentlyIntrusive() && 2656 // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so 2657 // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT 2658 // (or lower as a safeguard) is sufficient to find the speedbump index. 2659 // We'll have to revisit this when more package priority buckets are introduced. 2660 record.getPackagePriority() <= Notification.PRIORITY_DEFAULT && 2661 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) { 2662 speedBumpIndex = keys.size() - 1; 2663 } 2664 } 2665 String[] keysAr = keys.toArray(new String[keys.size()]); 2666 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]); 2667 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides, 2668 speedBumpIndex); 2669 } 2670 2671 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { 2672 if (!listener.enabledAndUserMatches(sbn.getUserId())) { 2673 return false; 2674 } 2675 // TODO: remove this for older listeners. 2676 return true; 2677 } 2678 2679 public class NotificationListeners extends ManagedServices { 2680 2681 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>(); 2682 2683 public NotificationListeners() { 2684 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2685 } 2686 2687 @Override 2688 protected Config getConfig() { 2689 Config c = new Config(); 2690 c.caption = "notification listener"; 2691 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2692 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2693 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2694 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2695 c.clientLabel = R.string.notification_listener_binding_label; 2696 return c; 2697 } 2698 2699 @Override 2700 protected IInterface asInterface(IBinder binder) { 2701 return INotificationListener.Stub.asInterface(binder); 2702 } 2703 2704 @Override 2705 public void onServiceAdded(ManagedServiceInfo info) { 2706 final INotificationListener listener = (INotificationListener) info.service; 2707 final NotificationRankingUpdate update; 2708 synchronized (mNotificationList) { 2709 update = makeRankingUpdateLocked(info); 2710 } 2711 try { 2712 listener.onListenerConnected(update); 2713 } catch (RemoteException e) { 2714 // we tried 2715 } 2716 } 2717 2718 @Override 2719 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 2720 if (mListenersDisablingEffects.remove(removed)) { 2721 updateListenerHintsLocked(); 2722 } 2723 mLightTrimListeners.remove(removed); 2724 } 2725 2726 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) { 2727 if (trim == TRIM_LIGHT) { 2728 mLightTrimListeners.add(info); 2729 } else { 2730 mLightTrimListeners.remove(info); 2731 } 2732 } 2733 2734 public int getOnNotificationPostedTrim(ManagedServiceInfo info) { 2735 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL; 2736 2737 } 2738 2739 /** 2740 * asynchronously notify all listeners about a new notification 2741 * 2742 * <p> 2743 * Also takes care of removing a notification that has been visible to a listener before, 2744 * but isn't anymore. 2745 */ 2746 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) { 2747 // Lazily initialized snapshots of the notification. 2748 StatusBarNotification sbnClone = null; 2749 StatusBarNotification sbnCloneLight = null; 2750 2751 for (final ManagedServiceInfo info : mServices) { 2752 boolean sbnVisible = isVisibleToListener(sbn, info); 2753 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false; 2754 // This notification hasn't been and still isn't visible -> ignore. 2755 if (!oldSbnVisible && !sbnVisible) { 2756 continue; 2757 } 2758 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2759 2760 // This notification became invisible -> remove the old one. 2761 if (oldSbnVisible && !sbnVisible) { 2762 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight(); 2763 mHandler.post(new Runnable() { 2764 @Override 2765 public void run() { 2766 notifyRemoved(info, oldSbnLightClone, update); 2767 } 2768 }); 2769 continue; 2770 } 2771 2772 final int trim = mListeners.getOnNotificationPostedTrim(info); 2773 2774 if (trim == TRIM_LIGHT && sbnCloneLight == null) { 2775 sbnCloneLight = sbn.cloneLight(); 2776 } else if (trim == TRIM_FULL && sbnClone == null) { 2777 sbnClone = sbn.clone(); 2778 } 2779 final StatusBarNotification sbnToPost = 2780 (trim == TRIM_FULL) ? sbnClone : sbnCloneLight; 2781 2782 mHandler.post(new Runnable() { 2783 @Override 2784 public void run() { 2785 notifyPosted(info, sbnToPost, update); 2786 } 2787 }); 2788 } 2789 } 2790 2791 /** 2792 * asynchronously notify all listeners about a removed notification 2793 */ 2794 public void notifyRemovedLocked(StatusBarNotification sbn) { 2795 // make a copy in case changes are made to the underlying Notification object 2796 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 2797 // notification 2798 final StatusBarNotification sbnLight = sbn.cloneLight(); 2799 for (final ManagedServiceInfo info : mServices) { 2800 if (!isVisibleToListener(sbn, info)) { 2801 continue; 2802 } 2803 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2804 mHandler.post(new Runnable() { 2805 @Override 2806 public void run() { 2807 notifyRemoved(info, sbnLight, update); 2808 } 2809 }); 2810 } 2811 } 2812 2813 /** 2814 * asynchronously notify all listeners about a reordering of notifications 2815 */ 2816 public void notifyRankingUpdateLocked() { 2817 for (final ManagedServiceInfo serviceInfo : mServices) { 2818 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2819 continue; 2820 } 2821 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo); 2822 mHandler.post(new Runnable() { 2823 @Override 2824 public void run() { 2825 notifyRankingUpdate(serviceInfo, update); 2826 } 2827 }); 2828 } 2829 } 2830 2831 public void notifyListenerHintsChangedLocked(final int hints) { 2832 for (final ManagedServiceInfo serviceInfo : mServices) { 2833 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2834 continue; 2835 } 2836 mHandler.post(new Runnable() { 2837 @Override 2838 public void run() { 2839 notifyListenerHintsChanged(serviceInfo, hints); 2840 } 2841 }); 2842 } 2843 } 2844 2845 public void notifyInterruptionFilterChanged(final int interruptionFilter) { 2846 for (final ManagedServiceInfo serviceInfo : mServices) { 2847 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2848 continue; 2849 } 2850 mHandler.post(new Runnable() { 2851 @Override 2852 public void run() { 2853 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter); 2854 } 2855 }); 2856 } 2857 } 2858 2859 private void notifyPosted(final ManagedServiceInfo info, 2860 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 2861 final INotificationListener listener = (INotificationListener)info.service; 2862 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); 2863 try { 2864 listener.onNotificationPosted(sbnHolder, rankingUpdate); 2865 } catch (RemoteException ex) { 2866 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 2867 } 2868 } 2869 2870 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn, 2871 NotificationRankingUpdate rankingUpdate) { 2872 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2873 return; 2874 } 2875 final INotificationListener listener = (INotificationListener) info.service; 2876 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); 2877 try { 2878 listener.onNotificationRemoved(sbnHolder, rankingUpdate); 2879 } catch (RemoteException ex) { 2880 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 2881 } 2882 } 2883 2884 private void notifyRankingUpdate(ManagedServiceInfo info, 2885 NotificationRankingUpdate rankingUpdate) { 2886 final INotificationListener listener = (INotificationListener) info.service; 2887 try { 2888 listener.onNotificationRankingUpdate(rankingUpdate); 2889 } catch (RemoteException ex) { 2890 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 2891 } 2892 } 2893 2894 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) { 2895 final INotificationListener listener = (INotificationListener) info.service; 2896 try { 2897 listener.onListenerHintsChanged(hints); 2898 } catch (RemoteException ex) { 2899 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex); 2900 } 2901 } 2902 2903 private void notifyInterruptionFilterChanged(ManagedServiceInfo info, 2904 int interruptionFilter) { 2905 final INotificationListener listener = (INotificationListener) info.service; 2906 try { 2907 listener.onInterruptionFilterChanged(interruptionFilter); 2908 } catch (RemoteException ex) { 2909 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex); 2910 } 2911 } 2912 2913 private boolean isListenerPackage(String packageName) { 2914 if (packageName == null) { 2915 return false; 2916 } 2917 // TODO: clean up locking object later 2918 synchronized (mNotificationList) { 2919 for (final ManagedServiceInfo serviceInfo : mServices) { 2920 if (packageName.equals(serviceInfo.component.getPackageName())) { 2921 return true; 2922 } 2923 } 2924 } 2925 return false; 2926 } 2927 } 2928 2929 public static final class DumpFilter { 2930 public String pkgFilter; 2931 public boolean zen; 2932 2933 public static DumpFilter parseFromArguments(String[] args) { 2934 if (args != null && args.length == 2 && "p".equals(args[0]) 2935 && args[1] != null && !args[1].trim().isEmpty()) { 2936 final DumpFilter filter = new DumpFilter(); 2937 filter.pkgFilter = args[1].trim().toLowerCase(); 2938 return filter; 2939 } 2940 if (args != null && args.length == 1 && "zen".equals(args[0])) { 2941 final DumpFilter filter = new DumpFilter(); 2942 filter.zen = true; 2943 return filter; 2944 } 2945 return null; 2946 } 2947 2948 public boolean matches(StatusBarNotification sbn) { 2949 return zen ? true : sbn != null 2950 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg())); 2951 } 2952 2953 public boolean matches(ComponentName component) { 2954 return zen ? true : component != null && matches(component.getPackageName()); 2955 } 2956 2957 public boolean matches(String pkg) { 2958 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter); 2959 } 2960 2961 @Override 2962 public String toString() { 2963 return zen ? "zen" : ('\'' + pkgFilter + '\''); 2964 } 2965 } 2966 2967 /** 2968 * Wrapper for a StatusBarNotification object that allows transfer across a oneway 2969 * binder without sending large amounts of data over a oneway transaction. 2970 */ 2971 private static final class StatusBarNotificationHolder 2972 extends IStatusBarNotificationHolder.Stub { 2973 private StatusBarNotification mValue; 2974 2975 public StatusBarNotificationHolder(StatusBarNotification value) { 2976 mValue = value; 2977 } 2978 2979 /** Get the held value and clear it. This function should only be called once per holder */ 2980 @Override 2981 public StatusBarNotification get() { 2982 StatusBarNotification value = mValue; 2983 mValue = null; 2984 return value; 2985 } 2986 } 2987} 2988