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