NotificationManagerService.java revision ea75fddbb452638f286c2fcdbddff145ee1a85cb
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 * @param keys An array of notification keys to fetch, or null to fetch everything 1281 * @returns The return value will contain the notifications specified in keys, in that 1282 * order, or if keys is null, all the notifications, in natural order. 1283 */ 1284 @Override 1285 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener( 1286 INotificationListener token, String[] keys) { 1287 synchronized (mNotificationList) { 1288 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1289 final ArrayList<StatusBarNotification> list 1290 = new ArrayList<StatusBarNotification>(); 1291 final boolean getKeys = keys != null; 1292 final int N = getKeys ? keys.length : mNotificationList.size(); 1293 list.ensureCapacity(N); 1294 for (int i=0; i<N; i++) { 1295 final NotificationRecord r = getKeys 1296 ? mNotificationsByKey.get(keys[i]) 1297 : mNotificationList.get(i); 1298 if (r != null) { 1299 StatusBarNotification sbn = r.sbn; 1300 if (isVisibleToListener(sbn, info)) { 1301 list.add(sbn); 1302 } 1303 } 1304 } 1305 return new ParceledListSlice<StatusBarNotification>(list); 1306 } 1307 } 1308 1309 @Override 1310 public void requestHintsFromListener(INotificationListener token, int hints) { 1311 final long identity = Binder.clearCallingIdentity(); 1312 try { 1313 synchronized (mNotificationList) { 1314 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1315 final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0; 1316 if (disableEffects) { 1317 mListenersDisablingEffects.add(info); 1318 } else { 1319 mListenersDisablingEffects.remove(info); 1320 } 1321 mZenModeHelper.requestFromListener(hints); 1322 updateListenerHintsLocked(); 1323 updateEffectsSuppressorLocked(); 1324 } 1325 } finally { 1326 Binder.restoreCallingIdentity(identity); 1327 } 1328 } 1329 1330 @Override 1331 public int getHintsFromListener(INotificationListener token) { 1332 synchronized (mNotificationList) { 1333 return mListenerHints; 1334 } 1335 } 1336 1337 @Override 1338 public ZenModeConfig getZenModeConfig() { 1339 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig"); 1340 return mZenModeHelper.getConfig(); 1341 } 1342 1343 @Override 1344 public boolean setZenModeConfig(ZenModeConfig config) { 1345 checkCallerIsSystem(); 1346 return mZenModeHelper.setConfig(config); 1347 } 1348 1349 @Override 1350 public void notifyConditions(String pkg, IConditionProvider provider, 1351 Condition[] conditions) { 1352 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider); 1353 checkCallerIsSystemOrSameApp(pkg); 1354 final long identity = Binder.clearCallingIdentity(); 1355 try { 1356 mConditionProviders.notifyConditions(pkg, info, conditions); 1357 } finally { 1358 Binder.restoreCallingIdentity(identity); 1359 } 1360 } 1361 1362 @Override 1363 public void requestZenModeConditions(IConditionListener callback, int relevance) { 1364 enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions"); 1365 mConditionProviders.requestZenModeConditions(callback, relevance); 1366 } 1367 1368 @Override 1369 public void setZenModeCondition(Condition condition) { 1370 enforceSystemOrSystemUI("INotificationManager.setZenModeCondition"); 1371 final long identity = Binder.clearCallingIdentity(); 1372 try { 1373 mConditionProviders.setZenModeCondition(condition, "binderCall"); 1374 } finally { 1375 Binder.restoreCallingIdentity(identity); 1376 } 1377 } 1378 1379 @Override 1380 public void setAutomaticZenModeConditions(Uri[] conditionIds) { 1381 enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions"); 1382 mConditionProviders.setAutomaticZenModeConditions(conditionIds); 1383 } 1384 1385 @Override 1386 public Condition[] getAutomaticZenModeConditions() { 1387 enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions"); 1388 return mConditionProviders.getAutomaticZenModeConditions(); 1389 } 1390 1391 private void enforceSystemOrSystemUI(String message) { 1392 if (isCallerSystem()) return; 1393 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 1394 message); 1395 } 1396 1397 @Override 1398 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1399 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1400 != PackageManager.PERMISSION_GRANTED) { 1401 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1402 + Binder.getCallingPid() 1403 + ", uid=" + Binder.getCallingUid()); 1404 return; 1405 } 1406 1407 dumpImpl(pw, DumpFilter.parseFromArguments(args)); 1408 } 1409 1410 @Override 1411 public ComponentName getEffectsSuppressor() { 1412 enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor"); 1413 return mEffectsSuppressor; 1414 } 1415 }; 1416 1417 private String[] getActiveNotificationKeys(INotificationListener token) { 1418 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1419 final ArrayList<String> keys = new ArrayList<String>(); 1420 if (info.isEnabledForCurrentProfiles()) { 1421 synchronized (mNotificationList) { 1422 final int N = mNotificationList.size(); 1423 for (int i = 0; i < N; i++) { 1424 final StatusBarNotification sbn = mNotificationList.get(i).sbn; 1425 if (info.enabledAndUserMatches(sbn.getUserId())) { 1426 keys.add(sbn.getKey()); 1427 } 1428 } 1429 } 1430 } 1431 return keys.toArray(new String[keys.size()]); 1432 } 1433 1434 private boolean disableNotificationEffects() { 1435 return mDisableNotificationEffects || (mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0; 1436 } 1437 1438 void dumpImpl(PrintWriter pw, DumpFilter filter) { 1439 pw.print("Current Notification Manager state"); 1440 if (filter != null) { 1441 pw.print(" (filtered to "); pw.print(filter); pw.print(")"); 1442 } 1443 pw.println(':'); 1444 int N; 1445 final boolean zenOnly = filter != null && filter.zen; 1446 1447 if (!zenOnly) { 1448 synchronized (mToastQueue) { 1449 N = mToastQueue.size(); 1450 if (N > 0) { 1451 pw.println(" Toast Queue:"); 1452 for (int i=0; i<N; i++) { 1453 mToastQueue.get(i).dump(pw, " ", filter); 1454 } 1455 pw.println(" "); 1456 } 1457 } 1458 } 1459 1460 synchronized (mNotificationList) { 1461 if (!zenOnly) { 1462 N = mNotificationList.size(); 1463 if (N > 0) { 1464 pw.println(" Notification List:"); 1465 for (int i=0; i<N; i++) { 1466 final NotificationRecord nr = mNotificationList.get(i); 1467 if (filter != null && !filter.matches(nr.sbn)) continue; 1468 nr.dump(pw, " ", getContext()); 1469 } 1470 pw.println(" "); 1471 } 1472 1473 if (filter == null) { 1474 N = mLights.size(); 1475 if (N > 0) { 1476 pw.println(" Lights List:"); 1477 for (int i=0; i<N; i++) { 1478 pw.println(" " + mLights.get(i)); 1479 } 1480 pw.println(" "); 1481 } 1482 pw.println(" mUseAttentionLight=" + mUseAttentionLight); 1483 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled); 1484 pw.println(" mSoundNotification=" + mSoundNotification); 1485 pw.println(" mVibrateNotification=" + mVibrateNotification); 1486 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects); 1487 pw.println(" mSystemReady=" + mSystemReady); 1488 } 1489 pw.println(" mArchive=" + mArchive.toString()); 1490 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1491 int i=0; 1492 while (iter.hasNext()) { 1493 final StatusBarNotification sbn = iter.next(); 1494 if (filter != null && !filter.matches(sbn)) continue; 1495 pw.println(" " + sbn); 1496 if (++i >= 5) { 1497 if (iter.hasNext()) pw.println(" ..."); 1498 break; 1499 } 1500 } 1501 } 1502 1503 if (!zenOnly) { 1504 pw.println("\n Usage Stats:"); 1505 mUsageStats.dump(pw, " ", filter); 1506 } 1507 1508 if (filter == null || zenOnly) { 1509 pw.println("\n Zen Mode:"); 1510 mZenModeHelper.dump(pw, " "); 1511 1512 pw.println("\n Zen Log:"); 1513 ZenLog.dump(pw, " "); 1514 } 1515 1516 if (!zenOnly) { 1517 pw.println("\n Ranking Config:"); 1518 mRankingHelper.dump(pw, " ", filter); 1519 1520 pw.println("\n Notification listeners:"); 1521 mListeners.dump(pw, filter); 1522 pw.print(" mListenerHints: "); pw.println(mListenerHints); 1523 pw.print(" mListenersDisablingEffects: ("); 1524 N = mListenersDisablingEffects.size(); 1525 for (int i = 0; i < N; i++) { 1526 final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i); 1527 if (i > 0) pw.print(','); 1528 pw.print(listener.component); 1529 } 1530 pw.println(')'); 1531 } 1532 1533 pw.println("\n Condition providers:"); 1534 mConditionProviders.dump(pw, filter); 1535 } 1536 } 1537 1538 /** 1539 * The private API only accessible to the system process. 1540 */ 1541 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1542 @Override 1543 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, 1544 String tag, int id, Notification notification, int[] idReceived, int userId) { 1545 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 1546 idReceived, userId); 1547 } 1548 }; 1549 1550 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, 1551 final int callingPid, final String tag, final int id, final Notification notification, 1552 int[] idOut, int incomingUserId) { 1553 if (DBG) { 1554 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 1555 + " notification=" + notification); 1556 } 1557 checkCallerIsSystemOrSameApp(pkg); 1558 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 1559 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg); 1560 1561 final int userId = ActivityManager.handleIncomingUser(callingPid, 1562 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1563 final UserHandle user = new UserHandle(userId); 1564 1565 // Limit the number of notifications that any given package except the android 1566 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks. 1567 if (!isSystemNotification && !isNotificationFromListener) { 1568 synchronized (mNotificationList) { 1569 int count = 0; 1570 final int N = mNotificationList.size(); 1571 for (int i=0; i<N; i++) { 1572 final NotificationRecord r = mNotificationList.get(i); 1573 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1574 count++; 1575 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1576 Slog.e(TAG, "Package has already posted " + count 1577 + " notifications. Not showing more. package=" + pkg); 1578 return; 1579 } 1580 } 1581 } 1582 } 1583 } 1584 1585 // This conditional is a dirty hack to limit the logging done on 1586 // behalf of the download manager without affecting other apps. 1587 if (!pkg.equals("com.android.providers.downloads") 1588 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1589 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 1590 pkg, id, tag, userId, notification.toString()); 1591 } 1592 1593 if (pkg == null || notification == null) { 1594 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1595 + " id=" + id + " notification=" + notification); 1596 } 1597 if (notification.icon != 0) { 1598 if (!notification.isValid()) { 1599 throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg 1600 + " id=" + id + " notification=" + notification); 1601 } 1602 } 1603 1604 mHandler.post(new Runnable() { 1605 @Override 1606 public void run() { 1607 1608 // === Scoring === 1609 1610 // 0. Sanitize inputs 1611 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1612 Notification.PRIORITY_MAX); 1613 // Migrate notification flags to scores 1614 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1615 if (notification.priority < Notification.PRIORITY_MAX) { 1616 notification.priority = Notification.PRIORITY_MAX; 1617 } 1618 } else if (SCORE_ONGOING_HIGHER && 1619 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1620 if (notification.priority < Notification.PRIORITY_HIGH) { 1621 notification.priority = Notification.PRIORITY_HIGH; 1622 } 1623 } 1624 1625 // 1. initial score: buckets of 10, around the app [-20..20] 1626 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; 1627 1628 // 2. extract ranking signals from the notification data 1629 final StatusBarNotification n = new StatusBarNotification( 1630 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 1631 user); 1632 NotificationRecord r = new NotificationRecord(n, score); 1633 NotificationRecord old = mNotificationsByKey.get(n.getKey()); 1634 if (old != null) { 1635 // Retain ranking information from previous record 1636 r.copyRankingInformation(old); 1637 } 1638 mRankingHelper.extractSignals(r); 1639 1640 // 3. Apply local rules 1641 1642 // blocked apps 1643 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1644 if (!isSystemNotification) { 1645 r.score = JUNK_SCORE; 1646 Slog.e(TAG, "Suppressing notification from package " + pkg 1647 + " by user request."); 1648 } 1649 } 1650 1651 if (r.score < SCORE_DISPLAY_THRESHOLD) { 1652 // Notification will be blocked because the score is too low. 1653 return; 1654 } 1655 1656 synchronized (mNotificationList) { 1657 int index = indexOfNotificationLocked(n.getKey()); 1658 if (index < 0) { 1659 mNotificationList.add(r); 1660 mUsageStats.registerPostedByApp(r); 1661 } else { 1662 old = mNotificationList.get(index); 1663 mNotificationList.set(index, r); 1664 mUsageStats.registerUpdatedByApp(r, old); 1665 // Make sure we don't lose the foreground service state. 1666 notification.flags |= 1667 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1668 r.isUpdate = true; 1669 } 1670 1671 mNotificationsByKey.put(n.getKey(), r); 1672 1673 // Ensure if this is a foreground service that the proper additional 1674 // flags are set. 1675 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1676 notification.flags |= Notification.FLAG_ONGOING_EVENT 1677 | Notification.FLAG_NO_CLEAR; 1678 } 1679 1680 applyZenModeLocked(r); 1681 1682 try { 1683 mRankingHelper.sort(mNotificationList); 1684 } catch (RuntimeException ex) { 1685 // Don't crash the system server if something bad happened. 1686 Log.e(TAG, "Extreme badness during notification sort", ex); 1687 Log.e(TAG, "Current notification list: "); 1688 for (int ii=0; ii < mNotificationList.size(); ii++) { 1689 NotificationRecord nr = mNotificationList.get(ii); 1690 Log.e(TAG, String.format( 1691 " [%d] %s (group %s, rank %d, sortkey %s, proxy %s)", 1692 ii, nr, nr.getGroupKey(), nr.getAuthoritativeRank(), 1693 nr.getNotification().getSortKey(), 1694 nr.getRankingProxy())); 1695 } 1696 // STOPSHIP: remove once b/16626175 is found 1697 throw ex; 1698 } 1699 1700 if (notification.icon != 0) { 1701 StatusBarNotification oldSbn = (old != null) ? old.sbn : null; 1702 mListeners.notifyPostedLocked(n, oldSbn); 1703 } else { 1704 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1705 if (old != null && !old.isCanceled) { 1706 mListeners.notifyRemovedLocked(n); 1707 } 1708 // ATTENTION: in a future release we will bail out here 1709 // so that we do not play sounds, show lights, etc. for invalid 1710 // notifications 1711 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1712 + n.getPackageName()); 1713 } 1714 1715 buzzBeepBlinkLocked(r); 1716 } 1717 } 1718 }); 1719 1720 idOut[0] = id; 1721 } 1722 1723 private void buzzBeepBlinkLocked(NotificationRecord record) { 1724 boolean buzzBeepBlinked = false; 1725 final Notification notification = record.sbn.getNotification(); 1726 1727 // Should this notification make noise, vibe, or use the LED? 1728 final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) && 1729 !record.isIntercepted(); 1730 if (DBG || record.isIntercepted()) 1731 Slog.v(TAG, 1732 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt + 1733 " intercept=" + record.isIntercepted() 1734 ); 1735 1736 final int currentUser; 1737 final long token = Binder.clearCallingIdentity(); 1738 try { 1739 currentUser = ActivityManager.getCurrentUser(); 1740 } finally { 1741 Binder.restoreCallingIdentity(token); 1742 } 1743 1744 // If we're not supposed to beep, vibrate, etc. then don't. 1745 if (!disableNotificationEffects() 1746 && (!(record.isUpdate 1747 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1748 && (record.getUserId() == UserHandle.USER_ALL || 1749 record.getUserId() == currentUser || 1750 mUserProfiles.isCurrentProfile(record.getUserId())) 1751 && canInterrupt 1752 && mSystemReady 1753 && mAudioManager != null) { 1754 if (DBG) Slog.v(TAG, "Interrupting!"); 1755 1756 sendAccessibilityEvent(notification, record.sbn.getPackageName()); 1757 1758 // sound 1759 1760 // should we use the default notification sound? (indicated either by 1761 // DEFAULT_SOUND or because notification.sound is pointing at 1762 // Settings.System.NOTIFICATION_SOUND) 1763 final boolean useDefaultSound = 1764 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1765 Settings.System.DEFAULT_NOTIFICATION_URI 1766 .equals(notification.sound); 1767 1768 Uri soundUri = null; 1769 boolean hasValidSound = false; 1770 1771 if (useDefaultSound) { 1772 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1773 1774 // check to see if the default notification sound is silent 1775 ContentResolver resolver = getContext().getContentResolver(); 1776 hasValidSound = Settings.System.getString(resolver, 1777 Settings.System.NOTIFICATION_SOUND) != null; 1778 } else if (notification.sound != null) { 1779 soundUri = notification.sound; 1780 hasValidSound = (soundUri != null); 1781 } 1782 1783 if (hasValidSound) { 1784 boolean looping = 1785 (notification.flags & Notification.FLAG_INSISTENT) != 0; 1786 AudioAttributes audioAttributes; 1787 if (notification.audioAttributes != null) { 1788 audioAttributes = notification.audioAttributes; 1789 } else { 1790 audioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT; 1791 } 1792 mSoundNotification = record; 1793 // do not play notifications if stream volume is 0 (typically because 1794 // ringer mode is silent) or if there is a user of exclusive audio focus 1795 if ((mAudioManager.getStreamVolume( 1796 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0) 1797 && !mAudioManager.isAudioFocusExclusive()) { 1798 final long identity = Binder.clearCallingIdentity(); 1799 try { 1800 final IRingtonePlayer player = 1801 mAudioManager.getRingtonePlayer(); 1802 if (player != null) { 1803 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 1804 + " with attributes " + audioAttributes); 1805 player.playAsync(soundUri, record.sbn.getUser(), looping, 1806 audioAttributes); 1807 buzzBeepBlinked = true; 1808 } 1809 } catch (RemoteException e) { 1810 } finally { 1811 Binder.restoreCallingIdentity(identity); 1812 } 1813 } 1814 } 1815 1816 // vibrate 1817 // Does the notification want to specify its own vibration? 1818 final boolean hasCustomVibrate = notification.vibrate != null; 1819 1820 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1821 // mode, and no other vibration is specified, we fall back to vibration 1822 final boolean convertSoundToVibration = 1823 !hasCustomVibrate 1824 && hasValidSound 1825 && (mAudioManager.getRingerMode() 1826 == AudioManager.RINGER_MODE_VIBRATE); 1827 1828 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1829 final boolean useDefaultVibrate = 1830 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1831 1832 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1833 && !(mAudioManager.getRingerMode() 1834 == AudioManager.RINGER_MODE_SILENT)) { 1835 mVibrateNotification = record; 1836 1837 if (useDefaultVibrate || convertSoundToVibration) { 1838 // Escalate privileges so we can use the vibrator even if the 1839 // notifying app does not have the VIBRATE permission. 1840 long identity = Binder.clearCallingIdentity(); 1841 try { 1842 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 1843 useDefaultVibrate ? mDefaultVibrationPattern 1844 : mFallbackVibrationPattern, 1845 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1846 ? 0: -1, audioAttributesForNotification(notification)); 1847 buzzBeepBlinked = true; 1848 } finally { 1849 Binder.restoreCallingIdentity(identity); 1850 } 1851 } else if (notification.vibrate.length > 1) { 1852 // If you want your own vibration pattern, you need the VIBRATE 1853 // permission 1854 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 1855 notification.vibrate, 1856 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1857 ? 0: -1, audioAttributesForNotification(notification)); 1858 buzzBeepBlinked = true; 1859 } 1860 } 1861 } 1862 1863 // light 1864 // release the light 1865 boolean wasShowLights = mLights.remove(record.getKey()); 1866 if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) { 1867 mLedNotification = null; 1868 } 1869 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) { 1870 mLights.add(record.getKey()); 1871 updateLightsLocked(); 1872 if (mUseAttentionLight) { 1873 mAttentionLight.pulse(); 1874 } 1875 buzzBeepBlinked = true; 1876 } else if (wasShowLights) { 1877 updateLightsLocked(); 1878 } 1879 if (buzzBeepBlinked) { 1880 mHandler.post(mBuzzBeepBlinked); 1881 } 1882 } 1883 1884 private static AudioAttributes audioAttributesForNotification(Notification n) { 1885 if (n.audioAttributes != null 1886 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) { 1887 return n.audioAttributes; 1888 } 1889 return new AudioAttributes.Builder() 1890 .setLegacyStreamType(n.audioStreamType) 1891 .setUsage(AudioAttributes.usageForLegacyStreamType(n.audioStreamType)) 1892 .build(); 1893 } 1894 1895 void showNextToastLocked() { 1896 ToastRecord record = mToastQueue.get(0); 1897 while (record != null) { 1898 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1899 try { 1900 record.callback.show(); 1901 scheduleTimeoutLocked(record); 1902 return; 1903 } catch (RemoteException e) { 1904 Slog.w(TAG, "Object died trying to show notification " + record.callback 1905 + " in package " + record.pkg); 1906 // remove it from the list and let the process die 1907 int index = mToastQueue.indexOf(record); 1908 if (index >= 0) { 1909 mToastQueue.remove(index); 1910 } 1911 keepProcessAliveLocked(record.pid); 1912 if (mToastQueue.size() > 0) { 1913 record = mToastQueue.get(0); 1914 } else { 1915 record = null; 1916 } 1917 } 1918 } 1919 } 1920 1921 void cancelToastLocked(int index) { 1922 ToastRecord record = mToastQueue.get(index); 1923 try { 1924 record.callback.hide(); 1925 } catch (RemoteException e) { 1926 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1927 + " in package " + record.pkg); 1928 // don't worry about this, we're about to remove it from 1929 // the list anyway 1930 } 1931 mToastQueue.remove(index); 1932 keepProcessAliveLocked(record.pid); 1933 if (mToastQueue.size() > 0) { 1934 // Show the next one. If the callback fails, this will remove 1935 // it from the list, so don't assume that the list hasn't changed 1936 // after this point. 1937 showNextToastLocked(); 1938 } 1939 } 1940 1941 private void scheduleTimeoutLocked(ToastRecord r) 1942 { 1943 mHandler.removeCallbacksAndMessages(r); 1944 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1945 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 1946 mHandler.sendMessageDelayed(m, delay); 1947 } 1948 1949 private void handleTimeout(ToastRecord record) 1950 { 1951 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1952 synchronized (mToastQueue) { 1953 int index = indexOfToastLocked(record.pkg, record.callback); 1954 if (index >= 0) { 1955 cancelToastLocked(index); 1956 } 1957 } 1958 } 1959 1960 // lock on mToastQueue 1961 int indexOfToastLocked(String pkg, ITransientNotification callback) 1962 { 1963 IBinder cbak = callback.asBinder(); 1964 ArrayList<ToastRecord> list = mToastQueue; 1965 int len = list.size(); 1966 for (int i=0; i<len; i++) { 1967 ToastRecord r = list.get(i); 1968 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1969 return i; 1970 } 1971 } 1972 return -1; 1973 } 1974 1975 // lock on mToastQueue 1976 void keepProcessAliveLocked(int pid) 1977 { 1978 int toastCount = 0; // toasts from this pid 1979 ArrayList<ToastRecord> list = mToastQueue; 1980 int N = list.size(); 1981 for (int i=0; i<N; i++) { 1982 ToastRecord r = list.get(i); 1983 if (r.pid == pid) { 1984 toastCount++; 1985 } 1986 } 1987 try { 1988 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 1989 } catch (RemoteException e) { 1990 // Shouldn't happen. 1991 } 1992 } 1993 1994 private void handleRankingReconsideration(Message message) { 1995 if (!(message.obj instanceof RankingReconsideration)) return; 1996 RankingReconsideration recon = (RankingReconsideration) message.obj; 1997 recon.run(); 1998 boolean changed; 1999 synchronized (mNotificationList) { 2000 final NotificationRecord record = mNotificationsByKey.get(recon.getKey()); 2001 if (record == null) { 2002 return; 2003 } 2004 int indexBefore = findNotificationRecordIndexLocked(record); 2005 boolean interceptBefore = record.isIntercepted(); 2006 recon.applyChangesLocked(record); 2007 applyZenModeLocked(record); 2008 mRankingHelper.sort(mNotificationList); 2009 int indexAfter = findNotificationRecordIndexLocked(record); 2010 boolean interceptAfter = record.isIntercepted(); 2011 changed = indexBefore != indexAfter || interceptBefore != interceptAfter; 2012 if (interceptBefore && !interceptAfter) { 2013 buzzBeepBlinkLocked(record); 2014 } 2015 } 2016 if (changed) { 2017 scheduleSendRankingUpdate(); 2018 } 2019 } 2020 2021 private void handleRankingConfigChange() { 2022 synchronized (mNotificationList) { 2023 final int N = mNotificationList.size(); 2024 ArrayList<String> orderBefore = new ArrayList<String>(N); 2025 for (int i = 0; i < N; i++) { 2026 final NotificationRecord r = mNotificationList.get(i); 2027 orderBefore.add(r.getKey()); 2028 mRankingHelper.extractSignals(r); 2029 } 2030 mRankingHelper.sort(mNotificationList); 2031 for (int i = 0; i < N; i++) { 2032 if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) { 2033 scheduleSendRankingUpdate(); 2034 return; 2035 } 2036 } 2037 } 2038 } 2039 2040 // let zen mode evaluate this record 2041 private void applyZenModeLocked(NotificationRecord record) { 2042 record.setIntercepted(mZenModeHelper.shouldIntercept(record)); 2043 } 2044 2045 // lock on mNotificationList 2046 private int findNotificationRecordIndexLocked(NotificationRecord target) { 2047 return mRankingHelper.indexOf(mNotificationList, target); 2048 } 2049 2050 private void scheduleSendRankingUpdate() { 2051 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 2052 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 2053 mHandler.sendMessage(m); 2054 } 2055 2056 private void handleSendRankingUpdate() { 2057 synchronized (mNotificationList) { 2058 mListeners.notifyRankingUpdateLocked(); 2059 } 2060 } 2061 2062 private void scheduleListenerHintsChanged(int state) { 2063 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED); 2064 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget(); 2065 } 2066 2067 private void handleListenerHintsChanged(int hints) { 2068 synchronized (mNotificationList) { 2069 mListeners.notifyListenerHintsChangedLocked(hints); 2070 } 2071 } 2072 2073 private final class WorkerHandler extends Handler 2074 { 2075 @Override 2076 public void handleMessage(Message msg) 2077 { 2078 switch (msg.what) 2079 { 2080 case MESSAGE_TIMEOUT: 2081 handleTimeout((ToastRecord)msg.obj); 2082 break; 2083 case MESSAGE_SAVE_POLICY_FILE: 2084 handleSavePolicyFile(); 2085 break; 2086 case MESSAGE_SEND_RANKING_UPDATE: 2087 handleSendRankingUpdate(); 2088 break; 2089 case MESSAGE_LISTENER_HINTS_CHANGED: 2090 handleListenerHintsChanged(msg.arg1); 2091 break; 2092 } 2093 } 2094 2095 } 2096 2097 private final class RankingWorkerHandler extends Handler 2098 { 2099 public RankingWorkerHandler(Looper looper) { 2100 super(looper); 2101 } 2102 2103 @Override 2104 public void handleMessage(Message msg) { 2105 switch (msg.what) { 2106 case MESSAGE_RECONSIDER_RANKING: 2107 handleRankingReconsideration(msg); 2108 break; 2109 case MESSAGE_RANKING_CONFIG_CHANGE: 2110 handleRankingConfigChange(); 2111 break; 2112 } 2113 } 2114 } 2115 2116 // Notifications 2117 // ============================================================================ 2118 static int clamp(int x, int low, int high) { 2119 return (x < low) ? low : ((x > high) ? high : x); 2120 } 2121 2122 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 2123 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 2124 if (!manager.isEnabled()) { 2125 return; 2126 } 2127 2128 AccessibilityEvent event = 2129 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 2130 event.setPackageName(packageName); 2131 event.setClassName(Notification.class.getName()); 2132 event.setParcelableData(notification); 2133 CharSequence tickerText = notification.tickerText; 2134 if (!TextUtils.isEmpty(tickerText)) { 2135 event.getText().add(tickerText); 2136 } 2137 2138 manager.sendAccessibilityEvent(event); 2139 } 2140 2141 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 2142 // tell the app 2143 if (sendDelete) { 2144 if (r.getNotification().deleteIntent != null) { 2145 try { 2146 r.getNotification().deleteIntent.send(); 2147 } catch (PendingIntent.CanceledException ex) { 2148 // do nothing - there's no relevant way to recover, and 2149 // no reason to let this propagate 2150 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 2151 } 2152 } 2153 } 2154 2155 // status bar 2156 if (r.getNotification().icon != 0) { 2157 r.isCanceled = true; 2158 mListeners.notifyRemovedLocked(r.sbn); 2159 } 2160 2161 // sound 2162 if (mSoundNotification == r) { 2163 mSoundNotification = null; 2164 final long identity = Binder.clearCallingIdentity(); 2165 try { 2166 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2167 if (player != null) { 2168 player.stopAsync(); 2169 } 2170 } catch (RemoteException e) { 2171 } finally { 2172 Binder.restoreCallingIdentity(identity); 2173 } 2174 } 2175 2176 // vibrate 2177 if (mVibrateNotification == r) { 2178 mVibrateNotification = null; 2179 long identity = Binder.clearCallingIdentity(); 2180 try { 2181 mVibrator.cancel(); 2182 } 2183 finally { 2184 Binder.restoreCallingIdentity(identity); 2185 } 2186 } 2187 2188 // light 2189 mLights.remove(r.getKey()); 2190 if (mLedNotification == r) { 2191 mLedNotification = null; 2192 } 2193 2194 // Record usage stats 2195 switch (reason) { 2196 case REASON_DELEGATE_CANCEL: 2197 case REASON_DELEGATE_CANCEL_ALL: 2198 case REASON_LISTENER_CANCEL: 2199 case REASON_LISTENER_CANCEL_ALL: 2200 mUsageStats.registerDismissedByUser(r); 2201 break; 2202 case REASON_NOMAN_CANCEL: 2203 case REASON_NOMAN_CANCEL_ALL: 2204 mUsageStats.registerRemovedByApp(r); 2205 break; 2206 case REASON_DELEGATE_CLICK: 2207 mUsageStats.registerCancelDueToClick(r); 2208 break; 2209 default: 2210 mUsageStats.registerCancelUnknown(r); 2211 break; 2212 } 2213 2214 mNotificationsByKey.remove(r.sbn.getKey()); 2215 2216 // Save it for users of getHistoricalNotifications() 2217 mArchive.record(r.sbn); 2218 } 2219 2220 /** 2221 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2222 * and none of the {@code mustNotHaveFlags}. 2223 */ 2224 void cancelNotification(final int callingUid, final int callingPid, 2225 final String pkg, final String tag, final int id, 2226 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2227 final int userId, final int reason, final ManagedServiceInfo listener) { 2228 // In enqueueNotificationInternal notifications are added by scheduling the 2229 // work on the worker handler. Hence, we also schedule the cancel on this 2230 // handler to avoid a scenario where an add notification call followed by a 2231 // remove notification call ends up in not removing the notification. 2232 mHandler.post(new Runnable() { 2233 @Override 2234 public void run() { 2235 String listenerName = listener == null ? null : listener.component.toShortString(); 2236 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2237 mustHaveFlags, mustNotHaveFlags, reason, listenerName); 2238 2239 synchronized (mNotificationList) { 2240 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2241 if (index >= 0) { 2242 NotificationRecord r = mNotificationList.get(index); 2243 2244 // Ideally we'd do this in the caller of this method. However, that would 2245 // require the caller to also find the notification. 2246 if (reason == REASON_DELEGATE_CLICK) { 2247 mUsageStats.registerClickedByUser(r); 2248 } 2249 2250 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2251 return; 2252 } 2253 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2254 return; 2255 } 2256 2257 mNotificationList.remove(index); 2258 2259 cancelNotificationLocked(r, sendDelete, reason); 2260 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName); 2261 updateLightsLocked(); 2262 } 2263 } 2264 } 2265 }); 2266 } 2267 2268 /** 2269 * Determine whether the userId applies to the notification in question, either because 2270 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2271 */ 2272 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2273 return 2274 // looking for USER_ALL notifications? match everything 2275 userId == UserHandle.USER_ALL 2276 // a notification sent to USER_ALL matches any query 2277 || r.getUserId() == UserHandle.USER_ALL 2278 // an exact user match 2279 || r.getUserId() == userId; 2280 } 2281 2282 /** 2283 * Determine whether the userId applies to the notification in question, either because 2284 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2285 * because it matches one of the users profiles. 2286 */ 2287 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2288 return notificationMatchesUserId(r, userId) 2289 || mUserProfiles.isCurrentProfile(r.getUserId()); 2290 } 2291 2292 /** 2293 * Cancels all notifications from a given package that have all of the 2294 * {@code mustHaveFlags}. 2295 */ 2296 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2297 int mustNotHaveFlags, boolean doit, int userId, int reason, 2298 ManagedServiceInfo listener) { 2299 String listenerName = listener == null ? null : listener.component.toShortString(); 2300 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2301 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2302 listenerName); 2303 2304 synchronized (mNotificationList) { 2305 final int N = mNotificationList.size(); 2306 ArrayList<NotificationRecord> canceledNotifications = null; 2307 for (int i = N-1; i >= 0; --i) { 2308 NotificationRecord r = mNotificationList.get(i); 2309 if (!notificationMatchesUserId(r, userId)) { 2310 continue; 2311 } 2312 // Don't remove notifications to all, if there's no package name specified 2313 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2314 continue; 2315 } 2316 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2317 continue; 2318 } 2319 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2320 continue; 2321 } 2322 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2323 continue; 2324 } 2325 if (canceledNotifications == null) { 2326 canceledNotifications = new ArrayList<>(); 2327 } 2328 canceledNotifications.add(r); 2329 if (!doit) { 2330 return true; 2331 } 2332 mNotificationList.remove(i); 2333 cancelNotificationLocked(r, false, reason); 2334 } 2335 if (doit && canceledNotifications != null) { 2336 final int M = canceledNotifications.size(); 2337 for (int i = 0; i < M; i++) { 2338 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2339 listenerName); 2340 } 2341 } 2342 if (canceledNotifications != null) { 2343 updateLightsLocked(); 2344 } 2345 return canceledNotifications != null; 2346 } 2347 } 2348 2349 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2350 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2351 String listenerName = listener == null ? null : listener.component.toShortString(); 2352 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2353 null, userId, 0, 0, reason, listenerName); 2354 2355 ArrayList<NotificationRecord> canceledNotifications = null; 2356 final int N = mNotificationList.size(); 2357 for (int i=N-1; i>=0; i--) { 2358 NotificationRecord r = mNotificationList.get(i); 2359 if (includeCurrentProfiles) { 2360 if (!notificationMatchesCurrentProfiles(r, userId)) { 2361 continue; 2362 } 2363 } else { 2364 if (!notificationMatchesUserId(r, userId)) { 2365 continue; 2366 } 2367 } 2368 2369 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2370 | Notification.FLAG_NO_CLEAR)) == 0) { 2371 mNotificationList.remove(i); 2372 cancelNotificationLocked(r, true, reason); 2373 // Make a note so we can cancel children later. 2374 if (canceledNotifications == null) { 2375 canceledNotifications = new ArrayList<>(); 2376 } 2377 canceledNotifications.add(r); 2378 } 2379 } 2380 int M = canceledNotifications != null ? canceledNotifications.size() : 0; 2381 for (int i = 0; i < M; i++) { 2382 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2383 listenerName); 2384 } 2385 updateLightsLocked(); 2386 } 2387 2388 // Warning: The caller is responsible for invoking updateLightsLocked(). 2389 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid, 2390 String listenerName) { 2391 Notification n = r.getNotification(); 2392 if (!n.isGroupSummary()) { 2393 return; 2394 } 2395 2396 String pkg = r.sbn.getPackageName(); 2397 int userId = r.getUserId(); 2398 2399 if (pkg == null) { 2400 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey()); 2401 return; 2402 } 2403 2404 final int N = mNotificationList.size(); 2405 for (int i = N - 1; i >= 0; i--) { 2406 NotificationRecord childR = mNotificationList.get(i); 2407 Notification childN = childR.getNotification(); 2408 StatusBarNotification childSbn = childR.sbn; 2409 if (childR.getUserId() == userId && pkg.equals(childSbn.getPackageName()) && 2410 n.getGroup().equals(childN.getGroup())) { 2411 EventLogTags.writeNotificationCancel(callingUid, callingPid, 2412 pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0, 2413 REASON_GROUP_SUMMARY_CANCELED, listenerName); 2414 mNotificationList.remove(i); 2415 cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED); 2416 } 2417 } 2418 } 2419 2420 // lock on mNotificationList 2421 void updateLightsLocked() 2422 { 2423 // handle notification lights 2424 if (mLedNotification == null) { 2425 // get next notification, if any 2426 int n = mLights.size(); 2427 if (n > 0) { 2428 mLedNotification = mNotificationsByKey.get(mLights.get(n-1)); 2429 } 2430 } 2431 2432 // Don't flash while we are in a call or screen is on 2433 if (mLedNotification == null || mInCall || mScreenOn) { 2434 mNotificationLight.turnOff(); 2435 mStatusBar.notificationLightOff(); 2436 } else { 2437 final Notification ledno = mLedNotification.sbn.getNotification(); 2438 int ledARGB = ledno.ledARGB; 2439 int ledOnMS = ledno.ledOnMS; 2440 int ledOffMS = ledno.ledOffMS; 2441 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2442 ledARGB = mDefaultNotificationColor; 2443 ledOnMS = mDefaultNotificationLedOn; 2444 ledOffMS = mDefaultNotificationLedOff; 2445 } 2446 if (mNotificationPulseEnabled) { 2447 // pulse repeatedly 2448 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2449 ledOnMS, ledOffMS); 2450 } 2451 // let SystemUI make an independent decision 2452 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS); 2453 } 2454 } 2455 2456 // lock on mNotificationList 2457 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2458 { 2459 ArrayList<NotificationRecord> list = mNotificationList; 2460 final int len = list.size(); 2461 for (int i=0; i<len; i++) { 2462 NotificationRecord r = list.get(i); 2463 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2464 continue; 2465 } 2466 if (tag == null) { 2467 if (r.sbn.getTag() != null) { 2468 continue; 2469 } 2470 } else { 2471 if (!tag.equals(r.sbn.getTag())) { 2472 continue; 2473 } 2474 } 2475 if (r.sbn.getPackageName().equals(pkg)) { 2476 return i; 2477 } 2478 } 2479 return -1; 2480 } 2481 2482 // lock on mNotificationList 2483 int indexOfNotificationLocked(String key) { 2484 final int N = mNotificationList.size(); 2485 for (int i = 0; i < N; i++) { 2486 if (key.equals(mNotificationList.get(i).getKey())) { 2487 return i; 2488 } 2489 } 2490 return -1; 2491 } 2492 2493 private void updateNotificationPulse() { 2494 synchronized (mNotificationList) { 2495 updateLightsLocked(); 2496 } 2497 } 2498 2499 private static boolean isUidSystem(int uid) { 2500 final int appid = UserHandle.getAppId(uid); 2501 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2502 } 2503 2504 private static boolean isCallerSystem() { 2505 return isUidSystem(Binder.getCallingUid()); 2506 } 2507 2508 private static void checkCallerIsSystem() { 2509 if (isCallerSystem()) { 2510 return; 2511 } 2512 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2513 } 2514 2515 private static void checkCallerIsSystemOrSameApp(String pkg) { 2516 if (isCallerSystem()) { 2517 return; 2518 } 2519 final int uid = Binder.getCallingUid(); 2520 try { 2521 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2522 pkg, 0, UserHandle.getCallingUserId()); 2523 if (ai == null) { 2524 throw new SecurityException("Unknown package " + pkg); 2525 } 2526 if (!UserHandle.isSameApp(ai.uid, uid)) { 2527 throw new SecurityException("Calling uid " + uid + " gave package" 2528 + pkg + " which is owned by uid " + ai.uid); 2529 } 2530 } catch (RemoteException re) { 2531 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2532 } 2533 } 2534 2535 /** 2536 * Generates a NotificationRankingUpdate from 'sbns', considering only 2537 * notifications visible to the given listener. 2538 * 2539 * <p>Caller must hold a lock on mNotificationList.</p> 2540 */ 2541 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { 2542 int speedBumpIndex = -1; 2543 final int N = mNotificationList.size(); 2544 ArrayList<String> keys = new ArrayList<String>(N); 2545 ArrayList<String> interceptedKeys = new ArrayList<String>(N); 2546 for (int i = 0; i < N; i++) { 2547 NotificationRecord record = mNotificationList.get(i); 2548 if (!isVisibleToListener(record.sbn, info)) { 2549 continue; 2550 } 2551 keys.add(record.sbn.getKey()); 2552 if (record.isIntercepted()) { 2553 interceptedKeys.add(record.sbn.getKey()); 2554 } 2555 if (speedBumpIndex == -1 && 2556 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) { 2557 speedBumpIndex = keys.size() - 1; 2558 } 2559 } 2560 String[] keysAr = keys.toArray(new String[keys.size()]); 2561 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]); 2562 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, speedBumpIndex); 2563 } 2564 2565 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { 2566 if (!listener.enabledAndUserMatches(sbn.getUserId())) { 2567 return false; 2568 } 2569 // TODO: remove this for older listeners. 2570 return true; 2571 } 2572 2573 public class NotificationListeners extends ManagedServices { 2574 2575 public NotificationListeners() { 2576 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2577 } 2578 2579 @Override 2580 protected Config getConfig() { 2581 Config c = new Config(); 2582 c.caption = "notification listener"; 2583 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2584 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2585 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2586 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2587 c.clientLabel = R.string.notification_listener_binding_label; 2588 return c; 2589 } 2590 2591 @Override 2592 protected IInterface asInterface(IBinder binder) { 2593 return INotificationListener.Stub.asInterface(binder); 2594 } 2595 2596 @Override 2597 public void onServiceAdded(ManagedServiceInfo info) { 2598 final INotificationListener listener = (INotificationListener) info.service; 2599 final NotificationRankingUpdate update; 2600 synchronized (mNotificationList) { 2601 update = makeRankingUpdateLocked(info); 2602 } 2603 try { 2604 listener.onListenerConnected(update); 2605 } catch (RemoteException e) { 2606 // we tried 2607 } 2608 } 2609 2610 @Override 2611 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 2612 if (mListenersDisablingEffects.remove(removed)) { 2613 updateListenerHintsLocked(); 2614 } 2615 } 2616 2617 /** 2618 * asynchronously notify all listeners about a new notification 2619 * 2620 * <p> 2621 * Also takes care of removing a notification that has been visible to a listener before, 2622 * but isn't anymore. 2623 */ 2624 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) { 2625 // make a copy in case changes are made to the underlying Notification object 2626 final StatusBarNotification sbnClone = sbn.clone(); 2627 for (final ManagedServiceInfo info : mServices) { 2628 boolean sbnVisible = isVisibleToListener(sbn, info); 2629 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false; 2630 // This notification hasn't been and still isn't visible -> ignore. 2631 if (!oldSbnVisible && !sbnVisible) { 2632 continue; 2633 } 2634 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2635 2636 // This notification became invisible -> remove the old one. 2637 if (oldSbnVisible && !sbnVisible) { 2638 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight(); 2639 mHandler.post(new Runnable() { 2640 @Override 2641 public void run() { 2642 notifyRemoved(info, oldSbnLightClone, update); 2643 } 2644 }); 2645 continue; 2646 } 2647 2648 mHandler.post(new Runnable() { 2649 @Override 2650 public void run() { 2651 notifyPosted(info, sbnClone, update); 2652 } 2653 }); 2654 } 2655 } 2656 2657 /** 2658 * asynchronously notify all listeners about a removed notification 2659 */ 2660 public void notifyRemovedLocked(StatusBarNotification sbn) { 2661 // make a copy in case changes are made to the underlying Notification object 2662 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 2663 // notification 2664 final StatusBarNotification sbnLight = sbn.cloneLight(); 2665 for (final ManagedServiceInfo info : mServices) { 2666 if (!isVisibleToListener(sbn, info)) { 2667 continue; 2668 } 2669 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2670 mHandler.post(new Runnable() { 2671 @Override 2672 public void run() { 2673 notifyRemoved(info, sbnLight, update); 2674 } 2675 }); 2676 } 2677 } 2678 2679 /** 2680 * asynchronously notify all listeners about a reordering of notifications 2681 */ 2682 public void notifyRankingUpdateLocked() { 2683 for (final ManagedServiceInfo serviceInfo : mServices) { 2684 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2685 continue; 2686 } 2687 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo); 2688 mHandler.post(new Runnable() { 2689 @Override 2690 public void run() { 2691 notifyRankingUpdate(serviceInfo, update); 2692 } 2693 }); 2694 } 2695 } 2696 2697 public void notifyListenerHintsChangedLocked(final int hints) { 2698 for (final ManagedServiceInfo serviceInfo : mServices) { 2699 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2700 continue; 2701 } 2702 mHandler.post(new Runnable() { 2703 @Override 2704 public void run() { 2705 notifyListenerHintsChanged(serviceInfo, hints); 2706 } 2707 }); 2708 } 2709 } 2710 2711 private void notifyPosted(final ManagedServiceInfo info, 2712 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 2713 final INotificationListener listener = (INotificationListener)info.service; 2714 try { 2715 listener.onNotificationPosted(sbn, rankingUpdate); 2716 } catch (RemoteException ex) { 2717 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 2718 } 2719 } 2720 2721 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn, 2722 NotificationRankingUpdate rankingUpdate) { 2723 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2724 return; 2725 } 2726 final INotificationListener listener = (INotificationListener) info.service; 2727 try { 2728 listener.onNotificationRemoved(sbn, rankingUpdate); 2729 } catch (RemoteException ex) { 2730 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 2731 } 2732 } 2733 2734 private void notifyRankingUpdate(ManagedServiceInfo info, 2735 NotificationRankingUpdate rankingUpdate) { 2736 final INotificationListener listener = (INotificationListener) info.service; 2737 try { 2738 listener.onNotificationRankingUpdate(rankingUpdate); 2739 } catch (RemoteException ex) { 2740 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 2741 } 2742 } 2743 2744 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) { 2745 final INotificationListener listener = (INotificationListener) info.service; 2746 try { 2747 listener.onListenerHintsChanged(hints); 2748 } catch (RemoteException ex) { 2749 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex); 2750 } 2751 } 2752 2753 private boolean isListenerPackage(String packageName) { 2754 if (packageName == null) { 2755 return false; 2756 } 2757 // TODO: clean up locking object later 2758 synchronized (mNotificationList) { 2759 for (final ManagedServiceInfo serviceInfo : mServices) { 2760 if (packageName.equals(serviceInfo.component.getPackageName())) { 2761 return true; 2762 } 2763 } 2764 } 2765 return false; 2766 } 2767 } 2768 2769 public static final class DumpFilter { 2770 public String pkgFilter; 2771 public boolean zen; 2772 2773 public static DumpFilter parseFromArguments(String[] args) { 2774 if (args != null && args.length == 2 && "p".equals(args[0]) 2775 && args[1] != null && !args[1].trim().isEmpty()) { 2776 final DumpFilter filter = new DumpFilter(); 2777 filter.pkgFilter = args[1].trim().toLowerCase(); 2778 return filter; 2779 } 2780 if (args != null && args.length == 1 && "zen".equals(args[0])) { 2781 final DumpFilter filter = new DumpFilter(); 2782 filter.zen = true; 2783 return filter; 2784 } 2785 return null; 2786 } 2787 2788 public boolean matches(StatusBarNotification sbn) { 2789 return zen ? true : sbn != null 2790 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg())); 2791 } 2792 2793 public boolean matches(ComponentName component) { 2794 return zen ? true : component != null && matches(component.getPackageName()); 2795 } 2796 2797 public boolean matches(String pkg) { 2798 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter); 2799 } 2800 2801 @Override 2802 public String toString() { 2803 return zen ? "zen" : ('\'' + pkgFilter + '\''); 2804 } 2805 } 2806} 2807