• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package com.android.server.hdmi;
17 
18 import android.hardware.hdmi.IHdmiControlCallback;
19 import android.os.Handler;
20 import android.os.Looper;
21 import android.os.Message;
22 import android.os.RemoteException;
23 import android.util.Pair;
24 import android.util.Slog;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
28 
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.List;
32 
33 /**
34  * Encapsulates a sequence of CEC command exchange for a certain feature.
35  * <p>
36  * Many CEC features are accomplished by CEC devices on the bus exchanging more than one
37  * command. {@link HdmiCecFeatureAction} represents the life cycle of the communication, manages the
38  * state as the process progresses, and if necessary, returns the result to the caller which
39  * initiates the action, through the callback given at the creation of the object. All the actual
40  * action classes inherit FeatureAction.
41  * <p>
42  * More than one FeatureAction objects can be up and running simultaneously, maintained by
43  * {@link HdmiCecLocalDevice}. Each action is passed a new command arriving from the bus, and either
44  * consumes it if the command is what the action expects, or yields it to other action. Declared as
45  * package private, accessed by {@link HdmiControlService} only.
46  */
47 abstract class HdmiCecFeatureAction {
48     private static final String TAG = "HdmiCecFeatureAction";
49 
50     // Timer handler message used for timeout event
51     protected static final int MSG_TIMEOUT = 100;
52 
53     // Default state used in common by all the feature actions.
54     protected static final int STATE_NONE = 0;
55 
56     // Delay to query avr's audio status, some avrs could report the old volume first. It could
57     // show inverse mute state on TV.
58     protected static final long DELAY_GIVE_AUDIO_STATUS = 500;
59 
60     // Internal state indicating the progress of action.
61     protected int mState = STATE_NONE;
62 
63     private final HdmiControlService mService;
64     private final HdmiCecLocalDevice mSource;
65 
66     // Timer that manages timeout events.
67     protected ActionTimer mActionTimer;
68 
69     private ArrayList<Pair<HdmiCecFeatureAction, Runnable>> mOnFinishedCallbacks;
70 
71     final List<IHdmiControlCallback> mCallbacks = new ArrayList<>();
72 
HdmiCecFeatureAction(HdmiCecLocalDevice source)73     HdmiCecFeatureAction(HdmiCecLocalDevice source) {
74         this(source, new ArrayList<>());
75     }
76 
HdmiCecFeatureAction(HdmiCecLocalDevice source, IHdmiControlCallback callback)77     HdmiCecFeatureAction(HdmiCecLocalDevice source, IHdmiControlCallback callback) {
78         this(source, Arrays.asList(callback));
79     }
80 
HdmiCecFeatureAction(HdmiCecLocalDevice source, List<IHdmiControlCallback> callbacks)81     HdmiCecFeatureAction(HdmiCecLocalDevice source, List<IHdmiControlCallback> callbacks) {
82         for (IHdmiControlCallback callback : callbacks) {
83             addCallback(callback);
84         }
85         mSource = source;
86         mService = mSource.getService();
87         mActionTimer = createActionTimer(mService.getServiceLooper());
88     }
89 
90     @VisibleForTesting
setActionTimer(ActionTimer actionTimer)91     void setActionTimer(ActionTimer actionTimer) {
92         mActionTimer = actionTimer;
93     }
94 
95     /**
96      * Called after the action is created. Initialization or first step to take
97      * for the action can be done in this method. Shall update {@code mState} to
98      * indicate that the action has started.
99      *
100      * @return true if the operation is successful; otherwise false.
101      */
start()102     abstract boolean start();
103 
104     /**
105      * Process the command. Called whenever a new command arrives.
106      *
107      * @param cmd command to process
108      * @return true if the command was consumed in the process; Otherwise false.
109      */
processCommand(HdmiCecMessage cmd)110     abstract boolean processCommand(HdmiCecMessage cmd);
111 
112     /**
113      * Called when the action should handle the timer event it created before.
114      *
115      * <p>CEC standard mandates each command transmission should be responded within
116      * certain period of time. The method is called when the timer it created as it transmitted
117      * a command gets expired. Inner logic should take an appropriate action.
118      *
119      * @param state the state associated with the time when the timer was created
120      */
handleTimerEvent(int state)121     abstract void handleTimerEvent(int state);
122 
123     /**
124      * Timer handler interface used for FeatureAction classes.
125      */
126     interface ActionTimer {
127         /**
128          * Send a timer message.
129          *
130          * Also carries the state of the action when the timer is created. Later this state is
131          * compared to the one the action is in when it receives the timer to let the action tell
132          * the right timer to handle.
133          *
134          * @param state state of the action is in
135          * @param delayMillis amount of delay for the timer
136          */
sendTimerMessage(int state, long delayMillis)137         void sendTimerMessage(int state, long delayMillis);
138 
139         /**
140          * Removes any pending timer message.
141          */
clearTimerMessage()142         void clearTimerMessage();
143     }
144 
145     private class ActionTimerHandler extends Handler implements ActionTimer {
146 
ActionTimerHandler(Looper looper)147         public ActionTimerHandler(Looper looper) {
148             super(looper);
149         }
150 
151         @Override
sendTimerMessage(int state, long delayMillis)152         public void sendTimerMessage(int state, long delayMillis) {
153             // The third argument(0) is not used.
154             sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state, 0), delayMillis);
155         }
156 
157         @Override
clearTimerMessage()158         public void clearTimerMessage() {
159             removeMessages(MSG_TIMEOUT);
160         }
161 
162         @Override
handleMessage(Message msg)163         public void handleMessage(Message msg) {
164             switch (msg.what) {
165             case MSG_TIMEOUT:
166                 handleTimerEvent(msg.arg1);
167                 break;
168             default:
169                 Slog.w(TAG, "Unsupported message:" + msg.what);
170                 break;
171             }
172         }
173     }
174 
createActionTimer(Looper looper)175     private ActionTimer createActionTimer(Looper looper) {
176         return new ActionTimerHandler(looper);
177     }
178 
179     // Add a new timer. The timer event will come to mActionTimer.handleMessage() in
180     // delayMillis.
addTimer(int state, int delayMillis)181     protected void addTimer(int state, int delayMillis) {
182         mActionTimer.sendTimerMessage(state, delayMillis);
183     }
184 
started()185     boolean started() {
186         return mState != STATE_NONE;
187     }
188 
sendCommand(HdmiCecMessage cmd)189     protected final void sendCommand(HdmiCecMessage cmd) {
190         mService.sendCecCommand(cmd);
191     }
192 
sendCommand(HdmiCecMessage cmd, HdmiControlService.SendMessageCallback callback)193     protected final void sendCommand(HdmiCecMessage cmd,
194             HdmiControlService.SendMessageCallback callback) {
195         mService.sendCecCommand(cmd, callback);
196     }
197 
sendCommandWithoutRetries(HdmiCecMessage cmd, HdmiControlService.SendMessageCallback callback)198     protected final void sendCommandWithoutRetries(HdmiCecMessage cmd,
199             HdmiControlService.SendMessageCallback callback) {
200         mService.sendCecCommandWithoutRetries(cmd, callback);
201     }
202 
addAndStartAction(HdmiCecFeatureAction action)203     protected final void addAndStartAction(HdmiCecFeatureAction action) {
204         mSource.addAndStartAction(action);
205     }
206 
getActions(final Class<T> clazz)207     protected final <T extends HdmiCecFeatureAction> List<T> getActions(final Class<T> clazz) {
208         return mSource.getActions(clazz);
209     }
210 
getCecMessageCache()211     protected final HdmiCecMessageCache getCecMessageCache() {
212         return mSource.getCecMessageCache();
213     }
214 
215     /**
216      * Remove the action from the action queue. This is called after the action finishes
217      * its role.
218      *
219      * @param action
220      */
removeAction(HdmiCecFeatureAction action)221     protected final void removeAction(HdmiCecFeatureAction action) {
222         mSource.removeAction(action);
223     }
224 
removeAction(final Class<T> clazz)225     protected final <T extends HdmiCecFeatureAction> void removeAction(final Class<T> clazz) {
226         mSource.removeActionExcept(clazz, null);
227     }
228 
removeActionExcept(final Class<T> clazz, final HdmiCecFeatureAction exception)229     protected final <T extends HdmiCecFeatureAction> void removeActionExcept(final Class<T> clazz,
230             final HdmiCecFeatureAction exception) {
231         mSource.removeActionExcept(clazz, exception);
232     }
233 
pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount)234     protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
235             int retryCount) {
236         pollDevices(callback, pickStrategy, retryCount, 0);
237     }
238 
pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount, long pollingMessageInterval)239     protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
240             int retryCount, long pollingMessageInterval) {
241         mService.pollDevices(
242                 callback, getSourceAddress(), pickStrategy, retryCount, pollingMessageInterval);
243     }
244 
245     /**
246      * Clean up action's state.
247      *
248      * <p>Declared as package-private. Only {@link HdmiControlService} can access it.
249      */
clear()250     void clear() {
251         mState = STATE_NONE;
252         // Clear all timers.
253         mActionTimer.clearTimerMessage();
254     }
255 
256     /**
257      * Finish up the action. Reset the state, and remove itself from the action queue.
258      */
finish()259     protected void finish() {
260         finish(true);
261     }
262 
finish(boolean removeSelf)263     void finish(boolean removeSelf) {
264         clear();
265         if (removeSelf) {
266             removeAction(this);
267         }
268         if (mOnFinishedCallbacks != null) {
269             for (Pair<HdmiCecFeatureAction, Runnable> actionCallbackPair: mOnFinishedCallbacks) {
270                 if (actionCallbackPair.first.mState != STATE_NONE) {
271                     actionCallbackPair.second.run();
272                 }
273             }
274             mOnFinishedCallbacks = null;
275         }
276     }
277 
localDevice()278     protected final HdmiCecLocalDevice localDevice() {
279         return mSource;
280     }
281 
playback()282     protected final HdmiCecLocalDevicePlayback playback() {
283         return (HdmiCecLocalDevicePlayback) mSource;
284     }
285 
source()286     protected final HdmiCecLocalDeviceSource source() {
287         return (HdmiCecLocalDeviceSource) mSource;
288     }
289 
tv()290     protected final HdmiCecLocalDeviceTv tv() {
291         return (HdmiCecLocalDeviceTv) mSource;
292     }
293 
audioSystem()294     protected final HdmiCecLocalDeviceAudioSystem audioSystem() {
295         return (HdmiCecLocalDeviceAudioSystem) mSource;
296     }
297 
getSourceAddress()298     protected final int getSourceAddress() {
299         return mSource.getDeviceInfo().getLogicalAddress();
300     }
301 
getSourcePath()302     protected final int getSourcePath() {
303         return mSource.getDeviceInfo().getPhysicalAddress();
304     }
305 
getServicePath()306     protected final int getServicePath() {
307         return mService.getPhysicalAddress();
308     }
309 
sendUserControlPressedAndReleased(int targetAddress, int uiCommand)310     protected final void sendUserControlPressedAndReleased(int targetAddress, int uiCommand) {
311         mSource.sendUserControlPressedAndReleased(targetAddress, uiCommand);
312     }
313 
addOnFinishedCallback(HdmiCecFeatureAction action, Runnable runnable)314     protected final void addOnFinishedCallback(HdmiCecFeatureAction action, Runnable runnable) {
315         if (mOnFinishedCallbacks == null) {
316             mOnFinishedCallbacks = new ArrayList<>();
317         }
318         mOnFinishedCallbacks.add(Pair.create(action, runnable));
319     }
320 
finishWithCallback(int returnCode)321     protected void finishWithCallback(int returnCode) {
322         invokeCallback(returnCode);
323         finish();
324     }
325 
addCallback(IHdmiControlCallback callback)326     public void addCallback(IHdmiControlCallback callback) {
327         mCallbacks.add(callback);
328     }
329 
invokeCallback(int result)330     private void invokeCallback(int result) {
331         try {
332             for (IHdmiControlCallback callback : mCallbacks) {
333                 if (callback == null) {
334                     continue;
335                 }
336                 callback.onComplete(result);
337             }
338         } catch (RemoteException e) {
339             Slog.e(TAG, "Callback failed:" + e);
340         }
341     }
342 }
343