Audio: Add VTS tests for invalid enum-strings, Part 1

Add tests that try passing invalid enum-string values to HAL
methods taking enum-strings. Fix issues found in the default
wrapper.

Interface updates:

- Update AudioConfig structure to indicate whether
  AudioOffloadInfo is specified.

- Add return value to IStreamIn.updateSinkMetadata
  and IStreamOut.updateSourceMetadata to provide indication
  of invalid arguments.

- Specify the behavior of IDevice.open{Input|Output}Stream
  in the case of invalid arguments vs. rejected config.

Bug: 142480271
Test: atest VtsHalAudioV6_0TargetTest
Test: atest VtsHalAudioV7_0TargetTest
      with side-loaded V7 default wrapper
Change-Id: I6bd7be3869cc7a8d5d00506565bbf0b3a050b630
This commit is contained in:
Mikhail Naganov 2020-12-17 15:01:54 -08:00
parent daedb0dc2e
commit 3f1457b953
22 changed files with 1006 additions and 340 deletions

View file

@ -103,6 +103,11 @@ interface IDevice {
* If the stream can not be opened with the proposed audio config,
* HAL must provide suggested values for the audio config.
*
* Note: INVALID_ARGUMENTS is returned both in the case when the
* HAL can not use the provided config and in the case when
* the value of any argument is invalid. In the latter case the
* HAL must provide a default initialized suggested config.
*
* @param ioHandle handle assigned by AudioFlinger.
* @param device device type and (if needed) address.
* @param config stream configuration.
@ -111,7 +116,8 @@ interface IDevice {
May be used by implementations to configure hardware effects.
* @return retval operation completion status.
* @return outStream created output stream.
* @return suggestedConfig in case of invalid parameters, suggested config.
* @return suggestedConfig in the case of rejection of the proposed config,
* a config suggested by the HAL.
*/
openOutputStream(
AudioIoHandle ioHandle,
@ -128,6 +134,11 @@ interface IDevice {
* If the stream can not be opened with the proposed audio config,
* HAL must provide suggested values for the audio config.
*
* Note: INVALID_ARGUMENTS is returned both in the case when the
* HAL can not use the provided config and in the case when
* the value of any argument is invalid. In the latter case the
* HAL must provide a default initialized suggested config.
*
* @param ioHandle handle assigned by AudioFlinger.
* @param device device type and (if needed) address.
* @param config stream configuration.
@ -136,7 +147,8 @@ interface IDevice {
* May be used by implementations to configure processing effects.
* @return retval operation completion status.
* @return inStream in case of success, created input stream.
* @return suggestedConfig in case of invalid parameters, suggested config.
* @return suggestedConfig in the case of rejection of the proposed config,
* a config suggested by the HAL.
*/
openInputStream(
AudioIoHandle ioHandle,

View file

@ -40,6 +40,18 @@ interface IStreamIn extends IStream {
*/
setGain(float gain) generates (Result retval);
/**
* Called when the metadata of the stream's sink has been changed.
* Optional method
*
* @param sinkMetadata Description of the audio that is suggested by the clients.
* @return retval operation completion status.
* If any of the metadata fields contains an invalid value,
* returns INVALID_ARGUMENTS.
* If method isn't supported by the HAL returns NOT_SUPPORTED.
*/
updateSinkMetadata(SinkMetadata sinkMetadata) generates (Result retval);
/**
* Commands that can be executed on the driver reader thread.
*/
@ -81,12 +93,6 @@ interface IStreamIn extends IStream {
} reply;
};
/**
* Called when the metadata of the stream's sink has been changed.
* @param sinkMetadata Description of the audio that is suggested by the clients.
*/
updateSinkMetadata(SinkMetadata sinkMetadata);
/**
* Set up required transports for receiving audio buffers from the driver.
*

View file

@ -44,6 +44,18 @@ interface IStreamOut extends IStream {
*/
setVolume(float left, float right) generates (Result retval);
/**
* Called when the metadata of the stream's source has been changed.
* Optional method
*
* @param sourceMetadata Description of the audio that is played by the clients.
* @return retval operation completion status.
* If any of the metadata fields contains an invalid value,
* returns INVALID_ARGUMENTS.
* If method isn't supported by the HAL returns NOT_SUPPORTED.
*/
updateSourceMetadata(SourceMetadata sourceMetadata) generates (Result retval);
/**
* Commands that can be executed on the driver writer thread.
*/
@ -76,12 +88,6 @@ interface IStreamOut extends IStream {
} reply;
};
/**
* Called when the metadata of the stream's source has been changed.
* @param sourceMetadata Description of the audio that is played by the clients.
*/
updateSourceMetadata(SourceMetadata sourceMetadata);
/**
* Set up required transports for passing audio buffers to the driver.
*

View file

@ -212,12 +212,11 @@ static inline bool isOutputDevice(const std::string& device) {
return isOutputDevice(stringToAudioDevice(device));
}
static inline bool isVendorExtension(const std::string& device) {
static inline bool isVendorExtension(const std::string& s) {
// Must match the "vendorExtension" rule from the XSD file.
static const std::string vendorPrefix = "VX_";
return device.size() > vendorPrefix.size() &&
device.substr(0, vendorPrefix.size()) == vendorPrefix &&
std::all_of(device.begin() + vendorPrefix.size(), device.end(),
return s.size() > vendorPrefix.size() && s.substr(0, vendorPrefix.size()) == vendorPrefix &&
std::all_of(s.begin() + vendorPrefix.size(), s.end(),
[](unsigned char c) { return c == '_' || std::isalnum(c); });
}

View file

@ -270,7 +270,10 @@ struct AudioOffloadInfo {
*/
struct AudioConfig {
AudioConfigBase base;
AudioOffloadInfo offloadInfo;
safe_union OffloadInfo {
Monostate unspecified;
AudioOffloadInfo info;
} offloadInfo;
uint64_t frameCount;
};
@ -278,7 +281,8 @@ struct AudioConfig {
* AudioTag is an additional use case qualifier complementing
* AudioUsage and AudioContentType. Tags are set by vendor specific applications
* and must be prefixed by "VX_". Vendor must namespace their tag
* names to avoid conflicts.
* names to avoid conflicts. See 'vendorExtension' in audio_policy_configuration.xsd
* for a formal definition.
*/
typedef string AudioTag;

View file

@ -21,6 +21,7 @@
#include <log/log.h>
#include <android_audio_policy_configuration_V7_0-enums.h>
#include <common/all-versions/HidlSupport.h>
#include <common/all-versions/VersionUtils.h>
#include "HidlUtils.h"
@ -306,7 +307,12 @@ status_t HidlUtils::audioConfigFromHal(const audio_config_t& halConfig, bool isI
audio_config_base_t halConfigBase = {halConfig.sample_rate, halConfig.channel_mask,
halConfig.format};
CONVERT_CHECKED(audioConfigBaseFromHal(halConfigBase, isInput, &config->base), result);
CONVERT_CHECKED(audioOffloadInfoFromHal(halConfig.offload_info, &config->offloadInfo), result);
if (halConfig.offload_info.sample_rate != 0) {
config->offloadInfo.info({});
CONVERT_CHECKED(
audioOffloadInfoFromHal(halConfig.offload_info, &config->offloadInfo.info()),
result);
}
config->frameCount = halConfig.frame_count;
return result;
}
@ -319,7 +325,11 @@ status_t HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t*
halConfig->sample_rate = halConfigBase.sample_rate;
halConfig->channel_mask = halConfigBase.channel_mask;
halConfig->format = halConfigBase.format;
CONVERT_CHECKED(audioOffloadInfoToHal(config.offloadInfo, &halConfig->offload_info), result);
if (config.offloadInfo.getDiscriminator() ==
AudioConfig::OffloadInfo::hidl_discriminator::info) {
CONVERT_CHECKED(audioOffloadInfoToHal(config.offloadInfo.info(), &halConfig->offload_info),
result);
}
halConfig->frame_count = config.frameCount;
return result;
}
@ -800,6 +810,47 @@ status_t HidlUtils::audioProfileToHal(const AudioProfile& profile,
return result;
}
status_t HidlUtils::audioTagsFromHal(const char* halTags, hidl_vec<AudioTag>* tags) {
std::vector<std::string> strTags = utils::splitString(halTags, sAudioTagSeparator);
status_t result = NO_ERROR;
tags->resize(strTags.size());
size_t to = 0;
for (size_t from = 0; from < strTags.size(); ++from) {
if (xsd::isVendorExtension(strTags[from])) {
(*tags)[to++] = strTags[from];
} else {
result = BAD_VALUE;
}
}
if (to != strTags.size()) {
tags->resize(to);
}
return result;
}
status_t HidlUtils::audioTagsToHal(const hidl_vec<AudioTag>& tags, char* halTags) {
memset(halTags, 0, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE);
status_t result = NO_ERROR;
std::ostringstream halTagsBuffer;
bool hasValue = false;
for (const auto& tag : tags) {
if (hasValue) {
halTagsBuffer << sAudioTagSeparator;
}
if (xsd::isVendorExtension(tag) && strchr(tag.c_str(), sAudioTagSeparator) == nullptr) {
halTagsBuffer << tag;
hasValue = true;
} else {
result = BAD_VALUE;
}
}
std::string fullHalTags{std::move(halTagsBuffer.str())};
strncpy(halTags, fullHalTags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE);
CONVERT_CHECKED(fullHalTags.length() <= AUDIO_ATTRIBUTES_TAGS_MAX_SIZE ? NO_ERROR : BAD_VALUE,
result);
return result;
}
status_t HidlUtils::deviceAddressFromHal(audio_devices_t halDeviceType,
const char* halDeviceAddress, DeviceAddress* device) {
status_t result = NO_ERROR;

View file

@ -109,6 +109,8 @@ struct HidlUtils {
AudioStreamType* streamType);
static status_t audioStreamTypeToHal(const AudioStreamType& streamType,
audio_stream_type_t* halStreamType);
static status_t audioTagsFromHal(const char* halTags, hidl_vec<AudioTag>* tags);
static status_t audioTagsToHal(const hidl_vec<AudioTag>& tags, char* halTags);
private:
static status_t audioIndexChannelMaskFromHal(audio_channel_mask_t halChannelMask,

View file

@ -589,16 +589,29 @@ TEST(HidlUtils, ConvertConfig) {
config.base.sampleRateHz = 44100;
config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
config.offloadInfo.base = config.base;
config.offloadInfo.streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC);
config.offloadInfo.bitRatePerSecond = 320;
config.offloadInfo.durationMicroseconds = -1;
config.offloadInfo.bitWidth = 16;
config.offloadInfo.bufferSize = 1024;
config.offloadInfo.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA);
config.offloadInfo.encapsulationMode = AudioEncapsulationMode::ELEMENTARY_STREAM;
config.offloadInfo.contentId = 42;
config.offloadInfo.syncId = 13;
audio_config_t halConfig;
EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(config, &halConfig));
AudioConfig configBack;
EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigFromHal(halConfig, false /*isInput*/, &configBack));
EXPECT_EQ(config, configBack);
}
TEST(HidlUtils, ConvertConfigWithOffloadInfo) {
AudioConfig config = {};
config.base.sampleRateHz = 44100;
config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
config.offloadInfo.info(
AudioOffloadInfo{.base = config.base,
.streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC),
.bitRatePerSecond = 320,
.durationMicroseconds = -1,
.bitWidth = 16,
.bufferSize = 1024,
.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
.encapsulationMode = AudioEncapsulationMode::ELEMENTARY_STREAM,
.contentId = 42,
.syncId = 13});
audio_config_t halConfig;
EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(config, &halConfig));
AudioConfig configBack;
@ -707,3 +720,43 @@ TEST(HidlUtils, ConvertAudioPort) {
EXPECT_EQ(NO_ERROR, HidlUtils::audioPortToHal(portBack, &halPortBack));
EXPECT_TRUE(audio_ports_v7_are_equal(&halPort, &halPortBack));
}
TEST(HidlUtils, ConvertInvalidAudioTags) {
char halTag[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE] = {};
hidl_vec<AudioTag> emptyTag = {{""}};
EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(emptyTag, halTag));
hidl_vec<AudioTag> longTag = {{std::string(AUDIO_ATTRIBUTES_TAGS_MAX_SIZE + 1, 'A')}};
EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(longTag, halTag));
hidl_vec<AudioTag> tagSeparator = {
{std::string(AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1, HidlUtils::sAudioTagSeparator)}};
EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(tagSeparator, halTag));
hidl_vec<AudioTag> notExtensions = {{"random string", "VX_", "VX_GOOGLE_$$"}};
EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(notExtensions, halTag));
}
TEST(HidlUtils, ConvertAudioTags) {
hidl_vec<AudioTag> emptyTags;
char halEmptyTags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE] = {};
EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsToHal(emptyTags, halEmptyTags));
hidl_vec<AudioTag> emptyTagsBack;
EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsFromHal(halEmptyTags, &emptyTagsBack));
EXPECT_EQ(emptyTags, emptyTagsBack);
hidl_vec<AudioTag> oneTag = {{"VX_GOOGLE_VR"}};
char halOneTag[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE] = {};
EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsToHal(oneTag, halOneTag));
hidl_vec<AudioTag> oneTagBack;
EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsFromHal(halOneTag, &oneTagBack));
EXPECT_EQ(oneTag, oneTagBack);
hidl_vec<AudioTag> twoTags = {{"VX_GOOGLE_VR_42", "VX_GOOGLE_1E100"}};
char halTwoTags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE] = {};
EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsToHal(twoTags, halTwoTags));
hidl_vec<AudioTag> twoTagsBack;
EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsFromHal(halTwoTags, &twoTagsBack));
EXPECT_EQ(twoTags, twoTagsBack);
}

View file

@ -20,6 +20,9 @@
#include <hidl/HidlSupport.h>
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
namespace android::hardware::audio::common::utils {
@ -29,6 +32,16 @@ bool isValidHidlEnum(Enum e) {
return std::find(values.begin(), values.end(), e) != values.end();
}
static inline std::vector<std::string> splitString(const std::string& s, char separator) {
std::istringstream iss(s);
std::string t;
std::vector<std::string> result;
while (std::getline(iss, t, separator)) {
result.push_back(std::move(t));
}
return result;
}
} // namespace android::hardware::audio::common::utils
#endif // android_hardware_audio_common_HidlSupport_H_

View file

@ -32,14 +32,10 @@ namespace implementation {
using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
#if MAJOR_VERSION <= 6
std::string deviceAddressToHal(const DeviceAddress& address) {
audio_devices_t halDevice;
char halAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
(void)deviceAddressToHal(address, &halDevice, halAddress);
return halAddress;
}
#endif
#define CONVERT_CHECKED(expr, result) \
if (status_t status = (expr); status != NO_ERROR) { \
result = status; \
}
status_t deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
char* halDeviceAddress) {
@ -97,6 +93,52 @@ bool halToMicrophoneCharacteristics(MicrophoneInfo* pDst,
}
return status;
}
status_t sinkMetadataToHal(const SinkMetadata& sinkMetadata,
std::vector<record_track_metadata>* halTracks) {
status_t result = NO_ERROR;
if (halTracks != nullptr) {
halTracks->reserve(sinkMetadata.tracks.size());
}
for (auto& metadata : sinkMetadata.tracks) {
record_track_metadata halTrackMetadata{.gain = metadata.gain};
CONVERT_CHECKED(HidlUtils::audioSourceToHal(metadata.source, &halTrackMetadata.source),
result);
#if MAJOR_VERSION >= 5
if (metadata.destination.getDiscriminator() ==
RecordTrackMetadata::Destination::hidl_discriminator::device) {
CONVERT_CHECKED(
deviceAddressToHal(metadata.destination.device(), &halTrackMetadata.dest_device,
halTrackMetadata.dest_device_address),
result);
}
#endif
if (halTracks != nullptr) {
halTracks->push_back(std::move(halTrackMetadata));
}
}
return result;
}
status_t sourceMetadataToHal(const SourceMetadata& sourceMetadata,
std::vector<playback_track_metadata_t>* halTracks) {
status_t result = NO_ERROR;
if (halTracks != nullptr) {
halTracks->reserve(sourceMetadata.tracks.size());
}
for (auto& metadata : sourceMetadata.tracks) {
playback_track_metadata_t halTrackMetadata{.gain = metadata.gain};
CONVERT_CHECKED(HidlUtils::audioUsageToHal(metadata.usage, &halTrackMetadata.usage),
result);
CONVERT_CHECKED(HidlUtils::audioContentTypeToHal(metadata.contentType,
&halTrackMetadata.content_type),
result);
if (halTracks != nullptr) {
halTracks->push_back(std::move(halTrackMetadata));
}
}
return result;
}
#endif // MAJOR_VERSION >= 4
#if MAJOR_VERSION >= 7
@ -135,6 +177,50 @@ bool audioOutputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_output_f
}
return success;
}
status_t sinkMetadataToHalV7(const SinkMetadata& sinkMetadata,
std::vector<record_track_metadata_v7_t>* halTracks) {
std::vector<record_track_metadata> bases;
status_t result = sinkMetadataToHal(sinkMetadata, halTracks != nullptr ? &bases : nullptr);
if (halTracks != nullptr) {
halTracks->reserve(bases.size());
}
auto baseIter = std::make_move_iterator(bases.begin());
for (auto& metadata : sinkMetadata.tracks) {
record_track_metadata_v7_t halTrackMetadata;
CONVERT_CHECKED(HidlUtils::audioChannelMaskToHal(metadata.channelMask,
&halTrackMetadata.channel_mask),
result);
CONVERT_CHECKED(HidlUtils::audioTagsToHal(metadata.tags, halTrackMetadata.tags), result);
if (halTracks != nullptr) {
halTrackMetadata.base = std::move(*baseIter++);
halTracks->push_back(std::move(halTrackMetadata));
}
}
return result;
}
status_t sourceMetadataToHalV7(const SourceMetadata& sourceMetadata,
std::vector<playback_track_metadata_v7_t>* halTracks) {
std::vector<playback_track_metadata_t> bases;
status_t result = sourceMetadataToHal(sourceMetadata, halTracks != nullptr ? &bases : nullptr);
if (halTracks != nullptr) {
halTracks->reserve(bases.size());
}
auto baseIter = std::make_move_iterator(bases.begin());
for (auto& metadata : sourceMetadata.tracks) {
playback_track_metadata_v7_t halTrackMetadata;
CONVERT_CHECKED(HidlUtils::audioChannelMaskToHal(metadata.channelMask,
&halTrackMetadata.channel_mask),
result);
CONVERT_CHECKED(HidlUtils::audioTagsToHal(metadata.tags, halTrackMetadata.tags), result);
if (halTracks != nullptr) {
halTrackMetadata.base = std::move(*baseIter++);
halTracks->push_back(std::move(halTrackMetadata));
}
}
return result;
}
#endif
} // namespace implementation

View file

@ -135,13 +135,14 @@ Return<void> Device::getMasterMute(getMasterMute_cb _hidl_cb) {
Return<void> Device::getInputBufferSize(const AudioConfig& config, getInputBufferSize_cb _hidl_cb) {
audio_config_t halConfig;
HidlUtils::audioConfigToHal(config, &halConfig);
size_t halBufferSize = mDevice->get_input_buffer_size(mDevice, &halConfig);
Result retval(Result::INVALID_ARGUMENTS);
uint64_t bufferSize = 0;
if (halBufferSize != 0) {
retval = Result::OK;
bufferSize = halBufferSize;
if (HidlUtils::audioConfigToHal(config, &halConfig) == NO_ERROR) {
size_t halBufferSize = mDevice->get_input_buffer_size(mDevice, &halConfig);
if (halBufferSize != 0) {
retval = Result::OK;
bufferSize = halBufferSize;
}
}
_hidl_cb(retval, bufferSize);
return Void();
@ -153,7 +154,9 @@ std::tuple<Result, sp<IStreamOut>> Device::openOutputStreamImpl(int32_t ioHandle
const AudioOutputFlags& flags,
AudioConfig* suggestedConfig) {
audio_config_t halConfig;
HidlUtils::audioConfigToHal(config, &halConfig);
if (HidlUtils::audioConfigToHal(config, &halConfig) != NO_ERROR) {
return {Result::INVALID_ARGUMENTS, nullptr};
}
audio_stream_out_t* halStream;
audio_devices_t halDevice;
char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
@ -186,7 +189,9 @@ std::tuple<Result, sp<IStreamIn>> Device::openInputStreamImpl(
int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
const AudioInputFlags& flags, AudioSource source, AudioConfig* suggestedConfig) {
audio_config_t halConfig;
HidlUtils::audioConfigToHal(config, &halConfig);
if (HidlUtils::audioConfigToHal(config, &halConfig) != NO_ERROR) {
return {Result::INVALID_ARGUMENTS, nullptr};
}
audio_stream_in_t* halStream;
audio_devices_t halDevice;
char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
@ -248,6 +253,14 @@ Return<void> Device::openOutputStream(int32_t ioHandle, const DeviceAddress& dev
#endif
const SourceMetadata& sourceMetadata,
openOutputStream_cb _hidl_cb) {
#if MAJOR_VERSION <= 6
if (status_t status = sourceMetadataToHal(sourceMetadata, nullptr); status != NO_ERROR) {
#else
if (status_t status = sourceMetadataToHalV7(sourceMetadata, nullptr); status != NO_ERROR) {
#endif
_hidl_cb(analyzeStatus("sourceMetadataToHal", status), nullptr, AudioConfig{});
return Void();
}
AudioConfig suggestedConfig;
auto [result, streamOut] =
openOutputStreamImpl(ioHandle, device, config, flags, &suggestedConfig);
@ -271,7 +284,15 @@ Return<void> Device::openInputStream(int32_t ioHandle, const DeviceAddress& devi
// This should never happen, the framework must not create as stream
// if there is no client
ALOGE("openInputStream called without tracks connected");
_hidl_cb(Result::INVALID_ARGUMENTS, nullptr, AudioConfig());
_hidl_cb(Result::INVALID_ARGUMENTS, nullptr, AudioConfig{});
return Void();
}
#if MAJOR_VERSION <= 6
if (status_t status = sinkMetadataToHal(sinkMetadata, nullptr); status != NO_ERROR) {
#else
if (status_t status = sinkMetadataToHalV7(sinkMetadata, nullptr); status != NO_ERROR) {
#endif
_hidl_cb(analyzeStatus("sinkMetadataToHal", status), nullptr, AudioConfig{});
return Void();
}
// Pick the first one as the main.

View file

@ -17,6 +17,7 @@
#define LOG_TAG "StreamHAL"
#include "core/default/Stream.h"
#include "common/all-versions/HidlSupport.h"
#include "common/all-versions/default/EffectMap.h"
#include "core/default/Conversions.h"
#include "core/default/Util.h"
@ -37,6 +38,7 @@ namespace CPP_VERSION {
namespace implementation {
using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
using ::android::hardware::audio::common::utils::splitString;
Stream::Stream(bool isInput, audio_stream_t* stream) : mIsInput(isInput), mStream(stream) {
(void)mIsInput; // prevent 'unused field' warnings in pre-V7 versions.
@ -220,7 +222,7 @@ Return<void> Stream::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
// Ensure that the separator is one character, despite that it's defined as a C string.
static_assert(sizeof(AUDIO_PARAMETER_VALUE_LIST_SEPARATOR) == 2);
std::vector<std::string> halFormats =
util::splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
hidl_vec<AudioFormat> formats;
(void)HidlUtils::audioFormatsFromHal(halFormats, &formats);
std::vector<AudioProfile> tempProfiles;
@ -235,7 +237,7 @@ Return<void> Stream::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
result = getParam(AudioParameter::keyStreamSupportedSamplingRates, &halListValue, context);
if (result != Result::OK) break;
std::vector<std::string> halSampleRates =
util::splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
hidl_vec<uint32_t> sampleRates;
sampleRates.resize(halSampleRates.size());
for (size_t i = 0; i < sampleRates.size(); ++i) {
@ -245,7 +247,7 @@ Return<void> Stream::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
result = getParam(AudioParameter::keyStreamSupportedChannels, &halListValue, context);
if (result != Result::OK) break;
std::vector<std::string> halChannelMasks =
util::splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
hidl_vec<AudioChannelMask> channelMasks;
(void)HidlUtils::audioChannelMasksFromHal(halChannelMasks, &channelMasks);
// Create a profile.

View file

@ -478,87 +478,59 @@ Return<void> StreamIn::debug(const hidl_handle& fd, const hidl_vec<hidl_string>&
}
#if MAJOR_VERSION >= 4
record_track_metadata StreamIn::convertRecordTrackMetadata(
const RecordTrackMetadata& trackMetadata) {
record_track_metadata halTrackMetadata = {.gain = trackMetadata.gain};
(void)HidlUtils::audioSourceToHal(trackMetadata.source, &halTrackMetadata.source);
#if MAJOR_VERSION >= 5
if (trackMetadata.destination.getDiscriminator() ==
RecordTrackMetadata::Destination::hidl_discriminator::device) {
(void)deviceAddressToHal(trackMetadata.destination.device(), &halTrackMetadata.dest_device,
halTrackMetadata.dest_device_address);
}
#endif
return halTrackMetadata;
}
void StreamIn::doUpdateSinkMetadata(const SinkMetadata& sinkMetadata) {
Result StreamIn::doUpdateSinkMetadata(const SinkMetadata& sinkMetadata,
bool abortOnConversionFailure) {
std::vector<record_track_metadata> halTracks;
halTracks.reserve(sinkMetadata.tracks.size());
for (auto& metadata : sinkMetadata.tracks) {
halTracks.push_back(convertRecordTrackMetadata(metadata));
if (status_t status = sinkMetadataToHal(sinkMetadata, &halTracks);
status != NO_ERROR && abortOnConversionFailure) {
return Stream::analyzeStatus("sinkMetadataToHal", status);
}
const sink_metadata_t halMetadata = {
.track_count = halTracks.size(),
.tracks = halTracks.data(),
};
mStream->update_sink_metadata(mStream, &halMetadata);
return Result::OK;
}
#if MAJOR_VERSION >= 7
record_track_metadata_v7 StreamIn::convertRecordTrackMetadataV7(
const RecordTrackMetadata& trackMetadata) {
record_track_metadata_v7 halTrackMetadata;
halTrackMetadata.base = convertRecordTrackMetadata(trackMetadata);
(void)HidlUtils::audioChannelMaskToHal(trackMetadata.channelMask,
&halTrackMetadata.channel_mask);
std::string halTags;
for (const auto& tag : trackMetadata.tags) {
if (&tag != &trackMetadata.tags[0]) {
halTags += HidlUtils::sAudioTagSeparator;
}
halTags += tag.c_str();
}
strncpy(halTrackMetadata.tags, halTags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE);
return halTrackMetadata;
}
void StreamIn::doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata) {
Result StreamIn::doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata) {
std::vector<record_track_metadata_v7> halTracks;
halTracks.reserve(sinkMetadata.tracks.size());
for (auto& metadata : sinkMetadata.tracks) {
halTracks.push_back(convertRecordTrackMetadataV7(metadata));
if (status_t status = sinkMetadataToHalV7(sinkMetadata, &halTracks); status != NO_ERROR) {
return Stream::analyzeStatus("sinkMetadataToHal", status);
}
const sink_metadata_v7_t halMetadata = {
.track_count = halTracks.size(),
.tracks = halTracks.data(),
};
mStream->update_sink_metadata_v7(mStream, &halMetadata);
return Result::OK;
}
#endif // MAJOR_VERSION >= 7
#if MAJOR_VERSION <= 6
Return<void> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
#if MAJOR_VERSION < 7
if (mStream->update_sink_metadata == nullptr) {
return Void(); // not supported by the HAL
}
doUpdateSinkMetadata(sinkMetadata);
#else
if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) {
if (mStream->update_sink_metadata == nullptr) {
return Void(); // not supported by the HAL
}
doUpdateSinkMetadata(sinkMetadata);
} else {
if (mStream->update_sink_metadata_v7 == nullptr) {
return Void(); // not supported by the HAL
}
doUpdateSinkMetadataV7(sinkMetadata);
}
#endif // MAJOR_VERSION < 7
(void)doUpdateSinkMetadata(sinkMetadata, false /*abortOnConversionFailure*/);
return Void();
}
#elif MAJOR_VERSION >= 7
Return<Result> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) {
if (mStream->update_sink_metadata == nullptr) {
return Result::NOT_SUPPORTED;
}
return doUpdateSinkMetadata(sinkMetadata, true /*abortOnConversionFailure*/);
} else {
if (mStream->update_sink_metadata_v7 == nullptr) {
return Result::NOT_SUPPORTED;
}
return doUpdateSinkMetadataV7(sinkMetadata);
}
}
#endif
Return<void> StreamIn::getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) {
Result retval = Result::NOT_SUPPORTED;

View file

@ -17,6 +17,7 @@
#define LOG_TAG "StreamOutHAL"
#include "core/default/StreamOut.h"
#include "core/default/Conversions.h"
#include "core/default/Util.h"
//#define LOG_NDEBUG 0
@ -585,81 +586,59 @@ Return<void> StreamOut::debug(const hidl_handle& fd, const hidl_vec<hidl_string>
}
#if MAJOR_VERSION >= 4
playback_track_metadata StreamOut::convertPlaybackTrackMetadata(
const PlaybackTrackMetadata& trackMetadata) {
playback_track_metadata_t halTrackMetadata = {.gain = trackMetadata.gain};
(void)HidlUtils::audioUsageToHal(trackMetadata.usage, &halTrackMetadata.usage);
(void)HidlUtils::audioContentTypeToHal(trackMetadata.contentType,
&halTrackMetadata.content_type);
return halTrackMetadata;
}
void StreamOut::doUpdateSourceMetadata(const SourceMetadata& sourceMetadata) {
Result StreamOut::doUpdateSourceMetadata(const SourceMetadata& sourceMetadata,
bool abortOnConversionFailure) {
std::vector<playback_track_metadata_t> halTracks;
halTracks.reserve(sourceMetadata.tracks.size());
for (auto& metadata : sourceMetadata.tracks) {
halTracks.push_back(convertPlaybackTrackMetadata(metadata));
if (status_t status = sourceMetadataToHal(sourceMetadata, &halTracks);
status != NO_ERROR && abortOnConversionFailure) {
return Stream::analyzeStatus("sourceMetadataToHal", status);
}
const source_metadata_t halMetadata = {
.track_count = halTracks.size(),
.tracks = halTracks.data(),
};
mStream->update_source_metadata(mStream, &halMetadata);
return Result::OK;
}
#if MAJOR_VERSION >= 7
playback_track_metadata_v7 StreamOut::convertPlaybackTrackMetadataV7(
const PlaybackTrackMetadata& trackMetadata) {
playback_track_metadata_v7 halTrackMetadata;
halTrackMetadata.base = convertPlaybackTrackMetadata(trackMetadata);
(void)HidlUtils::audioChannelMaskToHal(trackMetadata.channelMask,
&halTrackMetadata.channel_mask);
std::string halTags;
for (const auto& tag : trackMetadata.tags) {
if (&tag != &trackMetadata.tags[0]) {
halTags += HidlUtils::sAudioTagSeparator;
}
halTags += tag.c_str();
}
strncpy(halTrackMetadata.tags, halTags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE);
return halTrackMetadata;
}
void StreamOut::doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata) {
Result StreamOut::doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata) {
std::vector<playback_track_metadata_v7> halTracks;
halTracks.reserve(sourceMetadata.tracks.size());
for (auto& metadata : sourceMetadata.tracks) {
halTracks.push_back(convertPlaybackTrackMetadataV7(metadata));
if (status_t status = sourceMetadataToHalV7(sourceMetadata, &halTracks); status != NO_ERROR) {
return Stream::analyzeStatus("sourceMetadataToHal", status);
}
const source_metadata_v7_t halMetadata = {
.track_count = halTracks.size(),
.tracks = halTracks.data(),
};
mStream->update_source_metadata_v7(mStream, &halMetadata);
return Result::OK;
}
#endif // MAJOR_VERSION >= 7
#if MAJOR_VERSION <= 6
Return<void> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
#if MAJOR_VERSION < 7
if (mStream->update_source_metadata == nullptr) {
return Void(); // not supported by the HAL
}
doUpdateSourceMetadata(sourceMetadata);
#else
if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) {
if (mStream->update_source_metadata == nullptr) {
return Void(); // not supported by the HAL
}
doUpdateSourceMetadata(sourceMetadata);
} else {
if (mStream->update_source_metadata_v7 == nullptr) {
return Void(); // not supported by the HAL
}
doUpdateSourceMetadataV7(sourceMetadata);
}
#endif // MAJOR_VERSION < 7
(void)doUpdateSourceMetadata(sourceMetadata, false /*abortOnConversionFailure*/);
return Void();
}
#elif MAJOR_VERSION >= 7
Return<Result> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) {
if (mStream->update_source_metadata == nullptr) {
return Result::NOT_SUPPORTED;
}
return doUpdateSourceMetadata(sourceMetadata, true /*abortOnConversionFailure*/);
} else {
if (mStream->update_source_metadata_v7 == nullptr) {
return Result::NOT_SUPPORTED;
}
return doUpdateSourceMetadataV7(sourceMetadata);
}
}
#endif
Return<Result> StreamOut::selectPresentation(int32_t /*presentationId*/, int32_t /*programId*/) {
return Result::NOT_SUPPORTED; // TODO: propagate to legacy

View file

@ -35,12 +35,6 @@ using ::android::hardware::hidl_vec;
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::CPP_VERSION;
#if MAJOR_VERSION <= 6
// Temporary version for compatibility with forks of the default implementation.
// Will be removed, do not use!
std::string deviceAddressToHal(const DeviceAddress& address);
#endif
status_t deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
char* halDeviceAddress);
status_t deviceAddressFromHal(audio_devices_t halDeviceType, const char* halDeviceAddress,
@ -49,6 +43,10 @@ status_t deviceAddressFromHal(audio_devices_t halDeviceType, const char* halDevi
#if MAJOR_VERSION >= 4
bool halToMicrophoneCharacteristics(MicrophoneInfo* pDst,
const struct audio_microphone_characteristic_t& src);
status_t sinkMetadataToHal(const SinkMetadata& sinkMetadata,
std::vector<record_track_metadata>* halTracks);
status_t sourceMetadataToHal(const SourceMetadata& sourceMetadata,
std::vector<playback_track_metadata_t>* halTracks);
#endif
#if MAJOR_VERSION <= 6
@ -69,6 +67,11 @@ inline bool audioOutputFlagsToHal(AudioOutputFlags flags, audio_output_flags_t*
#else
bool audioInputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_input_flags_t* halFlags);
bool audioOutputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_output_flags_t* halFlags);
// Overloading isn't convenient when passing a nullptr.
status_t sinkMetadataToHalV7(const SinkMetadata& sinkMetadata,
std::vector<record_track_metadata_v7_t>* halTracks);
status_t sourceMetadataToHalV7(const SourceMetadata& sourceMetadata,
std::vector<playback_track_metadata_v7_t>* halTracks);
#endif
} // namespace implementation

View file

@ -114,9 +114,13 @@ struct StreamIn : public IStreamIn {
Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
#if MAJOR_VERSION >= 4
#if MAJOR_VERSION <= 6
Return<void> updateSinkMetadata(const SinkMetadata& sinkMetadata) override;
Return<void> getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) override;
#else
Return<Result> updateSinkMetadata(const SinkMetadata& sinkMetadata) override;
#endif
Return<void> getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) override;
#endif // MAJOR_VERSION >= 4
#if MAJOR_VERSION >= 5
Return<Result> setMicrophoneDirection(MicrophoneDirection direction) override;
Return<Result> setMicrophoneFieldDimension(float zoom) override;
@ -126,13 +130,11 @@ struct StreamIn : public IStreamIn {
private:
#if MAJOR_VERSION >= 4
record_track_metadata convertRecordTrackMetadata(const RecordTrackMetadata& trackMetadata);
void doUpdateSinkMetadata(const SinkMetadata& sinkMetadata);
Result doUpdateSinkMetadata(const SinkMetadata& sinkMetadata, bool abortOnConversionFailure);
#if MAJOR_VERSION >= 7
record_track_metadata_v7 convertRecordTrackMetadataV7(const RecordTrackMetadata& trackMetadata);
void doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata);
#endif
Result doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata);
#endif
#endif // MAJOR_VERSION >= 4
const sp<Device> mDevice;
audio_stream_in_t* mStream;

View file

@ -123,9 +123,13 @@ struct StreamOut : public IStreamOut {
Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
#if MAJOR_VERSION >= 4
Return<void> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
Return<Result> selectPresentation(int32_t presentationId, int32_t programId) override;
#if MAJOR_VERSION <= 6
Return<void> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
#else
Return<Result> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
#endif
#endif // MAJOR_VERSION >= 4
#if MAJOR_VERSION >= 6
Return<void> getDualMonoMode(getDualMonoMode_cb _hidl_cb) override;
Return<Result> setDualMonoMode(DualMonoMode mode) override;
@ -144,15 +148,12 @@ struct StreamOut : public IStreamOut {
private:
#if MAJOR_VERSION >= 4
playback_track_metadata convertPlaybackTrackMetadata(
const PlaybackTrackMetadata& trackMetadata);
void doUpdateSourceMetadata(const SourceMetadata& sourceMetadata);
Result doUpdateSourceMetadata(const SourceMetadata& sourceMetadata,
bool abortOnConversionFailure);
#if MAJOR_VERSION >= 7
playback_track_metadata_v7 convertPlaybackTrackMetadataV7(
const PlaybackTrackMetadata& trackMetadata);
void doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata);
#endif
Result doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata);
#endif
#endif // MAJOR_VERSION >= 4
const sp<Device> mDevice;
audio_stream_out_t* mStream;

View file

@ -20,8 +20,6 @@
#include PATH(android/hardware/audio/FILE_VERSION/types.h)
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include <system/audio.h>
@ -72,16 +70,6 @@ static inline Result analyzeStatus(const char* className, const char* funcName,
return analyzeStatus(status);
}
static inline std::vector<std::string> splitString(const std::string& s, char separator) {
std::istringstream iss(s);
std::string t;
std::vector<std::string> result;
while (std::getline(iss, t, separator)) {
result.push_back(std::move(t));
}
return result;
}
} // namespace util
} // namespace implementation
} // namespace CPP_VERSION

View file

@ -237,26 +237,14 @@ TEST_IO_STREAM(GetHwAvSync, "Get hardware sync can not fail", checkGetHwAVSync(g
TEST_P(InputStreamTest, updateSinkMetadata) {
doc::test("The HAL should not crash on metadata change");
#if MAJOR_VERSION <= 6
hidl_enum_range<AudioSource> range;
#elif MAJOR_VERSION >= 7
xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioSource> range;
#endif
// Test all possible track configuration
for (auto source : range) {
for (float volume : {0.0, 0.5, 1.0}) {
#if MAJOR_VERSION <= 6
const SinkMetadata metadata = {{{.source = source, .gain = volume}}};
#elif MAJOR_VERSION >= 7
const SinkMetadata metadata = {
{{.source = toString(source),
.gain = volume,
.tags = {},
.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}};
#endif
ASSERT_OK(stream->updateSinkMetadata(metadata))
<< "source=" << toString(source) << ", volume=" << volume;
<< "source=" << toString(source) << ", volume=" << volume;
}
}
@ -264,9 +252,30 @@ TEST_P(InputStreamTest, updateSinkMetadata) {
// Set no metadata as if all stream track had stopped
ASSERT_OK(stream->updateSinkMetadata({}));
// Restore initial
ASSERT_OK(stream->updateSinkMetadata(initMetadata));
#elif MAJOR_VERSION >= 7
xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioSource> range;
// Test all possible track configuration
for (auto source : range) {
for (float volume : {0.0, 0.5, 1.0}) {
const SinkMetadata metadata = {
{{.source = toString(source),
.gain = volume,
.tags = {},
.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}};
ASSERT_RESULT(okOrNotSupported, stream->updateSinkMetadata(metadata))
<< "source=" << toString(source) << ", volume=" << volume;
}
}
// Do not test concurrent capture as this is not officially supported
// Set no metadata as if all stream track had stopped
ASSERT_RESULT(okOrNotSupported, stream->updateSinkMetadata({}));
// Restore initial
ASSERT_RESULT(okOrNotSupported, stream->updateSinkMetadata(initMetadata));
#endif
}
TEST_P(OutputStreamTest, SelectPresentation) {
@ -276,44 +285,55 @@ TEST_P(OutputStreamTest, SelectPresentation) {
TEST_P(OutputStreamTest, updateSourceMetadata) {
doc::test("The HAL should not crash on metadata change");
#if MAJOR_VERSION <= 6
hidl_enum_range<AudioUsage> usageRange;
hidl_enum_range<AudioContentType> contentRange;
#elif MAJOR_VERSION >= 7
xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioUsage> usageRange;
xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioContentType> contentRange;
#endif
// Test all possible track configuration
for (auto usage : usageRange) {
for (auto content : contentRange) {
for (float volume : {0.0, 0.5, 1.0}) {
#if MAJOR_VERSION <= 6
const SourceMetadata metadata = {{{usage, content, volume}}};
#elif MAJOR_VERSION >= 7
const SourceMetadata metadata = {
{{toString(usage),
toString(content),
{} /* tags */,
toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
volume}}};
#endif
ASSERT_OK(stream->updateSourceMetadata(metadata))
<< "usage=" << toString(usage) << ", content=" << toString(content)
<< ", volume=" << volume;
}
}
}
// clang-format off
// Set many track of different configuration
// clang-format off
ASSERT_OK(stream->updateSourceMetadata(
#if MAJOR_VERSION <= 6
{{{AudioUsage::MEDIA, AudioContentType::MUSIC, 0.1},
{AudioUsage::VOICE_COMMUNICATION, AudioContentType::SPEECH, 1.0},
{AudioUsage::ALARM, AudioContentType::SONIFICATION, 0.0},
{AudioUsage::ASSISTANT, AudioContentType::UNKNOWN, 0.3}}}
));
// clang-format on
// Set no metadata as if all stream track had stopped
ASSERT_OK(stream->updateSourceMetadata({}));
// Restore initial
ASSERT_OK(stream->updateSourceMetadata(initMetadata));
#elif MAJOR_VERSION >= 7
xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioUsage> usageRange;
xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioContentType> contentRange;
// Test all possible track configuration
for (auto usage : usageRange) {
for (auto content : contentRange) {
for (float volume : {0.0, 0.5, 1.0}) {
const SourceMetadata metadata = {
{{toString(usage),
toString(content),
{} /* tags */,
toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
volume}}};
ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(metadata))
<< "usage=" << toString(usage) << ", content=" << toString(content)
<< ", volume=" << volume;
}
}
}
// Set many track of different configuration
// clang-format off
ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(
{{{toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
{},
@ -334,15 +354,13 @@ TEST_P(OutputStreamTest, updateSourceMetadata) {
{},
toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO),
0.3}}}
#endif
));
// clang-format on
// Set no metadata as if all stream track had stopped
ASSERT_OK(stream->updateSourceMetadata({}));
ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata({}));
// Restore initial
ASSERT_OK(stream->updateSourceMetadata(initMetadata));
ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(initMetadata));
#endif
}
TEST_P(AudioPrimaryHidlTest, setMode) {

View file

@ -18,84 +18,110 @@
#include "5.0/AudioPrimaryHidlHalTest.cpp"
#if MAJOR_VERSION <= 6
const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters() {
static std::vector<DeviceConfigParameter> parameters = [] {
std::vector<DeviceConfigParameter> result;
for (const auto& device : getDeviceParameters()) {
auto module =
getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
for (const auto& ioProfile : module->getOutputProfiles()) {
for (const auto& profile : ioProfile->getAudioProfiles()) {
const auto& channels = profile->getChannels();
const auto& sampleRates = profile->getSampleRates();
auto configs = ConfigHelper::combineAudioConfig(
std::vector<audio_channel_mask_t>(channels.begin(), channels.end()),
std::vector<uint32_t>(sampleRates.begin(), sampleRates.end()),
profile->getFormat());
auto flags = ioProfile->getFlags();
for (auto& config : configs) {
// Some combinations of flags declared in the config file require special
// treatment.
if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
config.offloadInfo.sampleRateHz = config.sampleRateHz;
config.offloadInfo.channelMask = config.channelMask;
config.offloadInfo.format = config.format;
config.offloadInfo.streamType = AudioStreamType::MUSIC;
config.offloadInfo.bitRatePerSecond = 320;
config.offloadInfo.durationMicroseconds = -1;
config.offloadInfo.bitWidth = 16;
config.offloadInfo.bufferSize = 256; // arbitrary value
config.offloadInfo.usage = AudioUsage::MEDIA;
result.emplace_back(device, config,
AudioOutputFlag(AudioOutputFlag::COMPRESS_OFFLOAD |
AudioOutputFlag::DIRECT));
} else {
if (flags & AUDIO_OUTPUT_FLAG_PRIMARY) { // ignore the flag
flags &= ~AUDIO_OUTPUT_FLAG_PRIMARY;
}
result.emplace_back(device, config, AudioOutputFlag(flags));
static std::vector<DeviceConfigParameter> generateOutputDeviceConfigParameters(
bool oneProfilePerDevice) {
std::vector<DeviceConfigParameter> result;
for (const auto& device : getDeviceParameters()) {
auto module =
getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
for (const auto& ioProfile : module->getOutputProfiles()) {
for (const auto& profile : ioProfile->getAudioProfiles()) {
const auto& channels = profile->getChannels();
const auto& sampleRates = profile->getSampleRates();
auto configs = ConfigHelper::combineAudioConfig(
std::vector<audio_channel_mask_t>(channels.begin(), channels.end()),
std::vector<uint32_t>(sampleRates.begin(), sampleRates.end()),
profile->getFormat());
auto flags = ioProfile->getFlags();
for (auto& config : configs) {
// Some combinations of flags declared in the config file require special
// treatment.
if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
config.offloadInfo.sampleRateHz = config.sampleRateHz;
config.offloadInfo.channelMask = config.channelMask;
config.offloadInfo.format = config.format;
config.offloadInfo.streamType = AudioStreamType::MUSIC;
config.offloadInfo.bitRatePerSecond = 320;
config.offloadInfo.durationMicroseconds = -1;
config.offloadInfo.bitWidth = 16;
config.offloadInfo.bufferSize = 256; // arbitrary value
config.offloadInfo.usage = AudioUsage::MEDIA;
result.emplace_back(device, config,
AudioOutputFlag(AudioOutputFlag::COMPRESS_OFFLOAD |
AudioOutputFlag::DIRECT));
} else {
if (flags & AUDIO_OUTPUT_FLAG_PRIMARY) { // ignore the flag
flags &= ~AUDIO_OUTPUT_FLAG_PRIMARY;
}
result.emplace_back(device, config, AudioOutputFlag(flags));
}
if (oneProfilePerDevice) break;
}
if (oneProfilePerDevice) break;
}
if (oneProfilePerDevice) break;
}
return result;
}();
}
return result;
}
const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters() {
static std::vector<DeviceConfigParameter> parameters =
generateOutputDeviceConfigParameters(false);
return parameters;
}
const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters() {
static std::vector<DeviceConfigParameter> parameters = [] {
std::vector<DeviceConfigParameter> result;
for (const auto& device : getDeviceParameters()) {
auto module =
getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
for (const auto& ioProfile : module->getInputProfiles()) {
for (const auto& profile : ioProfile->getAudioProfiles()) {
const auto& channels = profile->getChannels();
const auto& sampleRates = profile->getSampleRates();
auto configs = ConfigHelper::combineAudioConfig(
std::vector<audio_channel_mask_t>(channels.begin(), channels.end()),
std::vector<uint32_t>(sampleRates.begin(), sampleRates.end()),
profile->getFormat());
for (const auto& config : configs) {
result.emplace_back(device, config, AudioInputFlag(ioProfile->getFlags()));
}
const std::vector<DeviceConfigParameter>& getOutputDeviceSingleConfigParameters() {
static std::vector<DeviceConfigParameter> parameters =
generateOutputDeviceConfigParameters(true);
return parameters;
}
static std::vector<DeviceConfigParameter> generateInputDeviceConfigParameters(
bool oneProfilePerDevice) {
std::vector<DeviceConfigParameter> result;
for (const auto& device : getDeviceParameters()) {
auto module =
getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
for (const auto& ioProfile : module->getInputProfiles()) {
for (const auto& profile : ioProfile->getAudioProfiles()) {
const auto& channels = profile->getChannels();
const auto& sampleRates = profile->getSampleRates();
auto configs = ConfigHelper::combineAudioConfig(
std::vector<audio_channel_mask_t>(channels.begin(), channels.end()),
std::vector<uint32_t>(sampleRates.begin(), sampleRates.end()),
profile->getFormat());
for (const auto& config : configs) {
result.emplace_back(device, config, AudioInputFlag(ioProfile->getFlags()));
if (oneProfilePerDevice) break;
}
if (oneProfilePerDevice) break;
}
if (oneProfilePerDevice) break;
}
return result;
}();
}
return result;
}
const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters() {
static std::vector<DeviceConfigParameter> parameters =
generateInputDeviceConfigParameters(false);
return parameters;
}
const std::vector<DeviceConfigParameter>& getInputDeviceSingleConfigParameters() {
static std::vector<DeviceConfigParameter> parameters =
generateInputDeviceConfigParameters(true);
return parameters;
}
#endif // MAJOR_VERSION <= 6
TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedOutputStreams) {
doc::test("Verify that a device can't be closed if there are streams opened");
class SingleOutputConfigTest : public AudioHidlTestWithDeviceConfigParameter {};
TEST_P(SingleOutputConfigTest, CloseDeviceWithOpenedOutputStreams) {
doc::test("Verify that a device can't be closed if there are output streams opened");
#if MAJOR_VERSION <= 6
DeviceAddress address{.device = AudioDevice::OUT_DEFAULT};
SourceMetadata initMetadata = {{{AudioUsage::MEDIA, AudioContentType::MUSIC, 1 /* gain */}}};
auto flags = hidl_bitfield<AudioOutputFlag>(AudioOutputFlag::NONE);
#elif MAJOR_VERSION >= 7
DeviceAddress address{.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT)};
SourceMetadata initMetadata = {{{toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
@ -103,9 +129,9 @@ TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedOutputStreams) {
{} /* tags */,
toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
1 /* gain */}}};
hidl_vec<AudioInOutFlag> flags;
#endif
AudioConfig config{};
const AudioConfig& config = getConfig();
auto flags = getOutputFlags();
sp<IStreamOut> stream;
StreamHelper<IStreamOut> helper(stream);
AudioConfig suggestedConfig{};
@ -120,16 +146,17 @@ TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedOutputStreams) {
ASSERT_OK(getDevice()->close());
ASSERT_TRUE(resetDevice());
}
INSTANTIATE_TEST_CASE_P(SingleOutputConfig, SingleOutputConfigTest,
::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
&DeviceConfigParameterToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleOutputConfig);
TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedInputStreams) {
doc::test("Verify that a device can't be closed if there are streams opened");
if (!getCachedPolicyConfig().haveInputProfilesInModule(getDeviceName())) {
GTEST_SKIP() << "Device doesn't have input profiles";
}
class SingleInputConfigTest : public AudioHidlTestWithDeviceConfigParameter {};
TEST_P(SingleInputConfigTest, CloseDeviceWithOpenedInputStreams) {
doc::test("Verify that a device can't be closed if there are input streams opened");
#if MAJOR_VERSION <= 6
DeviceAddress address{.device = AudioDevice::IN_DEFAULT};
SinkMetadata initMetadata = {{{.source = AudioSource::MIC, .gain = 1}}};
auto flags = hidl_bitfield<AudioInputFlag>(AudioInputFlag::NONE);
#elif MAJOR_VERSION >= 7
DeviceAddress address{.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT)};
SinkMetadata initMetadata = {
@ -137,9 +164,9 @@ TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedInputStreams) {
.gain = 1,
.tags = {},
.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}};
hidl_vec<AudioInOutFlag> flags;
#endif
AudioConfig config{};
const AudioConfig& config = getConfig();
auto flags = getInputFlags();
sp<IStreamIn> stream;
StreamHelper<IStreamIn> helper(stream);
AudioConfig suggestedConfig{};
@ -154,6 +181,10 @@ TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedInputStreams) {
ASSERT_OK(getDevice()->close());
ASSERT_TRUE(resetDevice());
}
INSTANTIATE_TEST_CASE_P(SingleInputConfig, SingleInputConfigTest,
::testing::ValuesIn(getInputDeviceSingleConfigParameters()),
&DeviceConfigParameterToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleInputConfig);
TEST_P(AudioPatchHidlTest, UpdatePatchInvalidHandle) {
doc::test("Verify that passing an invalid handle to updateAudioPatch is checked");

View file

@ -25,7 +25,6 @@ static std::vector<AudioConfig> combineAudioConfig(std::vector<xsd::AudioChannel
for (auto channelMask : channelMasks) {
for (auto sampleRate : sampleRates) {
AudioConfig config{};
// leave offloadInfo to 0
config.base.channelMask = toString(channelMask);
config.base.sampleRateHz = sampleRate;
config.base.format = format;
@ -35,56 +34,158 @@ static std::vector<AudioConfig> combineAudioConfig(std::vector<xsd::AudioChannel
return configs;
}
static std::tuple<std::vector<AudioInOutFlag>, bool> generateOutFlags(
const xsd::MixPorts::MixPort& mixPort) {
static const std::vector<AudioInOutFlag> offloadFlags = {
toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD),
toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_DIRECT)};
std::vector<AudioInOutFlag> flags;
bool isOffload = false;
if (mixPort.hasFlags()) {
auto xsdFlags = mixPort.getFlags();
isOffload = std::find(xsdFlags.begin(), xsdFlags.end(),
xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) !=
xsdFlags.end();
if (!isOffload) {
for (auto flag : xsdFlags) {
if (flag != xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_PRIMARY) {
flags.push_back(toString(flag));
}
}
} else {
flags = offloadFlags;
}
}
return {flags, isOffload};
}
static AudioOffloadInfo generateOffloadInfo(const AudioConfigBase& base) {
return AudioOffloadInfo{
.base = base,
.streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC),
.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
.bitRatePerSecond = 320,
.durationMicroseconds = -1,
.bitWidth = 16,
.bufferSize = 256 // arbitrary value
};
}
static std::vector<DeviceConfigParameter> generateOutputDeviceConfigParameters(
bool oneProfilePerDevice) {
std::vector<DeviceConfigParameter> result;
for (const auto& device : getDeviceParameters()) {
auto module =
getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
if (!module || !module->getFirstMixPorts()) break;
for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
if (mixPort.getRole() != xsd::Role::source) continue; // not an output profile
auto [flags, isOffload] = generateOutFlags(mixPort);
for (const auto& profile : mixPort.getProfile()) {
auto configs = combineAudioConfig(profile.getChannelMasks(),
profile.getSamplingRates(), profile.getFormat());
for (auto& config : configs) {
// Some combinations of flags declared in the config file require special
// treatment.
if (isOffload) {
config.offloadInfo.info(generateOffloadInfo(config.base));
}
result.emplace_back(device, config, flags);
if (oneProfilePerDevice) break;
}
if (oneProfilePerDevice) break;
}
if (oneProfilePerDevice) break;
}
}
return result;
}
const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters() {
static std::vector<DeviceConfigParameter> parameters = [] {
static std::vector<DeviceConfigParameter> parameters =
generateOutputDeviceConfigParameters(false);
return parameters;
}
const std::vector<DeviceConfigParameter>& getOutputDeviceSingleConfigParameters() {
static std::vector<DeviceConfigParameter> parameters =
generateOutputDeviceConfigParameters(true);
return parameters;
}
const std::vector<DeviceConfigParameter>& getOutputDeviceInvalidConfigParameters(
bool generateInvalidFlags = true) {
static std::vector<DeviceConfigParameter> parameters = [&] {
std::vector<DeviceConfigParameter> result;
const std::vector<AudioInOutFlag> offloadFlags = {
toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD),
toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_DIRECT)};
for (const auto& device : getDeviceParameters()) {
auto module =
getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
if (!module || !module->getFirstMixPorts()) break;
bool hasRegularConfig = false, hasOffloadConfig = false;
for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
if (mixPort.getRole() != xsd::Role::source) continue; // not an output profile
std::vector<AudioInOutFlag> flags;
bool isOffload = false;
if (mixPort.hasFlags()) {
auto xsdFlags = mixPort.getFlags();
isOffload =
std::find(xsdFlags.begin(), xsdFlags.end(),
xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) !=
xsdFlags.end();
if (!isOffload) {
for (auto flag : xsdFlags) {
if (flag != xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_PRIMARY) {
flags.push_back(toString(flag));
}
}
} else {
flags = offloadFlags;
}
}
auto [validFlags, isOffload] = generateOutFlags(mixPort);
if ((!isOffload && hasRegularConfig) || (isOffload && hasOffloadConfig)) continue;
for (const auto& profile : mixPort.getProfile()) {
auto configs =
combineAudioConfig(profile.getChannelMasks(),
profile.getSamplingRates(), profile.getFormat());
for (auto& config : configs) {
// Some combinations of flags declared in the config file require special
// treatment.
if (!profile.hasFormat() || !profile.hasSamplingRates() ||
!profile.hasChannelMasks())
continue;
AudioConfigBase validBase = {
profile.getFormat(),
static_cast<uint32_t>(profile.getSamplingRates()[0]),
toString(profile.getChannelMasks()[0])};
{
AudioConfig config{.base = validBase};
config.base.channelMask = "random_string";
if (isOffload) {
config.offloadInfo.base = config.base;
config.offloadInfo.streamType =
toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC);
config.offloadInfo.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA);
config.offloadInfo.bitRatePerSecond = 320;
config.offloadInfo.durationMicroseconds = -1;
config.offloadInfo.bitWidth = 16;
config.offloadInfo.bufferSize = 256; // arbitrary value
config.offloadInfo.info(generateOffloadInfo(validBase));
}
result.emplace_back(device, config, validFlags);
}
{
AudioConfig config{.base = validBase};
config.base.format = "random_string";
if (isOffload) {
config.offloadInfo.info(generateOffloadInfo(validBase));
}
result.emplace_back(device, config, validFlags);
}
if (generateInvalidFlags) {
AudioConfig config{.base = validBase};
if (isOffload) {
config.offloadInfo.info(generateOffloadInfo(validBase));
}
std::vector<AudioInOutFlag> flags = {"random_string", ""};
result.emplace_back(device, config, flags);
}
if (isOffload) {
{
AudioConfig config{.base = validBase};
config.offloadInfo.info(generateOffloadInfo(validBase));
config.offloadInfo.info().base.channelMask = "random_string";
}
{
AudioConfig config{.base = validBase};
config.offloadInfo.info(generateOffloadInfo(validBase));
config.offloadInfo.info().base.format = "random_string";
}
{
AudioConfig config{.base = validBase};
config.offloadInfo.info(generateOffloadInfo(validBase));
config.offloadInfo.info().streamType = "random_string";
}
{
AudioConfig config{.base = validBase};
config.offloadInfo.info(generateOffloadInfo(validBase));
config.offloadInfo.info().usage = "random_string";
}
hasOffloadConfig = true;
} else {
hasRegularConfig = true;
}
break;
}
if (hasOffloadConfig && hasRegularConfig) break;
}
}
return result;
@ -92,32 +193,346 @@ const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters() {
return parameters;
}
static std::vector<DeviceConfigParameter> generateInputDeviceConfigParameters(
bool oneProfilePerDevice) {
std::vector<DeviceConfigParameter> result;
for (const auto& device : getDeviceParameters()) {
auto module =
getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
if (!module || !module->getFirstMixPorts()) break;
for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
if (mixPort.getRole() != xsd::Role::sink) continue; // not an input profile
std::vector<AudioInOutFlag> flags;
if (mixPort.hasFlags()) {
std::transform(mixPort.getFlags().begin(), mixPort.getFlags().end(),
std::back_inserter(flags), [](auto flag) { return toString(flag); });
}
for (const auto& profile : mixPort.getProfile()) {
auto configs = combineAudioConfig(profile.getChannelMasks(),
profile.getSamplingRates(), profile.getFormat());
for (const auto& config : configs) {
result.emplace_back(device, config, flags);
if (oneProfilePerDevice) break;
}
if (oneProfilePerDevice) break;
}
if (oneProfilePerDevice) break;
}
}
return result;
}
const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters() {
static std::vector<DeviceConfigParameter> parameters = [] {
static std::vector<DeviceConfigParameter> parameters =
generateInputDeviceConfigParameters(false);
return parameters;
}
const std::vector<DeviceConfigParameter>& getInputDeviceSingleConfigParameters() {
static std::vector<DeviceConfigParameter> parameters =
generateInputDeviceConfigParameters(true);
return parameters;
}
const std::vector<DeviceConfigParameter>& getInputDeviceInvalidConfigParameters(
bool generateInvalidFlags = true) {
static std::vector<DeviceConfigParameter> parameters = [&] {
std::vector<DeviceConfigParameter> result;
for (const auto& device : getDeviceParameters()) {
auto module =
getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
if (!module || !module->getFirstMixPorts()) break;
bool hasConfig = false;
for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
if (mixPort.getRole() != xsd::Role::sink) continue; // not an input profile
std::vector<AudioInOutFlag> flags;
std::vector<AudioInOutFlag> validFlags;
if (mixPort.hasFlags()) {
std::transform(mixPort.getFlags().begin(), mixPort.getFlags().end(),
std::back_inserter(flags),
std::back_inserter(validFlags),
[](auto flag) { return toString(flag); });
}
for (const auto& profile : mixPort.getProfile()) {
auto configs =
combineAudioConfig(profile.getChannelMasks(),
profile.getSamplingRates(), profile.getFormat());
for (const auto& config : configs) {
if (!profile.hasFormat() || !profile.hasSamplingRates() ||
!profile.hasChannelMasks())
continue;
AudioConfigBase validBase = {
profile.getFormat(),
static_cast<uint32_t>(profile.getSamplingRates()[0]),
toString(profile.getChannelMasks()[0])};
{
AudioConfig config{.base = validBase};
config.base.channelMask = "random_string";
result.emplace_back(device, config, validFlags);
}
{
AudioConfig config{.base = validBase};
config.base.format = "random_string";
result.emplace_back(device, config, validFlags);
}
if (generateInvalidFlags) {
AudioConfig config{.base = validBase};
std::vector<AudioInOutFlag> flags = {"random_string", ""};
result.emplace_back(device, config, flags);
}
hasConfig = true;
break;
}
if (hasConfig) break;
}
}
return result;
}();
return parameters;
}
class InvalidInputConfigNoFlagsTest : public AudioHidlTestWithDeviceConfigParameter {};
TEST_P(InvalidInputConfigNoFlagsTest, InputBufferSizeTest) {
doc::test("Verify that invalid config is rejected by IDevice::getInputBufferSize method.");
uint64_t bufferSize;
ASSERT_OK(getDevice()->getInputBufferSize(getConfig(), returnIn(res, bufferSize)));
EXPECT_EQ(Result::INVALID_ARGUMENTS, res);
}
INSTANTIATE_TEST_CASE_P(
InputBufferSizeInvalidConfig, InvalidInputConfigNoFlagsTest,
::testing::ValuesIn(getInputDeviceInvalidConfigParameters(false /*generateInvalidFlags*/)),
&DeviceConfigParameterToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputBufferSizeInvalidConfig);
enum { PARAM_DEVICE_CONFIG, PARAM_ADDRESS, PARAM_METADATA };
enum { INDEX_SINK, INDEX_SOURCE };
using SinkOrSourceMetadata = std::variant<SinkMetadata, SourceMetadata>;
using StreamOpenParameter = std::tuple<DeviceConfigParameter, DeviceAddress, SinkOrSourceMetadata>;
static std::string StreamOpenParameterToString(
const ::testing::TestParamInfo<StreamOpenParameter>& info) {
return DeviceConfigParameterToString(::testing::TestParamInfo<DeviceConfigParameter>{
std::get<PARAM_DEVICE_CONFIG>(info.param), info.index}) +
"__" +
SanitizeStringForGTestName(
::testing::PrintToString(std::get<PARAM_ADDRESS>(info.param))) +
"__" +
SanitizeStringForGTestName(std::visit(
[](auto&& arg) -> std::string { return ::testing::PrintToString(arg); },
std::get<PARAM_METADATA>(info.param)));
}
class StreamOpenTest : public HidlTest, public ::testing::WithParamInterface<StreamOpenParameter> {
protected:
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(HidlTest::SetUp()); // setup base
ASSERT_TRUE(getDevicesFactory() != nullptr);
ASSERT_TRUE(getDevice() != nullptr);
}
const std::string& getFactoryName() const override {
return std::get<PARAM_FACTORY_NAME>(
std::get<PARAM_DEVICE>(std::get<PARAM_DEVICE_CONFIG>(GetParam())));
}
const std::string& getDeviceName() const override {
return std::get<PARAM_DEVICE_NAME>(
std::get<PARAM_DEVICE>(std::get<PARAM_DEVICE_CONFIG>(GetParam())));
}
const AudioConfig& getConfig() const {
return std::get<PARAM_CONFIG>(std::get<PARAM_DEVICE_CONFIG>(GetParam()));
}
const hidl_vec<AudioInOutFlag> getFlags() const {
return std::get<PARAM_FLAGS>(std::get<PARAM_DEVICE_CONFIG>(GetParam()));
}
const DeviceAddress& getDeviceAddress() const { return std::get<PARAM_ADDRESS>(GetParam()); }
bool isParamForInputStream() const {
return std::get<PARAM_METADATA>(GetParam()).index() == INDEX_SINK;
}
const SinkMetadata& getSinkMetadata() const {
return std::get<INDEX_SINK>(std::get<PARAM_METADATA>(GetParam()));
}
const SourceMetadata& getSourceMetadata() const {
return std::get<INDEX_SOURCE>(std::get<PARAM_METADATA>(GetParam()));
}
};
static const DeviceAddress& getValidInputDeviceAddress() {
static const DeviceAddress valid = {
.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT)};
return valid;
}
static const DeviceAddress& getValidOutputDeviceAddress() {
static const DeviceAddress valid = {
.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT)};
return valid;
}
static const DeviceAddress& getInvalidDeviceAddress() {
static const DeviceAddress valid = {.deviceType = "random_string"};
return valid;
}
static const RecordTrackMetadata& getValidRecordTrackMetadata() {
static const RecordTrackMetadata valid = {
.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT), .gain = 1};
return valid;
}
static const RecordTrackMetadata& getValidRecordTrackMetadataWithDest() {
static const RecordTrackMetadata valid = {
.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
.gain = 1,
.destination = [] {
RecordTrackMetadata::Destination dest;
dest.device(getValidOutputDeviceAddress());
return dest;
}()};
return valid;
}
static const RecordTrackMetadata& getInvalidSourceRecordTrackMetadata() {
static const RecordTrackMetadata invalid = {.source = "random_string", .gain = 1};
return invalid;
}
static const RecordTrackMetadata& getRecordTrackMetadataWithInvalidDest() {
static const RecordTrackMetadata invalid = {
.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
.gain = 1,
.destination = [] {
RecordTrackMetadata::Destination dest;
dest.device(getInvalidDeviceAddress());
return dest;
}()};
return invalid;
}
static const PlaybackTrackMetadata& getValidPlaybackTrackMetadata() {
static const PlaybackTrackMetadata valid = {
.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
.contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
.gain = 1};
return valid;
}
static const PlaybackTrackMetadata& getInvalidUsagePlaybackTrackMetadata() {
static const PlaybackTrackMetadata invalid = {
.usage = "random_string",
.contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
.gain = 1};
return invalid;
}
static const PlaybackTrackMetadata& getInvalidContentTypePlaybackTrackMetadata() {
static const PlaybackTrackMetadata invalid = {
.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
.contentType = "random_string",
.gain = 1};
return invalid;
}
static const std::vector<SourceMetadata>& getInvalidSourceMetadatas() {
static const std::vector<SourceMetadata> invalids = {
SourceMetadata{.tracks = {{getInvalidUsagePlaybackTrackMetadata()}}},
SourceMetadata{.tracks = {{getInvalidContentTypePlaybackTrackMetadata()}}},
SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
getInvalidUsagePlaybackTrackMetadata()}}},
SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
getInvalidContentTypePlaybackTrackMetadata()}}}};
return invalids;
}
static const std::vector<SinkMetadata>& getInvalidSinkMetadatas() {
static const std::vector<SinkMetadata> invalids = {
SinkMetadata{.tracks = {{getInvalidSourceRecordTrackMetadata()}}},
SinkMetadata{.tracks = {{getRecordTrackMetadataWithInvalidDest()}}},
SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
getInvalidSourceRecordTrackMetadata()}}},
SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
getRecordTrackMetadataWithInvalidDest()}}}};
return invalids;
}
template <typename T>
static inline std::vector<SinkOrSourceMetadata> wrapMetadata(const std::vector<T>& metadata) {
return std::vector<SinkOrSourceMetadata>{metadata.begin(), metadata.end()};
}
TEST_P(StreamOpenTest, OpenInputOrOutputStreamTest) {
doc::test(
"Verify that invalid arguments are rejected by "
"IDevice::open{Input|Output}Stream method.");
AudioConfig suggestedConfig{};
if (isParamForInputStream()) {
sp<IStreamIn> stream;
ASSERT_OK(getDevice()->openInputStream(AudioIoHandle{}, getDeviceAddress(), getConfig(),
getFlags(), getSinkMetadata(),
returnIn(res, stream, suggestedConfig)));
ASSERT_TRUE(stream == nullptr);
} else {
sp<IStreamOut> stream;
ASSERT_OK(getDevice()->openOutputStream(AudioIoHandle{}, getDeviceAddress(), getConfig(),
getFlags(), getSourceMetadata(),
returnIn(res, stream, suggestedConfig)));
ASSERT_TRUE(stream == nullptr);
}
EXPECT_EQ(Result::INVALID_ARGUMENTS, res);
EXPECT_EQ(AudioConfig{}, suggestedConfig);
}
INSTANTIATE_TEST_CASE_P(
InputStreamInvalidConfig, StreamOpenTest,
::testing::Combine(::testing::ValuesIn(getInputDeviceInvalidConfigParameters()),
::testing::Values(getValidInputDeviceAddress()),
::testing::Values(SinkMetadata{
.tracks = {{getValidRecordTrackMetadata(),
getValidRecordTrackMetadataWithDest()}}})),
&StreamOpenParameterToString);
INSTANTIATE_TEST_CASE_P(
InputStreamInvalidAddress, StreamOpenTest,
::testing::Combine(::testing::ValuesIn(getInputDeviceSingleConfigParameters()),
::testing::Values(getInvalidDeviceAddress()),
::testing::Values(SinkMetadata{
.tracks = {{getValidRecordTrackMetadata(),
getValidRecordTrackMetadataWithDest()}}})),
&StreamOpenParameterToString);
INSTANTIATE_TEST_CASE_P(
InputStreamInvalidMetadata, StreamOpenTest,
::testing::Combine(::testing::ValuesIn(getInputDeviceSingleConfigParameters()),
::testing::Values(getValidInputDeviceAddress()),
::testing::ValuesIn(wrapMetadata(getInvalidSinkMetadatas()))),
&StreamOpenParameterToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputStreamInvalidConfig);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputStreamInvalidAddress);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputStreamInvalidMetadata);
INSTANTIATE_TEST_CASE_P(
OutputStreamInvalidConfig, StreamOpenTest,
::testing::Combine(::testing::ValuesIn(getOutputDeviceInvalidConfigParameters()),
::testing::Values(getValidOutputDeviceAddress()),
::testing::Values(SourceMetadata{
.tracks = {{getValidPlaybackTrackMetadata()}}})),
&StreamOpenParameterToString);
INSTANTIATE_TEST_CASE_P(
OutputStreamInvalidAddress, StreamOpenTest,
::testing::Combine(::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
::testing::Values(getInvalidDeviceAddress()),
::testing::Values(SourceMetadata{
.tracks = {{getValidPlaybackTrackMetadata()}}})),
&StreamOpenParameterToString);
INSTANTIATE_TEST_CASE_P(
OutputStreamInvalidMetadata, StreamOpenTest,
::testing::Combine(::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
::testing::Values(getValidOutputDeviceAddress()),
::testing::ValuesIn(wrapMetadata(getInvalidSourceMetadatas()))),
&StreamOpenParameterToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OutputStreamInvalidConfig);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OutputStreamInvalidAddress);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OutputStreamInvalidMetadata);
TEST_P(OutputStreamTest, UpdateInvalidSourceMetadata) {
doc::test("Verify that invalid metadata is rejected by IStreamOut::updateSourceMetadata");
for (const auto& metadata : getInvalidSourceMetadatas()) {
ASSERT_RESULT(invalidArgsOrNotSupported, stream->updateSourceMetadata(metadata))
<< ::testing::PrintToString(metadata);
}
}
TEST_P(InputStreamTest, UpdateInvalidSinkMetadata) {
doc::test("Verify that invalid metadata is rejected by IStreamIn::updateSinkMetadata");
for (const auto& metadata : getInvalidSinkMetadatas()) {
ASSERT_RESULT(invalidArgsOrNotSupported, stream->updateSinkMetadata(metadata))
<< ::testing::PrintToString(metadata);
}
}

View file

@ -522,7 +522,9 @@ using DeviceConfigParameter = std::tuple<DeviceParameter, AudioConfig, std::vect
#if MAJOR_VERSION >= 6
const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters();
const std::vector<DeviceConfigParameter>& getInputDeviceSingleConfigParameters();
const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters();
const std::vector<DeviceConfigParameter>& getOutputDeviceSingleConfigParameters();
#endif
#if MAJOR_VERSION >= 4
@ -883,7 +885,7 @@ class OpenStreamTest : public AudioHidlTestWithDeviceConfigParameter {
AudioHidlTestWithDeviceConfigParameter::TearDown();
}
protected:
protected:
AudioConfig audioConfig;
DeviceAddress address = {};
sp<Stream> stream;
@ -973,7 +975,7 @@ class InputStreamTest : public OpenStreamTest<IStreamIn> {
ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
#if MAJOR_VERSION <= 6
address.device = AudioDevice::IN_DEFAULT;
#elif MAJOR_VERSION <= 7
#elif MAJOR_VERSION >= 7
address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT);
#endif
const AudioConfig& config = getConfig();
@ -988,7 +990,7 @@ class InputStreamTest : public OpenStreamTest<IStreamIn> {
protected:
#if MAJOR_VERSION == 2
const AudioSource initMetadata = AudioSource::DEFAULT;
const AudioSource initMetadata = AudioSource::DEFAULT;
#elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6
const SinkMetadata initMetadata = {{ {.source = AudioSource::DEFAULT, .gain = 1 } }};
#elif MAJOR_VERSION >= 7