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