Merge "audio HAL: Support for external device connections"

This commit is contained in:
Treehugger Robot
2022-08-24 15:01:49 +00:00
committed by Gerrit Code Review
14 changed files with 1038 additions and 158 deletions

View File

@@ -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: [
],
}

View File

@@ -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);

View File

@@ -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 <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.audio.core;
@JavaDerive(equals=true, toString=true) @VintfStability
parcelable ModuleDebug {
boolean simulateDeviceConnections;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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: [

View File

@@ -16,14 +16,15 @@
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include <aidl/android/media/audio/common/AudioDeviceType.h>
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
#include <aidl/android/media/audio/common/AudioFormatType.h>
#include <aidl/android/media/audio/common/AudioIoFlags.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#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<int32_t>& 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<AudioPortExt::Tag::device>(deviceExt);
}
@@ -102,8 +105,64 @@ static AudioRoute createRoute(const std::vector<int32_t>& 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<AudioProfile> 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<int32_t>(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<int32_t>(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());

View File

@@ -18,7 +18,6 @@
#include <set>
#define LOG_TAG "AHAL_Module"
#define LOG_NDEBUG 0
#include <android-base/logging.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
@@ -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<AudioPort>(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<AudioPortExt::Tag::device>();
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<AudioPortExt::Tag::device>();
auto& connectedDevicePort = connectedPort.ext.get<AudioPortExt::Tag::device>();
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<AudioPort>(ports, connectedPortId);
if (connectedPortIt->ext.get<AudioPortExt::Tag::device>().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<AudioRoute> 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<AudioPort>(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<AudioPortConfig>(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<AudioPatch>* _aidl_return) {
*_aidl_return = getConfig().patches;
LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " patches";
@@ -171,6 +319,23 @@ ndk::ScopedAStatus Module::getAudioRoutes(std::vector<AudioRoute>* _aidl_return)
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Module::getAudioRoutesForAudioPort(int32_t in_portId,
std::vector<AudioRoute>* _aidl_return) {
auto& ports = getConfig().ports;
if (auto portIt = findById<AudioPort>(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<IStreamIn>* _aidl_return) {

View File

@@ -16,6 +16,7 @@
#pragma once
#include <map>
#include <vector>
#include <aidl/android/hardware/audio/core/AudioPatch.h>
@@ -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<int32_t, std::vector<::aidl::android::media::audio::common::AudioProfile>>
connectedProfiles;
std::vector<AudioRoute> routes;
std::vector<AudioPatch> patches;
int32_t nextPortId = 1;

View File

@@ -18,6 +18,7 @@
#include <map>
#include <memory>
#include <set>
#include <aidl/android/hardware/audio/core/BnModule.h>
@@ -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<AudioPatch>* _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<AudioRoute>* _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<internal::Configuration> mConfig;
ModuleDebug mDebug;
// ids of ports created at runtime via 'connectExternalDevice'.
std::set<int32_t> 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.

View File

@@ -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 <typename C, typename P>
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 {

View File

@@ -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<AudioPortExt::Tag::device>();
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<AudioPort> ModuleConfig::getAttachedDevicePorts() const {
std::vector<AudioPort> 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<AudioPort> ModuleConfig::getExternalDevicePorts() const {
std::vector<AudioPort> 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<AudioPort> ModuleConfig::getInputMixPorts() const {
std::vector<AudioPort> result;
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [](const auto& port) {
@@ -229,6 +247,23 @@ std::vector<ModuleConfig::SrcSinkGroup> 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<AudioPortConfig> combineAudioConfigs(const AudioPort& port,
const AudioProfile& profile) {
std::vector<AudioPortConfig> configs;
@@ -319,10 +354,14 @@ std::vector<AudioPortConfig> 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;
}

View File

@@ -37,6 +37,8 @@ class ModuleConfig {
android::binder::Status getStatus() const { return mStatus; }
std::string getError() const { return mStatus.toString8().c_str(); }
std::vector<android::media::audio::common::AudioPort> getAttachedDevicePorts() const;
std::vector<android::media::audio::common::AudioPort> getExternalDevicePorts() const;
std::vector<android::media::audio::common::AudioPort> getInputMixPorts() const;
std::vector<android::media::audio::common::AudioPort> getOutputMixPorts() const;
std::vector<android::media::audio::common::AudioPort> getMixPorts(bool isInput) const {
@@ -59,6 +61,10 @@ class ModuleConfig {
std::optional<SrcSinkPair> getRoutableSrcSinkPair(bool isInput) const;
std::vector<SrcSinkGroup> getRoutableSrcSinkGroups(bool isInput) const;
std::vector<android::media::audio::common::AudioPortConfig>
getPortConfigsForAttachedDevicePorts() const {
return generateAudioDevicePortConfigs(getAttachedDevicePorts(), false);
}
std::vector<android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts() const {
auto inputs = generateInputAudioMixPortConfigs(getInputMixPorts(), false);
auto outputs = generateOutputAudioMixPortConfigs(getOutputMixPorts(), false);
@@ -98,15 +104,18 @@ class ModuleConfig {
}
}
std::vector<android::media::audio::common::AudioPortConfig> 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<android::media::audio::common::AudioPortConfig> generateInputAudioMixPortConfigs(
const std::vector<android::media::audio::common::AudioPort>& 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<android::media::audio::common::AudioPortConfig> generateAudioDevicePortConfigs(
const std::vector<android::media::audio::common::AudioPort>& ports,
bool singleProfile) const;
@@ -127,5 +137,6 @@ class ModuleConfig {
std::vector<android::media::audio::common::AudioPortConfig> mInitialConfigs;
std::set<int32_t> mAttachedSinkDevicePorts;
std::set<int32_t> mAttachedSourceDevicePorts;
std::set<int32_t> mExternalDevicePorts;
std::vector<android::hardware::audio::core::AudioRoute> mRoutes;
};

View File

@@ -22,6 +22,9 @@
#include <set>
#include <string>
#define LOG_TAG "VtsHalAudioCore"
#include <android-base/logging.h>
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <android-base/properties.h>
@@ -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<T>& v, int32_t id) {
}
template <typename C>
std::vector<int32_t> getNonExistentIds(const C& allIds) {
std::vector<int32_t> GetNonExistentIds(const C& allIds) {
if (allIds.empty()) {
return std::vector<int32_t>{-1, 0, 1};
}
@@ -73,6 +79,12 @@ std::vector<int32_t> getNonExistentIds(const C& allIds) {
return nonExistentIds;
}
AudioDeviceAddress GenerateUniqueDeviceAddress() {
static int nextId = 1;
// TODO: Use connection-specific ID.
return AudioDeviceAddress::make<AudioDeviceAddress::Tag::id>(std::to_string(++nextId));
}
struct AidlDeathRecipient : IBinder::DeathRecipient {
std::mutex mutex;
std::condition_variable condition;
@@ -107,70 +119,28 @@ constexpr IsInput<IStreamOut>::operator bool() const {
return false;
}
class AudioCoreModule : public testing::TestWithParam<std::string> {
class WithDebugFlags {
public:
void SetUp() override { ASSERT_NO_FATAL_FAILURE(ConnectToService()); }
void ConnectToService() {
module = android::waitForDeclaredService<IModule>(String16(GetParam().c_str()));
ASSERT_NE(module, nullptr);
}
void RestartService() {
ASSERT_NE(module, nullptr);
moduleConfig.reset();
deathHandler = sp<AidlDeathRecipient>::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 <typename Entity>
void GetAllEntityIds(std::set<int32_t>* entityIds,
Status (IModule::*getter)(std::vector<Entity>*),
const std::string& errorMessage) {
std::vector<Entity> 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<int32_t>* patchIds) {
return GetAllEntityIds<AudioPatch>(
patchIds, &IModule::getAudioPatches,
"IDs of audio patches returned by IModule.getAudioPatches are not unique");
}
void GetAllPortIds(std::set<int32_t>* portIds) {
return GetAllEntityIds<AudioPort>(
portIds, &IModule::getAudioPorts,
"IDs of audio ports returned by IModule.getAudioPorts are not unique");
}
void GetAllPortConfigIds(std::set<int32_t>* portConfigIds) {
return GetAllEntityIds<AudioPortConfig>(
portConfigIds, &IModule::getAudioPortConfigs,
"IDs of audio port configs returned by IModule.getAudioPortConfigs are not unique");
}
void SetUpModuleConfig() {
if (moduleConfig == nullptr) {
moduleConfig = std::make_unique<ModuleConfig>(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<IModule> module;
sp<AidlDeathRecipient> deathHandler;
std::unique_ptr<ModuleConfig> 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<std::string> {
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<IModule>(String16(GetParam().c_str()));
ASSERT_NE(module, nullptr);
}
void RestartService() {
ASSERT_NE(module, nullptr);
moduleConfig.reset();
deathHandler = sp<AidlDeathRecipient>::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<AudioPortConfig>& 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<AudioPortConfig> 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 <typename Entity>
void GetAllEntityIds(std::set<int32_t>* entityIds,
Status (IModule::*getter)(std::vector<Entity>*),
const std::string& errorMessage) {
std::vector<Entity> 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<int32_t>* patchIds) {
return GetAllEntityIds<AudioPatch>(
patchIds, &IModule::getAudioPatches,
"IDs of audio patches returned by IModule.getAudioPatches are not unique");
}
void GetAllPortIds(std::set<int32_t>* portIds) {
return GetAllEntityIds<AudioPort>(
portIds, &IModule::getAudioPorts,
"IDs of audio ports returned by IModule.getAudioPorts are not unique");
}
void GetAllPortConfigIds(std::set<int32_t>* portConfigIds) {
return GetAllEntityIds<AudioPortConfig>(
portConfigIds, &IModule::getAudioPortConfigs,
"IDs of audio port configs returned by IModule.getAudioPortConfigs are not unique");
}
void SetUpModuleConfig() {
if (moduleConfig == nullptr) {
moduleConfig = std::make_unique<ModuleConfig>(module.get());
ASSERT_EQ(Status::EX_NONE, moduleConfig->getStatus().exceptionCode())
<< "ModuleConfig init error: " << moduleConfig->getError();
}
}
sp<IModule> module;
sp<AidlDeathRecipient> deathHandler;
std::unique_ptr<ModuleConfig> 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<AudioPortExt::Tag::device>().device.address = address;
return result;
}
const AudioPort mIdAndData;
IModule* mModule = nullptr;
AudioPort mConnectedPort;
};
template <typename Stream>
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<AudioPort> 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<AudioRoute> 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<int32_t> portIds;
ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
if (portIds.empty()) {
GTEST_SKIP() << "No ports in the module.";
}
for (const auto portId : portIds) {
std::vector<AudioRoute> 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<AudioRoute> 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<AudioPort> 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<AudioPort> 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<AudioPortExt::Tag::device>().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<AudioPortExt::Tag::device>().device,
portConnected.get().ext.get<AudioPortExt::Tag::device>().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<AudioPort> 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<int32_t> portConfigIds;
ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds));
for (const auto portConfigId : getNonExistentIds(portConfigIds)) {
for (const auto portConfigId : GetNonExistentIds(portConfigIds)) {
{
sp<IStreamIn> stream;
Status status = module->openInputStream(portConfigId, {}, &stream);
@@ -537,7 +723,7 @@ TEST_P(AudioCoreModule, PortConfigPortIdsAreValid) {
TEST_P(AudioCoreModule, ResetAudioPortConfigInvalidId) {
std::set<int32_t> 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<AudioPort> 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<AudioPortConfig> 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<int32_t> 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<int32_t> 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<AudioPort> 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<AudioPortExt::Tag::device>().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<AudioPort> 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<int32_t> 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<AudioPort> 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<AudioPortExt::Tag::device>();
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<AudioPort> 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<AudioPortExt::Tag::device>().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<AudioPortExt::Tag::device>().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<AudioPort> 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<AudioPort> ports = moduleConfig->getExternalDevicePorts();
if (ports.empty()) {
GTEST_SKIP() << "No external devices in the module.";
}
for (const auto& port : ports) {
std::vector<AudioRoute> 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<AudioRoute> 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<AudioRoute> 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<AudioRoute> 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<AudioRoute> 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 <typename Stream>
class AudioStream : public AudioCoreModule {
public:
@@ -899,7 +1278,7 @@ class AudioModulePatch : public AudioCoreModule {
std::set<int32_t> 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<int32_t> 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<int32_t> 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;