TimerRecordingAction.java revision 12e5dcefe136b58562f39604e6a8460ac92cb895
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 17package com.android.server.hdmi; 18 19import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANALOGUE; 20import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL; 21import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL; 22import static android.hardware.hdmi.HdmiControlManager.TIME_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION; 23 24import android.util.Slog; 25 26import com.android.server.hdmi.HdmiControlService.SendMessageCallback; 27 28import java.util.Arrays; 29 30/** 31 * Feature action that performs timer recording. 32 */ 33public class TimerRecordingAction extends FeatureAction { 34 private static final String TAG = "TimerRecordingAction"; 35 36 // Timer out for waiting <Timer Status> 120s. 37 private static final int TIMER_STATUS_TIMEOUT_MS = 120000; 38 39 // State that waits for <Timer Status> once sending <Set XXX Timer> 40 private static final int STATE_WAITING_FOR_TIMER_STATUS = 1; 41 42 private final int mRecorderAddress; 43 private final int mSourceType; 44 private final byte[] mRecordSource; 45 46 TimerRecordingAction(HdmiCecLocalDevice source, int recorderAddress, int sourceType, 47 byte[] recordSource) { 48 super(source); 49 mRecorderAddress = recorderAddress; 50 mSourceType = sourceType; 51 mRecordSource = recordSource; 52 } 53 54 @Override 55 boolean start() { 56 sendTimerMessage(); 57 return true; 58 } 59 60 private void sendTimerMessage() { 61 HdmiCecMessage message = null; 62 switch (mSourceType) { 63 case TIMER_RECORDING_TYPE_DIGITAL: 64 message = HdmiCecMessageBuilder.buildSetDigitalTimer(getSourceAddress(), 65 mRecorderAddress, mRecordSource); 66 break; 67 case TIMER_RECORDING_TYPE_ANALOGUE: 68 message = HdmiCecMessageBuilder.buildSetAnalogueTimer(getSourceAddress(), 69 mRecorderAddress, mRecordSource); 70 break; 71 case TIMER_RECORDING_TYPE_EXTERNAL: 72 message = HdmiCecMessageBuilder.buildSetExternalTimer(getSourceAddress(), 73 mRecorderAddress, mRecordSource); 74 break; 75 default: 76 tv().announceTimerRecordingResult( 77 TIME_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION); 78 finish(); 79 return; 80 } 81 sendCommand(message, new SendMessageCallback() { 82 @Override 83 public void onSendCompleted(int error) { 84 if (error != Constants.SEND_RESULT_SUCCESS) { 85 mState = STATE_WAITING_FOR_TIMER_STATUS; 86 addTimer(mState, TIMER_STATUS_TIMEOUT_MS); 87 finish(); 88 return; 89 } 90 } 91 }); 92 } 93 94 @Override 95 boolean processCommand(HdmiCecMessage cmd) { 96 if (mState != STATE_WAITING_FOR_TIMER_STATUS) { 97 return false; 98 } 99 100 if (cmd.getSource() != mRecorderAddress) { 101 return false; 102 } 103 104 switch (cmd.getOpcode()) { 105 case Constants.MESSAGE_TIMER_STATUS: 106 return handleTimerStatus(cmd); 107 case Constants.MESSAGE_FEATURE_ABORT: 108 return handleFeatureAbort(cmd); 109 } 110 return false; 111 } 112 113 private boolean handleTimerStatus(HdmiCecMessage cmd) { 114 byte[] timerStatusData = cmd.getParams(); 115 // [Timer Status Data] should be one or three bytes. 116 if (timerStatusData.length == 1 || timerStatusData.length == 3) { 117 tv().announceTimerRecordingResult(bytesToInt(timerStatusData)); 118 Slog.i(TAG, "Received [Timer Status Data]:" + Arrays.toString(timerStatusData)); 119 } else { 120 Slog.w(TAG, "Invalid [Timer Status Data]:" + Arrays.toString(timerStatusData)); 121 } 122 123 // Unlike one touch record, finish timer record when <Timer Status> is received. 124 finish(); 125 return true; 126 } 127 128 private boolean handleFeatureAbort(HdmiCecMessage cmd) { 129 byte[] params = cmd.getParams(); 130 int messageType = params[0]; 131 switch (messageType) { 132 case Constants.MESSAGE_SET_DIGITAL_TIMER: // fall through 133 case Constants.MESSAGE_SET_ANALOG_TIMER: // fall through 134 case Constants.MESSAGE_SET_EXTERNAL_TIMER: // fall through 135 break; 136 default: 137 return false; 138 } 139 int reason = params[1]; 140 Slog.i(TAG, "[Feature Abort] for " + messageType + " reason:" + reason); 141 tv().announceTimerRecordingResult(TIME_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION); 142 finish(); 143 return true; 144 } 145 146 // Convert byte array to int. 147 private static int bytesToInt(byte[] data) { 148 if (data.length > 4) { 149 throw new IllegalArgumentException("Invalid data size:" + Arrays.toString(data)); 150 } 151 int result = 0; 152 for (int i = 0; i < data.length; ++i) { 153 int shift = (3 - i) * 8; 154 result |= ((data[i] & 0xFF) << shift); 155 } 156 return result; 157 } 158 159 @Override 160 void handleTimerEvent(int state) { 161 if (mState != state) { 162 Slog.w(TAG, "Timeout in invalid state:[Expected:" + mState + ", Actual:" + state + "]"); 163 return; 164 } 165 166 tv().announceTimerRecordingResult(TIME_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION); 167 finish(); 168 } 169} 170