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