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

NextAlarmConditionProvider

public class NextAlarmConditionProvider extends android.service.notification.ConditionProviderService
Built-in zen condition provider for alarm-clock-based conditions.

If the user's next alarm is within a lookahead threshold (config, default 12hrs), advertise it as an exit condition for zen mode.

The next alarm is defined as {@link AlarmManager#getNextAlarmClock(int)}, which does not survive a reboot. Maintain the illusion of a consistent next alarm value by holding on to a persisted condition until we receive the first value after reboot, or timeout with no value.

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private static final long
SECONDS
private static final long
MINUTES
private static final long
HOURS
private static final long
BAD_CONDITION
public static final android.content.ComponentName
COMPONENT
private final android.content.Context
mContext
private final NextAlarmTracker
mTracker
private final android.util.ArraySet
mSubscriptions
private boolean
mConnected
private long
mLookaheadThreshold
private boolean
mRequesting
private final NextAlarmTracker.Callback
mTrackerCallback
Constructors Summary
public NextAlarmConditionProvider(NextAlarmTracker tracker)


       
        if (DEBUG) Slog.d(TAG, "new NextAlarmConditionProvider()");
        mTracker = tracker;
    
Methods Summary
public android.service.notification.IConditionProviderasInterface()

        return (IConditionProvider) onBind(null);
    
public voidattachBase(android.content.Context base)

        attachBaseContext(base);
    
public voiddump(java.io.PrintWriter pw, com.android.server.notification.NotificationManagerService.DumpFilter filter)

        pw.println("    NextAlarmConditionProvider:");
        pw.print("      mConnected="); pw.println(mConnected);
        pw.print("      mLookaheadThreshold="); pw.print(mLookaheadThreshold);
        pw.print(" ("); TimeUtils.formatDuration(mLookaheadThreshold, pw); pw.println(")");
        pw.print("      mSubscriptions="); pw.println(mSubscriptions);
        pw.print("      mRequesting="); pw.println(mRequesting);
    
private booleanisWithinLookaheadThreshold(android.app.AlarmManager.AlarmClockInfo alarm)

        if (alarm == null) return false;
        final long delta = NextAlarmTracker.getEarlyTriggerTime(alarm) - System.currentTimeMillis();
        return delta > 0 && (mLookaheadThreshold <= 0 || delta < mLookaheadThreshold);
    
private android.net.UrinewConditionId(android.app.AlarmManager.AlarmClockInfo nextAlarm)

        return new Uri.Builder().scheme(Condition.SCHEME)
                .authority(ZenModeConfig.SYSTEM_AUTHORITY)
                .appendPath(ZenModeConfig.NEXT_ALARM_PATH)
                .appendPath(Integer.toString(mTracker.getCurrentUserId()))
                .appendPath(Long.toString(nextAlarm.getTriggerTime()))
                .build();
    
private voidnotifyCondition(android.net.Uri id, android.app.AlarmManager.AlarmClockInfo alarm, int state, java.lang.String reason)

        final String formattedAlarm = alarm == null ? "" : mTracker.formatAlarm(alarm);
        if (DEBUG) Slog.d(TAG, "notifyCondition " + Condition.stateToString(state)
                + " alarm=" + formattedAlarm + " reason=" + reason);
        notifyCondition(new Condition(id,
                mContext.getString(R.string.zen_mode_next_alarm_summary, formattedAlarm),
                mContext.getString(R.string.zen_mode_next_alarm_line_one),
                formattedAlarm, 0, state, Condition.FLAG_RELEVANT_NOW));
    
public voidonConnected()

        if (DEBUG) Slog.d(TAG, "onConnected");
        mLookaheadThreshold = PropConfig.getInt(mContext, "nextalarm.condition.lookahead",
                R.integer.config_next_alarm_condition_lookahead_threshold_hrs) * HOURS;
        mConnected = true;
        mTracker.addCallback(mTrackerCallback);
    
public voidonDestroy()

        super.onDestroy();
        if (DEBUG) Slog.d(TAG, "onDestroy");
        mTracker.removeCallback(mTrackerCallback);
        mConnected = false;
    
private voidonEvaluate(android.app.AlarmManager.AlarmClockInfo nextAlarm, long wakeupTime, boolean booted)

        final boolean withinThreshold = isWithinLookaheadThreshold(nextAlarm);
        final long nextAlarmTime = nextAlarm != null ? nextAlarm.getTriggerTime() : 0;
        if (DEBUG) Slog.d(TAG, "onEvaluate mSubscriptions=" + mSubscriptions
                + " nextAlarmTime=" +  mTracker.formatAlarmDebug(nextAlarmTime)
                + " nextAlarmWakeup=" + mTracker.formatAlarmDebug(wakeupTime)
                + " withinThreshold=" + withinThreshold
                + " booted=" + booted);

        ArraySet<Uri> conditions = mSubscriptions;
        if (mRequesting && nextAlarm != null && withinThreshold) {
            final Uri id = newConditionId(nextAlarm);
            if (!conditions.contains(id)) {
                conditions = new ArraySet<Uri>(conditions);
                conditions.add(id);
            }
        }
        for (Uri conditionId : conditions) {
            final long time = tryParseNextAlarmCondition(conditionId);
            if (time == BAD_CONDITION) {
                notifyCondition(conditionId, nextAlarm, Condition.STATE_FALSE, "badCondition");
            } else if (!booted) {
                // we don't know yet
                if (mSubscriptions.contains(conditionId)) {
                    notifyCondition(conditionId, nextAlarm, Condition.STATE_UNKNOWN, "!booted");
                }
            } else if (time != nextAlarmTime) {
                // next alarm changed since subscription, consider obsolete
                notifyCondition(conditionId, nextAlarm, Condition.STATE_FALSE, "changed");
            } else if (!withinThreshold) {
                // next alarm outside threshold or in the past, condition = false
                notifyCondition(conditionId, nextAlarm, Condition.STATE_FALSE, "!within");
            } else {
                // next alarm within threshold and in the future, condition = true
                notifyCondition(conditionId, nextAlarm, Condition.STATE_TRUE, "within");
            }
        }
    
public voidonRequestConditions(int relevance)

        if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance);
        if (!mConnected) return;
        mRequesting = (relevance & Condition.FLAG_RELEVANT_NOW) != 0;
        mTracker.evaluate();
    
public voidonSubscribe(android.net.Uri conditionId)

        if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
        if (tryParseNextAlarmCondition(conditionId) == BAD_CONDITION) {
            notifyCondition(conditionId, null, Condition.STATE_FALSE, "badCondition");
            return;
        }
        mSubscriptions.add(conditionId);
        mTracker.evaluate();
    
public voidonUnsubscribe(android.net.Uri conditionId)

        if (DEBUG) Slog.d(TAG, "onUnsubscribe " + conditionId);
        mSubscriptions.remove(conditionId);
    
private static longtryParseLong(java.lang.String value, long defValue)

        if (TextUtils.isEmpty(value)) return defValue;
        try {
            return Long.valueOf(value);
        } catch (NumberFormatException e) {
            return defValue;
        }
    
private longtryParseNextAlarmCondition(android.net.Uri conditionId)

        return conditionId != null && conditionId.getScheme().equals(Condition.SCHEME)
                && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
                && conditionId.getPathSegments().size() == 3
                && conditionId.getPathSegments().get(0).equals(ZenModeConfig.NEXT_ALARM_PATH)
                && conditionId.getPathSegments().get(1)
                        .equals(Integer.toString(mTracker.getCurrentUserId()))
                                ? tryParseLong(conditionId.getPathSegments().get(2), BAD_CONDITION)
                                : BAD_CONDITION;