summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/status/widget/StateTracker.java
blob: fd476b6a0ae0456c9b9e778ec9cabe1da5d11c7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package com.android.server.status.widget;

import com.android.server.status.widget.PowerButton;
import android.content.Context;
import android.content.Intent;
import android.util.Log;


/**
 * The state machine for Wifi and Bluetooth toggling, tracking reality
 * versus the user's intent.
 * 
 * This is necessary because reality moves relatively slowly (turning on
 * & off radio drivers), compared to user's expectations.
 */
public abstract class StateTracker {
    // Is the state in the process of changing?
    private boolean mInTransition = false;
    private Boolean mActualState = null; // initially not set
    private Boolean mIntendedState = null; // initially not set

    // Did a toggle request arrive while a state update was
    // already in-flight? If so, the mIntendedState needs to be
    // requested when the other one is done, unless we happened to
    // arrive at that state already.
    private boolean mDeferredStateChangeRequestNeeded = false;

    /**
     * User pressed a button to change the state. Something should
     * immediately appear to the user afterwards, even if we effectively do
     * nothing. Their press must be heard.
     */
    public final void toggleState(Context context) {
        int currentState = getTriState(context);
        boolean newState = false;
        switch (currentState) {
            case PowerButton.STATE_ENABLED:
                newState = false;
                break;
            case PowerButton.STATE_DISABLED:
                newState = true;
                break;
            case PowerButton.STATE_INTERMEDIATE:
                if (mIntendedState != null) {
                    newState = !mIntendedState;
                }
                break;
        }
        mIntendedState = newState;
        if (mInTransition) {
            // We don't send off a transition request if we're
            // already transitioning. Makes our state tracking
            // easier, and is probably nicer on lower levels.
            // (even though they should be able to take it...)
            mDeferredStateChangeRequestNeeded = true;
        } else {
            mInTransition = true;
            requestStateChange(context, newState);
        }
    }

    /**
     * Update internal state from a broadcast state change.
     */
    public abstract void onActualStateChange(Context context, Intent intent);

    /**
     * Sets the value that we're now in. To be called from
     * onActualStateChange.
     * 
     * @param newState
     *            one of STATE_DISABLED, STATE_ENABLED, STATE_TURNING_ON,
     *            STATE_TURNING_OFF, STATE_UNKNOWN
     */
    protected final void setCurrentState(Context context, int newState) {
        final boolean wasInTransition = mInTransition;
        switch (newState) {
        case PowerButton.STATE_DISABLED:
            mInTransition = false;
            mActualState = false;
            break;
        case PowerButton.STATE_ENABLED:
            mInTransition = false;
            mActualState = true;
            break;
        case PowerButton.STATE_TURNING_ON:
            mInTransition = true;
            mActualState = false;
            break;
        case PowerButton.STATE_TURNING_OFF:
            mInTransition = true;
            mActualState = true;
            break;
        }

        if (wasInTransition && !mInTransition) {
            if (mDeferredStateChangeRequestNeeded) {
                Log.v("StateTracker", "processing deferred state change");
                if (mActualState != null && mIntendedState != null
                        && mIntendedState.equals(mActualState)) {
                    Log.v("StateTracker", "... but intended state matches, so no changes.");
                } else if (mIntendedState != null) {
                    mInTransition = true;
                    requestStateChange(context, mIntendedState);
                }
                mDeferredStateChangeRequestNeeded = false;
            }
        }
    }

    /**
     * If we're in a transition mode, this returns true if we're
     * transitioning towards being enabled.
     */
    public final boolean isTurningOn() {
        return mIntendedState != null && mIntendedState;
    }

    /**
     * Returns simplified 3-state value from underlying 5-state.
     * 
     * @param context
     * @return STATE_ENABLED, STATE_DISABLED, or STATE_INTERMEDIATE
     */
    public final int getTriState(Context context) {
        /*if (mInTransition) {
            // If we know we just got a toggle request recently
            // (which set mInTransition), don't even ask the
            // underlying interface for its state. We know we're
            // changing. This avoids blocking the UI thread
            // during UI refresh post-toggle if the underlying
            // service state accessor has coarse locking on its
            // state (to be fixed separately).
            return PowerButton.STATE_INTERMEDIATE;
        }*/
        switch (getActualState(context)) {
            case PowerButton.STATE_DISABLED:
                return PowerButton.STATE_DISABLED;
            case PowerButton.STATE_ENABLED:
                return PowerButton.STATE_ENABLED;
            default:
                return PowerButton.STATE_INTERMEDIATE;
        }
    }

    /**
     * Gets underlying actual state.
     * 
     * @param context
     * @return STATE_ENABLED, STATE_DISABLED, STATE_ENABLING,
     *         STATE_DISABLING, or or STATE_UNKNOWN.
     */
    public abstract int getActualState(Context context);

    /**
     * Actually make the desired change to the underlying radio API.
     */
    protected abstract void requestStateChange(Context context,
            boolean desiredState);
}