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