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