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