aosp aidl bluetooth audio hal implementation

Bug: 228804498
Test: atest VtsHalAudioCoreTargetTest
Change-Id: Id5fb60fe53ead9f5d2e6ecbb9988a23835cf2509
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 4e583a4..b6cfc13 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -82,6 +82,9 @@
         "alsa/ModuleAlsa.cpp",
         "alsa/StreamAlsa.cpp",
         "alsa/Utils.cpp",
+        "bluetooth/DevicePortProxy.cpp",
+        "bluetooth/ModuleBluetooth.cpp",
+        "bluetooth/StreamBluetooth.cpp",
         "r_submix/ModuleRemoteSubmix.cpp",
         "r_submix/RemoteSubmixUtils.cpp",
         "r_submix/SubmixRoute.cpp",
@@ -105,7 +108,9 @@
         "audio_policy_engine_configuration_aidl_default",
     ],
     shared_libs: [
+        "android.hardware.bluetooth.audio-V3-ndk",
         "libaudio_aidl_conversion_common_ndk",
+        "libbluetooth_audio_session_aidl",
         "libmedia_helper",
         "libstagefright_foundation",
     ],
@@ -136,7 +141,9 @@
         "libaudioserviceexampleimpl",
     ],
     shared_libs: [
+        "android.hardware.bluetooth.audio-V3-ndk",
         "libaudio_aidl_conversion_common_ndk",
+        "libbluetooth_audio_session_aidl",
         "libmedia_helper",
         "libstagefright_foundation",
     ],
diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp
index a71c6ea..385ffbf 100644
--- a/audio/aidl/default/Configuration.cpp
+++ b/audio/aidl/default/Configuration.cpp
@@ -561,4 +561,86 @@
     return std::make_unique<Configuration>(configuration);
 }
 
+// Bluetooth configuration:
+//
+// Device ports:
+//  * "BT A2DP Out", OUT_DEVICE, CONNECTION_BT_A2DP
+//    - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
+//  * "BT A2DP Headphones", OUT_HEADSET, CONNECTION_BT_A2DP
+//    - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
+//  * "BT A2DP Speaker", OUT_SPEAKER, CONNECTION_BT_A2DP
+//    - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
+//  * "BT Hearing Aid Out", OUT_HEARING_AID, CONNECTION_WIRELESS
+//    - no profiles specified
+//
+// Mix ports:
+//  * "a2dp output", 1 max open, 1 max active stream
+//    - no profiles specified
+//  * "hearing aid output", 1 max open, 1 max active stream
+//    - profile PCM 16-bit; STEREO; 16000, 24000
+//
+// Routes:
+//  "a2dp output" -> "BT A2DP Out"
+//  "a2dp output" -> "BT A2DP Headphones"
+//  "a2dp output" -> "BT A2DP Speaker"
+//  "hearing aid output" -> "BT Hearing Aid Out"
+//
+std::unique_ptr<Configuration> getBluetoothConfiguration() {
+    static const Configuration configuration = []() {
+        const std::vector<AudioProfile> standardPcmAudioProfiles = {
+                createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO},
+                              {44100, 48000, 88200, 96000})};
+        Configuration c;
+
+        // Device ports
+        AudioPort btOutDevice =
+                createPort(c.nextPortId++, "BT A2DP Out", 0, false,
+                           createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
+                                           AudioDeviceDescription::CONNECTION_BT_A2DP));
+        c.ports.push_back(btOutDevice);
+        c.connectedProfiles[btOutDevice.id] = standardPcmAudioProfiles;
+
+        AudioPort btOutHeadphone =
+                createPort(c.nextPortId++, "BT A2DP Headphones", 0, false,
+                           createDeviceExt(AudioDeviceType::OUT_HEADSET, 0,
+                                           AudioDeviceDescription::CONNECTION_BT_A2DP));
+        c.ports.push_back(btOutHeadphone);
+        c.connectedProfiles[btOutHeadphone.id] = standardPcmAudioProfiles;
+
+        AudioPort btOutSpeaker =
+                createPort(c.nextPortId++, "BT A2DP Speaker", 0, false,
+                           createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0,
+                                           AudioDeviceDescription::CONNECTION_BT_A2DP));
+        c.ports.push_back(btOutSpeaker);
+        c.connectedProfiles[btOutSpeaker.id] = standardPcmAudioProfiles;
+
+        AudioPort btOutHearingAid =
+                createPort(c.nextPortId++, "BT Hearing Aid Out", 0, false,
+                           createDeviceExt(AudioDeviceType::OUT_HEARING_AID, 0,
+                                           AudioDeviceDescription::CONNECTION_WIRELESS));
+        c.ports.push_back(btOutHearingAid);
+        c.connectedProfiles[btOutHearingAid.id] = std::vector<AudioProfile>(
+                {createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000})});
+
+        // Mix ports
+        AudioPort btInMix =
+                createPort(c.nextPortId++, "a2dp output", 0, true, createPortMixExt(1, 1));
+        c.ports.push_back(btInMix);
+
+        AudioPort btHeadsetInMix =
+                createPort(c.nextPortId++, "hearing aid output", 0, true, createPortMixExt(1, 1));
+        btHeadsetInMix.profiles.push_back(createProfile(
+                PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000, 24000}));
+        c.ports.push_back(btHeadsetInMix);
+
+        c.routes.push_back(createRoute({btInMix}, btOutDevice));
+        c.routes.push_back(createRoute({btInMix}, btOutHeadphone));
+        c.routes.push_back(createRoute({btInMix}, btOutSpeaker));
+        c.routes.push_back(createRoute({btHeadsetInMix}, btOutHearingAid));
+
+        return c;
+    }();
+    return std::make_unique<Configuration>(configuration);
+}
+
 }  // namespace aidl::android::hardware::audio::core::internal
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index b59bd7c..5625a44 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -26,6 +26,7 @@
 #include <error/expected_utils.h>
 
 #include "core-impl/Module.h"
+#include "core-impl/ModuleBluetooth.h"
 #include "core-impl/ModulePrimary.h"
 #include "core-impl/ModuleRemoteSubmix.h"
 #include "core-impl/ModuleStub.h"
@@ -117,6 +118,8 @@
             return ndk::SharedRefBase::make<ModuleStub>();
         case Type::USB:
             return ndk::SharedRefBase::make<ModuleUsb>();
+        case Type::BLUETOOTH:
+            return ndk::SharedRefBase::make<ModuleBluetooth>();
     }
 }
 
@@ -134,6 +137,9 @@
         case Module::Type::USB:
             os << "usb";
             break;
+        case Module::Type::BLUETOOTH:
+            os << "bluetooth";
+            break;
     }
     return os;
 }
@@ -301,6 +307,9 @@
         case Type::USB:
             config = std::move(internal::getUsbConfiguration());
             break;
+        case Type::BLUETOOTH:
+            config = std::move(internal::getBluetoothConfiguration());
+            break;
     }
     return config;
 }
diff --git a/audio/aidl/default/android.hardware.audio.service-aidl.xml b/audio/aidl/default/android.hardware.audio.service-aidl.xml
index c9d6314..9db6061 100644
--- a/audio/aidl/default/android.hardware.audio.service-aidl.xml
+++ b/audio/aidl/default/android.hardware.audio.service-aidl.xml
@@ -22,6 +22,11 @@
   <hal format="aidl">
     <name>android.hardware.audio.core</name>
     <version>1</version>
+    <fqname>IModule/bluetooth</fqname>
+  </hal>
+  <hal format="aidl">
+    <name>android.hardware.audio.core</name>
+    <version>1</version>
     <fqname>IConfig/default</fqname>
   </hal>
 </manifest>
diff --git a/audio/aidl/default/bluetooth/DevicePortProxy.cpp b/audio/aidl/default/bluetooth/DevicePortProxy.cpp
new file mode 100644
index 0000000..aabf60f
--- /dev/null
+++ b/audio/aidl/default/bluetooth/DevicePortProxy.cpp
@@ -0,0 +1,562 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AHAL_BluetoothPortProxy"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <audio_utils/primitives.h>
+#include <inttypes.h>
+#include <log/log.h>
+
+#include "core-impl/DevicePortProxy.h"
+
+namespace android::bluetooth::audio::aidl {
+
+namespace {
+
+// The maximum time to wait in std::condition_variable::wait_for()
+constexpr unsigned int kMaxWaitingTimeMs = 4500;
+
+}  // namespace
+
+using ::aidl::android::hardware::audio::common::SinkMetadata;
+using ::aidl::android::hardware::audio::common::SourceMetadata;
+using ::aidl::android::hardware::bluetooth::audio::AudioConfiguration;
+using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioSessionControl;
+using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus;
+using ::aidl::android::hardware::bluetooth::audio::ChannelMode;
+using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration;
+using ::aidl::android::hardware::bluetooth::audio::PortStatusCallbacks;
+using ::aidl::android::hardware::bluetooth::audio::PresentationPosition;
+using ::aidl::android::hardware::bluetooth::audio::SessionType;
+using ::aidl::android::media::audio::common::AudioDeviceDescription;
+using ::aidl::android::media::audio::common::AudioDeviceType;
+using ::android::base::StringPrintf;
+
+std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state) {
+    switch (state) {
+        case BluetoothStreamState::DISABLED:
+            return os << "DISABLED";
+        case BluetoothStreamState::STANDBY:
+            return os << "STANDBY";
+        case BluetoothStreamState::STARTING:
+            return os << "STARTING";
+        case BluetoothStreamState::STARTED:
+            return os << "STARTED";
+        case BluetoothStreamState::SUSPENDING:
+            return os << "SUSPENDING";
+        case BluetoothStreamState::UNKNOWN:
+            return os << "UNKNOWN";
+        default:
+            return os << android::base::StringPrintf("%#hhx", state);
+    }
+}
+
+BluetoothAudioPortAidl::BluetoothAudioPortAidl()
+    : mCookie(::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined),
+      mState(BluetoothStreamState::DISABLED),
+      mSessionType(SessionType::UNKNOWN) {}
+
+BluetoothAudioPortAidl::~BluetoothAudioPortAidl() {
+    unregisterPort();
+}
+
+bool BluetoothAudioPortAidl::registerPort(const AudioDeviceDescription& description) {
+    if (inUse()) {
+        LOG(ERROR) << __func__ << debugMessage() << " already in use";
+        return false;
+    }
+
+    if (!initSessionType(description)) return false;
+
+    auto control_result_cb = [port = this](uint16_t cookie, bool start_resp,
+                                           const BluetoothAudioStatus& status) {
+        (void)start_resp;
+        port->controlResultHandler(cookie, status);
+    };
+    auto session_changed_cb = [port = this](uint16_t cookie) {
+        port->sessionChangedHandler(cookie);
+    };
+    // TODO: Add audio_config_changed_cb
+    PortStatusCallbacks cbacks = {
+            .control_result_cb_ = control_result_cb,
+            .session_changed_cb_ = session_changed_cb,
+    };
+    mCookie = BluetoothAudioSessionControl::RegisterControlResultCback(mSessionType, cbacks);
+    auto isOk = (mCookie != ::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined);
+    if (isOk) {
+        std::lock_guard guard(mCvMutex);
+        mState = BluetoothStreamState::STANDBY;
+    }
+    LOG(DEBUG) << __func__ << debugMessage();
+    return isOk;
+}
+
+bool BluetoothAudioPortAidl::initSessionType(const AudioDeviceDescription& description) {
+    if (description.connection == AudioDeviceDescription::CONNECTION_BT_A2DP &&
+        (description.type == AudioDeviceType::OUT_DEVICE ||
+         description.type == AudioDeviceType::OUT_HEADPHONE ||
+         description.type == AudioDeviceType::OUT_SPEAKER)) {
+        LOG(VERBOSE) << __func__
+                     << ": device=AUDIO_DEVICE_OUT_BLUETOOTH_A2DP (HEADPHONES/SPEAKER) ("
+                     << description.toString() << ")";
+        mSessionType = SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
+    } else if (description.connection == AudioDeviceDescription::CONNECTION_WIRELESS &&
+               description.type == AudioDeviceType::OUT_HEARING_AID) {
+        LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_HEARING_AID (MEDIA/VOICE) ("
+                     << description.toString() << ")";
+        mSessionType = SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH;
+    } else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE &&
+               description.type == AudioDeviceType::OUT_HEADSET) {
+        LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_BLE_HEADSET (MEDIA/VOICE) ("
+                     << description.toString() << ")";
+        mSessionType = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
+    } else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE &&
+               description.type == AudioDeviceType::OUT_SPEAKER) {
+        LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_BLE_SPEAKER (MEDIA) ("
+                     << description.toString() << ")";
+        mSessionType = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
+    } else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE &&
+               description.type == AudioDeviceType::IN_HEADSET) {
+        LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_IN_BLE_HEADSET (VOICE) ("
+                     << description.toString() << ")";
+        mSessionType = SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH;
+    } else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE &&
+               description.type == AudioDeviceType::OUT_BROADCAST) {
+        LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_BLE_BROADCAST (MEDIA) ("
+                     << description.toString() << ")";
+        mSessionType = SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH;
+    } else {
+        LOG(ERROR) << __func__ << ": unknown device=" << description.toString();
+        return false;
+    }
+
+    if (!BluetoothAudioSessionControl::IsSessionReady(mSessionType)) {
+        LOG(ERROR) << __func__ << ": device=" << description.toString()
+                   << ", session_type=" << toString(mSessionType) << " is not ready";
+        return false;
+    }
+    return true;
+}
+
+void BluetoothAudioPortAidl::unregisterPort() {
+    if (!inUse()) {
+        LOG(WARNING) << __func__ << ": BluetoothAudioPortAidl is not in use";
+        return;
+    }
+    BluetoothAudioSessionControl::UnregisterControlResultCback(mSessionType, mCookie);
+    mCookie = ::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined;
+    LOG(VERBOSE) << __func__ << debugMessage() << " port unregistered";
+}
+
+void BluetoothAudioPortAidl::controlResultHandler(uint16_t cookie,
+                                                  const BluetoothAudioStatus& status) {
+    std::lock_guard guard(mCvMutex);
+    if (!inUse()) {
+        LOG(ERROR) << "control_result_cb: BluetoothAudioPortAidl is not in use";
+        return;
+    }
+    if (mCookie != cookie) {
+        LOG(ERROR) << "control_result_cb: proxy of device port (cookie="
+                   << StringPrintf("%#hx", cookie) << ") is corrupted";
+        return;
+    }
+    BluetoothStreamState previous_state = mState;
+    LOG(INFO) << "control_result_cb:" << debugMessage() << ", previous_state=" << previous_state
+              << ", status=" << toString(status);
+
+    switch (previous_state) {
+        case BluetoothStreamState::STARTED:
+            /* Only Suspend signal can be send in STARTED state*/
+            if (status == BluetoothAudioStatus::RECONFIGURATION ||
+                status == BluetoothAudioStatus::SUCCESS) {
+                mState = BluetoothStreamState::STANDBY;
+            } else {
+                LOG(WARNING) << StringPrintf(
+                        "control_result_cb: status=%s failure for session_type= %s, cookie=%#hx, "
+                        "previous_state=%#hhx",
+                        toString(status).c_str(), toString(mSessionType).c_str(), mCookie,
+                        previous_state);
+            }
+            break;
+        case BluetoothStreamState::STARTING:
+            if (status == BluetoothAudioStatus::SUCCESS) {
+                mState = BluetoothStreamState::STARTED;
+            } else {
+                // Set to standby since the stack may be busy switching between outputs
+                LOG(WARNING) << StringPrintf(
+                        "control_result_cb: status=%s failure for session_type= %s, cookie=%#hx, "
+                        "previous_state=%#hhx",
+                        toString(status).c_str(), toString(mSessionType).c_str(), mCookie,
+                        previous_state);
+                mState = BluetoothStreamState::STANDBY;
+            }
+            break;
+        case BluetoothStreamState::SUSPENDING:
+            if (status == BluetoothAudioStatus::SUCCESS) {
+                mState = BluetoothStreamState::STANDBY;
+            } else {
+                // It will be failed if the headset is disconnecting, and set to disable
+                // to wait for re-init again
+                LOG(WARNING) << StringPrintf(
+                        "control_result_cb: status=%s failure for session_type= %s, cookie=%#hx, "
+                        "previous_state=%#hhx",
+                        toString(status).c_str(), toString(mSessionType).c_str(), mCookie,
+                        previous_state);
+                mState = BluetoothStreamState::DISABLED;
+            }
+            break;
+        default:
+            LOG(ERROR) << "control_result_cb: unexpected previous_state="
+                       << StringPrintf(
+                                  "control_result_cb: status=%s failure for session_type= %s, "
+                                  "cookie=%#hx, previous_state=%#hhx",
+                                  toString(status).c_str(), toString(mSessionType).c_str(), mCookie,
+                                  previous_state);
+            return;
+    }
+    mInternalCv.notify_all();
+}
+
+void BluetoothAudioPortAidl::sessionChangedHandler(uint16_t cookie) {
+    std::lock_guard guard(mCvMutex);
+    if (!inUse()) {
+        LOG(ERROR) << "session_changed_cb: BluetoothAudioPortAidl is not in use";
+        return;
+    }
+    if (mCookie != cookie) {
+        LOG(ERROR) << "session_changed_cb: proxy of device port (cookie="
+                   << StringPrintf("%#hx", cookie) << ") is corrupted";
+        return;
+    }
+    BluetoothStreamState previous_state = mState;
+    LOG(VERBOSE) << "session_changed_cb:" << debugMessage()
+                 << ", previous_state=" << previous_state;
+    mState = BluetoothStreamState::DISABLED;
+    mInternalCv.notify_all();
+}
+
+bool BluetoothAudioPortAidl::inUse() const {
+    return (mCookie != ::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined);
+}
+
+bool BluetoothAudioPortAidl::getPreferredDataIntervalUs(size_t* interval_us) const {
+    if (!interval_us) {
+        LOG(ERROR) << __func__ << ": bad input arg";
+        return false;
+    }
+
+    if (!inUse()) {
+        LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
+        return false;
+    }
+
+    const AudioConfiguration& hal_audio_cfg =
+            BluetoothAudioSessionControl::GetAudioConfig(mSessionType);
+    if (hal_audio_cfg.getTag() != AudioConfiguration::pcmConfig) {
+        LOG(ERROR) << __func__ << ": unsupported audio cfg tag";
+        return false;
+    }
+
+    *interval_us = hal_audio_cfg.get<AudioConfiguration::pcmConfig>().dataIntervalUs;
+    return true;
+}
+
+bool BluetoothAudioPortAidl::loadAudioConfig(PcmConfiguration* audio_cfg) const {
+    if (!audio_cfg) {
+        LOG(ERROR) << __func__ << ": bad input arg";
+        return false;
+    }
+
+    if (!inUse()) {
+        LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
+        return false;
+    }
+
+    const AudioConfiguration& hal_audio_cfg =
+            BluetoothAudioSessionControl::GetAudioConfig(mSessionType);
+    if (hal_audio_cfg.getTag() != AudioConfiguration::pcmConfig) {
+        LOG(ERROR) << __func__ << ": unsupported audio cfg tag";
+        return false;
+    }
+    *audio_cfg = hal_audio_cfg.get<AudioConfiguration::pcmConfig>();
+    LOG(VERBOSE) << __func__ << debugMessage() << ", state*=" << getState() << ", PcmConfig=["
+                 << audio_cfg->toString() << "]";
+    if (audio_cfg->channelMode == ChannelMode::UNKNOWN) {
+        return false;
+    }
+    return true;
+}
+
+bool BluetoothAudioPortAidl::condWaitState(BluetoothStreamState state) {
+    const auto waitTime = std::chrono::milliseconds(kMaxWaitingTimeMs);
+    std::unique_lock lock(mCvMutex);
+    base::ScopedLockAssertion lock_assertion(mCvMutex);
+    switch (state) {
+        case BluetoothStreamState::STARTING: {
+            LOG(VERBOSE) << __func__ << debugMessage() << " waiting for STARTED";
+            mInternalCv.wait_for(lock, waitTime, [this] {
+                base::ScopedLockAssertion lock_assertion(mCvMutex);
+                return mState != BluetoothStreamState::STARTING;
+            });
+            return mState == BluetoothStreamState::STARTED;
+        }
+        case BluetoothStreamState::SUSPENDING: {
+            LOG(VERBOSE) << __func__ << debugMessage() << " waiting for SUSPENDED";
+            mInternalCv.wait_for(lock, waitTime, [this] {
+                base::ScopedLockAssertion lock_assertion(mCvMutex);
+                return mState != BluetoothStreamState::SUSPENDING;
+            });
+            return mState == BluetoothStreamState::STANDBY;
+        }
+        default:
+            LOG(WARNING) << __func__ << debugMessage() << " waiting for KNOWN";
+            return false;
+    }
+    return false;
+}
+
+bool BluetoothAudioPortAidl::start() {
+    if (!inUse()) {
+        LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
+        return false;
+    }
+    LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState()
+                 << ", mono=" << (mIsStereoToMono ? "true" : "false") << " request";
+
+    {
+        std::unique_lock lock(mCvMutex);
+        base::ScopedLockAssertion lock_assertion(mCvMutex);
+        if (mState == BluetoothStreamState::STARTED) {
+            return true;  // nop, return
+        } else if (mState == BluetoothStreamState::SUSPENDING ||
+                   mState == BluetoothStreamState::STARTING) {
+            /* If port is in transient state, give some time to respond */
+            auto state_ = mState;
+            lock.unlock();
+            if (!condWaitState(state_)) {
+                LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() << " failure";
+                return false;
+            }
+        }
+    }
+
+    bool retval = false;
+    {
+        std::unique_lock lock(mCvMutex);
+        base::ScopedLockAssertion lock_assertion(mCvMutex);
+        if (mState == BluetoothStreamState::STARTED) {
+            retval = true;
+        } else if (mState == BluetoothStreamState::STANDBY) {
+            mState = BluetoothStreamState::STARTING;
+            lock.unlock();
+            if (BluetoothAudioSessionControl::StartStream(mSessionType)) {
+                retval = condWaitState(BluetoothStreamState::STARTING);
+            } else {
+                LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState()
+                           << " Hal fails";
+            }
+        }
+    }
+
+    if (retval) {
+        LOG(INFO) << __func__ << debugMessage() << ", state=" << getState()
+                  << ", mono=" << (mIsStereoToMono ? "true" : "false") << " done";
+    } else {
+        LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() << " failure";
+    }
+
+    return retval;  // false if any failure like timeout
+}
+
+bool BluetoothAudioPortAidl::suspend() {
+    if (!inUse()) {
+        LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
+        return false;
+    }
+    LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState() << " request";
+
+    {
+        std::unique_lock lock(mCvMutex);
+        base::ScopedLockAssertion lock_assertion(mCvMutex);
+        if (mState == BluetoothStreamState::STANDBY) {
+            return true;  // nop, return
+        } else if (mState == BluetoothStreamState::SUSPENDING ||
+                   mState == BluetoothStreamState::STARTING) {
+            /* If port is in transient state, give some time to respond */
+            auto state_ = mState;
+            lock.unlock();
+            if (!condWaitState(state_)) {
+                LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() << " failure";
+                return false;
+            }
+        }
+    }
+
+    bool retval = false;
+    {
+        std::unique_lock lock(mCvMutex);
+        base::ScopedLockAssertion lock_assertion(mCvMutex);
+        if (mState == BluetoothStreamState::STANDBY) {
+            retval = true;
+        } else if (mState == BluetoothStreamState::STARTED) {
+            mState = BluetoothStreamState::SUSPENDING;
+            lock.unlock();
+            if (BluetoothAudioSessionControl::SuspendStream(mSessionType)) {
+                retval = condWaitState(BluetoothStreamState::SUSPENDING);
+            } else {
+                LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState()
+                           << " Hal fails";
+            }
+        }
+    }
+
+    if (retval) {
+        LOG(INFO) << __func__ << debugMessage() << ", state=" << getState() << " done";
+    } else {
+        LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() << " failure";
+    }
+
+    return retval;  // false if any failure like timeout
+}
+
+void BluetoothAudioPortAidl::stop() {
+    if (!inUse()) {
+        LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
+        return;
+    }
+    std::lock_guard guard(mCvMutex);
+    LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState() << " request";
+    if (mState != BluetoothStreamState::DISABLED) {
+        BluetoothAudioSessionControl::StopStream(mSessionType);
+        mState = BluetoothStreamState::DISABLED;
+    }
+    LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState() << " done";
+}
+
+size_t BluetoothAudioPortAidlOut::writeData(const void* buffer, size_t bytes) const {
+    if (!buffer) {
+        LOG(ERROR) << __func__ << ": bad input arg";
+        return 0;
+    }
+
+    if (!inUse()) {
+        LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
+        return 0;
+    }
+
+    if (!mIsStereoToMono) {
+        return BluetoothAudioSessionControl::OutWritePcmData(mSessionType, buffer, bytes);
+    }
+
+    // WAR to mix the stereo into Mono (16 bits per sample)
+    const size_t write_frames = bytes >> 2;
+    if (write_frames == 0) return 0;
+    auto src = static_cast<const int16_t*>(buffer);
+    std::unique_ptr<int16_t[]> dst{new int16_t[write_frames]};
+    downmix_to_mono_i16_from_stereo_i16(dst.get(), src, write_frames);
+    // a frame is 16 bits, and the size of a mono frame is equal to half a stereo.
+    auto totalWrite = BluetoothAudioSessionControl::OutWritePcmData(mSessionType, dst.get(),
+                                                                    write_frames * 2);
+    return totalWrite * 2;
+}
+
+size_t BluetoothAudioPortAidlIn::readData(void* buffer, size_t bytes) const {
+    if (!buffer) {
+        LOG(ERROR) << __func__ << ": bad input arg";
+        return 0;
+    }
+
+    if (!inUse()) {
+        LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
+        return 0;
+    }
+
+    return BluetoothAudioSessionControl::InReadPcmData(mSessionType, buffer, bytes);
+}
+
+bool BluetoothAudioPortAidl::getPresentationPosition(
+        PresentationPosition& presentation_position) const {
+    if (!inUse()) {
+        LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
+        return false;
+    }
+    bool retval = BluetoothAudioSessionControl::GetPresentationPosition(mSessionType,
+                                                                        presentation_position);
+    LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState()
+                 << presentation_position.toString();
+
+    return retval;
+}
+
+bool BluetoothAudioPortAidl::updateSourceMetadata(const SourceMetadata& source_metadata) const {
+    if (!inUse()) {
+        LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
+        return false;
+    }
+    LOG(DEBUG) << __func__ << debugMessage() << ", state=" << getState() << ", "
+               << source_metadata.tracks.size() << " track(s)";
+    if (source_metadata.tracks.size() == 0) return true;
+    return BluetoothAudioSessionControl::UpdateSourceMetadata(mSessionType, source_metadata);
+}
+
+bool BluetoothAudioPortAidl::updateSinkMetadata(const SinkMetadata& sink_metadata) const {
+    if (!inUse()) {
+        LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
+        return false;
+    }
+    LOG(DEBUG) << __func__ << debugMessage() << ", state=" << getState() << ", "
+               << sink_metadata.tracks.size() << " track(s)";
+    if (sink_metadata.tracks.size() == 0) return true;
+    return BluetoothAudioSessionControl::UpdateSinkMetadata(mSessionType, sink_metadata);
+}
+
+BluetoothStreamState BluetoothAudioPortAidl::getState() const {
+    return mState;
+}
+
+bool BluetoothAudioPortAidl::setState(BluetoothStreamState state) {
+    if (!inUse()) {
+        LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
+        return false;
+    }
+    std::lock_guard guard(mCvMutex);
+    LOG(DEBUG) << __func__ << ": BluetoothAudioPortAidl old state = " << mState
+               << " new state = " << state;
+    mState = state;
+    return true;
+}
+
+bool BluetoothAudioPortAidl::isA2dp() const {
+    return mSessionType == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+           mSessionType == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
+}
+
+bool BluetoothAudioPortAidl::isLeAudio() const {
+    return mSessionType == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
+           mSessionType == SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH ||
+           mSessionType == SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
+           mSessionType == SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
+           mSessionType == SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH ||
+           mSessionType == SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
+}
+
+std::string BluetoothAudioPortAidl::debugMessage() const {
+    return StringPrintf(": session_type=%s, cookie=%#hx", toString(mSessionType).c_str(), mCookie);
+}
+
+}  // namespace android::bluetooth::audio::aidl
diff --git a/audio/aidl/default/bluetooth/ModuleBluetooth.cpp b/audio/aidl/default/bluetooth/ModuleBluetooth.cpp
new file mode 100644
index 0000000..bb11f0a
--- /dev/null
+++ b/audio/aidl/default/bluetooth/ModuleBluetooth.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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_ModuleBluetooth"
+
+#include <android-base/logging.h>
+
+#include "core-impl/ModuleBluetooth.h"
+#include "core-impl/StreamBluetooth.h"
+
+namespace aidl::android::hardware::audio::core {
+
+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::MicrophoneInfo;
+
+ndk::ScopedAStatus ModuleBluetooth::getMicMute(bool* _aidl_return __unused) {
+    LOG(DEBUG) << __func__ << ": is not supported";
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus ModuleBluetooth::setMicMute(bool in_mute __unused) {
+    LOG(DEBUG) << __func__ << ": is not supported";
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus ModuleBluetooth::createInputStream(
+        StreamContext&& context, const SinkMetadata& sinkMetadata,
+        const std::vector<MicrophoneInfo>& microphones, std::shared_ptr<StreamIn>* result) {
+    return createStreamInstance<StreamInBluetooth>(result, std::move(context), sinkMetadata,
+                                                   microphones);
+}
+
+ndk::ScopedAStatus ModuleBluetooth::createOutputStream(
+        StreamContext&& context, const SourceMetadata& sourceMetadata,
+        const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
+    return createStreamInstance<StreamOutBluetooth>(result, std::move(context), sourceMetadata,
+                                                    offloadInfo);
+}
+
+ndk::ScopedAStatus ModuleBluetooth::onMasterMuteChanged(bool) {
+    LOG(DEBUG) << __func__ << ": is not supported";
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus ModuleBluetooth::onMasterVolumeChanged(float) {
+    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/bluetooth/StreamBluetooth.cpp b/audio/aidl/default/bluetooth/StreamBluetooth.cpp
new file mode 100644
index 0000000..dfccb0e
--- /dev/null
+++ b/audio/aidl/default/bluetooth/StreamBluetooth.cpp
@@ -0,0 +1,298 @@
+/*
+ * 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_StreamBluetooth"
+
+#include <Utils.h>
+#include <android-base/logging.h>
+#include <audio_utils/clock.h>
+
+#include "BluetoothAudioSessionControl.h"
+#include "core-impl/StreamBluetooth.h"
+
+namespace aidl::android::hardware::audio::core {
+
+using ::aidl::android::hardware::audio::common::SinkMetadata;
+using ::aidl::android::hardware::audio::common::SourceMetadata;
+using ::aidl::android::hardware::audio::core::VendorParameter;
+using ::aidl::android::hardware::bluetooth::audio::ChannelMode;
+using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration;
+using ::aidl::android::hardware::bluetooth::audio::PresentationPosition;
+using ::aidl::android::media::audio::common::AudioChannelLayout;
+using ::aidl::android::media::audio::common::AudioDevice;
+using ::aidl::android::media::audio::common::AudioDeviceAddress;
+using ::aidl::android::media::audio::common::AudioFormatDescription;
+using ::aidl::android::media::audio::common::AudioFormatType;
+using ::aidl::android::media::audio::common::AudioOffloadInfo;
+using ::aidl::android::media::audio::common::MicrophoneDynamicInfo;
+using ::aidl::android::media::audio::common::MicrophoneInfo;
+using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
+using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn;
+using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
+using ::android::bluetooth::audio::aidl::BluetoothStreamState;
+
+constexpr int kBluetoothDefaultInputBufferMs = 20;
+constexpr int kBluetoothDefaultOutputBufferMs = 10;
+// constexpr int kBluetoothSpatializerOutputBufferMs = 10;
+
+size_t getFrameCount(uint64_t durationUs, uint32_t sampleRate) {
+    return (durationUs * sampleRate) / 1000000;
+}
+
+// pcm configuration params are not really used by the module
+StreamBluetooth::StreamBluetooth(StreamContext* context, const Metadata& metadata)
+    : StreamCommonImpl(context, metadata),
+      mSampleRate(getContext().getSampleRate()),
+      mChannelLayout(getContext().getChannelLayout()),
+      mFormat(getContext().getFormat()),
+      mFrameSizeBytes(getContext().getFrameSize()),
+      mIsInput(isInput(metadata)) {
+    mPreferredDataIntervalUs =
+            mIsInput ? kBluetoothDefaultInputBufferMs : kBluetoothDefaultOutputBufferMs;
+    mPreferredFrameCount = getFrameCount(mPreferredDataIntervalUs, mSampleRate);
+    mIsInitialized = false;
+    mIsReadyToClose = false;
+}
+
+::android::status_t StreamBluetooth::init() {
+    return ::android::OK;  // defering this till we get AudioDeviceDescription
+}
+
+const StreamCommonInterface::ConnectedDevices& StreamBluetooth::getConnectedDevices() const {
+    std::lock_guard guard(mLock);
+    return StreamCommonImpl::getConnectedDevices();
+}
+
+ndk::ScopedAStatus StreamBluetooth::setConnectedDevices(
+        const std::vector<AudioDevice>& connectedDevices) {
+    if (mIsInput && connectedDevices.size() > 1) {
+        LOG(ERROR) << __func__ << ": wrong device size(" << connectedDevices.size()
+                   << ") for input stream";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+    for (const auto& connectedDevice : connectedDevices) {
+        if (connectedDevice.address.getTag() != AudioDeviceAddress::mac) {
+            LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString();
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+    }
+    std::lock_guard guard(mLock);
+    RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices));
+    mIsInitialized = false;  // updated connected device list, need initialization
+    return ndk::ScopedAStatus::ok();
+}
+
+::android::status_t StreamBluetooth::drain(StreamDescriptor::DrainMode) {
+    usleep(1000);
+    return ::android::OK;
+}
+
+::android::status_t StreamBluetooth::flush() {
+    usleep(1000);
+    return ::android::OK;
+}
+
+::android::status_t StreamBluetooth::pause() {
+    return standby();
+}
+
+::android::status_t StreamBluetooth::transfer(void* buffer, size_t frameCount,
+                                              size_t* actualFrameCount, int32_t* latencyMs) {
+    std::lock_guard guard(mLock);
+    if (!mIsInitialized || mIsReadyToClose) {
+        // 'setConnectedDevices' has been called or stream is ready to close, so no transfers
+        *actualFrameCount = 0;
+        *latencyMs = StreamDescriptor::LATENCY_UNKNOWN;
+        return ::android::OK;
+    }
+    *actualFrameCount = 0;
+    *latencyMs = 0;
+    for (auto proxy : mBtDeviceProxies) {
+        if (!proxy->start()) {
+            LOG(ERROR) << __func__ << ": state = " << proxy->getState() << " failed to start ";
+            return -EIO;
+        }
+        const size_t fc = std::min(frameCount, mPreferredFrameCount);
+        const size_t bytesToTransfer = fc * mFrameSizeBytes;
+        if (mIsInput) {
+            const size_t totalRead = proxy->readData(buffer, bytesToTransfer);
+            *actualFrameCount = std::max(*actualFrameCount, totalRead / mFrameSizeBytes);
+        } else {
+            const size_t totalWrite = proxy->writeData(buffer, bytesToTransfer);
+            *actualFrameCount = std::max(*actualFrameCount, totalWrite / mFrameSizeBytes);
+        }
+        PresentationPosition presentation_position;
+        if (!proxy->getPresentationPosition(presentation_position)) {
+            LOG(ERROR) << __func__ << ": getPresentationPosition returned error ";
+            return ::android::UNKNOWN_ERROR;
+        }
+        *latencyMs =
+                std::max(*latencyMs, (int32_t)(presentation_position.remoteDeviceAudioDelayNanos /
+                                               NANOS_PER_MILLISECOND));
+    }
+    return ::android::OK;
+}
+
+::android::status_t StreamBluetooth::initialize() {
+    if (!::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::IsAidlAvailable()) {
+        LOG(ERROR) << __func__ << ": IBluetoothAudioProviderFactory service not available";
+        return ::android::UNKNOWN_ERROR;
+    }
+    if (StreamCommonImpl::getConnectedDevices().empty()) {
+        LOG(ERROR) << __func__ << ", has no connected devices";
+        return ::android::NO_INIT;
+    }
+    // unregister older proxies (if any)
+    for (auto proxy : mBtDeviceProxies) {
+        proxy->stop();
+        proxy->unregisterPort();
+    }
+    mBtDeviceProxies.clear();
+    for (auto it = StreamCommonImpl::getConnectedDevices().begin();
+         it != StreamCommonImpl::getConnectedDevices().end(); ++it) {
+        std::shared_ptr<BluetoothAudioPortAidl> proxy =
+                mIsInput ? std::shared_ptr<BluetoothAudioPortAidl>(
+                                   std::make_shared<BluetoothAudioPortAidlIn>())
+                         : std::shared_ptr<BluetoothAudioPortAidl>(
+                                   std::make_shared<BluetoothAudioPortAidlOut>());
+        if (proxy->registerPort(it->type)) {
+            LOG(ERROR) << __func__ << ": cannot init HAL";
+            return ::android::UNKNOWN_ERROR;
+        }
+        PcmConfiguration config;
+        if (!proxy->loadAudioConfig(&config)) {
+            LOG(ERROR) << __func__ << ": state=" << proxy->getState()
+                       << " failed to get audio config";
+            return ::android::UNKNOWN_ERROR;
+        }
+        // TODO: Ensure minimum duration for spatialized output?
+        // WAR to support Mono / 16 bits per sample as the Bluetooth stack required
+        if (!mIsInput && config.channelMode == ChannelMode::MONO && config.bitsPerSample == 16) {
+            proxy->forcePcmStereoToMono(true);
+            config.channelMode = ChannelMode::STEREO;
+            LOG(INFO) << __func__ << ": force channels = to be AUDIO_CHANNEL_OUT_STEREO";
+        }
+        if (!checkConfigParams(config)) {
+            LOG(ERROR) << __func__ << " checkConfigParams failed";
+            return ::android::UNKNOWN_ERROR;
+        }
+        mBtDeviceProxies.push_back(std::move(proxy));
+    }
+    mIsInitialized = true;
+    return ::android::OK;
+}
+
+bool StreamBluetooth::checkConfigParams(
+        ::aidl::android::hardware::bluetooth::audio::PcmConfiguration& config) {
+    if ((int)mSampleRate != config.sampleRateHz) {
+        LOG(ERROR) << __func__ << ": Sample Rate mismatch, stream val = " << mSampleRate
+                   << " hal val = " << config.sampleRateHz;
+        return false;
+    }
+    auto channelCount = aidl::android::hardware::audio::common::getChannelCount(mChannelLayout);
+    if ((config.channelMode == ChannelMode::MONO && channelCount != 1) ||
+        (config.channelMode == ChannelMode::STEREO && channelCount != 2)) {
+        LOG(ERROR) << __func__ << ": Channel count mismatch, stream val = " << channelCount
+                   << " hal val = " << toString(config.channelMode);
+        return false;
+    }
+    if (mFormat.type != AudioFormatType::PCM) {
+        LOG(ERROR) << __func__ << ": unexpected format type "
+                   << aidl::android::media::audio::common::toString(mFormat.type);
+        return false;
+    }
+    int8_t bps = aidl::android::hardware::audio::common::getPcmSampleSizeInBytes(mFormat.pcm) * 8;
+    if (bps != config.bitsPerSample) {
+        LOG(ERROR) << __func__ << ": bits per sample mismatch, stream val = " << bps
+                   << " hal val = " << config.bitsPerSample;
+        return false;
+    }
+    if (config.dataIntervalUs > 0) {
+        mPreferredDataIntervalUs =
+                std::min((int32_t)mPreferredDataIntervalUs, config.dataIntervalUs);
+        mPreferredFrameCount = getFrameCount(mPreferredDataIntervalUs, mSampleRate);
+    }
+    return true;
+}
+
+ndk::ScopedAStatus StreamBluetooth::prepareToClose() {
+    std::lock_guard guard(mLock);
+    mIsReadyToClose = true;
+    return ndk::ScopedAStatus::ok();
+}
+
+::android::status_t StreamBluetooth::standby() {
+    std::lock_guard guard(mLock);
+    if (!mIsInitialized) {
+        if (auto status = initialize(); status != ::android::OK) return status;
+    }
+    for (auto proxy : mBtDeviceProxies) {
+        if (!proxy->suspend()) {
+            LOG(ERROR) << __func__ << ": state = " << proxy->getState() << " failed to stand by ";
+            return -EIO;
+        }
+    }
+    return ::android::OK;
+}
+
+::android::status_t StreamBluetooth::start() {
+    std::lock_guard guard(mLock);
+    if (!mIsInitialized) return initialize();
+    return ::android::OK;
+}
+
+void StreamBluetooth::shutdown() {
+    std::lock_guard guard(mLock);
+    for (auto proxy : mBtDeviceProxies) {
+        proxy->stop();
+        proxy->unregisterPort();
+    }
+    mBtDeviceProxies.clear();
+}
+
+ndk::ScopedAStatus StreamBluetooth::updateMetadataCommon(const Metadata& metadata) {
+    std::lock_guard guard(mLock);
+    if (!mIsInitialized) return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    bool isOk = true;
+    if (isInput(metadata)) {
+        isOk = mBtDeviceProxies[0]->updateSinkMetadata(std::get<SinkMetadata>(metadata));
+    } else {
+        for (auto proxy : mBtDeviceProxies) {
+            if (!proxy->updateSourceMetadata(std::get<SourceMetadata>(metadata))) isOk = false;
+        }
+    }
+    return isOk ? ndk::ScopedAStatus::ok()
+                : ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+StreamInBluetooth::StreamInBluetooth(StreamContext&& context, const SinkMetadata& sinkMetadata,
+                                     const std::vector<MicrophoneInfo>& microphones)
+    : StreamIn(std::move(context), microphones),
+      StreamBluetooth(&(StreamIn::mContext), sinkMetadata) {}
+
+ndk::ScopedAStatus StreamInBluetooth::getActiveMicrophones(
+        std::vector<MicrophoneDynamicInfo>* _aidl_return __unused) {
+    LOG(DEBUG) << __func__ << ": not supported";
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+StreamOutBluetooth::StreamOutBluetooth(StreamContext&& context,
+                                       const SourceMetadata& sourceMetadata,
+                                       const std::optional<AudioOffloadInfo>& offloadInfo)
+    : StreamOut(std::move(context), offloadInfo),
+      StreamBluetooth(&(StreamOut::mContext), sourceMetadata) {}
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/Configuration.h b/audio/aidl/default/include/core-impl/Configuration.h
index 25bf7af..6277c38 100644
--- a/audio/aidl/default/include/core-impl/Configuration.h
+++ b/audio/aidl/default/include/core-impl/Configuration.h
@@ -47,5 +47,6 @@
 std::unique_ptr<Configuration> getRSubmixConfiguration();
 std::unique_ptr<Configuration> getStubConfiguration();
 std::unique_ptr<Configuration> getUsbConfiguration();
+std::unique_ptr<Configuration> getBluetoothConfiguration();
 
 }  // namespace aidl::android::hardware::audio::core::internal
diff --git a/audio/aidl/default/include/core-impl/DevicePortProxy.h b/audio/aidl/default/include/core-impl/DevicePortProxy.h
new file mode 100644
index 0000000..72ffaad
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/DevicePortProxy.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <mutex>
+
+#include <android-base/thread_annotations.h>
+
+#include <aidl/android/hardware/audio/common/SinkMetadata.h>
+#include <aidl/android/hardware/audio/common/SourceMetadata.h>
+#include <aidl/android/hardware/bluetooth/audio/BluetoothAudioStatus.h>
+#include <aidl/android/hardware/bluetooth/audio/PcmConfiguration.h>
+#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
+#include <aidl/android/media/audio/common/AudioDeviceDescription.h>
+
+#include "BluetoothAudioSessionControl.h"
+
+namespace android::bluetooth::audio::aidl {
+
+enum class BluetoothStreamState : uint8_t {
+    DISABLED = 0,  // This stream is closing or Bluetooth profiles (A2DP/LE) is disabled
+    STANDBY,
+    STARTING,
+    STARTED,
+    SUSPENDING,
+    UNKNOWN,
+};
+
+std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state);
+
+/**
+ * Proxy for Bluetooth Audio HW Module to communicate with Bluetooth Audio
+ * Session Control. All methods are not thread safe, so users must acquire a
+ * lock. Note: currently, getState() of DevicePortProxy is only used for
+ * verbose logging, it is not locked, so the state may not be synchronized.
+ */
+class BluetoothAudioPort {
+  public:
+    BluetoothAudioPort() = default;
+    virtual ~BluetoothAudioPort() = default;
+
+    /**
+     * Fetch output control / data path of BluetoothAudioPort and setup
+     * callbacks into BluetoothAudioProvider. If registerPort() returns false, the audio
+     * HAL must delete this BluetoothAudioPort and return EINVAL to caller
+     */
+    virtual bool registerPort(
+            const ::aidl::android::media::audio::common::AudioDeviceDescription&) = 0;
+
+    /**
+     * Unregister this BluetoothAudioPort from BluetoothAudioSessionControl.
+     * Audio HAL must delete this BluetoothAudioPort after calling this.
+     */
+    virtual void unregisterPort() = 0;
+
+    /**
+     * When the Audio framework / HAL tries to query audio config about format,
+     * channel mask and sample rate, it uses this function to fetch from the
+     * Bluetooth stack
+     */
+    virtual bool loadAudioConfig(
+            ::aidl::android::hardware::bluetooth::audio::PcmConfiguration*) const = 0;
+
+    /**
+     * WAR to support Mono mode / 16 bits per sample
+     */
+    virtual void forcePcmStereoToMono(bool) = 0;
+
+    /**
+     * When the Audio framework / HAL wants to change the stream state, it invokes
+     * these 3 functions to control the Bluetooth stack (Audio Control Path).
+     * Note: Both start() and suspend() will return true when there are no errors.
+     * Called by Audio framework / HAL to start the stream
+     */
+    virtual bool start() = 0;
+
+    /**
+     * Called by Audio framework / HAL to suspend the stream
+     */
+    virtual bool suspend() = 0;
+
+    /**
+     * Called by Audio framework / HAL to stop the stream
+     */
+    virtual void stop() = 0;
+
+    /**
+     * Called by the Audio framework / HAL to fetch information about audio frames
+     * presented to an external sink, or frames presented fror an internal sink
+     */
+    virtual bool getPresentationPosition(
+            ::aidl::android::hardware::bluetooth::audio::PresentationPosition&) const = 0;
+
+    /**
+     * Called by the Audio framework / HAL when the metadata of the stream's
+     * source has been changed.
+     */
+    virtual bool updateSourceMetadata(
+            const ::aidl::android::hardware::audio::common::SourceMetadata&) const {
+        return false;
+    }
+
+    /**
+     * Called by the Audio framework / HAL when the metadata of the stream's
+     * sink has been changed.
+     */
+    virtual bool updateSinkMetadata(
+            const ::aidl::android::hardware::audio::common::SinkMetadata&) const {
+        return false;
+    }
+
+    /**
+     * Return the current BluetoothStreamState
+     */
+    virtual BluetoothStreamState getState() const = 0;
+
+    /**
+     * Set the current BluetoothStreamState
+     */
+    virtual bool setState(BluetoothStreamState) = 0;
+
+    virtual bool isA2dp() const = 0;
+
+    virtual bool isLeAudio() const = 0;
+
+    virtual bool getPreferredDataIntervalUs(size_t*) const = 0;
+
+    virtual size_t writeData(const void*, size_t) const { return 0; }
+
+    virtual size_t readData(void*, size_t) const { return 0; }
+};
+
+class BluetoothAudioPortAidl : public BluetoothAudioPort {
+  public:
+    BluetoothAudioPortAidl();
+    virtual ~BluetoothAudioPortAidl();
+
+    bool registerPort(const ::aidl::android::media::audio::common::AudioDeviceDescription&
+                              description) override;
+
+    void unregisterPort() override;
+
+    bool loadAudioConfig(::aidl::android::hardware::bluetooth::audio::PcmConfiguration* audio_cfg)
+            const override;
+
+    void forcePcmStereoToMono(bool force) override { mIsStereoToMono = force; }
+
+    bool start() override;
+    bool suspend() override;
+    void stop() override;
+
+    bool getPresentationPosition(::aidl::android::hardware::bluetooth::audio::PresentationPosition&
+                                         presentation_position) const override;
+
+    bool updateSourceMetadata(const ::aidl::android::hardware::audio::common::SourceMetadata&
+                                      sourceMetadata) const override;
+
+    bool updateSinkMetadata(const ::aidl::android::hardware::audio::common::SinkMetadata&
+                                    sinkMetadata) const override;
+
+    /**
+     * Return the current BluetoothStreamState
+     * Note: This method is used for logging, does not lock, so value returned may not be latest
+     */
+    BluetoothStreamState getState() const override NO_THREAD_SAFETY_ANALYSIS;
+
+    bool setState(BluetoothStreamState state) override;
+
+    bool isA2dp() const override;
+
+    bool isLeAudio() const override;
+
+    bool getPreferredDataIntervalUs(size_t* interval_us) const override;
+
+  protected:
+    uint16_t mCookie;
+    BluetoothStreamState mState GUARDED_BY(mCvMutex);
+    ::aidl::android::hardware::bluetooth::audio::SessionType mSessionType;
+    // WR to support Mono: True if fetching Stereo and mixing into Mono
+    bool mIsStereoToMono = false;
+
+    bool inUse() const;
+
+    std::string debugMessage() const;
+
+  private:
+    // start()/suspend() report state change status via callback. Wait until kMaxWaitingTimeMs or a
+    // state change after a call to start()/suspend() and analyse the returned status. Below mutex,
+    // conditional variable serves this purpose.
+    mutable std::mutex mCvMutex;
+    std::condition_variable mInternalCv GUARDED_BY(mCvMutex);
+
+    // Check and initialize session type for |devices| If failed, this
+    // BluetoothAudioPortAidl is not initialized and must be deleted.
+    bool initSessionType(
+            const ::aidl::android::media::audio::common::AudioDeviceDescription& description);
+
+    bool condWaitState(BluetoothStreamState state);
+
+    void controlResultHandler(
+            uint16_t cookie,
+            const ::aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus& status);
+    void sessionChangedHandler(uint16_t cookie);
+};
+
+class BluetoothAudioPortAidlOut : public BluetoothAudioPortAidl {
+  public:
+    // The audio data path to the Bluetooth stack (Software encoding)
+    size_t writeData(const void* buffer, size_t bytes) const override;
+};
+
+class BluetoothAudioPortAidlIn : public BluetoothAudioPortAidl {
+  public:
+    // The audio data path from the Bluetooth stack (Software decoded)
+    size_t readData(void* buffer, size_t bytes) const override;
+};
+
+}  // namespace android::bluetooth::audio::aidl
\ No newline at end of file
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 539221d..e52940e 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -33,7 +33,7 @@
   public:
     // This value is used for all AudioPatches and reported by all streams.
     static constexpr int32_t kLatencyMs = 10;
-    enum Type : int { DEFAULT, R_SUBMIX, STUB, USB };
+    enum Type : int { DEFAULT, R_SUBMIX, STUB, USB, BLUETOOTH };
 
     static std::shared_ptr<Module> createInstance(Type type);
 
diff --git a/audio/aidl/default/include/core-impl/ModuleBluetooth.h b/audio/aidl/default/include/core-impl/ModuleBluetooth.h
new file mode 100644
index 0000000..e20f7a3
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/ModuleBluetooth.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "core-impl/Module.h"
+
+namespace aidl::android::hardware::audio::core {
+
+class ModuleBluetooth final : public Module {
+  public:
+    ModuleBluetooth() : Module(Type::BLUETOOTH) {}
+
+  private:
+    ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
+    ndk::ScopedAStatus setMicMute(bool in_mute) override;
+
+    ndk::ScopedAStatus createInputStream(
+            StreamContext&& context,
+            const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+            const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
+            std::shared_ptr<StreamIn>* result) override;
+    ndk::ScopedAStatus createOutputStream(
+            StreamContext&& context,
+            const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+            const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+                    offloadInfo,
+            std::shared_ptr<StreamOut>* result) override;
+    ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
+    ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
+};
+
+}  // namespace aidl::android::hardware::audio::core
\ No newline at end of file
diff --git a/audio/aidl/default/include/core-impl/StreamBluetooth.h b/audio/aidl/default/include/core-impl/StreamBluetooth.h
new file mode 100644
index 0000000..b9c5ea9
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/StreamBluetooth.h
@@ -0,0 +1,97 @@
+/*
+ * 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/DevicePortProxy.h"
+#include "core-impl/Stream.h"
+
+namespace aidl::android::hardware::audio::core {
+
+class StreamBluetooth : public StreamCommonImpl {
+  public:
+    StreamBluetooth(StreamContext* context, const Metadata& metadata);
+
+    // Methods of 'DriverInterface'.
+    ::android::status_t init() override;
+    ::android::status_t drain(StreamDescriptor::DrainMode) override;
+    ::android::status_t flush() override;
+    ::android::status_t pause() override;
+    ::android::status_t standby() override;
+    ::android::status_t start() override;
+    ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
+                                 int32_t* latencyMs) override;
+    void shutdown() override;
+
+    // Overridden methods of 'StreamCommonImpl', called on a Binder thread.
+    ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) override;
+    ndk::ScopedAStatus prepareToClose() override;
+    const ConnectedDevices& getConnectedDevices() const override;
+    ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override;
+
+  private:
+    // Audio Pcm Config
+    const uint32_t mSampleRate;
+    const ::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout;
+    const ::aidl::android::media::audio::common::AudioFormatDescription mFormat;
+    const size_t mFrameSizeBytes;
+    const bool mIsInput;
+
+    size_t mPreferredDataIntervalUs;
+    size_t mPreferredFrameCount;
+
+    mutable std::mutex mLock;
+    bool mIsInitialized GUARDED_BY(mLock);
+    bool mIsReadyToClose GUARDED_BY(mLock);
+    std::vector<std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl>>
+            mBtDeviceProxies GUARDED_BY(mLock);
+
+    ::android::status_t initialize() REQUIRES(mLock);
+    bool checkConfigParams(::aidl::android::hardware::bluetooth::audio::PcmConfiguration& config);
+};
+
+class StreamInBluetooth final : public StreamIn, public StreamBluetooth {
+  public:
+    friend class ndk::SharedRefBase;
+    StreamInBluetooth(
+            StreamContext&& context,
+            const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+            const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
+
+  private:
+    void onClose(StreamDescriptor::State) override { defaultOnClose(); }
+    ndk::ScopedAStatus getActiveMicrophones(
+            std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
+            override;
+};
+
+class StreamOutBluetooth final : public StreamOut, public StreamBluetooth {
+  public:
+    friend class ndk::SharedRefBase;
+    StreamOutBluetooth(
+            StreamContext&& context,
+            const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+            const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+                    offloadInfo);
+
+  private:
+    void onClose(StreamDescriptor::State) override { defaultOnClose(); }
+};
+
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/main.cpp b/audio/aidl/default/main.cpp
index 93fb366..a0c0fab 100644
--- a/audio/aidl/default/main.cpp
+++ b/audio/aidl/default/main.cpp
@@ -65,7 +65,8 @@
         return std::make_pair(module, moduleBinder);
     };
     auto modules = {createModule(Module::Type::DEFAULT), createModule(Module::Type::R_SUBMIX),
-                    createModule(Module::Type::USB), createModule(Module::Type::STUB)};
+                    createModule(Module::Type::USB), createModule(Module::Type::STUB),
+                    createModule(Module::Type::BLUETOOTH)};
     (void)modules;
 
     ABinderProcess_joinThreadPool();