SetArcTransmissionStateAction.java revision 67ea521d14f366fe5aac09e512865d31bfa0ee53
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 android.hardware.hdmi.HdmiCec;
20import android.hardware.hdmi.HdmiCecMessage;
21import android.util.Slog;
22
23/**
24 * Feature action that handles enabling/disabling of ARC transmission channel.
25 * Once TV gets <Initiate ARC>, TV sends <Report ARC Initiated> to AV Receiver.
26 * If it fails or it gets <Terminate ARC>, TV just disables ARC.
27 */
28final class SetArcTransmissionStateAction extends FeatureAction {
29    private static final String TAG = "SetArcTransmissionStateAction";
30
31    // State in which the action sent <Rerpot Arc Initiated> and
32    // is waiting for time out. If it receives <Feature Abort> within timeout
33    // ARC should be disabled.
34    private static final int STATE_WAITING_TIMEOUT = 1;
35
36    private final boolean mEnabled;
37    private final int mAvrAddress;
38
39    /**
40     * @Constructor
41     *
42     * @param service an instance of {@link HdmiControlService}
43     * @param sourceAddress logical address to be used as source address
44     * @param enabled whether to enable ARC Transmission channel
45     */
46    SetArcTransmissionStateAction(HdmiControlService service, int sourceAddress, int avrAddress,
47            boolean enabled) {
48        super(service, sourceAddress);
49        verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
50        verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
51        mAvrAddress = avrAddress;
52        mEnabled = enabled;
53    }
54
55    // TODO: extract it as separate utility class.
56    private static void verifyAddressType(int logicalAddress, int deviceType) {
57        int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress);
58        if (actualDeviceType != deviceType) {
59            throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
60                    + ", Actual:" + actualDeviceType);
61        }
62    }
63
64    @Override
65    boolean start() {
66        if (mEnabled) {
67            if (sendCommand(
68                    HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress))) {
69                // Enable ARC status immediately after sending <Report Arc Initiated>.
70                // If AVR responds with <Feature Abort>, disable ARC status again.
71                // This is different from spec that says that turns ARC status to "Enabled"
72                // if <Report ARC Initiated> is acknowledged and no <Feature Abort> is received.
73                // But implemented this way to save the time having to wait for <Feature Abort>.
74                setArcStatus(true);
75                // If succeeds to send <Report ARC Initiated>, wait general timeout
76                // to check whether there is no <Feature Abort> for <Report ARC Initiated>.
77                mState = STATE_WAITING_TIMEOUT;
78                addTimer(mState, TIMEOUT_MS);
79            } else {
80                // If fails to send <Report ARC Initiated>, disable ARC and
81                // send <Report ARC Terminated> directly.
82                Slog.w(TAG, "Failed to send <Report ARC Initiated>:[source:" + mSourceAddress
83                        + ", avr Address:" + mAvrAddress + "]");
84                setArcStatus(false);
85                finish();
86            }
87        } else {
88            setArcStatus(false);
89            finish();
90        }
91        return true;
92    }
93
94    private void setArcStatus(boolean enabled) {
95        boolean wasEnabled = mService.setArcStatus(enabled);
96        Slog.i(TAG, "Change arc status [old:" + wasEnabled + " ,new:" + enabled);
97
98        // If enabled before and set to "disabled" and send <Report Arc Terminated> to
99        // av reciever.
100        if (!enabled && wasEnabled) {
101            sendCommand(
102                    HdmiCecMessageBuilder.buildReportArcTerminated(mSourceAddress, mAvrAddress));
103        }
104    }
105
106    @Override
107    boolean processCommand(HdmiCecMessage cmd) {
108        if (mState != STATE_WAITING_TIMEOUT) {
109            return false;
110        }
111
112        int opcode = cmd.getOpcode();
113        if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) {
114            setArcStatus(false);
115        }
116        finish();
117        return true;
118    }
119
120    @Override
121    void handleTimerEvent(int state) {
122        if (mState != state || mState != STATE_WAITING_TIMEOUT) {
123            return;
124        }
125        // Expire timeout for <Feature Abort>.
126        finish();
127    }
128}
129