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