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