/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.hdmi; import android.hardware.hdmi.HdmiDeviceInfo; import android.util.Slog; /** * Feature action that handles enabling/disabling of ARC transmission channel. * Once TV gets <Initiate ARC>, TV sends <Report ARC Initiated> to AV Receiver. * If it fails or it gets <Terminate ARC>, TV just disables ARC. */ final class SetArcTransmissionStateAction extends HdmiCecFeatureAction { private static final String TAG = "SetArcTransmissionStateAction"; // State in which the action sent and // is waiting for time out. If it receives within timeout // ARC should be disabled. private static final int STATE_WAITING_TIMEOUT = 1; private final boolean mEnabled; private final int mAvrAddress; /** * @Constructor * * @param source {@link HdmiCecLocalDevice} instance * @param enabled whether to enable ARC Transmission channel */ SetArcTransmissionStateAction(HdmiCecLocalDevice source, int avrAddress, boolean enabled) { super(source); HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV); HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); mAvrAddress = avrAddress; mEnabled = enabled; } @Override boolean start() { // Seq #37. if (mEnabled) { // Enable ARC status immediately before sending . // If AVR responds with , disable ARC status again. // This is different from spec that says that turns ARC status to // "Enabled" if is acknowledged and no // is received. // But implemented this way to save the time having to wait for // . setArcStatus(true); // If succeeds to send , wait general timeout // to check whether there is no for . mState = STATE_WAITING_TIMEOUT; addTimer(mState, HdmiConfig.TIMEOUT_MS); sendReportArcInitiated(); } else { setArcStatus(false); finish(); } return true; } private void sendReportArcInitiated() { HdmiCecMessage command = HdmiCecMessageBuilder.buildReportArcInitiated(getSourceAddress(), mAvrAddress); sendCommand(command, new HdmiControlService.SendMessageCallback() { @Override public void onSendCompleted(int error) { switch (error) { case Constants.SEND_RESULT_SUCCESS: case Constants.SEND_RESULT_BUSY: case Constants.SEND_RESULT_FAILURE: // The result of the command transmission, unless it is an obvious // failure indicated by the target device (or lack thereof), should // not affect the ARC status. Ignores it silently. break; case Constants.SEND_RESULT_NAK: // If is negatively ack'ed, disable ARC and // send directly. setArcStatus(false); HdmiLogger.debug("Failed to send ."); finish(); break; } } }); } private void setArcStatus(boolean enabled) { boolean wasEnabled = tv().setArcStatus(enabled); Slog.i(TAG, "Change arc status [old:" + wasEnabled + ", new:" + enabled + "]"); // If enabled before and set to "disabled" and send to // av reciever. if (!enabled && wasEnabled) { sendCommand(HdmiCecMessageBuilder.buildReportArcTerminated(getSourceAddress(), mAvrAddress)); } } @Override boolean processCommand(HdmiCecMessage cmd) { if (mState != STATE_WAITING_TIMEOUT) { return false; } int opcode = cmd.getOpcode(); if (opcode == Constants.MESSAGE_FEATURE_ABORT) { int originalOpcode = cmd.getParams()[0] & 0xFF; if (originalOpcode == Constants.MESSAGE_REPORT_ARC_INITIATED) { HdmiLogger.debug("Feature aborted for "); setArcStatus(false); finish(); return true; } } return false; } @Override void handleTimerEvent(int state) { if (mState != state || mState != STATE_WAITING_TIMEOUT) { return; } // Expire timeout for . finish(); } }