| /* |
| * Copyright (C) 2021 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 static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED; |
| |
| import static com.android.server.hdmi.Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL; |
| |
| import android.hardware.hdmi.DeviceFeatures; |
| import android.hardware.hdmi.HdmiControlManager; |
| import android.hardware.hdmi.HdmiDeviceInfo; |
| import android.hardware.hdmi.IHdmiControlCallback; |
| import android.hardware.tv.cec.V1_0.SendMessageResult; |
| |
| /** |
| * Determines whether a target device supports the <Set Audio Volume Level> message. |
| * |
| * Sends the device <Set Audio Volume Level>[0x7F]. The value 0x7F is defined by the spec such that |
| * setting the volume to this level results in no change to the current volume level. |
| * |
| * The target device supports <Set Audio Volume Level> only if it does not respond with |
| * <Feature Abort> within {@link HdmiConfig.TIMEOUT_MS} milliseconds. |
| */ |
| public class SetAudioVolumeLevelDiscoveryAction extends HdmiCecFeatureAction { |
| private static final String TAG = "SetAudioVolumeLevelDiscoveryAction"; |
| |
| private static final int STATE_WAITING_FOR_FEATURE_ABORT = 1; |
| |
| private final int mTargetAddress; |
| |
| public SetAudioVolumeLevelDiscoveryAction(HdmiCecLocalDevice source, |
| int targetAddress, IHdmiControlCallback callback) { |
| super(source, callback); |
| |
| mTargetAddress = targetAddress; |
| } |
| |
| boolean start() { |
| sendCommand(SetAudioVolumeLevelMessage.build( |
| getSourceAddress(), mTargetAddress, Constants.AUDIO_VOLUME_STATUS_UNKNOWN), |
| result -> { |
| if (result == SendMessageResult.SUCCESS) { |
| // Message sent successfully; wait for <Feature Abort> in response |
| mState = STATE_WAITING_FOR_FEATURE_ABORT; |
| addTimer(mState, HdmiConfig.TIMEOUT_MS); |
| } else { |
| finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED); |
| } |
| }); |
| return true; |
| } |
| |
| boolean processCommand(HdmiCecMessage cmd) { |
| if (mState != STATE_WAITING_FOR_FEATURE_ABORT) { |
| return false; |
| } |
| switch (cmd.getOpcode()) { |
| case Constants.MESSAGE_FEATURE_ABORT: |
| return handleFeatureAbort(cmd); |
| default: |
| return false; |
| } |
| } |
| |
| private boolean handleFeatureAbort(HdmiCecMessage cmd) { |
| if (cmd.getParams().length < 2) { |
| return false; |
| } |
| int originalOpcode = cmd.getParams()[0] & 0xFF; |
| if (originalOpcode == MESSAGE_SET_AUDIO_VOLUME_LEVEL && cmd.getSource() == mTargetAddress) { |
| // No need to update the network, since it should already have processed this message. |
| finishWithCallback(HdmiControlManager.RESULT_SUCCESS); |
| return true; |
| } |
| return false; |
| } |
| |
| void handleTimerEvent(int state) { |
| if (updateSetAudioVolumeLevelSupport(FEATURE_SUPPORTED)) { |
| finishWithCallback(HdmiControlManager.RESULT_SUCCESS); |
| } else { |
| finishWithCallback(HdmiControlManager.RESULT_EXCEPTION); |
| } |
| } |
| |
| /** |
| * Updates the System Audio device's support for <Set Audio Volume Level> in the |
| * {@link HdmiCecNetwork}. Can fail if the System Audio device is not in our |
| * {@link HdmiCecNetwork}. |
| * |
| * @return Whether support was successfully updated in the network. |
| */ |
| private boolean updateSetAudioVolumeLevelSupport( |
| @DeviceFeatures.FeatureSupportStatus int setAudioVolumeLevelSupport) { |
| HdmiCecNetwork network = localDevice().mService.getHdmiCecNetwork(); |
| HdmiDeviceInfo currentDeviceInfo = network.getCecDeviceInfo(mTargetAddress); |
| |
| if (currentDeviceInfo == null) { |
| return false; |
| } else { |
| network.updateCecDevice( |
| currentDeviceInfo.toBuilder() |
| .setDeviceFeatures(currentDeviceInfo.getDeviceFeatures().toBuilder() |
| .setSetAudioVolumeLevelSupport(setAudioVolumeLevelSupport) |
| .build()) |
| .build() |
| ); |
| return true; |
| } |
| } |
| |
| /** |
| * Returns the logical address of this action's target device. |
| */ |
| public int getTargetAddress() { |
| return mTargetAddress; |
| } |
| } |