From 0632ae6cd125153936020e70c80aa0f4a763b075 Mon Sep 17 00:00:00 2001 From: Shunkai Yao Date: Wed, 28 Sep 2022 17:37:50 +0000 Subject: [PATCH 1/2] AIDL effect: Add effect AIDL definition Bug: 238913361 Test: atest VtsHalAudioEffectTargetTest; atest VtsHalAudioEffectFactoryTargetTest Merged-In: I8cc902df0a396d0703839b8933318a26aab2e38d Change-Id: I8cc902df0a396d0703839b8933318a26aab2e38d --- audio/aidl/Android.bp | 8 ++ .../hardware/audio/effect/Capability.aidl | 43 +++++++ .../hardware/audio/effect/CommandId.aidl | 50 +++++++++ .../hardware/audio/effect/Descriptor.aidl | 2 + .../hardware/audio/effect/Equalizer.aidl | 46 ++++++++ .../android/hardware/audio/effect/Flags.aidl | 37 ++++++ .../hardware/audio/effect/IEffect.aidl | 18 ++- .../hardware/audio/effect/Parameter.aidl | 62 +++++++++++ .../android/hardware/audio/effect/State.aidl | 40 +++++++ .../hardware/audio/effect/Capability.aidl | 46 ++++++++ .../hardware/audio/effect/CommandId.aidl | 70 ++++++++++++ .../hardware/audio/effect/Descriptor.aidl | 16 ++- .../hardware/audio/effect/Equalizer.aidl | 47 ++++++++ .../android/hardware/audio/effect/Flags.aidl | 26 +++++ .../hardware/audio/effect/IEffect.aidl | 105 +++++++++++++++++- .../hardware/audio/effect/Parameter.aidl | 87 +++++++++++++++ .../android/hardware/audio/effect/State.aidl | 86 ++++++++++++++ .../android/hardware/audio/effect/state.gv | 36 ++++++ 18 files changed, 815 insertions(+), 10 deletions(-) create mode 100644 audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Capability.aidl create mode 100644 audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl create mode 100644 audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl create mode 100644 audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl create mode 100644 audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl create mode 100644 audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl create mode 100644 audio/aidl/android/hardware/audio/effect/Capability.aidl create mode 100644 audio/aidl/android/hardware/audio/effect/CommandId.aidl create mode 100644 audio/aidl/android/hardware/audio/effect/Equalizer.aidl create mode 100644 audio/aidl/android/hardware/audio/effect/Flags.aidl create mode 100644 audio/aidl/android/hardware/audio/effect/Parameter.aidl create mode 100644 audio/aidl/android/hardware/audio/effect/State.aidl create mode 100644 audio/aidl/android/hardware/audio/effect/state.gv diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp index d6e6f50ae9..4f9af12779 100644 --- a/audio/aidl/Android.bp +++ b/audio/aidl/Android.bp @@ -147,11 +147,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", ], diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Capability.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Capability.aidl new file mode 100644 index 0000000000..11acf5e9ed --- /dev/null +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Capability.aidl @@ -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 -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; + } +} diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl new file mode 100644 index 0000000000..79299eeef5 --- /dev/null +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl @@ -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 -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, +} diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl index 94cacd9da6..1c86cf3f3a 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl @@ -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; } } diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl new file mode 100644 index 0000000000..31732d3663 --- /dev/null +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl @@ -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 -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; + } +} diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl new file mode 100644 index 0000000000..af774e8d7d --- /dev/null +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl @@ -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 -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 { +} diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl index 7f868ad36d..e5c96f5164 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl @@ -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 statusMQ; + android.hardware.common.fmq.MQDescriptor inputDataMQ; + android.hardware.common.fmq.MQDescriptor outputDataMQ; + } } diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl new file mode 100644 index 0000000000..16bd3bbd33 --- /dev/null +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl @@ -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 -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; + } +} diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl new file mode 100644 index 0000000000..3176b0103a --- /dev/null +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl @@ -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 -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, +} diff --git a/audio/aidl/android/hardware/audio/effect/Capability.aidl b/audio/aidl/android/hardware/audio/effect/Capability.aidl new file mode 100644 index 0000000000..e792f8652e --- /dev/null +++ b/audio/aidl/android/hardware/audio/effect/Capability.aidl @@ -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; +} diff --git a/audio/aidl/android/hardware/audio/effect/CommandId.aidl b/audio/aidl/android/hardware/audio/effect/CommandId.aidl new file mode 100644 index 0000000000..208c1637b4 --- /dev/null +++ b/audio/aidl/android/hardware/audio/effect/CommandId.aidl @@ -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, +} diff --git a/audio/aidl/android/hardware/audio/effect/Descriptor.aidl b/audio/aidl/android/hardware/audio/effect/Descriptor.aidl index 51b31c2371..aca9bbe326 100644 --- a/audio/aidl/android/hardware/audio/effect/Descriptor.aidl +++ b/audio/aidl/android/hardware/audio/effect/Descriptor.aidl @@ -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; } diff --git a/audio/aidl/android/hardware/audio/effect/Equalizer.aidl b/audio/aidl/android/hardware/audio/effect/Equalizer.aidl new file mode 100644 index 0000000000..309874d183 --- /dev/null +++ b/audio/aidl/android/hardware/audio/effect/Equalizer.aidl @@ -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; +} diff --git a/audio/aidl/android/hardware/audio/effect/Flags.aidl b/audio/aidl/android/hardware/audio/effect/Flags.aidl new file mode 100644 index 0000000000..8add975752 --- /dev/null +++ b/audio/aidl/android/hardware/audio/effect/Flags.aidl @@ -0,0 +1,26 @@ +/* + * 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; + +/** + * 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. +} diff --git a/audio/aidl/android/hardware/audio/effect/IEffect.aidl b/audio/aidl/android/hardware/audio/effect/IEffect.aidl index d7a9501569..9ab8b02249 100644 --- a/audio/aidl/android/hardware/audio/effect/IEffect.aidl +++ b/audio/aidl/android/hardware/audio/effect/IEffect.aidl @@ -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 statusMQ; + // Message queue for input data buffer. + MQDescriptor inputDataMQ; + // Message queue for output data buffer. + MQDescriptor 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); } diff --git a/audio/aidl/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/android/hardware/audio/effect/Parameter.aidl new file mode 100644 index 0000000000..2951660b6e --- /dev/null +++ b/audio/aidl/android/hardware/audio/effect/Parameter.aidl @@ -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; +} diff --git a/audio/aidl/android/hardware/audio/effect/State.aidl b/audio/aidl/android/hardware/audio/effect/State.aidl new file mode 100644 index 0000000000..85a4afc085 --- /dev/null +++ b/audio/aidl/android/hardware/audio/effect/State.aidl @@ -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, +} diff --git a/audio/aidl/android/hardware/audio/effect/state.gv b/audio/aidl/android/hardware/audio/effect/state.gv new file mode 100644 index 0000000000..e19e6c7a1d --- /dev/null +++ b/audio/aidl/android/hardware/audio/effect/state.gv @@ -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"]; +} \ No newline at end of file From ea24c1a128f37e52a23d7ffca2f5b1fc3a4f1ee9 Mon Sep 17 00:00:00 2001 From: Shunkai Yao Date: Wed, 28 Sep 2022 17:39:23 +0000 Subject: [PATCH 2/2] AIDL effect: Add effect AIDL implementationi and vts test Bug: 238913361 Test: atest VtsHalAudioEffectTargetTest; atest VtsHalAudioEffectFactoryTargetTest Merged-In: If8000b7396360996bdfb8eb269bc3de543871673 Change-Id: If8000b7396360996bdfb8eb269bc3de543871673 --- audio/aidl/default/Android.bp | 13 +- audio/aidl/default/EffectFactory.cpp | 135 ++++- audio/aidl/default/EffectThread.cpp | 131 +++++ audio/aidl/default/equalizer/Android.bp | 17 +- audio/aidl/default/equalizer/Equalizer.cpp | 236 +++++++- .../include/effect-impl/EffectThread.h | 59 ++ .../default/include/effect-impl/EffectUUID.h | 45 ++ .../effectFactory-impl/EffectFactory.h | 21 + .../include/equalizer-impl/Equalizer.h | 51 -- .../include/equalizer-impl/EqualizerSw.h | 86 +++ .../include/visualizer-impl/Visualizer.h | 31 -- audio/aidl/vts/Android.bp | 32 ++ audio/aidl/vts/EffectFactoryHelper.h | 132 +++++ .../VtsHalAudioEffectFactoryTargetTest.cpp | 235 ++++++++ .../aidl/vts/VtsHalAudioEffectTargetTest.cpp | 502 ++++++++++++------ 15 files changed, 1435 insertions(+), 291 deletions(-) create mode 100644 audio/aidl/default/EffectThread.cpp create mode 100644 audio/aidl/default/include/effect-impl/EffectThread.h create mode 100644 audio/aidl/default/include/effect-impl/EffectUUID.h delete mode 100644 audio/aidl/default/include/equalizer-impl/Equalizer.h create mode 100644 audio/aidl/default/include/equalizer-impl/EqualizerSw.h delete mode 100644 audio/aidl/default/include/visualizer-impl/Visualizer.h create mode 100644 audio/aidl/vts/EffectFactoryHelper.h create mode 100644 audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp index 53ed908908..c48777f810 100644 --- a/audio/aidl/default/Android.bp +++ b/audio/aidl/default/Android.bp @@ -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", diff --git a/audio/aidl/default/EffectFactory.cpp b/audio/aidl/default/EffectFactory.cpp index a9848fd018..4877956ef7 100644 --- a/audio/aidl/default/EffectFactory.cpp +++ b/audio/aidl/default/EffectFactory.cpp @@ -16,57 +16,156 @@ #define LOG_TAG "AHAL_EffectFactory" #include +#include +#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 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{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& in_type, - const std::optional& 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& in_type_uuid, + const std::optional& in_impl_uuid, std::vector* _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* _aidl_return) { LOG(DEBUG) << __func__ << ": UUID " << in_impl_uuid.toString(); if (in_impl_uuid == EqualizerSwImplUUID) { - *_aidl_return = ndk::SharedRefBase::make(); + 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(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 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(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& in_handle) { + std::weak_ptr wpHandle(in_handle); + // find UUID with key (std::weak_ptr) + 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& 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 diff --git a/audio/aidl/default/EffectThread.cpp b/audio/aidl/default/EffectThread.cpp new file mode 100644 index 0000000000..0ad9a146c6 --- /dev/null +++ b/audio/aidl/default/EffectThread.cpp @@ -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 +#include +#include + +#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 diff --git a/audio/aidl/default/equalizer/Android.bp b/audio/aidl/default/equalizer/Android.bp index b842149241..9c11347941 100644 --- a/audio/aidl/default/equalizer/Android.bp +++ b/audio/aidl/default/equalizer/Android.bp @@ -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", diff --git a/audio/aidl/default/equalizer/Equalizer.cpp b/audio/aidl/default/equalizer/Equalizer.cpp index 8b157faf0f..2e4e538b63 100644 --- a/audio/aidl/default/equalizer/Equalizer.cpp +++ b/audio/aidl/default/equalizer/Equalizer.cpp @@ -14,27 +14,243 @@ * limitations under the License. */ -#define LOG_TAG "AHAL_Equalizer" +#define LOG_TAG "AHAL_EqualizerSw" +#include #include +#include -#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* instanceSpp) { + if (instanceSpp) { + *instanceSpp = ndk::SharedRefBase::make(); + 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& 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()); + } + case Parameter::specific: { + return setSpecificParameter(in_param.get()); + } + 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(mCommonParam); + LOG(DEBUG) << __func__ << " get: " << _aidl_return->toString(); + return ndk::ScopedAStatus::ok(); + } + case Parameter::Id::specificTag: { + auto& id = in_paramId.get(); + 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(mEqualizerParam); + _aidl_return->set(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(statusDepth, true /*configureEventFlagWord*/); + mInputMQ = std::make_unique(inBufferSize); + mOutputMQ = std::make_unique(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(); + 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 diff --git a/audio/aidl/default/include/effect-impl/EffectThread.h b/audio/aidl/default/include/effect-impl/EffectThread.h new file mode 100644 index 0000000000..e831cea4b3 --- /dev/null +++ b/audio/aidl/default/include/effect-impl/EffectThread.h @@ -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 +#include +#include + +#include +#include + +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 diff --git a/audio/aidl/default/include/effect-impl/EffectUUID.h b/audio/aidl/default/include/effect-impl/EffectUUID.h new file mode 100644 index 0000000000..99f6c24d83 --- /dev/null +++ b/audio/aidl/default/include/effect-impl/EffectUUID.h @@ -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 + +namespace aidl::android::hardware::audio::effect { + +using ::aidl::android::media::audio::common::AudioUuid; + +// Equalizer type UUID. +static const AudioUuid EqualizerTypeUUID = {static_cast(0x0bed4300), + 0xddd6, + 0x11db, + 0x8f34, + {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}; + +// Equalizer implementation UUID. +static const AudioUuid EqualizerSwImplUUID = {static_cast(0x0bed4300), + 0x847d, + 0x11df, + 0xbb17, + {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}; + +// Visualizer type UUID. +static const AudioUuid VisualizerTypeUUID = {static_cast(0x1d4033c0), + 0x8557, + 0x11df, + 0x9f2d, + {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}; + +} // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h index 8da55258cf..d3730489bb 100644 --- a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h +++ b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h @@ -16,6 +16,8 @@ #pragma once +#include +#include #include #include @@ -63,7 +65,26 @@ class Factory : public BnFactory { override; private: + ~Factory(); // List of effect descriptors supported by the devices. std::vector mIdentityList; + + typedef binder_exception_t (*EffectCreateFunctor)(std::shared_ptr*); + typedef binder_exception_t (*EffectDestroyFunctor)(const std::shared_ptr&); + struct effect_interface_s { + EffectCreateFunctor createEffectFunc; + EffectDestroyFunctor destroyEffectFunc; + }; + + std::map> /* dlHandle */, + std::unique_ptr>> + mEffectLibMap; + std::map, aidl::android::media::audio::common::AudioUuid, + std::owner_less<>> + mEffectUuidMap; + + ndk::ScopedAStatus destroyEffectImpl(const std::shared_ptr& in_handle); + void cleanupEffectMap(); }; } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/include/equalizer-impl/Equalizer.h b/audio/aidl/default/include/equalizer-impl/Equalizer.h deleted file mode 100644 index ea16cb9fac..0000000000 --- a/audio/aidl/default/include/equalizer-impl/Equalizer.h +++ /dev/null @@ -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 -#include - -namespace aidl::android::hardware::audio::effect { - -// Equalizer type UUID. -static const ::aidl::android::media::audio::common::AudioUuid EqualizerTypeUUID = { - static_cast(0x0bed4300), - 0xddd6, - 0x11db, - 0x8f34, - {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}; - -// Equalizer implementation UUID. -static const ::aidl::android::media::audio::common::AudioUuid EqualizerSwImplUUID = { - static_cast(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 diff --git a/audio/aidl/default/include/equalizer-impl/EqualizerSw.h b/audio/aidl/default/include/equalizer-impl/EqualizerSw.h new file mode 100644 index 0000000000..58ad1dee1d --- /dev/null +++ b/audio/aidl/default/include/equalizer-impl/EqualizerSw.h @@ -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 +#include +#include +#include + +#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(); + 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 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 mStatusMQ; + std::unique_ptr mInputMQ; + std::unique_ptr 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 diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/audio/aidl/default/include/visualizer-impl/Visualizer.h deleted file mode 100644 index 4b82dd0911..0000000000 --- a/audio/aidl/default/include/visualizer-impl/Visualizer.h +++ /dev/null @@ -1,31 +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 - -namespace aidl::android::hardware::audio::effect { - -// Visualizer implementation UUID. -static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = { - static_cast(0x1d4033c0), - 0x8557, - 0x11df, - 0x9f2d, - {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}; - -} // namespace aidl::android::hardware::audio::effect \ No newline at end of file diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp index 6ea7cef0e2..63fc4158b5 100644 --- a/audio/aidl/vts/Android.bp +++ b/audio/aidl/vts/Android.bp @@ -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", diff --git a/audio/aidl/vts/EffectFactoryHelper.h b/audio/aidl/vts/EffectFactoryHelper.h new file mode 100644 index 0000000000..3cbca45eb4 --- /dev/null +++ b/audio/aidl/vts/EffectFactoryHelper.h @@ -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 +#include +#include +#include + +#include + +#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& in_type, + const std::optional& in_instance, + std::vector* _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 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>& uuid_status) { + ASSERT_NE(mEffectFactory, nullptr); + for (const auto& it : uuid_status) { + std::shared_ptr 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& 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 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 GetFactory() { return mEffectFactory; } + const std::vector& GetEffectIds() { return mIds; } + const std::vector& GetCompleteEffectIdList() { return mCompleteIds; } + const std::map, Descriptor::Identity>& GetEffectMap() { + return mEffectIdMap; + } + void ClearEffectMap() { mEffectIdMap.clear(); } + + private: + std::shared_ptr mEffectFactory; + std::string mServiceName; + AudioHalBinderServiceUtil binderUtil; + std::vector mIds; + std::vector mCompleteIds; + + std::map, Descriptor::Identity> mEffectIdMap; +}; diff --git a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp new file mode 100644 index 0000000000..dd17a6fc0d --- /dev/null +++ b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp @@ -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 +#include +#include + +#define LOG_TAG "VtsHalAudioEffectFactory" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 { + 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(0xec7178ec), + 0xe5e1, + 0x4432, + 0xa3f4, + {0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}}; + const AudioUuid zeroUuid = { + static_cast(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 descriptors; + mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors); + EXPECT_NE(descriptors.size(), 0UL); +} + +TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) { + std::vector 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 descriptors; + mFactory.QueryEffects(nullUuid, std::nullopt, &descriptors); + EXPECT_EQ(descriptors.size(), 0UL); +} + +TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) { + std::vector descriptors; + mFactory.QueryEffects(std::nullopt, nullUuid, &descriptors); + EXPECT_EQ(descriptors.size(), 0UL); +} + +TEST_P(EffectFactoryTest, CreateAndDestroyOnce) { + std::vector 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 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 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> 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 spDummyEffect(nullptr); + + mFactory.DestroyEffectAndExpect(spDummyEffect, EX_ILLEGAL_ARGUMENT); +} + +TEST_P(EffectFactoryTest, CreateAndRemoveReference) { + std::vector 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 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 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(); +} \ No newline at end of file diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp index 8b5eb13cdb..23b20bd1fb 100644 --- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp @@ -30,205 +30,68 @@ #include #include +#include #include +#include +#include #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& in_type, - const std::optional& in_instance, - std::vector* _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 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 GetFactory() { return mEffectFactory; } - const std::vector& GetEffectIds() { return mIds; } - const std::vector& GetCompleteEffectIdList() { return mCompleteIds; } - const std::unordered_map, Descriptor::Identity>& GetEffectMap() { - return mEffectIdMap; - } - - private: - std::shared_ptr mEffectFactory; - std::string mServiceName; - AudioHalBinderServiceUtil binderUtil; - std::vector mIds; - std::vector mCompleteIds; - std::unordered_map, Descriptor::Identity> mEffectIdMap; -}; - -/// Effect factory testing. -class EffectFactoryTest : public testing::TestWithParam { - 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(0xec7178ec), - 0xe5e1, - 0x4432, - 0xa3f4, - {0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}}; - const AudioUuid zeroUuid = { - static_cast(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 descriptors; - mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors); - EXPECT_NE(descriptors.size(), 0UL); -} - -TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) { - std::vector 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 descriptors; - mFactory.QueryEffects(nullUuid, std::nullopt, &descriptors); - EXPECT_EQ(descriptors.size(), 0UL); -} - -TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) { - std::vector descriptors; - mFactory.QueryEffects(std::nullopt, nullUuid, &descriptors); - EXPECT_EQ(descriptors.size(), 0UL); -} - -TEST_P(EffectFactoryTest, CreateAndDestroyRepeat) { - std::vector 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 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 { 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& effect) { EXPECT_IS_OK(effect->open()); }; + auto open = [&](const std::shared_ptr& 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& effect) { EXPECT_IS_OK(effect->close()); }; + void CloseEffects(const binder_status_t status = EX_NONE) { + auto close = [&](const std::shared_ptr& 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& effect) { Descriptor desc; @@ -237,16 +100,101 @@ class AudioEffect : public testing::TestWithParam { EXPECT_NO_FATAL_FAILURE(ForEachEffect(get)); } + void CommandEffects(CommandId command) { + auto close = [&](const std::shared_ptr& 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& effect) { + EXPECT_STATUS(status, effect->command(command)); + }; + EXPECT_NO_FATAL_FAILURE(ForEachEffect(func)); + } + + void ExpectState(State expected) { + auto get = [&](const std::shared_ptr& 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& effect) { + Parameter param; + param.set(mCommon); + EXPECT_IS_OK(effect->setParameter(param)); + }; + EXPECT_NO_FATAL_FAILURE(ForEachEffect(func)); + } + + void VerifyParameters() { + auto func = [&](const std::shared_ptr& effect) { + Parameter paramCommonGet = Parameter(), paramCommonExpect = Parameter(); + Parameter::Id id; + id.set(0); + paramCommonExpect.set(mCommon); + EXPECT_IS_OK(effect->getParameter(id, ¶mCommonGet)); + EXPECT_EQ(paramCommonExpect, paramCommonGet) + << paramCommonExpect.toString() << " vs " << paramCommonGet.toString(); + }; + EXPECT_NO_FATAL_FAILURE(ForEachEffect(func)); + } + template 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(); + 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::LAYOUT_STEREO); + AudioChannelLayout mOutputChannelLayout = + AudioChannelLayout::make( + 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 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()(id.toString()); }; - auto vec = mFactory.GetCompleteEffectIdList(); + auto vec = mFactoryHelper.GetCompleteEffectIdList(); std::unordered_set 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);