HdmiCecFeatureAction.java revision a7221ce87683fab16603290378408ce92f02e88a
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 // 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); 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 // Internal state indicating the progress of action. 57 protected int mState = STATE_NONE; 58 59 private final HdmiControlService mService; 60 private final HdmiCecLocalDevice mSource; 61 62 // Timer that manages timeout events. 63 protected ActionTimer mActionTimer; 64 65 private ArrayList<Pair<HdmiCecFeatureAction, Runnable>> mOnFinishedCallbacks; 66 67 HdmiCecFeatureAction(HdmiCecLocalDevice source) { 68 mSource = source; 69 mService = mSource.getService(); 70 mActionTimer = createActionTimer(mService.getServiceLooper()); 71 } 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); 121 122 /** 123 * Removes any pending timer message. 124 */ 125 void clearTimerMessage(); 126 } 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) { 136 // The third argument(0) is not used. 137 sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state, 0), delayMillis); 138 } 139 140 @Override 141 public void clearTimerMessage() { 142 removeMessages(MSG_TIMEOUT); 143 } 144 145 @Override 146 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 168 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); 175 } 176 177 protected final void addAndStartAction(HdmiCecFeatureAction action) { 178 mSource.addAndStartAction(action); 179 } 180 181 protected final <T extends HdmiCecFeatureAction> List<T> getActions(final Class<T> clazz) { 182 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 */ 195 protected final void removeAction(HdmiCecFeatureAction action) { 196 mSource.removeAction(action); 197 } 198 199 protected final <T extends HdmiCecFeatureAction> void removeAction(final Class<T> clazz) { 200 mSource.removeActionExcept(clazz, null); 201 } 202 203 protected final <T extends HdmiCecFeatureAction> void removeActionExcept(final Class<T> clazz, 204 final HdmiCecFeatureAction exception) { 205 mSource.removeActionExcept(clazz, exception); 206 } 207 208 protected final void pollDevices(DevicePollingCallback callback, int pickStrategy, 209 int retryCount) { 210 mService.pollDevices(callback, getSourceAddress(), pickStrategy, retryCount); 211 } 212 213 /** 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(); 222 } 223 224 /** 225 * Finish up the action. Reset the state, and remove itself from the action queue. 226 */ 227 protected void finish() { 228 finish(true); 229 } 230 231 void finish(boolean removeSelf) { 232 clear(); 233 if (removeSelf) { 234 removeAction(this); 235 } 236 if (mOnFinishedCallbacks != null) { 237 for (Pair<HdmiCecFeatureAction, Runnable> actionCallbackPair: mOnFinishedCallbacks) { 238 if (actionCallbackPair.first.mState != STATE_NONE) { 239 actionCallbackPair.second.run(); 240 } 241 } 242 mOnFinishedCallbacks = null; 243 } 244 } 245 246 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(); 264 } 265 266 protected final void sendUserControlPressedAndReleased(int targetAddress, int uiCommand) { 267 sendCommand(HdmiCecMessageBuilder.buildUserControlPressed( 268 getSourceAddress(), targetAddress, uiCommand)); 269 sendCommand(HdmiCecMessageBuilder.buildUserControlReleased( 270 getSourceAddress(), targetAddress)); 271 } 272 273 protected final void addOnFinishedCallback(HdmiCecFeatureAction action, Runnable runnable) { 274 if (mOnFinishedCallbacks == null) { 275 mOnFinishedCallbacks = new ArrayList<>(); 276 } 277 mOnFinishedCallbacks.add(Pair.create(action, runnable)); 278 } 279} 280