diff --git a/bluetooth/audio/2.2/Android.bp b/bluetooth/audio/2.2/Android.bp new file mode 100644 index 0000000000..6449c08bc9 --- /dev/null +++ b/bluetooth/audio/2.2/Android.bp @@ -0,0 +1,32 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +hidl_interface { + name: "android.hardware.bluetooth.audio@2.2", + root: "android.hardware", + srcs: [ + "types.hal", + "IBluetoothAudioProvider.hal", + "IBluetoothAudioProvidersFactory.hal", + ], + interfaces: [ + "android.hardware.audio.common@5.0", + "android.hardware.bluetooth.audio@2.0", + "android.hardware.bluetooth.audio@2.1", + "android.hidl.base@1.0", + "android.hidl.safe_union@1.0", + ], + apex_available: [ + "//apex_available:platform", + "com.android.bluetooth.updatable", + ], + gen_java: false, +} diff --git a/bluetooth/audio/2.2/IBluetoothAudioProvider.hal b/bluetooth/audio/2.2/IBluetoothAudioProvider.hal new file mode 100644 index 0000000000..ad8c8392a9 --- /dev/null +++ b/bluetooth/audio/2.2/IBluetoothAudioProvider.hal @@ -0,0 +1,62 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.bluetooth.audio@2.2; + +import @2.1::IBluetoothAudioProvider; +import @2.0::IBluetoothAudioPort; +import @2.0::Status; + +/** + * HAL interface from the Bluetooth stack to the Audio HAL + * + * The Bluetooth stack calls methods in this interface to start and end audio + * sessions and sends callback events to the Audio HAL. + * + * Note: For HIDL APIs with a "generates" statement, the callback parameter used + * for return value must be invoked synchronously before the API call returns. + */ +interface IBluetoothAudioProvider extends @2.1::IBluetoothAudioProvider { + + /** + * This method indicates that the Bluetooth stack is ready to stream audio. + * It registers an instance of IBluetoothAudioPort with and provides the + * current negotiated codec to the Audio HAL. After this method is called, + * the Audio HAL can invoke IBluetoothAudioPort.startStream(). + * + * Note: endSession() must be called to unregister this IBluetoothAudioPort + * + * @param hostIf An instance of IBluetoothAudioPort for stream control + * @param audioConfig The audio configuration negotiated with the remote + * device. The PCM parameters are set if software based encoding, + * otherwise the correct codec configuration is used for hardware + * encoding. + * + * @return status One of the following + * SUCCESS if this IBluetoothAudioPort was successfully registered with + * the Audio HAL + * UNSUPPORTED_CODEC_CONFIGURATION if the Audio HAL cannot register this + * IBluetoothAudioPort with the given codec configuration + * FAILURE if the Audio HAL cannot register this IBluetoothAudioPort for + * any other reason + * @return dataMQ The fast message queue for audio data from/to this + * provider. Audio data will be in PCM format as specified by the + * audioConfig.pcmConfig parameter. Invalid if streaming is offloaded + * from/to hardware or on failure. + */ + startSession_2_2(IBluetoothAudioPort hostIf, AudioConfiguration audioConfig) + generates (Status status, fmq_sync dataMQ); +}; diff --git a/bluetooth/audio/2.2/IBluetoothAudioProvidersFactory.hal b/bluetooth/audio/2.2/IBluetoothAudioProvidersFactory.hal new file mode 100644 index 0000000000..eeff4de0a4 --- /dev/null +++ b/bluetooth/audio/2.2/IBluetoothAudioProvidersFactory.hal @@ -0,0 +1,33 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.bluetooth.audio@2.2; + +import @2.1::IBluetoothAudioProvidersFactory; + +/** + * This factory allows a HAL implementation to be split into multiple + * independent providers. + * + * When the Bluetooth stack is ready to create an audio session, it must first + * obtain the IBluetoothAudioProvider for that session type by calling + * openProvider(). + * + * Note: For HIDL APIs with a "generates" statement, the callback parameter used + * for return value must be invoked synchronously before the API call returns. + */ +interface IBluetoothAudioProvidersFactory extends @2.1::IBluetoothAudioProvidersFactory { +}; diff --git a/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.cpp new file mode 100644 index 0000000000..126bc9efdd --- /dev/null +++ b/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2021 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 "BTAudioProviderA2dpOffload" + +#include "A2dpOffloadAudioProvider.h" + +#include +#include +#include + +#include "BluetoothAudioSessionReport_2_2.h" +#include "BluetoothAudioSupportedCodecsDB_2_1.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_2; +using ::android::hardware::kSynchronizedReadWrite; +using ::android::hardware::MessageQueue; +using ::android::hardware::Void; +using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration; + +using DataMQ = MessageQueue; + +A2dpOffloadAudioProvider::A2dpOffloadAudioProvider() + : BluetoothAudioProvider() { + session_type_ = V2_1::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH; +} + +bool A2dpOffloadAudioProvider::isValid(const V2_0::SessionType& sessionType) { + return isValid(static_cast(sessionType)); +} + +bool A2dpOffloadAudioProvider::isValid(const V2_1::SessionType& sessionType) { + return (sessionType == session_type_); +} + +Return A2dpOffloadAudioProvider::startSession( + const sp& hostIf, + const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + /** + * Initialize the audio platform if audioConfiguration is supported. + * Save the IBluetoothAudioPort interface, so that it can be used + * later to send stream control commands to the HAL client, based on + * interaction with Audio framework. + */ + if (audioConfig.getDiscriminator() != + AudioConfiguration::hidl_discriminator::codecConfig) { + LOG(WARNING) << __func__ + << " - Invalid Audio Configuration=" << toString(audioConfig); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } else if (!android::bluetooth::audio::IsOffloadCodecConfigurationValid( + session_type_, audioConfig.codecConfig())) { + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } + + return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb); +} + +Return A2dpOffloadAudioProvider::onSessionReady( + startSession_cb _hidl_cb) { + BluetoothAudioSessionReport_2_2::OnSessionStarted(session_type_, stack_iface_, + nullptr, audio_config_); + _hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor()); + return Void(); +} + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.h b/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.h new file mode 100644 index 0000000000..7ccdedcbe5 --- /dev/null +++ b/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.h @@ -0,0 +1,48 @@ +/* + * Copyright 2021 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 "BluetoothAudioProvider.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +class A2dpOffloadAudioProvider : public BluetoothAudioProvider { + public: + A2dpOffloadAudioProvider(); + + bool isValid(const V2_1::SessionType& sessionType) override; + bool isValid(const V2_0::SessionType& sessionType) override; + + Return startSession(const sp& hostIf, + const V2_0::AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + + private: + Return onSessionReady(startSession_cb _hidl_cb) override; +}; + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.cpp b/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.cpp new file mode 100644 index 0000000000..0d918e1978 --- /dev/null +++ b/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2021 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 "BTAudioProviderA2dpSoftware" + +#include "A2dpSoftwareAudioProvider.h" + +#include + +#include "BluetoothAudioSessionReport_2_2.h" +#include "BluetoothAudioSupportedCodecsDB_2_1.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_2; +using ::android::hardware::Void; +using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration; + +// Here the buffer size is based on SBC +static constexpr uint32_t kPcmFrameSize = 4; // 16 bits per sample / stereo +// SBC is 128, and here we choose the LCM of 16, 24, and 32 +static constexpr uint32_t kPcmFrameCount = 96; +static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount; +// The max counts by 1 tick (20ms) for SBC is about 7. Since using 96 for the +// PCM counts, here we just choose a greater number +static constexpr uint32_t kRtpFrameCount = 10; +static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount; +static constexpr uint32_t kBufferCount = 2; // double buffer +static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount; + +A2dpSoftwareAudioProvider::A2dpSoftwareAudioProvider() + : BluetoothAudioProvider(), mDataMQ(nullptr) { + LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize + << " byte(s)"; + std::unique_ptr tempDataMQ( + new DataMQ(kDataMqSize, /* EventFlag */ true)); + if (tempDataMQ && tempDataMQ->isValid()) { + mDataMQ = std::move(tempDataMQ); + session_type_ = V2_1::SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH; + } else { + ALOGE_IF(!tempDataMQ, "failed to allocate data MQ"); + ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid"); + } +} + +bool A2dpSoftwareAudioProvider::isValid(const V2_0::SessionType& sessionType) { + return isValid(static_cast(sessionType)); +} + +bool A2dpSoftwareAudioProvider::isValid(const V2_1::SessionType& sessionType) { + return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid()); +} + +Return A2dpSoftwareAudioProvider::startSession( + const sp& hostIf, + const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + /** + * Initialize the audio platform if audioConfiguration is supported. + * Save the IBluetoothAudioPort interface, so that it can be used + * later to send stream control commands to the HAL client, based on + * interaction with Audio framework. + */ + if (audioConfig.getDiscriminator() != + AudioConfiguration::hidl_discriminator::pcmConfig) { + LOG(WARNING) << __func__ + << " - Invalid Audio Configuration=" << toString(audioConfig); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid( + audioConfig.pcmConfig())) { + LOG(WARNING) << __func__ << " - Unsupported PCM Configuration=" + << toString(audioConfig.pcmConfig()); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } + + return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb); +} + +Return A2dpSoftwareAudioProvider::onSessionReady( + startSession_cb _hidl_cb) { + if (mDataMQ && mDataMQ->isValid()) { + BluetoothAudioSessionReport_2_2::OnSessionStarted( + session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_); + _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc()); + } else { + _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor()); + } + return Void(); +} + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.h b/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.h new file mode 100644 index 0000000000..3d4f0ccd1f --- /dev/null +++ b/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.h @@ -0,0 +1,59 @@ +/* + * Copyright 2021 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 "BluetoothAudioProvider.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +using ::android::hardware::kSynchronizedReadWrite; +using ::android::hardware::MessageQueue; + +using DataMQ = MessageQueue; + +class A2dpSoftwareAudioProvider : public BluetoothAudioProvider { + public: + A2dpSoftwareAudioProvider(); + + bool isValid(const V2_1::SessionType& sessionType) override; + bool isValid(const V2_0::SessionType& sessionType) override; + + Return startSession(const sp& hostIf, + const V2_0::AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + + private: + // audio data queue for software encoding + std::unique_ptr mDataMQ; + + Return onSessionReady(startSession_cb _hidl_cb) override; +}; + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/Android.bp b/bluetooth/audio/2.2/default/Android.bp new file mode 100644 index 0000000000..7a5ae75900 --- /dev/null +++ b/bluetooth/audio/2.2/default/Android.bp @@ -0,0 +1,37 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +cc_library_shared { + name: "android.hardware.bluetooth.audio@2.2-impl", + defaults: ["hidl_defaults"], + vendor: true, + relative_install_path: "hw", + srcs: [ + "BluetoothAudioProvidersFactory.cpp", + "BluetoothAudioProvider.cpp", + "A2dpOffloadAudioProvider.cpp", + "A2dpSoftwareAudioProvider.cpp", + "HearingAidAudioProvider.cpp", + "LeAudioAudioProvider.cpp", + "LeAudioOffloadAudioProvider.cpp", + ], + header_libs: ["libhardware_headers"], + shared_libs: [ + "android.hardware.bluetooth.audio@2.0", + "android.hardware.bluetooth.audio@2.1", + "android.hardware.bluetooth.audio@2.2", + "libbase", + "libbluetooth_audio_session", + "libcutils", + "libfmq", + "libhidlbase", + "liblog", + "libutils", + ], +} diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp new file mode 100644 index 0000000000..3655bc08bc --- /dev/null +++ b/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp @@ -0,0 +1,189 @@ +/* + * Copyright 2021 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 "BTAudioProviderStub" + +#include "BluetoothAudioProvider.h" + +#include + +#include "BluetoothAudioSessionReport_2_2.h" +#include "BluetoothAudioSupportedCodecsDB_2_1.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_2; +using ::android::hardware::kSynchronizedReadWrite; +using ::android::hardware::MessageQueue; +using ::android::hardware::Void; + +using DataMQ = MessageQueue; + +void BluetoothAudioDeathRecipient::serviceDied( + uint64_t cookie __unused, + const wp<::android::hidl::base::V1_0::IBase>& who __unused) { + LOG(ERROR) << "BluetoothAudioDeathRecipient::" << __func__ + << " - BluetoothAudio Service died"; + provider_->endSession(); +} + +BluetoothAudioProvider::BluetoothAudioProvider() + : death_recipient_(new BluetoothAudioDeathRecipient(this)), + session_type_(V2_1::SessionType::UNKNOWN), + audio_config_({}) {} + +Return BluetoothAudioProvider::startSession( + const sp& hostIf, + const V2_0::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + AudioConfiguration audioConfig_2_2; + + if (audioConfig.getDiscriminator() == + V2_0::AudioConfiguration::hidl_discriminator::pcmConfig) { + audioConfig_2_2.pcmConfig( + {.sampleRate = + static_cast(audioConfig.pcmConfig().sampleRate), + .channelMode = audioConfig.pcmConfig().channelMode, + .bitsPerSample = audioConfig.pcmConfig().bitsPerSample, + .dataIntervalUs = 0}); + } else { + audioConfig_2_2.codecConfig(audioConfig.codecConfig()); + } + + return startSession_2_2(hostIf, audioConfig_2_2, _hidl_cb); +} + +Return BluetoothAudioProvider::startSession_2_1( + const sp& hostIf, + const V2_1::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + AudioConfiguration audioConfig_2_2; + if (audioConfig.getDiscriminator() == + V2_1::AudioConfiguration::hidl_discriminator::leAudioCodecConfig) { + audioConfig_2_2.leAudioConfig().mode = LeAudioMode::UNKNOWN; + audioConfig_2_2.leAudioConfig().config.unicastConfig() = { + .streamMap = {{ + .streamHandle = 0xFFFF, + .audioChannelAllocation = + audioConfig.leAudioCodecConfig().audioChannelAllocation, + }}, + .peerDelay = 0, + .lc3Config = audioConfig.leAudioCodecConfig().lc3Config}; + } else if (audioConfig.getDiscriminator() == + V2_1::AudioConfiguration::hidl_discriminator::pcmConfig) { + audioConfig_2_2.pcmConfig(audioConfig.pcmConfig()); + } else { + audioConfig_2_2.codecConfig(audioConfig.codecConfig()); + } + + return startSession_2_2(hostIf, audioConfig_2_2, _hidl_cb); +} + +Return BluetoothAudioProvider::startSession_2_2( + const sp& hostIf, + const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + if (hostIf == nullptr) { + _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor()); + return Void(); + } + + /** + * Initialize the audio platform if audioConfiguration is supported. + * Save the IBluetoothAudioPort interface, so that it can be used + * later to send stream control commands to the HAL client, based on + * interaction with Audio framework. + */ + audio_config_ = audioConfig; + stack_iface_ = hostIf; + stack_iface_->linkToDeath(death_recipient_, 0); + + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << ", AudioConfiguration=[" << toString(audio_config_) << "]"; + + onSessionReady(_hidl_cb); + return Void(); +} + +Return BluetoothAudioProvider::streamStarted( + BluetoothAudioStatus status) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << ", status=" << toString(status); + + /** + * Streaming on control path has started, + * HAL server should start the streaming on data path. + */ + if (stack_iface_) { + BluetoothAudioSessionReport_2_2::ReportControlStatus(session_type_, true, + status); + } else { + LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_) + << ", status=" << toString(status) << " has NO session"; + } + + return Void(); +} + +Return BluetoothAudioProvider::streamSuspended( + BluetoothAudioStatus status) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << ", status=" << toString(status); + + /** + * Streaming on control path has suspend, + * HAL server should suspend the streaming on data path. + */ + if (stack_iface_) { + BluetoothAudioSessionReport_2_2::ReportControlStatus(session_type_, false, + status); + } else { + LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_) + << ", status=" << toString(status) << " has NO session"; + } + + return Void(); +} + +Return BluetoothAudioProvider::endSession() { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_); + + if (stack_iface_) { + BluetoothAudioSessionReport_2_2::OnSessionEnded(session_type_); + stack_iface_->unlinkToDeath(death_recipient_); + } else { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << " has NO session"; + } + + /** + * Clean up the audio platform as remote audio device is no + * longer active + */ + stack_iface_ = nullptr; + audio_config_ = {}; + + return Void(); +} + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvider.h b/bluetooth/audio/2.2/default/BluetoothAudioProvider.h new file mode 100644 index 0000000000..b7581ba0c3 --- /dev/null +++ b/bluetooth/audio/2.2/default/BluetoothAudioProvider.h @@ -0,0 +1,85 @@ +/* + * Copyright 2021 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 + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +using ::android::sp; +using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort; + +using BluetoothAudioStatus = + ::android::hardware::bluetooth::audio::V2_0::Status; + +class BluetoothAudioDeathRecipient; + +class BluetoothAudioProvider : public IBluetoothAudioProvider { + public: + BluetoothAudioProvider(); + ~BluetoothAudioProvider() = default; + + virtual bool isValid(const V2_1::SessionType& sessionType) = 0; + virtual bool isValid(const V2_0::SessionType& sessionType) = 0; + + Return startSession(const sp& hostIf, + const V2_0::AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + Return startSession_2_1(const sp& hostIf, + const V2_1::AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + Return startSession_2_2(const sp& hostIf, + const AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + Return streamStarted(BluetoothAudioStatus status) override; + Return streamSuspended(BluetoothAudioStatus status) override; + Return endSession() override; + + protected: + sp death_recipient_; + + V2_1::SessionType session_type_; + AudioConfiguration audio_config_; + sp stack_iface_; + + virtual Return onSessionReady(startSession_cb _hidl_cb) = 0; +}; + +class BluetoothAudioDeathRecipient : public hidl_death_recipient { + public: + BluetoothAudioDeathRecipient(const sp provider) + : provider_(provider) {} + + virtual void serviceDied( + uint64_t cookie, + const wp<::android::hidl::base::V1_0::IBase>& who) override; + + private: + sp provider_; +}; + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvidersFactory.cpp b/bluetooth/audio/2.2/default/BluetoothAudioProvidersFactory.cpp new file mode 100644 index 0000000000..7438c80d4c --- /dev/null +++ b/bluetooth/audio/2.2/default/BluetoothAudioProvidersFactory.cpp @@ -0,0 +1,191 @@ +/* + * Copyright 2021 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 "BTAudioProvidersFactory" + +#include "BluetoothAudioProvidersFactory.h" + +#include + +#include "BluetoothAudioSupportedCodecsDB_2_1.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +using ::android::hardware::hidl_vec; +using ::android::hardware::Void; +using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities; + +A2dpSoftwareAudioProvider + BluetoothAudioProvidersFactory::a2dp_software_provider_instance_; +A2dpOffloadAudioProvider + BluetoothAudioProvidersFactory::a2dp_offload_provider_instance_; +HearingAidAudioProvider + BluetoothAudioProvidersFactory::hearing_aid_provider_instance_; +LeAudioOutputAudioProvider + BluetoothAudioProvidersFactory::leaudio_output_provider_instance_; +LeAudioOffloadOutputAudioProvider + BluetoothAudioProvidersFactory::leaudio_offload_output_provider_instance_; +LeAudioInputAudioProvider + BluetoothAudioProvidersFactory::leaudio_input_provider_instance_; +LeAudioOffloadInputAudioProvider + BluetoothAudioProvidersFactory::leaudio_offload_input_provider_instance_; + +Return BluetoothAudioProvidersFactory::openProvider( + const V2_0::SessionType sessionType, openProvider_cb _hidl_cb) { + LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType); + BluetoothAudioStatus status = BluetoothAudioStatus::SUCCESS; + BluetoothAudioProvider* provider = nullptr; + + switch (sessionType) { + case V2_0::SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH: + provider = &a2dp_software_provider_instance_; + break; + case V2_0::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH: + provider = &a2dp_offload_provider_instance_; + break; + case V2_0::SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH: + provider = &hearing_aid_provider_instance_; + break; + default: + status = BluetoothAudioStatus::FAILURE; + } + + if (provider == nullptr || !provider->isValid(sessionType)) { + provider = nullptr; + status = BluetoothAudioStatus::FAILURE; + LOG(ERROR) << __func__ << " - SessionType=" << toString(sessionType) + << ", status=" << toString(status); + } + + _hidl_cb(status, provider); + return Void(); +} + +Return BluetoothAudioProvidersFactory::openProvider_2_1( + const V2_1::SessionType sessionType, openProvider_2_1_cb _hidl_cb) { + LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType); + BluetoothAudioStatus status = BluetoothAudioStatus::SUCCESS; + BluetoothAudioProvider* provider = nullptr; + + switch (sessionType) { + case V2_1::SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH: + provider = &a2dp_software_provider_instance_; + break; + case V2_1::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH: + provider = &a2dp_offload_provider_instance_; + break; + case V2_1::SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH: + provider = &hearing_aid_provider_instance_; + break; + case V2_1::SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH: + provider = &leaudio_output_provider_instance_; + break; + case V2_1::SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH: + provider = &leaudio_offload_output_provider_instance_; + break; + case V2_1::SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH: + provider = &leaudio_input_provider_instance_; + break; + case V2_1::SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH: + provider = &leaudio_offload_input_provider_instance_; + break; + default: + status = BluetoothAudioStatus::FAILURE; + } + + if (provider == nullptr || !provider->isValid(sessionType)) { + provider = nullptr; + status = BluetoothAudioStatus::FAILURE; + LOG(ERROR) << __func__ << " - SessionType=" << toString(sessionType) + << ", status=" << toString(status); + } + + _hidl_cb(status, provider); + return Void(); +} + +Return BluetoothAudioProvidersFactory::getProviderCapabilities( + const V2_0::SessionType sessionType, getProviderCapabilities_cb _hidl_cb) { + hidl_vec audio_capabilities = + hidl_vec(0); + if (sessionType == V2_0::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) { + std::vector db_codec_capabilities = + android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType); + if (db_codec_capabilities.size()) { + audio_capabilities.resize(db_codec_capabilities.size()); + for (int i = 0; i < db_codec_capabilities.size(); ++i) { + audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]); + } + } + } else if (sessionType != V2_0::SessionType::UNKNOWN) { + std::vector<::android::hardware::bluetooth::audio::V2_0::PcmParameters> + db_pcm_capabilities = + android::bluetooth::audio::GetSoftwarePcmCapabilities(); + if (db_pcm_capabilities.size() == 1) { + audio_capabilities.resize(1); + audio_capabilities[0].pcmCapabilities(db_pcm_capabilities[0]); + } + } + LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType) + << " supports " << audio_capabilities.size() << " codecs"; + _hidl_cb(audio_capabilities); + return Void(); +} + +Return BluetoothAudioProvidersFactory::getProviderCapabilities_2_1( + const V2_1::SessionType sessionType, + getProviderCapabilities_2_1_cb _hidl_cb) { + hidl_vec audio_capabilities = + hidl_vec(0); + if (sessionType == V2_1::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) { + std::vector db_codec_capabilities = + android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType); + if (db_codec_capabilities.size()) { + audio_capabilities.resize(db_codec_capabilities.size()); + for (int i = 0; i < db_codec_capabilities.size(); ++i) { + audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]); + } + } + } else if (sessionType != V2_1::SessionType::UNKNOWN) { + std::vector db_pcm_capabilities = + android::bluetooth::audio::GetSoftwarePcmCapabilities_2_1(); + if (db_pcm_capabilities.size() == 1) { + audio_capabilities.resize(1); + audio_capabilities[0].pcmCapabilities(db_pcm_capabilities[0]); + } + } + LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType) + << " supports " << audio_capabilities.size() << " codecs"; + _hidl_cb(audio_capabilities); + return Void(); +} + +IBluetoothAudioProvidersFactory* HIDL_FETCH_IBluetoothAudioProvidersFactory( + const char* /* name */) { + return new BluetoothAudioProvidersFactory(); +} + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvidersFactory.h b/bluetooth/audio/2.2/default/BluetoothAudioProvidersFactory.h new file mode 100644 index 0000000000..8db330b22b --- /dev/null +++ b/bluetooth/audio/2.2/default/BluetoothAudioProvidersFactory.h @@ -0,0 +1,73 @@ +/* + * Copyright 2021 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 "A2dpOffloadAudioProvider.h" +#include "A2dpSoftwareAudioProvider.h" +#include "BluetoothAudioProvider.h" +#include "HearingAidAudioProvider.h" +#include "LeAudioAudioProvider.h" +#include "LeAudioOffloadAudioProvider.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +class BluetoothAudioProvidersFactory : public IBluetoothAudioProvidersFactory { + public: + BluetoothAudioProvidersFactory() {} + + Return openProvider(const V2_0::SessionType sessionType, + openProvider_cb _hidl_cb) override; + + Return getProviderCapabilities( + const V2_0::SessionType sessionType, + getProviderCapabilities_cb _hidl_cb) override; + + Return openProvider_2_1(const V2_1::SessionType sessionType, + openProvider_2_1_cb _hidl_cb) override; + + Return getProviderCapabilities_2_1( + const V2_1::SessionType sessionType, + getProviderCapabilities_2_1_cb _hidl_cb) override; + + private: + static A2dpSoftwareAudioProvider a2dp_software_provider_instance_; + static A2dpOffloadAudioProvider a2dp_offload_provider_instance_; + static HearingAidAudioProvider hearing_aid_provider_instance_; + static LeAudioOutputAudioProvider leaudio_output_provider_instance_; + static LeAudioInputAudioProvider leaudio_input_provider_instance_; + static LeAudioOffloadOutputAudioProvider + leaudio_offload_output_provider_instance_; + static LeAudioOffloadInputAudioProvider + leaudio_offload_input_provider_instance_; +}; + +extern "C" IBluetoothAudioProvidersFactory* +HIDL_FETCH_IBluetoothAudioProvidersFactory(const char* name); + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/HearingAidAudioProvider.cpp b/bluetooth/audio/2.2/default/HearingAidAudioProvider.cpp new file mode 100644 index 0000000000..c79b910ab9 --- /dev/null +++ b/bluetooth/audio/2.2/default/HearingAidAudioProvider.cpp @@ -0,0 +1,112 @@ +/* + * Copyright 2021 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 "BTAudioProviderHearingAid" + +#include "HearingAidAudioProvider.h" + +#include + +#include "BluetoothAudioSessionReport_2_2.h" +#include "BluetoothAudioSupportedCodecsDB_2_1.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_2; +using ::android::hardware::Void; +using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration; + +static constexpr uint32_t kPcmFrameSize = 4; // 16 bits per sample / stereo +static constexpr uint32_t kPcmFrameCount = 128; +static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount; +static constexpr uint32_t kRtpFrameCount = 7; // max counts by 1 tick (20ms) +static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount; +static constexpr uint32_t kBufferCount = 1; // single buffer +static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount; + +HearingAidAudioProvider::HearingAidAudioProvider() + : BluetoothAudioProvider(), mDataMQ(nullptr) { + LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize + << " byte(s)"; + std::unique_ptr tempDataMQ( + new DataMQ(kDataMqSize, /* EventFlag */ true)); + if (tempDataMQ && tempDataMQ->isValid()) { + mDataMQ = std::move(tempDataMQ); + session_type_ = V2_1::SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH; + } else { + ALOGE_IF(!tempDataMQ, "failed to allocate data MQ"); + ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid"); + } +} + +bool HearingAidAudioProvider::isValid(const V2_0::SessionType& sessionType) { + return isValid(static_cast(sessionType)); +} + +bool HearingAidAudioProvider::isValid(const V2_1::SessionType& sessionType) { + return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid()); +} + +Return HearingAidAudioProvider::startSession( + const sp& hostIf, + const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + /** + * Initialize the audio platform if audioConfiguration is supported. + * Save the IBluetoothAudioPort interface, so that it can be used + * later to send stream control commands to the HAL client, based on + * interaction with Audio framework. + */ + if (audioConfig.getDiscriminator() != + AudioConfiguration::hidl_discriminator::pcmConfig) { + LOG(WARNING) << __func__ + << " - Invalid Audio Configuration=" << toString(audioConfig); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid( + audioConfig.pcmConfig())) { + LOG(WARNING) << __func__ << " - Unsupported PCM Configuration=" + << toString(audioConfig.pcmConfig()); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } + + return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb); +} + +Return HearingAidAudioProvider::onSessionReady(startSession_cb _hidl_cb) { + if (mDataMQ && mDataMQ->isValid()) { + BluetoothAudioSessionReport_2_2::OnSessionStarted( + session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_); + _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc()); + } else { + _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor()); + } + return Void(); +} + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/HearingAidAudioProvider.h b/bluetooth/audio/2.2/default/HearingAidAudioProvider.h new file mode 100644 index 0000000000..426c443d89 --- /dev/null +++ b/bluetooth/audio/2.2/default/HearingAidAudioProvider.h @@ -0,0 +1,59 @@ +/* + * Copyright 2021 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 "BluetoothAudioProvider.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +using ::android::hardware::kSynchronizedReadWrite; +using ::android::hardware::MessageQueue; + +using DataMQ = MessageQueue; + +class HearingAidAudioProvider : public BluetoothAudioProvider { + public: + HearingAidAudioProvider(); + + bool isValid(const V2_1::SessionType& sessionType) override; + bool isValid(const V2_0::SessionType& sessionType) override; + + Return startSession(const sp& hostIf, + const V2_0::AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + + private: + // audio data queue for software encoding + std::unique_ptr mDataMQ; + + Return onSessionReady(startSession_cb _hidl_cb) override; +}; + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/LeAudioAudioProvider.cpp b/bluetooth/audio/2.2/default/LeAudioAudioProvider.cpp new file mode 100644 index 0000000000..af6ec99a92 --- /dev/null +++ b/bluetooth/audio/2.2/default/LeAudioAudioProvider.cpp @@ -0,0 +1,219 @@ +/* + * Copyright 2021 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 "BTAudioProviderLeAudio" + +#include "LeAudioAudioProvider.h" + +#include + +#include "BluetoothAudioSessionReport_2_2.h" +#include "BluetoothAudioSupportedCodecsDB_2_1.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_2; +using ::android::hardware::Void; +using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample; +using ::android::hardware::bluetooth::audio::V2_0::ChannelMode; +using ::android::hardware::bluetooth::audio::V2_1::SampleRate; + +static constexpr uint32_t kBufferOutCount = 2; // two frame buffer +static constexpr uint32_t kBufferInCount = 2; // two frame buffer + +LeAudioOutputAudioProvider::LeAudioOutputAudioProvider() + : LeAudioAudioProvider() { + session_type_ = V2_1::SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH; +} + +LeAudioInputAudioProvider::LeAudioInputAudioProvider() + : LeAudioAudioProvider() { + session_type_ = V2_1::SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH; +} + +LeAudioAudioProvider::LeAudioAudioProvider() + : BluetoothAudioProvider(), mDataMQ(nullptr) {} + +bool LeAudioAudioProvider::isValid(const V2_0::SessionType& sessionType) { + LOG(ERROR) << __func__ << ", invalid session type for Le Audio provider: " + << toString(sessionType); + + return false; +} + +bool LeAudioAudioProvider::isValid(const V2_1::SessionType& sessionType) { + return (sessionType == session_type_); +} + +Return LeAudioAudioProvider::startSession_2_1( + const sp& hostIf, + const V2_1::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + if (audioConfig.getDiscriminator() != + V2_1::AudioConfiguration::hidl_discriminator::pcmConfig) { + LOG(WARNING) << __func__ + << " - Invalid Audio Configuration=" << toString(audioConfig); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } + + AudioConfiguration audioConfig_2_2; + audioConfig_2_2.pcmConfig( + {.sampleRate = + static_cast(audioConfig.pcmConfig().sampleRate), + .channelMode = audioConfig.pcmConfig().channelMode, + .bitsPerSample = audioConfig.pcmConfig().bitsPerSample, + .dataIntervalUs = 0}); + + return startSession_2_2(hostIf, audioConfig_2_2, _hidl_cb); +} + +Return LeAudioAudioProvider::startSession_2_2( + const sp& hostIf, + const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + /** + * Initialize the audio platform if audioConfiguration is supported. + * Save the IBluetoothAudioPort interface, so that it can be used + * later to send stream control commands to the HAL client, based on + * interaction with Audio framework. + */ + if (audioConfig.getDiscriminator() != + AudioConfiguration::hidl_discriminator::pcmConfig) { + LOG(WARNING) << __func__ + << " - Invalid Audio Configuration=" << toString(audioConfig); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid_2_1( + audioConfig.pcmConfig())) { + LOG(WARNING) << __func__ << " - Unsupported PCM Configuration=" + << toString(audioConfig.pcmConfig()); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } + + uint32_t kDataMqSize = 0; + switch (audioConfig.pcmConfig().sampleRate) { + case SampleRate::RATE_8000: + kDataMqSize = 8000; + break; + case SampleRate::RATE_16000: + kDataMqSize = 16000; + break; + case SampleRate::RATE_24000: + kDataMqSize = 24000; + break; + case SampleRate::RATE_32000: + kDataMqSize = 32000; + break; + case SampleRate::RATE_44100: + kDataMqSize = 44100; + break; + case SampleRate::RATE_48000: + kDataMqSize = 48000; + break; + default: + LOG(WARNING) << __func__ << " - Unsupported sampling frequency=" + << toString(audioConfig.pcmConfig()); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } + + /* Number of samples per millisecond */ + kDataMqSize = ceil(kDataMqSize / 1000); + + switch (audioConfig.pcmConfig().channelMode) { + case ChannelMode::MONO: + break; + case ChannelMode::STEREO: + kDataMqSize *= 2; + break; + default: + /* This should never happen it would be caught while validating + * parameters. + */ + break; + } + + switch (audioConfig.pcmConfig().bitsPerSample) { + case BitsPerSample::BITS_16: + kDataMqSize *= 2; + break; + case BitsPerSample::BITS_24: + kDataMqSize *= 3; + break; + case BitsPerSample::BITS_32: + kDataMqSize *= 4; + break; + default: + /* This should never happen it would be caught while validating + * parameters. + */ + break; + } + + if (session_type_ == V2_1::SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH) + kDataMqSize *= kBufferOutCount; + else if (session_type_ == + V2_1::SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH) + kDataMqSize *= kBufferInCount; + else + LOG(WARNING) << __func__ << ", default single buffer used"; + + kDataMqSize *= audioConfig.pcmConfig().dataIntervalUs / 1000; + + LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize + << " byte(s)"; + + std::unique_ptr tempDataMQ( + new DataMQ(kDataMqSize, /* EventFlag */ true)); + if (tempDataMQ && tempDataMQ->isValid()) { + mDataMQ = std::move(tempDataMQ); + } else { + ALOGE_IF(!tempDataMQ, "failed to allocate data MQ"); + ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid"); + _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor()); + return Void(); + } + + return BluetoothAudioProvider::startSession_2_2(hostIf, audioConfig, + _hidl_cb); +} + +Return LeAudioAudioProvider::onSessionReady(startSession_cb _hidl_cb) { + if (mDataMQ && mDataMQ->isValid()) { + BluetoothAudioSessionReport_2_2::OnSessionStarted( + session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_); + _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc()); + } else { + _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor()); + } + return Void(); +} + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/LeAudioAudioProvider.h b/bluetooth/audio/2.2/default/LeAudioAudioProvider.h new file mode 100644 index 0000000000..40c26e0cbf --- /dev/null +++ b/bluetooth/audio/2.2/default/LeAudioAudioProvider.h @@ -0,0 +1,74 @@ +/* + * Copyright 2021 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 "BluetoothAudioProvider.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +using ::android::hardware::kSynchronizedReadWrite; +using ::android::hardware::MessageQueue; + +using DataMQ = MessageQueue; + +class LeAudioAudioProvider : public BluetoothAudioProvider { + public: + LeAudioAudioProvider(); + + bool isValid(const V2_1::SessionType& sessionType) override; + bool isValid(const V2_0::SessionType& sessionType) override; + + Return startSession_2_1(const sp& hostIf, + const V2_1::AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + + Return startSession_2_2(const sp& hostIf, + const AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + + private: + /** queue for software encodec/decoded audio data */ + std::unique_ptr mDataMQ; + + Return onSessionReady(startSession_cb _hidl_cb) override; +}; + +class LeAudioOutputAudioProvider : public LeAudioAudioProvider { + public: + LeAudioOutputAudioProvider(); +}; + +class LeAudioInputAudioProvider : public LeAudioAudioProvider { + public: + LeAudioInputAudioProvider(); +}; + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.cpp new file mode 100644 index 0000000000..7b70654ba8 --- /dev/null +++ b/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.cpp @@ -0,0 +1,141 @@ +/* + * Copyright 2021 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 "BTAudioProviderLeAudioOffload" + +#include "LeAudioOffloadAudioProvider.h" + +#include + +#include "BluetoothAudioSessionReport_2_2.h" +#include "BluetoothAudioSupportedCodecsDB_2_1.h" +#include "BluetoothAudioSupportedCodecsDB_2_2.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_2; +using ::android::hardware::Void; +using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample; +using ::android::hardware::bluetooth::audio::V2_0::ChannelMode; +using ::android::hardware::bluetooth::audio::V2_1::SampleRate; + +using DataMQ = MessageQueue; + +LeAudioOffloadOutputAudioProvider::LeAudioOffloadOutputAudioProvider() + : LeAudioOffloadAudioProvider() { + session_type_ = + V2_1::SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH; +} + +LeAudioOffloadInputAudioProvider::LeAudioOffloadInputAudioProvider() + : LeAudioOffloadAudioProvider() { + session_type_ = + V2_1::SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH; +} + +LeAudioOffloadAudioProvider::LeAudioOffloadAudioProvider() + : BluetoothAudioProvider() {} + +bool LeAudioOffloadAudioProvider::isValid( + const V2_0::SessionType& sessionType) { + LOG(ERROR) << __func__ + << ", invalid session type for Offloaded Le Audio provider: " + << toString(sessionType); + + return false; +} + +bool LeAudioOffloadAudioProvider::isValid( + const V2_1::SessionType& sessionType) { + return (sessionType == session_type_); +} + +Return LeAudioOffloadAudioProvider::startSession_2_1( + const sp& hostIf, + const V2_1::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + if (audioConfig.getDiscriminator() != + V2_1::AudioConfiguration::hidl_discriminator::leAudioCodecConfig) { + LOG(WARNING) << __func__ + << " - Invalid Audio Configuration=" << toString(audioConfig); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } + + AudioConfiguration audioConfig_2_2; + audioConfig_2_2.leAudioConfig().mode = LeAudioMode::UNKNOWN; + audioConfig_2_2.leAudioConfig().config.unicastConfig() = { + .streamMap = {{ + .streamHandle = 0xFFFF, + .audioChannelAllocation = + audioConfig.leAudioCodecConfig().audioChannelAllocation, + }}, + .peerDelay = 0, + .lc3Config = audioConfig.leAudioCodecConfig().lc3Config}; + + return startSession_2_2(hostIf, audioConfig_2_2, _hidl_cb); +} + +Return LeAudioOffloadAudioProvider::startSession_2_2( + const sp& hostIf, + const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + /** + * Initialize the audio platform if audioConfiguration is supported. + * Save the IBluetoothAudioPort interface, so that it can be used + * later to send stream control commands to the HAL client, based on + * interaction with Audio framework. + */ + if (audioConfig.getDiscriminator() != + AudioConfiguration::hidl_discriminator::leAudioConfig) { + LOG(WARNING) << __func__ + << " - Invalid Audio Configuration=" << toString(audioConfig); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } + + if (!android::bluetooth::audio::IsOffloadLeAudioConfigurationValid( + session_type_, audioConfig.leAudioConfig())) { + LOG(WARNING) << __func__ << " - Unsupported LC3 Offloaded Configuration=" + << toString(audioConfig.leAudioConfig()); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } + + return BluetoothAudioProvider::startSession_2_2(hostIf, audioConfig, + _hidl_cb); +} + +Return LeAudioOffloadAudioProvider::onSessionReady( + startSession_cb _hidl_cb) { + BluetoothAudioSessionReport_2_2::OnSessionStarted(session_type_, stack_iface_, + nullptr, audio_config_); + _hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor()); + return Void(); +} + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.h b/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.h new file mode 100644 index 0000000000..5620295530 --- /dev/null +++ b/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.h @@ -0,0 +1,64 @@ +/* + * Copyright 2021 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 "BluetoothAudioProvider.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_2 { +namespace implementation { + +class LeAudioOffloadAudioProvider : public BluetoothAudioProvider { + public: + LeAudioOffloadAudioProvider(); + + bool isValid(const V2_1::SessionType& sessionType) override; + bool isValid(const V2_0::SessionType& sessionType) override; + + Return startSession_2_1(const sp& hostIf, + const V2_1::AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + + Return startSession_2_2(const sp& hostIf, + const AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + + private: + Return onSessionReady(startSession_cb _hidl_cb) override; +}; + +class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider { + public: + LeAudioOffloadOutputAudioProvider(); +}; + +class LeAudioOffloadInputAudioProvider : public LeAudioOffloadAudioProvider { + public: + LeAudioOffloadInputAudioProvider(); +}; + +} // namespace implementation +} // namespace V2_2 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.2/types.hal b/bluetooth/audio/2.2/types.hal new file mode 100644 index 0000000000..d5f8a3fc78 --- /dev/null +++ b/bluetooth/audio/2.2/types.hal @@ -0,0 +1,72 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.bluetooth.audio@2.2; + +import @2.1::Lc3Parameters; +import @2.1::PcmParameters; +import @2.0::CodecConfiguration; + +enum LeAudioMode : uint8_t { + UNKNOWN = 0x00, + UNICAST = 0x01, + BROADCAST = 0x02, +}; + +struct UnicastStreamMap { + /* The connection handle used for a unicast or a broadcast group. */ + uint16_t streamHandle; + /* Audio channel allocation is a bit field, each enabled bit means that given audio direction, + * i.e. "left", or "right" is used. Ordering of audio channels comes from the least significant + * bit to the most significant bit. */ + uint32_t audioChannelAllocation; +}; + +struct BroadcastStreamMap { + /* The connection handle used for a unicast or a broadcast group. */ + uint16_t streamHandle; + /* Audio channel allocation is a bit field, each enabled bit means that given audio direction, + * i.e. "left", or "right" is used. Ordering of audio channels comes from the least significant + * bit to the most significant bit. */ + uint32_t audioChannelAllocation; + Lc3Parameters lc3Config; +}; + +struct UnicastConfig { + vec streamMap; + uint32_t peerDelay; + Lc3Parameters lc3Config; +}; + +struct BroadcastConfig { + vec streamMap; +}; + +struct LeAudioConfiguration { + /* The mode of the LE audio */ + LeAudioMode mode; + safe_union CodecConfig { + UnicastConfig unicastConfig; + BroadcastConfig broadcastConfig; + } config; +}; + +/** Used to configure either a Hardware or Software Encoding session based on session type */ +safe_union AudioConfiguration { + PcmParameters pcmConfig; + CodecConfiguration codecConfig; + LeAudioConfiguration leAudioConfig; +}; diff --git a/bluetooth/audio/utils/Android.bp b/bluetooth/audio/utils/Android.bp index 551bc50618..19d2d920ba 100644 --- a/bluetooth/audio/utils/Android.bp +++ b/bluetooth/audio/utils/Android.bp @@ -14,14 +14,17 @@ cc_library_shared { srcs: [ "session/BluetoothAudioSession.cpp", "session/BluetoothAudioSession_2_1.cpp", + "session/BluetoothAudioSession_2_2.cpp", "session/BluetoothAudioSupportedCodecsDB.cpp", "session/BluetoothAudioSupportedCodecsDB_2_1.cpp", + "session/BluetoothAudioSupportedCodecsDB_2_2.cpp", ], export_include_dirs: ["session/"], header_libs: ["libhardware_headers"], shared_libs: [ "android.hardware.bluetooth.audio@2.0", "android.hardware.bluetooth.audio@2.1", + "android.hardware.bluetooth.audio@2.2", "libbase", "libcutils", "libfmq", diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession.h b/bluetooth/audio/utils/session/BluetoothAudioSession.h index 83e20ad588..3469cc05b6 100644 --- a/bluetooth/audio/utils/session/BluetoothAudioSession.h +++ b/bluetooth/audio/utils/session/BluetoothAudioSession.h @@ -80,6 +80,7 @@ struct PortStatusCallbacks { class BluetoothAudioSession { friend class BluetoothAudioSession_2_1; + friend class BluetoothAudioSession_2_2; private: // using recursive_mutex to allow hwbinder to re-enter agian. diff --git a/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h new file mode 100644 index 0000000000..194259ae67 --- /dev/null +++ b/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h @@ -0,0 +1,70 @@ +/* + * Copyright 2021 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 "BluetoothAudioSession_2_2.h" + +namespace android { +namespace bluetooth { +namespace audio { + +class BluetoothAudioSessionReport_2_2 { + public: + // The API reports the Bluetooth stack has started the session, and will + // inform registered bluetooth_audio outputs + static void OnSessionStarted( + const ::android::hardware::bluetooth::audio::V2_1::SessionType& + session_type, + const sp host_iface, + const DataMQ::Descriptor* dataMQ, + const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration& + audio_config) { + std::shared_ptr session_ptr = + BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type); + if (session_ptr != nullptr) { + session_ptr->OnSessionStarted(host_iface, dataMQ, audio_config); + } + } + + // The API reports the Bluetooth stack has ended the session, and will + // inform registered bluetooth_audio outputs + static void OnSessionEnded( + const ::android::hardware::bluetooth::audio::V2_1::SessionType& + session_type) { + std::shared_ptr session_ptr = + BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type); + if (session_ptr != nullptr) { + session_ptr->GetAudioSession()->OnSessionEnded(); + } + } + // The API reports the Bluetooth stack has replied the result of startStream + // or suspendStream, and will inform registered bluetooth_audio outputs + static void ReportControlStatus( + const ::android::hardware::bluetooth::audio::V2_1::SessionType& + session_type, + const bool& start_resp, const BluetoothAudioStatus& status) { + std::shared_ptr session_ptr = + BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type); + if (session_ptr != nullptr) { + session_ptr->GetAudioSession()->ReportControlStatus(start_resp, status); + } + } +}; + +} // namespace audio +} // namespace bluetooth +} // namespace android diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp new file mode 100644 index 0000000000..9d9ea41fd3 --- /dev/null +++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp @@ -0,0 +1,227 @@ +/* + * Copyright 2018 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 "BTAudioProviderSession_2_2" + +#include "BluetoothAudioSession_2_2.h" + +#include +#include + +namespace android { +namespace bluetooth { +namespace audio { +using SessionType_2_1 = + ::android::hardware::bluetooth::audio::V2_1::SessionType; +using SessionType_2_0 = + ::android::hardware::bluetooth::audio::V2_0::SessionType; + +::android::hardware::bluetooth::audio::V2_2::AudioConfiguration + BluetoothAudioSession_2_2::invalidSoftwareAudioConfiguration = {}; +::android::hardware::bluetooth::audio::V2_2::AudioConfiguration + BluetoothAudioSession_2_2::invalidOffloadAudioConfiguration = {}; + +namespace { +bool is_2_0_session_type( + const ::android::hardware::bluetooth::audio::V2_1::SessionType& + session_type) { + if (session_type == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH || + session_type == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH || + session_type == SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH) { + return true; + } else { + return false; + } +} +} // namespace + +BluetoothAudioSession_2_2::BluetoothAudioSession_2_2( + const ::android::hardware::bluetooth::audio::V2_1::SessionType& + session_type) + : audio_session(BluetoothAudioSessionInstance::GetSessionInstance( + static_cast(session_type))) { + if (is_2_0_session_type(session_type)) { + session_type_2_1_ = (SessionType_2_1::UNKNOWN); + } else { + session_type_2_1_ = (session_type); + } +} + +bool BluetoothAudioSession_2_2::IsSessionReady() { + if (session_type_2_1_ != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH) { + return audio_session->IsSessionReady(); + } + + std::lock_guard guard(audio_session->mutex_); + return audio_session->stack_iface_ != nullptr; +} + +std::shared_ptr +BluetoothAudioSession_2_2::GetAudioSession() { + return audio_session; +} + +// The control function is for the bluetooth_audio module to get the current +// AudioConfiguration +const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration +BluetoothAudioSession_2_2::GetAudioConfig() { + std::lock_guard guard(audio_session->mutex_); + if (IsSessionReady()) { + // If session is unknown it means it should be 2.0 type + if (session_type_2_1_ != SessionType_2_1::UNKNOWN) return audio_config_2_2_; + + ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration toConf; + const AudioConfiguration fromConf = GetAudioSession()->GetAudioConfig(); + // pcmConfig only differs between 2.0 and 2.1 in AudioConfiguration + if (fromConf.getDiscriminator() == + AudioConfiguration::hidl_discriminator::codecConfig) { + toConf.codecConfig() = fromConf.codecConfig(); + } else { + toConf.pcmConfig() = { + .sampleRate = static_cast< + ::android::hardware::bluetooth::audio::V2_1::SampleRate>( + fromConf.pcmConfig().sampleRate), + .channelMode = fromConf.pcmConfig().channelMode, + .bitsPerSample = fromConf.pcmConfig().bitsPerSample, + .dataIntervalUs = 0}; + } + return toConf; + } else if (session_type_2_1_ == + SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH) { + return kInvalidOffloadAudioConfiguration; + } else { + return kInvalidSoftwareAudioConfiguration; + } +} + +bool BluetoothAudioSession_2_2::UpdateAudioConfig( + const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration& + audio_config) { + bool is_software_session = + (session_type_2_1_ == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH || + session_type_2_1_ == + SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH || + session_type_2_1_ == + SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH || + session_type_2_1_ == + SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH); + bool is_offload_a2dp_session = + (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH); + bool is_offload_le_audio_session = + (session_type_2_1_ == + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH || + session_type_2_1_ == + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH); + auto audio_config_discriminator = audio_config.getDiscriminator(); + bool is_software_audio_config = + (is_software_session && + audio_config_discriminator == + ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration:: + hidl_discriminator::pcmConfig); + bool is_a2dp_offload_audio_config = + (is_offload_a2dp_session && + audio_config_discriminator == + ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration:: + hidl_discriminator::codecConfig); + bool is_le_audio_offload_audio_config = + (is_offload_le_audio_session && + audio_config_discriminator == + ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration:: + hidl_discriminator::leAudioConfig); + if (!is_software_audio_config && !is_a2dp_offload_audio_config && + !is_le_audio_offload_audio_config) { + return false; + } + audio_config_2_2_ = audio_config; + return true; +} + +// The report function is used to report that the Bluetooth stack has started +// this session without any failure, and will invoke session_changed_cb_ to +// notify those registered bluetooth_audio outputs +void BluetoothAudioSession_2_2::OnSessionStarted( + const sp stack_iface, const DataMQ::Descriptor* dataMQ, + const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration& + audio_config) { + if (session_type_2_1_ == SessionType_2_1::UNKNOWN) { + ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration config; + if (audio_config.getDiscriminator() == + ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration:: + hidl_discriminator::codecConfig) { + config.codecConfig(audio_config.codecConfig()); + } else { + auto& tmpPcm = audio_config.pcmConfig(); + config.pcmConfig( + ::android::hardware::bluetooth::audio::V2_0::PcmParameters{ + .sampleRate = static_cast(tmpPcm.sampleRate), + .channelMode = tmpPcm.channelMode, + .bitsPerSample = tmpPcm.bitsPerSample + /*dataIntervalUs is not passed to 2.0 */ + }); + } + + audio_session->OnSessionStarted(stack_iface, dataMQ, config); + } else { + std::lock_guard guard(audio_session->mutex_); + if (stack_iface == nullptr) { + LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << ", IBluetoothAudioPort Invalid"; + } else if (!UpdateAudioConfig(audio_config)) { + LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << ", AudioConfiguration=" << toString(audio_config) + << " Invalid"; + } else if (!audio_session->UpdateDataPath(dataMQ)) { + LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << " DataMQ Invalid"; + audio_config_2_2_ = + (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH + ? kInvalidOffloadAudioConfiguration + : kInvalidSoftwareAudioConfiguration); + } else { + audio_session->stack_iface_ = stack_iface; + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_) + << ", AudioConfiguration=" << toString(audio_config); + audio_session->ReportSessionStatus(); + }; + } +} + +std::unique_ptr + BluetoothAudioSessionInstance_2_2::instance_ptr = + std::unique_ptr( + new BluetoothAudioSessionInstance_2_2()); + +// API to fetch the session of A2DP / Hearing Aid +std::shared_ptr +BluetoothAudioSessionInstance_2_2::GetSessionInstance( + const SessionType_2_1& session_type) { + std::lock_guard guard(instance_ptr->mutex_); + if (!instance_ptr->sessions_map_.empty()) { + auto entry = instance_ptr->sessions_map_.find(session_type); + if (entry != instance_ptr->sessions_map_.end()) { + return entry->second; + } + } + std::shared_ptr session_ptr = + std::make_shared(session_type); + instance_ptr->sessions_map_[session_type] = session_ptr; + return session_ptr; +} + +} // namespace audio +} // namespace bluetooth +} // namespace android diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h new file mode 100644 index 0000000000..d3d0bd3dd9 --- /dev/null +++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h @@ -0,0 +1,99 @@ +/* + * Copyright 2018 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 "BluetoothAudioSession.h" + +namespace android { +namespace bluetooth { +namespace audio { + +class BluetoothAudioSession_2_2 { + private: + std::shared_ptr audio_session; + + ::android::hardware::bluetooth::audio::V2_1::SessionType session_type_2_1_; + + // audio data configuration for both software and offloading + ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration + audio_config_2_2_; + + bool UpdateAudioConfig( + const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration& + audio_config); + + static ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration + invalidSoftwareAudioConfiguration; + static ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration + invalidOffloadAudioConfiguration; + + public: + BluetoothAudioSession_2_2( + const ::android::hardware::bluetooth::audio::V2_1::SessionType& + session_type); + + // The function helps to check if this session is ready or not + // @return: true if the Bluetooth stack has started the specified session + bool IsSessionReady(); + + std::shared_ptr GetAudioSession(); + + // The report function is used to report that the Bluetooth stack has started + // this session without any failure, and will invoke session_changed_cb_ to + // notify those registered bluetooth_audio outputs + void OnSessionStarted( + const sp stack_iface, + const DataMQ::Descriptor* dataMQ, + const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration& + audio_config); + + // The control function is for the bluetooth_audio module to get the current + // AudioConfiguration + const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration + GetAudioConfig(); + + static constexpr ::android::hardware::bluetooth::audio::V2_2:: + AudioConfiguration& kInvalidSoftwareAudioConfiguration = + invalidSoftwareAudioConfiguration; + static constexpr ::android::hardware::bluetooth::audio::V2_2:: + AudioConfiguration& kInvalidOffloadAudioConfiguration = + invalidOffloadAudioConfiguration; +}; + +class BluetoothAudioSessionInstance_2_2 { + public: + // The API is to fetch the specified session of A2DP / Hearing Aid + static std::shared_ptr GetSessionInstance( + const ::android::hardware::bluetooth::audio::V2_1::SessionType& + session_type); + + private: + static std::unique_ptr instance_ptr; + std::mutex mutex_; + std::unordered_map<::android::hardware::bluetooth::audio::V2_1::SessionType, + std::shared_ptr> + sessions_map_; +}; + +} // namespace audio +} // namespace bluetooth +} // namespace android diff --git a/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_2.cpp b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_2.cpp new file mode 100644 index 0000000000..5becdaa2d0 --- /dev/null +++ b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_2.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2021 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 "BTAudioProviderSessionCodecsDB_2_2" + +#include "BluetoothAudioSupportedCodecsDB_2_2.h" + +#include + +namespace android { +namespace bluetooth { +namespace audio { + +using SessionType_2_1 = + ::android::hardware::bluetooth::audio::V2_1::SessionType; + +bool IsOffloadLeAudioConfigurationValid( + const ::android::hardware::bluetooth::audio::V2_1::SessionType& + session_type, + const ::android::hardware::bluetooth::audio::V2_2::LeAudioConfiguration&) { + if (session_type != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH && + session_type != + SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) { + return false; + } + + // TODO: perform checks on le_audio_codec_config once we know supported + // parameters + + return true; +} + +} // namespace audio +} // namespace bluetooth +} // namespace android diff --git a/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_2.h new file mode 100644 index 0000000000..59d22b7a0a --- /dev/null +++ b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_2.h @@ -0,0 +1,34 @@ +/* + * Copyright 2021 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 "BluetoothAudioSupportedCodecsDB.h" + +namespace android { +namespace bluetooth { +namespace audio { + +bool IsOffloadLeAudioConfigurationValid( + const ::android::hardware::bluetooth::audio::V2_1::SessionType& + session_type, + const ::android::hardware::bluetooth::audio::V2_2::LeAudioConfiguration& + le_audio_codec_config); +} // namespace audio +} // namespace bluetooth +} // namespace android diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index b2c0ccde96..ece4de73cc 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -133,7 +133,7 @@ android.hardware.bluetooth.audio - 2.0-1 + 2.0-2 IBluetoothAudioProvidersFactory default