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