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 25bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Choimport android.hardware.tv.cec.V1_0.SendMessageResult; 2612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport android.util.Slog; 2712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport com.android.server.hdmi.HdmiControlService.SendMessageCallback; 2812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport java.util.Arrays; 2912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 3012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang/** 3112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * Feature action that performs timer recording. 3212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang */ 33b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jangpublic class TimerRecordingAction extends HdmiCecFeatureAction { 3412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private static final String TAG = "TimerRecordingAction"; 3512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 3612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang // Timer out for waiting <Timer Status> 120s. 3712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private static final int TIMER_STATUS_TIMEOUT_MS = 120000; 3812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 3912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang // State that waits for <Timer Status> once sending <Set XXX Timer> 4012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private static final int STATE_WAITING_FOR_TIMER_STATUS = 1; 4112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 4212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private final int mRecorderAddress; 4312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private final int mSourceType; 4412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private final byte[] mRecordSource; 4512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 4612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang TimerRecordingAction(HdmiCecLocalDevice source, int recorderAddress, int sourceType, 4712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang byte[] recordSource) { 4812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang super(source); 4912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mRecorderAddress = recorderAddress; 5012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mSourceType = sourceType; 5112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mRecordSource = recordSource; 5212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 5312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 5412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang @Override 5512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang boolean start() { 5612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang sendTimerMessage(); 5712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return true; 5812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 5912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 6012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private void sendTimerMessage() { 6112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang HdmiCecMessage message = null; 6212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang switch (mSourceType) { 6312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case TIMER_RECORDING_TYPE_DIGITAL: 6412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang message = HdmiCecMessageBuilder.buildSetDigitalTimer(getSourceAddress(), 6512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mRecorderAddress, mRecordSource); 6612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang break; 6712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case TIMER_RECORDING_TYPE_ANALOGUE: 6812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang message = HdmiCecMessageBuilder.buildSetAnalogueTimer(getSourceAddress(), 6912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mRecorderAddress, mRecordSource); 7012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang break; 7112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case TIMER_RECORDING_TYPE_EXTERNAL: 7212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang message = HdmiCecMessageBuilder.buildSetExternalTimer(getSourceAddress(), 7312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mRecorderAddress, mRecordSource); 7412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang break; 7512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang default: 76326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang tv().announceTimerRecordingResult(mRecorderAddress, 77e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE); 7812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang finish(); 7912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return; 8012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 8112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang sendCommand(message, new SendMessageCallback() { 8212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang @Override 8312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang public void onSendCompleted(int error) { 84bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho if (error != SendMessageResult.SUCCESS) { 85326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang tv().announceTimerRecordingResult(mRecorderAddress, 86faa49bc896be859d5bcf2da3bddd4507b5e6494cJungshik Jang TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION); 8712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang finish(); 8812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return; 8912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 90faa49bc896be859d5bcf2da3bddd4507b5e6494cJungshik Jang mState = STATE_WAITING_FOR_TIMER_STATUS; 91faa49bc896be859d5bcf2da3bddd4507b5e6494cJungshik Jang addTimer(mState, TIMER_STATUS_TIMEOUT_MS); 9212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 9312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang }); 9412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 9512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 9612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang @Override 9712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang boolean processCommand(HdmiCecMessage cmd) { 985352081c662299b618335bf3024058fa04ef2dfdJungshik Jang if (mState != STATE_WAITING_FOR_TIMER_STATUS 995352081c662299b618335bf3024058fa04ef2dfdJungshik Jang || cmd.getSource() != mRecorderAddress) { 10012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return false; 10112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 10212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 10312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang switch (cmd.getOpcode()) { 10412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case Constants.MESSAGE_TIMER_STATUS: 10512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return handleTimerStatus(cmd); 10612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case Constants.MESSAGE_FEATURE_ABORT: 10712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return handleFeatureAbort(cmd); 10812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 10912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return false; 11012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 11112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 11212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private boolean handleTimerStatus(HdmiCecMessage cmd) { 11312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang byte[] timerStatusData = cmd.getParams(); 11412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang // [Timer Status Data] should be one or three bytes. 11512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang if (timerStatusData.length == 1 || timerStatusData.length == 3) { 116326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang tv().announceTimerRecordingResult(mRecorderAddress, bytesToInt(timerStatusData)); 11712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang Slog.i(TAG, "Received [Timer Status Data]:" + Arrays.toString(timerStatusData)); 11812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } else { 11912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang Slog.w(TAG, "Invalid [Timer Status Data]:" + Arrays.toString(timerStatusData)); 12012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 12112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 12212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang // Unlike one touch record, finish timer record when <Timer Status> is received. 12312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang finish(); 12412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return true; 12512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 12612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 12712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private boolean handleFeatureAbort(HdmiCecMessage cmd) { 12812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang byte[] params = cmd.getParams(); 129faa49bc896be859d5bcf2da3bddd4507b5e6494cJungshik Jang int messageType = params[0] & 0xFF; 13012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang switch (messageType) { 13112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case Constants.MESSAGE_SET_DIGITAL_TIMER: // fall through 13212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case Constants.MESSAGE_SET_ANALOG_TIMER: // fall through 13312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang case Constants.MESSAGE_SET_EXTERNAL_TIMER: // fall through 13412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang break; 13512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang default: 13612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return false; 13712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 138faa49bc896be859d5bcf2da3bddd4507b5e6494cJungshik Jang int reason = params[1] & 0xFF; 13912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang Slog.i(TAG, "[Feature Abort] for " + messageType + " reason:" + reason); 140326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang tv().announceTimerRecordingResult(mRecorderAddress, 141326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION); 14212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang finish(); 14312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return true; 14412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 14512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 14612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang // Convert byte array to int. 14712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang private static int bytesToInt(byte[] data) { 14812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang if (data.length > 4) { 14912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang throw new IllegalArgumentException("Invalid data size:" + Arrays.toString(data)); 15012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 15112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang int result = 0; 15212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang for (int i = 0; i < data.length; ++i) { 15312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang int shift = (3 - i) * 8; 15412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang result |= ((data[i] & 0xFF) << shift); 15512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 15612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return result; 15712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 15812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 15912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang @Override 16012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang void handleTimerEvent(int state) { 16112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang if (mState != state) { 16212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang Slog.w(TAG, "Timeout in invalid state:[Expected:" + mState + ", Actual:" + state + "]"); 16312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang return; 16412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 16512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 166326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang tv().announceTimerRecordingResult(mRecorderAddress, 167326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION); 16812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang finish(); 16912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 17012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang} 171