HdmiCecFeatureAction.java revision 2e8f1b6399089626b4f0249427626ba6e63a62ef
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.server.hdmi;
17
18import android.os.Handler;
19import android.os.Looper;
20import android.os.Message;
21import android.util.Pair;
22import android.util.Slog;
23
24import com.android.internal.annotations.VisibleForTesting;
25import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
26
27import java.util.ArrayList;
28import java.util.List;
29
30/**
31 * Encapsulates a sequence of CEC command exchange for a certain feature.
32 * <p>
33 * Many CEC features are accomplished by CEC devices on the bus exchanging more than one
34 * command. {@link HdmiCecFeatureAction} represents the life cycle of the communication, manages the
35 * state as the process progresses, and if necessary, returns the result to the caller which
36 * initiates the action, through the callback given at the creation of the object. All the actual
37 * action classes inherit FeatureAction.
38 * <p>
39 * More than one FeatureAction objects can be up and running simultaneously, maintained by
40 * {@link HdmiCecLocalDevice}. Each action is passed a new command arriving from the bus, and either
41 * consumes it if the command is what the action expects, or yields it to other action. Declared as
42 * package private, accessed by {@link HdmiControlService} only.
43 */
44abstract class HdmiCecFeatureAction {
45    private static final String TAG = "HdmiCecFeatureAction";
46
47    // Timer handler message used for timeout event
48    protected static final int MSG_TIMEOUT = 100;
49
50    // Default state used in common by all the feature actions.
51    protected static final int STATE_NONE = 0;
52
53    // Internal state indicating the progress of action.
54    protected int mState = STATE_NONE;
55
56    private final HdmiControlService mService;
57    private final HdmiCecLocalDevice mSource;
58
59    // Timer that manages timeout events.
60    protected ActionTimer mActionTimer;
61
62    private ArrayList<Pair<HdmiCecFeatureAction, Runnable>> mOnFinishedCallbacks;
63
64    HdmiCecFeatureAction(HdmiCecLocalDevice source) {
65        mSource = source;
66        mService = mSource.getService();
67        mActionTimer = createActionTimer(mService.getServiceLooper());
68    }
69
70    @VisibleForTesting
71    void setActionTimer(ActionTimer actionTimer) {
72        mActionTimer = actionTimer;
73    }
74
75    /**
76     * Called right after the action is created. Initialization or first step to take
77     * for the action can be done in this method.
78     *
79     * @return true if the operation is successful; otherwise false.
80     */
81    abstract boolean start();
82
83    /**
84     * Process the command. Called whenever a new command arrives.
85     *
86     * @param cmd command to process
87     * @return true if the command was consumed in the process; Otherwise false, which
88     *          indicates that the command shall be handled by other actions.
89     */
90    abstract boolean processCommand(HdmiCecMessage cmd);
91
92    /**
93     * Called when the action should handle the timer event it created before.
94     *
95     * <p>CEC standard mandates each command transmission should be responded within
96     * certain period of time. The method is called when the timer it created as it transmitted
97     * a command gets expired. Inner logic should take an appropriate action.
98     *
99     * @param state the state associated with the time when the timer was created
100     */
101    abstract void handleTimerEvent(int state);
102
103    /**
104     * Timer handler interface used for FeatureAction classes.
105     */
106    interface ActionTimer {
107        /**
108         * Send a timer message.
109         *
110         * Also carries the state of the action when the timer is created. Later this state is
111         * compared to the one the action is in when it receives the timer to let the action tell
112         * the right timer to handle.
113         *
114         * @param state state of the action is in
115         * @param delayMillis amount of delay for the timer
116         */
117        void sendTimerMessage(int state, long delayMillis);
118
119        /**
120         * Removes any pending timer message.
121         */
122        void clearTimerMessage();
123    }
124
125    private class ActionTimerHandler extends Handler implements ActionTimer {
126
127        public ActionTimerHandler(Looper looper) {
128            super(looper);
129        }
130
131        @Override
132        public void sendTimerMessage(int state, long delayMillis) {
133            // The third argument(0) is not used.
134            sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state, 0), delayMillis);
135        }
136
137        @Override
138        public void clearTimerMessage() {
139            removeMessages(MSG_TIMEOUT);
140        }
141
142        @Override
143        public void handleMessage(Message msg) {
144            switch (msg.what) {
145            case MSG_TIMEOUT:
146                handleTimerEvent(msg.arg1);
147                break;
148            default:
149                Slog.w(TAG, "Unsupported message:" + msg.what);
150                break;
151            }
152        }
153    }
154
155    private ActionTimer createActionTimer(Looper looper) {
156        return new ActionTimerHandler(looper);
157    }
158
159    // Add a new timer. The timer event will come to mActionTimer.handleMessage() in
160    // delayMillis.
161    protected void addTimer(int state, int delayMillis) {
162        mActionTimer.sendTimerMessage(state, delayMillis);
163    }
164
165    protected final void sendCommand(HdmiCecMessage cmd) {
166        mService.sendCecCommand(cmd);
167    }
168
169    protected final void sendCommand(HdmiCecMessage cmd,
170            HdmiControlService.SendMessageCallback callback) {
171        mService.sendCecCommand(cmd, callback);
172    }
173
174    protected final void addAndStartAction(HdmiCecFeatureAction action) {
175        mSource.addAndStartAction(action);
176    }
177
178    protected final <T extends HdmiCecFeatureAction> List<T> getActions(final Class<T> clazz) {
179        return mSource.getActions(clazz);
180    }
181
182    protected final HdmiCecMessageCache getCecMessageCache() {
183        return mSource.getCecMessageCache();
184    }
185
186    /**
187     * Remove the action from the action queue. This is called after the action finishes
188     * its role.
189     *
190     * @param action
191     */
192    protected final void removeAction(HdmiCecFeatureAction action) {
193        mSource.removeAction(action);
194    }
195
196    protected final <T extends HdmiCecFeatureAction> void removeAction(final Class<T> clazz) {
197        mSource.removeActionExcept(clazz, null);
198    }
199
200    protected final <T extends HdmiCecFeatureAction> void removeActionExcept(final Class<T> clazz,
201            final HdmiCecFeatureAction exception) {
202        mSource.removeActionExcept(clazz, exception);
203    }
204
205    protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
206            int retryCount) {
207        mService.pollDevices(callback, getSourceAddress(), pickStrategy, retryCount);
208    }
209
210    /**
211     * Clean up action's state.
212     *
213     * <p>Declared as package-private. Only {@link HdmiControlService} can access it.
214     */
215    void clear() {
216        mState = STATE_NONE;
217        // Clear all timers.
218        mActionTimer.clearTimerMessage();
219    }
220
221    /**
222     * Finish up the action. Reset the state, and remove itself from the action queue.
223     */
224    protected void finish() {
225        finish(true);
226    }
227
228    void finish(boolean removeSelf) {
229        clear();
230        if (removeSelf) {
231            removeAction(this);
232        }
233        if (mOnFinishedCallbacks != null) {
234            for (Pair<HdmiCecFeatureAction, Runnable> actionCallbackPair: mOnFinishedCallbacks) {
235                if (actionCallbackPair.first.mState != STATE_NONE) {
236                    actionCallbackPair.second.run();
237                }
238            }
239            mOnFinishedCallbacks = null;
240        }
241    }
242
243    protected final HdmiCecLocalDevice localDevice() {
244        return mSource;
245    }
246
247    protected final HdmiCecLocalDevicePlayback playback() {
248        return (HdmiCecLocalDevicePlayback) mSource;
249    }
250
251    protected final HdmiCecLocalDeviceTv tv() {
252        return (HdmiCecLocalDeviceTv) mSource;
253    }
254
255    protected final int getSourceAddress() {
256        return mSource.getDeviceInfo().getLogicalAddress();
257    }
258
259    protected final int getSourcePath() {
260        return mSource.getDeviceInfo().getPhysicalAddress();
261    }
262
263    protected final void sendUserControlPressedAndReleased(int targetAddress, int uiCommand) {
264        mSource.sendUserControlPressedAndReleased(targetAddress, uiCommand);
265    }
266
267    protected final void addOnFinishedCallback(HdmiCecFeatureAction action, Runnable runnable) {
268        if (mOnFinishedCallbacks == null) {
269            mOnFinishedCallbacks = new ArrayList<>();
270        }
271        mOnFinishedCallbacks.add(Pair.create(action, runnable));
272    }
273}
274