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