Merge changes from topic "cherrypicker-L18700000961261875:N88400001378560897"

* changes:
  add fuzz test and fix the Thread network HAL compile errors
  Add Thread network HAL
diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h
index bd903d7..3b08de7 100644
--- a/audio/aidl/common/include/Utils.h
+++ b/audio/aidl/common/include/Utils.h
@@ -29,6 +29,21 @@
 #include <aidl/android/media/audio/common/AudioMode.h>
 #include <aidl/android/media/audio/common/AudioOutputFlags.h>
 #include <aidl/android/media/audio/common/PcmType.h>
+#include <android/binder_auto_utils.h>
+
+namespace ndk {
+
+// This enables use of 'error/expected_utils' for ScopedAStatus.
+
+inline bool errorIsOk(const ScopedAStatus& s) {
+    return s.isOk();
+}
+
+inline std::string errorToString(const ScopedAStatus& s) {
+    return s.getDescription();
+}
+
+}  // namespace ndk
 
 namespace aidl::android::hardware::audio::common {
 
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index bda0de2..e9294cf 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -18,6 +18,7 @@
         "libbinder_ndk",
         "libcutils",
         "libfmq",
+        "libnbaio_mono",
         "libstagefright_foundation",
         "libtinyalsav2",
         "libutils",
@@ -76,6 +77,10 @@
         "Stream.cpp",
         "StreamStub.cpp",
         "Telephony.cpp",
+        "r_submix/ModuleRemoteSubmix.cpp",
+        "r_submix/RemoteSubmixUtils.cpp",
+        "r_submix/SubmixRoute.cpp",
+        "r_submix/StreamRemoteSubmix.cpp",
         "usb/ModuleUsb.cpp",
         "usb/StreamUsb.cpp",
         "usb/UsbAlsaMixerControl.cpp",
diff --git a/audio/aidl/default/EffectConfig.cpp b/audio/aidl/default/EffectConfig.cpp
index 71d111b..f3f674f 100644
--- a/audio/aidl/default/EffectConfig.cpp
+++ b/audio/aidl/default/EffectConfig.cpp
@@ -18,6 +18,7 @@
 #include <string>
 #define LOG_TAG "AHAL_EffectConfig"
 #include <android-base/logging.h>
+#include <system/audio_aidl_utils.h>
 #include <system/audio_effects/audio_effects_conf.h>
 #include <system/audio_effects/effect_uuid.h>
 
@@ -162,7 +163,7 @@
     RETURN_VALUE_IF((libraryUuid.uuid == getEffectUuidZero()), false, "invalidUuidAttribute");
 
     LOG(DEBUG) << __func__ << (isProxy ? " proxy " : libraryUuid.name) << " : "
-               << libraryUuid.uuid.toString();
+               << ::android::audio::utils::toString(libraryUuid.uuid);
     return true;
 }
 
@@ -250,6 +251,7 @@
     V("downmix", Downmix)                                  \
     V("dynamics_processing", DynamicsProcessing)           \
     V("equalizer", Equalizer)                              \
+    V("extensioneffect", Extension)                        \
     V("haptic_generator", HapticGenerator)                 \
     V("loudness_enhancer", LoudnessEnhancer)               \
     V("env_reverb", EnvReverb)                             \
diff --git a/audio/aidl/default/EffectFactory.cpp b/audio/aidl/default/EffectFactory.cpp
index 7073a10..8ed62c9 100644
--- a/audio/aidl/default/EffectFactory.cpp
+++ b/audio/aidl/default/EffectFactory.cpp
@@ -25,6 +25,7 @@
 
 #include <android-base/logging.h>
 #include <android/binder_ibinder_platform.h>
+#include <system/audio_aidl_utils.h>
 #include <system/audio_effects/effect_uuid.h>
 #include <system/thread_defs.h>
 
@@ -47,7 +48,7 @@
         for (const auto& it : mEffectMap) {
             if (auto spEffect = it.first.lock()) {
                 LOG(ERROR) << __func__ << " erase remaining instance UUID "
-                           << it.second.first.toString();
+                           << ::android::audio::utils::toString(it.second.first);
                 destroyEffectImpl(spEffect);
             }
         }
@@ -123,7 +124,7 @@
 
 ndk::ScopedAStatus Factory::createEffect(const AudioUuid& in_impl_uuid,
                                          std::shared_ptr<IEffect>* _aidl_return) {
-    LOG(DEBUG) << __func__ << ": UUID " << in_impl_uuid.toString();
+    LOG(DEBUG) << __func__ << ": UUID " << ::android::audio::utils::toString(in_impl_uuid);
     if (mEffectLibMap.count(in_impl_uuid)) {
         auto& entry = mEffectLibMap[in_impl_uuid];
         getDlSyms(entry);
@@ -163,7 +164,8 @@
                       "dlNulldestroyEffectFunc");
             RETURN_IF_BINDER_EXCEPTION(interface->destroyEffectFunc(in_handle));
         } else {
-            LOG(ERROR) << __func__ << ": UUID " << uuid.toString() << " does not exist in libMap!";
+            LOG(ERROR) << __func__ << ": UUID " << ::android::audio::utils::toString(uuid)
+                       << " does not exist in libMap!";
             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
         }
         mEffectMap.erase(effectIt);
@@ -207,8 +209,8 @@
         return false;
     }
 
-    LOG(INFO) << __func__ << " dlopen lib:" << path << "\nimpl:" << impl.toString()
-              << "\nhandle:" << libHandle;
+    LOG(INFO) << __func__ << " dlopen lib:" << path
+              << "\nimpl:" << ::android::audio::utils::toString(impl) << "\nhandle:" << libHandle;
     auto interface = new effect_dl_interface_s{nullptr, nullptr, nullptr};
     mEffectLibMap.insert(
             {impl,
@@ -228,8 +230,10 @@
         id.uuid = configLib.uuid;
         id.proxy = proxyUuid;
         LOG(DEBUG) << __func__ << " loading lib " << path->second << ": typeUuid "
-                   << id.type.toString() << "\nimplUuid " << id.uuid.toString() << " proxyUuid "
-                   << (proxyUuid.has_value() ? proxyUuid->toString() : "null");
+                   << ::android::audio::utils::toString(id.type) << "\nimplUuid "
+                   << ::android::audio::utils::toString(id.uuid) << " proxyUuid "
+                   << (proxyUuid.has_value() ? ::android::audio::utils::toString(proxyUuid.value())
+                                             : "null");
         if (openEffectLibrary(id.uuid, path->second)) {
             mIdentitySet.insert(std::move(id));
         }
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 6885a49..48d1458 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -18,19 +18,19 @@
 #include <set>
 
 #define LOG_TAG "AHAL_Module"
-#include <android-base/logging.h>
-#include <android/binder_ibinder_platform.h>
-
 #include <Utils.h>
 #include <aidl/android/media/audio/common/AudioInputFlags.h>
 #include <aidl/android/media/audio/common/AudioOutputFlags.h>
+#include <android-base/logging.h>
+#include <android/binder_ibinder_platform.h>
+#include <error/expected_utils.h>
 
 #include "core-impl/Bluetooth.h"
 #include "core-impl/Module.h"
+#include "core-impl/ModuleRemoteSubmix.h"
 #include "core-impl/ModuleUsb.h"
 #include "core-impl/SoundDose.h"
 #include "core-impl/StreamStub.h"
-#include "core-impl/StreamUsb.h"
 #include "core-impl/Telephony.h"
 #include "core-impl/utils.h"
 
@@ -112,37 +112,14 @@
     switch (type) {
         case Module::Type::USB:
             return ndk::SharedRefBase::make<ModuleUsb>(type);
-        case Type::DEFAULT:
         case Type::R_SUBMIX:
+            return ndk::SharedRefBase::make<ModuleRemoteSubmix>(type);
+        case Type::DEFAULT:
         default:
             return ndk::SharedRefBase::make<Module>(type);
     }
 }
 
-// static
-StreamIn::CreateInstance Module::getStreamInCreator(Type type) {
-    switch (type) {
-        case Type::USB:
-            return StreamInUsb::createInstance;
-        case Type::DEFAULT:
-        case Type::R_SUBMIX:
-        default:
-            return StreamInStub::createInstance;
-    }
-}
-
-// static
-StreamOut::CreateInstance Module::getStreamOutCreator(Type type) {
-    switch (type) {
-        case Type::USB:
-            return StreamOutUsb::createInstance;
-        case Type::DEFAULT:
-        case Type::R_SUBMIX:
-        default:
-            return StreamOutStub::createInstance;
-    }
-}
-
 std::ostream& operator<<(std::ostream& os, Module::Type t) {
     switch (t) {
         case Module::Type::DEFAULT:
@@ -206,8 +183,9 @@
         StreamContext temp(
                 std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
                 std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
-                portConfigIt->format.value(), portConfigIt->channelMask.value(),
-                portConfigIt->sampleRate.value().value,
+                portConfigIt->portId, portConfigIt->format.value(),
+                portConfigIt->channelMask.value(), portConfigIt->sampleRate.value().value, flags,
+                portConfigIt->ext.get<AudioPortExt::mix>().handle,
                 std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
                 asyncCallback, outEventCallback, params);
         if (temp.isValid()) {
@@ -339,30 +317,61 @@
     do_insert(patch.sinkPortConfigIds);
 }
 
-void Module::updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch) {
+ndk::ScopedAStatus Module::updateStreamsConnectedState(const AudioPatch& oldPatch,
+                                                       const AudioPatch& newPatch) {
     // Streams from the old patch need to be disconnected, streams from the new
     // patch need to be connected. If the stream belongs to both patches, no need
     // to update it.
-    std::set<int32_t> idsToDisconnect, idsToConnect;
+    auto maybeFailure = ndk::ScopedAStatus::ok();
+    std::set<int32_t> idsToDisconnect, idsToConnect, idsToDisconnectOnFailure;
     idsToDisconnect.insert(oldPatch.sourcePortConfigIds.begin(),
                            oldPatch.sourcePortConfigIds.end());
     idsToDisconnect.insert(oldPatch.sinkPortConfigIds.begin(), oldPatch.sinkPortConfigIds.end());
     idsToConnect.insert(newPatch.sourcePortConfigIds.begin(), newPatch.sourcePortConfigIds.end());
     idsToConnect.insert(newPatch.sinkPortConfigIds.begin(), newPatch.sinkPortConfigIds.end());
     std::for_each(idsToDisconnect.begin(), idsToDisconnect.end(), [&](const auto& portConfigId) {
-        if (idsToConnect.count(portConfigId) == 0) {
-            LOG(DEBUG) << "The stream on port config id " << portConfigId << " is not connected";
-            mStreams.setStreamIsConnected(portConfigId, {});
+        if (idsToConnect.count(portConfigId) == 0 && mStreams.count(portConfigId) != 0) {
+            if (auto status = mStreams.setStreamConnectedDevices(portConfigId, {}); status.isOk()) {
+                LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id "
+                           << portConfigId << " has been disconnected";
+            } else {
+                // Disconnection is tricky to roll back, just register a failure.
+                maybeFailure = std::move(status);
+            }
         }
     });
+    if (!maybeFailure.isOk()) return maybeFailure;
     std::for_each(idsToConnect.begin(), idsToConnect.end(), [&](const auto& portConfigId) {
-        if (idsToDisconnect.count(portConfigId) == 0) {
+        if (idsToDisconnect.count(portConfigId) == 0 && mStreams.count(portConfigId) != 0) {
             const auto connectedDevices = findConnectedDevices(portConfigId);
-            LOG(DEBUG) << "The stream on port config id " << portConfigId
-                       << " is connected to: " << ::android::internal::ToString(connectedDevices);
-            mStreams.setStreamIsConnected(portConfigId, connectedDevices);
+            if (connectedDevices.empty()) {
+                // This is important as workers use the vector size to derive the connection status.
+                LOG(FATAL) << "updateStreamsConnectedState: No connected devices found for port "
+                              "config id "
+                           << portConfigId;
+            }
+            if (auto status = mStreams.setStreamConnectedDevices(portConfigId, connectedDevices);
+                status.isOk()) {
+                LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id "
+                           << portConfigId << " has been connected to: "
+                           << ::android::internal::ToString(connectedDevices);
+            } else {
+                maybeFailure = std::move(status);
+                idsToDisconnectOnFailure.insert(portConfigId);
+            }
         }
     });
+    if (!maybeFailure.isOk()) {
+        LOG(WARNING) << __func__ << ": Due to a failure, disconnecting streams on port config ids "
+                     << ::android::internal::ToString(idsToDisconnectOnFailure);
+        std::for_each(idsToDisconnectOnFailure.begin(), idsToDisconnectOnFailure.end(),
+                      [&](const auto& portConfigId) {
+                          auto status = mStreams.setStreamConnectedDevices(portConfigId, {});
+                          (void)status.isOk();  // Can't do much about a failure here.
+                      });
+        return maybeFailure;
+    }
+    return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus Module::setModuleDebug(
@@ -469,10 +478,7 @@
     }
 
     if (!mDebug.simulateDeviceConnections) {
-        if (ndk::ScopedAStatus status = populateConnectedDevicePort(&connectedPort);
-            !status.isOk()) {
-            return status;
-        }
+        RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort));
     } else {
         auto& connectedProfiles = getConfig().connectedProfiles;
         if (auto connectedProfilesIt = connectedProfiles.find(templateId);
@@ -486,6 +492,17 @@
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
 
+    for (auto profile : connectedPort.profiles) {
+        if (profile.channelMasks.empty()) {
+            LOG(ERROR) << __func__ << ": the profile " << profile.name << " has no channel masks";
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+        }
+        if (profile.sampleRates.empty()) {
+            LOG(ERROR) << __func__ << ": the profile " << profile.name << " has no sample rates";
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+        }
+    }
+
     connectedPort.id = ++getConfig().nextPortId;
     auto [connectedPortsIt, _] =
             mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::vector<int32_t>()));
@@ -647,34 +664,26 @@
     LOG(DEBUG) << __func__ << ": port config id " << in_args.portConfigId << ", buffer size "
                << in_args.bufferSizeFrames << " frames";
     AudioPort* port = nullptr;
-    if (auto status = findPortIdForNewStream(in_args.portConfigId, &port); !status.isOk()) {
-        return status;
-    }
+    RETURN_STATUS_IF_ERROR(findPortIdForNewStream(in_args.portConfigId, &port));
     if (port->flags.getTag() != AudioIoFlags::Tag::input) {
         LOG(ERROR) << __func__ << ": port config id " << in_args.portConfigId
                    << " does not correspond to an input mix port";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
     StreamContext context;
-    if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames, nullptr,
-                                          nullptr, &context);
-        !status.isOk()) {
-        return status;
-    }
+    RETURN_STATUS_IF_ERROR(createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
+                                               nullptr, nullptr, &context));
     context.fillDescriptor(&_aidl_return->desc);
     std::shared_ptr<StreamIn> stream;
-    ndk::ScopedAStatus status = getStreamInCreator(mType)(in_args.sinkMetadata, std::move(context),
-                                                          mConfig->microphones, &stream);
-    if (!status.isOk()) {
-        return status;
-    }
+    RETURN_STATUS_IF_ERROR(createInputStream(in_args.sinkMetadata, std::move(context),
+                                             mConfig->microphones, &stream));
     StreamWrapper streamWrapper(stream);
+    if (auto patchIt = mPatches.find(in_args.portConfigId); patchIt != mPatches.end()) {
+        RETURN_STATUS_IF_ERROR(
+                streamWrapper.setConnectedDevices(findConnectedDevices(in_args.portConfigId)));
+    }
     AIBinder_setMinSchedulerPolicy(streamWrapper.getBinder().get(), SCHED_NORMAL,
                                    ANDROID_PRIORITY_AUDIO);
-    auto patchIt = mPatches.find(in_args.portConfigId);
-    if (patchIt != mPatches.end()) {
-        streamWrapper.setStreamIsConnected(findConnectedDevices(in_args.portConfigId));
-    }
     mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
     _aidl_return->stream = std::move(stream);
     return ndk::ScopedAStatus::ok();
@@ -686,9 +695,7 @@
                << (in_args.offloadInfo.has_value()) << ", buffer size " << in_args.bufferSizeFrames
                << " frames";
     AudioPort* port = nullptr;
-    if (auto status = findPortIdForNewStream(in_args.portConfigId, &port); !status.isOk()) {
-        return status;
-    }
+    RETURN_STATUS_IF_ERROR(findPortIdForNewStream(in_args.portConfigId, &port));
     if (port->flags.getTag() != AudioIoFlags::Tag::output) {
         LOG(ERROR) << __func__ << ": port config id " << in_args.portConfigId
                    << " does not correspond to an output mix port";
@@ -709,26 +716,20 @@
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
     StreamContext context;
-    if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
-                                          isNonBlocking ? in_args.callback : nullptr,
-                                          in_args.eventCallback, &context);
-        !status.isOk()) {
-        return status;
-    }
+    RETURN_STATUS_IF_ERROR(createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
+                                               isNonBlocking ? in_args.callback : nullptr,
+                                               in_args.eventCallback, &context));
     context.fillDescriptor(&_aidl_return->desc);
     std::shared_ptr<StreamOut> stream;
-    ndk::ScopedAStatus status = getStreamOutCreator(mType)(
-            in_args.sourceMetadata, std::move(context), in_args.offloadInfo, &stream);
-    if (!status.isOk()) {
-        return status;
-    }
+    RETURN_STATUS_IF_ERROR(createOutputStream(in_args.sourceMetadata, std::move(context),
+                                              in_args.offloadInfo, &stream));
     StreamWrapper streamWrapper(stream);
+    if (auto patchIt = mPatches.find(in_args.portConfigId); patchIt != mPatches.end()) {
+        RETURN_STATUS_IF_ERROR(
+                streamWrapper.setConnectedDevices(findConnectedDevices(in_args.portConfigId)));
+    }
     AIBinder_setMinSchedulerPolicy(streamWrapper.getBinder().get(), SCHED_NORMAL,
                                    ANDROID_PRIORITY_AUDIO);
-    auto patchIt = mPatches.find(in_args.portConfigId);
-    if (patchIt != mPatches.end()) {
-        streamWrapper.setStreamIsConnected(findConnectedDevices(in_args.portConfigId));
-    }
     mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
     _aidl_return->stream = std::move(stream);
     return ndk::ScopedAStatus::ok();
@@ -796,10 +797,7 @@
             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
         }
     }
-
-    if (auto status = checkAudioPatchEndpointsMatch(sources, sinks); !status.isOk()) {
-        return status;
-    }
+    RETURN_STATUS_IF_ERROR(checkAudioPatchEndpointsMatch(sources, sinks));
 
     auto& patches = getConfig().patches;
     auto existing = patches.end();
@@ -834,13 +832,20 @@
     if (existing == patches.end()) {
         _aidl_return->id = getConfig().nextPatchId++;
         patches.push_back(*_aidl_return);
-        existing = patches.begin() + (patches.size() - 1);
     } else {
         oldPatch = *existing;
-        *existing = *_aidl_return;
     }
-    registerPatch(*existing);
-    updateStreamsConnectedState(oldPatch, *_aidl_return);
+    patchesBackup = mPatches;
+    registerPatch(*_aidl_return);
+    if (auto status = updateStreamsConnectedState(oldPatch, *_aidl_return); !status.isOk()) {
+        mPatches = std::move(*patchesBackup);
+        if (existing == patches.end()) {
+            patches.pop_back();
+        } else {
+            *existing = oldPatch;
+        }
+        return status;
+    }
 
     LOG(DEBUG) << __func__ << ": " << (oldPatch.id == 0 ? "created" : "updated") << " patch "
                << _aidl_return->toString();
@@ -992,8 +997,12 @@
     auto& patches = getConfig().patches;
     auto patchIt = findById<AudioPatch>(patches, in_patchId);
     if (patchIt != patches.end()) {
+        auto patchesBackup = mPatches;
         cleanUpPatch(patchIt->id);
-        updateStreamsConnectedState(*patchIt, AudioPatch{});
+        if (auto status = updateStreamsConnectedState(*patchIt, AudioPatch{}); !status.isOk()) {
+            mPatches = std::move(patchesBackup);
+            return status;
+        }
         patches.erase(patchIt);
         LOG(DEBUG) << __func__ << ": erased patch " << in_patchId;
         return ndk::ScopedAStatus::ok();
@@ -1325,6 +1334,22 @@
     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/Stream.cpp b/audio/aidl/default/Stream.cpp
index 77b0601..251dea0 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -152,6 +152,7 @@
         case Tag::halReservedExit:
             if (const int32_t cookie = command.get<Tag::halReservedExit>();
                 cookie == mInternalCommandCookie) {
+                mDriver->shutdown();
                 setClosed();
                 // This is an internal command, no need to reply.
                 return Status::EXIT;
@@ -165,10 +166,15 @@
         case Tag::start:
             if (mState == StreamDescriptor::State::STANDBY ||
                 mState == StreamDescriptor::State::DRAINING) {
-                populateReply(&reply, mIsConnected);
-                mState = mState == StreamDescriptor::State::STANDBY
-                                 ? StreamDescriptor::State::IDLE
-                                 : StreamDescriptor::State::ACTIVE;
+                if (::android::status_t status = mDriver->start(); status == ::android::OK) {
+                    populateReply(&reply, mIsConnected);
+                    mState = mState == StreamDescriptor::State::STANDBY
+                                     ? StreamDescriptor::State::IDLE
+                                     : StreamDescriptor::State::ACTIVE;
+                } else {
+                    LOG(ERROR) << __func__ << ": start failed: " << status;
+                    mState = StreamDescriptor::State::ERROR;
+                }
             } else {
                 populateReplyWrongState(&reply, command);
             }
@@ -364,6 +370,7 @@
         case Tag::halReservedExit:
             if (const int32_t cookie = command.get<Tag::halReservedExit>();
                 cookie == mInternalCommandCookie) {
+                mDriver->shutdown();
                 setClosed();
                 // This is an internal command, no need to reply.
                 return Status::EXIT;
@@ -375,26 +382,36 @@
             populateReply(&reply, mIsConnected);
             break;
         case Tag::start: {
-            bool commandAccepted = true;
+            std::optional<StreamDescriptor::State> nextState;
             switch (mState) {
                 case StreamDescriptor::State::STANDBY:
-                    mState = StreamDescriptor::State::IDLE;
+                    nextState = StreamDescriptor::State::IDLE;
                     break;
                 case StreamDescriptor::State::PAUSED:
-                    mState = StreamDescriptor::State::ACTIVE;
+                    nextState = StreamDescriptor::State::ACTIVE;
                     break;
                 case StreamDescriptor::State::DRAIN_PAUSED:
-                    switchToTransientState(StreamDescriptor::State::DRAINING);
+                    nextState = StreamDescriptor::State::DRAINING;
                     break;
                 case StreamDescriptor::State::TRANSFER_PAUSED:
-                    switchToTransientState(StreamDescriptor::State::TRANSFERRING);
+                    nextState = StreamDescriptor::State::TRANSFERRING;
                     break;
                 default:
                     populateReplyWrongState(&reply, command);
-                    commandAccepted = false;
             }
-            if (commandAccepted) {
-                populateReply(&reply, mIsConnected);
+            if (nextState.has_value()) {
+                if (::android::status_t status = mDriver->start(); status == ::android::OK) {
+                    populateReply(&reply, mIsConnected);
+                    if (*nextState == StreamDescriptor::State::IDLE ||
+                        *nextState == StreamDescriptor::State::ACTIVE) {
+                        mState = *nextState;
+                    } else {
+                        switchToTransientState(*nextState);
+                    }
+                } else {
+                    LOG(ERROR) << __func__ << ": start failed: " << status;
+                    mState = StreamDescriptor::State::ERROR;
+                }
             }
         } break;
         case Tag::burst:
@@ -567,8 +584,7 @@
     return !fatal;
 }
 
-template <class Metadata>
-StreamCommonImpl<Metadata>::~StreamCommonImpl() {
+StreamCommonImpl::~StreamCommonImpl() {
     if (!isClosed()) {
         LOG(ERROR) << __func__ << ": stream was not closed prior to destruction, resource leak";
         stopWorker();
@@ -576,19 +592,16 @@
     }
 }
 
-template <class Metadata>
-void StreamCommonImpl<Metadata>::createStreamCommon(
+ndk::ScopedAStatus StreamCommonImpl::initInstance(
         const std::shared_ptr<StreamCommonInterface>& delegate) {
-    if (mCommon != nullptr) {
-        LOG(FATAL) << __func__ << ": attempting to create the common interface twice";
-    }
-    mCommon = ndk::SharedRefBase::make<StreamCommon>(delegate);
+    mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
     mCommonBinder = mCommon->asBinder();
     AIBinder_setMinSchedulerPolicy(mCommonBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
+    return mWorker->start() ? ndk::ScopedAStatus::ok()
+                            : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 }
 
-template <class Metadata>
-ndk::ScopedAStatus StreamCommonImpl<Metadata>::getStreamCommon(
+ndk::ScopedAStatus StreamCommonImpl::getStreamCommonCommon(
         std::shared_ptr<IStreamCommon>* _aidl_return) {
     if (mCommon == nullptr) {
         LOG(FATAL) << __func__ << ": the common interface was not created";
@@ -598,30 +611,26 @@
     return ndk::ScopedAStatus::ok();
 }
 
-template <class Metadata>
-ndk::ScopedAStatus StreamCommonImpl<Metadata>::updateHwAvSyncId(int32_t in_hwAvSyncId) {
+ndk::ScopedAStatus StreamCommonImpl::updateHwAvSyncId(int32_t in_hwAvSyncId) {
     LOG(DEBUG) << __func__ << ": id " << in_hwAvSyncId;
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
-template <class Metadata>
-ndk::ScopedAStatus StreamCommonImpl<Metadata>::getVendorParameters(
+ndk::ScopedAStatus StreamCommonImpl::getVendorParameters(
         const std::vector<std::string>& in_ids, std::vector<VendorParameter>* _aidl_return) {
     LOG(DEBUG) << __func__ << ": id count: " << in_ids.size();
     (void)_aidl_return;
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
-template <class Metadata>
-ndk::ScopedAStatus StreamCommonImpl<Metadata>::setVendorParameters(
+ndk::ScopedAStatus StreamCommonImpl::setVendorParameters(
         const std::vector<VendorParameter>& in_parameters, bool in_async) {
     LOG(DEBUG) << __func__ << ": parameters count " << in_parameters.size()
                << ", async: " << in_async;
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
-template <class Metadata>
-ndk::ScopedAStatus StreamCommonImpl<Metadata>::addEffect(
+ndk::ScopedAStatus StreamCommonImpl::addEffect(
         const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
     if (in_effect == nullptr) {
         LOG(DEBUG) << __func__ << ": null effect";
@@ -631,8 +640,7 @@
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
-template <class Metadata>
-ndk::ScopedAStatus StreamCommonImpl<Metadata>::removeEffect(
+ndk::ScopedAStatus StreamCommonImpl::removeEffect(
         const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
     if (in_effect == nullptr) {
         LOG(DEBUG) << __func__ << ": null effect";
@@ -642,8 +650,7 @@
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
-template <class Metadata>
-ndk::ScopedAStatus StreamCommonImpl<Metadata>::close() {
+ndk::ScopedAStatus StreamCommonImpl::close() {
     LOG(DEBUG) << __func__;
     if (!isClosed()) {
         stopWorker();
@@ -659,8 +666,7 @@
     }
 }
 
-template <class Metadata>
-ndk::ScopedAStatus StreamCommonImpl<Metadata>::prepareToClose() {
+ndk::ScopedAStatus StreamCommonImpl::prepareToClose() {
     LOG(DEBUG) << __func__;
     if (!isClosed()) {
         return ndk::ScopedAStatus::ok();
@@ -669,8 +675,7 @@
     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 }
 
-template <class Metadata>
-void StreamCommonImpl<Metadata>::stopWorker() {
+void StreamCommonImpl::stopWorker() {
     if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
         LOG(DEBUG) << __func__ << ": asking the worker to exit...";
         auto cmd = StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::halReservedExit>(
@@ -686,10 +691,12 @@
     }
 }
 
-template <class Metadata>
-ndk::ScopedAStatus StreamCommonImpl<Metadata>::updateMetadata(const Metadata& metadata) {
+ndk::ScopedAStatus StreamCommonImpl::updateMetadataCommon(const Metadata& metadata) {
     LOG(DEBUG) << __func__;
     if (!isClosed()) {
+        if (metadata.index() != mMetadata.index()) {
+            LOG(FATAL) << __func__ << ": changing metadata variant is not allowed";
+        }
         mMetadata = metadata;
         return ndk::ScopedAStatus::ok();
     }
@@ -697,12 +704,10 @@
     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 }
 
-// static
-ndk::ScopedAStatus StreamIn::initInstance(const std::shared_ptr<StreamIn>& stream) {
-    if (auto status = stream->init(); !status.isOk()) {
-        return status;
-    }
-    stream->createStreamCommon(stream);
+ndk::ScopedAStatus StreamCommonImpl::setConnectedDevices(
+        const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
+    mWorker->setIsConnected(!devices.empty());
+    mConnectedDevices = devices;
     return ndk::ScopedAStatus::ok();
 }
 
@@ -716,12 +721,8 @@
 }
 }  // namespace
 
-StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext&& context,
-                   const DriverInterface::CreateInstance& createDriver,
-                   const StreamWorkerInterface::CreateInstance& createWorker,
-                   const std::vector<MicrophoneInfo>& microphones)
-    : StreamCommonImpl<SinkMetadata>(sinkMetadata, std::move(context), createDriver, createWorker),
-      mMicrophones(transformMicrophones(microphones)) {
+StreamIn::StreamIn(const std::vector<MicrophoneInfo>& microphones)
+    : mMicrophones(transformMicrophones(microphones)) {
     LOG(DEBUG) << __func__;
 }
 
@@ -729,9 +730,9 @@
         std::vector<MicrophoneDynamicInfo>* _aidl_return) {
     std::vector<MicrophoneDynamicInfo> result;
     std::vector<MicrophoneDynamicInfo::ChannelMapping> channelMapping{
-            getChannelCount(mContext.getChannelLayout()),
+            getChannelCount(getContext().getChannelLayout()),
             MicrophoneDynamicInfo::ChannelMapping::DIRECT};
-    for (auto it = mConnectedDevices.begin(); it != mConnectedDevices.end(); ++it) {
+    for (auto it = getConnectedDevices().begin(); it != getConnectedDevices().end(); ++it) {
         if (auto micIt = mMicrophones.find(*it); micIt != mMicrophones.end()) {
             MicrophoneDynamicInfo dynMic;
             dynMic.id = micIt->second;
@@ -777,22 +778,8 @@
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
-// static
-ndk::ScopedAStatus StreamOut::initInstance(const std::shared_ptr<StreamOut>& stream) {
-    if (auto status = stream->init(); !status.isOk()) {
-        return status;
-    }
-    stream->createStreamCommon(stream);
-    return ndk::ScopedAStatus::ok();
-}
-
-StreamOut::StreamOut(const SourceMetadata& sourceMetadata, StreamContext&& context,
-                     const DriverInterface::CreateInstance& createDriver,
-                     const StreamWorkerInterface::CreateInstance& createWorker,
-                     const std::optional<AudioOffloadInfo>& offloadInfo)
-    : StreamCommonImpl<SourceMetadata>(sourceMetadata, std::move(context), createDriver,
-                                       createWorker),
-      mOffloadInfo(offloadInfo) {
+StreamOut::StreamOut(const std::optional<AudioOffloadInfo>& offloadInfo)
+    : mOffloadInfo(offloadInfo) {
     LOG(DEBUG) << __func__;
 }
 
diff --git a/audio/aidl/default/StreamStub.cpp b/audio/aidl/default/StreamStub.cpp
index 2467320..2dcf4d4 100644
--- a/audio/aidl/default/StreamStub.cpp
+++ b/audio/aidl/default/StreamStub.cpp
@@ -31,34 +31,69 @@
 
 namespace aidl::android::hardware::audio::core {
 
-DriverStub::DriverStub(const StreamContext& context, bool isInput)
-    : mFrameSizeBytes(context.getFrameSize()),
-      mSampleRate(context.getSampleRate()),
-      mIsAsynchronous(!!context.getAsyncCallback()),
-      mIsInput(isInput) {}
+StreamStub::StreamStub(const Metadata& metadata, StreamContext&& context)
+    : StreamCommonImpl(metadata, std::move(context)),
+      mFrameSizeBytes(getContext().getFrameSize()),
+      mSampleRate(getContext().getSampleRate()),
+      mIsAsynchronous(!!getContext().getAsyncCallback()),
+      mIsInput(isInput(metadata)) {}
 
-::android::status_t DriverStub::init() {
+::android::status_t StreamStub::init() {
+    mIsInitialized = true;
     usleep(500);
     return ::android::OK;
 }
 
-::android::status_t DriverStub::drain(StreamDescriptor::DrainMode) {
+::android::status_t StreamStub::drain(StreamDescriptor::DrainMode) {
+    if (!mIsInitialized) {
+        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+    }
     usleep(500);
     return ::android::OK;
 }
 
-::android::status_t DriverStub::flush() {
+::android::status_t StreamStub::flush() {
+    if (!mIsInitialized) {
+        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+    }
     usleep(500);
     return ::android::OK;
 }
 
-::android::status_t DriverStub::pause() {
+::android::status_t StreamStub::pause() {
+    if (!mIsInitialized) {
+        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+    }
     usleep(500);
     return ::android::OK;
 }
 
-::android::status_t DriverStub::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
+::android::status_t StreamStub::standby() {
+    if (!mIsInitialized) {
+        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+    }
+    usleep(500);
+    mIsStandby = true;
+    return ::android::OK;
+}
+
+::android::status_t StreamStub::start() {
+    if (!mIsInitialized) {
+        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+    }
+    usleep(500);
+    mIsStandby = false;
+    return ::android::OK;
+}
+
+::android::status_t StreamStub::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                          int32_t* latencyMs) {
+    if (!mIsInitialized) {
+        LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+    }
+    if (mIsStandby) {
+        LOG(FATAL) << __func__ << ": must not happen while in standby";
+    }
     static constexpr float kMicrosPerSecond = MICROS_PER_SECOND;
     static constexpr float kScaleFactor = .8f;
     if (mIsAsynchronous) {
@@ -79,69 +114,16 @@
     return ::android::OK;
 }
 
-::android::status_t DriverStub::standby() {
-    usleep(500);
-    return ::android::OK;
-}
-
-::android::status_t DriverStub::setConnectedDevices(
-        const std::vector<AudioDevice>& connectedDevices __unused) {
-    usleep(500);
-    return ::android::OK;
-}
-
-// static
-ndk::ScopedAStatus StreamInStub::createInstance(const SinkMetadata& sinkMetadata,
-                                                StreamContext&& context,
-                                                const std::vector<MicrophoneInfo>& microphones,
-                                                std::shared_ptr<StreamIn>* result) {
-    std::shared_ptr<StreamIn> stream =
-            ndk::SharedRefBase::make<StreamInStub>(sinkMetadata, std::move(context), microphones);
-    if (auto status = initInstance(stream); !status.isOk()) {
-        return status;
-    }
-    *result = std::move(stream);
-    return ndk::ScopedAStatus::ok();
+void StreamStub::shutdown() {
+    mIsInitialized = false;
 }
 
 StreamInStub::StreamInStub(const SinkMetadata& sinkMetadata, StreamContext&& context,
                            const std::vector<MicrophoneInfo>& microphones)
-    : StreamIn(
-              sinkMetadata, std::move(context),
-              [](const StreamContext& ctx) -> DriverInterface* {
-                  return new DriverStub(ctx, true /*isInput*/);
-              },
-              [](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
-                  // The default worker implementation is used.
-                  return new StreamInWorker(ctx, driver);
-              },
-              microphones) {}
-
-// static
-ndk::ScopedAStatus StreamOutStub::createInstance(const SourceMetadata& sourceMetadata,
-                                                 StreamContext&& context,
-                                                 const std::optional<AudioOffloadInfo>& offloadInfo,
-                                                 std::shared_ptr<StreamOut>* result) {
-    std::shared_ptr<StreamOut> stream = ndk::SharedRefBase::make<StreamOutStub>(
-            sourceMetadata, std::move(context), offloadInfo);
-    if (auto status = initInstance(stream); !status.isOk()) {
-        return status;
-    }
-    *result = std::move(stream);
-    return ndk::ScopedAStatus::ok();
-}
+    : StreamStub(sinkMetadata, std::move(context)), StreamIn(microphones) {}
 
 StreamOutStub::StreamOutStub(const SourceMetadata& sourceMetadata, StreamContext&& context,
                              const std::optional<AudioOffloadInfo>& offloadInfo)
-    : StreamOut(
-              sourceMetadata, std::move(context),
-              [](const StreamContext& ctx) -> DriverInterface* {
-                  return new DriverStub(ctx, false /*isInput*/);
-              },
-              [](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
-                  // The default worker implementation is used.
-                  return new StreamOutWorker(ctx, driver);
-              },
-              offloadInfo) {}
+    : StreamStub(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {}
 
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/extension/ExtensionEffect.cpp b/audio/aidl/default/extension/ExtensionEffect.cpp
index c4eebc0..4a4d71b6 100644
--- a/audio/aidl/default/extension/ExtensionEffect.cpp
+++ b/audio/aidl/default/extension/ExtensionEffect.cpp
@@ -30,8 +30,8 @@
 using aidl::android::hardware::audio::effect::DefaultExtension;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::ExtensionEffect;
-using aidl::android::hardware::audio::effect::getEffectUuidExtensionImpl;
-using aidl::android::hardware::audio::effect::getEffectUuidExtensionType;
+using aidl::android::hardware::audio::effect::getEffectImplUuidExtension;
+using aidl::android::hardware::audio::effect::getEffectTypeUuidExtension;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::Range;
 using aidl::android::hardware::audio::effect::VendorExtension;
@@ -39,7 +39,7 @@
 
 extern "C" binder_exception_t createEffect(const AudioUuid* in_impl_uuid,
                                            std::shared_ptr<IEffect>* instanceSpp) {
-    if (!in_impl_uuid || *in_impl_uuid != getEffectUuidExtensionImpl()) {
+    if (!in_impl_uuid || *in_impl_uuid != getEffectImplUuidExtension()) {
         LOG(ERROR) << __func__ << "uuid not supported";
         return EX_ILLEGAL_ARGUMENT;
     }
@@ -54,7 +54,7 @@
 }
 
 extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descriptor* _aidl_return) {
-    if (!in_impl_uuid || *in_impl_uuid != getEffectUuidExtensionImpl()) {
+    if (!in_impl_uuid || *in_impl_uuid != getEffectImplUuidExtension()) {
         LOG(ERROR) << __func__ << "uuid not supported";
         return EX_ILLEGAL_ARGUMENT;
     }
@@ -67,8 +67,8 @@
 const std::string ExtensionEffect::kEffectName = "ExtensionEffectExample";
 
 const Descriptor ExtensionEffect::kDescriptor = {
-        .common = {.id = {.type = getEffectUuidExtensionType(),
-                          .uuid = getEffectUuidExtensionImpl(),
+        .common = {.id = {.type = getEffectTypeUuidExtension(),
+                          .uuid = getEffectImplUuidExtension(),
                           .proxy = std::nullopt},
                    .name = ExtensionEffect::kEffectName,
                    .implementor = "The Android Open Source Project"}};
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 83ecfaa..4a23637 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -33,39 +33,13 @@
     static constexpr int32_t kLatencyMs = 10;
     enum Type : int { DEFAULT, R_SUBMIX, USB };
 
+    static std::shared_ptr<Module> createInstance(Type type);
+
     explicit Module(Type type) : mType(type) {}
 
-    static std::shared_ptr<Module> createInstance(Type type);
-    static StreamIn::CreateInstance getStreamInCreator(Type type);
-    static StreamOut::CreateInstance getStreamOutCreator(Type type);
-
-  private:
-    struct VendorDebug {
-        static const std::string kForceTransientBurstName;
-        static const std::string kForceSynchronousDrainName;
-        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; }
-    };
+  protected:
+    // The vendor extension done via inheritance can override interface methods and augment
+    // a call to the base implementation.
 
     ndk::ScopedAStatus setModuleDebug(
             const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) override;
@@ -146,29 +120,46 @@
     ndk::ScopedAStatus getAAudioMixerBurstCount(int32_t* _aidl_return) override;
     ndk::ScopedAStatus getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) override;
 
-    void cleanUpPatch(int32_t patchId);
-    ndk::ScopedAStatus createStreamContext(
-            int32_t in_portConfigId, int64_t in_bufferSizeFrames,
-            std::shared_ptr<IStreamCallback> asyncCallback,
-            std::shared_ptr<IStreamOutEventCallback> outEventCallback,
-            ::aidl::android::hardware::audio::core::StreamContext* out_context);
-    std::vector<::aidl::android::media::audio::common::AudioDevice> findConnectedDevices(
-            int32_t portConfigId);
-    std::set<int32_t> findConnectedPortConfigIds(int32_t portConfigId);
-    ndk::ScopedAStatus findPortIdForNewStream(
-            int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port);
-    internal::Configuration& getConfig();
-    template <typename C>
-    std::set<int32_t> portIdsFromPortConfigIds(C portConfigIds);
-    void registerPatch(const AudioPatch& patch);
-    void updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch);
-    bool isMmapSupported();
-
     // This value is used for all AudioPatches.
     static constexpr int32_t kMinimumStreamBufferSizeFrames = 256;
     // The maximum stream buffer size is 1 GiB = 2 ** 30 bytes;
     static constexpr int32_t kMaximumStreamBufferSizeBytes = 1 << 30;
 
+  private:
+    struct VendorDebug {
+        static const std::string kForceTransientBurstName;
+        static const std::string kForceSynchronousDrainName;
+        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.
+    using ConnectedDevicePorts = std::map<int32_t, std::vector<int32_t>>;
+    // Maps port ids and port config ids to patch ids.
+    // Multimap because both ports and configs can be used by multiple patches.
+    using Patches = std::multimap<int32_t, int32_t>;
+
     const Type mType;
     std::unique_ptr<internal::Configuration> mConfig;
     ModuleDebug mDebug;
@@ -177,19 +168,29 @@
     ChildInterface<IBluetooth> mBluetooth;
     ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
     ChildInterface<IBluetoothLe> mBluetoothLe;
-    // ids of device ports created at runtime via 'connectExternalDevice'.
-    // Also stores ids of mix ports with dynamic profiles which got populated from the connected
-    // port.
-    std::map<int32_t, std::vector<int32_t>> mConnectedDevicePorts;
+    ConnectedDevicePorts mConnectedDevicePorts;
     Streams mStreams;
-    // Maps port ids and port config ids to patch ids.
-    // Multimap because both ports and configs can be used by multiple patches.
-    std::multimap<int32_t, int32_t> mPatches;
+    Patches mPatches;
     bool mMicMute = false;
+    bool mMasterMute = false;
+    float mMasterVolume = 1.0f;
     ChildInterface<sounddose::ISoundDose> mSoundDose;
     std::optional<bool> mIsMmapSupported;
 
   protected:
+    // The following virtual functions are intended for vendor extension via inheritance.
+
+    virtual 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);
+    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);
     // 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,8 +205,31 @@
     virtual ndk::ScopedAStatus onMasterMuteChanged(bool mute);
     virtual ndk::ScopedAStatus onMasterVolumeChanged(float volume);
 
-    bool mMasterMute = false;
-    float mMasterVolume = 1.0f;
+    // Utility and helper functions accessible to subclasses.
+    void cleanUpPatch(int32_t patchId);
+    ndk::ScopedAStatus createStreamContext(
+            int32_t in_portConfigId, int64_t in_bufferSizeFrames,
+            std::shared_ptr<IStreamCallback> asyncCallback,
+            std::shared_ptr<IStreamOutEventCallback> outEventCallback,
+            ::aidl::android::hardware::audio::core::StreamContext* out_context);
+    std::vector<::aidl::android::media::audio::common::AudioDevice> findConnectedDevices(
+            int32_t portConfigId);
+    std::set<int32_t> findConnectedPortConfigIds(int32_t portConfigId);
+    ndk::ScopedAStatus findPortIdForNewStream(
+            int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port);
+    internal::Configuration& getConfig();
+    const ConnectedDevicePorts& getConnectedDevicePorts() const { return mConnectedDevicePorts; }
+    bool getMasterMute() const { return mMasterMute; }
+    bool getMasterVolume() const { return mMasterVolume; }
+    bool getMicMute() const { return mMicMute; }
+    const Patches& getPatches() const { return mPatches; }
+    const Streams& getStreams() const { return mStreams; }
+    bool isMmapSupported();
+    template <typename C>
+    std::set<int32_t> portIdsFromPortConfigIds(C portConfigIds);
+    void registerPatch(const AudioPatch& patch);
+    ndk::ScopedAStatus updateStreamsConnectedState(const AudioPatch& oldPatch,
+                                                   const AudioPatch& newPatch);
 };
 
 }  // 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
new file mode 100644
index 0000000..7b1d375
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
@@ -0,0 +1,59 @@
+/*
+ * 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 ModuleRemoteSubmix : public Module {
+  public:
+    explicit ModuleRemoteSubmix(Module::Type type) : Module(type) {}
+
+  private:
+    // IModule interfaces
+    ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
+    ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
+    ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
+    ndk::ScopedAStatus setMicMute(bool in_mute) override;
+
+    // Module interfaces
+    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;
+    ndk::ScopedAStatus populateConnectedDevicePort(
+            ::aidl::android::media::audio::common::AudioPort* audioPort) override;
+    ndk::ScopedAStatus checkAudioPatchEndpointsMatch(
+            const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources,
+            const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks)
+            override;
+    void onExternalDeviceConnectionChanged(
+            const ::aidl::android::media::audio::common::AudioPort& audioPort,
+            bool connected) override;
+    ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
+    ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
+};
+
+}  // 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 1aa2244..5a5429d 100644
--- a/audio/aidl/default/include/core-impl/ModuleUsb.h
+++ b/audio/aidl/default/include/core-impl/ModuleUsb.h
@@ -32,6 +32,17 @@
     ndk::ScopedAStatus setMicMute(bool in_mute) override;
 
     // Module interfaces
+    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;
     ndk::ScopedAStatus populateConnectedDevicePort(
             ::aidl::android::media::audio::common::AudioPort* audioPort) override;
     ndk::ScopedAStatus checkAudioPatchEndpointsMatch(
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 476f1ff..aaf5860 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -25,6 +25,7 @@
 #include <variant>
 
 #include <StreamWorker.h>
+#include <Utils.h>
 #include <aidl/android/hardware/audio/common/SinkMetadata.h>
 #include <aidl/android/hardware/audio/common/SourceMetadata.h>
 #include <aidl/android/hardware/audio/core/BnStreamCommon.h>
@@ -34,8 +35,10 @@
 #include <aidl/android/hardware/audio/core/IStreamOutEventCallback.h>
 #include <aidl/android/hardware/audio/core/StreamDescriptor.h>
 #include <aidl/android/media/audio/common/AudioDevice.h>
+#include <aidl/android/media/audio/common/AudioIoFlags.h>
 #include <aidl/android/media/audio/common/AudioOffloadInfo.h>
 #include <aidl/android/media/audio/common/MicrophoneInfo.h>
+#include <error/expected_utils.h>
 #include <fmq/AidlMessageQueue.h>
 #include <system/thread_defs.h>
 #include <utils/Errors.h>
@@ -75,18 +78,23 @@
 
     StreamContext() = default;
     StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
+                  int portId,
                   const ::aidl::android::media::audio::common::AudioFormatDescription& format,
                   const ::aidl::android::media::audio::common::AudioChannelLayout& channelLayout,
-                  int sampleRate, std::unique_ptr<DataMQ> dataMQ,
+                  int sampleRate, const ::aidl::android::media::audio::common::AudioIoFlags& flags,
+                  int32_t mixPortHandle, std::unique_ptr<DataMQ> dataMQ,
                   std::shared_ptr<IStreamCallback> asyncCallback,
                   std::shared_ptr<IStreamOutEventCallback> outEventCallback,
                   DebugParameters debugParameters)
         : mCommandMQ(std::move(commandMQ)),
           mInternalCommandCookie(std::rand()),
           mReplyMQ(std::move(replyMQ)),
+          mPortId(portId),
           mFormat(format),
           mChannelLayout(channelLayout),
           mSampleRate(sampleRate),
+          mFlags(flags),
+          mMixPortHandle(mixPortHandle),
           mDataMQ(std::move(dataMQ)),
           mAsyncCallback(asyncCallback),
           mOutEventCallback(outEventCallback),
@@ -95,9 +103,12 @@
         : mCommandMQ(std::move(other.mCommandMQ)),
           mInternalCommandCookie(other.mInternalCommandCookie),
           mReplyMQ(std::move(other.mReplyMQ)),
+          mPortId(other.mPortId),
           mFormat(other.mFormat),
           mChannelLayout(other.mChannelLayout),
           mSampleRate(other.mSampleRate),
+          mFlags(std::move(other.mFlags)),
+          mMixPortHandle(other.mMixPortHandle),
           mDataMQ(std::move(other.mDataMQ)),
           mAsyncCallback(std::move(other.mAsyncCallback)),
           mOutEventCallback(std::move(other.mOutEventCallback)),
@@ -106,9 +117,12 @@
         mCommandMQ = std::move(other.mCommandMQ);
         mInternalCommandCookie = other.mInternalCommandCookie;
         mReplyMQ = std::move(other.mReplyMQ);
+        mPortId = std::move(other.mPortId);
         mFormat = std::move(other.mFormat);
         mChannelLayout = std::move(other.mChannelLayout);
         mSampleRate = other.mSampleRate;
+        mFlags = std::move(other.mFlags);
+        mMixPortHandle = other.mMixPortHandle;
         mDataMQ = std::move(other.mDataMQ);
         mAsyncCallback = std::move(other.mAsyncCallback);
         mOutEventCallback = std::move(other.mOutEventCallback);
@@ -126,13 +140,16 @@
     ::aidl::android::media::audio::common::AudioFormatDescription getFormat() const {
         return mFormat;
     }
+    ::aidl::android::media::audio::common::AudioIoFlags getFlags() const { return mFlags; }
     bool getForceTransientBurst() const { return mDebugParameters.forceTransientBurst; }
     bool getForceSynchronousDrain() const { return mDebugParameters.forceSynchronousDrain; }
     size_t getFrameSize() const;
     int getInternalCommandCookie() const { return mInternalCommandCookie; }
+    int32_t getMixPortHandle() const { return mMixPortHandle; }
     std::shared_ptr<IStreamOutEventCallback> getOutEventCallback() const {
         return mOutEventCallback;
     }
+    int getPortId() const { return mPortId; }
     ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
     int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; }
     int getSampleRate() const { return mSampleRate; }
@@ -143,31 +160,31 @@
     std::unique_ptr<CommandMQ> mCommandMQ;
     int mInternalCommandCookie;  // The value used to confirm that the command was posted internally
     std::unique_ptr<ReplyMQ> mReplyMQ;
+    int mPortId;
     ::aidl::android::media::audio::common::AudioFormatDescription mFormat;
     ::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout;
     int mSampleRate;
+    ::aidl::android::media::audio::common::AudioIoFlags mFlags;
+    int32_t mMixPortHandle;
     std::unique_ptr<DataMQ> mDataMQ;
     std::shared_ptr<IStreamCallback> mAsyncCallback;
     std::shared_ptr<IStreamOutEventCallback> mOutEventCallback;  // Only used by output streams
     DebugParameters mDebugParameters;
 };
 
+// This interface provides operations of the stream which are executed on the worker thread.
 struct DriverInterface {
-    using CreateInstance = std::function<DriverInterface*(const StreamContext&)>;
     virtual ~DriverInterface() = default;
     // All the methods below are called on the worker thread.
     virtual ::android::status_t init() = 0;  // This function is only called once.
     virtual ::android::status_t drain(StreamDescriptor::DrainMode mode) = 0;
     virtual ::android::status_t flush() = 0;
     virtual ::android::status_t pause() = 0;
+    virtual ::android::status_t standby() = 0;
+    virtual ::android::status_t start() = 0;
     virtual ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                          int32_t* latencyMs) = 0;
-    virtual ::android::status_t standby() = 0;
-    // The method below is called from a thread of the Binder pool. Access to data shared with other
-    // methods of this interface must be done in a thread-safe manner.
-    virtual ::android::status_t setConnectedDevices(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>&
-                    connectedDevices) = 0;
+    virtual void shutdown() = 0;  // This function is only called once.
 };
 
 class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
@@ -284,14 +301,20 @@
 };
 using StreamOutWorker = StreamWorkerImpl<StreamOutWorkerLogic>;
 
-// This provides a C++ interface with methods of the IStreamCommon Binder interface,
-// but intentionally does not inherit from it. This is needed to avoid inheriting
-// StreamIn and StreamOut from two Binder interface classes, as these parts of the class
-// will be reference counted separately.
-//
-// The implementation of these common methods is in the StreamCommonImpl template class.
+// This interface provides operations of the stream which are executed on a Binder pool thread.
+// These methods originate both from the AIDL interface and its implementation.
 struct StreamCommonInterface {
+    using ConnectedDevices = std::vector<::aidl::android::media::audio::common::AudioDevice>;
+    using Metadata =
+            std::variant<::aidl::android::hardware::audio::common::SinkMetadata /*IStreamIn*/,
+                         ::aidl::android::hardware::audio::common::SourceMetadata /*IStreamOut*/>;
+
+    static constexpr bool isInput(const Metadata& metadata) { return metadata.index() == 0; }
+
     virtual ~StreamCommonInterface() = default;
+    // Methods below originate from the 'IStreamCommon' interface.
+    // This is semantically equivalent to inheriting from 'IStreamCommon' with a benefit
+    // that concrete stream implementations can inherit both from this interface and IStreamIn/Out.
     virtual ndk::ScopedAStatus close() = 0;
     virtual ndk::ScopedAStatus prepareToClose() = 0;
     virtual ndk::ScopedAStatus updateHwAvSyncId(int32_t in_hwAvSyncId) = 0;
@@ -305,11 +328,30 @@
     virtual ndk::ScopedAStatus removeEffect(
             const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>&
                     in_effect) = 0;
+    // Methods below are common for both 'IStreamIn' and 'IStreamOut'. Note that
+    // 'updateMetadata' in them uses an individual structure which is wrapped here.
+    // The 'Common' suffix is added to distinguish them from the methods from 'IStreamIn/Out'.
+    virtual ndk::ScopedAStatus getStreamCommonCommon(
+            std::shared_ptr<IStreamCommon>* _aidl_return) = 0;
+    virtual ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) = 0;
+    // Methods below are called by implementation of 'IModule', 'IStreamIn' and 'IStreamOut'.
+    virtual ndk::ScopedAStatus initInstance(
+            const std::shared_ptr<StreamCommonInterface>& delegate) = 0;
+    virtual const StreamContext& getContext() const = 0;
+    virtual bool isClosed() const = 0;
+    virtual const ConnectedDevices& getConnectedDevices() const = 0;
+    virtual ndk::ScopedAStatus setConnectedDevices(
+            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) = 0;
 };
 
-class StreamCommon : public BnStreamCommon {
+// This is equivalent to automatically generated 'IStreamCommonDelegator' but uses
+// a weak pointer to avoid creating a reference loop. The loop will occur because
+// 'IStreamIn/Out.getStreamCommon' must return the same instance every time, thus
+// the stream implementation must hold a strong pointer to an instance of 'IStreamCommon'.
+// Also, we use 'StreamCommonInterface' here instead of 'IStreamCommon'.
+class StreamCommonDelegator : public BnStreamCommon {
   public:
-    explicit StreamCommon(const std::shared_ptr<StreamCommonInterface>& delegate)
+    explicit StreamCommonDelegator(const std::shared_ptr<StreamCommonInterface>& delegate)
         : mDelegate(delegate) {}
 
   private:
@@ -360,9 +402,20 @@
     std::weak_ptr<StreamCommonInterface> mDelegate;
 };
 
-template <class Metadata>
-class StreamCommonImpl : public StreamCommonInterface {
+// The implementation of DriverInterface must be provided by each concrete stream implementation.
+class StreamCommonImpl : virtual public StreamCommonInterface, virtual public DriverInterface {
   public:
+    StreamCommonImpl(const Metadata& metadata, StreamContext&& context,
+                     const StreamWorkerInterface::CreateInstance& createWorker)
+        : mMetadata(metadata),
+          mContext(std::move(context)),
+          mWorker(createWorker(mContext, this)) {}
+    StreamCommonImpl(const Metadata& metadata, StreamContext&& context)
+        : StreamCommonImpl(
+                  metadata, std::move(context),
+                  isInput(metadata) ? getDefaultInWorkerCreator() : getDefaultOutWorkerCreator()) {}
+    ~StreamCommonImpl();
+
     ndk::ScopedAStatus close() override;
     ndk::ScopedAStatus prepareToClose() override;
     ndk::ScopedAStatus updateHwAvSyncId(int32_t in_hwAvSyncId) override;
@@ -377,46 +430,50 @@
             const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
             override;
 
-    ndk::ScopedAStatus getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return);
-    ndk::ScopedAStatus init() {
-        return mWorker->start() ? ndk::ScopedAStatus::ok()
-                                : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-    bool isClosed() const { return mWorker->isClosed(); }
-    void setIsConnected(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
-        mWorker->setIsConnected(!devices.empty());
-        mConnectedDevices = devices;
-        mDriver->setConnectedDevices(devices);
-    }
-    ndk::ScopedAStatus updateMetadata(const Metadata& metadata);
+    ndk::ScopedAStatus getStreamCommonCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override;
+    ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) override;
+
+    ndk::ScopedAStatus initInstance(
+            const std::shared_ptr<StreamCommonInterface>& delegate) override;
+    const StreamContext& getContext() const override { return mContext; }
+    bool isClosed() const override { return mWorker->isClosed(); }
+    const ConnectedDevices& getConnectedDevices() const override { return mConnectedDevices; }
+    ndk::ScopedAStatus setConnectedDevices(
+            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
+            override;
 
   protected:
-    StreamCommonImpl(const Metadata& metadata, StreamContext&& context,
-                     const DriverInterface::CreateInstance& createDriver,
-                     const StreamWorkerInterface::CreateInstance& createWorker)
-        : mMetadata(metadata),
-          mContext(std::move(context)),
-          mDriver(createDriver(mContext)),
-          mWorker(createWorker(mContext, mDriver.get())) {}
-    ~StreamCommonImpl();
-    void stopWorker();
-    void createStreamCommon(const std::shared_ptr<StreamCommonInterface>& delegate);
+    static StreamWorkerInterface::CreateInstance getDefaultInWorkerCreator() {
+        return [](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
+            return new StreamInWorker(ctx, driver);
+        };
+    }
+    static StreamWorkerInterface::CreateInstance getDefaultOutWorkerCreator() {
+        return [](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
+            return new StreamOutWorker(ctx, driver);
+        };
+    }
 
-    std::shared_ptr<StreamCommon> mCommon;
-    ndk::SpAIBinder mCommonBinder;
+    void stopWorker();
+
     Metadata mMetadata;
     StreamContext mContext;
-    std::unique_ptr<DriverInterface> mDriver;
     std::unique_ptr<StreamWorkerInterface> mWorker;
-    std::vector<::aidl::android::media::audio::common::AudioDevice> mConnectedDevices;
+    std::shared_ptr<StreamCommonDelegator> mCommon;
+    ndk::SpAIBinder mCommonBinder;
+    ConnectedDevices mConnectedDevices;
 };
 
-class StreamIn : public StreamCommonImpl<::aidl::android::hardware::audio::common::SinkMetadata>,
-                 public BnStreamIn {
+// Note: 'StreamIn/Out' can not be used on their own. Instead, they must be used for defining
+// concrete input/output stream implementations.
+class StreamIn : virtual public StreamCommonInterface, public BnStreamIn {
+  protected:
     ndk::ScopedAStatus getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override {
-        return StreamCommonImpl<::aidl::android::hardware::audio::common::SinkMetadata>::
-                getStreamCommon(_aidl_return);
+        return getStreamCommonCommon(_aidl_return);
+    }
+    ndk::ScopedAStatus updateMetadata(const ::aidl::android::hardware::audio::common::SinkMetadata&
+                                              in_sinkMetadata) override {
+        return updateMetadataCommon(in_sinkMetadata);
     }
     ndk::ScopedAStatus getActiveMicrophones(
             std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
@@ -425,49 +482,26 @@
     ndk::ScopedAStatus setMicrophoneDirection(MicrophoneDirection in_direction) override;
     ndk::ScopedAStatus getMicrophoneFieldDimension(float* _aidl_return) override;
     ndk::ScopedAStatus setMicrophoneFieldDimension(float in_zoom) override;
-    ndk::ScopedAStatus updateMetadata(const ::aidl::android::hardware::audio::common::SinkMetadata&
-                                              in_sinkMetadata) override {
-        return StreamCommonImpl<::aidl::android::hardware::audio::common::SinkMetadata>::
-                updateMetadata(in_sinkMetadata);
-    }
     ndk::ScopedAStatus getHwGain(std::vector<float>* _aidl_return) override;
     ndk::ScopedAStatus setHwGain(const std::vector<float>& in_channelGains) override;
 
-  protected:
     friend class ndk::SharedRefBase;
 
-    static ndk::ScopedAStatus initInstance(const std::shared_ptr<StreamIn>& stream);
-
-    StreamIn(const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
-             StreamContext&& context, const DriverInterface::CreateInstance& createDriver,
-             const StreamWorkerInterface::CreateInstance& createWorker,
-             const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
-    void createStreamCommon(const std::shared_ptr<StreamIn>& myPtr) {
-        StreamCommonImpl<
-                ::aidl::android::hardware::audio::common::SinkMetadata>::createStreamCommon(myPtr);
-    }
+    explicit StreamIn(
+            const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
 
     const std::map<::aidl::android::media::audio::common::AudioDevice, std::string> mMicrophones;
-
-  public:
-    using CreateInstance = std::function<ndk::ScopedAStatus(
-            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)>;
 };
 
-class StreamOut : public StreamCommonImpl<::aidl::android::hardware::audio::common::SourceMetadata>,
-                  public BnStreamOut {
+class StreamOut : virtual public StreamCommonInterface, public BnStreamOut {
+  protected:
     ndk::ScopedAStatus getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override {
-        return StreamCommonImpl<::aidl::android::hardware::audio::common::SourceMetadata>::
-                getStreamCommon(_aidl_return);
+        return getStreamCommonCommon(_aidl_return);
     }
     ndk::ScopedAStatus updateMetadata(
             const ::aidl::android::hardware::audio::common::SourceMetadata& in_sourceMetadata)
             override {
-        return StreamCommonImpl<::aidl::android::hardware::audio::common::SourceMetadata>::
-                updateMetadata(in_sourceMetadata);
+        return updateMetadataCommon(in_sourceMetadata);
     }
     ndk::ScopedAStatus updateOffloadMetadata(
             const ::aidl::android::hardware::audio::common::AudioOffloadMetadata&
@@ -492,34 +526,27 @@
             override;
     ndk::ScopedAStatus selectPresentation(int32_t in_presentationId, int32_t in_programId) override;
 
-    void createStreamCommon(const std::shared_ptr<StreamOut>& myPtr) {
-        StreamCommonImpl<::aidl::android::hardware::audio::common::SourceMetadata>::
-                createStreamCommon(myPtr);
-    }
-
-  protected:
     friend class ndk::SharedRefBase;
 
-    static ndk::ScopedAStatus initInstance(const std::shared_ptr<StreamOut>& stream);
-
-    StreamOut(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
-              StreamContext&& context, const DriverInterface::CreateInstance& createDriver,
-              const StreamWorkerInterface::CreateInstance& createWorker,
-              const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
-                      offloadInfo);
+    explicit StreamOut(const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+                               offloadInfo);
 
     std::optional<::aidl::android::media::audio::common::AudioOffloadInfo> mOffloadInfo;
     std::optional<::aidl::android::hardware::audio::common::AudioOffloadMetadata> mOffloadMetadata;
-
-  public:
-    using CreateInstance = std::function<ndk::ScopedAStatus(
-            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)>;
 };
 
+// The recommended way to create a stream instance.
+// 'StreamImpl' is the concrete stream implementation, 'StreamInOrOut' is either 'StreamIn' or
+// 'StreamOut', the rest are the arguments forwarded to the constructor of 'StreamImpl'.
+template <class StreamImpl, class StreamInOrOut, class... Args>
+ndk::ScopedAStatus createStreamInstance(std::shared_ptr<StreamInOrOut>* result, Args&&... args) {
+    std::shared_ptr<StreamInOrOut> stream =
+            ::ndk::SharedRefBase::make<StreamImpl>(std::forward<Args>(args)...);
+    RETURN_STATUS_IF_ERROR(stream->initInstance(stream));
+    *result = std::move(stream);
+    return ndk::ScopedAStatus::ok();
+}
+
 class StreamWrapper {
   public:
     explicit StreamWrapper(const std::shared_ptr<StreamIn>& streamIn)
@@ -528,25 +555,18 @@
         : mStream(streamOut), mStreamBinder(streamOut->asBinder()) {}
     ndk::SpAIBinder getBinder() const { return mStreamBinder; }
     bool isStreamOpen() const {
-        return std::visit(
-                [](auto&& ws) -> bool {
-                    auto s = ws.lock();
-                    return s && !s->isClosed();
-                },
-                mStream);
+        auto s = mStream.lock();
+        return s && !s->isClosed();
     }
-    void setStreamIsConnected(
+    ndk::ScopedAStatus setConnectedDevices(
             const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
-        std::visit(
-                [&](auto&& ws) {
-                    auto s = ws.lock();
-                    if (s) s->setIsConnected(devices);
-                },
-                mStream);
+        auto s = mStream.lock();
+        if (s) return s->setConnectedDevices(devices);
+        return ndk::ScopedAStatus::ok();
     }
 
   private:
-    std::variant<std::weak_ptr<StreamIn>, std::weak_ptr<StreamOut>> mStream;
+    std::weak_ptr<StreamCommonInterface> mStream;
     ndk::SpAIBinder mStreamBinder;
 };
 
@@ -564,12 +584,13 @@
         mStreams.insert(std::pair{portConfigId, sw});
         mStreams.insert(std::pair{portId, std::move(sw)});
     }
-    void setStreamIsConnected(
+    ndk::ScopedAStatus setStreamConnectedDevices(
             int32_t portConfigId,
             const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
         if (auto it = mStreams.find(portConfigId); it != mStreams.end()) {
-            it->second.setStreamIsConnected(devices);
+            return it->second.setConnectedDevices(devices);
         }
+        return ndk::ScopedAStatus::ok();
     }
 
   private:
diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
new file mode 100644
index 0000000..2253ec7
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
@@ -0,0 +1,98 @@
+/*
+ * 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 <mutex>
+#include <vector>
+
+#include "core-impl/Stream.h"
+#include "r_submix/SubmixRoute.h"
+
+namespace aidl::android::hardware::audio::core {
+
+using aidl::android::hardware::audio::core::r_submix::AudioConfig;
+using aidl::android::hardware::audio::core::r_submix::SubmixRoute;
+
+class StreamRemoteSubmix : public StreamCommonImpl {
+  public:
+    StreamRemoteSubmix(const Metadata& metadata, StreamContext&& context);
+
+    ::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.
+    ndk::ScopedAStatus prepareToClose() override;
+
+  private:
+    size_t getPipeSizeInFrames();
+    size_t getStreamPipeSizeInFrames();
+    ::android::status_t outWrite(void* buffer, size_t frameCount, size_t* actualFrameCount);
+    ::android::status_t inRead(void* buffer, size_t frameCount, size_t* actualFrameCount);
+
+    const int mPortId;
+    const bool mIsInput;
+    AudioConfig mStreamConfig;
+    std::shared_ptr<SubmixRoute> mCurrentRoute = nullptr;
+
+    // Mutex lock to protect vector of submix routes, each of these submix routes have their mutex
+    // locks and none of the mutex locks should be taken together.
+    static std::mutex sSubmixRoutesLock;
+    static std::map<int32_t, std::shared_ptr<SubmixRoute>> sSubmixRoutes
+            GUARDED_BY(sSubmixRoutesLock);
+
+    // limit for number of read error log entries to avoid spamming the logs
+    static constexpr int kMaxReadErrorLogs = 5;
+    // The duration of kMaxReadFailureAttempts * READ_ATTEMPT_SLEEP_MS must be strictly inferior
+    // to the duration of a record buffer at the current record sample rate (of the device, not of
+    // the recording itself). Here we have: 3 * 5ms = 15ms < 1024 frames * 1000 / 48000 = 21.333ms
+    static constexpr int kMaxReadFailureAttempts = 3;
+    // 5ms between two read attempts when pipe is empty
+    static constexpr int kReadAttemptSleepUs = 5000;
+};
+
+class StreamInRemoteSubmix final : public StreamRemoteSubmix, public StreamIn {
+  public:
+    friend class ndk::SharedRefBase;
+    StreamInRemoteSubmix(
+            const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+            StreamContext&& context,
+            const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
+
+  private:
+    ndk::ScopedAStatus getActiveMicrophones(
+            std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
+            override;
+};
+
+class StreamOutRemoteSubmix final : public StreamRemoteSubmix, public StreamOut {
+  public:
+    friend class ndk::SharedRefBase;
+    StreamOutRemoteSubmix(
+            const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+            StreamContext&& context,
+            const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+                    offloadInfo);
+};
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/StreamStub.h b/audio/aidl/default/include/core-impl/StreamStub.h
index 436e610..6b1b2dd 100644
--- a/audio/aidl/default/include/core-impl/StreamStub.h
+++ b/audio/aidl/default/include/core-impl/StreamStub.h
@@ -20,37 +20,31 @@
 
 namespace aidl::android::hardware::audio::core {
 
-class DriverStub : public DriverInterface {
+class StreamStub : public StreamCommonImpl {
   public:
-    DriverStub(const StreamContext& context, bool isInput);
+    StreamStub(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;
-    ::android::status_t standby() override;
-    // Note: called on a different thread.
-    ::android::status_t setConnectedDevices(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>& connectedDevices)
-            override;
+    void shutdown() override;
 
   private:
     const size_t mFrameSizeBytes;
     const int mSampleRate;
     const bool mIsAsynchronous;
     const bool mIsInput;
+    bool mIsInitialized = false;  // Used for validating the state machine logic.
+    bool mIsStandby = true;       // Used for validating the state machine logic.
 };
 
-class StreamInStub final : public StreamIn {
+class StreamInStub final : public StreamStub, public StreamIn {
   public:
-    static ndk::ScopedAStatus createInstance(
-            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);
-
-  private:
     friend class ndk::SharedRefBase;
     StreamInStub(
             const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
@@ -58,16 +52,8 @@
             const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
 };
 
-class StreamOutStub final : public StreamOut {
+class StreamOutStub final : public StreamStub, public StreamOut {
   public:
-    static ndk::ScopedAStatus createInstance(
-            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);
-
-  private:
     friend class ndk::SharedRefBase;
     StreamOutStub(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
                   StreamContext&& context,
diff --git a/audio/aidl/default/include/core-impl/StreamUsb.h b/audio/aidl/default/include/core-impl/StreamUsb.h
index 05d889a..8c40782 100644
--- a/audio/aidl/default/include/core-impl/StreamUsb.h
+++ b/audio/aidl/default/include/core-impl/StreamUsb.h
@@ -16,7 +16,10 @@
 
 #pragma once
 
+#include <atomic>
+#include <functional>
 #include <mutex>
+#include <optional>
 #include <vector>
 
 #include <aidl/android/media/audio/common/AudioChannelLayout.h>
@@ -30,72 +33,64 @@
 
 namespace aidl::android::hardware::audio::core {
 
-class DriverUsb : public DriverInterface {
+class StreamUsb : public StreamCommonImpl {
   public:
-    DriverUsb(const StreamContext& context, bool isInput);
+    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;
-    ::android::status_t standby() override;
-    // Note: called on a different thread.
-    ::android::status_t setConnectedDevices(
-            const std::vector<::aidl::android::media::audio::common::AudioDevice>& connectedDevices)
-            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:
-    ::android::status_t exitStandby();
+    using AlsaDeviceProxyDeleter = std::function<void(alsa_device_proxy*)>;
+    using AlsaDeviceProxy = std::unique_ptr<alsa_device_proxy, AlsaDeviceProxyDeleter>;
 
-    std::mutex mLock;
+    static std::optional<struct pcm_config> maybePopulateConfig(const StreamContext& context,
+                                                                bool isInput);
+
+    mutable std::mutex mLock;
 
     const size_t mFrameSizeBytes;
-    std::optional<struct pcm_config> mConfig;
     const bool mIsInput;
-    // Cached device addresses for connected devices.
-    std::vector<::aidl::android::media::audio::common::AudioDeviceAddress> mConnectedDevices
-            GUARDED_BY(mLock);
-    std::vector<std::shared_ptr<alsa_device_proxy>> mAlsaDeviceProxies GUARDED_BY(mLock);
-    bool mIsStandby = true;
+    const std::optional<struct pcm_config> mConfig;
+    std::atomic<bool> mConnectedDevicesUpdated = false;
+    // All fields below are only used on the worker thread.
+    std::vector<AlsaDeviceProxy> mAlsaDeviceProxies;
 };
 
-class StreamInUsb final : public StreamIn {
-    ndk::ScopedAStatus getActiveMicrophones(
-            std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
-            override;
-
+class StreamInUsb final : public StreamUsb, public StreamIn {
   public:
-    static ndk::ScopedAStatus createInstance(
-            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);
-
-  private:
     friend class ndk::SharedRefBase;
     StreamInUsb(
             const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
             StreamContext&& context,
             const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
-};
-
-class StreamOutUsb final : public StreamOut {
-  public:
-    static ndk::ScopedAStatus createInstance(
-            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);
 
   private:
+    ndk::ScopedAStatus getActiveMicrophones(
+            std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
+            override;
+};
+
+class StreamOutUsb final : public StreamUsb, public StreamOut {
+  public:
     friend class ndk::SharedRefBase;
     StreamOutUsb(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
                  StreamContext&& context,
                  const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
                          offloadInfo);
 
+  private:
     ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
     ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
 
diff --git a/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp
new file mode 100644
index 0000000..2b79f51
--- /dev/null
+++ b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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_ModuleRemoteSubmix"
+
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "RemoteSubmixUtils.h"
+#include "core-impl/ModuleRemoteSubmix.h"
+#include "core-impl/StreamRemoteSubmix.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 ModuleRemoteSubmix::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
+    *_aidl_return = nullptr;
+    LOG(DEBUG) << __func__ << ": returning null";
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModuleRemoteSubmix::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
+    *_aidl_return = nullptr;
+    LOG(DEBUG) << __func__ << ": returning null";
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModuleRemoteSubmix::getMicMute(bool* _aidl_return __unused) {
+    LOG(DEBUG) << __func__ << ": is not supported";
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus ModuleRemoteSubmix::setMicMute(bool in_mute __unused) {
+    LOG(DEBUG) << __func__ << ": is not supported";
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus ModuleRemoteSubmix::createInputStream(
+        const SinkMetadata& sinkMetadata, StreamContext&& context,
+        const std::vector<MicrophoneInfo>& microphones, std::shared_ptr<StreamIn>* result) {
+    return createStreamInstance<StreamInRemoteSubmix>(result, sinkMetadata, std::move(context),
+                                                      microphones);
+}
+
+ndk::ScopedAStatus ModuleRemoteSubmix::createOutputStream(
+        const SourceMetadata& sourceMetadata, StreamContext&& context,
+        const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
+    return createStreamInstance<StreamOutRemoteSubmix>(result, sourceMetadata, std::move(context),
+                                                       offloadInfo);
+}
+
+ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* audioPort) {
+    LOG(VERBOSE) << __func__ << ": Profiles already populated by Configuration";
+    for (auto profile : audioPort->profiles) {
+        for (auto channelMask : profile.channelMasks) {
+            if (!r_submix::isChannelMaskSupported(channelMask)) {
+                LOG(ERROR) << __func__ << ": the profile " << profile.name
+                           << " has unsupported channel mask : " << channelMask.toString();
+                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+            }
+        }
+        for (auto sampleRate : profile.sampleRates) {
+            if (!r_submix::isSampleRateSupported(sampleRate)) {
+                LOG(ERROR) << __func__ << ": the profile " << profile.name
+                           << " has unsupported sample rate : " << sampleRate;
+                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+            }
+        }
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ModuleRemoteSubmix::checkAudioPatchEndpointsMatch(
+        const std::vector<AudioPortConfig*>& sources, const std::vector<AudioPortConfig*>& sinks) {
+    for (const auto& source : sources) {
+        for (const auto& sink : sinks) {
+            if (source->sampleRate != sink->sampleRate ||
+                source->channelMask != sink->channelMask || source->format != sink->format) {
+                LOG(ERROR) << __func__
+                           << ": mismatch port configuration, source=" << source->toString()
+                           << ", sink=" << sink->toString();
+                return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+            }
+        }
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+void ModuleRemoteSubmix::onExternalDeviceConnectionChanged(
+        const ::aidl::android::media::audio::common::AudioPort& audioPort __unused,
+        bool connected __unused) {
+    LOG(DEBUG) << __func__ << ": do nothing and return";
+}
+
+ndk::ScopedAStatus ModuleRemoteSubmix::onMasterMuteChanged(bool __unused) {
+    LOG(DEBUG) << __func__ << ": is not supported";
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus ModuleRemoteSubmix::onMasterVolumeChanged(float __unused) {
+    LOG(DEBUG) << __func__ << ": is not supported";
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/r_submix/RemoteSubmixUtils.cpp b/audio/aidl/default/r_submix/RemoteSubmixUtils.cpp
new file mode 100644
index 0000000..2f5d17d
--- /dev/null
+++ b/audio/aidl/default/r_submix/RemoteSubmixUtils.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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>
+
+#include "RemoteSubmixUtils.h"
+
+namespace aidl::android::hardware::audio::core::r_submix {
+
+bool isChannelMaskSupported(const AudioChannelLayout& channelMask) {
+    const static std::vector<AudioChannelLayout> kSupportedChannelMask = {
+            AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+                    AudioChannelLayout::LAYOUT_MONO),
+            AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+                    AudioChannelLayout::LAYOUT_STEREO)};
+
+    if (std::find(kSupportedChannelMask.begin(), kSupportedChannelMask.end(), channelMask) !=
+        kSupportedChannelMask.end()) {
+        return true;
+    }
+    return false;
+}
+
+bool isSampleRateSupported(int sampleRate) {
+    const static std::vector<int> kSupportedSampleRates = {8000,  11025, 12000, 16000, 22050,
+                                                           24000, 32000, 44100, 48000};
+
+    if (std::find(kSupportedSampleRates.begin(), kSupportedSampleRates.end(), sampleRate) !=
+        kSupportedSampleRates.end()) {
+        return true;
+    }
+    return false;
+}
+
+}  // namespace aidl::android::hardware::audio::core::r_submix
diff --git a/audio/aidl/default/r_submix/RemoteSubmixUtils.h b/audio/aidl/default/r_submix/RemoteSubmixUtils.h
new file mode 100644
index 0000000..952a992
--- /dev/null
+++ b/audio/aidl/default/r_submix/RemoteSubmixUtils.h
@@ -0,0 +1,30 @@
+/*
+ * 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>
+
+using aidl::android::media::audio::common::AudioChannelLayout;
+
+namespace aidl::android::hardware::audio::core::r_submix {
+
+bool isChannelMaskSupported(const AudioChannelLayout& channelMask);
+
+bool isSampleRateSupported(int sampleRate);
+
+}  // namespace aidl::android::hardware::audio::core::r_submix
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
new file mode 100644
index 0000000..5af0d91
--- /dev/null
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -0,0 +1,352 @@
+/*
+ * 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_StreamRemoteSubmix"
+#include <android-base/logging.h>
+
+#include <cmath>
+
+#include "core-impl/StreamRemoteSubmix.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::MicrophoneDynamicInfo;
+using aidl::android::media::audio::common::MicrophoneInfo;
+
+namespace aidl::android::hardware::audio::core {
+
+StreamRemoteSubmix::StreamRemoteSubmix(const Metadata& metadata, StreamContext&& context)
+    : StreamCommonImpl(metadata, std::move(context)),
+      mPortId(context.getPortId()),
+      mIsInput(isInput(metadata)) {
+    mStreamConfig.frameSize = context.getFrameSize();
+    mStreamConfig.format = context.getFormat();
+    mStreamConfig.channelLayout = context.getChannelLayout();
+    mStreamConfig.sampleRate = context.getSampleRate();
+}
+
+std::mutex StreamRemoteSubmix::sSubmixRoutesLock;
+std::map<int32_t, std::shared_ptr<SubmixRoute>> StreamRemoteSubmix::sSubmixRoutes;
+
+::android::status_t StreamRemoteSubmix::init() {
+    {
+        std::lock_guard guard(sSubmixRoutesLock);
+        if (sSubmixRoutes.find(mPortId) != sSubmixRoutes.end()) {
+            mCurrentRoute = sSubmixRoutes[mPortId];
+        }
+    }
+    // If route is not available for this port, add it.
+    if (mCurrentRoute == nullptr) {
+        // Initialize the pipe.
+        mCurrentRoute = std::make_shared<SubmixRoute>();
+        if (::android::OK != mCurrentRoute->createPipe(mStreamConfig)) {
+            LOG(ERROR) << __func__ << ": create pipe failed";
+            return ::android::NO_INIT;
+        }
+        {
+            std::lock_guard guard(sSubmixRoutesLock);
+            sSubmixRoutes.emplace(mPortId, mCurrentRoute);
+        }
+    } else {
+        if (!mCurrentRoute->isStreamConfigValid(mIsInput, mStreamConfig)) {
+            LOG(ERROR) << __func__ << ": invalid stream config";
+            return ::android::NO_INIT;
+        }
+        sp<MonoPipe> sink = mCurrentRoute->getSink();
+        if (sink == nullptr) {
+            LOG(ERROR) << __func__ << ": nullptr sink when opening stream";
+            return ::android::NO_INIT;
+        }
+        // If the sink has been shutdown or pipe recreation is forced, delete the pipe and
+        // recreate it.
+        if (sink->isShutdown()) {
+            LOG(DEBUG) << __func__ << ": Non-nullptr shut down sink when opening stream";
+            if (::android::OK != mCurrentRoute->resetPipe()) {
+                LOG(ERROR) << __func__ << ": reset pipe failed";
+                return ::android::NO_INIT;
+            }
+        }
+    }
+
+    mCurrentRoute->openStream(mIsInput);
+    return ::android::OK;
+}
+
+::android::status_t StreamRemoteSubmix::drain(StreamDescriptor::DrainMode) {
+    usleep(1000);
+    return ::android::OK;
+}
+
+::android::status_t StreamRemoteSubmix::flush() {
+    usleep(1000);
+    return ::android::OK;
+}
+
+::android::status_t StreamRemoteSubmix::pause() {
+    usleep(1000);
+    return ::android::OK;
+}
+
+::android::status_t StreamRemoteSubmix::standby() {
+    mCurrentRoute->standby(mIsInput);
+    return ::android::OK;
+}
+
+::android::status_t StreamRemoteSubmix::start() {
+    mCurrentRoute->exitStandby(mIsInput);
+    return ::android::OK;
+}
+
+ndk::ScopedAStatus StreamRemoteSubmix::prepareToClose() {
+    if (!mIsInput) {
+        std::shared_ptr<SubmixRoute> route = nullptr;
+        {
+            std::lock_guard guard(sSubmixRoutesLock);
+            if (sSubmixRoutes.find(mPortId) != sSubmixRoutes.end()) {
+                route = sSubmixRoutes[mPortId];
+            }
+        }
+        if (route != nullptr) {
+            sp<MonoPipe> sink = route->getSink();
+            if (sink == nullptr) {
+                ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+            }
+            LOG(DEBUG) << __func__ << ": shutting down MonoPipe sink";
+
+            sink->shutdown(true);
+        } else {
+            LOG(DEBUG) << __func__ << ": stream already closed.";
+            ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+        }
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+// Remove references to the specified input and output streams.  When the device no longer
+// references input and output streams destroy the associated pipe.
+void StreamRemoteSubmix::shutdown() {
+    mCurrentRoute->closeStream(mIsInput);
+    // If all stream instances are closed, we can remove route information for this port.
+    if (!mCurrentRoute->hasAtleastOneStreamOpen()) {
+        mCurrentRoute->releasePipe();
+        LOG(DEBUG) << __func__ << ": pipe destroyed";
+
+        std::lock_guard guard(sSubmixRoutesLock);
+        sSubmixRoutes.erase(mPortId);
+    }
+    mCurrentRoute.reset();
+}
+
+::android::status_t StreamRemoteSubmix::transfer(void* buffer, size_t frameCount,
+                                                 size_t* actualFrameCount, int32_t* latencyMs) {
+    *latencyMs = (getStreamPipeSizeInFrames() * MILLIS_PER_SECOND) / mStreamConfig.sampleRate;
+    LOG(VERBOSE) << __func__ << ": Latency " << *latencyMs << "ms";
+
+    sp<MonoPipe> sink = mCurrentRoute->getSink();
+    if (sink != nullptr) {
+        if (sink->isShutdown()) {
+            sink.clear();
+            LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the transfer.";
+            // the pipe has already been shutdown, this buffer will be lost but we must simulate
+            // timing so we don't drain the output faster than realtime
+            const size_t delayUs = static_cast<size_t>(
+                    std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
+            usleep(delayUs);
+
+            *actualFrameCount = frameCount;
+            return ::android::OK;
+        }
+    } else {
+        LOG(ERROR) << __func__ << ": transfer without a pipe!";
+        return ::android::UNEXPECTED_NULL;
+    }
+
+    return (mIsInput ? inRead(buffer, frameCount, actualFrameCount)
+                     : outWrite(buffer, frameCount, actualFrameCount));
+}
+
+// Calculate the maximum size of the pipe buffer in frames for the specified stream.
+size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
+    auto pipeConfig = mCurrentRoute->mPipeConfig;
+    const size_t maxFrameSize = std::max(mStreamConfig.frameSize, pipeConfig.frameSize);
+    return (pipeConfig.frameCount * pipeConfig.frameSize) / maxFrameSize;
+}
+
+::android::status_t StreamRemoteSubmix::outWrite(void* buffer, size_t frameCount,
+                                                 size_t* actualFrameCount) {
+    sp<MonoPipe> sink = mCurrentRoute->getSink();
+    if (sink != nullptr) {
+        if (sink->isShutdown()) {
+            sink.clear();
+            LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the write.";
+            // the pipe has already been shutdown, this buffer will be lost but we must
+            // simulate timing so we don't drain the output faster than realtime
+            const size_t delayUs = static_cast<size_t>(
+                    std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
+            usleep(delayUs);
+            *actualFrameCount = frameCount;
+            return ::android::OK;
+        }
+    } else {
+        LOG(FATAL) << __func__ << ": without a pipe!";
+        return ::android::UNKNOWN_ERROR;
+    }
+
+    const size_t availableToWrite = sink->availableToWrite();
+    // NOTE: sink has been checked above and sink and source life cycles are synchronized
+    sp<MonoPipeReader> source = mCurrentRoute->getSource();
+    // If the write to the sink should be blocked, flush enough frames from the pipe to make space
+    // to write the most recent data.
+    if (!mCurrentRoute->shouldBlockWrite() && availableToWrite < frameCount) {
+        static uint8_t flushBuffer[64];
+        const size_t flushBufferSizeFrames = sizeof(flushBuffer) / mStreamConfig.frameSize;
+        size_t framesToFlushFromSource = frameCount - availableToWrite;
+        LOG(VERBOSE) << __func__ << ": flushing " << framesToFlushFromSource
+                     << " frames from the pipe to avoid blocking";
+        while (framesToFlushFromSource) {
+            const size_t flushSize = std::min(framesToFlushFromSource, flushBufferSizeFrames);
+            framesToFlushFromSource -= flushSize;
+            // read does not block
+            source->read(flushBuffer, flushSize);
+        }
+    }
+
+    ssize_t writtenFrames = sink->write(buffer, frameCount);
+    if (writtenFrames < 0) {
+        if (writtenFrames == (ssize_t)::android::NEGOTIATE) {
+            LOG(ERROR) << __func__ << ": write to pipe returned NEGOTIATE";
+            sink.clear();
+            *actualFrameCount = 0;
+            return ::android::UNKNOWN_ERROR;
+        } else {
+            // write() returned UNDERRUN or WOULD_BLOCK, retry
+            LOG(ERROR) << __func__ << ": write to pipe returned unexpected " << writtenFrames;
+            writtenFrames = sink->write(buffer, frameCount);
+        }
+    }
+    sink.clear();
+
+    if (writtenFrames < 0) {
+        LOG(ERROR) << __func__ << ": failed writing to pipe with " << writtenFrames;
+        *actualFrameCount = 0;
+        return ::android::UNKNOWN_ERROR;
+    }
+    LOG(VERBOSE) << __func__ << ": wrote " << writtenFrames << "frames";
+    *actualFrameCount = writtenFrames;
+    return ::android::OK;
+}
+
+::android::status_t StreamRemoteSubmix::inRead(void* buffer, size_t frameCount,
+                                               size_t* actualFrameCount) {
+    // about to read from audio source
+    sp<MonoPipeReader> source = mCurrentRoute->getSource();
+    if (source == nullptr) {
+        int readErrorCount = mCurrentRoute->notifyReadError();
+        if (readErrorCount < kMaxReadErrorLogs) {
+            LOG(ERROR)
+                    << __func__
+                    << ": no audio pipe yet we're trying to read! (not all errors will be logged)";
+        } else {
+            LOG(ERROR) << __func__ << ": Read errors " << readErrorCount;
+        }
+        const size_t delayUs = static_cast<size_t>(
+                std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
+        usleep(delayUs);
+        memset(buffer, 0, mStreamConfig.frameSize * frameCount);
+        *actualFrameCount = frameCount;
+        return ::android::OK;
+    }
+
+    // read the data from the pipe
+    int attempts = 0;
+    const size_t delayUs = static_cast<size_t>(std::roundf(kReadAttemptSleepUs));
+    char* buff = (char*)buffer;
+    size_t remainingFrames = frameCount;
+
+    while ((remainingFrames > 0) && (attempts < kMaxReadFailureAttempts)) {
+        LOG(VERBOSE) << __func__ << ": frames available to read " << source->availableToRead();
+
+        ssize_t framesRead = source->read(buff, remainingFrames);
+
+        LOG(VERBOSE) << __func__ << ": frames read " << framesRead;
+
+        if (framesRead > 0) {
+            remainingFrames -= framesRead;
+            buff += framesRead * mStreamConfig.frameSize;
+            LOG(VERBOSE) << __func__ << ": (attempts = " << attempts << ") got " << framesRead
+                         << " frames, remaining=" << remainingFrames;
+        } else {
+            attempts++;
+            LOG(WARNING) << __func__ << ": read returned " << framesRead
+                         << " , read failure attempts = " << attempts;
+            usleep(delayUs);
+        }
+    }
+    // done using the source
+    source.clear();
+
+    if (remainingFrames > 0) {
+        const size_t remainingBytes = remainingFrames * mStreamConfig.frameSize;
+        LOG(VERBOSE) << __func__ << ": clearing remaining_frames = " << remainingFrames;
+        memset(((char*)buffer) + (mStreamConfig.frameSize * frameCount) - remainingBytes, 0,
+               remainingBytes);
+    }
+
+    long readCounterFrames = mCurrentRoute->updateReadCounterFrames(frameCount);
+    *actualFrameCount = frameCount;
+
+    // compute how much we need to sleep after reading the data by comparing the wall clock with
+    //   the projected time at which we should return.
+    // wall clock after reading from the pipe
+    auto recordDurationUs = std::chrono::steady_clock::now() - mCurrentRoute->getRecordStartTime();
+
+    // readCounterFrames contains the number of frames that have been read since the beginning of
+    // recording (including this call): it's converted to usec and compared to how long we've been
+    // recording for, which gives us how long we must wait to sync the projected recording time, and
+    // the observed recording time.
+    static constexpr float kScaleFactor = .8f;
+    const size_t projectedVsObservedOffsetUs =
+            kScaleFactor * (static_cast<size_t>(std::roundf((readCounterFrames * MICROS_PER_SECOND /
+                                                             mStreamConfig.sampleRate) -
+                                                            recordDurationUs.count())));
+
+    LOG(VERBOSE) << __func__ << ": record duration " << recordDurationUs.count()
+                 << " microseconds, will wait: " << projectedVsObservedOffsetUs << " microseconds";
+    if (projectedVsObservedOffsetUs > 0) {
+        usleep(projectedVsObservedOffsetUs);
+    }
+    return ::android::OK;
+}
+
+StreamInRemoteSubmix::StreamInRemoteSubmix(const SinkMetadata& sinkMetadata,
+                                           StreamContext&& context,
+                                           const std::vector<MicrophoneInfo>& microphones)
+    : StreamRemoteSubmix(sinkMetadata, std::move(context)), StreamIn(microphones) {}
+
+ndk::ScopedAStatus StreamInRemoteSubmix::getActiveMicrophones(
+        std::vector<MicrophoneDynamicInfo>* _aidl_return) {
+    LOG(DEBUG) << __func__ << ": not supported";
+    *_aidl_return = std::vector<MicrophoneDynamicInfo>();
+    return ndk::ScopedAStatus::ok();
+}
+
+StreamOutRemoteSubmix::StreamOutRemoteSubmix(const SourceMetadata& sourceMetadata,
+                                             StreamContext&& context,
+                                             const std::optional<AudioOffloadInfo>& offloadInfo)
+    : StreamRemoteSubmix(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {}
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/r_submix/SubmixRoute.cpp b/audio/aidl/default/r_submix/SubmixRoute.cpp
new file mode 100644
index 0000000..8f5b8cb
--- /dev/null
+++ b/audio/aidl/default/r_submix/SubmixRoute.cpp
@@ -0,0 +1,230 @@
+/*
+ * 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_SubmixRoute"
+#include <android-base/logging.h>
+#include <media/AidlConversionCppNdk.h>
+
+#include <Utils.h>
+
+#include "SubmixRoute.h"
+
+using aidl::android::hardware::audio::common::getChannelCount;
+
+namespace aidl::android::hardware::audio::core::r_submix {
+
+// Verify a submix input or output stream can be opened.
+bool SubmixRoute::isStreamConfigValid(bool isInput, const AudioConfig streamConfig) {
+    // If the stream is already open, don't open it again.
+    // ENABLE_LEGACY_INPUT_OPEN is default behaviour
+    if (!isInput && isStreamOutOpen()) {
+        LOG(ERROR) << __func__ << ": output stream already open.";
+        return false;
+    }
+    // If either stream is open, verify the existing pipe config matches the stream config.
+    if (hasAtleastOneStreamOpen() && !isStreamConfigCompatible(streamConfig)) {
+        return false;
+    }
+    return true;
+}
+
+// Compare this stream config with existing pipe config, returning false if they do *not*
+// match, true otherwise.
+bool SubmixRoute::isStreamConfigCompatible(const AudioConfig streamConfig) {
+    if (streamConfig.channelLayout != mPipeConfig.channelLayout) {
+        LOG(ERROR) << __func__ << ": channel count mismatch, stream channels = "
+                   << streamConfig.channelLayout.toString()
+                   << " pipe config channels = " << mPipeConfig.channelLayout.toString();
+        return false;
+    }
+    if (streamConfig.sampleRate != mPipeConfig.sampleRate) {
+        LOG(ERROR) << __func__
+                   << ": sample rate mismatch, stream sample rate = " << streamConfig.sampleRate
+                   << " pipe config sample rate = " << mPipeConfig.sampleRate;
+        return false;
+    }
+    if (streamConfig.format != mPipeConfig.format) {
+        LOG(ERROR) << __func__
+                   << ": format mismatch, stream format = " << streamConfig.format.toString()
+                   << " pipe config format = " << mPipeConfig.format.toString();
+        return false;
+    }
+    return true;
+}
+
+bool SubmixRoute::hasAtleastOneStreamOpen() {
+    std::lock_guard guard(mLock);
+    return (mStreamInOpen || mStreamOutOpen);
+}
+
+// We DO NOT block if:
+// - no peer input stream is present
+// - the peer input is in standby AFTER having been active.
+// We DO block if:
+// - the input was never activated to avoid discarding first frames in the pipe in case capture
+// start was delayed
+bool SubmixRoute::shouldBlockWrite() {
+    std::lock_guard guard(mLock);
+    return (mStreamInOpen || (mStreamInStandby && (mReadCounterFrames != 0)));
+}
+
+int SubmixRoute::notifyReadError() {
+    std::lock_guard guard(mLock);
+    return ++mReadErrorCount;
+}
+
+long SubmixRoute::updateReadCounterFrames(size_t frameCount) {
+    std::lock_guard guard(mLock);
+    mReadCounterFrames += frameCount;
+    return mReadCounterFrames;
+}
+
+void SubmixRoute::openStream(bool isInput) {
+    std::lock_guard guard(mLock);
+    if (isInput) {
+        if (mStreamInOpen) {
+            mInputRefCount++;
+        } else {
+            mInputRefCount = 1;
+            mStreamInOpen = true;
+        }
+        mStreamInStandby = true;
+        mReadCounterFrames = 0;
+        mReadErrorCount = 0;
+    } else {
+        mStreamOutOpen = true;
+    }
+}
+
+void SubmixRoute::closeStream(bool isInput) {
+    std::lock_guard guard(mLock);
+    if (isInput) {
+        mInputRefCount--;
+        if (mInputRefCount == 0) {
+            mStreamInOpen = false;
+            if (mSink != nullptr) {
+                mSink->shutdown(true);
+            }
+        }
+    } else {
+        mStreamOutOpen = false;
+    }
+}
+
+// If SubmixRoute doesn't exist for a port, create a pipe for the submix audio device of size
+// buffer_size_frames and store config of the submix audio device.
+::android::status_t SubmixRoute::createPipe(const AudioConfig streamConfig) {
+    const int channelCount = getChannelCount(streamConfig.channelLayout);
+    const audio_format_t audioFormat = VALUE_OR_RETURN_STATUS(
+            aidl2legacy_AudioFormatDescription_audio_format_t(streamConfig.format));
+    const ::android::NBAIO_Format format =
+            ::android::Format_from_SR_C(streamConfig.sampleRate, channelCount, audioFormat);
+    const ::android::NBAIO_Format offers[1] = {format};
+    size_t numCounterOffers = 0;
+
+    const size_t pipeSizeInFrames =
+            r_submix::kDefaultPipeSizeInFrames *
+            ((float)streamConfig.sampleRate / r_submix::kDefaultSampleRateHz);
+    LOG(VERBOSE) << __func__ << ": creating pipe, rate : " << streamConfig.sampleRate
+                 << ", pipe size : " << pipeSizeInFrames;
+
+    // Create a MonoPipe with optional blocking set to true.
+    sp<MonoPipe> sink = sp<MonoPipe>::make(pipeSizeInFrames, format, true /*writeCanBlock*/);
+    if (sink == nullptr) {
+        LOG(FATAL) << __func__ << ": sink is null";
+        return ::android::UNEXPECTED_NULL;
+    }
+
+    // Negotiation between the source and sink cannot fail as the device open operation
+    // creates both ends of the pipe using the same audio format.
+    ssize_t index = sink->negotiate(offers, 1, nullptr, numCounterOffers);
+    if (index != 0) {
+        LOG(FATAL) << __func__ << ": Negotiation for the sink failed, index = " << index;
+        return ::android::BAD_INDEX;
+    }
+    sp<MonoPipeReader> source = sp<MonoPipeReader>::make(sink.get());
+    if (source == nullptr) {
+        LOG(FATAL) << __func__ << ": source is null";
+        return ::android::UNEXPECTED_NULL;
+    }
+    numCounterOffers = 0;
+    index = source->negotiate(offers, 1, nullptr, numCounterOffers);
+    if (index != 0) {
+        LOG(FATAL) << __func__ << ": Negotiation for the source failed, index = " << index;
+        return ::android::BAD_INDEX;
+    }
+    LOG(VERBOSE) << __func__ << ": created pipe";
+
+    mPipeConfig = streamConfig;
+    mPipeConfig.frameCount = sink->maxFrames();
+
+    LOG(VERBOSE) << __func__ << ": Pipe frame size : " << mPipeConfig.frameSize
+                 << ", pipe frames : " << mPipeConfig.frameCount;
+
+    // Save references to the source and sink.
+    {
+        std::lock_guard guard(mLock);
+        mSink = std::move(sink);
+        mSource = std::move(source);
+    }
+
+    return ::android::OK;
+}
+
+// Release references to the sink and source.
+void SubmixRoute::releasePipe() {
+    std::lock_guard guard(mLock);
+    mSink.clear();
+    mSource.clear();
+}
+
+::android::status_t SubmixRoute::resetPipe() {
+    releasePipe();
+    return createPipe(mPipeConfig);
+}
+
+void SubmixRoute::standby(bool isInput) {
+    std::lock_guard guard(mLock);
+
+    if (isInput) {
+        mStreamInStandby = true;
+    } else {
+        mStreamOutStandby = true;
+        mStreamOutStandbyTransition = !mStreamOutStandbyTransition;
+    }
+}
+
+void SubmixRoute::exitStandby(bool isInput) {
+    std::lock_guard guard(mLock);
+
+    if (isInput) {
+        if (mStreamInStandby || mStreamOutStandbyTransition) {
+            mStreamInStandby = false;
+            mStreamOutStandbyTransition = false;
+            // keep track of when we exit input standby (== first read == start "real recording")
+            // or when we start recording silence, and reset projected time
+            mRecordStartTime = std::chrono::steady_clock::now();
+            mReadCounterFrames = 0;
+        }
+    } else {
+        if (mStreamOutStandby) {
+            mStreamOutStandby = false;
+            mStreamOutStandbyTransition = true;
+        }
+    }
+}
+
+}  // namespace aidl::android::hardware::audio::core::r_submix
diff --git a/audio/aidl/default/r_submix/SubmixRoute.h b/audio/aidl/default/r_submix/SubmixRoute.h
new file mode 100644
index 0000000..5f7ea75
--- /dev/null
+++ b/audio/aidl/default/r_submix/SubmixRoute.h
@@ -0,0 +1,138 @@
+/*
+ * 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 <mutex>
+
+#include <audio_utils/clock.h>
+
+#include <media/nbaio/MonoPipe.h>
+#include <media/nbaio/MonoPipeReader.h>
+
+#include <aidl/android/media/audio/common/AudioChannelLayout.h>
+
+#include "core-impl/Stream.h"
+
+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;
+using ::android::MonoPipe;
+using ::android::MonoPipeReader;
+using ::android::sp;
+
+namespace aidl::android::hardware::audio::core::r_submix {
+
+static constexpr int kDefaultSampleRateHz = 48000;
+// Size at default sample rate
+// NOTE: This value will be rounded up to the nearest power of 2 by MonoPipe().
+static constexpr int kDefaultPipeSizeInFrames = (1024 * 4);
+
+// Configuration of the audio stream.
+struct AudioConfig {
+    int sampleRate = kDefaultSampleRateHz;
+    AudioFormatDescription format =
+            AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = PcmType::INT_16_BIT};
+    AudioChannelLayout channelLayout =
+            AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+                    AudioChannelLayout::LAYOUT_STEREO);
+    size_t frameSize;
+    size_t frameCount;
+};
+
+class SubmixRoute {
+  public:
+    AudioConfig mPipeConfig;
+
+    bool isStreamInOpen() {
+        std::lock_guard guard(mLock);
+        return mStreamInOpen;
+    }
+    bool getStreamInStandby() {
+        std::lock_guard guard(mLock);
+        return mStreamInStandby;
+    }
+    bool isStreamOutOpen() {
+        std::lock_guard guard(mLock);
+        return mStreamOutOpen;
+    }
+    bool getStreamOutStandby() {
+        std::lock_guard guard(mLock);
+        return mStreamOutStandby;
+    }
+    long getReadCounterFrames() {
+        std::lock_guard guard(mLock);
+        return mReadCounterFrames;
+    }
+    int getReadErrorCount() {
+        std::lock_guard guard(mLock);
+        return mReadErrorCount;
+    }
+    std::chrono::time_point<std::chrono::steady_clock> getRecordStartTime() {
+        std::lock_guard guard(mLock);
+        return mRecordStartTime;
+    }
+    sp<MonoPipe> getSink() {
+        std::lock_guard guard(mLock);
+        return mSink;
+    }
+    sp<MonoPipeReader> getSource() {
+        std::lock_guard guard(mLock);
+        return mSource;
+    }
+
+    bool isStreamConfigValid(bool isInput, const AudioConfig streamConfig);
+    void closeStream(bool isInput);
+    ::android::status_t createPipe(const AudioConfig streamConfig);
+    void exitStandby(bool isInput);
+    bool hasAtleastOneStreamOpen();
+    int notifyReadError();
+    void openStream(bool isInput);
+    void releasePipe();
+    ::android::status_t resetPipe();
+    bool shouldBlockWrite();
+    void standby(bool isInput);
+    long updateReadCounterFrames(size_t frameCount);
+
+  private:
+    bool isStreamConfigCompatible(const AudioConfig streamConfig);
+
+    std::mutex mLock;
+
+    bool mStreamInOpen GUARDED_BY(mLock) = false;
+    int mInputRefCount GUARDED_BY(mLock) = 0;
+    bool mStreamInStandby GUARDED_BY(mLock) = true;
+    bool mStreamOutStandbyTransition GUARDED_BY(mLock) = false;
+    bool mStreamOutOpen GUARDED_BY(mLock) = false;
+    bool mStreamOutStandby GUARDED_BY(mLock) = true;
+    // how many frames have been requested to be read since standby
+    long mReadCounterFrames GUARDED_BY(mLock) = 0;
+    int mReadErrorCount GUARDED_BY(mLock) = 0;
+    // wall clock when recording starts
+    std::chrono::time_point<std::chrono::steady_clock> mRecordStartTime GUARDED_BY(mLock);
+
+    // Pipe variables: they handle the ring buffer that "pipes" audio:
+    //  - from the submix virtual audio output == what needs to be played
+    //    remotely, seen as an output for the client
+    //  - to the virtual audio source == what is captured by the component
+    //    which "records" the submix / virtual audio source, and handles it as needed.
+    // A usecase example is one where the component capturing the audio is then sending it over
+    // Wifi for presentation on a remote Wifi Display device (e.g. a dongle attached to a TV, or a
+    // TV with Wifi Display capabilities), or to a wireless audio player.
+    sp<MonoPipe> mSink GUARDED_BY(mLock);
+    sp<MonoPipeReader> mSource GUARDED_BY(mLock);
+};
+
+}  // namespace aidl::android::hardware::audio::core::r_submix
diff --git a/audio/aidl/default/usb/ModuleUsb.cpp b/audio/aidl/default/usb/ModuleUsb.cpp
index 28116ae..5c9d477 100644
--- a/audio/aidl/default/usb/ModuleUsb.cpp
+++ b/audio/aidl/default/usb/ModuleUsb.cpp
@@ -25,11 +25,14 @@
 #include "UsbAlsaMixerControl.h"
 #include "UsbAlsaUtils.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;
@@ -37,10 +40,12 @@
 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 {
 
@@ -97,6 +102,25 @@
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
+ndk::ScopedAStatus ModuleUsb::createInputStream(const SinkMetadata& sinkMetadata,
+                                                StreamContext&& context,
+                                                const std::vector<MicrophoneInfo>& microphones,
+                                                std::shared_ptr<StreamIn>* result) {
+    return createStreamInstance<StreamInUsb>(result, sinkMetadata, std::move(context), microphones);
+}
+
+ndk::ScopedAStatus ModuleUsb::createOutputStream(const SourceMetadata& sourceMetadata,
+                                                 StreamContext&& context,
+                                                 const std::optional<AudioOffloadInfo>& offloadInfo,
+                                                 std::shared_ptr<StreamOut>* result) {
+    if (offloadInfo.has_value()) {
+        LOG(ERROR) << __func__ << ": offload is not supported";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    return createStreamInstance<StreamOutUsb>(result, sourceMetadata, std::move(context),
+                                              offloadInfo);
+}
+
 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";
@@ -175,8 +199,8 @@
         return;
     }
     const int card = address.get<AudioDeviceAddress::alsa>()[0];
-    usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(card, mMasterMute,
-                                                                     mMasterVolume, connected);
+    usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(card, getMasterMute(),
+                                                                     getMasterVolume(), connected);
 }
 
 ndk::ScopedAStatus ModuleUsb::onMasterMuteChanged(bool mute) {
diff --git a/audio/aidl/default/usb/StreamUsb.cpp b/audio/aidl/default/usb/StreamUsb.cpp
index 9ac1cc9..17e1ab4 100644
--- a/audio/aidl/default/usb/StreamUsb.cpp
+++ b/audio/aidl/default/usb/StreamUsb.cpp
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
+#include <limits>
+
 #define LOG_TAG "AHAL_StreamUsb"
 #include <android-base/logging.h>
 
 #include <Utils.h>
+#include <error/expected_utils.h>
 
 #include "UsbAlsaMixerControl.h"
 #include "UsbAlsaUtils.h"
@@ -42,119 +45,126 @@
 
 namespace aidl::android::hardware::audio::core {
 
-DriverUsb::DriverUsb(const StreamContext& context, bool isInput)
-    : mFrameSizeBytes(context.getFrameSize()), mIsInput(isInput) {
+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;
+        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;
+        return std::nullopt;
     }
     config.rate = context.getSampleRate();
     if (config.rate == 0) {
         LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate;
-        return;
+        return std::nullopt;
     }
-    mConfig = config;
+    return config;
 }
 
-::android::status_t DriverUsb::init() {
+::android::status_t StreamUsb::init() {
     return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
 }
 
-::android::status_t DriverUsb::setConnectedDevices(
+const StreamCommonInterface::ConnectedDevices& StreamUsb::getConnectedDevices() const {
+    std::lock_guard guard(mLock);
+    return mConnectedDevices;
+}
+
+ndk::ScopedAStatus StreamUsb::setConnectedDevices(
         const std::vector<AudioDevice>& connectedDevices) {
     if (mIsInput && connectedDevices.size() > 1) {
         LOG(ERROR) << __func__ << ": wrong device size(" << connectedDevices.size()
                    << ") for input stream";
-        return ::android::BAD_VALUE;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
     }
     for (const auto& connectedDevice : connectedDevices) {
         if (connectedDevice.address.getTag() != AudioDeviceAddress::alsa) {
             LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString();
-            return ::android::BAD_VALUE;
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
         }
     }
     std::lock_guard guard(mLock);
-    mAlsaDeviceProxies.clear();
-    mConnectedDevices.clear();
-    for (const auto& connectedDevice : connectedDevices) {
-        mConnectedDevices.push_back(connectedDevice.address);
-    }
-    return ::android::OK;
+    RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices));
+    mConnectedDevicesUpdated.store(true, std::memory_order_release);
+    return ndk::ScopedAStatus::ok();
 }
 
-::android::status_t DriverUsb::drain(StreamDescriptor::DrainMode) {
+::android::status_t StreamUsb::drain(StreamDescriptor::DrainMode) {
     usleep(1000);
     return ::android::OK;
 }
 
-::android::status_t DriverUsb::flush() {
+::android::status_t StreamUsb::flush() {
     usleep(1000);
     return ::android::OK;
 }
 
-::android::status_t DriverUsb::pause() {
+::android::status_t StreamUsb::pause() {
     usleep(1000);
     return ::android::OK;
 }
 
-::android::status_t DriverUsb::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
+::android::status_t StreamUsb::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                         int32_t* latencyMs) {
-    {
-        std::lock_guard guard(mLock);
-        if (!mConfig.has_value() || mConnectedDevices.empty()) {
-            LOG(ERROR) << __func__ << ": failed, has config: " << mConfig.has_value()
-                       << ", has connected devices: " << mConnectedDevices.empty();
-            return ::android::NO_INIT;
-        }
-    }
-    if (mIsStandby) {
-        if (::android::status_t status = exitStandby(); status != ::android::OK) {
-            LOG(ERROR) << __func__ << ": failed to exit standby, status=" << status;
-            return status;
-        }
-    }
-    std::vector<std::shared_ptr<alsa_device_proxy>> alsaDeviceProxies;
-    {
-        std::lock_guard guard(mLock);
-        alsaDeviceProxies = mAlsaDeviceProxies;
+    if (mConnectedDevicesUpdated.load(std::memory_order_acquire)) {
+        // 'setConnectedDevices' has been 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(alsaDeviceProxies[0].get(), buffer, bytesToTransfer);
+        proxy_read(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer);
+        maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
     } else {
-        for (auto& proxy : alsaDeviceProxies) {
+        for (auto& proxy : mAlsaDeviceProxies) {
             proxy_write(proxy.get(), buffer, bytesToTransfer);
+            maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get()));
         }
     }
     *actualFrameCount = frameCount;
-    *latencyMs = Module::kLatencyMs;
+    maxLatency = std::min(maxLatency, static_cast<unsigned>(std::numeric_limits<int32_t>::max()));
+    *latencyMs = maxLatency;
     return ::android::OK;
 }
 
-::android::status_t DriverUsb::standby() {
-    if (!mIsStandby) {
-        std::lock_guard guard(mLock);
-        mAlsaDeviceProxies.clear();
-        mIsStandby = true;
-    }
+::android::status_t StreamUsb::standby() {
+    mAlsaDeviceProxies.clear();
     return ::android::OK;
 }
 
-::android::status_t DriverUsb::exitStandby() {
+void StreamUsb::shutdown() {
+    mAlsaDeviceProxies.clear();
+}
+
+::android::status_t StreamUsb::start() {
     std::vector<AudioDeviceAddress> connectedDevices;
     {
         std::lock_guard guard(mLock);
-        connectedDevices = mConnectedDevices;
+        std::transform(mConnectedDevices.begin(), mConnectedDevices.end(),
+                       std::back_inserter(connectedDevices),
+                       [](const auto& device) { return device.address; });
+        mConnectedDevicesUpdated.store(false, std::memory_order_release);
     }
-    std::vector<std::shared_ptr<alsa_device_proxy>> alsaDeviceProxies;
+    decltype(mAlsaDeviceProxies) alsaDeviceProxies;
     for (const auto& device : connectedDevices) {
         alsa_device_profile profile;
         profile_init(&profile, mIsInput ? PCM_IN : PCM_OUT);
@@ -166,16 +176,16 @@
             return ::android::UNKNOWN_ERROR;
         }
 
-        auto proxy = std::shared_ptr<alsa_device_proxy>(new alsa_device_proxy(),
-                                                        [](alsa_device_proxy* proxy) {
-                                                            proxy_close(proxy);
-                                                            free(proxy);
-                                                        });
+        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, &mConfig.value(), true /*is_bit_perfect*/);
+        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;
@@ -188,40 +198,13 @@
         }
         alsaDeviceProxies.push_back(std::move(proxy));
     }
-    {
-        std::lock_guard guard(mLock);
-        mAlsaDeviceProxies = alsaDeviceProxies;
-    }
-    mIsStandby = false;
+    mAlsaDeviceProxies = std::move(alsaDeviceProxies);
     return ::android::OK;
 }
 
-// static
-ndk::ScopedAStatus StreamInUsb::createInstance(const SinkMetadata& sinkMetadata,
-                                               StreamContext&& context,
-                                               const std::vector<MicrophoneInfo>& microphones,
-                                               std::shared_ptr<StreamIn>* result) {
-    std::shared_ptr<StreamIn> stream =
-            ndk::SharedRefBase::make<StreamInUsb>(sinkMetadata, std::move(context), microphones);
-    if (auto status = initInstance(stream); !status.isOk()) {
-        return status;
-    }
-    *result = std::move(stream);
-    return ndk::ScopedAStatus::ok();
-}
-
 StreamInUsb::StreamInUsb(const SinkMetadata& sinkMetadata, StreamContext&& context,
                          const std::vector<MicrophoneInfo>& microphones)
-    : StreamIn(
-              sinkMetadata, std::move(context),
-              [](const StreamContext& ctx) -> DriverInterface* {
-                  return new DriverUsb(ctx, true /*isInput*/);
-              },
-              [](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
-                  // The default worker implementation is used.
-                  return new StreamInWorker(ctx, driver);
-              },
-              microphones) {}
+    : StreamUsb(sinkMetadata, std::move(context)), StreamIn(microphones) {}
 
 ndk::ScopedAStatus StreamInUsb::getActiveMicrophones(
         std::vector<MicrophoneDynamicInfo>* _aidl_return __unused) {
@@ -229,37 +212,10 @@
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
-// static
-ndk::ScopedAStatus StreamOutUsb::createInstance(const SourceMetadata& sourceMetadata,
-                                                StreamContext&& context,
-                                                const std::optional<AudioOffloadInfo>& offloadInfo,
-                                                std::shared_ptr<StreamOut>* result) {
-    if (offloadInfo.has_value()) {
-        LOG(ERROR) << __func__ << ": offload is not supported";
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
-    std::shared_ptr<StreamOut> stream =
-            ndk::SharedRefBase::make<StreamOutUsb>(sourceMetadata, std::move(context), offloadInfo);
-    if (auto status = initInstance(stream); !status.isOk()) {
-        return status;
-    }
-    *result = std::move(stream);
-    return ndk::ScopedAStatus::ok();
-}
-
 StreamOutUsb::StreamOutUsb(const SourceMetadata& sourceMetadata, StreamContext&& context,
                            const std::optional<AudioOffloadInfo>& offloadInfo)
-    : StreamOut(
-              sourceMetadata, std::move(context),
-              [](const StreamContext& ctx) -> DriverInterface* {
-                  return new DriverUsb(ctx, false /*isInput*/);
-              },
-              [](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
-                  // The default worker implementation is used.
-                  return new StreamOutWorker(ctx, driver);
-              },
-              offloadInfo) {
-    mChannelCount = getChannelCount(mContext.getChannelLayout());
+    : StreamUsb(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {
+    mChannelCount = getChannelCount(getContext().getChannelLayout());
 }
 
 ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector<float>* _aidl_return) {
@@ -268,7 +224,7 @@
 }
 
 ndk::ScopedAStatus StreamOutUsb::setHwVolume(const std::vector<float>& in_channelVolumes) {
-    for (const auto& device : mConnectedDevices) {
+    for (const auto& device : getConnectedDevices()) {
         if (device.address.getTag() != AudioDeviceAddress::alsa) {
             LOG(DEBUG) << __func__ << ": skip as the device address is not alsa";
             continue;
diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h
index 4e84f6b..685d07d 100644
--- a/audio/aidl/vts/EffectHelper.h
+++ b/audio/aidl/vts/EffectHelper.h
@@ -30,6 +30,7 @@
 #include <android/binder_auto_utils.h>
 #include <fmq/AidlMessageQueue.h>
 #include <gtest/gtest.h>
+#include <system/audio_aidl_utils.h>
 #include <system/audio_effects/aidl_effects_utils.h>
 #include <system/audio_effects/effect_uuid.h>
 
@@ -51,6 +52,7 @@
 using aidl::android::media::audio::common::AudioFormatType;
 using aidl::android::media::audio::common::AudioUuid;
 using aidl::android::media::audio::common::PcmType;
+using ::android::audio::utils::toString;
 using ::android::hardware::EventFlag;
 
 const AudioFormatDescription kDefaultFormatDescription = {
@@ -63,6 +65,12 @@
                                     ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
         DataMQ;
 
+static inline std::string getPrefix(Descriptor& descriptor) {
+    std::string prefix = "Implementor_" + descriptor.common.implementor + "_name_" +
+                         descriptor.common.name + "_UUID_" + toString(descriptor.common.id.uuid);
+    return prefix;
+}
+
 class EffectHelper {
   public:
     static void create(std::shared_ptr<IFactory> factory, std::shared_ptr<IEffect>& effect,
@@ -71,7 +79,7 @@
         auto& id = desc.common.id;
         ASSERT_STATUS(status, factory->createEffect(id.uuid, &effect));
         if (status == EX_NONE) {
-            ASSERT_NE(effect, nullptr) << id.uuid.toString();
+            ASSERT_NE(effect, nullptr) << toString(id.uuid);
         }
     }
 
diff --git a/audio/aidl/vts/TestUtils.h b/audio/aidl/vts/TestUtils.h
index 72ca56f..10c2fc6 100644
--- a/audio/aidl/vts/TestUtils.h
+++ b/audio/aidl/vts/TestUtils.h
@@ -77,3 +77,10 @@
 #define EXPECT_STATUS(expected, ret)                                                       \
     EXPECT_PRED_FORMAT2(::android::hardware::audio::common::testing::detail::assertResult, \
                         expected, ret)
+
+#define SKIP_TEST_IF_DATA_UNSUPPORTED(flags)                                                  \
+    ({                                                                                        \
+        if ((flags).hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL || (flags).bypass) {  \
+            GTEST_SKIP() << "Skip data path for offload";                                     \
+        }                                                                                     \
+    })
\ No newline at end of file
diff --git a/audio/aidl/vts/VtsHalAECTargetTest.cpp b/audio/aidl/vts/VtsHalAECTargetTest.cpp
index 8828c41..1a7c3d4 100644
--- a/audio/aidl/vts/VtsHalAECTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAECTargetTest.cpp
@@ -162,10 +162,8 @@
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string echoDelay = std::to_string(std::get<PARAM_ECHO_DELAY>(info.param));
             std::string mobileMode = std::get<PARAM_MOBILE_MODE>(info.param) ? "true" : "false";
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_EchoDelay_" + echoDelay +
-                               "_MobileMode_" + mobileMode;
+            std::string name =
+                    getPrefix(descriptor) + "_EchoDelay_" + echoDelay + "_MobileMode_" + mobileMode;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/audio/aidl/vts/VtsHalAGC1TargetTest.cpp b/audio/aidl/vts/VtsHalAGC1TargetTest.cpp
index edfcdf6..65c6a8f 100644
--- a/audio/aidl/vts/VtsHalAGC1TargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAGC1TargetTest.cpp
@@ -177,11 +177,9 @@
                     std::to_string(std::get<PARAM_MAX_COMPRESSION_GAIN>(info.param));
             std::string enableLimiter = std::to_string(std::get<PARAM_ENABLE_LIMITER>(info.param));
 
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_target_peak_level_" +
-                               targetPeakLevel + "_max_compression_gain_" + maxCompressionGain +
-                               "_enable_limiter_" + enableLimiter;
+            std::string name = getPrefix(descriptor) + "_target_peak_level_" + targetPeakLevel +
+                               "_max_compression_gain_" + maxCompressionGain + "_enable_limiter_" +
+                               enableLimiter;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/audio/aidl/vts/VtsHalAGC2TargetTest.cpp b/audio/aidl/vts/VtsHalAGC2TargetTest.cpp
index 8ba8e45..46a569e 100644
--- a/audio/aidl/vts/VtsHalAGC2TargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAGC2TargetTest.cpp
@@ -183,9 +183,7 @@
             std::string margin =
                     std::to_string(static_cast<int>(std::get<PARAM_SATURATION_MARGIN>(info.param)));
 
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_digital_gain_" + gain +
+            std::string name = getPrefix(descriptor) + "_digital_gain_" + gain +
                                "_level_estimator_" + estimator + "_margin_" + margin;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 0ae8cfc..a2e2ef7 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -2763,6 +2763,7 @@
             ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
             std::vector<MicrophoneDynamicInfo> activeMics;
             EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics));
+            EXPECT_FALSE(activeMics.empty());
             for (const auto& mic : activeMics) {
                 EXPECT_NE(micInfos.end(),
                           std::find_if(micInfos.begin(), micInfos.end(),
diff --git a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
index 9cd6c22..8084a59 100644
--- a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
@@ -36,6 +36,11 @@
 #include "EffectFactoryHelper.h"
 #include "TestUtils.h"
 
+#include <system/audio_aidl_utils.h>
+
+using namespace android;
+using ::android::audio::utils::toString;
+
 using namespace android;
 
 using aidl::android::hardware::audio::effect::Descriptor;
@@ -93,7 +98,7 @@
             std::shared_ptr<IEffect> effect;
             EXPECT_STATUS(expectStatus, mEffectFactory->createEffect(uuid, &effect));
             if (expectStatus == EX_NONE) {
-                EXPECT_NE(effect, nullptr) << " null effect with uuid: " << uuid.toString();
+                EXPECT_NE(effect, nullptr) << " null effect with uuid: " << toString(uuid);
                 effects.push_back(std::move(effect));
             }
         }
@@ -148,7 +153,7 @@
     }
     std::string msg = " missing type UUID:\n";
     for (const auto& uuid : typeUuidSet) {
-        msg += (uuid.toString() + "\n");
+        msg += (toString(uuid) + "\n");
     }
     SCOPED_TRACE(msg);
     EXPECT_EQ(0UL, typeUuidSet.size());
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
index 436f2a3..3011a5e 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
@@ -46,6 +46,7 @@
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
 using aidl::android::hardware::audio::effect::State;
+using aidl::android::hardware::audio::effect::Flags;
 using aidl::android::media::audio::common::AudioDeviceDescription;
 using aidl::android::media::audio::common::AudioDeviceType;
 using aidl::android::media::audio::common::AudioMode;
@@ -85,6 +86,14 @@
     }
 };
 
+class AudioEffectDataPathTest : public AudioEffectTest {
+    public:
+        void SetUp() override {
+            AudioEffectTest::SetUp();
+            SKIP_TEST_IF_DATA_UNSUPPORTED(mDescriptor.common.flags);
+        }
+};
+
 TEST_P(AudioEffectTest, SetupAndTearDown) {
     // Intentionally empty test body.
 }
@@ -577,7 +586,8 @@
 
 /// Data processing test
 // Send data to effects and expect it to be consumed by checking statusMQ.
-TEST_P(AudioEffectTest, ConsumeDataInProcessingState) {
+// Effects exposing bypass flags or operating in offload mode will be skipped.
+TEST_P(AudioEffectDataPathTest, ConsumeDataInProcessingState) {
     ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
 
     Parameter::Common common = EffectHelper::createParamCommon(
@@ -610,7 +620,8 @@
 }
 
 // Send data to effects and expect it to be consumed after effect restart.
-TEST_P(AudioEffectTest, ConsumeDataAfterRestart) {
+// Effects exposing bypass flags or operating in offload mode will be skipped.
+TEST_P(AudioEffectDataPathTest, ConsumeDataAfterRestart) {
     ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
 
     Parameter::Common common = EffectHelper::createParamCommon(
@@ -649,7 +660,8 @@
 }
 
 // Send data to IDLE effects and expect it to be consumed after effect start.
-TEST_P(AudioEffectTest, SendDataAtIdleAndConsumeDataInProcessing) {
+// Effects exposing bypass flags or operating in offload mode will be skipped.
+TEST_P(AudioEffectDataPathTest, SendDataAtIdleAndConsumeDataInProcessing) {
     ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
 
     Parameter::Common common = EffectHelper::createParamCommon(
@@ -682,7 +694,8 @@
 }
 
 // Send data multiple times.
-TEST_P(AudioEffectTest, ProcessDataMultipleTimes) {
+// Effects exposing bypass flags or operating in offload mode will be skipped.
+TEST_P(AudioEffectDataPathTest, ProcessDataMultipleTimes) {
     ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
 
     Parameter::Common common = EffectHelper::createParamCommon(
@@ -721,7 +734,8 @@
 }
 
 // Send data to processing state effects, stop, and restart.
-TEST_P(AudioEffectTest, ConsumeDataAndRestart) {
+// Effects exposing bypass flags or operating in offload mode will be skipped.
+TEST_P(AudioEffectDataPathTest, ConsumeDataAndRestart) {
     ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
 
     Parameter::Common common = EffectHelper::createParamCommon(
@@ -762,7 +776,8 @@
 }
 
 // Send data to closed effects and expect it not be consumed.
-TEST_P(AudioEffectTest, NotConsumeDataByClosedEffect) {
+// Effects exposing bypass flags or operating in offload mode will be skipped.
+TEST_P(AudioEffectDataPathTest, NotConsumeDataByClosedEffect) {
     ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
 
     Parameter::Common common = EffectHelper::createParamCommon(
@@ -788,7 +803,8 @@
 }
 
 // Send data to multiple effects.
-TEST_P(AudioEffectTest, ConsumeDataMultipleEffects) {
+// Effects exposing bypass flags or operating in offload mode will be skipped.
+TEST_P(AudioEffectDataPathTest, ConsumeDataMultipleEffects) {
     std::shared_ptr<IEffect> effect1, effect2;
     ASSERT_NO_FATAL_FAILURE(create(mFactory, effect1, mDescriptor));
     ASSERT_NO_FATAL_FAILURE(create(mFactory, effect2, mDescriptor));
@@ -848,16 +864,27 @@
                 EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor))),
         [](const testing::TestParamInfo<AudioEffectTest::ParamType>& info) {
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_TYPE_" +
-                               descriptor.common.id.type.toString() + "_UUID_" +
-                               descriptor.common.id.uuid.toString();
+            std::string name = getPrefix(descriptor);
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
         });
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffectTest);
 
+INSTANTIATE_TEST_SUITE_P(
+        SingleEffectInstanceTest, AudioEffectDataPathTest,
+        ::testing::Combine(testing::ValuesIn(
+                EffectFactoryHelper::getAllEffectDescriptors(IFactory::descriptor))),
+        [](const testing::TestParamInfo<AudioEffectDataPathTest::ParamType>& info) {
+            auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
+            std::string name = getPrefix(descriptor);
+            std::replace_if(
+                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+            return name;
+        });
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffectDataPathTest);
+
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     ABinderProcess_setThreadPoolMaxThreadCount(1);
diff --git a/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp b/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp
index 9cfdc50..afddb84 100644
--- a/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp
@@ -145,9 +145,7 @@
         [](const testing::TestParamInfo<BassBoostParamTest::ParamType>& info) {
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string strength = std::to_string(std::get<PARAM_STRENGTH>(info.param));
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_strength_" + strength;
+            std::string name = getPrefix(descriptor) + "_strength_" + strength;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
index 5aeebde..7a2f31b 100644
--- a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
@@ -126,9 +126,7 @@
         [](const testing::TestParamInfo<DownmixParamTest::ParamType>& info) {
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string type = std::to_string(static_cast<int>(std::get<PARAM_TYPE>(info.param)));
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_type" + type;
+            std::string name = getPrefix(descriptor) + "_type" + type;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
index 033e3b5..5509c76 100644
--- a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
+++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
@@ -513,9 +513,7 @@
             auto descriptor = std::get<ENGINE_TEST_INSTANCE_NAME>(info.param).second;
             DynamicsProcessing::EngineArchitecture cfg;
             fillEngineArchConfig(cfg, info.param);
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_Cfg_" + cfg.toString();
+            std::string name = getPrefix(descriptor) + "_Cfg_" + cfg.toString();
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
index 05c2c5b..f2ef185 100644
--- a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
@@ -209,9 +209,7 @@
             auto descriptor = std::get<0>(info.param).second;
             std::string roomLevel = std::to_string(std::get<1>(info.param));
 
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_roomLevel" + roomLevel;
+            std::string name = getPrefix(descriptor) + "_roomLevel" + roomLevel;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
index 716a2c6..37e7c0a 100644
--- a/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
@@ -209,9 +209,7 @@
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string bandLevel =
                     ::android::internal::ToString(std::get<PARAM_BAND_LEVEL>(info.param));
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_preset_" +
+            std::string name = getPrefix(descriptor) + "_preset_" +
                                std::to_string(std::get<PARAM_PRESET>(info.param)) + "_bandLevel_" +
                                bandLevel;
             std::replace_if(
diff --git a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
index 7c79d1b..54caed9 100644
--- a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
@@ -195,12 +195,10 @@
                     std::to_string(std::get<PARAM_VIBRATION_INFORMATION_Q_FACTOR>(info.param));
             std::string maxAmplitude =
                     std::to_string(std::get<PARAM_VIBRATION_INFORMATION_MAX_AMPLITUDE>(info.param));
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_hapticScaleId" +
-                               hapticScaleID + "_hapticScaleVibScale" + hapticScaleVibScale +
-                               "_resonantFrequency" + resonantFrequency + "_qFactor" + qFactor +
-                               "_maxAmplitude" + maxAmplitude;
+            std::string name = getPrefix(descriptor) + "_hapticScaleId" + hapticScaleID +
+                               "_hapticScaleVibScale" + hapticScaleVibScale + "_resonantFrequency" +
+                               resonantFrequency + "_qFactor" + qFactor + "_maxAmplitude" +
+                               maxAmplitude;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp b/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp
index 96b048e..cbb80a9 100644
--- a/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp
@@ -131,9 +131,7 @@
         [](const testing::TestParamInfo<LoudnessEnhancerParamTest::ParamType>& info) {
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string gainMb = std::to_string(std::get<PARAM_GAIN_MB>(info.param));
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_gainMb_" + gainMb;
+            std::string name = getPrefix(descriptor) + "_gainMb_" + gainMb;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/audio/aidl/vts/VtsHalNSTargetTest.cpp b/audio/aidl/vts/VtsHalNSTargetTest.cpp
index 5525c80..bbb11fc 100644
--- a/audio/aidl/vts/VtsHalNSTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalNSTargetTest.cpp
@@ -155,10 +155,7 @@
                     std::get<PARAM_LEVEL>(info.param));
             std::string type = aidl::android::hardware::audio::effect::toString(
                     std::get<PARAM_TYPE>(info.param));
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_level_" + level + "_type_" +
-                               type;
+            std::string name = getPrefix(descriptor) + "_level_" + level + "_type_" + type;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp b/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp
index 8fb4ebf..3056c6c 100644
--- a/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp
@@ -137,9 +137,7 @@
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string preset =
                     std::to_string(static_cast<int>(std::get<PARAM_PRESETS>(info.param)));
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_preset" + preset;
+            std::string name = getPrefix(descriptor) + "_preset" + preset;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp b/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp
index 6b1da63..07a9fa4 100644
--- a/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp
@@ -141,9 +141,7 @@
         [](const testing::TestParamInfo<VirtualizerParamTest::ParamType>& info) {
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string strength = std::to_string(std::get<PARAM_STRENGTH>(info.param));
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_strength" + strength;
+            std::string name = getPrefix(descriptor) + "_strength" + strength;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp b/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp
index f41ba30..903ba69 100644
--- a/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp
@@ -195,9 +195,7 @@
                     std::get<PARAM_MEASUREMENT_MODE>(info.param));
             std::string latency = std::to_string(std::get<PARAM_LATENCY>(info.param));
 
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_captureSize" + captureSize +
+            std::string name = getPrefix(descriptor) + "_captureSize" + captureSize +
                                "_scalingMode" + scalingMode + "_measurementMode" + measurementMode +
                                "_latency" + latency;
             std::replace_if(
diff --git a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
index 90b7f37..0b5b9fc 100644
--- a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
@@ -149,10 +149,7 @@
             auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
             std::string level = std::to_string(std::get<PARAM_LEVEL>(info.param));
             std::string mute = std::to_string(std::get<PARAM_MUTE>(info.param));
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               descriptor.common.id.uuid.toString() + "_level" + level + "_mute" +
-                               mute;
+            std::string name = getPrefix(descriptor) + "_level" + level + "_mute" + mute;
             std::replace_if(
                     name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
             return name;
diff --git a/automotive/evs/1.0/vts/functional/FrameHandler.cpp b/automotive/evs/1.0/vts/functional/FrameHandler.cpp
index 6a01a44..4233430 100644
--- a/automotive/evs/1.0/vts/functional/FrameHandler.cpp
+++ b/automotive/evs/1.0/vts/functional/FrameHandler.cpp
@@ -133,6 +133,9 @@
     // Local flag we use to keep track of when the stream is stopping
     bool timeToStop = false;
 
+    // Another local flag telling whether or not current frame is displayed.
+    bool frameDisplayed = false;
+
     if (bufferArg.memHandle.getNativeHandle() == nullptr) {
         // Signal that the last frame has been received and the stream is stopped
         timeToStop = true;
@@ -172,9 +175,7 @@
                 } else {
                     // Everything looks good!
                     // Keep track so tests or watch dogs can monitor progress
-                    mLock.lock();
-                    mFramesDisplayed++;
-                    mLock.unlock();
+                    frameDisplayed = true;
                 }
             }
         }
@@ -197,12 +198,15 @@
     }
 
 
-    // Update our received frame count and notify anybody who cares that things have changed
+    // Update frame counters and notify anybody who cares that things have changed.
     mLock.lock();
     if (timeToStop) {
         mRunning = false;
     } else {
         mFramesReceived++;
+        if (frameDisplayed) {
+            mFramesDisplayed++;
+        }
     }
     mLock.unlock();
     mSignal.notify_all();
diff --git a/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h b/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
index 622846a..f023fd2 100644
--- a/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
+++ b/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
@@ -167,7 +167,15 @@
                          .maxSampleRate = 10.0f,
                  },
          .initialValue = {.floatValues = {0.0f}}},
-
+        {.config =
+                 {
+                         .prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED_DISPLAY),
+                         .access = VehiclePropertyAccess::READ,
+                         .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+                         .minSampleRate = 1.0f,
+                         .maxSampleRate = 10.0f,
+                 },
+         .initialValue = {.floatValues = {0.0f}}},
         {.config =
                  {
                          .prop = toInt(VehicleProperty::VEHICLE_SPEED_DISPLAY_UNITS),
@@ -177,7 +185,7 @@
                                          toInt(VehicleUnit::MILES_PER_HOUR),
                                          toInt(VehicleUnit::KILOMETERS_PER_HOUR)},
                  },
-         .initialValue = {.int32Values = {toInt(VehicleUnit::KILOMETERS_PER_HOUR)}}},
+         .initialValue = {.int32Values = {toInt(VehicleUnit::MILES_PER_HOUR)}}},
 
         {.config =
                  {
@@ -1025,7 +1033,7 @@
                          .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                          .configArray = {(int)VehicleUnit::LITER, (int)VehicleUnit::US_GALLON},
                  },
-         .initialValue = {.int32Values = {(int)VehicleUnit::LITER}}},
+         .initialValue = {.int32Values = {(int)VehicleUnit::US_GALLON}}},
 
         {.config =
                  {
diff --git a/bluetooth/aidl/default/BluetoothHci.cpp b/bluetooth/aidl/default/BluetoothHci.cpp
index 18a371d..782122f 100644
--- a/bluetooth/aidl/default/BluetoothHci.cpp
+++ b/bluetooth/aidl/default/BluetoothHci.cpp
@@ -224,6 +224,7 @@
     ALOGI("Unable to open Linux interface, trying default path.");
     mFd = getFdFromDevPath();
     if (mFd < 0) {
+      mState = HalState::READY;
       cb->initializationComplete(Status::UNABLE_TO_OPEN_INTERFACE);
       return ndk::ScopedAStatus::ok();
     }
@@ -281,6 +282,7 @@
   {
     std::lock_guard<std::mutex> guard(mStateMutex);
     if (mState != HalState::ONE_CLIENT) {
+      ASSERT(mState != HalState::INITIALIZING);
       ALOGI("Already closed");
       return ndk::ScopedAStatus::ok();
     }
diff --git a/camera/device/3.2/default/convert.cpp b/camera/device/3.2/default/convert.cpp
index 06ad7e9..2075607 100644
--- a/camera/device/3.2/default/convert.cpp
+++ b/camera/device/3.2/default/convert.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "android.hardware.camera.device@3.2-convert-impl"
 #include <log/log.h>
+#include <system/camera_metadata.h>
 
 #include "include/convert.h"
 
@@ -43,6 +44,13 @@
         ALOGE("%s: input CameraMetadata is corrupt!", __FUNCTION__);
         return false;
     }
+
+    if (validate_camera_metadata_structure((camera_metadata_t*)data, /*expected_size=*/NULL) !=
+        OK) {
+        ALOGE("%s: Failed to validate the metadata structure", __FUNCTION__);
+        return false;
+    }
+
     *dst = (camera_metadata_t*) data;
     return true;
 }
diff --git a/contexthub/aidl/Android.bp b/contexthub/aidl/Android.bp
index a865445..7a0a93b 100644
--- a/contexthub/aidl/Android.bp
+++ b/contexthub/aidl/Android.bp
@@ -34,6 +34,9 @@
         ndk: {
             apps_enabled: false,
         },
+        rust: {
+            enabled: true,
+        },
     },
     versions_with_info: [
         {
diff --git a/gnss/common/utils/default/DeviceFileReader.cpp b/gnss/common/utils/default/DeviceFileReader.cpp
index dfc086a..91e75eb 100644
--- a/gnss/common/utils/default/DeviceFileReader.cpp
+++ b/gnss/common/utils/default/DeviceFileReader.cpp
@@ -32,40 +32,52 @@
         return;
     }
 
-    int mGnssFd = open(deviceFilePath.c_str(), O_RDWR | O_NONBLOCK);
-
-    if (mGnssFd == -1) {
+    int gnss_fd, epoll_fd;
+    if ((gnss_fd = open(deviceFilePath.c_str(), O_RDWR | O_NONBLOCK)) == -1) {
+        return;
+    }
+    if (write(gnss_fd, command.c_str(), command.size()) <= 0) {
+        close(gnss_fd);
         return;
     }
 
-    int bytes_write = write(mGnssFd, command.c_str(), command.size());
-    if (bytes_write <= 0) {
-        close(mGnssFd);
+    // Create an epoll instance.
+    if ((epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
+        close(gnss_fd);
         return;
     }
 
+    // Add file descriptor to epoll instance.
     struct epoll_event ev, events[1];
-    ev.data.fd = mGnssFd;
+    memset(&ev, 0, sizeof(ev));
+    ev.data.fd = gnss_fd;
     ev.events = EPOLLIN;
-    int epoll_fd = epoll_create1(0);
-    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, mGnssFd, &ev);
+    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, gnss_fd, &ev) == -1) {
+        close(gnss_fd);
+        close(epoll_fd);
+        return;
+    }
+
+    // Wait for device file event.
+    if (epoll_wait(epoll_fd, events, 1, mMinIntervalMs) == -1) {
+        close(gnss_fd);
+        close(epoll_fd);
+        return;
+    }
+
+    // Handle event and write data to string buffer.
     int bytes_read = -1;
     std::string inputStr = "";
-    int epoll_ret = epoll_wait(epoll_fd, events, 1, mMinIntervalMs);
-
-    if (epoll_ret == -1) {
-        close(mGnssFd);
-        return;
-    }
     while (true) {
         memset(inputBuffer, 0, INPUT_BUFFER_SIZE);
-        bytes_read = read(mGnssFd, &inputBuffer, INPUT_BUFFER_SIZE);
+        bytes_read = read(gnss_fd, &inputBuffer, INPUT_BUFFER_SIZE);
         if (bytes_read <= 0) {
             break;
         }
         s_buffer_ += std::string(inputBuffer, bytes_read);
     }
-    close(mGnssFd);
+    close(gnss_fd);
+    close(epoll_fd);
 
     // Trim end of file mark(\n\n\n\n).
     auto pos = s_buffer_.find("\n\n\n\n");
diff --git a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerCommandEngine.h b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerCommandEngine.h
index ab67eb1..9ae6173 100644
--- a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerCommandEngine.h
+++ b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerCommandEngine.h
@@ -207,7 +207,7 @@
 
     bool executeSetClientTarget(uint16_t length) {
         // 4 parameters followed by N rectangles
-        if ((length - 4) % 4 != 0) {
+        if (!length || (length - 4) % 4 != 0) {
             return false;
         }
 
diff --git a/light/aidl/Android.bp b/light/aidl/Android.bp
index c11934f..c9fba95 100644
--- a/light/aidl/Android.bp
+++ b/light/aidl/Android.bp
@@ -18,6 +18,9 @@
         java: {
             sdk_version: "module_current",
         },
+        rust: {
+            enabled: true,
+        },
     },
     versions_with_info: [
         {
diff --git a/light/aidl/aidl_api/android.hardware.light/2/.hash b/light/aidl/aidl_api/android.hardware.light/2/.hash
index d27f4ad..2d4e7f0 100644
--- a/light/aidl/aidl_api/android.hardware.light/2/.hash
+++ b/light/aidl/aidl_api/android.hardware.light/2/.hash
@@ -1 +1,2 @@
 c8b1e8ebb88c57dcb2c350a8d9b722e77dd864c8
+c7d3d941d303c70d1c22759a0b09e41930c1cddb
diff --git a/light/aidl/aidl_api/android.hardware.light/2/android/hardware/light/HwLight.aidl b/light/aidl/aidl_api/android.hardware.light/2/android/hardware/light/HwLight.aidl
index 25a2dce..5ac2a34 100644
--- a/light/aidl/aidl_api/android.hardware.light/2/android/hardware/light/HwLight.aidl
+++ b/light/aidl/aidl_api/android.hardware.light/2/android/hardware/light/HwLight.aidl
@@ -32,7 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.light;
-@VintfStability
+@RustDerive(Clone=true, Copy=true) @VintfStability
 parcelable HwLight {
   int id;
   int ordinal;
diff --git a/light/aidl/aidl_api/android.hardware.light/2/android/hardware/light/HwLightState.aidl b/light/aidl/aidl_api/android.hardware.light/2/android/hardware/light/HwLightState.aidl
index 40e520b..2878ce2 100644
--- a/light/aidl/aidl_api/android.hardware.light/2/android/hardware/light/HwLightState.aidl
+++ b/light/aidl/aidl_api/android.hardware.light/2/android/hardware/light/HwLightState.aidl
@@ -32,7 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.light;
-@VintfStability
+@RustDerive(Clone=true, Copy=true) @VintfStability
 parcelable HwLightState {
   int color;
   android.hardware.light.FlashMode flashMode;
diff --git a/light/aidl/aidl_api/android.hardware.light/current/android/hardware/light/HwLight.aidl b/light/aidl/aidl_api/android.hardware.light/current/android/hardware/light/HwLight.aidl
index 25a2dce..5ac2a34 100644
--- a/light/aidl/aidl_api/android.hardware.light/current/android/hardware/light/HwLight.aidl
+++ b/light/aidl/aidl_api/android.hardware.light/current/android/hardware/light/HwLight.aidl
@@ -32,7 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.light;
-@VintfStability
+@RustDerive(Clone=true, Copy=true) @VintfStability
 parcelable HwLight {
   int id;
   int ordinal;
diff --git a/light/aidl/aidl_api/android.hardware.light/current/android/hardware/light/HwLightState.aidl b/light/aidl/aidl_api/android.hardware.light/current/android/hardware/light/HwLightState.aidl
index 40e520b..2878ce2 100644
--- a/light/aidl/aidl_api/android.hardware.light/current/android/hardware/light/HwLightState.aidl
+++ b/light/aidl/aidl_api/android.hardware.light/current/android/hardware/light/HwLightState.aidl
@@ -32,7 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.light;
-@VintfStability
+@RustDerive(Clone=true, Copy=true) @VintfStability
 parcelable HwLightState {
   int color;
   android.hardware.light.FlashMode flashMode;
diff --git a/light/aidl/android/hardware/light/HwLight.aidl b/light/aidl/android/hardware/light/HwLight.aidl
index 43fdb4b..8db32cc 100644
--- a/light/aidl/android/hardware/light/HwLight.aidl
+++ b/light/aidl/android/hardware/light/HwLight.aidl
@@ -22,7 +22,7 @@
  * A description of a single light. Multiple lights can map to the same physical
  * LED. Separate physical LEDs are always represented by separate instances.
  */
-@VintfStability
+@RustDerive(Clone=true, Copy=true) @VintfStability
 parcelable HwLight {
     /**
      * Integer ID used for controlling this light
diff --git a/light/aidl/android/hardware/light/HwLightState.aidl b/light/aidl/android/hardware/light/HwLightState.aidl
index 24d3250..3ba6c78 100644
--- a/light/aidl/android/hardware/light/HwLightState.aidl
+++ b/light/aidl/android/hardware/light/HwLightState.aidl
@@ -25,7 +25,7 @@
  * Not all lights must support all parameters. If you
  * can do something backward-compatible, do it.
  */
-@VintfStability
+@RustDerive(Clone=true, Copy=true) @VintfStability
 parcelable HwLightState {
     /**
      * The color of the LED in ARGB.
diff --git a/light/aidl/default/Android.bp b/light/aidl/default/Android.bp
index 7920503..285329e 100644
--- a/light/aidl/default/Android.bp
+++ b/light/aidl/default/Android.bp
@@ -7,19 +7,17 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-cc_binary {
+rust_binary {
     name: "android.hardware.lights-service.example",
     relative_install_path: "hw",
     init_rc: ["lights-default.rc"],
     vintf_fragments: ["lights-default.xml"],
     vendor: true,
-    shared_libs: [
-        "libbase",
-        "libbinder_ndk",
-        "android.hardware.light-V2-ndk",
+    rustlibs: [
+        "liblogger",
+        "liblog_rust",
+        "libbinder_rs",
+        "android.hardware.light-V2-rust",
     ],
-    srcs: [
-        "Lights.cpp",
-        "main.cpp",
-    ],
+    srcs: [ "main.rs" ],
 }
diff --git a/light/aidl/default/Lights.cpp b/light/aidl/default/Lights.cpp
deleted file mode 100644
index 9bf3b20..0000000
--- a/light/aidl/default/Lights.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 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 "Lights.h"
-
-#include <android-base/logging.h>
-
-namespace aidl {
-namespace android {
-namespace hardware {
-namespace light {
-
-static constexpr int kNumDefaultLights = 3;
-
-ndk::ScopedAStatus Lights::setLightState(int id, const HwLightState& state) {
-    LOG(INFO) << "Lights setting state for id=" << id << " to color " << std::hex << state.color;
-    if (id <= 0 || id > kNumDefaultLights) {
-        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
-    } else {
-        return ndk::ScopedAStatus::ok();
-    }
-}
-
-ndk::ScopedAStatus Lights::getLights(std::vector<HwLight>* lights) {
-    for (int i = 1; i <= kNumDefaultLights; i++) {
-        lights->push_back({i, i});
-    }
-    LOG(INFO) << "Lights reporting supported lights";
-    return ndk::ScopedAStatus::ok();
-}
-
-}  // namespace light
-}  // namespace hardware
-}  // namespace android
-}  // namespace aidl
diff --git a/light/aidl/default/Lights.h b/light/aidl/default/Lights.h
deleted file mode 100644
index cba147f..0000000
--- a/light/aidl/default/Lights.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <aidl/android/hardware/light/BnLights.h>
-
-namespace aidl {
-namespace android {
-namespace hardware {
-namespace light {
-
-// Default implementation that reports a few placeholder lights.
-class Lights : public BnLights {
-    ndk::ScopedAStatus setLightState(int id, const HwLightState& state) override;
-    ndk::ScopedAStatus getLights(std::vector<HwLight>* lights) override;
-};
-
-}  // namespace light
-}  // namespace hardware
-}  // namespace android
-}  // namespace aidl
diff --git a/light/aidl/default/lights.rs b/light/aidl/default/lights.rs
new file mode 100644
index 0000000..6c8aa3f
--- /dev/null
+++ b/light/aidl/default/lights.rs
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+//! This module implements the ILights AIDL interface.
+
+use std::collections::HashMap;
+use std::sync::Mutex;
+
+use log::info;
+
+use android_hardware_light::aidl::android::hardware::light::{
+    HwLight::HwLight, HwLightState::HwLightState, ILights::ILights, LightType::LightType,
+};
+
+use binder::{ExceptionCode, Interface, Status};
+
+struct Light {
+    hw_light: HwLight,
+    state: HwLightState,
+}
+
+const NUM_DEFAULT_LIGHTS: i32 = 3;
+
+/// Defined so we can implement the ILights AIDL interface.
+pub struct LightsService {
+    lights: Mutex<HashMap<i32, Light>>,
+}
+
+impl Interface for LightsService {}
+
+impl LightsService {
+    fn new(hw_lights: impl IntoIterator<Item = HwLight>) -> Self {
+        let mut lights_map = HashMap::new();
+
+        for hw_light in hw_lights {
+            lights_map.insert(hw_light.id, Light { hw_light, state: Default::default() });
+        }
+
+        Self { lights: Mutex::new(lights_map) }
+    }
+}
+
+impl Default for LightsService {
+    fn default() -> Self {
+        let id_mapping_closure =
+            |light_id| HwLight { id: light_id, ordinal: light_id, r#type: LightType::BACKLIGHT };
+
+        Self::new((1..=NUM_DEFAULT_LIGHTS).map(id_mapping_closure))
+    }
+}
+
+impl ILights for LightsService {
+    fn setLightState(&self, id: i32, state: &HwLightState) -> binder::Result<()> {
+        info!("Lights setting state for id={} to color {:x}", id, state.color);
+
+        if let Some(light) = self.lights.lock().unwrap().get_mut(&id) {
+            light.state = *state;
+            Ok(())
+        } else {
+            Err(Status::new_exception(ExceptionCode::UNSUPPORTED_OPERATION, None))
+        }
+    }
+
+    fn getLights(&self) -> binder::Result<Vec<HwLight>> {
+        info!("Lights reporting supported lights");
+        Ok(self.lights.lock().unwrap().values().map(|light| light.hw_light).collect())
+    }
+}
diff --git a/light/aidl/default/main.cpp b/light/aidl/default/main.cpp
deleted file mode 100644
index 54e1316..0000000
--- a/light/aidl/default/main.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Lights.h"
-
-#include <android-base/logging.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-
-using ::aidl::android::hardware::light::Lights;
-
-int main() {
-    ABinderProcess_setThreadPoolMaxThreadCount(0);
-    std::shared_ptr<Lights> lights = ndk::SharedRefBase::make<Lights>();
-
-    const std::string instance = std::string() + Lights::descriptor + "/default";
-    binder_status_t status = AServiceManager_addService(lights->asBinder().get(), instance.c_str());
-    CHECK_EQ(status, STATUS_OK);
-
-    ABinderProcess_joinThreadPool();
-    return EXIT_FAILURE;  // should not reached
-}
diff --git a/light/aidl/default/main.rs b/light/aidl/default/main.rs
new file mode 100644
index 0000000..8f32470
--- /dev/null
+++ b/light/aidl/default/main.rs
@@ -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.
+ */
+//! This implements the Lights Example Service.
+
+use android_hardware_light::aidl::android::hardware::light::ILights::{BnLights, ILights};
+use binder::BinderFeatures;
+
+mod lights;
+use lights::LightsService;
+
+const LOG_TAG: &str = "lights_service_example_rust";
+
+use log::Level;
+
+fn main() {
+    let logger_success = logger::init(
+        logger::Config::default().with_tag_on_device(LOG_TAG).with_min_level(Level::Trace),
+    );
+    if !logger_success {
+        panic!("{LOG_TAG}: Failed to start logger.");
+    }
+
+    binder::ProcessState::set_thread_pool_max_thread_count(0);
+
+    let lights_service = LightsService::default();
+    let lights_service_binder = BnLights::new_binder(lights_service, BinderFeatures::default());
+
+    let service_name = format!("{}/default", LightsService::get_descriptor());
+    binder::add_service(&service_name, lights_service_binder.as_binder())
+        .expect("Failed to register service");
+
+    binder::ProcessState::join_thread_pool()
+}
diff --git a/security/keymint/aidl/default/android.hardware.security.keymint-service.xml b/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
index a4d0302..0568ae6 100644
--- a/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
+++ b/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
@@ -1,12 +1,12 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.security.keymint</name>
-        <version>2</version>
+        <version>3</version>
         <fqname>IKeyMintDevice/default</fqname>
     </hal>
     <hal format="aidl">
         <name>android.hardware.security.keymint</name>
-        <version>2</version>
+        <version>3</version>
         <fqname>IRemotelyProvisionedComponent/default</fqname>
     </hal>
 </manifest>
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
index c25c9ac..0499079 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -82,7 +82,7 @@
 
     string imei = ::android::base::Trim(out[0]);
     if (imei.compare("null") == 0) {
-        LOG(ERROR) << "Error in getting IMEI from Telephony service: value is null. Cmd: " << cmd;
+        LOG(WARNING) << "Failed to get IMEI from Telephony service: value is null. Cmd: " << cmd;
         return "";
     }
 
@@ -950,10 +950,7 @@
         vector<Certificate> attested_key_cert_chain;
         auto result = GenerateKey(builder, attest_key, &attested_key_blob,
                                   &attested_key_characteristics, &attested_key_cert_chain);
-
-        ASSERT_TRUE(result == ErrorCode::CANNOT_ATTEST_IDS || result == ErrorCode::INVALID_TAG)
-                << "result = " << result;
-        device_id_attestation_vsr_check(result);
+        device_id_attestation_check_acceptable_error(invalid_tag.tag, result);
     }
 }
 
@@ -1016,8 +1013,6 @@
     ASSERT_EQ(result, ErrorCode::OK);
     KeyBlobDeleter attested_deleter(keymint_, attested_key_blob);
 
-    device_id_attestation_vsr_check(result);
-
     AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
     AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
 
@@ -1095,8 +1090,6 @@
     ASSERT_EQ(result, ErrorCode::OK);
     KeyBlobDeleter attested_deleter(keymint_, attested_key_blob);
 
-    device_id_attestation_vsr_check(result);
-
     AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
     AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
 
diff --git a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
index 55bb5b4..8e9aded 100644
--- a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
+++ b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -374,8 +374,8 @@
         // Add the tag that doesn't match the local device's real ID.
         builder.push_back(invalid_tag);
         auto result = GenerateKey(builder, &key_blob, &key_characteristics);
-        ASSERT_TRUE(result == ErrorCode::CANNOT_ATTEST_IDS || result == ErrorCode::INVALID_TAG);
-        device_id_attestation_vsr_check(result);
+
+        device_id_attestation_check_acceptable_error(invalid_tag.tag, result);
     }
 }
 
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index b2fd08e..ee490a3 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -71,6 +71,11 @@
 // additional overhead, for the digest algorithmIdentifier required by PKCS#1.
 const size_t kPkcs1UndigestedSignaturePaddingOverhead = 11;
 
+size_t count_tag_invalid_entries(const std::vector<KeyParameter>& authorizations) {
+    return std::count_if(authorizations.begin(), authorizations.end(),
+                         [](const KeyParameter& e) -> bool { return e.tag == Tag::INVALID; });
+}
+
 typedef KeyMintAidlTestBase::KeyData KeyData;
 // Predicate for testing basic characteristics validity in generation or import.
 bool KeyCharacteristicsBasicallyValid(SecurityLevel secLevel,
@@ -84,6 +89,8 @@
             return false;
         }
 
+        EXPECT_EQ(count_tag_invalid_entries(entry.authorizations), 0);
+
         // Just ignore the SecurityLevel::KEYSTORE as the KM won't do any enforcement on this.
         if (entry.securityLevel == SecurityLevel::KEYSTORE) continue;
 
@@ -2042,6 +2049,27 @@
     return retval;
 }
 
+void assert_mgf_digests_present_in_key_characteristics(
+        const vector<KeyCharacteristics>& key_characteristics,
+        std::vector<android::hardware::security::keymint::Digest>& expected_mgf_digests) {
+    AuthorizationSet auths;
+    for (auto& entry : key_characteristics) {
+        auths.push_back(AuthorizationSet(entry.authorizations));
+    }
+    for (auto digest : expected_mgf_digests) {
+        ASSERT_TRUE(auths.Contains(TAG_RSA_OAEP_MGF_DIGEST, digest));
+    }
+}
+
+bool is_mgf_digest_present(const vector<KeyCharacteristics>& key_characteristics,
+                           android::hardware::security::keymint::Digest expected_mgf_digest) {
+    AuthorizationSet auths;
+    for (auto& entry : key_characteristics) {
+        auths.push_back(AuthorizationSet(entry.authorizations));
+    }
+    return auths.Contains(TAG_RSA_OAEP_MGF_DIGEST, expected_mgf_digest);
+}
+
 namespace {
 
 void check_cose_key(const vector<uint8_t>& data, bool testMode) {
@@ -2162,14 +2190,32 @@
     *signingKey = std::move(pubKey);
 }
 
-void device_id_attestation_vsr_check(const ErrorCode& result) {
-    if (get_vsr_api_level() > __ANDROID_API_T__) {
-        ASSERT_FALSE(result == ErrorCode::INVALID_TAG)
+// Check the error code from an attempt to perform device ID attestation with an invalid value.
+void device_id_attestation_check_acceptable_error(Tag tag, const ErrorCode& result) {
+    // Standard/default error code for ID mismatch.
+    if (result == ErrorCode::CANNOT_ATTEST_IDS) {
+        return;
+    }
+
+    // Depending on the situation, other error codes may be acceptable.  First, allow older
+    // implementations to use INVALID_TAG.
+    if (result == ErrorCode::INVALID_TAG) {
+        ASSERT_FALSE(get_vsr_api_level() > __ANDROID_API_T__)
                 << "It is a specification violation for INVALID_TAG to be returned due to ID "
                 << "mismatch in a Device ID Attestation call. INVALID_TAG is only intended to "
                 << "be used for a case where updateAad() is called after update(). As of "
                 << "VSR-14, this is now enforced as an error.";
     }
+
+    // If the device is not a phone, it will not have IMEI/MEID values available.  Allow
+    // ATTESTATION_IDS_NOT_PROVISIONED in this case.
+    if (result == ErrorCode::ATTESTATION_IDS_NOT_PROVISIONED) {
+        ASSERT_TRUE((tag == TAG_ATTESTATION_ID_IMEI || tag == TAG_ATTESTATION_ID_MEID ||
+                     tag == TAG_ATTESTATION_ID_SECOND_IMEI))
+                << "incorrect error code on attestation ID mismatch";
+    }
+    ADD_FAILURE() << "Error code " << result
+                  << " returned on attestation ID mismatch, should be CANNOT_ATTEST_IDS";
 }
 
 // Check whether the given named feature is available.
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index aa3069a..5d32268 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -429,10 +429,15 @@
 X509_Ptr parse_cert_blob(const vector<uint8_t>& blob);
 ASN1_OCTET_STRING* get_attestation_record(X509* certificate);
 vector<uint8_t> make_name_from_str(const string& name);
+void assert_mgf_digests_present_in_key_characteristics(
+        const vector<KeyCharacteristics>& key_characteristics,
+        std::vector<android::hardware::security::keymint::Digest>& expected_mgf_digests);
+bool is_mgf_digest_present(const vector<KeyCharacteristics>& key_characteristics,
+                           android::hardware::security::keymint::Digest expected_mgf_digest);
 void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
                         vector<uint8_t>* payload_value);
 void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey);
-void device_id_attestation_vsr_check(const ErrorCode& result);
+void device_id_attestation_check_acceptable_error(Tag tag, const ErrorCode& result);
 bool check_feature(const std::string& name);
 
 AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index c534a37..051ace4 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -3430,7 +3430,6 @@
  * Verifies ECDSA signature/verification for all digests and required curves.
  */
 TEST_P(SigningOperationsTest, EcdsaAllDigestsAndCurves) {
-
     string message = "1234567890";
     string corrupt_message = "2234567890";
     for (auto curve : ValidCurves()) {
@@ -4727,6 +4726,102 @@
     }
 }
 
+/*
+ * ImportKeyTest.RsaOaepMGFDigestSuccess
+ *
+ * Include MGF-Digest explicitly in import key authorization list.
+ * Test should import RSA key with OAEP padding and mgf-digests and verify that imported key
+ * should have the correct characteristics.
+ */
+TEST_P(ImportKeyTest, RsaOaepMGFDigestSuccess) {
+    auto mgf_digests = ValidDigests(false /* withNone */, true /* withMD5 */);
+    size_t key_size = 2048;
+
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .OaepMGFDigest(mgf_digests)
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .RsaEncryptionKey(key_size, 65537)
+                                               .Digest(Digest::SHA_2_256)
+                                               .Padding(PaddingMode::RSA_OAEP)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, rsa_2048_key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::RSA);
+    CheckCryptoParam(TAG_KEY_SIZE, key_size);
+    CheckCryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_PADDING, PaddingMode::RSA_OAEP);
+    CheckOrigin();
+
+    // Make sure explicitly specified mgf-digests exist in key characteristics.
+    assert_mgf_digests_present_in_key_characteristics(key_characteristics_, mgf_digests);
+
+    string message = "Hello";
+
+    for (auto digest : mgf_digests) {
+        SCOPED_TRACE(testing::Message() << "digest-" << digest);
+        auto params = AuthorizationSetBuilder()
+                              .Authorization(TAG_RSA_OAEP_MGF_DIGEST, digest)
+                              .Digest(Digest::SHA_2_256)
+                              .Padding(PaddingMode::RSA_OAEP);
+        string ciphertext1 = LocalRsaEncryptMessage(message, params);
+        if (HasNonfatalFailure()) std::cout << "-->" << digest << std::endl;
+        EXPECT_EQ(key_size / 8, ciphertext1.size());
+
+        string ciphertext2 = LocalRsaEncryptMessage(message, params);
+        if (HasNonfatalFailure()) std::cout << "-->" << digest << std::endl;
+        EXPECT_EQ(key_size / 8, ciphertext2.size());
+
+        // OAEP randomizes padding so every result should be different (with astronomically high
+        // probability).
+        EXPECT_NE(ciphertext1, ciphertext2);
+
+        string plaintext1 = DecryptMessage(ciphertext1, params);
+        EXPECT_EQ(message, plaintext1) << "RSA-OAEP failed with digest " << digest;
+        string plaintext2 = DecryptMessage(ciphertext2, params);
+        EXPECT_EQ(message, plaintext2) << "RSA-OAEP failed with digest " << digest;
+
+        // Decrypting corrupted ciphertext should fail.
+        size_t offset_to_corrupt = ciphertext1.size() - 1;
+        char corrupt_byte = ~ciphertext1[offset_to_corrupt];
+        ciphertext1[offset_to_corrupt] = corrupt_byte;
+
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+        string result;
+        EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result));
+        EXPECT_EQ(0U, result.size());
+    }
+}
+
+/*
+ * ImportKeyTest.RsaOaepMGFDigestDefaultSuccess
+ *
+ * Don't specify MGF-Digest explicitly in import key authorization list.
+ * Test should import RSA key with OAEP padding and default mgf-digest (SHA1) and
+ * verify that imported key should have the correct characteristics. Default
+ * mgf-digest shouldn't be included in key charecteristics.
+ */
+TEST_P(ImportKeyTest, RsaOaepMGFDigestDefaultSuccess) {
+    size_t key_size = 2048;
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .RsaEncryptionKey(key_size, 65537)
+                                               .Digest(Digest::SHA_2_256)
+                                               .Padding(PaddingMode::RSA_OAEP)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, rsa_2048_key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::RSA);
+    CheckCryptoParam(TAG_KEY_SIZE, key_size);
+    CheckCryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_PADDING, PaddingMode::RSA_OAEP);
+    CheckOrigin();
+
+    // Make sure default mgf-digest (SHA1) is not included in Key characteristics.
+    ASSERT_FALSE(is_mgf_digest_present(key_characteristics_, Digest::SHA1));
+}
+
 INSTANTIATE_KEYMINT_AIDL_TEST(ImportKeyTest);
 
 auto wrapped_key = hex2str(
@@ -5152,16 +5247,19 @@
  */
 TEST_P(EncryptionOperationsTest, RsaOaepSuccess) {
     auto digests = ValidDigests(false /* withNone */, true /* withMD5 */);
+    auto mgf_digest = Digest::SHA1;
 
     size_t key_size = 2048;  // Need largish key for SHA-512 test.
-    ASSERT_EQ(ErrorCode::OK,
-              GenerateKey(AuthorizationSetBuilder()
-                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                  .RsaEncryptionKey(key_size, 65537)
-                                  .Padding(PaddingMode::RSA_OAEP)
-                                  .Digest(digests)
-                                  .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA1)
-                                  .SetDefaultValidity()));
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(key_size, 65537)
+                                                 .Padding(PaddingMode::RSA_OAEP)
+                                                 .Digest(digests)
+                                                 .Authorization(TAG_RSA_OAEP_MGF_DIGEST, mgf_digest)
+                                                 .SetDefaultValidity()));
+
+    // Make sure explicitly specified mgf-digest exist in key characteristics.
+    ASSERT_TRUE(is_mgf_digest_present(key_characteristics_, mgf_digest));
 
     string message = "Hello";
 
@@ -5287,6 +5385,20 @@
                                                  .Digest(Digest::SHA_2_256)
                                                  .SetDefaultValidity()));
 
+    std::vector<Digest> mgf1DigestsInAuths;
+    mgf1DigestsInAuths.reserve(digests.size());
+    const auto& hw_auths = SecLevelAuthorizations(key_characteristics_);
+    std::for_each(hw_auths.begin(), hw_auths.end(), [&](auto& param) {
+        if (param.tag == Tag::RSA_OAEP_MGF_DIGEST) {
+            KeyParameterValue value = param.value;
+            mgf1DigestsInAuths.push_back(param.value.template get<KeyParameterValue::digest>());
+        }
+    });
+
+    std::sort(digests.begin(), digests.end());
+    std::sort(mgf1DigestsInAuths.begin(), mgf1DigestsInAuths.end());
+    EXPECT_EQ(digests, mgf1DigestsInAuths);
+
     string message = "Hello";
 
     for (auto digest : digests) {
@@ -5341,6 +5453,9 @@
                                                  .Digest(Digest::SHA_2_256)
                                                  .SetDefaultValidity()));
 
+    // Make sure default mgf-digest (SHA1) is not included in Key characteristics.
+    ASSERT_FALSE(is_mgf_digest_present(key_characteristics_, Digest::SHA1));
+
     // Do local RSA encryption using the default MGF digest of SHA-1.
     string message = "Hello";
     auto params =
@@ -5375,14 +5490,19 @@
  */
 TEST_P(EncryptionOperationsTest, RsaOaepMGFDigestDefaultFail) {
     size_t key_size = 2048;
-    ASSERT_EQ(ErrorCode::OK,
-              GenerateKey(AuthorizationSetBuilder()
-                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                  .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_256)
-                                  .RsaEncryptionKey(key_size, 65537)
-                                  .Padding(PaddingMode::RSA_OAEP)
-                                  .Digest(Digest::SHA_2_256)
-                                  .SetDefaultValidity()));
+    auto mgf_digest = Digest::SHA_2_256;
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Authorization(TAG_RSA_OAEP_MGF_DIGEST, mgf_digest)
+                                                 .RsaEncryptionKey(key_size, 65537)
+                                                 .Padding(PaddingMode::RSA_OAEP)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .SetDefaultValidity()));
+
+    // Make sure explicitly specified mgf-digest exist in key characteristics.
+    ASSERT_TRUE(is_mgf_digest_present(key_characteristics_, mgf_digest));
+    // Make sure default mgf-digest is not included in key characteristics.
+    ASSERT_FALSE(is_mgf_digest_present(key_characteristics_, Digest::SHA1));
 
     // Do local RSA encryption using the default MGF digest of SHA-1.
     string message = "Hello";
@@ -5406,14 +5526,17 @@
  * with incompatible MGF digest.
  */
 TEST_P(EncryptionOperationsTest, RsaOaepWithMGFIncompatibleDigest) {
-    ASSERT_EQ(ErrorCode::OK,
-              GenerateKey(AuthorizationSetBuilder()
-                                  .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_256)
-                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                  .RsaEncryptionKey(2048, 65537)
-                                  .Padding(PaddingMode::RSA_OAEP)
-                                  .Digest(Digest::SHA_2_256)
-                                  .SetDefaultValidity()));
+    auto mgf_digest = Digest::SHA_2_256;
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_RSA_OAEP_MGF_DIGEST, mgf_digest)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::RSA_OAEP)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .SetDefaultValidity()));
+    // Make sure explicitly specified mgf-digest exist in key characteristics.
+    ASSERT_TRUE(is_mgf_digest_present(key_characteristics_, mgf_digest));
+
     string message = "Hello World!";
 
     auto params = AuthorizationSetBuilder()
@@ -5430,14 +5553,17 @@
  * with unsupported MGF digest.
  */
 TEST_P(EncryptionOperationsTest, RsaOaepWithMGFUnsupportedDigest) {
-    ASSERT_EQ(ErrorCode::OK,
-              GenerateKey(AuthorizationSetBuilder()
-                                  .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_256)
-                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                  .RsaEncryptionKey(2048, 65537)
-                                  .Padding(PaddingMode::RSA_OAEP)
-                                  .Digest(Digest::SHA_2_256)
-                                  .SetDefaultValidity()));
+    auto mgf_digest = Digest::SHA_2_256;
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_RSA_OAEP_MGF_DIGEST, mgf_digest)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::RSA_OAEP)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .SetDefaultValidity()));
+    // Make sure explicitly specified mgf-digest exist in key characteristics.
+    ASSERT_TRUE(is_mgf_digest_present(key_characteristics_, mgf_digest));
+
     string message = "Hello World!";
 
     auto params = AuthorizationSetBuilder()
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index 3cb783c..c9c3e4d 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -115,6 +115,36 @@
     return std::make_tuple(std::move(pubX), std::move(pubY));
 }
 
+ErrMsgOr<bytevec> getRawPublicKey(const EVP_PKEY_Ptr& pubKey) {
+    if (pubKey.get() == nullptr) {
+        return "pkey is null.";
+    }
+    int keyType = EVP_PKEY_base_id(pubKey.get());
+    switch (keyType) {
+        case EVP_PKEY_EC: {
+            auto ecKey = EC_KEY_Ptr(EVP_PKEY_get1_EC_KEY(pubKey.get()));
+            if (ecKey.get() == nullptr) {
+                return "Failed to get ec key";
+            }
+            return ecKeyGetPublicKey(ecKey.get());
+        }
+        case EVP_PKEY_ED25519: {
+            bytevec rawPubKey;
+            size_t rawKeySize = 0;
+            if (!EVP_PKEY_get_raw_public_key(pubKey.get(), NULL, &rawKeySize)) {
+                return "Failed to get raw public key.";
+            }
+            rawPubKey.resize(rawKeySize);
+            if (!EVP_PKEY_get_raw_public_key(pubKey.get(), rawPubKey.data(), &rawKeySize)) {
+                return "Failed to get raw public key.";
+            }
+            return rawPubKey;
+        }
+        default:
+            return "Unknown key type.";
+    }
+}
+
 ErrMsgOr<std::tuple<bytevec, bytevec>> generateEc256KeyPair() {
     auto ec_key = EC_KEY_Ptr(EC_KEY_new());
     if (ec_key.get() == nullptr) {
@@ -706,11 +736,10 @@
 
 // Validates the certificate chain and returns the leaf public key.
 ErrMsgOr<bytevec> validateCertChain(const cppbor::Array& chain) {
-    uint8_t rawPubKey[64];
-    size_t rawPubKeySize = sizeof(rawPubKey);
+    bytevec rawPubKey;
     for (size_t i = 0; i < chain.size(); ++i) {
         // Root must be self-signed.
-        size_t signingCertIndex = (i > 1) ? i - 1 : i;
+        size_t signingCertIndex = (i > 0) ? i - 1 : i;
         auto& keyCertItem = chain[i];
         auto& signingCertItem = chain[signingCertIndex];
         if (!keyCertItem || !keyCertItem->asBstr()) {
@@ -724,7 +753,7 @@
         if (!keyCert) {
             return keyCert.message();
         }
-        auto signingCert = parseX509Cert(keyCertItem->asBstr()->value());
+        auto signingCert = parseX509Cert(signingCertItem->asBstr()->value());
         if (!signingCert) {
             return signingCert.message();
         }
@@ -749,17 +778,16 @@
             return "Certificate " + std::to_string(i) + " has wrong issuer. Signer subject is " +
                    signerSubj + " Issuer subject is " + certIssuer;
         }
-
-        rawPubKeySize = sizeof(rawPubKey);
-        if (!EVP_PKEY_get_raw_public_key(pubKey.get(), rawPubKey, &rawPubKeySize)) {
-            return "Failed to get raw public key.";
+        if (i == chain.size() - 1) {
+            auto key = getRawPublicKey(pubKey);
+            if (!key) key.moveMessage();
+            rawPubKey = key.moveValue();
         }
     }
-
-    return bytevec(rawPubKey, rawPubKey + rawPubKeySize);
+    return rawPubKey;
 }
 
-std::string validateUdsCerts(const cppbor::Map& udsCerts, const bytevec& udsPub) {
+std::string validateUdsCerts(const cppbor::Map& udsCerts, const bytevec& udsCoseKeyBytes) {
     for (const auto& [signerName, udsCertChain] : udsCerts) {
         if (!signerName || !signerName->asTstr()) {
             return "Signer Name must be a Tstr.";
@@ -775,8 +803,31 @@
         if (!leafPubKey) {
             return leafPubKey.message();
         }
+        auto coseKey = CoseKey::parse(udsCoseKeyBytes);
+        if (!coseKey) return coseKey.moveMessage();
+
+        auto curve = coseKey->getIntValue(CoseKey::CURVE);
+        if (!curve) {
+            return "CoseKey must contain curve.";
+        }
+        bytevec udsPub;
+        if (curve == CoseKeyCurve::P256 || curve == CoseKeyCurve::P384) {
+            auto pubKey = coseKey->getEcPublicKey();
+            if (!pubKey) return pubKey.moveMessage();
+            // convert public key to uncompressed form by prepending 0x04 at begin.
+            pubKey->insert(pubKey->begin(), 0x04);
+            udsPub = pubKey.moveValue();
+        } else if (curve == CoseKeyCurve::ED25519) {
+            auto& pubkey = coseKey->getMap().get(cppcose::CoseKey::PUBKEY_X);
+            if (!pubkey || !pubkey->asBstr()) {
+                return "Invalid public key.";
+            }
+            udsPub = pubkey->asBstr()->value();
+        } else {
+            return "Unknown curve.";
+        }
         if (*leafPubKey != udsPub) {
-            return "Leaf public key in UDS certificat chain doesn't match UDS public key.";
+            return "Leaf public key in UDS certificate chain doesn't match UDS public key.";
         }
     }
     return "";
diff --git a/sensors/2.1/default/apex/apex_manifest.json b/sensors/2.1/default/apex/apex_manifest.json
deleted file mode 100644
index 47e45ee..0000000
--- a/sensors/2.1/default/apex/apex_manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "name": "com.android.hardware.sensors",
-  "version": 1
-}
diff --git a/sensors/2.1/default/apex/com.android.hardware.sensors.rc b/sensors/2.1/default/apex/com.android.hardware.sensors.rc
deleted file mode 100644
index bd245b4..0000000
--- a/sensors/2.1/default/apex/com.android.hardware.sensors.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service vendor.sensors-hal-2-1-mock /apex/com.android.hardware.sensors/bin/hw/android.hardware.sensors@2.1-service.mock
-    interface android.hardware.sensors@2.0::ISensors default
-    interface android.hardware.sensors@2.1::ISensors default
-    class hal
-    user system
-    group system
-    rlimit rtprio 10 10
diff --git a/sensors/2.1/default/apex/file_contexts b/sensors/2.1/default/apex/file_contexts
deleted file mode 100644
index d0095c0..0000000
--- a/sensors/2.1/default/apex/file_contexts
+++ /dev/null
@@ -1,5 +0,0 @@
-(/.*)?							u:object_r:vendor_file:s0
-# Permission XMLs
-/etc/permissions(/.*)?					u:object_r:vendor_configs_file:s0
-# Service binary
-/bin/hw/android\.hardware\.sensors@2\.1-service\.mock	u:object_r:hal_sensors_default_exec:s0
diff --git a/sensors/2.1/multihal/android.hardware.sensors@2.1-service-multihal.rc b/sensors/2.1/multihal/android.hardware.sensors@2.1-service-multihal.rc
index deea16e..e149058 100644
--- a/sensors/2.1/multihal/android.hardware.sensors@2.1-service-multihal.rc
+++ b/sensors/2.1/multihal/android.hardware.sensors@2.1-service-multihal.rc
@@ -1,7 +1,7 @@
 service vendor.sensors-hal-2-1-multihal /vendor/bin/hw/android.hardware.sensors@2.1-service.multihal
     class hal
     user system
-    group system wakelock context_hub input
+    group system wakelock context_hub input uhid
     task_profiles ServiceCapacityLow
     capabilities BLOCK_SUSPEND
     rlimit rtprio 10 10
diff --git a/sensors/aidl/default/Android.bp b/sensors/aidl/default/Android.bp
index 49841a4..3c66744 100644
--- a/sensors/aidl/default/Android.bp
+++ b/sensors/aidl/default/Android.bp
@@ -23,6 +23,16 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
+filegroup {
+    name: "sensors-default.rc",
+    srcs: ["sensors-default.rc"],
+}
+
+filegroup {
+    name: "sensors-default.xml",
+    srcs: ["sensors-default.xml"],
+}
+
 cc_library_static {
     name: "libsensorsexampleimpl",
     vendor: true,
@@ -47,8 +57,8 @@
 cc_binary {
     name: "android.hardware.sensors-service.example",
     relative_install_path: "hw",
-    init_rc: ["sensors-default.rc"],
-    vintf_fragments: ["sensors-default.xml"],
+    init_rc: [":sensors-default.rc"],
+    vintf_fragments: [":sensors-default.xml"],
     vendor: true,
     shared_libs: [
         "libbase",
diff --git a/sensors/2.1/default/apex/Android.bp b/sensors/aidl/default/apex/Android.bp
similarity index 78%
rename from sensors/2.1/default/apex/Android.bp
rename to sensors/aidl/default/apex/Android.bp
index 3345b92..ceb428b 100644
--- a/sensors/2.1/default/apex/Android.bp
+++ b/sensors/aidl/default/apex/Android.bp
@@ -13,9 +13,16 @@
     certificate: "com.android.hardware.sensors",
 }
 
+genrule {
+    name: "com.android.hardware.sensors.rc-gen",
+    srcs: [":sensors-default.rc"],
+    out: ["com.android.hardware.sensors.rc"],
+    cmd: "sed -E 's/\\/vendor/\\/apex\\/com.android.hardware.sensors/' $(in) > $(out)",
+}
+
 prebuilt_etc {
     name: "com.android.hardware.sensors.rc",
-    src: "com.android.hardware.sensors.rc",
+    src: ":com.android.hardware.sensors.rc-gen",
     installable: false,
 }
 
@@ -31,7 +38,7 @@
     updatable: false,
     // Install the apex in /vendor/apex
     soc_specific: true,
-    binaries: ["android.hardware.sensors@2.1-service.mock"],
+    binaries: ["android.hardware.sensors-service.example"],
     prebuilts: [
         "com.android.hardware.sensors.rc",
         "android.hardware.sensor.ambient_temperature.prebuilt.xml",
@@ -42,5 +49,5 @@
         "android.hardware.sensor.proximity.prebuilt.xml",
         "android.hardware.sensor.relative_humidity.prebuilt.xml",
     ],
-    vintf_fragments: [":android.hardware.sensors@2.1.xml"],
+    vintf_fragments: [":sensors-default.xml"],
 }
diff --git a/sensors/aidl/default/apex/apex_manifest.json b/sensors/aidl/default/apex/apex_manifest.json
new file mode 100644
index 0000000..659e739
--- /dev/null
+++ b/sensors/aidl/default/apex/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+    "name": "com.android.hardware.sensors",
+    "version": 1
+}
diff --git a/sensors/2.1/default/apex/com.android.hardware.sensors.avbpubkey b/sensors/aidl/default/apex/com.android.hardware.sensors.avbpubkey
similarity index 100%
rename from sensors/2.1/default/apex/com.android.hardware.sensors.avbpubkey
rename to sensors/aidl/default/apex/com.android.hardware.sensors.avbpubkey
Binary files differ
diff --git a/sensors/2.1/default/apex/com.android.hardware.sensors.pem b/sensors/aidl/default/apex/com.android.hardware.sensors.pem
similarity index 100%
rename from sensors/2.1/default/apex/com.android.hardware.sensors.pem
rename to sensors/aidl/default/apex/com.android.hardware.sensors.pem
diff --git a/sensors/2.1/default/apex/com.android.hardware.sensors.pk8 b/sensors/aidl/default/apex/com.android.hardware.sensors.pk8
similarity index 100%
rename from sensors/2.1/default/apex/com.android.hardware.sensors.pk8
rename to sensors/aidl/default/apex/com.android.hardware.sensors.pk8
Binary files differ
diff --git a/sensors/2.1/default/apex/com.android.hardware.sensors.x509.pem b/sensors/aidl/default/apex/com.android.hardware.sensors.x509.pem
similarity index 100%
rename from sensors/2.1/default/apex/com.android.hardware.sensors.x509.pem
rename to sensors/aidl/default/apex/com.android.hardware.sensors.x509.pem
diff --git a/sensors/aidl/default/apex/file_contexts b/sensors/aidl/default/apex/file_contexts
new file mode 100644
index 0000000..27be16b
--- /dev/null
+++ b/sensors/aidl/default/apex/file_contexts
@@ -0,0 +1,5 @@
+(/.*)?							u:object_r:vendor_file:s0
+# Permission XMLs
+/etc/permissions(/.*)?					u:object_r:vendor_configs_file:s0
+# Service binary
+/bin/hw/android\.hardware\.sensors-service\.example	u:object_r:hal_sensors_default_exec:s0
\ No newline at end of file
diff --git a/tv/tuner/1.1/vts/functional/FilterTests.cpp b/tv/tuner/1.1/vts/functional/FilterTests.cpp
index 8bdf8f6..a24cd63 100644
--- a/tv/tuner/1.1/vts/functional/FilterTests.cpp
+++ b/tv/tuner/1.1/vts/functional/FilterTests.cpp
@@ -311,12 +311,6 @@
             android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilters[filterId]);
     if (filter_v1_1 != NULL) {
         status = filter_v1_1->configureMonitorEvent(monitorEventTypes);
-        if (monitorEventTypes & DemuxFilterMonitorEventType::SCRAMBLING_STATUS) {
-            mFilterCallbacks[filterId]->testFilterScramblingEvent();
-        }
-        if (monitorEventTypes & DemuxFilterMonitorEventType::IP_CID_CHANGE) {
-            mFilterCallbacks[filterId]->testFilterIpCidEvent();
-        }
     } else {
         ALOGW("[vts] Can't cast IFilter into v1_1.");
         return failure();
@@ -324,6 +318,17 @@
     return AssertionResult(status == Result::SUCCESS);
 }
 
+AssertionResult FilterTests::testMonitorEvent(uint64_t filterId, uint32_t monitorEventTypes) {
+    EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first.";
+    if (monitorEventTypes & DemuxFilterMonitorEventType::SCRAMBLING_STATUS) {
+        mFilterCallbacks[filterId]->testFilterScramblingEvent();
+    }
+    if (monitorEventTypes & DemuxFilterMonitorEventType::IP_CID_CHANGE) {
+        mFilterCallbacks[filterId]->testFilterIpCidEvent();
+    }
+    return AssertionResult(true);
+}
+
 AssertionResult FilterTests::startIdTest(uint64_t filterId) {
     EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first.";
     mFilterCallbacks[filterId]->testStartIdAfterReconfigure();
diff --git a/tv/tuner/1.1/vts/functional/FilterTests.h b/tv/tuner/1.1/vts/functional/FilterTests.h
index 1a1273e..e652154 100644
--- a/tv/tuner/1.1/vts/functional/FilterTests.h
+++ b/tv/tuner/1.1/vts/functional/FilterTests.h
@@ -159,6 +159,7 @@
     AssertionResult configAvFilterStreamType(AvStreamType type, uint64_t filterId);
     AssertionResult configIpFilterCid(uint32_t ipCid, uint64_t filterId);
     AssertionResult configureMonitorEvent(uint64_t filterId, uint32_t monitorEventTypes);
+    AssertionResult testMonitorEvent(uint64_t filterId, uint32_t monitorEventTypes);
     AssertionResult getFilterMQDescriptor(uint64_t filterId, bool getMqDesc);
     AssertionResult startFilter(uint64_t filterId);
     AssertionResult stopFilter(uint64_t filterId);
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
index 41acaa1..fccd2ed 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
@@ -48,6 +48,11 @@
     }
     ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.config1_0.getMqDesc));
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
+    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+    if (filterConf.monitorEventTypes > 0) {
+        ASSERT_TRUE(mFilterTests.testMonitorEvent(filterId, filterConf.monitorEventTypes));
+    }
+    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
     ASSERT_TRUE(mDemuxTests.closeDemux());
diff --git a/usb/1.1/vts/functional/VtsHalUsbV1_1TargetTest.cpp b/usb/1.1/vts/functional/VtsHalUsbV1_1TargetTest.cpp
index 19830a6..0883de2 100644
--- a/usb/1.1/vts/functional/VtsHalUsbV1_1TargetTest.cpp
+++ b/usb/1.1/vts/functional/VtsHalUsbV1_1TargetTest.cpp
@@ -95,6 +95,7 @@
                                             Status retval) override {
         UsbClientCallbackArgs arg;
         if (retval == Status::SUCCESS) {
+            arg.usb_last_port_status.status.portName = currentPortStatus[0].status.portName.c_str();
             arg.usb_last_port_status.status.supportedModes =
                 currentPortStatus[0].status.supportedModes;
             arg.usb_last_port_status.status.currentMode = currentPortStatus[0].status.currentMode;
@@ -165,9 +166,12 @@
     auto res = usb_cb_2->WaitForCallback(kCallbackNameNotifyPortStatusChange_1_1);
     EXPECT_TRUE(res.no_timeout);
     EXPECT_EQ(2, res.args->last_usb_cookie);
-    EXPECT_EQ(PortMode::NONE, res.args->usb_last_port_status.status.currentMode);
-    EXPECT_EQ(PortMode::NONE, res.args->usb_last_port_status.status.supportedModes);
-    EXPECT_EQ(Status::SUCCESS, res.args->usb_last_status);
+    // if there are no type-c ports, skip below checks
+    if (!res.args->usb_last_port_status.status.portName.empty()) {
+        EXPECT_EQ(PortMode::NONE, res.args->usb_last_port_status.status.currentMode);
+        EXPECT_EQ(PortMode::NONE, res.args->usb_last_port_status.status.supportedModes);
+        EXPECT_EQ(Status::SUCCESS, res.args->usb_last_status);
+    }
 }
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UsbHidlTest);
 INSTANTIATE_TEST_SUITE_P(
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
index 25d704e..ff9c247 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
@@ -34,23 +34,24 @@
 package android.hardware.uwb.fira_android;
 @Backing(type="int") @VintfStability
 enum UwbVendorCapabilityTlvTypes {
-  SUPPORTED_POWER_STATS_QUERY = 192,
-  CCC_SUPPORTED_CHAPS_PER_SLOT = 160,
-  CCC_SUPPORTED_SYNC_CODES = 161,
-  CCC_SUPPORTED_HOPPING_CONFIG_MODES_AND_SEQUENCES = 162,
-  CCC_SUPPORTED_CHANNELS = 163,
-  CCC_SUPPORTED_VERSIONS = 164,
-  CCC_SUPPORTED_UWB_CONFIGS = 165,
-  CCC_SUPPORTED_PULSE_SHAPE_COMBOS = 166,
-  CCC_SUPPORTED_RAN_MULTIPLIER = 167,
-  CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER = 168,
-  CCC_SUPPORTED_MIN_UWB_INITIATION_TIME_MS = 169,
-  SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 227,
-  SUPPORTED_MIN_RANGING_INTERVAL_MS = 228,
-  SUPPORTED_RANGE_DATA_NTF_CONFIG = 229,
-  SUPPORTED_RSSI_REPORTING = 230,
-  SUPPORTED_DIAGNOSTICS = 231,
-  SUPPORTED_MIN_SLOT_DURATION_RSTU = 232,
-  SUPPORTED_MAX_RANGING_SESSION_NUMBER = 233,
-  SUPPORTED_CHANNELS_AOA = 234,
+  SUPPORTED_POWER_STATS_QUERY = 0xC0,
+  CCC_SUPPORTED_CHAPS_PER_SLOT = 0xA0,
+  CCC_SUPPORTED_SYNC_CODES = 0xA1,
+  CCC_SUPPORTED_HOPPING_CONFIG_MODES_AND_SEQUENCES = 0xA2,
+  CCC_SUPPORTED_CHANNELS = 0xA3,
+  CCC_SUPPORTED_VERSIONS = 0xA4,
+  CCC_SUPPORTED_UWB_CONFIGS = 0xA5,
+  CCC_SUPPORTED_PULSE_SHAPE_COMBOS = 0xA6,
+  CCC_SUPPORTED_RAN_MULTIPLIER = 0xA7,
+  CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER = 0xA8,
+  CCC_SUPPORTED_MIN_UWB_INITIATION_TIME_MS = 0xA9,
+  RADAR_SUPPORT = 0xB0,
+  SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 0xE3,
+  SUPPORTED_MIN_RANGING_INTERVAL_MS = 0xE4,
+  SUPPORTED_RANGE_DATA_NTF_CONFIG = 0xE5,
+  SUPPORTED_RSSI_REPORTING = 0xE6,
+  SUPPORTED_DIAGNOSTICS = 0xE7,
+  SUPPORTED_MIN_SLOT_DURATION_RSTU = 0xE8,
+  SUPPORTED_MAX_RANGING_SESSION_NUMBER = 0xE9,
+  SUPPORTED_CHANNELS_AOA = 0xEA,
 }
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvValues.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvValues.aidl
index 0e33f70..702e561 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvValues.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvValues.aidl
@@ -40,17 +40,19 @@
   PULSE_SHAPE_PRECURSOR_FREE = 1,
   PULSE_SHAPE_PRECURSOR_FREE_SPECIAL = 2,
   CHAPS_PER_SLOT_3 = 1,
-  CHAPS_PER_SLOT_4 = 2,
-  CHAPS_PER_SLOT_6 = 4,
-  CHAPS_PER_SLOT_8 = 8,
-  CHAPS_PER_SLOT_9 = 16,
-  CHAPS_PER_SLOT_12 = 32,
-  CHAPS_PER_SLOT_24 = 64,
-  HOPPING_SEQUENCE_DEFAULT = 16,
-  HOPPING_SEQUENCE_AES = 8,
-  HOPPING_CONFIG_MODE_NONE = 128,
-  HOPPING_CONFIG_MODE_CONTINUOUS = 64,
-  HOPPING_CONFIG_MODE_ADAPTIVE = 32,
+  CHAPS_PER_SLOT_4 = (1 << 1) /* 2 */,
+  CHAPS_PER_SLOT_6 = (1 << 2) /* 4 */,
+  CHAPS_PER_SLOT_8 = (1 << 3) /* 8 */,
+  CHAPS_PER_SLOT_9 = (1 << 4) /* 16 */,
+  CHAPS_PER_SLOT_12 = (1 << 5) /* 32 */,
+  CHAPS_PER_SLOT_24 = (1 << 6) /* 64 */,
+  HOPPING_SEQUENCE_DEFAULT = (1 << 4) /* 16 */,
+  HOPPING_SEQUENCE_AES = (1 << 3) /* 8 */,
+  HOPPING_CONFIG_MODE_NONE = (1 << 7) /* 128 */,
+  HOPPING_CONFIG_MODE_CONTINUOUS = (1 << 6) /* 64 */,
+  HOPPING_CONFIG_MODE_ADAPTIVE = (1 << 5) /* 32 */,
   CCC_CHANNEL_5 = 1,
-  CCC_CHANNEL_9 = 2,
+  CCC_CHANNEL_9 = (1 << 1) /* 2 */,
+  RADAR_NOT_SUPPORTED = 0,
+  RADAR_SWEEP_SAMPLES_SUPPORTED = 1,
 }
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
index fbcfbff..34bc4ec 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
@@ -34,7 +34,10 @@
 package android.hardware.uwb.fira_android;
 @Backing(type="byte") @VintfStability
 enum UwbVendorGidAndroidOids {
-  ANDROID_GET_POWER_STATS = 0,
-  ANDROID_SET_COUNTRY_CODE = 1,
-  ANDROID_RANGE_DIAGNOSTICS = 2,
+  ANDROID_GET_POWER_STATS = 0x0,
+  ANDROID_SET_COUNTRY_CODE = 0x1,
+  ANDROID_RANGE_DIAGNOSTICS = 0x2,
+  RADAR_SET_APP_CONFIG = 0x11,
+  RADAR_GET_APP_CONFIG = 0x12,
+  RADAR_DATA_NTF = 0x13,
 }
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGids.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGids.aidl
index 5515c67..f02ed70 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGids.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGids.aidl
@@ -34,5 +34,5 @@
 package android.hardware.uwb.fira_android;
 @Backing(type="byte") @VintfStability
 enum UwbVendorGids {
-  ANDROID = 12,
+  ANDROID = 0xC,
 }
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorRadarAppConfigTlvTypes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorRadarAppConfigTlvTypes.aidl
new file mode 100644
index 0000000..760166c
--- /dev/null
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorRadarAppConfigTlvTypes.aidl
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.uwb.fira_android;
+@Backing(type="int") @VintfStability
+enum UwbVendorRadarAppConfigTlvTypes {
+  RADAR_TIMING_PARAMS = 0x0,
+  SAMPLES_PER_SWEEP = 0x1,
+  RADAR_CHANNEL_NUMBER = 0x2,
+  SWEEP_OFFSET = 0x3,
+  RADAR_RFRAME_CONFIG = 0x4,
+  RADAR_PREAMBLE_DURATION = 0x5,
+  RADAR_PREAMBLE_CODE_INDEX = 0x6,
+  RADAR_SESSION_PRIORITY = 0x7,
+  BITS_PER_SAMPLE = 0x8,
+  RADAR_PRF_MODE = 0x9,
+  NUMBER_OF_BURSTS = 0xA,
+  RADAR_DATA_TYPE = 0xB,
+}
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorRadarAppConfigTlvValues.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorRadarAppConfigTlvValues.aidl
new file mode 100644
index 0000000..1eb2ce9
--- /dev/null
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorRadarAppConfigTlvValues.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.uwb.fira_android;
+@Backing(type="int") @VintfStability
+enum UwbVendorRadarAppConfigTlvValues {
+  RADAR_DATA_TYPE_RADAR_SWEEP_SAMPLES = 0x0,
+}
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorReasonCodes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorReasonCodes.aidl
index a438cbe..db1e0db 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorReasonCodes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorReasonCodes.aidl
@@ -34,7 +34,7 @@
 package android.hardware.uwb.fira_android;
 @Backing(type="int") @VintfStability
 enum UwbVendorReasonCodes {
-  REASON_ERROR_INVALID_CHANNEL_WITH_AOA = 128,
-  REASON_ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT = 129,
-  REASON_REGULATION_UWB_OFF = 130,
+  REASON_ERROR_INVALID_CHANNEL_WITH_AOA = 0x80,
+  REASON_ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT = 0x81,
+  REASON_REGULATION_UWB_OFF = 0x82,
 }
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
index c3ac401..d02cf4d 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
@@ -34,16 +34,16 @@
 package android.hardware.uwb.fira_android;
 @Backing(type="int") @VintfStability
 enum UwbVendorSessionAppConfigTlvTypes {
-  CCC_HOP_MODE_KEY = 160,
-  CCC_UWB_TIME0 = 161,
-  CCC_RANGING_PROTOCOL_VER = 163,
-  CCC_UWB_CONFIG_ID = 164,
-  CCC_PULSESHAPE_COMBO = 165,
-  CCC_URSK_TTL = 166,
-  CCC_LAST_INDEX_USED = 168,
-  NB_OF_RANGE_MEASUREMENTS = 227,
-  NB_OF_AZIMUTH_MEASUREMENTS = 228,
-  NB_OF_ELEVATION_MEASUREMENTS = 229,
-  ENABLE_DIAGNOSTICS = 232,
-  DIAGRAMS_FRAME_REPORTS_FIELDS = 233,
+  CCC_HOP_MODE_KEY = 0xA0,
+  CCC_UWB_TIME0 = 0xA1,
+  CCC_RANGING_PROTOCOL_VER = 0xA3,
+  CCC_UWB_CONFIG_ID = 0xA4,
+  CCC_PULSESHAPE_COMBO = 0xA5,
+  CCC_URSK_TTL = 0xA6,
+  CCC_LAST_INDEX_USED = 0xA8,
+  NB_OF_RANGE_MEASUREMENTS = 0xE3,
+  NB_OF_AZIMUTH_MEASUREMENTS = 0xE4,
+  NB_OF_ELEVATION_MEASUREMENTS = 0xE5,
+  ENABLE_DIAGNOSTICS = 0xE8,
+  DIAGRAMS_FRAME_REPORTS_FIELDS = 0xE9,
 }
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvValues.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvValues.aidl
index a7f487b..5216e1f 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvValues.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvValues.aidl
@@ -34,5 +34,5 @@
 package android.hardware.uwb.fira_android;
 @Backing(type="int") @VintfStability
 enum UwbVendorSessionAppConfigTlvValues {
-  AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 240,
+  AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 0xF0,
 }
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionInitSessionType.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionInitSessionType.aidl
index 30a0a1b..bf968bd 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionInitSessionType.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionInitSessionType.aidl
@@ -34,5 +34,6 @@
 package android.hardware.uwb.fira_android;
 @Backing(type="int") @VintfStability
 enum UwbVendorSessionInitSessionType {
-  CCC = 160,
+  CCC = 0xA0,
+  RADAR = 0xA1,
 }
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorStatusCodes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorStatusCodes.aidl
index 28cf7fe..52f1350 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorStatusCodes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorStatusCodes.aidl
@@ -34,8 +34,8 @@
 package android.hardware.uwb.fira_android;
 @Backing(type="byte") @VintfStability
 enum UwbVendorStatusCodes {
-  STATUS_ERROR_CCC_SE_BUSY = 80,
-  STATUS_ERROR_CCC_LIFECYCLE = 81,
-  STATUS_ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT = 82,
-  STATUS_REGULATION_UWB_OFF = 83,
+  STATUS_ERROR_CCC_SE_BUSY = 0x50,
+  STATUS_ERROR_CCC_LIFECYCLE = 0x51,
+  STATUS_ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT = 0x52,
+  STATUS_REGULATION_UWB_OFF = 0x53,
 }
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/README.md b/uwb/aidl/android/hardware/uwb/fira_android/README.md
index e658d93..7912bbc 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/README.md
+++ b/uwb/aidl/android/hardware/uwb/fira_android/README.md
@@ -10,6 +10,3 @@
 All other interactions sent/received over the HAL interface is expected to
 comply with the UCI specification that can be found [here](
 https://groups.firaconsortium.org/wg/Technical/document/folder/127).
-
-TODO([b/196004116](b/196004116)): Link to the published specification.
-
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
index 22b7bfe..ceef1be 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
@@ -150,6 +150,16 @@
     CCC_SUPPORTED_MIN_UWB_INITIATION_TIME_MS = 0xA9,
 
     /*********************************************
+     * RADAR specific
+     ********************************************/
+    /**
+     * 1 byte bitmask to indicate the supported Radar data types.
+     * Each "1" in this bitmap corresponds to a specific radar data type where:
+     * 0x01 = "Radar Sweep Samples",
+     */
+    RADAR_SUPPORT = 0xB0,
+
+    /*********************************************
      * FIRA specific
      ********************************************/
     /**
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvValues.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvValues.aidl
index 7c86b79..6ef52fe 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvValues.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvValues.aidl
@@ -51,4 +51,10 @@
 
     CCC_CHANNEL_5 = 1,
     CCC_CHANNEL_9 = 1 << 1,
+
+    /*********************************************
+     * RADAR specific
+     ********************************************/
+    RADAR_NOT_SUPPORTED = 0,
+    RADAR_SWEEP_SAMPLES_SUPPORTED = 1,
 }
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
index 4768f55..203b940 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
@@ -37,4 +37,15 @@
     // Supported only if the UwbVendorCapabilityTlvTypes.SUPPORTED_DIAGNOSTICS set
     // to 1.
     ANDROID_RANGE_DIAGNOSTICS = 0x2,
+
+    /*********************************************
+     * Range 0x10 - 0x1F reserved for RADAR specific
+     * Supported only if the UwbVendorCapabilityTlvTypes.RADAR_SUPPORT is not 0x00.
+     ********************************************/
+    // Used to set application configurations for radar session.
+    RADAR_SET_APP_CONFIG = 0x11,
+    // Used to get application configurations for radar session.
+    RADAR_GET_APP_CONFIG = 0x12,
+    // Used to report radar data for certain radar data types.
+    RADAR_DATA_NTF = 0x13,
 }
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorRadarAppConfigTlvTypes.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorRadarAppConfigTlvTypes.aidl
new file mode 100644
index 0000000..a5ea688
--- /dev/null
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorRadarAppConfigTlvTypes.aidl
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+package android.hardware.uwb.fira_android;
+
+/**
+ * Android specific radar app params set/expected in UCI command:
+ * GID: 1100b (Android specific Group)
+ * OID: 010001b (RADAR_SET_APP_CONFIG_CMD)
+ * OID: 010010b (RADAR_GET_APP_CONFIG_CMD)
+ */
+@VintfStability
+@Backing(type="int")
+enum UwbVendorRadarAppConfigTlvTypes {
+    /**
+     * 7 byte data
+     * Radar frame timing parameters:
+     * Octet [3:0] - BURST_PERIOD
+     *   Duration between the start of two consecutive Radar bursts in ms.
+     * Octet [5:4] - SWEEP_PERIOD
+     *   Duration between the start times of two consecutive Radar sweeps in
+     *   RSTU.
+     * Octet [6] - SWEEPS_PER_BURST
+     *   Number of Radar sweeps within the Radar burst.
+     */
+    RADAR_TIMING_PARAMS = 0x0,
+    /**
+     * 1 byte data
+     * The number of samples captured for each radar sweep. (default = 64)
+     */
+    SAMPLES_PER_SWEEP = 0x1,
+    /**
+     * 1 byte data
+     * Same as in FiRa UCI Session App Config.
+     * (default = 9)
+     */
+    RADAR_CHANNEL_NUMBER = 0x2,
+    /**
+     * 2 byte data
+     * Defines the start offset with respect to 0cm distance to limit the sweep
+     * range. Signed value and unit in samples.
+     * (default = 0)
+     */
+    SWEEP_OFFSET = 0x3,
+    /**
+     * 1 byte data
+     * Same as in FiRa UCI Session App Config.
+     * (default = 0x0)
+     */
+    RADAR_RFRAME_CONFIG = 0x4,
+    /**
+     * 1 byte data
+     * Same as in FiRa UCI Session App Config, but extended to 0xA.
+     * (default = 0x2 : 128 symbols)
+     */
+    RADAR_PREAMBLE_DURATION = 0x5,
+    /**
+     * 1 byte data
+     * Same as in FiRa UCI Session App Config, but extended to 127.
+     * (default = 25)
+     */
+    RADAR_PREAMBLE_CODE_INDEX = 0x6,
+    /**
+     * 1 byte data
+     * Same as in FiRa UCI Session App Config.
+     * (default = 50)
+     */
+    RADAR_SESSION_PRIORITY = 0x7,
+    /**
+     * 1 byte data
+     * Bits per sample in the radar sweep.
+     * 0x00 = 32 bits per sample (default)
+     * 0x01 = 48 bits per sample
+     * 0x02 = 64 bits per sample
+     */
+    BITS_PER_SAMPLE = 0x8,
+    /**
+     * 1 byte data
+     * Same as in FiRa UCI Session App Config.
+     * (default = 0x1)
+     */
+    RADAR_PRF_MODE = 0x9,
+    /**
+     * 2 byte data
+     * Maximum number of Radar bursts to be executed in the session. The
+     * session is stopped and moved to SESSION_STATE_IDLE Session State when
+     * configured radar bursts are elapsed.
+     * 0x00 = Unlimited (default)
+     */
+    NUMBER_OF_BURSTS = 0xA,
+    /**
+     * 2 byte data
+     * Type of radar data to be reported.
+     * 0x00: Radar Sweep Samples. Reported in RADAR_DATA_NTF. (default)
+     */
+    RADAR_DATA_TYPE = 0xB,
+}
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorRadarAppConfigTlvValues.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorRadarAppConfigTlvValues.aidl
new file mode 100644
index 0000000..81c0a4d
--- /dev/null
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorRadarAppConfigTlvValues.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.hardware.uwb.fira_android;
+
+/**
+ * Android specific radar app config values set/expected in UCI command:
+ * GID: 1100b (Android specific Group)
+ * OID: 010001b (RADAR_SET_APP_CONFIG_CMD)
+ * OID: 010010b (RADAR_GET_APP_CONFIG_CMD)
+ */
+@VintfStability
+@Backing(type="int")
+enum UwbVendorRadarAppConfigTlvValues {
+    RADAR_DATA_TYPE_RADAR_SWEEP_SAMPLES = 0x0,
+}
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionInitSessionType.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionInitSessionType.aidl
index 1e2c817..d3df672 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionInitSessionType.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionInitSessionType.aidl
@@ -29,4 +29,5 @@
 enum UwbVendorSessionInitSessionType {
     /** Added in vendor version 0. */
     CCC = 0xA0,
+    RADAR = 0xA1,
 }