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