FileDocCategorySizeDatePackage
NotificationManagerService.javaAPI DocAndroid 5.1 API138437Thu Mar 12 22:22:42 GMT 2015com.android.server.notification

NotificationManagerService

public class NotificationManagerService extends com.android.server.SystemService
{@hide}

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_NEW
notification_enqueue status value for a newly enqueued notification.
private static final int
EVENTLOG_ENQUEUE_STATUS_UPDATE
notification_enqueue status value for an existing notification.
private static final int
EVENTLOG_ENQUEUE_STATUS_IGNORED
notification_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
mInternalService
The private API only accessible to the system process.
Constructors Summary
public NotificationManagerService(android.content.Context context)

        super(context);
    
Methods Summary
private voidapplyZenModeLocked(NotificationRecord record)

        record.setIntercepted(mZenModeHelper.shouldIntercept(record));
    
private static android.media.AudioAttributesaudioAttributesForNotification(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 voidbuzzBeepBlinkLocked(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.StringcallStateToString(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;
        }
    
voidcancelAllLocked(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();
    
booleancancelAllNotificationsInt(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 voidcancelGroupChildrenLocked(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);
            }
        }
    
voidcancelNotification(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 voidcancelNotificationLocked(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);
    
voidcancelToastLocked(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 voidcheckCallerIsSystem()

        if (isCallerSystem()) {
            return;
        }
        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
    
private static voidcheckCallerIsSystemOrSameApp(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 intclamp(int x, int low, int high)

        return (x < low) ? low : ((x > high) ? high : x);
    
private java.lang.StringdisableNotificationEffects(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;
    
voiddumpImpl(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());
                }
            }
        }
    
voidenqueueNotificationInternal(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 intfindNotificationRecordIndexLocked(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 voidhandleGroupedNotificationLocked(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 voidhandleListenerHintsChanged(int hints)

        synchronized (mNotificationList) {
            mListeners.notifyListenerHintsChangedLocked(hints);
        }
    
private voidhandleListenerInterruptionFilterChanged(int interruptionFilter)

        synchronized (mNotificationList) {
            mListeners.notifyInterruptionFilterChanged(interruptionFilter);
        }
    
private voidhandleRankingConfigChange()

        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 voidhandleRankingReconsideration(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 voidhandleSavePolicyFile()

        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 voidhandleSendRankingUpdate()

        synchronized (mNotificationList) {
            mListeners.notifyRankingUpdateLocked();
        }
    
private voidhandleTimeout(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 voidimportOldBlockDb()
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();
    
intindexOfNotificationLocked(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;
    
intindexOfNotificationLocked(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;
    
intindexOfToastLocked(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 booleanisCallerSystem()

        return isUidSystem(Binder.getCallingUid());
    
private static booleanisUidSystem(int uid)

        final int appid = UserHandle.getAppId(uid);
        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
    
private booleanisVisibleToListener(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;
    
voidkeepProcessAliveLocked(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 voidlistenForCallState()

        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 voidloadPolicyFile()

        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.NotificationRankingUpdatemakeRankingUpdateLocked(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 booleannoteNotificationOp(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 booleannotificationMatchesCurrentProfiles(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 booleannotificationMatchesUserId(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 voidonBootPhase(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 voidonStart()

        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 booleanremoveUnusedGroupedNotificationLocked(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.

return
true if the given notification can be ignored as an optimization

        // 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 voidsavePolicyFile()

        mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
        mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
    
private voidscheduleInterruptionFilterChanged(int listenerInterruptionFilter)

        mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
        mHandler.obtainMessage(
                MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
                listenerInterruptionFilter,
                0).sendToTarget();
    
private voidscheduleListenerHintsChanged(int state)

        mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
        mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
    
private voidscheduleSendRankingUpdate()

        mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
        Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
        mHandler.sendMessage(m);
    
private voidscheduleTimeoutLocked(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);
    
voidsendAccessibilityEvent(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);
    
voidsetNotificationsEnabledForPackageImpl(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);
        }
    
voidshowNextToastLocked()

        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 voidupdateEffectsSuppressorLocked()

        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 voidupdateInterruptionFilterLocked()

        int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
        if (interruptionFilter == mInterruptionFilter) return;
        mInterruptionFilter = interruptionFilter;
        scheduleInterruptionFilterChanged(interruptionFilter);
    
voidupdateLightsLocked()

        // 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 voidupdateListenerHintsLocked()

        final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
        if (hints == mListenerHints) return;
        mListenerHints = hints;
        scheduleListenerHintsChanged(hints);
    
private voidupdateNotificationPulse()

        synchronized (mNotificationList) {
            updateLightsLocked();
        }