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