SetArcTransmissionStateAction.java revision 404d704158d068c9b1f066ec647461f0f9ec22cd
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            sendReportArcInitiated();
68        } else {
69            setArcStatus(false);
70            finish();
71        }
72        return true;
73    }
74
75    private void sendReportArcInitiated() {
76        HdmiCecMessage command =
77                HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress);
78        sendCommand(command, new HdmiControlService.SendMessageCallback() {
79            @Override
80            public void onSendCompleted(int error) {
81                if (error == 0) {
82                    // Enable ARC status immediately after sending <Report Arc Initiated>.
83                    // If AVR responds with <Feature Abort>, disable ARC status again.
84                    // This is different from spec that says that turns ARC status to
85                    // "Enabled" if <Report ARC Initiated> is acknowledged and no
86                    // <Feature Abort> is received.
87                    // But implemented this way to save the time having to wait for
88                    // <Feature Abort>.
89                    setArcStatus(true);
90                    // If succeeds to send <Report ARC Initiated>, wait general timeout
91                    // to check whether there is no <Feature Abort> for <Report ARC Initiated>.
92                    mState = STATE_WAITING_TIMEOUT;
93                    addTimer(mState, TIMEOUT_MS);
94                } else {
95                    // If fails to send <Report ARC Initiated>, disable ARC and
96                    // send <Report ARC Terminated> directly.
97                    Slog.w(TAG, "Failed to send <Report ARC Initiated>:[source:"
98                            + mSourceAddress
99                            + ", avr Address:" + mAvrAddress + "]");
100                    setArcStatus(false);
101                    finish();
102                }
103            }
104        });
105    }
106
107    private void setArcStatus(boolean enabled) {
108        boolean wasEnabled = mService.setArcStatus(enabled);
109        Slog.i(TAG, "Change arc status [old:" + wasEnabled + " ,new:" + enabled);
110
111        // If enabled before and set to "disabled" and send <Report Arc Terminated> to
112        // av reciever.
113        if (!enabled && wasEnabled) {
114            sendCommand(
115                    HdmiCecMessageBuilder.buildReportArcTerminated(mSourceAddress, mAvrAddress));
116        }
117    }
118
119    @Override
120    boolean processCommand(HdmiCecMessage cmd) {
121        if (mState != STATE_WAITING_TIMEOUT) {
122            return false;
123        }
124
125        int opcode = cmd.getOpcode();
126        if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) {
127            setArcStatus(false);
128        }
129        finish();
130        return true;
131    }
132
133    @Override
134    void handleTimerEvent(int state) {
135        if (mState != state || mState != STATE_WAITING_TIMEOUT) {
136            return;
137        }
138        // Expire timeout for <Feature Abort>.
139        finish();
140    }
141}
142