mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 16:23:37 +00:00
Merge changes from topic "aidl_audio_effect3"
* changes: AIDL effect: Add effect AIDL implementationi and vts test AIDL effect: Add effect AIDL definition
This commit is contained in:
@@ -148,11 +148,19 @@ aidl_interface {
|
||||
name: "android.hardware.audio.effect",
|
||||
vendor_available: true,
|
||||
srcs: [
|
||||
"android/hardware/audio/effect/Capability.aidl",
|
||||
"android/hardware/audio/effect/CommandId.aidl",
|
||||
"android/hardware/audio/effect/Descriptor.aidl",
|
||||
"android/hardware/audio/effect/Equalizer.aidl",
|
||||
"android/hardware/audio/effect/Flags.aidl",
|
||||
"android/hardware/audio/effect/IEffect.aidl",
|
||||
"android/hardware/audio/effect/IFactory.aidl",
|
||||
"android/hardware/audio/effect/Parameter.aidl",
|
||||
"android/hardware/audio/effect/State.aidl",
|
||||
],
|
||||
imports: [
|
||||
"android.hardware.common-V2",
|
||||
"android.hardware.common.fmq-V1",
|
||||
"android.hardware.audio.common-V1",
|
||||
"android.media.audio.common.types-V2",
|
||||
],
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
@VintfStability
|
||||
union Capability {
|
||||
android.hardware.audio.effect.Capability.VendorEffectCapability vendor;
|
||||
android.hardware.audio.effect.Equalizer.Capability equalizer;
|
||||
@VintfStability
|
||||
parcelable VendorEffectCapability {
|
||||
ParcelableHolder extension;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
@Backing(type="int") @VintfStability
|
||||
enum CommandId {
|
||||
START = 0,
|
||||
STOP = 1,
|
||||
RESET = 2,
|
||||
VENDOR_COMMAND_0 = 256,
|
||||
VENDOR_COMMAND_1 = 257,
|
||||
VENDOR_COMMAND_2 = 258,
|
||||
VENDOR_COMMAND_3 = 259,
|
||||
VENDOR_COMMAND_4 = 260,
|
||||
VENDOR_COMMAND_5 = 261,
|
||||
VENDOR_COMMAND_6 = 262,
|
||||
VENDOR_COMMAND_7 = 263,
|
||||
VENDOR_COMMAND_8 = 264,
|
||||
VENDOR_COMMAND_9 = 265,
|
||||
}
|
||||
@@ -35,6 +35,7 @@ package android.hardware.audio.effect;
|
||||
@VintfStability
|
||||
parcelable Descriptor {
|
||||
android.hardware.audio.effect.Descriptor.Common common;
|
||||
android.hardware.audio.effect.Capability capability;
|
||||
const String EFFECT_TYPE_UUID_ENV_REVERB = "c2e5d5f0-94bd-4763-9cac-4e234d06839e";
|
||||
const String EFFECT_TYPE_UUID_PRESET_REVERB = "47382d60-ddd8-11db-bf3a-0002a5d5c51b";
|
||||
const String EFFECT_TYPE_UUID_EQUALIZER = "0bed4300-ddd6-11db-8f34-0002a5d5c51b";
|
||||
@@ -56,5 +57,6 @@ parcelable Descriptor {
|
||||
@VintfStability
|
||||
parcelable Common {
|
||||
android.hardware.audio.effect.Descriptor.Identity id;
|
||||
android.hardware.audio.effect.Flags flags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
@VintfStability
|
||||
union Equalizer {
|
||||
android.hardware.audio.effect.Equalizer.VendorExtension vendor;
|
||||
@VintfStability
|
||||
parcelable Capability {
|
||||
ParcelableHolder extension;
|
||||
}
|
||||
@VintfStability
|
||||
parcelable VendorExtension {
|
||||
ParcelableHolder extension;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
@VintfStability
|
||||
parcelable Flags {
|
||||
}
|
||||
@@ -34,7 +34,23 @@
|
||||
package android.hardware.audio.effect;
|
||||
@VintfStability
|
||||
interface IEffect {
|
||||
void open();
|
||||
android.hardware.audio.effect.IEffect.OpenEffectReturn open(in android.hardware.audio.effect.Parameter.Common common, in android.hardware.audio.effect.Parameter.Specific specific);
|
||||
void close();
|
||||
android.hardware.audio.effect.Descriptor getDescriptor();
|
||||
void command(in android.hardware.audio.effect.CommandId commandId);
|
||||
android.hardware.audio.effect.State getState();
|
||||
void setParameter(in android.hardware.audio.effect.Parameter param);
|
||||
android.hardware.audio.effect.Parameter getParameter(in android.hardware.audio.effect.Parameter.Id paramId);
|
||||
@FixedSize @VintfStability
|
||||
parcelable Status {
|
||||
int status;
|
||||
int fmqByteConsumed;
|
||||
int fmqByteProduced;
|
||||
}
|
||||
@VintfStability
|
||||
parcelable OpenEffectReturn {
|
||||
android.hardware.common.fmq.MQDescriptor<android.hardware.audio.effect.IEffect.Status,android.hardware.common.fmq.SynchronizedReadWrite> statusMQ;
|
||||
android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> inputDataMQ;
|
||||
android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> outputDataMQ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
@VintfStability
|
||||
union Parameter {
|
||||
android.hardware.audio.effect.Parameter.Common common;
|
||||
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;
|
||||
}
|
||||
@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 VendorEffectParameter {
|
||||
ParcelableHolder extension;
|
||||
}
|
||||
@VintfStability
|
||||
union Specific {
|
||||
android.hardware.audio.effect.Equalizer equalizer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
@Backing(type="byte") @VintfStability
|
||||
enum State {
|
||||
INIT = 0,
|
||||
IDLE = 1,
|
||||
PROCESSING = 2,
|
||||
}
|
||||
46
audio/aidl/android/hardware/audio/effect/Capability.aidl
Normal file
46
audio/aidl/android/hardware/audio/effect/Capability.aidl
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
|
||||
import android.hardware.audio.effect.Equalizer;
|
||||
|
||||
/**
|
||||
* Effect capability definitions.
|
||||
* This data structure is used as part of effect Descriptor to identify effect capabilities which
|
||||
* not meant to change at runtime.
|
||||
*/
|
||||
@VintfStability
|
||||
union Capability {
|
||||
/**
|
||||
* Vendor defined effect capability.
|
||||
* This extension can be used when vendor have a new effect implementated and need
|
||||
* capability definition for this new type of effect.
|
||||
* If vendor want to extend existing effect capabilities, it is recommended to expose though
|
||||
* the ParcelableHolder in each effect capability definition. For example:
|
||||
* Equalizer.Capability.extension.
|
||||
*/
|
||||
@VintfStability
|
||||
parcelable VendorEffectCapability {
|
||||
ParcelableHolder extension;
|
||||
}
|
||||
VendorEffectCapability vendor;
|
||||
|
||||
/**
|
||||
* Equalizer capability definition.
|
||||
*/
|
||||
Equalizer.Capability equalizer;
|
||||
}
|
||||
70
audio/aidl/android/hardware/audio/effect/CommandId.aidl
Normal file
70
audio/aidl/android/hardware/audio/effect/CommandId.aidl
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
|
||||
/**
|
||||
* Defines all commands supported by the effect instance.
|
||||
*
|
||||
* There are three groups of commands:
|
||||
* 1. Common part which MUST be supported by all effects.
|
||||
* 2. Commands MUST be supported by a specific type of effect.
|
||||
* 3. Extension commands for vendor.
|
||||
*/
|
||||
@VintfStability
|
||||
@Backing(type="int")
|
||||
enum CommandId {
|
||||
/// 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
|
||||
* IDLE state and have all necessary information. Otherwise it must:
|
||||
* 1. Throw a EX_ILLEGAL_STATE exception if effect is not in IDLE state, or
|
||||
* 2. Throw a EX_TRANSACTION_FAILED for all other errors.
|
||||
*
|
||||
* Depending on parameters set to the effect instance, effect may do process or reverse
|
||||
* process after START command.
|
||||
*/
|
||||
START = 0,
|
||||
/**
|
||||
* Stop effect engine processing with all resource kept.
|
||||
* The currently processed audio data will be discarded if the effect engine is in PROCESSING
|
||||
* state.
|
||||
* Effect instance must do nothing and return ok when it receive STOP command in IDLE state.
|
||||
*/
|
||||
STOP = 1,
|
||||
/**
|
||||
* Keep all parameter settings but reset the buffer content, stop engine processing, and transit
|
||||
* instance state to IDLE if its in PROCESSING state.
|
||||
* Effect instance must be able to handle RESET command at IDLE and PROCESSING states.
|
||||
*/
|
||||
RESET = 2,
|
||||
|
||||
/// MUST be supported by a specific type of effect.
|
||||
// Commands must supported by Equalizer.
|
||||
|
||||
/// Extension commands for vendor.
|
||||
VENDOR_COMMAND_0 = 0x100,
|
||||
VENDOR_COMMAND_1,
|
||||
VENDOR_COMMAND_2,
|
||||
VENDOR_COMMAND_3,
|
||||
VENDOR_COMMAND_4,
|
||||
VENDOR_COMMAND_5,
|
||||
VENDOR_COMMAND_6,
|
||||
VENDOR_COMMAND_7,
|
||||
VENDOR_COMMAND_8,
|
||||
VENDOR_COMMAND_9,
|
||||
}
|
||||
@@ -16,12 +16,13 @@
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
|
||||
import android.hardware.audio.effect.Capability;
|
||||
import android.hardware.audio.effect.Flags;
|
||||
import android.media.audio.common.AudioUuid;
|
||||
|
||||
/**
|
||||
* Effect descriptor contains all information (capabilities, attributes, and ownership) for an
|
||||
* effect implemented in the Audio Effect HAL. Framework uses this information to decide when and
|
||||
* how to apply the effect.
|
||||
* Descriptor contains all information (capabilities, attributes, etc) for an effect implementation.
|
||||
* The client uses this information to decide when and how to apply an effect implementation.
|
||||
*/
|
||||
@VintfStability
|
||||
parcelable Descriptor {
|
||||
@@ -77,6 +78,15 @@ parcelable Descriptor {
|
||||
* Identity of effect implementation.
|
||||
*/
|
||||
Identity id;
|
||||
/**
|
||||
* Effect engine defined capabilities/requirements flags.
|
||||
*/
|
||||
Flags flags;
|
||||
}
|
||||
Common common;
|
||||
|
||||
/**
|
||||
* Effect implementation capability.
|
||||
*/
|
||||
Capability capability;
|
||||
}
|
||||
|
||||
47
audio/aidl/android/hardware/audio/effect/Equalizer.aidl
Normal file
47
audio/aidl/android/hardware/audio/effect/Equalizer.aidl
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
|
||||
import android.media.audio.common.AudioProfile;
|
||||
|
||||
/**
|
||||
* Equalizer specific definitions.
|
||||
*/
|
||||
@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.
|
||||
*/
|
||||
@VintfStability
|
||||
parcelable Capability {
|
||||
/**
|
||||
* Equalizer capability extension, vendor can use this extension in case existing capability
|
||||
* definition not enough.
|
||||
*/
|
||||
ParcelableHolder extension;
|
||||
}
|
||||
|
||||
// Vendor Equalizer implementation definition for additional parameters.
|
||||
@VintfStability
|
||||
parcelable VendorExtension {
|
||||
ParcelableHolder extension;
|
||||
}
|
||||
VendorExtension vendor;
|
||||
}
|
||||
@@ -14,18 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
package android.hardware.audio.effect;
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace aidl::android::hardware::audio::effect {
|
||||
|
||||
// Visualizer implementation UUID.
|
||||
static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
|
||||
static_cast<int32_t>(0x1d4033c0),
|
||||
0x8557,
|
||||
0x11df,
|
||||
0x9f2d,
|
||||
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
|
||||
|
||||
} // namespace aidl::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.
|
||||
*/
|
||||
@VintfStability
|
||||
parcelable Flags {
|
||||
// TODO: add Effect engine defined capabilities/requirements flags.
|
||||
}
|
||||
@@ -16,24 +16,68 @@
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
|
||||
import android.hardware.audio.effect.CommandId;
|
||||
import android.hardware.audio.effect.Descriptor;
|
||||
import android.hardware.audio.effect.Parameter;
|
||||
import android.hardware.audio.effect.State;
|
||||
import android.hardware.common.fmq.MQDescriptor;
|
||||
import android.hardware.common.fmq.SynchronizedReadWrite;
|
||||
|
||||
/**
|
||||
* Effect interfaces definitions to configure and control the effect instance.
|
||||
*/
|
||||
@VintfStability
|
||||
interface IEffect {
|
||||
@VintfStability
|
||||
@FixedSize
|
||||
parcelable Status {
|
||||
/**
|
||||
* One of Binder STATUS_* statuses:
|
||||
* - STATUS_OK: the command has completed successfully;
|
||||
* - STATUS_BAD_VALUE: invalid value in the 'Command' structure;
|
||||
* - STATUS_INVALID_OPERATION: the mix port is not connected
|
||||
* to any producer or consumer, thus
|
||||
* positions can not be reported;
|
||||
* - STATUS_NOT_ENOUGH_DATA: a read or write error has
|
||||
* occurred for the 'audio.fmq' queue;
|
||||
*
|
||||
*/
|
||||
int status;
|
||||
/**
|
||||
* The amount of bytes consumed by the effect instance.
|
||||
*/
|
||||
int fmqByteConsumed;
|
||||
/**
|
||||
* The amount of bytes produced by the effect instance.
|
||||
*/
|
||||
int fmqByteProduced;
|
||||
}
|
||||
|
||||
// Return data structure of IEffect.open() interface.
|
||||
@VintfStability
|
||||
parcelable OpenEffectReturn {
|
||||
// Message queue for effect processing status.
|
||||
MQDescriptor<Status, SynchronizedReadWrite> statusMQ;
|
||||
// Message queue for input data buffer.
|
||||
MQDescriptor<byte, SynchronizedReadWrite> inputDataMQ;
|
||||
// Message queue for output data buffer.
|
||||
MQDescriptor<byte, SynchronizedReadWrite> outputDataMQ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an effect instance, effect should not start processing data before receive START
|
||||
* command. All necessary information should be allocated and instance should transfer to IDLE
|
||||
* state after open() call has been handled successfully.
|
||||
* After open, the effect instance should be able to handle all IEffect interface calls.
|
||||
* Open an effect instance, effect must not start processing data before receive
|
||||
* CommandId::START command. All necessary information should be allocated and instance must
|
||||
* transfer to State::IDLE state after open() call has been handled successfully. After open,
|
||||
* the effect instance must be able to handle all IEffect interface calls.
|
||||
*
|
||||
* @param common Parameters which MUST pass from client at open time.
|
||||
*
|
||||
* @throws EX_ILLEGAL_ARGUMENT if the effect instance receive unsupported command.
|
||||
* @throws a EX_UNSUPPORTED_OPERATION if device capability/resource is not enough or system
|
||||
* failure happens.
|
||||
* @note Open an already-opened effect instance should do nothing and should not throw an error.
|
||||
*/
|
||||
void open();
|
||||
OpenEffectReturn open(in Parameter.Common common, in Parameter.Specific specific);
|
||||
|
||||
/**
|
||||
* Called by the client to close the effect instance, processing thread should be destroyed and
|
||||
@@ -45,7 +89,7 @@ interface IEffect {
|
||||
*
|
||||
* Effect instance close interface should always succeed unless:
|
||||
* 1. The effect instance is not in a proper state to be closed, for example it's still in
|
||||
* processing state.
|
||||
* State::PROCESSING state.
|
||||
* 2. There is system/hardware related failure when close.
|
||||
*
|
||||
* @throws EX_ILLEGAL_STATE if the effect instance is not in a proper state to be closed.
|
||||
@@ -62,4 +106,53 @@ interface IEffect {
|
||||
* @return Descriptor The @c Descriptor of this effect instance.
|
||||
*/
|
||||
Descriptor getDescriptor();
|
||||
|
||||
/**
|
||||
* Send a command (defined in enum CommandId) to the effect instance, instance state can be
|
||||
* changed as result of command handling.
|
||||
*
|
||||
* Must be available for the effect instance after it has been open().
|
||||
*
|
||||
* @param commandId ID of the command send to the effect instance.
|
||||
*
|
||||
* @throws EX_ILLEGAL_STATE if the effect instance is not in a proper state to handle the
|
||||
* command.
|
||||
* @throws EX_ILLEGAL_ARGUMENT if the effect instance receive unsupported command.
|
||||
*/
|
||||
void command(in CommandId commandId);
|
||||
|
||||
/**
|
||||
* Get current state of the effect instance.
|
||||
*
|
||||
* Must be available for the effect instance at anytime and should always succeed.
|
||||
*
|
||||
* @return Current effect instance state.
|
||||
*/
|
||||
State getState();
|
||||
|
||||
/**
|
||||
* Set a parameter to the effect instance.
|
||||
*
|
||||
* Must be available for the effect instance after open().
|
||||
*
|
||||
* @param param Parameter data to set to the effect instance.
|
||||
*
|
||||
* @throws EX_ILLEGAL_ARGUMENT if the effect instance receive unsupported parameter.
|
||||
*/
|
||||
void setParameter(in Parameter param);
|
||||
|
||||
/**
|
||||
* Get a parameter from the effect instance with parameter ID.
|
||||
*
|
||||
* This interface must return the current parameter of the effect instance, if no parameter
|
||||
* has been set by client yet, the default value must be returned.
|
||||
*
|
||||
* Must be available for the effect instance after open().
|
||||
*
|
||||
* @param paramId The tag enum of parameter to get.
|
||||
* @return Parameter The parameter to get from the effect instance.
|
||||
*
|
||||
* @throws EX_ILLEGAL_ARGUMENT if the effect instance receive unsupported parameter tag.
|
||||
*/
|
||||
Parameter getParameter(in Parameter.Id paramId);
|
||||
}
|
||||
|
||||
87
audio/aidl/android/hardware/audio/effect/Parameter.aidl
Normal file
87
audio/aidl/android/hardware/audio/effect/Parameter.aidl
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
|
||||
import android.hardware.audio.effect.Equalizer;
|
||||
import android.media.audio.common.AudioConfig;
|
||||
import android.media.audio.common.AudioDeviceDescription;
|
||||
/**
|
||||
* Defines all parameters supported by the effect instance.
|
||||
*
|
||||
* There are three groups of parameters:
|
||||
* 1. Common parameters are essential parameters, MUST pass to effects at open() interface.
|
||||
* 2. Parameters defined for a specific effect type.
|
||||
* 3. Extension parameters for vendor.
|
||||
*
|
||||
* For all supported parameter, implementation MUST support both set and get.
|
||||
*/
|
||||
@VintfStability
|
||||
union Parameter {
|
||||
/**
|
||||
* Client can pass in Parameter.Id with the corresponding tag value in IEffect.getParameter()
|
||||
* call to get android.hardware.audio.effect.Parameter.
|
||||
*
|
||||
* As an example, if a client want to get audio.hardware.audio.effect.Specific.Equalizer, the
|
||||
* value of Id should be audio.hardware.audio.effect.Parameter.Specific.equalizer.
|
||||
*/
|
||||
@VintfStability
|
||||
union Id {
|
||||
// Common parameter tag.
|
||||
int commonTag;
|
||||
// Vendor defined parameter tag.
|
||||
int vendorTag;
|
||||
// Specific effect parameter tag.
|
||||
Specific.Tag specificTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common parameters MUST be supported by all effect implementations.
|
||||
*/
|
||||
@VintfStability
|
||||
parcelable Common {
|
||||
// Type of Audio device.
|
||||
int session;
|
||||
// I/O Handle.
|
||||
int ioHandle;
|
||||
// Type of Audio device.
|
||||
AudioDeviceDescription device;
|
||||
// Input config.
|
||||
AudioConfig input;
|
||||
// Output config.
|
||||
AudioConfig output;
|
||||
}
|
||||
Common common;
|
||||
|
||||
/**
|
||||
* Parameters for vendor extension effect implementation usage.
|
||||
*/
|
||||
@VintfStability
|
||||
parcelable VendorEffectParameter {
|
||||
ParcelableHolder extension;
|
||||
}
|
||||
VendorEffectParameter vendorEffect;
|
||||
|
||||
/**
|
||||
* Parameters MUST be supported by a Specific type of effect.
|
||||
*/
|
||||
@VintfStability
|
||||
union Specific {
|
||||
Equalizer equalizer;
|
||||
// TODO: add other effect definitions here
|
||||
}
|
||||
Specific specific;
|
||||
}
|
||||
86
audio/aidl/android/hardware/audio/effect/State.aidl
Normal file
86
audio/aidl/android/hardware/audio/effect/State.aidl
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.audio.effect;
|
||||
|
||||
/**
|
||||
* Possible states of an effect instance.
|
||||
* A typical effect instance will be in INIT state when it is created with IFactory.createEffect()
|
||||
* interface, transfer to IDLE after open(), and to PROCESSING after
|
||||
* IEffect.command(Command.Id.START) command. When an effect instance receive STOP or RESET command,
|
||||
* it should transfer to IDLE state after handle the command successfully. Effect instance should
|
||||
* consume minimal resource and transfer to INIT state after it was close().
|
||||
*
|
||||
* Refer to State.gv for detailed state diagram.
|
||||
*/
|
||||
@VintfStability
|
||||
@Backing(type="byte")
|
||||
enum State {
|
||||
|
||||
/**
|
||||
* An effect instance is in INIT state by default after it was created with
|
||||
* IFactory.createEffect(). When an effect instance is in INIT state, it should have instance
|
||||
* context initialized, and ready to handle IEffect.setParameter(), IEffect.open() as well as
|
||||
* all getter interfaces.
|
||||
*
|
||||
* In INIT state, effect instance must:
|
||||
* 1. Not handle any IEffect.command() and return EX_ILLEGAL_STATE with any Command.Id.
|
||||
* 2. Be able to handle all parameter setting with IEffect.setParameter().
|
||||
* 3. Be able to handle all getter interface calls like IEffect.getParameter() and
|
||||
* IEffect.getState().
|
||||
* 4. Be able to handle IEffect.open() successfully after configuration.
|
||||
*
|
||||
* Client is expected to do necessary configuration with IEffect.setParameter(), get all
|
||||
* resource ready with IEffect.open(), and make sure effect instance transfer to IDLE state
|
||||
* before sending commands with IEffect.command() interface. Effect instance must transfer
|
||||
* from INIT to IDLE state after handle IEffect.open() call successfully.
|
||||
*/
|
||||
INIT,
|
||||
/**
|
||||
* An effect instance transfer to IDLE state after it was open successfully with IEffect.open()
|
||||
* in INIT state, or after it was stop/reset with Command.Id.STOP/RESET in PROCESSING state.
|
||||
*
|
||||
* In IDLE state, effect instance must:
|
||||
* 1. Be able to start effect processing engine with IEffect.command(Command.Id.START) call.
|
||||
* 2. Be able to handle all parameter setting with IEffect.setParameter().
|
||||
* 3. Be able to handle all getter interface calls like IEffect.getParameter() and
|
||||
* IEffect.getState().
|
||||
*
|
||||
* The following state transfer can happen in IDLE state:
|
||||
* 1. Transfer to PROCESSING if instance receive an START command and start processing data
|
||||
* successfully.
|
||||
* 2. Transfer to INIT if instance receive a close() call.
|
||||
*/
|
||||
IDLE,
|
||||
/**
|
||||
* An effect instance is in PROCESSING state after it receive an START command and start
|
||||
* processing data successfully. Effect instance will transfer from PROCESSING to IDLE state if
|
||||
* it receive an STOP or RESET command and handle the command successfully.
|
||||
*
|
||||
* When an instance is in PROCESSING state, client should try not to close() it directly,
|
||||
* instead client should try to stop processing data first with STOP command before close(). In
|
||||
* the case of a close() call received when instance in PROCESSING state, it should try to stop
|
||||
* processing and transfer to IDLE first before close().
|
||||
*
|
||||
* In PROCESSING state, effect instance must:
|
||||
* 1. Return EX_ILLEGAL_STATE if it's not able to handle any parameter settings at runtime.
|
||||
* 2. Be able to handle STOP and RESET for IEffect.command() interface, and return
|
||||
* EX_ILLEGAL_STATE for all other commands.
|
||||
* 3. Must be able to handle all get* interface calls like IEffect.getParameter() and
|
||||
* IEffect.getState().
|
||||
*/
|
||||
PROCESSING,
|
||||
}
|
||||
36
audio/aidl/android/hardware/audio/effect/state.gv
Normal file
36
audio/aidl/android/hardware/audio/effect/state.gv
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// To render: "dot -Tpng state.gv -o state.png"
|
||||
digraph effect_state_machine {
|
||||
node [shape=point style=filled fillcolor=black width=0.5] I;
|
||||
node [shape=doublecircle] F;
|
||||
node [shape=oval width=1];
|
||||
node [fillcolor=lightgreen] INIT;
|
||||
node [fillcolor=lightblue] IDLE;
|
||||
node [fillcolor=lightyellow] PROCESSING;
|
||||
|
||||
I -> INIT [label="IFactory.createEffect" labelfontcolor="navy"];
|
||||
INIT -> F [label="IFactory.destroyEffect"];
|
||||
INIT -> IDLE [label="open()" labelfontcolor="lime"];
|
||||
IDLE -> PROCESSING [label="command(START"];
|
||||
PROCESSING -> IDLE [label="command(STOP)\ncommand(RESET)"];
|
||||
IDLE -> INIT [label="close()"];
|
||||
|
||||
INIT -> INIT [label="getState\ngetDescriptor"];
|
||||
IDLE -> IDLE [label="getXXX\nsetParameter\ncommand(RESET)"];
|
||||
PROCESSING -> PROCESSING [label="getXXX\nsetParameter"];
|
||||
}
|
||||
@@ -81,18 +81,23 @@ cc_defaults {
|
||||
cc_library_static {
|
||||
name: "libaudioeffectserviceexampleimpl",
|
||||
defaults: ["aidlaudioeffectservice_defaults"],
|
||||
export_include_dirs: [
|
||||
"include",
|
||||
"include/equalizer-impl/",
|
||||
],
|
||||
export_include_dirs: ["include"],
|
||||
srcs: [
|
||||
"EffectFactory.cpp",
|
||||
],
|
||||
header_libs: [
|
||||
"libsystem_headers",
|
||||
],
|
||||
visibility: [
|
||||
":__subpackages__",
|
||||
],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "effectCommonFile",
|
||||
srcs: ["EffectThread.cpp"],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "android.hardware.audio.effect.service-aidl.example",
|
||||
relative_install_path: "hw",
|
||||
|
||||
@@ -16,57 +16,156 @@
|
||||
|
||||
#define LOG_TAG "AHAL_EffectFactory"
|
||||
#include <android-base/logging.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "effect-impl/EffectUUID.h"
|
||||
#include "effectFactory-impl/EffectFactory.h"
|
||||
#include "equalizer-impl/Equalizer.h"
|
||||
#include "visualizer-impl/Visualizer.h"
|
||||
|
||||
using aidl::android::media::audio::common::AudioUuid;
|
||||
|
||||
namespace aidl::android::hardware::audio::effect {
|
||||
|
||||
Factory::Factory() {
|
||||
// TODO: implement this with xml parser on audio_effect.xml, and filter with optional
|
||||
// parameters.
|
||||
std::function<void(void*)> dlClose = [](void* handle) -> void {
|
||||
if (handle && dlclose(handle)) {
|
||||
LOG(ERROR) << "dlclose failed " << dlerror();
|
||||
}
|
||||
};
|
||||
// TODO: implement this with audio_effect.xml.
|
||||
auto libHandle =
|
||||
std::unique_ptr<void, decltype(dlClose)>{dlopen("libequalizer.so", RTLD_LAZY), dlClose};
|
||||
if (!libHandle) {
|
||||
LOG(ERROR) << __func__ << ": dlopen failed, err: " << dlerror();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << __func__ << " dlopen uuid: " << EqualizerSwImplUUID.toString() << " handle "
|
||||
<< libHandle;
|
||||
mEffectLibMap.insert({EqualizerSwImplUUID, std::make_pair(std::move(libHandle), nullptr)});
|
||||
|
||||
Descriptor::Identity id;
|
||||
id.type = EqualizerTypeUUID;
|
||||
id.uuid = EqualizerSwImplUUID;
|
||||
mIdentityList.push_back(id);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Factory::queryEffects(const std::optional<AudioUuid>& in_type,
|
||||
const std::optional<AudioUuid>& in_instance,
|
||||
Factory::~Factory() {
|
||||
if (auto count = mEffectUuidMap.size()) {
|
||||
LOG(ERROR) << __func__ << " remaining " << count
|
||||
<< " effect instances not destroyed indicating resource leak!";
|
||||
for (const auto& it : mEffectUuidMap) {
|
||||
if (auto spEffect = it.first.lock()) {
|
||||
LOG(ERROR) << __func__ << " erase remaining instance UUID " << it.second.toString();
|
||||
destroyEffectImpl(spEffect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Factory::queryEffects(const std::optional<AudioUuid>& in_type_uuid,
|
||||
const std::optional<AudioUuid>& in_impl_uuid,
|
||||
std::vector<Descriptor::Identity>* _aidl_return) {
|
||||
std::copy_if(mIdentityList.begin(), mIdentityList.end(), std::back_inserter(*_aidl_return),
|
||||
[&](auto& desc) {
|
||||
return (!in_type.has_value() || in_type.value() == desc.type) &&
|
||||
(!in_instance.has_value() || in_instance.value() == desc.uuid);
|
||||
return (!in_type_uuid.has_value() || in_type_uuid.value() == desc.type) &&
|
||||
(!in_impl_uuid.has_value() || in_impl_uuid.value() == desc.uuid);
|
||||
});
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Factory::createEffect(
|
||||
const AudioUuid& in_impl_uuid,
|
||||
std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>* _aidl_return) {
|
||||
#define RETURN_IF_BINDER_EXCEPTION(functor) \
|
||||
{ \
|
||||
binder_exception_t exception = functor; \
|
||||
if (EX_NONE != exception) { \
|
||||
LOG(ERROR) << #functor << ": failed with error " << exception; \
|
||||
return ndk::ScopedAStatus::fromExceptionCode(exception); \
|
||||
} \
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Factory::createEffect(const AudioUuid& in_impl_uuid,
|
||||
std::shared_ptr<IEffect>* _aidl_return) {
|
||||
LOG(DEBUG) << __func__ << ": UUID " << in_impl_uuid.toString();
|
||||
if (in_impl_uuid == EqualizerSwImplUUID) {
|
||||
*_aidl_return = ndk::SharedRefBase::make<Equalizer>();
|
||||
if (mEffectLibMap.count(in_impl_uuid)) {
|
||||
auto& lib = mEffectLibMap[in_impl_uuid];
|
||||
// didn't do dlsym yet
|
||||
if (nullptr == lib.second) {
|
||||
void* libHandle = lib.first.get();
|
||||
struct effect_interface_s intf = {
|
||||
.createEffectFunc = (EffectCreateFunctor)dlsym(libHandle, "createEffect"),
|
||||
.destroyEffectFunc =
|
||||
(EffectDestroyFunctor)dlsym(libHandle, "destroyEffect")};
|
||||
auto dlInterface = std::make_unique<struct effect_interface_s>(intf);
|
||||
if (!dlInterface->createEffectFunc || !dlInterface->destroyEffectFunc) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": create or destroy symbol not exist in library: " << libHandle
|
||||
<< "!";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
|
||||
}
|
||||
lib.second = std::move(dlInterface);
|
||||
}
|
||||
|
||||
auto& libInterface = lib.second;
|
||||
std::shared_ptr<IEffect> effectSp;
|
||||
RETURN_IF_BINDER_EXCEPTION(libInterface->createEffectFunc(&effectSp));
|
||||
if (!effectSp) {
|
||||
LOG(ERROR) << __func__ << ": library created null instance without return error!";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
|
||||
}
|
||||
*_aidl_return = effectSp;
|
||||
mEffectUuidMap[std::weak_ptr<IEffect>(effectSp)] = in_impl_uuid;
|
||||
LOG(DEBUG) << __func__ << ": instance " << effectSp.get() << " created successfully";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << ": library doesn't exist";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << ": UUID "
|
||||
<< " not supported";
|
||||
LOG(ERROR) << __func__ << ": UUID not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Factory::destroyEffect(
|
||||
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_handle) {
|
||||
if (in_handle) {
|
||||
// TODO: b/245393900 need check the instance state with IEffect.getState before destroy.
|
||||
ndk::ScopedAStatus Factory::destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle) {
|
||||
std::weak_ptr<IEffect> wpHandle(in_handle);
|
||||
// find UUID with key (std::weak_ptr<IEffect>)
|
||||
if (auto uuidIt = mEffectUuidMap.find(wpHandle); uuidIt != mEffectUuidMap.end()) {
|
||||
auto& uuid = uuidIt->second;
|
||||
// find implementation library with UUID
|
||||
if (auto libIt = mEffectLibMap.find(uuid); libIt != mEffectLibMap.end()) {
|
||||
if (libIt->second.second->destroyEffectFunc) {
|
||||
RETURN_IF_BINDER_EXCEPTION(libIt->second.second->destroyEffectFunc(in_handle));
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << ": UUID " << uuid.toString() << " does not exist in libMap!";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
mEffectUuidMap.erase(uuidIt);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << ": instance " << in_handle << " does not exist!";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
}
|
||||
|
||||
// go over the map and cleanup all expired weak_ptrs.
|
||||
void Factory::cleanupEffectMap() {
|
||||
for (auto it = mEffectUuidMap.begin(); it != mEffectUuidMap.end();) {
|
||||
if (nullptr == it->first.lock()) {
|
||||
it = mEffectUuidMap.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Factory::destroyEffect(const std::shared_ptr<IEffect>& in_handle) {
|
||||
LOG(DEBUG) << __func__ << ": instance " << in_handle.get();
|
||||
ndk::ScopedAStatus status = destroyEffectImpl(in_handle);
|
||||
// always do the cleanup
|
||||
cleanupEffectMap();
|
||||
return status;
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::effect
|
||||
|
||||
131
audio/aidl/default/EffectThread.cpp
Normal file
131
audio/aidl/default/EffectThread.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "AHAL_EffectThread"
|
||||
#include <android-base/logging.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "effect-impl/EffectThread.h"
|
||||
|
||||
namespace aidl::android::hardware::audio::effect {
|
||||
|
||||
EffectThread::EffectThread() {
|
||||
LOG(DEBUG) << __func__;
|
||||
}
|
||||
|
||||
EffectThread::~EffectThread() {
|
||||
destroy();
|
||||
LOG(DEBUG) << __func__ << " done";
|
||||
};
|
||||
|
||||
RetCode EffectThread::create(const std::string& name, const int priority) {
|
||||
if (mThread.joinable()) {
|
||||
LOG(WARNING) << __func__ << " thread already created, no-op";
|
||||
return RetCode::SUCCESS;
|
||||
}
|
||||
mName = name;
|
||||
mPriority = priority;
|
||||
mThread = std::thread(&EffectThread::threadLoop, this);
|
||||
LOG(DEBUG) << __func__ << " " << name << " priority " << mPriority << " done";
|
||||
return RetCode::SUCCESS;
|
||||
}
|
||||
|
||||
RetCode EffectThread::destroy() {
|
||||
{
|
||||
std::lock_guard lg(mMutex);
|
||||
mStop = mExit = true;
|
||||
}
|
||||
mCv.notify_one();
|
||||
|
||||
if (mThread.joinable()) {
|
||||
mThread.join();
|
||||
}
|
||||
LOG(DEBUG) << __func__ << " done";
|
||||
return RetCode::SUCCESS;
|
||||
}
|
||||
|
||||
RetCode EffectThread::start() {
|
||||
if (!mThread.joinable()) {
|
||||
LOG(ERROR) << __func__ << " thread already destroyed";
|
||||
return RetCode::ERROR;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lg(mMutex);
|
||||
if (!mStop) {
|
||||
LOG(WARNING) << __func__ << " already start";
|
||||
return RetCode::SUCCESS;
|
||||
}
|
||||
mStop = false;
|
||||
}
|
||||
|
||||
mCv.notify_one();
|
||||
LOG(DEBUG) << __func__ << " done";
|
||||
return RetCode::SUCCESS;
|
||||
}
|
||||
|
||||
RetCode EffectThread::stop() {
|
||||
if (!mThread.joinable()) {
|
||||
LOG(ERROR) << __func__ << " thread already destroyed";
|
||||
return RetCode::ERROR;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lg(mMutex);
|
||||
if (mStop) {
|
||||
LOG(WARNING) << __func__ << " already stop";
|
||||
return RetCode::SUCCESS;
|
||||
}
|
||||
mStop = true;
|
||||
}
|
||||
LOG(DEBUG) << __func__ << " done";
|
||||
return RetCode::SUCCESS;
|
||||
}
|
||||
|
||||
void EffectThread::threadLoop() {
|
||||
pthread_setname_np(pthread_self(), mName.substr(0, MAX_TASK_COMM_LEN - 1).c_str());
|
||||
setpriority(PRIO_PROCESS, 0, mPriority);
|
||||
while (true) {
|
||||
bool needExit = false;
|
||||
{
|
||||
std::unique_lock l(mMutex);
|
||||
mCv.wait(l, [&]() REQUIRES(mMutex) {
|
||||
needExit = mExit;
|
||||
return mExit || !mStop;
|
||||
});
|
||||
}
|
||||
if (needExit) {
|
||||
LOG(WARNING) << __func__ << " EXIT!";
|
||||
return;
|
||||
}
|
||||
// process without lock
|
||||
process();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -27,17 +27,28 @@ cc_library_shared {
|
||||
name: "libequalizer",
|
||||
vendor: true,
|
||||
shared_libs: [
|
||||
"libaudioaidlcommon",
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"libstagefright_foundation",
|
||||
"android.hardware.common-V2-ndk",
|
||||
],
|
||||
defaults: [
|
||||
"aidlaudioservice_defaults",
|
||||
"latest_android_media_audio_common_types_ndk_shared",
|
||||
"latest_android_hardware_audio_effect_ndk_shared",
|
||||
],
|
||||
include_dirs: ["hardware/interfaces/audio/aidl/default/include/equalizer-impl"],
|
||||
include_dirs: [
|
||||
"hardware/interfaces/audio/aidl/default/include",
|
||||
"system/media/audio/include",
|
||||
],
|
||||
srcs: [
|
||||
"Equalizer.cpp",
|
||||
":effectCommonFile",
|
||||
],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-Wthread-safety",
|
||||
],
|
||||
visibility: [
|
||||
"//hardware/interfaces/audio/aidl/default",
|
||||
|
||||
@@ -14,27 +14,243 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "AHAL_Equalizer"
|
||||
#define LOG_TAG "AHAL_EqualizerSw"
|
||||
#include <Utils.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "Equalizer.h"
|
||||
#include "effect-impl/EffectUUID.h"
|
||||
#include "equalizer-impl/EqualizerSw.h"
|
||||
|
||||
using android::hardware::audio::common::getFrameSizeInBytes;
|
||||
|
||||
namespace aidl::android::hardware::audio::effect {
|
||||
|
||||
ndk::ScopedAStatus Equalizer::open() {
|
||||
extern "C" binder_exception_t createEffect(std::shared_ptr<IEffect>* instanceSpp) {
|
||||
if (instanceSpp) {
|
||||
*instanceSpp = ndk::SharedRefBase::make<EqualizerSw>();
|
||||
LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
|
||||
return EX_NONE;
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << " invalid input parameter!";
|
||||
return EX_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
|
||||
State state;
|
||||
ndk::ScopedAStatus status = instanceSp->getState(&state);
|
||||
if (!status.isOk() || State::INIT != state) {
|
||||
LOG(ERROR) << __func__ << " instance " << instanceSp.get()
|
||||
<< " in state: " << toString(state) << ", status: " << status.getDescription();
|
||||
return EX_ILLEGAL_STATE;
|
||||
}
|
||||
LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
|
||||
return EX_NONE;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus EqualizerSw::open(const Parameter::Common& common,
|
||||
const Parameter::Specific& specific,
|
||||
OpenEffectReturn* _aidl_return) {
|
||||
LOG(DEBUG) << __func__;
|
||||
if (mState != State::INIT) {
|
||||
LOG(WARNING) << __func__ << " eq already open";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
// Set essential parameters before create worker thread.
|
||||
setCommonParameter(common);
|
||||
setSpecificParameter(specific);
|
||||
|
||||
LOG(DEBUG) << " common: " << common.toString() << " specific " << specific.toString();
|
||||
|
||||
auto& input = common.input;
|
||||
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)) {
|
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_UNSUPPORTED_OPERATION,
|
||||
"FailedToCreateFmq");
|
||||
}
|
||||
|
||||
// create the worker thread
|
||||
if (RetCode::SUCCESS != mWorker->create(LOG_TAG)) {
|
||||
LOG(ERROR) << __func__ << " created worker thread failed";
|
||||
destroyFmq();
|
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_UNSUPPORTED_OPERATION,
|
||||
"FailedToCreateFmq");
|
||||
}
|
||||
|
||||
mState = State::IDLE;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus EqualizerSw::close() {
|
||||
if (mState == State::INIT) {
|
||||
LOG(WARNING) << __func__ << " instance already closed";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
} else if (mState == State::PROCESSING) {
|
||||
LOG(ERROR) << __func__ << " instance still processing";
|
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
|
||||
"EqInstanceProcessing");
|
||||
}
|
||||
|
||||
// stop the worker thread
|
||||
mState = State::INIT;
|
||||
mWorker->destroy();
|
||||
destroyFmq();
|
||||
LOG(DEBUG) << __func__;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Equalizer::close() {
|
||||
LOG(DEBUG) << __func__;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Equalizer::getDescriptor(Descriptor* _aidl_return) {
|
||||
LOG(DEBUG) << __func__ << "descriptor " << mDesc.toString();
|
||||
ndk::ScopedAStatus EqualizerSw::getDescriptor(Descriptor* _aidl_return) {
|
||||
LOG(DEBUG) << __func__ << mDesc.toString();
|
||||
*_aidl_return = mDesc;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus EqualizerSw::command(CommandId in_commandId) {
|
||||
LOG(DEBUG) << __func__ << ": receive command:" << toString(in_commandId);
|
||||
if (mState == State::INIT) {
|
||||
LOG(ERROR) << __func__ << ": instance not open yet";
|
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
|
||||
"CommandStateError");
|
||||
}
|
||||
switch (in_commandId) {
|
||||
case CommandId::START:
|
||||
// start processing.
|
||||
mState = State::PROCESSING;
|
||||
mWorker->start();
|
||||
LOG(DEBUG) << __func__ << " state: " << toString(mState);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
case CommandId::STOP:
|
||||
// stop processing.
|
||||
mState = State::IDLE;
|
||||
mWorker->stop();
|
||||
LOG(DEBUG) << __func__ << " state: " << toString(mState);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
case CommandId::RESET:
|
||||
// TODO: reset buffer status.
|
||||
mState = State::IDLE;
|
||||
mWorker->stop();
|
||||
LOG(DEBUG) << __func__ << " state: " << toString(mState);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
default:
|
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
|
||||
"CommandIdNotSupported");
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus EqualizerSw::setParameter(const Parameter& in_param) {
|
||||
if (mState == State::INIT) {
|
||||
LOG(ERROR) << __func__ << ": instance not open yet";
|
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "StateError");
|
||||
}
|
||||
LOG(DEBUG) << __func__ << " with: " << in_param.toString();
|
||||
auto tag = in_param.getTag();
|
||||
switch (tag) {
|
||||
case Parameter::common: {
|
||||
return setCommonParameter(in_param.get<Parameter::common>());
|
||||
}
|
||||
case Parameter::specific: {
|
||||
return setSpecificParameter(in_param.get<Parameter::specific>());
|
||||
}
|
||||
default:
|
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
|
||||
"ParameterNotSupported");
|
||||
}
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus EqualizerSw::getParameter(const Parameter::Id& in_paramId,
|
||||
Parameter* _aidl_return) {
|
||||
LOG(DEBUG) << __func__ << in_paramId.toString();
|
||||
auto tag = in_paramId.getTag();
|
||||
switch (tag) {
|
||||
case Parameter::Id::commonTag: {
|
||||
_aidl_return->set<Parameter::common>(mCommonParam);
|
||||
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");
|
||||
}
|
||||
Parameter::Specific specific;
|
||||
specific.set<Parameter::Specific::equalizer>(mEqualizerParam);
|
||||
_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");
|
||||
}
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus EqualizerSw::getState(State* _aidl_return) {
|
||||
*_aidl_return = mState;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
/// 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();
|
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
|
||||
"EffectNotSupported");
|
||||
}
|
||||
|
||||
mEqualizerParam = specific.get<Parameter::Specific::equalizer>();
|
||||
LOG(DEBUG) << __func__ << mEqualizerParam.toString();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
void EqualizerSw::cleanUp() {
|
||||
if (State::PROCESSING == mState) {
|
||||
command(CommandId::STOP);
|
||||
}
|
||||
if (State::INIT != mState) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
// Processing method running in worker thread.
|
||||
void EqualizerSwWorker::process() {
|
||||
// TODO: add EQ processing with FMQ, should wait until data available before data processing.
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::effect
|
||||
|
||||
59
audio/aidl/default/include/effect-impl/EffectThread.h
Normal file
59
audio/aidl/default/include/effect-impl/EffectThread.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 <atomic>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <system/thread_defs.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
|
||||
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();
|
||||
|
||||
// Will call process() in a loop if the thread is running.
|
||||
void threadLoop();
|
||||
|
||||
// User of EffectThread must implement the effect processing logic in this method.
|
||||
virtual void process() = 0;
|
||||
const int MAX_TASK_COMM_LEN = 15;
|
||||
|
||||
private:
|
||||
std::mutex mMutex;
|
||||
std::condition_variable mCv;
|
||||
bool mExit GUARDED_BY(mMutex) = false;
|
||||
bool mStop GUARDED_BY(mMutex) = true;
|
||||
std::thread mThread;
|
||||
int mPriority;
|
||||
std::string mName;
|
||||
};
|
||||
} // namespace aidl::android::hardware::audio::effect
|
||||
45
audio/aidl/default/include/effect-impl/EffectUUID.h
Normal file
45
audio/aidl/default/include/effect-impl/EffectUUID.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 <aidl/android/media/audio/common/AudioUuid.h>
|
||||
|
||||
namespace aidl::android::hardware::audio::effect {
|
||||
|
||||
using ::aidl::android::media::audio::common::AudioUuid;
|
||||
|
||||
// Equalizer type UUID.
|
||||
static const AudioUuid EqualizerTypeUUID = {static_cast<int32_t>(0x0bed4300),
|
||||
0xddd6,
|
||||
0x11db,
|
||||
0x8f34,
|
||||
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
|
||||
|
||||
// Equalizer implementation UUID.
|
||||
static const AudioUuid EqualizerSwImplUUID = {static_cast<int32_t>(0x0bed4300),
|
||||
0x847d,
|
||||
0x11df,
|
||||
0xbb17,
|
||||
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
|
||||
|
||||
// Visualizer type UUID.
|
||||
static const AudioUuid VisualizerTypeUUID = {static_cast<int32_t>(0x1d4033c0),
|
||||
0x8557,
|
||||
0x11df,
|
||||
0x9f2d,
|
||||
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::effect
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <any>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
@@ -63,7 +65,26 @@ class Factory : public BnFactory {
|
||||
override;
|
||||
|
||||
private:
|
||||
~Factory();
|
||||
// List of effect descriptors supported by the devices.
|
||||
std::vector<Descriptor::Identity> mIdentityList;
|
||||
|
||||
typedef binder_exception_t (*EffectCreateFunctor)(std::shared_ptr<IEffect>*);
|
||||
typedef binder_exception_t (*EffectDestroyFunctor)(const std::shared_ptr<IEffect>&);
|
||||
struct effect_interface_s {
|
||||
EffectCreateFunctor createEffectFunc;
|
||||
EffectDestroyFunctor destroyEffectFunc;
|
||||
};
|
||||
|
||||
std::map<aidl::android::media::audio::common::AudioUuid /* implementationUUID */,
|
||||
std::pair<std::unique_ptr<void, std::function<void(void*)>> /* dlHandle */,
|
||||
std::unique_ptr<struct effect_interface_s>>>
|
||||
mEffectLibMap;
|
||||
std::map<std::weak_ptr<IEffect>, aidl::android::media::audio::common::AudioUuid,
|
||||
std::owner_less<>>
|
||||
mEffectUuidMap;
|
||||
|
||||
ndk::ScopedAStatus destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle);
|
||||
void cleanupEffectMap();
|
||||
};
|
||||
} // namespace aidl::android::hardware::audio::effect
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/audio/effect/BnEffect.h>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace aidl::android::hardware::audio::effect {
|
||||
|
||||
// Equalizer type UUID.
|
||||
static const ::aidl::android::media::audio::common::AudioUuid EqualizerTypeUUID = {
|
||||
static_cast<int32_t>(0x0bed4300),
|
||||
0xddd6,
|
||||
0x11db,
|
||||
0x8f34,
|
||||
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
|
||||
|
||||
// Equalizer implementation UUID.
|
||||
static const ::aidl::android::media::audio::common::AudioUuid EqualizerSwImplUUID = {
|
||||
static_cast<int32_t>(0x0bed4300),
|
||||
0x847d,
|
||||
0x11df,
|
||||
0xbb17,
|
||||
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
|
||||
|
||||
class Equalizer : public BnEffect {
|
||||
public:
|
||||
Equalizer() = default;
|
||||
ndk::ScopedAStatus open() override;
|
||||
ndk::ScopedAStatus close() override;
|
||||
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
|
||||
|
||||
private:
|
||||
// Effect descriptor.
|
||||
Descriptor mDesc = {.common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}};
|
||||
};
|
||||
} // namespace aidl::android::hardware::audio::effect
|
||||
86
audio/aidl/default/include/equalizer-impl/EqualizerSw.h
Normal file
86
audio/aidl/default/include/equalizer-impl/EqualizerSw.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 <aidl/android/hardware/audio/effect/BnEffect.h>
|
||||
#include <fmq/AidlMessageQueue.h>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
#include "effect-impl/EffectThread.h"
|
||||
|
||||
namespace aidl::android::hardware::audio::effect {
|
||||
|
||||
class EqualizerSwWorker : public EffectThread {
|
||||
// EqualizerSwWorker(const std::string name){EffectThread(name)};
|
||||
void process() override;
|
||||
};
|
||||
|
||||
class EqualizerSw : public BnEffect {
|
||||
public:
|
||||
EqualizerSw() {
|
||||
// create the worker
|
||||
mWorker = std::make_unique<EqualizerSwWorker>();
|
||||
LOG(DEBUG) << __func__;
|
||||
};
|
||||
~EqualizerSw() {
|
||||
cleanUp();
|
||||
LOG(DEBUG) << __func__;
|
||||
};
|
||||
ndk::ScopedAStatus open(const Parameter::Common& common, const Parameter::Specific& specific,
|
||||
OpenEffectReturn* _aidl_return) override;
|
||||
ndk::ScopedAStatus close() override;
|
||||
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
|
||||
|
||||
ndk::ScopedAStatus getState(State* _aidl_return) override;
|
||||
ndk::ScopedAStatus command(CommandId in_commandId) override;
|
||||
ndk::ScopedAStatus setParameter(const Parameter& in_param) override;
|
||||
ndk::ScopedAStatus getParameter(const Parameter::Id& in_paramId,
|
||||
Parameter* _aidl_return) override;
|
||||
|
||||
private:
|
||||
// effect processing thread.
|
||||
std::unique_ptr<EqualizerSwWorker> mWorker;
|
||||
// Effect descriptor.
|
||||
const Descriptor mDesc = {
|
||||
.common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}};
|
||||
|
||||
// Parameters.
|
||||
Parameter::Common mCommonParam;
|
||||
Equalizer mEqualizerParam; // TODO: the equalizer parameter needs to update
|
||||
|
||||
// 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;
|
||||
|
||||
std::unique_ptr<StatusMQ> mStatusMQ;
|
||||
std::unique_ptr<DataMQ> mInputMQ;
|
||||
std::unique_ptr<DataMQ> mOutputMQ;
|
||||
|
||||
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();
|
||||
void cleanUp();
|
||||
};
|
||||
} // namespace aidl::android::hardware::audio::effect
|
||||
@@ -42,6 +42,36 @@ cc_test {
|
||||
],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "VtsHalAudioEffectFactoryTargetTest",
|
||||
defaults: [
|
||||
"latest_android_media_audio_common_types_ndk_static",
|
||||
"VtsHalTargetTestDefaults",
|
||||
"use_libaidlvintf_gtest_helper_static",
|
||||
],
|
||||
srcs: [
|
||||
"VtsHalAudioEffectFactoryTargetTest.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbinder_ndk",
|
||||
],
|
||||
static_libs: [
|
||||
"android.hardware.audio.effect-V1-ndk",
|
||||
"android.hardware.common-V2-ndk",
|
||||
"android.hardware.common.fmq-V1-ndk",
|
||||
],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-Wthread-safety",
|
||||
],
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
"vts",
|
||||
],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "VtsHalAudioEffectTargetTest",
|
||||
defaults: [
|
||||
@@ -57,6 +87,8 @@ cc_test {
|
||||
],
|
||||
static_libs: [
|
||||
"android.hardware.audio.effect-V1-ndk",
|
||||
"android.hardware.common-V2-ndk",
|
||||
"android.hardware.common.fmq-V1-ndk",
|
||||
],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
|
||||
132
audio/aidl/vts/EffectFactoryHelper.h
Normal file
132
audio/aidl/vts/EffectFactoryHelper.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 <android/binder_auto_utils.h>
|
||||
|
||||
#include "TestUtils.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
using aidl::android::hardware::audio::effect::Descriptor;
|
||||
using aidl::android::hardware::audio::effect::IEffect;
|
||||
using aidl::android::hardware::audio::effect::IFactory;
|
||||
using aidl::android::hardware::audio::effect::Parameter;
|
||||
using aidl::android::media::audio::common::AudioUuid;
|
||||
|
||||
class EffectFactoryHelper {
|
||||
public:
|
||||
explicit EffectFactoryHelper(const std::string& name) : mServiceName(name) {}
|
||||
|
||||
void ConnectToFactoryService() {
|
||||
mEffectFactory = IFactory::fromBinder(binderUtil.connectToService(mServiceName));
|
||||
ASSERT_NE(mEffectFactory, nullptr);
|
||||
}
|
||||
|
||||
void RestartFactoryService() {
|
||||
ASSERT_NE(mEffectFactory, nullptr);
|
||||
mEffectFactory = IFactory::fromBinder(binderUtil.restartService());
|
||||
ASSERT_NE(mEffectFactory, nullptr);
|
||||
ClearEffectMap();
|
||||
}
|
||||
|
||||
void QueryEffects(const std::optional<AudioUuid>& in_type,
|
||||
const std::optional<AudioUuid>& in_instance,
|
||||
std::vector<Descriptor::Identity>* _aidl_return) {
|
||||
ASSERT_NE(mEffectFactory, nullptr);
|
||||
EXPECT_IS_OK(mEffectFactory->queryEffects(in_type, in_instance, _aidl_return));
|
||||
mIds = *_aidl_return;
|
||||
}
|
||||
|
||||
void CreateEffects() {
|
||||
ASSERT_NE(mEffectFactory, nullptr);
|
||||
for (const auto& id : mIds) {
|
||||
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);
|
||||
for (const auto& it : uuid_status) {
|
||||
std::shared_ptr<IEffect> effect;
|
||||
auto status = mEffectFactory->createEffect(it.first.uuid, &effect);
|
||||
EXPECT_STATUS(it.second, status);
|
||||
if (effect) {
|
||||
mEffectIdMap[effect] = it.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyEffectAndExpect(std::shared_ptr<IEffect>& instance, binder_exception_t exception) {
|
||||
ASSERT_NE(mEffectFactory, nullptr);
|
||||
auto status = mEffectFactory->destroyEffect(instance);
|
||||
EXPECT_STATUS(exception, status);
|
||||
}
|
||||
|
||||
void QueryAndCreateAllEffects() {
|
||||
ASSERT_NE(mEffectFactory, nullptr);
|
||||
EXPECT_IS_OK(mEffectFactory->queryEffects(std::nullopt, std::nullopt, &mCompleteIds));
|
||||
for (const auto& id : mCompleteIds) {
|
||||
std::shared_ptr<IEffect> effect;
|
||||
EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
|
||||
EXPECT_NE(effect, nullptr) << id.toString();
|
||||
mEffectIdMap[effect] = id;
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyEffects(const binder_exception_t expected = EX_NONE, const int remaining = 0) {
|
||||
ASSERT_NE(mEffectFactory, nullptr);
|
||||
|
||||
for (auto it = mEffectIdMap.begin(); it != mEffectIdMap.end();) {
|
||||
auto erased = it++;
|
||||
auto status = mEffectFactory->destroyEffect(erased->first);
|
||||
EXPECT_STATUS(expected, status);
|
||||
if (status.isOk()) {
|
||||
mEffectIdMap.erase(erased);
|
||||
}
|
||||
}
|
||||
EXPECT_EQ((unsigned int)remaining, mEffectIdMap.size());
|
||||
}
|
||||
|
||||
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() {
|
||||
return mEffectIdMap;
|
||||
}
|
||||
void ClearEffectMap() { mEffectIdMap.clear(); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<IFactory> mEffectFactory;
|
||||
std::string mServiceName;
|
||||
AudioHalBinderServiceUtil binderUtil;
|
||||
std::vector<Descriptor::Identity> mIds;
|
||||
std::vector<Descriptor::Identity> mCompleteIds;
|
||||
|
||||
std::map<std::shared_ptr<IEffect>, Descriptor::Identity> mEffectIdMap;
|
||||
};
|
||||
235
audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
Normal file
235
audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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 <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define LOG_TAG "VtsHalAudioEffectFactory"
|
||||
|
||||
#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 <aidl/android/hardware/audio/effect/IFactory.h>
|
||||
|
||||
#include "AudioHalBinderServiceUtil.h"
|
||||
#include "EffectFactoryHelper.h"
|
||||
#include "TestUtils.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
using aidl::android::hardware::audio::effect::Descriptor;
|
||||
using aidl::android::hardware::audio::effect::IFactory;
|
||||
using aidl::android::media::audio::common::AudioUuid;
|
||||
|
||||
/// Effect factory testing.
|
||||
class EffectFactoryTest : public testing::TestWithParam<std::string> {
|
||||
public:
|
||||
void SetUp() override { ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService()); }
|
||||
|
||||
void TearDown() override { mFactory.DestroyEffects(); }
|
||||
|
||||
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};
|
||||
};
|
||||
|
||||
TEST_P(EffectFactoryTest, SetupAndTearDown) {
|
||||
// Intentionally empty test body.
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, CanBeRestarted) {
|
||||
ASSERT_NO_FATAL_FAILURE(mFactory.RestartFactoryService());
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, QueriedDescriptorList) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
|
||||
EXPECT_NE(descriptors.size(), 0UL);
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, QueriedDescriptorNotExistType) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(nullUuid, std::nullopt, &descriptors);
|
||||
EXPECT_EQ(descriptors.size(), 0UL);
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(std::nullopt, nullUuid, &descriptors);
|
||||
EXPECT_EQ(descriptors.size(), 0UL);
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, CreateAndDestroyOnce) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
|
||||
auto numIds = mFactory.GetEffectIds().size();
|
||||
EXPECT_NE(numIds, 0UL);
|
||||
|
||||
auto& effectMap = mFactory.GetEffectMap();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(effectMap.size(), numIds);
|
||||
mFactory.DestroyEffects();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, CreateAndDestroyRepeat) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
|
||||
auto numIds = mFactory.GetEffectIds().size();
|
||||
EXPECT_NE(numIds, 0UL);
|
||||
|
||||
auto& effectMap = mFactory.GetEffectMap();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(effectMap.size(), numIds);
|
||||
mFactory.DestroyEffects();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
|
||||
// Create and destroy again
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(effectMap.size(), numIds);
|
||||
mFactory.DestroyEffects();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, CreateMultipleInstanceOfSameEffect) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
|
||||
auto numIds = mFactory.GetEffectIds().size();
|
||||
EXPECT_NE(numIds, 0UL);
|
||||
|
||||
auto& effectMap = mFactory.GetEffectMap();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(effectMap.size(), numIds);
|
||||
// Create effect instances of same implementation
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(effectMap.size(), 2 * numIds);
|
||||
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(effectMap.size(), 3 * numIds);
|
||||
|
||||
mFactory.DestroyEffects();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
}
|
||||
|
||||
// Expect EX_ILLEGAL_ARGUMENT when create with invalid UUID.
|
||||
TEST_P(EffectFactoryTest, CreateWithInvalidUuid) {
|
||||
std::vector<std::pair<Descriptor::Identity, binder_status_t>> descriptors;
|
||||
descriptors.push_back(std::make_pair(nullDesc, EX_ILLEGAL_ARGUMENT));
|
||||
descriptors.push_back(std::make_pair(zeroDesc, EX_ILLEGAL_ARGUMENT));
|
||||
|
||||
auto& effectMap = mFactory.GetEffectMap();
|
||||
mFactory.CreateEffectsAndExpect(descriptors);
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
}
|
||||
|
||||
// Expect EX_ILLEGAL_ARGUMENT when destroy null interface.
|
||||
TEST_P(EffectFactoryTest, DestroyWithInvalidInterface) {
|
||||
std::shared_ptr<IEffect> spDummyEffect(nullptr);
|
||||
|
||||
mFactory.DestroyEffectAndExpect(spDummyEffect, EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, CreateAndRemoveReference) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
|
||||
auto numIds = mFactory.GetEffectIds().size();
|
||||
EXPECT_NE(numIds, 0UL);
|
||||
|
||||
auto& effectMap = mFactory.GetEffectMap();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(effectMap.size(), numIds);
|
||||
// remove all reference
|
||||
mFactory.ClearEffectMap();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, CreateRemoveReferenceAndCreateDestroy) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
|
||||
auto numIds = mFactory.GetEffectIds().size();
|
||||
EXPECT_NE(numIds, 0UL);
|
||||
|
||||
auto& effectMap = mFactory.GetEffectMap();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(effectMap.size(), numIds);
|
||||
// remove all reference
|
||||
mFactory.ClearEffectMap();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
|
||||
// Create and destroy again
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(effectMap.size(), numIds);
|
||||
mFactory.DestroyEffects();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, CreateRestartAndCreateDestroy) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
|
||||
auto numIds = mFactory.GetEffectIds().size();
|
||||
auto& effectMap = mFactory.GetEffectMap();
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(effectMap.size(), numIds);
|
||||
ASSERT_NO_FATAL_FAILURE(mFactory.RestartFactoryService());
|
||||
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(effectMap.size(), numIds);
|
||||
mFactory.DestroyEffects();
|
||||
EXPECT_EQ(effectMap.size(), 0UL);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(EffectFactoryTest, EffectFactoryTest,
|
||||
testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
|
||||
android::PrintInstanceNameToString);
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EffectFactoryTest);
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
ABinderProcess_setThreadPoolMaxThreadCount(1);
|
||||
ABinderProcess_startThreadPool();
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -30,205 +30,68 @@
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/binder_process.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 "TestUtils.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
using ndk::ScopedAStatus;
|
||||
|
||||
using aidl::android::hardware::audio::effect::CommandId;
|
||||
using aidl::android::hardware::audio::effect::Descriptor;
|
||||
using aidl::android::hardware::audio::effect::IEffect;
|
||||
using aidl::android::hardware::audio::effect::IFactory;
|
||||
using aidl::android::media::audio::common::AudioUuid;
|
||||
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 EffectFactoryHelper {
|
||||
public:
|
||||
explicit EffectFactoryHelper(const std::string& name) : mServiceName(name) {}
|
||||
|
||||
void ConnectToFactoryService() {
|
||||
mEffectFactory = IFactory::fromBinder(binderUtil.connectToService(mServiceName));
|
||||
ASSERT_NE(mEffectFactory, nullptr);
|
||||
}
|
||||
|
||||
void RestartFactoryService() {
|
||||
ASSERT_NE(mEffectFactory, nullptr);
|
||||
mEffectFactory = IFactory::fromBinder(binderUtil.restartService());
|
||||
ASSERT_NE(mEffectFactory, nullptr);
|
||||
}
|
||||
|
||||
void QueryAllEffects() {
|
||||
EXPECT_NE(mEffectFactory, nullptr);
|
||||
EXPECT_IS_OK(mEffectFactory->queryEffects(std::nullopt, std::nullopt, &mCompleteIds));
|
||||
}
|
||||
|
||||
void QueryEffects(const std::optional<AudioUuid>& in_type,
|
||||
const std::optional<AudioUuid>& in_instance,
|
||||
std::vector<Descriptor::Identity>* _aidl_return) {
|
||||
EXPECT_NE(mEffectFactory, nullptr);
|
||||
EXPECT_IS_OK(mEffectFactory->queryEffects(in_type, in_instance, _aidl_return));
|
||||
mIds = *_aidl_return;
|
||||
}
|
||||
|
||||
void CreateEffects() {
|
||||
EXPECT_NE(mEffectFactory, nullptr);
|
||||
for (const auto& id : mIds) {
|
||||
std::shared_ptr<IEffect> effect;
|
||||
EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
|
||||
EXPECT_NE(effect, nullptr) << id.toString();
|
||||
mEffectIdMap[effect] = id;
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyEffects() {
|
||||
EXPECT_NE(mEffectFactory, nullptr);
|
||||
for (const auto& it : mEffectIdMap) {
|
||||
EXPECT_IS_OK(mEffectFactory->destroyEffect(it.first));
|
||||
}
|
||||
mEffectIdMap.clear();
|
||||
}
|
||||
|
||||
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::unordered_map<std::shared_ptr<IEffect>, Descriptor::Identity>& GetEffectMap() {
|
||||
return mEffectIdMap;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<IFactory> mEffectFactory;
|
||||
std::string mServiceName;
|
||||
AudioHalBinderServiceUtil binderUtil;
|
||||
std::vector<Descriptor::Identity> mIds;
|
||||
std::vector<Descriptor::Identity> mCompleteIds;
|
||||
std::unordered_map<std::shared_ptr<IEffect>, Descriptor::Identity> mEffectIdMap;
|
||||
};
|
||||
|
||||
/// Effect factory testing.
|
||||
class EffectFactoryTest : public testing::TestWithParam<std::string> {
|
||||
public:
|
||||
void SetUp() override { ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService()); }
|
||||
|
||||
void TearDown() override { mFactory.DestroyEffects(); }
|
||||
|
||||
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}};
|
||||
};
|
||||
|
||||
TEST_P(EffectFactoryTest, SetupAndTearDown) {
|
||||
// Intentionally empty test body.
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, CanBeRestarted) {
|
||||
ASSERT_NO_FATAL_FAILURE(mFactory.RestartFactoryService());
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, QueriedDescriptorList) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
|
||||
EXPECT_NE(descriptors.size(), 0UL);
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, QueriedDescriptorNotExistType) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(nullUuid, std::nullopt, &descriptors);
|
||||
EXPECT_EQ(descriptors.size(), 0UL);
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(std::nullopt, nullUuid, &descriptors);
|
||||
EXPECT_EQ(descriptors.size(), 0UL);
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, CreateAndDestroyRepeat) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
|
||||
auto numIds = mFactory.GetEffectIds().size();
|
||||
EXPECT_NE(numIds, 0UL);
|
||||
|
||||
EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(mFactory.GetEffectMap().size(), numIds);
|
||||
mFactory.DestroyEffects();
|
||||
EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
|
||||
|
||||
// Create and destroy again
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(mFactory.GetEffectMap().size(), numIds);
|
||||
mFactory.DestroyEffects();
|
||||
EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
|
||||
}
|
||||
|
||||
TEST_P(EffectFactoryTest, CreateMultipleInstanceOfSameEffect) {
|
||||
std::vector<Descriptor::Identity> descriptors;
|
||||
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
|
||||
auto numIds = mFactory.GetEffectIds().size();
|
||||
EXPECT_NE(numIds, 0UL);
|
||||
|
||||
EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(mFactory.GetEffectMap().size(), numIds);
|
||||
// Create effect instances of same implementation
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(mFactory.GetEffectMap().size(), 2 * numIds);
|
||||
|
||||
mFactory.CreateEffects();
|
||||
EXPECT_EQ(mFactory.GetEffectMap().size(), 3 * numIds);
|
||||
|
||||
mFactory.DestroyEffects();
|
||||
EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(EffectFactoryTest, EffectFactoryTest,
|
||||
testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
|
||||
android::PrintInstanceNameToString);
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EffectFactoryTest);
|
||||
|
||||
/// Effect testing.
|
||||
class AudioEffect : public testing::TestWithParam<std::string> {
|
||||
public:
|
||||
void SetUp() override {
|
||||
ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService());
|
||||
ASSERT_NO_FATAL_FAILURE(mFactory.CreateEffects());
|
||||
ASSERT_NO_FATAL_FAILURE(mFactoryHelper.ConnectToFactoryService());
|
||||
CreateEffects();
|
||||
initParamCommon();
|
||||
initParamSpecific();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
CloseEffects();
|
||||
ASSERT_NO_FATAL_FAILURE(mFactory.DestroyEffects());
|
||||
DestroyEffects();
|
||||
}
|
||||
|
||||
void OpenEffects() {
|
||||
auto open = [](const std::shared_ptr<IEffect>& effect) { EXPECT_IS_OK(effect->open()); };
|
||||
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() {
|
||||
auto close = [](const std::shared_ptr<IEffect>& effect) { EXPECT_IS_OK(effect->close()); };
|
||||
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;
|
||||
@@ -237,16 +100,101 @@ class AudioEffect : public testing::TestWithParam<std::string> {
|
||||
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, ¶mCommonGet));
|
||||
EXPECT_EQ(paramCommonExpect, paramCommonGet)
|
||||
<< paramCommonExpect.toString() << " vs " << paramCommonGet.toString();
|
||||
};
|
||||
EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
|
||||
}
|
||||
|
||||
template <typename Functor>
|
||||
void ForEachEffect(Functor functor) {
|
||||
auto effectMap = mFactory.GetEffectMap();
|
||||
auto effectMap = mFactoryHelper.GetEffectMap();
|
||||
for (const auto& it : effectMap) {
|
||||
SCOPED_TRACE(it.second.toString());
|
||||
functor(it.first);
|
||||
}
|
||||
}
|
||||
|
||||
EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam());
|
||||
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) {
|
||||
@@ -286,7 +234,7 @@ TEST_P(AudioEffect, DescriptorIdExistAndUnique) {
|
||||
Descriptor desc;
|
||||
std::vector<Descriptor::Identity> idList;
|
||||
EXPECT_IS_OK(effect->getDescriptor(&desc));
|
||||
mFactory.QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList);
|
||||
mFactoryHelper.QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList);
|
||||
EXPECT_EQ(idList.size(), 1UL);
|
||||
};
|
||||
EXPECT_NO_FATAL_FAILURE(ForEachEffect(checker));
|
||||
@@ -295,7 +243,7 @@ TEST_P(AudioEffect, DescriptorIdExistAndUnique) {
|
||||
auto stringHash = [](const Descriptor::Identity& id) {
|
||||
return std::hash<std::string>()(id.toString());
|
||||
};
|
||||
auto vec = mFactory.GetCompleteEffectIdList();
|
||||
auto vec = mFactoryHelper.GetCompleteEffectIdList();
|
||||
std::unordered_set<Descriptor::Identity, decltype(stringHash)> idSet(0, stringHash);
|
||||
for (auto it : vec) {
|
||||
EXPECT_EQ(idSet.count(it), 0UL);
|
||||
@@ -303,6 +251,212 @@ TEST_P(AudioEffect, DescriptorIdExistAndUnique) {
|
||||
}
|
||||
}
|
||||
|
||||
/// State testing.
|
||||
// An effect instance is in INIT state by default after it was created.
|
||||
TEST_P(AudioEffect, 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());
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(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));
|
||||
ExpectState(State::PROCESSING);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
|
||||
EXPECT_NO_FATAL_FAILURE(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));
|
||||
ExpectState(State::PROCESSING);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(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));
|
||||
ExpectState(State::PROCESSING);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(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));
|
||||
ExpectState(State::PROCESSING);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(CloseEffects());
|
||||
ExpectState(State::INIT);
|
||||
}
|
||||
|
||||
// An effect instance shouldn't accept any command before open.
|
||||
TEST_P(AudioEffect, 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));
|
||||
ExpectState(State::INIT);
|
||||
}
|
||||
|
||||
// No-op when receive STOP command in IDLE state.
|
||||
TEST_P(AudioEffect, StopCommandInIdleStateNoOp) {
|
||||
ExpectState(State::INIT);
|
||||
EXPECT_NO_FATAL_FAILURE(OpenEffects());
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(CloseEffects());
|
||||
}
|
||||
|
||||
// No-op when receive STOP command in IDLE state.
|
||||
TEST_P(AudioEffect, ResetCommandInIdleStateNoOp) {
|
||||
ExpectState(State::INIT);
|
||||
EXPECT_NO_FATAL_FAILURE(OpenEffects());
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(CloseEffects());
|
||||
}
|
||||
|
||||
// Repeat START and STOP command.
|
||||
TEST_P(AudioEffect, RepeatStartAndStop) {
|
||||
EXPECT_NO_FATAL_FAILURE(OpenEffects());
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
|
||||
ExpectState(State::PROCESSING);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
|
||||
ExpectState(State::PROCESSING);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(CloseEffects());
|
||||
}
|
||||
|
||||
// Repeat START and RESET command.
|
||||
TEST_P(AudioEffect, RepeatStartAndReset) {
|
||||
EXPECT_NO_FATAL_FAILURE(OpenEffects());
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
|
||||
ExpectState(State::PROCESSING);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
|
||||
ExpectState(State::PROCESSING);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(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));
|
||||
ExpectState(State::PROCESSING);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
|
||||
ExpectState(State::PROCESSING);
|
||||
EXPECT_NO_FATAL_FAILURE(CloseEffects(EX_ILLEGAL_STATE));
|
||||
// cleanup
|
||||
EXPECT_NO_FATAL_FAILURE(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) {
|
||||
// cleanup all effects.
|
||||
EXPECT_NO_FATAL_FAILURE(CloseEffects());
|
||||
ASSERT_NO_FATAL_FAILURE(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());
|
||||
}
|
||||
|
||||
/// 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());
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
// 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));
|
||||
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));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(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));
|
||||
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));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
|
||||
EXPECT_NO_FATAL_FAILURE(CloseEffects());
|
||||
}
|
||||
|
||||
// Multiple instances of same implementation running.
|
||||
TEST_P(AudioEffect, MultipleInstancesRunning) {
|
||||
EXPECT_NO_FATAL_FAILURE(CreateEffects(3));
|
||||
ExpectState(State::INIT);
|
||||
EXPECT_NO_FATAL_FAILURE(OpenEffects());
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(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));
|
||||
ExpectState(State::IDLE);
|
||||
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
|
||||
EXPECT_NO_FATAL_FAILURE(CloseEffects());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(AudioEffectTest, AudioEffect,
|
||||
testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
|
||||
android::PrintInstanceNameToString);
|
||||
|
||||
Reference in New Issue
Block a user