NotificationManagerService.java revision 333a61c3a5a83fe9c50ebeb5c947317f61385b7b
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<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); 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 checkCallerIsSystem(); 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 1482 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] 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); 1489 if (!mSignalExtractors.isEmpty()) { 1490 for (NotificationSignalExtractor extractor : mSignalExtractors) { 1491 try { 1492 RankingReconsideration recon = extractor.process(r); 1493 scheduleRankingReconsideration(recon); 1494 } catch (Throwable t) { 1495 Slog.w(TAG, "NotificationSignalExtractor failed.", t); 1496 } 1497 } 1498 } 1499 1500 // 3. Apply local rules 1501 1502 // blocked apps 1503 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1504 if (!isSystemNotification) { 1505 score = JUNK_SCORE; 1506 Slog.e(TAG, "Suppressing notification from package " + pkg 1507 + " by user request."); 1508 } 1509 } 1510 1511 if (score < SCORE_DISPLAY_THRESHOLD) { 1512 // Notification will be blocked because the score is too low. 1513 return; 1514 } 1515 1516 synchronized (mNotificationList) { 1517 applyZenModeLocked(r); 1518 1519 // Should this notification make noise, vibe, or use the LED? 1520 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && 1521 !r.isIntercepted(); 1522 if (DBG || r.isIntercepted()) Slog.v(TAG, 1523 "pkg=" + pkg + " canInterrupt=" + canInterrupt + 1524 " intercept=" + r.isIntercepted()); 1525 NotificationRecord old = null; 1526 int index = indexOfNotificationLocked(n.getKey()); 1527 if (index < 0) { 1528 mNotificationList.add(r); 1529 mUsageStats.registerPostedByApp(r); 1530 } else { 1531 old = mNotificationList.get(index); 1532 mNotificationList.set(index, r); 1533 mUsageStats.registerUpdatedByApp(r, old); 1534 // Make sure we don't lose the foreground service state. 1535 notification.flags |= 1536 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1537 // Retain ranking information from previous record 1538 r.copyRankingInformation(old); 1539 mNotificationsByKey.remove(old.sbn.getKey()); 1540 } 1541 mNotificationsByKey.put(n.getKey(), r); 1542 1543 Collections.sort(mNotificationList, mRankingComparator); 1544 1545 // Ensure if this is a foreground service that the proper additional 1546 // flags are set. 1547 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1548 notification.flags |= Notification.FLAG_ONGOING_EVENT 1549 | Notification.FLAG_NO_CLEAR; 1550 } 1551 1552 final int currentUser; 1553 final long token = Binder.clearCallingIdentity(); 1554 try { 1555 currentUser = ActivityManager.getCurrentUser(); 1556 } finally { 1557 Binder.restoreCallingIdentity(token); 1558 } 1559 1560 if (notification.icon != 0) { 1561 if (old != null && !old.isCanceled) { 1562 final long identity = Binder.clearCallingIdentity(); 1563 try { 1564 mStatusBar.updateNotification(n); 1565 } finally { 1566 Binder.restoreCallingIdentity(identity); 1567 } 1568 } else { 1569 final long identity = Binder.clearCallingIdentity(); 1570 try { 1571 mStatusBar.addNotification(n); 1572 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0 1573 && canInterrupt) { 1574 mAttentionLight.pulse(); 1575 } 1576 } finally { 1577 Binder.restoreCallingIdentity(identity); 1578 } 1579 } 1580 // Send accessibility events only for the current user. 1581 if (currentUser == userId) { 1582 sendAccessibilityEvent(notification, pkg); 1583 } 1584 1585 mListeners.notifyPostedLocked(r.sbn); 1586 } else { 1587 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1588 if (old != null && !old.isCanceled) { 1589 final long identity = Binder.clearCallingIdentity(); 1590 try { 1591 mStatusBar.removeNotification(r.getKey()); 1592 } finally { 1593 Binder.restoreCallingIdentity(identity); 1594 } 1595 1596 mListeners.notifyRemovedLocked(r.sbn); 1597 } 1598 // ATTENTION: in a future release we will bail out here 1599 // so that we do not play sounds, show lights, etc. for invalid 1600 // notifications 1601 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1602 + n.getPackageName()); 1603 } 1604 1605 // If we're not supposed to beep, vibrate, etc. then don't. 1606 if (!mDisableNotificationAlerts 1607 && (!(old != null 1608 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1609 && (r.getUserId() == UserHandle.USER_ALL || 1610 (r.getUserId() == userId && r.getUserId() == currentUser) || 1611 mUserProfiles.isCurrentProfile(r.getUserId())) 1612 && canInterrupt 1613 && mSystemReady 1614 && mAudioManager != null) { 1615 if (DBG) Slog.v(TAG, "Interrupting!"); 1616 // sound 1617 1618 // should we use the default notification sound? (indicated either by 1619 // DEFAULT_SOUND or because notification.sound is pointing at 1620 // Settings.System.NOTIFICATION_SOUND) 1621 final boolean useDefaultSound = 1622 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1623 Settings.System.DEFAULT_NOTIFICATION_URI 1624 .equals(notification.sound); 1625 1626 Uri soundUri = null; 1627 boolean hasValidSound = false; 1628 1629 if (useDefaultSound) { 1630 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1631 1632 // check to see if the default notification sound is silent 1633 ContentResolver resolver = getContext().getContentResolver(); 1634 hasValidSound = Settings.System.getString(resolver, 1635 Settings.System.NOTIFICATION_SOUND) != null; 1636 } else if (notification.sound != null) { 1637 soundUri = notification.sound; 1638 hasValidSound = (soundUri != null); 1639 } 1640 1641 if (hasValidSound) { 1642 boolean looping = 1643 (notification.flags & Notification.FLAG_INSISTENT) != 0; 1644 int audioStreamType; 1645 if (notification.audioStreamType >= 0) { 1646 audioStreamType = notification.audioStreamType; 1647 } else { 1648 audioStreamType = DEFAULT_STREAM_TYPE; 1649 } 1650 mSoundNotification = r; 1651 // do not play notifications if stream volume is 0 (typically because 1652 // ringer mode is silent) or if there is a user of exclusive audio focus 1653 if ((mAudioManager.getStreamVolume(audioStreamType) != 0) 1654 && !mAudioManager.isAudioFocusExclusive()) { 1655 final long identity = Binder.clearCallingIdentity(); 1656 try { 1657 final IRingtonePlayer player = 1658 mAudioManager.getRingtonePlayer(); 1659 if (player != null) { 1660 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 1661 + " on stream " + audioStreamType); 1662 player.playAsync(soundUri, user, looping, audioStreamType); 1663 } 1664 } catch (RemoteException e) { 1665 } finally { 1666 Binder.restoreCallingIdentity(identity); 1667 } 1668 } 1669 } 1670 1671 // vibrate 1672 // Does the notification want to specify its own vibration? 1673 final boolean hasCustomVibrate = notification.vibrate != null; 1674 1675 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1676 // mode, and no other vibration is specified, we fall back to vibration 1677 final boolean convertSoundToVibration = 1678 !hasCustomVibrate 1679 && hasValidSound 1680 && (mAudioManager.getRingerMode() 1681 == AudioManager.RINGER_MODE_VIBRATE); 1682 1683 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1684 final boolean useDefaultVibrate = 1685 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1686 1687 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1688 && !(mAudioManager.getRingerMode() 1689 == AudioManager.RINGER_MODE_SILENT)) { 1690 mVibrateNotification = r; 1691 1692 if (useDefaultVibrate || convertSoundToVibration) { 1693 // Escalate privileges so we can use the vibrator even if the 1694 // notifying app does not have the VIBRATE permission. 1695 long identity = Binder.clearCallingIdentity(); 1696 try { 1697 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1698 useDefaultVibrate ? mDefaultVibrationPattern 1699 : mFallbackVibrationPattern, 1700 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1701 ? 0: -1, notification.audioStreamType); 1702 } finally { 1703 Binder.restoreCallingIdentity(identity); 1704 } 1705 } else if (notification.vibrate.length > 1) { 1706 // If you want your own vibration pattern, you need the VIBRATE 1707 // permission 1708 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1709 notification.vibrate, 1710 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1711 ? 0: -1, notification.audioStreamType); 1712 } 1713 } 1714 } 1715 1716 // light 1717 // the most recent thing gets the light 1718 mLights.remove(old); 1719 if (mLedNotification == old) { 1720 mLedNotification = null; 1721 } 1722 //Slog.i(TAG, "notification.lights=" 1723 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) 1724 // != 0)); 1725 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 1726 && canInterrupt) { 1727 mLights.add(r); 1728 updateLightsLocked(); 1729 } else { 1730 if (old != null 1731 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) { 1732 updateLightsLocked(); 1733 } 1734 } 1735 } 1736 } 1737 }); 1738 1739 idOut[0] = id; 1740 } 1741 1742 void showNextToastLocked() { 1743 ToastRecord record = mToastQueue.get(0); 1744 while (record != null) { 1745 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1746 try { 1747 record.callback.show(); 1748 scheduleTimeoutLocked(record); 1749 return; 1750 } catch (RemoteException e) { 1751 Slog.w(TAG, "Object died trying to show notification " + record.callback 1752 + " in package " + record.pkg); 1753 // remove it from the list and let the process die 1754 int index = mToastQueue.indexOf(record); 1755 if (index >= 0) { 1756 mToastQueue.remove(index); 1757 } 1758 keepProcessAliveLocked(record.pid); 1759 if (mToastQueue.size() > 0) { 1760 record = mToastQueue.get(0); 1761 } else { 1762 record = null; 1763 } 1764 } 1765 } 1766 } 1767 1768 void cancelToastLocked(int index) { 1769 ToastRecord record = mToastQueue.get(index); 1770 try { 1771 record.callback.hide(); 1772 } catch (RemoteException e) { 1773 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1774 + " in package " + record.pkg); 1775 // don't worry about this, we're about to remove it from 1776 // the list anyway 1777 } 1778 mToastQueue.remove(index); 1779 keepProcessAliveLocked(record.pid); 1780 if (mToastQueue.size() > 0) { 1781 // Show the next one. If the callback fails, this will remove 1782 // it from the list, so don't assume that the list hasn't changed 1783 // after this point. 1784 showNextToastLocked(); 1785 } 1786 } 1787 1788 private void scheduleTimeoutLocked(ToastRecord r) 1789 { 1790 mHandler.removeCallbacksAndMessages(r); 1791 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1792 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 1793 mHandler.sendMessageDelayed(m, delay); 1794 } 1795 1796 private void handleTimeout(ToastRecord record) 1797 { 1798 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1799 synchronized (mToastQueue) { 1800 int index = indexOfToastLocked(record.pkg, record.callback); 1801 if (index >= 0) { 1802 cancelToastLocked(index); 1803 } 1804 } 1805 } 1806 1807 // lock on mToastQueue 1808 int indexOfToastLocked(String pkg, ITransientNotification callback) 1809 { 1810 IBinder cbak = callback.asBinder(); 1811 ArrayList<ToastRecord> list = mToastQueue; 1812 int len = list.size(); 1813 for (int i=0; i<len; i++) { 1814 ToastRecord r = list.get(i); 1815 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1816 return i; 1817 } 1818 } 1819 return -1; 1820 } 1821 1822 // lock on mToastQueue 1823 void keepProcessAliveLocked(int pid) 1824 { 1825 int toastCount = 0; // toasts from this pid 1826 ArrayList<ToastRecord> list = mToastQueue; 1827 int N = list.size(); 1828 for (int i=0; i<N; i++) { 1829 ToastRecord r = list.get(i); 1830 if (r.pid == pid) { 1831 toastCount++; 1832 } 1833 } 1834 try { 1835 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 1836 } catch (RemoteException e) { 1837 // Shouldn't happen. 1838 } 1839 } 1840 1841 private void scheduleRankingReconsideration(RankingReconsideration recon) { 1842 if (recon != null) { 1843 Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, recon); 1844 long delay = recon.getDelay(TimeUnit.MILLISECONDS); 1845 mRankingHandler.sendMessageDelayed(m, delay); 1846 } 1847 } 1848 1849 private void handleRankingReconsideration(Message message) { 1850 if (!(message.obj instanceof RankingReconsideration)) return; 1851 RankingReconsideration recon = (RankingReconsideration) message.obj; 1852 recon.run(); 1853 boolean changed; 1854 synchronized (mNotificationList) { 1855 final NotificationRecord record = mNotificationsByKey.get(recon.getKey()); 1856 if (record == null) { 1857 return; 1858 } 1859 int indexBefore = findNotificationRecordIndexLocked(record); 1860 boolean interceptBefore = record.isIntercepted(); 1861 recon.applyChangesLocked(record); 1862 applyZenModeLocked(record); 1863 Collections.sort(mNotificationList, mRankingComparator); 1864 int indexAfter = findNotificationRecordIndexLocked(record); 1865 boolean interceptAfter = record.isIntercepted(); 1866 changed = indexBefore != indexAfter || interceptBefore != interceptAfter; 1867 } 1868 if (changed) { 1869 scheduleSendRankingUpdate(); 1870 } 1871 } 1872 1873 // let zen mode evaluate this record and then make note of that for the future 1874 private void applyZenModeLocked(NotificationRecord record) { 1875 record.setIntercepted(mZenModeHelper.shouldIntercept(record, record.wasTouchedByZen())); 1876 record.setTouchedByZen(); 1877 } 1878 1879 // lock on mNotificationList 1880 private int findNotificationRecordIndexLocked(NotificationRecord target) { 1881 return Collections.binarySearch(mNotificationList, target, mRankingComparator); 1882 } 1883 1884 private void scheduleSendRankingUpdate() { 1885 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 1886 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 1887 mHandler.sendMessage(m); 1888 } 1889 1890 private void handleSendRankingUpdate() { 1891 synchronized (mNotificationList) { 1892 mListeners.notifyRankingUpdateLocked(); 1893 } 1894 } 1895 1896 private final class WorkerHandler extends Handler 1897 { 1898 @Override 1899 public void handleMessage(Message msg) 1900 { 1901 switch (msg.what) 1902 { 1903 case MESSAGE_TIMEOUT: 1904 handleTimeout((ToastRecord)msg.obj); 1905 break; 1906 case MESSAGE_SAVE_POLICY_FILE: 1907 handleSavePolicyFile(); 1908 break; 1909 case MESSAGE_SEND_RANKING_UPDATE: 1910 handleSendRankingUpdate(); 1911 break; 1912 } 1913 } 1914 1915 } 1916 1917 private final class RankingWorkerHandler extends Handler 1918 { 1919 public RankingWorkerHandler(Looper looper) { 1920 super(looper); 1921 } 1922 1923 @Override 1924 public void handleMessage(Message msg) { 1925 switch (msg.what) { 1926 case MESSAGE_RECONSIDER_RANKING: 1927 handleRankingReconsideration(msg); 1928 break; 1929 } 1930 } 1931 } 1932 1933 // Notifications 1934 // ============================================================================ 1935 static int clamp(int x, int low, int high) { 1936 return (x < low) ? low : ((x > high) ? high : x); 1937 } 1938 1939 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 1940 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 1941 if (!manager.isEnabled()) { 1942 return; 1943 } 1944 1945 AccessibilityEvent event = 1946 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 1947 event.setPackageName(packageName); 1948 event.setClassName(Notification.class.getName()); 1949 event.setParcelableData(notification); 1950 CharSequence tickerText = notification.tickerText; 1951 if (!TextUtils.isEmpty(tickerText)) { 1952 event.getText().add(tickerText); 1953 } 1954 1955 manager.sendAccessibilityEvent(event); 1956 } 1957 1958 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 1959 // tell the app 1960 if (sendDelete) { 1961 if (r.getNotification().deleteIntent != null) { 1962 try { 1963 r.getNotification().deleteIntent.send(); 1964 } catch (PendingIntent.CanceledException ex) { 1965 // do nothing - there's no relevant way to recover, and 1966 // no reason to let this propagate 1967 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 1968 } 1969 } 1970 } 1971 1972 // status bar 1973 if (r.getNotification().icon != 0) { 1974 final long identity = Binder.clearCallingIdentity(); 1975 try { 1976 mStatusBar.removeNotification(r.getKey()); 1977 } finally { 1978 Binder.restoreCallingIdentity(identity); 1979 } 1980 r.isCanceled = true; 1981 mListeners.notifyRemovedLocked(r.sbn); 1982 } 1983 1984 // sound 1985 if (mSoundNotification == r) { 1986 mSoundNotification = null; 1987 final long identity = Binder.clearCallingIdentity(); 1988 try { 1989 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 1990 if (player != null) { 1991 player.stopAsync(); 1992 } 1993 } catch (RemoteException e) { 1994 } finally { 1995 Binder.restoreCallingIdentity(identity); 1996 } 1997 } 1998 1999 // vibrate 2000 if (mVibrateNotification == r) { 2001 mVibrateNotification = null; 2002 long identity = Binder.clearCallingIdentity(); 2003 try { 2004 mVibrator.cancel(); 2005 } 2006 finally { 2007 Binder.restoreCallingIdentity(identity); 2008 } 2009 } 2010 2011 // light 2012 mLights.remove(r); 2013 if (mLedNotification == r) { 2014 mLedNotification = null; 2015 } 2016 2017 // Record usage stats 2018 switch (reason) { 2019 case REASON_DELEGATE_CANCEL: 2020 case REASON_DELEGATE_CANCEL_ALL: 2021 case REASON_LISTENER_CANCEL: 2022 case REASON_LISTENER_CANCEL_ALL: 2023 mUsageStats.registerDismissedByUser(r); 2024 break; 2025 case REASON_NOMAN_CANCEL: 2026 case REASON_NOMAN_CANCEL_ALL: 2027 mUsageStats.registerRemovedByApp(r); 2028 break; 2029 case REASON_DELEGATE_CLICK: 2030 mUsageStats.registerCancelDueToClick(r); 2031 break; 2032 default: 2033 mUsageStats.registerCancelUnknown(r); 2034 break; 2035 } 2036 2037 // Save it for users of getHistoricalNotifications() 2038 mArchive.record(r.sbn); 2039 } 2040 2041 /** 2042 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2043 * and none of the {@code mustNotHaveFlags}. 2044 */ 2045 void cancelNotification(final int callingUid, final int callingPid, 2046 final String pkg, final String tag, final int id, 2047 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2048 final int userId, final int reason, final ManagedServiceInfo listener) { 2049 // In enqueueNotificationInternal notifications are added by scheduling the 2050 // work on the worker handler. Hence, we also schedule the cancel on this 2051 // handler to avoid a scenario where an add notification call followed by a 2052 // remove notification call ends up in not removing the notification. 2053 mHandler.post(new Runnable() { 2054 @Override 2055 public void run() { 2056 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2057 mustHaveFlags, mustNotHaveFlags, reason, 2058 listener == null ? null : listener.component.toShortString()); 2059 2060 synchronized (mNotificationList) { 2061 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2062 if (index >= 0) { 2063 NotificationRecord r = mNotificationList.get(index); 2064 2065 // Ideally we'd do this in the caller of this method. However, that would 2066 // require the caller to also find the notification. 2067 if (reason == REASON_DELEGATE_CLICK) { 2068 mUsageStats.registerClickedByUser(r); 2069 } 2070 2071 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2072 return; 2073 } 2074 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2075 return; 2076 } 2077 2078 mNotificationList.remove(index); 2079 mNotificationsByKey.remove(r.sbn.getKey()); 2080 2081 cancelNotificationLocked(r, sendDelete, reason); 2082 updateLightsLocked(); 2083 } 2084 } 2085 } 2086 }); 2087 } 2088 2089 /** 2090 * Determine whether the userId applies to the notification in question, either because 2091 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2092 */ 2093 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2094 return 2095 // looking for USER_ALL notifications? match everything 2096 userId == UserHandle.USER_ALL 2097 // a notification sent to USER_ALL matches any query 2098 || r.getUserId() == UserHandle.USER_ALL 2099 // an exact user match 2100 || r.getUserId() == userId; 2101 } 2102 2103 /** 2104 * Determine whether the userId applies to the notification in question, either because 2105 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2106 * because it matches one of the users profiles. 2107 */ 2108 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2109 return notificationMatchesUserId(r, userId) 2110 || mUserProfiles.isCurrentProfile(r.getUserId()); 2111 } 2112 2113 /** 2114 * Cancels all notifications from a given package that have all of the 2115 * {@code mustHaveFlags}. 2116 */ 2117 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2118 int mustNotHaveFlags, boolean doit, int userId, int reason, 2119 ManagedServiceInfo listener) { 2120 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2121 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2122 listener == null ? null : listener.component.toShortString()); 2123 2124 synchronized (mNotificationList) { 2125 final int N = mNotificationList.size(); 2126 boolean canceledSomething = false; 2127 for (int i = N-1; i >= 0; --i) { 2128 NotificationRecord r = mNotificationList.get(i); 2129 if (!notificationMatchesUserId(r, userId)) { 2130 continue; 2131 } 2132 // Don't remove notifications to all, if there's no package name specified 2133 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2134 continue; 2135 } 2136 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2137 continue; 2138 } 2139 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2140 continue; 2141 } 2142 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2143 continue; 2144 } 2145 canceledSomething = true; 2146 if (!doit) { 2147 return true; 2148 } 2149 mNotificationList.remove(i); 2150 mNotificationsByKey.remove(r.sbn.getKey()); 2151 cancelNotificationLocked(r, false, reason); 2152 } 2153 if (canceledSomething) { 2154 updateLightsLocked(); 2155 } 2156 return canceledSomething; 2157 } 2158 } 2159 2160 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2161 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2162 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2163 null, userId, 0, 0, reason, 2164 listener == null ? null : listener.component.toShortString()); 2165 2166 final int N = mNotificationList.size(); 2167 for (int i=N-1; i>=0; i--) { 2168 NotificationRecord r = mNotificationList.get(i); 2169 if (includeCurrentProfiles) { 2170 if (!notificationMatchesCurrentProfiles(r, userId)) { 2171 continue; 2172 } 2173 } else { 2174 if (!notificationMatchesUserId(r, userId)) { 2175 continue; 2176 } 2177 } 2178 2179 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2180 | Notification.FLAG_NO_CLEAR)) == 0) { 2181 mNotificationList.remove(i); 2182 mNotificationsByKey.remove(r.sbn.getKey()); 2183 cancelNotificationLocked(r, true, reason); 2184 } 2185 } 2186 updateLightsLocked(); 2187 } 2188 2189 // lock on mNotificationList 2190 void updateLightsLocked() 2191 { 2192 // handle notification lights 2193 if (mLedNotification == null) { 2194 // get next notification, if any 2195 int n = mLights.size(); 2196 if (n > 0) { 2197 mLedNotification = mLights.get(n-1); 2198 } 2199 } 2200 2201 // Don't flash while we are in a call or screen is on 2202 if (mLedNotification == null || mInCall || mScreenOn) { 2203 mNotificationLight.turnOff(); 2204 } else { 2205 final Notification ledno = mLedNotification.sbn.getNotification(); 2206 int ledARGB = ledno.ledARGB; 2207 int ledOnMS = ledno.ledOnMS; 2208 int ledOffMS = ledno.ledOffMS; 2209 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2210 ledARGB = mDefaultNotificationColor; 2211 ledOnMS = mDefaultNotificationLedOn; 2212 ledOffMS = mDefaultNotificationLedOff; 2213 } 2214 if (mNotificationPulseEnabled) { 2215 // pulse repeatedly 2216 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2217 ledOnMS, ledOffMS); 2218 } 2219 } 2220 } 2221 2222 // lock on mNotificationList 2223 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2224 { 2225 ArrayList<NotificationRecord> list = mNotificationList; 2226 final int len = list.size(); 2227 for (int i=0; i<len; i++) { 2228 NotificationRecord r = list.get(i); 2229 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2230 continue; 2231 } 2232 if (tag == null) { 2233 if (r.sbn.getTag() != null) { 2234 continue; 2235 } 2236 } else { 2237 if (!tag.equals(r.sbn.getTag())) { 2238 continue; 2239 } 2240 } 2241 if (r.sbn.getPackageName().equals(pkg)) { 2242 return i; 2243 } 2244 } 2245 return -1; 2246 } 2247 2248 // lock on mNotificationList 2249 int indexOfNotificationLocked(String key) { 2250 NotificationRecord r = mNotificationsByKey.get(key); 2251 if (r == null) { 2252 return -1; 2253 } 2254 int index = Collections.binarySearch(mNotificationList, r, mRankingComparator); 2255 // Guarantee to return -1 when not found. 2256 return (index >= 0) ? index : -1; 2257 } 2258 2259 2260 private void updateNotificationPulse() { 2261 synchronized (mNotificationList) { 2262 updateLightsLocked(); 2263 } 2264 } 2265 2266 private static boolean isUidSystem(int uid) { 2267 final int appid = UserHandle.getAppId(uid); 2268 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2269 } 2270 2271 private static boolean isCallerSystem() { 2272 return isUidSystem(Binder.getCallingUid()); 2273 } 2274 2275 private static void checkCallerIsSystem() { 2276 if (isCallerSystem()) { 2277 return; 2278 } 2279 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2280 } 2281 2282 private static void checkCallerIsSystemOrSameApp(String pkg) { 2283 if (isCallerSystem()) { 2284 return; 2285 } 2286 final int uid = Binder.getCallingUid(); 2287 try { 2288 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2289 pkg, 0, UserHandle.getCallingUserId()); 2290 if (!UserHandle.isSameApp(ai.uid, uid)) { 2291 throw new SecurityException("Calling uid " + uid + " gave package" 2292 + pkg + " which is owned by uid " + ai.uid); 2293 } 2294 } catch (RemoteException re) { 2295 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2296 } 2297 } 2298 2299 /** 2300 * Generates a NotificationRankingUpdate from 'sbns', considering only 2301 * notifications visible to the given listener. 2302 * 2303 * <p>Caller must hold a lock on mNotificationList.</p> 2304 */ 2305 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { 2306 int speedBumpIndex = -1; 2307 final int N = mNotificationList.size(); 2308 ArrayList<String> keys = new ArrayList<String>(N); 2309 ArrayList<String> dndKeys = new ArrayList<String>(N); 2310 for (int i = 0; i < N; i++) { 2311 NotificationRecord record = mNotificationList.get(i); 2312 if (!info.enabledAndUserMatches(record.sbn.getUserId())) { 2313 continue; 2314 } 2315 keys.add(record.sbn.getKey()); 2316 if (record.isIntercepted()) { 2317 dndKeys.add(record.sbn.getKey()); 2318 } 2319 if (speedBumpIndex == -1 && 2320 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) { 2321 speedBumpIndex = keys.size() - 1; 2322 } 2323 } 2324 String[] keysAr = keys.toArray(new String[keys.size()]); 2325 String[] dndKeysAr = dndKeys.toArray(new String[dndKeys.size()]); 2326 return new NotificationRankingUpdate(keysAr, dndKeysAr, speedBumpIndex); 2327 } 2328 2329 public class NotificationListeners extends ManagedServices { 2330 2331 public NotificationListeners() { 2332 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2333 } 2334 2335 @Override 2336 protected Config getConfig() { 2337 Config c = new Config(); 2338 c.caption = "notification listener"; 2339 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2340 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2341 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2342 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2343 c.clientLabel = R.string.notification_listener_binding_label; 2344 return c; 2345 } 2346 2347 @Override 2348 protected IInterface asInterface(IBinder binder) { 2349 return INotificationListener.Stub.asInterface(binder); 2350 } 2351 2352 @Override 2353 public void onServiceAdded(ManagedServiceInfo info) { 2354 final INotificationListener listener = (INotificationListener) info.service; 2355 final NotificationRankingUpdate update; 2356 synchronized (mNotificationList) { 2357 update = makeRankingUpdateLocked(info); 2358 } 2359 try { 2360 listener.onListenerConnected(update); 2361 } catch (RemoteException e) { 2362 // we tried 2363 } 2364 } 2365 2366 /** 2367 * asynchronously notify all listeners about a new notification 2368 */ 2369 public void notifyPostedLocked(StatusBarNotification sbn) { 2370 // make a copy in case changes are made to the underlying Notification object 2371 final StatusBarNotification sbnClone = sbn.clone(); 2372 for (final ManagedServiceInfo info : mServices) { 2373 if (!info.isEnabledForCurrentProfiles()) { 2374 continue; 2375 } 2376 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2377 if (update.getOrderedKeys().length == 0) { 2378 continue; 2379 } 2380 mHandler.post(new Runnable() { 2381 @Override 2382 public void run() { 2383 notifyPostedIfUserMatch(info, sbnClone, update); 2384 } 2385 }); 2386 } 2387 } 2388 2389 /** 2390 * asynchronously notify all listeners about a removed notification 2391 */ 2392 public void notifyRemovedLocked(StatusBarNotification sbn) { 2393 // make a copy in case changes are made to the underlying Notification object 2394 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 2395 // notification 2396 final StatusBarNotification sbnLight = sbn.cloneLight(); 2397 for (final ManagedServiceInfo info : mServices) { 2398 if (!info.isEnabledForCurrentProfiles()) { 2399 continue; 2400 } 2401 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2402 mHandler.post(new Runnable() { 2403 @Override 2404 public void run() { 2405 notifyRemovedIfUserMatch(info, sbnLight, update); 2406 } 2407 }); 2408 } 2409 } 2410 2411 /** 2412 * asynchronously notify all listeners about a reordering of notifications 2413 */ 2414 public void notifyRankingUpdateLocked() { 2415 for (final ManagedServiceInfo serviceInfo : mServices) { 2416 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2417 continue; 2418 } 2419 final NotificationRankingUpdate update = 2420 makeRankingUpdateLocked(serviceInfo); 2421 mHandler.post(new Runnable() { 2422 @Override 2423 public void run() { 2424 notifyRankingUpdate(serviceInfo, update); 2425 } 2426 }); 2427 } 2428 } 2429 2430 private void notifyPostedIfUserMatch(final ManagedServiceInfo info, 2431 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 2432 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2433 return; 2434 } 2435 final INotificationListener listener = (INotificationListener)info.service; 2436 try { 2437 listener.onNotificationPosted(sbn, rankingUpdate); 2438 } catch (RemoteException ex) { 2439 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 2440 } 2441 } 2442 2443 private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn, 2444 NotificationRankingUpdate rankingUpdate) { 2445 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2446 return; 2447 } 2448 final INotificationListener listener = (INotificationListener) info.service; 2449 try { 2450 listener.onNotificationRemoved(sbn, rankingUpdate); 2451 } catch (RemoteException ex) { 2452 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 2453 } 2454 } 2455 2456 private void notifyRankingUpdate(ManagedServiceInfo info, 2457 NotificationRankingUpdate rankingUpdate) { 2458 final INotificationListener listener = (INotificationListener) info.service; 2459 try { 2460 listener.onNotificationRankingUpdate(rankingUpdate); 2461 } catch (RemoteException ex) { 2462 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 2463 } 2464 } 2465 } 2466} 2467