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