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