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.HdmiDeviceInfo; 20import android.hardware.tv.cec.V1_0.SendMessageResult; 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 HdmiCecFeatureAction { 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 source {@link HdmiCecLocalDevice} instance 43 * @param enabled whether to enable ARC Transmission channel 44 */ 45 SetArcTransmissionStateAction(HdmiCecLocalDevice source, int avrAddress, 46 boolean enabled) { 47 super(source); 48 HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV); 49 HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); 50 mAvrAddress = avrAddress; 51 mEnabled = enabled; 52 } 53 54 @Override 55 boolean start() { 56 // Seq #37. 57 if (mEnabled) { 58 // Enable ARC status immediately before sending <Report Arc Initiated>. 59 // If AVR responds with <Feature Abort>, disable ARC status again. 60 // This is different from spec that says that turns ARC status to 61 // "Enabled" if <Report ARC Initiated> is acknowledged and no 62 // <Feature Abort> is received. 63 // But implemented this way to save the time having to wait for 64 // <Feature Abort>. 65 setArcStatus(true); 66 // If succeeds to send <Report ARC Initiated>, wait general timeout 67 // to check whether there is no <Feature Abort> for <Report ARC Initiated>. 68 mState = STATE_WAITING_TIMEOUT; 69 addTimer(mState, HdmiConfig.TIMEOUT_MS); 70 sendReportArcInitiated(); 71 } else { 72 setArcStatus(false); 73 finish(); 74 } 75 return true; 76 } 77 78 private void sendReportArcInitiated() { 79 HdmiCecMessage command = 80 HdmiCecMessageBuilder.buildReportArcInitiated(getSourceAddress(), mAvrAddress); 81 sendCommand(command, new HdmiControlService.SendMessageCallback() { 82 @Override 83 public void onSendCompleted(int error) { 84 switch (error) { 85 case SendMessageResult.SUCCESS: 86 case SendMessageResult.BUSY: 87 case SendMessageResult.FAIL: 88 // The result of the command transmission, unless it is an obvious 89 // failure indicated by the target device (or lack thereof), should 90 // not affect the ARC status. Ignores it silently. 91 break; 92 case SendMessageResult.NACK: 93 // If <Report ARC Initiated> is negatively ack'ed, disable ARC and 94 // send <Report ARC Terminated> directly. 95 setArcStatus(false); 96 HdmiLogger.debug("Failed to send <Report Arc Initiated>."); 97 finish(); 98 break; 99 } 100 } 101 }); 102 } 103 104 private void setArcStatus(boolean enabled) { 105 boolean wasEnabled = tv().setArcStatus(enabled); 106 Slog.i(TAG, "Change arc status [old:" + wasEnabled + ", new:" + enabled + "]"); 107 108 // If enabled before and set to "disabled" and send <Report Arc Terminated> to 109 // av reciever. 110 if (!enabled && wasEnabled) { 111 sendCommand(HdmiCecMessageBuilder.buildReportArcTerminated(getSourceAddress(), 112 mAvrAddress)); 113 } 114 } 115 116 @Override 117 boolean processCommand(HdmiCecMessage cmd) { 118 if (mState != STATE_WAITING_TIMEOUT) { 119 return false; 120 } 121 122 int opcode = cmd.getOpcode(); 123 if (opcode == Constants.MESSAGE_FEATURE_ABORT) { 124 int originalOpcode = cmd.getParams()[0] & 0xFF; 125 if (originalOpcode == Constants.MESSAGE_REPORT_ARC_INITIATED) { 126 HdmiLogger.debug("Feature aborted for <Report Arc Initiated>"); 127 setArcStatus(false); 128 finish(); 129 return true; 130 } 131 } 132 return false; 133 } 134 135 @Override 136 void handleTimerEvent(int state) { 137 if (mState != state || mState != STATE_WAITING_TIMEOUT) { 138 return; 139 } 140 // Expire timeout for <Feature Abort>. 141 finish(); 142 } 143} 144