mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 15:58:43 +00:00
audio: Add effect attachment to devices and streams
Add the following methods:
- IModule.{add|remove}DeviceEffect;
- IStream.{add|remove}Effect.
Bug: 205884982
Test: atest VtsHalAudioCoreTargetTest
Change-Id: I4066e2d10a8e08d634010cfe9eb8f832157e725f
This commit is contained in:
@@ -131,6 +131,7 @@ aidl_interface {
|
||||
"android.hardware.common-V2",
|
||||
"android.hardware.common.fmq-V1",
|
||||
"android.hardware.audio.common-V1",
|
||||
"android.hardware.audio.effect-V1",
|
||||
"android.media.audio.common.types-V2",
|
||||
],
|
||||
backend: {
|
||||
|
||||
@@ -64,6 +64,8 @@ interface IModule {
|
||||
int generateHwAvSyncId();
|
||||
android.hardware.audio.core.VendorParameter[] getVendorParameters(in @utf8InCpp String[] ids);
|
||||
void setVendorParameters(in android.hardware.audio.core.VendorParameter[] parameters, boolean async);
|
||||
void addDeviceEffect(int portConfigId, in android.hardware.audio.effect.IEffect effect);
|
||||
void removeDeviceEffect(int portConfigId, in android.hardware.audio.effect.IEffect effect);
|
||||
@VintfStability
|
||||
parcelable OpenInputStreamArguments {
|
||||
int portConfigId;
|
||||
|
||||
@@ -38,4 +38,6 @@ interface IStreamCommon {
|
||||
void updateHwAvSyncId(int hwAvSyncId);
|
||||
android.hardware.audio.core.VendorParameter[] getVendorParameters(in @utf8InCpp String[] ids);
|
||||
void setVendorParameters(in android.hardware.audio.core.VendorParameter[] parameters, boolean async);
|
||||
void addEffect(in android.hardware.audio.effect.IEffect effect);
|
||||
void removeEffect(in android.hardware.audio.effect.IEffect effect);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.hardware.audio.core.MicrophoneInfo;
|
||||
import android.hardware.audio.core.ModuleDebug;
|
||||
import android.hardware.audio.core.StreamDescriptor;
|
||||
import android.hardware.audio.core.VendorParameter;
|
||||
import android.hardware.audio.effect.IEffect;
|
||||
import android.media.audio.common.AudioOffloadInfo;
|
||||
import android.media.audio.common.AudioPort;
|
||||
import android.media.audio.common.AudioPortConfig;
|
||||
@@ -515,7 +516,8 @@ interface IModule {
|
||||
* @throws EX_ILLEGAL_ARGUMENT If the port config can not be found by the ID.
|
||||
* @throws EX_ILLEGAL_STATE In the following cases:
|
||||
* - If the port config has a stream opened on it;
|
||||
* - If the port config is used by a patch.
|
||||
* - If the port config is used by a patch;
|
||||
* - If the port config has an audio effect on it.
|
||||
*/
|
||||
void resetAudioPortConfig(int portConfigId);
|
||||
|
||||
@@ -728,4 +730,34 @@ interface IModule {
|
||||
* @throws EX_UNSUPPORTED_OPERATION If the module does not support vendor parameters.
|
||||
*/
|
||||
void setVendorParameters(in VendorParameter[] parameters, boolean async);
|
||||
|
||||
/**
|
||||
* Apply an audio effect to a device port.
|
||||
*
|
||||
* The audio effect applies to all audio input or output on the specific
|
||||
* configuration of the device audio port. The effect is inserted according
|
||||
* to its insertion preference specified by the 'flags.insert' field of the
|
||||
* EffectDescriptor.
|
||||
*
|
||||
* @param portConfigId The ID of the audio port config.
|
||||
* @param effect The effect instance.
|
||||
* @throws EX_ILLEGAL_ARGUMENT If the device port config can not be found by the ID,
|
||||
* or the effect reference is invalid.
|
||||
* @throws EX_UNSUPPORTED_OPERATION If the module does not support device port effects.
|
||||
*/
|
||||
void addDeviceEffect(int portConfigId, in IEffect effect);
|
||||
|
||||
/**
|
||||
* Stop applying an audio effect to a device port.
|
||||
*
|
||||
* Undo the action of the 'addDeviceEffect' method.
|
||||
*
|
||||
* @param portConfigId The ID of the audio port config.
|
||||
* @param effect The effect instance.
|
||||
* @throws EX_ILLEGAL_ARGUMENT If the device port config can not be found by the ID,
|
||||
* or the effect reference is invalid, or the effect is
|
||||
* not currently applied to the port config.
|
||||
* @throws EX_UNSUPPORTED_OPERATION If the module does not support device port effects.
|
||||
*/
|
||||
void removeDeviceEffect(int portConfigId, in IEffect effect);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.hardware.audio.core;
|
||||
|
||||
import android.hardware.audio.core.VendorParameter;
|
||||
import android.hardware.audio.effect.IEffect;
|
||||
|
||||
/**
|
||||
* This interface contains operations that are common to input and output
|
||||
@@ -86,4 +87,30 @@ interface IStreamCommon {
|
||||
* @throws EX_UNSUPPORTED_OPERATION If the stream does not support vendor parameters.
|
||||
*/
|
||||
void setVendorParameters(in VendorParameter[] parameters, boolean async);
|
||||
|
||||
/**
|
||||
* Apply an audio effect to the stream.
|
||||
*
|
||||
* This method is intended for the cases when the effect has an offload
|
||||
* implementation, since software effects can be applied at the client side.
|
||||
*
|
||||
* @param effect The effect instance.
|
||||
* @throws EX_ILLEGAL_ARGUMENT If the effect reference is invalid.
|
||||
* @throws EX_ILLEGAL_STATE If the stream is closed.
|
||||
* @throws EX_UNSUPPORTED_OPERATION If the module does not support audio effects.
|
||||
*/
|
||||
void addEffect(in IEffect effect);
|
||||
|
||||
/**
|
||||
* Stop applying an audio effect to the stream.
|
||||
*
|
||||
* Undo the action of the 'addEffect' method.
|
||||
*
|
||||
* @param effect The effect instance.
|
||||
* @throws EX_ILLEGAL_ARGUMENT If the effect reference is invalid, or the effect is
|
||||
* not currently applied to the stream.
|
||||
* @throws EX_ILLEGAL_STATE If the stream is closed.
|
||||
* @throws EX_UNSUPPORTED_OPERATION If the module does not support audio effects.
|
||||
*/
|
||||
void removeEffect(in IEffect effect);
|
||||
}
|
||||
|
||||
@@ -969,4 +969,28 @@ ndk::ScopedAStatus Module::setVendorParameters(const std::vector<VendorParameter
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::addDeviceEffect(
|
||||
int32_t in_portConfigId,
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
|
||||
if (in_effect == nullptr) {
|
||||
LOG(DEBUG) << __func__ << ": port id " << in_portConfigId << ", null effect";
|
||||
} else {
|
||||
LOG(DEBUG) << __func__ << ": port id " << in_portConfigId << ", effect Binder "
|
||||
<< in_effect->asBinder().get();
|
||||
}
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::removeDeviceEffect(
|
||||
int32_t in_portConfigId,
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
|
||||
if (in_effect == nullptr) {
|
||||
LOG(DEBUG) << __func__ << ": port id " << in_portConfigId << ", null effect";
|
||||
} else {
|
||||
LOG(DEBUG) << __func__ << ": port id " << in_portConfigId << ", effect Binder "
|
||||
<< in_effect->asBinder().get();
|
||||
}
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
||||
@@ -540,6 +540,28 @@ ndk::ScopedAStatus StreamCommonImpl<Metadata, StreamWorker>::setVendorParameters
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
template <class Metadata, class StreamWorker>
|
||||
ndk::ScopedAStatus StreamCommonImpl<Metadata, StreamWorker>::addEffect(
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
|
||||
if (in_effect == nullptr) {
|
||||
LOG(DEBUG) << __func__ << ": null effect";
|
||||
} else {
|
||||
LOG(DEBUG) << __func__ << ": effect Binder" << in_effect->asBinder().get();
|
||||
}
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
template <class Metadata, class StreamWorker>
|
||||
ndk::ScopedAStatus StreamCommonImpl<Metadata, StreamWorker>::removeEffect(
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
|
||||
if (in_effect == nullptr) {
|
||||
LOG(DEBUG) << __func__ << ": null effect";
|
||||
} else {
|
||||
LOG(DEBUG) << __func__ << ": effect Binder" << in_effect->asBinder().get();
|
||||
}
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
template <class Metadata, class StreamWorker>
|
||||
ndk::ScopedAStatus StreamCommonImpl<Metadata, StreamWorker>::close() {
|
||||
LOG(DEBUG) << __func__;
|
||||
|
||||
@@ -92,6 +92,14 @@ class Module : public BnModule {
|
||||
std::vector<VendorParameter>* _aidl_return) override;
|
||||
ndk::ScopedAStatus setVendorParameters(const std::vector<VendorParameter>& in_parameters,
|
||||
bool in_async) override;
|
||||
ndk::ScopedAStatus addDeviceEffect(
|
||||
int32_t in_portConfigId,
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
|
||||
override;
|
||||
ndk::ScopedAStatus removeDeviceEffect(
|
||||
int32_t in_portConfigId,
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
|
||||
override;
|
||||
|
||||
void cleanUpPatch(int32_t patchId);
|
||||
ndk::ScopedAStatus createStreamContext(
|
||||
|
||||
@@ -212,6 +212,12 @@ struct StreamCommonInterface {
|
||||
std::vector<VendorParameter>* _aidl_return) = 0;
|
||||
virtual ndk::ScopedAStatus setVendorParameters(
|
||||
const std::vector<VendorParameter>& in_parameters, bool in_async) = 0;
|
||||
virtual ndk::ScopedAStatus addEffect(
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>&
|
||||
in_effect) = 0;
|
||||
virtual ndk::ScopedAStatus removeEffect(
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>&
|
||||
in_effect) = 0;
|
||||
};
|
||||
|
||||
class StreamCommon : public BnStreamCommon {
|
||||
@@ -242,6 +248,20 @@ class StreamCommon : public BnStreamCommon {
|
||||
return delegate != nullptr ? delegate->setVendorParameters(in_parameters, in_async)
|
||||
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
ndk::ScopedAStatus addEffect(
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
|
||||
override {
|
||||
auto delegate = mDelegate.lock();
|
||||
return delegate != nullptr ? delegate->addEffect(in_effect)
|
||||
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
ndk::ScopedAStatus removeEffect(
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
|
||||
override {
|
||||
auto delegate = mDelegate.lock();
|
||||
return delegate != nullptr ? delegate->removeEffect(in_effect)
|
||||
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
// It is possible that on the client side the proxy for IStreamCommon will outlive
|
||||
// the IStream* instance, and the server side IStream* instance will get destroyed
|
||||
// while this IStreamCommon instance is still alive.
|
||||
@@ -257,6 +277,12 @@ class StreamCommonImpl : public StreamCommonInterface {
|
||||
std::vector<VendorParameter>* _aidl_return) override;
|
||||
ndk::ScopedAStatus setVendorParameters(const std::vector<VendorParameter>& in_parameters,
|
||||
bool in_async) override;
|
||||
ndk::ScopedAStatus addEffect(
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
|
||||
override;
|
||||
ndk::ScopedAStatus removeEffect(
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
|
||||
override;
|
||||
|
||||
ndk::ScopedAStatus getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return);
|
||||
ndk::ScopedAStatus init() {
|
||||
|
||||
@@ -1746,6 +1746,33 @@ TEST_P(AudioCoreModule, SetVendorParameters) {
|
||||
}
|
||||
}
|
||||
|
||||
// See b/262930731. In the absence of offloaded effect implementations,
|
||||
// currently we can only pass a nullptr, and the HAL module must either reject
|
||||
// it as an invalid argument, or say that offloaded effects are not supported.
|
||||
TEST_P(AudioCoreModule, AddRemoveEffectInvalidArguments) {
|
||||
ndk::ScopedAStatus addEffectStatus = module->addDeviceEffect(-1, nullptr);
|
||||
ndk::ScopedAStatus removeEffectStatus = module->removeDeviceEffect(-1, nullptr);
|
||||
const bool isSupported = addEffectStatus.getExceptionCode() != EX_UNSUPPORTED_OPERATION;
|
||||
if (isSupported) {
|
||||
EXPECT_EQ(EX_ILLEGAL_ARGUMENT, addEffectStatus.getExceptionCode());
|
||||
EXPECT_EQ(EX_ILLEGAL_ARGUMENT, removeEffectStatus.getExceptionCode());
|
||||
} else if (EX_UNSUPPORTED_OPERATION != removeEffectStatus.getExceptionCode()) {
|
||||
GTEST_FAIL() << "addEffect and removeEffect must be either supported or not supported "
|
||||
<< "together";
|
||||
} else {
|
||||
GTEST_SKIP() << "Offloaded effects not supported";
|
||||
}
|
||||
// Test rejection of a nullptr effect with a valid device port Id.
|
||||
ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
|
||||
const auto configs = moduleConfig->getPortConfigsForAttachedDevicePorts();
|
||||
for (const auto& config : configs) {
|
||||
WithAudioPortConfig portConfig(config);
|
||||
ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get()));
|
||||
EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->addDeviceEffect(portConfig.getId(), nullptr));
|
||||
EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->removeDeviceEffect(portConfig.getId(), nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
class AudioCoreTelephony : public AudioCoreModuleBase, public testing::TestWithParam<std::string> {
|
||||
public:
|
||||
void SetUp() override {
|
||||
@@ -2080,6 +2107,43 @@ class AudioStream : public AudioCoreModule {
|
||||
}
|
||||
}
|
||||
|
||||
// See b/262930731. In the absence of offloaded effect implementations,
|
||||
// currently we can only pass a nullptr, and the HAL module must either reject
|
||||
// it as an invalid argument, or say that offloaded effects are not supported.
|
||||
void AddRemoveEffectInvalidArguments() {
|
||||
const auto ports =
|
||||
moduleConfig->getMixPorts(IOTraits<Stream>::is_input, false /*attachedOnly*/);
|
||||
if (ports.empty()) {
|
||||
GTEST_SKIP() << "No mix ports";
|
||||
}
|
||||
bool atLeastOneSupports = false;
|
||||
for (const auto& port : ports) {
|
||||
const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port);
|
||||
if (!portConfig.has_value()) continue;
|
||||
WithStream<Stream> stream(portConfig.value());
|
||||
ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
|
||||
std::shared_ptr<IStreamCommon> streamCommon;
|
||||
ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon));
|
||||
ASSERT_NE(nullptr, streamCommon);
|
||||
ndk::ScopedAStatus addEffectStatus = streamCommon->addEffect(nullptr);
|
||||
ndk::ScopedAStatus removeEffectStatus = streamCommon->removeEffect(nullptr);
|
||||
const bool isSupported = addEffectStatus.getExceptionCode() != EX_UNSUPPORTED_OPERATION;
|
||||
if (isSupported) {
|
||||
EXPECT_EQ(EX_ILLEGAL_ARGUMENT, addEffectStatus.getExceptionCode());
|
||||
EXPECT_EQ(EX_ILLEGAL_ARGUMENT, removeEffectStatus.getExceptionCode());
|
||||
atLeastOneSupports = true;
|
||||
} else if (EX_UNSUPPORTED_OPERATION != removeEffectStatus.getExceptionCode()) {
|
||||
ADD_FAILURE()
|
||||
<< "addEffect and removeEffect must be either supported or not supported "
|
||||
<< "together";
|
||||
atLeastOneSupports = true;
|
||||
}
|
||||
}
|
||||
if (!atLeastOneSupports) {
|
||||
GTEST_SKIP() << "Offloaded effects not supported";
|
||||
}
|
||||
}
|
||||
|
||||
void OpenTwiceSamePortConfigImpl(const AudioPortConfig& portConfig) {
|
||||
WithStream<Stream> stream1(portConfig);
|
||||
ASSERT_NO_FATAL_FAILURE(stream1.SetUp(module.get(), kDefaultBufferSizeFrames));
|
||||
@@ -2157,6 +2221,7 @@ TEST_IN_AND_OUT_STREAM(UpdateHwAvSyncId);
|
||||
TEST_IN_AND_OUT_STREAM(GetVendorParameters);
|
||||
TEST_IN_AND_OUT_STREAM(SetVendorParameters);
|
||||
TEST_IN_AND_OUT_STREAM(HwGainHwVolume);
|
||||
TEST_IN_AND_OUT_STREAM(AddRemoveEffectInvalidArguments);
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
std::ostream& operator<<(std::ostream& os, const IStreamIn::MicrophoneDirection& md) {
|
||||
|
||||
Reference in New Issue
Block a user