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