blob: 85f5be2143d50b68ee84c5f3d5d8eccc022acb62 [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";
Jungshik Janga7221ce2014-08-28 16:35:30 +090046 // As all actions run in the same thread (service thread), it's fine to have single logger.
47 // TODO: create global logger for each threads and use them.
48 protected static final HdmiLogger DLOGGER = new HdmiLogger(TAG);
Jinsuk Kimc70d2292014-04-30 15:43:16 +090049
50 // Timer handler message used for timeout event
51 protected static final int MSG_TIMEOUT = 100;
52
Jinsuk Kimc70d2292014-04-30 15:43:16 +090053 // Default state used in common by all the feature actions.
54 protected static final int STATE_NONE = 0;
55
56 // Internal state indicating the progress of action.
57 protected int mState = STATE_NONE;
58
Jungshik Jang79c58a42014-06-16 16:45:36 +090059 private final HdmiControlService mService;
60 private final HdmiCecLocalDevice mSource;
Jinsuk Kimc70d2292014-04-30 15:43:16 +090061
62 // Timer that manages timeout events.
63 protected ActionTimer mActionTimer;
64
Jungshik Jangb509c2e2014-08-07 13:45:01 +090065 private ArrayList<Pair<HdmiCecFeatureAction, Runnable>> mOnFinishedCallbacks;
Yuncheol Heoc516d652014-07-11 18:23:24 +090066
Jungshik Jangb509c2e2014-08-07 13:45:01 +090067 HdmiCecFeatureAction(HdmiCecLocalDevice source) {
Jungshik Jang79c58a42014-06-16 16:45:36 +090068 mSource = source;
69 mService = mSource.getService();
70 mActionTimer = createActionTimer(mService.getServiceLooper());
Jinsuk Kimc70d2292014-04-30 15:43:16 +090071 }
72
73 @VisibleForTesting
74 void setActionTimer(ActionTimer actionTimer) {
75 mActionTimer = actionTimer;
76 }
77
78 /**
79 * Called right after the action is created. Initialization or first step to take
80 * for the action can be done in this method.
81 *
82 * @return true if the operation is successful; otherwise false.
83 */
84 abstract boolean start();
85
86 /**
87 * Process the command. Called whenever a new command arrives.
88 *
89 * @param cmd command to process
90 * @return true if the command was consumed in the process; Otherwise false, which
91 * indicates that the command shall be handled by other actions.
92 */
93 abstract boolean processCommand(HdmiCecMessage cmd);
94
95 /**
96 * Called when the action should handle the timer event it created before.
97 *
98 * <p>CEC standard mandates each command transmission should be responded within
99 * certain period of time. The method is called when the timer it created as it transmitted
100 * a command gets expired. Inner logic should take an appropriate action.
101 *
102 * @param state the state associated with the time when the timer was created
103 */
104 abstract void handleTimerEvent(int state);
105
106 /**
107 * Timer handler interface used for FeatureAction classes.
108 */
109 interface ActionTimer {
110 /**
111 * Send a timer message.
112 *
113 * Also carries the state of the action when the timer is created. Later this state is
114 * compared to the one the action is in when it receives the timer to let the action tell
115 * the right timer to handle.
116 *
117 * @param state state of the action is in
118 * @param delayMillis amount of delay for the timer
119 */
120 void sendTimerMessage(int state, long delayMillis);
Jungshik Jang67ea5212014-05-15 14:05:24 +0900121
122 /**
123 * Removes any pending timer message.
124 */
125 void clearTimerMessage();
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900126 }
127
128 private class ActionTimerHandler extends Handler implements ActionTimer {
129
130 public ActionTimerHandler(Looper looper) {
131 super(looper);
132 }
133
134 @Override
135 public void sendTimerMessage(int state, long delayMillis) {
Jinsuk Kimb0674f02014-05-28 17:42:56 +0900136 // The third argument(0) is not used.
137 sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state, 0), delayMillis);
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900138 }
139
140 @Override
Jungshik Jang67ea5212014-05-15 14:05:24 +0900141 public void clearTimerMessage() {
142 removeMessages(MSG_TIMEOUT);
143 }
144
145 @Override
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900146 public void handleMessage(Message msg) {
147 switch (msg.what) {
148 case MSG_TIMEOUT:
149 handleTimerEvent(msg.arg1);
150 break;
151 default:
152 Slog.w(TAG, "Unsupported message:" + msg.what);
153 break;
154 }
155 }
156 }
157
158 private ActionTimer createActionTimer(Looper looper) {
159 return new ActionTimerHandler(looper);
160 }
161
162 // Add a new timer. The timer event will come to mActionTimer.handleMessage() in
163 // delayMillis.
164 protected void addTimer(int state, int delayMillis) {
165 mActionTimer.sendTimerMessage(state, delayMillis);
166 }
167
Jungshik Jangd643f762014-05-22 19:28:09 +0900168 protected final void sendCommand(HdmiCecMessage cmd) {
169 mService.sendCecCommand(cmd);
170 }
171
172 protected final void sendCommand(HdmiCecMessage cmd,
173 HdmiControlService.SendMessageCallback callback) {
174 mService.sendCecCommand(cmd, callback);
Jungshik Jang67ea5212014-05-15 14:05:24 +0900175 }
176
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900177 protected final void addAndStartAction(HdmiCecFeatureAction action) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900178 mSource.addAndStartAction(action);
179 }
180
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900181 protected final <T extends HdmiCecFeatureAction> List<T> getActions(final Class<T> clazz) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900182 return mSource.getActions(clazz);
183 }
184
185 protected final HdmiCecMessageCache getCecMessageCache() {
186 return mSource.getCecMessageCache();
187 }
188
189 /**
190 * Remove the action from the action queue. This is called after the action finishes
191 * its role.
192 *
193 * @param action
194 */
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900195 protected final void removeAction(HdmiCecFeatureAction action) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900196 mSource.removeAction(action);
197 }
198
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900199 protected final <T extends HdmiCecFeatureAction> void removeAction(final Class<T> clazz) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900200 mSource.removeActionExcept(clazz, null);
201 }
202
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900203 protected final <T extends HdmiCecFeatureAction> void removeActionExcept(final Class<T> clazz,
204 final HdmiCecFeatureAction exception) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900205 mSource.removeActionExcept(clazz, exception);
206 }
207
208 protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
209 int retryCount) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900210 mService.pollDevices(callback, getSourceAddress(), pickStrategy, retryCount);
Jungshik Jang79c58a42014-06-16 16:45:36 +0900211 }
212
Jungshik Jang67ea5212014-05-15 14:05:24 +0900213 /**
214 * Clean up action's state.
215 *
216 * <p>Declared as package-private. Only {@link HdmiControlService} can access it.
217 */
218 void clear() {
219 mState = STATE_NONE;
220 // Clear all timers.
221 mActionTimer.clearTimerMessage();
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900222 }
223
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900224 /**
225 * Finish up the action. Reset the state, and remove itself from the action queue.
226 */
227 protected void finish() {
Yuncheol Heoc516d652014-07-11 18:23:24 +0900228 finish(true);
229 }
230
231 void finish(boolean removeSelf) {
Jungshik Jang67ea5212014-05-15 14:05:24 +0900232 clear();
Yuncheol Heoc516d652014-07-11 18:23:24 +0900233 if (removeSelf) {
234 removeAction(this);
235 }
236 if (mOnFinishedCallbacks != null) {
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900237 for (Pair<HdmiCecFeatureAction, Runnable> actionCallbackPair: mOnFinishedCallbacks) {
Yuncheol Heoc516d652014-07-11 18:23:24 +0900238 if (actionCallbackPair.first.mState != STATE_NONE) {
239 actionCallbackPair.second.run();
240 }
241 }
242 mOnFinishedCallbacks = null;
243 }
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900244 }
245
Jungshik Jang79c58a42014-06-16 16:45:36 +0900246 protected final HdmiCecLocalDevice localDevice() {
247 return mSource;
248 }
249
250 protected final HdmiCecLocalDevicePlayback playback() {
251 return (HdmiCecLocalDevicePlayback) mSource;
252 }
253
254 protected final HdmiCecLocalDeviceTv tv() {
255 return (HdmiCecLocalDeviceTv) mSource;
256 }
257
258 protected final int getSourceAddress() {
259 return mSource.getDeviceInfo().getLogicalAddress();
260 }
261
262 protected final int getSourcePath() {
263 return mSource.getDeviceInfo().getPhysicalAddress();
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900264 }
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900265
Yuncheol Heoc516d652014-07-11 18:23:24 +0900266 protected final void sendUserControlPressedAndReleased(int targetAddress, int uiCommand) {
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900267 sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
268 getSourceAddress(), targetAddress, uiCommand));
269 sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
270 getSourceAddress(), targetAddress));
271 }
Yuncheol Heoc516d652014-07-11 18:23:24 +0900272
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900273 protected final void addOnFinishedCallback(HdmiCecFeatureAction action, Runnable runnable) {
Yuncheol Heoc516d652014-07-11 18:23:24 +0900274 if (mOnFinishedCallbacks == null) {
275 mOnFinishedCallbacks = new ArrayList<>();
276 }
277 mOnFinishedCallbacks.add(Pair.create(action, runnable));
278 }
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900279}