Merge "audio: Create libraries for data types in core and effect" am: 1df025e5f4 am: 3923c634a0

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/1566743

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ie43669f44b1bd8d38f70426bdb09ebf5f6e175ec
diff --git a/audio/7.0/config/api/current.txt b/audio/7.0/config/api/current.txt
index 8b9d332..c1940e5 100644
--- a/audio/7.0/config/api/current.txt
+++ b/audio/7.0/config/api/current.txt
@@ -255,7 +255,6 @@
     enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_HW_AV_SYNC;
     enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_HW_HOTWORD;
     enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_MMAP_NOIRQ;
-    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_NONE;
     enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_RAW;
     enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_SYNC;
     enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_VOIP_TX;
@@ -268,7 +267,6 @@
     enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO;
     enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
     enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
-    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_NONE;
     enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_NON_BLOCKING;
     enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_PRIMARY;
     enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_RAW;
diff --git a/audio/7.0/config/audio_policy_configuration.xsd b/audio/7.0/config/audio_policy_configuration.xsd
index a72c8ac..4d224c8 100644
--- a/audio/7.0/config/audio_policy_configuration.xsd
+++ b/audio/7.0/config/audio_policy_configuration.xsd
@@ -159,13 +159,9 @@
         <xs:annotation>
             <xs:documentation xml:lang="en">
               The flags indicate suggested stream attributes supported by the profile.
-              Use of AUDIO_{INPUT|OUTPUT}_FLAG_NONE in the XML file isn't required
-              as empty flag lists are allowed. However these constants are useful for
-              representing an empty enum value.
             </xs:documentation>
         </xs:annotation>
         <xs:restriction base="xs:string">
-            <xs:enumeration value="AUDIO_OUTPUT_FLAG_NONE" />
             <xs:enumeration value="AUDIO_OUTPUT_FLAG_DIRECT" />
             <xs:enumeration value="AUDIO_OUTPUT_FLAG_PRIMARY" />
             <xs:enumeration value="AUDIO_OUTPUT_FLAG_FAST" />
@@ -181,7 +177,6 @@
             <xs:enumeration value="AUDIO_OUTPUT_FLAG_MMAP_NOIRQ" />
             <xs:enumeration value="AUDIO_OUTPUT_FLAG_VOIP_RX" />
             <xs:enumeration value="AUDIO_OUTPUT_FLAG_INCALL_MUSIC" />
-            <xs:enumeration value="AUDIO_INPUT_FLAG_NONE" />
             <xs:enumeration value="AUDIO_INPUT_FLAG_FAST" />
             <xs:enumeration value="AUDIO_INPUT_FLAG_HW_HOTWORD" />
             <xs:enumeration value="AUDIO_INPUT_FLAG_RAW" />
diff --git a/audio/README.md b/audio/README.md
index b77b9ba..1938ad4 100644
--- a/audio/README.md
+++ b/audio/README.md
@@ -7,47 +7,49 @@
 
 ## Directory Structure
 
-* `2.0` -- version 2.0 of the core HIDL API. Note that `.hal` files
+* `2.0` — version 2.0 of the core HIDL API. Note that `.hal` files
   can not be moved into the `core` directory because that would change
   its namespace and include path.
-   - `config` -- the XSD schema for the Audio Policy Manager
+   - `config` — the XSD schema for the Audio Policy Manager
      configuration file.
-* `4.0` -- version 4.0 of the core HIDL API.
+* `4.0` — version 4.0 of the core HIDL API.
 * ...
-* `common` -- common types for audio core and effect HIDL API.
-   - `2.0` -- version 2.0 of the common types HIDL API.
-   - `4.0` -- version 4.0.
+* `common` — common types for audio core and effect HIDL API.
+   - `2.0` — version 2.0 of the common types HIDL API.
+   - `4.0` — version 4.0.
    - ...
-   - `7.0` -- version 7.0.
-      - `example` -- example implementation of the core and effect
+   - `7.0` — version 7.0.
+      - `example` — example implementation of the core and effect
         V7.0 API. It represents a "fake" audio HAL that doesn't
         actually communicate with hardware.
-   - `all-versions` -- code common to all version of both core and effect API.
-      - `default` -- shared code of the default implementation.
-         - `service` -- vendor HAL service for hosting the default
+   - `all-versions` — code common to all version of both core and effect API.
+      - `default` — shared code of the default implementation.
+         - `service` — vendor HAL service for hosting the default
            implementation.
-      - `test` -- utilities used by tests.
-      - `util` -- utilities used by both implementation and tests.
-* `core` -- VTS tests and the default implementation of the core API
+      - `test` — utilities used by tests.
+      - `util` — utilities used by both implementation and tests.
+* `core` — VTS tests and the default implementation of the core API
   (not HIDL API, it's in `audio/N.M`).
-   - `7.0` -- code specific to version V7.0 of the core HIDL API
-   - `all-versions` -- the code is common between all versions,
+   - `7.0` — code specific to version V7.0 of the core HIDL API
+   - `all-versions` — the code is common between all versions,
      version-specific parts are enclosed into conditional directives
      of preprocessor or reside in dedicated files.
-       - `default` -- code that wraps the legacy API (from
+       - `default` — code that wraps the legacy API (from
          `hardware/libhardware`).
+         - `util` — utilities for the default implementation.
        - `vts` VTS tests for the core HIDL API.
-* `effect` -- same for the effect HIDL API.
+* `effect` — same for the effect HIDL API.
    - `2.0`
-      - `config` -- the XSD schema for the Audio Effects configuration
-        file.
+      - `config` — the XSD schema for the Audio Effects configuration file.
    - `4.0`
    - ...
    - `all-versions`
-      - `default`
-      - `vts`
-* `policy` -- Configurable Audio Policy schemes.
-   - `1.0` -- note that versions of CAP are not linked to the versions
+       - `default` — code that wraps the legacy API (from
+         `hardware/libhardware`).
+         - `util` — utilities for the default implementation.
+       - `vts` VTS tests for the effect HIDL API.
+* `policy` — Configurable Audio Policy schemes.
+   - `1.0` — note that versions of CAP are not linked to the versions
      of audio HAL.
-      - `vts` -- VTS tests for validating actual configuration files.
-      - `xml` -- XSD schemas for CAP configuration files.
+      - `vts` — VTS tests for validating actual configuration files.
+      - `xml` — XSD schemas for CAP configuration files.
diff --git a/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h b/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
index c0042db..b427f3a 100644
--- a/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
+++ b/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
@@ -212,10 +212,16 @@
     return isOutputDevice(stringToAudioDevice(device));
 }
 
+static inline bool maybeVendorExtension(const std::string& s) {
+    // Only checks whether the string starts with the "vendor prefix".
+    static const std::string vendorPrefix = "VX_";
+    return s.size() > vendorPrefix.size() && s.substr(0, vendorPrefix.size()) == vendorPrefix;
+}
+
 static inline bool isVendorExtension(const std::string& s) {
     // Must match the "vendorExtension" rule from the XSD file.
     static const std::string vendorPrefix = "VX_";
-    return s.size() > vendorPrefix.size() && s.substr(0, vendorPrefix.size()) == vendorPrefix &&
+    return maybeVendorExtension(s) &&
            std::all_of(s.begin() + vendorPrefix.size(), s.end(),
                        [](unsigned char c) { return c == '_' || std::isalnum(c); });
 }
diff --git a/audio/common/7.0/types.hal b/audio/common/7.0/types.hal
index 3903a0b..99c2e5a 100644
--- a/audio/common/7.0/types.hal
+++ b/audio/common/7.0/types.hal
@@ -309,15 +309,15 @@
 struct PlaybackTrackMetadata {
     AudioUsage usage;
     AudioContentType contentType;
-    /** Tags from AudioTrack audio atttributes */
-    vec<AudioTag> tags;
-    AudioChannelMask channelMask;
     /**
      * Positive linear gain applied to the track samples. 0 being muted and 1 is no attenuation,
      * 2 means double amplification...
      * Must not be negative.
      */
     float gain;
+    AudioChannelMask channelMask;
+    /** Tags from AudioTrack audio atttributes */
+    vec<AudioTag> tags;
 };
 
 /** Metadatas of the source of a StreamOut. */
@@ -328,9 +328,6 @@
 /** Metadata of a record track for a StreamIn. */
 struct RecordTrackMetadata {
     AudioSource source;
-    /** Tags from AudioTrack audio atttributes */
-    vec<AudioTag> tags;
-    AudioChannelMask channelMask;
     /**
      * Positive linear gain applied to the track samples. 0 being muted and 1 is no attenuation,
      * 2 means double amplification...
@@ -344,6 +341,9 @@
         Monostate unspecified;
         DeviceAddress device;
     } destination;
+    AudioChannelMask channelMask;
+    /** Tags from AudioTrack audio atttributes */
+    vec<AudioTag> tags;
 };
 
 /** Metadatas of the sink of a StreamIn. */
diff --git a/audio/common/all-versions/default/7.0/HidlUtils.cpp b/audio/common/all-versions/default/7.0/HidlUtils.cpp
index f09db5e..bb3a596 100644
--- a/audio/common/all-versions/default/7.0/HidlUtils.cpp
+++ b/audio/common/all-versions/default/7.0/HidlUtils.cpp
@@ -16,6 +16,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <algorithm>
 
 #define LOG_TAG "HidlUtils"
 #include <log/log.h>
@@ -281,7 +282,7 @@
                                              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) {
+    for (uint32_t bit = 0; halGainModeMask != 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);
@@ -291,6 +292,7 @@
                 ALOGE("Unknown audio gain mode value 0x%X", flag);
                 status = BAD_VALUE;
             }
+            halGainModeMask = static_cast<audio_gain_mode_t>(halGainModeMask & ~flag);
         }
     }
     *gainModeMask = result;
@@ -858,15 +860,17 @@
     return result;
 }
 
-status_t HidlUtils::audioTagsFromHal(const char* halTags, hidl_vec<AudioTag>* tags) {
-    std::vector<std::string> strTags = utils::splitString(halTags, sAudioTagSeparator);
+status_t HidlUtils::audioTagsFromHal(const std::vector<std::string>& strTags,
+                                     hidl_vec<AudioTag>* tags) {
     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];
+        const auto& tag = strTags[from];
+        if (xsd::isVendorExtension(tag)) {
+            (*tags)[to++] = tag;
         } else {
+            ALOGE("Vendor extension tag is ill-formed: \"%s\"", tag.c_str());
             result = BAD_VALUE;
         }
     }
@@ -889,6 +893,7 @@
             halTagsBuffer << tag;
             hasValue = true;
         } else {
+            ALOGE("Vendor extension tag is ill-formed: \"%s\"", tag.c_str());
             result = BAD_VALUE;
         }
     }
@@ -899,6 +904,31 @@
     return result;
 }
 
+hidl_vec<AudioTag> HidlUtils::filterOutNonVendorTags(const hidl_vec<AudioTag>& tags) {
+    hidl_vec<AudioTag> result;
+    result.resize(tags.size());
+    size_t resultIdx = 0;
+    for (const auto& tag : tags) {
+        if (xsd::maybeVendorExtension(tag)) {
+            result[resultIdx++] = tag;
+        }
+    }
+    if (resultIdx != result.size()) {
+        result.resize(resultIdx);
+    }
+    return result;
+}
+
+std::vector<std::string> HidlUtils::filterOutNonVendorTags(const std::vector<std::string>& tags) {
+    std::vector<std::string> result;
+    std::copy_if(tags.begin(), tags.end(), std::back_inserter(result), xsd::maybeVendorExtension);
+    return result;
+}
+
+std::vector<std::string> HidlUtils::splitAudioTags(const char* halTags) {
+    return utils::splitString(halTags, sAudioTagSeparator);
+}
+
 status_t HidlUtils::deviceAddressFromHal(audio_devices_t halDeviceType,
                                          const char* halDeviceAddress, DeviceAddress* device) {
     status_t result = NO_ERROR;
diff --git a/audio/common/all-versions/default/HidlUtils.h b/audio/common/all-versions/default/HidlUtils.h
index cc4fbf2..22b7152 100644
--- a/audio/common/all-versions/default/HidlUtils.h
+++ b/audio/common/all-versions/default/HidlUtils.h
@@ -20,6 +20,8 @@
 #include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include <system/audio.h>
 
@@ -118,8 +120,12 @@
                                            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 audioTagsFromHal(const std::vector<std::string>& strTags,
+                                     hidl_vec<AudioTag>* tags);
     static status_t audioTagsToHal(const hidl_vec<AudioTag>& tags, char* halTags);
+    static hidl_vec<AudioTag> filterOutNonVendorTags(const hidl_vec<AudioTag>& tags);
+    static std::vector<std::string> filterOutNonVendorTags(const std::vector<std::string>& tags);
+    static std::vector<std::string> splitAudioTags(const char* halTags);
 
   private:
     static status_t audioIndexChannelMaskFromHal(audio_channel_mask_t halChannelMask,
diff --git a/audio/common/all-versions/default/UuidUtils.cpp b/audio/common/all-versions/default/UuidUtils.cpp
index 85edc7b..6c4c94d 100644
--- a/audio/common/all-versions/default/UuidUtils.cpp
+++ b/audio/common/all-versions/default/UuidUtils.cpp
@@ -42,6 +42,14 @@
     memcpy(halUuid->node, uuid.node.data(), uuid.node.size());
 }
 
+std::string UuidUtils::uuidToString(const audio_uuid_t& halUuid) {
+    char str[64];
+    snprintf(str, sizeof(str), "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", halUuid.timeLow,
+             halUuid.timeMid, halUuid.timeHiAndVersion, halUuid.clockSeq, halUuid.node[0],
+             halUuid.node[1], halUuid.node[2], halUuid.node[3], halUuid.node[4], halUuid.node[5]);
+    return str;
+}
+
 }  // namespace implementation
 }  // namespace CPP_VERSION
 }  // namespace common
diff --git a/audio/common/all-versions/default/UuidUtils.h b/audio/common/all-versions/default/UuidUtils.h
index 38db48a..cd04fb0 100644
--- a/audio/common/all-versions/default/UuidUtils.h
+++ b/audio/common/all-versions/default/UuidUtils.h
@@ -17,14 +17,14 @@
 #ifndef android_hardware_audio_Uuid_Utils_H_
 #define android_hardware_audio_Uuid_Utils_H_
 
+#include <string>
+
 // clang-format off
 #include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
 // clang-format on
 
 #include <system/audio.h>
 
-using ::android::hardware::hidl_vec;
-
 namespace android {
 namespace hardware {
 namespace audio {
@@ -38,6 +38,7 @@
   public:
     static void uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid);
     static void uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid);
+    static std::string uuidToString(const audio_uuid_t& halUuid);
 };
 
 }  // namespace implementation
diff --git a/audio/common/all-versions/default/tests/hidlutils_tests.cpp b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
index 642ece3..40fc5c8 100644
--- a/audio/common/all-versions/default/tests/hidlutils_tests.cpp
+++ b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
@@ -762,9 +762,7 @@
 
 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.base = generateValidConfigBase(false /*isInput*/);
     offloadInfo.streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC);
     offloadInfo.bitRatePerSecond = 320;
     offloadInfo.durationMicroseconds = -1;
@@ -783,33 +781,76 @@
 
 TEST(HidlUtils, ConvertInvalidConfig) {
     AudioConfig invalid;
-    audio_config_t halInvalid = AUDIO_CONFIG_INITIALIZER;
-    halInvalid.channel_mask = kInvalidHalChannelMask;
-    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));
+    audio_config_t halInvalidChannelMask = AUDIO_CONFIG_INITIALIZER;
+    halInvalidChannelMask.channel_mask = kInvalidHalChannelMask;
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigFromHal(halInvalidChannelMask, false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigFromHal(halInvalidChannelMask, true /*isInput*/, &invalid));
+    audio_config_t halInvalidFormat = AUDIO_CONFIG_INITIALIZER;
+    halInvalidFormat.format = kInvalidHalFormat;
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigFromHal(halInvalidFormat, false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigFromHal(halInvalidFormat, true /*isInput*/, &invalid));
+
+    AudioConfig invalidChannelMask;
+    audio_config_t halInvalid;
+    invalidChannelMask.base.channelMask = "random string";
+    invalidChannelMask.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_DEFAULT);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigToHal(invalidChannelMask, &halInvalid));
+    AudioConfig invalidFormat;
+    invalidFormat.base.format = "random string";
+    invalidFormat.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigToHal(invalidFormat, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertConfigDefault) {
+    audio_config_t halDefault = AUDIO_CONFIG_INITIALIZER;
+    AudioConfig defaultOut, defaultIn;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigFromHal(halDefault, false /*isInput*/, &defaultOut));
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigFromHal(halDefault, true /*isInput*/, &defaultIn));
+    EXPECT_EQ(defaultOut, defaultIn);
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(defaultOut, &halDefault));
+
+    // Note: empty channel mask and config are not valid values.
+    AudioConfig defaultCfg{};
+    defaultCfg.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    defaultCfg.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_DEFAULT);
+    audio_config_t halDefaultCfg;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(defaultCfg, &halDefaultCfg));
+    AudioConfig defaultCfgBackOut, defaultCfgBackIn;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigFromHal(halDefaultCfg, false /*isInput*/, &defaultCfgBackOut));
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigFromHal(halDefaultCfg, true /*isInput*/, &defaultCfgBackIn));
+    EXPECT_EQ(defaultCfgBackOut, defaultCfgBackIn);
+    EXPECT_EQ(defaultCfg, defaultCfgBackOut);
 }
 
 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);
-    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);
+    AudioConfig configOut{};
+    configOut.base = generateValidConfigBase(false /*isInput*/);
+    audio_config_t halConfigOut;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(configOut, &halConfigOut));
+    AudioConfig configOutBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigFromHal(halConfigOut, false /*isInput*/, &configOutBack));
+    EXPECT_EQ(configOut, configOutBack);
+
+    AudioConfig configIn{};
+    configIn.base = generateValidConfigBase(true /*isInput*/);
+    audio_config_t halConfigIn;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(configIn, &halConfigIn));
+    AudioConfig configInBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigFromHal(halConfigIn, true /*isInput*/, &configInBack));
+    EXPECT_EQ(configIn, configInBack);
 }
 
 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.base = generateValidConfigBase(false /*isInput*/);
     config.offloadInfo.info(
             AudioOffloadInfo{.base = config.base,
                              .streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC),
@@ -952,20 +993,51 @@
     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(NO_ERROR,
+              HidlUtils::audioTagsFromHal(HidlUtils::splitAudioTags(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(NO_ERROR,
+              HidlUtils::audioTagsFromHal(HidlUtils::splitAudioTags(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(NO_ERROR,
+              HidlUtils::audioTagsFromHal(HidlUtils::splitAudioTags(halTwoTags), &twoTagsBack));
     EXPECT_EQ(twoTags, twoTagsBack);
 }
+
+template <typename T>
+class FilterTest : public ::testing::Test {};
+using FilterTestTypeParams = ::testing::Types<hidl_vec<AudioTag>, std::vector<std::string>>;
+TYPED_TEST_SUITE(FilterTest, FilterTestTypeParams);
+
+TYPED_TEST(FilterTest, FilterOutNonVendorTags) {
+    TypeParam emptyTags;
+    EXPECT_EQ(emptyTags, HidlUtils::filterOutNonVendorTags(emptyTags));
+
+    TypeParam allVendorTags = {{"VX_GOOGLE_VR_42", "VX_GOOGLE_1E100"}};
+    EXPECT_EQ(allVendorTags, HidlUtils::filterOutNonVendorTags(allVendorTags));
+
+    TypeParam oneVendorTag = {{"", "VX_GOOGLE_VR", "random_string"}};
+    TypeParam oneVendorTagOnly = HidlUtils::filterOutNonVendorTags(oneVendorTag);
+    EXPECT_EQ(1, oneVendorTagOnly.size());
+    EXPECT_EQ(oneVendorTag[1], oneVendorTagOnly[0]);
+
+    // The vendor extension isn't valid, however it must not be filtered out
+    // so the converter can detect the issue.
+    TypeParam oneMaybeVendorTag = {{"", "random string", "VX_GOOGLE_$$"}};
+    TypeParam oneMaybeVendorTagOnly = HidlUtils::filterOutNonVendorTags(oneMaybeVendorTag);
+    EXPECT_EQ(1, oneMaybeVendorTagOnly.size());
+    EXPECT_EQ(oneMaybeVendorTag[2], oneMaybeVendorTagOnly[0]);
+
+    TypeParam noVendorTags = {{"", "random string", "V_"}};
+    EXPECT_EQ(emptyTags, HidlUtils::filterOutNonVendorTags(noVendorTags));
+}
diff --git a/audio/core/all-versions/default/Android.bp b/audio/core/all-versions/default/Android.bp
index e0f0860..dcff425 100644
--- a/audio/core/all-versions/default/Android.bp
+++ b/audio/core/all-versions/default/Android.bp
@@ -1,7 +1,6 @@
 filegroup {
     name: "android.hardware.audio-impl_srcs",
     srcs: [
-        "Conversions.cpp",
         "Device.cpp",
         "DevicesFactory.cpp",
         "ParametersUtil.cpp",
@@ -63,6 +62,7 @@
     defaults: ["android.hardware.audio-impl_default"],
     shared_libs: [
         "android.hardware.audio@2.0",
+        "android.hardware.audio@2.0-util",
         "android.hardware.audio.common@2.0",
         "android.hardware.audio.common@2.0-util",
     ],
@@ -79,6 +79,7 @@
 
     shared_libs: [
         "android.hardware.audio@4.0",
+        "android.hardware.audio@4.0-util",
         "android.hardware.audio.common@4.0",
         "android.hardware.audio.common@4.0-util",
     ],
@@ -94,6 +95,7 @@
     defaults: ["android.hardware.audio-impl_default"],
     shared_libs: [
         "android.hardware.audio@5.0",
+        "android.hardware.audio@5.0-util",
         "android.hardware.audio.common@5.0",
         "android.hardware.audio.common@5.0-util",
     ],
@@ -109,6 +111,7 @@
     defaults: ["android.hardware.audio-impl_default"],
     shared_libs: [
         "android.hardware.audio@6.0",
+        "android.hardware.audio@6.0-util",
         "android.hardware.audio.common@6.0",
         "android.hardware.audio.common@6.0-util",
     ],
@@ -129,6 +132,7 @@
     defaults: ["android.hardware.audio-impl_default"],
     shared_libs: [
         "android.hardware.audio@7.0",
+        "android.hardware.audio@7.0-util",
         "android.hardware.audio.common@7.0",
         "android.hardware.audio.common@7.0-enums",
         "android.hardware.audio.common@7.0-util",
diff --git a/audio/core/all-versions/default/Conversions.cpp b/audio/core/all-versions/default/Conversions.cpp
deleted file mode 100644
index f1752cc..0000000
--- a/audio/core/all-versions/default/Conversions.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2018 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 "core/default/Conversions.h"
-
-#include <stdio.h>
-
-#if MAJOR_VERSION >= 7
-#include <android_audio_policy_configuration_V7_0-enums.h>
-#endif
-#include <HidlUtils.h>
-#include <log/log.h>
-
-namespace android {
-namespace hardware {
-namespace audio {
-namespace CPP_VERSION {
-namespace implementation {
-
-using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
-
-#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) {
-#if MAJOR_VERSION >= 5
-    return HidlUtils::deviceAddressToHal(device, halDeviceType, halDeviceAddress);
-#else
-    return HidlUtils::deviceAddressToHalImpl(device, halDeviceType, halDeviceAddress);
-#endif
-}
-
-status_t deviceAddressFromHal(audio_devices_t halDeviceType, const char* halDeviceAddress,
-                              DeviceAddress* device) {
-#if MAJOR_VERSION >= 5
-    return HidlUtils::deviceAddressFromHal(halDeviceType, halDeviceAddress, device);
-#else
-    return HidlUtils::deviceAddressFromHalImpl(halDeviceType, halDeviceAddress, device);
-#endif
-}
-
-#if MAJOR_VERSION >= 4
-bool halToMicrophoneCharacteristics(MicrophoneInfo* pDst,
-                                    const struct audio_microphone_characteristic_t& src) {
-    bool status = false;
-    if (pDst != NULL) {
-        pDst->deviceId = src.device_id;
-
-        if (deviceAddressFromHal(src.device, src.address, &pDst->deviceAddress) != OK) {
-            return false;
-        }
-        pDst->channelMapping.resize(AUDIO_CHANNEL_COUNT_MAX);
-        for (size_t ch = 0; ch < pDst->channelMapping.size(); ch++) {
-            pDst->channelMapping[ch] = AudioMicrophoneChannelMapping(src.channel_mapping[ch]);
-        }
-        pDst->location = AudioMicrophoneLocation(src.location);
-        pDst->group = (AudioMicrophoneGroup)src.group;
-        pDst->indexInTheGroup = (uint32_t)src.index_in_the_group;
-        pDst->sensitivity = src.sensitivity;
-        pDst->maxSpl = src.max_spl;
-        pDst->minSpl = src.min_spl;
-        pDst->directionality = AudioMicrophoneDirectionality(src.directionality);
-        pDst->frequencyResponse.resize(src.num_frequency_responses);
-        for (size_t k = 0; k < src.num_frequency_responses; k++) {
-            pDst->frequencyResponse[k].frequency = src.frequency_responses[0][k];
-            pDst->frequencyResponse[k].level = src.frequency_responses[1][k];
-        }
-        pDst->position.x = src.geometric_location.x;
-        pDst->position.y = src.geometric_location.y;
-        pDst->position.z = src.geometric_location.z;
-
-        pDst->orientation.x = src.orientation.x;
-        pDst->orientation.y = src.orientation.y;
-        pDst->orientation.z = src.orientation.z;
-
-        status = true;
-    }
-    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
-namespace xsd {
-using namespace ::android::audio::policy::configuration::V7_0;
-}
-
-bool audioInputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_input_flags_t* halFlags) {
-    bool success = true;
-    *halFlags = {};
-    for (const auto& flag : flags) {
-        audio_input_flags_t halFlag;
-        if (!xsd::isUnknownAudioInOutFlag(flag) &&
-            audio_input_flag_from_string(flag.c_str(), &halFlag)) {
-            *halFlags = static_cast<audio_input_flags_t>(*halFlags | halFlag);
-        } else {
-            ALOGE("Unknown audio input flag \"%s\"", flag.c_str());
-            success = false;
-        }
-    }
-    return success;
-}
-
-bool audioOutputFlagsToHal(const hidl_vec<AudioInOutFlag>& flags, audio_output_flags_t* halFlags) {
-    bool success = true;
-    *halFlags = {};
-    for (const auto& flag : flags) {
-        audio_output_flags_t halFlag;
-        if (!xsd::isUnknownAudioInOutFlag(flag) &&
-            audio_output_flag_from_string(flag.c_str(), &halFlag)) {
-            *halFlags = static_cast<audio_output_flags_t>(*halFlags | halFlag);
-        } else {
-            ALOGE("Unknown audio output flag \"%s\"", flag.c_str());
-            success = false;
-        }
-    }
-    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
-}  // namespace CPP_VERSION
-}  // namespace audio
-}  // namespace hardware
-}  // namespace android
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index 7caed44..70a1a4d 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -17,9 +17,7 @@
 #define LOG_TAG "DeviceHAL"
 
 #include "core/default/Device.h"
-#include <HidlUtils.h>
 #include "common/all-versions/default/EffectMap.h"
-#include "core/default/Conversions.h"
 #include "core/default/StreamIn.h"
 #include "core/default/StreamOut.h"
 #include "core/default/Util.h"
@@ -33,6 +31,8 @@
 
 #include <android/log.h>
 
+#include <HidlUtils.h>
+
 namespace android {
 namespace hardware {
 namespace audio {
@@ -160,11 +160,11 @@
     audio_stream_out_t* halStream;
     audio_devices_t halDevice;
     char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
-    if (deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
+    if (CoreUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
         return {Result::INVALID_ARGUMENTS, nullptr};
     }
     audio_output_flags_t halFlags;
-    if (!audioOutputFlagsToHal(flags, &halFlags)) {
+    if (CoreUtils::audioOutputFlagsToHal(flags, &halFlags) != NO_ERROR) {
         return {Result::INVALID_ARGUMENTS, nullptr};
     }
     ALOGV("open_output_stream handle: %d devices: %x flags: %#x "
@@ -195,12 +195,12 @@
     audio_stream_in_t* halStream;
     audio_devices_t halDevice;
     char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
-    if (deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
+    if (CoreUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
         return {Result::INVALID_ARGUMENTS, nullptr};
     }
     audio_input_flags_t halFlags;
     audio_source_t halSource;
-    if (!audioInputFlagsToHal(flags, &halFlags) ||
+    if (CoreUtils::audioInputFlagsToHal(flags, &halFlags) != NO_ERROR ||
         HidlUtils::audioSourceToHal(source, &halSource) != NO_ERROR) {
         return {Result::INVALID_ARGUMENTS, nullptr};
     }
@@ -254,9 +254,12 @@
                                       const SourceMetadata& sourceMetadata,
                                       openOutputStream_cb _hidl_cb) {
 #if MAJOR_VERSION <= 6
-    if (status_t status = sourceMetadataToHal(sourceMetadata, nullptr); status != NO_ERROR) {
+    if (status_t status = CoreUtils::sourceMetadataToHal(sourceMetadata, nullptr);
+        status != NO_ERROR) {
 #else
-    if (status_t status = sourceMetadataToHalV7(sourceMetadata, nullptr); status != NO_ERROR) {
+    if (status_t status = CoreUtils::sourceMetadataToHalV7(sourceMetadata,
+                                                           false /*ignoreNonVendorTags*/, nullptr);
+        status != NO_ERROR) {
 #endif
         _hidl_cb(analyzeStatus("sourceMetadataToHal", status), nullptr, AudioConfig{});
         return Void();
@@ -288,9 +291,11 @@
         return Void();
     }
 #if MAJOR_VERSION <= 6
-    if (status_t status = sinkMetadataToHal(sinkMetadata, nullptr); status != NO_ERROR) {
+    if (status_t status = CoreUtils::sinkMetadataToHal(sinkMetadata, nullptr); status != NO_ERROR) {
 #else
-    if (status_t status = sinkMetadataToHalV7(sinkMetadata, nullptr); status != NO_ERROR) {
+    if (status_t status = CoreUtils::sinkMetadataToHalV7(sinkMetadata,
+                                                         false /*ignoreNonVendorTags*/, nullptr);
+        status != NO_ERROR) {
 #endif
         _hidl_cb(analyzeStatus("sinkMetadataToHal", status), nullptr, AudioConfig{});
         return Void();
@@ -444,7 +449,7 @@
         mDevice->get_microphones(mDevice, &mic_array[0], &actual_mics) == 0) {
         microphones.resize(actual_mics);
         for (size_t i = 0; i < actual_mics; ++i) {
-            halToMicrophoneCharacteristics(&microphones[i], mic_array[i]);
+            (void)CoreUtils::microphoneInfoFromHal(mic_array[i], &microphones[i]);
         }
         retval = Result::OK;
     }
diff --git a/audio/core/all-versions/default/ParametersUtil.cpp b/audio/core/all-versions/default/ParametersUtil.cpp
index 694eb73..4d53645 100644
--- a/audio/core/all-versions/default/ParametersUtil.cpp
+++ b/audio/core/all-versions/default/ParametersUtil.cpp
@@ -15,11 +15,12 @@
  */
 
 #include "core/default/ParametersUtil.h"
-#include "core/default/Conversions.h"
 #include "core/default/Util.h"
 
 #include <system/audio.h>
 
+#include <util/CoreUtils.h>
+
 namespace android {
 namespace hardware {
 namespace audio {
@@ -153,7 +154,7 @@
 Result ParametersUtil::setParam(const char* name, const DeviceAddress& address) {
     audio_devices_t halDeviceType;
     char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
-    if (deviceAddressToHal(address, &halDeviceType, halDeviceAddress) != NO_ERROR) {
+    if (CoreUtils::deviceAddressToHal(address, &halDeviceType, halDeviceAddress) != NO_ERROR) {
         return Result::INVALID_ARGUMENTS;
     }
     AudioParameter params{String8(halDeviceAddress)};
diff --git a/audio/core/all-versions/default/Stream.cpp b/audio/core/all-versions/default/Stream.cpp
index 3f8efd2..7e32573 100644
--- a/audio/core/all-versions/default/Stream.cpp
+++ b/audio/core/all-versions/default/Stream.cpp
@@ -19,7 +19,6 @@
 #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"
 
 #include <inttypes.h>
@@ -30,6 +29,7 @@
 #include <hardware/audio_effect.h>
 #include <media/AudioContainers.h>
 #include <media/TypeConverter.h>
+#include <util/CoreUtils.h>
 
 namespace android {
 namespace hardware {
@@ -373,9 +373,10 @@
     hidl_vec<DeviceAddress> devices;
     if (retval == Result::OK) {
         devices.resize(1);
-        retval = Stream::analyzeStatus("get_devices",
-                                       deviceAddressFromHal(static_cast<audio_devices_t>(halDevice),
-                                                            nullptr, &devices[0]));
+        retval = Stream::analyzeStatus(
+                "get_devices",
+                CoreUtils::deviceAddressFromHal(static_cast<audio_devices_t>(halDevice), nullptr,
+                                                &devices[0]));
     }
     _hidl_cb(retval, devices);
     return Void();
diff --git a/audio/core/all-versions/default/StreamIn.cpp b/audio/core/all-versions/default/StreamIn.cpp
index c670a4d..599f3c3 100644
--- a/audio/core/all-versions/default/StreamIn.cpp
+++ b/audio/core/all-versions/default/StreamIn.cpp
@@ -17,7 +17,6 @@
 #define LOG_TAG "StreamInHAL"
 
 #include "core/default/StreamIn.h"
-#include "core/default/Conversions.h"
 #include "core/default/Util.h"
 #include "common/all-versions/HidlSupport.h"
 
@@ -27,6 +26,7 @@
 #include <HidlUtils.h>
 #include <android/log.h>
 #include <hardware/audio.h>
+#include <util/CoreUtils.h>
 #include <utils/Trace.h>
 #include <cmath>
 #include <memory>
@@ -481,13 +481,15 @@
 Result StreamIn::doUpdateSinkMetadata(const SinkMetadata& sinkMetadata) {
     std::vector<record_track_metadata> halTracks;
 #if MAJOR_VERSION <= 6
-    (void)sinkMetadataToHal(sinkMetadata, &halTracks);
+    (void)CoreUtils::sinkMetadataToHal(sinkMetadata, &halTracks);
 #else
     // Validate whether a conversion to V7 is possible. This is needed
     // to have a consistent behavior of the HAL regardless of the API
     // version of the legacy HAL (and also to be consistent with openInputStream).
     std::vector<record_track_metadata_v7> halTracksV7;
-    if (status_t status = sinkMetadataToHalV7(sinkMetadata, &halTracksV7); status == NO_ERROR) {
+    if (status_t status = CoreUtils::sinkMetadataToHalV7(
+                sinkMetadata, false /*ignoreNonVendorTags*/, &halTracksV7);
+        status == NO_ERROR) {
         halTracks.reserve(halTracksV7.size());
         for (auto metadata_v7 : halTracksV7) {
             halTracks.push_back(std::move(metadata_v7.base));
@@ -507,7 +509,9 @@
 #if MAJOR_VERSION >= 7
 Result StreamIn::doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata) {
     std::vector<record_track_metadata_v7> halTracks;
-    if (status_t status = sinkMetadataToHalV7(sinkMetadata, &halTracks); status != NO_ERROR) {
+    if (status_t status = CoreUtils::sinkMetadataToHalV7(sinkMetadata,
+                                                         false /*ignoreNonVendorTags*/, &halTracks);
+        status != NO_ERROR) {
         return Stream::analyzeStatus("sinkMetadataToHal", status);
     }
     const sink_metadata_v7_t halMetadata = {
@@ -553,7 +557,7 @@
         mStream->get_active_microphones(mStream, &mic_array[0], &actual_mics) == 0) {
         microphones.resize(actual_mics);
         for (size_t i = 0; i < actual_mics; ++i) {
-            halToMicrophoneCharacteristics(&microphones[i], mic_array[i]);
+            (void)CoreUtils::microphoneInfoFromHal(mic_array[i], &microphones[i]);
         }
         retval = Result::OK;
     }
diff --git a/audio/core/all-versions/default/StreamOut.cpp b/audio/core/all-versions/default/StreamOut.cpp
index 233575c..a089f6b 100644
--- a/audio/core/all-versions/default/StreamOut.cpp
+++ b/audio/core/all-versions/default/StreamOut.cpp
@@ -17,7 +17,6 @@
 #define LOG_TAG "StreamOutHAL"
 
 #include "core/default/StreamOut.h"
-#include "core/default/Conversions.h"
 #include "core/default/Util.h"
 
 //#define LOG_NDEBUG 0
@@ -30,6 +29,7 @@
 #include <HidlUtils.h>
 #include <android/log.h>
 #include <hardware/audio.h>
+#include <util/CoreUtils.h>
 #include <utils/Trace.h>
 
 namespace android {
@@ -589,13 +589,15 @@
 Result StreamOut::doUpdateSourceMetadata(const SourceMetadata& sourceMetadata) {
     std::vector<playback_track_metadata_t> halTracks;
 #if MAJOR_VERSION <= 6
-    (void)sourceMetadataToHal(sourceMetadata, &halTracks);
+    (void)CoreUtils::sourceMetadataToHal(sourceMetadata, &halTracks);
 #else
     // Validate whether a conversion to V7 is possible. This is needed
     // to have a consistent behavior of the HAL regardless of the API
     // version of the legacy HAL (and also to be consistent with openOutputStream).
     std::vector<playback_track_metadata_v7> halTracksV7;
-    if (status_t status = sourceMetadataToHalV7(sourceMetadata, &halTracksV7); status == NO_ERROR) {
+    if (status_t status = CoreUtils::sourceMetadataToHalV7(
+                sourceMetadata, false /*ignoreNonVendorTags*/, &halTracksV7);
+        status == NO_ERROR) {
         halTracks.reserve(halTracksV7.size());
         for (auto metadata_v7 : halTracksV7) {
             halTracks.push_back(std::move(metadata_v7.base));
@@ -615,7 +617,9 @@
 #if MAJOR_VERSION >= 7
 Result StreamOut::doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata) {
     std::vector<playback_track_metadata_v7> halTracks;
-    if (status_t status = sourceMetadataToHalV7(sourceMetadata, &halTracks); status != NO_ERROR) {
+    if (status_t status = CoreUtils::sourceMetadataToHalV7(
+                sourceMetadata, false /*ignoreNonVendorTags*/, &halTracks);
+        status != NO_ERROR) {
         return Stream::analyzeStatus("sourceMetadataToHal", status);
     }
     const source_metadata_v7_t halMetadata = {
diff --git a/audio/core/all-versions/default/TEST_MAPPING b/audio/core/all-versions/default/TEST_MAPPING
new file mode 100644
index 0000000..d53c97a
--- /dev/null
+++ b/audio/core/all-versions/default/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "android.hardware.audio@7.0-util_tests"
+    }
+  ]
+}
diff --git a/audio/core/all-versions/default/include/core/default/Conversions.h b/audio/core/all-versions/default/include/core/default/Conversions.h
deleted file mode 100644
index 61720d5..0000000
--- a/audio/core/all-versions/default/include/core/default/Conversions.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef ANDROID_HARDWARE_AUDIO_CONVERSIONS_H_
-#define ANDROID_HARDWARE_AUDIO_CONVERSIONS_H_
-
-#include PATH(android/hardware/audio/FILE_VERSION/types.h)
-
-#include <string>
-
-#include <system/audio.h>
-
-#include <VersionUtils.h>
-
-namespace android {
-namespace hardware {
-namespace audio {
-namespace CPP_VERSION {
-namespace implementation {
-
-using ::android::hardware::hidl_vec;
-using namespace ::android::hardware::audio::common::CPP_VERSION;
-using namespace ::android::hardware::audio::CPP_VERSION;
-
-status_t deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
-                            char* halDeviceAddress);
-status_t deviceAddressFromHal(audio_devices_t halDeviceType, const char* halDeviceAddress,
-                              DeviceAddress* device);
-
-#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
-using AudioInputFlags =
-        ::android::hardware::audio::common::CPP_VERSION::implementation::AudioInputFlagBitfield;
-using AudioOutputFlags =
-        ::android::hardware::audio::common::CPP_VERSION::implementation::AudioOutputFlagBitfield;
-
-inline bool audioInputFlagsToHal(AudioInputFlags flags, audio_input_flags_t* halFlags) {
-    *halFlags = static_cast<audio_input_flags_t>(flags);
-    return true;
-}
-
-inline bool audioOutputFlagsToHal(AudioOutputFlags flags, audio_output_flags_t* halFlags) {
-    *halFlags = static_cast<audio_output_flags_t>(flags);
-    return true;
-}
-#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
-}  // namespace CPP_VERSION
-}  // namespace audio
-}  // namespace hardware
-}  // namespace android
-
-#endif  // ANDROID_HARDWARE_AUDIO_CONVERSIONS_H_
diff --git a/audio/core/all-versions/default/include/core/default/Device.h b/audio/core/all-versions/default/include/core/default/Device.h
index 2a4d226..5851fc9 100644
--- a/audio/core/all-versions/default/include/core/default/Device.h
+++ b/audio/core/all-versions/default/include/core/default/Device.h
@@ -31,6 +31,7 @@
 #include <hidl/MQDescriptor.h>
 
 #include <VersionUtils.h>
+#include <util/CoreUtils.h>
 
 namespace android {
 namespace hardware {
@@ -43,17 +44,10 @@
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
-#if MAJOR_VERSION <= 6
-using AudioInputFlags =
-        ::android::hardware::audio::common::CPP_VERSION::implementation::AudioInputFlagBitfield;
-using AudioOutputFlags =
-        ::android::hardware::audio::common::CPP_VERSION::implementation::AudioOutputFlagBitfield;
-#else
-using AudioInputFlags = hidl_vec<::android::hardware::audio::CPP_VERSION::AudioInOutFlag>;
-using AudioOutputFlags = hidl_vec<::android::hardware::audio::CPP_VERSION::AudioInOutFlag>;
-#endif
 using namespace ::android::hardware::audio::common::CPP_VERSION;
 using namespace ::android::hardware::audio::CPP_VERSION;
+using AudioInputFlags = CoreUtils::AudioInputFlags;
+using AudioOutputFlags = CoreUtils::AudioOutputFlags;
 
 struct Device : public IDevice, public ParametersUtil {
     explicit Device(audio_hw_device_t* device);
diff --git a/audio/core/all-versions/default/util/Android.bp b/audio/core/all-versions/default/util/Android.bp
new file mode 100644
index 0000000..447184b
--- /dev/null
+++ b/audio/core/all-versions/default/util/Android.bp
@@ -0,0 +1,139 @@
+cc_defaults {
+    name: "android.hardware.audio-util_default",
+    defaults: ["hidl_defaults"],
+
+    vendor_available: true,
+
+    export_include_dirs: ["include"],
+
+    srcs: [
+        "CoreUtils.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libhidlbase",
+        "android.hardware.audio.common-util",
+    ],
+    export_shared_lib_headers: [
+        "android.hardware.audio.common-util",
+    ],
+
+    header_libs: [
+        "libaudio_system_headers",
+        "libhardware_headers",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio@2.0-util",
+    defaults: ["android.hardware.audio-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@2.0",
+        "android.hardware.audio.common@2.0-util",
+        "android.hardware.audio@2.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=2",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio@4.0-util",
+    defaults: ["android.hardware.audio-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@4.0",
+        "android.hardware.audio.common@4.0-util",
+        "android.hardware.audio@4.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=4",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio@5.0-util",
+    defaults: ["android.hardware.audio-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@5.0",
+        "android.hardware.audio.common@5.0-util",
+        "android.hardware.audio@5.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=5",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio@6.0-util",
+    defaults: ["android.hardware.audio-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@6.0",
+        "android.hardware.audio.common@6.0-util",
+        "android.hardware.audio@6.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=6",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library {
+    name: "android.hardware.audio@7.0-util",
+    defaults: ["android.hardware.audio-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.common@7.0-util",
+        "android.hardware.audio@7.0",
+        "libbase",
+        "libxml2",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-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@7.0-util_tests",
+    defaults: ["android.hardware.audio-util_default"],
+
+    srcs: ["tests/coreutils_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",
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.common@7.0-util",
+        "android.hardware.audio@7.0",
+        "android.hardware.audio@7.0-util",
+    ],
+
+    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/core/all-versions/default/util/CoreUtils.cpp b/audio/core/all-versions/default/util/CoreUtils.cpp
new file mode 100644
index 0000000..14f76f3
--- /dev/null
+++ b/audio/core/all-versions/default/util/CoreUtils.cpp
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#if MAJOR_VERSION >= 7
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#endif
+#include <HidlUtils.h>
+#include <log/log.h>
+
+#include "util/CoreUtils.h"
+
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+#if MAJOR_VERSION >= 7
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+#endif
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace CPP_VERSION {
+namespace implementation {
+
+#define CONVERT_CHECKED(expr, result)                   \
+    if (status_t status = (expr); status != NO_ERROR) { \
+        result = status;                                \
+    }
+
+status_t CoreUtils::deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+                                       char* halDeviceAddress) {
+#if MAJOR_VERSION >= 5
+    return HidlUtils::deviceAddressToHal(device, halDeviceType, halDeviceAddress);
+#else
+    return HidlUtils::deviceAddressToHalImpl(device, halDeviceType, halDeviceAddress);
+#endif
+}
+
+status_t CoreUtils::deviceAddressFromHal(audio_devices_t halDeviceType,
+                                         const char* halDeviceAddress, DeviceAddress* device) {
+#if MAJOR_VERSION >= 5
+    return HidlUtils::deviceAddressFromHal(halDeviceType, halDeviceAddress, device);
+#else
+    return HidlUtils::deviceAddressFromHalImpl(halDeviceType, halDeviceAddress, device);
+#endif
+}
+
+#if MAJOR_VERSION >= 4
+status_t CoreUtils::microphoneInfoFromHal(
+        const struct audio_microphone_characteristic_t& halMicInfo, MicrophoneInfo* micInfo) {
+    status_t result = NO_ERROR;
+    micInfo->deviceId = halMicInfo.device_id;
+    CONVERT_CHECKED(
+            deviceAddressFromHal(halMicInfo.device, halMicInfo.address, &micInfo->deviceAddress),
+            result);
+    size_t chCount;
+    for (chCount = 0; chCount < AUDIO_CHANNEL_COUNT_MAX; ++chCount) {
+        if (halMicInfo.channel_mapping[chCount] == AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED) {
+            break;
+        }
+    }
+    micInfo->channelMapping.resize(chCount);
+    for (size_t ch = 0; ch < micInfo->channelMapping.size(); ch++) {
+        micInfo->channelMapping[ch] = AudioMicrophoneChannelMapping(halMicInfo.channel_mapping[ch]);
+    }
+    micInfo->location = AudioMicrophoneLocation(halMicInfo.location);
+    micInfo->group = AudioMicrophoneGroup(halMicInfo.group);
+    micInfo->indexInTheGroup = static_cast<uint32_t>(halMicInfo.index_in_the_group);
+    micInfo->sensitivity = halMicInfo.sensitivity;
+    micInfo->maxSpl = halMicInfo.max_spl;
+    micInfo->minSpl = halMicInfo.min_spl;
+    micInfo->directionality = AudioMicrophoneDirectionality(halMicInfo.directionality);
+    micInfo->frequencyResponse.resize(halMicInfo.num_frequency_responses);
+    for (size_t k = 0; k < halMicInfo.num_frequency_responses; k++) {
+        micInfo->frequencyResponse[k].frequency = halMicInfo.frequency_responses[0][k];
+        micInfo->frequencyResponse[k].level = halMicInfo.frequency_responses[1][k];
+    }
+    micInfo->position.x = halMicInfo.geometric_location.x;
+    micInfo->position.y = halMicInfo.geometric_location.y;
+    micInfo->position.z = halMicInfo.geometric_location.z;
+    micInfo->orientation.x = halMicInfo.orientation.x;
+    micInfo->orientation.y = halMicInfo.orientation.y;
+    micInfo->orientation.z = halMicInfo.orientation.z;
+    return result;
+}
+
+status_t CoreUtils::microphoneInfoToHal(const MicrophoneInfo& micInfo,
+                                        audio_microphone_characteristic_t* halMicInfo) {
+    status_t result = NO_ERROR;
+    strncpy(halMicInfo->device_id, micInfo.deviceId.c_str(), AUDIO_MICROPHONE_ID_MAX_LEN);
+    halMicInfo->device_id[AUDIO_MICROPHONE_ID_MAX_LEN - 1] = '\0';
+    if (micInfo.deviceId.size() >= AUDIO_MICROPHONE_ID_MAX_LEN) {
+        ALOGE("HIDL MicrophoneInfo device ID is too long: %zu", micInfo.deviceId.size());
+        result = BAD_VALUE;
+    }
+    CONVERT_CHECKED(
+            deviceAddressToHal(micInfo.deviceAddress, &halMicInfo->device, halMicInfo->address),
+            result);
+    if (micInfo.channelMapping.size() > AUDIO_CHANNEL_COUNT_MAX) {
+        ALOGE("HIDL MicrophoneInfo has too many channelMapping elements: %zu",
+              micInfo.channelMapping.size());
+        result = BAD_VALUE;
+    }
+    size_t ch;
+    for (ch = 0; ch < micInfo.channelMapping.size() && ch < AUDIO_CHANNEL_COUNT_MAX; ch++) {
+        halMicInfo->channel_mapping[ch] =
+                static_cast<audio_microphone_channel_mapping_t>(micInfo.channelMapping[ch]);
+    }
+    for (; ch < AUDIO_CHANNEL_COUNT_MAX; ch++) {
+        halMicInfo->channel_mapping[ch] = AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED;
+    }
+    halMicInfo->location = static_cast<audio_microphone_location_t>(micInfo.location);
+    halMicInfo->group = static_cast<audio_microphone_group_t>(micInfo.group);
+    halMicInfo->index_in_the_group = static_cast<unsigned int>(micInfo.indexInTheGroup);
+    halMicInfo->sensitivity = micInfo.sensitivity;
+    halMicInfo->max_spl = micInfo.maxSpl;
+    halMicInfo->min_spl = micInfo.minSpl;
+    halMicInfo->directionality =
+            static_cast<audio_microphone_directionality_t>(micInfo.directionality);
+    halMicInfo->num_frequency_responses =
+            static_cast<unsigned int>(micInfo.frequencyResponse.size());
+    if (halMicInfo->num_frequency_responses > AUDIO_MICROPHONE_MAX_FREQUENCY_RESPONSES) {
+        ALOGE("HIDL MicrophoneInfo has too many frequency responses: %u",
+              halMicInfo->num_frequency_responses);
+        halMicInfo->num_frequency_responses = AUDIO_MICROPHONE_MAX_FREQUENCY_RESPONSES;
+        result = BAD_VALUE;
+    }
+    for (size_t k = 0; k < halMicInfo->num_frequency_responses; k++) {
+        halMicInfo->frequency_responses[0][k] = micInfo.frequencyResponse[k].frequency;
+        halMicInfo->frequency_responses[1][k] = micInfo.frequencyResponse[k].level;
+    }
+    halMicInfo->geometric_location.x = micInfo.position.x;
+    halMicInfo->geometric_location.y = micInfo.position.y;
+    halMicInfo->geometric_location.z = micInfo.position.z;
+    halMicInfo->orientation.x = micInfo.orientation.x;
+    halMicInfo->orientation.y = micInfo.orientation.y;
+    halMicInfo->orientation.z = micInfo.orientation.z;
+    return result;
+}
+
+status_t CoreUtils::sinkMetadataFromHal(const std::vector<record_track_metadata_t>& halTracks,
+                                        SinkMetadata* sinkMetadata) {
+    status_t result = NO_ERROR;
+    sinkMetadata->tracks.resize(halTracks.size());
+    for (size_t i = 0; i < sinkMetadata->tracks.size(); ++i) {
+        const auto& halTrackMetadata = halTracks[i];
+        RecordTrackMetadata trackMetadata{};
+        CONVERT_CHECKED(
+                HidlUtils::audioSourceFromHal(halTrackMetadata.source, &trackMetadata.source),
+                result);
+        trackMetadata.gain = halTrackMetadata.gain;
+#if MAJOR_VERSION >= 5
+        if (halTrackMetadata.dest_device != AUDIO_DEVICE_NONE) {
+            DeviceAddress address;
+            if (status_t status =
+                        deviceAddressFromHal(halTrackMetadata.dest_device,
+                                             halTrackMetadata.dest_device_address, &address);
+                status == NO_ERROR) {
+                trackMetadata.destination.device(std::move(address));
+            } else {
+                result = status;
+            }
+        }
+#if MAJOR_VERSION >= 7
+        trackMetadata.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+#endif
+#endif  // MAJOR_VERSION >= 5
+        sinkMetadata->tracks[i] = std::move(trackMetadata);
+    }
+    return result;
+}
+
+status_t CoreUtils::sinkMetadataFromHalV7(const std::vector<record_track_metadata_v7_t>& halTracks,
+                                          bool ignoreNonVendorTags, SinkMetadata* sinkMetadata) {
+    std::vector<record_track_metadata_t> bases;
+    bases.reserve(halTracks.size());
+    std::transform(halTracks.begin(), halTracks.end(), std::back_inserter(bases),
+                   [](const record_track_metadata_v7_t& src) -> record_track_metadata_t {
+                       record_track_metadata_t result;
+                       record_track_metadata_from_v7(&result, &src);
+                       return result;
+                   });
+    status_t result = sinkMetadataFromHal(bases, sinkMetadata);
+#if MAJOR_VERSION >= 7
+    for (size_t i = 0; i < halTracks.size(); ++i) {
+        auto& trackMetadata = sinkMetadata->tracks[i];
+        const auto& halTrackMetadata = halTracks[i];
+        CONVERT_CHECKED(
+                HidlUtils::audioChannelMaskFromHal(halTrackMetadata.channel_mask, true /*isInput*/,
+                                                   &trackMetadata.channelMask),
+                result);
+        std::vector<std::string> strTags = HidlUtils::splitAudioTags(halTrackMetadata.tags);
+        if (ignoreNonVendorTags) {
+            strTags = HidlUtils::filterOutNonVendorTags(strTags);
+        }
+        CONVERT_CHECKED(HidlUtils::audioTagsFromHal(strTags, &trackMetadata.tags), result);
+    }
+#else
+    (void)ignoreNonVendorTags;
+#endif
+    return result;
+}
+
+status_t CoreUtils::sinkMetadataToHal(const SinkMetadata& sinkMetadata,
+                                      std::vector<record_track_metadata_t>* halTracks) {
+    status_t result = NO_ERROR;
+    if (halTracks != nullptr) {
+        halTracks->reserve(sinkMetadata.tracks.size());
+    }
+    for (auto& trackMetadata : sinkMetadata.tracks) {
+        record_track_metadata halTrackMetadata{.gain = trackMetadata.gain};
+        CONVERT_CHECKED(HidlUtils::audioSourceToHal(trackMetadata.source, &halTrackMetadata.source),
+                        result);
+#if MAJOR_VERSION >= 5
+        if (trackMetadata.destination.getDiscriminator() ==
+            RecordTrackMetadata::Destination::hidl_discriminator::device) {
+            CONVERT_CHECKED(deviceAddressToHal(trackMetadata.destination.device(),
+                                               &halTrackMetadata.dest_device,
+                                               halTrackMetadata.dest_device_address),
+                            result);
+        }
+#endif
+        if (halTracks != nullptr) {
+            halTracks->push_back(std::move(halTrackMetadata));
+        }
+    }
+    return result;
+}
+
+status_t CoreUtils::sinkMetadataToHalV7(const SinkMetadata& sinkMetadata, bool ignoreNonVendorTags,
+                                        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(sinkMetadata.tracks.size());
+    }
+    for (size_t i = 0; i < sinkMetadata.tracks.size(); ++i) {
+        record_track_metadata_v7_t halTrackMetadata;
+        if (halTracks != nullptr) {
+            record_track_metadata_to_v7(&halTrackMetadata, &bases[i]);
+        }
+#if MAJOR_VERSION >= 7
+        const auto& trackMetadata = sinkMetadata.tracks[i];
+        CONVERT_CHECKED(HidlUtils::audioChannelMaskToHal(trackMetadata.channelMask,
+                                                         &halTrackMetadata.channel_mask),
+                        result);
+        if (ignoreNonVendorTags) {
+            CONVERT_CHECKED(
+                    HidlUtils::audioTagsToHal(HidlUtils::filterOutNonVendorTags(trackMetadata.tags),
+                                              halTrackMetadata.tags),
+                    result);
+        } else {
+            CONVERT_CHECKED(HidlUtils::audioTagsToHal(trackMetadata.tags, halTrackMetadata.tags),
+                            result);
+        }
+#else
+        (void)ignoreNonVendorTags;
+#endif
+        if (halTracks != nullptr) {
+            halTracks->push_back(std::move(halTrackMetadata));
+        }
+    }
+    return result;
+}
+
+status_t CoreUtils::sourceMetadataFromHal(const std::vector<playback_track_metadata_t>& halTracks,
+                                          SourceMetadata* sourceMetadata) {
+    status_t result = NO_ERROR;
+    sourceMetadata->tracks.resize(halTracks.size());
+    for (size_t i = 0; i < sourceMetadata->tracks.size(); ++i) {
+        const auto& halTrackMetadata = halTracks[i];
+        PlaybackTrackMetadata trackMetadata{};
+        CONVERT_CHECKED(HidlUtils::audioUsageFromHal(halTrackMetadata.usage, &trackMetadata.usage),
+                        result);
+        CONVERT_CHECKED(HidlUtils::audioContentTypeFromHal(halTrackMetadata.content_type,
+                                                           &trackMetadata.contentType),
+                        result);
+        trackMetadata.gain = halTrackMetadata.gain;
+#if MAJOR_VERSION >= 7
+        trackMetadata.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+#endif
+        sourceMetadata->tracks[i] = std::move(trackMetadata);
+    }
+    return result;
+}
+
+status_t CoreUtils::sourceMetadataFromHalV7(
+        const std::vector<playback_track_metadata_v7_t>& halTracks, bool ignoreNonVendorTags,
+        SourceMetadata* sourceMetadata) {
+    std::vector<playback_track_metadata_t> bases;
+    bases.reserve(halTracks.size());
+    std::transform(halTracks.begin(), halTracks.end(), std::back_inserter(bases),
+                   [](const playback_track_metadata_v7_t& src) -> playback_track_metadata_t {
+                       playback_track_metadata_t result;
+                       playback_track_metadata_from_v7(&result, &src);
+                       return result;
+                   });
+    status_t result = sourceMetadataFromHal(bases, sourceMetadata);
+#if MAJOR_VERSION >= 7
+    for (size_t i = 0; i < halTracks.size(); ++i) {
+        auto& trackMetadata = sourceMetadata->tracks[i];
+        const auto& halTrackMetadata = halTracks[i];
+        CONVERT_CHECKED(
+                HidlUtils::audioChannelMaskFromHal(halTrackMetadata.channel_mask, false /*isInput*/,
+                                                   &trackMetadata.channelMask),
+                result);
+        std::vector<std::string> strTags = HidlUtils::splitAudioTags(halTrackMetadata.tags);
+        if (ignoreNonVendorTags) {
+            strTags = HidlUtils::filterOutNonVendorTags(strTags);
+        }
+        CONVERT_CHECKED(HidlUtils::audioTagsFromHal(strTags, &trackMetadata.tags), result);
+    }
+#else
+    (void)ignoreNonVendorTags;
+#endif
+    return result;
+}
+
+status_t CoreUtils::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& trackMetadata : sourceMetadata.tracks) {
+        playback_track_metadata_t halTrackMetadata{.gain = trackMetadata.gain};
+        CONVERT_CHECKED(HidlUtils::audioUsageToHal(trackMetadata.usage, &halTrackMetadata.usage),
+                        result);
+        CONVERT_CHECKED(HidlUtils::audioContentTypeToHal(trackMetadata.contentType,
+                                                         &halTrackMetadata.content_type),
+                        result);
+        if (halTracks != nullptr) {
+            halTracks->push_back(std::move(halTrackMetadata));
+        }
+    }
+    return result;
+}
+
+status_t CoreUtils::sourceMetadataToHalV7(const SourceMetadata& sourceMetadata,
+                                          bool ignoreNonVendorTags,
+                                          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(sourceMetadata.tracks.size());
+    }
+    for (size_t i = 0; i < sourceMetadata.tracks.size(); ++i) {
+        playback_track_metadata_v7_t halTrackMetadata;
+        if (halTracks != nullptr) {
+            playback_track_metadata_to_v7(&halTrackMetadata, &bases[i]);
+        }
+#if MAJOR_VERSION >= 7
+        const auto& trackMetadata = sourceMetadata.tracks[i];
+        CONVERT_CHECKED(HidlUtils::audioChannelMaskToHal(trackMetadata.channelMask,
+                                                         &halTrackMetadata.channel_mask),
+                        result);
+        if (ignoreNonVendorTags) {
+            CONVERT_CHECKED(
+                    HidlUtils::audioTagsToHal(HidlUtils::filterOutNonVendorTags(trackMetadata.tags),
+                                              halTrackMetadata.tags),
+                    result);
+        } else {
+            CONVERT_CHECKED(HidlUtils::audioTagsToHal(trackMetadata.tags, halTrackMetadata.tags),
+                            result);
+        }
+#else
+        (void)ignoreNonVendorTags;
+#endif
+        if (halTracks != nullptr) {
+            halTracks->push_back(std::move(halTrackMetadata));
+        }
+    }
+    return result;
+}
+#endif  // MAJOR_VERSION >= 4
+
+#if MAJOR_VERSION >= 7
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+status_t CoreUtils::audioInputFlagsFromHal(audio_input_flags_t halFlagMask,
+                                           AudioInputFlags* flags) {
+    status_t status = NO_ERROR;
+    std::vector<AudioInOutFlag> result;
+    for (uint32_t bit = 0; halFlagMask != 0 && bit < sizeof(audio_input_flags_t) * 8; ++bit) {
+        audio_input_flags_t flag = static_cast<audio_input_flags_t>(1u << bit);
+        if ((flag & halFlagMask) == flag) {
+            AudioInOutFlag flagStr = audio_input_flag_to_string(flag);
+            if (!flagStr.empty() && !xsd::isUnknownAudioInOutFlag(flagStr)) {
+                result.push_back(flagStr);
+            } else {
+                ALOGE("Unknown audio input flag value 0x%X", flag);
+                status = BAD_VALUE;
+            }
+            halFlagMask = static_cast<audio_input_flags_t>(halFlagMask & ~flag);
+        }
+    }
+    *flags = result;
+    return status;
+}
+
+status_t CoreUtils::audioInputFlagsToHal(const AudioInputFlags& flags,
+                                         audio_input_flags_t* halFlagMask) {
+    status_t status = NO_ERROR;
+    *halFlagMask = {};
+    for (const auto& flag : flags) {
+        audio_input_flags_t halFlag;
+        if (!xsd::isUnknownAudioInOutFlag(flag) &&
+            audio_input_flag_from_string(flag.c_str(), &halFlag)) {
+            *halFlagMask = static_cast<audio_input_flags_t>(*halFlagMask | halFlag);
+        } else {
+            ALOGE("Unknown audio input flag \"%s\"", flag.c_str());
+            status = BAD_VALUE;
+        }
+    }
+    return status;
+}
+
+status_t CoreUtils::audioOutputFlagsFromHal(audio_output_flags_t halFlagMask,
+                                            AudioOutputFlags* flags) {
+    status_t status = NO_ERROR;
+    std::vector<AudioInOutFlag> result;
+    for (uint32_t bit = 0; halFlagMask != 0 && bit < sizeof(audio_output_flags_t) * 8; ++bit) {
+        audio_output_flags_t flag = static_cast<audio_output_flags_t>(1u << bit);
+        if ((flag & halFlagMask) == flag) {
+            AudioInOutFlag flagStr = audio_output_flag_to_string(flag);
+            if (!flagStr.empty() && !xsd::isUnknownAudioInOutFlag(flagStr)) {
+                result.push_back(flagStr);
+            } else {
+                ALOGE("Unknown audio output flag value 0x%X", flag);
+                status = BAD_VALUE;
+            }
+            halFlagMask = static_cast<audio_output_flags_t>(halFlagMask & ~flag);
+        }
+    }
+    *flags = result;
+    return status;
+}
+
+status_t CoreUtils::audioOutputFlagsToHal(const AudioOutputFlags& flags,
+                                          audio_output_flags_t* halFlagMask) {
+    status_t status = NO_ERROR;
+    *halFlagMask = {};
+    for (const auto& flag : flags) {
+        audio_output_flags_t halFlag;
+        if (!xsd::isUnknownAudioInOutFlag(flag) &&
+            audio_output_flag_from_string(flag.c_str(), &halFlag)) {
+            *halFlagMask = static_cast<audio_output_flags_t>(*halFlagMask | halFlag);
+        } else {
+            ALOGE("Unknown audio output flag \"%s\"", flag.c_str());
+            status = BAD_VALUE;
+        }
+    }
+    return status;
+}
+#endif
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/core/all-versions/default/util/include/util/CoreUtils.h b/audio/core/all-versions/default/util/include/util/CoreUtils.h
new file mode 100644
index 0000000..1e5272a
--- /dev/null
+++ b/audio/core/all-versions/default/util/include/util/CoreUtils.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+// clang-format off
+
+#include <vector>
+
+#include <system/audio.h>
+
+#include <common/all-versions/VersionUtils.h>
+#include <VersionUtils.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace CPP_VERSION {
+namespace implementation {
+
+using ::android::hardware::audio::common::utils::EnumBitfield;
+using ::android::hardware::hidl_vec;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+
+struct CoreUtils {
+    // Note: the converters for DeviceAddress have to be in CoreUtils for HAL V4
+    // because DeviceAddress used to be defined in the core HAL. For V5 and above
+    // these functions simply delegate to HidlUtils.
+    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);
+#if MAJOR_VERSION >= 4
+    static status_t microphoneInfoFromHal(const struct audio_microphone_characteristic_t& halMicInfo,
+            MicrophoneInfo* micInfo);
+    static status_t microphoneInfoToHal(const MicrophoneInfo& micInfo, audio_microphone_characteristic_t* halMicInfo);
+    // Note: {Sink|Source}Metadata types are defined in 'common' (since V5), so they can be used
+    // by the BT HAL. However, the converters are defined here, not in HidlUtils to avoid adding
+    // conditionals to handle V4. The converters are only used by 'core' HAL anyways.
+    static status_t sinkMetadataFromHal(const std::vector<record_track_metadata_t>& halTracks,
+            SinkMetadata* sinkMetadata);
+    static status_t sinkMetadataFromHalV7(const std::vector<record_track_metadata_v7_t>& halTracks,
+            bool ignoreNonVendorTags, SinkMetadata* sinkMetadata);
+    static status_t sinkMetadataToHal(const SinkMetadata& sinkMetadata,
+            std::vector<record_track_metadata_t>* halTracks);
+    static status_t sinkMetadataToHalV7(const SinkMetadata& sinkMetadata, bool ignoreNonVendorTags,
+            std::vector<record_track_metadata_v7_t>* halTracks);
+    static status_t sourceMetadataFromHal(const std::vector<playback_track_metadata_t>& halTracks,
+            SourceMetadata* sourceMetadata);
+    static status_t sourceMetadataFromHalV7(const std::vector<playback_track_metadata_v7_t>& halTracks,
+            bool ignoreNonVendorTags, SourceMetadata* sourceMetadata);
+    static status_t sourceMetadataToHal(const SourceMetadata& sourceMetadata,
+            std::vector<playback_track_metadata_t>* halTracks);
+    static status_t sourceMetadataToHalV7(const SourceMetadata& sourceMetadata, bool ignoreNonVendorTags,
+            std::vector<playback_track_metadata_v7_t>* halTracks);
+#endif
+
+#if MAJOR_VERSION <= 6
+    using AudioInputFlags =
+            ::android::hardware::audio::common::CPP_VERSION::implementation::AudioInputFlagBitfield;
+    using AudioOutputFlags =
+            ::android::hardware::audio::common::CPP_VERSION::implementation::AudioOutputFlagBitfield;
+    static inline status_t audioInputFlagsFromHal(audio_input_flags_t halFlagMask, AudioInputFlags* flags) {
+        *flags = EnumBitfield<AudioInputFlag>(halFlagMask);
+        return NO_ERROR;
+    }
+    static inline status_t audioInputFlagsToHal(AudioInputFlags flags, audio_input_flags_t* halFlagMask) {
+        *halFlagMask = static_cast<audio_input_flags_t>(flags);
+        return NO_ERROR;
+    }
+    static inline status_t audioOutputFlagsFromHal(audio_output_flags_t halFlagMask, AudioOutputFlags* flags) {
+        *flags = EnumBitfield<AudioOutputFlag>(halFlagMask);
+        return NO_ERROR;
+    }
+    static inline status_t audioOutputFlagsToHal(AudioOutputFlags flags, audio_output_flags_t* halFlagMask) {
+        *halFlagMask = static_cast<audio_output_flags_t>(flags);
+        return NO_ERROR;
+    }
+#else
+    using AudioInputFlags = hidl_vec<::android::hardware::audio::CPP_VERSION::AudioInOutFlag>;
+    using AudioOutputFlags = hidl_vec<::android::hardware::audio::CPP_VERSION::AudioInOutFlag>;
+    static status_t audioInputFlagsFromHal(audio_input_flags_t halFlagMask, AudioInputFlags* flags);
+    static status_t audioInputFlagsToHal(const AudioInputFlags& flags, audio_input_flags_t* halFlagMask);
+    static status_t audioOutputFlagsFromHal(audio_output_flags_t halFlagMask, AudioOutputFlags* flags);
+    static status_t audioOutputFlagsToHal(const AudioOutputFlags& flags, audio_output_flags_t* halFlagMask);
+#endif
+
+};
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/core/all-versions/default/util/tests/coreutils_tests.cpp b/audio/core/all-versions/default/util/tests/coreutils_tests.cpp
new file mode 100644
index 0000000..0c18482
--- /dev/null
+++ b/audio/core/all-versions/default/util/tests/coreutils_tests.cpp
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2021 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 <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#define LOG_TAG "CoreUtils_Test"
+#include <log/log.h>
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <system/audio.h>
+#include <util/CoreUtils.h>
+#include <xsdc/XsdcSupport.h>
+
+using namespace android;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::audio::CPP_VERSION::implementation::CoreUtils;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+static constexpr audio_channel_mask_t kInvalidHalChannelMask = AUDIO_CHANNEL_INVALID;
+static constexpr audio_content_type_t kInvalidHalContentType =
+        static_cast<audio_content_type_t>(0xFFFFFFFFU);
+static constexpr audio_devices_t kInvalidHalDevice = static_cast<audio_devices_t>(0xFFFFFFFFU);
+static constexpr audio_input_flags_t kInvalidInputFlags =
+        static_cast<audio_input_flags_t>(0xFFFFFFFFU);
+static constexpr audio_output_flags_t kInvalidOutputFlags =
+        static_cast<audio_output_flags_t>(0xFFFFFFFFU);
+// AUDIO_SOURCE_INVALID is framework-only.
+static constexpr audio_source_t kInvalidHalSource = static_cast<audio_source_t>(-1);
+static constexpr audio_usage_t kInvalidHalUsage = static_cast<audio_usage_t>(0xFFFFFFFFU);
+
+static bool isInputFlag(xsd::AudioInOutFlag flag) {
+    return toString(flag).find("_INPUT_FLAG_") != std::string::npos;
+}
+
+static bool isOutputFlag(xsd::AudioInOutFlag flag) {
+    return toString(flag).find("_OUTPUT_FLAG_") != std::string::npos;
+}
+
+TEST(CoreUtils, ConvertInvalidInputFlagMask) {
+    CoreUtils::AudioInputFlags invalid;
+    EXPECT_EQ(BAD_VALUE, CoreUtils::audioInputFlagsFromHal(kInvalidInputFlags, &invalid));
+    audio_input_flags_t halInvalid;
+    invalid.resize(1);
+    invalid[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::audioInputFlagsToHal(invalid, &halInvalid));
+}
+
+TEST(CoreUtils, ConvertInputFlagMask) {
+    CoreUtils::AudioInputFlags emptyInputFlags;
+    audio_input_flags_t halEmptyInputFlags;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioInputFlagsToHal(emptyInputFlags, &halEmptyInputFlags));
+    EXPECT_EQ(AUDIO_INPUT_FLAG_NONE, halEmptyInputFlags);
+    CoreUtils::AudioInputFlags emptyInputFlagsBack;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::audioInputFlagsFromHal(halEmptyInputFlags, &emptyInputFlagsBack));
+    EXPECT_EQ(emptyInputFlags, emptyInputFlagsBack);
+    CoreUtils::AudioInputFlags emptyInputFlagsFromNone;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::audioInputFlagsFromHal(AUDIO_INPUT_FLAG_NONE, &emptyInputFlagsFromNone));
+    EXPECT_EQ(emptyInputFlags, emptyInputFlagsFromNone);
+
+    std::vector<std::string> allEnumValues;
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioInOutFlag>{}) {
+        if (isInputFlag(enumVal)) {
+            allEnumValues.push_back(toString(enumVal));
+        }
+    }
+    CoreUtils::AudioInputFlags allInputFlags;
+    allInputFlags.resize(allEnumValues.size());
+    for (size_t i = 0; i < allEnumValues.size(); ++i) {
+        allInputFlags[i] = allEnumValues[i];
+    }
+    audio_input_flags_t halAllInputFlags;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioInputFlagsToHal(allInputFlags, &halAllInputFlags));
+    CoreUtils::AudioInputFlags allInputFlagsBack;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioInputFlagsFromHal(halAllInputFlags, &allInputFlagsBack));
+    EXPECT_EQ(allInputFlags, allInputFlagsBack);
+}
+
+TEST(CoreUtils, ConvertInvalidOutputFlagMask) {
+    CoreUtils::AudioOutputFlags invalid;
+    EXPECT_EQ(BAD_VALUE, CoreUtils::audioOutputFlagsFromHal(kInvalidOutputFlags, &invalid));
+    audio_output_flags_t halInvalid;
+    invalid.resize(1);
+    invalid[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::audioOutputFlagsToHal(invalid, &halInvalid));
+}
+
+TEST(CoreUtils, ConvertOutputFlagMask) {
+    CoreUtils::AudioOutputFlags emptyOutputFlags;
+    audio_output_flags_t halEmptyOutputFlags;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioOutputFlagsToHal(emptyOutputFlags, &halEmptyOutputFlags));
+    EXPECT_EQ(AUDIO_OUTPUT_FLAG_NONE, halEmptyOutputFlags);
+    CoreUtils::AudioOutputFlags emptyOutputFlagsBack;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::audioOutputFlagsFromHal(halEmptyOutputFlags, &emptyOutputFlagsBack));
+    EXPECT_EQ(emptyOutputFlags, emptyOutputFlagsBack);
+    CoreUtils::AudioOutputFlags emptyOutputFlagsFromNone;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioOutputFlagsFromHal(AUDIO_OUTPUT_FLAG_NONE,
+                                                           &emptyOutputFlagsFromNone));
+    EXPECT_EQ(emptyOutputFlags, emptyOutputFlagsFromNone);
+
+    std::vector<std::string> allEnumValues;
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioInOutFlag>{}) {
+        if (isOutputFlag(enumVal)) {
+            allEnumValues.push_back(toString(enumVal));
+        }
+    }
+    CoreUtils::AudioOutputFlags allOutputFlags;
+    allOutputFlags.resize(allEnumValues.size());
+    for (size_t i = 0; i < allEnumValues.size(); ++i) {
+        allOutputFlags[i] = allEnumValues[i];
+    }
+    audio_output_flags_t halAllOutputFlags;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioOutputFlagsToHal(allOutputFlags, &halAllOutputFlags));
+    CoreUtils::AudioOutputFlags allOutputFlagsBack;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioOutputFlagsFromHal(halAllOutputFlags, &allOutputFlagsBack));
+    EXPECT_EQ(allOutputFlags, allOutputFlagsBack);
+}
+
+static MicrophoneInfo generateValidMicrophoneInfo() {
+    MicrophoneInfo micInfo{};
+    micInfo.deviceAddress.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_BUILTIN_MIC);
+    micInfo.channelMapping.resize(1);
+    micInfo.channelMapping[0] = AudioMicrophoneChannelMapping::DIRECT;
+    micInfo.location = AudioMicrophoneLocation::MAINBODY_MOVABLE;
+    micInfo.group = 42;
+    micInfo.indexInTheGroup = 13;
+    micInfo.sensitivity = 65.5;
+    micInfo.maxSpl = 100.5;
+    micInfo.minSpl = 36.6;
+    micInfo.directionality = AudioMicrophoneDirectionality::HYPER_CARDIOID;
+    micInfo.frequencyResponse.resize(1);
+    micInfo.frequencyResponse[0].frequency = 1000;
+    micInfo.frequencyResponse[0].level = 85;
+    micInfo.position.x = 0;
+    micInfo.position.y = 1;
+    micInfo.position.z = 0;
+    micInfo.orientation.x = 0;
+    micInfo.orientation.y = 0;
+    micInfo.orientation.z = 1;
+    return micInfo;
+}
+
+TEST(CoreUtils, ConvertInvalidMicrophoneInfo) {
+    MicrophoneInfo invalid;
+    audio_microphone_characteristic_t halInvalid{};
+    halInvalid.device = kInvalidHalDevice;
+    EXPECT_EQ(BAD_VALUE, CoreUtils::microphoneInfoFromHal(halInvalid, &invalid));
+
+    MicrophoneInfo oversizeDeviceId = generateValidMicrophoneInfo();
+    oversizeDeviceId.deviceId = std::string(AUDIO_MICROPHONE_ID_MAX_LEN + 1, 'A');
+    EXPECT_EQ(BAD_VALUE, CoreUtils::microphoneInfoToHal(oversizeDeviceId, &halInvalid));
+    MicrophoneInfo invalidDeviceType = generateValidMicrophoneInfo();
+    invalidDeviceType.deviceAddress.deviceType = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::microphoneInfoToHal(invalidDeviceType, &halInvalid));
+    MicrophoneInfo oversizeChannelMapping = generateValidMicrophoneInfo();
+    oversizeChannelMapping.channelMapping.resize(AUDIO_CHANNEL_COUNT_MAX + 1);
+    EXPECT_EQ(BAD_VALUE, CoreUtils::microphoneInfoToHal(oversizeChannelMapping, &halInvalid));
+    MicrophoneInfo oversizeFrequencyResponses = generateValidMicrophoneInfo();
+    oversizeFrequencyResponses.frequencyResponse.resize(AUDIO_MICROPHONE_MAX_FREQUENCY_RESPONSES +
+                                                        1);
+    EXPECT_EQ(BAD_VALUE, CoreUtils::microphoneInfoToHal(oversizeFrequencyResponses, &halInvalid));
+}
+
+TEST(CoreUtils, ConvertMicrophoneInfo) {
+    MicrophoneInfo micInfo = generateValidMicrophoneInfo();
+    audio_microphone_characteristic_t halMicInfo;
+    EXPECT_EQ(NO_ERROR, CoreUtils::microphoneInfoToHal(micInfo, &halMicInfo));
+    MicrophoneInfo micInfoBack;
+    EXPECT_EQ(NO_ERROR, CoreUtils::microphoneInfoFromHal(halMicInfo, &micInfoBack));
+    EXPECT_EQ(micInfo, micInfoBack);
+}
+
+static RecordTrackMetadata generateMinimalRecordTrackMetadata() {
+    RecordTrackMetadata metadata{};
+    metadata.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT);
+    metadata.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return metadata;
+}
+
+static RecordTrackMetadata generateValidRecordTrackMetadata() {
+    RecordTrackMetadata metadata = generateMinimalRecordTrackMetadata();
+    metadata.tags.resize(1);
+    metadata.tags[0] = "VX_GOOGLE_42";
+    metadata.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO);
+    metadata.gain = 1.0;
+    return metadata;
+}
+
+static RecordTrackMetadata generateValidRecordTrackMetadataWithDevice() {
+    RecordTrackMetadata metadata = generateValidRecordTrackMetadata();
+    metadata.destination.device({});
+    metadata.destination.device().deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+    return metadata;
+}
+
+using SinkTracks = hidl_vec<RecordTrackMetadata>;
+
+TEST(CoreUtils, ConvertInvalidSinkMetadata) {
+    SinkMetadata invalidSource;
+    invalidSource.tracks = SinkTracks{generateMinimalRecordTrackMetadata()};
+    invalidSource.tracks[0].source = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHal(invalidSource, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHalV7(invalidSource,
+                                                        false /*ignoreNonVendorTags*/, nullptr));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataToHalV7(invalidSource, true /*ignoreNonVendorTags*/, nullptr));
+    SinkMetadata invalidDeviceType;
+    invalidDeviceType.tracks = SinkTracks{generateValidRecordTrackMetadataWithDevice()};
+    invalidDeviceType.tracks[0].destination.device().deviceType = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHal(invalidDeviceType, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHalV7(invalidDeviceType,
+                                                        false /*ignoreNonVendorTags*/, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHalV7(invalidDeviceType,
+                                                        true /*ignoreNonVendorTags*/, nullptr));
+    SinkMetadata invalidChannelMask;
+    invalidChannelMask.tracks = SinkTracks{generateValidRecordTrackMetadata()};
+    invalidChannelMask.tracks[0].channelMask = "random string";
+    // Channel mask is sliced away by 'sinkMetadataToHal'
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataToHal(invalidChannelMask, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHalV7(invalidChannelMask,
+                                                        false /*ignoreNonVendorTags*/, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHalV7(invalidChannelMask,
+                                                        true /*ignoreNonVendorTags*/, nullptr));
+    SinkMetadata invalidTags;
+    invalidTags.tracks = SinkTracks{generateValidRecordTrackMetadata()};
+    invalidTags.tracks[0].tags[0] = "random string";
+    // Tags are sliced away by 'sinkMetadataToHal'
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataToHal(invalidTags, nullptr));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataToHalV7(invalidTags, false /*ignoreNonVendorTags*/, nullptr));
+    // Non-vendor tags should be filtered out.
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataToHalV7(invalidTags, true /*ignoreNonVendorTags*/, nullptr));
+
+    // Verify that a default-initialized metadata is valid.
+    std::vector<record_track_metadata_t> halValid(1, record_track_metadata_t{});
+    std::vector<record_track_metadata_v7_t> halValidV7(1, record_track_metadata_v7_t{});
+    SinkMetadata valid;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataFromHal(halValid, &valid));
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataFromHalV7(halValidV7, false /*ignoreNonVendorTags*/, &valid));
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataFromHalV7(halValidV7, true /*ignoreNonVendorTags*/, &valid));
+
+    std::vector<record_track_metadata_t> halInvalidSource = {{.source = kInvalidHalSource}};
+    std::vector<record_track_metadata_v7_t> halInvalidSourceV7 = {
+            {.base = {.source = kInvalidHalSource}}};
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataFromHal(halInvalidSource, &invalidSource));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataFromHalV7(halInvalidSourceV7, false /*ignoreNonVendorTags*/,
+                                               &invalidSource));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataFromHalV7(
+                                 halInvalidSourceV7, true /*ignoreNonVendorTags*/, &invalidSource));
+    std::vector<record_track_metadata_t> halInvalidDeviceType = {
+            {.dest_device = kInvalidHalDevice}};
+    std::vector<record_track_metadata_v7_t> halInvalidDeviceTypeV7 = {
+            {.base = {.dest_device = kInvalidHalDevice}}};
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataFromHal(halInvalidDeviceType, &invalidDeviceType));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataFromHalV7(halInvalidDeviceTypeV7,
+                                               false /*ignoreNonVendorTags*/, &invalidDeviceType));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataFromHalV7(halInvalidDeviceTypeV7, true /*ignoreNonVendorTags*/,
+                                               &invalidDeviceType));
+    std::vector<record_track_metadata_v7_t> halInvalidChannelMaskV7 = {
+            {.channel_mask = kInvalidHalChannelMask}};
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataFromHalV7(halInvalidChannelMaskV7,
+                                               false /*ignoreNonVendorTags*/, &invalidChannelMask));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataFromHalV7(halInvalidChannelMaskV7,
+                                               true /*ignoreNonVendorTags*/, &invalidChannelMask));
+    std::vector<record_track_metadata_v7_t> halInvalidTagsV7 = {{.tags = "random string"}};
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataFromHalV7(
+                                 halInvalidTagsV7, false /*ignoreNonVendorTags*/, &invalidTags));
+    // Non-vendor tags should be filtered out.
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataFromHalV7(
+                                halInvalidTagsV7, true /*ignoreNonVendorTags*/, &invalidTags));
+}
+
+TEST(CoreUtils, ConvertEmptySinkMetadata) {
+    SinkMetadata emptySinkMetadata;
+    std::vector<record_track_metadata_t> halEmptySinkMetadata;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataToHal(emptySinkMetadata, &halEmptySinkMetadata));
+    EXPECT_TRUE(halEmptySinkMetadata.empty());
+    SinkMetadata emptySinkMetadataBack;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataFromHal(halEmptySinkMetadata, &emptySinkMetadataBack));
+    EXPECT_EQ(emptySinkMetadata, emptySinkMetadataBack);
+    std::vector<record_track_metadata_v7_t> halEmptySinkMetadataV7;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataToHalV7(emptySinkMetadata, false /*ignoreNonVendorTags*/,
+                                             &halEmptySinkMetadataV7));
+    EXPECT_TRUE(halEmptySinkMetadataV7.empty());
+    SinkMetadata emptySinkMetadataBackFromV7;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataFromHalV7(halEmptySinkMetadataV7,
+                                                         false /*ignoreNonVendorTags*/,
+                                                         &emptySinkMetadataBackFromV7));
+    EXPECT_EQ(emptySinkMetadata, emptySinkMetadataBackFromV7);
+}
+
+class SinkMetadataConvertTest : public ::testing::TestWithParam<SinkTracks> {};
+
+TEST_P(SinkMetadataConvertTest, ToFromHal) {
+    SinkMetadata sinkMetadata;
+    sinkMetadata.tracks = GetParam();
+    std::vector<record_track_metadata_t> halSinkMetadata;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataToHal(sinkMetadata, &halSinkMetadata));
+    EXPECT_EQ(sinkMetadata.tracks.size(), halSinkMetadata.size());
+    SinkMetadata sinkMetadataBackTrimmed;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataFromHal(halSinkMetadata, &sinkMetadataBackTrimmed));
+    // Can't compare 'sinkMetadata' to 'sinkMetadataBackTrimmed'
+    std::vector<record_track_metadata_v7_t> halSinkMetadataV7;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataToHalV7(sinkMetadata, false /*ignoreNonVendorTags*/,
+                                                       &halSinkMetadataV7));
+    EXPECT_EQ(sinkMetadata.tracks.size(), halSinkMetadataV7.size());
+    SinkMetadata sinkMetadataBackFromV7;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataFromHalV7(halSinkMetadataV7, false /*ignoreNonVendorTags*/,
+                                               &sinkMetadataBackFromV7));
+    EXPECT_EQ(sinkMetadata, sinkMetadataBackFromV7);
+    std::vector<record_track_metadata_v7_t> halSinkMetadataV7FromTrimmed;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataToHalV7(sinkMetadataBackTrimmed, false /*ignoreNonVendorTags*/,
+                                             &halSinkMetadataV7FromTrimmed));
+    EXPECT_EQ(sinkMetadata.tracks.size(), halSinkMetadataV7FromTrimmed.size());
+    SinkMetadata sinkMetadataBackFromV7Trimmed;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataFromHalV7(halSinkMetadataV7FromTrimmed,
+                                                         false /*ignoreNonVendorTags*/,
+                                                         &sinkMetadataBackFromV7Trimmed));
+    EXPECT_EQ(sinkMetadataBackTrimmed, sinkMetadataBackFromV7Trimmed);
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidRecordTrackMetadatas, SinkMetadataConvertTest,
+                         ::testing::Values(SinkTracks{generateMinimalRecordTrackMetadata()},
+                                           SinkTracks{generateValidRecordTrackMetadata()},
+                                           SinkTracks{generateValidRecordTrackMetadataWithDevice()},
+                                           SinkTracks{
+                                                   generateMinimalRecordTrackMetadata(),
+                                                   generateValidRecordTrackMetadata(),
+                                                   generateValidRecordTrackMetadataWithDevice()}));
+
+static PlaybackTrackMetadata generateMinimalPlaybackTrackMetadata() {
+    PlaybackTrackMetadata metadata{};
+    metadata.usage = toString(xsd::AudioUsage::AUDIO_USAGE_UNKNOWN);
+    metadata.contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_UNKNOWN);
+    metadata.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return metadata;
+}
+
+static PlaybackTrackMetadata generateValidPlaybackTrackMetadata() {
+    PlaybackTrackMetadata metadata = generateMinimalPlaybackTrackMetadata();
+    metadata.tags.resize(1);
+    metadata.tags[0] = "VX_GOOGLE_42";
+    metadata.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
+    metadata.gain = 1.0;
+    return metadata;
+}
+
+using SourceTracks = hidl_vec<PlaybackTrackMetadata>;
+
+TEST(CoreUtils, ConvertInvalidSourceMetadata) {
+    SourceMetadata invalidUsage;
+    invalidUsage.tracks = SourceTracks{generateMinimalPlaybackTrackMetadata()};
+    invalidUsage.tracks[0].usage = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHal(invalidUsage, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHalV7(invalidUsage,
+                                                          false /*ignoreNonVendorTags*/, nullptr));
+    SourceMetadata invalidContentType;
+    invalidContentType.tracks = SourceTracks{generateMinimalPlaybackTrackMetadata()};
+    invalidContentType.tracks[0].contentType = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHal(invalidContentType, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHalV7(invalidContentType,
+                                                          false /*ignoreNonVendorTags*/, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHalV7(invalidContentType,
+                                                          true /*ignoreNonVendorTags*/, nullptr));
+    SourceMetadata invalidChannelMask;
+    invalidChannelMask.tracks = SourceTracks{generateValidPlaybackTrackMetadata()};
+    invalidChannelMask.tracks[0].channelMask = "random string";
+    // Channel mask is sliced away by 'sourceMetadataToHal'
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataToHal(invalidChannelMask, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHalV7(invalidChannelMask,
+                                                          false /*ignoreNonVendorTags*/, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHalV7(invalidChannelMask,
+                                                          true /*ignoreNonVendorTags*/, nullptr));
+    SourceMetadata invalidTags;
+    invalidTags.tracks = SourceTracks{generateValidPlaybackTrackMetadata()};
+    invalidTags.tracks[0].tags[0] = "random string";
+    // Tags are sliced away by 'sourceMetadataToHal'
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataToHal(invalidTags, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHalV7(invalidTags,
+                                                          false /*ignoreNonVendorTags*/, nullptr));
+    // Non-vendor tags should be filtered out.
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataToHalV7(invalidTags, true /*ignoreNonVendorTags*/, nullptr));
+
+    // Verify that a default-initialized metadata is valid.
+    std::vector<playback_track_metadata_t> halValid(1, playback_track_metadata_t{});
+    std::vector<playback_track_metadata_v7_t> halValidV7(1, playback_track_metadata_v7_t{});
+    SourceMetadata valid;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataFromHal(halValid, &valid));
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataFromHalV7(halValidV7,
+                                                           false /*ignoreNonVendorTags*/, &valid));
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataFromHalV7(halValidV7, true /*ignoreNonVendorTags*/, &valid));
+
+    std::vector<playback_track_metadata_t> halInvalidUsage = {{.usage = kInvalidHalUsage}};
+    std::vector<playback_track_metadata_v7_t> halInvalidUsageV7 = {
+            {.base = {.usage = kInvalidHalUsage}}};
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataFromHal(halInvalidUsage, &invalidUsage));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataFromHalV7(
+                                 halInvalidUsageV7, false /*ignoreNonVendorTags*/, &invalidUsage));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataFromHalV7(
+                                 halInvalidUsageV7, true /*ignoreNonVendorTags*/, &invalidUsage));
+    std::vector<playback_track_metadata_t> halInvalidContentType = {
+            {.content_type = kInvalidHalContentType}};
+    std::vector<playback_track_metadata_v7_t> halInvalidContentTypeV7 = {
+            {.base = {.content_type = kInvalidHalContentType}}};
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sourceMetadataFromHal(halInvalidContentType, &invalidContentType));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sourceMetadataFromHalV7(
+                      halInvalidContentTypeV7, false /*ignoreNonVendorTags*/, &invalidContentType));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sourceMetadataFromHalV7(
+                      halInvalidContentTypeV7, true /*ignoreNonVendorTags*/, &invalidContentType));
+    std::vector<playback_track_metadata_v7_t> halInvalidChannelMaskV7 = {
+            {.channel_mask = kInvalidHalChannelMask}};
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sourceMetadataFromHalV7(
+                      halInvalidChannelMaskV7, false /*ignoreNonVendorTags*/, &invalidChannelMask));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sourceMetadataFromHalV7(
+                      halInvalidChannelMaskV7, true /*ignoreNonVendorTags*/, &invalidChannelMask));
+    std::vector<playback_track_metadata_v7_t> halInvalidTagsV7 = {{.tags = "random string"}};
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataFromHalV7(
+                                 halInvalidTagsV7, false /*ignoreNonVendorTags*/, &invalidTags));
+    // Non-vendor tags should be filtered out.
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataFromHalV7(
+                                halInvalidTagsV7, true /*ignoreNonVendorTags*/, &invalidTags));
+}
+
+TEST(CoreUtils, ConvertEmptySourceMetadata) {
+    SourceMetadata emptySourceMetadata;
+    std::vector<playback_track_metadata_t> halEmptySourceMetadata;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataToHal(emptySourceMetadata, &halEmptySourceMetadata));
+    EXPECT_TRUE(halEmptySourceMetadata.empty());
+    SourceMetadata emptySourceMetadataBack;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataFromHal(halEmptySourceMetadata, &emptySourceMetadataBack));
+    EXPECT_EQ(emptySourceMetadata, emptySourceMetadataBack);
+    std::vector<playback_track_metadata_v7_t> halEmptySourceMetadataV7;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataToHalV7(emptySourceMetadata, false /*ignoreNonVendorTags*/,
+                                               &halEmptySourceMetadataV7));
+    EXPECT_TRUE(halEmptySourceMetadataV7.empty());
+    SourceMetadata emptySourceMetadataBackFromV7;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataFromHalV7(halEmptySourceMetadataV7,
+                                                           false /*ignoreNonVendorTags*/,
+                                                           &emptySourceMetadataBackFromV7));
+    EXPECT_EQ(emptySourceMetadata, emptySourceMetadataBackFromV7);
+}
+
+class SourceMetadataConvertTest : public ::testing::TestWithParam<SourceTracks> {};
+
+TEST_P(SourceMetadataConvertTest, ToFromHal) {
+    SourceMetadata sourceMetadata;
+    sourceMetadata.tracks = GetParam();
+    std::vector<playback_track_metadata_t> halSourceMetadata;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataToHal(sourceMetadata, &halSourceMetadata));
+    EXPECT_EQ(sourceMetadata.tracks.size(), halSourceMetadata.size());
+    SourceMetadata sourceMetadataBackTrimmed;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataFromHal(halSourceMetadata, &sourceMetadataBackTrimmed));
+    // Can't compare 'sourceMetadata' to 'sourceMetadataBackTrimmed'
+    std::vector<playback_track_metadata_v7_t> halSourceMetadataV7;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataToHalV7(sourceMetadata, false /*ignoreNonVendorTags*/,
+                                               &halSourceMetadataV7));
+    EXPECT_EQ(sourceMetadata.tracks.size(), halSourceMetadataV7.size());
+    SourceMetadata sourceMetadataBackFromV7;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataFromHalV7(halSourceMetadataV7, false /*ignoreNonVendorTags*/,
+                                                 &sourceMetadataBackFromV7));
+    EXPECT_EQ(sourceMetadata, sourceMetadataBackFromV7);
+    std::vector<playback_track_metadata_v7_t> halSourceMetadataV7FromTrimmed;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataToHalV7(sourceMetadataBackTrimmed,
+                                                         false /*ignoreNonVendorTags*/,
+                                                         &halSourceMetadataV7FromTrimmed));
+    EXPECT_EQ(sourceMetadata.tracks.size(), halSourceMetadataV7FromTrimmed.size());
+    SourceMetadata sourceMetadataBackFromV7Trimmed;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataFromHalV7(halSourceMetadataV7FromTrimmed,
+                                                           false /*ignoreNonVendorTags*/,
+                                                           &sourceMetadataBackFromV7Trimmed));
+    EXPECT_EQ(sourceMetadataBackTrimmed, sourceMetadataBackFromV7Trimmed);
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidPlaybackTrackMetadatas, SourceMetadataConvertTest,
+                         ::testing::Values(SourceTracks{generateMinimalPlaybackTrackMetadata()},
+                                           SourceTracks{generateValidPlaybackTrackMetadata()},
+                                           SourceTracks{generateMinimalPlaybackTrackMetadata(),
+                                                        generateValidPlaybackTrackMetadata()}));
diff --git a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
index 5076dcf..bb7c6d3 100644
--- a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
@@ -336,24 +336,24 @@
     ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(
         {{{toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
                       toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
-                      {},
+                      0.1, // gain
                       toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
-                      0.1},
+                      {}}, // tags
           {toString(xsd::AudioUsage::AUDIO_USAGE_VOICE_COMMUNICATION),
                       toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_SPEECH),
-                      {}, // tags
+                      1.0,
                       toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO),
-                      1.0},
+                      {}},
           {toString(xsd::AudioUsage::AUDIO_USAGE_ALARM),
                       toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_SONIFICATION),
-                      {}, // tags
+                      0.0,
                       toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
-                      0.0},
+                      {}},
           {toString(xsd::AudioUsage::AUDIO_USAGE_ASSISTANT),
                       toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_UNKNOWN),
-                      {},
+                      0.3,
                       toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO),
-                      0.3}}}
+                      {}}}}
     ));
     // clang-format on
     // Set no metadata as if all stream track had stopped
diff --git a/audio/effect/all-versions/default/Android.bp b/audio/effect/all-versions/default/Android.bp
index a0cd612..99b1120 100644
--- a/audio/effect/all-versions/default/Android.bp
+++ b/audio/effect/all-versions/default/Android.bp
@@ -8,7 +8,6 @@
         "AudioBufferManager.cpp",
         "AutomaticGainControlEffect.cpp",
         "BassBoostEffect.cpp",
-        "Conversions.cpp",
         "DownmixEffect.cpp",
         "Effect.cpp",
         "EffectsFactory.cpp",
@@ -51,6 +50,7 @@
         "android.hardware.audio.common@2.0",
         "android.hardware.audio.common@2.0-util",
         "android.hardware.audio.effect@2.0",
+        "android.hardware.audio.effect@2.0-util",
     ],
     cflags: [
         "-DMAJOR_VERSION=2",
@@ -66,6 +66,7 @@
         "android.hardware.audio.common@4.0",
         "android.hardware.audio.common@4.0-util",
         "android.hardware.audio.effect@4.0",
+        "android.hardware.audio.effect@4.0-util",
     ],
     cflags: [
         "-DMAJOR_VERSION=4",
@@ -81,6 +82,7 @@
         "android.hardware.audio.common@5.0",
         "android.hardware.audio.common@5.0-util",
         "android.hardware.audio.effect@5.0",
+        "android.hardware.audio.effect@5.0-util",
     ],
     cflags: [
         "-DMAJOR_VERSION=5",
@@ -96,6 +98,7 @@
         "android.hardware.audio.common@6.0",
         "android.hardware.audio.common@6.0-util",
         "android.hardware.audio.effect@6.0",
+        "android.hardware.audio.effect@6.0-util",
     ],
     cflags: [
         "-DMAJOR_VERSION=6",
@@ -111,6 +114,7 @@
         "android.hardware.audio.common@7.0",
         "android.hardware.audio.common@7.0-util",
         "android.hardware.audio.effect@7.0",
+        "android.hardware.audio.effect@7.0-util",
     ],
     cflags: [
         "-DMAJOR_VERSION=7",
diff --git a/audio/effect/all-versions/default/Conversions.cpp b/audio/effect/all-versions/default/Conversions.cpp
deleted file mode 100644
index 0cc8767..0000000
--- a/audio/effect/all-versions/default/Conversions.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2018 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 "Conversions.h"
-#include "UuidUtils.h"
-
-#include <memory.h>
-#include <stdio.h>
-
-#include <common/all-versions/VersionUtils.h>
-
-using ::android::hardware::audio::common::utils::EnumBitfield;
-
-namespace android {
-namespace hardware {
-namespace audio {
-namespace effect {
-namespace CPP_VERSION {
-namespace implementation {
-
-using ::android::hardware::audio::common::CPP_VERSION::implementation::UuidUtils;
-
-void effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
-                             EffectDescriptor* descriptor) {
-    UuidUtils::uuidFromHal(halDescriptor.type, &descriptor->type);
-    UuidUtils::uuidFromHal(halDescriptor.uuid, &descriptor->uuid);
-    descriptor->flags = EnumBitfield<EffectFlags>(halDescriptor.flags);
-    descriptor->cpuLoad = halDescriptor.cpuLoad;
-    descriptor->memoryUsage = halDescriptor.memoryUsage;
-    memcpy(descriptor->name.data(), halDescriptor.name, descriptor->name.size());
-    memcpy(descriptor->implementor.data(), halDescriptor.implementor,
-           descriptor->implementor.size());
-}
-
-std::string uuidToString(const effect_uuid_t& halUuid) {
-    char str[64];
-    snprintf(str, sizeof(str), "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", halUuid.timeLow,
-             halUuid.timeMid, halUuid.timeHiAndVersion, halUuid.clockSeq, halUuid.node[0],
-             halUuid.node[1], halUuid.node[2], halUuid.node[3], halUuid.node[4], halUuid.node[5]);
-    return str;
-}
-
-}  // namespace implementation
-}  // namespace CPP_VERSION
-}  // namespace effect
-}  // namespace audio
-}  // namespace hardware
-}  // namespace android
diff --git a/audio/effect/all-versions/default/Conversions.h b/audio/effect/all-versions/default/Conversions.h
deleted file mode 100644
index 75aab24..0000000
--- a/audio/effect/all-versions/default/Conversions.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef ANDROID_HARDWARE_AUDIO_EFFECT_CONVERSIONS_H_
-#define ANDROID_HARDWARE_AUDIO_EFFECT_CONVERSIONS_H_
-
-#include PATH(android/hardware/audio/effect/FILE_VERSION/types.h)
-
-#include <string>
-
-#include <system/audio_effect.h>
-
-namespace android {
-namespace hardware {
-namespace audio {
-namespace effect {
-namespace CPP_VERSION {
-namespace implementation {
-
-using ::android::hardware::audio::effect::CPP_VERSION::EffectDescriptor;
-
-void effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
-                             EffectDescriptor* descriptor);
-std::string uuidToString(const effect_uuid_t& halUuid);
-
-}  // namespace implementation
-}  // namespace CPP_VERSION
-}  // namespace effect
-}  // namespace audio
-}  // namespace hardware
-}  // namespace android
-
-#endif  // ANDROID_HARDWARE_AUDIO_EFFECT_CONVERSIONS_H_
diff --git a/audio/effect/all-versions/default/Effect.cpp b/audio/effect/all-versions/default/Effect.cpp
index 58f1779..ccfc6b2 100644
--- a/audio/effect/all-versions/default/Effect.cpp
+++ b/audio/effect/all-versions/default/Effect.cpp
@@ -19,7 +19,6 @@
 #define LOG_TAG "EffectHAL"
 #define ATRACE_TAG ATRACE_TAG_AUDIO
 
-#include "Conversions.h"
 #include "Effect.h"
 #include "common/all-versions/default/EffectMap.h"
 
@@ -30,6 +29,7 @@
 #include <HidlUtils.h>
 #include <android/log.h>
 #include <media/EffectsFactoryApi.h>
+#include <util/EffectUtils.h>
 #include <utils/Trace.h>
 
 #include "VersionUtils.h"
@@ -202,34 +202,6 @@
     halConfig->aux_channels = static_cast<audio_channel_mask_t>(config.auxChannels);
 }
 
-void Effect::effectBufferConfigFromHal(const buffer_config_t& halConfig,
-                                       EffectBufferConfig* config) {
-    config->buffer.id = 0;
-    config->buffer.frameCount = 0;
-    config->samplingRateHz = halConfig.samplingRate;
-    config->channels = AudioChannelBitfield(halConfig.channels);
-    config->format = AudioFormat(halConfig.format);
-    config->accessMode = EffectBufferAccess(halConfig.accessMode);
-    config->mask = static_cast<decltype(config->mask)>(halConfig.mask);
-}
-
-// static
-void Effect::effectBufferConfigToHal(const EffectBufferConfig& config, buffer_config_t* halConfig) {
-    // Note: setting the buffers directly is considered obsolete. They need to be set
-    // using 'setProcessBuffers'.
-    halConfig->buffer.frameCount = 0;
-    halConfig->buffer.raw = NULL;
-    halConfig->samplingRate = config.samplingRateHz;
-    halConfig->channels = static_cast<uint32_t>(config.channels);
-    // Note: The framework code does not use BP.
-    halConfig->bufferProvider.cookie = NULL;
-    halConfig->bufferProvider.getBuffer = NULL;
-    halConfig->bufferProvider.releaseBuffer = NULL;
-    halConfig->format = static_cast<uint8_t>(config.format);
-    halConfig->accessMode = static_cast<uint8_t>(config.accessMode);
-    halConfig->mask = static_cast<uint8_t>(config.mask);
-}
-
 #else  // MAJOR_VERSION <= 6
 
 void Effect::effectAuxChannelsConfigFromHal(const channel_config_t& halConfig,
@@ -247,68 +219,8 @@
     (void)HidlUtils::audioChannelMaskToHal(config.auxChannels, &halConfig->aux_channels);
 }
 
-void Effect::effectBufferConfigFromHal(const buffer_config_t& halConfig,
-                                       EffectBufferConfig* config) {
-    config->buffer.unspecified();
-    audio_config_base_t halConfigBase = {halConfig.samplingRate,
-                                         static_cast<audio_channel_mask_t>(halConfig.channels),
-                                         static_cast<audio_format_t>(halConfig.format)};
-    (void)HidlUtils::audioConfigBaseOptionalFromHal(
-            halConfigBase, mIsInput, halConfig.mask & EFFECT_CONFIG_FORMAT,
-            halConfig.mask & EFFECT_CONFIG_SMP_RATE, halConfig.mask & EFFECT_CONFIG_CHANNELS,
-            &config->base);
-    if (halConfig.mask & EFFECT_CONFIG_ACC_MODE) {
-        config->accessMode.value(EffectBufferAccess(halConfig.accessMode));
-    }
-}
-
-// static
-void Effect::effectBufferConfigToHal(const EffectBufferConfig& config, buffer_config_t* halConfig) {
-    // Note: setting the buffers directly is considered obsolete. They need to be set
-    // using 'setProcessBuffers'.
-    halConfig->buffer.frameCount = 0;
-    halConfig->buffer.raw = nullptr;
-    audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
-    bool formatSpecified = false, sRateSpecified = false, channelMaskSpecified = false;
-    (void)HidlUtils::audioConfigBaseOptionalToHal(config.base, &halConfigBase, &formatSpecified,
-                                                  &sRateSpecified, &channelMaskSpecified);
-    halConfig->mask = 0;
-    if (sRateSpecified) {
-        halConfig->mask |= EFFECT_CONFIG_SMP_RATE;
-        halConfig->samplingRate = halConfigBase.sample_rate;
-    }
-    if (channelMaskSpecified) {
-        halConfig->mask |= EFFECT_CONFIG_CHANNELS;
-        halConfig->channels = halConfigBase.channel_mask;
-    }
-    if (formatSpecified) {
-        halConfig->mask |= EFFECT_CONFIG_FORMAT;
-        halConfig->format = halConfigBase.format;
-    }
-    // Note: The framework code does not use BP.
-    halConfig->bufferProvider.cookie = nullptr;
-    halConfig->bufferProvider.getBuffer = nullptr;
-    halConfig->bufferProvider.releaseBuffer = nullptr;
-    if (config.accessMode.getDiscriminator() ==
-        EffectBufferConfig::OptionalAccessMode::hidl_discriminator::value) {
-        halConfig->mask |= EFFECT_CONFIG_ACC_MODE;
-        halConfig->accessMode = static_cast<uint8_t>(config.accessMode.value());
-    }
-}
-
 #endif  // MAJOR_VERSION <= 6
 
-void Effect::effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config) {
-    effectBufferConfigFromHal(halConfig.inputCfg, &config->inputCfg);
-    effectBufferConfigFromHal(halConfig.outputCfg, &config->outputCfg);
-}
-
-// static
-void Effect::effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig) {
-    effectBufferConfigToHal(config.inputCfg, &halConfig->inputCfg);
-    effectBufferConfigToHal(config.outputCfg, &halConfig->outputCfg);
-}
-
 // static
 void Effect::effectOffloadParamToHal(const EffectOffloadParameter& offload,
                                      effect_offload_param_t* halOffload) {
@@ -373,7 +285,7 @@
         (*mHandle)->command(mHandle, commandCode, 0, NULL, &halResultSize, &halConfig);
     EffectConfig config;
     if (status == OK) {
-        effectConfigFromHal(halConfig, &config);
+        status = EffectUtils::effectConfigFromHal(halConfig, mIsInput, &config);
     }
     cb(analyzeCommandStatus(commandName, sContextCallToCommand, status), config);
 }
@@ -538,7 +450,7 @@
                              const sp<IEffectBufferProviderCallback>& inputBufferProvider,
                              const sp<IEffectBufferProviderCallback>& outputBufferProvider) {
     effect_config_t halConfig;
-    effectConfigToHal(config, &halConfig);
+    EffectUtils::effectConfigToHal(config, &halConfig);
     if (inputBufferProvider != 0) {
         LOG_FATAL("Using input buffer provider is not supported");
     }
@@ -733,7 +645,7 @@
     status_t status = (*mHandle)->get_descriptor(mHandle, &halDescriptor);
     EffectDescriptor descriptor;
     if (status == OK) {
-        effectDescriptorFromHal(halDescriptor, &descriptor);
+        status = EffectUtils::effectDescriptorFromHal(halDescriptor, &descriptor);
     }
     _hidl_cb(analyzeStatus("get_descriptor", "", sContextCallFunction, status), descriptor);
     return Void();
diff --git a/audio/effect/all-versions/default/Effect.h b/audio/effect/all-versions/default/Effect.h
index 9aa47ea..d5218f7 100644
--- a/audio/effect/all-versions/default/Effect.h
+++ b/audio/effect/all-versions/default/Effect.h
@@ -203,11 +203,6 @@
                                         EffectAuxChannelsConfig* config);
     static void effectAuxChannelsConfigToHal(const EffectAuxChannelsConfig& config,
                                              channel_config_t* halConfig);
-    void effectBufferConfigFromHal(const buffer_config_t& halConfig, EffectBufferConfig* config);
-    static void effectBufferConfigToHal(const EffectBufferConfig& config,
-                                        buffer_config_t* halConfig);
-    void effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config);
-    static void effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig);
     static void effectOffloadParamToHal(const EffectOffloadParameter& offload,
                                         effect_offload_param_t* halOffload);
     static std::vector<uint8_t> parameterToHal(uint32_t paramSize, const void* paramData,
diff --git a/audio/effect/all-versions/default/EffectsFactory.cpp b/audio/effect/all-versions/default/EffectsFactory.cpp
index 1ea990b..eb1cb49 100644
--- a/audio/effect/all-versions/default/EffectsFactory.cpp
+++ b/audio/effect/all-versions/default/EffectsFactory.cpp
@@ -19,7 +19,6 @@
 #include "AcousticEchoCancelerEffect.h"
 #include "AutomaticGainControlEffect.h"
 #include "BassBoostEffect.h"
-#include "Conversions.h"
 #include "DownmixEffect.h"
 #include "Effect.h"
 #include "EnvironmentalReverbEffect.h"
@@ -27,11 +26,11 @@
 #include "LoudnessEnhancerEffect.h"
 #include "NoiseSuppressionEffect.h"
 #include "PresetReverbEffect.h"
-#include "UuidUtils.h"
 #include "VirtualizerEffect.h"
 #include "VisualizerEffect.h"
 #include "common/all-versions/default/EffectMap.h"
 
+#include <UuidUtils.h>
 #include <android/log.h>
 #include <media/EffectsFactoryApi.h>
 #include <system/audio_effects/effect_aec.h>
@@ -45,6 +44,7 @@
 #include <system/audio_effects/effect_presetreverb.h>
 #include <system/audio_effects/effect_virtualizer.h>
 #include <system/audio_effects/effect_visualizer.h>
+#include <util/EffectUtils.h>
 
 namespace android {
 namespace hardware {
@@ -107,7 +107,7 @@
         effect_descriptor_t halDescriptor;
         status = EffectQueryEffect(i, &halDescriptor);
         if (status == OK) {
-            effectDescriptorFromHal(halDescriptor, &result[i]);
+            EffectUtils::effectDescriptorFromHal(halDescriptor, &result[i]);
         } else {
             ALOGE("Error querying effect at position %d / %d: %s", i, numEffects,
                   strerror(-status));
@@ -141,11 +141,11 @@
     effect_descriptor_t halDescriptor;
     status_t status = EffectGetDescriptor(&halUuid, &halDescriptor);
     EffectDescriptor descriptor;
-    effectDescriptorFromHal(halDescriptor, &descriptor);
+    EffectUtils::effectDescriptorFromHal(halDescriptor, &descriptor);
     Result retval(Result::OK);
     if (status != OK) {
-        ALOGE("Error querying effect descriptor for %s: %s", uuidToString(halUuid).c_str(),
-              strerror(-status));
+        ALOGE("Error querying effect descriptor for %s: %s",
+              UuidUtils::uuidToString(halUuid).c_str(), strerror(-status));
         if (status == -ENOENT) {
             retval = Result::INVALID_ARGUMENTS;
         } else {
@@ -191,13 +191,14 @@
             effect = dispatchEffectInstanceCreation(halDescriptor, handle);
             effectId = EffectMap::getInstance().add(handle);
         } else {
-            ALOGE("Error querying effect descriptor for %s: %s", uuidToString(halUuid).c_str(),
-                  strerror(-status));
+            ALOGE("Error querying effect descriptor for %s: %s",
+                  UuidUtils::uuidToString(halUuid).c_str(), strerror(-status));
             EffectRelease(handle);
         }
     }
     if (status != OK) {
-        ALOGE("Error creating effect %s: %s", uuidToString(halUuid).c_str(), strerror(-status));
+        ALOGE("Error creating effect %s: %s", UuidUtils::uuidToString(halUuid).c_str(),
+              strerror(-status));
         if (status == -ENOENT) {
             retval = Result::INVALID_ARGUMENTS;
         } else {
diff --git a/audio/effect/all-versions/default/TEST_MAPPING b/audio/effect/all-versions/default/TEST_MAPPING
new file mode 100644
index 0000000..b66e4e4
--- /dev/null
+++ b/audio/effect/all-versions/default/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "android.hardware.audio.effect@7.0-util_tests"
+    }
+  ]
+}
diff --git a/audio/effect/all-versions/default/util/Android.bp b/audio/effect/all-versions/default/util/Android.bp
new file mode 100644
index 0000000..5ba19d3
--- /dev/null
+++ b/audio/effect/all-versions/default/util/Android.bp
@@ -0,0 +1,136 @@
+cc_defaults {
+    name: "android.hardware.audio.effect-util_default",
+    defaults: ["hidl_defaults"],
+
+    vendor_available: true,
+
+    export_include_dirs: ["include"],
+
+    srcs: [
+        "EffectUtils.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libhidlbase",
+        "android.hardware.audio.common-util",
+    ],
+    export_shared_lib_headers: [
+        "android.hardware.audio.common-util",
+    ],
+
+    header_libs: [
+        "libaudio_system_headers",
+        "libhardware_headers",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio.effect@2.0-util",
+    defaults: ["android.hardware.audio.effect-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@2.0",
+        "android.hardware.audio.common@2.0-util",
+        "android.hardware.audio.effect@2.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=2",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio.effect@4.0-util",
+    defaults: ["android.hardware.audio.effect-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@4.0",
+        "android.hardware.audio.common@4.0-util",
+        "android.hardware.audio.effect@4.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=4",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio.effect@5.0-util",
+    defaults: ["android.hardware.audio.effect-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@5.0",
+        "android.hardware.audio.common@5.0-util",
+        "android.hardware.audio.effect@5.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=5",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio.effect@6.0-util",
+    defaults: ["android.hardware.audio.effect-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@6.0",
+        "android.hardware.audio.common@6.0-util",
+        "android.hardware.audio.effect@6.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=6",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library {
+    name: "android.hardware.audio.effect@7.0-util",
+    defaults: ["android.hardware.audio.effect-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-util",
+        "android.hardware.audio.effect@7.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-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.effect@7.0-util_tests",
+    defaults: ["android.hardware.audio.effect-util_default"],
+
+    srcs: ["tests/effectutils_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",
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.common@7.0-util",
+        "android.hardware.audio.effect@7.0",
+        "android.hardware.audio.effect@7.0-util",
+    ],
+
+    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/effect/all-versions/default/util/EffectUtils.cpp b/audio/effect/all-versions/default/util/EffectUtils.cpp
new file mode 100644
index 0000000..1c0419a
--- /dev/null
+++ b/audio/effect/all-versions/default/util/EffectUtils.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2021 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 <memory.h>
+
+#include <HidlUtils.h>
+#include <UuidUtils.h>
+#include <common/all-versions/VersionUtils.h>
+
+#include "util/EffectUtils.h"
+
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::UuidUtils;
+using ::android::hardware::audio::common::utils::EnumBitfield;
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace effect {
+namespace CPP_VERSION {
+namespace implementation {
+
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+
+#define CONVERT_CHECKED(expr, result)                   \
+    if (status_t status = (expr); status != NO_ERROR) { \
+        result = status;                                \
+    }
+
+#if MAJOR_VERSION <= 6
+
+status_t EffectUtils::effectBufferConfigFromHal(const buffer_config_t& halConfig, bool /*isInput*/,
+                                                EffectBufferConfig* config) {
+    config->buffer.id = 0;
+    config->buffer.frameCount = 0;
+    config->samplingRateHz = halConfig.samplingRate;
+    config->channels = EnumBitfield<AudioChannelMask>(halConfig.channels);
+    config->format = AudioFormat(halConfig.format);
+    config->accessMode = EffectBufferAccess(halConfig.accessMode);
+    config->mask = EnumBitfield<EffectConfigParameters>(halConfig.mask);
+    return NO_ERROR;
+}
+
+status_t EffectUtils::effectBufferConfigToHal(const EffectBufferConfig& config,
+                                              buffer_config_t* halConfig) {
+    // Note: setting the buffers directly is considered obsolete. They need to be set
+    // using 'setProcessBuffers'.
+    halConfig->buffer.frameCount = 0;
+    halConfig->buffer.raw = nullptr;
+    halConfig->samplingRate = config.samplingRateHz;
+    halConfig->channels = static_cast<uint32_t>(config.channels);
+    // Note: The framework code does not use BP.
+    halConfig->bufferProvider.cookie = nullptr;
+    halConfig->bufferProvider.getBuffer = nullptr;
+    halConfig->bufferProvider.releaseBuffer = nullptr;
+    halConfig->format = static_cast<uint8_t>(config.format);
+    halConfig->accessMode = static_cast<uint8_t>(config.accessMode);
+    halConfig->mask = static_cast<uint8_t>(config.mask);
+    return NO_ERROR;
+}
+
+#else
+
+status_t EffectUtils::effectBufferConfigFromHal(const buffer_config_t& halConfig, bool isInput,
+                                                EffectBufferConfig* config) {
+    status_t result = NO_ERROR;
+    config->buffer.unspecified();
+    audio_config_base_t halConfigBase = {halConfig.samplingRate,
+                                         static_cast<audio_channel_mask_t>(halConfig.channels),
+                                         static_cast<audio_format_t>(halConfig.format)};
+    CONVERT_CHECKED(HidlUtils::audioConfigBaseOptionalFromHal(
+                            halConfigBase, isInput, halConfig.mask & EFFECT_CONFIG_FORMAT,
+                            halConfig.mask & EFFECT_CONFIG_SMP_RATE,
+                            halConfig.mask & EFFECT_CONFIG_CHANNELS, &config->base),
+                    result);
+    if (halConfig.mask & EFFECT_CONFIG_ACC_MODE) {
+        config->accessMode.value(EffectBufferAccess(halConfig.accessMode));
+    }
+    return result;
+}
+
+status_t EffectUtils::effectBufferConfigToHal(const EffectBufferConfig& config,
+                                              buffer_config_t* halConfig) {
+    status_t result = NO_ERROR;
+    // Note: setting the buffers directly is considered obsolete. They need to be set
+    // using 'setProcessBuffers'.
+    halConfig->buffer.frameCount = 0;
+    halConfig->buffer.raw = nullptr;
+    audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
+    bool formatSpecified = false, sRateSpecified = false, channelMaskSpecified = false;
+    CONVERT_CHECKED(
+            HidlUtils::audioConfigBaseOptionalToHal(config.base, &halConfigBase, &formatSpecified,
+                                                    &sRateSpecified, &channelMaskSpecified),
+            result);
+    halConfig->mask = 0;
+    if (sRateSpecified) {
+        halConfig->mask |= EFFECT_CONFIG_SMP_RATE;
+        halConfig->samplingRate = halConfigBase.sample_rate;
+    }
+    if (channelMaskSpecified) {
+        halConfig->mask |= EFFECT_CONFIG_CHANNELS;
+        halConfig->channels = halConfigBase.channel_mask;
+    }
+    if (formatSpecified) {
+        halConfig->mask |= EFFECT_CONFIG_FORMAT;
+        halConfig->format = halConfigBase.format;
+    }
+    // Note: The framework code does not use BP.
+    halConfig->bufferProvider.cookie = nullptr;
+    halConfig->bufferProvider.getBuffer = nullptr;
+    halConfig->bufferProvider.releaseBuffer = nullptr;
+    if (config.accessMode.getDiscriminator() ==
+        EffectBufferConfig::OptionalAccessMode::hidl_discriminator::value) {
+        halConfig->mask |= EFFECT_CONFIG_ACC_MODE;
+        halConfig->accessMode = static_cast<uint8_t>(config.accessMode.value());
+    }
+    return result;
+}
+
+#endif  // MAJOR_VERSION >= 6
+
+status_t EffectUtils::effectConfigFromHal(const effect_config_t& halConfig, bool isInput,
+                                          EffectConfig* config) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(effectBufferConfigFromHal(halConfig.inputCfg, isInput, &config->inputCfg),
+                    result);
+    CONVERT_CHECKED(effectBufferConfigFromHal(halConfig.outputCfg, isInput, &config->outputCfg),
+                    result);
+    return result;
+}
+
+status_t EffectUtils::effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(effectBufferConfigToHal(config.inputCfg, &halConfig->inputCfg), result);
+    CONVERT_CHECKED(effectBufferConfigToHal(config.outputCfg, &halConfig->outputCfg), result);
+    return result;
+}
+
+status_t EffectUtils::effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
+                                              EffectDescriptor* descriptor) {
+    UuidUtils::uuidFromHal(halDescriptor.type, &descriptor->type);
+    UuidUtils::uuidFromHal(halDescriptor.uuid, &descriptor->uuid);
+    descriptor->flags = EnumBitfield<EffectFlags>(halDescriptor.flags);
+    descriptor->cpuLoad = halDescriptor.cpuLoad;
+    descriptor->memoryUsage = halDescriptor.memoryUsage;
+    memcpy(descriptor->name.data(), halDescriptor.name, descriptor->name.size());
+    memcpy(descriptor->implementor.data(), halDescriptor.implementor,
+           descriptor->implementor.size());
+    return NO_ERROR;
+}
+
+status_t EffectUtils::effectDescriptorToHal(const EffectDescriptor& descriptor,
+                                            effect_descriptor_t* halDescriptor) {
+    UuidUtils::uuidToHal(descriptor.type, &halDescriptor->type);
+    UuidUtils::uuidToHal(descriptor.uuid, &halDescriptor->uuid);
+    halDescriptor->flags = static_cast<uint32_t>(descriptor.flags);
+    halDescriptor->cpuLoad = descriptor.cpuLoad;
+    halDescriptor->memoryUsage = descriptor.memoryUsage;
+    memcpy(halDescriptor->name, descriptor.name.data(), descriptor.name.size());
+    memcpy(halDescriptor->implementor, descriptor.implementor.data(),
+           descriptor.implementor.size());
+    return NO_ERROR;
+}
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace effect
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/effect/all-versions/default/util/include/util/EffectUtils.h b/audio/effect/all-versions/default/util/include/util/EffectUtils.h
new file mode 100644
index 0000000..23963b4
--- /dev/null
+++ b/audio/effect/all-versions/default/util/include/util/EffectUtils.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// clang-format off
+#include PATH(android/hardware/audio/effect/FILE_VERSION/types.h)
+// clang-format on
+
+#include <system/audio_effect.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace effect {
+namespace CPP_VERSION {
+namespace implementation {
+
+using namespace ::android::hardware::audio::effect::CPP_VERSION;
+
+struct EffectUtils {
+    static status_t effectBufferConfigFromHal(const buffer_config_t& halConfig, bool isInput,
+                                              EffectBufferConfig* config);
+    static status_t effectBufferConfigToHal(const EffectBufferConfig& config,
+                                            buffer_config_t* halConfig);
+    static status_t effectConfigFromHal(const effect_config_t& halConfig, bool isInput,
+                                        EffectConfig* config);
+    static status_t effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig);
+    static status_t effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
+                                            EffectDescriptor* descriptor);
+    static status_t effectDescriptorToHal(const EffectDescriptor& descriptor,
+                                          effect_descriptor_t* halDescriptor);
+};
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace effect
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp b/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
new file mode 100644
index 0000000..7eb8cd2
--- /dev/null
+++ b/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2021 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 <string>
+
+#include <gtest/gtest.h>
+
+#define LOG_TAG "EffectUtils_Test"
+#include <log/log.h>
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <system/audio_effect.h>
+#include <util/EffectUtils.h>
+#include <xsdc/XsdcSupport.h>
+
+using namespace android;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::effect::CPP_VERSION;
+using ::android::hardware::audio::effect::CPP_VERSION::implementation::EffectUtils;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+static constexpr audio_channel_mask_t kInvalidHalChannelMask = AUDIO_CHANNEL_INVALID;
+static constexpr audio_format_t kInvalidHalFormat = AUDIO_FORMAT_INVALID;
+
+// Not generated automatically because AudioBuffer contains
+// instances of hidl_memory which can't be compared properly
+// in general case due to presence of handles.
+//
+// However, in this particular case, handles must not present
+// thus comparison is possible.
+//
+// operator== must be defined in the same namespace as the structures.
+namespace android {
+namespace hardware {
+namespace audio {
+namespace effect {
+namespace CPP_VERSION {
+inline bool operator==(const AudioBuffer& lhs, const AudioBuffer& rhs) {
+    return lhs.id == rhs.id && lhs.frameCount == rhs.frameCount && lhs.data.handle() == nullptr &&
+           rhs.data.handle() == nullptr;
+}
+
+inline bool operator==(const EffectBufferConfig& lhs, const EffectBufferConfig& rhs) {
+    return lhs.buffer.getDiscriminator() == rhs.buffer.getDiscriminator() &&
+           (lhs.buffer.getDiscriminator() ==
+                    EffectBufferConfig::OptionalBuffer::hidl_discriminator::unspecified ||
+            lhs.buffer.buf() == rhs.buffer.buf()) &&
+           lhs.base == rhs.base && lhs.accessMode == rhs.accessMode;
+}
+
+inline bool operator==(const EffectConfig& lhs, const EffectConfig& rhs) {
+    return lhs.inputCfg == rhs.inputCfg && lhs.outputCfg == rhs.outputCfg;
+}
+}  // namespace CPP_VERSION
+}  // namespace effect
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
+
+TEST(EffectUtils, ConvertInvalidBufferConfig) {
+    buffer_config_t halInvalid;
+    EffectBufferConfig invalidChannelMask;
+    invalidChannelMask.base.channelMask.value("random string");
+    EXPECT_EQ(BAD_VALUE, EffectUtils::effectBufferConfigToHal(invalidChannelMask, &halInvalid));
+    EffectBufferConfig invalidFormat;
+    invalidFormat.base.format.value("random string");
+    EXPECT_EQ(BAD_VALUE, EffectUtils::effectBufferConfigToHal(invalidFormat, &halInvalid));
+
+    buffer_config_t halInvalidChannelMask;
+    EffectBufferConfig invalid;
+    halInvalidChannelMask.channels = kInvalidHalChannelMask;
+    halInvalidChannelMask.mask = EFFECT_CONFIG_CHANNELS;
+    EXPECT_EQ(BAD_VALUE, EffectUtils::effectBufferConfigFromHal(halInvalidChannelMask,
+                                                                false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, EffectUtils::effectBufferConfigFromHal(halInvalidChannelMask,
+                                                                true /*isInput*/, &invalid));
+    buffer_config_t halInvalidFormat;
+    halInvalidFormat.format = (uint8_t)kInvalidHalFormat;
+    halInvalidFormat.mask = EFFECT_CONFIG_FORMAT;
+    EXPECT_EQ(BAD_VALUE, EffectUtils::effectBufferConfigFromHal(halInvalidFormat, false /*isInput*/,
+                                                                &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              EffectUtils::effectBufferConfigFromHal(halInvalidFormat, true /*isInput*/, &invalid));
+}
+
+TEST(EffectUtils, ConvertBufferConfig) {
+    EffectBufferConfig empty;
+    buffer_config_t halEmpty;
+    EXPECT_EQ(NO_ERROR, EffectUtils::effectBufferConfigToHal(empty, &halEmpty));
+    EffectBufferConfig emptyBackOut;
+    EXPECT_EQ(NO_ERROR,
+              EffectUtils::effectBufferConfigFromHal(halEmpty, false /*isInput*/, &emptyBackOut));
+    EXPECT_EQ(empty, emptyBackOut);
+    EffectBufferConfig emptyBackIn;
+    EXPECT_EQ(NO_ERROR,
+              EffectUtils::effectBufferConfigFromHal(halEmpty, true /*isInput*/, &emptyBackIn));
+    EXPECT_EQ(empty, emptyBackIn);
+
+    EffectBufferConfig chanMask;
+    chanMask.base.channelMask.value(toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO));
+    buffer_config_t halChanMask;
+    EXPECT_EQ(NO_ERROR, EffectUtils::effectBufferConfigToHal(chanMask, &halChanMask));
+    EffectBufferConfig chanMaskBack;
+    EXPECT_EQ(NO_ERROR, EffectUtils::effectBufferConfigFromHal(halChanMask, false /*isInput*/,
+                                                               &chanMaskBack));
+    EXPECT_EQ(chanMask, chanMaskBack);
+
+    EffectBufferConfig format;
+    format.base.format.value(toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT));
+    buffer_config_t halFormat;
+    EXPECT_EQ(NO_ERROR, EffectUtils::effectBufferConfigToHal(format, &halFormat));
+    EffectBufferConfig formatBackOut;
+    EXPECT_EQ(NO_ERROR,
+              EffectUtils::effectBufferConfigFromHal(halFormat, false /*isInput*/, &formatBackOut));
+    EXPECT_EQ(format, formatBackOut);
+    EffectBufferConfig formatBackIn;
+    EXPECT_EQ(NO_ERROR,
+              EffectUtils::effectBufferConfigFromHal(halFormat, true /*isInput*/, &formatBackIn));
+    EXPECT_EQ(format, formatBackIn);
+}
+
+TEST(EffectUtils, ConvertDescriptor) {
+    EffectDescriptor desc{};
+    effect_descriptor_t halDesc;
+    EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorToHal(desc, &halDesc));
+    EffectDescriptor descBack;
+    EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorFromHal(halDesc, &descBack));
+    EXPECT_EQ(desc, descBack);
+}