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