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

ZenModeHelper

public class ZenModeHelper extends Object implements AudioManagerInternal.RingerModeDelegate
NotificationManagerService helper for functionality related to zen mode.

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private final android.content.Context
mContext
private final H
mHandler
private final SettingsObserver
mSettingsObserver
private final android.app.AppOpsManager
mAppOps
private final android.service.notification.ZenModeConfig
mDefaultConfig
private final ArrayList
mCallbacks
private android.content.ComponentName
mDefaultPhoneApp
private int
mZenMode
private android.service.notification.ZenModeConfig
mConfig
private android.media.AudioManagerInternal
mAudioManager
private int
mPreviousRingerMode
private boolean
mEffectsSuppressed
Constructors Summary
public ZenModeHelper(android.content.Context context, android.os.Looper looper)


         
        mContext = context;
        mHandler = new H(looper);
        mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        mDefaultConfig = readDefaultConfig(context.getResources());
        mConfig = mDefaultConfig;
        mSettingsObserver = new SettingsObserver(mHandler);
        mSettingsObserver.observe();
    
Methods Summary
public voidaddCallback(com.android.server.notification.ZenModeHelper$Callback callback)

        mCallbacks.add(callback);
    
private voidapplyRestrictions()

        final boolean zen = mZenMode != Global.ZEN_MODE_OFF;

        // notification restrictions
        final boolean muteNotifications = mEffectsSuppressed;
        applyRestrictions(muteNotifications, USAGE_NOTIFICATION);

        // call restrictions
        final boolean muteCalls = zen && !mConfig.allowCalls || mEffectsSuppressed;
        applyRestrictions(muteCalls, USAGE_NOTIFICATION_RINGTONE);

        // alarm restrictions
        final boolean muteAlarms = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
        applyRestrictions(muteAlarms, USAGE_ALARM);
    
private voidapplyRestrictions(boolean mute, int usage)

        final String[] exceptionPackages = null; // none (for now)
        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, usage,
                mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
                exceptionPackages);
        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, usage,
                mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
                exceptionPackages);
    
private voidapplyZenToRingerMode()

        if (mAudioManager == null) return;
        // force the ringer mode into compliance
        final int ringerModeInternal = mAudioManager.getRingerModeInternal();
        int newRingerModeInternal = ringerModeInternal;
        switch (mZenMode) {
            case Global.ZEN_MODE_NO_INTERRUPTIONS:
                if (ringerModeInternal != AudioManager.RINGER_MODE_SILENT) {
                    mPreviousRingerMode = ringerModeInternal;
                    newRingerModeInternal = AudioManager.RINGER_MODE_SILENT;
                }
                break;
            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
            case Global.ZEN_MODE_OFF:
                if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) {
                    newRingerModeInternal = mPreviousRingerMode != -1 ? mPreviousRingerMode
                            : AudioManager.RINGER_MODE_NORMAL;
                    mPreviousRingerMode = -1;
                }
                break;
        }
        if (newRingerModeInternal != -1) {
            mAudioManager.setRingerModeInternal(newRingerModeInternal, TAG);
        }
    
private booleanaudienceMatches(float contactAffinity)

        switch (mConfig.allowFrom) {
            case ZenModeConfig.SOURCE_ANYONE:
                return true;
            case ZenModeConfig.SOURCE_CONTACT:
                return contactAffinity >= ValidateNotificationPeople.VALID_CONTACT;
            case ZenModeConfig.SOURCE_STAR:
                return contactAffinity >= ValidateNotificationPeople.STARRED_CONTACT;
            default:
                Slog.w(TAG, "Encountered unknown source: " + mConfig.allowFrom);
                return true;
        }
    
private voiddispatchOnConfigChanged()

        for (Callback callback : mCallbacks) {
            callback.onConfigChanged();
        }
    
private voiddispatchOnZenModeChanged()

        for (Callback callback : mCallbacks) {
            callback.onZenModeChanged();
        }
    
public voiddump(java.io.PrintWriter pw, java.lang.String prefix)

        pw.print(prefix); pw.print("mZenMode=");
        pw.println(Global.zenModeToString(mZenMode));
        pw.print(prefix); pw.print("mConfig="); pw.println(mConfig);
        pw.print(prefix); pw.print("mDefaultConfig="); pw.println(mDefaultConfig);
        pw.print(prefix); pw.print("mPreviousRingerMode="); pw.println(mPreviousRingerMode);
        pw.print(prefix); pw.print("mDefaultPhoneApp="); pw.println(mDefaultPhoneApp);
        pw.print(prefix); pw.print("mEffectsSuppressed="); pw.println(mEffectsSuppressed);
    
public android.service.notification.ZenModeConfiggetConfig()

        return mConfig;
    
public intgetZenMode()

        return mZenMode;
    
public intgetZenModeListenerInterruptionFilter()

        switch (mZenMode) {
            case Global.ZEN_MODE_OFF:
                return NotificationListenerService.INTERRUPTION_FILTER_ALL;
            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                return NotificationListenerService.INTERRUPTION_FILTER_PRIORITY;
            case Global.ZEN_MODE_NO_INTERRUPTIONS:
                return NotificationListenerService.INTERRUPTION_FILTER_NONE;
            default:
                return 0;
        }
    
private static booleanisAlarm(NotificationRecord record)

        return record.isCategory(Notification.CATEGORY_ALARM)
                || record.isAudioStream(AudioManager.STREAM_ALARM)
                || record.isAudioAttributesUsage(AudioAttributes.USAGE_ALARM);
    
public booleanisCall(NotificationRecord record)

        return record != null && (isDefaultPhoneApp(record.sbn.getPackageName())
                || record.isCategory(Notification.CATEGORY_CALL));
    
private booleanisDefaultMessagingApp(NotificationRecord record)

        final int userId = record.getUserId();
        if (userId == UserHandle.USER_NULL || userId == UserHandle.USER_ALL) return false;
        final String defaultApp = Secure.getStringForUser(mContext.getContentResolver(),
                Secure.SMS_DEFAULT_APPLICATION, userId);
        return Objects.equals(defaultApp, record.sbn.getPackageName());
    
private booleanisDefaultPhoneApp(java.lang.String pkg)

        if (mDefaultPhoneApp == null) {
            final TelecomManager telecomm =
                    (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
            mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultPhoneApp() : null;
            if (DEBUG) Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp);
        }
        return pkg != null && mDefaultPhoneApp != null
                && pkg.equals(mDefaultPhoneApp.getPackageName());
    
private static booleanisEvent(NotificationRecord record)

        return record.isCategory(Notification.CATEGORY_EVENT);
    
private booleanisMessage(NotificationRecord record)

        return record.isCategory(Notification.CATEGORY_MESSAGE) || isDefaultMessagingApp(record);
    
private static booleanisSystem(NotificationRecord record)

        return record.isCategory(Notification.CATEGORY_SYSTEM);
    
public booleanmatchesCallFilter(android.os.UserHandle userHandle, android.os.Bundle extras, ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity)

param
extras extras of the notification with EXTRA_PEOPLE populated
param
contactsTimeoutMs timeout in milliseconds to wait for contacts response
param
timeoutAffinity affinity to return when the timeout specified via contactsTimeoutMs is hit

        final int zen = mZenMode;
        if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
        if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
            if (!mConfig.allowCalls) return false; // no calls get through
            if (validator != null) {
                final float contactAffinity = validator.getContactAffinity(userHandle, extras,
                        contactsTimeoutMs, timeoutAffinity);
                return audienceMatches(contactAffinity);
            }
        }
        return true;
    
public intonSetRingerModeExternal(int ringerModeOld, int ringerModeNew, java.lang.String caller, int ringerModeInternal)

        int ringerModeInternalOut = ringerModeNew;
        final boolean isChange = ringerModeOld != ringerModeNew;
        final boolean isVibrate = ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;

        int newZen = -1;
        switch (ringerModeNew) {
            case AudioManager.RINGER_MODE_SILENT:
                if (isChange) {
                    if (mZenMode == Global.ZEN_MODE_OFF) {
                        newZen = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
                    }
                    ringerModeInternalOut = isVibrate ? AudioManager.RINGER_MODE_VIBRATE
                            : AudioManager.RINGER_MODE_NORMAL;
                } else {
                    ringerModeInternalOut = ringerModeInternal;
                }
                break;
            case AudioManager.RINGER_MODE_VIBRATE:
            case AudioManager.RINGER_MODE_NORMAL:
                if (mZenMode != Global.ZEN_MODE_OFF) {
                    newZen = Global.ZEN_MODE_OFF;
                }
                break;
        }
        if (newZen != -1) {
            setZenMode(newZen, "ringerModeExternal", false /*setRingerMode*/);
        }

        ZenLog.traceSetRingerModeExternal(ringerModeOld, ringerModeNew, caller, ringerModeInternal,
                ringerModeInternalOut);
        return ringerModeInternalOut;
    
public intonSetRingerModeInternal(int ringerModeOld, int ringerModeNew, java.lang.String caller, int ringerModeExternal)

        final boolean isChange = ringerModeOld != ringerModeNew;

        int ringerModeExternalOut = ringerModeNew;

        int newZen = -1;
        switch (ringerModeNew) {
            case AudioManager.RINGER_MODE_SILENT:
                if (isChange) {
                    if (mZenMode != Global.ZEN_MODE_NO_INTERRUPTIONS) {
                        newZen = Global.ZEN_MODE_NO_INTERRUPTIONS;
                    }
                }
                break;
            case AudioManager.RINGER_MODE_VIBRATE:
            case AudioManager.RINGER_MODE_NORMAL:
                if (isChange && ringerModeOld == AudioManager.RINGER_MODE_SILENT
                        && mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
                    newZen = Global.ZEN_MODE_OFF;
                } else if (mZenMode != Global.ZEN_MODE_OFF) {
                    ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT;
                }
                break;
        }
        if (newZen != -1) {
            setZenMode(newZen, "ringerModeInternal", false /*setRingerMode*/);
        }

        if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) {
            ZenLog.traceSetRingerModeInternal(ringerModeOld, ringerModeNew, caller,
                    ringerModeExternal, ringerModeExternalOut);
        }
        return ringerModeExternalOut;
    
public voidonSystemReady()

        mAudioManager = LocalServices.getService(AudioManagerInternal.class);
        if (mAudioManager != null) {
            mAudioManager.setRingerModeDelegate(this);
        }
    
public static android.service.notification.ZenModeConfigreadDefaultConfig(android.content.res.Resources resources)

        XmlResourceParser parser = null;
        try {
            parser = resources.getXml(R.xml.default_zen_mode_config);
            while (parser.next() != XmlPullParser.END_DOCUMENT) {
                final ZenModeConfig config = ZenModeConfig.readXml(parser);
                if (config != null) return config;
            }
        } catch (Exception e) {
            Slog.w(TAG, "Error reading default zen mode config from resource", e);
        } finally {
            IoUtils.closeQuietly(parser);
        }
        return new ZenModeConfig();
    
public voidreadXml(org.xmlpull.v1.XmlPullParser parser)

        final ZenModeConfig config = ZenModeConfig.readXml(parser);
        if (config != null) {
            setConfig(config);
        }
    
public voidreadZenModeFromSetting()

        final int newMode = Global.getInt(mContext.getContentResolver(),
                Global.ZEN_MODE, Global.ZEN_MODE_OFF);
        setZenMode(newMode, "setting");
    
public voidremoveCallback(com.android.server.notification.ZenModeHelper$Callback callback)

        mCallbacks.remove(callback);
    
public voidrequestFromListener(android.content.ComponentName name, int interruptionFilter)

        final int newZen = zenModeFromListenerInterruptionFilter(interruptionFilter, -1);
        if (newZen != -1) {
            setZenMode(newZen, "listener:" + (name != null ? name.flattenToShortString() : null));
        }
    
public booleansetConfig(android.service.notification.ZenModeConfig config)

        if (config == null || !config.isValid()) return false;
        if (config.equals(mConfig)) return true;
        ZenLog.traceConfig(mConfig, config);
        mConfig = config;
        dispatchOnConfigChanged();
        final String val = Integer.toString(mConfig.hashCode());
        Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
        applyRestrictions();
        return true;
    
public voidsetEffectsSuppressed(boolean effectsSuppressed)

        if (mEffectsSuppressed == effectsSuppressed) return;
        mEffectsSuppressed = effectsSuppressed;
        applyRestrictions();
    
public voidsetZenMode(int zenMode, java.lang.String reason)

        setZenMode(zenMode, reason, true);
    
private voidsetZenMode(int zenMode, java.lang.String reason, boolean setRingerMode)

        ZenLog.traceSetZenMode(zenMode, reason);
        if (mZenMode == zenMode) return;
        ZenLog.traceUpdateZenMode(mZenMode, zenMode);
        mZenMode = zenMode;
        Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, mZenMode);
        if (setRingerMode) {
            applyZenToRingerMode();
        }
        applyRestrictions();
        mHandler.postDispatchOnZenModeChanged();
    
public booleanshouldIntercept(NotificationRecord record)

        if (isSystem(record)) {
            return false;
        }
        switch (mZenMode) {
            case Global.ZEN_MODE_NO_INTERRUPTIONS:
                // #notevenalarms
                ZenLog.traceIntercepted(record, "none");
                return true;
            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                if (isAlarm(record)) {
                    // Alarms are always priority
                    return false;
                }
                // allow user-prioritized packages through in priority mode
                if (record.getPackagePriority() == Notification.PRIORITY_MAX) {
                    ZenLog.traceNotIntercepted(record, "priorityApp");
                    return false;
                }
                if (isCall(record)) {
                    if (!mConfig.allowCalls) {
                        ZenLog.traceIntercepted(record, "!allowCalls");
                        return true;
                    }
                    return shouldInterceptAudience(record);
                }
                if (isMessage(record)) {
                    if (!mConfig.allowMessages) {
                        ZenLog.traceIntercepted(record, "!allowMessages");
                        return true;
                    }
                    return shouldInterceptAudience(record);
                }
                if (isEvent(record)) {
                    if (!mConfig.allowEvents) {
                        ZenLog.traceIntercepted(record, "!allowEvents");
                        return true;
                    }
                    return false;
                }
                ZenLog.traceIntercepted(record, "!priority");
                return true;
            default:
                return false;
        }
    
private booleanshouldInterceptAudience(NotificationRecord record)

        if (!audienceMatches(record.getContactAffinity())) {
            ZenLog.traceIntercepted(record, "!audienceMatches");
            return true;
        }
        return false;
    
public java.lang.StringtoString()

        return TAG;
    
public voidwriteXml(org.xmlpull.v1.XmlSerializer out)

        mConfig.writeXml(out);
    
private static intzenModeFromListenerInterruptionFilter(int listenerInterruptionFilter, int defValue)

        switch (listenerInterruptionFilter) {
            case NotificationListenerService.INTERRUPTION_FILTER_ALL:
                return Global.ZEN_MODE_OFF;
            case NotificationListenerService.INTERRUPTION_FILTER_PRIORITY:
                return Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
            case NotificationListenerService.INTERRUPTION_FILTER_NONE:
                return Global.ZEN_MODE_NO_INTERRUPTIONS;
            default:
                return defValue;
        }