diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp index 4e583a4808..b6cfc1329c 100644 --- a/audio/aidl/default/Android.bp +++ b/audio/aidl/default/Android.bp @@ -82,6 +82,9 @@ cc_library { "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 @@ cc_library { "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 @@ cc_binary { "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 a71c6ea734..385ffbf9fc 100644 --- a/audio/aidl/default/Configuration.cpp +++ b/audio/aidl/default/Configuration.cpp @@ -561,4 +561,86 @@ std::unique_ptr getStubConfiguration() { return std::make_unique(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 getBluetoothConfiguration() { + static const Configuration configuration = []() { + const std::vector 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( + {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); +} + } // namespace aidl::android::hardware::audio::core::internal diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp index b59bd7c081..5625a44400 100644 --- a/audio/aidl/default/Module.cpp +++ b/audio/aidl/default/Module.cpp @@ -26,6 +26,7 @@ #include #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 @@ std::shared_ptr Module::createInstance(Type type) { return ndk::SharedRefBase::make(); case Type::USB: return ndk::SharedRefBase::make(); + case Type::BLUETOOTH: + return ndk::SharedRefBase::make(); } } @@ -134,6 +137,9 @@ std::ostream& operator<<(std::ostream& os, Module::Type t) { case Module::Type::USB: os << "usb"; break; + case Module::Type::BLUETOOTH: + os << "bluetooth"; + break; } return os; } @@ -301,6 +307,9 @@ std::unique_ptr Module::initializeConfig() { 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 c9d6314e32..9db606157a 100644 --- a/audio/aidl/default/android.hardware.audio.service-aidl.xml +++ b/audio/aidl/default/android.hardware.audio.service-aidl.xml @@ -19,6 +19,11 @@ 1 IModule/usb + + android.hardware.audio.core + 1 + IModule/bluetooth + android.hardware.audio.core 1 diff --git a/audio/aidl/default/bluetooth/DevicePortProxy.cpp b/audio/aidl/default/bluetooth/DevicePortProxy.cpp new file mode 100644 index 0000000000..aabf60fac3 --- /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 +#include +#include +#include +#include + +#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().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(); + 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(buffer); + std::unique_ptr 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 0000000000..bb11f0ae85 --- /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 + +#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& microphones, std::shared_ptr* result) { + return createStreamInstance(result, std::move(context), sinkMetadata, + microphones); +} + +ndk::ScopedAStatus ModuleBluetooth::createOutputStream( + StreamContext&& context, const SourceMetadata& sourceMetadata, + const std::optional& offloadInfo, std::shared_ptr* result) { + return createStreamInstance(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 0000000000..dfccb0eec5 --- /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 +#include +#include + +#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& 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 proxy = + mIsInput ? std::shared_ptr( + std::make_shared()) + : std::shared_ptr( + std::make_shared()); + 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(metadata)); + } else { + for (auto proxy : mBtDeviceProxies) { + if (!proxy->updateSourceMetadata(std::get(metadata))) isOk = false; + } + } + return isOk ? ndk::ScopedAStatus::ok() + : ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +StreamInBluetooth::StreamInBluetooth(StreamContext&& context, const SinkMetadata& sinkMetadata, + const std::vector& microphones) + : StreamIn(std::move(context), microphones), + StreamBluetooth(&(StreamIn::mContext), sinkMetadata) {} + +ndk::ScopedAStatus StreamInBluetooth::getActiveMicrophones( + std::vector* _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& 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 25bf7afa81..6277c38db2 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 getPrimaryConfiguration(); std::unique_ptr getRSubmixConfiguration(); std::unique_ptr getStubConfiguration(); std::unique_ptr getUsbConfiguration(); +std::unique_ptr 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 0000000000..72ffaadafc --- /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 +#include + +#include + +#include +#include +#include +#include +#include +#include + +#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 539221d5d2..e52940eeb1 100644 --- a/audio/aidl/default/include/core-impl/Module.h +++ b/audio/aidl/default/include/core-impl/Module.h @@ -33,7 +33,7 @@ class Module : public BnModule { 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 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 0000000000..e20f7a3d25 --- /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* 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* 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 0000000000..b9c5ea9bbd --- /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 +#include + +#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> + 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 93fb36607e..a0c0fab40a 100644 --- a/audio/aidl/default/main.cpp +++ b/audio/aidl/default/main.cpp @@ -65,7 +65,8 @@ int main() { 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();