112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang/* 212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * Copyright (C) 2014 The Android Open Source Project 312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * 412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * Licensed under the Apache License, Version 2.0 (the "License"); 512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * you may not use this file except in compliance with the License. 612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * You may obtain a copy of the License at 712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * 812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * http://www.apache.org/licenses/LICENSE-2.0 912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * 1012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * Unless required by applicable law or agreed to in writing, software 1112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * distributed under the License is distributed on an "AS IS" BASIS, 1212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * See the License for the specific language governing permissions and 1412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * limitations under the License. 1512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang */ 1612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 1712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangpackage com.android.server.hdmi; 1812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 19e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jangimport static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION; 20e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jangimport static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE; 2112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANALOGUE; 2212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL; 2312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL; 2412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 2512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport android.util.Slog; 2612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 2712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport com.android.server.hdmi.HdmiControlService.SendMessageCallback; 2812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 2912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport java.util.Arrays; 3012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 3112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang/** 3212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * Feature action that performs timer recording. 3312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang */ 34b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jangpublic class TimerRecordingAction extends HdmiCecFeatureAction { 3512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private static final String TAG = "TimerRecordingAction"; 3612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 3712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang // Timer out for waiting <Timer Status> 120s. 3812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private static final int TIMER_STATUS_TIMEOUT_MS = 120000; 3912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 4012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang // State that waits for <Timer Status> once sending <Set XXX Timer> 4112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private static final int STATE_WAITING_FOR_TIMER_STATUS = 1; 4212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 4312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private final int mRecorderAddress; 4412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private final int mSourceType; 4512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private final byte[] mRecordSource; 4612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 4712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang TimerRecordingAction(HdmiCecLocalDevice source, int recorderAddress, int sourceType, 4812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang byte[] recordSource) { 4912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang super(source); 5012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mRecorderAddress = recorderAddress; 5112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mSourceType = sourceType; 5212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mRecordSource = recordSource; 5312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 5412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 5512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang @Override 5612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang boolean start() { 5712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang sendTimerMessage(); 5812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return true; 5912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 6012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 6112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private void sendTimerMessage() { 6212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang HdmiCecMessage message = null; 6312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang switch (mSourceType) { 6412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case TIMER_RECORDING_TYPE_DIGITAL: 6512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang message = HdmiCecMessageBuilder.buildSetDigitalTimer(getSourceAddress(), 6612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mRecorderAddress, mRecordSource); 6712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang break; 6812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case TIMER_RECORDING_TYPE_ANALOGUE: 6912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang message = HdmiCecMessageBuilder.buildSetAnalogueTimer(getSourceAddress(), 7012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mRecorderAddress, mRecordSource); 7112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang break; 7212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case TIMER_RECORDING_TYPE_EXTERNAL: 7312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang message = HdmiCecMessageBuilder.buildSetExternalTimer(getSourceAddress(), 7412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mRecorderAddress, mRecordSource); 7512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang break; 7612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang default: 77326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang tv().announceTimerRecordingResult(mRecorderAddress, 78e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE); 7912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang finish(); 8012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return; 8112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 8212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang sendCommand(message, new SendMessageCallback() { 8312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang @Override 8412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang public void onSendCompleted(int error) { 8512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang if (error != Constants.SEND_RESULT_SUCCESS) { 86326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang tv().announceTimerRecordingResult(mRecorderAddress, 87faa49bc896be859d5bcf2da3bddd4507b5e6494cJungshik Jang TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION); 8812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang finish(); 8912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return; 9012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 91faa49bc896be859d5bcf2da3bddd4507b5e6494cJungshik Jang mState = STATE_WAITING_FOR_TIMER_STATUS; 92faa49bc896be859d5bcf2da3bddd4507b5e6494cJungshik Jang addTimer(mState, TIMER_STATUS_TIMEOUT_MS); 9312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 9412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang }); 9512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 9612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 9712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang @Override 9812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang boolean processCommand(HdmiCecMessage cmd) { 995352081c662299b618335bf3024058fa04ef2dfdJungshik Jang if (mState != STATE_WAITING_FOR_TIMER_STATUS 1005352081c662299b618335bf3024058fa04ef2dfdJungshik Jang || cmd.getSource() != mRecorderAddress) { 10112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return false; 10212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 10312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 10412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang switch (cmd.getOpcode()) { 10512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case Constants.MESSAGE_TIMER_STATUS: 10612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return handleTimerStatus(cmd); 10712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case Constants.MESSAGE_FEATURE_ABORT: 10812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return handleFeatureAbort(cmd); 10912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 11012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return false; 11112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 11212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 11312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private boolean handleTimerStatus(HdmiCecMessage cmd) { 11412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang byte[] timerStatusData = cmd.getParams(); 11512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang // [Timer Status Data] should be one or three bytes. 11612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang if (timerStatusData.length == 1 || timerStatusData.length == 3) { 117326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang tv().announceTimerRecordingResult(mRecorderAddress, bytesToInt(timerStatusData)); 11812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang Slog.i(TAG, "Received [Timer Status Data]:" + Arrays.toString(timerStatusData)); 11912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } else { 12012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang Slog.w(TAG, "Invalid [Timer Status Data]:" + Arrays.toString(timerStatusData)); 12112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 12212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 12312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang // Unlike one touch record, finish timer record when <Timer Status> is received. 12412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang finish(); 12512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return true; 12612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 12712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 12812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private boolean handleFeatureAbort(HdmiCecMessage cmd) { 12912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang byte[] params = cmd.getParams(); 130faa49bc896be859d5bcf2da3bddd4507b5e6494cJungshik Jang int messageType = params[0] & 0xFF; 13112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang switch (messageType) { 13212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case Constants.MESSAGE_SET_DIGITAL_TIMER: // fall through 13312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case Constants.MESSAGE_SET_ANALOG_TIMER: // fall through 13412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case Constants.MESSAGE_SET_EXTERNAL_TIMER: // fall through 13512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang break; 13612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang default: 13712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return false; 13812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 139faa49bc896be859d5bcf2da3bddd4507b5e6494cJungshik Jang int reason = params[1] & 0xFF; 14012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang Slog.i(TAG, "[Feature Abort] for " + messageType + " reason:" + reason); 141326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang tv().announceTimerRecordingResult(mRecorderAddress, 142326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION); 14312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang finish(); 14412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return true; 14512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 14612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 14712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang // Convert byte array to int. 14812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private static int bytesToInt(byte[] data) { 14912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang if (data.length > 4) { 15012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang throw new IllegalArgumentException("Invalid data size:" + Arrays.toString(data)); 15112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 15212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang int result = 0; 15312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang for (int i = 0; i < data.length; ++i) { 15412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang int shift = (3 - i) * 8; 15512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang result |= ((data[i] & 0xFF) << shift); 15612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 15712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return result; 15812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 15912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 16012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang @Override 16112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang void handleTimerEvent(int state) { 16212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang if (mState != state) { 16312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang Slog.w(TAG, "Timeout in invalid state:[Expected:" + mState + ", Actual:" + state + "]"); 16412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return; 16512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 16612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 167326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang tv().announceTimerRecordingResult(mRecorderAddress, 168326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION); 16912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang finish(); 17012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 17112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang} 172