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