Merge changes from topics "presubmit-am-7ed5c317d7b347f1a59eb332f20e8522", "presubmit-am-86ce18b053454a4db98a349f8b39985e", "presubmit-am-94d3142a4e1c418fadfd6eca6208920a", "presubmit-am-a466ed1382304d549cb550a5cae804e6" into tm-dev
* changes: Add Absolute Volume Control support for a TV as the System Audio device Implement support for Absolute Volume Control Add call to onBootPhase to tests that use HdmiControlService Add wrapper for AudioDeviceVolumeManager in the HDMI framework
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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;
|
||||
|
||||
/**
|
||||
* Action to query and track the audio status of the System Audio device when enabling or using
|
||||
* Absolute Volume Control. Must be removed when AVC is disabled. Performs two main functions:
|
||||
* 1. When enabling AVC: queries the starting audio status of the System Audio device and
|
||||
* enables the feature upon receiving a response.
|
||||
* 2. While AVC is enabled: monitors <Report Audio Status> messages from the System Audio device and
|
||||
* notifies AudioService if the audio status changes.
|
||||
*/
|
||||
final class AbsoluteVolumeAudioStatusAction extends HdmiCecFeatureAction {
|
||||
private static final String TAG = "AbsoluteVolumeAudioStatusAction";
|
||||
|
||||
private int mInitialAudioStatusRetriesLeft = 2;
|
||||
|
||||
private static final int STATE_WAIT_FOR_INITIAL_AUDIO_STATUS = 1;
|
||||
private static final int STATE_MONITOR_AUDIO_STATUS = 2;
|
||||
|
||||
private final int mTargetAddress;
|
||||
|
||||
private AudioStatus mLastAudioStatus;
|
||||
|
||||
AbsoluteVolumeAudioStatusAction(HdmiCecLocalDevice source, int targetAddress) {
|
||||
super(source);
|
||||
mTargetAddress = targetAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean start() {
|
||||
mState = STATE_WAIT_FOR_INITIAL_AUDIO_STATUS;
|
||||
sendGiveAudioStatus();
|
||||
return true;
|
||||
}
|
||||
|
||||
void updateVolume(int volumeIndex) {
|
||||
mLastAudioStatus = new AudioStatus(volumeIndex, mLastAudioStatus.getMute());
|
||||
}
|
||||
|
||||
private void sendGiveAudioStatus() {
|
||||
addTimer(mState, HdmiConfig.TIMEOUT_MS);
|
||||
sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(), mTargetAddress));
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean processCommand(HdmiCecMessage cmd) {
|
||||
switch (cmd.getOpcode()) {
|
||||
case Constants.MESSAGE_REPORT_AUDIO_STATUS:
|
||||
return handleReportAudioStatus(cmd);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleReportAudioStatus(HdmiCecMessage cmd) {
|
||||
if (mTargetAddress != cmd.getSource() || cmd.getParams().length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean mute = HdmiUtils.isAudioStatusMute(cmd);
|
||||
int volume = HdmiUtils.getAudioStatusVolume(cmd);
|
||||
AudioStatus audioStatus = new AudioStatus(volume, mute);
|
||||
if (mState == STATE_WAIT_FOR_INITIAL_AUDIO_STATUS) {
|
||||
localDevice().getService().enableAbsoluteVolumeControl(audioStatus);
|
||||
mState = STATE_MONITOR_AUDIO_STATUS;
|
||||
} else if (mState == STATE_MONITOR_AUDIO_STATUS) {
|
||||
if (audioStatus.getVolume() != mLastAudioStatus.getVolume()) {
|
||||
localDevice().getService().notifyAvcVolumeChange(audioStatus.getVolume());
|
||||
}
|
||||
if (audioStatus.getMute() != mLastAudioStatus.getMute()) {
|
||||
localDevice().getService().notifyAvcMuteChange(audioStatus.getMute());
|
||||
}
|
||||
}
|
||||
mLastAudioStatus = audioStatus;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
void handleTimerEvent(int state) {
|
||||
if (mState != state) {
|
||||
return;
|
||||
} else if (mInitialAudioStatusRetriesLeft > 0) {
|
||||
mInitialAudioStatusRetriesLeft--;
|
||||
sendGiveAudioStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.annotation.CallbackExecutor;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.media.AudioDeviceAttributes;
|
||||
import android.media.AudioDeviceVolumeManager;
|
||||
import android.media.VolumeInfo;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Wrapper for {@link AudioDeviceVolumeManager}. Creates an instance of the class and directly
|
||||
* passes method calls to that instance.
|
||||
*/
|
||||
public class AudioDeviceVolumeManagerWrapper
|
||||
implements AudioDeviceVolumeManagerWrapperInterface {
|
||||
|
||||
private static final String TAG = "AudioDeviceVolumeManagerWrapper";
|
||||
|
||||
private final AudioDeviceVolumeManager mAudioDeviceVolumeManager;
|
||||
|
||||
public AudioDeviceVolumeManagerWrapper(Context context) {
|
||||
mAudioDeviceVolumeManager = new AudioDeviceVolumeManager(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOnDeviceVolumeBehaviorChangedListener(
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener listener)
|
||||
throws SecurityException {
|
||||
mAudioDeviceVolumeManager.addOnDeviceVolumeBehaviorChangedListener(executor, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOnDeviceVolumeBehaviorChangedListener(
|
||||
@NonNull AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener listener) {
|
||||
mAudioDeviceVolumeManager.removeOnDeviceVolumeBehaviorChangedListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeviceAbsoluteVolumeBehavior(
|
||||
@NonNull AudioDeviceAttributes device,
|
||||
@NonNull VolumeInfo volume,
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener,
|
||||
boolean handlesVolumeAdjustment) {
|
||||
mAudioDeviceVolumeManager.setDeviceAbsoluteVolumeBehavior(device, volume, executor,
|
||||
vclistener, handlesVolumeAdjustment);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.media.AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener;
|
||||
import static android.media.AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.NonNull;
|
||||
import android.media.AudioDeviceAttributes;
|
||||
import android.media.AudioDeviceVolumeManager;
|
||||
import android.media.VolumeInfo;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Interface with the methods from {@link AudioDeviceVolumeManager} used by the HDMI framework.
|
||||
* Allows the class to be faked for tests.
|
||||
*/
|
||||
public interface AudioDeviceVolumeManagerWrapperInterface {
|
||||
|
||||
/**
|
||||
* Wrapper for {@link AudioDeviceVolumeManager#addOnDeviceVolumeBehaviorChangedListener(
|
||||
* Executor, OnDeviceVolumeBehaviorChangedListener)}
|
||||
*/
|
||||
void addOnDeviceVolumeBehaviorChangedListener(
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener listener);
|
||||
|
||||
/**
|
||||
* Wrapper for {@link AudioDeviceVolumeManager#removeOnDeviceVolumeBehaviorChangedListener(
|
||||
* OnDeviceVolumeBehaviorChangedListener)}
|
||||
*/
|
||||
void removeOnDeviceVolumeBehaviorChangedListener(
|
||||
@NonNull AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener listener);
|
||||
|
||||
/**
|
||||
* Wrapper for {@link AudioDeviceVolumeManager#setDeviceAbsoluteVolumeBehavior(
|
||||
* AudioDeviceAttributes, VolumeInfo, Executor, OnAudioDeviceVolumeChangedListener, boolean)}
|
||||
*/
|
||||
void setDeviceAbsoluteVolumeBehavior(
|
||||
@NonNull AudioDeviceAttributes device,
|
||||
@NonNull VolumeInfo volume,
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener,
|
||||
boolean handlesVolumeAdjustment);
|
||||
}
|
||||
67
services/core/java/com/android/server/hdmi/AudioStatus.java
Normal file
67
services/core/java/com/android/server/hdmi/AudioStatus.java
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.annotation.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Immutable representation of the information in the [Audio Status] operand:
|
||||
* volume status (0 <= N <= 100) and mute status (muted or unmuted).
|
||||
*/
|
||||
public class AudioStatus {
|
||||
public static final int MAX_VOLUME = 100;
|
||||
public static final int MIN_VOLUME = 0;
|
||||
|
||||
int mVolume;
|
||||
boolean mMute;
|
||||
|
||||
public AudioStatus(int volume, boolean mute) {
|
||||
mVolume = volume;
|
||||
mMute = mute;
|
||||
}
|
||||
|
||||
public int getVolume() {
|
||||
return mVolume;
|
||||
}
|
||||
|
||||
public boolean getMute() {
|
||||
return mMute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (!(obj instanceof AudioStatus)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioStatus other = (AudioStatus) obj;
|
||||
return mVolume == other.mVolume
|
||||
&& mMute == other.mMute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mVolume, mMute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AudioStatus mVolume:" + mVolume + " mMute:" + mMute;
|
||||
}
|
||||
}
|
||||
@@ -118,6 +118,7 @@ final class Constants {
|
||||
MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
|
||||
MESSAGE_GIVE_AUDIO_STATUS,
|
||||
MESSAGE_SET_SYSTEM_AUDIO_MODE,
|
||||
MESSAGE_SET_AUDIO_VOLUME_LEVEL,
|
||||
MESSAGE_REPORT_AUDIO_STATUS,
|
||||
MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS,
|
||||
MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
|
||||
@@ -197,9 +198,9 @@ final class Constants {
|
||||
static final int MESSAGE_SYSTEM_AUDIO_MODE_REQUEST = 0x70;
|
||||
static final int MESSAGE_GIVE_AUDIO_STATUS = 0x71;
|
||||
static final int MESSAGE_SET_SYSTEM_AUDIO_MODE = 0x72;
|
||||
static final int MESSAGE_SET_AUDIO_VOLUME_LEVEL = 0x73;
|
||||
static final int MESSAGE_REPORT_AUDIO_STATUS = 0x7A;
|
||||
static final int MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D;
|
||||
static final int MESSAGE_SET_AUDIO_VOLUME_LEVEL = 0x73;
|
||||
static final int MESSAGE_SYSTEM_AUDIO_MODE_STATUS = 0x7E;
|
||||
static final int MESSAGE_ROUTING_CHANGE = 0x80;
|
||||
static final int MESSAGE_ROUTING_INFORMATION = 0x81;
|
||||
|
||||
@@ -303,6 +303,13 @@ abstract class HdmiCecLocalDevice {
|
||||
if (dispatchMessageToAction(message)) {
|
||||
return Constants.HANDLED;
|
||||
}
|
||||
|
||||
// If a message type has its own class, all valid messages of that type
|
||||
// will be represented by an instance of that class.
|
||||
if (message instanceof SetAudioVolumeLevelMessage) {
|
||||
return handleSetAudioVolumeLevel((SetAudioVolumeLevelMessage) message);
|
||||
}
|
||||
|
||||
switch (message.getOpcode()) {
|
||||
case Constants.MESSAGE_ACTIVE_SOURCE:
|
||||
return handleActiveSource(message);
|
||||
@@ -637,6 +644,11 @@ abstract class HdmiCecLocalDevice {
|
||||
return Constants.NOT_HANDLED;
|
||||
}
|
||||
|
||||
@Constants.HandleMessageResult
|
||||
protected int handleSetAudioVolumeLevel(SetAudioVolumeLevelMessage message) {
|
||||
return Constants.NOT_HANDLED;
|
||||
}
|
||||
|
||||
@Constants.RcProfile
|
||||
protected abstract int getRcProfile();
|
||||
|
||||
@@ -1002,6 +1014,57 @@ abstract class HdmiCecLocalDevice {
|
||||
action.start();
|
||||
}
|
||||
|
||||
void addAvcAudioStatusAction(int targetAddress) {
|
||||
if (!hasAction(AbsoluteVolumeAudioStatusAction.class)) {
|
||||
addAndStartAction(new AbsoluteVolumeAudioStatusAction(this, targetAddress));
|
||||
}
|
||||
}
|
||||
|
||||
void removeAvcAudioStatusAction() {
|
||||
removeAction(AbsoluteVolumeAudioStatusAction.class);
|
||||
}
|
||||
|
||||
void updateAvcVolume(int volumeIndex) {
|
||||
for (AbsoluteVolumeAudioStatusAction action :
|
||||
getActions(AbsoluteVolumeAudioStatusAction.class)) {
|
||||
action.updateVolume(volumeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether {@code targetAddress} supports <Set Audio Volume Level>. Does two things
|
||||
* in parallel: send <Give Features> (to get <Report Features> in response),
|
||||
* and send <Set Audio Volume Level> (to see if it gets a <Feature Abort> in response).
|
||||
*/
|
||||
@ServiceThreadOnly
|
||||
void queryAvcSupport(int targetAddress) {
|
||||
assertRunOnServiceThread();
|
||||
|
||||
// Send <Give Features> if using CEC 2.0 or above.
|
||||
if (mService.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) {
|
||||
synchronized (mLock) {
|
||||
mService.sendCecCommand(HdmiCecMessageBuilder.buildGiveFeatures(
|
||||
getDeviceInfo().getLogicalAddress(), targetAddress));
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't already have a {@link SetAudioVolumeLevelDiscoveryAction} for the target
|
||||
// device, start one.
|
||||
List<SetAudioVolumeLevelDiscoveryAction> savlDiscoveryActions =
|
||||
getActions(SetAudioVolumeLevelDiscoveryAction.class);
|
||||
if (savlDiscoveryActions.stream().noneMatch(a -> a.getTargetAddress() == targetAddress)) {
|
||||
addAndStartAction(new SetAudioVolumeLevelDiscoveryAction(this, targetAddress,
|
||||
new IHdmiControlCallback.Stub() {
|
||||
@Override
|
||||
public void onComplete(int result) {
|
||||
if (result == HdmiControlManager.RESULT_SUCCESS) {
|
||||
getService().checkAndUpdateAbsoluteVolumeControlState();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@ServiceThreadOnly
|
||||
void startQueuedActions() {
|
||||
assertRunOnServiceThread();
|
||||
@@ -1205,6 +1268,9 @@ abstract class HdmiCecLocalDevice {
|
||||
*/
|
||||
protected void disableDevice(
|
||||
boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
|
||||
removeAction(AbsoluteVolumeAudioStatusAction.class);
|
||||
removeAction(SetAudioVolumeLevelDiscoveryAction.class);
|
||||
|
||||
mPendingActionClearedCallback =
|
||||
new PendingActionClearedCallback() {
|
||||
@Override
|
||||
|
||||
@@ -307,6 +307,7 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
|
||||
protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
|
||||
removeAction(OneTouchPlayAction.class);
|
||||
removeAction(DevicePowerStatusAction.class);
|
||||
removeAction(AbsoluteVolumeAudioStatusAction.class);
|
||||
|
||||
super.disableDevice(initiatedByCec, callback);
|
||||
}
|
||||
|
||||
@@ -1166,6 +1166,19 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
|
||||
return Constants.HANDLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Constants.HandleMessageResult
|
||||
protected int handleSetAudioVolumeLevel(SetAudioVolumeLevelMessage message) {
|
||||
// <Set Audio Volume Level> should only be sent to the System Audio device, so we don't
|
||||
// handle it when System Audio Mode is enabled.
|
||||
if (mService.isSystemAudioActivated()) {
|
||||
return Constants.ABORT_NOT_IN_CORRECT_MODE;
|
||||
} else {
|
||||
mService.setStreamMusicVolume(message.getAudioVolumeLevel(), 0);
|
||||
return Constants.HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
void announceOneTouchRecordResult(int recorderAddress, int result) {
|
||||
mService.invokeOneTouchRecordResult(recorderAddress, result);
|
||||
}
|
||||
@@ -1202,6 +1215,13 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
|
||||
return mService.getHdmiCecNetwork().getSafeCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the audio output device used for System Audio Mode.
|
||||
*/
|
||||
AudioDeviceAttributes getSystemAudioOutputDevice() {
|
||||
return HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI_ARC;
|
||||
}
|
||||
|
||||
|
||||
@ServiceThreadOnly
|
||||
void handleRemoveActiveRoutingPath(int path) {
|
||||
@@ -1296,6 +1316,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
|
||||
removeAction(OneTouchRecordAction.class);
|
||||
removeAction(TimerRecordingAction.class);
|
||||
removeAction(NewDeviceAction.class);
|
||||
removeAction(AbsoluteVolumeAudioStatusAction.class);
|
||||
|
||||
disableSystemAudioIfExist();
|
||||
disableArcIfExist();
|
||||
@@ -1318,7 +1339,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
|
||||
removeAction(SystemAudioActionFromAvr.class);
|
||||
removeAction(SystemAudioActionFromTv.class);
|
||||
removeAction(SystemAudioAutoInitiationAction.class);
|
||||
removeAction(SystemAudioStatusAction.class);
|
||||
removeAction(VolumeControlAction.class);
|
||||
|
||||
if (!mService.isControlEnabled()) {
|
||||
@@ -1589,6 +1609,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
|
||||
return DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
|
||||
.setRecordTvScreenSupport(FEATURE_SUPPORTED)
|
||||
.setArcTxSupport(hasArcPort ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED)
|
||||
.setSetAudioVolumeLevelSupport(FEATURE_SUPPORTED)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -262,6 +262,8 @@ public class HdmiCecMessage {
|
||||
return "Give Audio Status";
|
||||
case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
|
||||
return "Set System Audio Mode";
|
||||
case Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL:
|
||||
return "Set Audio Volume Level";
|
||||
case Constants.MESSAGE_REPORT_AUDIO_STATUS:
|
||||
return "Report Audio Status";
|
||||
case Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
|
||||
|
||||
@@ -259,6 +259,7 @@ public class HdmiCecNetwork {
|
||||
// The addition of a local device should not notify listeners
|
||||
return;
|
||||
}
|
||||
mHdmiControlService.checkAndUpdateAbsoluteVolumeControlState();
|
||||
if (info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) {
|
||||
// Don't notify listeners of devices that haven't reported their physical address yet
|
||||
return;
|
||||
@@ -383,7 +384,7 @@ public class HdmiCecNetwork {
|
||||
final void removeCecDevice(HdmiCecLocalDevice localDevice, int address) {
|
||||
assertRunOnServiceThread();
|
||||
HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
|
||||
|
||||
mHdmiControlService.checkAndUpdateAbsoluteVolumeControlState();
|
||||
localDevice.mCecMessageCache.flushMessagesFrom(address);
|
||||
if (info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) {
|
||||
// Don't notify listeners of devices that haven't reported their physical address yet
|
||||
@@ -586,6 +587,8 @@ public class HdmiCecNetwork {
|
||||
.build();
|
||||
|
||||
updateCecDevice(newDeviceInfo);
|
||||
|
||||
mHdmiControlService.checkAndUpdateAbsoluteVolumeControlState();
|
||||
}
|
||||
|
||||
@ServiceThreadOnly
|
||||
@@ -617,6 +620,8 @@ public class HdmiCecNetwork {
|
||||
)
|
||||
.build();
|
||||
updateCecDevice(newDeviceInfo);
|
||||
|
||||
mHdmiControlService.checkAndUpdateAbsoluteVolumeControlState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import static com.android.server.hdmi.Constants.OPTION_MHL_SERVICE_CONTROL;
|
||||
import static com.android.server.power.ShutdownThread.SHUTDOWN_ACTION_PROPERTY;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
@@ -38,6 +39,7 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.hdmi.DeviceFeatures;
|
||||
import android.hardware.hdmi.HdmiControlManager;
|
||||
import android.hardware.hdmi.HdmiDeviceInfo;
|
||||
import android.hardware.hdmi.HdmiHotplugEvent;
|
||||
@@ -56,7 +58,12 @@ import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
|
||||
import android.hardware.hdmi.IHdmiVendorCommandListener;
|
||||
import android.hardware.tv.cec.V1_0.OptionKey;
|
||||
import android.hardware.tv.cec.V1_0.SendMessageResult;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioDeviceAttributes;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioDeviceVolumeManager;
|
||||
import android.media.AudioManager;
|
||||
import android.media.VolumeInfo;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.media.tv.TvInputManager;
|
||||
@@ -83,6 +90,7 @@ import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -101,6 +109,7 @@ import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -202,6 +211,27 @@ public class HdmiControlService extends SystemService {
|
||||
public @interface WakeReason {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_HDMI = new AudioDeviceAttributes(
|
||||
AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_HDMI, "");
|
||||
@VisibleForTesting
|
||||
static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_HDMI_ARC =
|
||||
new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
|
||||
AudioDeviceInfo.TYPE_HDMI_ARC, "");
|
||||
static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_HDMI_EARC =
|
||||
new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
|
||||
AudioDeviceInfo.TYPE_HDMI_EARC, "");
|
||||
|
||||
// Audio output devices used for Absolute Volume Control
|
||||
private static final List<AudioDeviceAttributes> AVC_AUDIO_OUTPUT_DEVICES =
|
||||
Collections.unmodifiableList(Arrays.asList(AUDIO_OUTPUT_DEVICE_HDMI,
|
||||
AUDIO_OUTPUT_DEVICE_HDMI_ARC, AUDIO_OUTPUT_DEVICE_HDMI_EARC));
|
||||
|
||||
// AudioAttributes for STREAM_MUSIC
|
||||
@VisibleForTesting
|
||||
static final AudioAttributes STREAM_MUSIC_ATTRIBUTES =
|
||||
new AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_MUSIC).build();
|
||||
|
||||
private final Executor mServiceThreadExecutor = new Executor() {
|
||||
@Override
|
||||
public void execute(Runnable r) {
|
||||
@@ -209,6 +239,10 @@ public class HdmiControlService extends SystemService {
|
||||
}
|
||||
};
|
||||
|
||||
Executor getServiceThreadExecutor() {
|
||||
return mServiceThreadExecutor;
|
||||
}
|
||||
|
||||
// Logical address of the active source.
|
||||
@GuardedBy("mLock")
|
||||
protected final ActiveSource mActiveSource = new ActiveSource();
|
||||
@@ -222,6 +256,13 @@ public class HdmiControlService extends SystemService {
|
||||
@HdmiControlManager.VolumeControl
|
||||
private int mHdmiCecVolumeControl;
|
||||
|
||||
// Caches the volume behaviors of all audio output devices in AVC_AUDIO_OUTPUT_DEVICES.
|
||||
@GuardedBy("mLock")
|
||||
private Map<AudioDeviceAttributes, Integer> mAudioDeviceVolumeBehaviors = new HashMap<>();
|
||||
|
||||
// Maximum volume of AudioManager.STREAM_MUSIC. Set upon gaining access to system services.
|
||||
private int mStreamMusicMaxVolume;
|
||||
|
||||
// Make sure HdmiCecConfig is instantiated and the XMLs are read.
|
||||
private HdmiCecConfig mHdmiCecConfig;
|
||||
|
||||
@@ -410,6 +451,12 @@ public class HdmiControlService extends SystemService {
|
||||
@Nullable
|
||||
private PowerManagerInternalWrapper mPowerManagerInternal;
|
||||
|
||||
@Nullable
|
||||
private AudioManager mAudioManager;
|
||||
|
||||
@Nullable
|
||||
private AudioDeviceVolumeManagerWrapperInterface mAudioDeviceVolumeManager;
|
||||
|
||||
@Nullable
|
||||
private Looper mIoLooper;
|
||||
|
||||
@@ -439,11 +486,21 @@ public class HdmiControlService extends SystemService {
|
||||
|
||||
private final SelectRequestBuffer mSelectRequestBuffer = new SelectRequestBuffer();
|
||||
|
||||
@VisibleForTesting HdmiControlService(Context context, List<Integer> deviceTypes) {
|
||||
/**
|
||||
* Constructor for testing.
|
||||
*
|
||||
* It's critical to use a fake AudioDeviceVolumeManager because a normally instantiated
|
||||
* AudioDeviceVolumeManager can access the "real" AudioService on the DUT.
|
||||
*
|
||||
* @see FakeAudioDeviceVolumeManagerWrapper
|
||||
*/
|
||||
@VisibleForTesting HdmiControlService(Context context, List<Integer> deviceTypes,
|
||||
AudioDeviceVolumeManagerWrapperInterface audioDeviceVolumeManager) {
|
||||
super(context);
|
||||
mLocalDevices = deviceTypes;
|
||||
mSettingsObserver = new SettingsObserver(mHandler);
|
||||
mHdmiCecConfig = new HdmiCecConfig(context);
|
||||
mAudioDeviceVolumeManager = audioDeviceVolumeManager;
|
||||
}
|
||||
|
||||
public HdmiControlService(Context context) {
|
||||
@@ -744,6 +801,14 @@ public class HdmiControlService extends SystemService {
|
||||
Context.TV_INPUT_SERVICE);
|
||||
mPowerManager = new PowerManagerWrapper(getContext());
|
||||
mPowerManagerInternal = new PowerManagerInternalWrapper();
|
||||
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
mStreamMusicMaxVolume = getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
|
||||
if (mAudioDeviceVolumeManager == null) {
|
||||
mAudioDeviceVolumeManager =
|
||||
new AudioDeviceVolumeManagerWrapper(getContext());
|
||||
}
|
||||
getAudioDeviceVolumeManager().addOnDeviceVolumeBehaviorChangedListener(
|
||||
mServiceThreadExecutor, this::onDeviceVolumeBehaviorChanged);
|
||||
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
|
||||
runOnServiceThread(this::bootCompleted);
|
||||
}
|
||||
@@ -2419,6 +2484,7 @@ public class HdmiControlService extends SystemService {
|
||||
pw.println("mPowerStatus: " + mPowerStatusController.getPowerStatus());
|
||||
pw.println("mIsCecAvailable: " + mIsCecAvailable);
|
||||
pw.println("mCecVersion: " + mCecVersion);
|
||||
pw.println("mIsAbsoluteVolumeControlEnabled: " + isAbsoluteVolumeControlEnabled());
|
||||
|
||||
// System settings
|
||||
pw.println("System_settings:");
|
||||
@@ -2578,6 +2644,7 @@ public class HdmiControlService extends SystemService {
|
||||
@HdmiControlManager.VolumeControl int hdmiCecVolumeControl) {
|
||||
mHdmiCecVolumeControl = hdmiCecVolumeControl;
|
||||
announceHdmiCecVolumeControlFeatureChange(hdmiCecVolumeControl);
|
||||
runOnServiceThread(this::checkAndUpdateAbsoluteVolumeControlState);
|
||||
}
|
||||
|
||||
// Get the source address to send out commands to devices connected to the current device
|
||||
@@ -3086,15 +3153,17 @@ public class HdmiControlService extends SystemService {
|
||||
private void announceHdmiCecVolumeControlFeatureChange(
|
||||
@HdmiControlManager.VolumeControl int hdmiCecVolumeControl) {
|
||||
assertRunOnServiceThread();
|
||||
mHdmiCecVolumeControlFeatureListenerRecords.broadcast(listener -> {
|
||||
try {
|
||||
listener.onHdmiCecVolumeControlFeature(hdmiCecVolumeControl);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG,
|
||||
"Failed to report HdmiControlVolumeControlStatusChange: "
|
||||
+ hdmiCecVolumeControl);
|
||||
}
|
||||
});
|
||||
synchronized (mLock) {
|
||||
mHdmiCecVolumeControlFeatureListenerRecords.broadcast(listener -> {
|
||||
try {
|
||||
listener.onHdmiCecVolumeControlFeature(hdmiCecVolumeControl);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG,
|
||||
"Failed to report HdmiControlVolumeControlStatusChange: "
|
||||
+ hdmiCecVolumeControl);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public HdmiCecLocalDeviceTv tv() {
|
||||
@@ -3131,8 +3200,20 @@ public class HdmiControlService extends SystemService {
|
||||
HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null before the boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}.
|
||||
*/
|
||||
@Nullable
|
||||
AudioManager getAudioManager() {
|
||||
return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
return mAudioManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null before the boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}.
|
||||
*/
|
||||
@Nullable
|
||||
private AudioDeviceVolumeManagerWrapperInterface getAudioDeviceVolumeManager() {
|
||||
return mAudioDeviceVolumeManager;
|
||||
}
|
||||
|
||||
boolean isControlEnabled() {
|
||||
@@ -3486,6 +3567,7 @@ public class HdmiControlService extends SystemService {
|
||||
synchronized (mLock) {
|
||||
mSystemAudioActivated = on;
|
||||
}
|
||||
runOnServiceThread(this::checkAndUpdateAbsoluteVolumeControlState);
|
||||
}
|
||||
|
||||
@ServiceThreadOnly
|
||||
@@ -3599,6 +3681,8 @@ public class HdmiControlService extends SystemService {
|
||||
device.addActiveSourceHistoryItem(new ActiveSource(logicalAddress, physicalAddress),
|
||||
deviceIsActiveSource, caller);
|
||||
}
|
||||
|
||||
runOnServiceThread(this::checkAndUpdateAbsoluteVolumeControlState);
|
||||
}
|
||||
|
||||
// This method should only be called when the device can be the active source
|
||||
@@ -3791,4 +3875,332 @@ public class HdmiControlService extends SystemService {
|
||||
Slog.e(TAG, "Failed to report setting change", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for changes to the volume behavior of an audio output device. Caches the
|
||||
* volume behavior of devices used for Absolute Volume Control.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@ServiceThreadOnly
|
||||
void onDeviceVolumeBehaviorChanged(AudioDeviceAttributes device, int volumeBehavior) {
|
||||
assertRunOnServiceThread();
|
||||
if (AVC_AUDIO_OUTPUT_DEVICES.contains(device)) {
|
||||
synchronized (mLock) {
|
||||
mAudioDeviceVolumeBehaviors.put(device, volumeBehavior);
|
||||
}
|
||||
checkAndUpdateAbsoluteVolumeControlState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for {@link AudioManager#getDeviceVolumeBehavior} that takes advantage of cached
|
||||
* results for the volume behaviors of HDMI audio devices.
|
||||
*/
|
||||
@AudioManager.DeviceVolumeBehavior
|
||||
private int getDeviceVolumeBehavior(AudioDeviceAttributes device) {
|
||||
if (AVC_AUDIO_OUTPUT_DEVICES.contains(device)) {
|
||||
synchronized (mLock) {
|
||||
if (mAudioDeviceVolumeBehaviors.containsKey(device)) {
|
||||
return mAudioDeviceVolumeBehaviors.get(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
return getAudioManager().getDeviceVolumeBehavior(device);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Absolute Volume Control is enabled or not. This is determined by the
|
||||
* volume behavior of the relevant HDMI audio output device(s) for this device's type.
|
||||
*/
|
||||
public boolean isAbsoluteVolumeControlEnabled() {
|
||||
if (!isTvDevice() && !isPlaybackDevice()) {
|
||||
return false;
|
||||
}
|
||||
AudioDeviceAttributes avcAudioOutputDevice = getAvcAudioOutputDevice();
|
||||
if (avcAudioOutputDevice == null) {
|
||||
return false;
|
||||
}
|
||||
return getDeviceVolumeBehavior(avcAudioOutputDevice)
|
||||
== AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE;
|
||||
}
|
||||
|
||||
private AudioDeviceAttributes getAvcAudioOutputDevice() {
|
||||
if (isTvDevice()) {
|
||||
return tv().getSystemAudioOutputDevice();
|
||||
} else if (isPlaybackDevice()) {
|
||||
return AUDIO_OUTPUT_DEVICE_HDMI;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the conditions for Absolute Volume Control (AVC), and enables or disables the feature
|
||||
* if necessary. AVC is enabled precisely when a specific audio output device
|
||||
* (HDMI for playback devices, and HDMI_ARC or HDMI_EARC for TVs) is using absolute volume
|
||||
* behavior.
|
||||
*
|
||||
* AVC must be enabled on a Playback device or TV precisely when it is playing
|
||||
* audio on an external device (the System Audio device) that supports the feature.
|
||||
* This reduces to these conditions:
|
||||
*
|
||||
* 1. If the System Audio Device is an Audio System: System Audio Mode is active
|
||||
* 2. Our HDMI audio output device is using full volume behavior
|
||||
* 3. CEC volume is enabled
|
||||
* 4. The System Audio device supports AVC (i.e. it supports <Set Audio Volume Level>)
|
||||
*
|
||||
* If not all of these conditions are met, this method disables AVC if necessary.
|
||||
*
|
||||
* If all of these conditions are met, this method starts an action to query the System Audio
|
||||
* device's audio status, which enables AVC upon obtaining the audio status.
|
||||
*/
|
||||
@ServiceThreadOnly
|
||||
void checkAndUpdateAbsoluteVolumeControlState() {
|
||||
assertRunOnServiceThread();
|
||||
|
||||
// Can't enable or disable AVC before we have access to system services
|
||||
if (getAudioManager() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
HdmiCecLocalDevice localCecDevice;
|
||||
if (isTvDevice() && tv() != null) {
|
||||
localCecDevice = tv();
|
||||
// Condition 1: TVs need System Audio Mode to be active
|
||||
// (Doesn't apply to Playback Devices, where if SAM isn't active, we assume the
|
||||
// TV is the System Audio Device instead.)
|
||||
if (!isSystemAudioActivated()) {
|
||||
disableAbsoluteVolumeControl();
|
||||
return;
|
||||
}
|
||||
} else if (isPlaybackDevice() && playback() != null) {
|
||||
localCecDevice = playback();
|
||||
} else {
|
||||
// Either this device type doesn't support AVC, or it hasn't fully initialized yet
|
||||
return;
|
||||
}
|
||||
|
||||
HdmiDeviceInfo systemAudioDeviceInfo = getHdmiCecNetwork().getSafeCecDeviceInfo(
|
||||
localCecDevice.findAudioReceiverAddress());
|
||||
@AudioManager.DeviceVolumeBehavior int currentVolumeBehavior =
|
||||
getDeviceVolumeBehavior(getAvcAudioOutputDevice());
|
||||
|
||||
// Condition 2: Already using full or absolute volume behavior
|
||||
boolean alreadyUsingFullOrAbsoluteVolume =
|
||||
currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL
|
||||
|| currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE;
|
||||
// Condition 3: CEC volume is enabled
|
||||
boolean cecVolumeEnabled =
|
||||
getHdmiCecVolumeControl() == HdmiControlManager.VOLUME_CONTROL_ENABLED;
|
||||
|
||||
if (!cecVolumeEnabled || !alreadyUsingFullOrAbsoluteVolume) {
|
||||
disableAbsoluteVolumeControl();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for safety: if the System Audio device is a candidate for AVC, we should already
|
||||
// have received messages from it to trigger the other conditions.
|
||||
if (systemAudioDeviceInfo == null) {
|
||||
disableAbsoluteVolumeControl();
|
||||
return;
|
||||
}
|
||||
// Condition 4: The System Audio device supports AVC (i.e. <Set Audio Volume Level>).
|
||||
switch (systemAudioDeviceInfo.getDeviceFeatures().getSetAudioVolumeLevelSupport()) {
|
||||
case DeviceFeatures.FEATURE_SUPPORTED:
|
||||
if (!isAbsoluteVolumeControlEnabled()) {
|
||||
// Start an action that will call {@link #enableAbsoluteVolumeControl}
|
||||
// once the System Audio device sends <Report Audio Status>
|
||||
localCecDevice.addAvcAudioStatusAction(
|
||||
systemAudioDeviceInfo.getLogicalAddress());
|
||||
}
|
||||
return;
|
||||
case DeviceFeatures.FEATURE_NOT_SUPPORTED:
|
||||
disableAbsoluteVolumeControl();
|
||||
return;
|
||||
case DeviceFeatures.FEATURE_SUPPORT_UNKNOWN:
|
||||
disableAbsoluteVolumeControl();
|
||||
localCecDevice.queryAvcSupport(systemAudioDeviceInfo.getLogicalAddress());
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void disableAbsoluteVolumeControl() {
|
||||
if (isPlaybackDevice()) {
|
||||
playback().removeAvcAudioStatusAction();
|
||||
} else if (isTvDevice()) {
|
||||
tv().removeAvcAudioStatusAction();
|
||||
}
|
||||
AudioDeviceAttributes device = getAvcAudioOutputDevice();
|
||||
if (getDeviceVolumeBehavior(device) == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
|
||||
getAudioManager().setDeviceVolumeBehavior(device,
|
||||
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables Absolute Volume Control. Should only be called when all the conditions for
|
||||
* AVC are met (see {@link #checkAndUpdateAbsoluteVolumeControlState}).
|
||||
* @param audioStatus The initial audio status to set the audio output device to
|
||||
*/
|
||||
void enableAbsoluteVolumeControl(AudioStatus audioStatus) {
|
||||
HdmiCecLocalDevice localDevice = isPlaybackDevice() ? playback() : tv();
|
||||
HdmiDeviceInfo systemAudioDevice = getHdmiCecNetwork().getDeviceInfo(
|
||||
localDevice.findAudioReceiverAddress());
|
||||
VolumeInfo volumeInfo = new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
|
||||
.setMuted(audioStatus.getMute())
|
||||
.setVolumeIndex(audioStatus.getVolume())
|
||||
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
|
||||
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
|
||||
.build();
|
||||
mAbsoluteVolumeChangedListener = new AbsoluteVolumeChangedListener(
|
||||
localDevice, systemAudioDevice);
|
||||
|
||||
// AudioService sets the volume of the stream and device based on the input VolumeInfo
|
||||
// when enabling absolute volume behavior, but not the mute state
|
||||
notifyAvcMuteChange(audioStatus.getMute());
|
||||
getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeBehavior(
|
||||
getAvcAudioOutputDevice(), volumeInfo, mServiceThreadExecutor,
|
||||
mAbsoluteVolumeChangedListener, true);
|
||||
}
|
||||
|
||||
private AbsoluteVolumeChangedListener mAbsoluteVolumeChangedListener;
|
||||
|
||||
@VisibleForTesting
|
||||
AbsoluteVolumeChangedListener getAbsoluteVolumeChangedListener() {
|
||||
return mAbsoluteVolumeChangedListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listeners for changes reported by AudioService to the state of an audio output device using
|
||||
* absolute volume behavior.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
class AbsoluteVolumeChangedListener implements
|
||||
AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener {
|
||||
private HdmiCecLocalDevice mLocalDevice;
|
||||
private HdmiDeviceInfo mSystemAudioDevice;
|
||||
|
||||
private AbsoluteVolumeChangedListener(HdmiCecLocalDevice localDevice,
|
||||
HdmiDeviceInfo systemAudioDevice) {
|
||||
mLocalDevice = localDevice;
|
||||
mSystemAudioDevice = systemAudioDevice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when AudioService sets the volume level of an absolute volume audio output device
|
||||
* to a numeric value.
|
||||
*/
|
||||
@Override
|
||||
public void onAudioDeviceVolumeChanged(
|
||||
@NonNull AudioDeviceAttributes audioDevice,
|
||||
@NonNull VolumeInfo volumeInfo) {
|
||||
int localDeviceAddress;
|
||||
synchronized (mLocalDevice.mLock) {
|
||||
localDeviceAddress = mLocalDevice.getDeviceInfo().getLogicalAddress();
|
||||
}
|
||||
sendCecCommand(SetAudioVolumeLevelMessage.build(
|
||||
localDeviceAddress,
|
||||
mSystemAudioDevice.getLogicalAddress(),
|
||||
volumeInfo.getVolumeIndex()),
|
||||
// If sending the message fails, ask the System Audio device for its
|
||||
// audio status so that we can update AudioService
|
||||
(int errorCode) -> {
|
||||
if (errorCode == SendMessageResult.SUCCESS) {
|
||||
// Update the volume tracked in our AbsoluteVolumeAudioStatusAction
|
||||
// so it correctly processes incoming <Report Audio Status> messages
|
||||
HdmiCecLocalDevice avcDevice = isTvDevice() ? tv() : playback();
|
||||
avcDevice.updateAvcVolume(volumeInfo.getVolumeIndex());
|
||||
} else {
|
||||
sendCecCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(
|
||||
localDeviceAddress,
|
||||
mSystemAudioDevice.getLogicalAddress()
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when AudioService adjusts the volume or mute state of an absolute volume
|
||||
* audio output device
|
||||
*/
|
||||
@Override
|
||||
public void onAudioDeviceVolumeAdjusted(
|
||||
@NonNull AudioDeviceAttributes audioDevice,
|
||||
@NonNull VolumeInfo volumeInfo,
|
||||
@AudioManager.VolumeAdjustment int direction,
|
||||
@AudioDeviceVolumeManager.VolumeAdjustmentMode int mode
|
||||
) {
|
||||
int keyCode;
|
||||
switch (direction) {
|
||||
case AudioManager.ADJUST_RAISE:
|
||||
keyCode = KeyEvent.KEYCODE_VOLUME_UP;
|
||||
break;
|
||||
case AudioManager.ADJUST_LOWER:
|
||||
keyCode = KeyEvent.KEYCODE_VOLUME_DOWN;
|
||||
break;
|
||||
case AudioManager.ADJUST_TOGGLE_MUTE:
|
||||
case AudioManager.ADJUST_MUTE:
|
||||
case AudioManager.ADJUST_UNMUTE:
|
||||
// Many CEC devices only support toggle mute. Therefore, we send the
|
||||
// same keycode for all three mute options.
|
||||
keyCode = KeyEvent.KEYCODE_VOLUME_MUTE;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case AudioDeviceVolumeManager.ADJUST_MODE_NORMAL:
|
||||
mLocalDevice.sendVolumeKeyEvent(keyCode, true);
|
||||
mLocalDevice.sendVolumeKeyEvent(keyCode, false);
|
||||
break;
|
||||
case AudioDeviceVolumeManager.ADJUST_MODE_START:
|
||||
mLocalDevice.sendVolumeKeyEvent(keyCode, true);
|
||||
break;
|
||||
case AudioDeviceVolumeManager.ADJUST_MODE_END:
|
||||
mLocalDevice.sendVolumeKeyEvent(keyCode, false);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies AudioService of a change in the volume of the System Audio device. Has no effect if
|
||||
* AVC is disabled, or the audio output device for AVC is not playing for STREAM_MUSIC
|
||||
*/
|
||||
void notifyAvcVolumeChange(int volume) {
|
||||
if (!isAbsoluteVolumeControlEnabled()) return;
|
||||
List<AudioDeviceAttributes> streamMusicDevices =
|
||||
getAudioManager().getDevicesForAttributes(STREAM_MUSIC_ATTRIBUTES);
|
||||
if (streamMusicDevices.contains(getAvcAudioOutputDevice())) {
|
||||
setStreamMusicVolume(volume, AudioManager.FLAG_ABSOLUTE_VOLUME);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies AudioService of a change in the mute status of the System Audio device. Has no
|
||||
* effect if AVC is disabled, or the audio output device for AVC is not playing for STREAM_MUSIC
|
||||
*/
|
||||
void notifyAvcMuteChange(boolean mute) {
|
||||
if (!isAbsoluteVolumeControlEnabled()) return;
|
||||
List<AudioDeviceAttributes> streamMusicDevices =
|
||||
getAudioManager().getDevicesForAttributes(STREAM_MUSIC_ATTRIBUTES);
|
||||
if (streamMusicDevices.contains(getAvcAudioOutputDevice())) {
|
||||
int direction = mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE;
|
||||
getAudioManager().adjustStreamVolume(AudioManager.STREAM_MUSIC, direction,
|
||||
AudioManager.FLAG_ABSOLUTE_VOLUME);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the volume index of {@link AudioManager#STREAM_MUSIC}. Rescales the input volume index
|
||||
* from HDMI-CEC volume range to STREAM_MUSIC's.
|
||||
*/
|
||||
void setStreamMusicVolume(int volume, int flags) {
|
||||
getAudioManager().setStreamVolume(AudioManager.STREAM_MUSIC,
|
||||
volume * mStreamMusicMaxVolume / AudioStatus.MAX_VOLUME, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,8 +172,19 @@ final class SendKeyAction extends HdmiCecFeatureAction {
|
||||
}
|
||||
|
||||
private void sendKeyUp() {
|
||||
sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
|
||||
mTargetAddress));
|
||||
// When using Absolute Volume Control, query audio status after a volume key is released.
|
||||
// This allows us to notify AudioService of the resulting volume or mute status changes.
|
||||
if (HdmiCecKeycode.isVolumeKeycode(mLastKeycode)
|
||||
&& localDevice().getService().isAbsoluteVolumeControlEnabled()) {
|
||||
sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
|
||||
mTargetAddress),
|
||||
__ -> sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(
|
||||
getSourceAddress(),
|
||||
localDevice().findAudioReceiverAddress())));
|
||||
} else {
|
||||
sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
|
||||
mTargetAddress));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -122,4 +122,11 @@ public class SetAudioVolumeLevelDiscoveryAction extends HdmiCecFeatureAction {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the logical address of this action's target device.
|
||||
*/
|
||||
public int getTargetAddress() {
|
||||
return mTargetAddress;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
|
||||
boolean receivedStatus = HdmiUtils.parseCommandParamSystemAudioStatus(cmd);
|
||||
if (receivedStatus == mTargetAudioStatus) {
|
||||
setSystemAudioMode(receivedStatus);
|
||||
startAudioStatusAction();
|
||||
finish();
|
||||
return true;
|
||||
} else {
|
||||
HdmiLogger.debug("Unexpected system audio mode request:" + receivedStatus);
|
||||
@@ -168,11 +168,6 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
|
||||
}
|
||||
}
|
||||
|
||||
protected void startAudioStatusAction() {
|
||||
addAndStartAction(new SystemAudioStatusAction(tv(), mAvrLogicalAddress, mCallbacks));
|
||||
finish();
|
||||
}
|
||||
|
||||
protected void removeSystemAudioActionInProgress() {
|
||||
removeActionExcept(SystemAudioActionFromTv.class, this);
|
||||
removeActionExcept(SystemAudioActionFromAvr.class, this);
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import android.hardware.hdmi.HdmiDeviceInfo;
|
||||
import android.hardware.hdmi.HdmiControlManager;
|
||||
import android.hardware.hdmi.HdmiDeviceInfo;
|
||||
import android.hardware.hdmi.IHdmiControlCallback;
|
||||
|
||||
/**
|
||||
@@ -65,7 +65,7 @@ final class SystemAudioActionFromAvr extends SystemAudioAction {
|
||||
|
||||
if (mTargetAudioStatus) {
|
||||
setSystemAudioMode(true);
|
||||
startAudioStatusAction();
|
||||
finish();
|
||||
} else {
|
||||
setSystemAudioMode(false);
|
||||
finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* 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.HdmiControlManager;
|
||||
import android.hardware.hdmi.IHdmiControlCallback;
|
||||
import android.hardware.tv.cec.V1_0.SendMessageResult;
|
||||
|
||||
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Action to update audio status (volume or mute) of audio amplifier
|
||||
*/
|
||||
final class SystemAudioStatusAction extends HdmiCecFeatureAction {
|
||||
private static final String TAG = "SystemAudioStatusAction";
|
||||
|
||||
// State that waits for <ReportAudioStatus>.
|
||||
private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 1;
|
||||
|
||||
private final int mAvrAddress;
|
||||
|
||||
SystemAudioStatusAction(
|
||||
HdmiCecLocalDevice source, int avrAddress, List<IHdmiControlCallback> callbacks) {
|
||||
super(source, callbacks);
|
||||
mAvrAddress = avrAddress;
|
||||
}
|
||||
|
||||
SystemAudioStatusAction(HdmiCecLocalDevice source, int avrAddress,
|
||||
IHdmiControlCallback callback) {
|
||||
super(source, callback);
|
||||
mAvrAddress = avrAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean start() {
|
||||
mState = STATE_WAIT_FOR_REPORT_AUDIO_STATUS;
|
||||
addTimer(mState, HdmiConfig.TIMEOUT_MS);
|
||||
sendGiveAudioStatus();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendGiveAudioStatus() {
|
||||
sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(), mAvrAddress),
|
||||
new SendMessageCallback() {
|
||||
@Override
|
||||
public void onSendCompleted(int error) {
|
||||
if (error != SendMessageResult.SUCCESS) {
|
||||
handleSendGiveAudioStatusFailure();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleSendGiveAudioStatusFailure() {
|
||||
|
||||
// Still return SUCCESS to callback.
|
||||
finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean processCommand(HdmiCecMessage cmd) {
|
||||
if (mState != STATE_WAIT_FOR_REPORT_AUDIO_STATUS || mAvrAddress != cmd.getSource()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (cmd.getOpcode()) {
|
||||
case Constants.MESSAGE_REPORT_AUDIO_STATUS:
|
||||
handleReportAudioStatus(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleReportAudioStatus(HdmiCecMessage cmd) {
|
||||
byte[] params = cmd.getParams();
|
||||
boolean mute = HdmiUtils.isAudioStatusMute(cmd);
|
||||
int volume = HdmiUtils.getAudioStatusVolume(cmd);
|
||||
tv().setAudioStatus(mute, volume);
|
||||
|
||||
if (!(tv().isSystemAudioActivated() ^ mute)) {
|
||||
// Toggle AVR's mute status to match with the system audio status.
|
||||
sendUserControlPressedAndReleased(mAvrAddress, HdmiCecKeycode.CEC_KEYCODE_MUTE);
|
||||
}
|
||||
finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
void handleTimerEvent(int state) {
|
||||
if (mState != state) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleSendGiveAudioStatusFailure();
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,8 @@ public class ActiveSourceActionTest {
|
||||
public void setUp() throws Exception {
|
||||
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
|
||||
|
||||
mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
|
||||
mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
AudioManager getAudioManager() {
|
||||
return new AudioManager() {
|
||||
@@ -93,6 +94,7 @@ public class ActiveSourceActionTest {
|
||||
mHdmiControlService.setCecController(hdmiCecController);
|
||||
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
|
||||
mHdmiControlService.setPowerManager(mPowerManager);
|
||||
mPhysicalAddress = 0x2000;
|
||||
@@ -153,7 +155,6 @@ public class ActiveSourceActionTest {
|
||||
mHdmiControlService);
|
||||
audioDevice.init();
|
||||
mLocalDevices.add(audioDevice);
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
|
||||
@@ -68,7 +68,8 @@ public class ArcInitiationActionFromAvrTest {
|
||||
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
|
||||
|
||||
HdmiControlService hdmiControlService =
|
||||
new HdmiControlService(mContextSpy, Collections.emptyList()) {
|
||||
new HdmiControlService(mContextSpy, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
boolean isPowerStandby() {
|
||||
return false;
|
||||
|
||||
@@ -68,7 +68,8 @@ public class ArcTerminationActionFromAvrTest {
|
||||
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
|
||||
|
||||
HdmiControlService hdmiControlService =
|
||||
new HdmiControlService(mContextSpy, Collections.emptyList()) {
|
||||
new HdmiControlService(mContextSpy, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
AudioManager getAudioManager() {
|
||||
return mAudioManager;
|
||||
|
||||
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* 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.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
|
||||
|
||||
import static com.android.server.hdmi.HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP;
|
||||
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_BOOT_UP;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.TruthJUnit.assume;
|
||||
|
||||
import static org.mockito.AdditionalMatchers.not;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.hardware.hdmi.DeviceFeatures;
|
||||
import android.hardware.hdmi.HdmiControlManager;
|
||||
import android.hardware.hdmi.HdmiDeviceInfo;
|
||||
import android.hardware.hdmi.HdmiPortInfo;
|
||||
import android.hardware.tv.cec.V1_0.SendMessageResult;
|
||||
import android.media.AudioDeviceAttributes;
|
||||
import android.media.AudioDeviceVolumeManager;
|
||||
import android.media.AudioManager;
|
||||
import android.media.VolumeInfo;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.os.test.TestLooper;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import com.android.server.SystemService;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Tests that Absolute Volume Control (AVC) is enabled and disabled correctly, and that
|
||||
* the device responds correctly to incoming <Report Audio Status> messages and API calls
|
||||
* from AudioService when AVC is active.
|
||||
*
|
||||
* This is an abstract base class. Concrete subclasses specify the type of the local device, and the
|
||||
* type of the System Audio device. This allows each test to be run for multiple setups.
|
||||
*
|
||||
* We test the following pairs of (local device, System Audio device):
|
||||
* (Playback, TV): {@link PlaybackDeviceToTvAvcTest}
|
||||
* (Playback, Audio System): {@link PlaybackDeviceToAudioSystemAvcTest}
|
||||
* (TV, Audio System): {@link TvToAudioSystemAvcTest}
|
||||
*/
|
||||
public abstract class BaseAbsoluteVolumeControlTest {
|
||||
private HdmiControlService mHdmiControlService;
|
||||
private HdmiCecController mHdmiCecController;
|
||||
private HdmiCecLocalDevice mHdmiCecLocalDevice;
|
||||
private FakeHdmiCecConfig mHdmiCecConfig;
|
||||
private FakePowerManagerWrapper mPowerManager;
|
||||
private Looper mLooper;
|
||||
private Context mContextSpy;
|
||||
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
|
||||
|
||||
@Mock protected AudioManager mAudioManager;
|
||||
protected FakeAudioDeviceVolumeManagerWrapper mAudioDeviceVolumeManager;
|
||||
|
||||
protected TestLooper mTestLooper = new TestLooper();
|
||||
protected FakeNativeWrapper mNativeWrapper;
|
||||
|
||||
// Audio Status given by the System Audio device in its initial <Report Audio Status> that
|
||||
// triggers AVC being enabled
|
||||
private static final AudioStatus INITIAL_SYSTEM_AUDIO_DEVICE_STATUS =
|
||||
new AudioStatus(50, false);
|
||||
|
||||
// VolumeInfo passed to AudioDeviceVolumeManager#setDeviceAbsoluteVolumeBehavior to enable AVC
|
||||
private static final VolumeInfo ENABLE_AVC_VOLUME_INFO =
|
||||
new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
|
||||
.setMuted(INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute())
|
||||
.setVolumeIndex(INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume())
|
||||
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
|
||||
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
|
||||
.build();
|
||||
|
||||
protected abstract HdmiCecLocalDevice createLocalDevice(HdmiControlService hdmiControlService);
|
||||
|
||||
protected abstract int getPhysicalAddress();
|
||||
protected abstract int getDeviceType();
|
||||
protected abstract AudioDeviceAttributes getAudioOutputDevice();
|
||||
|
||||
protected abstract int getSystemAudioDeviceLogicalAddress();
|
||||
protected abstract int getSystemAudioDeviceType();
|
||||
|
||||
@Before
|
||||
public void setUp() throws RemoteException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContextSpy = spy(new ContextWrapper(
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext()));
|
||||
|
||||
mAudioDeviceVolumeManager = spy(new FakeAudioDeviceVolumeManagerWrapper());
|
||||
|
||||
mHdmiControlService =
|
||||
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
|
||||
Collections.singletonList(getDeviceType()),
|
||||
mAudioDeviceVolumeManager) {
|
||||
@Override
|
||||
AudioManager getAudioManager() {
|
||||
return mAudioManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeStringSystemProperty(String key, String value) {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
|
||||
mLooper = mTestLooper.getLooper();
|
||||
mHdmiControlService.setIoLooper(mLooper);
|
||||
|
||||
mHdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
|
||||
mHdmiCecConfig.setIntValue(
|
||||
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
|
||||
HdmiControlManager.HDMI_CEC_VERSION_2_0);
|
||||
mHdmiCecConfig.setIntValue(
|
||||
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
|
||||
HdmiControlManager.VOLUME_CONTROL_DISABLED);
|
||||
mHdmiControlService.setHdmiCecConfig(mHdmiCecConfig);
|
||||
|
||||
mNativeWrapper = new FakeNativeWrapper();
|
||||
mNativeWrapper.setPhysicalAddress(getPhysicalAddress());
|
||||
mNativeWrapper.setPollAddressResponse(Constants.ADDR_TV, SendMessageResult.SUCCESS);
|
||||
|
||||
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
|
||||
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
|
||||
mHdmiControlService.setCecController(mHdmiCecController);
|
||||
mHdmiControlService.setHdmiMhlController(
|
||||
HdmiMhlControllerStub.create(mHdmiControlService));
|
||||
mHdmiControlService.initService();
|
||||
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
|
||||
mHdmiControlService.setPowerManager(mPowerManager);
|
||||
|
||||
mHdmiCecLocalDevice = createLocalDevice(mHdmiControlService);
|
||||
mHdmiCecLocalDevice.init();
|
||||
mLocalDevices.add(mHdmiCecLocalDevice);
|
||||
|
||||
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
|
||||
hdmiPortInfos[0] =
|
||||
new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
|
||||
mNativeWrapper.setPortInfo(hdmiPortInfos);
|
||||
mNativeWrapper.setPortConnectionStatus(1, true);
|
||||
|
||||
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_BOOT_UP);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
// Simulate AudioManager's behavior and response when setDeviceVolumeBehavior is called
|
||||
doAnswer(invocation -> {
|
||||
setDeviceVolumeBehavior(invocation.getArgument(0), invocation.getArgument(1));
|
||||
return null;
|
||||
}).when(mAudioManager).setDeviceVolumeBehavior(any(), anyInt());
|
||||
|
||||
// Set starting volume behavior
|
||||
doReturn(AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE)
|
||||
.when(mAudioManager).getDeviceVolumeBehavior(eq(getAudioOutputDevice()));
|
||||
|
||||
// Audio service always plays STREAM_MUSIC on the device we need
|
||||
doReturn(Collections.singletonList(getAudioOutputDevice())).when(mAudioManager)
|
||||
.getDevicesForAttributes(HdmiControlService.STREAM_MUSIC_ATTRIBUTES);
|
||||
|
||||
// Max volume of STREAM_MUSIC
|
||||
doReturn(25).when(mAudioManager).getStreamMaxVolume(AudioManager.STREAM_MUSIC);
|
||||
|
||||
// Receive messages from devices to make sure they're registered in HdmiCecNetwork
|
||||
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
|
||||
Constants.ADDR_TV, getLogicalAddress()));
|
||||
if (getSystemAudioDeviceType() == DEVICE_AUDIO_SYSTEM) {
|
||||
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
|
||||
Constants.ADDR_AUDIO_SYSTEM, getLogicalAddress()));
|
||||
}
|
||||
|
||||
mHdmiControlService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
|
||||
mHdmiControlService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
|
||||
mTestLooper.dispatchAll();
|
||||
}
|
||||
|
||||
protected int getLogicalAddress() {
|
||||
synchronized (mHdmiCecLocalDevice.mLock) {
|
||||
return mHdmiCecLocalDevice.getDeviceInfo().getLogicalAddress();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates the volume behavior of {@code device} being set to {@code behavior}.
|
||||
*/
|
||||
protected void setDeviceVolumeBehavior(AudioDeviceAttributes device,
|
||||
@AudioManager.DeviceVolumeBehavior int behavior) {
|
||||
doReturn(behavior).when(mAudioManager).getDeviceVolumeBehavior(eq(device));
|
||||
mHdmiControlService.onDeviceVolumeBehaviorChanged(device, behavior);
|
||||
mTestLooper.dispatchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the setting for CEC volume.
|
||||
*/
|
||||
protected void setCecVolumeControlSetting(@HdmiControlManager.VolumeControl int setting) {
|
||||
mHdmiControlService.getHdmiCecConfig().setIntValue(
|
||||
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, setting);
|
||||
mTestLooper.dispatchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the device receive a <Report Features> message from the System Audio device specifying
|
||||
* whether <Set Audio Volume Level> is supported or not.
|
||||
*/
|
||||
protected void receiveSetAudioVolumeLevelSupport(
|
||||
@DeviceFeatures.FeatureSupportStatus int featureSupportStatus) {
|
||||
// <Report Features> can't specify an unknown feature support status
|
||||
if (featureSupportStatus != DeviceFeatures.FEATURE_SUPPORT_UNKNOWN) {
|
||||
mNativeWrapper.onCecMessage(ReportFeaturesMessage.build(
|
||||
getSystemAudioDeviceLogicalAddress(), HdmiControlManager.HDMI_CEC_VERSION_2_0,
|
||||
Arrays.asList(getSystemAudioDeviceType()), Constants.RC_PROFILE_SOURCE,
|
||||
Collections.emptyList(),
|
||||
DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
|
||||
.setSetAudioVolumeLevelSupport(featureSupportStatus)
|
||||
.build()));
|
||||
mTestLooper.dispatchAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables System Audio mode if the System Audio device is an Audio System.
|
||||
*/
|
||||
protected void enableSystemAudioModeIfNeeded() {
|
||||
if (getSystemAudioDeviceType() == DEVICE_AUDIO_SYSTEM) {
|
||||
receiveSetSystemAudioMode(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets System Audio Mode by having the device receive <Set System Audio Mode>
|
||||
* from the Audio System.
|
||||
*/
|
||||
protected void receiveSetSystemAudioMode(boolean status) {
|
||||
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildSetSystemAudioMode(
|
||||
Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_BROADCAST, status));
|
||||
mTestLooper.dispatchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the device receive a <Report Audio Status> reporting the status in
|
||||
* {@link #INITIAL_SYSTEM_AUDIO_DEVICE_STATUS}
|
||||
*/
|
||||
protected void receiveInitialReportAudioStatus() {
|
||||
receiveReportAudioStatus(
|
||||
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume(),
|
||||
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the device receive a <Report Audio Status> message from the System Audio Device.
|
||||
*/
|
||||
protected void receiveReportAudioStatus(int volume, boolean mute) {
|
||||
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportAudioStatus(
|
||||
getSystemAudioDeviceLogicalAddress(),
|
||||
getLogicalAddress(),
|
||||
volume,
|
||||
mute));
|
||||
mTestLooper.dispatchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers all the conditions required to enable Absolute Volume Control.
|
||||
*/
|
||||
protected void enableAbsoluteVolumeControl() {
|
||||
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
|
||||
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
|
||||
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
|
||||
enableSystemAudioModeIfNeeded();
|
||||
receiveInitialReportAudioStatus();
|
||||
|
||||
verifyAbsoluteVolumeEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that AVC was enabled - that is the audio output device's volume behavior was last
|
||||
* set to absolute volume behavior.
|
||||
*/
|
||||
protected void verifyAbsoluteVolumeEnabled() {
|
||||
InOrder inOrder = inOrder(mAudioManager, mAudioDeviceVolumeManager);
|
||||
inOrder.verify(mAudioDeviceVolumeManager, atLeastOnce()).setDeviceAbsoluteVolumeBehavior(
|
||||
eq(getAudioOutputDevice()), any(), any(), any(), anyBoolean());
|
||||
inOrder.verify(mAudioManager, never()).setDeviceVolumeBehavior(
|
||||
eq(getAudioOutputDevice()), not(eq(AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that AVC was disabled - that is, the audio output device's volume behavior was
|
||||
* last set to something other than absolute volume behavior.
|
||||
*/
|
||||
protected void verifyAbsoluteVolumeDisabled() {
|
||||
InOrder inOrder = inOrder(mAudioManager, mAudioDeviceVolumeManager);
|
||||
inOrder.verify(mAudioManager, atLeastOnce()).setDeviceVolumeBehavior(
|
||||
eq(getAudioOutputDevice()), not(eq(AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE)));
|
||||
inOrder.verify(mAudioDeviceVolumeManager, never()).setDeviceAbsoluteVolumeBehavior(
|
||||
eq(getAudioOutputDevice()), any(), any(), any(), anyBoolean());
|
||||
}
|
||||
|
||||
protected void verifyGiveAudioStatusNeverSent() {
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(
|
||||
HdmiCecMessageBuilder.buildGiveAudioStatus(
|
||||
getLogicalAddress(), getSystemAudioDeviceLogicalAddress()));
|
||||
}
|
||||
|
||||
protected void verifyGiveAudioStatusSent() {
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(
|
||||
HdmiCecMessageBuilder.buildGiveAudioStatus(
|
||||
getLogicalAddress(), getSystemAudioDeviceLogicalAddress()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allConditionsExceptSavlSupportMet_sendsSetAudioVolumeLevelAndGiveFeatures() {
|
||||
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
|
||||
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
|
||||
enableSystemAudioModeIfNeeded();
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(
|
||||
SetAudioVolumeLevelMessage.build(
|
||||
getLogicalAddress(), getSystemAudioDeviceLogicalAddress(),
|
||||
Constants.AUDIO_VOLUME_STATUS_UNKNOWN));
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(
|
||||
HdmiCecMessageBuilder.buildGiveFeatures(
|
||||
getLogicalAddress(), getSystemAudioDeviceLogicalAddress()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allConditionsMet_savlSupportLast_reportFeatures_giveAudioStatusSent() {
|
||||
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
|
||||
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
|
||||
enableSystemAudioModeIfNeeded();
|
||||
verifyGiveAudioStatusNeverSent();
|
||||
|
||||
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
|
||||
verifyGiveAudioStatusSent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allConditionsMet_savlSupportLast_noFeatureAbort_giveAudioStatusSent() {
|
||||
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
|
||||
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
|
||||
enableSystemAudioModeIfNeeded();
|
||||
verifyGiveAudioStatusNeverSent();
|
||||
|
||||
mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
|
||||
mTestLooper.dispatchAll();
|
||||
verifyGiveAudioStatusSent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allConditionsMet_cecVolumeEnabledLast_giveAudioStatusSent() {
|
||||
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
|
||||
enableSystemAudioModeIfNeeded();
|
||||
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
|
||||
verifyGiveAudioStatusNeverSent();
|
||||
|
||||
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
|
||||
verifyGiveAudioStatusSent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allConditionsMet_fullVolumeBehaviorLast_giveAudioStatusSent() {
|
||||
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
|
||||
enableSystemAudioModeIfNeeded();
|
||||
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
|
||||
verifyGiveAudioStatusNeverSent();
|
||||
|
||||
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
|
||||
verifyGiveAudioStatusSent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allConditionsMet_systemAudioModeEnabledLast_giveAudioStatusSent() {
|
||||
// Only run when the System Audio device is an Audio System.
|
||||
assume().that(getSystemAudioDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
|
||||
|
||||
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
|
||||
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
|
||||
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
|
||||
verifyGiveAudioStatusNeverSent();
|
||||
|
||||
receiveSetSystemAudioMode(true);
|
||||
verifyGiveAudioStatusSent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void giveAudioStatusSent_systemAudioDeviceSendsReportAudioStatus_avcEnabled() {
|
||||
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
|
||||
enableSystemAudioModeIfNeeded();
|
||||
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
|
||||
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
|
||||
|
||||
// Verify that AVC was never enabled
|
||||
verify(mAudioDeviceVolumeManager, never()).setDeviceAbsoluteVolumeBehavior(
|
||||
eq(getAudioOutputDevice()), any(), any(), any(), anyBoolean());
|
||||
receiveInitialReportAudioStatus();
|
||||
|
||||
verifyAbsoluteVolumeEnabled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void avcEnabled_cecVolumeDisabled_absoluteVolumeDisabled() {
|
||||
enableAbsoluteVolumeControl();
|
||||
|
||||
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_DISABLED);
|
||||
verifyAbsoluteVolumeDisabled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void avcEnabled_setAudioVolumeLevelNotSupported_absoluteVolumeDisabled() {
|
||||
enableAbsoluteVolumeControl();
|
||||
|
||||
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
|
||||
verifyAbsoluteVolumeDisabled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void avcEnabled_setAudioVolumeLevelFeatureAborted_absoluteVolumeDisabled() {
|
||||
enableAbsoluteVolumeControl();
|
||||
|
||||
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
|
||||
getSystemAudioDeviceLogicalAddress(), getLogicalAddress(),
|
||||
Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, Constants.ABORT_UNRECOGNIZED_OPCODE));
|
||||
mTestLooper.dispatchAll();
|
||||
verifyAbsoluteVolumeDisabled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void avcEnabled_systemAudioModeDisabled_absoluteVolumeDisabled() {
|
||||
// Only run when the System Audio device is an Audio System.
|
||||
assume().that(getSystemAudioDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
|
||||
|
||||
enableAbsoluteVolumeControl();
|
||||
|
||||
receiveSetSystemAudioMode(false);
|
||||
verifyAbsoluteVolumeDisabled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void avcEnabled_receiveReportAudioStatus_notifiesVolumeOrMuteChanges() {
|
||||
// Initial <Report Audio Status> has volume=50 and mute=false
|
||||
enableAbsoluteVolumeControl();
|
||||
|
||||
// New volume and mute status: sets both
|
||||
receiveReportAudioStatus(20, true);
|
||||
verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(5),
|
||||
anyInt());
|
||||
verify(mAudioManager).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
|
||||
eq(AudioManager.ADJUST_MUTE), anyInt());
|
||||
clearInvocations(mAudioManager);
|
||||
|
||||
// New volume only: sets volume only
|
||||
receiveReportAudioStatus(32, true);
|
||||
verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8),
|
||||
anyInt());
|
||||
verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
|
||||
eq(AudioManager.ADJUST_MUTE), anyInt());
|
||||
clearInvocations(mAudioManager);
|
||||
|
||||
// New mute status only: sets mute only
|
||||
receiveReportAudioStatus(32, false);
|
||||
verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8),
|
||||
anyInt());
|
||||
verify(mAudioManager).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
|
||||
eq(AudioManager.ADJUST_UNMUTE), anyInt());
|
||||
clearInvocations(mAudioManager);
|
||||
|
||||
// Repeat of earlier message: sets neither volume nor mute
|
||||
receiveReportAudioStatus(32, false);
|
||||
verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8),
|
||||
anyInt());
|
||||
verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
|
||||
eq(AudioManager.ADJUST_UNMUTE), anyInt());
|
||||
clearInvocations(mAudioManager);
|
||||
|
||||
// If AudioService causes us to send <Set Audio Volume Level>, the System Audio device's
|
||||
// volume changes. Afterward, a duplicate of an earlier <Report Audio Status> should
|
||||
// still cause us to call setStreamVolume()
|
||||
mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeChanged(
|
||||
getAudioOutputDevice(),
|
||||
new VolumeInfo.Builder(ENABLE_AVC_VOLUME_INFO)
|
||||
.setVolumeIndex(20)
|
||||
.build()
|
||||
);
|
||||
mTestLooper.dispatchAll();
|
||||
receiveReportAudioStatus(32, false);
|
||||
verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8),
|
||||
anyInt());
|
||||
verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC),
|
||||
eq(AudioManager.ADJUST_UNMUTE), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void avcEnabled_audioDeviceVolumeAdjusted_sendsUserControlPressedAndGiveAudioStatus() {
|
||||
enableAbsoluteVolumeControl();
|
||||
mNativeWrapper.clearResultMessages();
|
||||
|
||||
mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeAdjusted(
|
||||
getAudioOutputDevice(),
|
||||
ENABLE_AVC_VOLUME_INFO,
|
||||
AudioManager.ADJUST_RAISE,
|
||||
AudioDeviceVolumeManager.ADJUST_MODE_NORMAL
|
||||
);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(
|
||||
HdmiCecMessageBuilder.buildUserControlPressed(getLogicalAddress(),
|
||||
getSystemAudioDeviceLogicalAddress(), CEC_KEYCODE_VOLUME_UP));
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(
|
||||
HdmiCecMessageBuilder.buildUserControlReleased(getLogicalAddress(),
|
||||
getSystemAudioDeviceLogicalAddress()));
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(
|
||||
HdmiCecMessageBuilder.buildGiveAudioStatus(getLogicalAddress(),
|
||||
getSystemAudioDeviceLogicalAddress()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void avcEnabled_audioDeviceVolumeChanged_sendsSetAudioVolumeLevel() {
|
||||
enableAbsoluteVolumeControl();
|
||||
mNativeWrapper.clearResultMessages();
|
||||
|
||||
mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeChanged(
|
||||
getAudioOutputDevice(),
|
||||
new VolumeInfo.Builder(ENABLE_AVC_VOLUME_INFO)
|
||||
.setVolumeIndex(20)
|
||||
.build()
|
||||
);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(
|
||||
SetAudioVolumeLevelMessage.build(getLogicalAddress(),
|
||||
getSystemAudioDeviceLogicalAddress(), 20));
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public class DetectTvSystemAudioModeSupportActionTest {
|
||||
mDeviceInfoForTests = HdmiDeviceInfo.hardwarePort(1001, 1234);
|
||||
HdmiControlService hdmiControlService =
|
||||
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
|
||||
Collections.emptyList()) {
|
||||
Collections.emptyList(), new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
|
||||
@Override
|
||||
void sendCecCommand(
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
|
||||
import static com.android.server.hdmi.Constants.ADDR_TV;
|
||||
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
|
||||
@@ -78,7 +79,8 @@ public class DevicePowerStatusActionTest {
|
||||
|
||||
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
|
||||
|
||||
mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
|
||||
mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
AudioManager getAudioManager() {
|
||||
return new AudioManager() {
|
||||
@@ -110,6 +112,7 @@ public class DevicePowerStatusActionTest {
|
||||
mHdmiControlService.setCecController(hdmiCecController);
|
||||
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
|
||||
mHdmiControlService.setPowerManager(mPowerManager);
|
||||
mPhysicalAddress = 0x2000;
|
||||
|
||||
@@ -20,6 +20,7 @@ import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_ON;
|
||||
import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_STANDBY;
|
||||
import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
|
||||
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
|
||||
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_3;
|
||||
@@ -100,7 +101,7 @@ public class DeviceSelectActionFromPlaybackTest {
|
||||
|
||||
mHdmiControlService =
|
||||
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
|
||||
Collections.emptyList()) {
|
||||
Collections.emptyList(), new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
boolean isControlEnabled() {
|
||||
return true;
|
||||
@@ -136,6 +137,7 @@ public class DeviceSelectActionFromPlaybackTest {
|
||||
|
||||
mLocalDevices.add(mHdmiCecLocalDevicePlayback);
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
|
||||
mNativeWrapper.setPhysicalAddress(0x0000);
|
||||
mPowerManager = new FakePowerManagerWrapper(context);
|
||||
|
||||
@@ -20,6 +20,7 @@ import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_ON;
|
||||
import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_STANDBY;
|
||||
import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
|
||||
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
|
||||
import static com.android.server.hdmi.Constants.ADDR_TV;
|
||||
@@ -109,7 +110,7 @@ public class DeviceSelectActionFromTvTest {
|
||||
|
||||
mHdmiControlService =
|
||||
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
|
||||
Collections.emptyList()) {
|
||||
Collections.emptyList(), new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
boolean isControlEnabled() {
|
||||
return true;
|
||||
@@ -145,6 +146,7 @@ public class DeviceSelectActionFromTvTest {
|
||||
true, false, false);
|
||||
mNativeWrapper.setPortInfo(hdmiPortInfos);
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mPowerManager = new FakePowerManagerWrapper(context);
|
||||
mHdmiControlService.setPowerManager(mPowerManager);
|
||||
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.media.AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener;
|
||||
import static android.media.AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.NonNull;
|
||||
import android.media.AudioDeviceAttributes;
|
||||
import android.media.AudioDeviceVolumeManager;
|
||||
import android.media.AudioManager;
|
||||
import android.media.VolumeInfo;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Wrapper for {@link AudioDeviceVolumeManager} that stubs its methods. Useful for testing.
|
||||
*/
|
||||
public class FakeAudioDeviceVolumeManagerWrapper implements
|
||||
AudioDeviceVolumeManagerWrapperInterface {
|
||||
|
||||
private final Set<OnDeviceVolumeBehaviorChangedListener> mVolumeBehaviorListeners;
|
||||
|
||||
public FakeAudioDeviceVolumeManagerWrapper() {
|
||||
mVolumeBehaviorListeners = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOnDeviceVolumeBehaviorChangedListener(
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull OnDeviceVolumeBehaviorChangedListener listener)
|
||||
throws SecurityException {
|
||||
mVolumeBehaviorListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOnDeviceVolumeBehaviorChangedListener(
|
||||
@NonNull OnDeviceVolumeBehaviorChangedListener listener) {
|
||||
mVolumeBehaviorListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeviceAbsoluteVolumeBehavior(
|
||||
@NonNull AudioDeviceAttributes device,
|
||||
@NonNull VolumeInfo volume,
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull OnAudioDeviceVolumeChangedListener vclistener,
|
||||
boolean handlesVolumeAdjustment) {
|
||||
// Notify all volume behavior listeners that the device adopted absolute volume behavior
|
||||
for (OnDeviceVolumeBehaviorChangedListener listener : mVolumeBehaviorListeners) {
|
||||
listener.onDeviceVolumeBehaviorChanged(device,
|
||||
AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,8 @@ public class HdmiCecAtomLoggingTest {
|
||||
mContextSpy = spy(new ContextWrapper(
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext()));
|
||||
|
||||
mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
|
||||
mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()));
|
||||
doNothing().when(mHdmiControlServiceSpy)
|
||||
.writeStringSystemProperty(anyString(), anyString());
|
||||
doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
|
||||
|
||||
@@ -101,7 +101,8 @@ public class HdmiCecControllerTest {
|
||||
mMyLooper = mTestLooper.getLooper();
|
||||
|
||||
mHdmiControlServiceSpy = spy(new HdmiControlService(
|
||||
InstrumentationRegistry.getTargetContext(), Collections.emptyList()));
|
||||
InstrumentationRegistry.getTargetContext(), Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()));
|
||||
doReturn(mMyLooper).when(mHdmiControlServiceSpy).getIoLooper();
|
||||
doReturn(mMyLooper).when(mHdmiControlServiceSpy).getServiceLooper();
|
||||
doAnswer(__ -> mCecVersion).when(mHdmiControlServiceSpy).getCecVersion();
|
||||
|
||||
@@ -88,7 +88,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
|
||||
|
||||
mHdmiControlService =
|
||||
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
|
||||
Collections.emptyList()) {
|
||||
Collections.emptyList(), new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
AudioManager getAudioManager() {
|
||||
return new AudioManager() {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.Constants.ABORT_UNRECOGNIZED_OPCODE;
|
||||
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
|
||||
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
|
||||
@@ -92,7 +93,7 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
|
||||
mHdmiControlService =
|
||||
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
|
||||
Collections.emptyList()) {
|
||||
Collections.emptyList(), new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
void wakeUp() {
|
||||
mWokenUp = true;
|
||||
@@ -151,6 +152,7 @@ public class HdmiCecLocalDevicePlaybackTest {
|
||||
mNativeWrapper.setPortInfo(hdmiPortInfos);
|
||||
mNativeWrapper.setPortConnectionStatus(1, true);
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mPowerManager = new FakePowerManagerWrapper(context);
|
||||
mHdmiControlService.setPowerManager(mPowerManager);
|
||||
mHdmiControlService.setPowerManagerInternal(mPowerManagerInternal);
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.android.server.hdmi;
|
||||
|
||||
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
|
||||
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
|
||||
import static com.android.server.hdmi.Constants.ADDR_TV;
|
||||
@@ -137,7 +138,8 @@ public class HdmiCecLocalDeviceTest {
|
||||
Context context = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
mHdmiControlService =
|
||||
new HdmiControlService(context, Collections.emptyList()) {
|
||||
new HdmiControlService(context, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
boolean isControlEnabled() {
|
||||
return isControlEnabled;
|
||||
@@ -190,6 +192,7 @@ public class HdmiCecLocalDeviceTest {
|
||||
mNativeWrapper.setPortInfo(hdmiPortInfos);
|
||||
mNativeWrapper.setPortConnectionStatus(1, true);
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
|
||||
mNativeWrapper.setPhysicalAddress(0x2000);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.Constants.ABORT_UNRECOGNIZED_OPCODE;
|
||||
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
|
||||
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
|
||||
@@ -29,8 +30,10 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.hdmi.HdmiControlManager;
|
||||
@@ -125,7 +128,7 @@ public class HdmiCecLocalDeviceTvTest {
|
||||
|
||||
mHdmiControlService =
|
||||
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
|
||||
Collections.emptyList()) {
|
||||
Collections.emptyList(), new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
void wakeUp() {
|
||||
mWokenUp = true;
|
||||
@@ -179,6 +182,7 @@ public class HdmiCecLocalDeviceTvTest {
|
||||
new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true);
|
||||
mNativeWrapper.setPortInfo(hdmiPortInfos);
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mPowerManager = new FakePowerManagerWrapper(context);
|
||||
mHdmiControlService.setPowerManager(mPowerManager);
|
||||
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
|
||||
@@ -768,7 +772,7 @@ public class HdmiCecLocalDeviceTvTest {
|
||||
// When the device reports its physical address, the listener eventually is invoked.
|
||||
HdmiCecMessage reportPhysicalAddress =
|
||||
HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
|
||||
ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK);
|
||||
ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK);
|
||||
mNativeWrapper.onCecMessage(reportPhysicalAddress);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
@@ -777,6 +781,54 @@ public class HdmiCecLocalDeviceTvTest {
|
||||
assertThat(mDeviceEventListeners.size()).isEqualTo(1);
|
||||
assertThat(mDeviceEventListeners.get(0).getStatus())
|
||||
.isEqualTo(HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void receiveSetAudioVolumeLevel_samNotActivated_noFeatureAbort_volumeChanges() {
|
||||
when(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)).thenReturn(25);
|
||||
|
||||
// Max volume of STREAM_MUSIC is retrieved on boot
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
mNativeWrapper.onCecMessage(SetAudioVolumeLevelMessage.build(
|
||||
ADDR_PLAYBACK_1,
|
||||
ADDR_TV,
|
||||
20));
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
// <Feature Abort>[Not in correct mode] not sent
|
||||
HdmiCecMessage featureAbortMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
|
||||
ADDR_TV,
|
||||
ADDR_PLAYBACK_1,
|
||||
Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
|
||||
Constants.ABORT_NOT_IN_CORRECT_MODE);
|
||||
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbortMessage);
|
||||
|
||||
// <Set Audio Volume Level> uses volume range [0, 100]; STREAM_MUSIC uses range [0, 25]
|
||||
verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(5), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void receiveSetAudioVolumeLevel_samActivated_respondsFeatureAbort_noVolumeChange() {
|
||||
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildSetSystemAudioMode(
|
||||
ADDR_AUDIO_SYSTEM, ADDR_TV, true));
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
mNativeWrapper.onCecMessage(SetAudioVolumeLevelMessage.build(
|
||||
ADDR_PLAYBACK_1, ADDR_TV, 50));
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
// <Feature Abort>[Not in correct mode] sent
|
||||
HdmiCecMessage featureAbortMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
|
||||
ADDR_TV,
|
||||
ADDR_PLAYBACK_1,
|
||||
Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
|
||||
Constants.ABORT_NOT_IN_CORRECT_MODE);
|
||||
assertThat(mNativeWrapper.getResultMessages()).contains(featureAbortMessage);
|
||||
|
||||
// AudioManager not notified of volume change
|
||||
verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(),
|
||||
anyInt());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ public class HdmiCecMessageValidatorTest {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
HdmiControlService mHdmiControlService = new HdmiControlService(
|
||||
InstrumentationRegistry.getTargetContext(), Collections.emptyList());
|
||||
InstrumentationRegistry.getTargetContext(), Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper());
|
||||
|
||||
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
|
||||
}
|
||||
|
||||
@@ -67,7 +67,8 @@ public class HdmiCecNetworkTest {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mContext = InstrumentationRegistry.getTargetContext();
|
||||
mHdmiControlService = new HdmiControlService(mContext, Collections.emptyList()) {
|
||||
mHdmiControlService = new HdmiControlService(mContext, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
|
||||
mDeviceEventListenerStatuses.add(status);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
@@ -65,7 +66,8 @@ public class HdmiCecPowerStatusControllerTest {
|
||||
Context contextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
|
||||
Looper myLooper = mTestLooper.getLooper();
|
||||
|
||||
mHdmiControlService = new HdmiControlService(contextSpy, Collections.emptyList()) {
|
||||
mHdmiControlService = new HdmiControlService(contextSpy, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
boolean isControlEnabled() {
|
||||
return true;
|
||||
@@ -105,6 +107,7 @@ public class HdmiCecPowerStatusControllerTest {
|
||||
mNativeWrapper.setPortInfo(hdmiPortInfos);
|
||||
mNativeWrapper.setPortConnectionStatus(1, true);
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mPowerManager = new FakePowerManagerWrapper(contextSpy);
|
||||
mHdmiControlService.setPowerManager(mPowerManager);
|
||||
mHdmiControlService.getHdmiCecNetwork().initPortInfo();
|
||||
|
||||
@@ -91,7 +91,8 @@ public class HdmiControlServiceTest {
|
||||
|
||||
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
|
||||
|
||||
mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
|
||||
mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()));
|
||||
doNothing().when(mHdmiControlServiceSpy)
|
||||
.writeStringSystemProperty(anyString(), anyString());
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.Constants.ADDR_TV;
|
||||
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
|
||||
import static com.android.server.hdmi.OneTouchPlayAction.STATE_WAITING_FOR_REPORT_POWER_STATUS;
|
||||
@@ -87,7 +88,8 @@ public class OneTouchPlayActionTest {
|
||||
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
|
||||
mHdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
|
||||
|
||||
mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
|
||||
mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
AudioManager getAudioManager() {
|
||||
return new AudioManager() {
|
||||
@@ -120,6 +122,7 @@ public class OneTouchPlayActionTest {
|
||||
mHdmiControlService.setCecController(hdmiCecController);
|
||||
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
|
||||
mHdmiControlService.setPowerManager(mPowerManager);
|
||||
mPhysicalAddress = 0x2000;
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.DeviceFeatures;
|
||||
import android.hardware.hdmi.HdmiControlManager;
|
||||
import android.hardware.hdmi.HdmiDeviceInfo;
|
||||
import android.media.AudioDeviceAttributes;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Tests for Absolute Volume Control where the local device is a Playback device and the
|
||||
* System Audio device is an Audio System.
|
||||
*/
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@RunWith(JUnit4.class)
|
||||
public class PlaybackDeviceToAudioSystemAvcTest extends BaseAbsoluteVolumeControlTest {
|
||||
|
||||
@Override
|
||||
protected HdmiCecLocalDevice createLocalDevice(HdmiControlService hdmiControlService) {
|
||||
return new HdmiCecLocalDevicePlayback(hdmiControlService);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPhysicalAddress() {
|
||||
return 0x1100;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDeviceType() {
|
||||
return HdmiDeviceInfo.DEVICE_PLAYBACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AudioDeviceAttributes getAudioOutputDevice() {
|
||||
return HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSystemAudioDeviceLogicalAddress() {
|
||||
return Constants.ADDR_AUDIO_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSystemAudioDeviceType() {
|
||||
return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* AVC is disabled if the Audio System disables System Audio mode, and the TV has unknown
|
||||
* support for <Set Audio Volume Level>. It is enabled once the TV confirms support for
|
||||
* <Set Audio Volume Level> and sends <Report Audio Status>.
|
||||
*/
|
||||
@Test
|
||||
public void switchToTv_absoluteVolumeControlDisabledUntilAllConditionsMet() {
|
||||
enableAbsoluteVolumeControl();
|
||||
|
||||
// Audio System disables System Audio Mode. AVC should be disabled.
|
||||
receiveSetSystemAudioMode(false);
|
||||
verifyAbsoluteVolumeDisabled();
|
||||
|
||||
// TV reports support for <Set Audio Volume Level>
|
||||
mNativeWrapper.onCecMessage(ReportFeaturesMessage.build(
|
||||
Constants.ADDR_TV, HdmiControlManager.HDMI_CEC_VERSION_2_0,
|
||||
Arrays.asList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
|
||||
Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
|
||||
DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
|
||||
.setSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED)
|
||||
.build()));
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
// TV reports its initial audio status
|
||||
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportAudioStatus(
|
||||
Constants.ADDR_TV,
|
||||
getLogicalAddress(),
|
||||
30,
|
||||
false));
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verifyAbsoluteVolumeEnabled();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 org.mockito.Mockito.clearInvocations;
|
||||
|
||||
import android.hardware.hdmi.DeviceFeatures;
|
||||
import android.hardware.hdmi.HdmiControlManager;
|
||||
import android.hardware.hdmi.HdmiDeviceInfo;
|
||||
import android.media.AudioDeviceAttributes;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Tests for Absolute Volume Control where the local device is a Playback device and the
|
||||
* System Audio device is a TV.
|
||||
*/
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@RunWith(JUnit4.class)
|
||||
public class PlaybackDeviceToTvAvcTest extends BaseAbsoluteVolumeControlTest {
|
||||
|
||||
@Override
|
||||
protected HdmiCecLocalDevice createLocalDevice(HdmiControlService hdmiControlService) {
|
||||
return new HdmiCecLocalDevicePlayback(hdmiControlService);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPhysicalAddress() {
|
||||
return 0x1100;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDeviceType() {
|
||||
return HdmiDeviceInfo.DEVICE_PLAYBACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AudioDeviceAttributes getAudioOutputDevice() {
|
||||
return HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSystemAudioDeviceLogicalAddress() {
|
||||
return Constants.ADDR_TV;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSystemAudioDeviceType() {
|
||||
return HdmiDeviceInfo.DEVICE_TV;
|
||||
}
|
||||
|
||||
/**
|
||||
* AVC is disabled when an Audio System with unknown support for <Set Audio Volume Level>
|
||||
* becomes the System Audio device. It is enabled once the Audio System reports that it
|
||||
* supports <Set Audio Volume Level> and sends <Report Audio Status>.
|
||||
*/
|
||||
@Test
|
||||
public void switchToAudioSystem_absoluteVolumeControlDisabledUntilAllConditionsMet() {
|
||||
enableAbsoluteVolumeControl();
|
||||
|
||||
// Audio System enables System Audio Mode. AVC should be disabled.
|
||||
receiveSetSystemAudioMode(true);
|
||||
verifyAbsoluteVolumeDisabled();
|
||||
|
||||
clearInvocations(mAudioManager, mAudioDeviceVolumeManager);
|
||||
|
||||
// Audio System reports support for <Set Audio Volume Level>
|
||||
mNativeWrapper.onCecMessage(ReportFeaturesMessage.build(
|
||||
Constants.ADDR_AUDIO_SYSTEM, HdmiControlManager.HDMI_CEC_VERSION_2_0,
|
||||
Arrays.asList(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_SOURCE,
|
||||
Collections.emptyList(),
|
||||
DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
|
||||
.setSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED)
|
||||
.build()));
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
// Audio system reports its initial audio status
|
||||
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportAudioStatus(
|
||||
Constants.ADDR_AUDIO_SYSTEM,
|
||||
getLogicalAddress(),
|
||||
30,
|
||||
false));
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verifyAbsoluteVolumeEnabled();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
|
||||
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
|
||||
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
|
||||
@@ -68,7 +69,8 @@ public class PowerStatusMonitorActionTest {
|
||||
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
|
||||
|
||||
mHdmiControlService = new HdmiControlService(mContextSpy,
|
||||
Collections.singletonList(HdmiDeviceInfo.DEVICE_TV)) {
|
||||
Collections.singletonList(HdmiDeviceInfo.DEVICE_TV),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
AudioManager getAudioManager() {
|
||||
return new AudioManager() {
|
||||
@@ -110,6 +112,7 @@ public class PowerStatusMonitorActionTest {
|
||||
new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
|
||||
mNativeWrapper.setPortInfo(hdmiPortInfo);
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
|
||||
mHdmiControlService.setPowerManager(mPowerManager);
|
||||
mPhysicalAddress = 0x0000;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
|
||||
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
|
||||
|
||||
@@ -96,8 +97,8 @@ public class RequestSadActionTest {
|
||||
mMyLooper = mTestLooper.getLooper();
|
||||
|
||||
mHdmiControlService =
|
||||
new HdmiControlService(context,
|
||||
Collections.emptyList()) {
|
||||
new HdmiControlService(context, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
boolean isControlEnabled() {
|
||||
return true;
|
||||
@@ -125,6 +126,7 @@ public class RequestSadActionTest {
|
||||
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
|
||||
mLocalDevices.add(mHdmiCecLocalDeviceTv);
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mPowerManager = new FakePowerManagerWrapper(context);
|
||||
mHdmiControlService.setPowerManager(mPowerManager);
|
||||
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
|
||||
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
|
||||
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
|
||||
@@ -149,7 +150,7 @@ public class RoutingControlActionTest {
|
||||
|
||||
mHdmiControlService =
|
||||
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
|
||||
Collections.emptyList()) {
|
||||
Collections.emptyList(), new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
boolean isControlEnabled() {
|
||||
return true;
|
||||
@@ -186,6 +187,7 @@ public class RoutingControlActionTest {
|
||||
true, false, false);
|
||||
mNativeWrapper.setPortInfo(hdmiPortInfos);
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mPowerManager = new FakePowerManagerWrapper(context);
|
||||
mHdmiControlService.setPowerManager(mPowerManager);
|
||||
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
|
||||
|
||||
@@ -20,6 +20,7 @@ import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED;
|
||||
import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
|
||||
import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORT_UNKNOWN;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
@@ -81,7 +82,8 @@ public class SetAudioVolumeLevelDiscoveryActionTest {
|
||||
mContextSpy = spy(new ContextWrapper(
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext()));
|
||||
|
||||
mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
|
||||
mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()));
|
||||
doNothing().when(mHdmiControlServiceSpy)
|
||||
.writeStringSystemProperty(anyString(), anyString());
|
||||
|
||||
@@ -98,6 +100,7 @@ public class SetAudioVolumeLevelDiscoveryActionTest {
|
||||
mHdmiControlServiceSpy.setHdmiMhlController(
|
||||
HdmiMhlControllerStub.create(mHdmiControlServiceSpy));
|
||||
mHdmiControlServiceSpy.initService();
|
||||
mHdmiControlServiceSpy.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
|
||||
mHdmiControlServiceSpy.setPowerManager(mPowerManager);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.hdmi;
|
||||
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
|
||||
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
|
||||
import static com.android.server.hdmi.SystemAudioAutoInitiationAction.RETRIES_ON_TIMEOUT;
|
||||
@@ -69,7 +70,8 @@ public class SystemAudioAutoInitiationActionTest {
|
||||
|
||||
Looper myLooper = mTestLooper.getLooper();
|
||||
|
||||
mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
|
||||
mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList(),
|
||||
new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
AudioManager getAudioManager() {
|
||||
return new AudioManager() {
|
||||
@@ -108,6 +110,7 @@ public class SystemAudioAutoInitiationActionTest {
|
||||
new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true);
|
||||
mNativeWrapper.setPortInfo(hdmiPortInfos);
|
||||
mHdmiControlService.initService();
|
||||
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
|
||||
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
|
||||
mHdmiControlService.setPowerManager(mPowerManager);
|
||||
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
|
||||
|
||||
@@ -69,7 +69,7 @@ public class SystemAudioInitiationActionFromAvrTest {
|
||||
Context context = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
HdmiControlService hdmiControlService = new HdmiControlService(context,
|
||||
Collections.emptyList()) {
|
||||
Collections.emptyList(), new FakeAudioDeviceVolumeManagerWrapper()) {
|
||||
@Override
|
||||
void sendCecCommand(
|
||||
HdmiCecMessage command, @Nullable SendMessageCallback callback) {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.media.AudioDeviceAttributes;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/**
|
||||
* Tests for Absolute Volume Control where the local device is a TV and the System Audio device
|
||||
* is an Audio System. Assumes that the TV uses ARC (rather than eARC).
|
||||
*/
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@RunWith(JUnit4.class)
|
||||
public class TvToAudioSystemAvcTest extends BaseAbsoluteVolumeControlTest {
|
||||
|
||||
@Override
|
||||
protected HdmiCecLocalDevice createLocalDevice(HdmiControlService hdmiControlService) {
|
||||
return new HdmiCecLocalDeviceTv(hdmiControlService);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPhysicalAddress() {
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDeviceType() {
|
||||
return HdmiDeviceInfo.DEVICE_TV;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AudioDeviceAttributes getAudioOutputDevice() {
|
||||
return HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI_ARC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSystemAudioDeviceLogicalAddress() {
|
||||
return Constants.ADDR_AUDIO_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSystemAudioDeviceType() {
|
||||
return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user