Merge "Add setSatellitePlmn() related HAL apis" into main
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 78b59d4..19b2397 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -73,18 +73,23 @@
         "Configuration.cpp",
         "EngineConfigXmlConverter.cpp",
         "Module.cpp",
+        "ModulePrimary.cpp",
         "SoundDose.cpp",
         "Stream.cpp",
-        "StreamStub.cpp",
         "Telephony.cpp",
+        "alsa/Mixer.cpp",
+        "alsa/ModuleAlsa.cpp",
+        "alsa/StreamAlsa.cpp",
+        "alsa/Utils.cpp",
         "r_submix/ModuleRemoteSubmix.cpp",
         "r_submix/RemoteSubmixUtils.cpp",
         "r_submix/SubmixRoute.cpp",
         "r_submix/StreamRemoteSubmix.cpp",
+        "stub/ModuleStub.cpp",
+        "stub/StreamStub.cpp",
         "usb/ModuleUsb.cpp",
         "usb/StreamUsb.cpp",
         "usb/UsbAlsaMixerControl.cpp",
-        "usb/UsbAlsaUtils.cpp",
     ],
     generated_sources: [
         "audio_policy_configuration_aidl_default",
diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp
index d41ea67..a71c6ea 100644
--- a/audio/aidl/default/Configuration.cpp
+++ b/audio/aidl/default/Configuration.cpp
@@ -144,10 +144,6 @@
 //    - no profiles specified
 //  * "FM Tuner", IN_FM_TUNER
 //    - no profiles specified
-//  * "USB Out", OUT_DEVICE, CONNECTION_USB
-//    - no profiles specified
-//  * "USB In", IN_DEVICE, CONNECTION_USB
-//    - no profiles specified
 //
 // Mix ports:
 //  * "primary output", PRIMARY, 1 max open, 1 max active stream
@@ -172,8 +168,7 @@
 //
 // Routes:
 //  "primary out", "compressed offload" -> "Speaker"
-//  "primary out", "compressed offload" -> "USB Out"
-//  "Built-in Mic", "USB In" -> "primary input"
+//  "Built-in Mic" -> "primary input"
 //  "telephony_tx" -> "Telephony Tx"
 //  "Telephony Rx" -> "telephony_rx"
 //  "FM Tuner" -> "fm_tuner"
@@ -185,14 +180,6 @@
 //  * "Telephony Rx" device port: PCM 24-bit; MONO; 48000
 //  * "FM Tuner" device port: PCM 24-bit; STEREO; 48000
 //
-// Profiles for device port connected state:
-//  * USB Out":
-//    - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
-//    - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
-//  * USB In":
-//    - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
-//    - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
-//
 std::unique_ptr<Configuration> getPrimaryConfiguration() {
     static const Configuration configuration = []() {
         const std::vector<AudioProfile> standardPcmAudioProfiles = {
@@ -252,19 +239,6 @@
                                  AudioChannelLayout::LAYOUT_STEREO, 48000, 0, true,
                                  createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0)));
 
-        AudioPort usbOutDevice =
-                createPort(c.nextPortId++, "USB Out", 0, false,
-                           createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
-                                           AudioDeviceDescription::CONNECTION_USB));
-        c.ports.push_back(usbOutDevice);
-        c.connectedProfiles[usbOutDevice.id] = standardPcmAudioProfiles;
-
-        AudioPort usbInDevice = createPort(c.nextPortId++, "USB In", 0, true,
-                                           createDeviceExt(AudioDeviceType::IN_DEVICE, 0,
-                                                           AudioDeviceDescription::CONNECTION_USB));
-        c.ports.push_back(usbInDevice);
-        c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles;
-
         // Mix ports
 
         AudioPort primaryOutMix = createPort(c.nextPortId++, "primary output",
@@ -323,8 +297,7 @@
         c.ports.push_back(fmTunerInMix);
 
         c.routes.push_back(createRoute({primaryOutMix, compressedOffloadOutMix}, speakerOutDevice));
-        c.routes.push_back(createRoute({primaryOutMix, compressedOffloadOutMix}, usbOutDevice));
-        c.routes.push_back(createRoute({micInDevice, usbInDevice}, primaryInMix));
+        c.routes.push_back(createRoute({micInDevice}, primaryInMix));
         c.routes.push_back(createRoute({telephonyTxOutMix}, telephonyTxOutDevice));
         c.routes.push_back(createRoute({telephonyRxInDevice}, telephonyRxInMix));
         c.routes.push_back(createRoute({fmTunerInDevice}, fmTunerInMix));
@@ -406,22 +379,31 @@
 // Usb configuration:
 //
 // Device ports:
+//  * "USB Device Out", OUT_DEVICE, CONNECTION_USB
+//    - no profiles specified
 //  * "USB Headset Out", OUT_HEADSET, CONNECTION_USB
 //    - no profiles specified
+//  * "USB Device In", IN_DEVICE, CONNECTION_USB
+//    - no profiles specified
 //  * "USB Headset In", IN_HEADSET, CONNECTION_USB
 //    - no profiles specified
 //
 // Mix ports:
-//  * "usb_headset output", 1 max open, 1 max active stream
+//  * "usb_device output", 1 max open, 1 max active stream
 //    - no profiles specified
-//  * "usb_headset input", 1 max open, 1 max active stream
+//  * "usb_device input", 1 max open, 1 max active stream
 //    - no profiles specified
 //
+// Routes:
+//  * "usb_device output" -> "USB Device Out"
+//  * "usb_device output" -> "USB Headset Out"
+//  * "USB Device In", "USB Headset In" -> "usb_device input"
+//
 // Profiles for device port connected state:
-//  * USB Headset Out":
+//  * "USB Device Out", "USB Headset Out":
 //    - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
 //    - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
-//  * USB Headset In":
+//  * "USB Device In", "USB Headset In":
 //    - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
 //    - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
 //
@@ -440,6 +422,13 @@
 
         // Device ports
 
+        AudioPort usbOutDevice =
+                createPort(c.nextPortId++, "USB Device Out", 0, false,
+                           createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
+                                           AudioDeviceDescription::CONNECTION_USB));
+        c.ports.push_back(usbOutDevice);
+        c.connectedProfiles[usbOutDevice.id] = standardPcmAudioProfiles;
+
         AudioPort usbOutHeadset =
                 createPort(c.nextPortId++, "USB Headset Out", 0, false,
                            createDeviceExt(AudioDeviceType::OUT_HEADSET, 0,
@@ -447,6 +436,12 @@
         c.ports.push_back(usbOutHeadset);
         c.connectedProfiles[usbOutHeadset.id] = standardPcmAudioProfiles;
 
+        AudioPort usbInDevice = createPort(c.nextPortId++, "USB Device In", 0, true,
+                                           createDeviceExt(AudioDeviceType::IN_DEVICE, 0,
+                                                           AudioDeviceDescription::CONNECTION_USB));
+        c.ports.push_back(usbInDevice);
+        c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles;
+
         AudioPort usbInHeadset =
                 createPort(c.nextPortId++, "USB Headset In", 0, true,
                            createDeviceExt(AudioDeviceType::IN_HEADSET, 0,
@@ -456,16 +451,110 @@
 
         // Mix ports
 
-        AudioPort usbHeadsetOutMix =
-                createPort(c.nextPortId++, "usb_headset output", 0, false, createPortMixExt(1, 1));
-        c.ports.push_back(usbHeadsetOutMix);
+        AudioPort usbDeviceOutMix =
+                createPort(c.nextPortId++, "usb_device output", 0, false, createPortMixExt(1, 1));
+        c.ports.push_back(usbDeviceOutMix);
 
-        AudioPort usbHeadsetInMix =
-                createPort(c.nextPortId++, "usb_headset input", 0, true, createPortMixExt(1, 1));
-        c.ports.push_back(usbHeadsetInMix);
+        AudioPort usbDeviceInMix =
+                createPort(c.nextPortId++, "usb_device input", 0, true, createPortMixExt(1, 1));
+        c.ports.push_back(usbDeviceInMix);
 
-        c.routes.push_back(createRoute({usbHeadsetOutMix}, usbOutHeadset));
-        c.routes.push_back(createRoute({usbInHeadset}, usbHeadsetInMix));
+        c.routes.push_back(createRoute({usbDeviceOutMix}, usbOutDevice));
+        c.routes.push_back(createRoute({usbDeviceOutMix}, usbOutHeadset));
+        c.routes.push_back(createRoute({usbInDevice, usbInHeadset}, usbDeviceInMix));
+
+        return c;
+    }();
+    return std::make_unique<Configuration>(configuration);
+}
+
+// Stub configuration:
+//
+// Device ports:
+//  * "Test Out", OUT_AFE_PROXY
+//    - no profiles specified
+//  * "Test In", IN_AFE_PROXY
+//    - no profiles specified
+//
+// Mix ports:
+//  * "test output", 1 max open, 1 max active stream
+//    - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
+//  * "compressed offload", DIRECT|COMPRESS_OFFLOAD|NON_BLOCKING, 1 max open, 1 max active stream
+//    - profile MP3; MONO, STEREO; 44100, 48000
+//  * "test input", 2 max open, 2 max active streams
+//    - profile PCM 24-bit; MONO, STEREO, FRONT_BACK;
+//        8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+//
+// Routes:
+//  "test output", "compressed offload" -> "Test Out"
+//  "Test In" -> "test input"
+//
+// Initial port configs:
+//  * "Test Out" device port: PCM 24-bit; STEREO; 48000
+//  * "Test In" device port: PCM 24-bit; MONO; 48000
+//
+std::unique_ptr<Configuration> getStubConfiguration() {
+    static const Configuration configuration = []() {
+        Configuration c;
+
+        // Device ports
+
+        AudioPort testOutDevice = createPort(c.nextPortId++, "Test Out", 0, false,
+                                             createDeviceExt(AudioDeviceType::OUT_AFE_PROXY, 0));
+        c.ports.push_back(testOutDevice);
+        c.initialConfigs.push_back(
+                createPortConfig(testOutDevice.id, testOutDevice.id, PcmType::INT_24_BIT,
+                                 AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false,
+                                 createDeviceExt(AudioDeviceType::OUT_AFE_PROXY, 0)));
+
+        AudioPort testInDevice = createPort(c.nextPortId++, "Test In", 0, true,
+                                            createDeviceExt(AudioDeviceType::IN_AFE_PROXY, 0));
+        c.ports.push_back(testInDevice);
+        c.initialConfigs.push_back(
+                createPortConfig(testInDevice.id, testInDevice.id, PcmType::INT_24_BIT,
+                                 AudioChannelLayout::LAYOUT_MONO, 48000, 0, true,
+                                 createDeviceExt(AudioDeviceType::IN_AFE_PROXY, 0)));
+
+        // Mix ports
+
+        AudioPort testOutMix =
+                createPort(c.nextPortId++, "test output", 0, false, createPortMixExt(1, 1));
+        testOutMix.profiles.push_back(
+                createProfile(PcmType::INT_24_BIT,
+                              {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
+                              {8000, 11025, 16000, 32000, 44100, 48000}));
+        c.ports.push_back(testOutMix);
+
+        AudioPort compressedOffloadOutMix =
+                createPort(c.nextPortId++, "compressed offload",
+                           makeBitPositionFlagMask({AudioOutputFlags::DIRECT,
+                                                    AudioOutputFlags::COMPRESS_OFFLOAD,
+                                                    AudioOutputFlags::NON_BLOCKING}),
+                           false, createPortMixExt(1, 1));
+        compressedOffloadOutMix.profiles.push_back(
+                createProfile(::android::MEDIA_MIMETYPE_AUDIO_MPEG,
+                              {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
+                              {44100, 48000}));
+        c.ports.push_back(compressedOffloadOutMix);
+
+        AudioPort testInMIx =
+                createPort(c.nextPortId++, "test input", 0, true, createPortMixExt(2, 2));
+        testInMIx.profiles.push_back(
+                createProfile(PcmType::INT_16_BIT,
+                              {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
+                               AudioChannelLayout::LAYOUT_FRONT_BACK},
+                              {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
+        testInMIx.profiles.push_back(
+                createProfile(PcmType::INT_24_BIT,
+                              {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
+                               AudioChannelLayout::LAYOUT_FRONT_BACK},
+                              {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
+        c.ports.push_back(testInMIx);
+
+        c.routes.push_back(createRoute({testOutMix, compressedOffloadOutMix}, testOutDevice));
+        c.routes.push_back(createRoute({testInDevice}, testInMIx));
+
+        c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end());
 
         return c;
     }();
diff --git a/audio/aidl/default/EffectConfig.cpp b/audio/aidl/default/EffectConfig.cpp
index f3f674f..730c0bf 100644
--- a/audio/aidl/default/EffectConfig.cpp
+++ b/audio/aidl/default/EffectConfig.cpp
@@ -117,53 +117,59 @@
 
 bool EffectConfig::parseEffect(const tinyxml2::XMLElement& xml) {
     struct EffectLibraries effectLibraries;
-    std::vector<LibraryUuid> libraryUuids;
+    std::vector<Library> libraries;
     std::string name = xml.Attribute("name");
     RETURN_VALUE_IF(name == "", false, "effectsNoName");
 
     LOG(DEBUG) << __func__ << dump(xml);
-    struct LibraryUuid libraryUuid;
+    struct Library library;
     if (std::strcmp(xml.Name(), "effectProxy") == 0) {
         // proxy lib and uuid
-        RETURN_VALUE_IF(!parseLibraryUuid(xml, libraryUuid, true), false, "parseProxyLibFailed");
-        effectLibraries.proxyLibrary = libraryUuid;
+        RETURN_VALUE_IF(!parseLibrary(xml, library, true), false, "parseProxyLibFailed");
+        effectLibraries.proxyLibrary = library;
         // proxy effect libs and UUID
         auto xmlProxyLib = xml.FirstChildElement();
         RETURN_VALUE_IF(!xmlProxyLib, false, "noLibForProxy");
         while (xmlProxyLib) {
-            struct LibraryUuid tempLibraryUuid;
-            RETURN_VALUE_IF(!parseLibraryUuid(*xmlProxyLib, tempLibraryUuid), false,
+            struct Library tempLibrary;
+            RETURN_VALUE_IF(!parseLibrary(*xmlProxyLib, tempLibrary), false,
                             "parseEffectLibFailed");
-            libraryUuids.push_back(std::move(tempLibraryUuid));
+            libraries.push_back(std::move(tempLibrary));
             xmlProxyLib = xmlProxyLib->NextSiblingElement();
         }
     } else {
         // expect only one library if not proxy
-        RETURN_VALUE_IF(!parseLibraryUuid(xml, libraryUuid), false, "parseEffectLibFailed");
-        libraryUuids.push_back(std::move(libraryUuid));
+        RETURN_VALUE_IF(!parseLibrary(xml, library), false, "parseEffectLibFailed");
+        libraries.push_back(std::move(library));
     }
 
-    effectLibraries.libraries = std::move(libraryUuids);
+    effectLibraries.libraries = std::move(libraries);
     mEffectsMap[name] = std::move(effectLibraries);
     return true;
 }
 
-bool EffectConfig::parseLibraryUuid(const tinyxml2::XMLElement& xml,
-                                    struct LibraryUuid& libraryUuid, bool isProxy) {
+bool EffectConfig::parseLibrary(const tinyxml2::XMLElement& xml, struct Library& library,
+                                bool isProxy) {
     // Retrieve library name only if not effectProxy element
     if (!isProxy) {
         const char* name = xml.Attribute("library");
         RETURN_VALUE_IF(!name, false, "noLibraryAttribute");
-        libraryUuid.name = name;
+        library.name = name;
     }
 
     const char* uuidStr = xml.Attribute("uuid");
     RETURN_VALUE_IF(!uuidStr, false, "noUuidAttribute");
-    libraryUuid.uuid = stringToUuid(uuidStr);
-    RETURN_VALUE_IF((libraryUuid.uuid == getEffectUuidZero()), false, "invalidUuidAttribute");
+    library.uuid = stringToUuid(uuidStr);
+    if (const char* typeUuidStr = xml.Attribute("type")) {
+        library.type = stringToUuid(typeUuidStr);
+    }
+    RETURN_VALUE_IF((library.uuid == getEffectUuidZero()), false, "invalidUuidAttribute");
 
-    LOG(DEBUG) << __func__ << (isProxy ? " proxy " : libraryUuid.name) << " : "
-               << ::android::audio::utils::toString(libraryUuid.uuid);
+    LOG(DEBUG) << __func__ << (isProxy ? " proxy " : library.name) << " : uuid "
+               << ::android::audio::utils::toString(library.uuid)
+               << (library.type.has_value()
+                           ? ::android::audio::utils::toString(library.type.value())
+                           : "");
     return true;
 }
 
@@ -241,7 +247,8 @@
     return mProcessingMap;
 }
 
-bool EffectConfig::findUuid(const std::string& xmlEffectName, AudioUuid* uuid) {
+bool EffectConfig::findUuid(const std::pair<std::string, struct EffectLibraries>& effectElem,
+                            AudioUuid* uuid) {
 // Difference from EFFECT_TYPE_LIST_DEF, there could be multiple name mapping to same Effect Type
 #define EFFECT_XML_TYPE_LIST_DEF(V)                        \
     V("acoustic_echo_canceler", AcousticEchoCanceler)      \
@@ -268,6 +275,7 @@
 
 #define GENERATE_MAP_ENTRY_V(s, symbol) {s, &getEffectTypeUuid##symbol},
 
+    const std::string xmlEffectName = effectElem.first;
     typedef const AudioUuid& (*UuidGetter)(void);
     static const std::map<std::string, UuidGetter> uuidMap{
             // std::make_pair("s", &getEffectTypeUuidExtension)};
@@ -276,6 +284,14 @@
         *uuid = (*it->second)();
         return true;
     }
+
+    const auto& libs = effectElem.second.libraries;
+    for (const auto& lib : libs) {
+        if (lib.type.has_value()) {
+            *uuid = lib.type.value();
+            return true;
+        }
+    }
     return false;
 }
 
diff --git a/audio/aidl/default/EffectFactory.cpp b/audio/aidl/default/EffectFactory.cpp
index 8ed62c9..62f3c7e 100644
--- a/audio/aidl/default/EffectFactory.cpp
+++ b/audio/aidl/default/EffectFactory.cpp
@@ -105,7 +105,7 @@
         if (!in_type.has_value() || in_type.value() == procIter.first) {
             Processing process = {.type = procIter.first /* Processing::Type */};
             for (const auto& libs : procIter.second /* std::vector<struct EffectLibraries> */) {
-                for (const auto& lib : libs.libraries /* std::vector<struct LibraryUuid> */) {
+                for (const auto& lib : libs.libraries /* std::vector<struct Library> */) {
                     Descriptor desc;
                     if (libs.proxyLibrary.has_value()) {
                         desc.common.id.proxy = libs.proxyLibrary.value().uuid;
@@ -219,7 +219,7 @@
     return true;
 }
 
-void Factory::createIdentityWithConfig(const EffectConfig::LibraryUuid& configLib,
+void Factory::createIdentityWithConfig(const EffectConfig::Library& configLib,
                                        const AudioUuid& typeUuid,
                                        const std::optional<AudioUuid> proxyUuid) {
     static const auto& libMap = mConfig.getLibraryMap();
@@ -246,8 +246,7 @@
 void Factory::loadEffectLibs() {
     const auto& configEffectsMap = mConfig.getEffectsMap();
     for (const auto& configEffects : configEffectsMap) {
-        if (AudioUuid uuid;
-            EffectConfig::findUuid(configEffects.first /* xml effect name */, &uuid)) {
+        if (AudioUuid type; EffectConfig::findUuid(configEffects /* xml effect */, &type)) {
             const auto& configLibs = configEffects.second;
             std::optional<AudioUuid> proxyUuid;
             if (configLibs.proxyLibrary.has_value()) {
@@ -255,7 +254,7 @@
                 proxyUuid = proxyLib.uuid;
             }
             for (const auto& configLib : configLibs.libraries) {
-                createIdentityWithConfig(configLib, uuid, proxyUuid);
+                createIdentityWithConfig(configLib, type, proxyUuid);
             }
         } else {
             LOG(ERROR) << __func__ << ": can not find type UUID for effect " << configEffects.first
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 48d1458..5478633 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -25,13 +25,12 @@
 #include <android/binder_ibinder_platform.h>
 #include <error/expected_utils.h>
 
-#include "core-impl/Bluetooth.h"
 #include "core-impl/Module.h"
+#include "core-impl/ModulePrimary.h"
 #include "core-impl/ModuleRemoteSubmix.h"
+#include "core-impl/ModuleStub.h"
 #include "core-impl/ModuleUsb.h"
 #include "core-impl/SoundDose.h"
-#include "core-impl/StreamStub.h"
-#include "core-impl/Telephony.h"
 #include "core-impl/utils.h"
 
 using aidl::android::hardware::audio::common::getFrameSizeInBytes;
@@ -110,13 +109,14 @@
 // static
 std::shared_ptr<Module> Module::createInstance(Type type) {
     switch (type) {
-        case Module::Type::USB:
-            return ndk::SharedRefBase::make<ModuleUsb>(type);
-        case Type::R_SUBMIX:
-            return ndk::SharedRefBase::make<ModuleRemoteSubmix>(type);
         case Type::DEFAULT:
-        default:
-            return ndk::SharedRefBase::make<Module>(type);
+            return ndk::SharedRefBase::make<ModulePrimary>();
+        case Type::R_SUBMIX:
+            return ndk::SharedRefBase::make<ModuleRemoteSubmix>();
+        case Type::STUB:
+            return ndk::SharedRefBase::make<ModuleStub>();
+        case Type::USB:
+            return ndk::SharedRefBase::make<ModuleUsb>();
     }
 }
 
@@ -128,6 +128,9 @@
         case Module::Type::R_SUBMIX:
             os << "r_submix";
             break;
+        case Module::Type::STUB:
+            os << "stub";
+            break;
         case Module::Type::USB:
             os << "usb";
             break;
@@ -283,19 +286,28 @@
     return result;
 }
 
+std::unique_ptr<internal::Configuration> Module::initializeConfig() {
+    std::unique_ptr<internal::Configuration> config;
+    switch (getType()) {
+        case Type::DEFAULT:
+            config = std::move(internal::getPrimaryConfiguration());
+            break;
+        case Type::R_SUBMIX:
+            config = std::move(internal::getRSubmixConfiguration());
+            break;
+        case Type::STUB:
+            config = std::move(internal::getStubConfiguration());
+            break;
+        case Type::USB:
+            config = std::move(internal::getUsbConfiguration());
+            break;
+    }
+    return config;
+}
+
 internal::Configuration& Module::getConfig() {
     if (!mConfig) {
-        switch (mType) {
-            case Type::DEFAULT:
-                mConfig = std::move(internal::getPrimaryConfiguration());
-                break;
-            case Type::R_SUBMIX:
-                mConfig = std::move(internal::getRSubmixConfiguration());
-                break;
-            case Type::USB:
-                mConfig = std::move(internal::getUsbConfiguration());
-                break;
-        }
+        mConfig = std::move(initializeConfig());
     }
     return *mConfig;
 }
@@ -395,38 +407,26 @@
 }
 
 ndk::ScopedAStatus Module::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
-    if (!mTelephony) {
-        mTelephony = ndk::SharedRefBase::make<Telephony>();
-    }
-    *_aidl_return = mTelephony.getPtr();
-    LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get();
+    *_aidl_return = nullptr;
+    LOG(DEBUG) << __func__ << ": returning null";
     return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus Module::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
-    if (!mBluetooth) {
-        mBluetooth = ndk::SharedRefBase::make<Bluetooth>();
-    }
-    *_aidl_return = mBluetooth.getPtr();
-    LOG(DEBUG) << __func__ << ": returning instance of IBluetooth: " << _aidl_return->get();
+    *_aidl_return = nullptr;
+    LOG(DEBUG) << __func__ << ": returning null";
     return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus Module::getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
-    if (!mBluetoothA2dp) {
-        mBluetoothA2dp = ndk::SharedRefBase::make<BluetoothA2dp>();
-    }
-    *_aidl_return = mBluetoothA2dp.getPtr();
-    LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
+    *_aidl_return = nullptr;
+    LOG(DEBUG) << __func__ << ": returning null";
     return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus Module::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
-    if (!mBluetoothLe) {
-        mBluetoothLe = ndk::SharedRefBase::make<BluetoothLe>();
-    }
-    *_aidl_return = mBluetoothLe.getPtr();
-    LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
+    *_aidl_return = nullptr;
+    LOG(DEBUG) << __func__ << ": returning null";
     return ndk::ScopedAStatus::ok();
 }
 
@@ -1334,22 +1334,6 @@
     return mIsMmapSupported.value();
 }
 
-ndk::ScopedAStatus Module::createInputStream(const SinkMetadata& sinkMetadata,
-                                             StreamContext&& context,
-                                             const std::vector<MicrophoneInfo>& microphones,
-                                             std::shared_ptr<StreamIn>* result) {
-    return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
-                                              microphones);
-}
-
-ndk::ScopedAStatus Module::createOutputStream(const SourceMetadata& sourceMetadata,
-                                              StreamContext&& context,
-                                              const std::optional<AudioOffloadInfo>& offloadInfo,
-                                              std::shared_ptr<StreamOut>* result) {
-    return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
-                                               offloadInfo);
-}
-
 ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort __unused) {
     LOG(VERBOSE) << __func__ << ": do nothing and return ok";
     return ndk::ScopedAStatus::ok();
diff --git a/audio/aidl/default/ModulePrimary.cpp b/audio/aidl/default/ModulePrimary.cpp
new file mode 100644
index 0000000..cbb6730
--- /dev/null
+++ b/audio/aidl/default/ModulePrimary.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 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 <vector>
+
+#define LOG_TAG "AHAL_ModulePrimary"
+#include <Utils.h>
+#include <android-base/logging.h>
+
+#include "core-impl/ModulePrimary.h"
+#include "core-impl/StreamStub.h"
+#include "core-impl/Telephony.h"
+
+using aidl::android::hardware::audio::common::SinkMetadata;
+using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::media::audio::common::AudioOffloadInfo;
+using aidl::android::media::audio::common::AudioPort;
+using aidl::android::media::audio::common::AudioPortConfig;
+using aidl::android::media::audio::common::MicrophoneInfo;
+
+namespace aidl::android::hardware::audio::core {
+
+ndk::ScopedAStatus ModulePrimary::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
+    if (!mTelephony) {
+        mTelephony = ndk::SharedRefBase::make<Telephony>();
+    }
+    *_aidl_return = mTelephony.getPtr();
+    LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModulePrimary::createInputStream(const SinkMetadata& sinkMetadata,
+                                                    StreamContext&& context,
+                                                    const std::vector<MicrophoneInfo>& microphones,
+                                                    std::shared_ptr<StreamIn>* result) {
+    return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
+                                              microphones);
+}
+
+ndk::ScopedAStatus ModulePrimary::createOutputStream(
+        const SourceMetadata& sourceMetadata, StreamContext&& context,
+        const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
+    return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
+                                               offloadInfo);
+}
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 251dea0..215de94 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -110,10 +110,12 @@
     if (isConnected) {
         reply->observable.frames = mFrameCount;
         reply->observable.timeNs = ::android::elapsedRealtimeNano();
-    } else {
-        reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
-        reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
+        if (auto status = mDriver->getPosition(&reply->observable); status == ::android::OK) {
+            return;
+        }
     }
+    reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
+    reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
 }
 
 void StreamWorkerCommonLogic::populateReplyWrongState(
diff --git a/audio/aidl/default/alsa/Mixer.cpp b/audio/aidl/default/alsa/Mixer.cpp
new file mode 100644
index 0000000..f0393e3
--- /dev/null
+++ b/audio/aidl/default/alsa/Mixer.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#define LOG_TAG "AHAL_AlsaMixer"
+#include <android-base/logging.h>
+
+#include <cmath>
+
+#include <android/binder_status.h>
+
+#include "Mixer.h"
+
+namespace aidl::android::hardware::audio::core::alsa {
+
+//-----------------------------------------------------------------------------
+
+MixerControl::MixerControl(struct mixer_ctl* ctl)
+    : mCtl(ctl),
+      mNumValues(mixer_ctl_get_num_values(ctl)),
+      mMinValue(mixer_ctl_get_range_min(ctl)),
+      mMaxValue(mixer_ctl_get_range_max(ctl)) {}
+
+unsigned int MixerControl::getNumValues() const {
+    return mNumValues;
+}
+
+int MixerControl::getMaxValue() const {
+    return mMaxValue;
+}
+
+int MixerControl::getMinValue() const {
+    return mMinValue;
+}
+
+int MixerControl::setArray(const void* array, size_t count) {
+    const std::lock_guard guard(mLock);
+    return mixer_ctl_set_array(mCtl, array, count);
+}
+
+//-----------------------------------------------------------------------------
+
+// static
+const std::map<Mixer::Control, std::vector<Mixer::ControlNamesAndExpectedCtlType>>
+        Mixer::kPossibleControls = {
+                {Mixer::MASTER_SWITCH, {{"Master Playback Switch", MIXER_CTL_TYPE_BOOL}}},
+                {Mixer::MASTER_VOLUME, {{"Master Playback Volume", MIXER_CTL_TYPE_INT}}},
+                {Mixer::HW_VOLUME,
+                 {{"Headphone Playback Volume", MIXER_CTL_TYPE_INT},
+                  {"Headset Playback Volume", MIXER_CTL_TYPE_INT},
+                  {"PCM Playback Volume", MIXER_CTL_TYPE_INT}}}};
+
+// static
+std::map<Mixer::Control, std::shared_ptr<MixerControl>> Mixer::initializeMixerControls(
+        struct mixer* mixer) {
+    std::map<Mixer::Control, std::shared_ptr<MixerControl>> mixerControls;
+    std::string mixerCtlNames;
+    for (const auto& [control, possibleCtls] : kPossibleControls) {
+        for (const auto& [ctlName, expectedCtlType] : possibleCtls) {
+            struct mixer_ctl* ctl = mixer_get_ctl_by_name(mixer, ctlName.c_str());
+            if (ctl != nullptr && mixer_ctl_get_type(ctl) == expectedCtlType) {
+                mixerControls.emplace(control, std::make_unique<MixerControl>(ctl));
+                if (!mixerCtlNames.empty()) {
+                    mixerCtlNames += ",";
+                }
+                mixerCtlNames += ctlName;
+                break;
+            }
+        }
+    }
+    LOG(DEBUG) << __func__ << ": available mixer control names=[" << mixerCtlNames << "]";
+    return mixerControls;
+}
+
+Mixer::Mixer(struct mixer* mixer)
+    : mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {}
+
+Mixer::~Mixer() {
+    mixer_close(mMixer);
+}
+
+namespace {
+
+int volumeFloatToInteger(float fValue, int maxValue, int minValue) {
+    return minValue + std::ceil((maxValue - minValue) * fValue);
+}
+
+}  // namespace
+
+ndk::ScopedAStatus Mixer::setMasterMute(bool muted) {
+    auto it = mMixerControls.find(Mixer::MASTER_SWITCH);
+    if (it == mMixerControls.end()) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+    const int numValues = it->second->getNumValues();
+    std::vector<int> values(numValues, muted ? 0 : 1);
+    if (int err = it->second->setArray(values.data(), numValues); err != 0) {
+        LOG(ERROR) << __func__ << ": failed to set master mute, err=" << err;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Mixer::setMasterVolume(float volume) {
+    auto it = mMixerControls.find(Mixer::MASTER_VOLUME);
+    if (it == mMixerControls.end()) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+    const int numValues = it->second->getNumValues();
+    std::vector<int> values(numValues, volumeFloatToInteger(volume, it->second->getMaxValue(),
+                                                            it->second->getMinValue()));
+    if (int err = it->second->setArray(values.data(), numValues); err != 0) {
+        LOG(ERROR) << __func__ << ": failed to set master volume, err=" << err;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Mixer::setVolumes(const std::vector<float>& volumes) {
+    auto it = mMixerControls.find(Mixer::HW_VOLUME);
+    if (it == mMixerControls.end()) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+    const int numValues = it->second->getNumValues();
+    if (numValues < 0) {
+        LOG(FATAL) << __func__ << ": negative number of values: " << numValues;
+    }
+    const int maxValue = it->second->getMaxValue();
+    const int minValue = it->second->getMinValue();
+    std::vector<int> values;
+    size_t i = 0;
+    for (; i < static_cast<size_t>(numValues) && i < values.size(); ++i) {
+        values.emplace_back(volumeFloatToInteger(volumes[i], maxValue, minValue));
+    }
+    if (int err = it->second->setArray(values.data(), values.size()); err != 0) {
+        LOG(ERROR) << __func__ << ": failed to set volume, err=" << err;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/alsa/Mixer.h b/audio/aidl/default/alsa/Mixer.h
new file mode 100644
index 0000000..de9e6f4
--- /dev/null
+++ b/audio/aidl/default/alsa/Mixer.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 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
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+#include <android/binder_auto_utils.h>
+
+extern "C" {
+#include <tinyalsa/mixer.h>
+}
+
+namespace aidl::android::hardware::audio::core::alsa {
+
+class MixerControl {
+  public:
+    explicit MixerControl(struct mixer_ctl* ctl);
+
+    unsigned int getNumValues() const;
+    int getMaxValue() const;
+    int getMinValue() const;
+    int setArray(const void* array, size_t count);
+
+  private:
+    std::mutex mLock;
+    // The mixer_ctl object is owned by ALSA and will be released when the mixer is closed.
+    struct mixer_ctl* mCtl GUARDED_BY(mLock);
+    const unsigned int mNumValues;
+    const int mMinValue;
+    const int mMaxValue;
+};
+
+class Mixer {
+  public:
+    explicit Mixer(struct mixer* mixer);
+
+    ~Mixer();
+
+    bool isValid() const { return mMixer != nullptr; }
+
+    ndk::ScopedAStatus setMasterMute(bool muted);
+    ndk::ScopedAStatus setMasterVolume(float volume);
+    ndk::ScopedAStatus setVolumes(const std::vector<float>& volumes);
+
+  private:
+    enum Control {
+        MASTER_SWITCH,
+        MASTER_VOLUME,
+        HW_VOLUME,
+    };
+    using ControlNamesAndExpectedCtlType = std::pair<std::string, enum mixer_ctl_type>;
+    static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
+    static std::map<Control, std::shared_ptr<MixerControl>> initializeMixerControls(
+            struct mixer* mixer);
+
+    // The mixer object is owned by ALSA and will be released when the mixer is closed.
+    struct mixer* mMixer;
+    // `mMixerControls` will only be initialized in constructor. After that, it wil only be
+    // read but not be modified.
+    const std::map<Control, std::shared_ptr<MixerControl>> mMixerControls;
+};
+
+}  // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/alsa/ModuleAlsa.cpp b/audio/aidl/default/alsa/ModuleAlsa.cpp
new file mode 100644
index 0000000..8e75d56
--- /dev/null
+++ b/audio/aidl/default/alsa/ModuleAlsa.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#define LOG_TAG "AHAL_ModuleAlsa"
+
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "Utils.h"
+#include "core-impl/ModuleAlsa.h"
+
+extern "C" {
+#include "alsa_device_profile.h"
+}
+
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioPort;
+using aidl::android::media::audio::common::AudioProfile;
+
+namespace aidl::android::hardware::audio::core {
+
+ndk::ScopedAStatus ModuleAlsa::populateConnectedDevicePort(AudioPort* audioPort) {
+    auto deviceProfile = alsa::getDeviceProfile(*audioPort);
+    if (!deviceProfile.has_value()) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    auto profile = alsa::readAlsaDeviceInfo(*deviceProfile);
+    if (!profile.has_value()) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+
+    std::vector<AudioChannelLayout> channels = alsa::getChannelMasksFromProfile(&profile.value());
+    std::vector<int> sampleRates = alsa::getSampleRatesFromProfile(&profile.value());
+
+    for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) &&
+                       profile->formats[i] != PCM_FORMAT_INVALID;
+         ++i) {
+        auto audioFormatDescription =
+                alsa::c2aidl_pcm_format_AudioFormatDescription(profile->formats[i]);
+        if (audioFormatDescription.type == AudioFormatType::DEFAULT) {
+            LOG(WARNING) << __func__ << ": unknown pcm type=" << profile->formats[i];
+            continue;
+        }
+        AudioProfile audioProfile = {.format = audioFormatDescription,
+                                     .channelMasks = channels,
+                                     .sampleRates = sampleRates};
+        audioPort->profiles.push_back(std::move(audioProfile));
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/alsa/StreamAlsa.cpp b/audio/aidl/default/alsa/StreamAlsa.cpp
new file mode 100644
index 0000000..17c7feb
--- /dev/null
+++ b/audio/aidl/default/alsa/StreamAlsa.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 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 <limits>
+
+#define LOG_TAG "AHAL_StreamAlsa"
+#include <android-base/logging.h>
+
+#include <Utils.h>
+#include <audio_utils/clock.h>
+#include <error/expected_utils.h>
+
+#include "core-impl/StreamAlsa.h"
+
+namespace aidl::android::hardware::audio::core {
+
+StreamAlsa::StreamAlsa(const Metadata& metadata, StreamContext&& context)
+    : StreamCommonImpl(metadata, std::move(context)),
+      mFrameSizeBytes(getContext().getFrameSize()),
+      mIsInput(isInput(metadata)),
+      mConfig(alsa::getPcmConfig(getContext(), mIsInput)) {}
+
+::android::status_t StreamAlsa::init() {
+    return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
+}
+
+::android::status_t StreamAlsa::standby() {
+    mAlsaDeviceProxies.clear();
+    return ::android::OK;
+}
+
+::android::status_t StreamAlsa::start() {
+    decltype(mAlsaDeviceProxies) alsaDeviceProxies;
+    for (const auto& device : getDeviceProfiles()) {
+        auto profile = alsa::readAlsaDeviceInfo(device);
+        if (!profile.has_value()) {
+            LOG(ERROR) << __func__ << ": unable to read device info, device address=" << device;
+            return ::android::UNKNOWN_ERROR;
+        }
+
+        auto proxy = alsa::makeDeviceProxy();
+        // Always ask for alsa configure as required since the configuration should be supported
+        // by the connected device. That is guaranteed by `setAudioPortConfig` and `setAudioPatch`.
+        if (int err = proxy_prepare(proxy.get(), &profile.value(),
+                                    const_cast<struct pcm_config*>(&mConfig.value()),
+                                    true /*require_exact_match*/);
+            err != 0) {
+            LOG(ERROR) << __func__ << ": fail to prepare for device address=" << device
+                       << " error=" << err;
+            return ::android::UNKNOWN_ERROR;
+        }
+        if (int err = proxy_open(proxy.get()); err != 0) {
+            LOG(ERROR) << __func__ << ": failed to open device, address=" << device
+                       << " error=" << err;
+            return ::android::UNKNOWN_ERROR;
+        }
+        alsaDeviceProxies.push_back(std::move(proxy));
+    }
+    mAlsaDeviceProxies = std::move(alsaDeviceProxies);
+    return ::android::OK;
+}
+
+::android::status_t StreamAlsa::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
+                                         int32_t* latencyMs) {
+    const size_t bytesToTransfer = frameCount * mFrameSizeBytes;
+    unsigned maxLatency = 0;
+    if (mIsInput) {
+        if (mAlsaDeviceProxies.empty()) {
+            LOG(FATAL) << __func__ << ": no input devices";
+            return ::android::NO_INIT;
+        }
+        // For input case, only support single device.
+        proxy_read(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer);
+        maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
+    } else {
+        for (auto& proxy : mAlsaDeviceProxies) {
+            proxy_write(proxy.get(), buffer, bytesToTransfer);
+            maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get()));
+        }
+    }
+    *actualFrameCount = frameCount;
+    maxLatency = std::min(maxLatency, static_cast<unsigned>(std::numeric_limits<int32_t>::max()));
+    *latencyMs = maxLatency;
+    return ::android::OK;
+}
+
+::android::status_t StreamAlsa::getPosition(StreamDescriptor::Position* position) {
+    if (mAlsaDeviceProxies.empty()) {
+        LOG(FATAL) << __func__ << ": no input devices";
+        return ::android::NO_INIT;
+    }
+    if (mIsInput) {
+        if (int ret = proxy_get_capture_position(mAlsaDeviceProxies[0].get(), &position->frames,
+                                                 &position->timeNs);
+            ret != 0) {
+            LOG(WARNING) << __func__ << ": failed to retrieve capture position: " << ret;
+            return ::android::INVALID_OPERATION;
+        }
+    } else {
+        uint64_t hwFrames;
+        struct timespec timestamp;
+        if (int ret = proxy_get_presentation_position(mAlsaDeviceProxies[0].get(), &hwFrames,
+                                                      &timestamp);
+            ret == 0) {
+            if (hwFrames > std::numeric_limits<int64_t>::max()) {
+                hwFrames -= std::numeric_limits<int64_t>::max();
+            }
+            position->frames = static_cast<int64_t>(hwFrames);
+            position->timeNs = audio_utils_ns_from_timespec(&timestamp);
+        } else {
+            LOG(WARNING) << __func__ << ": failed to retrieve presentation position: " << ret;
+            return ::android::INVALID_OPERATION;
+        }
+    }
+    return ::android::OK;
+}
+
+void StreamAlsa::shutdown() {
+    mAlsaDeviceProxies.clear();
+}
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/alsa/Utils.cpp b/audio/aidl/default/alsa/Utils.cpp
new file mode 100644
index 0000000..162f852
--- /dev/null
+++ b/audio/aidl/default/alsa/Utils.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2023 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 <map>
+#include <set>
+
+#define LOG_TAG "AHAL_AlsaUtils"
+#include <Utils.h>
+#include <aidl/android/media/audio/common/AudioFormatType.h>
+#include <aidl/android/media/audio/common/PcmType.h>
+#include <android-base/logging.h>
+
+#include "Utils.h"
+#include "core-impl/utils.h"
+
+using aidl::android::hardware::audio::common::getChannelCount;
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioDeviceAddress;
+using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioIoFlags;
+using aidl::android::media::audio::common::AudioPortExt;
+using aidl::android::media::audio::common::PcmType;
+
+namespace aidl::android::hardware::audio::core::alsa {
+
+namespace {
+
+using AudioChannelCountToMaskMap = std::map<unsigned int, AudioChannelLayout>;
+using AudioFormatDescToPcmFormatMap = std::map<AudioFormatDescription, enum pcm_format>;
+using PcmFormatToAudioFormatDescMap = std::map<enum pcm_format, AudioFormatDescription>;
+
+AudioChannelLayout getInvalidChannelLayout() {
+    static const AudioChannelLayout invalidChannelLayout =
+            AudioChannelLayout::make<AudioChannelLayout::Tag::invalid>(0);
+    return invalidChannelLayout;
+}
+
+static AudioChannelCountToMaskMap make_ChannelCountToMaskMap(
+        const std::set<AudioChannelLayout>& channelMasks) {
+    AudioChannelCountToMaskMap channelMaskToCountMap;
+    for (const auto& channelMask : channelMasks) {
+        channelMaskToCountMap.emplace(getChannelCount(channelMask), channelMask);
+    }
+    return channelMaskToCountMap;
+}
+
+#define DEFINE_CHANNEL_LAYOUT_MASK(n) \
+    AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(AudioChannelLayout::LAYOUT_##n)
+
+const AudioChannelCountToMaskMap& getSupportedChannelOutLayoutMap() {
+    static const std::set<AudioChannelLayout> supportedOutChannelLayouts = {
+            DEFINE_CHANNEL_LAYOUT_MASK(MONO),          DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
+            DEFINE_CHANNEL_LAYOUT_MASK(2POINT1),       DEFINE_CHANNEL_LAYOUT_MASK(QUAD),
+            DEFINE_CHANNEL_LAYOUT_MASK(PENTA),         DEFINE_CHANNEL_LAYOUT_MASK(5POINT1),
+            DEFINE_CHANNEL_LAYOUT_MASK(6POINT1),       DEFINE_CHANNEL_LAYOUT_MASK(7POINT1),
+            DEFINE_CHANNEL_LAYOUT_MASK(7POINT1POINT4), DEFINE_CHANNEL_LAYOUT_MASK(22POINT2),
+    };
+    static const AudioChannelCountToMaskMap outLayouts =
+            make_ChannelCountToMaskMap(supportedOutChannelLayouts);
+    return outLayouts;
+}
+
+const AudioChannelCountToMaskMap& getSupportedChannelInLayoutMap() {
+    static const std::set<AudioChannelLayout> supportedInChannelLayouts = {
+            DEFINE_CHANNEL_LAYOUT_MASK(MONO),
+            DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
+    };
+    static const AudioChannelCountToMaskMap inLayouts =
+            make_ChannelCountToMaskMap(supportedInChannelLayouts);
+    return inLayouts;
+}
+
+#undef DEFINE_CHANNEL_LAYOUT_MASK
+#define DEFINE_CHANNEL_INDEX_MASK(n) \
+    AudioChannelLayout::make<AudioChannelLayout::Tag::indexMask>(AudioChannelLayout::INDEX_MASK_##n)
+
+const AudioChannelCountToMaskMap& getSupportedChannelIndexLayoutMap() {
+    static const std::set<AudioChannelLayout> supportedIndexChannelLayouts = {
+            DEFINE_CHANNEL_INDEX_MASK(1),  DEFINE_CHANNEL_INDEX_MASK(2),
+            DEFINE_CHANNEL_INDEX_MASK(3),  DEFINE_CHANNEL_INDEX_MASK(4),
+            DEFINE_CHANNEL_INDEX_MASK(5),  DEFINE_CHANNEL_INDEX_MASK(6),
+            DEFINE_CHANNEL_INDEX_MASK(7),  DEFINE_CHANNEL_INDEX_MASK(8),
+            DEFINE_CHANNEL_INDEX_MASK(9),  DEFINE_CHANNEL_INDEX_MASK(10),
+            DEFINE_CHANNEL_INDEX_MASK(11), DEFINE_CHANNEL_INDEX_MASK(12),
+            DEFINE_CHANNEL_INDEX_MASK(13), DEFINE_CHANNEL_INDEX_MASK(14),
+            DEFINE_CHANNEL_INDEX_MASK(15), DEFINE_CHANNEL_INDEX_MASK(16),
+            DEFINE_CHANNEL_INDEX_MASK(17), DEFINE_CHANNEL_INDEX_MASK(18),
+            DEFINE_CHANNEL_INDEX_MASK(19), DEFINE_CHANNEL_INDEX_MASK(20),
+            DEFINE_CHANNEL_INDEX_MASK(21), DEFINE_CHANNEL_INDEX_MASK(22),
+            DEFINE_CHANNEL_INDEX_MASK(23), DEFINE_CHANNEL_INDEX_MASK(24),
+    };
+    static const AudioChannelCountToMaskMap indexLayouts =
+            make_ChannelCountToMaskMap(supportedIndexChannelLayouts);
+    return indexLayouts;
+}
+
+#undef DEFINE_CHANNEL_INDEX_MASK
+
+AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
+    AudioFormatDescription result;
+    result.type = type;
+    return result;
+}
+
+AudioFormatDescription make_AudioFormatDescription(PcmType pcm) {
+    auto result = make_AudioFormatDescription(AudioFormatType::PCM);
+    result.pcm = pcm;
+    return result;
+}
+
+const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() {
+    static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = {
+            {make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8},
+            {make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE},
+            {make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE},
+            {make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE},
+            {make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE},
+            {make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE},
+    };
+    return formatDescToPcmFormatMap;
+}
+
+static PcmFormatToAudioFormatDescMap make_PcmFormatToAudioFormatDescMap(
+        const AudioFormatDescToPcmFormatMap& formatDescToPcmFormatMap) {
+    PcmFormatToAudioFormatDescMap result;
+    for (const auto& formatPair : formatDescToPcmFormatMap) {
+        result.emplace(formatPair.second, formatPair.first);
+    }
+    return result;
+}
+
+const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() {
+    static const PcmFormatToAudioFormatDescMap pcmFormatToFormatDescMap =
+            make_PcmFormatToAudioFormatDescMap(getAudioFormatDescriptorToPcmFormatMap());
+    return pcmFormatToFormatDescMap;
+}
+
+}  // namespace
+
+std::ostream& operator<<(std::ostream& os, const DeviceProfile& device) {
+    return os << "<" << device.card << "," << device.device << ">";
+}
+
+AudioChannelLayout getChannelLayoutMaskFromChannelCount(unsigned int channelCount, int isInput) {
+    return findValueOrDefault(
+            isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
+            channelCount, getInvalidChannelLayout());
+}
+
+AudioChannelLayout getChannelIndexMaskFromChannelCount(unsigned int channelCount) {
+    return findValueOrDefault(getSupportedChannelIndexLayoutMap(), channelCount,
+                              getInvalidChannelLayout());
+}
+
+unsigned int getChannelCountFromChannelMask(const AudioChannelLayout& channelMask, bool isInput) {
+    switch (channelMask.getTag()) {
+        case AudioChannelLayout::Tag::layoutMask: {
+            return findKeyOrDefault(
+                    isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
+                    static_cast<unsigned>(getChannelCount(channelMask)), 0u /*defaultValue*/);
+        }
+        case AudioChannelLayout::Tag::indexMask: {
+            return findKeyOrDefault(getSupportedChannelIndexLayoutMap(),
+                                    static_cast<unsigned>(getChannelCount(channelMask)),
+                                    0u /*defaultValue*/);
+        }
+        case AudioChannelLayout::Tag::none:
+        case AudioChannelLayout::Tag::invalid:
+        case AudioChannelLayout::Tag::voiceMask:
+        default:
+            return 0;
+    }
+}
+
+std::vector<AudioChannelLayout> getChannelMasksFromProfile(const alsa_device_profile* profile) {
+    const bool isInput = profile->direction == PCM_IN;
+    std::vector<AudioChannelLayout> channels;
+    for (size_t i = 0; i < AUDIO_PORT_MAX_CHANNEL_MASKS && profile->channel_counts[i] != 0; ++i) {
+        auto layoutMask =
+                alsa::getChannelLayoutMaskFromChannelCount(profile->channel_counts[i], isInput);
+        if (layoutMask.getTag() == AudioChannelLayout::Tag::layoutMask) {
+            channels.push_back(layoutMask);
+        }
+        auto indexMask = alsa::getChannelIndexMaskFromChannelCount(profile->channel_counts[i]);
+        if (indexMask.getTag() == AudioChannelLayout::Tag::indexMask) {
+            channels.push_back(indexMask);
+        }
+    }
+    return channels;
+}
+
+std::optional<DeviceProfile> getDeviceProfile(
+        const ::aidl::android::media::audio::common::AudioDevice& audioDevice, bool isInput) {
+    if (audioDevice.address.getTag() != AudioDeviceAddress::Tag::alsa) {
+        LOG(ERROR) << __func__ << ": not alsa address: " << audioDevice.toString();
+        return std::nullopt;
+    }
+    auto& alsaAddress = audioDevice.address.get<AudioDeviceAddress::Tag::alsa>();
+    if (alsaAddress.size() != 2 || alsaAddress[0] < 0 || alsaAddress[1] < 0) {
+        LOG(ERROR) << __func__
+                   << ": malformed alsa address: " << ::android::internal::ToString(alsaAddress);
+        return std::nullopt;
+    }
+    return DeviceProfile{.card = alsaAddress[0],
+                         .device = alsaAddress[1],
+                         .direction = isInput ? PCM_IN : PCM_OUT};
+}
+
+std::optional<DeviceProfile> getDeviceProfile(
+        const ::aidl::android::media::audio::common::AudioPort& audioPort) {
+    if (audioPort.ext.getTag() != AudioPortExt::Tag::device) {
+        LOG(ERROR) << __func__ << ": port id " << audioPort.id << " is not a device port";
+        return std::nullopt;
+    }
+    auto& devicePort = audioPort.ext.get<AudioPortExt::Tag::device>();
+    return getDeviceProfile(devicePort.device, audioPort.flags.getTag() == AudioIoFlags::input);
+}
+
+std::optional<struct pcm_config> getPcmConfig(const StreamContext& context, bool isInput) {
+    struct pcm_config config;
+    config.channels = alsa::getChannelCountFromChannelMask(context.getChannelLayout(), isInput);
+    if (config.channels == 0) {
+        LOG(ERROR) << __func__ << ": invalid channel=" << context.getChannelLayout().toString();
+        return std::nullopt;
+    }
+    config.format = alsa::aidl2c_AudioFormatDescription_pcm_format(context.getFormat());
+    if (config.format == PCM_FORMAT_INVALID) {
+        LOG(ERROR) << __func__ << ": invalid format=" << context.getFormat().toString();
+        return std::nullopt;
+    }
+    config.rate = context.getSampleRate();
+    if (config.rate == 0) {
+        LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate;
+        return std::nullopt;
+    }
+    return config;
+}
+
+std::vector<int> getSampleRatesFromProfile(const alsa_device_profile* profile) {
+    std::vector<int> sampleRates;
+    for (int i = 0; i < std::min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) &&
+                    profile->sample_rates[i] != 0;
+         i++) {
+        sampleRates.push_back(profile->sample_rates[i]);
+    }
+    return sampleRates;
+}
+
+DeviceProxy makeDeviceProxy() {
+    return DeviceProxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) {
+        if (proxy != nullptr) {
+            proxy_close(proxy);
+            delete proxy;
+        }
+    });
+}
+
+std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile) {
+    alsa_device_profile profile;
+    profile_init(&profile, deviceProfile.direction);
+    profile.card = deviceProfile.card;
+    profile.device = deviceProfile.device;
+    if (!profile_read_device_info(&profile)) {
+        LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card
+                   << ", device=" << profile.device;
+        return std::nullopt;
+    }
+    return profile;
+}
+
+AudioFormatDescription c2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy) {
+    return findValueOrDefault(getPcmFormatToAudioFormatDescMap(), legacy, AudioFormatDescription());
+}
+
+pcm_format aidl2c_AudioFormatDescription_pcm_format(const AudioFormatDescription& aidl) {
+    return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID);
+}
+
+}  // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/alsa/Utils.h b/audio/aidl/default/alsa/Utils.h
new file mode 100644
index 0000000..c1b9b38
--- /dev/null
+++ b/audio/aidl/default/alsa/Utils.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 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
+
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <vector>
+
+#include <aidl/android/media/audio/common/AudioChannelLayout.h>
+#include <aidl/android/media/audio/common/AudioFormatDescription.h>
+#include <aidl/android/media/audio/common/AudioPort.h>
+
+#include "core-impl/Stream.h"
+
+extern "C" {
+#include <tinyalsa/pcm.h>
+#include "alsa_device_profile.h"
+#include "alsa_device_proxy.h"
+}
+
+namespace aidl::android::hardware::audio::core::alsa {
+
+struct DeviceProfile {
+    int card;
+    int device;
+    int direction; /* PCM_OUT or PCM_IN */
+};
+std::ostream& operator<<(std::ostream& os, const DeviceProfile& device);
+using DeviceProxyDeleter = std::function<void(alsa_device_proxy*)>;
+using DeviceProxy = std::unique_ptr<alsa_device_proxy, DeviceProxyDeleter>;
+
+::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount(
+        unsigned int channelCount, int isInput);
+::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount(
+        unsigned int channelCount);
+unsigned int getChannelCountFromChannelMask(
+        const ::aidl::android::media::audio::common::AudioChannelLayout& channelMask, bool isInput);
+std::vector<::aidl::android::media::audio::common::AudioChannelLayout> getChannelMasksFromProfile(
+        const alsa_device_profile* profile);
+std::optional<DeviceProfile> getDeviceProfile(
+        const ::aidl::android::media::audio::common::AudioDevice& audioDevice, bool isInput);
+std::optional<DeviceProfile> getDeviceProfile(
+        const ::aidl::android::media::audio::common::AudioPort& audioPort);
+std::optional<struct pcm_config> getPcmConfig(const StreamContext& context, bool isInput);
+std::vector<int> getSampleRatesFromProfile(const alsa_device_profile* profile);
+DeviceProxy makeDeviceProxy();
+std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile);
+
+::aidl::android::media::audio::common::AudioFormatDescription
+c2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy);
+pcm_format aidl2c_AudioFormatDescription_pcm_format(
+        const ::aidl::android::media::audio::common::AudioFormatDescription& aidl);
+
+}  // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/android.hardware.audio.service-aidl.xml b/audio/aidl/default/android.hardware.audio.service-aidl.xml
index 9636a58..c9d6314 100644
--- a/audio/aidl/default/android.hardware.audio.service-aidl.xml
+++ b/audio/aidl/default/android.hardware.audio.service-aidl.xml
@@ -12,6 +12,11 @@
   <hal format="aidl">
     <name>android.hardware.audio.core</name>
     <version>1</version>
+    <fqname>IModule/stub</fqname>
+  </hal>
+  <hal format="aidl">
+    <name>android.hardware.audio.core</name>
+    <version>1</version>
     <fqname>IModule/usb</fqname>
   </hal>
   <hal format="aidl">
diff --git a/audio/aidl/default/audio_effects_config.xml b/audio/aidl/default/audio_effects_config.xml
index 6627ae7..00de797 100644
--- a/audio/aidl/default/audio_effects_config.xml
+++ b/audio/aidl/default/audio_effects_config.xml
@@ -50,7 +50,8 @@
     </libraries>
 
     <!-- list of effects to load.
-         Each "effect" element must contain a "name", "library" and a "uuid" attribute.
+         Each "effect" element must contain a "name", "library" and a "uuid" attribute, an optional
+         "type" attribute can be used to add any customized effect type.
          The value of the "library" attribute must correspond to the name of one library element in
          the "libraries" element.
          The "name" attribute used to specific effect type, and should be mapping to a key of
@@ -62,8 +63,8 @@
          result of IFactory.queryEffects() to decide which effect implementation should be part of
          proxy and which not.
 
-         Only "name", "library", and "uuid" attributes in "effects" element are meaningful and
-         parsed out by EffectConfig class, all other attributes are ignored.
+         Only "name", "library", "uuid", and "type" attributes in "effects" element are meaningful
+          and parsed out by EffectConfig class, all other attributes are ignored.
          Only "name" and "uuid" attributes in "effectProxy" element are meaningful and parsed out
          by EffectConfig class, all other attributes are ignored.
     -->
@@ -94,7 +95,7 @@
             <libsw library="equalizersw" uuid="0bed4300-847d-11df-bb17-0002a5d5c51b"/>
             <libsw library="bundle" uuid="ce772f20-847d-11df-bb17-0002a5d5c51b"/>
         </effectProxy>
-        <effect name="extensioneffect" library="extensioneffect" uuid="fa81dd00-588b-11ed-9b6a-0242ac120002"/>
+        <effect name="extension_effect" library="extensioneffect" uuid="fa81dd00-588b-11ed-9b6a-0242ac120002" type="fa81de0e-588b-11ed-9b6a-0242ac120002"/>
         <effect name="acoustic_echo_canceler" library="aecsw" uuid="bb392ec0-8d4d-11e0-a896-0002a5d5c51b"/>
         <effect name="noise_suppression" library="nssw" uuid="c06c8400-8e06-11e0-9cb6-0002a5d5c51b"/>
     </effects>
diff --git a/audio/aidl/default/include/core-impl/ChildInterface.h b/audio/aidl/default/include/core-impl/ChildInterface.h
new file mode 100644
index 0000000..1b31691
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/ChildInterface.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 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
+
+#include <memory>
+#include <utility>
+
+#include <android/binder_auto_utils.h>
+#include <android/binder_ibinder_platform.h>
+#include <system/thread_defs.h>
+
+namespace aidl::android::hardware::audio::core {
+
+// Helper used for interfaces that require a persistent instance. We hold them via a strong
+// pointer. The binder token is retained for a call to 'setMinSchedulerPolicy'.
+template <class C>
+struct ChildInterface : private std::pair<std::shared_ptr<C>, ndk::SpAIBinder> {
+    ChildInterface() = default;
+    ChildInterface& operator=(const std::shared_ptr<C>& c) {
+        return operator=(std::shared_ptr<C>(c));
+    }
+    ChildInterface& operator=(std::shared_ptr<C>&& c) {
+        this->first = std::move(c);
+        this->second = this->first->asBinder();
+        AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
+        return *this;
+    }
+    explicit operator bool() const { return !!this->first; }
+    C& operator*() const { return *(this->first); }
+    C* operator->() const { return this->first; }
+    std::shared_ptr<C> getPtr() const { return this->first; }
+};
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/Configuration.h b/audio/aidl/default/include/core-impl/Configuration.h
index 70320e4..25bf7af 100644
--- a/audio/aidl/default/include/core-impl/Configuration.h
+++ b/audio/aidl/default/include/core-impl/Configuration.h
@@ -45,6 +45,7 @@
 
 std::unique_ptr<Configuration> getPrimaryConfiguration();
 std::unique_ptr<Configuration> getRSubmixConfiguration();
+std::unique_ptr<Configuration> getStubConfiguration();
 std::unique_ptr<Configuration> getUsbConfiguration();
 
 }  // namespace aidl::android::hardware::audio::core::internal
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 4a23637..294cc0e 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -16,12 +16,14 @@
 
 #pragma once
 
+#include <iostream>
 #include <map>
 #include <memory>
 #include <set>
 
 #include <aidl/android/hardware/audio/core/BnModule.h>
 
+#include "core-impl/ChildInterface.h"
 #include "core-impl/Configuration.h"
 #include "core-impl/Stream.h"
 
@@ -31,7 +33,7 @@
   public:
     // This value is used for all AudioPatches and reported by all streams.
     static constexpr int32_t kLatencyMs = 10;
-    enum Type : int { DEFAULT, R_SUBMIX, USB };
+    enum Type : int { DEFAULT, R_SUBMIX, STUB, USB };
 
     static std::shared_ptr<Module> createInstance(Type type);
 
@@ -132,26 +134,6 @@
         bool forceTransientBurst = false;
         bool forceSynchronousDrain = false;
     };
-    // Helper used for interfaces that require a persistent instance. We hold them via a strong
-    // pointer. The binder token is retained for a call to 'setMinSchedulerPolicy'.
-    template <class C>
-    struct ChildInterface : private std::pair<std::shared_ptr<C>, ndk::SpAIBinder> {
-        ChildInterface() {}
-        ChildInterface& operator=(const std::shared_ptr<C>& c) {
-            return operator=(std::shared_ptr<C>(c));
-        }
-        ChildInterface& operator=(std::shared_ptr<C>&& c) {
-            this->first = std::move(c);
-            this->second = this->first->asBinder();
-            AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL,
-                                           ANDROID_PRIORITY_AUDIO);
-            return *this;
-        }
-        explicit operator bool() const { return !!this->first; }
-        C& operator*() const { return *(this->first); }
-        C* operator->() const { return this->first; }
-        std::shared_ptr<C> getPtr() const { return this->first; }
-    };
     // ids of device ports created at runtime via 'connectExternalDevice'.
     // Also stores a list of ids of mix ports with dynamic profiles that were populated from
     // the connected port. This list can be empty, thus an int->int multimap can't be used.
@@ -164,10 +146,6 @@
     std::unique_ptr<internal::Configuration> mConfig;
     ModuleDebug mDebug;
     VendorDebug mVendorDebug;
-    ChildInterface<ITelephony> mTelephony;
-    ChildInterface<IBluetooth> mBluetooth;
-    ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
-    ChildInterface<IBluetoothLe> mBluetoothLe;
     ConnectedDevicePorts mConnectedDevicePorts;
     Streams mStreams;
     Patches mPatches;
@@ -184,13 +162,13 @@
             const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
             StreamContext&& context,
             const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
-            std::shared_ptr<StreamIn>* result);
+            std::shared_ptr<StreamIn>* result) = 0;
     virtual ndk::ScopedAStatus createOutputStream(
             const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
             StreamContext&& context,
             const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
                     offloadInfo,
-            std::shared_ptr<StreamOut>* result);
+            std::shared_ptr<StreamOut>* result) = 0;
     // If the module is unable to populate the connected device port correctly, the returned error
     // code must correspond to the errors of `IModule.connectedExternalDevice` method.
     virtual ndk::ScopedAStatus populateConnectedDevicePort(
@@ -204,6 +182,7 @@
             const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected);
     virtual ndk::ScopedAStatus onMasterMuteChanged(bool mute);
     virtual ndk::ScopedAStatus onMasterVolumeChanged(float volume);
+    virtual std::unique_ptr<internal::Configuration> initializeConfig();
 
     // Utility and helper functions accessible to subclasses.
     void cleanUpPatch(int32_t patchId);
@@ -224,6 +203,7 @@
     bool getMicMute() const { return mMicMute; }
     const Patches& getPatches() const { return mPatches; }
     const Streams& getStreams() const { return mStreams; }
+    Type getType() const { return mType; }
     bool isMmapSupported();
     template <typename C>
     std::set<int32_t> portIdsFromPortConfigIds(C portConfigIds);
@@ -232,4 +212,6 @@
                                                    const AudioPatch& newPatch);
 };
 
+std::ostream& operator<<(std::ostream& os, Module::Type t);
+
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/ModuleAlsa.h b/audio/aidl/default/include/core-impl/ModuleAlsa.h
new file mode 100644
index 0000000..5815961
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/ModuleAlsa.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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
+
+#include "core-impl/Module.h"
+
+namespace aidl::android::hardware::audio::core {
+
+// This class is intended to be used as a base class for implementations
+// that use TinyAlsa. This can be either a primary module or a USB Audio
+// module. This class does not define a complete module implementation,
+// and should never be used on its own. Derived classes are expected to
+// provide necessary overrides for all interface methods omitted here.
+class ModuleAlsa : public Module {
+  public:
+    explicit ModuleAlsa(Module::Type type) : Module(type) {}
+
+  protected:
+    // Extension methods of 'Module'.
+    ndk::ScopedAStatus populateConnectedDevicePort(
+            ::aidl::android::media::audio::common::AudioPort* audioPort) override;
+};
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/ModulePrimary.h b/audio/aidl/default/include/core-impl/ModulePrimary.h
new file mode 100644
index 0000000..bc808ab
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/ModulePrimary.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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
+
+#include "core-impl/Module.h"
+
+namespace aidl::android::hardware::audio::core {
+
+class ModulePrimary final : public Module {
+  public:
+    ModulePrimary() : Module(Type::DEFAULT) {}
+
+  protected:
+    ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
+
+    ndk::ScopedAStatus createInputStream(
+            const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+            StreamContext&& context,
+            const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
+            std::shared_ptr<StreamIn>* result) override;
+    ndk::ScopedAStatus createOutputStream(
+            const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+            StreamContext&& context,
+            const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+                    offloadInfo,
+            std::shared_ptr<StreamOut>* result) override;
+
+  private:
+    ChildInterface<ITelephony> mTelephony;
+};
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
index 7b1d375..ccfcdd9 100644
--- a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
@@ -22,7 +22,7 @@
 
 class ModuleRemoteSubmix : public Module {
   public:
-    explicit ModuleRemoteSubmix(Module::Type type) : Module(type) {}
+    ModuleRemoteSubmix() : Module(Type::R_SUBMIX) {}
 
   private:
     // IModule interfaces
diff --git a/audio/aidl/default/include/core-impl/ModuleStub.h b/audio/aidl/default/include/core-impl/ModuleStub.h
new file mode 100644
index 0000000..59c343f
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/ModuleStub.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 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
+
+#include "core-impl/Module.h"
+
+namespace aidl::android::hardware::audio::core {
+
+class ModuleStub final : public Module {
+  public:
+    ModuleStub() : Module(Type::STUB) {}
+
+  protected:
+    ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
+    ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) override;
+    ndk::ScopedAStatus getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) override;
+
+    ndk::ScopedAStatus createInputStream(
+            const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+            StreamContext&& context,
+            const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
+            std::shared_ptr<StreamIn>* result) override;
+    ndk::ScopedAStatus createOutputStream(
+            const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+            StreamContext&& context,
+            const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+                    offloadInfo,
+            std::shared_ptr<StreamOut>* result) override;
+
+  private:
+    ChildInterface<IBluetooth> mBluetooth;
+    ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
+    ChildInterface<IBluetoothLe> mBluetoothLe;
+};
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/ModuleUsb.h b/audio/aidl/default/include/core-impl/ModuleUsb.h
index 5a5429d..e6b3e66 100644
--- a/audio/aidl/default/include/core-impl/ModuleUsb.h
+++ b/audio/aidl/default/include/core-impl/ModuleUsb.h
@@ -16,13 +16,13 @@
 
 #pragma once
 
-#include "core-impl/Module.h"
+#include "core-impl/ModuleAlsa.h"
 
 namespace aidl::android::hardware::audio::core {
 
-class ModuleUsb : public Module {
+class ModuleUsb final : public ModuleAlsa {
   public:
-    explicit ModuleUsb(Module::Type type) : Module(type) {}
+    ModuleUsb() : ModuleAlsa(Type::USB) {}
 
   private:
     // IModule interfaces
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index aaf5860..e64c578 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -184,6 +184,12 @@
     virtual ::android::status_t start() = 0;
     virtual ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                          int32_t* latencyMs) = 0;
+    // No need to implement 'getPosition' unless the driver can provide more precise
+    // data than just total frame count. For example, the driver may correctly account
+    // for any intermediate buffers.
+    virtual ::android::status_t getPosition(StreamDescriptor::Position* /*position*/) {
+        return ::android::OK;
+    }
     virtual void shutdown() = 0;  // This function is only called once.
 };
 
diff --git a/audio/aidl/default/include/core-impl/StreamAlsa.h b/audio/aidl/default/include/core-impl/StreamAlsa.h
new file mode 100644
index 0000000..5744d66
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/StreamAlsa.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 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
+
+#include <optional>
+#include <vector>
+
+#include "Stream.h"
+#include "alsa/Utils.h"
+
+namespace aidl::android::hardware::audio::core {
+
+// This class is intended to be used as a base class for implementations
+// that use TinyAlsa.
+// This class does not define a complete stream implementation,
+// and should never be used on its own. Derived classes are expected to
+// provide necessary overrides for all interface methods omitted here.
+class StreamAlsa : public StreamCommonImpl {
+  public:
+    StreamAlsa(const Metadata& metadata, StreamContext&& context);
+    // Methods of 'DriverInterface'.
+    ::android::status_t init() override;
+    ::android::status_t standby() override;
+    ::android::status_t start() override;
+    ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
+                                 int32_t* latencyMs) override;
+    ::android::status_t getPosition(StreamDescriptor::Position* position) override;
+    void shutdown() override;
+
+  protected:
+    // Called from 'start' to initialize 'mAlsaDeviceProxies', the vector must be non-empty.
+    virtual std::vector<alsa::DeviceProfile> getDeviceProfiles() = 0;
+
+    const size_t mFrameSizeBytes;
+    const bool mIsInput;
+    const std::optional<struct pcm_config> mConfig;
+    // All fields below are only used on the worker thread.
+    std::vector<alsa::DeviceProxy> mAlsaDeviceProxies;
+};
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
index 2253ec7..1bca910 100644
--- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
@@ -39,6 +39,7 @@
     ::android::status_t start() override;
     ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                  int32_t* latencyMs) override;
+    ::android::status_t getPosition(StreamDescriptor::Position* position) override;
     void shutdown() override;
 
     // Overridden methods of 'StreamCommonImpl', called on a Binder thread.
diff --git a/audio/aidl/default/include/core-impl/StreamUsb.h b/audio/aidl/default/include/core-impl/StreamUsb.h
index 8c40782..44f742a 100644
--- a/audio/aidl/default/include/core-impl/StreamUsb.h
+++ b/audio/aidl/default/include/core-impl/StreamUsb.h
@@ -17,55 +17,34 @@
 #pragma once
 
 #include <atomic>
-#include <functional>
 #include <mutex>
-#include <optional>
 #include <vector>
 
 #include <aidl/android/media/audio/common/AudioChannelLayout.h>
 
-#include "core-impl/Stream.h"
-
-extern "C" {
-#include <tinyalsa/pcm.h>
-#include "alsa_device_proxy.h"
-}
+#include "StreamAlsa.h"
 
 namespace aidl::android::hardware::audio::core {
 
-class StreamUsb : public StreamCommonImpl {
+class StreamUsb : public StreamAlsa {
   public:
     StreamUsb(const Metadata& metadata, StreamContext&& context);
     // Methods of 'DriverInterface'.
-    ::android::status_t init() override;
     ::android::status_t drain(StreamDescriptor::DrainMode) override;
     ::android::status_t flush() override;
     ::android::status_t pause() override;
-    ::android::status_t standby() override;
-    ::android::status_t start() override;
     ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                  int32_t* latencyMs) override;
-    void shutdown() override;
 
     // Overridden methods of 'StreamCommonImpl', called on a Binder thread.
-    const ConnectedDevices& getConnectedDevices() const override;
     ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override;
 
-  private:
-    using AlsaDeviceProxyDeleter = std::function<void(alsa_device_proxy*)>;
-    using AlsaDeviceProxy = std::unique_ptr<alsa_device_proxy, AlsaDeviceProxyDeleter>;
-
-    static std::optional<struct pcm_config> maybePopulateConfig(const StreamContext& context,
-                                                                bool isInput);
+  protected:
+    std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
 
     mutable std::mutex mLock;
-
-    const size_t mFrameSizeBytes;
-    const bool mIsInput;
-    const std::optional<struct pcm_config> mConfig;
+    std::vector<alsa::DeviceProfile> mConnectedDeviceProfiles GUARDED_BY(mLock);
     std::atomic<bool> mConnectedDevicesUpdated = false;
-    // All fields below are only used on the worker thread.
-    std::vector<AlsaDeviceProxy> mAlsaDeviceProxies;
 };
 
 class StreamInUsb final : public StreamUsb, public StreamIn {
@@ -94,7 +73,7 @@
     ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
     ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
 
-    int mChannelCount;
+    const int mChannelCount;
     std::vector<float> mHwVolumes;
 };
 
diff --git a/audio/aidl/default/include/effectFactory-impl/EffectConfig.h b/audio/aidl/default/include/effectFactory-impl/EffectConfig.h
index f8a86e1..344846a 100644
--- a/audio/aidl/default/include/effectFactory-impl/EffectConfig.h
+++ b/audio/aidl/default/include/effectFactory-impl/EffectConfig.h
@@ -40,14 +40,15 @@
   public:
     explicit EffectConfig(const std::string& file);
 
-    struct LibraryUuid {
+    struct Library {
         std::string name;  // library name
-        ::aidl::android::media::audio::common::AudioUuid uuid;
+        ::aidl::android::media::audio::common::AudioUuid uuid;  // implementation UUID
+        std::optional<::aidl::android::media::audio::common::AudioUuid> type;  // optional type UUID
     };
     // <effects>
     struct EffectLibraries {
-        std::optional<struct LibraryUuid> proxyLibrary;
-        std::vector<struct LibraryUuid> libraries;
+        std::optional<struct Library> proxyLibrary;
+        std::vector<struct Library> libraries;
     };
 
     int getSkippedElements() const { return mSkippedElements; }
@@ -56,7 +57,7 @@
         return mEffectsMap;
     }
 
-    static bool findUuid(const std::string& xmlEffectName,
+    static bool findUuid(const std::pair<std::string, struct EffectLibraries>& effectElem,
                          ::aidl::android::media::audio::common::AudioUuid* uuid);
 
     using ProcessingLibrariesMap = std::map<Processing::Type, std::vector<struct EffectLibraries>>;
@@ -96,8 +97,8 @@
     bool parseProcessing(Processing::Type::Tag typeTag, const tinyxml2::XMLElement& xml);
 
     // Function to parse effect.library name and effect.uuid from xml
-    bool parseLibraryUuid(const tinyxml2::XMLElement& xml, struct LibraryUuid& libraryUuid,
-                          bool isProxy = false);
+    bool parseLibrary(const tinyxml2::XMLElement& xml, struct Library& library,
+                      bool isProxy = false);
 
     const char* dump(const tinyxml2::XMLElement& element,
                      tinyxml2::XMLPrinter&& printer = {}) const;
diff --git a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
index ad59ca7..1401db0 100644
--- a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
+++ b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
@@ -104,7 +104,7 @@
     bool openEffectLibrary(const ::aidl::android::media::audio::common::AudioUuid& impl,
                            const std::string& path);
     void createIdentityWithConfig(
-            const EffectConfig::LibraryUuid& configLib,
+            const EffectConfig::Library& configLib,
             const ::aidl::android::media::audio::common::AudioUuid& typeUuidStr,
             const std::optional<::aidl::android::media::audio::common::AudioUuid> proxyUuid);
 
diff --git a/audio/aidl/default/main.cpp b/audio/aidl/default/main.cpp
index 12c0c4b..93fb366 100644
--- a/audio/aidl/default/main.cpp
+++ b/audio/aidl/default/main.cpp
@@ -16,20 +16,21 @@
 
 #include <cstdlib>
 #include <ctime>
+#include <sstream>
 #include <utility>
+#include <vector>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android/binder_ibinder_platform.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 
 #include "core-impl/Config.h"
 #include "core-impl/Module.h"
-#include "core-impl/ModuleUsb.h"
 
 using aidl::android::hardware::audio::core::Config;
 using aidl::android::hardware::audio::core::Module;
-using aidl::android::hardware::audio::core::ModuleUsb;
 
 int main() {
     // Random values are used in the implementation.
@@ -52,18 +53,19 @@
     CHECK_EQ(STATUS_OK, status);
 
     // Make modules
-    auto createModule = [](Module::Type type, const std::string& instance) {
+    auto createModule = [](Module::Type type) {
         auto module = Module::createInstance(type);
         ndk::SpAIBinder moduleBinder = module->asBinder();
-        const std::string moduleName = std::string(Module::descriptor).append("/").append(instance);
+        std::stringstream moduleName;
+        moduleName << Module::descriptor << "/" << type;
         AIBinder_setMinSchedulerPolicy(moduleBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
-        binder_status_t status = AServiceManager_addService(moduleBinder.get(), moduleName.c_str());
+        binder_status_t status =
+                AServiceManager_addService(moduleBinder.get(), moduleName.str().c_str());
         CHECK_EQ(STATUS_OK, status);
         return std::make_pair(module, moduleBinder);
     };
-    auto modules = {createModule(Module::Type::DEFAULT, "default"),
-                    createModule(Module::Type::R_SUBMIX, "r_submix"),
-                    createModule(Module::Type::USB, "usb")};
+    auto modules = {createModule(Module::Type::DEFAULT), createModule(Module::Type::R_SUBMIX),
+                    createModule(Module::Type::USB), createModule(Module::Type::STUB)};
     (void)modules;
 
     ABinderProcess_joinThreadPool();
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index 5af0d91..6d5185b 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -179,6 +179,27 @@
                      : outWrite(buffer, frameCount, actualFrameCount));
 }
 
+::android::status_t StreamRemoteSubmix::getPosition(StreamDescriptor::Position* position) {
+    sp<MonoPipeReader> source = mCurrentRoute->getSource();
+    if (source == nullptr) {
+        return ::android::NO_INIT;
+    }
+    const ssize_t framesInPipe = source->availableToRead();
+    if (framesInPipe < 0) {
+        return ::android::INVALID_OPERATION;
+    }
+    if (mIsInput) {
+        position->frames += framesInPipe;
+    } else {
+        if (position->frames > framesInPipe) {
+            position->frames -= framesInPipe;
+        } else {
+            position->frames = 0;
+        }
+    }
+    return ::android::OK;
+}
+
 // Calculate the maximum size of the pipe buffer in frames for the specified stream.
 size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
     auto pipeConfig = mCurrentRoute->mPipeConfig;
diff --git a/audio/aidl/default/stub/ModuleStub.cpp b/audio/aidl/default/stub/ModuleStub.cpp
new file mode 100644
index 0000000..a600752
--- /dev/null
+++ b/audio/aidl/default/stub/ModuleStub.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 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 <vector>
+
+#define LOG_TAG "AHAL_ModuleStub"
+#include <Utils.h>
+#include <android-base/logging.h>
+
+#include "core-impl/Bluetooth.h"
+#include "core-impl/ModuleStub.h"
+#include "core-impl/StreamStub.h"
+
+using aidl::android::hardware::audio::common::SinkMetadata;
+using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::media::audio::common::AudioOffloadInfo;
+using aidl::android::media::audio::common::AudioPort;
+using aidl::android::media::audio::common::AudioPortConfig;
+using aidl::android::media::audio::common::MicrophoneInfo;
+
+namespace aidl::android::hardware::audio::core {
+
+ndk::ScopedAStatus ModuleStub::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
+    if (!mBluetooth) {
+        mBluetooth = ndk::SharedRefBase::make<Bluetooth>();
+    }
+    *_aidl_return = mBluetooth.getPtr();
+    LOG(DEBUG) << __func__ << ": returning instance of IBluetooth: " << _aidl_return->get();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModuleStub::getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
+    if (!mBluetoothA2dp) {
+        mBluetoothA2dp = ndk::SharedRefBase::make<BluetoothA2dp>();
+    }
+    *_aidl_return = mBluetoothA2dp.getPtr();
+    LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModuleStub::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
+    if (!mBluetoothLe) {
+        mBluetoothLe = ndk::SharedRefBase::make<BluetoothLe>();
+    }
+    *_aidl_return = mBluetoothLe.getPtr();
+    LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModuleStub::createInputStream(const SinkMetadata& sinkMetadata,
+                                                 StreamContext&& context,
+                                                 const std::vector<MicrophoneInfo>& microphones,
+                                                 std::shared_ptr<StreamIn>* result) {
+    return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
+                                              microphones);
+}
+
+ndk::ScopedAStatus ModuleStub::createOutputStream(
+        const SourceMetadata& sourceMetadata, StreamContext&& context,
+        const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
+    return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
+                                               offloadInfo);
+}
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/StreamStub.cpp b/audio/aidl/default/stub/StreamStub.cpp
similarity index 100%
rename from audio/aidl/default/StreamStub.cpp
rename to audio/aidl/default/stub/StreamStub.cpp
diff --git a/audio/aidl/default/usb/ModuleUsb.cpp b/audio/aidl/default/usb/ModuleUsb.cpp
index 5c9d477..a812e4d 100644
--- a/audio/aidl/default/usb/ModuleUsb.cpp
+++ b/audio/aidl/default/usb/ModuleUsb.cpp
@@ -14,68 +14,34 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "AHAL_ModuleUsb"
-
 #include <vector>
 
+#define LOG_TAG "AHAL_ModuleUsb"
 #include <Utils.h>
 #include <android-base/logging.h>
-#include <tinyalsa/asoundlib.h>
 
 #include "UsbAlsaMixerControl.h"
-#include "UsbAlsaUtils.h"
+#include "alsa/Utils.h"
 #include "core-impl/ModuleUsb.h"
 #include "core-impl/StreamUsb.h"
 
-extern "C" {
-#include "alsa_device_profile.h"
-}
-
 using aidl::android::hardware::audio::common::SinkMetadata;
 using aidl::android::hardware::audio::common::SourceMetadata;
-using aidl::android::media::audio::common::AudioChannelLayout;
-using aidl::android::media::audio::common::AudioDeviceAddress;
 using aidl::android::media::audio::common::AudioDeviceDescription;
-using aidl::android::media::audio::common::AudioDeviceType;
-using aidl::android::media::audio::common::AudioFormatDescription;
-using aidl::android::media::audio::common::AudioFormatType;
-using aidl::android::media::audio::common::AudioIoFlags;
 using aidl::android::media::audio::common::AudioOffloadInfo;
 using aidl::android::media::audio::common::AudioPort;
 using aidl::android::media::audio::common::AudioPortConfig;
 using aidl::android::media::audio::common::AudioPortExt;
-using aidl::android::media::audio::common::AudioProfile;
 using aidl::android::media::audio::common::MicrophoneInfo;
 
 namespace aidl::android::hardware::audio::core {
 
 namespace {
 
-std::vector<AudioChannelLayout> populateChannelMasksFromProfile(const alsa_device_profile* profile,
-                                                                bool isInput) {
-    std::vector<AudioChannelLayout> channels;
-    for (size_t i = 0; i < AUDIO_PORT_MAX_CHANNEL_MASKS && profile->channel_counts[i] != 0; ++i) {
-        auto layoutMask =
-                usb::getChannelLayoutMaskFromChannelCount(profile->channel_counts[i], isInput);
-        if (layoutMask.getTag() == AudioChannelLayout::Tag::layoutMask) {
-            channels.push_back(layoutMask);
-        }
-        auto indexMask = usb::getChannelIndexMaskFromChannelCount(profile->channel_counts[i]);
-        if (indexMask.getTag() == AudioChannelLayout::Tag::indexMask) {
-            channels.push_back(indexMask);
-        }
-    }
-    return channels;
-}
-
-std::vector<int> populateSampleRatesFromProfile(const alsa_device_profile* profile) {
-    std::vector<int> sampleRates;
-    for (int i = 0; i < std::min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) &&
-                    profile->sample_rates[i] != 0;
-         i++) {
-        sampleRates.push_back(profile->sample_rates[i]);
-    }
-    return sampleRates;
+bool isUsbDevicePort(const AudioPort& audioPort) {
+    return audioPort.ext.getTag() == AudioPortExt::Tag::device &&
+           audioPort.ext.get<AudioPortExt::Tag::device>().device.type.connection ==
+                   AudioDeviceDescription::CONNECTION_USB;
 }
 
 }  // namespace
@@ -122,55 +88,11 @@
 }
 
 ndk::ScopedAStatus ModuleUsb::populateConnectedDevicePort(AudioPort* audioPort) {
-    if (audioPort->ext.getTag() != AudioPortExt::Tag::device) {
-        LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a device port";
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-    auto& devicePort = audioPort->ext.get<AudioPortExt::Tag::device>();
-    if (devicePort.device.type.connection != AudioDeviceDescription::CONNECTION_USB) {
+    if (!isUsbDevicePort(*audioPort)) {
         LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a usb device port";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
-    if (devicePort.device.address.getTag() != AudioDeviceAddress::Tag::alsa) {
-        LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not using alsa address";
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-    auto& alsaAddress = devicePort.device.address.get<AudioDeviceAddress::Tag::alsa>();
-    if (alsaAddress.size() != 2 || alsaAddress[0] < 0 || alsaAddress[1] < 0) {
-        LOG(ERROR) << __func__ << ": port id " << audioPort->id << " invalid alsa address";
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-
-    const bool isInput = audioPort->flags.getTag() == AudioIoFlags::input;
-    alsa_device_profile profile;
-    profile_init(&profile, isInput ? PCM_IN : PCM_OUT);
-    profile.card = alsaAddress[0];
-    profile.device = alsaAddress[1];
-    if (!profile_read_device_info(&profile)) {
-        LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card
-                   << ", device=" << profile.device;
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-
-    std::vector<AudioChannelLayout> channels = populateChannelMasksFromProfile(&profile, isInput);
-    std::vector<int> sampleRates = populateSampleRatesFromProfile(&profile);
-
-    for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) &&
-                       profile.formats[i] != PCM_FORMAT_INVALID;
-         ++i) {
-        auto audioFormatDescription =
-                usb::legacy2aidl_pcm_format_AudioFormatDescription(profile.formats[i]);
-        if (audioFormatDescription.type == AudioFormatType::DEFAULT) {
-            LOG(WARNING) << __func__ << ": unknown pcm type=" << profile.formats[i];
-            continue;
-        }
-        AudioProfile audioProfile = {.format = audioFormatDescription,
-                                     .channelMasks = channels,
-                                     .sampleRates = sampleRates};
-        audioPort->profiles.push_back(std::move(audioProfile));
-    }
-
-    return ndk::ScopedAStatus::ok();
+    return ModuleAlsa::populateConnectedDevicePort(audioPort);
 }
 
 ndk::ScopedAStatus ModuleUsb::checkAudioPatchEndpointsMatch(
@@ -191,15 +113,14 @@
 
 void ModuleUsb::onExternalDeviceConnectionChanged(
         const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected) {
-    if (audioPort.ext.getTag() != AudioPortExt::Tag::device) {
+    if (!isUsbDevicePort(audioPort)) {
         return;
     }
-    const auto& address = audioPort.ext.get<AudioPortExt::Tag::device>().device.address;
-    if (address.getTag() != AudioDeviceAddress::alsa) {
+    auto profile = alsa::getDeviceProfile(audioPort);
+    if (!profile.has_value()) {
         return;
     }
-    const int card = address.get<AudioDeviceAddress::alsa>()[0];
-    usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(card, getMasterMute(),
+    usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(profile->card, getMasterMute(),
                                                                      getMasterVolume(), connected);
 }
 
diff --git a/audio/aidl/default/usb/StreamUsb.cpp b/audio/aidl/default/usb/StreamUsb.cpp
index 17e1ab4..da0ad11 100644
--- a/audio/aidl/default/usb/StreamUsb.cpp
+++ b/audio/aidl/default/usb/StreamUsb.cpp
@@ -23,64 +23,20 @@
 #include <error/expected_utils.h>
 
 #include "UsbAlsaMixerControl.h"
-#include "UsbAlsaUtils.h"
-#include "core-impl/Module.h"
 #include "core-impl/StreamUsb.h"
 
-extern "C" {
-#include "alsa_device_profile.h"
-}
-
 using aidl::android::hardware::audio::common::getChannelCount;
 using aidl::android::hardware::audio::common::SinkMetadata;
 using aidl::android::hardware::audio::common::SourceMetadata;
 using aidl::android::media::audio::common::AudioDevice;
-using aidl::android::media::audio::common::AudioDeviceAddress;
 using aidl::android::media::audio::common::AudioOffloadInfo;
-using aidl::android::media::audio::common::AudioPortExt;
 using aidl::android::media::audio::common::MicrophoneDynamicInfo;
 using aidl::android::media::audio::common::MicrophoneInfo;
-using android::OK;
-using android::status_t;
 
 namespace aidl::android::hardware::audio::core {
 
 StreamUsb::StreamUsb(const Metadata& metadata, StreamContext&& context)
-    : StreamCommonImpl(metadata, std::move(context)),
-      mFrameSizeBytes(getContext().getFrameSize()),
-      mIsInput(isInput(metadata)),
-      mConfig(maybePopulateConfig(getContext(), mIsInput)) {}
-
-// static
-std::optional<struct pcm_config> StreamUsb::maybePopulateConfig(const StreamContext& context,
-                                                                bool isInput) {
-    struct pcm_config config;
-    config.channels = usb::getChannelCountFromChannelMask(context.getChannelLayout(), isInput);
-    if (config.channels == 0) {
-        LOG(ERROR) << __func__ << ": invalid channel=" << context.getChannelLayout().toString();
-        return std::nullopt;
-    }
-    config.format = usb::aidl2legacy_AudioFormatDescription_pcm_format(context.getFormat());
-    if (config.format == PCM_FORMAT_INVALID) {
-        LOG(ERROR) << __func__ << ": invalid format=" << context.getFormat().toString();
-        return std::nullopt;
-    }
-    config.rate = context.getSampleRate();
-    if (config.rate == 0) {
-        LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate;
-        return std::nullopt;
-    }
-    return config;
-}
-
-::android::status_t StreamUsb::init() {
-    return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
-}
-
-const StreamCommonInterface::ConnectedDevices& StreamUsb::getConnectedDevices() const {
-    std::lock_guard guard(mLock);
-    return mConnectedDevices;
-}
+    : StreamAlsa(metadata, std::move(context)) {}
 
 ndk::ScopedAStatus StreamUsb::setConnectedDevices(
         const std::vector<AudioDevice>& connectedDevices) {
@@ -89,14 +45,19 @@
                    << ") for input stream";
         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
     }
+    std::vector<alsa::DeviceProfile> connectedDeviceProfiles;
     for (const auto& connectedDevice : connectedDevices) {
-        if (connectedDevice.address.getTag() != AudioDeviceAddress::alsa) {
-            LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString();
+        auto profile = alsa::getDeviceProfile(connectedDevice, mIsInput);
+        if (!profile.has_value()) {
+            LOG(ERROR) << __func__
+                       << ": unsupported device address=" << connectedDevice.address.toString();
             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
         }
+        connectedDeviceProfiles.push_back(*profile);
     }
-    std::lock_guard guard(mLock);
     RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices));
+    std::lock_guard guard(mLock);
+    mConnectedDeviceProfiles = std::move(connectedDeviceProfiles);
     mConnectedDevicesUpdated.store(true, std::memory_order_release);
     return ndk::ScopedAStatus::ok();
 }
@@ -119,87 +80,22 @@
 ::android::status_t StreamUsb::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                         int32_t* latencyMs) {
     if (mConnectedDevicesUpdated.load(std::memory_order_acquire)) {
-        // 'setConnectedDevices' has been called. I/O will be restarted.
+        // 'setConnectedDevices' was called. I/O will be restarted.
         *actualFrameCount = 0;
         *latencyMs = StreamDescriptor::LATENCY_UNKNOWN;
         return ::android::OK;
     }
-    const size_t bytesToTransfer = frameCount * mFrameSizeBytes;
-    unsigned maxLatency = 0;
-    if (mIsInput) {
-        if (mAlsaDeviceProxies.empty()) {
-            LOG(FATAL) << __func__ << ": no input devices";
-            return ::android::NO_INIT;
-        }
-        // For input case, only support single device.
-        proxy_read(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer);
-        maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
-    } else {
-        for (auto& proxy : mAlsaDeviceProxies) {
-            proxy_write(proxy.get(), buffer, bytesToTransfer);
-            maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get()));
-        }
-    }
-    *actualFrameCount = frameCount;
-    maxLatency = std::min(maxLatency, static_cast<unsigned>(std::numeric_limits<int32_t>::max()));
-    *latencyMs = maxLatency;
-    return ::android::OK;
+    return StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs);
 }
 
-::android::status_t StreamUsb::standby() {
-    mAlsaDeviceProxies.clear();
-    return ::android::OK;
-}
-
-void StreamUsb::shutdown() {
-    mAlsaDeviceProxies.clear();
-}
-
-::android::status_t StreamUsb::start() {
-    std::vector<AudioDeviceAddress> connectedDevices;
+std::vector<alsa::DeviceProfile> StreamUsb::getDeviceProfiles() {
+    std::vector<alsa::DeviceProfile> connectedDevices;
     {
         std::lock_guard guard(mLock);
-        std::transform(mConnectedDevices.begin(), mConnectedDevices.end(),
-                       std::back_inserter(connectedDevices),
-                       [](const auto& device) { return device.address; });
+        connectedDevices = mConnectedDeviceProfiles;
         mConnectedDevicesUpdated.store(false, std::memory_order_release);
     }
-    decltype(mAlsaDeviceProxies) alsaDeviceProxies;
-    for (const auto& device : connectedDevices) {
-        alsa_device_profile profile;
-        profile_init(&profile, mIsInput ? PCM_IN : PCM_OUT);
-        profile.card = device.get<AudioDeviceAddress::alsa>()[0];
-        profile.device = device.get<AudioDeviceAddress::alsa>()[1];
-        if (!profile_read_device_info(&profile)) {
-            LOG(ERROR) << __func__
-                       << ": unable to read device info, device address=" << device.toString();
-            return ::android::UNKNOWN_ERROR;
-        }
-
-        AlsaDeviceProxy proxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) {
-            proxy_close(proxy);
-            free(proxy);
-        });
-        // Always ask for alsa configure as required since the configuration should be supported
-        // by the connected device. That is guaranteed by `setAudioPortConfig` and
-        // `setAudioPatch`.
-        if (int err = proxy_prepare(proxy.get(), &profile,
-                                    const_cast<struct pcm_config*>(&mConfig.value()),
-                                    true /*is_bit_perfect*/);
-            err != 0) {
-            LOG(ERROR) << __func__ << ": fail to prepare for device address=" << device.toString()
-                       << " error=" << err;
-            return ::android::UNKNOWN_ERROR;
-        }
-        if (int err = proxy_open(proxy.get()); err != 0) {
-            LOG(ERROR) << __func__ << ": failed to open device, address=" << device.toString()
-                       << " error=" << err;
-            return ::android::UNKNOWN_ERROR;
-        }
-        alsaDeviceProxies.push_back(std::move(proxy));
-    }
-    mAlsaDeviceProxies = std::move(alsaDeviceProxies);
-    return ::android::OK;
+    return connectedDevices;
 }
 
 StreamInUsb::StreamInUsb(const SinkMetadata& sinkMetadata, StreamContext&& context,
@@ -214,9 +110,9 @@
 
 StreamOutUsb::StreamOutUsb(const SourceMetadata& sourceMetadata, StreamContext&& context,
                            const std::optional<AudioOffloadInfo>& offloadInfo)
-    : StreamUsb(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {
-    mChannelCount = getChannelCount(getContext().getChannelLayout());
-}
+    : StreamUsb(sourceMetadata, std::move(context)),
+      StreamOut(offloadInfo),
+      mChannelCount(getChannelCount(getContext().getChannelLayout())) {}
 
 ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector<float>* _aidl_return) {
     *_aidl_return = mHwVolumes;
@@ -224,17 +120,17 @@
 }
 
 ndk::ScopedAStatus StreamOutUsb::setHwVolume(const std::vector<float>& in_channelVolumes) {
+    // Avoid using mConnectedDeviceProfiles because it requires a lock.
     for (const auto& device : getConnectedDevices()) {
-        if (device.address.getTag() != AudioDeviceAddress::alsa) {
-            LOG(DEBUG) << __func__ << ": skip as the device address is not alsa";
-            continue;
-        }
-        const int card = device.address.get<AudioDeviceAddress::alsa>()[0];
-        if (auto result =
-                    usb::UsbAlsaMixerControl::getInstance().setVolumes(card, in_channelVolumes);
-            !result.isOk()) {
-            LOG(ERROR) << __func__ << ": failed to set volume for device, card=" << card;
-            return result;
+        if (auto deviceProfile = alsa::getDeviceProfile(device, mIsInput);
+            deviceProfile.has_value()) {
+            if (auto result = usb::UsbAlsaMixerControl::getInstance().setVolumes(
+                        deviceProfile->card, in_channelVolumes);
+                !result.isOk()) {
+                LOG(ERROR) << __func__
+                           << ": failed to set volume for device address=" << *deviceProfile;
+                return result;
+            }
         }
     }
     mHwVolumes = in_channelVolumes;
diff --git a/audio/aidl/default/usb/UsbAlsaMixerControl.cpp b/audio/aidl/default/usb/UsbAlsaMixerControl.cpp
index 6c0c24b..769d739 100644
--- a/audio/aidl/default/usb/UsbAlsaMixerControl.cpp
+++ b/audio/aidl/default/usb/UsbAlsaMixerControl.cpp
@@ -17,144 +17,12 @@
 #define LOG_TAG "AHAL_UsbAlsaMixerControl"
 #include <android-base/logging.h>
 
-#include <cmath>
-#include <string>
-#include <vector>
-
 #include <android/binder_status.h>
 
 #include "UsbAlsaMixerControl.h"
 
 namespace aidl::android::hardware::audio::core::usb {
 
-//-----------------------------------------------------------------------------
-
-MixerControl::MixerControl(struct mixer_ctl* ctl)
-    : mCtl(ctl),
-      mNumValues(mixer_ctl_get_num_values(ctl)),
-      mMinValue(mixer_ctl_get_range_min(ctl)),
-      mMaxValue(mixer_ctl_get_range_max(ctl)) {}
-
-unsigned int MixerControl::getNumValues() const {
-    return mNumValues;
-}
-
-int MixerControl::getMaxValue() const {
-    return mMaxValue;
-}
-
-int MixerControl::getMinValue() const {
-    return mMinValue;
-}
-
-int MixerControl::setArray(const void* array, size_t count) {
-    const std::lock_guard guard(mLock);
-    return mixer_ctl_set_array(mCtl, array, count);
-}
-
-//-----------------------------------------------------------------------------
-
-// static
-const std::map<AlsaMixer::Control, std::vector<AlsaMixer::ControlNamesAndExpectedCtlType>>
-        AlsaMixer::kPossibleControls = {
-                {AlsaMixer::MASTER_SWITCH, {{"Master Playback Switch", MIXER_CTL_TYPE_BOOL}}},
-                {AlsaMixer::MASTER_VOLUME, {{"Master Playback Volume", MIXER_CTL_TYPE_INT}}},
-                {AlsaMixer::HW_VOLUME,
-                 {{"Headphone Playback Volume", MIXER_CTL_TYPE_INT},
-                  {"Headset Playback Volume", MIXER_CTL_TYPE_INT},
-                  {"PCM Playback Volume", MIXER_CTL_TYPE_INT}}}};
-
-// static
-std::map<AlsaMixer::Control, std::shared_ptr<MixerControl>> AlsaMixer::initializeMixerControls(
-        struct mixer* mixer) {
-    std::map<AlsaMixer::Control, std::shared_ptr<MixerControl>> mixerControls;
-    std::string mixerCtlNames;
-    for (const auto& [control, possibleCtls] : kPossibleControls) {
-        for (const auto& [ctlName, expectedCtlType] : possibleCtls) {
-            struct mixer_ctl* ctl = mixer_get_ctl_by_name(mixer, ctlName.c_str());
-            if (ctl != nullptr && mixer_ctl_get_type(ctl) == expectedCtlType) {
-                mixerControls.emplace(control, std::make_unique<MixerControl>(ctl));
-                if (!mixerCtlNames.empty()) {
-                    mixerCtlNames += ",";
-                }
-                mixerCtlNames += ctlName;
-                break;
-            }
-        }
-    }
-    LOG(DEBUG) << __func__ << ": available mixer control names=[" << mixerCtlNames << "]";
-    return mixerControls;
-}
-
-AlsaMixer::AlsaMixer(struct mixer* mixer)
-    : mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {}
-
-AlsaMixer::~AlsaMixer() {
-    mixer_close(mMixer);
-}
-
-namespace {
-
-int volumeFloatToInteger(float fValue, int maxValue, int minValue) {
-    return minValue + std::ceil((maxValue - minValue) * fValue);
-}
-
-}  // namespace
-
-ndk::ScopedAStatus AlsaMixer::setMasterMute(bool muted) {
-    auto it = mMixerControls.find(AlsaMixer::MASTER_SWITCH);
-    if (it == mMixerControls.end()) {
-        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
-    }
-    const int numValues = it->second->getNumValues();
-    std::vector<int> values(numValues, muted ? 0 : 1);
-    if (int err = it->second->setArray(values.data(), numValues); err != 0) {
-        LOG(ERROR) << __func__ << ": failed to set master mute, err=" << err;
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus AlsaMixer::setMasterVolume(float volume) {
-    auto it = mMixerControls.find(AlsaMixer::MASTER_VOLUME);
-    if (it == mMixerControls.end()) {
-        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
-    }
-    const int numValues = it->second->getNumValues();
-    std::vector<int> values(numValues, volumeFloatToInteger(volume, it->second->getMaxValue(),
-                                                            it->second->getMinValue()));
-    if (int err = it->second->setArray(values.data(), numValues); err != 0) {
-        LOG(ERROR) << __func__ << ": failed to set master volume, err=" << err;
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus AlsaMixer::setVolumes(std::vector<float> volumes) {
-    auto it = mMixerControls.find(AlsaMixer::HW_VOLUME);
-    if (it == mMixerControls.end()) {
-        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
-    }
-    const int numValues = it->second->getNumValues();
-    if (numValues < 0) {
-        LOG(FATAL) << __func__ << ": negative number of values: " << numValues;
-    }
-    const int maxValue = it->second->getMaxValue();
-    const int minValue = it->second->getMinValue();
-    std::vector<int> values;
-    size_t i = 0;
-    for (; i < static_cast<size_t>(numValues) && i < values.size(); ++i) {
-        values.emplace_back(volumeFloatToInteger(volumes[i], maxValue, minValue));
-    }
-    if (int err = it->second->setArray(values.data(), values.size()); err != 0) {
-        LOG(ERROR) << __func__ << ": failed to set volume, err=" << err;
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-    return ndk::ScopedAStatus::ok();
-}
-
-//-----------------------------------------------------------------------------
-
 // static
 UsbAlsaMixerControl& UsbAlsaMixerControl::getInstance() {
     static UsbAlsaMixerControl gInstance;
@@ -170,7 +38,7 @@
             PLOG(ERROR) << __func__ << ": failed to open mixer for card=" << card;
             return;
         }
-        auto alsaMixer = std::make_shared<AlsaMixer>(mixer);
+        auto alsaMixer = std::make_shared<alsa::Mixer>(mixer);
         alsaMixer->setMasterMute(masterMuted);
         alsaMixer->setMasterVolume(masterVolume);
         const std::lock_guard guard(mLock);
@@ -209,7 +77,7 @@
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, std::vector<float> volumes) {
+ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, const std::vector<float>& volumes) {
     auto alsaMixer = getAlsaMixer(card);
     if (alsaMixer == nullptr) {
         LOG(ERROR) << __func__ << ": no mixer control found for card=" << card;
@@ -218,13 +86,13 @@
     return alsaMixer->setVolumes(volumes);
 }
 
-std::shared_ptr<AlsaMixer> UsbAlsaMixerControl::getAlsaMixer(int card) {
+std::shared_ptr<alsa::Mixer> UsbAlsaMixerControl::getAlsaMixer(int card) {
     const std::lock_guard guard(mLock);
     const auto it = mMixerControls.find(card);
     return it == mMixerControls.end() ? nullptr : it->second;
 }
 
-std::map<int, std::shared_ptr<AlsaMixer>> UsbAlsaMixerControl::getAlsaMixers() {
+std::map<int, std::shared_ptr<alsa::Mixer>> UsbAlsaMixerControl::getAlsaMixers() {
     const std::lock_guard guard(mLock);
     return mMixerControls;
 }
diff --git a/audio/aidl/default/usb/UsbAlsaMixerControl.h b/audio/aidl/default/usb/UsbAlsaMixerControl.h
index cbcddd8..c3265f8 100644
--- a/audio/aidl/default/usb/UsbAlsaMixerControl.h
+++ b/audio/aidl/default/usb/UsbAlsaMixerControl.h
@@ -19,67 +19,15 @@
 #include <map>
 #include <memory>
 #include <mutex>
-#include <optional>
-#include <string>
 #include <vector>
 
 #include <android-base/thread_annotations.h>
 #include <android/binder_auto_utils.h>
 
-extern "C" {
-#include <tinyalsa/mixer.h>
-}
+#include "alsa/Mixer.h"
 
 namespace aidl::android::hardware::audio::core::usb {
 
-class MixerControl {
-  public:
-    explicit MixerControl(struct mixer_ctl* ctl);
-
-    unsigned int getNumValues() const;
-    int getMaxValue() const;
-    int getMinValue() const;
-    int setArray(const void* array, size_t count);
-
-  private:
-    std::mutex mLock;
-    // The mixer_ctl object is owned by ALSA and will be released when the mixer is closed.
-    struct mixer_ctl* mCtl GUARDED_BY(mLock);
-    const unsigned int mNumValues;
-    const int mMinValue;
-    const int mMaxValue;
-};
-
-class AlsaMixer {
-  public:
-    explicit AlsaMixer(struct mixer* mixer);
-
-    ~AlsaMixer();
-
-    bool isValid() const { return mMixer != nullptr; }
-
-    ndk::ScopedAStatus setMasterMute(bool muted);
-    ndk::ScopedAStatus setMasterVolume(float volume);
-    ndk::ScopedAStatus setVolumes(std::vector<float> volumes);
-
-  private:
-    enum Control {
-        MASTER_SWITCH,
-        MASTER_VOLUME,
-        HW_VOLUME,
-    };
-    using ControlNamesAndExpectedCtlType = std::pair<std::string, enum mixer_ctl_type>;
-    static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
-    static std::map<Control, std::shared_ptr<MixerControl>> initializeMixerControls(
-            struct mixer* mixer);
-
-    // The mixer object is owned by ALSA and will be released when the mixer is closed.
-    struct mixer* mMixer;
-    // `mMixerControls` will only be initialized in constructor. After that, it wil only be
-    // read but not be modified.
-    const std::map<Control, std::shared_ptr<MixerControl>> mMixerControls;
-};
-
 class UsbAlsaMixerControl {
   public:
     static UsbAlsaMixerControl& getInstance();
@@ -91,16 +39,16 @@
     ndk::ScopedAStatus setMasterMute(bool muted);
     ndk::ScopedAStatus setMasterVolume(float volume);
     // The volume settings can be different on sound cards. It is controlled by streams.
-    ndk::ScopedAStatus setVolumes(int card, std::vector<float> volumes);
+    ndk::ScopedAStatus setVolumes(int card, const std::vector<float>& volumes);
 
   private:
-    std::shared_ptr<AlsaMixer> getAlsaMixer(int card);
-    std::map<int, std::shared_ptr<AlsaMixer>> getAlsaMixers();
+    std::shared_ptr<alsa::Mixer> getAlsaMixer(int card);
+    std::map<int, std::shared_ptr<alsa::Mixer>> getAlsaMixers();
 
     std::mutex mLock;
     // A map whose key is the card number and value is a shared pointer to corresponding
     // AlsaMixer object.
-    std::map<int, std::shared_ptr<AlsaMixer>> mMixerControls GUARDED_BY(mLock);
+    std::map<int, std::shared_ptr<alsa::Mixer>> mMixerControls GUARDED_BY(mLock);
 };
 
 }  // namespace aidl::android::hardware::audio::core::usb
diff --git a/audio/aidl/default/usb/UsbAlsaUtils.cpp b/audio/aidl/default/usb/UsbAlsaUtils.cpp
deleted file mode 100644
index 74d9c28..0000000
--- a/audio/aidl/default/usb/UsbAlsaUtils.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2023 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 <map>
-#include <set>
-
-#include <Utils.h>
-#include <aidl/android/media/audio/common/AudioFormatType.h>
-#include <aidl/android/media/audio/common/PcmType.h>
-
-#include "UsbAlsaUtils.h"
-#include "core-impl/utils.h"
-
-using aidl::android::hardware::audio::common::getChannelCount;
-using aidl::android::media::audio::common::AudioChannelLayout;
-using aidl::android::media::audio::common::AudioFormatDescription;
-using aidl::android::media::audio::common::AudioFormatType;
-using aidl::android::media::audio::common::PcmType;
-
-namespace aidl::android::hardware::audio::core::usb {
-
-namespace {
-
-using AudioChannelCountToMaskMap = std::map<unsigned int, AudioChannelLayout>;
-using AudioFormatDescToPcmFormatMap = std::map<AudioFormatDescription, enum pcm_format>;
-using PcmFormatToAudioFormatDescMap = std::map<enum pcm_format, AudioFormatDescription>;
-
-static const AudioChannelLayout INVALID_CHANNEL_LAYOUT =
-        AudioChannelLayout::make<AudioChannelLayout::Tag::invalid>(0);
-
-#define DEFINE_CHANNEL_LAYOUT_MASK(n) \
-    AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(AudioChannelLayout::LAYOUT_##n)
-
-static const std::set<AudioChannelLayout> SUPPORTED_OUT_CHANNEL_LAYOUTS = {
-        DEFINE_CHANNEL_LAYOUT_MASK(MONO),          DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
-        DEFINE_CHANNEL_LAYOUT_MASK(2POINT1),       DEFINE_CHANNEL_LAYOUT_MASK(QUAD),
-        DEFINE_CHANNEL_LAYOUT_MASK(PENTA),         DEFINE_CHANNEL_LAYOUT_MASK(5POINT1),
-        DEFINE_CHANNEL_LAYOUT_MASK(6POINT1),       DEFINE_CHANNEL_LAYOUT_MASK(7POINT1),
-        DEFINE_CHANNEL_LAYOUT_MASK(7POINT1POINT4), DEFINE_CHANNEL_LAYOUT_MASK(22POINT2),
-};
-
-static const std::set<AudioChannelLayout> SUPPORTED_IN_CHANNEL_LAYOUTS = {
-        DEFINE_CHANNEL_LAYOUT_MASK(MONO),
-        DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
-};
-
-#define DEFINE_CHANNEL_INDEX_MASK(n) \
-    AudioChannelLayout::make<AudioChannelLayout::Tag::indexMask>(AudioChannelLayout::INDEX_MASK_##n)
-
-static const std::set<AudioChannelLayout> SUPPORTED_INDEX_CHANNEL_LAYOUTS = {
-        DEFINE_CHANNEL_INDEX_MASK(1),  DEFINE_CHANNEL_INDEX_MASK(2),  DEFINE_CHANNEL_INDEX_MASK(3),
-        DEFINE_CHANNEL_INDEX_MASK(4),  DEFINE_CHANNEL_INDEX_MASK(5),  DEFINE_CHANNEL_INDEX_MASK(6),
-        DEFINE_CHANNEL_INDEX_MASK(7),  DEFINE_CHANNEL_INDEX_MASK(8),  DEFINE_CHANNEL_INDEX_MASK(9),
-        DEFINE_CHANNEL_INDEX_MASK(10), DEFINE_CHANNEL_INDEX_MASK(11), DEFINE_CHANNEL_INDEX_MASK(12),
-        DEFINE_CHANNEL_INDEX_MASK(13), DEFINE_CHANNEL_INDEX_MASK(14), DEFINE_CHANNEL_INDEX_MASK(15),
-        DEFINE_CHANNEL_INDEX_MASK(16), DEFINE_CHANNEL_INDEX_MASK(17), DEFINE_CHANNEL_INDEX_MASK(18),
-        DEFINE_CHANNEL_INDEX_MASK(19), DEFINE_CHANNEL_INDEX_MASK(20), DEFINE_CHANNEL_INDEX_MASK(21),
-        DEFINE_CHANNEL_INDEX_MASK(22), DEFINE_CHANNEL_INDEX_MASK(23), DEFINE_CHANNEL_INDEX_MASK(24),
-};
-
-static AudioChannelCountToMaskMap make_ChannelCountToMaskMap(
-        const std::set<AudioChannelLayout>& channelMasks) {
-    AudioChannelCountToMaskMap channelMaskToCountMap;
-    for (const auto& channelMask : channelMasks) {
-        channelMaskToCountMap.emplace(getChannelCount(channelMask), channelMask);
-    }
-    return channelMaskToCountMap;
-}
-
-const AudioChannelCountToMaskMap& getSupportedChannelOutLayoutMap() {
-    static const AudioChannelCountToMaskMap outLayouts =
-            make_ChannelCountToMaskMap(SUPPORTED_OUT_CHANNEL_LAYOUTS);
-    return outLayouts;
-}
-
-const AudioChannelCountToMaskMap& getSupportedChannelInLayoutMap() {
-    static const AudioChannelCountToMaskMap inLayouts =
-            make_ChannelCountToMaskMap(SUPPORTED_IN_CHANNEL_LAYOUTS);
-    return inLayouts;
-}
-
-const AudioChannelCountToMaskMap& getSupportedChannelIndexLayoutMap() {
-    static const AudioChannelCountToMaskMap indexLayouts =
-            make_ChannelCountToMaskMap(SUPPORTED_INDEX_CHANNEL_LAYOUTS);
-    return indexLayouts;
-}
-
-AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
-    AudioFormatDescription result;
-    result.type = type;
-    return result;
-}
-
-AudioFormatDescription make_AudioFormatDescription(PcmType pcm) {
-    auto result = make_AudioFormatDescription(AudioFormatType::PCM);
-    result.pcm = pcm;
-    return result;
-}
-
-const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() {
-    static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = {
-            {make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8},
-            {make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE},
-            {make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE},
-            {make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE},
-            {make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE},
-            {make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE},
-    };
-    return formatDescToPcmFormatMap;
-}
-
-static PcmFormatToAudioFormatDescMap make_PcmFormatToAudioFormatDescMap(
-        const AudioFormatDescToPcmFormatMap& formatDescToPcmFormatMap) {
-    PcmFormatToAudioFormatDescMap result;
-    for (const auto& formatPair : formatDescToPcmFormatMap) {
-        result.emplace(formatPair.second, formatPair.first);
-    }
-    return result;
-}
-
-const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() {
-    static const PcmFormatToAudioFormatDescMap pcmFormatToFormatDescMap =
-            make_PcmFormatToAudioFormatDescMap(getAudioFormatDescriptorToPcmFormatMap());
-    return pcmFormatToFormatDescMap;
-}
-
-}  // namespace
-
-AudioChannelLayout getChannelLayoutMaskFromChannelCount(unsigned int channelCount, int isInput) {
-    return findValueOrDefault(
-            isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
-            channelCount, INVALID_CHANNEL_LAYOUT);
-}
-
-AudioChannelLayout getChannelIndexMaskFromChannelCount(unsigned int channelCount) {
-    return findValueOrDefault(getSupportedChannelIndexLayoutMap(), channelCount,
-                              INVALID_CHANNEL_LAYOUT);
-}
-
-unsigned int getChannelCountFromChannelMask(const AudioChannelLayout& channelMask, bool isInput) {
-    switch (channelMask.getTag()) {
-        case AudioChannelLayout::Tag::layoutMask: {
-            return findKeyOrDefault(
-                    isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
-                    (unsigned int)getChannelCount(channelMask), 0u /*defaultValue*/);
-        }
-        case AudioChannelLayout::Tag::indexMask: {
-            return findKeyOrDefault(getSupportedChannelIndexLayoutMap(),
-                                    (unsigned int)getChannelCount(channelMask),
-                                    0u /*defaultValue*/);
-        }
-        case AudioChannelLayout::Tag::none:
-        case AudioChannelLayout::Tag::invalid:
-        case AudioChannelLayout::Tag::voiceMask:
-        default:
-            return 0;
-    }
-}
-
-AudioFormatDescription legacy2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy) {
-    return findValueOrDefault(getPcmFormatToAudioFormatDescMap(), legacy, AudioFormatDescription());
-}
-
-pcm_format aidl2legacy_AudioFormatDescription_pcm_format(const AudioFormatDescription& aidl) {
-    return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID);
-}
-
-}  // namespace aidl::android::hardware::audio::core::usb
diff --git a/audio/aidl/default/usb/UsbAlsaUtils.h b/audio/aidl/default/usb/UsbAlsaUtils.h
deleted file mode 100644
index 2d2f0f4..0000000
--- a/audio/aidl/default/usb/UsbAlsaUtils.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2023 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
-
-#include <aidl/android/media/audio/common/AudioChannelLayout.h>
-#include <aidl/android/media/audio/common/AudioFormatDescription.h>
-
-extern "C" {
-#include <tinyalsa/pcm.h>
-}
-
-namespace aidl::android::hardware::audio::core::usb {
-
-::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount(
-        unsigned int channelCount, int isInput);
-::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount(
-        unsigned int channelCount);
-unsigned int getChannelCountFromChannelMask(
-        const ::aidl::android::media::audio::common::AudioChannelLayout& channelMask, bool isInput);
-::aidl::android::media::audio::common::AudioFormatDescription
-legacy2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy);
-pcm_format aidl2legacy_AudioFormatDescription_pcm_format(
-        const ::aidl::android::media::audio::common::AudioFormatDescription& aidl);
-
-}  // namespace aidl::android::hardware::audio::core::usb
\ No newline at end of file
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index b3ca293..712f28a 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/Android.bp
@@ -31,6 +31,7 @@
         "kernel_config_q_4.14",
         "kernel_config_q_4.19",
     ],
+    core_hals: "only",
 }
 
 vintf_compatibility_matrix {
@@ -44,6 +45,7 @@
         "kernel_config_r_4.19",
         "kernel_config_r_5.4",
     ],
+    core_hals: "only",
 }
 
 vintf_compatibility_matrix {
@@ -57,6 +59,7 @@
         "kernel_config_s_5.4",
         "kernel_config_s_5.10",
     ],
+    core_hals: "only",
 }
 
 vintf_compatibility_matrix {
@@ -69,6 +72,7 @@
         "kernel_config_t_5.10",
         "kernel_config_t_5.15",
     ],
+    core_hals: "only",
 }
 
 vintf_compatibility_matrix {
@@ -81,6 +85,7 @@
         "kernel_config_u_5.15",
         "kernel_config_u_6.1",
     ],
+    core_hals: "only",
 }
 
 vintf_compatibility_matrix {
@@ -93,4 +98,5 @@
         "kernel_config_v_5.15",
         "kernel_config_v_6.1",
     ],
+    core_hals: "only",
 }
diff --git a/compatibility_matrices/compatibility_matrix.9.xml b/compatibility_matrices/compatibility_matrix.9.xml
index 14b9210..559e7c2 100644
--- a/compatibility_matrices/compatibility_matrix.9.xml
+++ b/compatibility_matrices/compatibility_matrix.9.xml
@@ -248,7 +248,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.graphics.composer3</name>
-        <version>2</version>
+        <version>3</version>
         <interface>
             <name>IComposer</name>
             <instance>default</instance>
diff --git a/graphics/Android.bp b/graphics/Android.bp
index 2fbcb41..8a69f09 100644
--- a/graphics/Android.bp
+++ b/graphics/Android.bp
@@ -53,13 +53,13 @@
 cc_defaults {
     name: "android.hardware.graphics.composer3-ndk_static",
     static_libs: [
-        "android.hardware.graphics.composer3-V2-ndk",
+        "android.hardware.graphics.composer3-V3-ndk",
     ],
 }
 
 cc_defaults {
     name: "android.hardware.graphics.composer3-ndk_shared",
     shared_libs: [
-        "android.hardware.graphics.composer3-V2-ndk",
+        "android.hardware.graphics.composer3-V3-ndk",
     ],
 }
diff --git a/graphics/composer/aidl/Android.bp b/graphics/composer/aidl/Android.bp
index 40448ec..88abb73 100644
--- a/graphics/composer/aidl/Android.bp
+++ b/graphics/composer/aidl/Android.bp
@@ -31,7 +31,7 @@
         enabled: true,
         support_system_process: true,
     },
-    frozen: true,
+    frozen: false,
     vndk_use_version: "1",
     srcs: [
         "android/hardware/graphics/composer3/*.aidl",
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayConfiguration.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayConfiguration.aidl
new file mode 100644
index 0000000..908842a
--- /dev/null
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayConfiguration.aidl
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.graphics.composer3;
+@VintfStability
+parcelable DisplayConfiguration {
+  int configId;
+  int width;
+  int height;
+  @nullable android.hardware.graphics.composer3.DisplayConfiguration.Dpi dpi;
+  int configGroup;
+  int vsyncPeriod;
+  parcelable Dpi {
+    float x;
+    float y;
+  }
+}
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl
index cb85a88..2f08b6f 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl
@@ -44,6 +44,9 @@
   float[] getDataspaceSaturationMatrix(android.hardware.graphics.common.Dataspace dataspace);
   int getDisplayAttribute(long display, int config, android.hardware.graphics.composer3.DisplayAttribute attribute);
   android.hardware.graphics.composer3.DisplayCapability[] getDisplayCapabilities(long display);
+  /**
+   * @deprecated use getDisplayConfigurations instead. For legacy support getDisplayConfigs should return at least one valid config. All the configs returned from the getDisplayConfigs should also be returned from getDisplayConfigurations.
+   */
   int[] getDisplayConfigs(long display);
   android.hardware.graphics.composer3.DisplayConnectionType getDisplayConnectionType(long display);
   android.hardware.graphics.composer3.DisplayIdentification getDisplayIdentificationData(long display);
@@ -79,6 +82,7 @@
   android.hardware.graphics.common.HdrConversionCapability[] getHdrConversionCapabilities();
   android.hardware.graphics.common.Hdr setHdrConversionStrategy(in android.hardware.graphics.common.HdrConversionStrategy conversionStrategy);
   void setRefreshRateChangedCallbackDebugEnabled(long display, boolean enabled);
+  android.hardware.graphics.composer3.DisplayConfiguration[] getDisplayConfigurations(long display);
   const int EX_BAD_CONFIG = 1;
   const int EX_BAD_DISPLAY = 2;
   const int EX_BAD_LAYER = 3;
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayConfiguration.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayConfiguration.aidl
new file mode 100644
index 0000000..b0095d2
--- /dev/null
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayConfiguration.aidl
@@ -0,0 +1,63 @@
+/**
+ * Copyright 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.graphics.composer3;
+
+@VintfStability
+parcelable DisplayConfiguration {
+    /**
+     * The config id, to be used with IComposerClient.setActiveConfig.
+     */
+    int configId;
+
+    /**
+     * Dimensions in pixels
+     */
+    int width;
+    int height;
+
+    /**
+     * Dots per thousand inches.
+     * If the DPI for a configuration is unavailable or is
+     * considered unreliable, the device may set null instead.
+     */
+    parcelable Dpi {
+        float x;
+        float y;
+    }
+    @nullable Dpi dpi;
+
+    /**
+     * The configuration group ID this config is associated to.
+     * Switching between configurations within the same group may be
+     * done seamlessly in some conditions via
+     * setActiveConfigWithConstraints. Configurations which share the
+     * same config group are similar in all attributes except for the
+     * vsync period.
+     */
+    int configGroup;
+
+    /**
+     * Vsync period in nanoseconds. This period represents the internal
+     * frequency of the display. IComposerCallback.onVsync is expected
+     * to be called on each vsync event. For non-VRR configurations, a
+     * frame can be presented on each vsync event.
+     *
+     * A present fence, retrieved from CommandResultPayload.presentFence
+     * must be signaled on a vsync boundary.
+     */
+    int vsyncPeriod;
+}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl
index 4e77f86..5d04a28 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl
@@ -28,6 +28,7 @@
 import android.hardware.graphics.composer3.DisplayAttribute;
 import android.hardware.graphics.composer3.DisplayCapability;
 import android.hardware.graphics.composer3.DisplayCommand;
+import android.hardware.graphics.composer3.DisplayConfiguration;
 import android.hardware.graphics.composer3.DisplayConnectionType;
 import android.hardware.graphics.composer3.DisplayContentSample;
 import android.hardware.graphics.composer3.DisplayContentSamplingAttributes;
@@ -263,15 +264,12 @@
     DisplayCapability[] getDisplayCapabilities(long display);
 
     /**
-     * Returns handles for all of the valid display configurations on this
-     * display.
-     * This should never return INVALID_CONFIGURATION as a valid value.
+     * @deprecated use getDisplayConfigurations instead.
+     * For legacy support getDisplayConfigs should return at least one valid config.
+     * All the configs returned from the getDisplayConfigs should also be returned
+     * from getDisplayConfigurations.
      *
-     * @param display is the display to query.
-     *
-     * @return is an array of configuration handles.
-     *
-     * @exception EX_BAD_DISPLAY when an invalid display handle was passed in.
+     * @see getDisplayConfigurations
      */
     int[] getDisplayConfigs(long display);
 
@@ -864,4 +862,15 @@
      *        false when refresh rate callback is disabled.
      */
     void setRefreshRateChangedCallbackDebugEnabled(long display, boolean enabled);
+
+    /**
+     * Returns all of the valid display configurations.
+     * getDisplayConfigurations is the superset of getDisplayConfigs and
+     * getDisplayConfigs should return at least one config.
+     *
+     * @param display is the display for which the configurations are requested.
+     *
+     * @see getDisplayConfigs
+     */
+    DisplayConfiguration[] getDisplayConfigurations(long display);
 }
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.cpp b/graphics/composer/aidl/vts/VtsComposerClient.cpp
index 25b0ca0..bf42d88 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.cpp
+++ b/graphics/composer/aidl/vts/VtsComposerClient.cpp
@@ -58,7 +58,7 @@
     return verifyComposerCallbackParams() && destroyAllLayers();
 }
 
-std::pair<ScopedAStatus, int32_t> VtsComposerClient::getInterfaceVersion() {
+std::pair<ScopedAStatus, int32_t> VtsComposerClient::getInterfaceVersion() const {
     int32_t version = 1;
     auto status = mComposerClient->getInterfaceVersion(&version);
     return {std::move(status), version};
@@ -295,7 +295,24 @@
 std::pair<ScopedAStatus, std::vector<int32_t>> VtsComposerClient::getDisplayConfigs(
         int64_t display) {
     std::vector<int32_t> outConfigs;
-    return {mComposerClient->getDisplayConfigs(display, &outConfigs), outConfigs};
+    if (!getDisplayConfigurationSupported()) {
+        return {mComposerClient->getDisplayConfigs(display, &outConfigs), outConfigs};
+    }
+
+    auto [status, configs] = getDisplayConfigurations(display);
+    if (!status.isOk()) {
+        return {std::move(status), outConfigs};
+    }
+    for (const auto& config : configs) {
+        outConfigs.emplace_back(config.configId);
+    }
+    return {std::move(status), outConfigs};
+}
+
+std::pair<ScopedAStatus, std::vector<DisplayConfiguration>>
+VtsComposerClient::getDisplayConfigurations(int64_t display) {
+    std::vector<DisplayConfiguration> outConfigs;
+    return {mComposerClient->getDisplayConfigurations(display, &outConfigs), outConfigs};
 }
 
 std::pair<ScopedAStatus, int32_t> VtsComposerClient::getDisplayVsyncPeriod(int64_t display) {
@@ -439,31 +456,41 @@
         vtsDisplays.reserve(displays.size());
         for (int64_t display : displays) {
             auto vtsDisplay = VtsDisplay{display};
-            auto configs = getDisplayConfigs(display);
-            if (!configs.first.isOk()) {
-                ALOGE("Unable to get the displays for test, failed to get the configs "
-                      "for display %" PRId64,
-                      display);
-                return {std::move(configs.first), vtsDisplays};
-            }
-            for (int config : configs.second) {
-                auto status = addDisplayConfig(&vtsDisplay, config);
+            if (getDisplayConfigurationSupported()) {
+                auto [status, configs] = getDisplayConfigurations(display);
                 if (!status.isOk()) {
-                    ALOGE("Unable to get the displays for test, failed to add config "
+                    ALOGE("Unable to get the displays for test, failed to get the DisplayConfigs "
                           "for display %" PRId64,
                           display);
                     return {std::move(status), vtsDisplays};
                 }
+                addDisplayConfigs(&vtsDisplay, configs);
+            } else {
+                auto [status, configs] = getDisplayConfigs(display);
+                if (!status.isOk()) {
+                    ALOGE("Unable to get the displays for test, failed to get the configs "
+                          "for display %" PRId64,
+                          display);
+                    return {std::move(status), vtsDisplays};
+                }
+                for (int config : configs) {
+                    status = addDisplayConfigLegacy(&vtsDisplay, config);
+                    if (!status.isOk()) {
+                        ALOGE("Unable to get the displays for test, failed to add config "
+                              "for display %" PRId64,
+                              display);
+                        return {std::move(status), vtsDisplays};
+                    }
+                }
             }
-
-            auto config = getActiveConfig(display);
-            if (!config.first.isOk()) {
+            auto activeConfig = getActiveConfig(display);
+            if (!activeConfig.first.isOk()) {
                 ALOGE("Unable to get the displays for test, failed to get active config "
-                      "for display %" PRId64, display);
-                return {std::move(config.first), vtsDisplays};
+                      "for display %" PRId64,
+                      display);
+                return {std::move(activeConfig.first), vtsDisplays};
             }
-
-            auto status = updateDisplayProperties(&vtsDisplay, config.second);
+            auto status = updateDisplayProperties(&vtsDisplay, activeConfig.second);
             if (!status.isOk()) {
                 ALOGE("Unable to get the displays for test, "
                       "failed to update the properties "
@@ -480,39 +507,53 @@
     }
 }
 
-ScopedAStatus VtsComposerClient::addDisplayConfig(VtsDisplay* vtsDisplay, int32_t config) {
-    const auto width =
-            getDisplayAttribute(vtsDisplay->getDisplayId(), config, DisplayAttribute::WIDTH);
-    const auto height =
-            getDisplayAttribute(vtsDisplay->getDisplayId(), config, DisplayAttribute::HEIGHT);
+void VtsComposerClient::addDisplayConfigs(VtsDisplay* vtsDisplay,
+                                          const std::vector<DisplayConfiguration>& configs) {
+    for (const auto& config : configs) {
+        vtsDisplay->addDisplayConfig(config.configId, {config.vsyncPeriod, config.configGroup});
+    }
+}
+
+ScopedAStatus VtsComposerClient::addDisplayConfigLegacy(VtsDisplay* vtsDisplay, int32_t config) {
     const auto vsyncPeriod =
             getDisplayAttribute(vtsDisplay->getDisplayId(), config, DisplayAttribute::VSYNC_PERIOD);
     const auto configGroup =
             getDisplayAttribute(vtsDisplay->getDisplayId(), config, DisplayAttribute::CONFIG_GROUP);
-    if (width.first.isOk() && height.first.isOk() && vsyncPeriod.first.isOk() &&
-        configGroup.first.isOk()) {
+    if (vsyncPeriod.first.isOk() && configGroup.first.isOk()) {
         vtsDisplay->addDisplayConfig(config, {vsyncPeriod.second, configGroup.second});
         return ScopedAStatus::ok();
     }
 
-    LOG(ERROR) << "Failed to update display property for width: " << width.first.isOk()
-               << ", height: " << height.first.isOk() << ", vsync: " << vsyncPeriod.first.isOk()
+    LOG(ERROR) << "Failed to update display property vsync: " << vsyncPeriod.first.isOk()
                << ", config: " << configGroup.first.isOk();
     return ScopedAStatus::fromServiceSpecificError(IComposerClient::EX_BAD_CONFIG);
 }
 
 ScopedAStatus VtsComposerClient::updateDisplayProperties(VtsDisplay* vtsDisplay, int32_t config) {
-    const auto width =
-            getDisplayAttribute(vtsDisplay->getDisplayId(), config, DisplayAttribute::WIDTH);
-    const auto height =
-            getDisplayAttribute(vtsDisplay->getDisplayId(), config, DisplayAttribute::HEIGHT);
-    if (width.first.isOk() && height.first.isOk()) {
-        vtsDisplay->setDimensions(width.second, height.second);
-        return ScopedAStatus::ok();
-    }
+    if (getDisplayConfigurationSupported()) {
+        auto [status, configs] = getDisplayConfigurations(vtsDisplay->getDisplayId());
+        if (status.isOk()) {
+            for (const auto& displayConfig : configs) {
+                if (displayConfig.configId == config) {
+                    vtsDisplay->setDimensions(displayConfig.width, displayConfig.height);
+                    return ScopedAStatus::ok();
+                }
+            }
+        }
+        LOG(ERROR) << "Failed to update display property with DisplayConfig";
+    } else {
+        const auto width =
+                getDisplayAttribute(vtsDisplay->getDisplayId(), config, DisplayAttribute::WIDTH);
+        const auto height =
+                getDisplayAttribute(vtsDisplay->getDisplayId(), config, DisplayAttribute::HEIGHT);
+        if (width.first.isOk() && height.first.isOk()) {
+            vtsDisplay->setDimensions(width.second, height.second);
+            return ScopedAStatus::ok();
+        }
 
-    LOG(ERROR) << "Failed to update display property for width: " << width.first.isOk()
-               << ", height: " << height.first.isOk();
+        LOG(ERROR) << "Failed to update display property for width: " << width.first.isOk()
+                   << ", height: " << height.first.isOk();
+    }
     return ScopedAStatus::fromServiceSpecificError(IComposerClient::EX_BAD_CONFIG);
 }
 
@@ -576,6 +617,13 @@
     return isValid;
 }
 
+bool VtsComposerClient::getDisplayConfigurationSupported() const {
+    auto [status, interfaceVersion] = getInterfaceVersion();
+    EXPECT_TRUE(status.isOk());
+    // getDisplayConfigurations api is supported starting interface version 3
+    return interfaceVersion >= 3;
+}
+
 bool VtsComposerClient::destroyAllLayers() {
     std::unordered_map<int64_t, DisplayResource> physicalDisplays;
     while (!mDisplayResources.empty()) {
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.h b/graphics/composer/aidl/vts/VtsComposerClient.h
index ea3318c..1add23c 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.h
+++ b/graphics/composer/aidl/vts/VtsComposerClient.h
@@ -61,7 +61,7 @@
 
     bool tearDown();
 
-    std::pair<ScopedAStatus, int32_t> getInterfaceVersion();
+    std::pair<ScopedAStatus, int32_t> getInterfaceVersion() const;
 
     std::pair<ScopedAStatus, VirtualDisplay> createVirtualDisplay(int32_t width, int32_t height,
                                                                   PixelFormat pixelFormat,
@@ -142,6 +142,9 @@
 
     std::pair<ScopedAStatus, std::vector<int32_t>> getDisplayConfigs(int64_t display);
 
+    std::pair<ScopedAStatus, std::vector<DisplayConfiguration>> getDisplayConfigurations(
+            int64_t display);
+
     std::pair<ScopedAStatus, int32_t> getDisplayVsyncPeriod(int64_t display);
 
     ScopedAStatus setAutoLowLatencyMode(int64_t display, bool isEnabled);
@@ -190,7 +193,9 @@
     std::vector<RefreshRateChangedDebugData> takeListOfRefreshRateChangedDebugData();
 
   private:
-    ScopedAStatus addDisplayConfig(VtsDisplay* vtsDisplay, int32_t config);
+    void addDisplayConfigs(VtsDisplay*, const std::vector<DisplayConfiguration>&);
+    ScopedAStatus addDisplayConfigLegacy(VtsDisplay*, int32_t config);
+    bool getDisplayConfigurationSupported() const;
     ScopedAStatus updateDisplayProperties(VtsDisplay* vtsDisplay, int32_t config);
 
     ScopedAStatus addDisplayToDisplayResources(int64_t display, bool isVirtual);
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
index bbcd36f..d559213 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
@@ -1208,6 +1208,110 @@
     }
 }
 
+class GraphicsComposerAidlV3Test : public GraphicsComposerAidlTest {
+  protected:
+    void SetUp() override {
+        GraphicsComposerAidlTest::SetUp();
+        if (getInterfaceVersion() <= 2) {
+            GTEST_SKIP() << "Device interface version is expected to be >= 3";
+        }
+    }
+};
+
+TEST_P(GraphicsComposerAidlV3Test, GetDisplayConfigurations) {
+    for (const auto& display : mDisplays) {
+        const auto& [status, displayConfigurations] =
+                mComposerClient->getDisplayConfigurations(display.getDisplayId());
+        EXPECT_TRUE(status.isOk());
+        EXPECT_FALSE(displayConfigurations.empty());
+
+        for (const auto& displayConfig : displayConfigurations) {
+            EXPECT_NE(-1, displayConfig.width);
+            EXPECT_NE(-1, displayConfig.height);
+            EXPECT_NE(-1, displayConfig.vsyncPeriod);
+            EXPECT_NE(-1, displayConfig.configGroup);
+            if (displayConfig.dpi) {
+                EXPECT_NE(-1, displayConfig.dpi->x);
+                EXPECT_NE(-1, displayConfig.dpi->y);
+            }
+        }
+    }
+}
+
+TEST_P(GraphicsComposerAidlV3Test, GetDisplayConfigsIsSubsetOfGetDisplayConfigurations) {
+    for (const auto& display : mDisplays) {
+        const auto& [status, displayConfigurations] =
+                mComposerClient->getDisplayConfigurations(display.getDisplayId());
+        EXPECT_TRUE(status.isOk());
+
+        const auto& [legacyConfigStatus, legacyConfigs] =
+                mComposerClient->getDisplayConfigs(display.getDisplayId());
+        EXPECT_TRUE(legacyConfigStatus.isOk());
+        EXPECT_FALSE(legacyConfigs.empty());
+        EXPECT_TRUE(legacyConfigs.size() <= displayConfigurations.size());
+
+        for (const auto legacyConfigId : legacyConfigs) {
+            const auto& legacyWidth = mComposerClient->getDisplayAttribute(
+                    display.getDisplayId(), legacyConfigId, DisplayAttribute::WIDTH);
+            const auto& legacyHeight = mComposerClient->getDisplayAttribute(
+                    display.getDisplayId(), legacyConfigId, DisplayAttribute::HEIGHT);
+            const auto& legacyVsyncPeriod = mComposerClient->getDisplayAttribute(
+                    display.getDisplayId(), legacyConfigId, DisplayAttribute::VSYNC_PERIOD);
+            const auto& legacyConfigGroup = mComposerClient->getDisplayAttribute(
+                    display.getDisplayId(), legacyConfigId, DisplayAttribute::CONFIG_GROUP);
+            const auto& legacyDpiX = mComposerClient->getDisplayAttribute(
+                    display.getDisplayId(), legacyConfigId, DisplayAttribute::DPI_X);
+            const auto& legacyDpiY = mComposerClient->getDisplayAttribute(
+                    display.getDisplayId(), legacyConfigId, DisplayAttribute::DPI_Y);
+
+            EXPECT_TRUE(legacyWidth.first.isOk() && legacyHeight.first.isOk() &&
+                        legacyVsyncPeriod.first.isOk() && legacyConfigGroup.first.isOk());
+
+            EXPECT_TRUE(std::any_of(
+                    displayConfigurations.begin(), displayConfigurations.end(),
+                    [&](const auto& displayConfiguration) {
+                        const bool requiredAttributesPredicate =
+                                displayConfiguration.configId == legacyConfigId &&
+                                displayConfiguration.width == legacyWidth.second &&
+                                displayConfiguration.height == legacyHeight.second &&
+                                displayConfiguration.vsyncPeriod == legacyVsyncPeriod.second &&
+                                displayConfiguration.configGroup == legacyConfigGroup.second;
+
+                        if (!requiredAttributesPredicate) {
+                            // Required attributes did not match
+                            return false;
+                        }
+
+                        // Check optional attributes
+                        const auto& [legacyDpiXStatus, legacyDpiXValue] = legacyDpiX;
+                        const auto& [legacyDpiYStatus, legacyDpiYValue] = legacyDpiY;
+                        if (displayConfiguration.dpi) {
+                            if (!legacyDpiXStatus.isOk() || !legacyDpiYStatus.isOk()) {
+                                // getDisplayAttribute failed for optional attributes
+                                return false;
+                            }
+
+                            // DPI values in DisplayConfigurations are not scaled (* 1000.f)
+                            // the way they are in the legacy DisplayConfigs.
+                            constexpr float kEpsilon = 0.001f;
+                            return std::abs(displayConfiguration.dpi->x -
+                                            legacyDpiXValue / 1000.f) < kEpsilon &&
+                                   std::abs(displayConfiguration.dpi->y -
+                                            legacyDpiYValue / 1000.f) < kEpsilon;
+                        } else {
+                            return !legacyDpiXStatus.isOk() && !legacyDpiYStatus.isOk() &&
+                                   EX_SERVICE_SPECIFIC == legacyDpiXStatus.getExceptionCode() &&
+                                   EX_SERVICE_SPECIFIC == legacyDpiYStatus.getExceptionCode() &&
+                                   IComposerClient::EX_UNSUPPORTED ==
+                                           legacyDpiXStatus.getServiceSpecificError() &&
+                                   IComposerClient::EX_UNSUPPORTED ==
+                                           legacyDpiYStatus.getServiceSpecificError();
+                        }
+                    }));
+        }
+    }
+}
+
 // Tests for Command.
 class GraphicsComposerAidlCommandTest : public GraphicsComposerAidlTest {
   protected:
@@ -2641,6 +2745,11 @@
         PerInstance, GraphicsComposerAidlV2Test,
         testing::ValuesIn(::android::getAidlHalInstanceNames(IComposer::descriptor)),
         ::android::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsComposerAidlV3Test);
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, GraphicsComposerAidlV3Test,
+        testing::ValuesIn(::android::getAidlHalInstanceNames(IComposer::descriptor)),
+        ::android::PrintInstanceNameToString);
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsComposerAidlCommandV2Test);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, GraphicsComposerAidlCommandV2Test,