NotificationManagerService.java revision d5092bcb11c28f656eb470bd9b74585287fd75b4
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 1877 private void applyZenModeLocked(NotificationRecord record) { 1878 record.setIntercepted(mZenModeHelper.shouldIntercept(record)); 1879 } 1880 1881 // lock on mNotificationList 1882 private int findNotificationRecordIndexLocked(NotificationRecord target) { 1883 return Collections.binarySearch(mNotificationList, target, mRankingComparator); 1884 } 1885 1886 private void scheduleSendRankingUpdate() { 1887 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 1888 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 1889 mHandler.sendMessage(m); 1890 } 1891 1892 private void handleSendRankingUpdate() { 1893 synchronized (mNotificationList) { 1894 mListeners.notifyRankingUpdateLocked(); 1895 } 1896 } 1897 1898 private final class WorkerHandler extends Handler 1899 { 1900 @Override 1901 public void handleMessage(Message msg) 1902 { 1903 switch (msg.what) 1904 { 1905 case MESSAGE_TIMEOUT: 1906 handleTimeout((ToastRecord)msg.obj); 1907 break; 1908 case MESSAGE_SAVE_POLICY_FILE: 1909 handleSavePolicyFile(); 1910 break; 1911 case MESSAGE_SEND_RANKING_UPDATE: 1912 handleSendRankingUpdate(); 1913 break; 1914 } 1915 } 1916 1917 } 1918 1919 private final class RankingWorkerHandler extends Handler 1920 { 1921 public RankingWorkerHandler(Looper looper) { 1922 super(looper); 1923 } 1924 1925 @Override 1926 public void handleMessage(Message msg) { 1927 switch (msg.what) { 1928 case MESSAGE_RECONSIDER_RANKING: 1929 handleRankingReconsideration(msg); 1930 break; 1931 } 1932 } 1933 } 1934 1935 // Notifications 1936 // ============================================================================ 1937 static int clamp(int x, int low, int high) { 1938 return (x < low) ? low : ((x > high) ? high : x); 1939 } 1940 1941 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 1942 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 1943 if (!manager.isEnabled()) { 1944 return; 1945 } 1946 1947 AccessibilityEvent event = 1948 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 1949 event.setPackageName(packageName); 1950 event.setClassName(Notification.class.getName()); 1951 event.setParcelableData(notification); 1952 CharSequence tickerText = notification.tickerText; 1953 if (!TextUtils.isEmpty(tickerText)) { 1954 event.getText().add(tickerText); 1955 } 1956 1957 manager.sendAccessibilityEvent(event); 1958 } 1959 1960 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 1961 // tell the app 1962 if (sendDelete) { 1963 if (r.getNotification().deleteIntent != null) { 1964 try { 1965 r.getNotification().deleteIntent.send(); 1966 } catch (PendingIntent.CanceledException ex) { 1967 // do nothing - there's no relevant way to recover, and 1968 // no reason to let this propagate 1969 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 1970 } 1971 } 1972 } 1973 1974 // status bar 1975 if (r.getNotification().icon != 0) { 1976 r.isCanceled = true; 1977 mListeners.notifyRemovedLocked(r.sbn); 1978 } 1979 1980 // sound 1981 if (mSoundNotification == r) { 1982 mSoundNotification = null; 1983 final long identity = Binder.clearCallingIdentity(); 1984 try { 1985 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 1986 if (player != null) { 1987 player.stopAsync(); 1988 } 1989 } catch (RemoteException e) { 1990 } finally { 1991 Binder.restoreCallingIdentity(identity); 1992 } 1993 } 1994 1995 // vibrate 1996 if (mVibrateNotification == r) { 1997 mVibrateNotification = null; 1998 long identity = Binder.clearCallingIdentity(); 1999 try { 2000 mVibrator.cancel(); 2001 } 2002 finally { 2003 Binder.restoreCallingIdentity(identity); 2004 } 2005 } 2006 2007 // light 2008 mLights.remove(r.getKey()); 2009 if (mLedNotification == r) { 2010 mLedNotification = null; 2011 } 2012 2013 // Record usage stats 2014 switch (reason) { 2015 case REASON_DELEGATE_CANCEL: 2016 case REASON_DELEGATE_CANCEL_ALL: 2017 case REASON_LISTENER_CANCEL: 2018 case REASON_LISTENER_CANCEL_ALL: 2019 mUsageStats.registerDismissedByUser(r); 2020 break; 2021 case REASON_NOMAN_CANCEL: 2022 case REASON_NOMAN_CANCEL_ALL: 2023 mUsageStats.registerRemovedByApp(r); 2024 break; 2025 case REASON_DELEGATE_CLICK: 2026 mUsageStats.registerCancelDueToClick(r); 2027 break; 2028 default: 2029 mUsageStats.registerCancelUnknown(r); 2030 break; 2031 } 2032 2033 // Save it for users of getHistoricalNotifications() 2034 mArchive.record(r.sbn); 2035 } 2036 2037 /** 2038 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2039 * and none of the {@code mustNotHaveFlags}. 2040 */ 2041 void cancelNotification(final int callingUid, final int callingPid, 2042 final String pkg, final String tag, final int id, 2043 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2044 final int userId, final int reason, final ManagedServiceInfo listener) { 2045 // In enqueueNotificationInternal notifications are added by scheduling the 2046 // work on the worker handler. Hence, we also schedule the cancel on this 2047 // handler to avoid a scenario where an add notification call followed by a 2048 // remove notification call ends up in not removing the notification. 2049 mHandler.post(new Runnable() { 2050 @Override 2051 public void run() { 2052 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2053 mustHaveFlags, mustNotHaveFlags, reason, 2054 listener == null ? null : listener.component.toShortString()); 2055 2056 synchronized (mNotificationList) { 2057 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2058 if (index >= 0) { 2059 NotificationRecord r = mNotificationList.get(index); 2060 2061 // Ideally we'd do this in the caller of this method. However, that would 2062 // require the caller to also find the notification. 2063 if (reason == REASON_DELEGATE_CLICK) { 2064 mUsageStats.registerClickedByUser(r); 2065 } 2066 2067 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2068 return; 2069 } 2070 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2071 return; 2072 } 2073 2074 mNotificationList.remove(index); 2075 mNotificationsByKey.remove(r.sbn.getKey()); 2076 2077 cancelNotificationLocked(r, sendDelete, reason); 2078 updateLightsLocked(); 2079 } 2080 } 2081 } 2082 }); 2083 } 2084 2085 /** 2086 * Determine whether the userId applies to the notification in question, either because 2087 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2088 */ 2089 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2090 return 2091 // looking for USER_ALL notifications? match everything 2092 userId == UserHandle.USER_ALL 2093 // a notification sent to USER_ALL matches any query 2094 || r.getUserId() == UserHandle.USER_ALL 2095 // an exact user match 2096 || r.getUserId() == userId; 2097 } 2098 2099 /** 2100 * Determine whether the userId applies to the notification in question, either because 2101 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2102 * because it matches one of the users profiles. 2103 */ 2104 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2105 return notificationMatchesUserId(r, userId) 2106 || mUserProfiles.isCurrentProfile(r.getUserId()); 2107 } 2108 2109 /** 2110 * Cancels all notifications from a given package that have all of the 2111 * {@code mustHaveFlags}. 2112 */ 2113 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2114 int mustNotHaveFlags, boolean doit, int userId, int reason, 2115 ManagedServiceInfo listener) { 2116 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2117 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2118 listener == null ? null : listener.component.toShortString()); 2119 2120 synchronized (mNotificationList) { 2121 final int N = mNotificationList.size(); 2122 boolean canceledSomething = false; 2123 for (int i = N-1; i >= 0; --i) { 2124 NotificationRecord r = mNotificationList.get(i); 2125 if (!notificationMatchesUserId(r, userId)) { 2126 continue; 2127 } 2128 // Don't remove notifications to all, if there's no package name specified 2129 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2130 continue; 2131 } 2132 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2133 continue; 2134 } 2135 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2136 continue; 2137 } 2138 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2139 continue; 2140 } 2141 canceledSomething = true; 2142 if (!doit) { 2143 return true; 2144 } 2145 mNotificationList.remove(i); 2146 mNotificationsByKey.remove(r.sbn.getKey()); 2147 cancelNotificationLocked(r, false, reason); 2148 } 2149 if (canceledSomething) { 2150 updateLightsLocked(); 2151 } 2152 return canceledSomething; 2153 } 2154 } 2155 2156 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2157 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2158 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2159 null, userId, 0, 0, reason, 2160 listener == null ? null : listener.component.toShortString()); 2161 2162 final int N = mNotificationList.size(); 2163 for (int i=N-1; i>=0; i--) { 2164 NotificationRecord r = mNotificationList.get(i); 2165 if (includeCurrentProfiles) { 2166 if (!notificationMatchesCurrentProfiles(r, userId)) { 2167 continue; 2168 } 2169 } else { 2170 if (!notificationMatchesUserId(r, userId)) { 2171 continue; 2172 } 2173 } 2174 2175 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2176 | Notification.FLAG_NO_CLEAR)) == 0) { 2177 mNotificationList.remove(i); 2178 mNotificationsByKey.remove(r.sbn.getKey()); 2179 cancelNotificationLocked(r, true, reason); 2180 } 2181 } 2182 updateLightsLocked(); 2183 } 2184 2185 // lock on mNotificationList 2186 void updateLightsLocked() 2187 { 2188 // handle notification lights 2189 if (mLedNotification == null) { 2190 // get next notification, if any 2191 int n = mLights.size(); 2192 if (n > 0) { 2193 mLedNotification = mNotificationsByKey.get(mLights.get(n-1)); 2194 } 2195 } 2196 2197 // Don't flash while we are in a call or screen is on 2198 if (mLedNotification == null || mInCall || mScreenOn) { 2199 mNotificationLight.turnOff(); 2200 } else { 2201 final Notification ledno = mLedNotification.sbn.getNotification(); 2202 int ledARGB = ledno.ledARGB; 2203 int ledOnMS = ledno.ledOnMS; 2204 int ledOffMS = ledno.ledOffMS; 2205 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2206 ledARGB = mDefaultNotificationColor; 2207 ledOnMS = mDefaultNotificationLedOn; 2208 ledOffMS = mDefaultNotificationLedOff; 2209 } 2210 if (mNotificationPulseEnabled) { 2211 // pulse repeatedly 2212 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2213 ledOnMS, ledOffMS); 2214 } 2215 } 2216 } 2217 2218 // lock on mNotificationList 2219 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2220 { 2221 ArrayList<NotificationRecord> list = mNotificationList; 2222 final int len = list.size(); 2223 for (int i=0; i<len; i++) { 2224 NotificationRecord r = list.get(i); 2225 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2226 continue; 2227 } 2228 if (tag == null) { 2229 if (r.sbn.getTag() != null) { 2230 continue; 2231 } 2232 } else { 2233 if (!tag.equals(r.sbn.getTag())) { 2234 continue; 2235 } 2236 } 2237 if (r.sbn.getPackageName().equals(pkg)) { 2238 return i; 2239 } 2240 } 2241 return -1; 2242 } 2243 2244 // lock on mNotificationList 2245 int indexOfNotificationLocked(String key) { 2246 final int N = mNotificationList.size(); 2247 for (int i = 0; i < N; i++) { 2248 if (key.equals(mNotificationList.get(i).getKey())) { 2249 return i; 2250 } 2251 } 2252 return -1; 2253 } 2254 2255 private void updateNotificationPulse() { 2256 synchronized (mNotificationList) { 2257 updateLightsLocked(); 2258 } 2259 } 2260 2261 private static boolean isUidSystem(int uid) { 2262 final int appid = UserHandle.getAppId(uid); 2263 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2264 } 2265 2266 private static boolean isCallerSystem() { 2267 return isUidSystem(Binder.getCallingUid()); 2268 } 2269 2270 private static void checkCallerIsSystem() { 2271 if (isCallerSystem()) { 2272 return; 2273 } 2274 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2275 } 2276 2277 private static void checkCallerIsSystemOrSameApp(String pkg) { 2278 if (isCallerSystem()) { 2279 return; 2280 } 2281 final int uid = Binder.getCallingUid(); 2282 try { 2283 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2284 pkg, 0, UserHandle.getCallingUserId()); 2285 if (!UserHandle.isSameApp(ai.uid, uid)) { 2286 throw new SecurityException("Calling uid " + uid + " gave package" 2287 + pkg + " which is owned by uid " + ai.uid); 2288 } 2289 } catch (RemoteException re) { 2290 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2291 } 2292 } 2293 2294 /** 2295 * Generates a NotificationRankingUpdate from 'sbns', considering only 2296 * notifications visible to the given listener. 2297 * 2298 * <p>Caller must hold a lock on mNotificationList.</p> 2299 */ 2300 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { 2301 int speedBumpIndex = -1; 2302 final int N = mNotificationList.size(); 2303 ArrayList<String> keys = new ArrayList<String>(N); 2304 ArrayList<String> dndKeys = new ArrayList<String>(N); 2305 for (int i = 0; i < N; i++) { 2306 NotificationRecord record = mNotificationList.get(i); 2307 if (!info.enabledAndUserMatches(record.sbn.getUserId())) { 2308 continue; 2309 } 2310 keys.add(record.sbn.getKey()); 2311 if (record.isIntercepted()) { 2312 dndKeys.add(record.sbn.getKey()); 2313 } 2314 if (speedBumpIndex == -1 && 2315 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) { 2316 speedBumpIndex = keys.size() - 1; 2317 } 2318 } 2319 String[] keysAr = keys.toArray(new String[keys.size()]); 2320 String[] dndKeysAr = dndKeys.toArray(new String[dndKeys.size()]); 2321 return new NotificationRankingUpdate(keysAr, dndKeysAr, speedBumpIndex); 2322 } 2323 2324 public class NotificationListeners extends ManagedServices { 2325 2326 public NotificationListeners() { 2327 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2328 } 2329 2330 @Override 2331 protected Config getConfig() { 2332 Config c = new Config(); 2333 c.caption = "notification listener"; 2334 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2335 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2336 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2337 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2338 c.clientLabel = R.string.notification_listener_binding_label; 2339 return c; 2340 } 2341 2342 @Override 2343 protected IInterface asInterface(IBinder binder) { 2344 return INotificationListener.Stub.asInterface(binder); 2345 } 2346 2347 @Override 2348 public void onServiceAdded(ManagedServiceInfo info) { 2349 final INotificationListener listener = (INotificationListener) info.service; 2350 final NotificationRankingUpdate update; 2351 synchronized (mNotificationList) { 2352 update = makeRankingUpdateLocked(info); 2353 } 2354 try { 2355 listener.onListenerConnected(update); 2356 } catch (RemoteException e) { 2357 // we tried 2358 } 2359 } 2360 2361 /** 2362 * asynchronously notify all listeners about a new notification 2363 */ 2364 public void notifyPostedLocked(StatusBarNotification sbn) { 2365 // make a copy in case changes are made to the underlying Notification object 2366 final StatusBarNotification sbnClone = sbn.clone(); 2367 for (final ManagedServiceInfo info : mServices) { 2368 if (!info.isEnabledForCurrentProfiles()) { 2369 continue; 2370 } 2371 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2372 if (update.getOrderedKeys().length == 0) { 2373 continue; 2374 } 2375 mHandler.post(new Runnable() { 2376 @Override 2377 public void run() { 2378 notifyPostedIfUserMatch(info, sbnClone, update); 2379 } 2380 }); 2381 } 2382 } 2383 2384 /** 2385 * asynchronously notify all listeners about a removed notification 2386 */ 2387 public void notifyRemovedLocked(StatusBarNotification sbn) { 2388 // make a copy in case changes are made to the underlying Notification object 2389 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 2390 // notification 2391 final StatusBarNotification sbnLight = sbn.cloneLight(); 2392 for (final ManagedServiceInfo info : mServices) { 2393 if (!info.isEnabledForCurrentProfiles()) { 2394 continue; 2395 } 2396 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2397 mHandler.post(new Runnable() { 2398 @Override 2399 public void run() { 2400 notifyRemovedIfUserMatch(info, sbnLight, update); 2401 } 2402 }); 2403 } 2404 } 2405 2406 /** 2407 * asynchronously notify all listeners about a reordering of notifications 2408 */ 2409 public void notifyRankingUpdateLocked() { 2410 for (final ManagedServiceInfo serviceInfo : mServices) { 2411 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2412 continue; 2413 } 2414 final NotificationRankingUpdate update = 2415 makeRankingUpdateLocked(serviceInfo); 2416 mHandler.post(new Runnable() { 2417 @Override 2418 public void run() { 2419 notifyRankingUpdate(serviceInfo, update); 2420 } 2421 }); 2422 } 2423 } 2424 2425 private void notifyPostedIfUserMatch(final ManagedServiceInfo info, 2426 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 2427 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2428 return; 2429 } 2430 final INotificationListener listener = (INotificationListener)info.service; 2431 try { 2432 listener.onNotificationPosted(sbn, rankingUpdate); 2433 } catch (RemoteException ex) { 2434 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 2435 } 2436 } 2437 2438 private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn, 2439 NotificationRankingUpdate rankingUpdate) { 2440 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2441 return; 2442 } 2443 final INotificationListener listener = (INotificationListener) info.service; 2444 try { 2445 listener.onNotificationRemoved(sbn, rankingUpdate); 2446 } catch (RemoteException ex) { 2447 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 2448 } 2449 } 2450 2451 private void notifyRankingUpdate(ManagedServiceInfo info, 2452 NotificationRankingUpdate rankingUpdate) { 2453 final INotificationListener listener = (INotificationListener) info.service; 2454 try { 2455 listener.onNotificationRankingUpdate(rankingUpdate); 2456 } catch (RemoteException ex) { 2457 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 2458 } 2459 } 2460 } 2461 2462 public static final class DumpFilter { 2463 public String pkgFilter; 2464 2465 public static DumpFilter parseFromArguments(String[] args) { 2466 if (args == null || args.length != 2 || !"p".equals(args[0]) 2467 || args[1] == null || args[1].trim().isEmpty()) { 2468 return null; 2469 } 2470 final DumpFilter filter = new DumpFilter(); 2471 filter.pkgFilter = args[1].trim().toLowerCase(); 2472 return filter; 2473 } 2474 2475 public boolean matches(StatusBarNotification sbn) { 2476 return sbn != null && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg())); 2477 } 2478 2479 public boolean matches(ComponentName component) { 2480 return component != null && matches(component.getPackageName()); 2481 } 2482 2483 public boolean matches(String pkg) { 2484 return pkg != null && pkg.toLowerCase().contains(pkgFilter); 2485 } 2486 } 2487} 2488