HdmiCecFeatureAction.java revision eaab72ac4198fcc02090da11b1e942e6e338696a
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 after the action is created. Initialization or first step to take
77     * for the action can be done in this method. Shall update {@code mState} to
78     * indicate that the action has started.
79     *
80     * @return true if the operation is successful; otherwise false.
81     */
82    abstract boolean start();
83
84    /**
85     * Process the command. Called whenever a new command arrives.
86     *
87     * @param cmd command to process
88     * @return true if the command was consumed in the process; Otherwise false, which
89     *          indicates that the command shall be handled by other actions.
90     */
91    abstract boolean processCommand(HdmiCecMessage cmd);
92
93    /**
94     * Called when the action should handle the timer event it created before.
95     *
96     * <p>CEC standard mandates each command transmission should be responded within
97     * certain period of time. The method is called when the timer it created as it transmitted
98     * a command gets expired. Inner logic should take an appropriate action.
99     *
100     * @param state the state associated with the time when the timer was created
101     */
102    abstract void handleTimerEvent(int state);
103
104    /**
105     * Timer handler interface used for FeatureAction classes.
106     */
107    interface ActionTimer {
108        /**
109         * Send a timer message.
110         *
111         * Also carries the state of the action when the timer is created. Later this state is
112         * compared to the one the action is in when it receives the timer to let the action tell
113         * the right timer to handle.
114         *
115         * @param state state of the action is in
116         * @param delayMillis amount of delay for the timer
117         */
118        void sendTimerMessage(int state, long delayMillis);
119
120        /**
121         * Removes any pending timer message.
122         */
123        void clearTimerMessage();
124    }
125
126    private class ActionTimerHandler extends Handler implements ActionTimer {
127
128        public ActionTimerHandler(Looper looper) {
129            super(looper);
130        }
131
132        @Override
133        public void sendTimerMessage(int state, long delayMillis) {
134            // The third argument(0) is not used.
135            sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state, 0), delayMillis);
136        }
137
138        @Override
139        public void clearTimerMessage() {
140            removeMessages(MSG_TIMEOUT);
141        }
142
143        @Override
144        public void handleMessage(Message msg) {
145            switch (msg.what) {
146            case MSG_TIMEOUT:
147                handleTimerEvent(msg.arg1);
148                break;
149            default:
150                Slog.w(TAG, "Unsupported message:" + msg.what);
151                break;
152            }
153        }
154    }
155
156    private ActionTimer createActionTimer(Looper looper) {
157        return new ActionTimerHandler(looper);
158    }
159
160    // Add a new timer. The timer event will come to mActionTimer.handleMessage() in
161    // delayMillis.
162    protected void addTimer(int state, int delayMillis) {
163        mActionTimer.sendTimerMessage(state, delayMillis);
164    }
165
166    boolean started() {
167        return mState != STATE_NONE;
168    }
169
170    protected final void sendCommand(HdmiCecMessage cmd) {
171        mService.sendCecCommand(cmd);
172    }
173
174    protected final void sendCommand(HdmiCecMessage cmd,
175            HdmiControlService.SendMessageCallback callback) {
176        mService.sendCecCommand(cmd, callback);
177    }
178
179    protected final void addAndStartAction(HdmiCecFeatureAction action) {
180        mSource.addAndStartAction(action);
181    }
182
183    protected final <T extends HdmiCecFeatureAction> List<T> getActions(final Class<T> clazz) {
184        return mSource.getActions(clazz);
185    }
186
187    protected final HdmiCecMessageCache getCecMessageCache() {
188        return mSource.getCecMessageCache();
189    }
190
191    /**
192     * Remove the action from the action queue. This is called after the action finishes
193     * its role.
194     *
195     * @param action
196     */
197    protected final void removeAction(HdmiCecFeatureAction action) {
198        mSource.removeAction(action);
199    }
200
201    protected final <T extends HdmiCecFeatureAction> void removeAction(final Class<T> clazz) {
202        mSource.removeActionExcept(clazz, null);
203    }
204
205    protected final <T extends HdmiCecFeatureAction> void removeActionExcept(final Class<T> clazz,
206            final HdmiCecFeatureAction exception) {
207        mSource.removeActionExcept(clazz, exception);
208    }
209
210    protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
211            int retryCount) {
212        mService.pollDevices(callback, getSourceAddress(), pickStrategy, retryCount);
213    }
214
215    /**
216     * Clean up action's state.
217     *
218     * <p>Declared as package-private. Only {@link HdmiControlService} can access it.
219     */
220    void clear() {
221        mState = STATE_NONE;
222        // Clear all timers.
223        mActionTimer.clearTimerMessage();
224    }
225
226    /**
227     * Finish up the action. Reset the state, and remove itself from the action queue.
228     */
229    protected void finish() {
230        finish(true);
231    }
232
233    void finish(boolean removeSelf) {
234        clear();
235        if (removeSelf) {
236            removeAction(this);
237        }
238        if (mOnFinishedCallbacks != null) {
239            for (Pair<HdmiCecFeatureAction, Runnable> actionCallbackPair: mOnFinishedCallbacks) {
240                if (actionCallbackPair.first.mState != STATE_NONE) {
241                    actionCallbackPair.second.run();
242                }
243            }
244            mOnFinishedCallbacks = null;
245        }
246    }
247
248    protected final HdmiCecLocalDevice localDevice() {
249        return mSource;
250    }
251
252    protected final HdmiCecLocalDevicePlayback playback() {
253        return (HdmiCecLocalDevicePlayback) mSource;
254    }
255
256    protected final HdmiCecLocalDeviceTv tv() {
257        return (HdmiCecLocalDeviceTv) mSource;
258    }
259
260    protected final int getSourceAddress() {
261        return mSource.getDeviceInfo().getLogicalAddress();
262    }
263
264    protected final int getSourcePath() {
265        return mSource.getDeviceInfo().getPhysicalAddress();
266    }
267
268    protected final void sendUserControlPressedAndReleased(int targetAddress, int uiCommand) {
269        mSource.sendUserControlPressedAndReleased(targetAddress, uiCommand);
270    }
271
272    protected final void addOnFinishedCallback(HdmiCecFeatureAction action, Runnable runnable) {
273        if (mOnFinishedCallbacks == null) {
274            mOnFinishedCallbacks = new ArrayList<>();
275        }
276        mOnFinishedCallbacks.add(Pair.create(action, runnable));
277    }
278}
279