aosp aidl bluetooth audio hal implementation

Bug: 228804498
Test: atest VtsHalAudioCoreTargetTest
Change-Id: Id5fb60fe53ead9f5d2e6ecbb9988a23835cf2509
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