Merge changes from topic "adjust-only-avb" into udc-dev

* changes:
  Check for feature flag for numeric volume UI
  Implement soundbar volume UI for TV panels.
  Add wrapper for AudioManager, allowing it to be faked in tests
  Rename "Absolute Volume Control" to "absolute volume behavior"
  Fix system server crash on receiving <Report Audio Status>
This commit is contained in:
Yan Han
2023-05-11 12:15:01 +00:00
committed by Android (Google) Code Review
48 changed files with 1396 additions and 799 deletions

View File

@@ -17,11 +17,14 @@
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
* Action to query and track the audio status of the System Audio device when using
* absolute volume behavior, or adjust-only absolute volume behavior. Must be removed when
* neither behavior is used.
*
* Performs two main functions:
* 1. When enabling AVB: queries the starting audio status of the System Audio device and
* adopts the appropriate volume behavior upon receiving a response.
* 2. While AVB 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 {
@@ -74,16 +77,23 @@ final class AbsoluteVolumeAudioStatusAction extends HdmiCecFeatureAction {
boolean mute = HdmiUtils.isAudioStatusMute(cmd);
int volume = HdmiUtils.getAudioStatusVolume(cmd);
// If the volume is out of range, report it as handled and ignore the message.
// According to the spec, such values are either reserved or indicate an unknown volume.
if (volume == Constants.UNKNOWN_VOLUME) {
return true;
}
AudioStatus audioStatus = new AudioStatus(volume, mute);
if (mState == STATE_WAIT_FOR_INITIAL_AUDIO_STATUS) {
localDevice().getService().enableAbsoluteVolumeControl(audioStatus);
localDevice().getService().enableAbsoluteVolumeBehavior(audioStatus);
mState = STATE_MONITOR_AUDIO_STATUS;
} else if (mState == STATE_MONITOR_AUDIO_STATUS) {
if (audioStatus.getVolume() != mLastAudioStatus.getVolume()) {
localDevice().getService().notifyAvcVolumeChange(audioStatus.getVolume());
localDevice().getService().notifyAvbVolumeChange(audioStatus.getVolume());
}
if (audioStatus.getMute() != mLastAudioStatus.getMute()) {
localDevice().getService().notifyAvcMuteChange(audioStatus.getMute());
localDevice().getService().notifyAvbMuteChange(audioStatus.getMute());
}
}
mLastAudioStatus = audioStatus;

View File

@@ -16,9 +16,11 @@
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.content.Context;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceVolumeManager;
import android.media.VolumeInfo;
@@ -26,42 +28,48 @@ 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.
* Interface with the methods from {@link AudioDeviceVolumeManager} used by the HDMI framework.
* Allows the class to be faked for tests.
*
* See implementations {@link DefaultAudioDeviceVolumeManagerWrapper} and
* {@link FakeAudioFramework.FakeAudioDeviceVolumeManagerWrapper}.
*/
public class AudioDeviceVolumeManagerWrapper
implements AudioDeviceVolumeManagerWrapperInterface {
public interface AudioDeviceVolumeManagerWrapper {
private static final String TAG = "AudioDeviceVolumeManagerWrapper";
private final AudioDeviceVolumeManager mAudioDeviceVolumeManager;
public AudioDeviceVolumeManagerWrapper(Context context) {
mAudioDeviceVolumeManager = new AudioDeviceVolumeManager(context);
}
@Override
public void addOnDeviceVolumeBehaviorChangedListener(
/**
* Wrapper for {@link AudioDeviceVolumeManager#addOnDeviceVolumeBehaviorChangedListener(
* Executor, OnDeviceVolumeBehaviorChangedListener)}
*/
void addOnDeviceVolumeBehaviorChangedListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener listener)
throws SecurityException {
mAudioDeviceVolumeManager.addOnDeviceVolumeBehaviorChangedListener(executor, listener);
}
@NonNull AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener listener);
@Override
public void removeOnDeviceVolumeBehaviorChangedListener(
@NonNull AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener listener) {
mAudioDeviceVolumeManager.removeOnDeviceVolumeBehaviorChangedListener(listener);
}
/**
* Wrapper for {@link AudioDeviceVolumeManager#removeOnDeviceVolumeBehaviorChangedListener(
* OnDeviceVolumeBehaviorChangedListener)}
*/
void removeOnDeviceVolumeBehaviorChangedListener(
@NonNull AudioDeviceVolumeManager.OnDeviceVolumeBehaviorChangedListener listener);
@Override
public void setDeviceAbsoluteVolumeBehavior(
/**
* 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) {
mAudioDeviceVolumeManager.setDeviceAbsoluteVolumeBehavior(device, volume, executor,
vclistener, handlesVolumeAdjustment);
}
boolean handlesVolumeAdjustment);
/**
* Wrapper for {@link AudioDeviceVolumeManager#setDeviceAbsoluteVolumeAdjustOnlyBehavior(
* AudioDeviceAttributes, VolumeInfo, Executor, OnAudioDeviceVolumeChangedListener, boolean)}
*/
void setDeviceAbsoluteVolumeAdjustOnlyBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull VolumeInfo volume,
@NonNull @CallbackExecutor Executor executor,
@NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener,
boolean handlesVolumeAdjustment);
}

View File

@@ -1,61 +0,0 @@
/*
* 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);
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2023 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.NonNull;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceVolumeManager;
import android.media.AudioManager;
import java.util.List;
/**
* Interface with the methods from {@link AudioDeviceVolumeManager} used by the HDMI control
* framework. Allows the class to be faked for tests.
*
* See implementations {@link DefaultAudioManagerWrapper} and
* {@link FakeAudioFramework.FakeAudioManagerWrapper}.
*/
public interface AudioManagerWrapper {
/**
* Wraps {@link AudioManager#adjustStreamVolume(int, int, int)}
*/
void adjustStreamVolume(int streamType, int direction,
@AudioManager.PublicVolumeFlags int flags);
/**
* Wraps {@link AudioManager#setStreamVolume(int, int, int)}
*/
void setStreamVolume(int streamType, int index, @AudioManager.PublicVolumeFlags int flags);
/**
* Wraps {@link AudioManager#getStreamVolume(int)}
*/
int getStreamVolume(int streamType);
/**
* Wraps {@link AudioManager#getStreamMinVolume(int)}
*/
int getStreamMinVolume(int streamType);
/**
* Wraps {@link AudioManager#getStreamMaxVolume(int)}
*/
int getStreamMaxVolume(int streamType);
/**
* Wraps {@link AudioManager#isStreamMute(int)}
*/
boolean isStreamMute(int streamType);
/**
* Wraps {@link AudioManager#setStreamMute(int, boolean)}
*/
void setStreamMute(int streamType, boolean state);
/**
* Wraps {@link AudioManager#setHdmiSystemAudioSupported(boolean)}
*/
int setHdmiSystemAudioSupported(boolean on);
/**
* Wraps {@link AudioManager#setWiredDeviceConnectionState(AudioDeviceAttributes, int)}
*/
void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state);
/**
* Wraps {@link AudioManager#setWiredDeviceConnectionState(int, int, String, String)}
*/
void setWiredDeviceConnectionState(int device, int state, String address, String name);
/**
* Wraps {@link AudioManager#getDeviceVolumeBehavior(AudioDeviceAttributes)}
*/
@AudioManager.DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device);
/**
* Wraps {@link AudioManager#setDeviceVolumeBehavior(AudioDeviceAttributes, int)}
*/
void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
@AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior);
/**
* Wraps {@link AudioManager#getDevicesForAttributes(AudioAttributes)}
*/
@NonNull
List<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes);
}

View File

@@ -23,6 +23,8 @@ 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).
* The volume level is limited to the range [0, 100] upon construction.
* This object cannot represent an audio status where the volume is unknown, or out of bounds.
*/
public class AudioStatus {
public static final int MAX_VOLUME = 100;
@@ -32,7 +34,7 @@ public class AudioStatus {
boolean mMute;
public AudioStatus(int volume, boolean mute) {
mVolume = volume;
mVolume = Math.max(Math.min(volume, MAX_VOLUME), MIN_VOLUME);
mMute = mute;
}

View File

@@ -631,10 +631,14 @@ final class Constants {
static final String DEVICE_CONFIG_FEATURE_FLAG_ENABLE_EARC_TX = "enable_earc_tx";
static final String DEVICE_CONFIG_FEATURE_FLAG_TRANSITION_ARC_TO_EARC_TX =
"transition_arc_to_earc_tx";
// Name is abbreviated slightly to avoid line length issues
static final String DEVICE_CONFIG_FEATURE_FLAG_TV_NUMERIC_SOUNDBAR_VOLUME_UI =
"enable_numeric_soundbar_volume_ui_on_tv";
@StringDef({
DEVICE_CONFIG_FEATURE_FLAG_SOUNDBAR_MODE,
DEVICE_CONFIG_FEATURE_FLAG_ENABLE_EARC_TX,
DEVICE_CONFIG_FEATURE_FLAG_TRANSITION_ARC_TO_EARC_TX
DEVICE_CONFIG_FEATURE_FLAG_TRANSITION_ARC_TO_EARC_TX,
DEVICE_CONFIG_FEATURE_FLAG_TV_NUMERIC_SOUNDBAR_VOLUME_UI
})
@interface FeatureFlag {}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2023 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;
/**
* "Default" wrapper for {@link AudioDeviceVolumeManager}, as opposed to a "Fake" wrapper for
* testing - see {@link FakeAudioFramework.FakeAudioDeviceVolumeManagerWrapper}.
*
* Creates an instance of {@link AudioDeviceVolumeManager} and directly passes method calls
* to that instance.
*/
public class DefaultAudioDeviceVolumeManagerWrapper
implements AudioDeviceVolumeManagerWrapper {
private static final String TAG = "AudioDeviceVolumeManagerWrapper";
private final AudioDeviceVolumeManager mAudioDeviceVolumeManager;
public DefaultAudioDeviceVolumeManagerWrapper(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);
}
@Override
public void setDeviceAbsoluteVolumeAdjustOnlyBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull VolumeInfo volume,
@NonNull @CallbackExecutor Executor executor,
@NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener,
boolean handlesVolumeAdjustment) {
mAudioDeviceVolumeManager.setDeviceAbsoluteVolumeAdjustOnlyBehavior(device, volume,
executor, vclistener, handlesVolumeAdjustment);
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2023 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.NonNull;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import java.util.List;
/**
* "Default" wrapper for {@link AudioManager}, as opposed to a "Fake" wrapper for testing -
* see {@link FakeAudioFramework.FakeAudioManagerWrapper}.
*
* Creates an instance of {@link AudioManager} and directly passes method calls to that instance.
*
*/
public class DefaultAudioManagerWrapper implements AudioManagerWrapper {
private static final String TAG = "DefaultAudioManagerWrapper";
private final AudioManager mAudioManager;
public DefaultAudioManagerWrapper(Context context) {
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
@Override
public void adjustStreamVolume(int streamType, int direction,
@AudioManager.PublicVolumeFlags int flags) {
mAudioManager.adjustStreamVolume(streamType, direction, flags);
}
@Override
public void setStreamVolume(int streamType, int index,
@AudioManager.PublicVolumeFlags int flags) {
mAudioManager.setStreamVolume(streamType, index, flags);
}
@Override
public int getStreamVolume(int streamType) {
return mAudioManager.getStreamVolume(streamType);
}
@Override
public int getStreamMinVolume(int streamType) {
return mAudioManager.getStreamMinVolume(streamType);
}
@Override
public int getStreamMaxVolume(int streamType) {
return mAudioManager.getStreamMaxVolume(streamType);
}
@Override
public boolean isStreamMute(int streamType) {
return mAudioManager.isStreamMute(streamType);
}
@Override
public void setStreamMute(int streamType, boolean state) {
mAudioManager.setStreamMute(streamType, state);
}
@Override
public int setHdmiSystemAudioSupported(boolean on) {
return mAudioManager.setHdmiSystemAudioSupported(on);
}
@Override
public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state) {
mAudioManager.setWiredDeviceConnectionState(attributes, state);
}
@Override
public void setWiredDeviceConnectionState(int device, int state, String address, String name) {
mAudioManager.setWiredDeviceConnectionState(device, state, address, name);
}
@Override
@AudioManager.DeviceVolumeBehavior
public int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
return mAudioManager.getDeviceVolumeBehavior(device);
}
@Override
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
@AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
mAudioManager.setDeviceVolumeBehavior(device, deviceVolumeBehavior);
}
@Override
@NonNull
public List<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
return mAudioManager.getDevicesForAttributes(attributes);
}
}

View File

@@ -1012,17 +1012,23 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
action.start();
}
void addAvcAudioStatusAction(int targetAddress) {
@ServiceThreadOnly
void addAvbAudioStatusAction(int targetAddress) {
assertRunOnServiceThread();
if (!hasAction(AbsoluteVolumeAudioStatusAction.class)) {
addAndStartAction(new AbsoluteVolumeAudioStatusAction(this, targetAddress));
}
}
void removeAvcAudioStatusAction() {
@ServiceThreadOnly
void removeAvbAudioStatusAction() {
assertRunOnServiceThread();
removeAction(AbsoluteVolumeAudioStatusAction.class);
}
void updateAvcVolume(int volumeIndex) {
@ServiceThreadOnly
void updateAvbVolume(int volumeIndex) {
assertRunOnServiceThread();
for (AbsoluteVolumeAudioStatusAction action :
getActions(AbsoluteVolumeAudioStatusAction.class)) {
action.updateVolume(volumeIndex);
@@ -1035,7 +1041,7 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
* and send <Set Audio Volume Level> (to see if it gets a <Feature Abort> in response).
*/
@ServiceThreadOnly
void queryAvcSupport(int targetAddress) {
void querySetAudioVolumeLevelSupport(int targetAddress) {
assertRunOnServiceThread();
// Send <Give Features> if using CEC 2.0 or above.
@@ -1054,7 +1060,7 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
@Override
public void onComplete(int result) {
if (result == HdmiControlManager.RESULT_SUCCESS) {
getService().checkAndUpdateAbsoluteVolumeControlState();
getService().checkAndUpdateAbsoluteVolumeBehavior();
}
}
}));

View File

@@ -66,7 +66,7 @@ import java.util.stream.Collectors;
/**
* Represent a logical device of type TV residing in Android system.
*/
final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
private static final String TAG = "HdmiCecLocalDeviceTv";
// Whether ARC is available or not. "true" means that ARC is established between TV and

View File

@@ -259,7 +259,7 @@ public class HdmiCecNetwork {
// The addition of a local device should not notify listeners
return;
}
mHdmiControlService.checkAndUpdateAbsoluteVolumeControlState();
mHdmiControlService.checkAndUpdateAbsoluteVolumeBehavior();
if (info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) {
// Don't notify listeners of devices that haven't reported their physical address yet
return;
@@ -384,7 +384,7 @@ public class HdmiCecNetwork {
final void removeCecDevice(HdmiCecLocalDevice localDevice, int address) {
assertRunOnServiceThread();
HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
mHdmiControlService.checkAndUpdateAbsoluteVolumeControlState();
mHdmiControlService.checkAndUpdateAbsoluteVolumeBehavior();
localDevice.mCecMessageCache.flushMessagesFrom(address);
if (info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) {
// Don't notify listeners of devices that haven't reported their physical address yet
@@ -592,7 +592,7 @@ public class HdmiCecNetwork {
updateCecDevice(newDeviceInfo);
mHdmiControlService.checkAndUpdateAbsoluteVolumeControlState();
mHdmiControlService.checkAndUpdateAbsoluteVolumeBehavior();
}
@ServiceThreadOnly
@@ -625,7 +625,7 @@ public class HdmiCecNetwork {
.build();
updateCecDevice(newDeviceInfo);
mHdmiControlService.checkAndUpdateAbsoluteVolumeControlState();
mHdmiControlService.checkAndUpdateAbsoluteVolumeBehavior();
}
}

View File

@@ -231,8 +231,8 @@ public class HdmiControlService extends SystemService {
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 =
// Audio output devices used for absolute volume behavior
private static final List<AudioDeviceAttributes> AVB_AUDIO_OUTPUT_DEVICES =
Collections.unmodifiableList(Arrays.asList(AUDIO_OUTPUT_DEVICE_HDMI,
AUDIO_OUTPUT_DEVICE_HDMI_ARC, AUDIO_OUTPUT_DEVICE_HDMI_EARC));
@@ -265,7 +265,7 @@ public class HdmiControlService extends SystemService {
@HdmiControlManager.VolumeControl
private int mHdmiCecVolumeControl;
// Caches the volume behaviors of all audio output devices in AVC_AUDIO_OUTPUT_DEVICES.
// Caches the volume behaviors of all audio output devices in AVB_AUDIO_OUTPUT_DEVICES.
@GuardedBy("mLock")
private Map<AudioDeviceAttributes, Integer> mAudioDeviceVolumeBehaviors = new HashMap<>();
@@ -457,6 +457,9 @@ public class HdmiControlService extends SystemService {
@ServiceThreadOnly
private boolean mEarcTxFeatureFlagEnabled = false;
@ServiceThreadOnly
private boolean mNumericSoundbarVolumeUiOnTvFeatureFlagEnabled = false;
@ServiceThreadOnly
private boolean mTransitionFromArcToEarcTxEnabled = false;
@@ -491,10 +494,10 @@ public class HdmiControlService extends SystemService {
private PowerManagerInternalWrapper mPowerManagerInternal;
@Nullable
private AudioManager mAudioManager;
private AudioManagerWrapper mAudioManager;
@Nullable
private AudioDeviceVolumeManagerWrapperInterface mAudioDeviceVolumeManager;
private AudioDeviceVolumeManagerWrapper mAudioDeviceVolumeManager;
@Nullable
private Looper mIoLooper;
@@ -528,18 +531,20 @@ public class HdmiControlService extends SystemService {
/**
* Constructor for testing.
*
* It's critical to use a fake AudioDeviceVolumeManager because a normally instantiated
* AudioDeviceVolumeManager can access the "real" AudioService on the DUT.
* Takes fakes for AudioManager and AudioDeviceVolumeManager.
*
* @see FakeAudioDeviceVolumeManagerWrapper
* This is especially important for AudioDeviceVolumeManager because a normally instantiated
* AudioDeviceVolumeManager can access the "real" AudioService on the DUT.
*/
@VisibleForTesting HdmiControlService(Context context, List<Integer> deviceTypes,
AudioDeviceVolumeManagerWrapperInterface audioDeviceVolumeManager) {
AudioManagerWrapper audioManager,
AudioDeviceVolumeManagerWrapper audioDeviceVolumeManager) {
super(context);
mCecLocalDevices = deviceTypes;
mSettingsObserver = new SettingsObserver(mHandler);
mHdmiCecConfig = new HdmiCecConfig(context);
mDeviceConfig = new DeviceConfigWrapper();
mAudioManager = audioManager;
mAudioDeviceVolumeManager = audioDeviceVolumeManager;
}
@@ -680,6 +685,8 @@ public class HdmiControlService extends SystemService {
Constants.DEVICE_CONFIG_FEATURE_FLAG_ENABLE_EARC_TX, false);
mTransitionFromArcToEarcTxEnabled = mDeviceConfig.getBoolean(
Constants.DEVICE_CONFIG_FEATURE_FLAG_TRANSITION_ARC_TO_EARC_TX, false);
mNumericSoundbarVolumeUiOnTvFeatureFlagEnabled = mDeviceConfig.getBoolean(
Constants.DEVICE_CONFIG_FEATURE_FLAG_TV_NUMERIC_SOUNDBAR_VOLUME_UI, false);
synchronized (mLock) {
mEarcEnabled = (mHdmiCecConfig.getIntValue(
@@ -899,6 +906,17 @@ public class HdmiControlService extends SystemService {
false);
}
});
mDeviceConfig.addOnPropertiesChangedListener(getContext().getMainExecutor(),
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
mNumericSoundbarVolumeUiOnTvFeatureFlagEnabled = properties.getBoolean(
Constants.DEVICE_CONFIG_FEATURE_FLAG_TV_NUMERIC_SOUNDBAR_VOLUME_UI,
false);
checkAndUpdateAbsoluteVolumeBehavior();
}
});
}
/** Returns true if the device screen is off */
boolean isScreenOff() {
@@ -933,11 +951,6 @@ public class HdmiControlService extends SystemService {
return HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
}
@VisibleForTesting
void setAudioManager(AudioManager audioManager) {
mAudioManager = audioManager;
}
@VisibleForTesting
void setCecController(HdmiCecController cecController) {
mCecController = cecController;
@@ -975,11 +988,13 @@ public class HdmiControlService extends SystemService {
Context.TV_INPUT_SERVICE);
mPowerManager = new PowerManagerWrapper(getContext());
mPowerManagerInternal = new PowerManagerInternalWrapper();
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
if (mAudioManager == null) {
mAudioManager = new DefaultAudioManagerWrapper(getContext());
}
mStreamMusicMaxVolume = getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
if (mAudioDeviceVolumeManager == null) {
mAudioDeviceVolumeManager =
new AudioDeviceVolumeManagerWrapper(getContext());
new DefaultAudioDeviceVolumeManagerWrapper(getContext());
}
getAudioDeviceVolumeManager().addOnDeviceVolumeBehaviorChangedListener(
mServiceThreadExecutor, this::onDeviceVolumeBehaviorChanged);
@@ -1773,7 +1788,7 @@ public class HdmiControlService extends SystemService {
== HdmiControlManager.VOLUME_CONTROL_DISABLED) {
return;
}
AudioManager audioManager = getAudioManager();
AudioManagerWrapper audioManager = getAudioManager();
boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
if (mute) {
if (!muted) {
@@ -2751,7 +2766,7 @@ public class HdmiControlService extends SystemService {
pw.println("mPowerStatus: " + mPowerStatusController.getPowerStatus());
pw.println("mIsCecAvailable: " + mIsCecAvailable);
pw.println("mCecVersion: " + mCecVersion);
pw.println("mIsAbsoluteVolumeControlEnabled: " + isAbsoluteVolumeControlEnabled());
pw.println("mIsAbsoluteVolumeBehaviorEnabled: " + isAbsoluteVolumeBehaviorEnabled());
// System settings
pw.println("System_settings:");
@@ -2919,7 +2934,7 @@ public class HdmiControlService extends SystemService {
@HdmiControlManager.VolumeControl int hdmiCecVolumeControl) {
mHdmiCecVolumeControl = hdmiCecVolumeControl;
announceHdmiCecVolumeControlFeatureChange(hdmiCecVolumeControl);
runOnServiceThread(this::checkAndUpdateAbsoluteVolumeControlState);
runOnServiceThread(this::checkAndUpdateAbsoluteVolumeBehavior);
}
// Get the source address to send out commands to devices connected to the current device
@@ -3485,7 +3500,7 @@ public class HdmiControlService extends SystemService {
* Returns null before the boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}.
*/
@Nullable
AudioManager getAudioManager() {
AudioManagerWrapper getAudioManager() {
return mAudioManager;
}
@@ -3493,7 +3508,7 @@ public class HdmiControlService extends SystemService {
* Returns null before the boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}.
*/
@Nullable
private AudioDeviceVolumeManagerWrapperInterface getAudioDeviceVolumeManager() {
private AudioDeviceVolumeManagerWrapper getAudioDeviceVolumeManager() {
return mAudioDeviceVolumeManager;
}
@@ -3882,7 +3897,7 @@ public class HdmiControlService extends SystemService {
synchronized (mLock) {
mSystemAudioActivated = on;
}
runOnServiceThread(this::checkAndUpdateAbsoluteVolumeControlState);
runOnServiceThread(this::checkAndUpdateAbsoluteVolumeBehavior);
}
@ServiceThreadOnly
@@ -3993,7 +4008,7 @@ public class HdmiControlService extends SystemService {
deviceIsActiveSource, caller);
}
runOnServiceThread(this::checkAndUpdateAbsoluteVolumeControlState);
runOnServiceThread(this::checkAndUpdateAbsoluteVolumeBehavior);
}
// This method should only be called when the device can be the active source
@@ -4189,17 +4204,17 @@ public class HdmiControlService extends SystemService {
/**
* Listener for changes to the volume behavior of an audio output device. Caches the
* volume behavior of devices used for Absolute Volume Control.
* volume behavior of devices used for absolute volume behavior.
*/
@VisibleForTesting
@ServiceThreadOnly
void onDeviceVolumeBehaviorChanged(AudioDeviceAttributes device, int volumeBehavior) {
assertRunOnServiceThread();
if (AVC_AUDIO_OUTPUT_DEVICES.contains(device)) {
if (AVB_AUDIO_OUTPUT_DEVICES.contains(device)) {
synchronized (mLock) {
mAudioDeviceVolumeBehaviors.put(device, volumeBehavior);
}
checkAndUpdateAbsoluteVolumeControlState();
checkAndUpdateAbsoluteVolumeBehavior();
}
}
@@ -4209,7 +4224,7 @@ public class HdmiControlService extends SystemService {
*/
@AudioManager.DeviceVolumeBehavior
private int getDeviceVolumeBehavior(AudioDeviceAttributes device) {
if (AVC_AUDIO_OUTPUT_DEVICES.contains(device)) {
if (AVB_AUDIO_OUTPUT_DEVICES.contains(device)) {
synchronized (mLock) {
if (mAudioDeviceVolumeBehaviors.containsKey(device)) {
return mAudioDeviceVolumeBehaviors.get(device);
@@ -4220,25 +4235,29 @@ public class HdmiControlService extends SystemService {
}
/**
* Returns whether Absolute Volume Control is enabled or not. This is determined by the
* Returns whether absolute volume behavior 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() {
public boolean isAbsoluteVolumeBehaviorEnabled() {
if (!isTvDevice() && !isPlaybackDevice()) {
return false;
}
AudioDeviceAttributes avcAudioOutputDevice = getAvcAudioOutputDevice();
if (avcAudioOutputDevice == null) {
AudioDeviceAttributes avbAudioOutputDevice = getAvbAudioOutputDevice();
if (avbAudioOutputDevice == null) {
return false;
}
return getDeviceVolumeBehavior(avcAudioOutputDevice)
== AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE;
@AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior =
getDeviceVolumeBehavior(avbAudioOutputDevice);
return deviceVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE
|| deviceVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY;
}
private AudioDeviceAttributes getAvcAudioOutputDevice() {
if (isTvDevice()) {
private AudioDeviceAttributes getAvbAudioOutputDevice() {
if (tv() != null) {
return tv().getSystemAudioOutputDevice();
} else if (isPlaybackDevice()) {
} else if (playback() != null) {
return AUDIO_OUTPUT_DEVICE_HDMI;
} else {
return null;
@@ -4246,30 +4265,30 @@ public class HdmiControlService extends SystemService {
}
/**
* 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.
* This method is responsible for adopting or disabling absolute volume behavior and
* adjust-only absolute volume behavior in AudioService. These volume behaviors are adopted on
* specific audio output devices: HDMI for playback devices, and HDMI_ARC or HDMI_EARC for TVs.
*
* 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:
* This method enables absolute volume behavior on a Playback device or TV panel when it is
* playing audio on an external device (the System Audio device) that supports the feature.
* This allows the volume level of the System Audio device to be tracked and set by Android.
*
* Absolute volume behavior requires the following 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>)
* 4. The System Audio device supports the <Set Audio Volume Level> message
*
* 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.
* This method enables adjust-only absolute volume behavior on TV panels when conditions
* 1, 2, and 3 are met, but condition 4 is not. This allows TVs to track the volume level of
* the System Audio device and display numeric volume UI for it, even if the System Audio device
* does not support <Set Audio Volume Level>.
*/
@ServiceThreadOnly
void checkAndUpdateAbsoluteVolumeControlState() {
void checkAndUpdateAbsoluteVolumeBehavior() {
assertRunOnServiceThread();
// Can't enable or disable AVC before we have access to system services
// Can't set volume behavior before we have access to system services
if (getAudioManager() == null) {
return;
}
@@ -4281,84 +4300,110 @@ public class HdmiControlService extends SystemService {
// (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();
switchToFullVolumeBehavior();
return;
}
} else if (isPlaybackDevice() && playback() != null) {
localCecDevice = playback();
} else {
// Either this device type doesn't support AVC, or it hasn't fully initialized yet
// Either this device type doesn't support AVB, or it hasn't fully initialized yet
return;
}
HdmiDeviceInfo systemAudioDeviceInfo = getHdmiCecNetwork().getSafeCecDeviceInfo(
HdmiDeviceInfo systemAudioDeviceInfo = getDeviceInfo(
localCecDevice.findAudioReceiverAddress());
@AudioManager.DeviceVolumeBehavior int currentVolumeBehavior =
getDeviceVolumeBehavior(getAvcAudioOutputDevice());
getDeviceVolumeBehavior(getAvbAudioOutputDevice());
// Condition 2: Already using full or absolute volume behavior
boolean alreadyUsingFullOrAbsoluteVolume =
currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL
|| currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE;
(currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL)
|| (currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE)
|| (currentVolumeBehavior
== AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
// Condition 3: CEC volume is enabled
boolean cecVolumeEnabled =
getHdmiCecVolumeControl() == HdmiControlManager.VOLUME_CONTROL_ENABLED;
if (!cecVolumeEnabled || !alreadyUsingFullOrAbsoluteVolume) {
disableAbsoluteVolumeControl();
switchToFullVolumeBehavior();
return;
}
// Check for safety: if the System Audio device is a candidate for AVC, we should already
// Check for safety: if the System Audio device is a candidate for AVB, we should already
// have received messages from it to trigger the other conditions.
if (systemAudioDeviceInfo == null) {
disableAbsoluteVolumeControl();
switchToFullVolumeBehavior();
return;
}
// Condition 4: The System Audio device supports AVC (i.e. <Set Audio Volume Level>).
// Condition 4: The System Audio device supports <Set Audio Volume Level>
switch (systemAudioDeviceInfo.getDeviceFeatures().getSetAudioVolumeLevelSupport()) {
case DeviceFeatures.FEATURE_SUPPORTED:
if (!isAbsoluteVolumeControlEnabled()) {
// Start an action that will call {@link #enableAbsoluteVolumeControl}
if (currentVolumeBehavior != AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
// If we're currently using adjust-only absolute volume behavior, switch to
// full volume behavior until we successfully adopt absolute volume behavior
switchToFullVolumeBehavior();
// Start an action that will call enableAbsoluteVolumeBehavior
// once the System Audio device sends <Report Audio Status>
localCecDevice.addAvcAudioStatusAction(
localCecDevice.addAvbAudioStatusAction(
systemAudioDeviceInfo.getLogicalAddress());
}
return;
case DeviceFeatures.FEATURE_NOT_SUPPORTED:
disableAbsoluteVolumeControl();
// TVs may adopt adjust-only absolute volume behavior if condition 4 isn't met.
// This allows the device to display numeric volume UI for the System Audio device.
if (tv() != null && mNumericSoundbarVolumeUiOnTvFeatureFlagEnabled) {
if (currentVolumeBehavior
!= AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY) {
// If we're currently using absolute volume behavior, switch to full volume
// behavior until we successfully adopt adjust-only absolute volume behavior
switchToFullVolumeBehavior();
// Start an action that will call enableAbsoluteVolumeBehavior
// once the System Audio device sends <Report Audio Status>
localCecDevice.addAvbAudioStatusAction(
systemAudioDeviceInfo.getLogicalAddress());
}
} else {
switchToFullVolumeBehavior();
}
return;
case DeviceFeatures.FEATURE_SUPPORT_UNKNOWN:
disableAbsoluteVolumeControl();
localCecDevice.queryAvcSupport(systemAudioDeviceInfo.getLogicalAddress());
return;
default:
return;
switchToFullVolumeBehavior();
localCecDevice.querySetAudioVolumeLevelSupport(
systemAudioDeviceInfo.getLogicalAddress());
}
}
private void disableAbsoluteVolumeControl() {
if (isPlaybackDevice()) {
playback().removeAvcAudioStatusAction();
} else if (isTvDevice()) {
tv().removeAvcAudioStatusAction();
/**
* Switches to full volume behavior, if either absolute or adjust-only absolute volume behavior
* are currently used. Removes the action for handling volume updates for these behaviors.
*/
private void switchToFullVolumeBehavior() {
if (playback() != null) {
playback().removeAvbAudioStatusAction();
} else if (tv() != null) {
tv().removeAvbAudioStatusAction();
}
AudioDeviceAttributes device = getAvcAudioOutputDevice();
if (getDeviceVolumeBehavior(device) == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
AudioDeviceAttributes device = getAvbAudioOutputDevice();
int volumeBehavior = getDeviceVolumeBehavior(device);
if (volumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE
|| volumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY) {
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}).
* Enables absolute volume behavior or adjust-only absolute volume behavior. Should only be
* called when the conditions for one of these behaviors is met -
* see {@link #checkAndUpdateAbsoluteVolumeBehavior}.
*
* @param audioStatus The initial audio status to set the audio output device to
*/
void enableAbsoluteVolumeControl(AudioStatus audioStatus) {
void enableAbsoluteVolumeBehavior(AudioStatus audioStatus) {
HdmiCecLocalDevice localDevice = isPlaybackDevice() ? playback() : tv();
HdmiDeviceInfo systemAudioDevice = getHdmiCecNetwork().getDeviceInfo(
localDevice.findAudioReceiverAddress());
HdmiDeviceInfo systemAudioDevice = getDeviceInfo(localDevice.findAudioReceiverAddress());
VolumeInfo volumeInfo = new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
.setMuted(audioStatus.getMute())
.setVolumeIndex(audioStatus.getVolume())
@@ -4370,10 +4415,21 @@ public class HdmiControlService extends SystemService {
// 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);
notifyAvbMuteChange(audioStatus.getMute());
// If <Set Audio Volume Level> is supported, enable absolute volume behavior.
// Otherwise, enable adjust-only AVB on TVs only.
if (systemAudioDevice.getDeviceFeatures().getSetAudioVolumeLevelSupport()
== DeviceFeatures.FEATURE_SUPPORTED) {
getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeBehavior(
getAvbAudioOutputDevice(), volumeInfo, mServiceThreadExecutor,
mAbsoluteVolumeChangedListener, true);
} else if (tv() != null) {
getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeAdjustOnlyBehavior(
getAvbAudioOutputDevice(), volumeInfo, mServiceThreadExecutor,
mAbsoluteVolumeChangedListener, true);
}
}
private AbsoluteVolumeChangedListener mAbsoluteVolumeChangedListener;
@@ -4407,6 +4463,14 @@ public class HdmiControlService extends SystemService {
public void onAudioDeviceVolumeChanged(
@NonNull AudioDeviceAttributes audioDevice,
@NonNull VolumeInfo volumeInfo) {
// Do nothing if the System Audio device does not support <Set Audio Volume Level>
if (mSystemAudioDevice.getDeviceFeatures().getSetAudioVolumeLevelSupport()
!= DeviceFeatures.FEATURE_SUPPORTED) {
return;
}
// Send <Set Audio Volume Level> to notify the System Audio device of the volume change
int localDeviceAddress = mLocalDevice.getDeviceInfo().getLogicalAddress();
sendCecCommand(SetAudioVolumeLevelMessage.build(
localDeviceAddress,
@@ -4418,8 +4482,8 @@ public class HdmiControlService extends SystemService {
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());
HdmiCecLocalDevice avbDevice = isTvDevice() ? tv() : playback();
avbDevice.updateAvbVolume(volumeInfo.getVolumeIndex());
} else {
sendCecCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(
localDeviceAddress,
@@ -4477,13 +4541,13 @@ public class HdmiControlService extends SystemService {
/**
* 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
* AVB is disabled, or the audio output device for AVB is not playing for STREAM_MUSIC
*/
void notifyAvcVolumeChange(int volume) {
if (!isAbsoluteVolumeControlEnabled()) return;
void notifyAvbVolumeChange(int volume) {
if (!isAbsoluteVolumeBehaviorEnabled()) return;
List<AudioDeviceAttributes> streamMusicDevices =
getAudioManager().getDevicesForAttributes(STREAM_MUSIC_ATTRIBUTES);
if (streamMusicDevices.contains(getAvcAudioOutputDevice())) {
if (streamMusicDevices.contains(getAvbAudioOutputDevice())) {
int flags = AudioManager.FLAG_ABSOLUTE_VOLUME;
if (isTvDevice()) {
flags |= AudioManager.FLAG_SHOW_UI;
@@ -4494,13 +4558,13 @@ public class HdmiControlService extends SystemService {
/**
* 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
* effect if AVB is disabled, or the audio output device for AVB is not playing for STREAM_MUSIC
*/
void notifyAvcMuteChange(boolean mute) {
if (!isAbsoluteVolumeControlEnabled()) return;
void notifyAvbMuteChange(boolean mute) {
if (!isAbsoluteVolumeBehaviorEnabled()) return;
List<AudioDeviceAttributes> streamMusicDevices =
getAudioManager().getDevicesForAttributes(STREAM_MUSIC_ATTRIBUTES);
if (streamMusicDevices.contains(getAvcAudioOutputDevice())) {
if (streamMusicDevices.contains(getAvbAudioOutputDevice())) {
int direction = mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE;
int flags = AudioManager.FLAG_ABSOLUTE_VOLUME;
if (isTvDevice()) {

View File

@@ -172,10 +172,10 @@ final class SendKeyAction extends HdmiCecFeatureAction {
}
private void sendKeyUp() {
// When using Absolute Volume Control, query audio status after a volume key is released.
// When using absolute volume behavior, 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()) {
&& localDevice().getService().isAbsoluteVolumeBehaviorEnabled()) {
sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
mTargetAddress),
__ -> sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(

View File

@@ -90,7 +90,7 @@ public class SetAudioVolumeLevelDiscoveryAction extends HdmiCecFeatureAction {
}
void handleTimerEvent(int state) {
if (updateAvcSupport(FEATURE_SUPPORTED)) {
if (updateSetAudioVolumeLevelSupport(FEATURE_SUPPORTED)) {
finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
} else {
finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
@@ -104,7 +104,7 @@ public class SetAudioVolumeLevelDiscoveryAction extends HdmiCecFeatureAction {
*
* @return Whether support was successfully updated in the network.
*/
private boolean updateAvcSupport(
private boolean updateSetAudioVolumeLevelSupport(
@DeviceFeatures.FeatureSupportStatus int setAudioVolumeLevelSupport) {
HdmiCecNetwork network = localDevice().mService.getHdmiCecNetwork();
HdmiDeviceInfo currentDeviceInfo = network.getCecDeviceInfo(mTargetAddress);

View File

@@ -159,7 +159,7 @@ final class VolumeControlAction extends HdmiCecFeatureAction {
// Update audio status if current volume position is edge of volume bar,
// i.e max or min volume.
AudioManager audioManager = tv().getService().getAudioManager();
AudioManagerWrapper audioManager = tv().getService().getAudioManager();
int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
if (mIsVolumeUp) {
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);

View File

@@ -26,7 +26,6 @@ import static org.mockito.Mockito.spy;
import android.content.Context;
import android.content.ContextWrapper;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -61,19 +60,9 @@ public class ActiveSourceActionTest {
public void setUp() throws Exception {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList(),
new FakeAudioDeviceVolumeManagerWrapper()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@Override
public void setWiredDeviceConnectionState(
int type, int state, String address, String name) {
// Do nothing.
}
};
}
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isPowerStandby() {
return false;

View File

@@ -25,7 +25,6 @@ import static org.mockito.Mockito.spy;
import android.content.Context;
import android.content.ContextWrapper;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -37,8 +36,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Collections;
@@ -58,27 +55,21 @@ public class ArcInitiationActionFromAvrTest {
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
@Mock
private AudioManager mAudioManager;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
FakeAudioFramework audioFramework = new FakeAudioFramework();
HdmiControlService hdmiControlService =
new HdmiControlService(mContextSpy, Collections.emptyList(),
new FakeAudioDeviceVolumeManagerWrapper()) {
audioFramework.getAudioManager(),
audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isPowerStandby() {
return false;
}
@Override
AudioManager getAudioManager() {
return mAudioManager;
}
@Override
boolean isAddressAllocated() {

View File

@@ -27,7 +27,6 @@ import android.content.ContextWrapper;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -39,7 +38,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -61,8 +59,6 @@ public class ArcTerminationActionFromAvrTest {
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
@Mock
private AudioManager mAudioManager;
@Before
public void setUp() throws Exception {
@@ -70,14 +66,12 @@ public class ArcTerminationActionFromAvrTest {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
FakeAudioFramework audioFramework = new FakeAudioFramework();
HdmiControlService hdmiControlService =
new HdmiControlService(mContextSpy, Collections.emptyList(),
new FakeAudioDeviceVolumeManagerWrapper()) {
@Override
AudioManager getAudioManager() {
return mAudioManager;
}
audioFramework.getAudioManager(),
audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isPowerStandby() {
return false;

View File

@@ -24,16 +24,11 @@ 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;
@@ -59,8 +54,6 @@ 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;
@@ -68,20 +61,20 @@ import java.util.Arrays;
import java.util.Collections;
/**
* Tests that Absolute Volume Control (AVC) is enabled and disabled correctly, and that
* Tests that absolute volume behavior (AVB) 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.
* from AudioService when AVB 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}
* (Playback, TV): {@link PlaybackDeviceToTvAvbTest}
* (Playback, Audio System): {@link PlaybackDeviceToAudioSystemAvbTest}
* (TV, Audio System): {@link TvToAudioSystemAvbTest}
*/
public abstract class BaseAbsoluteVolumeControlTest {
private HdmiControlService mHdmiControlService;
public abstract class BaseAbsoluteVolumeBehaviorTest {
protected HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private HdmiCecLocalDevice mHdmiCecLocalDevice;
private FakeHdmiCecConfig mHdmiCecConfig;
@@ -90,19 +83,20 @@ public abstract class BaseAbsoluteVolumeControlTest {
private Context mContextSpy;
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
@Mock protected AudioManager mAudioManager;
protected FakeAudioDeviceVolumeManagerWrapper mAudioDeviceVolumeManager;
protected FakeAudioFramework mAudioFramework;
protected AudioManagerWrapper mAudioManager;
protected AudioDeviceVolumeManagerWrapper 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
// Default Audio Status given by the System Audio device in its initial <Report Audio Status>
// that triggers AVB 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 =
// VolumeInfo passed to AudioDeviceVolumeManager#setDeviceAbsoluteVolumeBehavior to enable AVB
private static final VolumeInfo ENABLE_AVB_VOLUME_INFO =
new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
.setMuted(INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute())
.setVolumeIndex(INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume())
@@ -110,6 +104,8 @@ public abstract class BaseAbsoluteVolumeControlTest {
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
.build();
private static final int EMPTY_FLAGS = 0;
protected abstract HdmiCecLocalDevice createLocalDevice(HdmiControlService hdmiControlService);
protected abstract int getPhysicalAddress();
@@ -126,17 +122,17 @@ public abstract class BaseAbsoluteVolumeControlTest {
mContextSpy = spy(new ContextWrapper(
InstrumentationRegistry.getInstrumentation().getTargetContext()));
mAudioDeviceVolumeManager = spy(new FakeAudioDeviceVolumeManagerWrapper());
mAudioFramework = new FakeAudioFramework();
mAudioManager = spy(mAudioFramework.getAudioManager());
mAudioDeviceVolumeManager = spy(mAudioFramework.getAudioDeviceVolumeManager());
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, EMPTY_FLAGS);
mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
Collections.singletonList(getDeviceType()),
mAudioDeviceVolumeManager) {
@Override
AudioManager getAudioManager() {
return mAudioManager;
}
mAudioManager, mAudioDeviceVolumeManager) {
@Override
protected void writeStringSystemProperty(String key, String value) {
// do nothing
@@ -186,22 +182,12 @@ public abstract class BaseAbsoluteVolumeControlTest {
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);
mAudioFramework.setDevicesForAttributes(HdmiControlService.STREAM_MUSIC_ATTRIBUTES,
Collections.singletonList(getAudioOutputDevice()));
// Max volume of STREAM_MUSIC
doReturn(25).when(mAudioManager).getStreamMaxVolume(AudioManager.STREAM_MUSIC);
mAudioFramework.setStreamMaxVolume(AudioManager.STREAM_MUSIC, 25);
// Receive messages from devices to make sure they're registered in HdmiCecNetwork
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
@@ -220,16 +206,6 @@ public abstract class BaseAbsoluteVolumeControlTest {
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.
*/
@@ -277,16 +253,6 @@ public abstract class BaseAbsoluteVolumeControlTest {
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.
*/
@@ -300,40 +266,34 @@ public abstract class BaseAbsoluteVolumeControlTest {
}
/**
* Triggers all the conditions required to enable Absolute Volume Control.
* Triggers all the conditions required to enable absolute volume behavior.
*/
protected void enableAbsoluteVolumeControl() {
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
protected void enableAbsoluteVolumeBehavior() {
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
enableSystemAudioModeIfNeeded();
receiveInitialReportAudioStatus();
receiveReportAudioStatus(
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume(),
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute());
verifyAbsoluteVolumeEnabled();
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
/**
* 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)));
}
protected void enableAdjustOnlyAbsoluteVolumeBehavior() {
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
enableSystemAudioModeIfNeeded();
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
receiveReportAudioStatus(
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume(),
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute());
/**
* 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());
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
}
protected void verifyGiveAudioStatusNeverSent() {
@@ -350,7 +310,8 @@ public abstract class BaseAbsoluteVolumeControlTest {
@Test
public void allConditionsExceptSavlSupportMet_sendsSetAudioVolumeLevelAndGiveFeatures() {
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
enableSystemAudioModeIfNeeded();
@@ -365,7 +326,8 @@ public abstract class BaseAbsoluteVolumeControlTest {
@Test
public void allConditionsMet_savlSupportLast_reportFeatures_giveAudioStatusSent() {
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
enableSystemAudioModeIfNeeded();
verifyGiveAudioStatusNeverSent();
@@ -376,7 +338,8 @@ public abstract class BaseAbsoluteVolumeControlTest {
@Test
public void allConditionsMet_savlSupportLast_noFeatureAbort_giveAudioStatusSent() {
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
enableSystemAudioModeIfNeeded();
verifyGiveAudioStatusNeverSent();
@@ -388,7 +351,8 @@ public abstract class BaseAbsoluteVolumeControlTest {
@Test
public void allConditionsMet_cecVolumeEnabledLast_giveAudioStatusSent() {
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
enableSystemAudioModeIfNeeded();
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
verifyGiveAudioStatusNeverSent();
@@ -404,7 +368,9 @@ public abstract class BaseAbsoluteVolumeControlTest {
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
verifyGiveAudioStatusNeverSent();
setDeviceVolumeBehavior(getAudioOutputDevice(), AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
mTestLooper.dispatchAll();
verifyGiveAudioStatusSent();
}
@@ -413,7 +379,8 @@ public abstract class BaseAbsoluteVolumeControlTest {
// 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);
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
verifyGiveAudioStatusNeverSent();
@@ -423,62 +390,97 @@ public abstract class BaseAbsoluteVolumeControlTest {
}
@Test
public void giveAudioStatusSent_systemAudioDeviceSendsReportAudioStatus_avcEnabled() {
public void giveAudioStatusSent_systemAudioDeviceSendsReportAudioStatus_avbEnabled() {
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
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();
// AVB should not be enabled before receiving <Report Audio Status>
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
verifyAbsoluteVolumeEnabled();
receiveReportAudioStatus(60, false);
// Check that absolute volume behavior was the last one adopted
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
// Check that the volume and mute status received were included when setting AVB
verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeBehavior(
eq(getAudioOutputDevice()),
eq(new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
.setVolumeIndex(60)
.setMuted(false)
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
.build()),
any(), any(), anyBoolean());
}
@Test
public void avcEnabled_cecVolumeDisabled_absoluteVolumeDisabled() {
enableAbsoluteVolumeControl();
public void giveAudioStatusSent_reportAudioStatusVolumeOutOfBounds_avbNotEnabled() {
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
enableSystemAudioModeIfNeeded();
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
receiveReportAudioStatus(127, false);
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
public void avbEnabled_cecVolumeDisabled_avbDisabled() {
enableAbsoluteVolumeBehavior();
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_DISABLED);
verifyAbsoluteVolumeDisabled();
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
public void avcEnabled_setAudioVolumeLevelNotSupported_absoluteVolumeDisabled() {
enableAbsoluteVolumeControl();
public void avbEnabled_setAudioVolumeLevelNotSupported_avbDisabled() {
enableAbsoluteVolumeBehavior();
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
verifyAbsoluteVolumeDisabled();
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
public void avcEnabled_setAudioVolumeLevelFeatureAborted_absoluteVolumeDisabled() {
enableAbsoluteVolumeControl();
public void avbEnabled_setAudioVolumeLevelFeatureAborted_avbDisabled() {
enableAbsoluteVolumeBehavior();
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
getSystemAudioDeviceLogicalAddress(), getLogicalAddress(),
Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, Constants.ABORT_UNRECOGNIZED_OPCODE));
mTestLooper.dispatchAll();
verifyAbsoluteVolumeDisabled();
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
public void avcEnabled_systemAudioModeDisabled_absoluteVolumeDisabled() {
public void avbEnabled_systemAudioModeDisabled_avbDisabled() {
// Only run when the System Audio device is an Audio System.
assume().that(getSystemAudioDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
enableAbsoluteVolumeControl();
enableAbsoluteVolumeBehavior();
receiveSetSystemAudioMode(false);
verifyAbsoluteVolumeDisabled();
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
public void avcEnabled_receiveReportAudioStatus_notifiesVolumeOrMuteChanges() {
public void avbEnabled_receiveReportAudioStatus_notifiesVolumeOrMuteChanges() {
// Initial <Report Audio Status> has volume=50 and mute=false
enableAbsoluteVolumeControl();
enableAbsoluteVolumeBehavior();
// New volume and mute status: sets both
receiveReportAudioStatus(20, true);
@@ -512,12 +514,20 @@ public abstract class BaseAbsoluteVolumeControlTest {
eq(AudioManager.ADJUST_UNMUTE), anyInt());
clearInvocations(mAudioManager);
// Volume not within range [0, 100]: sets neither volume nor mute
receiveReportAudioStatus(127, true);
verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(),
anyInt());
verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(),
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)
new VolumeInfo.Builder(ENABLE_AVB_VOLUME_INFO)
.setVolumeIndex(20)
.build()
);
@@ -530,13 +540,13 @@ public abstract class BaseAbsoluteVolumeControlTest {
}
@Test
public void avcEnabled_audioDeviceVolumeAdjusted_sendsUserControlPressedAndGiveAudioStatus() {
enableAbsoluteVolumeControl();
public void avbEnabled_audioDeviceVolumeAdjusted_sendsUserControlPressedAndGiveAudioStatus() {
enableAbsoluteVolumeBehavior();
mNativeWrapper.clearResultMessages();
mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeAdjusted(
getAudioOutputDevice(),
ENABLE_AVC_VOLUME_INFO,
ENABLE_AVB_VOLUME_INFO,
AudioManager.ADJUST_RAISE,
AudioDeviceVolumeManager.ADJUST_MODE_NORMAL
);
@@ -554,14 +564,16 @@ public abstract class BaseAbsoluteVolumeControlTest {
}
@Test
public void avcEnabled_audioDeviceVolumeChanged_sendsSetAudioVolumeLevel() {
enableAbsoluteVolumeControl();
public void avbEnabled_audioDeviceVolumeChanged_sendsSetAudioVolumeLevel() {
enableAbsoluteVolumeBehavior();
mNativeWrapper.clearResultMessages();
mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeChanged(
getAudioOutputDevice(),
new VolumeInfo.Builder(ENABLE_AVC_VOLUME_INFO)
new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
.setVolumeIndex(20)
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
.build()
);
mTestLooper.dispatchAll();

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2023 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.media.AudioManager;
import org.junit.Test;
/**
* Base class for tests for absolute volume behavior on Playback devices. Contains tests that are
* relevant to Playback devices but not to TVs.
*
* Subclasses contain tests for the following pairs of (local device, System Audio device):
* (Playback, TV): {@link PlaybackDeviceToTvAvbTest}
* (Playback, Audio System): {@link PlaybackDeviceToAudioSystemAvbTest}
*/
public abstract class BasePlaybackDeviceAvbTest extends BaseAbsoluteVolumeBehaviorTest {
@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;
}
/**
* Unlike TVs, Playback devices don't start the process for adopting adjust-only AVB
* if the System Audio device doesn't support <Set Audio Volume Level>
*/
@Test
public void savlNotSupported_allOtherConditionsMet_giveAudioStatusNotSent() {
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
enableSystemAudioModeIfNeeded();
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
verifyGiveAudioStatusNeverSent();
}
}

View File

@@ -54,9 +54,13 @@ public class DetectTvSystemAudioModeSupportActionTest {
@Before
public void SetUp() {
mDeviceInfoForTests = HdmiDeviceInfo.hardwarePort(1001, 1234);
FakeAudioFramework audioFramework = new FakeAudioFramework();
HdmiControlService hdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
Collections.emptyList(), new FakeAudioDeviceVolumeManagerWrapper()) {
Collections.emptyList(), audioFramework.getAudioManager(),
audioFramework.getAudioDeviceVolumeManager()) {
@Override
void sendCecCommand(

View File

@@ -31,7 +31,6 @@ import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -76,19 +75,11 @@ public class DevicePowerStatusActionTest {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService = new HdmiControlService(mContextSpy,
Collections.singletonList(HdmiDeviceInfo.DEVICE_PLAYBACK),
new FakeAudioDeviceVolumeManagerWrapper()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@Override
public void setWiredDeviceConnectionState(
int type, int state, String address, String name) {
// Do nothing.
}
};
}
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isPowerStandby() {

View File

@@ -97,10 +97,13 @@ public class DeviceSelectActionFromPlaybackTest {
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
Collections.singletonList(HdmiDeviceInfo.DEVICE_PLAYBACK),
new FakeAudioDeviceVolumeManagerWrapper()) {
audioFramework.getAudioManager(),
audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isCecControlEnabled() {
return true;

View File

@@ -106,10 +106,13 @@ public class DeviceSelectActionFromTvTest {
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
Collections.singletonList(HdmiDeviceInfo.DEVICE_TV),
new FakeAudioDeviceVolumeManagerWrapper()) {
audioFramework.getAudioManager(),
audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isCecControlEnabled() {
return true;

View File

@@ -1,72 +0,0 @@
/*
* 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);
}
}
}

View File

@@ -0,0 +1,239 @@
/*
* Copyright (C) 2023 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.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.VolumeInfo;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
/**
* Contains a fake AudioManager and fake AudioDeviceVolumeManager.
* Stores the shared state for these managers, simulating a fake AudioService.
*/
public class FakeAudioFramework {
private final FakeAudioManagerWrapper mAudioManager = new FakeAudioManagerWrapper();
private final FakeAudioDeviceVolumeManagerWrapper mAudioDeviceVolumeManager =
new FakeAudioDeviceVolumeManagerWrapper();
private static final int DEFAULT_DEVICE_VOLUME_BEHAVIOR =
AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE;
private final Map<AudioDeviceAttributes, Integer> mDeviceVolumeBehaviors = new HashMap<>();
private final Set<OnDeviceVolumeBehaviorChangedListener> mVolumeBehaviorListeners =
new HashSet<>();
private final Map<AudioAttributes, List<AudioDeviceAttributes>> mDevicesForAttributes =
new HashMap<>();
private static final int DEFAULT_VOLUME = 0;
private final Map<Integer, Integer> mStreamVolumes = new HashMap<>();
private static final int DEFAULT_MAX_VOLUME = 100;
private final Map<Integer, Integer> mStreamMaxVolumes = new HashMap<>();
private static final boolean DEFAULT_MUTE_STATUS = false;
private final Map<Integer, Boolean> mStreamMuteStatuses = new HashMap<>();
public FakeAudioFramework() {
}
/**
* Returns a fake AudioManager whose methods affect this object's internal state.
*/
public FakeAudioManagerWrapper getAudioManager() {
return mAudioManager;
}
public class FakeAudioManagerWrapper implements AudioManagerWrapper {
@Override
public void adjustStreamVolume(int streamType, int direction,
@AudioManager.PublicVolumeFlags int flags) {
switch (direction) {
case AudioManager.ADJUST_MUTE:
mStreamMuteStatuses.put(streamType, true);
break;
case AudioManager.ADJUST_UNMUTE:
mStreamMuteStatuses.put(streamType, false);
break;
default:
// Other adjustments not implemented
}
}
@Override
public void setStreamVolume(int streamType, int index,
@AudioManager.PublicVolumeFlags int flags) {
mStreamVolumes.put(streamType, index);
}
@Override
public int getStreamVolume(int streamType) {
return mStreamVolumes.getOrDefault(streamType, DEFAULT_VOLUME);
}
@Override
public int getStreamMinVolume(int streamType) {
return 0;
}
@Override
public int getStreamMaxVolume(int streamType) {
return mStreamMaxVolumes.getOrDefault(streamType, DEFAULT_MAX_VOLUME);
}
@Override
public boolean isStreamMute(int streamType) {
return mStreamMuteStatuses.getOrDefault(streamType, DEFAULT_MUTE_STATUS);
}
@Override
public void setStreamMute(int streamType, boolean state) {
mStreamMuteStatuses.put(streamType, state);
}
@Override
public int setHdmiSystemAudioSupported(boolean on) {
return AudioSystem.DEVICE_NONE;
}
@Override
public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state) {
// Do nothing
}
@Override
public void setWiredDeviceConnectionState(int device, int state, String address,
String name) {
// Do nothing
}
@Override
@AudioManager.DeviceVolumeBehavior
public int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
return mDeviceVolumeBehaviors.getOrDefault(device, DEFAULT_DEVICE_VOLUME_BEHAVIOR);
}
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
@AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
setVolumeBehaviorHelper(device, deviceVolumeBehavior);
}
@Override
@NonNull
public List<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
return mDevicesForAttributes.getOrDefault(attributes, Collections.emptyList());
}
}
/**
* Returns a fake AudioDeviceVolumeManager whose methods affect this object's internal state.
*/
public FakeAudioDeviceVolumeManagerWrapper getAudioDeviceVolumeManager() {
return mAudioDeviceVolumeManager;
}
public class FakeAudioDeviceVolumeManagerWrapper implements AudioDeviceVolumeManagerWrapper {
@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) {
setVolumeBehaviorHelper(device, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
@Override
public void setDeviceAbsoluteVolumeAdjustOnlyBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull VolumeInfo volume,
@NonNull @CallbackExecutor Executor executor,
@NonNull OnAudioDeviceVolumeChangedListener vclistener,
boolean handlesVolumeAdjustment) {
setVolumeBehaviorHelper(device,
AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
}
}
/**
* Allows tests to manipulate the return value of
* {@link FakeAudioManagerWrapper#getDevicesForAttributes}
*/
public void setDevicesForAttributes(AudioAttributes attributes,
List<AudioDeviceAttributes> devices) {
mDevicesForAttributes.put(attributes, devices);
}
/**
* Allows tests to manipulate the return value of
* {@link FakeAudioManagerWrapper#getStreamMaxVolume}
*/
public void setStreamMaxVolume(int streamType, int maxVolume) {
mStreamMaxVolumes.put(streamType, maxVolume);
}
/**
* Helper method for changing an audio device's volume behavior. Notifies listeners.
*/
private void setVolumeBehaviorHelper(AudioDeviceAttributes device,
@AudioManager.DeviceVolumeBehavior int newVolumeBehavior) {
int currentVolumeBehavior = mDeviceVolumeBehaviors.getOrDefault(
device, DEFAULT_DEVICE_VOLUME_BEHAVIOR);
mDeviceVolumeBehaviors.put(device, newVolumeBehavior);
if (newVolumeBehavior != currentVolumeBehavior) {
// Notify volume behavior listeners
for (OnDeviceVolumeBehaviorChangedListener listener : mVolumeBehaviorListeners) {
listener.onDeviceVolumeBehaviorChanged(device, newVolumeBehavior);
}
}
}
}

View File

@@ -86,9 +86,11 @@ public class HdmiCecAtomLoggingTest {
mContextSpy = spy(new ContextWrapper(
InstrumentationRegistry.getInstrumentation().getTargetContext()));
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy,
Collections.singletonList(HdmiDeviceInfo.DEVICE_PLAYBACK),
new FakeAudioDeviceVolumeManagerWrapper()));
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();

View File

@@ -100,9 +100,11 @@ public class HdmiCecControllerTest {
public void SetUp() {
mMyLooper = mTestLooper.getLooper();
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlServiceSpy = spy(new HdmiControlService(
InstrumentationRegistry.getTargetContext(), Collections.emptyList(),
new FakeAudioDeviceVolumeManagerWrapper()));
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
doReturn(mMyLooper).when(mHdmiControlServiceSpy).getIoLooper();
doReturn(mMyLooper).when(mHdmiControlServiceSpy).getServiceLooper();
doAnswer(__ -> mCecVersion).when(mHdmiControlServiceSpy).getCecVersion();

View File

@@ -54,23 +54,23 @@ import java.util.ArrayList;
@RunWith(JUnit4.class)
/** Tests for {@link HdmiCecLocalDeviceAudioSystem} class. */
public class HdmiCecLocalDeviceAudioSystemTest {
private static final HdmiCecMessage MESSAGE_REQUEST_SAD_LCPM =
HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
ADDR_TV, ADDR_AUDIO_SYSTEM, new int[] {Constants.AUDIO_CODEC_LPCM});
private static final int EMPTY_FLAGS = 0;
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
private FakeNativeWrapper mNativeWrapper;
private FakePowerManagerWrapper mPowerManager;
private FakeAudioFramework mAudioFramework;
private AudioManagerWrapper mAudioManager;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private int mMusicVolume;
private int mMusicMaxVolume;
private boolean mMusicMute;
private static final int SELF_PHYSICAL_ADDRESS = 0x2000;
private static final int HDMI_1_PHYSICAL_ADDRESS = 0x2100;
private static final int HDMI_2_PHYSICAL_ADDRESS = 0x2200;
@@ -88,66 +88,12 @@ public class HdmiCecLocalDeviceAudioSystemTest {
mLocalDeviceTypes.add(HdmiDeviceInfo.DEVICE_PLAYBACK);
mLocalDeviceTypes.add(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
mAudioFramework = new FakeAudioFramework();
mAudioManager = mAudioFramework.getAudioManager();
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
mLocalDeviceTypes,
new FakeAudioDeviceVolumeManagerWrapper()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@Override
public int getStreamVolume(int streamType) {
switch (streamType) {
case STREAM_MUSIC:
return mMusicVolume;
default:
return 0;
}
}
@Override
public boolean isStreamMute(int streamType) {
switch (streamType) {
case STREAM_MUSIC:
return mMusicMute;
default:
return false;
}
}
@Override
public int getStreamMaxVolume(int streamType) {
switch (streamType) {
case STREAM_MUSIC:
return mMusicMaxVolume;
default:
return 100;
}
}
@Override
public void adjustStreamVolume(
int streamType, int direction, int flags) {
switch (streamType) {
case STREAM_MUSIC:
if (direction == AudioManager.ADJUST_UNMUTE) {
mMusicMute = false;
} else if (direction == AudioManager.ADJUST_MUTE) {
mMusicMute = true;
}
break;
default:
}
}
@Override
public void setWiredDeviceConnectionState(
int type, int state, String address, String name) {
// Do nothing.
}
};
}
mLocalDeviceTypes, mAudioManager,
mAudioFramework.getAudioDeviceVolumeManager()) {
@Override
void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
mDeviceInfo = device;
@@ -236,10 +182,12 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Test
public void handleGiveAudioStatus_volume_10_mute_true() throws Exception {
mMusicVolume = 10;
mMusicMute = true;
mMusicMaxVolume = 20;
int scaledVolume = VolumeControlAction.scaleToCecVolume(10, mMusicMaxVolume);
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 10, EMPTY_FLAGS);
mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_MUTE,
EMPTY_FLAGS);
mAudioFramework.setStreamMaxVolume(AudioManager.STREAM_MUSIC, 20);
int scaledVolume = VolumeControlAction.scaleToCecVolume(10,
mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC));
HdmiCecMessage expectedMessage =
HdmiCecMessageBuilder.buildReportAudioStatus(
ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
@@ -303,7 +251,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Test
@Ignore("b/120845532")
public void handleSetSystemAudioMode_setOn_orignalOff() throws Exception {
mMusicMute = true;
mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
HdmiCecMessage messageSet =
HdmiCecMessageBuilder.buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
HdmiCecMessage messageGive =
@@ -326,13 +274,13 @@ public class HdmiCecLocalDeviceAudioSystemTest {
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
assertThat(mMusicMute).isFalse();
assertThat(mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC)).isFalse();
}
@Test
@Ignore("b/120845532")
public void handleSystemAudioModeRequest_turnOffByTv() throws Exception {
assertThat(mMusicMute).isFalse();
assertThat(mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC)).isFalse();
// Check if feature correctly turned off
HdmiCecMessage messageGive =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
@@ -354,7 +302,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
assertThat(mMusicMute).isTrue();
assertThat(mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC)).isTrue();
}
@Test
@@ -368,7 +316,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
HdmiCecMessageBuilder.buildSetSystemAudioMode(
ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
assertThat(mMusicMute).isTrue();
assertThat(mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC)).isTrue();
}
@Test
@@ -463,13 +411,13 @@ public class HdmiCecLocalDeviceAudioSystemTest {
public void terminateSystemAudioMode_systemAudioModeOff() throws Exception {
mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(false);
assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
mMusicMute = false;
mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildSetSystemAudioMode(
ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
assertThat(mMusicMute).isFalse();
assertThat(mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC)).isFalse();
assertThat(mNativeWrapper.getResultMessages()).isEmpty();
}
@@ -477,13 +425,13 @@ public class HdmiCecLocalDeviceAudioSystemTest {
public void terminateSystemAudioMode_systemAudioModeOn() throws Exception {
mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(true);
assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
mMusicMute = false;
mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
HdmiCecMessage expectedMessage =
HdmiCecMessageBuilder.buildSetSystemAudioMode(
ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
assertThat(mMusicMute).isTrue();
assertThat(mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC)).isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
@@ -705,8 +653,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Test
public void giveAudioStatus_volumeEnabled() {
mMusicVolume = 50;
mMusicMaxVolume = 100;
mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
HdmiControlManager.VOLUME_CONTROL_ENABLED);
mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true);
@@ -733,8 +679,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Test
public void giveAudioStatus_volumeDisabled() {
mMusicVolume = 50;
mMusicMaxVolume = 100;
mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
HdmiControlManager.VOLUME_CONTROL_DISABLED);
mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true);
@@ -761,8 +705,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Test
public void reportAudioStatus_volumeEnabled() {
mMusicVolume = 50;
mMusicMaxVolume = 100;
mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
HdmiControlManager.VOLUME_CONTROL_ENABLED);
mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true);
@@ -786,8 +728,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Test
public void reportAudioStatus_volumeDisabled() {
mMusicVolume = 50;
mMusicMaxVolume = 100;
mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
HdmiControlManager.VOLUME_CONTROL_DISABLED);
mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true);

View File

@@ -31,7 +31,6 @@ import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioManager;
import android.os.Looper;
import android.os.RemoteException;
import android.os.test.TestLooper;
@@ -46,7 +45,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -88,8 +86,6 @@ public class HdmiCecLocalDevicePlaybackTest {
private boolean mActiveMediaSessionsPaused;
private FakePowerManagerInternalWrapper mPowerManagerInternal =
new FakePowerManagerInternalWrapper();
@Mock
protected AudioManager mAudioManager;
@Before
public void setUp() {
@@ -98,10 +94,12 @@ public class HdmiCecLocalDevicePlaybackTest {
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
Collections.singletonList(HdmiDeviceInfo.DEVICE_PLAYBACK),
new FakeAudioDeviceVolumeManagerWrapper()) {
audioFramework.getAudioManager(),
audioFramework.getAudioDeviceVolumeManager()) {
@Override
void wakeUp() {
@@ -109,11 +107,6 @@ public class HdmiCecLocalDevicePlaybackTest {
super.wakeUp();
}
@Override
AudioManager getAudioManager() {
return mAudioManager;
}
@Override
void pauseActiveMediaSessions() {
mActiveMediaSessionsPaused = true;

View File

@@ -33,6 +33,7 @@ import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -53,8 +54,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
@@ -128,18 +127,17 @@ public class HdmiCecLocalDeviceTest {
private boolean isControlEnabled;
private int mPowerStatus;
@Mock
private AudioManager mAudioManager;
private AudioManagerWrapper mAudioManager;
@Before
public void SetUp() {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getTargetContext();
FakeAudioFramework audioFramework = new FakeAudioFramework();
mAudioManager = spy(audioFramework.getAudioManager());
mHdmiControlService =
new HdmiControlService(context, Collections.emptyList(),
new FakeAudioDeviceVolumeManagerWrapper()) {
mAudioManager, audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isCecControlEnabled() {
return isControlEnabled;
@@ -171,11 +169,6 @@ public class HdmiCecLocalDeviceTest {
void wakeUp() {
mWakeupMessageReceived = true;
}
@Override
AudioManager getAudioManager() {
return mAudioManager;
}
};
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));

View File

@@ -33,9 +33,9 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
@@ -55,8 +55,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Collections;
@@ -122,20 +120,21 @@ public class HdmiCecLocalDeviceTvTest {
}
}
@Mock
private AudioManager mAudioManager;
private FakeAudioFramework mAudioFramework;
private AudioManagerWrapper mAudioManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
mAudioFramework = new FakeAudioFramework();
mAudioManager = spy(mAudioFramework.getAudioManager());
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
Collections.singletonList(HdmiDeviceInfo.DEVICE_TV),
new FakeAudioDeviceVolumeManagerWrapper()) {
mAudioManager, mAudioFramework.getAudioDeviceVolumeManager()) {
@Override
void wakeUp() {
mWokenUp = true;
@@ -166,11 +165,6 @@ public class HdmiCecLocalDeviceTvTest {
return false;
}
@Override
AudioManager getAudioManager() {
return mAudioManager;
}
@Override
void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
mDeviceEventListeners.add(new DeviceEventListener(device, status));
@@ -967,7 +961,7 @@ public class HdmiCecLocalDeviceTvTest {
@Test
public void receiveSetAudioVolumeLevel_samNotActivated_noFeatureAbort_volumeChanges() {
when(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)).thenReturn(25);
mAudioFramework.setStreamMaxVolume(AudioManager.STREAM_MUSIC, 25);
// Max volume of STREAM_MUSIC is retrieved on boot
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);

View File

@@ -58,9 +58,11 @@ public class HdmiCecMessageValidatorTest {
@Before
public void setUp() throws Exception {
FakeAudioFramework audioFramework = new FakeAudioFramework();
HdmiControlService mHdmiControlService = new HdmiControlService(
InstrumentationRegistry.getTargetContext(), Collections.emptyList(),
new FakeAudioDeviceVolumeManagerWrapper());
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager());
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
}

View File

@@ -67,8 +67,11 @@ public class HdmiCecNetworkTest {
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService = new HdmiControlService(mContext, Collections.emptyList(),
new FakeAudioDeviceVolumeManagerWrapper()) {
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()) {
@Override
void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
mDeviceEventListenerStatuses.add(status);

View File

@@ -64,9 +64,11 @@ public class HdmiCecPowerStatusControllerTest {
Context contextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
Looper myLooper = mTestLooper.getLooper();
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService = new HdmiControlService(contextSpy,
Collections.singletonList(HdmiDeviceInfo.DEVICE_PLAYBACK),
new FakeAudioDeviceVolumeManagerWrapper()) {
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isCecControlEnabled() {
return true;

View File

@@ -49,7 +49,6 @@ import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.hardware.hdmi.IHdmiVendorCommandListener;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Looper;
import android.os.RemoteException;
@@ -64,9 +63,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
@@ -96,19 +93,17 @@ public class HdmiControlServiceTest {
private HdmiPortInfo[] mHdmiPortInfo;
private ArrayList<Integer> mLocalDeviceTypes = new ArrayList<>();
@Mock protected AudioManager mAudioManager;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
mLocalDeviceTypes.add(HdmiDeviceInfo.DEVICE_PLAYBACK);
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, mLocalDeviceTypes,
new FakeAudioDeviceVolumeManagerWrapper()));
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
@@ -171,7 +166,6 @@ public class HdmiControlServiceTest {
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
mHdmiControlServiceSpy.setPowerManager(mPowerManager);
mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mHdmiControlServiceSpy.setAudioManager(mAudioManager);
mHdmiControlServiceSpy.setEarcSupported(true);
mTestLooper.dispatchAll();

View File

@@ -28,6 +28,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -35,7 +36,6 @@ import android.content.Context;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.AudioDescriptor;
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -49,7 +49,6 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -78,8 +77,7 @@ public class HdmiEarcLocalDeviceTxTest {
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
@Mock
private AudioManager mAudioManager;
private AudioManagerWrapper mAudioManager;
@Captor
ArgumentCaptor<AudioDeviceAttributes> mAudioAttributesCaptor;
@@ -91,10 +89,13 @@ public class HdmiEarcLocalDeviceTxTest {
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
FakeAudioFramework audioFramework = new FakeAudioFramework();
mAudioManager = spy(audioFramework.getAudioManager());
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
Collections.singletonList(HdmiDeviceInfo.DEVICE_TV),
new FakeAudioDeviceVolumeManagerWrapper()) {
mAudioManager, audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isCecControlEnabled() {
return true;
@@ -114,11 +115,6 @@ public class HdmiEarcLocalDeviceTxTest {
boolean isPowerStandby() {
return false;
}
@Override
AudioManager getAudioManager() {
return mAudioManager;
}
};
mHdmiControlService.setIoLooper(mMyLooper);

View File

@@ -31,7 +31,6 @@ import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -88,20 +87,11 @@ public class OneTouchPlayActionTest {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
mHdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService = new HdmiControlService(mContextSpy,
Collections.singletonList(HdmiDeviceInfo.DEVICE_PLAYBACK),
new FakeAudioDeviceVolumeManagerWrapper()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@Override
public void setWiredDeviceConnectionState(
int type, int state, String address, String name) {
// Do nothing.
}
};
}
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isPowerStandby() {
return false;

View File

@@ -16,10 +16,12 @@
package com.android.server.hdmi;
import static com.google.common.truth.Truth.assertThat;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -33,33 +35,13 @@ import org.junit.runners.JUnit4;
import java.util.Arrays;
/**
* Tests for Absolute Volume Control where the local device is a Playback device and the
* Tests for absolute volume behavior 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;
}
public class PlaybackDeviceToAudioSystemAvbTest extends BasePlaybackDeviceAvbTest {
@Override
protected int getSystemAudioDeviceLogicalAddress() {
@@ -72,17 +54,18 @@ public class PlaybackDeviceToAudioSystemAvcTest extends BaseAbsoluteVolumeContro
}
/**
* AVC is disabled if the Audio System disables System Audio mode, and the TV has unknown
* AVB 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();
enableAbsoluteVolumeBehavior();
// Audio System disables System Audio Mode. AVC should be disabled.
// Audio System disables System Audio Mode. AVB should be disabled.
receiveSetSystemAudioMode(false);
verifyAbsoluteVolumeDisabled();
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
// TV reports support for <Set Audio Volume Level>
mNativeWrapper.onCecMessage(ReportFeaturesMessage.build(
@@ -102,6 +85,7 @@ public class PlaybackDeviceToAudioSystemAvcTest extends BaseAbsoluteVolumeContro
false));
mTestLooper.dispatchAll();
verifyAbsoluteVolumeEnabled();
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
}

View File

@@ -16,12 +16,14 @@
package com.android.server.hdmi;
import static com.google.common.truth.Truth.assertThat;
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.media.AudioManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -34,33 +36,13 @@ import java.util.Arrays;
import java.util.Collections;
/**
* Tests for Absolute Volume Control where the local device is a Playback device and the
* Tests for absolute volume behavior 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;
}
public class PlaybackDeviceToTvAvbTest extends BasePlaybackDeviceAvbTest {
@Override
protected int getSystemAudioDeviceLogicalAddress() {
@@ -73,17 +55,18 @@ public class PlaybackDeviceToTvAvcTest extends BaseAbsoluteVolumeControlTest {
}
/**
* AVC is disabled when an Audio System with unknown support for <Set Audio Volume Level>
* AVB 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();
enableAbsoluteVolumeBehavior();
// Audio System enables System Audio Mode. AVC should be disabled.
// Audio System enables System Audio Mode. AVB should be disabled.
receiveSetSystemAudioMode(true);
verifyAbsoluteVolumeDisabled();
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
clearInvocations(mAudioManager, mAudioDeviceVolumeManager);
@@ -105,6 +88,7 @@ public class PlaybackDeviceToTvAvcTest extends BaseAbsoluteVolumeControlTest {
false));
mTestLooper.dispatchAll();
verifyAbsoluteVolumeEnabled();
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
}

View File

@@ -30,7 +30,6 @@ import android.content.ContextWrapper;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -65,20 +64,11 @@ public class PowerStatusMonitorActionTest {
public void setUp() throws Exception {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService = new HdmiControlService(mContextSpy,
Collections.singletonList(HdmiDeviceInfo.DEVICE_TV),
new FakeAudioDeviceVolumeManagerWrapper()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@Override
public void setWiredDeviceConnectionState(
int type, int state, String address, String name) {
// Do nothing.
}
};
}
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isPowerStandby() {
return false;

View File

@@ -95,9 +95,12 @@ public class RequestSadActionTest {
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService =
new HdmiControlService(context, Collections.singletonList(HdmiDeviceInfo.DEVICE_TV),
new FakeAudioDeviceVolumeManagerWrapper()) {
audioFramework.getAudioManager(),
audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isCecControlEnabled() {
return true;

View File

@@ -146,10 +146,13 @@ public class RoutingControlActionTest {
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context);
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
Collections.singletonList(HdmiDeviceInfo.DEVICE_TV),
new FakeAudioDeviceVolumeManagerWrapper()) {
audioFramework.getAudioManager(),
audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isCecControlEnabled() {
return true;

View File

@@ -80,9 +80,11 @@ public class SetAudioVolumeLevelDiscoveryActionTest {
mContextSpy = spy(new ContextWrapper(
InstrumentationRegistry.getInstrumentation().getTargetContext()));
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy,
Collections.singletonList(HdmiDeviceInfo.DEVICE_PLAYBACK),
new FakeAudioDeviceVolumeManagerWrapper()));
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
@@ -152,11 +154,11 @@ public class SetAudioVolumeLevelDiscoveryActionTest {
mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
mTestLooper.dispatchAll();
@DeviceFeatures.FeatureSupportStatus int avcSupport =
@DeviceFeatures.FeatureSupportStatus int savlSupport =
mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
.getDeviceFeatures().getSetAudioVolumeLevelSupport();
assertThat(avcSupport).isEqualTo(FEATURE_SUPPORTED);
assertThat(savlSupport).isEqualTo(FEATURE_SUPPORTED);
assertThat(mTestCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@@ -172,11 +174,11 @@ public class SetAudioVolumeLevelDiscoveryActionTest {
Constants.ABORT_UNRECOGNIZED_OPCODE));
mTestLooper.dispatchAll();
@DeviceFeatures.FeatureSupportStatus int avcSupport =
@DeviceFeatures.FeatureSupportStatus int savlSupport =
mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
.getDeviceFeatures().getSetAudioVolumeLevelSupport();
assertThat(avcSupport).isEqualTo(FEATURE_NOT_SUPPORTED);
assertThat(savlSupport).isEqualTo(FEATURE_NOT_SUPPORTED);
assertThat(mTestCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@@ -189,11 +191,11 @@ public class SetAudioVolumeLevelDiscoveryActionTest {
mPlaybackDevice.addAndStartAction(mAction);
mTestLooper.dispatchAll();
@DeviceFeatures.FeatureSupportStatus int avcSupport =
@DeviceFeatures.FeatureSupportStatus int savlSupport =
mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
.getDeviceFeatures().getSetAudioVolumeLevelSupport();
assertThat(avcSupport).isEqualTo(FEATURE_SUPPORT_UNKNOWN);
assertThat(savlSupport).isEqualTo(FEATURE_SUPPORT_UNKNOWN);
assertThat(mTestCallback.getResult()).isEqualTo(
HdmiControlManager.RESULT_COMMUNICATION_FAILED);
}

View File

@@ -29,7 +29,6 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -68,20 +67,11 @@ public class SystemAudioAutoInitiationActionTest {
Looper myLooper = mTestLooper.getLooper();
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService = new HdmiControlService(mContextSpy,
Collections.singletonList(HdmiDeviceInfo.DEVICE_TV),
new FakeAudioDeviceVolumeManagerWrapper()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@Override
public void setWiredDeviceConnectionState(
int type, int state, String address, String name) {
// Do nothing.
}
};
}
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()) {
@Override
boolean isPowerStandby() {
return false;

View File

@@ -24,7 +24,6 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -68,8 +67,11 @@ public class SystemAudioInitiationActionFromAvrTest {
Context context = InstrumentationRegistry.getTargetContext();
FakeAudioFramework audioFramework = new FakeAudioFramework();
HdmiControlService hdmiControlService = new HdmiControlService(context,
Collections.emptyList(), new FakeAudioDeviceVolumeManagerWrapper()) {
Collections.emptyList(), audioFramework.getAudioManager(),
audioFramework.getAudioDeviceVolumeManager()) {
@Override
void sendCecCommand(
HdmiCecMessage command, @Nullable SendMessageCallback callback) {
@@ -99,36 +101,6 @@ public class SystemAudioInitiationActionFromAvrTest {
}
}
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@Override
public int setHdmiSystemAudioSupported(boolean on) {
return 0;
}
@Override
public int getStreamVolume(int streamType) {
return 0;
}
@Override
public boolean isStreamMute(int streamType) {
return false;
}
@Override
public int getStreamMaxVolume(int streamType) {
return 100;
}
@Override
public void adjustStreamVolume(
int streamType, int direction, int flags) {}
};
}
@Override
boolean isPowerStandby() {
return false;

View File

@@ -0,0 +1,265 @@
/*
* 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 com.android.server.hdmi.HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceVolumeManager;
import android.media.AudioManager;
import android.media.VolumeInfo;
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;
/**
* Tests for absolute volume behavior 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 TvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehaviorTest {
@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;
}
/**
* TVs start the process for adopting adjust-only AVB if the System Audio device doesn't
* support <Set Audio Volume Level>
*/
@Test
public void savlNotSupported_allOtherConditionsMet_giveAudioStatusSent() {
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
enableSystemAudioModeIfNeeded();
verifyGiveAudioStatusNeverSent();
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
verifyGiveAudioStatusSent();
}
@Test
public void savlNotSupported_systemAudioDeviceSendsReportAudioStatus_adjustOnlyAvbEnabled() {
mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
enableSystemAudioModeIfNeeded();
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
// Adjust-only AVB should not be enabled before receiving <Report Audio Status>
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
receiveReportAudioStatus(20, false);
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior(
eq(getAudioOutputDevice()),
eq(new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
.setVolumeIndex(20)
.setMuted(false)
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
.build()),
any(), any(), anyBoolean());
}
@Test
public void avbEnabled_savlNotSupported_receiveReportAudioStatus_switchToAdjustOnlyAvb() {
enableAbsoluteVolumeBehavior();
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
receiveReportAudioStatus(40, true);
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior(
eq(getAudioOutputDevice()),
eq(new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
.setVolumeIndex(40)
.setMuted(true)
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
.build()),
any(), any(), anyBoolean());
}
@Test
public void avbEnabled_savlFeatureAborted_receiveReportAudioStatus_switchToAdjustOnlyAvb() {
enableAbsoluteVolumeBehavior();
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
getSystemAudioDeviceLogicalAddress(), getLogicalAddress(),
Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, Constants.ABORT_UNRECOGNIZED_OPCODE));
mTestLooper.dispatchAll();
receiveReportAudioStatus(40, true);
assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior(
eq(getAudioOutputDevice()),
eq(new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
.setVolumeIndex(40)
.setMuted(true)
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
.build()),
any(), any(), anyBoolean());
}
@Test
public void adjustOnlyAvbEnabled_receiveReportAudioStatus_notifiesVolumeOrMuteChanges() {
enableAdjustOnlyAbsoluteVolumeBehavior();
// 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());
// Volume not within range [0, 100]: sets neither volume nor mute
receiveReportAudioStatus(127, true);
verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(),
anyInt());
verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(),
anyInt());
}
@Test
public void adjustOnlyAvbEnabled_audioDeviceVolumeAdjusted_sendsUcpAndGiveAudioStatus() {
enableAdjustOnlyAbsoluteVolumeBehavior();
mNativeWrapper.clearResultMessages();
mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeAdjusted(
getAudioOutputDevice(),
new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
.build(),
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 adjustOnlyAvbEnabled_audioDeviceVolumeChanged_doesNotSendSetAudioVolumeLevel() {
enableAdjustOnlyAbsoluteVolumeBehavior();
mNativeWrapper.clearResultMessages();
mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeChanged(
getAudioOutputDevice(),
new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
.setVolumeIndex(20)
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
.build()
);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).isEmpty();
}
}

View File

@@ -1,66 +0,0 @@
/*
* 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;
}
}