Implement a.h.audio.common@7.0-util module

This includes a rewrite of HidlUtils for V7.
A unit test is added for its conversion functions.

Made necessary minor adjustments to the HAL V7 types.
Also, fixed definition of 'audioFormatsList' to allow for
vendor extensions.

Bug: 142480271
Bug: 173647783
Test: m && atest android.hardware.audio.common@7.0-util_tests
Change-Id: Ib883f1c246fce78c004846516699aa724d4b5d44
Merged-In: Ib883f1c246fce78c004846516699aa724d4b5d44
diff --git a/audio/common/all-versions/default/7.0/HidlUtils.cpp b/audio/common/all-versions/default/7.0/HidlUtils.cpp
new file mode 100644
index 0000000..1a66282
--- /dev/null
+++ b/audio/common/all-versions/default/7.0/HidlUtils.cpp
@@ -0,0 +1,847 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#define LOG_TAG "HidlUtils"
+#include <log/log.h>
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <common/all-versions/VersionUtils.h>
+
+#include "HidlUtils.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace common {
+namespace CPP_VERSION {
+namespace implementation {
+
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+#define CONVERT_CHECKED(expr, result)                   \
+    if (status_t status = (expr); status != NO_ERROR) { \
+        result = status;                                \
+    }
+
+status_t HidlUtils::audioIndexChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                 AudioChannelMask* channelMask) {
+    *channelMask = audio_channel_index_mask_to_string(halChannelMask);
+    if (!channelMask->empty() && !xsd::isUnknownAudioChannelMask(*channelMask)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown index channel mask value 0x%X", halChannelMask);
+    *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioInputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                 AudioChannelMask* channelMask) {
+    *channelMask = audio_channel_in_mask_to_string(halChannelMask);
+    if (!channelMask->empty() && !xsd::isUnknownAudioChannelMask(*channelMask)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown input channel mask value 0x%X", halChannelMask);
+    *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioOutputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                  AudioChannelMask* channelMask) {
+    *channelMask = audio_channel_out_mask_to_string(halChannelMask);
+    if (!channelMask->empty() && !xsd::isUnknownAudioChannelMask(*channelMask)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown output channel mask value 0x%X", halChannelMask);
+    *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioChannelMaskFromHal(audio_channel_mask_t halChannelMask, bool isInput,
+                                            AudioChannelMask* channelMask) {
+    if (halChannelMask != AUDIO_CHANNEL_NONE) {
+        if (audio_channel_mask_is_valid(halChannelMask)) {
+            switch (audio_channel_mask_get_representation(halChannelMask)) {
+                case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+                    return isInput ? audioInputChannelMaskFromHal(halChannelMask, channelMask)
+                                   : audioOutputChannelMaskFromHal(halChannelMask, channelMask);
+                case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+                    // Index masks do not have direction.
+                    return audioIndexChannelMaskFromHal(halChannelMask, channelMask);
+                    // no default
+            }
+        }
+        *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+        return BAD_VALUE;
+    }
+    *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return NO_ERROR;
+}
+
+status_t HidlUtils::audioChannelMaskToHal(const AudioChannelMask& channelMask,
+                                          audio_channel_mask_t* halChannelMask) {
+    if (!xsd::isUnknownAudioChannelMask(channelMask) &&
+        audio_channel_mask_from_string(channelMask.c_str(), halChannelMask)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown channel mask \"%s\"", channelMask.c_str());
+    *halChannelMask = AUDIO_CHANNEL_NONE;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioConfigBaseFromHal(const audio_config_base_t& halConfigBase, bool isInput,
+                                           AudioConfigBase* configBase) {
+    status_t result = NO_ERROR;
+    configBase->sampleRateHz = halConfigBase.sample_rate;
+    CONVERT_CHECKED(
+            audioChannelMaskFromHal(halConfigBase.channel_mask, isInput, &configBase->channelMask),
+            result);
+    CONVERT_CHECKED(audioFormatFromHal(halConfigBase.format, &configBase->format), result);
+    return result;
+}
+
+status_t HidlUtils::audioConfigBaseToHal(const AudioConfigBase& configBase,
+                                         audio_config_base_t* halConfigBase) {
+    status_t result = NO_ERROR;
+    halConfigBase->sample_rate = configBase.sampleRateHz;
+    CONVERT_CHECKED(audioChannelMaskToHal(configBase.channelMask, &halConfigBase->channel_mask),
+                    result);
+    CONVERT_CHECKED(audioFormatToHal(configBase.format, &halConfigBase->format), result);
+    return result;
+}
+
+status_t HidlUtils::audioDeviceTypeFromHal(audio_devices_t halDevice, AudioDevice* device) {
+    *device = audio_device_to_string(halDevice);
+    if (!device->empty() && !xsd::isUnknownAudioDevice(*device)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio device value 0x%X", halDevice);
+    *device = toString(xsd::AudioDevice::AUDIO_DEVICE_NONE);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioDeviceTypeToHal(const AudioDevice& device, audio_devices_t* halDevice) {
+    if (!xsd::isUnknownAudioDevice(device) && audio_device_from_string(device.c_str(), halDevice)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio device \"%s\"", device.c_str());
+    *halDevice = AUDIO_DEVICE_NONE;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioFormatFromHal(audio_format_t halFormat, AudioFormat* format) {
+    *format = audio_format_to_string(halFormat);
+    if (!format->empty() && !xsd::isUnknownAudioFormat(*format)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio format value 0x%X", halFormat);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioFormatToHal(const AudioFormat& format, audio_format_t* halFormat) {
+    if (!xsd::isUnknownAudioFormat(format) && audio_format_from_string(format.c_str(), halFormat)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio format \"%s\"", format.c_str());
+    *halFormat = AUDIO_FORMAT_DEFAULT;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioGainModeMaskFromHal(audio_gain_mode_t halGainModeMask,
+                                             hidl_vec<AudioGainMode>* gainModeMask) {
+    status_t status = NO_ERROR;
+    std::vector<AudioGainMode> result;
+    for (uint32_t bit = 0; bit < sizeof(audio_gain_mode_t) * 8; ++bit) {
+        audio_gain_mode_t flag = static_cast<audio_gain_mode_t>(1u << bit);
+        if ((flag & halGainModeMask) == flag) {
+            AudioGainMode flagStr = audio_gain_mode_to_string(flag);
+            if (!flagStr.empty() && !xsd::isUnknownAudioGainMode(flagStr)) {
+                result.push_back(flagStr);
+            } else {
+                ALOGE("Unknown audio gain mode value 0x%X", flag);
+                status = BAD_VALUE;
+            }
+        }
+    }
+    *gainModeMask = result;
+    return status;
+}
+
+status_t HidlUtils::audioGainModeMaskToHal(const hidl_vec<AudioGainMode>& gainModeMask,
+                                           audio_gain_mode_t* halGainModeMask) {
+    status_t status = NO_ERROR;
+    *halGainModeMask = {};
+    for (const auto& gainMode : gainModeMask) {
+        audio_gain_mode_t halGainMode;
+        if (!xsd::isUnknownAudioGainMode(gainMode) &&
+            audio_gain_mode_from_string(gainMode.c_str(), &halGainMode)) {
+            *halGainModeMask = static_cast<audio_gain_mode_t>(*halGainModeMask | halGainMode);
+        } else {
+            ALOGE("Unknown audio gain mode \"%s\"", gainMode.c_str());
+            status = BAD_VALUE;
+        }
+    }
+    return status;
+}
+
+status_t HidlUtils::audioSourceFromHal(audio_source_t halSource, AudioSource* source) {
+    *source = audio_source_to_string(halSource);
+    if (!source->empty() && !xsd::isUnknownAudioSource(*source)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio source value 0x%X", halSource);
+    *source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioSourceToHal(const AudioSource& source, audio_source_t* halSource) {
+    if (!xsd::isUnknownAudioSource(source) && audio_source_from_string(source.c_str(), halSource)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio source \"%s\"", source.c_str());
+    *halSource = AUDIO_SOURCE_DEFAULT;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioStreamTypeFromHal(audio_stream_type_t halStreamType,
+                                           AudioStreamType* streamType) {
+    *streamType = audio_stream_type_to_string(halStreamType);
+    if (!streamType->empty() && !xsd::isUnknownAudioStreamType(*streamType)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio stream type value 0x%X", halStreamType);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioStreamTypeToHal(const AudioStreamType& streamType,
+                                         audio_stream_type_t* halStreamType) {
+    if (!xsd::isUnknownAudioStreamType(streamType) &&
+        audio_stream_type_from_string(streamType.c_str(), halStreamType)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio stream type \"%s\"", streamType.c_str());
+    *halStreamType = AUDIO_STREAM_DEFAULT;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioConfigFromHal(const audio_config_t& halConfig, bool isInput,
+                                       AudioConfig* config) {
+    status_t result = NO_ERROR;
+    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);
+    config->frameCount = halConfig.frame_count;
+    return result;
+}
+
+status_t HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
+    status_t result = NO_ERROR;
+    *halConfig = AUDIO_CONFIG_INITIALIZER;
+    audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
+    CONVERT_CHECKED(audioConfigBaseToHal(config.base, &halConfigBase), result);
+    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);
+    halConfig->frame_count = config.frameCount;
+    return result;
+}
+
+status_t HidlUtils::audioGainConfigFromHal(const struct audio_gain_config& halConfig, bool isInput,
+                                           AudioGainConfig* config) {
+    status_t result = NO_ERROR;
+    config->index = halConfig.index;
+    CONVERT_CHECKED(audioGainModeMaskFromHal(halConfig.mode, &config->mode), result);
+    CONVERT_CHECKED(audioChannelMaskFromHal(halConfig.channel_mask, isInput, &config->channelMask),
+                    result);
+    if (halConfig.mode & AUDIO_GAIN_MODE_JOINT) {
+        config->values.resize(1);
+        config->values[0] = halConfig.values[0];
+    }
+    if (halConfig.mode & (AUDIO_GAIN_MODE_CHANNELS | AUDIO_GAIN_MODE_RAMP)) {
+        config->values.resize(__builtin_popcount(halConfig.channel_mask));
+        for (size_t i = 0; i < config->values.size(); ++i) {
+            config->values[i] = halConfig.values[i];
+        }
+    }
+    config->rampDurationMs = halConfig.ramp_duration_ms;
+    return result;
+}
+
+status_t HidlUtils::audioGainConfigToHal(const AudioGainConfig& config,
+                                         struct audio_gain_config* halConfig) {
+    status_t result = NO_ERROR;
+    halConfig->index = config.index;
+    CONVERT_CHECKED(audioGainModeMaskToHal(config.mode, &halConfig->mode), result);
+    CONVERT_CHECKED(audioChannelMaskToHal(config.channelMask, &halConfig->channel_mask), result);
+    memset(halConfig->values, 0, sizeof(halConfig->values));
+    if (halConfig->mode & AUDIO_GAIN_MODE_JOINT) {
+        if (config.values.size() > 0) {
+            halConfig->values[0] = config.values[0];
+        } else {
+            ALOGE("Empty values vector in AudioGainConfig");
+            result = BAD_VALUE;
+        }
+    }
+    if (halConfig->mode & (AUDIO_GAIN_MODE_CHANNELS | AUDIO_GAIN_MODE_RAMP)) {
+        size_t channelCount = __builtin_popcount(halConfig->channel_mask);
+        size_t valuesCount = config.values.size();
+        if (channelCount != valuesCount) {
+            ALOGE("Wrong number of values in AudioGainConfig, expected: %zu, found: %zu",
+                  channelCount, valuesCount);
+            result = BAD_VALUE;
+            if (channelCount < valuesCount) {
+                valuesCount = channelCount;
+            }
+        }
+        for (size_t i = 0; i < valuesCount; ++i) {
+            halConfig->values[i] = config.values[i];
+        }
+    }
+    halConfig->ramp_duration_ms = config.rampDurationMs;
+    return result;
+}
+
+status_t HidlUtils::audioGainFromHal(const struct audio_gain& halGain, bool isInput,
+                                     AudioGain* gain) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioGainModeMaskFromHal(halGain.mode, &gain->mode), result);
+    CONVERT_CHECKED(audioChannelMaskFromHal(halGain.channel_mask, isInput, &gain->channelMask),
+                    result);
+    gain->minValue = halGain.min_value;
+    gain->maxValue = halGain.max_value;
+    gain->defaultValue = halGain.default_value;
+    gain->stepValue = halGain.step_value;
+    gain->minRampMs = halGain.min_ramp_ms;
+    gain->maxRampMs = halGain.max_ramp_ms;
+    return result;
+}
+
+status_t HidlUtils::audioGainToHal(const AudioGain& gain, struct audio_gain* halGain) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioGainModeMaskToHal(gain.mode, &halGain->mode), result);
+    CONVERT_CHECKED(audioChannelMaskToHal(gain.channelMask, &halGain->channel_mask), result);
+    halGain->min_value = gain.minValue;
+    halGain->max_value = gain.maxValue;
+    halGain->default_value = gain.defaultValue;
+    halGain->step_value = gain.stepValue;
+    halGain->min_ramp_ms = gain.minRampMs;
+    halGain->max_ramp_ms = gain.maxRampMs;
+    return result;
+}
+
+status_t HidlUtils::audioUsageFromHal(audio_usage_t halUsage, AudioUsage* usage) {
+    if (halUsage == AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST ||
+        halUsage == AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT ||
+        halUsage == AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED ||
+        halUsage == AUDIO_USAGE_NOTIFICATION_EVENT) {
+        halUsage = AUDIO_USAGE_NOTIFICATION;
+    }
+    *usage = audio_usage_to_string(halUsage);
+    if (!usage->empty() && !xsd::isUnknownAudioUsage(*usage)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio usage %d", halUsage);
+    *usage = toString(xsd::AudioUsage::AUDIO_USAGE_UNKNOWN);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioUsageToHal(const AudioUsage& usage, audio_usage_t* halUsage) {
+    if (!xsd::isUnknownAudioUsage(usage) && audio_usage_from_string(usage.c_str(), halUsage)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio usage \"%s\"", usage.c_str());
+    *halUsage = AUDIO_USAGE_UNKNOWN;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
+                                            AudioOffloadInfo* offload) {
+    status_t result = NO_ERROR;
+    audio_config_base_t halConfigBase = {halOffload.sample_rate, halOffload.channel_mask,
+                                         halOffload.format};
+    CONVERT_CHECKED(audioConfigBaseFromHal(halConfigBase, false /*isInput*/, &offload->base),
+                    result);
+    CONVERT_CHECKED(audioStreamTypeFromHal(halOffload.stream_type, &offload->streamType), result);
+    offload->bitRatePerSecond = halOffload.bit_rate;
+    offload->durationMicroseconds = halOffload.duration_us;
+    offload->hasVideo = halOffload.has_video;
+    offload->isStreaming = halOffload.is_streaming;
+    offload->bitWidth = halOffload.bit_width;
+    offload->bufferSize = halOffload.offload_buffer_size;
+    CONVERT_CHECKED(audioUsageFromHal(halOffload.usage, &offload->usage), result);
+    if (halOffload.version >= AUDIO_OFFLOAD_INFO_VERSION_0_2) {
+        offload->encapsulationMode =
+                static_cast<AudioEncapsulationMode>(halOffload.encapsulation_mode);
+        offload->contentId = halOffload.content_id;
+        offload->syncId = halOffload.sync_id;
+    } else {
+        offload->encapsulationMode = AudioEncapsulationMode::NONE;
+        offload->contentId = 0;
+        offload->syncId = 0;
+    }
+    return result;
+}
+
+status_t HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
+                                          audio_offload_info_t* halOffload) {
+    status_t result = NO_ERROR;
+    *halOffload = AUDIO_INFO_INITIALIZER;
+    audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
+    CONVERT_CHECKED(audioConfigBaseToHal(offload.base, &halConfigBase), result);
+    halOffload->sample_rate = halConfigBase.sample_rate;
+    halOffload->channel_mask = halConfigBase.channel_mask;
+    halOffload->format = halConfigBase.format;
+    CONVERT_CHECKED(audioStreamTypeToHal(offload.streamType, &halOffload->stream_type), result);
+    halOffload->bit_rate = offload.bitRatePerSecond;
+    halOffload->duration_us = offload.durationMicroseconds;
+    halOffload->has_video = offload.hasVideo;
+    halOffload->is_streaming = offload.isStreaming;
+    halOffload->bit_width = offload.bitWidth;
+    halOffload->offload_buffer_size = offload.bufferSize;
+    CONVERT_CHECKED(audioUsageToHal(offload.usage, &halOffload->usage), result);
+    halOffload->encapsulation_mode =
+            static_cast<audio_encapsulation_mode_t>(offload.encapsulationMode);
+    halOffload->content_id = offload.contentId;
+    halOffload->sync_id = offload.syncId;
+    return result;
+}
+
+status_t HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
+                                           AudioPortConfig* config) {
+    status_t result = NO_ERROR;
+    bool isInput = false;
+    config->id = halConfig.id;
+    CONVERT_CHECKED(audioPortExtendedInfoFromHal(halConfig.role, halConfig.type,
+                                                 halConfig.ext.device, halConfig.ext.mix,
+                                                 halConfig.ext.session, &config->ext, &isInput),
+                    result);
+    if (audio_port_config_has_input_direction(&halConfig) != isInput) {
+        ALOGE("Inconsistent port config direction data, is input: %d (hal) != %d (converter)",
+              audio_port_config_has_input_direction(&halConfig), isInput);
+        result = BAD_VALUE;
+    }
+    if (halConfig.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+        config->base.sampleRateHz = halConfig.sample_rate;
+    } else {
+        config->base.sampleRateHz = {};
+    }
+    if (halConfig.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+        CONVERT_CHECKED(
+                audioChannelMaskFromHal(halConfig.channel_mask, isInput, &config->base.channelMask),
+                result);
+    } else {
+        config->base.channelMask = {};
+    }
+    if (halConfig.config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+        CONVERT_CHECKED(audioFormatFromHal(halConfig.format, &config->base.format), result);
+    } else {
+        config->base.format = {};
+    }
+    if (halConfig.config_mask & AUDIO_PORT_CONFIG_GAIN) {
+        config->gain.config({});
+        CONVERT_CHECKED(audioGainConfigFromHal(halConfig.gain, isInput, &config->gain.config()),
+                        result);
+    } else {
+        config->gain.unspecified({});
+    }
+    return result;
+}
+
+status_t HidlUtils::audioPortConfigToHal(const AudioPortConfig& config,
+                                         struct audio_port_config* halConfig) {
+    status_t result = NO_ERROR;
+    memset(halConfig, 0, sizeof(audio_port_config));
+    halConfig->id = config.id;
+    halConfig->config_mask = {};
+    if (config.base.sampleRateHz != 0) {
+        halConfig->config_mask |= AUDIO_PORT_CONFIG_SAMPLE_RATE;
+        halConfig->sample_rate = config.base.sampleRateHz;
+    }
+    if (!config.base.channelMask.empty()) {
+        halConfig->config_mask |= AUDIO_PORT_CONFIG_CHANNEL_MASK;
+        CONVERT_CHECKED(audioChannelMaskToHal(config.base.channelMask, &halConfig->channel_mask),
+                        result);
+    }
+    if (!config.base.format.empty()) {
+        halConfig->config_mask |= AUDIO_PORT_CONFIG_FORMAT;
+        CONVERT_CHECKED(audioFormatToHal(config.base.format, &halConfig->format), result);
+    }
+    if (config.gain.getDiscriminator() ==
+        AudioPortConfig::OptionalGain::hidl_discriminator::config) {
+        halConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+        CONVERT_CHECKED(audioGainConfigToHal(config.gain.config(), &halConfig->gain), result);
+    }
+    CONVERT_CHECKED(audioPortExtendedInfoToHal(config.ext, &halConfig->role, &halConfig->type,
+                                               &halConfig->ext.device, &halConfig->ext.mix,
+                                               &halConfig->ext.session),
+                    result);
+    return result;
+}
+
+status_t HidlUtils::audioPortExtendedInfoFromHal(
+        audio_port_role_t role, audio_port_type_t type,
+        const struct audio_port_config_device_ext& device,
+        const struct audio_port_config_mix_ext& mix,
+        const struct audio_port_config_session_ext& session, AudioPortExtendedInfo* ext,
+        bool* isInput) {
+    status_t result = NO_ERROR;
+    *isInput = false;
+    switch (type) {
+        case AUDIO_PORT_TYPE_NONE:
+            ext->unspecified({});
+            break;
+        case AUDIO_PORT_TYPE_DEVICE: {
+            *isInput = role == AUDIO_PORT_ROLE_SOURCE;
+            ext->device({});
+            CONVERT_CHECKED(deviceAddressFromHal(device.type, device.address, &ext->device()),
+                            result);
+            break;
+        }
+        case AUDIO_PORT_TYPE_MIX: {
+            *isInput = role == AUDIO_PORT_ROLE_SINK;
+            ext->mix({});
+            ext->mix().ioHandle = mix.handle;
+            if (role == AUDIO_PORT_ROLE_SOURCE) {
+                ext->mix().useCase.stream({});
+                CONVERT_CHECKED(
+                        audioStreamTypeFromHal(mix.usecase.stream, &ext->mix().useCase.stream()),
+                        result);
+            } else if (role == AUDIO_PORT_ROLE_SINK) {
+                ext->mix().useCase.source({});
+                CONVERT_CHECKED(
+                        audioSourceFromHal(mix.usecase.source, &ext->mix().useCase.source()),
+                        result);
+            }
+            break;
+        }
+        case AUDIO_PORT_TYPE_SESSION: {
+            ext->session(session.session);
+            break;
+        }
+    }
+    return result;
+}
+
+status_t HidlUtils::audioPortExtendedInfoToHal(const AudioPortExtendedInfo& ext,
+                                               audio_port_role_t* role, audio_port_type_t* type,
+                                               struct audio_port_config_device_ext* device,
+                                               struct audio_port_config_mix_ext* mix,
+                                               struct audio_port_config_session_ext* session) {
+    status_t result = NO_ERROR;
+    switch (ext.getDiscriminator()) {
+        case AudioPortExtendedInfo::hidl_discriminator::unspecified:
+            *role = AUDIO_PORT_ROLE_NONE;
+            *type = AUDIO_PORT_TYPE_NONE;
+            break;
+        case AudioPortExtendedInfo::hidl_discriminator::device:
+            *role = xsd::isOutputDevice(ext.device().deviceType) ? AUDIO_PORT_ROLE_SINK
+                                                                 : AUDIO_PORT_ROLE_SOURCE;
+            *type = AUDIO_PORT_TYPE_DEVICE;
+            CONVERT_CHECKED(deviceAddressToHal(ext.device(), &device->type, device->address),
+                            result);
+            break;
+        case AudioPortExtendedInfo::hidl_discriminator::mix:
+            *type = AUDIO_PORT_TYPE_MIX;
+            switch (ext.mix().useCase.getDiscriminator()) {
+                case AudioPortExtendedInfo::AudioPortMixExt::UseCase::hidl_discriminator::stream:
+                    *role = AUDIO_PORT_ROLE_SOURCE;
+                    CONVERT_CHECKED(
+                            audioStreamTypeToHal(ext.mix().useCase.stream(), &mix->usecase.stream),
+                            result);
+                    break;
+                case AudioPortExtendedInfo::AudioPortMixExt::UseCase::hidl_discriminator::source:
+                    *role = AUDIO_PORT_ROLE_SINK;
+                    CONVERT_CHECKED(
+                            audioSourceToHal(ext.mix().useCase.source(), &mix->usecase.source),
+                            result);
+                    break;
+            }
+            mix->handle = ext.mix().ioHandle;
+            break;
+        case AudioPortExtendedInfo::hidl_discriminator::session:
+            *role = AUDIO_PORT_ROLE_NONE;
+            *type = AUDIO_PORT_TYPE_SESSION;
+            session->session = static_cast<audio_session_t>(ext.session());
+            break;
+    }
+    return result;
+}
+
+status_t HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
+    struct audio_port_v7 halPortV7 = {};
+    audio_populate_audio_port_v7(&halPort, &halPortV7);
+    return audioPortFromHal(halPortV7, port);
+}
+
+status_t HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port* halPort) {
+    status_t result = NO_ERROR;
+    struct audio_port_v7 halPortV7 = {};
+    CONVERT_CHECKED(audioPortToHal(port, &halPortV7), result);
+    if (!audio_populate_audio_port(&halPortV7, halPort)) {
+        result = BAD_VALUE;
+    }
+    return result;
+}
+
+status_t HidlUtils::audioPortFromHal(const struct audio_port_v7& halPort, AudioPort* port) {
+    status_t result = NO_ERROR;
+    bool isInput = false;
+    port->id = halPort.id;
+    port->name.setToExternal(halPort.name, strlen(halPort.name));
+    // HAL uses slightly different but convertible structures for the extended info in port
+    // and port config structures.
+    struct audio_port_config_device_ext halDevice = {};
+    struct audio_port_config_mix_ext halMix = {};
+    struct audio_port_config_session_ext halSession = {};
+    switch (halPort.type) {
+        case AUDIO_PORT_TYPE_NONE:
+            break;
+        case AUDIO_PORT_TYPE_DEVICE:
+            halDevice.type = halPort.ext.device.type;
+            memcpy(halDevice.address, halPort.ext.device.address, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+            break;
+        case AUDIO_PORT_TYPE_MIX:
+            halMix.handle = halPort.ext.mix.handle;
+            break;
+        case AUDIO_PORT_TYPE_SESSION:
+            halSession.session = halPort.ext.session.session;
+            break;
+    }
+    CONVERT_CHECKED(audioPortExtendedInfoFromHal(halPort.role, halPort.type, halDevice, halMix,
+                                                 halSession, &port->ext, &isInput),
+                    result);
+    port->profiles.resize(halPort.num_audio_profiles);
+    for (size_t i = 0; i < halPort.num_audio_profiles; ++i) {
+        CONVERT_CHECKED(audioProfileFromHal(halPort.audio_profiles[i], isInput, &port->profiles[i]),
+                        result);
+    }
+    port->gains.resize(halPort.num_gains);
+    for (size_t i = 0; i < halPort.num_gains; ++i) {
+        CONVERT_CHECKED(audioGainFromHal(halPort.gains[i], isInput, &port->gains[i]), result);
+    }
+    CONVERT_CHECKED(audioPortConfigFromHal(halPort.active_config, &port->activeConfig), result);
+    return result;
+}
+
+status_t HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port_v7* halPort) {
+    status_t result = NO_ERROR;
+    halPort->id = port.id;
+    strncpy(halPort->name, port.name.c_str(), AUDIO_PORT_MAX_NAME_LEN);
+    halPort->name[AUDIO_PORT_MAX_NAME_LEN - 1] = '\0';
+    if (port.name.size() >= AUDIO_PORT_MAX_NAME_LEN) {
+        ALOGE("HIDL Audio Port name is too long: %zu", port.name.size());
+        result = BAD_VALUE;
+    }
+    halPort->num_audio_profiles = port.profiles.size();
+    if (halPort->num_audio_profiles > AUDIO_PORT_MAX_AUDIO_PROFILES) {
+        ALOGE("HIDL Audio Port has too many profiles: %u", halPort->num_audio_profiles);
+        halPort->num_audio_profiles = AUDIO_PORT_MAX_AUDIO_PROFILES;
+        result = BAD_VALUE;
+    }
+    for (size_t i = 0; i < halPort->num_audio_profiles; ++i) {
+        CONVERT_CHECKED(audioProfileToHal(port.profiles[i], &halPort->audio_profiles[i]), result);
+    }
+    halPort->num_gains = port.gains.size();
+    if (halPort->num_gains > AUDIO_PORT_MAX_GAINS) {
+        ALOGE("HIDL Audio Port has too many gains: %u", halPort->num_gains);
+        halPort->num_gains = AUDIO_PORT_MAX_GAINS;
+        result = BAD_VALUE;
+    }
+    for (size_t i = 0; i < halPort->num_gains; ++i) {
+        CONVERT_CHECKED(audioGainToHal(port.gains[i], &halPort->gains[i]), result);
+    }
+    // HAL uses slightly different but convertible structures for the extended info in port
+    // and port config structures.
+    struct audio_port_config_device_ext halDevice = {};
+    struct audio_port_config_mix_ext halMix = {};
+    struct audio_port_config_session_ext halSession = {};
+    CONVERT_CHECKED(audioPortExtendedInfoToHal(port.ext, &halPort->role, &halPort->type, &halDevice,
+                                               &halMix, &halSession),
+                    result);
+    switch (halPort->type) {
+        case AUDIO_PORT_TYPE_NONE:
+            break;
+        case AUDIO_PORT_TYPE_DEVICE:
+            halPort->ext.device.type = halDevice.type;
+            memcpy(halPort->ext.device.address, halDevice.address, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+            break;
+        case AUDIO_PORT_TYPE_MIX:
+            halPort->ext.mix.handle = halMix.handle;
+            break;
+        case AUDIO_PORT_TYPE_SESSION:
+            halPort->ext.session.session = halSession.session;
+            break;
+    }
+    CONVERT_CHECKED(audioPortConfigToHal(port.activeConfig, &halPort->active_config), result);
+    return result;
+}
+
+status_t HidlUtils::audioProfileFromHal(const struct audio_profile& halProfile, bool isInput,
+                                        AudioProfile* profile) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioFormatFromHal(halProfile.format, &profile->format), result);
+    profile->sampleRates.resize(halProfile.num_sample_rates);
+    for (size_t i = 0; i < halProfile.num_sample_rates; ++i) {
+        profile->sampleRates[i] = halProfile.sample_rates[i];
+    }
+    profile->channelMasks.resize(halProfile.num_channel_masks);
+    for (size_t i = 0; i < halProfile.num_channel_masks; ++i) {
+        CONVERT_CHECKED(audioChannelMaskFromHal(halProfile.channel_masks[i], isInput,
+                                                &profile->channelMasks[i]),
+                        result);
+    }
+    return result;
+}
+
+status_t HidlUtils::audioProfileToHal(const AudioProfile& profile,
+                                      struct audio_profile* halProfile) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioFormatToHal(profile.format, &halProfile->format), result);
+    memset(halProfile->sample_rates, 0, sizeof(halProfile->sample_rates));
+    halProfile->num_sample_rates = profile.sampleRates.size();
+    if (halProfile->num_sample_rates > AUDIO_PORT_MAX_SAMPLING_RATES) {
+        ALOGE("HIDL Audio profile has too many sample rates: %u", halProfile->num_sample_rates);
+        halProfile->num_sample_rates = AUDIO_PORT_MAX_SAMPLING_RATES;
+        result = BAD_VALUE;
+    }
+    for (size_t i = 0; i < halProfile->num_sample_rates; ++i) {
+        halProfile->sample_rates[i] = profile.sampleRates[i];
+    }
+    memset(halProfile->channel_masks, 0, sizeof(halProfile->channel_masks));
+    halProfile->num_channel_masks = profile.channelMasks.size();
+    if (halProfile->num_channel_masks > AUDIO_PORT_MAX_CHANNEL_MASKS) {
+        ALOGE("HIDL Audio profile has too many channel masks: %u", halProfile->num_channel_masks);
+        halProfile->num_channel_masks = AUDIO_PORT_MAX_CHANNEL_MASKS;
+        result = BAD_VALUE;
+    }
+    for (size_t i = 0; i < halProfile->num_channel_masks; ++i) {
+        CONVERT_CHECKED(
+                audioChannelMaskToHal(profile.channelMasks[i], &halProfile->channel_masks[i]),
+                status);
+    }
+    return result;
+}
+
+status_t HidlUtils::deviceAddressFromHal(audio_devices_t halDeviceType,
+                                         const char* halDeviceAddress, DeviceAddress* device) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioDeviceTypeFromHal(halDeviceType, &device->deviceType), result);
+    if (audio_is_a2dp_out_device(halDeviceType) || audio_is_a2dp_in_device(halDeviceType)) {
+        device->address.mac({});
+        if (halDeviceAddress != nullptr) {
+            auto& mac = device->address.mac();
+            int status = sscanf(halDeviceAddress, "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX", &mac[0], &mac[1],
+                                &mac[2], &mac[3], &mac[4], &mac[5]);
+            if (status != 6) {
+                ALOGE("BT A2DP device \"%s\" MAC address \"%s\" is invalid",
+                      device->deviceType.c_str(), halDeviceAddress);
+                result = BAD_VALUE;
+            }
+        } else {
+            ALOGE("BT A2DP device \"%s\" does not have a MAC address", halDeviceAddress);
+            result = BAD_VALUE;
+        }
+    } else if (halDeviceType == AUDIO_DEVICE_OUT_IP || halDeviceType == AUDIO_DEVICE_IN_IP) {
+        device->address.ipv4({});
+        if (halDeviceAddress != nullptr) {
+            auto& ipv4 = device->address.ipv4();
+            int status = sscanf(halDeviceAddress, "%hhu.%hhu.%hhu.%hhu", &ipv4[0], &ipv4[1],
+                                &ipv4[2], &ipv4[3]);
+            if (status != 4) {
+                ALOGE("IP device \"%s\" IPv4 address \"%s\" is invalid", device->deviceType.c_str(),
+                      halDeviceAddress);
+                result = BAD_VALUE;
+            }
+        } else {
+            ALOGE("IP device \"%s\" does not have an IPv4 address", device->deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else if (audio_is_usb_out_device(halDeviceType) || audio_is_usb_in_device(halDeviceType)) {
+        device->address.alsa({});
+        if (halDeviceAddress != nullptr) {
+            auto& alsa = device->address.alsa();
+            int status = sscanf(halDeviceAddress, "card=%d;device=%d", &alsa.card, &alsa.device);
+            if (status != 2) {
+                ALOGE("USB device \"%s\" ALSA address \"%s\" is invalid",
+                      device->deviceType.c_str(), halDeviceAddress);
+                result = BAD_VALUE;
+            }
+        } else {
+            ALOGE("USB device \"%s\" does not have ALSA address", device->deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else {
+        // Any other device type uses the 'id' field.
+        device->address.id(halDeviceAddress != nullptr ? halDeviceAddress : "");
+    }
+    return result;
+}
+
+status_t HidlUtils::deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+                                       char* halDeviceAddress) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioDeviceTypeToHal(device.deviceType, halDeviceType), result);
+    memset(halDeviceAddress, 0, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+    if (audio_is_a2dp_out_device(*halDeviceType) || audio_is_a2dp_in_device(*halDeviceType)) {
+        if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::mac) {
+            const auto& mac = device.address.mac();
+            snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN,
+                     "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4],
+                     mac[5]);
+        } else {
+            ALOGE("BT A2DP device \"%s\" does not have MAC address set", device.deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else if (*halDeviceType == AUDIO_DEVICE_OUT_IP || *halDeviceType == AUDIO_DEVICE_IN_IP) {
+        if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::ipv4) {
+            const auto& ipv4 = device.address.ipv4();
+            snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%d.%d.%d.%d", ipv4[0],
+                     ipv4[1], ipv4[2], ipv4[3]);
+        } else {
+            ALOGE("IP device \"%s\" does not have IPv4 address set", device.deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else if (audio_is_usb_out_device(*halDeviceType) || audio_is_usb_in_device(*halDeviceType)) {
+        if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::alsa) {
+            const auto& alsa = device.address.alsa();
+            snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "card=%d;device=%d", alsa.card,
+                     alsa.device);
+        } else {
+            ALOGE("USB device \"%s\" does not have ALSA address set", device.deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else {
+        // Any other device type uses the 'id' field.
+        if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::id) {
+            snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%s",
+                     device.address.id().c_str());
+        }
+    }
+    return result;
+}
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace common
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/common/all-versions/default/Android.bp b/audio/common/all-versions/default/Android.bp
index afaa7bb..b83a58a 100644
--- a/audio/common/all-versions/default/Android.bp
+++ b/audio/common/all-versions/default/Android.bp
@@ -39,15 +39,19 @@
     ],
 }
 
+filegroup {
+    name: "android.hardware.audio.common-util@2-6",
+    srcs: [
+        "HidlUtils.cpp",
+        "UuidUtils.cpp",
+    ],
+}
+
 cc_defaults {
     name: "android.hardware.audio.common-util_default",
     defaults: ["hidl_defaults"],
 
     vendor_available: true,
-    srcs: [
-        "HidlUtils.cpp",
-        "UuidUtils.cpp",
-    ],
 
     export_include_dirs: ["."],
 
@@ -70,6 +74,7 @@
 cc_library_shared {
     name: "android.hardware.audio.common@2.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [":android.hardware.audio.common-util@2-6"],
     shared_libs: [
         "android.hardware.audio.common@2.0",
     ],
@@ -83,6 +88,7 @@
 cc_library_shared {
     name: "android.hardware.audio.common@4.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [":android.hardware.audio.common-util@2-6"],
     shared_libs: [
         "android.hardware.audio.common@4.0",
     ],
@@ -96,6 +102,7 @@
 cc_library_shared {
     name: "android.hardware.audio.common@5.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [":android.hardware.audio.common-util@2-6"],
     shared_libs: [
         "android.hardware.audio.common@5.0",
     ],
@@ -109,6 +116,7 @@
 cc_library_shared {
     name: "android.hardware.audio.common@6.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [":android.hardware.audio.common-util@2-6"],
     shared_libs: [
         "android.hardware.audio.common@6.0",
     ],
@@ -119,12 +127,18 @@
     ],
 }
 
-cc_library_shared {
-    enabled: false,
+cc_library {
     name: "android.hardware.audio.common@7.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [
+        "7.0/HidlUtils.cpp",
+        "UuidUtils.cpp",
+    ],
     shared_libs: [
         "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-enums",
+        "libbase",
+        "libxml2",
     ],
     cflags: [
         "-DMAJOR_VERSION=7",
@@ -132,3 +146,35 @@
         "-include common/all-versions/VersionMacro.h",
     ],
 }
+
+// Note: this isn't a VTS test, but rather a unit test
+// to verify correctness of conversion utilities.
+cc_test {
+    name: "android.hardware.audio.common@7.0-util_tests",
+    defaults: ["android.hardware.audio.common-util_default"],
+
+    srcs: ["tests/hidlutils_tests.cpp"],
+
+    // Use static linking to allow running in presubmit on
+    // targets that don't have HAL V7.
+    static_libs: [
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.common@7.0-util",
+        "android.hardware.audio.common@7.0",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libxml2",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+
+    test_suites: ["device-tests"],
+}
diff --git a/audio/common/all-versions/default/HidlUtils.cpp b/audio/common/all-versions/default/HidlUtils.cpp
index 7530d05..ab3c1c7 100644
--- a/audio/common/all-versions/default/HidlUtils.cpp
+++ b/audio/common/all-versions/default/HidlUtils.cpp
@@ -37,13 +37,14 @@
     return status;
 }
 
-void HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
+status_t HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
     memset(halConfig, 0, sizeof(audio_config_t));
     halConfig->sample_rate = config.sampleRateHz;
     halConfig->channel_mask = static_cast<audio_channel_mask_t>(config.channelMask);
     halConfig->format = static_cast<audio_format_t>(config.format);
     audioOffloadInfoToHal(config.offloadInfo, &halConfig->offload_info);
     halConfig->frame_count = config.frameCount;
+    return NO_ERROR;
 }
 
 void HidlUtils::audioGainConfigFromHal(const struct audio_gain_config& halConfig,
@@ -57,8 +58,8 @@
     config->rampDurationMs = halConfig.ramp_duration_ms;
 }
 
-void HidlUtils::audioGainConfigToHal(const AudioGainConfig& config,
-                                     struct audio_gain_config* halConfig) {
+status_t HidlUtils::audioGainConfigToHal(const AudioGainConfig& config,
+                                         struct audio_gain_config* halConfig) {
     halConfig->index = config.index;
     halConfig->mode = static_cast<audio_gain_mode_t>(config.mode);
     halConfig->channel_mask = static_cast<audio_channel_mask_t>(config.channelMask);
@@ -67,6 +68,7 @@
         halConfig->values[i] = config.values[i];
     }
     halConfig->ramp_duration_ms = config.rampDurationMs;
+    return NO_ERROR;
 }
 
 void HidlUtils::audioGainFromHal(const struct audio_gain& halGain, AudioGain* gain) {
@@ -80,7 +82,7 @@
     gain->maxRampMs = halGain.max_ramp_ms;
 }
 
-void HidlUtils::audioGainToHal(const AudioGain& gain, struct audio_gain* halGain) {
+status_t HidlUtils::audioGainToHal(const AudioGain& gain, struct audio_gain* halGain) {
     halGain->mode = static_cast<audio_gain_mode_t>(gain.mode);
     halGain->channel_mask = static_cast<audio_channel_mask_t>(gain.channelMask);
     halGain->min_value = gain.minValue;
@@ -89,22 +91,26 @@
     halGain->step_value = gain.stepValue;
     halGain->min_ramp_ms = gain.minRampMs;
     halGain->max_ramp_ms = gain.maxRampMs;
+    return NO_ERROR;
 }
 
-AudioUsage HidlUtils::audioUsageFromHal(const audio_usage_t halUsage) {
+status_t HidlUtils::audioUsageFromHal(audio_usage_t halUsage, AudioUsage* usage) {
     switch (halUsage) {
         case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
         case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
         case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
         case AUDIO_USAGE_NOTIFICATION_EVENT:
-            return AudioUsage::NOTIFICATION;
+            *usage = AudioUsage::NOTIFICATION;
+            break;
         default:
-            return static_cast<AudioUsage>(halUsage);
+            *usage = static_cast<AudioUsage>(halUsage);
     }
+    return NO_ERROR;
 }
 
-audio_usage_t HidlUtils::audioUsageToHal(const AudioUsage usage) {
-    return static_cast<audio_usage_t>(usage);
+status_t HidlUtils::audioUsageToHal(const AudioUsage& usage, audio_usage_t* halUsage) {
+    *halUsage = static_cast<audio_usage_t>(usage);
+    return NO_ERROR;
 }
 
 status_t HidlUtils::audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
@@ -119,7 +125,7 @@
     offload->isStreaming = halOffload.is_streaming;
     offload->bitWidth = halOffload.bit_width;
     offload->bufferSize = halOffload.offload_buffer_size;
-    offload->usage = audioUsageFromHal(halOffload.usage);
+    audioUsageFromHal(halOffload.usage, &offload->usage);
 #if MAJOR_VERSION >= 6
     if (halOffload.version >= AUDIO_OFFLOAD_INFO_VERSION_0_2) {
         offload->encapsulationMode =
@@ -139,11 +145,11 @@
         return BAD_VALUE;
     }
 #endif
-    return OK;
+    return NO_ERROR;
 }
 
-void HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
-                                      audio_offload_info_t* halOffload) {
+status_t HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
+                                          audio_offload_info_t* halOffload) {
     *halOffload = AUDIO_INFO_INITIALIZER;
     halOffload->sample_rate = offload.sampleRateHz;
     halOffload->channel_mask = static_cast<audio_channel_mask_t>(offload.channelMask);
@@ -155,7 +161,7 @@
     halOffload->is_streaming = offload.isStreaming;
     halOffload->bit_width = offload.bitWidth;
     halOffload->offload_buffer_size = offload.bufferSize;
-    halOffload->usage = audioUsageToHal(offload.usage);
+    audioUsageToHal(offload.usage, &halOffload->usage);
 #if MAJOR_VERSION >= 6
     halOffload->encapsulation_mode =
             static_cast<audio_encapsulation_mode_t>(offload.encapsulationMode);
@@ -164,10 +170,11 @@
 #else
     // offload doesn't contain encapsulationMode, contentId, syncId, so this is OK.
 #endif
+    return NO_ERROR;
 }
 
-void HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
-                                       AudioPortConfig* config) {
+status_t HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
+                                           AudioPortConfig* config) {
     config->id = halConfig.id;
     config->role = AudioPortRole(halConfig.role);
     config->type = AudioPortType(halConfig.type);
@@ -201,10 +208,11 @@
             break;
         }
     }
+    return NO_ERROR;
 }
 
-void HidlUtils::audioPortConfigToHal(const AudioPortConfig& config,
-                                     struct audio_port_config* halConfig) {
+status_t HidlUtils::audioPortConfigToHal(const AudioPortConfig& config,
+                                         struct audio_port_config* halConfig) {
     memset(halConfig, 0, sizeof(audio_port_config));
     halConfig->id = config.id;
     halConfig->role = static_cast<audio_port_role_t>(config.role);
@@ -242,27 +250,10 @@
             break;
         }
     }
+    return NO_ERROR;
 }
 
-void HidlUtils::audioPortConfigsFromHal(unsigned int numHalConfigs,
-                                        const struct audio_port_config* halConfigs,
-                                        hidl_vec<AudioPortConfig>* configs) {
-    configs->resize(numHalConfigs);
-    for (unsigned int i = 0; i < numHalConfigs; ++i) {
-        audioPortConfigFromHal(halConfigs[i], &(*configs)[i]);
-    }
-}
-
-std::unique_ptr<audio_port_config[]> HidlUtils::audioPortConfigsToHal(
-    const hidl_vec<AudioPortConfig>& configs) {
-    std::unique_ptr<audio_port_config[]> halConfigs(new audio_port_config[configs.size()]);
-    for (size_t i = 0; i < configs.size(); ++i) {
-        audioPortConfigToHal(configs[i], &halConfigs[i]);
-    }
-    return halConfigs;
-}
-
-void HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
+status_t HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
     port->id = halPort.id;
     port->role = AudioPortRole(halPort.role);
     port->type = AudioPortType(halPort.type);
@@ -305,9 +296,10 @@
             break;
         }
     }
+    return NO_ERROR;
 }
 
-void HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port* halPort) {
+status_t HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port* halPort) {
     memset(halPort, 0, sizeof(audio_port));
     halPort->id = port.id;
     halPort->role = static_cast<audio_port_role_t>(port.role);
@@ -356,6 +348,7 @@
             break;
         }
     }
+    return NO_ERROR;
 }
 
 }  // namespace implementation
diff --git a/audio/common/all-versions/default/HidlUtils.h b/audio/common/all-versions/default/HidlUtils.h
index 20fddef..4e609ca 100644
--- a/audio/common/all-versions/default/HidlUtils.h
+++ b/audio/common/all-versions/default/HidlUtils.h
@@ -34,38 +34,123 @@
 
 using namespace ::android::hardware::audio::common::CPP_VERSION;
 
-class HidlUtils {
-  public:
-    // A failure here indicates a platform config that is incompatible with
-    // the compiled HIDL interface version.
+struct HidlUtils {
+#if MAJOR_VERSION < 7
     static status_t audioConfigFromHal(const audio_config_t& halConfig, AudioConfig* config);
-
-    static void audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig);
     static void audioGainConfigFromHal(const struct audio_gain_config& halConfig,
                                        AudioGainConfig* config);
-    static void audioGainConfigToHal(const AudioGainConfig& config,
-                                     struct audio_gain_config* halConfig);
     static void audioGainFromHal(const struct audio_gain& halGain, AudioGain* gain);
-    static void audioGainToHal(const AudioGain& gain, struct audio_gain* halGain);
-    static AudioUsage audioUsageFromHal(const audio_usage_t halUsage);
-    static audio_usage_t audioUsageToHal(const AudioUsage usage);
-    // A failure here indicates a platform offload info that is incompatible with
-    // the compiled HIDL interface version.
+#else
+    static status_t audioConfigFromHal(const audio_config_t& halConfig, bool isInput,
+                                       AudioConfig* config);
+    static status_t audioGainConfigFromHal(const struct audio_gain_config& halConfig, bool isInput,
+                                           AudioGainConfig* config);
+    static status_t audioGainFromHal(const struct audio_gain& halGain, bool isInput,
+                                     AudioGain* gain);
+#endif
+    static status_t audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig);
+    static status_t audioGainConfigToHal(const AudioGainConfig& config,
+                                         struct audio_gain_config* halConfig);
+    static status_t audioGainToHal(const AudioGain& gain, struct audio_gain* halGain);
+    static status_t audioUsageFromHal(audio_usage_t halUsage, AudioUsage* usage);
+    static status_t audioUsageToHal(const AudioUsage& usage, audio_usage_t* halUsage);
     static status_t audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
                                             AudioOffloadInfo* offload);
-    static void audioOffloadInfoToHal(const AudioOffloadInfo& offload,
-                                      audio_offload_info_t* halOffload);
-    static void audioPortConfigFromHal(const struct audio_port_config& halConfig,
-                                       AudioPortConfig* config);
-    static void audioPortConfigToHal(const AudioPortConfig& config,
-                                     struct audio_port_config* halConfig);
-    static void audioPortConfigsFromHal(unsigned int numHalConfigs,
-                                        const struct audio_port_config* halConfigs,
-                                        hidl_vec<AudioPortConfig>* configs);
+    static status_t audioOffloadInfoToHal(const AudioOffloadInfo& offload,
+                                          audio_offload_info_t* halOffload);
+    static status_t audioPortConfigFromHal(const struct audio_port_config& halConfig,
+                                           AudioPortConfig* config);
+    static status_t audioPortConfigToHal(const AudioPortConfig& config,
+                                         struct audio_port_config* halConfig);
+    static status_t audioPortConfigsFromHal(unsigned int numHalConfigs,
+                                            const struct audio_port_config* halConfigs,
+                                            hidl_vec<AudioPortConfig>* configs) {
+        status_t result = NO_ERROR;
+        configs->resize(numHalConfigs);
+        for (unsigned int i = 0; i < numHalConfigs; ++i) {
+            if (status_t status = audioPortConfigFromHal(halConfigs[i], &(*configs)[i]);
+                status != NO_ERROR) {
+                result = status;
+            }
+        }
+        return result;
+    }
+    static status_t audioPortConfigsToHal(const hidl_vec<AudioPortConfig>& configs,
+                                          std::unique_ptr<audio_port_config[]>* halConfigs) {
+        status_t result = NO_ERROR;
+        halConfigs->reset(new audio_port_config[configs.size()]);
+        for (size_t i = 0; i < configs.size(); ++i) {
+            if (status_t status = audioPortConfigToHal(configs[i], &(*halConfigs)[i]);
+                status != NO_ERROR) {
+                result = status;
+            }
+        }
+        return result;
+    }
+
+    // PLEASE DO NOT USE, will be removed in a couple of days
     static std::unique_ptr<audio_port_config[]> audioPortConfigsToHal(
-            const hidl_vec<AudioPortConfig>& configs);
-    static void audioPortFromHal(const struct audio_port& halPort, AudioPort* port);
-    static void audioPortToHal(const AudioPort& port, struct audio_port* halPort);
+            const hidl_vec<AudioPortConfig>& configs) {
+        std::unique_ptr<audio_port_config[]> halConfigs;
+        (void)audioPortConfigsToHal(configs, &halConfigs);
+        return halConfigs;
+    }
+
+    static status_t audioPortFromHal(const struct audio_port& halPort, AudioPort* port);
+    static status_t audioPortToHal(const AudioPort& port, struct audio_port* halPort);
+#if MAJOR_VERSION >= 7
+    static status_t audioChannelMaskFromHal(audio_channel_mask_t halChannelMask, bool isInput,
+                                            AudioChannelMask* channelMask);
+    static status_t audioChannelMaskToHal(const AudioChannelMask& channelMask,
+                                          audio_channel_mask_t* halChannelMask);
+    static status_t audioConfigBaseFromHal(const audio_config_base_t& halConfigBase, bool isInput,
+                                           AudioConfigBase* configBase);
+    static status_t audioConfigBaseToHal(const AudioConfigBase& configBase,
+                                         audio_config_base_t* halConfigBase);
+    static status_t audioDeviceTypeFromHal(audio_devices_t halDevice, AudioDevice* device);
+    static status_t audioDeviceTypeToHal(const AudioDevice& device, audio_devices_t* halDevice);
+    static status_t audioFormatFromHal(audio_format_t halFormat, AudioFormat* format);
+    static status_t audioFormatToHal(const AudioFormat& format, audio_format_t* halFormat);
+    static status_t audioGainModeMaskFromHal(audio_gain_mode_t halGainModeMask,
+                                             hidl_vec<AudioGainMode>* gainModeMask);
+    static status_t audioGainModeMaskToHal(const hidl_vec<AudioGainMode>& gainModeMask,
+                                           audio_gain_mode_t* halGainModeMask);
+    static status_t audioPortFromHal(const struct audio_port_v7& halPort, AudioPort* port);
+    static status_t audioPortToHal(const AudioPort& port, struct audio_port_v7* halPort);
+    static status_t audioProfileFromHal(const struct audio_profile& halProfile, bool isInput,
+                                        AudioProfile* profile);
+    static status_t audioProfileToHal(const AudioProfile& profile,
+                                      struct audio_profile* halProfile);
+    static status_t audioSourceFromHal(audio_source_t halSource, AudioSource* source);
+    static status_t audioSourceToHal(const AudioSource& source, audio_source_t* halSource);
+    static status_t audioStreamTypeFromHal(audio_stream_type_t halStreamType,
+                                           AudioStreamType* streamType);
+    static status_t audioStreamTypeToHal(const AudioStreamType& streamType,
+                                         audio_stream_type_t* halStreamType);
+    static status_t deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+                                       char* halDeviceAddress);
+    static status_t deviceAddressFromHal(audio_devices_t halDeviceType,
+                                         const char* halDeviceAddress, DeviceAddress* device);
+
+  private:
+    static status_t audioIndexChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                 AudioChannelMask* channelMask);
+    static status_t audioInputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                 AudioChannelMask* channelMask);
+    static status_t audioOutputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                  AudioChannelMask* channelMask);
+    static status_t audioPortExtendedInfoFromHal(
+            audio_port_role_t role, audio_port_type_t type,
+            const struct audio_port_config_device_ext& device,
+            const struct audio_port_config_mix_ext& mix,
+            const struct audio_port_config_session_ext& session, AudioPortExtendedInfo* ext,
+            bool* isInput);
+    static status_t audioPortExtendedInfoToHal(const AudioPortExtendedInfo& ext,
+                                               audio_port_role_t* role, audio_port_type_t* type,
+                                               struct audio_port_config_device_ext* device,
+                                               struct audio_port_config_mix_ext* mix,
+                                               struct audio_port_config_session_ext* session);
+#endif
 };
 
 }  // namespace implementation
diff --git a/audio/common/all-versions/default/TEST_MAPPING b/audio/common/all-versions/default/TEST_MAPPING
new file mode 100644
index 0000000..4316ccf
--- /dev/null
+++ b/audio/common/all-versions/default/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "android.hardware.audio.common@7.0-util_tests"
+    }
+  ]
+}
diff --git a/audio/common/all-versions/default/tests/hidlutils_tests.cpp b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
new file mode 100644
index 0000000..bfc99e6
--- /dev/null
+++ b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <array>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#define LOG_TAG "HidlUtils_Test"
+#include <log/log.h>
+
+#include <HidlUtils.h>
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <system/audio.h>
+#include <xsdc/XsdcSupport.h>
+
+using namespace android;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+static constexpr audio_channel_mask_t kInvalidHalChannelMask =
+        static_cast<audio_channel_mask_t>(0xFFFFFFFFU);
+static constexpr audio_devices_t kInvalidHalDevice = static_cast<audio_devices_t>(0xFFFFFFFFU);
+static constexpr audio_format_t kInvalidHalFormat = static_cast<audio_format_t>(0xFFFFFFFFU);
+static constexpr audio_gain_mode_t kInvalidHalGainMode =
+        static_cast<audio_gain_mode_t>(0xFFFFFFFFU);
+static constexpr audio_source_t kInvalidHalSource = static_cast<audio_source_t>(0xFFFFFFFFU);
+static constexpr audio_stream_type_t kInvalidHalStreamType =
+        static_cast<audio_stream_type_t>(0xFFFFFFFFU);
+static constexpr audio_usage_t kInvalidHalUsage = static_cast<audio_usage_t>(0xFFFFFFFFU);
+
+TEST(HidlUtils, ConvertInvalidChannelMask) {
+    AudioChannelMask invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(AUDIO_CHANNEL_INVALID,
+                                                            false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(AUDIO_CHANNEL_INVALID, true /*isInput*/,
+                                                            &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(kInvalidHalChannelMask,
+                                                            false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(kInvalidHalChannelMask,
+                                                            true /*isInput*/, &invalid));
+    audio_channel_mask_t halInvalid;
+    // INVALID channel mask is not in XSD thus it's not allowed for transfer over HIDL.
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskToHal("AUDIO_CHANNEL_INVALID", &halInvalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskToHal("random string", &halInvalid));
+}
+
+// Might move these to the audio_policy_configuration_V7_0-enums library
+// if there would be usages in the default wrapper code. In that case,
+// it would be better to reimplement these methods using a proper switch statement
+// over all known enum values.
+static bool isInputChannelMask(xsd::AudioChannelMask channelMask) {
+    return toString(channelMask).find("_CHANNEL_IN_") != std::string::npos;
+}
+
+static bool isOutputChannelMask(xsd::AudioChannelMask channelMask) {
+    return toString(channelMask).find("_CHANNEL_OUT_") != std::string::npos;
+}
+
+static bool isIndexChannelMask(xsd::AudioChannelMask channelMask) {
+    return toString(channelMask).find("_CHANNEL_INDEX_") != std::string::npos;
+}
+
+TEST(HidlUtils, ConvertChannelMask) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioChannelMask>{}) {
+        const AudioChannelMask channelMask = toString(enumVal);
+        audio_channel_mask_t halChannelMask, halChannelMaskBack;
+        AudioChannelMask channelMaskBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMaskToHal(channelMask, &halChannelMask))
+                << "Conversion of \"" << channelMask << "\" failed";
+        EXPECT_EQ(enumVal != xsd::AudioChannelMask::AUDIO_CHANNEL_NONE,
+                  audio_channel_mask_is_valid(halChannelMask))
+                << "Validity of \"" << channelMask << "\" is not as expected";
+        if (bool isInput = isInputChannelMask(enumVal); isInput || isOutputChannelMask(enumVal)) {
+            EXPECT_EQ(NO_ERROR,
+                      HidlUtils::audioChannelMaskFromHal(halChannelMask, isInput, &channelMaskBack))
+                    << "Conversion of " << (isInput ? "input" : "output") << " channel mask "
+                    << halChannelMask << " failed";
+            // Due to aliased values, the result of 'fromHal' might not be the same
+            // as 'channelMask', thus we need to compare the results of 'toHal' conversion instead.
+            EXPECT_EQ(NO_ERROR,
+                      HidlUtils::audioChannelMaskToHal(channelMaskBack, &halChannelMaskBack))
+                    << "Conversion of \"" << channelMaskBack << "\" failed";
+            EXPECT_EQ(halChannelMask, halChannelMaskBack);
+        } else if (isIndexChannelMask(enumVal) ||
+                   enumVal == xsd::AudioChannelMask::AUDIO_CHANNEL_NONE) {
+            // Conversions for indexed masks and "none" must not depend on the provided direction.
+            EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMaskFromHal(halChannelMask, true /*isInput*/,
+                                                                   &channelMaskBack))
+                    << "Conversion of indexed / none channel mask " << halChannelMask
+                    << " failed (as input channel mask)";
+            EXPECT_EQ(channelMask, channelMaskBack);
+            EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMaskFromHal(
+                                        halChannelMask, false /*isInput*/, &channelMaskBack))
+                    << "Conversion of indexed / none channel mask " << halChannelMask
+                    << " failed (as output channel mask)";
+            EXPECT_EQ(channelMask, channelMaskBack);
+        } else {
+            FAIL() << "Unrecognized channel mask \"" << channelMask << "\"";
+        }
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidConfigBase) {
+    AudioConfigBase invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseFromHal({.sample_rate = 0,
+                                                            .channel_mask = kInvalidHalChannelMask,
+                                                            .format = kInvalidHalFormat},
+                                                           false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseFromHal({.sample_rate = 0,
+                                                            .channel_mask = kInvalidHalChannelMask,
+                                                            .format = kInvalidHalFormat},
+                                                           true /*isInput*/, &invalid));
+    audio_config_base_t halInvalid;
+    invalid.sampleRateHz = 0;
+    invalid.channelMask = "random string";
+    invalid.format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertConfigBase) {
+    AudioConfigBase configBase;
+    configBase.sampleRateHz = 44100;
+    configBase.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    configBase.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    audio_config_base_t halConfigBase;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseToHal(configBase, &halConfigBase));
+    AudioConfigBase configBaseBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigBaseFromHal(halConfigBase, false /*isInput*/, &configBaseBack));
+    EXPECT_EQ(configBase, configBaseBack);
+}
+
+TEST(HidlUtils, ConvertInvalidDeviceType) {
+    AudioDevice invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioDeviceTypeFromHal(kInvalidHalDevice, &invalid));
+    audio_devices_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioDeviceTypeToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertDeviceType) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioDevice>{}) {
+        const AudioDevice deviceType = toString(enumVal);
+        audio_devices_t halDeviceType, halDeviceTypeBack;
+        AudioDevice deviceTypeBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioDeviceTypeToHal(deviceType, &halDeviceType))
+                << "Conversion of \"" << deviceType << "\" failed";
+        if (enumVal != xsd::AudioDevice::AUDIO_DEVICE_NONE) {
+            EXPECT_TRUE(audio_is_input_device(halDeviceType) ||
+                        audio_is_output_device(halDeviceType))
+                    << "Device \"" << deviceType << "\" is neither input, nor output device";
+        } else {
+            EXPECT_FALSE(audio_is_input_device(halDeviceType));
+            EXPECT_FALSE(audio_is_output_device(halDeviceType));
+        }
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioDeviceTypeFromHal(halDeviceType, &deviceTypeBack))
+                << "Conversion of device type " << halDeviceType << " failed";
+        // Due to aliased values, the result of 'fromHal' might not be the same
+        // as 'deviceType', thus we need to compare the results of 'toHal' conversion instead.
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioDeviceTypeToHal(deviceTypeBack, &halDeviceTypeBack))
+                << "Conversion of \"" << deviceTypeBack << "\" failed";
+        EXPECT_EQ(halDeviceType, halDeviceTypeBack);
+    }
+}
+
+// The enums module is too small to have unit tests on its own.
+TEST(HidlUtils, VendorExtension) {
+    EXPECT_TRUE(xsd::isVendorExtension("VX_GOOGLE_VR_42"));
+    EXPECT_FALSE(xsd::isVendorExtension("random string"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_GOOGLE_$$"));
+}
+
+TEST(HidlUtils, ConvertInvalidDeviceAddress) {
+    DeviceAddress invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER,
+                                                         nullptr, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER,
+                                                         "", &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_IP, nullptr, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_IP, "", &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_USB_HEADSET, nullptr, &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_USB_HEADSET, "", &invalid));
+
+    audio_devices_t halInvalid;
+    char halAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+    invalid = {};
+    invalid.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressToHal(invalid, &halInvalid, halAddress));
+    invalid.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_IP);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressToHal(invalid, &halInvalid, halAddress));
+    invalid.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_USB_HEADSET);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressToHal(invalid, &halInvalid, halAddress));
+}
+
+static void ConvertDeviceAddress(const DeviceAddress& device) {
+    audio_devices_t halDeviceType;
+    char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+    EXPECT_EQ(NO_ERROR, HidlUtils::deviceAddressToHal(device, &halDeviceType, halDeviceAddress));
+    DeviceAddress deviceBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::deviceAddressFromHal(halDeviceType, halDeviceAddress, &deviceBack));
+    EXPECT_EQ(device, deviceBack);
+}
+
+TEST(HidlUtils, ConvertUniqueDeviceAddress) {
+    DeviceAddress speaker;
+    speaker.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+    ConvertDeviceAddress(speaker);
+}
+
+TEST(HidlUtils, ConvertA2dpDeviceAddress) {
+    DeviceAddress a2dpSpeaker;
+    a2dpSpeaker.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
+    a2dpSpeaker.address.mac(std::array<uint8_t, 6>{1, 2, 3, 4, 5, 6});
+    ConvertDeviceAddress(a2dpSpeaker);
+}
+
+TEST(HidlUtils, ConvertIpv4DeviceAddress) {
+    DeviceAddress ipv4;
+    ipv4.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_IP);
+    ipv4.address.ipv4(std::array<uint8_t, 4>{1, 2, 3, 4});
+    ConvertDeviceAddress(ipv4);
+}
+
+TEST(HidlUtils, ConvertUsbDeviceAddress) {
+    DeviceAddress usbHeadset;
+    usbHeadset.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_USB_HEADSET);
+    usbHeadset.address.alsa({1, 2});
+    ConvertDeviceAddress(usbHeadset);
+}
+
+TEST(HidlUtils, ConvertBusDeviceAddress) {
+    DeviceAddress bus;
+    bus.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS);
+    bus.address.id("bus_device");
+    ConvertDeviceAddress(bus);
+}
+
+TEST(HidlUtils, ConvertRSubmixDeviceAddress) {
+    DeviceAddress rSubmix;
+    rSubmix.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_REMOTE_SUBMIX);
+    rSubmix.address.id(AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS);
+    ConvertDeviceAddress(rSubmix);
+}
+
+TEST(HidlUtils, ConvertVendorDeviceAddress) {
+    // The address part is not mandatory, both cases must work.
+    {
+        DeviceAddress vendor;
+        vendor.deviceType = "VX_GOOGLE_VR";
+        audio_devices_t halDeviceType;
+        char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+        // Ignore the result. Vendors will also add the extended device into
+        // the list of devices in audio-hal-enums.h. Without that, the conversion
+        // officially fails, but it still maps the device type to NONE.
+        (void)HidlUtils::deviceAddressToHal(vendor, &halDeviceType, halDeviceAddress);
+        EXPECT_EQ(AUDIO_DEVICE_NONE, halDeviceType);
+        EXPECT_EQ(0, strnlen(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN));
+    }
+    {
+        DeviceAddress vendor;
+        vendor.deviceType = "VX_GOOGLE_VR";
+        vendor.address.id("vr1");
+        audio_devices_t halDeviceType;
+        char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+        // Ignore the result. Vendors will also add the extended device into
+        // the list of devices in audio-hal-enums.h. Without that, the conversion
+        // officially fails, but it still maps the device type to NONE and converts
+        // the address.
+        (void)HidlUtils::deviceAddressToHal(vendor, &halDeviceType, halDeviceAddress);
+        EXPECT_EQ(AUDIO_DEVICE_NONE, halDeviceType);
+        EXPECT_EQ(0, strncmp("vr1", halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN));
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidFormat) {
+    AudioFormat invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatFromHal(kInvalidHalFormat, &invalid));
+    audio_format_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertFormat) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioFormat>{}) {
+        const AudioFormat format = toString(enumVal);
+        audio_format_t halFormat;
+        AudioFormat formatBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioFormatToHal(format, &halFormat))
+                << "Conversion of \"" << format << "\" failed";
+        EXPECT_TRUE(audio_is_valid_format(halFormat))
+                << "Converted format \"" << format << "\" is invalid";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioFormatFromHal(halFormat, &formatBack))
+                << "Conversion of format " << halFormat << " failed";
+        EXPECT_EQ(format, formatBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidGainModeMask) {
+    hidl_vec<AudioGainMode> invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainModeMaskFromHal(kInvalidHalGainMode, &invalid));
+    audio_gain_mode_t halInvalid;
+    invalid.resize(1);
+    invalid[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainModeMaskToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertGainModeMask) {
+    hidl_vec<AudioGainMode> emptyGainModes;
+    audio_gain_mode_t halEmptyGainModes;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainModeMaskToHal(emptyGainModes, &halEmptyGainModes));
+    hidl_vec<AudioGainMode> emptyGainModesBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioGainModeMaskFromHal(halEmptyGainModes, &emptyGainModesBack));
+    EXPECT_EQ(emptyGainModes, emptyGainModesBack);
+
+    std::vector<std::string> allEnumValues;
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioGainMode>{}) {
+        allEnumValues.push_back(toString(enumVal));
+    }
+    hidl_vec<AudioGainMode> allGainModes;
+    allGainModes.resize(allEnumValues.size());
+    for (size_t i = 0; i < allEnumValues.size(); ++i) {
+        allGainModes[i] = allEnumValues[i];
+    }
+    audio_gain_mode_t halAllGainModes;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainModeMaskToHal(allGainModes, &halAllGainModes));
+    hidl_vec<AudioGainMode> allGainModesBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainModeMaskFromHal(halAllGainModes, &allGainModesBack));
+    EXPECT_EQ(allGainModes, allGainModesBack);
+}
+
+TEST(HidlUtils, ConvertInvalidSource) {
+    AudioSource invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioSourceFromHal(kInvalidHalSource, &invalid));
+    audio_source_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioSourceToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertSource) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioSource>{}) {
+        const AudioSource source = toString(enumVal);
+        audio_source_t halSource;
+        AudioSource sourceBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioSourceToHal(source, &halSource))
+                << "Conversion of \"" << source << "\" failed";
+        EXPECT_EQ(enumVal != xsd::AudioSource::AUDIO_SOURCE_DEFAULT,
+                  audio_is_valid_audio_source(halSource))
+                << "Validity of \"" << source << "\" is not as expected";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioSourceFromHal(halSource, &sourceBack))
+                << "Conversion of source " << halSource << " failed";
+        EXPECT_EQ(source, sourceBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidStreamType) {
+    AudioStreamType invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioStreamTypeFromHal(kInvalidHalStreamType, &invalid));
+    audio_stream_type_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioStreamTypeToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertStreamType) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioStreamType>{}) {
+        const AudioStreamType streamType = toString(enumVal);
+        audio_stream_type_t halStreamType;
+        AudioStreamType streamTypeBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioStreamTypeToHal(streamType, &halStreamType))
+                << "Conversion of \"" << streamType << "\" failed";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioStreamTypeFromHal(halStreamType, &streamTypeBack))
+                << "Conversion of stream type " << halStreamType << " failed";
+        EXPECT_EQ(streamType, streamTypeBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidGain) {
+    AudioGain invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainFromHal({.mode = kInvalidHalGainMode},
+                                                     false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainFromHal({.mode = kInvalidHalGainMode},
+                                                     true /*isInput*/, &invalid));
+    struct audio_gain halInvalid;
+    invalid.mode.resize(1);
+    invalid.mode[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertGain) {
+    AudioGain gain = {};
+    gain.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    struct audio_gain halGain;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainToHal(gain, &halGain));
+    AudioGain gainBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainFromHal(halGain, false /*isInput*/, &gainBack));
+    EXPECT_EQ(gain, gainBack);
+    struct audio_gain halGainBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainToHal(gainBack, &halGainBack));
+    EXPECT_TRUE(audio_gains_are_equal(&halGain, &halGainBack));
+}
+
+TEST(HidlUtils, ConvertInvalidGainConfig) {
+    AudioGainConfig invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainConfigFromHal({.mode = kInvalidHalGainMode},
+                                                           false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainConfigFromHal({.mode = kInvalidHalGainMode},
+                                                           true /*isInput*/, &invalid));
+    struct audio_gain_config halInvalid;
+    invalid.mode.resize(1);
+    invalid.mode[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainConfigToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertGainConfig) {
+    AudioGainConfig gainConfig = {};
+    gainConfig.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    struct audio_gain_config halGainConfig;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainConfigToHal(gainConfig, &halGainConfig));
+    AudioGainConfig gainConfigBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioGainConfigFromHal(halGainConfig, false /*isInput*/, &gainConfigBack));
+    EXPECT_EQ(gainConfig, gainConfigBack);
+    struct audio_gain_config halGainConfigBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainConfigToHal(gainConfigBack, &halGainConfigBack));
+    EXPECT_TRUE(audio_gain_config_are_equal(&halGainConfig, &halGainConfigBack));
+}
+
+TEST(HidlUtils, ConvertInvalidUsage) {
+    AudioUsage invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioUsageFromHal(kInvalidHalUsage, &invalid));
+    audio_usage_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioUsageToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertUsage) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioUsage>{}) {
+        const AudioUsage usage = toString(enumVal);
+        audio_usage_t halUsage;
+        AudioUsage usageBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioUsageToHal(usage, &halUsage))
+                << "Conversion of \"" << usage << "\" failed";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioUsageFromHal(halUsage, &usageBack))
+                << "Conversion of usage " << halUsage << " failed";
+        EXPECT_EQ(usage, usageBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidOffloadInfo) {
+    AudioOffloadInfo invalid;
+    audio_offload_info_t halInvalid = AUDIO_INFO_INITIALIZER;
+    halInvalid.channel_mask = AUDIO_CHANNEL_INVALID;
+    halInvalid.format = kInvalidHalFormat;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioOffloadInfoFromHal(halInvalid, &invalid));
+    invalid.base.channelMask = "random string";
+    invalid.base.format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioOffloadInfoToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertOffloadInfo) {
+    AudioOffloadInfo offloadInfo = {};
+    offloadInfo.base.sampleRateHz = 44100;
+    offloadInfo.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    offloadInfo.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    offloadInfo.streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC);
+    offloadInfo.bitRatePerSecond = 320;
+    offloadInfo.durationMicroseconds = -1;
+    offloadInfo.bitWidth = 16;
+    offloadInfo.bufferSize = 1024;
+    offloadInfo.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA);
+    offloadInfo.encapsulationMode = AudioEncapsulationMode::ELEMENTARY_STREAM;
+    offloadInfo.contentId = 42;
+    offloadInfo.syncId = 13;
+    audio_offload_info_t halOffloadInfo;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioOffloadInfoToHal(offloadInfo, &halOffloadInfo));
+    AudioOffloadInfo offloadInfoBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioOffloadInfoFromHal(halOffloadInfo, &offloadInfoBack));
+    EXPECT_EQ(offloadInfo, offloadInfoBack);
+}
+
+TEST(HidlUtils, ConvertInvalidConfig) {
+    AudioConfig invalid;
+    audio_config_t halInvalid = AUDIO_CONFIG_INITIALIZER;
+    halInvalid.channel_mask = AUDIO_CHANNEL_INVALID;
+    halInvalid.format = kInvalidHalFormat;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigFromHal(halInvalid, false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigFromHal(halInvalid, true /*isInput*/, &invalid));
+    invalid.base.channelMask = "random string";
+    invalid.base.format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertConfig) {
+    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.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, ConvertInvalidAudioProfile) {
+    AudioProfile invalid;
+    struct audio_profile halInvalid = {};
+    halInvalid.format = kInvalidHalFormat;
+    halInvalid.num_sample_rates = 0;
+    halInvalid.num_channel_masks = 1;
+    halInvalid.channel_masks[0] = kInvalidHalChannelMask;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioProfileFromHal(halInvalid, false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioProfileFromHal(halInvalid, true /*isInput*/, &invalid));
+    invalid.format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioProfileToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioProfile) {
+    AudioProfile profile = {};
+    profile.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    profile.sampleRates.resize(2);
+    profile.sampleRates[0] = 44100;
+    profile.sampleRates[1] = 48000;
+    profile.channelMasks.resize(2);
+    profile.channelMasks[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
+    profile.channelMasks[1] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    struct audio_profile halProfile;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioProfileToHal(profile, &halProfile));
+    AudioProfile profileBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioProfileFromHal(halProfile, false /*isInput*/, &profileBack));
+    EXPECT_EQ(profile, profileBack);
+}
+
+TEST(HidlUtils, ConvertInvalidAudioPortConfig) {
+    AudioPortConfig invalid;
+    struct audio_port_config halInvalid = {};
+    halInvalid.type = AUDIO_PORT_TYPE_MIX;
+    halInvalid.role = AUDIO_PORT_ROLE_NONE;  // note: this is valid.
+    halInvalid.config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK;
+    halInvalid.channel_mask = AUDIO_CHANNEL_INVALID;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortConfigFromHal(halInvalid, &invalid));
+    invalid.base.channelMask = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortConfigToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioPortConfig) {
+    AudioPortConfig config = {};
+    config.id = 42;
+    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.gain.config({});
+    config.gain.config().channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    config.ext.device({});
+    config.ext.device().deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+    struct audio_port_config halConfig;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortConfigToHal(config, &halConfig));
+    AudioPortConfig configBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortConfigFromHal(halConfig, &configBack));
+    EXPECT_EQ(config, configBack);
+    struct audio_port_config halConfigBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortConfigToHal(configBack, &halConfigBack));
+    EXPECT_TRUE(audio_port_configs_are_equal(&halConfig, &halConfigBack));
+}
+
+TEST(HidlUtils, ConvertInvalidAudioPort) {
+    AudioPort invalid;
+    struct audio_port_v7 halInvalid = {};
+    halInvalid.type = AUDIO_PORT_TYPE_MIX;
+    halInvalid.role = AUDIO_PORT_ROLE_NONE;  // note: this is valid.
+    halInvalid.num_audio_profiles = 1;
+    halInvalid.audio_profiles[0].format = kInvalidHalFormat;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortFromHal(halInvalid, &invalid));
+    invalid.profiles.resize(1);
+    invalid.profiles[0].format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioPort) {
+    AudioPort port = {};
+    port.id = 42;
+    port.name = "test";
+    port.profiles.resize(1);
+    port.profiles[0].format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    port.profiles[0].sampleRates.resize(2);
+    port.profiles[0].sampleRates[0] = 44100;
+    port.profiles[0].sampleRates[1] = 48000;
+    port.profiles[0].channelMasks.resize(2);
+    port.profiles[0].channelMasks[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
+    port.profiles[0].channelMasks[1] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    port.gains.resize(1);
+    port.gains[0].channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    port.ext.device({});
+    port.ext.device().deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+    // active config left unspecified.
+    struct audio_port_v7 halPort;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortToHal(port, &halPort));
+    AudioPort portBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortFromHal(halPort, &portBack));
+    EXPECT_EQ(port, portBack);
+    struct audio_port_v7 halPortBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortToHal(portBack, &halPortBack));
+    EXPECT_TRUE(audio_ports_v7_are_equal(&halPort, &halPortBack));
+}