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