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