diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp index 4927e834d8..052411005b 100644 --- a/audio/aidl/Android.bp +++ b/audio/aidl/Android.bp @@ -75,6 +75,7 @@ aidl_interface { "android/hardware/audio/core/IModule.aidl", "android/hardware/audio/core/IStreamIn.aidl", "android/hardware/audio/core/IStreamOut.aidl", + "android/hardware/audio/core/ModuleDebug.aidl", ], imports: [ "android.hardware.audio.common-V1", @@ -86,6 +87,4 @@ aidl_interface { platform_apis: true, }, }, - versions: [ - ], } diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl index 33e8290608..f8bc2c79c6 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl @@ -34,11 +34,15 @@ package android.hardware.audio.core; @VintfStability interface IModule { + void setModuleDebug(in android.hardware.audio.core.ModuleDebug debug); + android.media.audio.common.AudioPort connectExternalDevice(in android.media.audio.common.AudioPort templateIdAndAdditionalData); + void disconnectExternalDevice(int portId); android.hardware.audio.core.AudioPatch[] getAudioPatches(); android.media.audio.common.AudioPort getAudioPort(int portId); android.media.audio.common.AudioPortConfig[] getAudioPortConfigs(); android.media.audio.common.AudioPort[] getAudioPorts(); android.hardware.audio.core.AudioRoute[] getAudioRoutes(); + android.hardware.audio.core.AudioRoute[] getAudioRoutesForAudioPort(int portId); android.hardware.audio.core.IStreamIn openInputStream(int portConfigId, in android.hardware.audio.common.SinkMetadata sinkMetadata); android.hardware.audio.core.IStreamOut openOutputStream(int portConfigId, in android.hardware.audio.common.SourceMetadata sourceMetadata, in @nullable android.media.audio.common.AudioOffloadInfo offloadInfo); android.hardware.audio.core.AudioPatch setAudioPatch(in android.hardware.audio.core.AudioPatch requested); diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl new file mode 100644 index 0000000000..80ee185bc8 --- /dev/null +++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl @@ -0,0 +1,38 @@ +/* + * 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.core; +@JavaDerive(equals=true, toString=true) @VintfStability +parcelable ModuleDebug { + boolean simulateDeviceConnections; +} diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl index d47ea3cf4d..f406cd8ee0 100644 --- a/audio/aidl/android/hardware/audio/core/IModule.aidl +++ b/audio/aidl/android/hardware/audio/core/IModule.aidl @@ -22,6 +22,7 @@ import android.hardware.audio.core.AudioPatch; import android.hardware.audio.core.AudioRoute; import android.hardware.audio.core.IStreamIn; import android.hardware.audio.core.IStreamOut; +import android.hardware.audio.core.ModuleDebug; import android.media.audio.common.AudioOffloadInfo; import android.media.audio.common.AudioPort; import android.media.audio.common.AudioPortConfig; @@ -43,6 +44,116 @@ import android.media.audio.common.AudioPortConfig; */ @VintfStability interface IModule { + /** + * Sets debugging configuration for the HAL module. This method is only + * called during xTS testing and is intended for validating the aspects of + * the HAL module behavior that would otherwise require human intervention. + * + * The HAL module must throw an error if there is an attempt to change + * the debug behavior for the aspect which is currently in use. + * + * @param debug The debug options. + * @throws EX_ILLEGAL_STATE If the flag(s) being changed affect functionality + * which is currently in use. + */ + void setModuleDebug(in ModuleDebug debug); + + /** + * Set a device port of an external device into connected state. + * + * This method is used to inform the HAL module that an external device has + * been connected to a device port selected using the 'id' field of the + * input AudioPort parameter. This device port must have dynamic profiles + * (an empty list of profiles). This port is further referenced to as "port + * template" because it acts as a template for creating a new instance of a + * "connected" device port which gets returned from this method. + * + * The input AudioPort parameter may contain any additional data obtained by + * the system side from other subsystems. The nature of data depends on the + * type of the connection. For example, for point-to-multipoint external + * device connections, the input parameter may contain the address of the + * connected external device. Another example is EDID information for HDMI + * connections (ExtraAudioDescriptor), which can be provided by the HDMI-CEC + * HAL module. + * + * It is the responsibility of the HAL module to query audio profiles + * supported by the connected device and return them as part of the returned + * AudioPort instance. In the case when the HAL is unable to query the + * external device, an error must be thrown. + * + * Thus, the returned audio port instance is the result of combining the + * following information: + * - a unique port ID generated by the HAL module; + * - static information from the port template; + * - list of audio profiles supported by the connected device; + * - additional data from the input AudioPort parameter. + * + * The HAL module must also update the list of audio routes to include the + * ID of the instantiated connected device port. Normally, the connected + * port allows the same routing as the port template. + * + * Also see notes on 'ModuleDebug.simulateDeviceConnections'. + * + * The following protocol is used by HAL module client for handling + * connection of an external device: + * 1. Obtain the list of device ports and their IDs via 'getAudioPorts' + * method. Select the appropriate port template using + * AudioDeviceDescription ('ext.device' field of AudioPort). + * 2. Combine the ID of the port template with any additional data and call + * 'connectExternalDevice'. The HAL module returns a new instance of + * AudioPort created using the rules explained above. Both + * 'getAudioPort' and 'getAudioPorts' methods will be returning the same + * information for this port until disconnection. + * 3. Configure the connected port with one of supported profiles using + * 'setAudioPortConfig'. + * 4. Query the list of AudioRoutes for the new AudioPort using + * 'getAudioRoutesForAudioPort' or 'getAudioRoutes' methods. + * + * External devices are distinguished by the connection type and device + * address. Calling this method multiple times to inform about connection of + * the same external device without disconnecting it first is an error. + * + * The HAL module must perform validation of the input parameter and throw + * an error if it is lacking required information, for example, when no + * device address is specified for a point-to-multipoint external device + * connection. + * + * Handling of a disconnect is done in a reverse order: + * 1. Reset port configuration using the 'resetAudioPortConfig' method. + * 2. Release the connected device port by calling the 'disconnectExternalDevice' + * method. This also removes the audio routes associated with this + * device port. + * + * @return New instance of an audio port for the connected external device. + * @param templateIdAndAdditionalData Specifies port template ID and any + * additional data. + * @throws EX_ILLEGAL_ARGUMENT In the following cases: + * - If the template port can not be found by the ID. + * - If the template is not a device port, or + * it does not have dynamic profiles. + * - If the input parameter is lacking required + * information. + * @throws EX_ILLEGAL_STATE In the following cases: + * - If the HAL module is unable to query audio profiles. + * - If the external device has already been connected. + */ + AudioPort connectExternalDevice(in AudioPort templateIdAndAdditionalData); + + /** + * Set a device port of a an external device into disconnected state. + * + * This method is used to inform the HAL module that an external device has + * been disconnected. The 'portId' must be of a connected device port + * instance previously instantiated using the 'connectExternalDevice' + * method. + * + * @throws EX_ILLEGAL_ARGUMENT In the following cases: + * - If the port can not be found by the ID. + * - If this is not a connected device port. + * @throws EX_ILLEGAL_STATE If the port has active configurations. + */ + void disconnectExternalDevice(int portId); + /** * Return all audio patches of this module. * @@ -57,12 +168,10 @@ interface IModule { * Return the current state of the audio port. * * Using the port ID provided on input, returns the current state of the - * audio port. For device port representing a connection to some external - * device, e.g. over HDMI or USB, currently supported audio profiles and - * extra audio descriptors may change. - * - * For all other audio ports it must be the same configuration as returned - * for this port ID by 'getAudioPorts'. + * audio port. The values of the AudioPort structure must be the same as + * currently returned by the 'getAudioPorts' method. The 'getAudioPort' + * method is provided to reduce overhead in the case when the client needs + * to check the state of one port only. * * @return The current state of an audio port. * @param portId The ID of the audio port. @@ -87,32 +196,44 @@ interface IModule { AudioPortConfig[] getAudioPortConfigs(); /** - * Return all audio ports provided by this module. + * Return the current state of all audio ports provided by this module. * - * Returns a list of all mix ports and device ports provided by this - * module. Each returned port must have a unique ID within this module - * ('AudioPort.id' field). The returned list must not change during - * the lifetime of the IModule instance. For audio ports with dynamic - * profiles (changing depending on external devices being connected - * to the system) an empty list of profiles must be returned. The list - * of currently supported audio profiles is obtained from 'getAudioPort' - * method. + * Returns a list of all mix ports and device ports provided by this HAL + * module, reflecting their current states. Each returned port must have a + * unique ID within this module ('AudioPort.id' field). The list also + * includes "connected" ports created using 'connectExternalDevice' method. * * @return The list of audio ports. */ AudioPort[] getAudioPorts(); /** - * Return all audio routes of this module. + * Return all current audio routes of this module. * - * Returns a list of audio routes, that is, allowed connections between - * audio ports. The returned list must not change during the lifetime of the - * IModule instance. + * Returns the current list of audio routes, that is, allowed connections + * between audio ports. The list can change when new device audio ports + * get created as a result of connecting or disconnecting of external + * devices. * * @return The list of audio routes. */ AudioRoute[] getAudioRoutes(); + /** + * Return audio routes related to the specified audio port. + * + * Returns the list of audio routes that include the specified port ID + * as a source or as a sink. The returned list is a subset of the result + * returned by the 'getAudioRoutes' method, filtered by the port ID. + * An empty list can be returned, indicating that the audio port can not + * be used for creating audio patches. + * + * @return The list of audio routes. + * @param portId The ID of the audio port. + * @throws EX_ILLEGAL_ARGUMENT If the port can not be found by the ID. + */ + AudioRoute[] getAudioRoutesForAudioPort(int portId); + /** * Open an input stream using an existing audio mix port configuration. * @@ -230,6 +351,10 @@ interface IModule { * parameter. The framework can then set the suggested configuration on a * subsequent retry call to this method. * + * Device ports with dynamic audio profiles (an empty list of profiles) + * can not be used with this method. The list of profiles must be filled in + * as a result of calling 'connectExternalDevice' method. + * * @return Whether the requested configuration has been applied. * @param requested Requested audio port configuration. * @param suggested Same as requested configuration, if it was applied. @@ -241,7 +366,7 @@ interface IModule { * - If the port can not be found by the port ID. * - If it is not possible to generate a suggested port * configuration, for example, if the port only has dynamic - * profiles and they are currently empty. + * profiles. */ boolean setAudioPortConfig(in AudioPortConfig requested, out AudioPortConfig suggested); diff --git a/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl b/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl new file mode 100644 index 0000000000..858a9bd841 --- /dev/null +++ b/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl @@ -0,0 +1,38 @@ +/* + * 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.core; + +/** + * This structure contains flags used for enabling various debugging aspects + * in a HAL module. By default, all debugging aspects are turned off. They + * can be enabled during xTS tests for functionality that, for example, would + * otherwise require human intervention (e.g. connection of external devices). + */ +@JavaDerive(equals=true, toString=true) +@VintfStability +parcelable ModuleDebug { + /** + * When set to 'true', HAL module must simulate connection of external + * devices. An external device becomes 'connected' after a call to + * IModule.connectExternalDevice, simulation of connection requires: + * - provision of at least one non-dynamic device port profile on + * connection (as if it was retrieved from a connected device); + * - simulating successful application of port configurations for reported + * profiles. + */ + boolean simulateDeviceConnections; +} diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp index 0a6fe60c39..4728a89cdd 100644 --- a/audio/aidl/default/Android.bp +++ b/audio/aidl/default/Android.bp @@ -13,6 +13,7 @@ cc_library_static { shared_libs: [ "libbase", "libbinder_ndk", + "android.media.audio.common.types-V1-ndk", "android.hardware.audio.core-V1-ndk", ], export_include_dirs: ["include"], @@ -36,6 +37,7 @@ cc_binary { shared_libs: [ "libbase", "libbinder_ndk", + "android.media.audio.common.types-V1-ndk", "android.hardware.audio.core-V1-ndk", ], static_libs: [ diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp index 1104caadcf..19d0b3c93f 100644 --- a/audio/aidl/default/Configuration.cpp +++ b/audio/aidl/default/Configuration.cpp @@ -16,14 +16,15 @@ #include #include +#include #include #include #include -#include "aidl/android/media/audio/common/AudioFormatDescription.h" #include "core-impl/Configuration.h" using aidl::android::media::audio::common::AudioChannelLayout; +using aidl::android::media::audio::common::AudioDeviceDescription; using aidl::android::media::audio::common::AudioDeviceType; using aidl::android::media::audio::common::AudioFormatDescription; using aidl::android::media::audio::common::AudioFormatType; @@ -54,9 +55,11 @@ static AudioProfile createProfile(PcmType pcmType, const std::vector& c return profile; } -static AudioPortExt createDeviceExt(AudioDeviceType devType, int32_t flags) { +static AudioPortExt createDeviceExt(AudioDeviceType devType, int32_t flags, + std::string connection = "") { AudioPortDeviceExt deviceExt; deviceExt.device.type.type = devType; + deviceExt.device.type.connection = std::move(connection); deviceExt.flags = flags; return AudioPortExt::make(deviceExt); } @@ -102,8 +105,64 @@ static AudioRoute createRoute(const std::vector& sources, int32_t sink) return route; } +// Configuration: +// +// Device ports: +// * "Null", OUT_SPEAKER, default +// - no profiles specified +// * "Loopback Out", OUT_SUBMIX +// - profile PCM 24-bit; STEREO; 48000 +// * "USB Out", OUT_DEVICE, CONNECTION_USB +// - no profiles specified +// * "Zero", IN_MICROPHONE, default +// - no profiles specified +// * "Loopback In", IN_SUBMIX +// - profile PCM 24-bit; STEREO; 48000 +// * "USB In", IN_DEVICE, CONNECTION_USB +// - no profiles specified +// +// Mix ports: +// * "primary output", PRIMARY, 1 max open, 1 max active stream +// - profile PCM 16-bit; MONO, STEREO; 44100, 48000 +// - profile PCM 24-bit; MONO, STEREO; 44100, 48000 +// * "loopback output", stream count unlimited +// - profile PCM 24-bit; STEREO; 48000 +// * "primary input", 2 max open, 2 max active streams +// - profile PCM 16-bit; MONO, STEREO, FRONT_BACK; +// 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 +// - profile PCM 24-bit; MONO, STEREO, FRONT_BACK; +// 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 +// * "loopback input", stream count unlimited +// - profile PCM 24-bit; STEREO; 48000 +// +// Routes: +// "primary out" -> "Null" +// "primary out" -> "USB Out" +// "loopback out" -> "Loopback Out" +// "Zero", "USB In" -> "primary input" +// "Loopback In" -> "loopback input" +// +// Initial port configs: +// * "Null" device port: PCM 24-bit; STEREO; 48000 +// * "Zero" device port: PCM 24-bit; MONO; 48000 +// +// Profiles for device port connected state: +// * USB Out": +// - profile PCM 16-bit; MONO, STEREO; 44100, 48000 +// - profile PCM 24-bit; MONO, STEREO; 44100, 48000 +// * USB In": +// - profile PCM 16-bit; MONO, STEREO; 44100, 48000 +// - profile PCM 24-bit; MONO, STEREO; 44100, 48000 +// Configuration& getNullPrimaryConfiguration() { static Configuration configuration = []() { + const std::vector standardPcmAudioProfiles = { + createProfile(PcmType::INT_16_BIT, + {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, + {44100, 48000}), + createProfile(PcmType::INT_24_BIT, + {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, + {44100, 48000})}; Configuration c; AudioPort nullOutDevice = @@ -111,27 +170,19 @@ Configuration& getNullPrimaryConfiguration() { createDeviceExt(AudioDeviceType::OUT_SPEAKER, 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE)); c.ports.push_back(nullOutDevice); - - AudioPort primaryOutMix = createPort(c.nextPortId++, "primary output", - 1 << static_cast(AudioOutputFlags::PRIMARY), - false, createPortMixExt(1, 1)); - primaryOutMix.profiles.push_back( - createProfile(PcmType::INT_16_BIT, - {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, - {44100, 48000})); - primaryOutMix.profiles.push_back( - createProfile(PcmType::INT_24_BIT, - {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, - {44100, 48000})); - c.ports.push_back(primaryOutMix); - - c.routes.push_back(createRoute({primaryOutMix.id}, nullOutDevice.id)); - c.initialConfigs.push_back( createPortConfig(nullOutDevice.id, nullOutDevice.id, PcmType::INT_24_BIT, AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false, createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0))); + AudioPort primaryOutMix = createPort(c.nextPortId++, "primary output", + 1 << static_cast(AudioOutputFlags::PRIMARY), + false, createPortMixExt(1, 1)); + primaryOutMix.profiles.insert(primaryOutMix.profiles.begin(), + standardPcmAudioProfiles.begin(), + standardPcmAudioProfiles.end()); + c.ports.push_back(primaryOutMix); + AudioPort loopOutDevice = createPort(c.nextPortId++, "Loopback Out", 0, false, createDeviceExt(AudioDeviceType::OUT_SUBMIX, 0)); loopOutDevice.profiles.push_back( @@ -144,13 +195,22 @@ Configuration& getNullPrimaryConfiguration() { createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000})); c.ports.push_back(loopOutMix); - c.routes.push_back(createRoute({loopOutMix.id}, loopOutDevice.id)); + AudioPort usbOutDevice = + createPort(c.nextPortId++, "USB Out", 0, false, + createDeviceExt(AudioDeviceType::OUT_DEVICE, 0, + AudioDeviceDescription::CONNECTION_USB)); + c.ports.push_back(usbOutDevice); + c.connectedProfiles[usbOutDevice.id] = standardPcmAudioProfiles; AudioPort zeroInDevice = createPort(c.nextPortId++, "Zero", 0, true, createDeviceExt(AudioDeviceType::IN_MICROPHONE, 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE)); c.ports.push_back(zeroInDevice); + c.initialConfigs.push_back( + createPortConfig(zeroInDevice.id, zeroInDevice.id, PcmType::INT_24_BIT, + AudioChannelLayout::LAYOUT_MONO, 48000, 0, true, + createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0))); AudioPort primaryInMix = createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(2, 2)); @@ -166,13 +226,6 @@ Configuration& getNullPrimaryConfiguration() { {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000})); c.ports.push_back(primaryInMix); - c.routes.push_back(createRoute({zeroInDevice.id}, primaryInMix.id)); - - c.initialConfigs.push_back( - createPortConfig(zeroInDevice.id, zeroInDevice.id, PcmType::INT_24_BIT, - AudioChannelLayout::LAYOUT_MONO, 48000, 0, true, - createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0))); - AudioPort loopInDevice = createPort(c.nextPortId++, "Loopback In", 0, true, createDeviceExt(AudioDeviceType::IN_SUBMIX, 0)); loopInDevice.profiles.push_back( @@ -185,6 +238,16 @@ Configuration& getNullPrimaryConfiguration() { createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000})); c.ports.push_back(loopInMix); + AudioPort usbInDevice = createPort(c.nextPortId++, "USB In", 0, true, + createDeviceExt(AudioDeviceType::IN_DEVICE, 0, + AudioDeviceDescription::CONNECTION_USB)); + c.ports.push_back(usbInDevice); + c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles; + + c.routes.push_back(createRoute({primaryOutMix.id}, nullOutDevice.id)); + c.routes.push_back(createRoute({primaryOutMix.id}, usbOutDevice.id)); + c.routes.push_back(createRoute({loopOutMix.id}, loopOutDevice.id)); + c.routes.push_back(createRoute({zeroInDevice.id, usbInDevice.id}, primaryInMix.id)); c.routes.push_back(createRoute({loopInDevice.id}, loopInMix.id)); c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end()); diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp index e0a68a5fcd..961ee84517 100644 --- a/audio/aidl/default/Module.cpp +++ b/audio/aidl/default/Module.cpp @@ -18,7 +18,6 @@ #include #define LOG_TAG "AHAL_Module" -#define LOG_NDEBUG 0 #include #include @@ -81,6 +80,7 @@ bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& forma } return false; } + } // namespace void Module::cleanUpPatch(int32_t patchId) { @@ -135,6 +135,154 @@ void Module::registerPatch(const AudioPatch& patch) { do_insert(patch.sinkPortConfigIds); } +ndk::ScopedAStatus Module::setModuleDebug( + const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) { + LOG(DEBUG) << __func__ << ": old flags:" << mDebug.toString() + << ", new flags: " << in_debug.toString(); + if (mDebug.simulateDeviceConnections != in_debug.simulateDeviceConnections && + !mConnectedDevicePorts.empty()) { + LOG(ERROR) << __func__ << ": attempting to change device connections simulation " + << "while having external devices connected"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + mDebug = in_debug; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdAndAdditionalData, + AudioPort* _aidl_return) { + const int32_t templateId = in_templateIdAndAdditionalData.id; + auto& ports = getConfig().ports; + AudioPort connectedPort; + { // Scope the template port so that we don't accidentally modify it. + auto templateIt = findById(ports, templateId); + if (templateIt == ports.end()) { + LOG(ERROR) << __func__ << ": port id " << templateId << " not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (templateIt->ext.getTag() != AudioPortExt::Tag::device) { + LOG(ERROR) << __func__ << ": port id " << templateId << " is not a device port"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (!templateIt->profiles.empty()) { + LOG(ERROR) << __func__ << ": port id " << templateId + << " does not have dynamic profiles"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + auto& templateDevicePort = templateIt->ext.get(); + if (templateDevicePort.device.type.connection.empty()) { + LOG(ERROR) << __func__ << ": port id " << templateId << " is permanently attached"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + // Postpone id allocation until we ensure that there are no client errors. + connectedPort = *templateIt; + connectedPort.extraAudioDescriptors = in_templateIdAndAdditionalData.extraAudioDescriptors; + const auto& inputDevicePort = + in_templateIdAndAdditionalData.ext.get(); + auto& connectedDevicePort = connectedPort.ext.get(); + connectedDevicePort.device.address = inputDevicePort.device.address; + LOG(DEBUG) << __func__ << ": device port " << connectedPort.id << " device set to " + << connectedDevicePort.device.toString(); + // Check if there is already a connected port with for the same external device. + for (auto connectedPortId : mConnectedDevicePorts) { + auto connectedPortIt = findById(ports, connectedPortId); + if (connectedPortIt->ext.get().device == + connectedDevicePort.device) { + LOG(ERROR) << __func__ << ": device " << connectedDevicePort.device.toString() + << " is already connected at the device port id " << connectedPortId; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + } + } + + if (!mDebug.simulateDeviceConnections) { + // In a real HAL here we would attempt querying the profiles from the device. + LOG(ERROR) << __func__ << ": failed to query supported device profiles"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + + connectedPort.id = ++getConfig().nextPortId; + mConnectedDevicePorts.insert(connectedPort.id); + LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, " + << "connected port ID " << connectedPort.id; + auto& connectedProfiles = getConfig().connectedProfiles; + if (auto connectedProfilesIt = connectedProfiles.find(templateId); + connectedProfilesIt != connectedProfiles.end()) { + connectedPort.profiles = connectedProfilesIt->second; + } + ports.push_back(connectedPort); + *_aidl_return = std::move(connectedPort); + + std::vector newRoutes; + auto& routes = getConfig().routes; + for (auto& r : routes) { + if (r.sinkPortId == templateId) { + AudioRoute newRoute; + newRoute.sourcePortIds = r.sourcePortIds; + newRoute.sinkPortId = connectedPort.id; + newRoute.isExclusive = r.isExclusive; + newRoutes.push_back(std::move(newRoute)); + } else { + auto& srcs = r.sourcePortIds; + if (std::find(srcs.begin(), srcs.end(), templateId) != srcs.end()) { + srcs.push_back(connectedPort.id); + } + } + } + routes.insert(routes.end(), newRoutes.begin(), newRoutes.end()); + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) { + auto& ports = getConfig().ports; + auto portIt = findById(ports, in_portId); + if (portIt == ports.end()) { + LOG(ERROR) << __func__ << ": port id " << in_portId << " not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (portIt->ext.getTag() != AudioPortExt::Tag::device) { + LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a device port"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (mConnectedDevicePorts.count(in_portId) == 0) { + LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a connected device port"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + auto& configs = getConfig().portConfigs; + auto& initials = getConfig().initialConfigs; + auto configIt = std::find_if(configs.begin(), configs.end(), [&](const auto& config) { + if (config.portId == in_portId) { + // Check if the configuration was provided by the client. + const auto& initialIt = findById(initials, config.id); + return initialIt == initials.end() || config != *initialIt; + } + return false; + }); + if (configIt != configs.end()) { + LOG(ERROR) << __func__ << ": port id " << in_portId << " has a non-default config with id " + << configIt->id; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + ports.erase(portIt); + mConnectedDevicePorts.erase(in_portId); + LOG(DEBUG) << __func__ << ": connected device port " << in_portId << " released"; + + auto& routes = getConfig().routes; + for (auto routesIt = routes.begin(); routesIt != routes.end();) { + if (routesIt->sinkPortId == in_portId) { + routesIt = routes.erase(routesIt); + } else { + // Note: the list of sourcePortIds can't become empty because there must + // be the id of the template port in the route. + erase_if(routesIt->sourcePortIds, [in_portId](auto src) { return src == in_portId; }); + ++routesIt; + } + } + + return ndk::ScopedAStatus::ok(); +} + ndk::ScopedAStatus Module::getAudioPatches(std::vector* _aidl_return) { *_aidl_return = getConfig().patches; LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " patches"; @@ -171,6 +319,23 @@ ndk::ScopedAStatus Module::getAudioRoutes(std::vector* _aidl_return) return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus Module::getAudioRoutesForAudioPort(int32_t in_portId, + std::vector* _aidl_return) { + auto& ports = getConfig().ports; + if (auto portIt = findById(ports, in_portId); portIt == ports.end()) { + LOG(ERROR) << __func__ << ": port id " << in_portId << " not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + auto& routes = getConfig().routes; + std::copy_if(routes.begin(), routes.end(), std::back_inserter(*_aidl_return), + [&](const auto& r) { + const auto& srcs = r.sourcePortIds; + return r.sinkPortId == in_portId || + std::find(srcs.begin(), srcs.end(), in_portId) != srcs.end(); + }); + return ndk::ScopedAStatus::ok(); +} + ndk::ScopedAStatus Module::openInputStream(int32_t in_portConfigId, const SinkMetadata& in_sinkMetadata, std::shared_ptr* _aidl_return) { diff --git a/audio/aidl/default/include/core-impl/Configuration.h b/audio/aidl/default/include/core-impl/Configuration.h index 17e342d80a..d5cd30b248 100644 --- a/audio/aidl/default/include/core-impl/Configuration.h +++ b/audio/aidl/default/include/core-impl/Configuration.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include @@ -29,6 +30,9 @@ struct Configuration { std::vector<::aidl::android::media::audio::common::AudioPort> ports; std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs; std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs; + // Port id -> List of profiles to use when the device port state is set to 'connected'. + std::map> + connectedProfiles; std::vector routes; std::vector patches; int32_t nextPortId = 1; diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h index 359626cc22..81a02baabb 100644 --- a/audio/aidl/default/include/core-impl/Module.h +++ b/audio/aidl/default/include/core-impl/Module.h @@ -18,6 +18,7 @@ #include #include +#include #include @@ -27,6 +28,12 @@ namespace aidl::android::hardware::audio::core { class Module : public BnModule { + ndk::ScopedAStatus setModuleDebug( + const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) override; + ndk::ScopedAStatus connectExternalDevice( + const ::aidl::android::media::audio::common::AudioPort& in_templateIdAndAdditionalData, + ::aidl::android::media::audio::common::AudioPort* _aidl_return) override; + ndk::ScopedAStatus disconnectExternalDevice(int32_t in_portId) override; ndk::ScopedAStatus getAudioPatches(std::vector* _aidl_return) override; ndk::ScopedAStatus getAudioPort( int32_t in_portId, @@ -37,6 +44,9 @@ class Module : public BnModule { ndk::ScopedAStatus getAudioPorts( std::vector<::aidl::android::media::audio::common::AudioPort>* _aidl_return) override; ndk::ScopedAStatus getAudioRoutes(std::vector* _aidl_return) override; + ndk::ScopedAStatus getAudioRoutesForAudioPort( + int32_t in_portId, + std::vector<::aidl::android::hardware::audio::core::AudioRoute>* _aidl_return) override; ndk::ScopedAStatus openInputStream( int32_t in_portConfigId, const ::aidl::android::hardware::audio::common::SinkMetadata& in_sinkMetadata, @@ -63,6 +73,9 @@ class Module : public BnModule { void registerPatch(const AudioPatch& patch); std::unique_ptr mConfig; + ModuleDebug mDebug; + // ids of ports created at runtime via 'connectExternalDevice'. + std::set mConnectedDevicePorts; Streams mStreams; // Maps port ids and port config ids to patch ids. // Multimap because both ports and configs can be used by multiple patches. diff --git a/audio/aidl/default/include/core-impl/utils.h b/audio/aidl/default/include/core-impl/utils.h index 7101012639..9d06f08679 100644 --- a/audio/aidl/default/include/core-impl/utils.h +++ b/audio/aidl/default/include/core-impl/utils.h @@ -38,11 +38,11 @@ auto erase_all(C& c, const V& keys) { return oldSize - c.size(); } -// Erase all the elements in the map that satisfy the provided predicate. +// Erase all the elements in the container that satisfy the provided predicate. template auto erase_if(C& c, P pred) { auto oldSize = c.size(); - for (auto it = c.begin(), last = c.end(); it != last;) { + for (auto it = c.begin(); it != c.end();) { if (pred(*it)) { it = c.erase(it); } else { diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp index 3faa39ab4f..3f8d0888aa 100644 --- a/audio/aidl/vts/ModuleConfig.cpp +++ b/audio/aidl/vts/ModuleConfig.cpp @@ -46,14 +46,16 @@ ModuleConfig::ModuleConfig(IModule* module) { for (const auto& port : mPorts) { if (port.ext.getTag() != AudioPortExt::Tag::device) continue; const auto& devicePort = port.ext.get(); - const bool isInput = port.flags.getTag() == AudioIoFlags::Tag::input; if (devicePort.device.type.connection.empty()) { + const bool isInput = port.flags.getTag() == AudioIoFlags::Tag::input; // Permanently attached device. if (isInput) { mAttachedSourceDevicePorts.insert(port.id); } else { mAttachedSinkDevicePorts.insert(port.id); } + } else if (port.profiles.empty()) { + mExternalDevicePorts.insert(port.id); } } if (!mStatus.isOk()) return; @@ -62,6 +64,22 @@ ModuleConfig::ModuleConfig(IModule* module) { mStatus = module->getAudioPortConfigs(&mInitialConfigs); } +std::vector ModuleConfig::getAttachedDevicePorts() const { + std::vector result; + std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) { + return mAttachedSinkDevicePorts.count(port.id) != 0 || + mAttachedSourceDevicePorts.count(port.id) != 0; + }); + return result; +} + +std::vector ModuleConfig::getExternalDevicePorts() const { + std::vector result; + std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), + [&](const auto& port) { return mExternalDevicePorts.count(port.id) != 0; }); + return result; +} + std::vector ModuleConfig::getInputMixPorts() const { std::vector result; std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [](const auto& port) { @@ -229,6 +247,23 @@ std::vector ModuleConfig::getRoutableSrcSinkGroups(b return result; } +std::string ModuleConfig::toString() const { + std::string result; + result.append("Ports: "); + result.append(android::internal::ToString(mPorts)); + result.append("Initial configs: "); + result.append(android::internal::ToString(mInitialConfigs)); + result.append("Attached sink device ports: "); + result.append(android::internal::ToString(mAttachedSinkDevicePorts)); + result.append("Attached source device ports: "); + result.append(android::internal::ToString(mAttachedSourceDevicePorts)); + result.append("External device ports: "); + result.append(android::internal::ToString(mExternalDevicePorts)); + result.append("Routes: "); + result.append(android::internal::ToString(mRoutes)); + return result; +} + static std::vector combineAudioConfigs(const AudioPort& port, const AudioProfile& profile) { std::vector configs; @@ -319,10 +354,14 @@ std::vector ModuleConfig::generateAudioDevicePortConfigs( if (singleProfile && !result.empty()) return result; } if (resultSizeBefore == result.size()) { - AudioPortConfig empty; - empty.portId = devicePort.id; - empty.ext = devicePort.ext; - result.push_back(empty); + std::copy_if(mInitialConfigs.begin(), mInitialConfigs.end(), std::back_inserter(result), + [&](const auto& config) { return config.portId == devicePort.id; }); + if (resultSizeBefore == result.size()) { + AudioPortConfig empty; + empty.portId = devicePort.id; + empty.ext = devicePort.ext; + result.push_back(empty); + } } if (singleProfile) return result; } diff --git a/audio/aidl/vts/ModuleConfig.h b/audio/aidl/vts/ModuleConfig.h index 2e86b97cf5..0e2738b306 100644 --- a/audio/aidl/vts/ModuleConfig.h +++ b/audio/aidl/vts/ModuleConfig.h @@ -37,6 +37,8 @@ class ModuleConfig { android::binder::Status getStatus() const { return mStatus; } std::string getError() const { return mStatus.toString8().c_str(); } + std::vector getAttachedDevicePorts() const; + std::vector getExternalDevicePorts() const; std::vector getInputMixPorts() const; std::vector getOutputMixPorts() const; std::vector getMixPorts(bool isInput) const { @@ -59,6 +61,10 @@ class ModuleConfig { std::optional getRoutableSrcSinkPair(bool isInput) const; std::vector getRoutableSrcSinkGroups(bool isInput) const; + std::vector + getPortConfigsForAttachedDevicePorts() const { + return generateAudioDevicePortConfigs(getAttachedDevicePorts(), false); + } std::vector getPortConfigsForMixPorts() const { auto inputs = generateInputAudioMixPortConfigs(getInputMixPorts(), false); auto outputs = generateOutputAudioMixPortConfigs(getOutputMixPorts(), false); @@ -98,15 +104,18 @@ class ModuleConfig { } } + std::vector getPortConfigsForDevicePort( + const android::media::audio::common::AudioPort& port) const { + return generateAudioDevicePortConfigs({port}, false); + } android::media::audio::common::AudioPortConfig getSingleConfigForDevicePort( const android::media::audio::common::AudioPort& port) const { - for (const auto& config : mInitialConfigs) { - if (config.portId == port.id) return config; - } const auto config = generateAudioDevicePortConfigs({port}, true); return *config.begin(); } + std::string toString() const; + private: std::vector generateInputAudioMixPortConfigs( const std::vector& ports, @@ -117,7 +126,8 @@ class ModuleConfig { // Unlike MixPorts, the generator for DevicePorts always returns a non-empty // vector for a non-empty input port list. If there are no profiles in the - // port, a vector with an empty config is returned. + // port, its initial configs are looked up, if there are none, + // then an empty config is used, assuming further negotiation via setAudioPortConfig. std::vector generateAudioDevicePortConfigs( const std::vector& ports, bool singleProfile) const; @@ -127,5 +137,6 @@ class ModuleConfig { std::vector mInitialConfigs; std::set mAttachedSinkDevicePorts; std::set mAttachedSourceDevicePorts; + std::set mExternalDevicePorts; std::vector mRoutes; }; diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp index cadeb0c50e..824ac8d3db 100644 --- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp @@ -22,6 +22,9 @@ #include #include +#define LOG_TAG "VtsHalAudioCore" +#include + #include #include #include @@ -45,9 +48,12 @@ using android::hardware::audio::core::AudioRoute; using android::hardware::audio::core::IModule; using android::hardware::audio::core::IStreamIn; using android::hardware::audio::core::IStreamOut; +using android::hardware::audio::core::ModuleDebug; using android::media::audio::common::AudioContentType; using android::media::audio::common::AudioDevice; +using android::media::audio::common::AudioDeviceAddress; using android::media::audio::common::AudioDeviceType; +using android::media::audio::common::AudioFormatType; using android::media::audio::common::AudioIoFlags; using android::media::audio::common::AudioOutputFlags; using android::media::audio::common::AudioPort; @@ -63,7 +69,7 @@ auto findById(std::vector& v, int32_t id) { } template -std::vector getNonExistentIds(const C& allIds) { +std::vector GetNonExistentIds(const C& allIds) { if (allIds.empty()) { return std::vector{-1, 0, 1}; } @@ -73,6 +79,12 @@ std::vector getNonExistentIds(const C& allIds) { return nonExistentIds; } +AudioDeviceAddress GenerateUniqueDeviceAddress() { + static int nextId = 1; + // TODO: Use connection-specific ID. + return AudioDeviceAddress::make(std::to_string(++nextId)); +} + struct AidlDeathRecipient : IBinder::DeathRecipient { std::mutex mutex; std::condition_variable condition; @@ -107,70 +119,28 @@ constexpr IsInput::operator bool() const { return false; } -class AudioCoreModule : public testing::TestWithParam { +class WithDebugFlags { public: - void SetUp() override { ASSERT_NO_FATAL_FAILURE(ConnectToService()); } - - void ConnectToService() { - module = android::waitForDeclaredService(String16(GetParam().c_str())); - ASSERT_NE(module, nullptr); - } - - void RestartService() { - ASSERT_NE(module, nullptr); - moduleConfig.reset(); - deathHandler = sp::make(); - ASSERT_EQ(NO_ERROR, IModule::asBinder(module)->linkToDeath(deathHandler)); - ASSERT_TRUE(base::SetProperty("sys.audio.restart.hal", "1")); - EXPECT_TRUE(deathHandler->waitForFired(3000)); - deathHandler = nullptr; - ASSERT_NO_FATAL_FAILURE(ConnectToService()); - } - - template - void GetAllEntityIds(std::set* entityIds, - Status (IModule::*getter)(std::vector*), - const std::string& errorMessage) { - std::vector entities; - { - Status status = (module.get()->*getter)(&entities); - ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status; - } - std::transform(entities.begin(), entities.end(), - std::inserter(*entityIds, entityIds->begin()), - [](const auto& entity) { return entity.id; }); - EXPECT_EQ(entities.size(), entityIds->size()) << errorMessage; - } - - void GetAllPatchIds(std::set* patchIds) { - return GetAllEntityIds( - patchIds, &IModule::getAudioPatches, - "IDs of audio patches returned by IModule.getAudioPatches are not unique"); - } - - void GetAllPortIds(std::set* portIds) { - return GetAllEntityIds( - portIds, &IModule::getAudioPorts, - "IDs of audio ports returned by IModule.getAudioPorts are not unique"); - } - - void GetAllPortConfigIds(std::set* portConfigIds) { - return GetAllEntityIds( - portConfigIds, &IModule::getAudioPortConfigs, - "IDs of audio port configs returned by IModule.getAudioPortConfigs are not unique"); - } - - void SetUpModuleConfig() { - if (moduleConfig == nullptr) { - moduleConfig = std::make_unique(module.get()); - ASSERT_EQ(Status::EX_NONE, moduleConfig->getStatus().exceptionCode()) - << "ModuleConfig init error: " << moduleConfig->getError(); + WithDebugFlags() {} + explicit WithDebugFlags(const ModuleDebug& initial) : mInitial(initial), mFlags(initial) {} + explicit WithDebugFlags(const WithDebugFlags& initial) + : mInitial(initial.mFlags), mFlags(initial.mFlags) {} + ~WithDebugFlags() { + if (mModule != nullptr) { + Status status = mModule->setModuleDebug(mInitial); + EXPECT_EQ(Status::EX_NONE, status.exceptionCode()) << status; } } + void SetUp(IModule* module) { + Status status = module->setModuleDebug(mFlags); + ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status; + } + ModuleDebug& flags() { return mFlags; } - sp module; - sp deathHandler; - std::unique_ptr moduleConfig; + private: + ModuleDebug mInitial; + ModuleDebug mFlags; + IModule* mModule = nullptr; }; // For consistency, WithAudioPortConfig can start both with a non-existent @@ -226,6 +196,147 @@ class WithAudioPortConfig { AudioPortConfig mConfig; }; +class AudioCoreModule : public testing::TestWithParam { + public: + void SetUp() override { + ASSERT_NO_FATAL_FAILURE(ConnectToService()); + debug.flags().simulateDeviceConnections = true; + ASSERT_NO_FATAL_FAILURE(debug.SetUp(module.get())); + } + + void TearDown() override { + if (module != nullptr) { + Status status = module->setModuleDebug(ModuleDebug{}); + EXPECT_EQ(Status::EX_NONE, status.exceptionCode()) + << status << " returned when resetting debug flags"; + } + } + + void ConnectToService() { + module = android::waitForDeclaredService(String16(GetParam().c_str())); + ASSERT_NE(module, nullptr); + } + + void RestartService() { + ASSERT_NE(module, nullptr); + moduleConfig.reset(); + deathHandler = sp::make(); + ASSERT_EQ(NO_ERROR, IModule::asBinder(module)->linkToDeath(deathHandler)); + ASSERT_TRUE(base::SetProperty("sys.audio.restart.hal", "1")); + EXPECT_TRUE(deathHandler->waitForFired(3000)); + deathHandler = nullptr; + ASSERT_NO_FATAL_FAILURE(ConnectToService()); + } + + void ApplyEveryConfig(const std::vector& configs) { + for (const auto& config : configs) { + ASSERT_NE(0, config.portId); + WithAudioPortConfig portConfig(config); + ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get())); // calls setAudioPortConfig + EXPECT_EQ(config.portId, portConfig.get().portId); + std::vector retrievedPortConfigs; + Status status = module->getAudioPortConfigs(&retrievedPortConfigs); + ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status; + const int32_t portConfigId = portConfig.getId(); + auto configIt = std::find_if( + retrievedPortConfigs.begin(), retrievedPortConfigs.end(), + [&portConfigId](const auto& retrConf) { return retrConf.id == portConfigId; }); + EXPECT_NE(configIt, retrievedPortConfigs.end()) + << "Port config id returned by setAudioPortConfig: " << portConfigId + << " is not found in the list returned by getAudioPortConfigs"; + if (configIt != retrievedPortConfigs.end()) { + EXPECT_EQ(portConfig.get(), *configIt) + << "Applied port config returned by setAudioPortConfig: " + << portConfig.get().toString() + << " is not the same as retrieved via getAudioPortConfigs: " + << configIt->toString(); + } + } + } + + template + void GetAllEntityIds(std::set* entityIds, + Status (IModule::*getter)(std::vector*), + const std::string& errorMessage) { + std::vector entities; + { + Status status = (module.get()->*getter)(&entities); + ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status; + } + std::transform(entities.begin(), entities.end(), + std::inserter(*entityIds, entityIds->begin()), + [](const auto& entity) { return entity.id; }); + EXPECT_EQ(entities.size(), entityIds->size()) << errorMessage; + } + + void GetAllPatchIds(std::set* patchIds) { + return GetAllEntityIds( + patchIds, &IModule::getAudioPatches, + "IDs of audio patches returned by IModule.getAudioPatches are not unique"); + } + + void GetAllPortIds(std::set* portIds) { + return GetAllEntityIds( + portIds, &IModule::getAudioPorts, + "IDs of audio ports returned by IModule.getAudioPorts are not unique"); + } + + void GetAllPortConfigIds(std::set* portConfigIds) { + return GetAllEntityIds( + portConfigIds, &IModule::getAudioPortConfigs, + "IDs of audio port configs returned by IModule.getAudioPortConfigs are not unique"); + } + + void SetUpModuleConfig() { + if (moduleConfig == nullptr) { + moduleConfig = std::make_unique(module.get()); + ASSERT_EQ(Status::EX_NONE, moduleConfig->getStatus().exceptionCode()) + << "ModuleConfig init error: " << moduleConfig->getError(); + } + } + + sp module; + sp deathHandler; + std::unique_ptr moduleConfig; + WithDebugFlags debug; +}; + +class WithDevicePortConnectedState { + public: + explicit WithDevicePortConnectedState(const AudioPort& idAndData) : mIdAndData(idAndData) {} + WithDevicePortConnectedState(const AudioPort& id, const AudioDeviceAddress& address) + : mIdAndData(setAudioPortAddress(id, address)) {} + ~WithDevicePortConnectedState() { + if (mModule != nullptr) { + Status status = mModule->disconnectExternalDevice(getId()); + EXPECT_EQ(Status::EX_NONE, status.exceptionCode()) + << status << " returned when disconnecting device port ID " << getId(); + } + } + void SetUp(IModule* module) { + Status status = module->connectExternalDevice(mIdAndData, &mConnectedPort); + ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) + << status << " returned when connecting device port ID & data " + << mIdAndData.toString(); + ASSERT_NE(mIdAndData.id, getId()) + << "ID of the connected port must not be the same as the ID of the template port"; + mModule = module; + } + int32_t getId() const { return mConnectedPort.id; } + const AudioPort& get() { return mConnectedPort; } + + private: + static AudioPort setAudioPortAddress(const AudioPort& id, const AudioDeviceAddress& address) { + AudioPort result = id; + result.ext.get().device.address = address; + return result; + } + + const AudioPort mIdAndData; + IModule* mModule = nullptr; + AudioPort mConnectedPort; +}; + template class WithStream { public: @@ -332,7 +443,7 @@ TEST_P(AudioCoreModule, PortIdsAreUnique) { ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds)); } -TEST_P(AudioCoreModule, GetAudioPortsIsStatic) { +TEST_P(AudioCoreModule, GetAudioPortsIsStable) { std::vector ports1; { Status status = module->getAudioPorts(&ports1); @@ -344,13 +455,13 @@ TEST_P(AudioCoreModule, GetAudioPortsIsStatic) { ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status; } ASSERT_EQ(ports1.size(), ports2.size()) - << "Sizes of audio port arrays do not match across calls to getAudioPorts"; + << "Sizes of audio port arrays do not match across consequent calls to getAudioPorts"; std::sort(ports1.begin(), ports1.end()); std::sort(ports2.begin(), ports2.end()); EXPECT_EQ(ports1, ports2); } -TEST_P(AudioCoreModule, GetAudioRoutesIsStatic) { +TEST_P(AudioCoreModule, GetAudioRoutesIsStable) { std::vector routes1; { Status status = module->getAudioRoutes(&routes1); @@ -362,7 +473,7 @@ TEST_P(AudioCoreModule, GetAudioRoutesIsStatic) { ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status; } ASSERT_EQ(routes1.size(), routes2.size()) - << "Sizes of audio route arrays do not match across calls to getAudioRoutes"; + << "Sizes of audio route arrays do not match across consequent calls to getAudioRoutes"; std::sort(routes1.begin(), routes1.end()); std::sort(routes2.begin(), routes2.end()); EXPECT_EQ(routes1, routes2); @@ -401,6 +512,32 @@ TEST_P(AudioCoreModule, GetAudioRoutesPortIdsAreValid) { } } +TEST_P(AudioCoreModule, GetAudioRoutesForAudioPort) { + std::set portIds; + ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds)); + if (portIds.empty()) { + GTEST_SKIP() << "No ports in the module."; + } + for (const auto portId : portIds) { + std::vector routes; + Status status = module->getAudioRoutesForAudioPort(portId, &routes); + EXPECT_EQ(Status::EX_NONE, status.exceptionCode()) << status; + for (const auto& r : routes) { + if (r.sinkPortId != portId) { + const auto& srcs = r.sourcePortIds; + EXPECT_TRUE(std::find(srcs.begin(), srcs.end(), portId) != srcs.end()) + << " port ID " << portId << " does not used by the route " << r.toString(); + } + } + } + for (const auto portId : GetNonExistentIds(portIds)) { + std::vector routes; + Status status = module->getAudioRoutesForAudioPort(portId, &routes); + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) + << status << " returned for port ID " << portId; + } +} + TEST_P(AudioCoreModule, CheckDevicePorts) { std::vector ports; { @@ -486,7 +623,7 @@ TEST_P(AudioCoreModule, GetAudioPort) { EXPECT_EQ(Status::EX_NONE, status.exceptionCode()) << status; EXPECT_EQ(portId, port.id); } - for (const auto portId : getNonExistentIds(portIds)) { + for (const auto portId : GetNonExistentIds(portIds)) { AudioPort port; Status status = module->getAudioPort(portId, &port); EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) @@ -494,10 +631,59 @@ TEST_P(AudioCoreModule, GetAudioPort) { } } +// Verify that HAL module reports for a connected device port at least one non-dynamic profile, +// that is, a profile with actual supported configuration. +// Note: This test relies on simulation of external device connections by the HAL module. +TEST_P(AudioCoreModule, GetAudioPortWithExternalDevices) { + ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); + std::vector ports = moduleConfig->getExternalDevicePorts(); + if (ports.empty()) { + GTEST_SKIP() << "No external devices in the module."; + } + for (const auto& port : ports) { + AudioPort portWithData = port; + portWithData.ext.get().device.address = + GenerateUniqueDeviceAddress(); + WithDevicePortConnectedState portConnected(portWithData); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + const int32_t connectedPortId = portConnected.getId(); + ASSERT_NE(portWithData.id, connectedPortId); + ASSERT_EQ(portWithData.ext.getTag(), portConnected.get().ext.getTag()); + EXPECT_EQ(portWithData.ext.get().device, + portConnected.get().ext.get().device); + // Verify that 'getAudioPort' and 'getAudioPorts' return the same connected port. + AudioPort connectedPort; + Status status = module->getAudioPort(connectedPortId, &connectedPort); + EXPECT_EQ(Status::EX_NONE, status.exceptionCode()) + << status << " returned for getAudioPort port ID " << connectedPortId; + EXPECT_EQ(portConnected.get(), connectedPort); + const auto& portProfiles = connectedPort.profiles; + EXPECT_NE(0, portProfiles.size()) + << "Connected port has no profiles: " << connectedPort.toString(); + const auto dynamicProfileIt = + std::find_if(portProfiles.begin(), portProfiles.end(), [](const auto& profile) { + return profile.format.type == AudioFormatType::DEFAULT; + }); + EXPECT_EQ(portProfiles.end(), dynamicProfileIt) << "Connected port contains dynamic " + << "profiles: " << connectedPort.toString(); + + std::vector allPorts; + { + Status status = module->getAudioPorts(&allPorts); + ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status; + } + const auto allPortsIt = findById(allPorts, connectedPortId); + EXPECT_NE(allPorts.end(), allPortsIt); + if (allPortsIt != allPorts.end()) { + EXPECT_EQ(portConnected.get(), *allPortsIt); + } + } +} + TEST_P(AudioCoreModule, OpenStreamInvalidPortConfigId) { std::set portConfigIds; ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds)); - for (const auto portConfigId : getNonExistentIds(portConfigIds)) { + for (const auto portConfigId : GetNonExistentIds(portConfigIds)) { { sp stream; Status status = module->openInputStream(portConfigId, {}, &stream); @@ -537,7 +723,7 @@ TEST_P(AudioCoreModule, PortConfigPortIdsAreValid) { TEST_P(AudioCoreModule, ResetAudioPortConfigInvalidId) { std::set portConfigIds; ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds)); - for (const auto portConfigId : getNonExistentIds(portConfigIds)) { + for (const auto portConfigId : GetNonExistentIds(portConfigIds)) { Status status = module->resetAudioPortConfig(portConfigId); EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) << status << " returned for port config ID " << portConfigId; @@ -608,39 +794,35 @@ TEST_P(AudioCoreModule, SetAudioPortConfigSuggestedConfig) { EXPECT_EQ(suggestedConfig.flags.value(), appliedConfig.flags.value()); } +TEST_P(AudioCoreModule, SetAllAttachedDevicePortConfigs) { + ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); + ASSERT_NO_FATAL_FAILURE(ApplyEveryConfig(moduleConfig->getPortConfigsForAttachedDevicePorts())); +} + +// Note: This test relies on simulation of external device connections by the HAL module. +TEST_P(AudioCoreModule, SetAllExternalDevicePortConfigs) { + ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); + std::vector ports = moduleConfig->getExternalDevicePorts(); + if (ports.empty()) { + GTEST_SKIP() << "No external devices in the module."; + } + for (const auto& port : ports) { + WithDevicePortConnectedState portConnected(port, GenerateUniqueDeviceAddress()); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + ASSERT_NO_FATAL_FAILURE( + ApplyEveryConfig(moduleConfig->getPortConfigsForDevicePort(portConnected.get()))); + } +} + TEST_P(AudioCoreModule, SetAllStaticAudioPortConfigs) { ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); - const auto allPortConfigs = moduleConfig->getPortConfigsForMixPorts(); - for (const auto& config : allPortConfigs) { - ASSERT_NE(0, config.portId); - WithAudioPortConfig portConfig(config); - ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get())); - EXPECT_EQ(config.portId, portConfig.get().portId); - std::vector retrievedPortConfigs; - { - Status status = module->getAudioPortConfigs(&retrievedPortConfigs); - ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status; - } - const int32_t portConfigId = portConfig.getId(); - auto configIt = std::find_if( - retrievedPortConfigs.begin(), retrievedPortConfigs.end(), - [&portConfigId](const auto& retrConf) { return retrConf.id == portConfigId; }); - EXPECT_NE(configIt, retrievedPortConfigs.end()) - << "Port config id returned by setAudioPortConfig: " << portConfigId - << " is not found in the list returned by getPortConfigsForMixPorts"; - if (configIt != retrievedPortConfigs.end()) { - EXPECT_EQ(portConfig.get(), *configIt) - << "Port config returned by getPortConfigsForMixPorts: " << configIt->toString() - << " is not the same as returned by setAudioPortConfig: " - << portConfig.get().toString(); - } - } + ASSERT_NO_FATAL_FAILURE(ApplyEveryConfig(moduleConfig->getPortConfigsForMixPorts())); } TEST_P(AudioCoreModule, SetAudioPortConfigInvalidPortId) { std::set portIds; ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds)); - for (const auto portId : getNonExistentIds(portIds)) { + for (const auto portId : GetNonExistentIds(portIds)) { AudioPortConfig portConfig, suggestedConfig; bool applied; portConfig.portId = portId; @@ -656,7 +838,7 @@ TEST_P(AudioCoreModule, SetAudioPortConfigInvalidPortId) { TEST_P(AudioCoreModule, SetAudioPortConfigInvalidPortConfigId) { std::set portConfigIds; ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds)); - for (const auto portConfigId : getNonExistentIds(portConfigIds)) { + for (const auto portConfigId : GetNonExistentIds(portConfigIds)) { AudioPortConfig portConfig, suggestedConfig; bool applied; portConfig.id = portConfigId; @@ -669,6 +851,203 @@ TEST_P(AudioCoreModule, SetAudioPortConfigInvalidPortConfigId) { } } +TEST_P(AudioCoreModule, TryConnectMissingDevice) { + ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); + std::vector ports = moduleConfig->getExternalDevicePorts(); + if (ports.empty()) { + GTEST_SKIP() << "No external devices in the module."; + } + AudioPort ignored; + WithDebugFlags doNotSimulateConnections(debug); + doNotSimulateConnections.flags().simulateDeviceConnections = false; + ASSERT_NO_FATAL_FAILURE(doNotSimulateConnections.SetUp(module.get())); + for (const auto& port : ports) { + AudioPort portWithData = port; + portWithData.ext.get().device.address = + GenerateUniqueDeviceAddress(); + Status status = module->connectExternalDevice(portWithData, &ignored); + EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode()) + << status << " returned for static port " << portWithData.toString(); + } +} + +TEST_P(AudioCoreModule, TryChangingConnectionSimulationMidway) { + ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); + std::vector ports = moduleConfig->getExternalDevicePorts(); + if (ports.empty()) { + GTEST_SKIP() << "No external devices in the module."; + } + WithDevicePortConnectedState portConnected(*ports.begin(), GenerateUniqueDeviceAddress()); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + ModuleDebug midwayDebugChange = debug.flags(); + midwayDebugChange.simulateDeviceConnections = false; + Status status = module->setModuleDebug(midwayDebugChange); + EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode()) + << status << " returned when trying to disable connections simulation " + << "while having a connected device"; +} + +TEST_P(AudioCoreModule, ConnectDisconnectExternalDeviceInvalidPorts) { + AudioPort ignored; + std::set portIds; + ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds)); + for (const auto portId : GetNonExistentIds(portIds)) { + AudioPort invalidPort; + invalidPort.id = portId; + Status status = module->connectExternalDevice(invalidPort, &ignored); + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) + << status << " returned for port ID " << portId << " when setting CONNECTED state"; + status = module->disconnectExternalDevice(portId); + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) + << status << " returned for port ID " << portId + << " when setting DISCONNECTED state"; + } + + std::vector ports; + { + Status status = module->getAudioPorts(&ports); + ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status; + } + for (const auto& port : ports) { + if (port.ext.getTag() != AudioPortExt::Tag::device) { + Status status = module->connectExternalDevice(port, &ignored); + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) + << status << " returned for non-device port ID " << port.id + << " when setting CONNECTED state"; + status = module->disconnectExternalDevice(port.id); + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) + << status << " returned for non-device port ID " << port.id + << " when setting DISCONNECTED state"; + } else { + const auto& devicePort = port.ext.get(); + if (devicePort.device.type.connection.empty()) { + Status status = module->connectExternalDevice(port, &ignored); + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) + << status << " returned for permanently attached device port ID " << port.id + << " when setting CONNECTED state"; + status = module->disconnectExternalDevice(port.id); + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) + << status << " returned for permanently attached device port ID " << port.id + << " when setting DISCONNECTED state"; + } + } + } +} + +// Note: This test relies on simulation of external device connections by the HAL module. +TEST_P(AudioCoreModule, ConnectDisconnectExternalDeviceTwice) { + ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); + AudioPort ignored; + std::vector ports = moduleConfig->getExternalDevicePorts(); + if (ports.empty()) { + GTEST_SKIP() << "No external devices in the module."; + } + for (const auto& port : ports) { + Status status = module->disconnectExternalDevice(port.id); + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) + << status << " returned when disconnecting already disconnected device port ID " + << port.id; + AudioPort portWithData = port; + portWithData.ext.get().device.address = + GenerateUniqueDeviceAddress(); + WithDevicePortConnectedState portConnected(portWithData); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + status = module->connectExternalDevice(portConnected.get(), &ignored); + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) + << status << " returned when trying to connect a connected device port " + << portConnected.get().toString(); + status = module->connectExternalDevice(portWithData, &ignored); + EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode()) + << status << " returned when connecting again the external device " + << portWithData.ext.get().device.toString(); + if (status.exceptionCode() == Status::EX_NONE) { + ADD_FAILURE() << "Returned connected port " << ignored.toString() << " for template " + << portWithData.toString(); + } + } +} + +// Note: This test relies on simulation of external device connections by the HAL module. +TEST_P(AudioCoreModule, DisconnectExternalDeviceNonResetPortConfig) { + ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); + std::vector ports = moduleConfig->getExternalDevicePorts(); + if (ports.empty()) { + GTEST_SKIP() << "No external devices in the module."; + } + for (const auto& port : ports) { + WithDevicePortConnectedState portConnected(port, GenerateUniqueDeviceAddress()); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + const auto portConfig = moduleConfig->getSingleConfigForDevicePort(portConnected.get()); + { + WithAudioPortConfig config(portConfig); + // Note: if SetUp fails, check the status of 'GetAudioPortWithExternalDevices' test. + // Our test assumes that 'getAudioPort' returns at least one profile, and it + // is not a dynamic profile. + ASSERT_NO_FATAL_FAILURE(config.SetUp(module.get())); + Status status = module->disconnectExternalDevice(portConnected.getId()); + EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode()) + << status << " returned when trying to disconnect device port ID " << port.id + << " with active configuration " << config.getId(); + } + } +} + +TEST_P(AudioCoreModule, ExternalDevicePortRoutes) { + ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); + std::vector ports = moduleConfig->getExternalDevicePorts(); + if (ports.empty()) { + GTEST_SKIP() << "No external devices in the module."; + } + for (const auto& port : ports) { + std::vector routesBefore; + { + Status status = module->getAudioRoutes(&routesBefore); + ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status; + } + + int32_t connectedPortId; + { + WithDevicePortConnectedState portConnected(port, GenerateUniqueDeviceAddress()); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + connectedPortId = portConnected.getId(); + std::vector connectedPortRoutes; + { + Status status = + module->getAudioRoutesForAudioPort(connectedPortId, &connectedPortRoutes); + ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) + << status << " returned when retrieving routes for connected port id " + << connectedPortId; + } + // There must be routes for the port to be useful. + if (connectedPortRoutes.empty()) { + std::vector allRoutes; + Status status = module->getAudioRoutes(&allRoutes); + ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status; + ADD_FAILURE() << " no routes returned for the connected port " + << portConnected.get().toString() + << "; all routes: " << android::internal::ToString(allRoutes); + } + } + std::vector ignored; + Status status = module->getAudioRoutesForAudioPort(connectedPortId, &ignored); + ASSERT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) + << status << " returned when retrieving routes for released connected port id " + << connectedPortId; + + std::vector routesAfter; + { + Status status = module->getAudioRoutes(&routesAfter); + ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status; + } + ASSERT_EQ(routesBefore.size(), routesAfter.size()) + << "Sizes of audio route arrays do not match after creating and " + << "releasing a connected port"; + std::sort(routesBefore.begin(), routesBefore.end()); + std::sort(routesAfter.begin(), routesAfter.end()); + EXPECT_EQ(routesBefore, routesAfter); + } +} + template class AudioStream : public AudioCoreModule { public: @@ -899,7 +1278,7 @@ class AudioModulePatch : public AudioCoreModule { std::set portConfigIds; ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds)); - for (const auto portConfigId : getNonExistentIds(portConfigIds)) { + for (const auto portConfigId : GetNonExistentIds(portConfigIds)) { EXPECT_NO_FATAL_FAILURE(SetInvalidPatchHelper( Status::EX_ILLEGAL_ARGUMENT, {portConfigId}, {sinkPortConfig.getId()})); EXPECT_NO_FATAL_FAILURE(SetInvalidPatchHelper(Status::EX_ILLEGAL_ARGUMENT, @@ -969,7 +1348,7 @@ class AudioModulePatch : public AudioCoreModule { // Then use the same patch setting, except for having an invalid ID. std::set patchIds; ASSERT_NO_FATAL_FAILURE(GetAllPatchIds(&patchIds)); - for (const auto patchId : getNonExistentIds(patchIds)) { + for (const auto patchId : GetNonExistentIds(patchIds)) { AudioPatch patchWithNonExistendId = patch.get(); patchWithNonExistendId.id = patchId; Status status = module->setAudioPatch(patchWithNonExistendId, &patchWithNonExistendId); @@ -995,7 +1374,7 @@ TEST_PATCH_BOTH_DIRECTIONS(UpdatePatch); TEST_P(AudioModulePatch, ResetInvalidPatchId) { std::set patchIds; ASSERT_NO_FATAL_FAILURE(GetAllPatchIds(&patchIds)); - for (const auto patchId : getNonExistentIds(patchIds)) { + for (const auto patchId : GetNonExistentIds(patchIds)) { Status status = module->resetAudioPatch(patchId); EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode()) << status << " returned for patch ID " << patchId;