NotificationManagerServicepublic class NotificationManagerService extends com.android.server.SystemService
Fields Summary |
---|
static final String | TAG | static final boolean | DBG | static final int | MAX_PACKAGE_NOTIFICATIONS | static final int | MESSAGE_TIMEOUT | static final int | MESSAGE_SAVE_POLICY_FILE | static final int | MESSAGE_RECONSIDER_RANKING | static final int | MESSAGE_RANKING_CONFIG_CHANGE | static final int | MESSAGE_SEND_RANKING_UPDATE | static final int | MESSAGE_LISTENER_HINTS_CHANGED | static final int | MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED | static final int | LONG_DELAY | static final int | SHORT_DELAY | static final long[] | DEFAULT_VIBRATE_PATTERN | static final int | VIBRATE_PATTERN_MAXLEN | static final int | DEFAULT_STREAM_TYPE | static final boolean | SCORE_ONGOING_HIGHER | static final int | JUNK_SCORE | static final int | NOTIFICATION_PRIORITY_MULTIPLIER | static final int | SCORE_DISPLAY_THRESHOLD | static final int | SCORE_INTERRUPTION_THRESHOLD | static final boolean | ENABLE_BLOCKED_NOTIFICATIONS | static final boolean | ENABLE_BLOCKED_TOASTS | static final int | MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS | static final float | MATCHES_CALL_FILTER_TIMEOUT_AFFINITY | private static final int | EVENTLOG_ENQUEUE_STATUS_NEWnotification_enqueue status value for a newly enqueued notification. | private static final int | EVENTLOG_ENQUEUE_STATUS_UPDATEnotification_enqueue status value for an existing notification. | private static final int | EVENTLOG_ENQUEUE_STATUS_IGNOREDnotification_enqueue status value for an ignored notification. | private android.app.IActivityManager | mAm | android.media.AudioManager | mAudioManager | com.android.server.statusbar.StatusBarManagerInternal | mStatusBar | android.os.Vibrator | mVibrator | final android.os.IBinder | mForegroundToken | private WorkerHandler | mHandler | private final android.os.HandlerThread | mRankingThread | private com.android.server.lights.Light | mNotificationLight | com.android.server.lights.Light | mAttentionLight | private int | mDefaultNotificationColor | private int | mDefaultNotificationLedOn | private int | mDefaultNotificationLedOff | private long[] | mDefaultVibrationPattern | private long[] | mFallbackVibrationPattern | private boolean | mUseAttentionLight | boolean | mSystemReady | private boolean | mDisableNotificationEffects | private int | mCallState | private String | mSoundNotificationKey | private String | mVibrateNotificationKey | private final android.util.ArraySet | mListenersDisablingEffects | private android.content.ComponentName | mEffectsSuppressor | private int | mListenerHints | private int | mInterruptionFilter | private boolean | mScreenOn | private boolean | mInCall | private boolean | mNotificationPulseEnabled | final ArrayList | mNotificationList | final android.util.ArrayMap | mNotificationsByKey | final ArrayList | mToastQueue | final android.util.ArrayMap | mSummaryByGroupKey | ArrayList | mLights | private android.app.AppOpsManager | mAppOps | private Archive | mArchive | private android.util.AtomicFile | mPolicyFile | private HashSet | mBlockedPackages | private static final int | DB_VERSION | private static final String | TAG_BODY | private static final String | ATTR_VERSION | private static final String | TAG_BLOCKED_PKGS | private static final String | TAG_PACKAGE | private static final String | ATTR_NAME | private RankingHelper | mRankingHelper | private final com.android.server.notification.ManagedServices.UserProfiles | mUserProfiles | private NotificationListeners | mListeners | private ConditionProviders | mConditionProviders | private NotificationUsageStats | mUsageStats | private static final int | MY_UID | private static final int | MY_PID | private static final int | REASON_DELEGATE_CLICK | private static final int | REASON_DELEGATE_CANCEL | private static final int | REASON_DELEGATE_CANCEL_ALL | private static final int | REASON_DELEGATE_ERROR | private static final int | REASON_PACKAGE_CHANGED | private static final int | REASON_USER_STOPPED | private static final int | REASON_PACKAGE_BANNED | private static final int | REASON_NOMAN_CANCEL | private static final int | REASON_NOMAN_CANCEL_ALL | private static final int | REASON_LISTENER_CANCEL | private static final int | REASON_LISTENER_CANCEL_ALL | private static final int | REASON_GROUP_SUMMARY_CANCELED | private static final int | REASON_GROUP_OPTIMIZATION | private final NotificationDelegate | mNotificationDelegate | private final android.content.BroadcastReceiver | mPackageIntentReceiver | private final android.content.BroadcastReceiver | mIntentReceiver | private SettingsObserver | mSettingsObserver | private ZenModeHelper | mZenModeHelper | private final Runnable | mBuzzBeepBlinked | private final android.os.IBinder | mService | private final NotificationManagerInternal | mInternalServiceThe private API only accessible to the system process. |
Methods Summary |
---|
private void | applyZenModeLocked(NotificationRecord record)
record.setIntercepted(mZenModeHelper.shouldIntercept(record));
| private static android.media.AudioAttributes | audioAttributesForNotification(android.app.Notification n)
if (n.audioAttributes != null
&& !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
// the audio attributes are set and different from the default, use them
return n.audioAttributes;
} else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
// the stream type is valid, use it
return new AudioAttributes.Builder()
.setInternalLegacyStreamType(n.audioStreamType)
.build();
} else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
return Notification.AUDIO_ATTRIBUTES_DEFAULT;
} else {
Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
return Notification.AUDIO_ATTRIBUTES_DEFAULT;
}
| private void | buzzBeepBlinkLocked(NotificationRecord record)
boolean buzzBeepBlinked = false;
final Notification notification = record.sbn.getNotification();
// Should this notification make noise, vibe, or use the LED?
final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
if (DBG || record.isIntercepted())
Slog.v(TAG,
"pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
" intercept=" + record.isIntercepted()
);
final int currentUser;
final long token = Binder.clearCallingIdentity();
try {
currentUser = ActivityManager.getCurrentUser();
} finally {
Binder.restoreCallingIdentity(token);
}
// If we're not supposed to beep, vibrate, etc. then don't.
final String disableEffects = disableNotificationEffects(record);
if (disableEffects != null) {
ZenLog.traceDisableEffects(record, disableEffects);
}
if (disableEffects == null
&& (!(record.isUpdate
&& (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
&& (record.getUserId() == UserHandle.USER_ALL ||
record.getUserId() == currentUser ||
mUserProfiles.isCurrentProfile(record.getUserId()))
&& canInterrupt
&& mSystemReady
&& mAudioManager != null) {
if (DBG) Slog.v(TAG, "Interrupting!");
sendAccessibilityEvent(notification, record.sbn.getPackageName());
// sound
// should we use the default notification sound? (indicated either by
// DEFAULT_SOUND or because notification.sound is pointing at
// Settings.System.NOTIFICATION_SOUND)
final boolean useDefaultSound =
(notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
Settings.System.DEFAULT_NOTIFICATION_URI
.equals(notification.sound);
Uri soundUri = null;
boolean hasValidSound = false;
if (useDefaultSound) {
soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
// check to see if the default notification sound is silent
ContentResolver resolver = getContext().getContentResolver();
hasValidSound = Settings.System.getString(resolver,
Settings.System.NOTIFICATION_SOUND) != null;
} else if (notification.sound != null) {
soundUri = notification.sound;
hasValidSound = (soundUri != null);
}
if (hasValidSound) {
boolean looping =
(notification.flags & Notification.FLAG_INSISTENT) != 0;
AudioAttributes audioAttributes = audioAttributesForNotification(notification);
mSoundNotificationKey = record.getKey();
// do not play notifications if stream volume is 0 (typically because
// ringer mode is silent) or if there is a user of exclusive audio focus
if ((mAudioManager.getStreamVolume(
AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
&& !mAudioManager.isAudioFocusExclusive()) {
final long identity = Binder.clearCallingIdentity();
try {
final IRingtonePlayer player =
mAudioManager.getRingtonePlayer();
if (player != null) {
if (DBG) Slog.v(TAG, "Playing sound " + soundUri
+ " with attributes " + audioAttributes);
player.playAsync(soundUri, record.sbn.getUser(), looping,
audioAttributes);
buzzBeepBlinked = true;
}
} catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
// vibrate
// Does the notification want to specify its own vibration?
final boolean hasCustomVibrate = notification.vibrate != null;
// new in 4.2: if there was supposed to be a sound and we're in vibrate
// mode, and no other vibration is specified, we fall back to vibration
final boolean convertSoundToVibration =
!hasCustomVibrate
&& hasValidSound
&& (mAudioManager.getRingerModeInternal()
== AudioManager.RINGER_MODE_VIBRATE);
// The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
final boolean useDefaultVibrate =
(notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
&& !(mAudioManager.getRingerModeInternal()
== AudioManager.RINGER_MODE_SILENT)) {
mVibrateNotificationKey = record.getKey();
if (useDefaultVibrate || convertSoundToVibration) {
// Escalate privileges so we can use the vibrator even if the
// notifying app does not have the VIBRATE permission.
long identity = Binder.clearCallingIdentity();
try {
mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
useDefaultVibrate ? mDefaultVibrationPattern
: mFallbackVibrationPattern,
((notification.flags & Notification.FLAG_INSISTENT) != 0)
? 0: -1, audioAttributesForNotification(notification));
buzzBeepBlinked = true;
} finally {
Binder.restoreCallingIdentity(identity);
}
} else if (notification.vibrate.length > 1) {
// If you want your own vibration pattern, you need the VIBRATE
// permission
mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
notification.vibrate,
((notification.flags & Notification.FLAG_INSISTENT) != 0)
? 0: -1, audioAttributesForNotification(notification));
buzzBeepBlinked = true;
}
}
}
// light
// release the light
boolean wasShowLights = mLights.remove(record.getKey());
if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
mLights.add(record.getKey());
updateLightsLocked();
if (mUseAttentionLight) {
mAttentionLight.pulse();
}
buzzBeepBlinked = true;
} else if (wasShowLights) {
updateLightsLocked();
}
if (buzzBeepBlinked) {
mHandler.post(mBuzzBeepBlinked);
}
| private static java.lang.String | callStateToString(int state)
switch (state) {
case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
default: return "CALL_STATE_UNKNOWN_" + state;
}
| void | cancelAllLocked(int callingUid, int callingPid, int userId, int reason, com.android.server.notification.ManagedServices.ManagedServiceInfo listener, boolean includeCurrentProfiles)
String listenerName = listener == null ? null : listener.component.toShortString();
EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
null, userId, 0, 0, reason, listenerName);
ArrayList<NotificationRecord> canceledNotifications = null;
final int N = mNotificationList.size();
for (int i=N-1; i>=0; i--) {
NotificationRecord r = mNotificationList.get(i);
if (includeCurrentProfiles) {
if (!notificationMatchesCurrentProfiles(r, userId)) {
continue;
}
} else {
if (!notificationMatchesUserId(r, userId)) {
continue;
}
}
if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
| Notification.FLAG_NO_CLEAR)) == 0) {
mNotificationList.remove(i);
cancelNotificationLocked(r, true, reason);
// Make a note so we can cancel children later.
if (canceledNotifications == null) {
canceledNotifications = new ArrayList<>();
}
canceledNotifications.add(r);
}
}
int M = canceledNotifications != null ? canceledNotifications.size() : 0;
for (int i = 0; i < M; i++) {
cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
listenerName, REASON_GROUP_SUMMARY_CANCELED);
}
updateLightsLocked();
| boolean | cancelAllNotificationsInt(int callingUid, int callingPid, java.lang.String pkg, int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason, com.android.server.notification.ManagedServices.ManagedServiceInfo listener)Cancels all notifications from a given package that have all of the
{@code mustHaveFlags}.
String listenerName = listener == null ? null : listener.component.toShortString();
EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
listenerName);
synchronized (mNotificationList) {
final int N = mNotificationList.size();
ArrayList<NotificationRecord> canceledNotifications = null;
for (int i = N-1; i >= 0; --i) {
NotificationRecord r = mNotificationList.get(i);
if (!notificationMatchesUserId(r, userId)) {
continue;
}
// Don't remove notifications to all, if there's no package name specified
if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
continue;
}
if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
continue;
}
if ((r.getFlags() & mustNotHaveFlags) != 0) {
continue;
}
if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
continue;
}
if (canceledNotifications == null) {
canceledNotifications = new ArrayList<>();
}
canceledNotifications.add(r);
if (!doit) {
return true;
}
mNotificationList.remove(i);
cancelNotificationLocked(r, false, reason);
}
if (doit && canceledNotifications != null) {
final int M = canceledNotifications.size();
for (int i = 0; i < M; i++) {
cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
listenerName, REASON_GROUP_SUMMARY_CANCELED);
}
}
if (canceledNotifications != null) {
updateLightsLocked();
}
return canceledNotifications != null;
}
| private void | cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid, java.lang.String listenerName, int reason)
Notification n = r.getNotification();
if (!n.isGroupSummary()) {
return;
}
String pkg = r.sbn.getPackageName();
int userId = r.getUserId();
if (pkg == null) {
if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
return;
}
final int N = mNotificationList.size();
for (int i = N - 1; i >= 0; i--) {
NotificationRecord childR = mNotificationList.get(i);
StatusBarNotification childSbn = childR.sbn;
if (childR.getNotification().isGroupChild() &&
childR.getGroupKey().equals(r.getGroupKey())) {
EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
childSbn.getTag(), userId, 0, 0, reason, listenerName);
mNotificationList.remove(i);
cancelNotificationLocked(childR, false, reason);
}
}
| void | cancelNotification(int callingUid, int callingPid, java.lang.String pkg, java.lang.String tag, int id, int mustHaveFlags, int mustNotHaveFlags, boolean sendDelete, int userId, int reason, com.android.server.notification.ManagedServices.ManagedServiceInfo listener)Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
and none of the {@code mustNotHaveFlags}.
// In enqueueNotificationInternal notifications are added by scheduling the
// work on the worker handler. Hence, we also schedule the cancel on this
// handler to avoid a scenario where an add notification call followed by a
// remove notification call ends up in not removing the notification.
mHandler.post(new Runnable() {
@Override
public void run() {
String listenerName = listener == null ? null : listener.component.toShortString();
EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
mustHaveFlags, mustNotHaveFlags, reason, listenerName);
synchronized (mNotificationList) {
int index = indexOfNotificationLocked(pkg, tag, id, userId);
if (index >= 0) {
NotificationRecord r = mNotificationList.get(index);
// Ideally we'd do this in the caller of this method. However, that would
// require the caller to also find the notification.
if (reason == REASON_DELEGATE_CLICK) {
mUsageStats.registerClickedByUser(r);
}
if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
return;
}
if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
return;
}
mNotificationList.remove(index);
cancelNotificationLocked(r, sendDelete, reason);
cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
REASON_GROUP_SUMMARY_CANCELED);
updateLightsLocked();
}
}
}
});
| private void | cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason)
// tell the app
if (sendDelete) {
if (r.getNotification().deleteIntent != null) {
try {
r.getNotification().deleteIntent.send();
} catch (PendingIntent.CanceledException ex) {
// do nothing - there's no relevant way to recover, and
// no reason to let this propagate
Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
}
}
}
// status bar
if (r.getNotification().icon != 0) {
r.isCanceled = true;
mListeners.notifyRemovedLocked(r.sbn);
}
final String canceledKey = r.getKey();
// sound
if (canceledKey.equals(mSoundNotificationKey)) {
mSoundNotificationKey = null;
final long identity = Binder.clearCallingIdentity();
try {
final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
if (player != null) {
player.stopAsync();
}
} catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(identity);
}
}
// vibrate
if (canceledKey.equals(mVibrateNotificationKey)) {
mVibrateNotificationKey = null;
long identity = Binder.clearCallingIdentity();
try {
mVibrator.cancel();
}
finally {
Binder.restoreCallingIdentity(identity);
}
}
// light
mLights.remove(canceledKey);
// Record usage stats
switch (reason) {
case REASON_DELEGATE_CANCEL:
case REASON_DELEGATE_CANCEL_ALL:
case REASON_LISTENER_CANCEL:
case REASON_LISTENER_CANCEL_ALL:
mUsageStats.registerDismissedByUser(r);
break;
case REASON_NOMAN_CANCEL:
case REASON_NOMAN_CANCEL_ALL:
mUsageStats.registerRemovedByApp(r);
break;
case REASON_DELEGATE_CLICK:
mUsageStats.registerCancelDueToClick(r);
break;
default:
mUsageStats.registerCancelUnknown(r);
break;
}
mNotificationsByKey.remove(r.sbn.getKey());
String groupKey = r.getGroupKey();
NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
mSummaryByGroupKey.remove(groupKey);
}
// Save it for users of getHistoricalNotifications()
mArchive.record(r.sbn);
EventLogTags.writeNotificationCanceled(canceledKey, reason);
| void | cancelToastLocked(int index)
ToastRecord record = mToastQueue.get(index);
try {
record.callback.hide();
} catch (RemoteException e) {
Slog.w(TAG, "Object died trying to hide notification " + record.callback
+ " in package " + record.pkg);
// don't worry about this, we're about to remove it from
// the list anyway
}
mToastQueue.remove(index);
keepProcessAliveLocked(record.pid);
if (mToastQueue.size() > 0) {
// Show the next one. If the callback fails, this will remove
// it from the list, so don't assume that the list hasn't changed
// after this point.
showNextToastLocked();
}
| private static void | checkCallerIsSystem()
if (isCallerSystem()) {
return;
}
throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
| private static void | checkCallerIsSystemOrSameApp(java.lang.String pkg)
if (isCallerSystem()) {
return;
}
final int uid = Binder.getCallingUid();
try {
ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
pkg, 0, UserHandle.getCallingUserId());
if (ai == null) {
throw new SecurityException("Unknown package " + pkg);
}
if (!UserHandle.isSameApp(ai.uid, uid)) {
throw new SecurityException("Calling uid " + uid + " gave package"
+ pkg + " which is owned by uid " + ai.uid);
}
} catch (RemoteException re) {
throw new SecurityException("Unknown package " + pkg + "\n" + re);
}
| static int | clamp(int x, int low, int high)
return (x < low) ? low : ((x > high) ? high : x);
| private java.lang.String | disableNotificationEffects(NotificationRecord record)
if (mDisableNotificationEffects) {
return "booleanState";
}
if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
return "listenerHints";
}
if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
return "callState";
}
return null;
| void | dumpImpl(java.io.PrintWriter pw, com.android.server.notification.NotificationManagerService$DumpFilter filter)
pw.print("Current Notification Manager state");
if (filter != null) {
pw.print(" (filtered to "); pw.print(filter); pw.print(")");
}
pw.println(':");
int N;
final boolean zenOnly = filter != null && filter.zen;
if (!zenOnly) {
synchronized (mToastQueue) {
N = mToastQueue.size();
if (N > 0) {
pw.println(" Toast Queue:");
for (int i=0; i<N; i++) {
mToastQueue.get(i).dump(pw, " ", filter);
}
pw.println(" ");
}
}
}
synchronized (mNotificationList) {
if (!zenOnly) {
N = mNotificationList.size();
if (N > 0) {
pw.println(" Notification List:");
for (int i=0; i<N; i++) {
final NotificationRecord nr = mNotificationList.get(i);
if (filter != null && !filter.matches(nr.sbn)) continue;
nr.dump(pw, " ", getContext());
}
pw.println(" ");
}
if (filter == null) {
N = mLights.size();
if (N > 0) {
pw.println(" Lights List:");
for (int i=0; i<N; i++) {
if (i == N - 1) {
pw.print(" > ");
} else {
pw.print(" ");
}
pw.println(mLights.get(i));
}
pw.println(" ");
}
pw.println(" mUseAttentionLight=" + mUseAttentionLight);
pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
pw.println(" mCallState=" + callStateToString(mCallState));
pw.println(" mSystemReady=" + mSystemReady);
}
pw.println(" mArchive=" + mArchive.toString());
Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
int i=0;
while (iter.hasNext()) {
final StatusBarNotification sbn = iter.next();
if (filter != null && !filter.matches(sbn)) continue;
pw.println(" " + sbn);
if (++i >= 5) {
if (iter.hasNext()) pw.println(" ...");
break;
}
}
}
if (!zenOnly) {
pw.println("\n Usage Stats:");
mUsageStats.dump(pw, " ", filter);
}
if (filter == null || zenOnly) {
pw.println("\n Zen Mode:");
pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
mZenModeHelper.dump(pw, " ");
pw.println("\n Zen Log:");
ZenLog.dump(pw, " ");
}
if (!zenOnly) {
pw.println("\n Ranking Config:");
mRankingHelper.dump(pw, " ", filter);
pw.println("\n Notification listeners:");
mListeners.dump(pw, filter);
pw.print(" mListenerHints: "); pw.println(mListenerHints);
pw.print(" mListenersDisablingEffects: (");
N = mListenersDisablingEffects.size();
for (int i = 0; i < N; i++) {
final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
if (i > 0) pw.print(',");
pw.print(listener.component);
}
pw.println(')");
}
pw.println("\n Condition providers:");
mConditionProviders.dump(pw, filter);
pw.println("\n Group summaries:");
for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
NotificationRecord r = entry.getValue();
pw.println(" " + entry.getKey() + " -> " + r.getKey());
if (mNotificationsByKey.get(r.getKey()) != r) {
pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
r.dump(pw, " ", getContext());
}
}
}
| void | enqueueNotificationInternal(java.lang.String pkg, java.lang.String opPkg, int callingUid, int callingPid, java.lang.String tag, int id, android.app.Notification notification, int[] idOut, int incomingUserId)
if (DBG) {
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
+ " notification=" + notification);
}
checkCallerIsSystemOrSameApp(pkg);
final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
final int userId = ActivityManager.handleIncomingUser(callingPid,
callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
final UserHandle user = new UserHandle(userId);
// Limit the number of notifications that any given package except the android
// package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationList) {
int count = 0;
final int N = mNotificationList.size();
for (int i=0; i<N; i++) {
final NotificationRecord r = mNotificationList.get(i);
if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
count++;
if (count >= MAX_PACKAGE_NOTIFICATIONS) {
Slog.e(TAG, "Package has already posted " + count
+ " notifications. Not showing more. package=" + pkg);
return;
}
}
}
}
}
if (pkg == null || notification == null) {
throw new IllegalArgumentException("null not allowed: pkg=" + pkg
+ " id=" + id + " notification=" + notification);
}
if (notification.icon != 0) {
if (!notification.isValid()) {
throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
+ " id=" + id + " notification=" + notification);
}
}
mHandler.post(new Runnable() {
@Override
public void run() {
synchronized (mNotificationList) {
// === Scoring ===
// 0. Sanitize inputs
notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
Notification.PRIORITY_MAX);
// Migrate notification flags to scores
if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
if (notification.priority < Notification.PRIORITY_MAX) {
notification.priority = Notification.PRIORITY_MAX;
}
} else if (SCORE_ONGOING_HIGHER &&
0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
if (notification.priority < Notification.PRIORITY_HIGH) {
notification.priority = Notification.PRIORITY_HIGH;
}
}
// 1. initial score: buckets of 10, around the app [-20..20]
final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
// 2. extract ranking signals from the notification data
final StatusBarNotification n = new StatusBarNotification(
pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
user);
NotificationRecord r = new NotificationRecord(n, score);
NotificationRecord old = mNotificationsByKey.get(n.getKey());
if (old != null) {
// Retain ranking information from previous record
r.copyRankingInformation(old);
}
// Handle grouped notifications and bail out early if we
// can to avoid extracting signals.
handleGroupedNotificationLocked(r, old, callingUid, callingPid);
boolean ignoreNotification =
removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
// This conditional is a dirty hack to limit the logging done on
// behalf of the download manager without affecting other apps.
if (!pkg.equals("com.android.providers.downloads")
|| Log.isLoggable("DownloadManager", Log.VERBOSE)) {
int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
if (ignoreNotification) {
enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
} else if (old != null) {
enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
}
EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
pkg, id, tag, userId, notification.toString(),
enqueueStatus);
}
if (ignoreNotification) {
return;
}
mRankingHelper.extractSignals(r);
// 3. Apply local rules
// blocked apps
if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
if (!isSystemNotification) {
r.score = JUNK_SCORE;
Slog.e(TAG, "Suppressing notification from package " + pkg
+ " by user request.");
}
}
if (r.score < SCORE_DISPLAY_THRESHOLD) {
// Notification will be blocked because the score is too low.
return;
}
int index = indexOfNotificationLocked(n.getKey());
if (index < 0) {
mNotificationList.add(r);
mUsageStats.registerPostedByApp(r);
} else {
old = mNotificationList.get(index);
mNotificationList.set(index, r);
mUsageStats.registerUpdatedByApp(r, old);
// Make sure we don't lose the foreground service state.
notification.flags |=
old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
r.isUpdate = true;
}
mNotificationsByKey.put(n.getKey(), r);
// Ensure if this is a foreground service that the proper additional
// flags are set.
if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
notification.flags |= Notification.FLAG_ONGOING_EVENT
| Notification.FLAG_NO_CLEAR;
}
applyZenModeLocked(r);
mRankingHelper.sort(mNotificationList);
if (notification.icon != 0) {
StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
mListeners.notifyPostedLocked(n, oldSbn);
} else {
Slog.e(TAG, "Not posting notification with icon==0: " + notification);
if (old != null && !old.isCanceled) {
mListeners.notifyRemovedLocked(n);
}
// ATTENTION: in a future release we will bail out here
// so that we do not play sounds, show lights, etc. for invalid
// notifications
Slog.e(TAG, "WARNING: In a future release this will crash the app: "
+ n.getPackageName());
}
buzzBeepBlinkLocked(r);
}
}
});
idOut[0] = id;
| private int | findNotificationRecordIndexLocked(NotificationRecord target)
return mRankingHelper.indexOf(mNotificationList, target);
| private java.lang.String[] | getActiveNotificationKeys(android.service.notification.INotificationListener token)
final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
final ArrayList<String> keys = new ArrayList<String>();
if (info.isEnabledForCurrentProfiles()) {
synchronized (mNotificationList) {
final int N = mNotificationList.size();
for (int i = 0; i < N; i++) {
final StatusBarNotification sbn = mNotificationList.get(i).sbn;
if (info.enabledAndUserMatches(sbn.getUserId())) {
keys.add(sbn.getKey());
}
}
}
}
return keys.toArray(new String[keys.size()]);
| static long[] | getLongArray(android.content.res.Resources r, int resid, int maxlen, long[] def)
int[] ar = r.getIntArray(resid);
if (ar == null) {
return def;
}
final int len = ar.length > maxlen ? maxlen : ar.length;
long[] out = new long[len];
for (int i=0; i<len; i++) {
out[i] = ar[i];
}
return out;
| private void | handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old, int callingUid, int callingPid)Ensures that grouped notification receive their special treatment.
Cancels group children if the new notification causes a group to lose
its summary.
Updates mSummaryByGroupKey.
StatusBarNotification sbn = r.sbn;
Notification n = sbn.getNotification();
String group = sbn.getGroupKey();
boolean isSummary = n.isGroupSummary();
Notification oldN = old != null ? old.sbn.getNotification() : null;
String oldGroup = old != null ? old.sbn.getGroupKey() : null;
boolean oldIsSummary = old != null && oldN.isGroupSummary();
if (oldIsSummary) {
NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
if (removedSummary != old) {
String removedKey =
removedSummary != null ? removedSummary.getKey() : "<null>";
Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
", removed=" + removedKey);
}
}
if (isSummary) {
mSummaryByGroupKey.put(group, r);
}
// Clear out group children of the old notification if the update
// causes the group summary to go away. This happens when the old
// notification was a summary and the new one isn't, or when the old
// notification was a summary and its group key changed.
if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
cancelGroupChildrenLocked(old, callingUid, callingPid, null,
REASON_GROUP_SUMMARY_CANCELED);
}
| private void | handleListenerHintsChanged(int hints)
synchronized (mNotificationList) {
mListeners.notifyListenerHintsChangedLocked(hints);
}
| private void | handleListenerInterruptionFilterChanged(int interruptionFilter)
synchronized (mNotificationList) {
mListeners.notifyInterruptionFilterChanged(interruptionFilter);
}
| private void | handleRankingConfigChange()
synchronized (mNotificationList) {
final int N = mNotificationList.size();
ArrayList<String> orderBefore = new ArrayList<String>(N);
int[] visibilities = new int[N];
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
orderBefore.add(r.getKey());
visibilities[i] = r.getPackageVisibilityOverride();
mRankingHelper.extractSignals(r);
}
for (int i = 0; i < N; i++) {
mRankingHelper.sort(mNotificationList);
final NotificationRecord r = mNotificationList.get(i);
if (!orderBefore.get(i).equals(r.getKey())
|| visibilities[i] != r.getPackageVisibilityOverride()) {
scheduleSendRankingUpdate();
return;
}
}
}
| private void | handleRankingReconsideration(android.os.Message message)
if (!(message.obj instanceof RankingReconsideration)) return;
RankingReconsideration recon = (RankingReconsideration) message.obj;
recon.run();
boolean changed;
synchronized (mNotificationList) {
final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
if (record == null) {
return;
}
int indexBefore = findNotificationRecordIndexLocked(record);
boolean interceptBefore = record.isIntercepted();
int visibilityBefore = record.getPackageVisibilityOverride();
recon.applyChangesLocked(record);
applyZenModeLocked(record);
mRankingHelper.sort(mNotificationList);
int indexAfter = findNotificationRecordIndexLocked(record);
boolean interceptAfter = record.isIntercepted();
int visibilityAfter = record.getPackageVisibilityOverride();
changed = indexBefore != indexAfter || interceptBefore != interceptAfter
|| visibilityBefore != visibilityAfter;
if (interceptBefore && !interceptAfter) {
buzzBeepBlinkLocked(record);
}
}
if (changed) {
scheduleSendRankingUpdate();
}
| private void | handleSavePolicyFile()
Slog.d(TAG, "handleSavePolicyFile");
synchronized (mPolicyFile) {
final FileOutputStream stream;
try {
stream = mPolicyFile.startWrite();
} catch (IOException e) {
Slog.w(TAG, "Failed to save policy file", e);
return;
}
try {
final XmlSerializer out = new FastXmlSerializer();
out.setOutput(stream, "utf-8");
out.startDocument(null, true);
out.startTag(null, TAG_BODY);
out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
mZenModeHelper.writeXml(out);
mRankingHelper.writeXml(out);
out.endTag(null, TAG_BODY);
out.endDocument();
mPolicyFile.finishWrite(stream);
} catch (IOException e) {
Slog.w(TAG, "Failed to save policy file, restoring backup", e);
mPolicyFile.failWrite(stream);
}
}
| private void | handleSendRankingUpdate()
synchronized (mNotificationList) {
mListeners.notifyRankingUpdateLocked();
}
| private void | handleTimeout(com.android.server.notification.NotificationManagerService$ToastRecord record)
if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
synchronized (mToastQueue) {
int index = indexOfToastLocked(record.pkg, record.callback);
if (index >= 0) {
cancelToastLocked(index);
}
}
| private void | importOldBlockDb()Read the old XML-based app block database and import those blockages into the AppOps system.
loadPolicyFile();
PackageManager pm = getContext().getPackageManager();
for (String pkg : mBlockedPackages) {
PackageInfo info = null;
try {
info = pm.getPackageInfo(pkg, 0);
setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
} catch (NameNotFoundException e) {
// forget you
}
}
mBlockedPackages.clear();
| int | indexOfNotificationLocked(java.lang.String pkg, java.lang.String tag, int id, int userId)
ArrayList<NotificationRecord> list = mNotificationList;
final int len = list.size();
for (int i=0; i<len; i++) {
NotificationRecord r = list.get(i);
if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
continue;
}
if (tag == null) {
if (r.sbn.getTag() != null) {
continue;
}
} else {
if (!tag.equals(r.sbn.getTag())) {
continue;
}
}
if (r.sbn.getPackageName().equals(pkg)) {
return i;
}
}
return -1;
| int | indexOfNotificationLocked(java.lang.String key)
final int N = mNotificationList.size();
for (int i = 0; i < N; i++) {
if (key.equals(mNotificationList.get(i).getKey())) {
return i;
}
}
return -1;
| int | indexOfToastLocked(java.lang.String pkg, android.app.ITransientNotification callback)
IBinder cbak = callback.asBinder();
ArrayList<ToastRecord> list = mToastQueue;
int len = list.size();
for (int i=0; i<len; i++) {
ToastRecord r = list.get(i);
if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
return i;
}
}
return -1;
| private static boolean | isCallerSystem()
return isUidSystem(Binder.getCallingUid());
| private static boolean | isUidSystem(int uid)
final int appid = UserHandle.getAppId(uid);
return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
| private boolean | isVisibleToListener(android.service.notification.StatusBarNotification sbn, com.android.server.notification.ManagedServices.ManagedServiceInfo listener)
if (!listener.enabledAndUserMatches(sbn.getUserId())) {
return false;
}
// TODO: remove this for older listeners.
return true;
| void | keepProcessAliveLocked(int pid)
int toastCount = 0; // toasts from this pid
ArrayList<ToastRecord> list = mToastQueue;
int N = list.size();
for (int i=0; i<N; i++) {
ToastRecord r = list.get(i);
if (r.pid == pid) {
toastCount++;
}
}
try {
mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
} catch (RemoteException e) {
// Shouldn't happen.
}
| private void | listenForCallState()
TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (mCallState == state) return;
if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
mCallState = state;
}
}, PhoneStateListener.LISTEN_CALL_STATE);
| private void | loadPolicyFile()
synchronized(mPolicyFile) {
mBlockedPackages.clear();
FileInputStream infile = null;
try {
infile = mPolicyFile.openRead();
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(infile, null);
int type;
String tag;
int version = DB_VERSION;
while ((type = parser.next()) != END_DOCUMENT) {
tag = parser.getName();
if (type == START_TAG) {
if (TAG_BODY.equals(tag)) {
version = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VERSION));
} else if (TAG_BLOCKED_PKGS.equals(tag)) {
while ((type = parser.next()) != END_DOCUMENT) {
tag = parser.getName();
if (TAG_PACKAGE.equals(tag)) {
mBlockedPackages.add(
parser.getAttributeValue(null, ATTR_NAME));
} else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
break;
}
}
}
}
mZenModeHelper.readXml(parser);
mRankingHelper.readXml(parser);
}
} catch (FileNotFoundException e) {
// No data yet
} catch (IOException e) {
Log.wtf(TAG, "Unable to read notification policy", e);
} catch (NumberFormatException e) {
Log.wtf(TAG, "Unable to parse notification policy", e);
} catch (XmlPullParserException e) {
Log.wtf(TAG, "Unable to parse notification policy", e);
} finally {
IoUtils.closeQuietly(infile);
}
}
| private android.service.notification.NotificationRankingUpdate | makeRankingUpdateLocked(com.android.server.notification.ManagedServices.ManagedServiceInfo info)Generates a NotificationRankingUpdate from 'sbns', considering only
notifications visible to the given listener.
Caller must hold a lock on mNotificationList.
int speedBumpIndex = -1;
final int N = mNotificationList.size();
ArrayList<String> keys = new ArrayList<String>(N);
ArrayList<String> interceptedKeys = new ArrayList<String>(N);
Bundle visibilityOverrides = new Bundle();
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
if (!isVisibleToListener(record.sbn, info)) {
continue;
}
keys.add(record.sbn.getKey());
if (record.isIntercepted()) {
interceptedKeys.add(record.sbn.getKey());
}
if (record.getPackageVisibilityOverride()
!= NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
visibilityOverrides.putInt(record.sbn.getKey(),
record.getPackageVisibilityOverride());
}
// Find first min-prio notification for speedbump placement.
if (speedBumpIndex == -1 &&
// Intrusiveness trumps priority, hence ignore intrusives.
!record.isRecentlyIntrusive() &&
// Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
// scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
// (or lower as a safeguard) is sufficient to find the speedbump index.
// We'll have to revisit this when more package priority buckets are introduced.
record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
speedBumpIndex = keys.size() - 1;
}
}
String[] keysAr = keys.toArray(new String[keys.size()]);
String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
speedBumpIndex);
| private boolean | noteNotificationOp(java.lang.String pkg, int uid)Use this when you actually want to post a notification or toast.
Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
!= AppOpsManager.MODE_ALLOWED) {
Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
return false;
}
return true;
| private boolean | notificationMatchesCurrentProfiles(NotificationRecord r, int userId)Determine whether the userId applies to the notification in question, either because
they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
because it matches one of the users profiles.
return notificationMatchesUserId(r, userId)
|| mUserProfiles.isCurrentProfile(r.getUserId());
| private boolean | notificationMatchesUserId(NotificationRecord r, int userId)Determine whether the userId applies to the notification in question, either because
they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
return
// looking for USER_ALL notifications? match everything
userId == UserHandle.USER_ALL
// a notification sent to USER_ALL matches any query
|| r.getUserId() == UserHandle.USER_ALL
// an exact user match
|| r.getUserId() == userId;
| public void | onBootPhase(int phase)
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
// no beeping until we're basically done booting
mSystemReady = true;
// Grab our optional AudioService
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mZenModeHelper.onSystemReady();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
// This observer will force an update when observe is called, causing us to
// bind to listener services.
mSettingsObserver.observe();
mListeners.onBootPhaseAppsCanStart();
mConditionProviders.onBootPhaseAppsCanStart();
}
| public void | onStart()
Resources resources = getContext().getResources();
mAm = ActivityManagerNative.getDefault();
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
mHandler = new WorkerHandler();
mRankingThread.start();
String[] extractorNames;
try {
extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
} catch (Resources.NotFoundException e) {
extractorNames = new String[0];
}
mRankingHelper = new RankingHelper(getContext(),
new RankingWorkerHandler(mRankingThread.getLooper()),
extractorNames);
mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper());
mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
public void onConfigChanged() {
savePolicyFile();
}
@Override
void onZenModeChanged() {
synchronized(mNotificationList) {
updateInterruptionFilterLocked();
}
}
});
final File systemDir = new File(Environment.getDataDirectory(), "system");
mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
mUsageStats = new NotificationUsageStats(getContext());
importOldBlockDb();
mListeners = new NotificationListeners();
mConditionProviders = new ConditionProviders(getContext(),
mHandler, mUserProfiles, mZenModeHelper);
mStatusBar = getLocalService(StatusBarManagerInternal.class);
mStatusBar.setNotificationDelegate(mNotificationDelegate);
final LightsManager lights = getLocalService(LightsManager.class);
mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
mDefaultNotificationColor = resources.getColor(
R.color.config_defaultNotificationColor);
mDefaultNotificationLedOn = resources.getInteger(
R.integer.config_defaultNotificationLedOn);
mDefaultNotificationLedOff = resources.getInteger(
R.integer.config_defaultNotificationLedOff);
mDefaultVibrationPattern = getLongArray(resources,
R.array.config_defaultNotificationVibePattern,
VIBRATE_PATTERN_MAXLEN,
DEFAULT_VIBRATE_PATTERN);
mFallbackVibrationPattern = getLongArray(resources,
R.array.config_notificationFallbackVibePattern,
VIBRATE_PATTERN_MAXLEN,
DEFAULT_VIBRATE_PATTERN);
mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
// Don't start allowing notifications until the setup wizard has run once.
// After that, including subsequent boots, init with notifications turned on.
// This works on the first boot because the setup wizard will toggle this
// flag at least once and we'll go back to 0 after that.
if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0)) {
mDisableNotificationEffects = true;
}
mZenModeHelper.readZenModeFromSetting();
mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
mUserProfiles.updateCache(getContext());
listenForCallState();
// register for various Intents
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(Intent.ACTION_USER_PRESENT);
filter.addAction(Intent.ACTION_USER_STOPPED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
getContext().registerReceiver(mIntentReceiver, filter);
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
pkgFilter.addDataScheme("package");
getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
null);
IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
null);
mSettingsObserver = new SettingsObserver(mHandler);
mArchive = new Archive(resources.getInteger(
R.integer.config_notificationServiceArchiveSize));
publishBinderService(Context.NOTIFICATION_SERVICE, mService);
publishLocalService(NotificationManagerInternal.class, mInternalService);
| private boolean | removeUnusedGroupedNotificationLocked(NotificationRecord r, NotificationRecord old, int callingUid, int callingPid)Performs group notification optimizations if SysUI is the only active
notification listener and returns whether the given notification should
be ignored.
Returns true if the given notification is a child of a group with a
summary, which means that SysUI will never show it, and hence the new
notification can be safely ignored. Also cancels any previous instance
of the ignored notification.
For summaries, cancels all children of that group, as SysUI will
never show them anymore.
// No optimizations are possible if listeners want groups.
if (mListeners.notificationGroupsDesired()) {
return false;
}
StatusBarNotification sbn = r.sbn;
String group = sbn.getGroupKey();
boolean isSummary = sbn.getNotification().isGroupSummary();
boolean isChild = sbn.getNotification().isGroupChild();
NotificationRecord summary = mSummaryByGroupKey.get(group);
if (isChild && summary != null) {
// Child with an active summary -> ignore
if (DBG) {
Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
+ summary.getKey());
}
// Make sure we don't leave an old version of the notification around.
if (old != null) {
if (DBG) {
Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
}
cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
}
return true;
} else if (isSummary) {
// Summary -> cancel children
cancelGroupChildrenLocked(r, callingUid, callingPid, null,
REASON_GROUP_OPTIMIZATION);
}
return false;
| public void | savePolicyFile()
mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
| private void | scheduleInterruptionFilterChanged(int listenerInterruptionFilter)
mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
mHandler.obtainMessage(
MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
listenerInterruptionFilter,
0).sendToTarget();
| private void | scheduleListenerHintsChanged(int state)
mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
| private void | scheduleSendRankingUpdate()
mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
mHandler.sendMessage(m);
| private void | scheduleTimeoutLocked(com.android.server.notification.NotificationManagerService$ToastRecord r)
mHandler.removeCallbacksAndMessages(r);
Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
mHandler.sendMessageDelayed(m, delay);
| void | sendAccessibilityEvent(android.app.Notification notification, java.lang.CharSequence packageName)
AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
if (!manager.isEnabled()) {
return;
}
AccessibilityEvent event =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
event.setPackageName(packageName);
event.setClassName(Notification.class.getName());
event.setParcelableData(notification);
CharSequence tickerText = notification.tickerText;
if (!TextUtils.isEmpty(tickerText)) {
event.getText().add(tickerText);
}
manager.sendAccessibilityEvent(event);
| void | setNotificationsEnabledForPackageImpl(java.lang.String pkg, int uid, boolean enabled)
Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
// Now, cancel any outstanding notifications that are part of a just-disabled app
if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
REASON_PACKAGE_BANNED, null);
}
| void | showNextToastLocked()
ToastRecord record = mToastQueue.get(0);
while (record != null) {
if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
try {
record.callback.show();
scheduleTimeoutLocked(record);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Object died trying to show notification " + record.callback
+ " in package " + record.pkg);
// remove it from the list and let the process die
int index = mToastQueue.indexOf(record);
if (index >= 0) {
mToastQueue.remove(index);
}
keepProcessAliveLocked(record.pid);
if (mToastQueue.size() > 0) {
record = mToastQueue.get(0);
} else {
record = null;
}
}
}
| private void | updateEffectsSuppressorLocked()
final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
? mListenersDisablingEffects.valueAt(0).component : null;
if (Objects.equals(suppressor, mEffectsSuppressor)) return;
mEffectsSuppressor = suppressor;
mZenModeHelper.setEffectsSuppressed(suppressor != null);
getContext().sendBroadcast(new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
| private void | updateInterruptionFilterLocked()
int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
if (interruptionFilter == mInterruptionFilter) return;
mInterruptionFilter = interruptionFilter;
scheduleInterruptionFilterChanged(interruptionFilter);
| void | updateLightsLocked()
// handle notification lights
NotificationRecord ledNotification = null;
while (ledNotification == null && !mLights.isEmpty()) {
final String owner = mLights.get(mLights.size() - 1);
ledNotification = mNotificationsByKey.get(owner);
if (ledNotification == null) {
Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
mLights.remove(owner);
}
}
// Don't flash while we are in a call or screen is on
if (ledNotification == null || mInCall || mScreenOn) {
mNotificationLight.turnOff();
mStatusBar.notificationLightOff();
} else {
final Notification ledno = ledNotification.sbn.getNotification();
int ledARGB = ledno.ledARGB;
int ledOnMS = ledno.ledOnMS;
int ledOffMS = ledno.ledOffMS;
if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
ledARGB = mDefaultNotificationColor;
ledOnMS = mDefaultNotificationLedOn;
ledOffMS = mDefaultNotificationLedOff;
}
if (mNotificationPulseEnabled) {
// pulse repeatedly
mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
ledOnMS, ledOffMS);
}
// let SystemUI make an independent decision
mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
}
| private void | updateListenerHintsLocked()
final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
if (hints == mListenerHints) return;
mListenerHints = hints;
scheduleListenerHintsChanged(hints);
| private void | updateNotificationPulse()
synchronized (mNotificationList) {
updateLightsLocked();
}
|
|