Merge changes from topic "aidl_audio_effect_5" am: bbefdc5d36

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2254121

Change-Id: I90d029fd5daa9c5726306898741d5516ef5df2b5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Shunkai Yao
2022-10-19 22:45:25 +00:00
committed by Automerger Merge Worker
26 changed files with 1569 additions and 445 deletions

View File

@@ -59,5 +59,9 @@ parcelable Descriptor {
@VintfStability
parcelable Common {
android.hardware.audio.effect.Descriptor.Identity id;
int cpuLoad;
int memoryUsage;
@utf8InCpp String name;
@utf8InCpp String implementor;
}
}

View File

@@ -35,12 +35,32 @@ package android.hardware.audio.effect;
@VintfStability
union Equalizer {
android.hardware.audio.effect.Equalizer.VendorExtension vendor;
@VintfStability
parcelable Capability {
ParcelableHolder extension;
}
android.hardware.audio.effect.Equalizer.BandLevel[] bandLevels;
int preset;
@VintfStability
parcelable VendorExtension {
ParcelableHolder extension;
}
@VintfStability
parcelable Capability {
ParcelableHolder extension;
android.hardware.audio.effect.Equalizer.BandFrequency[] bandFrequencies;
android.hardware.audio.effect.Equalizer.Preset[] presets;
}
@VintfStability
parcelable BandLevel {
int index;
int level;
}
@VintfStability
parcelable BandFrequency {
int index;
int min;
int max;
}
@VintfStability
parcelable Preset {
int index;
@utf8InCpp String name;
}
}

View File

@@ -34,4 +34,41 @@
package android.hardware.audio.effect;
@VintfStability
parcelable Flags {
android.hardware.audio.effect.Flags.Type type = android.hardware.audio.effect.Flags.Type.INSERT;
android.hardware.audio.effect.Flags.Insert insert = android.hardware.audio.effect.Flags.Insert.ANY;
android.hardware.audio.effect.Flags.Volume volume = android.hardware.audio.effect.Flags.Volume.NONE;
android.hardware.audio.effect.Flags.HardwareAccelerator hwAcceleratorMode = android.hardware.audio.effect.Flags.HardwareAccelerator.NONE;
boolean offloadIndication;
boolean deviceIndication;
boolean audioModeIndication;
boolean audioSourceIndication;
boolean noProcessing;
@Backing(type="byte") @VintfStability
enum Type {
INSERT = 0,
AUXILIARY = 1,
REPLACE = 2,
PRE_PROC = 3,
POST_PROC = 4,
}
@Backing(type="byte") @VintfStability
enum Insert {
ANY = 0,
FIRST = 1,
LAST = 2,
EXCLUSIVE = 3,
}
@Backing(type="byte") @VintfStability
enum Volume {
NONE = 0,
CTRL = 1,
IND = 2,
MONITOR = 3,
}
@Backing(type="byte") @VintfStability
enum HardwareAccelerator {
NONE = 0,
SIMPLE = 1,
TUNNEL = 2,
}
}

View File

@@ -35,28 +35,42 @@ package android.hardware.audio.effect;
@VintfStability
union Parameter {
android.hardware.audio.effect.Parameter.Common common;
android.media.audio.common.AudioDeviceType device;
android.media.audio.common.AudioMode mode;
android.media.audio.common.AudioSource source;
android.hardware.audio.effect.Parameter.Volume volume;
boolean offload;
android.hardware.audio.effect.Parameter.VendorEffectParameter vendorEffect;
android.hardware.audio.effect.Parameter.Specific specific;
@VintfStability
union Id {
int commonTag;
int vendorTag;
android.hardware.audio.effect.Parameter.Specific.Tag specificTag;
android.hardware.audio.effect.Parameter.Specific.Id specificId;
}
@VintfStability
parcelable Common {
int session;
int ioHandle;
android.media.audio.common.AudioDeviceDescription device;
android.media.audio.common.AudioConfig input;
android.media.audio.common.AudioConfig output;
}
@VintfStability
parcelable Volume {
float left;
float right;
}
@VintfStability
parcelable VendorEffectParameter {
ParcelableHolder extension;
}
@VintfStability
union Specific {
android.hardware.audio.effect.Parameter.Specific.Id id;
android.hardware.audio.effect.Equalizer equalizer;
@VintfStability
union Id {
android.hardware.audio.effect.Equalizer.Tag equalizerTag = android.hardware.audio.effect.Equalizer.Tag.vendor;
}
}
}

View File

@@ -27,7 +27,9 @@ package android.hardware.audio.effect;
@VintfStability
@Backing(type="int")
enum CommandId {
/// MUST be supported by all effects
/**
* Commands MUST be supported by all effects.
*/
/**
* Start effect engine processing.
* An effect instance must start processing data and transfer to PROCESSING state if it is in
@@ -53,10 +55,13 @@ enum CommandId {
*/
RESET = 2,
/// MUST be supported by a specific type of effect.
// Commands must supported by Equalizer.
/**
* Commands MUST be supported by a specific type of effect.
*/
/// Extension commands for vendor.
/**
* Extension commands for vendor.
*/
VENDOR_COMMAND_0 = 0x100,
VENDOR_COMMAND_1,
VENDOR_COMMAND_2,

View File

@@ -36,31 +36,57 @@ parcelable Descriptor {
/**
* UUID for effect types, these definitions are in sync with SDK, see @c AudioEffect.java.
*/
// UUID for environmental reverberation effect type.
/**
* UUID for environmental reverberation effect type.
*/
const String EFFECT_TYPE_UUID_ENV_REVERB = "c2e5d5f0-94bd-4763-9cac-4e234d06839e";
// UUID for preset reverberation effect type.
/**
* UUID for preset reverberation effect type.
*/
const String EFFECT_TYPE_UUID_PRESET_REVERB = "47382d60-ddd8-11db-bf3a-0002a5d5c51b";
// UUID for equalizer effect type.
/**
* UUID for equalizer effect type.
*/
const String EFFECT_TYPE_UUID_EQUALIZER = "0bed4300-ddd6-11db-8f34-0002a5d5c51b";
// UUID for bass boost effect type.
/**
* UUID for bass boost effect type.
*/
const String EFFECT_TYPE_UUID_BASS_BOOST = "0634f220-ddd4-11db-a0fc-0002a5d5c51b";
// UUID for virtualizer effect type.
/**
* UUID for virtualizer effect type.
*/
const String EFFECT_TYPE_UUID_VIRTUALIZER = "37cc2c00-dddd-11db-8577-0002a5d5c51b";
// UUID for Automatic Gain Control (AGC) type.
/**
* UUID for Automatic Gain Control (AGC) type.
*/
const String EFFECT_TYPE_UUID_AGC = "0a8abfe0-654c-11e0-ba26-0002a5d5c51b";
// UUID for Acoustic Echo Canceler (AEC) type.
/**
* UUID for Acoustic Echo Canceler (AEC) type.
*/
const String EFFECT_TYPE_UUID_AEC = "7b491460-8d4d-11e0-bd61-0002a5d5c51b";
// UUID for Noise Suppressor (NS) type.
/**
* UUID for Noise Suppressor (NS) type.
*/
const String EFFECT_TYPE_UUID_NS = "58b4b260-8e06-11e0-aa8e-0002a5d5c51b";
// UUID for Loudness Enhancer type.
/**
* UUID for Loudness Enhancer type.
*/
const String EFFECT_TYPE_UUID_LOUDNESS_ENHANCER = "fe3199be-aed0-413f-87bb-11260eb63cf1";
// UUID for Dynamics Processing type.
/**
* UUID for Dynamics Processing type.
*/
const String EFFECT_TYPE_UUID_DYNAMICS_PROCESSING = "7261676f-6d75-7369-6364-28e2fd3ac39e";
// UUID for Haptic Generator type.
/**
* UUID for Haptic Generator type.
*/
const String EFFECT_TYPE_UUID_HAPTIC_GENERATOR = "1411e6d6-aecd-4021-a1cf-a6aceb0d71e5";
// UUID for Spatializer type.
/**
* UUID for Spatializer type.
*/
const String EFFECT_TYPE_UUID_SPATIALIZER = "ccd4cf09-a79d-46c2-9aae-06a1698d6c8f";
// UUID for Volume type. The volume effect is used for automated tests only.
/**
* UUID for Volume type. The volume effect is used for automated tests only.
*/
const String EFFECT_TYPE_UUID_VOLUME = "09e8ede0-ddde-11db-b4f6-0002a5d5c51b";
/**
@@ -87,13 +113,32 @@ parcelable Descriptor {
Flags flags;
}
// Common attributes of all effect implementation.
/**
* Common attributes of all effect implementation.
*/
@VintfStability
parcelable Common {
/**
* Identity of effect implementation.
*/
Identity id;
/**
* CPU load indication expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE)
* with 0 WS.
*/
int cpuLoad;
/**
* Data memory usage expressed in KB and includes only dynamically allocated memory.
*/
int memoryUsage;
/**
* Human readable effect name, no intended to display on UI directly.
*/
@utf8InCpp String name;
/**
* Human readable effect implementor name, no intended to display on UI directly.
*/
@utf8InCpp String implementor;
}
Common common;

View File

@@ -20,14 +20,23 @@ import android.media.audio.common.AudioProfile;
/**
* Equalizer specific definitions.
*
* All parameters defined in union Equalizer must be gettable and settable. The capabilities defined
* in Equalizer.Capability can only acquired with IEffect.getDescriptor() and not settable.
*/
@VintfStability
union Equalizer {
/**
* Defines Equalizer implementation capabilities, it MUST be supported by all equalizer
* implementations.
*
* Equalizer.Capability definition is used by android.hardware.audio.effect.Capability.
* Vendor Equalizer implementation definition for additional parameters.
*/
@VintfStability
parcelable VendorExtension {
ParcelableHolder extension;
}
VendorExtension vendor;
/**
* Capability MUST be supported by Equalizer implementation.
*/
@VintfStability
parcelable Capability {
@@ -36,12 +45,55 @@ union Equalizer {
* definition not enough.
*/
ParcelableHolder extension;
/**
* Bands frequency ranges supported.
*/
BandFrequency[] bandFrequencies;
/**
* Presets name and index.
*/
Preset[] presets;
}
// Vendor Equalizer implementation definition for additional parameters.
/**
* Level setting for each band.
*/
@VintfStability
parcelable VendorExtension {
ParcelableHolder extension;
parcelable BandLevel {
int index;
int level;
}
VendorExtension vendor;
/**
* Supported minimal and maximal frequency for each band.
*/
@VintfStability
parcelable BandFrequency {
int index;
int min;
int max;
}
/**
* Factory presets supported.
*/
@VintfStability
parcelable Preset {
int index;
/**
* Preset name, used to identify presets but no intended to display on UI directly.
*/
@utf8InCpp String name;
}
/**
* Level for each band.
*/
BandLevel[] bandLevels;
/**
* Index of current preset.
*/
int preset;
}

View File

@@ -17,10 +17,131 @@
package android.hardware.audio.effect;
/**
* The common part of available capability/configuration for effects. For effect type specific
* capability, see @c android.hardware.audio.effect.Capability.
* Some common capability for an effect instance.
*/
@VintfStability
parcelable Flags {
// TODO: add Effect engine defined capabilities/requirements flags.
/**
* Type of connection.
*/
@VintfStability
@Backing(type="byte")
enum Type {
/**
* After track process.
*/
INSERT = 0,
/**
* Connect to track auxiliary output and use send level.
*/
AUXILIARY = 1,
/**
* Rreplaces track process function; must implement SRC, volume and mono to stereo.
*/
REPLACE = 2,
/**
* Applied below audio HAL on in.
*/
PRE_PROC = 3,
/**
* Applied below audio HAL on out.
*/
POST_PROC = 4,
}
Type type = Type.INSERT;
/**
* Insertion preference.
*/
@VintfStability
@Backing(type="byte")
enum Insert {
ANY = 0,
/**
* First of the chain.
*/
FIRST = 1,
/**
* Last of the chain.
*/
LAST = 2,
/**
* Exclusive (only effect in the insert chain.
*/
EXCLUSIVE = 3,
}
Insert insert = Insert.ANY;
@VintfStability
@Backing(type="byte")
enum Volume {
NONE = 0,
/**
* Implements volume control.
*/
CTRL = 1,
/**
* Requires volume indication.
*/
IND = 2,
/**
* Monitors requested volume.
*/
MONITOR = 3,
}
Volume volume = Volume.NONE;
@VintfStability
@Backing(type="byte")
enum HardwareAccelerator {
/**
* No hardware acceleration
*/
NONE = 0,
/**
* Non tunneled hw acceleration: effect reads the samples, send them to HW accelerated
* effect processor, reads back the processed samples and returns them to the output buffer.
*/
SIMPLE = 1,
/**
* The effect interface is only used to control the effect engine. This mode is relevant for
* global effects actually applied by the audio hardware on the output stream.
*/
TUNNEL = 2,
}
HardwareAccelerator hwAcceleratorMode = HardwareAccelerator.NONE;
/**
* Effect instance set this flag to true if it requires update on if the playback thread the
* effect attached to is offloaded or not. In this case the framework must call
* IEffect.setParameter(Parameter.offload) to notify effect instance when playback thread
* offload changes.
*/
boolean offloadIndication;
/**
* Effect instance set this flag to true if it requires device change update. In this case the
* framework must call IEffect.setParameter(Parameter.device) to notify effect instance when the
* device changes.
*/
boolean deviceIndication;
/**
* Effect instance set this flag to true if it requires audio mode change update. In this case
* the framework must call IEffect.setParameter(Parameter.mode) to notify effect instance when
* the audio mode changes.
*/
boolean audioModeIndication;
/**
* Effect instance set this flag to true if it requires audio source change update. In this case
* the framework must call IEffect.setParameter(Parameter.source) to notify effect instance when
* the audio source changes.
*/
boolean audioSourceIndication;
/**
* Set to true if no processing done for this effect instance.
*/
boolean noProcessing;
}

View File

@@ -53,14 +53,22 @@ interface IEffect {
int fmqByteProduced;
}
// Return data structure of IEffect.open() interface.
/**
* Return data structure of IEffect.open() interface.
*/
@VintfStability
parcelable OpenEffectReturn {
// Message queue for effect processing status.
/**
* Message queue for effect processing status.
*/
MQDescriptor<Status, SynchronizedReadWrite> statusMQ;
// Message queue for input data buffer.
/**
* Message queue for input data buffer.
*/
MQDescriptor<byte, SynchronizedReadWrite> inputDataMQ;
// Message queue for output data buffer.
/**
* Message queue for output data buffer.
*/
MQDescriptor<byte, SynchronizedReadWrite> outputDataMQ;
}

View File

@@ -50,7 +50,7 @@ interface IFactory {
* An effect can exist more than once in the returned list, which means this effect must be used
* in more than one processing type.
*
* @param type Type of processing to query, can be AudioStreamType or AudioSource.
* @param type Type of processing to query, can be AudioStreamType, AudioSource, or null.
* @return list of processing defined with the optional filter by Processing.Type.
*/
Processing[] queryProcessing(in @nullable Processing.Type type);

View File

@@ -18,7 +18,10 @@ package android.hardware.audio.effect;
import android.hardware.audio.effect.Equalizer;
import android.media.audio.common.AudioConfig;
import android.media.audio.common.AudioDeviceDescription;
import android.media.audio.common.AudioDeviceType;
import android.media.audio.common.AudioMode;
import android.media.audio.common.AudioSource;
/**
* Defines all parameters supported by the effect instance.
*
@@ -40,12 +43,18 @@ union Parameter {
*/
@VintfStability
union Id {
// Common parameter tag.
/**
* Common parameter tag.
*/
int commonTag;
// Vendor defined parameter tag.
/**
* Vendor defined parameter tag.
*/
int vendorTag;
// Specific effect parameter tag.
Specific.Tag specificTag;
/**
* Specific effect parameter tag.
*/
Specific.Id specificId;
}
/**
@@ -53,19 +62,61 @@ union Parameter {
*/
@VintfStability
parcelable Common {
// Type of Audio device.
/**
* Type of Audio device.
*/
int session;
// I/O Handle.
/**
* I/O Handle.
*/
int ioHandle;
// Type of Audio device.
AudioDeviceDescription device;
// Input config.
/**
* Input config.
*/
AudioConfig input;
// Output config.
/**
* Output config.
*/
AudioConfig output;
}
Common common;
/**
* Used by audio framework to set the device type to effect engine.
* Effect must implement setParameter(device) if Flags.deviceIndication set to true.
*/
AudioDeviceType device;
/**
* Used by audio framework to set the audio mode to effect engine.
* Effect must implement setParameter(mode) if Flags.audioModeIndication set to true.
*/
AudioMode mode;
/**
* Used by audio framework to set the audio source to effect engine.
* Effect must implement setParameter(source) if Flags.audioSourceIndication set to true.
*/
AudioSource source;
/**
* The volume gain for left and right channel, left and right equals to same value if it's mono.
*/
@VintfStability
parcelable Volume {
float left;
float right;
}
/**
* Used by audio framework to delegate volume control to effect engine.
* Effect must implement setParameter(volume) if Flags.volume set to Volume.IND.
*/
Volume volume;
/**
* Used by audio framework to delegate offload information to effect engine.
* Effect must implement setParameter(offload) if Flags.offloadSupported set to true.
*/
boolean offload;
/**
* Parameters for vendor extension effect implementation usage.
*/
@@ -80,8 +131,16 @@ union Parameter {
*/
@VintfStability
union Specific {
@VintfStability
union Id {
/**
* Equalizer.Tag to identify the parameters in Equalizer.
*/
Equalizer.Tag equalizerTag = Equalizer.Tag.vendor;
}
Id id;
Equalizer equalizer;
// TODO: add other effect definitions here
}
Specific specific;
}

View File

@@ -116,6 +116,6 @@ cc_binary {
cc_library_headers {
name: "libaudioaidl_headers",
export_include_dirs: ["include"],
vendor: true,
vendor_available: true,
host_supported: true,
}

View File

@@ -28,11 +28,11 @@ EffectThread::EffectThread() {
}
EffectThread::~EffectThread() {
destroy();
destroyThread();
LOG(DEBUG) << __func__ << " done";
};
RetCode EffectThread::create(const std::string& name, const int priority) {
RetCode EffectThread::createThread(const std::string& name, const int priority) {
if (mThread.joinable()) {
LOG(WARNING) << __func__ << " thread already created, no-op";
return RetCode::SUCCESS;
@@ -44,7 +44,7 @@ RetCode EffectThread::create(const std::string& name, const int priority) {
return RetCode::SUCCESS;
}
RetCode EffectThread::destroy() {
RetCode EffectThread::destroyThread() {
{
std::lock_guard lg(mMutex);
mStop = mExit = true;
@@ -58,10 +58,10 @@ RetCode EffectThread::destroy() {
return RetCode::SUCCESS;
}
RetCode EffectThread::start() {
RetCode EffectThread::startThread() {
if (!mThread.joinable()) {
LOG(ERROR) << __func__ << " thread already destroyed";
return RetCode::ERROR;
return RetCode::ERROR_THREAD;
}
{
@@ -78,10 +78,10 @@ RetCode EffectThread::start() {
return RetCode::SUCCESS;
}
RetCode EffectThread::stop() {
RetCode EffectThread::stopThread() {
if (!mThread.joinable()) {
LOG(ERROR) << __func__ << " thread already destroyed";
return RetCode::ERROR;
return RetCode::ERROR_THREAD;
}
{
@@ -117,15 +117,4 @@ void EffectThread::threadLoop() {
}
}
std::string toString(RetCode& code) {
switch (code) {
case RetCode::SUCCESS:
return "SUCCESS";
case RetCode::ERROR:
return "ERROR";
default:
return "EnumError";
}
}
} // namespace aidl::android::hardware::audio::effect

View File

@@ -16,10 +16,12 @@
#define LOG_TAG "AHAL_EqualizerSw"
#include <Utils.h>
#include <android-base/logging.h>
#include <algorithm>
#include <unordered_set>
#include "effect-impl/EffectUUID.h"
#include <android-base/logging.h>
#include <fmq/AidlMessageQueue.h>
#include "equalizer-impl/EqualizerSw.h"
using android::hardware::audio::common::getFrameSizeInBytes;
@@ -68,20 +70,26 @@ ndk::ScopedAStatus EqualizerSw::open(const Parameter::Common& common,
auto& output = common.output;
size_t inputFrameSize = getFrameSizeInBytes(input.base.format, input.base.channelMask);
size_t outputFrameSize = getFrameSizeInBytes(output.base.format, output.base.channelMask);
if (!createFmq(1, input.frameCount * inputFrameSize, output.frameCount * outputFrameSize,
_aidl_return)) {
mContext = std::make_shared<EqualizerSwContext>(1, input.frameCount * inputFrameSize,
output.frameCount * outputFrameSize);
if (!mContext) {
LOG(ERROR) << __func__ << " created EqualizerSwContext failed";
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_UNSUPPORTED_OPERATION,
"FailedToCreateFmq");
}
setContext(mContext);
// create the worker thread
if (RetCode::SUCCESS != mWorker->create(LOG_TAG)) {
if (RetCode::SUCCESS != createThread(LOG_TAG)) {
LOG(ERROR) << __func__ << " created worker thread failed";
destroyFmq();
mContext.reset();
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_UNSUPPORTED_OPERATION,
"FailedToCreateFmq");
"FailedToCreateWorker");
}
_aidl_return->statusMQ = mContext->getStatusFmq()->dupeDesc();
_aidl_return->inputDataMQ = mContext->getInputDataFmq()->dupeDesc();
_aidl_return->outputDataMQ = mContext->getOutputDataFmq()->dupeDesc();
mState = State::IDLE;
return ndk::ScopedAStatus::ok();
}
@@ -98,8 +106,9 @@ ndk::ScopedAStatus EqualizerSw::close() {
// stop the worker thread
mState = State::INIT;
mWorker->destroy();
destroyFmq();
destroyThread();
mContext.reset();
LOG(DEBUG) << __func__;
return ndk::ScopedAStatus::ok();
}
@@ -121,19 +130,19 @@ ndk::ScopedAStatus EqualizerSw::command(CommandId in_commandId) {
case CommandId::START:
// start processing.
mState = State::PROCESSING;
mWorker->start();
startThread();
LOG(DEBUG) << __func__ << " state: " << toString(mState);
return ndk::ScopedAStatus::ok();
case CommandId::STOP:
// stop processing.
mState = State::IDLE;
mWorker->stop();
stopThread();
LOG(DEBUG) << __func__ << " state: " << toString(mState);
return ndk::ScopedAStatus::ok();
case CommandId::RESET:
// TODO: reset buffer status.
mState = State::IDLE;
mWorker->stop();
stopThread();
LOG(DEBUG) << __func__ << " state: " << toString(mState);
return ndk::ScopedAStatus::ok();
default:
@@ -173,23 +182,27 @@ ndk::ScopedAStatus EqualizerSw::getParameter(const Parameter::Id& in_paramId,
LOG(DEBUG) << __func__ << " get: " << _aidl_return->toString();
return ndk::ScopedAStatus::ok();
}
case Parameter::Id::specificTag: {
auto& id = in_paramId.get<Parameter::Id::specificTag>();
if (id != Parameter::Specific::equalizer) {
LOG(ERROR) << " unsupported parameter Id: " << in_paramId.toString();
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
EX_ILLEGAL_ARGUMENT, "Parameter::IdNotSupported");
}
case Parameter::Id::specificId: {
auto& id = in_paramId.get<Parameter::Id::specificId>();
Parameter::Specific specific;
specific.set<Parameter::Specific::equalizer>(mEqualizerParam);
ndk::ScopedAStatus status = getSpecificParameter(id, &specific);
if (!status.isOk()) {
LOG(ERROR) << __func__
<< " getSpecificParameter error: " << status.getDescription();
return status;
}
_aidl_return->set<Parameter::specific>(specific);
LOG(DEBUG) << __func__ << _aidl_return->toString();
return ndk::ScopedAStatus::ok();
}
default:
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"Parameter::IdNotSupported");
case Parameter::Id::vendorTag: {
LOG(DEBUG) << __func__ << " noop for vendor tag now";
return ndk::ScopedAStatus::ok();
}
}
LOG(ERROR) << " unsupported tag: " << toString(tag);
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"Parameter:IdNotSupported");
}
ndk::ScopedAStatus EqualizerSw::getState(State* _aidl_return) {
@@ -198,35 +211,12 @@ ndk::ScopedAStatus EqualizerSw::getState(State* _aidl_return) {
}
/// Private methods.
bool EqualizerSw::createFmq(int statusDepth, int inBufferSize, int outBufferSize,
OpenEffectReturn* ret) {
mStatusMQ = std::make_unique<StatusMQ>(statusDepth, true /*configureEventFlagWord*/);
mInputMQ = std::make_unique<DataMQ>(inBufferSize);
mOutputMQ = std::make_unique<DataMQ>(outBufferSize);
if (!mStatusMQ->isValid() || !mInputMQ->isValid() || !mOutputMQ->isValid()) {
LOG(ERROR) << __func__ << " created invalid FMQ";
return false;
}
ret->statusMQ = mStatusMQ->dupeDesc();
ret->inputDataMQ = mInputMQ->dupeDesc();
ret->outputDataMQ = mOutputMQ->dupeDesc();
return true;
}
void EqualizerSw::destroyFmq() {
mStatusMQ.reset(nullptr);
mInputMQ.reset(nullptr);
mOutputMQ.reset(nullptr);
}
ndk::ScopedAStatus EqualizerSw::setCommonParameter(const Parameter::Common& common) {
mCommonParam = common;
LOG(DEBUG) << __func__ << " set: " << mCommonParam.toString();
return ndk::ScopedAStatus::ok();
}
// TODO: implementation need change to save all parameters.
ndk::ScopedAStatus EqualizerSw::setSpecificParameter(const Parameter::Specific& specific) {
if (Parameter::Specific::equalizer != specific.getTag()) {
LOG(ERROR) << " unsupported effect: " << specific.toString();
@@ -234,9 +224,76 @@ ndk::ScopedAStatus EqualizerSw::setSpecificParameter(const Parameter::Specific&
"EffectNotSupported");
}
mEqualizerParam = specific.get<Parameter::Specific::equalizer>();
LOG(DEBUG) << __func__ << mEqualizerParam.toString();
return ndk::ScopedAStatus::ok();
auto& eqParam = specific.get<Parameter::Specific::equalizer>();
auto tag = eqParam.getTag();
switch (tag) {
case Equalizer::bandLevels: {
auto& bandLevels = eqParam.get<Equalizer::bandLevels>();
const auto& [minItem, maxItem] = std::minmax_element(
bandLevels.begin(), bandLevels.end(),
[](const auto& a, const auto& b) { return a.index < b.index; });
if (bandLevels.size() >= NUM_OF_BANDS || minItem->index < 0 ||
maxItem->index >= NUM_OF_BANDS) {
LOG(ERROR) << " bandLevels " << bandLevels.size() << "minIndex " << minItem->index
<< "maxIndex " << maxItem->index << " illegal ";
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"ExceedMaxBandNum");
}
mBandLevels = bandLevels;
return ndk::ScopedAStatus::ok();
}
case Equalizer::preset: {
int preset = eqParam.get<Equalizer::preset>();
if (preset < 0 || preset >= NUM_OF_PRESETS) {
LOG(ERROR) << " preset: " << preset << " invalid";
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"ExceedMaxBandNum");
}
mPreset = preset;
LOG(DEBUG) << __func__ << " preset set to " << mPreset;
return ndk::ScopedAStatus::ok();
}
case Equalizer::vendor: {
LOG(DEBUG) << __func__ << " noop for vendor tag now";
return ndk::ScopedAStatus::ok();
}
}
LOG(ERROR) << __func__ << " unsupported eq param tag: " << toString(tag);
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"ParamNotSupported");
}
ndk::ScopedAStatus EqualizerSw::getSpecificParameter(Parameter::Specific::Id id,
Parameter::Specific* specific) {
Equalizer eqParam;
auto tag = id.getTag();
if (tag != Parameter::Specific::Id::equalizerTag) {
LOG(ERROR) << " invalid tag: " << toString(tag);
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"UnsupportedTag");
}
auto eqTag = id.get<Parameter::Specific::Id::equalizerTag>();
switch (eqTag) {
case Equalizer::bandLevels: {
eqParam.set<Equalizer::bandLevels>(mBandLevels);
specific->set<Parameter::Specific::equalizer>(eqParam);
return ndk::ScopedAStatus::ok();
}
case Equalizer::preset: {
eqParam.set<Equalizer::preset>(mPreset);
LOG(DEBUG) << __func__ << " preset " << mPreset;
specific->set<Parameter::Specific::equalizer>(eqParam);
return ndk::ScopedAStatus::ok();
}
case Equalizer::vendor: {
LOG(DEBUG) << __func__ << " noop for vendor tag now";
return ndk::ScopedAStatus::ok();
}
}
LOG(ERROR) << __func__ << " unsupported eq param: " << toString(eqTag);
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"ParamNotSupported");
}
void EqualizerSw::cleanUp() {
@@ -248,9 +305,18 @@ void EqualizerSw::cleanUp() {
}
}
// Processing method running in worker thread.
void EqualizerSwWorker::process() {
// TODO: add EQ processing with FMQ, should wait until data available before data processing.
IEffect::Status EqualizerSw::status(binder_status_t status, size_t consumed, size_t produced) {
IEffect::Status ret;
ret.status = status;
ret.fmqByteConsumed = consumed;
ret.fmqByteProduced = produced;
return ret;
}
// Processing method running in EffectWorker thread.
IEffect::Status EqualizerSw::effectProcessImpl() {
// TODO: get data buffer and process.
return status(STATUS_OK, mContext->availableToRead(), mContext->availableToWrite());
}
} // namespace aidl::android::hardware::audio::effect

View File

@@ -0,0 +1,65 @@
/*
* 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.
*/
#pragma once
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
#include <aidl/android/hardware/audio/effect/BnEffect.h>
#include <fmq/AidlMessageQueue.h>
namespace aidl::android::hardware::audio::effect {
class EffectContext {
public:
typedef ::android::AidlMessageQueue<
IEffect::Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
StatusMQ;
typedef ::android::AidlMessageQueue<
int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
DataMQ;
EffectContext(size_t statusDepth, size_t inBufferSize, size_t outBufferSize) {
mStatusMQ = std::make_shared<StatusMQ>(statusDepth, true /*configureEventFlagWord*/);
mInputMQ = std::make_shared<DataMQ>(inBufferSize);
mOutputMQ = std::make_shared<DataMQ>(outBufferSize);
if (!mStatusMQ->isValid() || !mInputMQ->isValid() || !mOutputMQ->isValid()) {
LOG(ERROR) << __func__ << " created invalid FMQ";
}
mWorkBuffer.reserve(std::max(inBufferSize, outBufferSize));
};
std::shared_ptr<StatusMQ> getStatusFmq() { return mStatusMQ; };
std::shared_ptr<DataMQ> getInputDataFmq() { return mInputMQ; };
std::shared_ptr<DataMQ> getOutputDataFmq() { return mOutputMQ; };
int8_t* getWorkBuffer() { return static_cast<int8_t*>(mWorkBuffer.data()); };
// TODO: update with actual available size
size_t availableToRead() { return mWorkBuffer.capacity(); };
size_t availableToWrite() { return mWorkBuffer.capacity(); };
private:
std::shared_ptr<StatusMQ> mStatusMQ;
std::shared_ptr<DataMQ> mInputMQ;
std::shared_ptr<DataMQ> mOutputMQ;
// TODO handle effect process input and output
// work buffer set by effect instances, the access and update are in same thread
std::vector<int8_t> mWorkBuffer;
};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -22,12 +22,10 @@
#include <android-base/thread_annotations.h>
#include <system/thread_defs.h>
#include "effect-impl/EffectTypes.h"
namespace aidl::android::hardware::audio::effect {
enum class RetCode { SUCCESS, ERROR };
std::string toString(RetCode& code);
class EffectThread {
public:
// default priority is same as HIDL: ANDROID_PRIORITY_URGENT_AUDIO
@@ -35,10 +33,11 @@ class EffectThread {
virtual ~EffectThread();
// called by effect implementation.
RetCode create(const std::string& name, const int priority = ANDROID_PRIORITY_URGENT_AUDIO);
RetCode destroy();
RetCode start();
RetCode stop();
RetCode createThread(const std::string& name,
const int priority = ANDROID_PRIORITY_URGENT_AUDIO);
RetCode destroyThread();
RetCode startThread();
RetCode stopThread();
// Will call process() in a loop if the thread is running.
void threadLoop();

View File

@@ -0,0 +1,51 @@
/*
* 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.
*/
#pragma once
#include <ostream>
#include <string>
namespace aidl::android::hardware::audio::effect {
enum class RetCode {
SUCCESS,
ERROR_ILLEGAL_PARAMETER, /* Illegal parameter */
ERROR_THREAD, /* Effect thread error */
ERROR_NULL_POINTER, /* NULL pointer */
ERROR_ALIGNMENT_ERROR, /* Memory alignment error */
ERROR_BLOCK_SIZE_EXCEED /* Maximum block size exceeded */
};
inline std::ostream& operator<<(std::ostream& out, const RetCode& code) {
switch (code) {
case RetCode::SUCCESS:
return out << "SUCCESS";
case RetCode::ERROR_ILLEGAL_PARAMETER:
return out << "ERROR_ILLEGAL_PARAMETER";
case RetCode::ERROR_THREAD:
return out << "ERROR_THREAD";
case RetCode::ERROR_NULL_POINTER:
return out << "ERROR_NULL_POINTER";
case RetCode::ERROR_ALIGNMENT_ERROR:
return out << "ERROR_ALIGNMENT_ERROR";
case RetCode::ERROR_BLOCK_SIZE_EXCEED:
return out << "ERROR_BLOCK_SIZE_EXCEED";
}
return out << "EnumError: " << code;
}
} // namespace aidl::android::hardware::audio::effect

View File

@@ -21,6 +21,17 @@ namespace aidl::android::hardware::audio::effect {
using ::aidl::android::media::audio::common::AudioUuid;
// Null UUID
static const AudioUuid EffectNullUuid = {static_cast<int32_t>(0xec7178ec),
0xe5e1,
0x4432,
0xa3f4,
{0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}};
// Zero UUID
static const AudioUuid EffectZeroUuid = {
static_cast<int32_t>(0x0), 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
// Equalizer type UUID.
static const AudioUuid EqualizerTypeUUID = {static_cast<int32_t>(0x0bed4300),
0xddd6,

View File

@@ -0,0 +1,74 @@
/*
* 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.
*/
#pragma once
#include <algorithm>
#include <memory>
#include <mutex>
#include <string>
#include "EffectContext.h"
#include "EffectThread.h"
namespace aidl::android::hardware::audio::effect {
std::string toString(RetCode& code);
class EffectWorker : public EffectThread {
public:
// set effect context for worker, suppose to only happen once here
void setContext(std::shared_ptr<EffectContext> context) {
std::call_once(mOnceFlag, [&]() { mContext = context; });
};
// handle FMQ and call effect implemented virtual function
void process() override {
if (!mContext) {
LOG(ERROR) << __func__ << " invalid context!";
return;
}
std::shared_ptr<EffectContext::StatusMQ> statusMQ = mContext->getStatusFmq();
std::shared_ptr<EffectContext::DataMQ> inputMQ = mContext->getInputDataFmq();
std::shared_ptr<EffectContext::DataMQ> outputMQ = mContext->getOutputDataFmq();
// Only this worker will read from input data MQ and write to output data MQ.
auto readSize = inputMQ->availableToRead(), writeSize = outputMQ->availableToWrite();
if (readSize && writeSize) {
LOG(DEBUG) << __func__ << " available to read " << readSize << " available to write "
<< writeSize;
auto buffer = mContext->getWorkBuffer();
inputMQ->read(buffer, readSize);
IEffect::Status status = effectProcessImpl();
writeSize = std::min((int32_t)writeSize, status.fmqByteProduced);
outputMQ->write(buffer, writeSize);
statusMQ->writeBlocking(&status, 1);
LOG(DEBUG) << __func__ << " done processing, effect consumed " << status.fmqByteConsumed
<< " produced " << status.fmqByteProduced;
} else {
// TODO: maybe add some sleep here to avoid busy waiting
}
}
// must implement by each effect implementation
virtual IEffect::Status effectProcessImpl() = 0;
private:
// make sure the context only set once.
std::once_flag mOnceFlag;
std::shared_ptr<EffectContext> mContext;
};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -21,20 +21,29 @@
#include <cstdlib>
#include <memory>
#include "effect-impl/EffectThread.h"
#include "effect-impl/EffectContext.h"
#include "effect-impl/EffectTypes.h"
#include "effect-impl/EffectUUID.h"
#include "effect-impl/EffectWorker.h"
namespace aidl::android::hardware::audio::effect {
class EqualizerSwWorker : public EffectThread {
// EqualizerSwWorker(const std::string name){EffectThread(name)};
void process() override;
class EqualizerSwContext : public EffectContext {
public:
EqualizerSwContext(int statusDepth, int inBufferSize, int outBufferSize)
: EffectContext(statusDepth, inBufferSize, outBufferSize) {
LOG(DEBUG) << __func__;
}
private:
// Add equalizer specific context for processing here
};
class EqualizerSw : public BnEffect {
class EqualizerSw : public BnEffect, EffectWorker {
public:
EqualizerSw() {
// create the worker
mWorker = std::make_unique<EqualizerSwWorker>();
Equalizer::Capability eqCap = {.bandFrequencies = mBandFrequency, .presets = mPresets};
mDesc.capability.set<Capability::equalizer>(eqCap);
LOG(DEBUG) << __func__;
};
~EqualizerSw() {
@@ -52,12 +61,11 @@ class EqualizerSw : public BnEffect {
ndk::ScopedAStatus getParameter(const Parameter::Id& in_paramId,
Parameter* _aidl_return) override;
IEffect::Status effectProcessImpl() override;
private:
// effect processing thread.
std::unique_ptr<EqualizerSwWorker> mWorker;
// Effect descriptor.
const Descriptor mDesc = {
.common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}};
Descriptor mDesc = {.common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}};
// Parameters.
Parameter::Common mCommonParam;
@@ -66,21 +74,32 @@ class EqualizerSw : public BnEffect {
// Instance state INIT by default.
State mState = State::INIT;
typedef ::android::AidlMessageQueue<
Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
StatusMQ;
typedef ::android::AidlMessageQueue<
int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
DataMQ;
int mPreset = PRESET_CUSTOM; // the current preset
const std::vector<Equalizer::BandFrequency> mBandFrequency = {{0, 30000, 120000},
{1, 120001, 460000},
{2, 460001, 1800000},
{3, 1800001, 7000000},
{4, 7000001, 20000000}};
// preset band level
std::vector<Equalizer::BandLevel> mBandLevels = {{0, 3}, {1, 0}, {2, 0}, {3, 0}, {4, 3}};
// presets supported by the device
const std::vector<Equalizer::Preset> mPresets = {
{0, "Normal"}, {1, "Classical"}, {2, "Dance"}, {3, "Flat"}, {4, "Folk"},
{5, "Heavy Metal"}, {6, "Hip Hop"}, {7, "Jazz"}, {8, "Pop"}, {9, "Rock"}};
static const int NUM_OF_BANDS = 5;
static const int NUM_OF_PRESETS = 10;
static const int PRESET_CUSTOM = -1;
std::unique_ptr<StatusMQ> mStatusMQ;
std::unique_ptr<DataMQ> mInputMQ;
std::unique_ptr<DataMQ> mOutputMQ;
// Equalizer worker context
std::shared_ptr<EqualizerSwContext> mContext;
ndk::ScopedAStatus setCommonParameter(const Parameter::Common& common_param);
ndk::ScopedAStatus setSpecificParameter(const Parameter::Specific& specific);
bool createFmq(int statusDepth, int inBufferSize, int outBufferSize, OpenEffectReturn* ret);
void destroyFmq();
ndk::ScopedAStatus getSpecificParameter(Parameter::Specific::Id id,
Parameter::Specific* specific);
void cleanUp();
IEffect::Status status(binder_status_t status, size_t consumed, size_t produced);
};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -60,6 +60,7 @@ cc_test {
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
],
header_libs: ["libaudioaidl_headers"],
cflags: [
"-Wall",
"-Wextra",
@@ -75,6 +76,7 @@ cc_test {
cc_test {
name: "VtsHalAudioEffectTargetTest",
defaults: [
"latest_android_hardware_audio_common_ndk_static",
"latest_android_media_audio_common_types_ndk_static",
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
@@ -84,12 +86,49 @@ cc_test {
],
shared_libs: [
"libbinder_ndk",
"libfmq",
],
static_libs: [
"android.hardware.audio.effect-V1-ndk",
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
"libaudioaidlcommon",
],
header_libs: ["libaudioaidl_headers"],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
"-Wthread-safety",
],
test_suites: [
"general-tests",
"vts",
],
}
cc_test {
name: "VtsHalEqualizerTargetTest",
defaults: [
"latest_android_hardware_audio_common_ndk_static",
"latest_android_media_audio_common_types_ndk_static",
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
],
srcs: [
"VtsHalEqualizerTargetTest.cpp",
],
shared_libs: [
"libbinder_ndk",
"libfmq",
],
static_libs: [
"android.hardware.audio.effect-V1-ndk",
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
"libaudioaidlcommon",
],
header_libs: ["libaudioaidl_headers"],
cflags: [
"-Wall",
"-Wextra",

View File

@@ -24,13 +24,14 @@
#include <android/binder_auto_utils.h>
#include "TestUtils.h"
#include "effect-impl/EffectUUID.h"
using namespace android;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::EffectNullUuid;
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
using aidl::android::hardware::audio::effect::Processing;
using aidl::android::media::audio::common::AudioUuid;
@@ -69,7 +70,6 @@ class EffectFactoryHelper {
}
void CreateEffects() {
ASSERT_NE(mEffectFactory, nullptr);
for (const auto& id : mIds) {
std::shared_ptr<IEffect> effect;
EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
@@ -80,6 +80,26 @@ class EffectFactoryHelper {
}
}
void QueryAndCreateEffects(const AudioUuid& type = EffectNullUuid) {
std::vector<Descriptor::Identity> ids;
ASSERT_NE(mEffectFactory, nullptr);
if (type == EffectNullUuid) {
EXPECT_IS_OK(mEffectFactory->queryEffects(std::nullopt, std::nullopt, &ids));
} else {
EXPECT_IS_OK(mEffectFactory->queryEffects(type, std::nullopt, &ids));
}
for (const auto& id : ids) {
ASSERT_EQ(id.type, type);
std::shared_ptr<IEffect> effect;
EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
EXPECT_NE(effect, nullptr) << id.toString();
if (effect) {
mEffectIdMap[effect] = id;
}
}
}
void CreateEffectsAndExpect(
const std::vector<std::pair<Descriptor::Identity, binder_exception_t>>& uuid_status) {
ASSERT_NE(mEffectFactory, nullptr);
@@ -126,8 +146,10 @@ class EffectFactoryHelper {
std::shared_ptr<IFactory> GetFactory() { return mEffectFactory; }
const std::vector<Descriptor::Identity>& GetEffectIds() { return mIds; }
const std::vector<Descriptor::Identity>& GetCompleteEffectIdList() { return mCompleteIds; }
const std::map<std::shared_ptr<IEffect>, Descriptor::Identity>& GetEffectMap() {
const std::vector<Descriptor::Identity>& GetCompleteEffectIdList() const {
return mCompleteIds;
}
const std::map<std::shared_ptr<IEffect>, Descriptor::Identity>& GetEffectMap() const {
return mEffectIdMap;
}
void ClearEffectMap() { mEffectIdMap.clear(); }

View File

@@ -0,0 +1,310 @@
/*
* 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.
*/
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <aidl/android/hardware/audio/effect/IEffect.h>
#include <aidl/android/hardware/audio/effect/IFactory.h>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include <aidl/android/media/audio/common/AudioDeviceType.h>
#include <android/binder_auto_utils.h>
#include <fmq/AidlMessageQueue.h>
#include "AudioHalBinderServiceUtil.h"
#include "EffectFactoryHelper.h"
#include "TestUtils.h"
using namespace android;
using aidl::android::hardware::audio::effect::CommandId;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::EffectNullUuid;
using aidl::android::hardware::audio::effect::EffectZeroUuid;
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::effect::Parameter;
using aidl::android::hardware::audio::effect::State;
using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioUuid;
using aidl::android::media::audio::common::PcmType;
const AudioFormatDescription DefaultFormat = {
.type = AudioFormatType::PCM, .pcm = PcmType::INT_16_BIT, .encoding = ""};
class EffectHelper {
public:
explicit EffectHelper(const std::string& name) : mFactoryHelper(EffectFactoryHelper(name)) {
mFactoryHelper.ConnectToFactoryService();
}
void OpenEffects(const AudioUuid& type = EffectNullUuid) {
auto open = [&](const std::shared_ptr<IEffect>& effect) {
IEffect::OpenEffectReturn ret;
EXPECT_IS_OK(effect->open(mCommon, mSpecific, &ret));
EffectParam params;
params.statusMQ = std::make_unique<StatusMQ>(ret.statusMQ);
params.inputMQ = std::make_unique<DataMQ>(ret.inputDataMQ);
params.outputMQ = std::make_unique<DataMQ>(ret.outputDataMQ);
mEffectParams.push_back(std::move(params));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(open, type));
}
void CloseEffects(const binder_status_t status = EX_NONE) {
auto close = [&](const std::shared_ptr<IEffect>& effect) {
EXPECT_STATUS(status, effect->close());
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
}
void CreateEffects(const int n = 1) {
for (int i = 0; i < n; i++) {
ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateAllEffects());
}
}
void CreateEffectsWithUUID(const AudioUuid& type = EffectNullUuid) {
ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateEffects(type));
}
void QueryEffects() { ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateAllEffects()); }
void DestroyEffects(const binder_status_t status = EX_NONE, const int remaining = 0) {
ASSERT_NO_FATAL_FAILURE(mFactoryHelper.DestroyEffects(status, remaining));
mEffectDescriptors.clear();
}
void GetEffectDescriptors() {
auto get = [&](const std::shared_ptr<IEffect>& effect) {
Descriptor desc;
EXPECT_IS_OK(effect->getDescriptor(&desc));
mEffectDescriptors.push_back(std::move(desc));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
}
void CommandEffects(CommandId command) {
auto close = [&](const std::shared_ptr<IEffect>& effect) {
EXPECT_IS_OK(effect->command(command));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
}
void CommandEffectsExpectStatus(CommandId command, const binder_status_t status) {
auto func = [&](const std::shared_ptr<IEffect>& effect) {
EXPECT_STATUS(status, effect->command(command));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
}
void ExpectState(State expected) {
auto get = [&](const std::shared_ptr<IEffect>& effect) {
State state = State::INIT;
EXPECT_IS_OK(effect->getState(&state));
EXPECT_EQ(expected, state);
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
}
void SetParameter() {
auto func = [&](const std::shared_ptr<IEffect>& effect) {
Parameter param;
param.set<Parameter::common>(mCommon);
EXPECT_IS_OK(effect->setParameter(param));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
}
void VerifyParameters() {
auto func = [&](const std::shared_ptr<IEffect>& effect) {
Parameter paramCommonGet = Parameter(), paramCommonExpect = Parameter();
Parameter::Id id;
id.set<Parameter::Id::commonTag>(0);
paramCommonExpect.set<Parameter::common>(mCommon);
EXPECT_IS_OK(effect->getParameter(id, &paramCommonGet));
EXPECT_EQ(paramCommonExpect, paramCommonGet)
<< paramCommonExpect.toString() << " vs " << paramCommonGet.toString();
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
}
void QueryEffects(const std::optional<AudioUuid>& in_type,
const std::optional<AudioUuid>& in_instance,
std::vector<Descriptor::Identity>* _aidl_return) {
mFactoryHelper.QueryEffects(in_type, in_instance, _aidl_return);
}
template <typename Functor>
void ForEachEffect(Functor functor, const std::optional<AudioUuid>& type = EffectNullUuid) {
auto effectMap = mFactoryHelper.GetEffectMap();
for (const auto& it : effectMap) {
SCOPED_TRACE(it.second.toString());
if (type != EffectNullUuid && it.second.type != type) continue;
functor(it.first);
}
}
template <typename Functor>
void ForEachDescriptor(Functor functor) {
for (size_t i = 0; i < mEffectDescriptors.size(); i++) {
SCOPED_TRACE(mEffectDescriptors[i].toString());
functor(i, mEffectDescriptors[i]);
}
}
static const size_t mWriteMQSize = 0x400;
enum class IO : char { INPUT = 0, OUTPUT = 1, INOUT = 2 };
void initParamCommonFormat(IO io = IO::INOUT,
const AudioFormatDescription& format = DefaultFormat) {
if (io == IO::INPUT || io == IO::INOUT) {
mCommon.input.base.format = format;
}
if (io == IO::OUTPUT || io == IO::INOUT) {
mCommon.output.base.format = format;
}
}
void initParamCommonSampleRate(IO io = IO::INOUT, const int& sampleRate = 48000) {
if (io == IO::INPUT || io == IO::INOUT) {
mCommon.input.base.sampleRate = sampleRate;
}
if (io == IO::OUTPUT || io == IO::INOUT) {
mCommon.output.base.sampleRate = sampleRate;
}
}
void initParamCommonFrameCount(IO io = IO::INOUT, const long& frameCount = 48000) {
if (io == IO::INPUT || io == IO::INOUT) {
mCommon.input.frameCount = frameCount;
}
if (io == IO::OUTPUT || io == IO::INOUT) {
mCommon.output.frameCount = frameCount;
}
}
void initParamCommon(int session = -1, int ioHandle = -1, int iSampleRate = 48000,
int oSampleRate = 48000, long iFrameCount = 0x100,
long oFrameCount = 0x100) {
mCommon.session = session;
mCommon.ioHandle = ioHandle;
auto& input = mCommon.input;
auto& output = mCommon.output;
input.base.sampleRate = iSampleRate;
input.base.channelMask = mInputChannelLayout;
input.frameCount = iFrameCount;
output.base.sampleRate = oSampleRate;
output.base.channelMask = mOutputChannelLayout;
output.frameCount = oFrameCount;
inputFrameSize = android::hardware::audio::common::getFrameSizeInBytes(
input.base.format, input.base.channelMask);
outputFrameSize = android::hardware::audio::common::getFrameSizeInBytes(
output.base.format, output.base.channelMask);
}
void setSpecific(Parameter::Specific& specific) { mSpecific = specific; }
// usually this function only call once.
void PrepareInputData(size_t s = mWriteMQSize) {
size_t maxInputSize = s;
for (auto& it : mEffectParams) {
auto& mq = it.inputMQ;
EXPECT_NE(nullptr, mq);
EXPECT_TRUE(mq->isValid());
const size_t bytesToWrite = mq->availableToWrite();
EXPECT_EQ(inputFrameSize * mCommon.input.frameCount, bytesToWrite);
EXPECT_NE(0UL, bytesToWrite);
EXPECT_TRUE(s <= bytesToWrite);
maxInputSize = std::max(maxInputSize, bytesToWrite);
}
mInputBuffer.resize(maxInputSize);
std::fill(mInputBuffer.begin(), mInputBuffer.end(), 0x5a);
}
void writeToFmq(size_t s = mWriteMQSize) {
for (auto& it : mEffectParams) {
auto& mq = it.inputMQ;
EXPECT_NE(nullptr, mq);
const size_t bytesToWrite = mq->availableToWrite();
EXPECT_NE(0Ul, bytesToWrite);
EXPECT_TRUE(s <= bytesToWrite);
EXPECT_TRUE(mq->write(mInputBuffer.data(), s));
}
}
void readFromFmq(size_t expectSize = mWriteMQSize) {
for (auto& it : mEffectParams) {
IEffect::Status status{};
auto& statusMq = it.statusMQ;
EXPECT_NE(nullptr, statusMq);
EXPECT_TRUE(statusMq->readBlocking(&status, 1));
EXPECT_EQ(STATUS_OK, status.status);
EXPECT_EQ(expectSize, (unsigned)status.fmqByteProduced);
auto& outputMq = it.outputMQ;
EXPECT_NE(nullptr, outputMq);
EXPECT_EQ(expectSize, outputMq->availableToRead());
}
}
void setInputChannelLayout(AudioChannelLayout input) { mInputChannelLayout = input; }
void setOutputChannelLayout(AudioChannelLayout output) { mOutputChannelLayout = output; }
const std::vector<Descriptor::Identity>& GetCompleteEffectIdList() const {
return mFactoryHelper.GetCompleteEffectIdList();
}
const std::vector<Descriptor>& getDescriptorVec() const { return mEffectDescriptors; }
private:
EffectFactoryHelper mFactoryHelper;
AudioChannelLayout mInputChannelLayout =
AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
AudioChannelLayout::LAYOUT_STEREO);
AudioChannelLayout mOutputChannelLayout =
AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
AudioChannelLayout::LAYOUT_STEREO);
Parameter::Common mCommon;
Parameter::Specific mSpecific;
size_t inputFrameSize, outputFrameSize;
std::vector<int8_t> mInputBuffer; // reuse same buffer for all effects testing
typedef ::android::AidlMessageQueue<
IEffect::Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
StatusMQ;
typedef ::android::AidlMessageQueue<
int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
DataMQ;
class EffectParam {
public:
std::unique_ptr<StatusMQ> statusMQ;
std::unique_ptr<DataMQ> inputMQ;
std::unique_ptr<DataMQ> outputMQ;
};
std::vector<EffectParam> mEffectParams;
std::vector<Descriptor> mEffectDescriptors;
};

View File

@@ -37,6 +37,8 @@
using namespace android;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::EffectNullUuid;
using aidl::android::hardware::audio::effect::EffectZeroUuid;
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Processing;
using aidl::android::media::audio::common::AudioUuid;
@@ -50,17 +52,8 @@ class EffectFactoryTest : public testing::TestWithParam<std::string> {
EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam());
// TODO: these UUID can get from config file
// ec7178ec-e5e1-4432-a3f4-4657e6795210
const AudioUuid nullUuid = {static_cast<int32_t>(0xec7178ec),
0xe5e1,
0x4432,
0xa3f4,
{0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}};
const AudioUuid zeroUuid = {
static_cast<int32_t>(0x0), 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
const Descriptor::Identity nullDesc = {.uuid = nullUuid};
const Descriptor::Identity zeroDesc = {.uuid = zeroUuid};
const Descriptor::Identity nullDesc = {.uuid = EffectNullUuid};
const Descriptor::Identity zeroDesc = {.uuid = EffectZeroUuid};
};
TEST_P(EffectFactoryTest, SetupAndTearDown) {
@@ -82,20 +75,20 @@ TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) {
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
// TODO: Factory eventually need to return the full list of MUST supported AOSP effects.
for (auto& desc : descriptors) {
EXPECT_NE(desc.type, zeroUuid);
EXPECT_NE(desc.uuid, zeroUuid);
EXPECT_NE(desc.type, EffectNullUuid);
EXPECT_NE(desc.uuid, EffectNullUuid);
}
}
TEST_P(EffectFactoryTest, QueriedDescriptorNotExistType) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(nullUuid, std::nullopt, &descriptors);
mFactory.QueryEffects(EffectNullUuid, std::nullopt, &descriptors);
EXPECT_EQ(descriptors.size(), 0UL);
}
TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, nullUuid, &descriptors);
mFactory.QueryEffects(std::nullopt, EffectNullUuid, &descriptors);
EXPECT_EQ(descriptors.size(), 0UL);
}

View File

@@ -14,14 +14,14 @@
* limitations under the License.
*/
#define LOG_TAG "VtsHalAudioEffectTargetTest"
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#define LOG_TAG "VtsHalAudioEffect"
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <android-base/logging.h>
@@ -30,13 +30,13 @@
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <Utils.h>
#include <aidl/android/hardware/audio/effect/IEffect.h>
#include <aidl/android/hardware/audio/effect/IFactory.h>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include <aidl/android/media/audio/common/AudioDeviceType.h>
#include "AudioHalBinderServiceUtil.h"
#include "EffectFactoryHelper.h"
#include "EffectHelper.h"
#include "TestUtils.h"
using namespace android;
@@ -49,201 +49,72 @@ using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
using aidl::android::hardware::audio::effect::State;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioDeviceType;
class AudioEffect : public testing::TestWithParam<std::string> {
class AudioEffectTest : public testing::TestWithParam<std::string>, public EffectHelper {
public:
AudioEffectTest() : EffectHelper(GetParam()) {}
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(mFactoryHelper.ConnectToFactoryService());
CreateEffects();
initParamCommonFormat();
initParamCommon();
initParamSpecific();
// initParamSpecific();
}
void TearDown() override {
CloseEffects();
DestroyEffects();
}
void OpenEffects() {
auto open = [&](const std::shared_ptr<IEffect>& effect) {
IEffect::OpenEffectReturn ret;
EXPECT_IS_OK(effect->open(mCommon, mSpecific, &ret));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(open));
}
void CloseEffects(const binder_status_t status = EX_NONE) {
auto close = [&](const std::shared_ptr<IEffect>& effect) {
EXPECT_STATUS(status, effect->close());
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
}
void CreateEffects(const int n = 1) {
for (int i = 0; i < n; i++) {
ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateAllEffects());
}
}
void DestroyEffects(const binder_status_t status = EX_NONE, const int remaining = 0) {
ASSERT_NO_FATAL_FAILURE(mFactoryHelper.DestroyEffects(status, remaining));
}
void GetEffectDescriptors() {
auto get = [](const std::shared_ptr<IEffect>& effect) {
Descriptor desc;
EXPECT_IS_OK(effect->getDescriptor(&desc));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
}
void CommandEffects(CommandId command) {
auto close = [&](const std::shared_ptr<IEffect>& effect) {
EXPECT_IS_OK(effect->command(command));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
}
void CommandEffectsExpectStatus(CommandId command, const binder_status_t status) {
auto func = [&](const std::shared_ptr<IEffect>& effect) {
EXPECT_STATUS(status, effect->command(command));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
}
void ExpectState(State expected) {
auto get = [&](const std::shared_ptr<IEffect>& effect) {
State state = State::INIT;
EXPECT_IS_OK(effect->getState(&state));
EXPECT_EQ(expected, state);
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
}
void SetParameter() {
auto func = [&](const std::shared_ptr<IEffect>& effect) {
Parameter param;
param.set<Parameter::common>(mCommon);
EXPECT_IS_OK(effect->setParameter(param));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
}
void VerifyParameters() {
auto func = [&](const std::shared_ptr<IEffect>& effect) {
Parameter paramCommonGet = Parameter(), paramCommonExpect = Parameter();
Parameter::Id id;
id.set<Parameter::Id::commonTag>(0);
paramCommonExpect.set<Parameter::common>(mCommon);
EXPECT_IS_OK(effect->getParameter(id, &paramCommonGet));
EXPECT_EQ(paramCommonExpect, paramCommonGet)
<< paramCommonExpect.toString() << " vs " << paramCommonGet.toString();
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
}
template <typename Functor>
void ForEachEffect(Functor functor) {
auto effectMap = mFactoryHelper.GetEffectMap();
for (const auto& it : effectMap) {
SCOPED_TRACE(it.second.toString());
functor(it.first);
}
}
void initParamCommon(int session = -1, int ioHandle = -1,
AudioDeviceType deviceType = AudioDeviceType::NONE,
int iSampleRate = 48000, int oSampleRate = 48000, long iFrameCount = 0x100,
long oFrameCount = 0x100) {
mCommon.session = session;
mCommon.ioHandle = ioHandle;
mCommon.device.type = deviceType;
mCommon.input.base.sampleRate = iSampleRate;
mCommon.input.base.channelMask = mInputChannelLayout;
mCommon.input.frameCount = iFrameCount;
mCommon.output.base.sampleRate = oSampleRate;
mCommon.output.base.channelMask = mOutputChannelLayout;
mCommon.output.frameCount = oFrameCount;
}
void initParamSpecific(Parameter::Specific::Tag tag = Parameter::Specific::equalizer) {
switch (tag) {
case Parameter::Specific::equalizer:
mSpecific.set<Parameter::Specific::equalizer>();
break;
default:
return;
}
}
void setInputChannelLayout(AudioChannelLayout input) { mInputChannelLayout = input; }
void setOutputChannelLayout(AudioChannelLayout output) { mOutputChannelLayout = output; }
EffectFactoryHelper mFactoryHelper = EffectFactoryHelper(GetParam());
private:
AudioChannelLayout mInputChannelLayout =
AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
AudioChannelLayout::LAYOUT_STEREO);
AudioChannelLayout mOutputChannelLayout =
AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
AudioChannelLayout::LAYOUT_STEREO);
Parameter::Common mCommon;
Parameter::Specific mSpecific;
static IEffect::OpenEffectReturn mOpenReturn;
};
TEST_P(AudioEffect, OpenEffectTest) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
TEST_P(AudioEffectTest, OpenEffectTest) {
OpenEffects();
}
TEST_P(AudioEffect, OpenAndCloseEffect) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
TEST_P(AudioEffectTest, OpenAndCloseEffect) {
OpenEffects();
CloseEffects();
}
TEST_P(AudioEffect, CloseUnopenedEffectTest) {
EXPECT_NO_FATAL_FAILURE(CloseEffects());
TEST_P(AudioEffectTest, CloseUnopenedEffectTest) {
CloseEffects();
}
TEST_P(AudioEffect, DoubleOpenCloseEffects) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
TEST_P(AudioEffectTest, DoubleOpenCloseEffects) {
OpenEffects();
CloseEffects();
OpenEffects();
CloseEffects();
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
OpenEffects();
OpenEffects();
CloseEffects();
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
OpenEffects();
CloseEffects();
CloseEffects();
}
TEST_P(AudioEffect, GetDescriptors) {
EXPECT_NO_FATAL_FAILURE(GetEffectDescriptors());
TEST_P(AudioEffectTest, GetDescriptors) {
GetEffectDescriptors();
}
TEST_P(AudioEffect, DescriptorIdExistAndUnique) {
TEST_P(AudioEffectTest, DescriptorIdExistAndUnique) {
auto checker = [&](const std::shared_ptr<IEffect>& effect) {
Descriptor desc;
std::vector<Descriptor::Identity> idList;
EXPECT_IS_OK(effect->getDescriptor(&desc));
mFactoryHelper.QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList);
QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList);
EXPECT_EQ(idList.size(), 1UL);
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(checker));
ForEachEffect(checker);
// Check unique with a set
auto stringHash = [](const Descriptor::Identity& id) {
return std::hash<std::string>()(id.toString());
};
auto vec = mFactoryHelper.GetCompleteEffectIdList();
auto vec = GetCompleteEffectIdList();
std::unordered_set<Descriptor::Identity, decltype(stringHash)> idSet(0, stringHash);
for (auto it : vec) {
EXPECT_EQ(idSet.count(it), 0UL);
@@ -253,218 +124,235 @@ TEST_P(AudioEffect, DescriptorIdExistAndUnique) {
/// State testing.
// An effect instance is in INIT state by default after it was created.
TEST_P(AudioEffect, InitStateAfterCreation) {
TEST_P(AudioEffectTest, InitStateAfterCreation) {
ExpectState(State::INIT);
}
// An effect instance transfer to INIT state after it was open successfully with IEffect.open().
TEST_P(AudioEffect, IdleStateAfterOpen) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
TEST_P(AudioEffectTest, IdleStateAfterOpen) {
OpenEffects();
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
CloseEffects();
}
// An effect instance is in PROCESSING state after it receive an START command.
TEST_P(AudioEffect, ProcessingStateAfterStart) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
TEST_P(AudioEffectTest, ProcessingStateAfterStart) {
OpenEffects();
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
EXPECT_NO_FATAL_FAILURE(CloseEffects());
CommandEffects(CommandId::STOP);
CloseEffects();
}
// An effect instance transfer to IDLE state after Command.Id.STOP in PROCESSING state.
TEST_P(AudioEffect, IdleStateAfterStop) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
TEST_P(AudioEffectTest, IdleStateAfterStop) {
OpenEffects();
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
CommandEffects(CommandId::STOP);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
CloseEffects();
}
// An effect instance transfer to IDLE state after Command.Id.RESET in PROCESSING state.
TEST_P(AudioEffect, IdleStateAfterReset) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
TEST_P(AudioEffectTest, IdleStateAfterReset) {
OpenEffects();
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
CommandEffects(CommandId::RESET);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
CloseEffects();
}
// An effect instance transfer to INIT if instance receive a close() call.
TEST_P(AudioEffect, InitStateAfterClose) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
TEST_P(AudioEffectTest, InitStateAfterClose) {
OpenEffects();
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
CommandEffects(CommandId::STOP);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
CloseEffects();
ExpectState(State::INIT);
}
// An effect instance shouldn't accept any command before open.
TEST_P(AudioEffect, NoCommandAcceptedBeforeOpen) {
TEST_P(AudioEffectTest, NoCommandAcceptedBeforeOpen) {
ExpectState(State::INIT);
EXPECT_NO_FATAL_FAILURE(CommandEffectsExpectStatus(CommandId::START, EX_ILLEGAL_STATE));
EXPECT_NO_FATAL_FAILURE(CommandEffectsExpectStatus(CommandId::STOP, EX_ILLEGAL_STATE));
EXPECT_NO_FATAL_FAILURE(CommandEffectsExpectStatus(CommandId::RESET, EX_ILLEGAL_STATE));
CommandEffectsExpectStatus(CommandId::START, EX_ILLEGAL_STATE);
CommandEffectsExpectStatus(CommandId::STOP, EX_ILLEGAL_STATE);
CommandEffectsExpectStatus(CommandId::RESET, EX_ILLEGAL_STATE);
ExpectState(State::INIT);
}
// No-op when receive STOP command in IDLE state.
TEST_P(AudioEffect, StopCommandInIdleStateNoOp) {
TEST_P(AudioEffectTest, StopCommandInIdleStateNoOp) {
ExpectState(State::INIT);
EXPECT_NO_FATAL_FAILURE(OpenEffects());
OpenEffects();
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
CommandEffects(CommandId::STOP);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
CloseEffects();
}
// No-op when receive STOP command in IDLE state.
TEST_P(AudioEffect, ResetCommandInIdleStateNoOp) {
TEST_P(AudioEffectTest, ResetCommandInIdleStateNoOp) {
ExpectState(State::INIT);
EXPECT_NO_FATAL_FAILURE(OpenEffects());
OpenEffects();
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
CommandEffects(CommandId::RESET);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
CloseEffects();
}
// Repeat START and STOP command.
TEST_P(AudioEffect, RepeatStartAndStop) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
TEST_P(AudioEffectTest, RepeatStartAndStop) {
OpenEffects();
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
CommandEffects(CommandId::STOP);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
CommandEffects(CommandId::STOP);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
CloseEffects();
}
// Repeat START and RESET command.
TEST_P(AudioEffect, RepeatStartAndReset) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
TEST_P(AudioEffectTest, RepeatStartAndReset) {
OpenEffects();
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
CommandEffects(CommandId::RESET);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
CommandEffects(CommandId::RESET);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
CloseEffects();
}
// Repeat START and STOP command, try to close at PROCESSING state.
TEST_P(AudioEffect, CloseProcessingStateEffects) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
TEST_P(AudioEffectTest, CloseProcessingStateEffects) {
OpenEffects();
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
CommandEffects(CommandId::STOP);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CloseEffects(EX_ILLEGAL_STATE));
CloseEffects(EX_ILLEGAL_STATE);
// cleanup
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
CommandEffects(CommandId::STOP);
ExpectState(State::IDLE);
}
// Expect EX_ILLEGAL_STATE if the effect instance is not in a proper state to be destroyed.
TEST_P(AudioEffect, DestroyOpenEffects) {
TEST_P(AudioEffectTest, DestroyOpenEffects) {
// cleanup all effects.
EXPECT_NO_FATAL_FAILURE(CloseEffects());
ASSERT_NO_FATAL_FAILURE(DestroyEffects());
CloseEffects();
DestroyEffects();
// open effects, destroy without close, expect to get EX_ILLEGAL_STATE status.
EXPECT_NO_FATAL_FAILURE(CreateEffects());
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(DestroyEffects(EX_ILLEGAL_STATE, 1));
EXPECT_NO_FATAL_FAILURE(CloseEffects());
CreateEffects();
OpenEffects();
DestroyEffects(EX_ILLEGAL_STATE, 1);
CloseEffects();
}
/// Parameter testing.
// Verify parameters pass in open can be successfully get.
TEST_P(AudioEffect, VerifyParametersAfterOpen) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
TEST_P(AudioEffectTest, VerifyParametersAfterOpen) {
OpenEffects();
VerifyParameters();
CloseEffects();
}
// Verify parameters pass in set can be successfully get.
TEST_P(AudioEffect, SetAndGetParameter) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */,
44100 /* iSampleRate */, 44100 /* oSampleRate */);
EXPECT_NO_FATAL_FAILURE(SetParameter());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
TEST_P(AudioEffectTest, SetAndGetParameter) {
OpenEffects();
VerifyParameters();
initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */,
44100 /* oSampleRate */);
SetParameter();
VerifyParameters();
CloseEffects();
}
// Verify parameters pass in set can be successfully get.
TEST_P(AudioEffect, SetAndGetParameterInProcessing) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
TEST_P(AudioEffectTest, SetAndGetParameterInProcessing) {
OpenEffects();
VerifyParameters();
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */,
44100 /* iSampleRate */, 44100 /* oSampleRate */);
EXPECT_NO_FATAL_FAILURE(SetParameter());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */,
44100 /* oSampleRate */);
SetParameter();
VerifyParameters();
CommandEffects(CommandId::STOP);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
CloseEffects();
}
// Parameters kept after reset.
TEST_P(AudioEffect, ResetAndVerifyParameter) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
TEST_P(AudioEffectTest, ResetAndVerifyParameter) {
OpenEffects();
VerifyParameters();
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */,
44100 /* iSampleRate */, 44100 /* oSampleRate */);
EXPECT_NO_FATAL_FAILURE(SetParameter());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */,
44100 /* oSampleRate */);
SetParameter();
VerifyParameters();
CommandEffects(CommandId::RESET);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
VerifyParameters();
CloseEffects();
}
// Multiple instances of same implementation running.
TEST_P(AudioEffect, MultipleInstancesRunning) {
EXPECT_NO_FATAL_FAILURE(CreateEffects(3));
TEST_P(AudioEffectTest, MultipleInstancesRunning) {
CreateEffects(3);
ExpectState(State::INIT);
EXPECT_NO_FATAL_FAILURE(OpenEffects());
OpenEffects();
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
CommandEffects(CommandId::START);
ExpectState(State::PROCESSING);
initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */,
44100 /* iSampleRate */, 44100 /* oSampleRate */);
EXPECT_NO_FATAL_FAILURE(SetParameter());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */,
44100 /* oSampleRate */);
SetParameter();
VerifyParameters();
CommandEffects(CommandId::STOP);
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
VerifyParameters();
CloseEffects();
}
INSTANTIATE_TEST_SUITE_P(AudioEffectTest, AudioEffect,
// Send data to effects and expect it to consume by check statusMQ.
TEST_P(AudioEffectTest, ExpectEffectsToConsumeDataInMQ) {
OpenEffects();
PrepareInputData(mWriteMQSize);
CommandEffects(CommandId::START);
writeToFmq(mWriteMQSize);
readFromFmq(mWriteMQSize);
ExpectState(State::PROCESSING);
CommandEffects(CommandId::STOP);
// cleanup
CommandEffects(CommandId::STOP);
ExpectState(State::IDLE);
CloseEffects();
}
INSTANTIATE_TEST_SUITE_P(AudioEffectTestTest, AudioEffectTest,
testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
android::PrintInstanceNameToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffect);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffectTest);
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
}
}

View File

@@ -0,0 +1,233 @@
/*
* 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.
*/
#include <algorithm>
#include <limits>
#include <map>
#include <memory>
#include <string>
#include <vector>
#define LOG_TAG "VtsHalEqualizerTest"
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android/binder_interface_utils.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <gtest/gtest.h>
#include <Utils.h>
#include <aidl/android/hardware/audio/effect/IEffect.h>
#include <aidl/android/hardware/audio/effect/IFactory.h>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include <aidl/android/media/audio/common/AudioDeviceType.h>
#include "AudioHalBinderServiceUtil.h"
#include "EffectHelper.h"
#include "TestUtils.h"
#include "effect-impl/EffectUUID.h"
using namespace android;
using aidl::android::hardware::audio::effect::Capability;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::EffectNullUuid;
using aidl::android::hardware::audio::effect::Equalizer;
using aidl::android::hardware::audio::effect::EqualizerTypeUUID;
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
/**
* Here we focus on specific parameter checking, general IEffect interfaces testing performed in
* VtsAudioEfectTargetTest.
*/
using EqualizerParamTestParam = std::tuple<int, int, int>;
class EqualizerParamTest : public ::testing::TestWithParam<EqualizerParamTestParam>,
public EffectHelper {
public:
EqualizerParamTest()
: EffectHelper(android::getAidlHalInstanceNames(IFactory::descriptor)[0]),
mParamPresetIndex(std::get<0 /* kPresetIndexRange */>(GetParam())),
mParamBandIndex(std::get<1 /* kBandIndexRange */>(GetParam())),
mParamBandLevel(std::get<2 /* kBandLevelRange */>(GetParam())) {}
void SetUp() override {
CreateEffectsWithUUID(EqualizerTypeUUID);
initParamCommonFormat();
initParamCommon();
initParamSpecific();
OpenEffects(EqualizerTypeUUID);
SCOPED_TRACE(testing::Message() << "preset: " << mParamPresetIndex << " bandIdx "
<< mParamBandIndex << " level " << mParamBandLevel);
}
void TearDown() override {
CloseEffects();
DestroyEffects();
CleanUp();
}
const int mParamPresetIndex;
const int mParamBandIndex;
const int mParamBandLevel;
void SetAndGetEqualizerParameters() {
auto functor = [&](const std::shared_ptr<IEffect>& effect) {
for (auto& it : mTags) {
auto& tag = it.first;
auto& eq = it.second;
// validate parameter
Descriptor desc;
ASSERT_STATUS(EX_NONE, effect->getDescriptor(&desc));
const bool valid = isTagInRange(it.first, it.second, desc);
const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
// set
Parameter expectParam;
Parameter::Specific specific;
specific.set<Parameter::Specific::equalizer>(*eq.get());
expectParam.set<Parameter::specific>(specific);
EXPECT_STATUS(expected, effect->setParameter(expectParam))
<< expectParam.toString();
// get
if (expected == EX_NONE) {
Parameter getParam;
Parameter::Specific::Id id;
id.set<Parameter::Specific::Id::equalizerTag>(tag);
// if set success, then get should match
EXPECT_STATUS(expected, effect->getParameter(id, &getParam));
EXPECT_EQ(expectParam, getParam) << "\n"
<< expectParam.toString() << "\n"
<< getParam.toString();
}
}
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(functor));
}
void addPresetParam(int preset) {
Equalizer eq;
eq.set<Equalizer::preset>(preset);
mTags.push_back({Equalizer::preset, std::make_unique<Equalizer>(std::move(eq))});
}
void addBandLevelsParam(std::vector<Equalizer::BandLevel>& bandLevels) {
Equalizer eq;
eq.set<Equalizer::bandLevels>(bandLevels);
mTags.push_back({Equalizer::bandLevels, std::make_unique<Equalizer>(std::move(eq))});
}
bool isTagInRange(const Equalizer::Tag& tag, const std::unique_ptr<Equalizer>& eq,
const Descriptor& desc) const {
std::cout << "xxx" << toString(tag) << " " << desc.toString();
const Equalizer::Capability& eqCap = desc.capability.get<Capability::equalizer>();
switch (tag) {
case Equalizer::preset: {
int index = eq->get<Equalizer::preset>();
return isPresetIndexInRange(eqCap, index);
}
case Equalizer::bandLevels: {
auto& bandLevel = eq->get<Equalizer::bandLevels>();
return isBandIndexInRange(eqCap, bandLevel);
}
default:
return false;
}
return false;
}
bool isPresetIndexInRange(const Equalizer::Capability& cap, int idx) const {
const auto [min, max] =
std::minmax_element(cap.presets.begin(), cap.presets.end(),
[](const auto& a, const auto& b) { return a.index < b.index; });
return idx >= min->index && idx <= max->index;
}
bool isBandIndexInRange(const Equalizer::Capability& cap,
const std::vector<Equalizer::BandLevel>& bandLevel) const {
for (auto& it : bandLevel) {
if (!isBandIndexInRange(cap, it.index)) return false;
}
return true;
}
bool isBandIndexInRange(const Equalizer::Capability& cap, int idx) const {
const auto [min, max] =
std::minmax_element(cap.bandFrequencies.begin(), cap.bandFrequencies.end(),
[](const auto& a, const auto& b) { return a.index < b.index; });
return idx >= min->index && idx <= max->index;
}
private:
Equalizer::VendorExtension mVendorExtension;
std::vector<std::pair<Equalizer::Tag, std::unique_ptr<Equalizer>>> mTags;
bool validCapabilityTag(Capability& cap) { return cap.getTag() == Capability::equalizer; }
void initParamSpecific() {
Equalizer eq;
eq.set<Equalizer::preset>(0);
Parameter::Specific specific;
specific.set<Parameter::Specific::equalizer>(eq);
setSpecific(specific);
}
void CleanUp() { mTags.clear(); }
};
TEST_P(EqualizerParamTest, SetAndGetPreset) {
EXPECT_NO_FATAL_FAILURE(addPresetParam(mParamPresetIndex));
SetAndGetEqualizerParameters();
}
TEST_P(EqualizerParamTest, SetAndGetSingleBand) {
Equalizer::BandLevel bandLevel = {mParamBandIndex, mParamBandLevel};
std::vector<Equalizer::BandLevel> bandLevels;
bandLevels.push_back(bandLevel);
EXPECT_NO_FATAL_FAILURE(addBandLevelsParam(bandLevels));
SetAndGetEqualizerParameters();
}
/**
Testing preset index range with [-10, 10], assuming the min/max preset index supported by
effect is in this range.
This range is verified with IEffect.getDescriptor(): for any index supported vts expect EX_NONE
from IEffect.setParameter(), otherwise expect EX_ILLEGAL_ARGUMENT.
*/
constexpr std::pair<int, int> kPresetIndexRange = {-1, 10}; // valid range [0, 9]
constexpr std::pair<int, int> kBandIndexRange = {-1, 5}; // valid range [0, 4]
constexpr std::pair<int, int> kBandLevelRange = {-5, 5}; // needs update with implementation
INSTANTIATE_TEST_SUITE_P(
EqualizerTest, EqualizerParamTest,
::testing::Combine(testing::Range(kPresetIndexRange.first, kPresetIndexRange.second),
testing::Range(kBandIndexRange.first, kBandIndexRange.second),
testing::Range(kBandLevelRange.first, kBandLevelRange.second)));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EqualizerTest);
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
}