NotificationManagerService.java revision 54bbef435ed857fc68941672799fc8001c101119
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.pkgFilter); pw.print("')"); 1344 } 1345 pw.println(':'); 1346 int N; 1347 1348 synchronized (mToastQueue) { 1349 N = mToastQueue.size(); 1350 if (N > 0) { 1351 pw.println(" Toast Queue:"); 1352 for (int i=0; i<N; i++) { 1353 mToastQueue.get(i).dump(pw, " ", filter); 1354 } 1355 pw.println(" "); 1356 } 1357 } 1358 1359 synchronized (mNotificationList) { 1360 N = mNotificationList.size(); 1361 if (N > 0) { 1362 pw.println(" Notification List:"); 1363 for (int i=0; i<N; i++) { 1364 final NotificationRecord nr = mNotificationList.get(i); 1365 if (filter != null && !filter.matches(nr.sbn)) continue; 1366 nr.dump(pw, " ", getContext()); 1367 } 1368 pw.println(" "); 1369 } 1370 1371 if (filter == null) { 1372 N = mLights.size(); 1373 if (N > 0) { 1374 pw.println(" Lights List:"); 1375 for (int i=0; i<N; i++) { 1376 pw.println(" " + mLights.get(i)); 1377 } 1378 pw.println(" "); 1379 } 1380 1381 pw.println(" mSoundNotification=" + mSoundNotification); 1382 pw.println(" mVibrateNotification=" + mVibrateNotification); 1383 pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts); 1384 pw.println(" mSystemReady=" + mSystemReady); 1385 } 1386 pw.println(" mArchive=" + mArchive.toString()); 1387 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1388 int i=0; 1389 while (iter.hasNext()) { 1390 final StatusBarNotification sbn = iter.next(); 1391 if (filter != null && !filter.matches(sbn)) continue; 1392 pw.println(" " + sbn); 1393 if (++i >= 5) { 1394 if (iter.hasNext()) pw.println(" ..."); 1395 break; 1396 } 1397 } 1398 1399 pw.println("\n Usage Stats:"); 1400 mUsageStats.dump(pw, " ", filter); 1401 1402 if (filter == null) { 1403 pw.println("\n Zen Mode:"); 1404 mZenModeHelper.dump(pw, " "); 1405 } 1406 1407 pw.println("\n Ranking Config:"); 1408 mRankingHelper.dump(pw, " ", filter); 1409 1410 pw.println("\n Notification listeners:"); 1411 mListeners.dump(pw, filter); 1412 1413 pw.println("\n Condition providers:"); 1414 mConditionProviders.dump(pw, filter); 1415 } 1416 } 1417 1418 /** 1419 * The private API only accessible to the system process. 1420 */ 1421 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1422 @Override 1423 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, 1424 String tag, int id, Notification notification, int[] idReceived, int userId) { 1425 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 1426 idReceived, userId); 1427 } 1428 }; 1429 1430 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, 1431 final int callingPid, final String tag, final int id, final Notification notification, 1432 int[] idOut, int incomingUserId) { 1433 if (DBG) { 1434 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 1435 + " notification=" + notification); 1436 } 1437 checkCallerIsSystemOrSameApp(pkg); 1438 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 1439 1440 final int userId = ActivityManager.handleIncomingUser(callingPid, 1441 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1442 final UserHandle user = new UserHandle(userId); 1443 1444 // Limit the number of notifications that any given package except the android 1445 // package can enqueue. Prevents DOS attacks and deals with leaks. 1446 if (!isSystemNotification) { 1447 synchronized (mNotificationList) { 1448 int count = 0; 1449 final int N = mNotificationList.size(); 1450 for (int i=0; i<N; i++) { 1451 final NotificationRecord r = mNotificationList.get(i); 1452 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1453 count++; 1454 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1455 Slog.e(TAG, "Package has already posted " + count 1456 + " notifications. Not showing more. package=" + pkg); 1457 return; 1458 } 1459 } 1460 } 1461 } 1462 } 1463 1464 // This conditional is a dirty hack to limit the logging done on 1465 // behalf of the download manager without affecting other apps. 1466 if (!pkg.equals("com.android.providers.downloads") 1467 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1468 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 1469 pkg, id, tag, userId, notification.toString()); 1470 } 1471 1472 if (pkg == null || notification == null) { 1473 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1474 + " id=" + id + " notification=" + notification); 1475 } 1476 if (notification.icon != 0) { 1477 if (notification.contentView == null) { 1478 throw new IllegalArgumentException("contentView required: pkg=" + pkg 1479 + " id=" + id + " notification=" + notification); 1480 } 1481 } 1482 1483 mHandler.post(new Runnable() { 1484 @Override 1485 public void run() { 1486 1487 // === Scoring === 1488 1489 // 0. Sanitize inputs 1490 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1491 Notification.PRIORITY_MAX); 1492 // Migrate notification flags to scores 1493 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1494 if (notification.priority < Notification.PRIORITY_MAX) { 1495 notification.priority = Notification.PRIORITY_MAX; 1496 } 1497 } else if (SCORE_ONGOING_HIGHER && 1498 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1499 if (notification.priority < Notification.PRIORITY_HIGH) { 1500 notification.priority = Notification.PRIORITY_HIGH; 1501 } 1502 } 1503 1504 // 1. initial score: buckets of 10, around the app [-20..20] 1505 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; 1506 1507 // 2. extract ranking signals from the notification data 1508 final StatusBarNotification n = new StatusBarNotification( 1509 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 1510 user); 1511 NotificationRecord r = new NotificationRecord(n, score); 1512 NotificationRecord old = mNotificationsByKey.get(n.getKey()); 1513 if (old != null) { 1514 // Retain ranking information from previous record 1515 r.copyRankingInformation(old); 1516 } 1517 mRankingHelper.extractSignals(r); 1518 1519 // 3. Apply local rules 1520 1521 // blocked apps 1522 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1523 if (!isSystemNotification) { 1524 r.score = JUNK_SCORE; 1525 Slog.e(TAG, "Suppressing notification from package " + pkg 1526 + " by user request."); 1527 } 1528 } 1529 1530 if (r.score < SCORE_DISPLAY_THRESHOLD) { 1531 // Notification will be blocked because the score is too low. 1532 return; 1533 } 1534 1535 synchronized (mNotificationList) { 1536 int index = indexOfNotificationLocked(n.getKey()); 1537 if (index < 0) { 1538 mNotificationList.add(r); 1539 mUsageStats.registerPostedByApp(r); 1540 } else { 1541 old = mNotificationList.get(index); 1542 mNotificationList.set(index, r); 1543 mUsageStats.registerUpdatedByApp(r, old); 1544 // Make sure we don't lose the foreground service state. 1545 notification.flags |= 1546 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1547 mNotificationsByKey.remove(old.sbn.getKey()); 1548 r.isUpdate = true; 1549 } 1550 1551 mNotificationsByKey.put(n.getKey(), r); 1552 1553 // Ensure if this is a foreground service that the proper additional 1554 // flags are set. 1555 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1556 notification.flags |= Notification.FLAG_ONGOING_EVENT 1557 | Notification.FLAG_NO_CLEAR; 1558 } 1559 1560 applyZenModeLocked(r); 1561 1562 mRankingHelper.sort(mNotificationList); 1563 1564 if (notification.icon != 0) { 1565 mListeners.notifyPostedLocked(n); 1566 } else { 1567 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1568 if (old != null && !old.isCanceled) { 1569 mListeners.notifyRemovedLocked(n); 1570 } 1571 // ATTENTION: in a future release we will bail out here 1572 // so that we do not play sounds, show lights, etc. for invalid 1573 // notifications 1574 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1575 + n.getPackageName()); 1576 } 1577 1578 buzzBeepBlinkLocked(r); 1579 } 1580 } 1581 }); 1582 1583 idOut[0] = id; 1584 } 1585 1586 private void buzzBeepBlinkLocked(NotificationRecord record) { 1587 final Notification notification = record.sbn.getNotification(); 1588 1589 // Should this notification make noise, vibe, or use the LED? 1590 final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) && 1591 !record.isIntercepted(); 1592 if (DBG || record.isIntercepted()) 1593 Slog.v(TAG, 1594 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt + 1595 " intercept=" + record.isIntercepted() 1596 ); 1597 1598 final int currentUser; 1599 final long token = Binder.clearCallingIdentity(); 1600 try { 1601 currentUser = ActivityManager.getCurrentUser(); 1602 } finally { 1603 Binder.restoreCallingIdentity(token); 1604 } 1605 1606 // If we're not supposed to beep, vibrate, etc. then don't. 1607 if (!mDisableNotificationAlerts 1608 && (!(record.isUpdate 1609 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1610 && (record.getUserId() == UserHandle.USER_ALL || 1611 record.getUserId() == currentUser || 1612 mUserProfiles.isCurrentProfile(record.getUserId())) 1613 && canInterrupt 1614 && mSystemReady 1615 && mAudioManager != null) { 1616 if (DBG) Slog.v(TAG, "Interrupting!"); 1617 1618 sendAccessibilityEvent(notification, record.sbn.getPackageName()); 1619 1620 // sound 1621 1622 // should we use the default notification sound? (indicated either by 1623 // DEFAULT_SOUND or because notification.sound is pointing at 1624 // Settings.System.NOTIFICATION_SOUND) 1625 final boolean useDefaultSound = 1626 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1627 Settings.System.DEFAULT_NOTIFICATION_URI 1628 .equals(notification.sound); 1629 1630 Uri soundUri = null; 1631 boolean hasValidSound = false; 1632 1633 if (useDefaultSound) { 1634 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1635 1636 // check to see if the default notification sound is silent 1637 ContentResolver resolver = getContext().getContentResolver(); 1638 hasValidSound = Settings.System.getString(resolver, 1639 Settings.System.NOTIFICATION_SOUND) != null; 1640 } else if (notification.sound != null) { 1641 soundUri = notification.sound; 1642 hasValidSound = (soundUri != null); 1643 } 1644 1645 if (hasValidSound) { 1646 boolean looping = 1647 (notification.flags & Notification.FLAG_INSISTENT) != 0; 1648 int audioStreamType; 1649 if (notification.audioStreamType >= 0) { 1650 audioStreamType = notification.audioStreamType; 1651 } else { 1652 audioStreamType = DEFAULT_STREAM_TYPE; 1653 } 1654 mSoundNotification = record; 1655 // do not play notifications if stream volume is 0 (typically because 1656 // ringer mode is silent) or if there is a user of exclusive audio focus 1657 if ((mAudioManager.getStreamVolume(audioStreamType) != 0) 1658 && !mAudioManager.isAudioFocusExclusive()) { 1659 final long identity = Binder.clearCallingIdentity(); 1660 try { 1661 final IRingtonePlayer player = 1662 mAudioManager.getRingtonePlayer(); 1663 if (player != null) { 1664 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 1665 + " on stream " + audioStreamType); 1666 player.playAsync(soundUri, record.sbn.getUser(), looping, 1667 audioStreamType); 1668 } 1669 } catch (RemoteException e) { 1670 } finally { 1671 Binder.restoreCallingIdentity(identity); 1672 } 1673 } 1674 } 1675 1676 // vibrate 1677 // Does the notification want to specify its own vibration? 1678 final boolean hasCustomVibrate = notification.vibrate != null; 1679 1680 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1681 // mode, and no other vibration is specified, we fall back to vibration 1682 final boolean convertSoundToVibration = 1683 !hasCustomVibrate 1684 && hasValidSound 1685 && (mAudioManager.getRingerMode() 1686 == AudioManager.RINGER_MODE_VIBRATE); 1687 1688 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1689 final boolean useDefaultVibrate = 1690 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1691 1692 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1693 && !(mAudioManager.getRingerMode() 1694 == AudioManager.RINGER_MODE_SILENT)) { 1695 mVibrateNotification = record; 1696 1697 if (useDefaultVibrate || convertSoundToVibration) { 1698 // Escalate privileges so we can use the vibrator even if the 1699 // notifying app does not have the VIBRATE permission. 1700 long identity = Binder.clearCallingIdentity(); 1701 try { 1702 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 1703 useDefaultVibrate ? mDefaultVibrationPattern 1704 : mFallbackVibrationPattern, 1705 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1706 ? 0: -1, notification.audioStreamType); 1707 } finally { 1708 Binder.restoreCallingIdentity(identity); 1709 } 1710 } else if (notification.vibrate.length > 1) { 1711 // If you want your own vibration pattern, you need the VIBRATE 1712 // permission 1713 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 1714 notification.vibrate, 1715 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1716 ? 0: -1, notification.audioStreamType); 1717 } 1718 } 1719 } 1720 1721 // light 1722 // release the light 1723 boolean wasShowLights = mLights.remove(record.getKey()); 1724 if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) { 1725 mLedNotification = null; 1726 } 1727 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) { 1728 mLights.add(record.getKey()); 1729 updateLightsLocked(); 1730 if (mUseAttentionLight) { 1731 mAttentionLight.pulse(); 1732 } 1733 } else if (wasShowLights) { 1734 updateLightsLocked(); 1735 } 1736 } 1737 1738 void showNextToastLocked() { 1739 ToastRecord record = mToastQueue.get(0); 1740 while (record != null) { 1741 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1742 try { 1743 record.callback.show(); 1744 scheduleTimeoutLocked(record); 1745 return; 1746 } catch (RemoteException e) { 1747 Slog.w(TAG, "Object died trying to show notification " + record.callback 1748 + " in package " + record.pkg); 1749 // remove it from the list and let the process die 1750 int index = mToastQueue.indexOf(record); 1751 if (index >= 0) { 1752 mToastQueue.remove(index); 1753 } 1754 keepProcessAliveLocked(record.pid); 1755 if (mToastQueue.size() > 0) { 1756 record = mToastQueue.get(0); 1757 } else { 1758 record = null; 1759 } 1760 } 1761 } 1762 } 1763 1764 void cancelToastLocked(int index) { 1765 ToastRecord record = mToastQueue.get(index); 1766 try { 1767 record.callback.hide(); 1768 } catch (RemoteException e) { 1769 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1770 + " in package " + record.pkg); 1771 // don't worry about this, we're about to remove it from 1772 // the list anyway 1773 } 1774 mToastQueue.remove(index); 1775 keepProcessAliveLocked(record.pid); 1776 if (mToastQueue.size() > 0) { 1777 // Show the next one. If the callback fails, this will remove 1778 // it from the list, so don't assume that the list hasn't changed 1779 // after this point. 1780 showNextToastLocked(); 1781 } 1782 } 1783 1784 private void scheduleTimeoutLocked(ToastRecord r) 1785 { 1786 mHandler.removeCallbacksAndMessages(r); 1787 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1788 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 1789 mHandler.sendMessageDelayed(m, delay); 1790 } 1791 1792 private void handleTimeout(ToastRecord record) 1793 { 1794 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1795 synchronized (mToastQueue) { 1796 int index = indexOfToastLocked(record.pkg, record.callback); 1797 if (index >= 0) { 1798 cancelToastLocked(index); 1799 } 1800 } 1801 } 1802 1803 // lock on mToastQueue 1804 int indexOfToastLocked(String pkg, ITransientNotification callback) 1805 { 1806 IBinder cbak = callback.asBinder(); 1807 ArrayList<ToastRecord> list = mToastQueue; 1808 int len = list.size(); 1809 for (int i=0; i<len; i++) { 1810 ToastRecord r = list.get(i); 1811 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1812 return i; 1813 } 1814 } 1815 return -1; 1816 } 1817 1818 // lock on mToastQueue 1819 void keepProcessAliveLocked(int pid) 1820 { 1821 int toastCount = 0; // toasts from this pid 1822 ArrayList<ToastRecord> list = mToastQueue; 1823 int N = list.size(); 1824 for (int i=0; i<N; i++) { 1825 ToastRecord r = list.get(i); 1826 if (r.pid == pid) { 1827 toastCount++; 1828 } 1829 } 1830 try { 1831 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 1832 } catch (RemoteException e) { 1833 // Shouldn't happen. 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 mRankingHelper.sort(mNotificationList); 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 private void handleRankingConfigChange() { 1865 synchronized (mNotificationList) { 1866 final int N = mNotificationList.size(); 1867 ArrayList<String> orderBefore = new ArrayList<String>(N); 1868 for (int i = 0; i < N; i++) { 1869 final NotificationRecord r = mNotificationList.get(i); 1870 orderBefore.add(r.getKey()); 1871 mRankingHelper.extractSignals(r); 1872 } 1873 mRankingHelper.sort(mNotificationList); 1874 for (int i = 0; i < N; i++) { 1875 if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) { 1876 scheduleSendRankingUpdate(); 1877 return; 1878 } 1879 } 1880 } 1881 } 1882 1883 // let zen mode evaluate this record 1884 private void applyZenModeLocked(NotificationRecord record) { 1885 record.setIntercepted(mZenModeHelper.shouldIntercept(record)); 1886 } 1887 1888 // lock on mNotificationList 1889 private int findNotificationRecordIndexLocked(NotificationRecord target) { 1890 return mRankingHelper.indexOf(mNotificationList, target); 1891 } 1892 1893 private void scheduleSendRankingUpdate() { 1894 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 1895 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 1896 mHandler.sendMessage(m); 1897 } 1898 1899 private void handleSendRankingUpdate() { 1900 synchronized (mNotificationList) { 1901 mListeners.notifyRankingUpdateLocked(); 1902 } 1903 } 1904 1905 private final class WorkerHandler extends Handler 1906 { 1907 @Override 1908 public void handleMessage(Message msg) 1909 { 1910 switch (msg.what) 1911 { 1912 case MESSAGE_TIMEOUT: 1913 handleTimeout((ToastRecord)msg.obj); 1914 break; 1915 case MESSAGE_SAVE_POLICY_FILE: 1916 handleSavePolicyFile(); 1917 break; 1918 case MESSAGE_SEND_RANKING_UPDATE: 1919 handleSendRankingUpdate(); 1920 break; 1921 } 1922 } 1923 1924 } 1925 1926 private final class RankingWorkerHandler extends Handler 1927 { 1928 public RankingWorkerHandler(Looper looper) { 1929 super(looper); 1930 } 1931 1932 @Override 1933 public void handleMessage(Message msg) { 1934 switch (msg.what) { 1935 case MESSAGE_RECONSIDER_RANKING: 1936 handleRankingReconsideration(msg); 1937 break; 1938 case MESSAGE_RANKING_CONFIG_CHANGE: 1939 handleRankingConfigChange(); 1940 break; 1941 } 1942 } 1943 } 1944 1945 // Notifications 1946 // ============================================================================ 1947 static int clamp(int x, int low, int high) { 1948 return (x < low) ? low : ((x > high) ? high : x); 1949 } 1950 1951 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 1952 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 1953 if (!manager.isEnabled()) { 1954 return; 1955 } 1956 1957 AccessibilityEvent event = 1958 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 1959 event.setPackageName(packageName); 1960 event.setClassName(Notification.class.getName()); 1961 event.setParcelableData(notification); 1962 CharSequence tickerText = notification.tickerText; 1963 if (!TextUtils.isEmpty(tickerText)) { 1964 event.getText().add(tickerText); 1965 } 1966 1967 manager.sendAccessibilityEvent(event); 1968 } 1969 1970 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 1971 // tell the app 1972 if (sendDelete) { 1973 if (r.getNotification().deleteIntent != null) { 1974 try { 1975 r.getNotification().deleteIntent.send(); 1976 } catch (PendingIntent.CanceledException ex) { 1977 // do nothing - there's no relevant way to recover, and 1978 // no reason to let this propagate 1979 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 1980 } 1981 } 1982 } 1983 1984 // status bar 1985 if (r.getNotification().icon != 0) { 1986 r.isCanceled = true; 1987 mListeners.notifyRemovedLocked(r.sbn); 1988 } 1989 1990 // sound 1991 if (mSoundNotification == r) { 1992 mSoundNotification = null; 1993 final long identity = Binder.clearCallingIdentity(); 1994 try { 1995 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 1996 if (player != null) { 1997 player.stopAsync(); 1998 } 1999 } catch (RemoteException e) { 2000 } finally { 2001 Binder.restoreCallingIdentity(identity); 2002 } 2003 } 2004 2005 // vibrate 2006 if (mVibrateNotification == r) { 2007 mVibrateNotification = null; 2008 long identity = Binder.clearCallingIdentity(); 2009 try { 2010 mVibrator.cancel(); 2011 } 2012 finally { 2013 Binder.restoreCallingIdentity(identity); 2014 } 2015 } 2016 2017 // light 2018 mLights.remove(r.getKey()); 2019 if (mLedNotification == r) { 2020 mLedNotification = null; 2021 } 2022 2023 // Record usage stats 2024 switch (reason) { 2025 case REASON_DELEGATE_CANCEL: 2026 case REASON_DELEGATE_CANCEL_ALL: 2027 case REASON_LISTENER_CANCEL: 2028 case REASON_LISTENER_CANCEL_ALL: 2029 mUsageStats.registerDismissedByUser(r); 2030 break; 2031 case REASON_NOMAN_CANCEL: 2032 case REASON_NOMAN_CANCEL_ALL: 2033 mUsageStats.registerRemovedByApp(r); 2034 break; 2035 case REASON_DELEGATE_CLICK: 2036 mUsageStats.registerCancelDueToClick(r); 2037 break; 2038 default: 2039 mUsageStats.registerCancelUnknown(r); 2040 break; 2041 } 2042 2043 // Save it for users of getHistoricalNotifications() 2044 mArchive.record(r.sbn); 2045 } 2046 2047 /** 2048 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2049 * and none of the {@code mustNotHaveFlags}. 2050 */ 2051 void cancelNotification(final int callingUid, final int callingPid, 2052 final String pkg, final String tag, final int id, 2053 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2054 final int userId, final int reason, final ManagedServiceInfo listener) { 2055 // In enqueueNotificationInternal notifications are added by scheduling the 2056 // work on the worker handler. Hence, we also schedule the cancel on this 2057 // handler to avoid a scenario where an add notification call followed by a 2058 // remove notification call ends up in not removing the notification. 2059 mHandler.post(new Runnable() { 2060 @Override 2061 public void run() { 2062 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2063 mustHaveFlags, mustNotHaveFlags, reason, 2064 listener == null ? null : listener.component.toShortString()); 2065 2066 synchronized (mNotificationList) { 2067 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2068 if (index >= 0) { 2069 NotificationRecord r = mNotificationList.get(index); 2070 2071 // Ideally we'd do this in the caller of this method. However, that would 2072 // require the caller to also find the notification. 2073 if (reason == REASON_DELEGATE_CLICK) { 2074 mUsageStats.registerClickedByUser(r); 2075 } 2076 2077 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2078 return; 2079 } 2080 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2081 return; 2082 } 2083 2084 mNotificationList.remove(index); 2085 mNotificationsByKey.remove(r.sbn.getKey()); 2086 2087 cancelNotificationLocked(r, sendDelete, reason); 2088 updateLightsLocked(); 2089 } 2090 } 2091 } 2092 }); 2093 } 2094 2095 /** 2096 * Determine whether the userId applies to the notification in question, either because 2097 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2098 */ 2099 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2100 return 2101 // looking for USER_ALL notifications? match everything 2102 userId == UserHandle.USER_ALL 2103 // a notification sent to USER_ALL matches any query 2104 || r.getUserId() == UserHandle.USER_ALL 2105 // an exact user match 2106 || r.getUserId() == userId; 2107 } 2108 2109 /** 2110 * Determine whether the userId applies to the notification in question, either because 2111 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2112 * because it matches one of the users profiles. 2113 */ 2114 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2115 return notificationMatchesUserId(r, userId) 2116 || mUserProfiles.isCurrentProfile(r.getUserId()); 2117 } 2118 2119 /** 2120 * Cancels all notifications from a given package that have all of the 2121 * {@code mustHaveFlags}. 2122 */ 2123 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2124 int mustNotHaveFlags, boolean doit, int userId, int reason, 2125 ManagedServiceInfo listener) { 2126 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2127 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2128 listener == null ? null : listener.component.toShortString()); 2129 2130 synchronized (mNotificationList) { 2131 final int N = mNotificationList.size(); 2132 boolean canceledSomething = false; 2133 for (int i = N-1; i >= 0; --i) { 2134 NotificationRecord r = mNotificationList.get(i); 2135 if (!notificationMatchesUserId(r, userId)) { 2136 continue; 2137 } 2138 // Don't remove notifications to all, if there's no package name specified 2139 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2140 continue; 2141 } 2142 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2143 continue; 2144 } 2145 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2146 continue; 2147 } 2148 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2149 continue; 2150 } 2151 canceledSomething = true; 2152 if (!doit) { 2153 return true; 2154 } 2155 mNotificationList.remove(i); 2156 mNotificationsByKey.remove(r.sbn.getKey()); 2157 cancelNotificationLocked(r, false, reason); 2158 } 2159 if (canceledSomething) { 2160 updateLightsLocked(); 2161 } 2162 return canceledSomething; 2163 } 2164 } 2165 2166 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2167 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2168 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2169 null, userId, 0, 0, reason, 2170 listener == null ? null : listener.component.toShortString()); 2171 2172 final int N = mNotificationList.size(); 2173 for (int i=N-1; i>=0; i--) { 2174 NotificationRecord r = mNotificationList.get(i); 2175 if (includeCurrentProfiles) { 2176 if (!notificationMatchesCurrentProfiles(r, userId)) { 2177 continue; 2178 } 2179 } else { 2180 if (!notificationMatchesUserId(r, userId)) { 2181 continue; 2182 } 2183 } 2184 2185 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2186 | Notification.FLAG_NO_CLEAR)) == 0) { 2187 mNotificationList.remove(i); 2188 mNotificationsByKey.remove(r.sbn.getKey()); 2189 cancelNotificationLocked(r, true, reason); 2190 } 2191 } 2192 updateLightsLocked(); 2193 } 2194 2195 // lock on mNotificationList 2196 void updateLightsLocked() 2197 { 2198 // handle notification lights 2199 if (mLedNotification == null) { 2200 // get next notification, if any 2201 int n = mLights.size(); 2202 if (n > 0) { 2203 mLedNotification = mNotificationsByKey.get(mLights.get(n-1)); 2204 } 2205 } 2206 2207 // Don't flash while we are in a call or screen is on 2208 if (mLedNotification == null || mInCall || mScreenOn) { 2209 mNotificationLight.turnOff(); 2210 } else { 2211 final Notification ledno = mLedNotification.sbn.getNotification(); 2212 int ledARGB = ledno.ledARGB; 2213 int ledOnMS = ledno.ledOnMS; 2214 int ledOffMS = ledno.ledOffMS; 2215 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2216 ledARGB = mDefaultNotificationColor; 2217 ledOnMS = mDefaultNotificationLedOn; 2218 ledOffMS = mDefaultNotificationLedOff; 2219 } 2220 if (mNotificationPulseEnabled) { 2221 // pulse repeatedly 2222 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2223 ledOnMS, ledOffMS); 2224 } 2225 } 2226 } 2227 2228 // lock on mNotificationList 2229 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2230 { 2231 ArrayList<NotificationRecord> list = mNotificationList; 2232 final int len = list.size(); 2233 for (int i=0; i<len; i++) { 2234 NotificationRecord r = list.get(i); 2235 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2236 continue; 2237 } 2238 if (tag == null) { 2239 if (r.sbn.getTag() != null) { 2240 continue; 2241 } 2242 } else { 2243 if (!tag.equals(r.sbn.getTag())) { 2244 continue; 2245 } 2246 } 2247 if (r.sbn.getPackageName().equals(pkg)) { 2248 return i; 2249 } 2250 } 2251 return -1; 2252 } 2253 2254 // lock on mNotificationList 2255 int indexOfNotificationLocked(String key) { 2256 final int N = mNotificationList.size(); 2257 for (int i = 0; i < N; i++) { 2258 if (key.equals(mNotificationList.get(i).getKey())) { 2259 return i; 2260 } 2261 } 2262 return -1; 2263 } 2264 2265 private void updateNotificationPulse() { 2266 synchronized (mNotificationList) { 2267 updateLightsLocked(); 2268 } 2269 } 2270 2271 private static boolean isUidSystem(int uid) { 2272 final int appid = UserHandle.getAppId(uid); 2273 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2274 } 2275 2276 private static boolean isCallerSystem() { 2277 return isUidSystem(Binder.getCallingUid()); 2278 } 2279 2280 private static void checkCallerIsSystem() { 2281 if (isCallerSystem()) { 2282 return; 2283 } 2284 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2285 } 2286 2287 private static void checkCallerIsSystemOrSameApp(String pkg) { 2288 if (isCallerSystem()) { 2289 return; 2290 } 2291 final int uid = Binder.getCallingUid(); 2292 try { 2293 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2294 pkg, 0, UserHandle.getCallingUserId()); 2295 if (!UserHandle.isSameApp(ai.uid, uid)) { 2296 throw new SecurityException("Calling uid " + uid + " gave package" 2297 + pkg + " which is owned by uid " + ai.uid); 2298 } 2299 } catch (RemoteException re) { 2300 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2301 } 2302 } 2303 2304 /** 2305 * Generates a NotificationRankingUpdate from 'sbns', considering only 2306 * notifications visible to the given listener. 2307 * 2308 * <p>Caller must hold a lock on mNotificationList.</p> 2309 */ 2310 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { 2311 int speedBumpIndex = -1; 2312 final int N = mNotificationList.size(); 2313 ArrayList<String> keys = new ArrayList<String>(N); 2314 ArrayList<String> interceptedKeys = new ArrayList<String>(N); 2315 for (int i = 0; i < N; i++) { 2316 NotificationRecord record = mNotificationList.get(i); 2317 if (!info.enabledAndUserMatches(record.sbn.getUserId())) { 2318 continue; 2319 } 2320 keys.add(record.sbn.getKey()); 2321 if (record.isIntercepted()) { 2322 interceptedKeys.add(record.sbn.getKey()); 2323 } 2324 if (speedBumpIndex == -1 && 2325 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) { 2326 speedBumpIndex = keys.size() - 1; 2327 } 2328 } 2329 String[] keysAr = keys.toArray(new String[keys.size()]); 2330 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]); 2331 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, speedBumpIndex); 2332 } 2333 2334 public class NotificationListeners extends ManagedServices { 2335 2336 public NotificationListeners() { 2337 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2338 } 2339 2340 @Override 2341 protected Config getConfig() { 2342 Config c = new Config(); 2343 c.caption = "notification listener"; 2344 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2345 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2346 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2347 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2348 c.clientLabel = R.string.notification_listener_binding_label; 2349 return c; 2350 } 2351 2352 @Override 2353 protected IInterface asInterface(IBinder binder) { 2354 return INotificationListener.Stub.asInterface(binder); 2355 } 2356 2357 @Override 2358 public void onServiceAdded(ManagedServiceInfo info) { 2359 final INotificationListener listener = (INotificationListener) info.service; 2360 final NotificationRankingUpdate update; 2361 synchronized (mNotificationList) { 2362 update = makeRankingUpdateLocked(info); 2363 } 2364 try { 2365 listener.onListenerConnected(update); 2366 } catch (RemoteException e) { 2367 // we tried 2368 } 2369 } 2370 2371 /** 2372 * asynchronously notify all listeners about a new notification 2373 */ 2374 public void notifyPostedLocked(StatusBarNotification sbn) { 2375 // make a copy in case changes are made to the underlying Notification object 2376 final StatusBarNotification sbnClone = sbn.clone(); 2377 for (final ManagedServiceInfo info : mServices) { 2378 if (!info.isEnabledForCurrentProfiles()) { 2379 continue; 2380 } 2381 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2382 if (update.getOrderedKeys().length == 0) { 2383 continue; 2384 } 2385 mHandler.post(new Runnable() { 2386 @Override 2387 public void run() { 2388 notifyPostedIfUserMatch(info, sbnClone, update); 2389 } 2390 }); 2391 } 2392 } 2393 2394 /** 2395 * asynchronously notify all listeners about a removed notification 2396 */ 2397 public void notifyRemovedLocked(StatusBarNotification sbn) { 2398 // make a copy in case changes are made to the underlying Notification object 2399 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 2400 // notification 2401 final StatusBarNotification sbnLight = sbn.cloneLight(); 2402 for (final ManagedServiceInfo info : mServices) { 2403 if (!info.isEnabledForCurrentProfiles()) { 2404 continue; 2405 } 2406 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 2407 mHandler.post(new Runnable() { 2408 @Override 2409 public void run() { 2410 notifyRemovedIfUserMatch(info, sbnLight, update); 2411 } 2412 }); 2413 } 2414 } 2415 2416 /** 2417 * asynchronously notify all listeners about a reordering of notifications 2418 */ 2419 public void notifyRankingUpdateLocked() { 2420 for (final ManagedServiceInfo serviceInfo : mServices) { 2421 if (!serviceInfo.isEnabledForCurrentProfiles()) { 2422 continue; 2423 } 2424 final NotificationRankingUpdate update = 2425 makeRankingUpdateLocked(serviceInfo); 2426 mHandler.post(new Runnable() { 2427 @Override 2428 public void run() { 2429 notifyRankingUpdate(serviceInfo, update); 2430 } 2431 }); 2432 } 2433 } 2434 2435 private void notifyPostedIfUserMatch(final ManagedServiceInfo info, 2436 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 2437 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2438 return; 2439 } 2440 final INotificationListener listener = (INotificationListener)info.service; 2441 try { 2442 listener.onNotificationPosted(sbn, rankingUpdate); 2443 } catch (RemoteException ex) { 2444 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 2445 } 2446 } 2447 2448 private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn, 2449 NotificationRankingUpdate rankingUpdate) { 2450 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2451 return; 2452 } 2453 final INotificationListener listener = (INotificationListener) info.service; 2454 try { 2455 listener.onNotificationRemoved(sbn, rankingUpdate); 2456 } catch (RemoteException ex) { 2457 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 2458 } 2459 } 2460 2461 private void notifyRankingUpdate(ManagedServiceInfo info, 2462 NotificationRankingUpdate rankingUpdate) { 2463 final INotificationListener listener = (INotificationListener) info.service; 2464 try { 2465 listener.onNotificationRankingUpdate(rankingUpdate); 2466 } catch (RemoteException ex) { 2467 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 2468 } 2469 } 2470 } 2471 2472 public static final class DumpFilter { 2473 public String pkgFilter; 2474 2475 public static DumpFilter parseFromArguments(String[] args) { 2476 if (args == null || args.length != 2 || !"p".equals(args[0]) 2477 || args[1] == null || args[1].trim().isEmpty()) { 2478 return null; 2479 } 2480 final DumpFilter filter = new DumpFilter(); 2481 filter.pkgFilter = args[1].trim().toLowerCase(); 2482 return filter; 2483 } 2484 2485 public boolean matches(StatusBarNotification sbn) { 2486 return sbn != null && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg())); 2487 } 2488 2489 public boolean matches(ComponentName component) { 2490 return component != null && matches(component.getPackageName()); 2491 } 2492 2493 public boolean matches(String pkg) { 2494 return pkg != null && pkg.toLowerCase().contains(pkgFilter); 2495 } 2496 } 2497} 2498