blob: d26be575f2143af49379614d51ab5470f866cc7e [file] [log] [blame]
Jinsuk Kimc70d2292014-04-30 15:43:16 +09001/*
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
Jinsuk Kimc70d2292014-04-30 15:43:16 +090018import android.os.Handler;
19import android.os.Looper;
20import android.os.Message;
Yuncheol Heoc516d652014-07-11 18:23:24 +090021import android.util.Pair;
Jinsuk Kimc70d2292014-04-30 15:43:16 +090022import android.util.Slog;
23
24import com.android.internal.annotations.VisibleForTesting;
Jungshik Jang79c58a42014-06-16 16:45:36 +090025import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
26
Yuncheol Heoc516d652014-07-11 18:23:24 +090027import java.util.ArrayList;
Jungshik Jang79c58a42014-06-16 16:45:36 +090028import java.util.List;
Jinsuk Kimc70d2292014-04-30 15:43:16 +090029
30/**
Jungshik Jang7df52862014-08-11 14:35:27 +090031 * Encapsulates a sequence of CEC command exchange for a certain feature.
Jungshik Jangb509c2e2014-08-07 13:45:01 +090032 * <p>
Jungshik Jang7df52862014-08-11 14:35:27 +090033 * Many CEC features are accomplished by CEC devices on the bus exchanging more than one
Jungshik Jangb509c2e2014-08-07 13:45:01 +090034 * 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.
Jinsuk Kimc70d2292014-04-30 15:43:16 +090043 */
Jungshik Jangb509c2e2014-08-07 13:45:01 +090044abstract class HdmiCecFeatureAction {
Jungshik Jang7df52862014-08-11 14:35:27 +090045 private static final String TAG = "HdmiCecFeatureAction";
Jinsuk Kimc70d2292014-04-30 15:43:16 +090046
47 // Timer handler message used for timeout event
48 protected static final int MSG_TIMEOUT = 100;
49
Jinsuk Kimc70d2292014-04-30 15:43:16 +090050 // 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
Jungshik Jang79c58a42014-06-16 16:45:36 +090056 private final HdmiControlService mService;
57 private final HdmiCecLocalDevice mSource;
Jinsuk Kimc70d2292014-04-30 15:43:16 +090058
59 // Timer that manages timeout events.
60 protected ActionTimer mActionTimer;
61
Jungshik Jangb509c2e2014-08-07 13:45:01 +090062 private ArrayList<Pair<HdmiCecFeatureAction, Runnable>> mOnFinishedCallbacks;
Yuncheol Heoc516d652014-07-11 18:23:24 +090063
Jungshik Jangb509c2e2014-08-07 13:45:01 +090064 HdmiCecFeatureAction(HdmiCecLocalDevice source) {
Jungshik Jang79c58a42014-06-16 16:45:36 +090065 mSource = source;
66 mService = mSource.getService();
67 mActionTimer = createActionTimer(mService.getServiceLooper());
Jinsuk Kimc70d2292014-04-30 15:43:16 +090068 }
69
70 @VisibleForTesting
71 void setActionTimer(ActionTimer actionTimer) {
72 mActionTimer = actionTimer;
73 }
74
75 /**
Jinsuk Kim6f87b4e2014-10-10 14:40:29 +090076 * 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.
Jinsuk Kimc70d2292014-04-30 15:43:16 +090079 *
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
Jungshik Jang5352081c2014-09-22 15:14:49 +090088 * @return true if the command was consumed in the process; Otherwise false.
Jinsuk Kimc70d2292014-04-30 15:43:16 +090089 */
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);
Jungshik Jang67ea5212014-05-15 14:05:24 +0900118
119 /**
120 * Removes any pending timer message.
121 */
122 void clearTimerMessage();
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900123 }
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) {
Jinsuk Kimb0674f02014-05-28 17:42:56 +0900133 // The third argument(0) is not used.
134 sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state, 0), delayMillis);
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900135 }
136
137 @Override
Jungshik Jang67ea5212014-05-15 14:05:24 +0900138 public void clearTimerMessage() {
139 removeMessages(MSG_TIMEOUT);
140 }
141
142 @Override
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900143 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
Jinsuk Kim6f87b4e2014-10-10 14:40:29 +0900165 boolean started() {
166 return mState != STATE_NONE;
167 }
168
Jungshik Jangd643f762014-05-22 19:28:09 +0900169 protected final void sendCommand(HdmiCecMessage cmd) {
170 mService.sendCecCommand(cmd);
171 }
172
173 protected final void sendCommand(HdmiCecMessage cmd,
174 HdmiControlService.SendMessageCallback callback) {
175 mService.sendCecCommand(cmd, callback);
Jungshik Jang67ea5212014-05-15 14:05:24 +0900176 }
177
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900178 protected final void addAndStartAction(HdmiCecFeatureAction action) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900179 mSource.addAndStartAction(action);
180 }
181
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900182 protected final <T extends HdmiCecFeatureAction> List<T> getActions(final Class<T> clazz) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900183 return mSource.getActions(clazz);
184 }
185
186 protected final HdmiCecMessageCache getCecMessageCache() {
187 return mSource.getCecMessageCache();
188 }
189
190 /**
191 * Remove the action from the action queue. This is called after the action finishes
192 * its role.
193 *
194 * @param action
195 */
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900196 protected final void removeAction(HdmiCecFeatureAction action) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900197 mSource.removeAction(action);
198 }
199
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900200 protected final <T extends HdmiCecFeatureAction> void removeAction(final Class<T> clazz) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900201 mSource.removeActionExcept(clazz, null);
202 }
203
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900204 protected final <T extends HdmiCecFeatureAction> void removeActionExcept(final Class<T> clazz,
205 final HdmiCecFeatureAction exception) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900206 mSource.removeActionExcept(clazz, exception);
207 }
208
209 protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
210 int retryCount) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900211 mService.pollDevices(callback, getSourceAddress(), pickStrategy, retryCount);
Jungshik Jang79c58a42014-06-16 16:45:36 +0900212 }
213
Jungshik Jang67ea5212014-05-15 14:05:24 +0900214 /**
215 * Clean up action's state.
216 *
217 * <p>Declared as package-private. Only {@link HdmiControlService} can access it.
218 */
219 void clear() {
220 mState = STATE_NONE;
221 // Clear all timers.
222 mActionTimer.clearTimerMessage();
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900223 }
224
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900225 /**
226 * Finish up the action. Reset the state, and remove itself from the action queue.
227 */
228 protected void finish() {
Yuncheol Heoc516d652014-07-11 18:23:24 +0900229 finish(true);
230 }
231
232 void finish(boolean removeSelf) {
Jungshik Jang67ea5212014-05-15 14:05:24 +0900233 clear();
Yuncheol Heoc516d652014-07-11 18:23:24 +0900234 if (removeSelf) {
235 removeAction(this);
236 }
237 if (mOnFinishedCallbacks != null) {
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900238 for (Pair<HdmiCecFeatureAction, Runnable> actionCallbackPair: mOnFinishedCallbacks) {
Yuncheol Heoc516d652014-07-11 18:23:24 +0900239 if (actionCallbackPair.first.mState != STATE_NONE) {
240 actionCallbackPair.second.run();
241 }
242 }
243 mOnFinishedCallbacks = null;
244 }
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900245 }
246
Jungshik Jang79c58a42014-06-16 16:45:36 +0900247 protected final HdmiCecLocalDevice localDevice() {
248 return mSource;
249 }
250
251 protected final HdmiCecLocalDevicePlayback playback() {
252 return (HdmiCecLocalDevicePlayback) mSource;
253 }
254
255 protected final HdmiCecLocalDeviceTv tv() {
256 return (HdmiCecLocalDeviceTv) mSource;
257 }
258
259 protected final int getSourceAddress() {
260 return mSource.getDeviceInfo().getLogicalAddress();
261 }
262
263 protected final int getSourcePath() {
264 return mSource.getDeviceInfo().getPhysicalAddress();
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900265 }
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900266
Yuncheol Heoc516d652014-07-11 18:23:24 +0900267 protected final void sendUserControlPressedAndReleased(int targetAddress, int uiCommand) {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900268 mSource.sendUserControlPressedAndReleased(targetAddress, uiCommand);
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900269 }
Yuncheol Heoc516d652014-07-11 18:23:24 +0900270
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900271 protected final void addOnFinishedCallback(HdmiCecFeatureAction action, Runnable runnable) {
Yuncheol Heoc516d652014-07-11 18:23:24 +0900272 if (mOnFinishedCallbacks == null) {
273 mOnFinishedCallbacks = new ArrayList<>();
274 }
275 mOnFinishedCallbacks.add(Pair.create(action, runnable));
276 }
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900277}