Introduce LE Audio software provider for bluetooth audio 2.1
Patch introduces LE Audio software encoding provider for bluetooth audio 2.1.
Bug: 150670922
Tag: #feature
Test: vts-tradefed run vts -m VtsHalBluetoothAudioV2_1TargetTest
Sponsor: jpawlowski@
Change-Id: If668593be7a46c9abcc45d21c5bdb9b493864cc1
diff --git a/bluetooth/audio/2.1/Android.bp b/bluetooth/audio/2.1/Android.bp
new file mode 100644
index 0000000..9af8add
--- /dev/null
+++ b/bluetooth/audio/2.1/Android.bp
@@ -0,0 +1,22 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.bluetooth.audio@2.1",
+ root: "android.hardware",
+ srcs: [
+ "types.hal",
+ "IBluetoothAudioProvider.hal",
+ "IBluetoothAudioProvidersFactory.hal",
+ ],
+ interfaces: [
+ "android.hardware.audio.common@5.0",
+ "android.hardware.bluetooth.audio@2.0",
+ "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.1/IBluetoothAudioProvider.hal b/bluetooth/audio/2.1/IBluetoothAudioProvider.hal
new file mode 100644
index 0000000..c462b9e
--- /dev/null
+++ b/bluetooth/audio/2.1/IBluetoothAudioProvider.hal
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.bluetooth.audio@2.1;
+
+import @2.0::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.0::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_1(IBluetoothAudioPort hostIf, AudioConfiguration audioConfig)
+ generates (Status status, fmq_sync<uint8_t> dataMQ);
+};
diff --git a/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.hal b/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.hal
new file mode 100644
index 0000000..5593c7c
--- /dev/null
+++ b/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.hal
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.bluetooth.audio@2.1;
+
+import IBluetoothAudioProvider;
+import @2.0::IBluetoothAudioProvidersFactory;
+import @2.0::Status;
+
+/**
+ * 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.0::IBluetoothAudioProvidersFactory {
+
+ /**
+ * Opens an audio provider for a session type. To close the provider, it is
+ * necessary to release references to the returned provider object.
+ *
+ * @param sessionType The session type (e.g.
+ * LE_AUDIO_SOFTWARE_ENCODING_DATAPATH).
+ *
+ * @return status One of the following
+ * SUCCESS if the Audio HAL successfully opens the provider with the
+ * given session type
+ * FAILURE if the Audio HAL cannot open the provider
+ * @return provider The provider of the specified session type
+ */
+ openProvider_2_1(SessionType sessionType)
+ generates (Status status, IBluetoothAudioProvider provider);
+
+ /**
+ * Gets a list of audio capabilities for a session type.
+ *
+ * For software encoding, the PCM capabilities are returned.
+ * For hardware encoding, the supported codecs and their capabilities are
+ * returned.
+ *
+ * @param sessionType The session type (e.g.
+ * A2DP_SOFTWARE_ENCODING_DATAPATH).
+ * @return audioCapabilities A list containing all the capabilities
+ * supported by the sesson type. The capabilities is a list of
+ * available options when configuring the codec for the session.
+ * For software encoding it is the PCM data rate.
+ * For hardware encoding it is the list of supported codecs and their
+ * capabilities.
+ * If a provider isn't supported, an empty list should be returned.
+ * Note: Only one entry should exist per codec when using hardware
+ * encoding.
+ */
+ getProviderCapabilities_2_1(SessionType sessionType)
+ generates (vec<AudioCapabilities> audioCapabilities);
+};
diff --git a/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.cpp
new file mode 100644
index 0000000..b4a61b6
--- /dev/null
+++ b/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderA2dpOffload"
+
+#include "A2dpOffloadAudioProvider.h"
+
+#include <android-base/logging.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+A2dpOffloadAudioProvider::A2dpOffloadAudioProvider()
+ : BluetoothAudioProvider() {
+ session_type_ = SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH;
+}
+
+bool A2dpOffloadAudioProvider::isValid(const V2_0::SessionType& sessionType) {
+ return isValid(static_cast<SessionType>(sessionType));
+}
+
+bool A2dpOffloadAudioProvider::isValid(const SessionType& sessionType) {
+ return (sessionType == session_type_);
+}
+
+Return<void> A2dpOffloadAudioProvider::startSession(
+ const sp<IBluetoothAudioPort>& 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<void> A2dpOffloadAudioProvider::onSessionReady(
+ startSession_cb _hidl_cb) {
+ BluetoothAudioSessionReport::OnSessionStarted(session_type_, stack_iface_,
+ nullptr, audio_config_);
+ _hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor());
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.h b/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.h
new file mode 100644
index 0000000..13e0b9a
--- /dev/null
+++ b/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+class A2dpOffloadAudioProvider : public BluetoothAudioProvider {
+ public:
+ A2dpOffloadAudioProvider();
+
+ bool isValid(const SessionType& sessionType) override;
+ bool isValid(const V2_0::SessionType& sessionType) override;
+
+ Return<void> startSession(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const V2_0::AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ private:
+ Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.cpp b/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.cpp
new file mode 100644
index 0000000..a67c341
--- /dev/null
+++ b/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderA2dpSoftware"
+
+#include "A2dpSoftwareAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+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 = 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<DataMQ> tempDataMQ(
+ new DataMQ(kDataMqSize, /* EventFlag */ true));
+ if (tempDataMQ && tempDataMQ->isValid()) {
+ mDataMQ = std::move(tempDataMQ);
+ session_type_ = 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>(sessionType));
+}
+
+bool A2dpSoftwareAudioProvider::isValid(const SessionType& sessionType) {
+ return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid());
+}
+
+Return<void> A2dpSoftwareAudioProvider::startSession(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const V2_0::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<void> A2dpSoftwareAudioProvider::onSessionReady(
+ startSession_cb _hidl_cb) {
+ if (mDataMQ && mDataMQ->isValid()) {
+ BluetoothAudioSessionReport::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_1
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.h b/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.h
new file mode 100644
index 0000000..20566d1
--- /dev/null
+++ b/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class A2dpSoftwareAudioProvider : public BluetoothAudioProvider {
+ public:
+ A2dpSoftwareAudioProvider();
+
+ bool isValid(const SessionType& sessionType) override;
+ bool isValid(const V2_0::SessionType& sessionType) override;
+
+ Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ const V2_0::AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ private:
+ // audio data queue for software encoding
+ std::unique_ptr<DataMQ> mDataMQ;
+
+ Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/Android.bp b/bluetooth/audio/2.1/default/Android.bp
new file mode 100644
index 0000000..5381fec
--- /dev/null
+++ b/bluetooth/audio/2.1/default/Android.bp
@@ -0,0 +1,48 @@
+cc_library_shared {
+ name: "android.hardware.bluetooth.audio@2.1-impl",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "BluetoothAudioProvidersFactory.cpp",
+ "BluetoothAudioProvider.cpp",
+ "A2dpOffloadAudioProvider.cpp",
+ "A2dpSoftwareAudioProvider.cpp",
+ "HearingAidAudioProvider.cpp",
+ "LeAudioAudioProvider.cpp",
+ ],
+ header_libs: ["libhardware_headers"],
+ shared_libs: [
+ "android.hardware.bluetooth.audio@2.0",
+ "android.hardware.bluetooth.audio@2.1",
+ "libbase",
+ "libbluetooth_audio_session_2_1",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ],
+}
+
+cc_library_shared {
+ name: "libbluetooth_audio_session_2_1",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ srcs: [
+ "session/BluetoothAudioSession.cpp",
+ "session/BluetoothAudioSupportedCodecsDB.cpp",
+ ],
+ export_include_dirs: ["session/"],
+ header_libs: ["libhardware_headers"],
+ shared_libs: [
+ "android.hardware.bluetooth.audio@2.0",
+ "android.hardware.bluetooth.audio@2.1",
+ "libbase",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
new file mode 100644
index 0000000..0f349a4
--- /dev/null
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderStub"
+
+#include "BluetoothAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Void;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+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_(SessionType::UNKNOWN),
+ audio_config_({}) {}
+
+Return<void> BluetoothAudioProvider::startSession(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const V2_0::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ AudioConfiguration audioConfig_2_1;
+
+ audioConfig_2_1.codecConfig() = audioConfig.codecConfig();
+ audioConfig_2_1.pcmConfig() = {
+ .sampleRate = static_cast<SampleRate>(audioConfig.pcmConfig().sampleRate),
+ .channelMode = audioConfig.pcmConfig().channelMode,
+ .bitsPerSample = audioConfig.pcmConfig().bitsPerSample,
+ .dataIntervalUs = 0};
+
+ return startSession_2_1(hostIf, audioConfig_2_1, _hidl_cb);
+}
+
+Return<void> BluetoothAudioProvider::startSession_2_1(
+ const sp<IBluetoothAudioPort>& 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<void> 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::ReportControlStatus(session_type_, true,
+ status);
+ } else {
+ LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", status=" << toString(status) << " has NO session";
+ }
+
+ return Void();
+}
+
+Return<void> 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::ReportControlStatus(session_type_, false,
+ status);
+ } else {
+ LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", status=" << toString(status) << " has NO session";
+ }
+
+ return Void();
+}
+
+Return<void> BluetoothAudioProvider::endSession() {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
+
+ if (stack_iface_) {
+ BluetoothAudioSessionReport::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_1
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvider.h b/bluetooth/audio/2.1/default/BluetoothAudioProvider.h
new file mode 100644
index 0000000..a2e611f
--- /dev/null
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvider.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.1/IBluetoothAudioProvider.h>
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+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 SessionType& sessionType) = 0;
+ virtual bool isValid(const V2_0::SessionType& sessionType) = 0;
+
+ Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ const V2_0::AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+ Return<void> startSession_2_1(const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+ Return<void> streamStarted(BluetoothAudioStatus status) override;
+ Return<void> streamSuspended(BluetoothAudioStatus status) override;
+ Return<void> endSession() override;
+
+ protected:
+ sp<BluetoothAudioDeathRecipient> death_recipient_;
+
+ SessionType session_type_;
+ AudioConfiguration audio_config_;
+ sp<V2_0::IBluetoothAudioPort> stack_iface_;
+
+ virtual Return<void> onSessionReady(startSession_cb _hidl_cb) = 0;
+};
+
+class BluetoothAudioDeathRecipient : public hidl_death_recipient {
+ public:
+ BluetoothAudioDeathRecipient(const sp<BluetoothAudioProvider> provider)
+ : provider_(provider) {}
+
+ virtual void serviceDied(
+ uint64_t cookie,
+ const wp<::android::hidl::base::V1_0::IBase>& who) override;
+
+ private:
+ sp<BluetoothAudioProvider> provider_;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp
new file mode 100644
index 0000000..adf2717
--- /dev/null
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProvidersFactory"
+
+#include "BluetoothAudioProvidersFactory.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+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_;
+LeAudioInputAudioProvider
+ BluetoothAudioProvidersFactory::leaudio_input_provider_instance_;
+
+Return<void> 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<void> BluetoothAudioProvidersFactory::openProvider_2_1(
+ const SessionType sessionType, openProvider_2_1_cb _hidl_cb) {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType);
+ BluetoothAudioStatus status = BluetoothAudioStatus::SUCCESS;
+ BluetoothAudioProvider* provider = nullptr;
+ switch (sessionType) {
+ case SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH:
+ provider = &a2dp_software_provider_instance_;
+ break;
+ case SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH:
+ provider = &a2dp_offload_provider_instance_;
+ break;
+ case SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH:
+ provider = &hearing_aid_provider_instance_;
+ break;
+ case SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH:
+ provider = &leaudio_output_provider_instance_;
+ break;
+ case SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH:
+ provider = &leaudio_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<void> BluetoothAudioProvidersFactory::getProviderCapabilities(
+ const V2_0::SessionType sessionType, getProviderCapabilities_cb _hidl_cb) {
+ hidl_vec<V2_0::AudioCapabilities> audio_capabilities =
+ hidl_vec<V2_0::AudioCapabilities>(0);
+ if (sessionType == V2_0::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ std::vector<CodecCapabilities> 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<void> BluetoothAudioProvidersFactory::getProviderCapabilities_2_1(
+ const SessionType sessionType, getProviderCapabilities_2_1_cb _hidl_cb) {
+ hidl_vec<AudioCapabilities> audio_capabilities =
+ hidl_vec<AudioCapabilities>(0);
+ if (sessionType == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ std::vector<CodecCapabilities> 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 != SessionType::UNKNOWN) {
+ std::vector<PcmParameters> 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_1
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.h b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.h
new file mode 100644
index 0000000..fd83694
--- /dev/null
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.h>
+
+#include "A2dpOffloadAudioProvider.h"
+#include "A2dpSoftwareAudioProvider.h"
+#include "BluetoothAudioProvider.h"
+#include "HearingAidAudioProvider.h"
+#include "LeAudioAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+class BluetoothAudioProvidersFactory : public IBluetoothAudioProvidersFactory {
+ public:
+ BluetoothAudioProvidersFactory() {}
+
+ Return<void> openProvider(const V2_0::SessionType sessionType,
+ openProvider_cb _hidl_cb) override;
+
+ Return<void> getProviderCapabilities(
+ const V2_0::SessionType sessionType,
+ getProviderCapabilities_cb _hidl_cb) override;
+
+ Return<void> openProvider_2_1(const SessionType sessionType,
+ openProvider_2_1_cb _hidl_cb) override;
+
+ Return<void> getProviderCapabilities_2_1(
+ const 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_;
+};
+
+extern "C" IBluetoothAudioProvidersFactory*
+HIDL_FETCH_IBluetoothAudioProvidersFactory(const char* name);
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/HearingAidAudioProvider.cpp b/bluetooth/audio/2.1/default/HearingAidAudioProvider.cpp
new file mode 100644
index 0000000..aded7e1
--- /dev/null
+++ b/bluetooth/audio/2.1/default/HearingAidAudioProvider.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderHearingAid"
+
+#include "HearingAidAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+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<DataMQ> tempDataMQ(
+ new DataMQ(kDataMqSize, /* EventFlag */ true));
+ if (tempDataMQ && tempDataMQ->isValid()) {
+ mDataMQ = std::move(tempDataMQ);
+ session_type_ = 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>(sessionType));
+}
+
+bool HearingAidAudioProvider::isValid(const SessionType& sessionType) {
+ return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid());
+}
+
+Return<void> HearingAidAudioProvider::startSession(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const V2_0::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<void> HearingAidAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
+ if (mDataMQ && mDataMQ->isValid()) {
+ BluetoothAudioSessionReport::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_1
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/HearingAidAudioProvider.h b/bluetooth/audio/2.1/default/HearingAidAudioProvider.h
new file mode 100644
index 0000000..c949257
--- /dev/null
+++ b/bluetooth/audio/2.1/default/HearingAidAudioProvider.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class HearingAidAudioProvider : public BluetoothAudioProvider {
+ public:
+ HearingAidAudioProvider();
+
+ bool isValid(const SessionType& sessionType) override;
+ bool isValid(const V2_0::SessionType& sessionType) override;
+
+ Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ const V2_0::AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ private:
+ // audio data queue for software encoding
+ std::unique_ptr<DataMQ> mDataMQ;
+
+ Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/LeAudioAudioProvider.cpp b/bluetooth/audio/2.1/default/LeAudioAudioProvider.cpp
new file mode 100644
index 0000000..1fa2dce
--- /dev/null
+++ b/bluetooth/audio/2.1/default/LeAudioAudioProvider.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2020 HIMSA II K/S - www.himsa.com. Represented by EHIMA -
+ * www.ehima.com
+ *
+ * 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 <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+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_ = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
+}
+
+LeAudioInputAudioProvider::LeAudioInputAudioProvider()
+ : LeAudioAudioProvider() {
+ session_type_ = 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 SessionType& sessionType) {
+ return (sessionType == session_type_);
+}
+
+Return<void> LeAudioAudioProvider::startSession_2_1(
+ const sp<V2_0::IBluetoothAudioPort>& 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_16000:
+ kDataMqSize = 16000;
+ break;
+ case SampleRate::RATE_24000:
+ kDataMqSize = 24000;
+ break;
+ case SampleRate::RATE_44100:
+ kDataMqSize = 44100;
+ break;
+ case SampleRate::RATE_48000:
+ kDataMqSize = 48000;
+ break;
+ case SampleRate::RATE_88200:
+ kDataMqSize = 88200;
+ break;
+ case SampleRate::RATE_96000:
+ kDataMqSize = 96000;
+ break;
+ case SampleRate::RATE_176400:
+ kDataMqSize = 176400;
+ break;
+ case SampleRate::RATE_192000:
+ kDataMqSize = 192000;
+ break;
+ default:
+ /* This should never happen it would be caught while validating
+ * parameters.
+ */
+ break;
+ }
+
+ /* 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_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH)
+ kDataMqSize *= kBufferOutCount;
+ else if (session_type_ == 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<DataMQ> 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_1(hostIf, audioConfig,
+ _hidl_cb);
+}
+
+Return<void> LeAudioAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
+ if (mDataMQ && mDataMQ->isValid()) {
+ BluetoothAudioSessionReport::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_1
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/LeAudioAudioProvider.h b/bluetooth/audio/2.1/default/LeAudioAudioProvider.h
new file mode 100644
index 0000000..09b2b54
--- /dev/null
+++ b/bluetooth/audio/2.1/default/LeAudioAudioProvider.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2020 HIMSA II K/S - www.himsa.com. Represented by EHIMA -
+ * www.ehima.com
+ *
+ * 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 <android/hardware/bluetooth/audio/2.1/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class LeAudioAudioProvider : public BluetoothAudioProvider {
+ public:
+ LeAudioAudioProvider();
+
+ bool isValid(const SessionType& sessionType) override;
+ bool isValid(const V2_0::SessionType& sessionType) override;
+
+ Return<void> startSession_2_1(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ private:
+ /** queue for software encodec/decoded audio data */
+ std::unique_ptr<DataMQ> mDataMQ;
+
+ Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+class LeAudioOutputAudioProvider : public LeAudioAudioProvider {
+ public:
+ LeAudioOutputAudioProvider();
+};
+
+class LeAudioInputAudioProvider : public LeAudioAudioProvider {
+ public:
+ LeAudioInputAudioProvider();
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/session/BluetoothAudioSession.cpp b/bluetooth/audio/2.1/default/session/BluetoothAudioSession.cpp
new file mode 100644
index 0000000..ea2c54a
--- /dev/null
+++ b/bluetooth/audio/2.1/default/session/BluetoothAudioSession.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderSession"
+
+#include "BluetoothAudioSession.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::hardware::audio::common::V5_0::AudioContentType;
+using ::android::hardware::audio::common::V5_0::AudioUsage;
+using ::android::hardware::audio::common::V5_0::PlaybackTrackMetadata;
+using ::android::hardware::audio::common::V5_0::SourceMetadata;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::TimeSpec;
+
+const CodecConfiguration BluetoothAudioSession::kInvalidCodecConfiguration = {
+ .codecType = CodecType::UNKNOWN,
+ .encodedAudioBitrate = 0x00000000,
+ .peerMtu = 0xffff,
+ .isScmstEnabled = false,
+ .config = {}};
+AudioConfiguration BluetoothAudioSession::invalidSoftwareAudioConfiguration =
+ {};
+AudioConfiguration BluetoothAudioSession::invalidOffloadAudioConfiguration = {};
+
+static constexpr int kFmqSendTimeoutMs = 1000; // 1000 ms timeout for sending
+static constexpr int kFmqReceiveTimeoutMs =
+ 1000; // 1000 ms timeout for receiving
+static constexpr int kWritePollMs = 1; // polled non-blocking interval
+static constexpr int kReadPollMs = 1; // polled non-blocking interval
+
+static inline timespec timespec_convert_from_hal(const TimeSpec& TS) {
+ return {.tv_sec = static_cast<long>(TS.tvSec),
+ .tv_nsec = static_cast<long>(TS.tvNSec)};
+}
+
+BluetoothAudioSession::BluetoothAudioSession(const SessionType& session_type)
+ : session_type_(session_type), stack_iface_(nullptr), mDataMQ(nullptr) {
+ invalidSoftwareAudioConfiguration.pcmConfig(kInvalidPcmParameters);
+ invalidOffloadAudioConfiguration.codecConfig(kInvalidCodecConfiguration);
+}
+
+// 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::OnSessionStarted(
+ const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
+ const AudioConfiguration& audio_config) {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (stack_iface == nullptr) {
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", IBluetoothAudioPort Invalid";
+ } else if (!UpdateAudioConfig(audio_config)) {
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", AudioConfiguration=" << toString(audio_config)
+ << " Invalid";
+ } else if (!UpdateDataPath(dataMQ)) {
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+ << " DataMQ Invalid";
+ audio_config_ =
+ (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
+ ? kInvalidOffloadAudioConfiguration
+ : kInvalidSoftwareAudioConfiguration);
+ } else {
+ stack_iface_ = stack_iface;
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", AudioConfiguration=" << toString(audio_config);
+ ReportSessionStatus();
+ }
+}
+
+// The report function is used to report that the Bluetooth stack has ended the
+// session, and will invoke session_changed_cb_ to notify registered
+// bluetooth_audio outputs
+void BluetoothAudioSession::OnSessionEnded() {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ bool toggled = IsSessionReady();
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
+ audio_config_ = (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
+ ? kInvalidOffloadAudioConfiguration
+ : kInvalidSoftwareAudioConfiguration);
+ stack_iface_ = nullptr;
+ UpdateDataPath(nullptr);
+ if (toggled) {
+ ReportSessionStatus();
+ }
+}
+
+// invoking the registered session_changed_cb_
+void BluetoothAudioSession::ReportSessionStatus() {
+ // This is locked already by OnSessionStarted / OnSessionEnded
+ if (observers_.empty()) {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO port state observer";
+ return;
+ }
+ for (auto& observer : observers_) {
+ uint16_t cookie = observer.first;
+ std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << " notify to bluetooth_audio=0x"
+ << android::base::StringPrintf("%04x", cookie);
+ cb->session_changed_cb_(cookie);
+ }
+}
+
+// The report function is used to report that the Bluetooth stack has notified
+// the result of startStream or suspendStream, and will invoke
+// control_result_cb_ to notify registered bluetooth_audio outputs
+void BluetoothAudioSession::ReportControlStatus(
+ bool start_resp, const BluetoothAudioStatus& status) {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (observers_.empty()) {
+ LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO port state observer";
+ return;
+ }
+ for (auto& observer : observers_) {
+ uint16_t cookie = observer.first;
+ std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
+ LOG(INFO) << __func__ << " - status=" << toString(status)
+ << " for SessionType=" << toString(session_type_)
+ << ", bluetooth_audio=0x"
+ << android::base::StringPrintf("%04x", cookie)
+ << (start_resp ? " started" : " suspended");
+ cb->control_result_cb_(cookie, start_resp, status);
+ }
+}
+
+// The function helps to check if this session is ready or not
+// @return: true if the Bluetooth stack has started the specified session
+bool BluetoothAudioSession::IsSessionReady() {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ bool dataMQ_valid =
+ (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
+ (mDataMQ != nullptr && mDataMQ->isValid()));
+ return stack_iface_ != nullptr && dataMQ_valid;
+}
+
+bool BluetoothAudioSession::UpdateDataPath(const DataMQ::Descriptor* dataMQ) {
+ if (dataMQ == nullptr) {
+ // usecase of reset by nullptr
+ mDataMQ = nullptr;
+ return true;
+ }
+ std::unique_ptr<DataMQ> tempDataMQ;
+ tempDataMQ.reset(new DataMQ(*dataMQ));
+ if (!tempDataMQ || !tempDataMQ->isValid()) {
+ mDataMQ = nullptr;
+ return false;
+ }
+ mDataMQ = std::move(tempDataMQ);
+ return true;
+}
+
+bool BluetoothAudioSession::UpdateAudioConfig(
+ const AudioConfiguration& audio_config) {
+ bool is_software_session =
+ (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+ session_type_ == SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH ||
+ session_type_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
+ session_type_ == SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
+ bool is_offload_session =
+ (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+ auto audio_config_discriminator = audio_config.getDiscriminator();
+ bool is_software_audio_config =
+ (is_software_session &&
+ audio_config_discriminator ==
+ AudioConfiguration::hidl_discriminator::pcmConfig);
+ bool is_offload_audio_config =
+ (is_offload_session &&
+ audio_config_discriminator ==
+ AudioConfiguration::hidl_discriminator::codecConfig);
+ if (!is_software_audio_config && !is_offload_audio_config) {
+ return false;
+ }
+ audio_config_ = audio_config;
+ return true;
+}
+
+// The control function helps the bluetooth_audio module to register
+// PortStatusCallbacks
+// @return: cookie - the assigned number to this bluetooth_audio output
+uint16_t BluetoothAudioSession::RegisterStatusCback(
+ const PortStatusCallbacks& cbacks) {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ uint16_t cookie = ObserversCookieGetInitValue(session_type_);
+ uint16_t cookie_upper_bound = ObserversCookieGetUpperBound(session_type_);
+
+ while (cookie < cookie_upper_bound) {
+ if (observers_.find(cookie) == observers_.end()) {
+ break;
+ }
+ ++cookie;
+ }
+ if (cookie >= cookie_upper_bound) {
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has " << observers_.size()
+ << " observers already (No Resource)";
+ return kObserversCookieUndefined;
+ }
+ std::shared_ptr<struct PortStatusCallbacks> cb =
+ std::make_shared<struct PortStatusCallbacks>();
+ *cb = cbacks;
+ observers_[cookie] = cb;
+ return cookie;
+}
+
+// The control function helps the bluetooth_audio module to unregister
+// PortStatusCallbacks
+// @param: cookie - indicates which bluetooth_audio output is
+void BluetoothAudioSession::UnregisterStatusCback(uint16_t cookie) {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (observers_.erase(cookie) != 1) {
+ LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+ << " no such provider=0x"
+ << android::base::StringPrintf("%04x", cookie);
+ }
+}
+
+// The control function is for the bluetooth_audio module to get the current
+// AudioConfiguration
+const AudioConfiguration& BluetoothAudioSession::GetAudioConfig() {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (IsSessionReady()) {
+ return audio_config_;
+ } else if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ return kInvalidOffloadAudioConfiguration;
+ } else {
+ return kInvalidSoftwareAudioConfiguration;
+ }
+}
+
+// Those control functions are for the bluetooth_audio module to start, suspend,
+// stop stream, to check position, and to update metadata.
+bool BluetoothAudioSession::StartStream() {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (!IsSessionReady()) {
+ LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO session";
+ return false;
+ }
+ auto hal_retval = stack_iface_->startStream();
+ if (!hal_retval.isOk()) {
+ LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+ << toString(session_type_) << " failed";
+ return false;
+ }
+ return true;
+}
+
+bool BluetoothAudioSession::SuspendStream() {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (!IsSessionReady()) {
+ LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO session";
+ return false;
+ }
+ auto hal_retval = stack_iface_->suspendStream();
+ if (!hal_retval.isOk()) {
+ LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+ << toString(session_type_) << " failed";
+ return false;
+ }
+ return true;
+}
+
+void BluetoothAudioSession::StopStream() {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (!IsSessionReady()) {
+ return;
+ }
+ auto hal_retval = stack_iface_->stopStream();
+ if (!hal_retval.isOk()) {
+ LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+ << toString(session_type_) << " failed";
+ }
+}
+
+bool BluetoothAudioSession::GetPresentationPosition(
+ uint64_t* remote_delay_report_ns, uint64_t* total_bytes_readed,
+ timespec* data_position) {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (!IsSessionReady()) {
+ LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO session";
+ return false;
+ }
+ bool retval = false;
+ auto hal_retval = stack_iface_->getPresentationPosition(
+ [&retval, &remote_delay_report_ns, &total_bytes_readed, &data_position](
+ BluetoothAudioStatus status,
+ const uint64_t& remoteDeviceAudioDelayNanos,
+ uint64_t transmittedOctets,
+ const TimeSpec& transmittedOctetsTimeStamp) {
+ if (status == BluetoothAudioStatus::SUCCESS) {
+ if (remote_delay_report_ns)
+ *remote_delay_report_ns = remoteDeviceAudioDelayNanos;
+ if (total_bytes_readed) *total_bytes_readed = transmittedOctets;
+ if (data_position)
+ *data_position =
+ timespec_convert_from_hal(transmittedOctetsTimeStamp);
+ retval = true;
+ }
+ });
+ if (!hal_retval.isOk()) {
+ LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+ << toString(session_type_) << " failed";
+ return false;
+ }
+ return retval;
+}
+
+void BluetoothAudioSession::UpdateTracksMetadata(
+ const struct source_metadata* source_metadata) {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (!IsSessionReady()) {
+ LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO session";
+ return;
+ }
+
+ ssize_t track_count = source_metadata->track_count;
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ", "
+ << track_count << " track(s)";
+ if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+ session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ return;
+ }
+
+ struct playback_track_metadata* track = source_metadata->tracks;
+ SourceMetadata sourceMetadata;
+ PlaybackTrackMetadata* halMetadata;
+
+ sourceMetadata.tracks.resize(track_count);
+ halMetadata = sourceMetadata.tracks.data();
+ while (track_count && track) {
+ halMetadata->usage = static_cast<AudioUsage>(track->usage);
+ halMetadata->contentType =
+ static_cast<AudioContentType>(track->content_type);
+ halMetadata->gain = track->gain;
+ LOG(VERBOSE) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", usage=" << toString(halMetadata->usage)
+ << ", content=" << toString(halMetadata->contentType)
+ << ", gain=" << halMetadata->gain;
+ --track_count;
+ ++track;
+ ++halMetadata;
+ }
+ auto hal_retval = stack_iface_->updateMetadata(sourceMetadata);
+ if (!hal_retval.isOk()) {
+ LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+ << toString(session_type_) << " failed";
+ }
+}
+
+// The control function writes stream to FMQ
+size_t BluetoothAudioSession::OutWritePcmData(const void* buffer,
+ size_t bytes) {
+ if (buffer == nullptr || !bytes) return 0;
+ size_t totalWritten = 0;
+ int ms_timeout = kFmqSendTimeoutMs;
+ do {
+ std::unique_lock<std::recursive_mutex> lock(mutex_);
+ if (!IsSessionReady()) break;
+ size_t availableToWrite = mDataMQ->availableToWrite();
+ if (availableToWrite) {
+ if (availableToWrite > (bytes - totalWritten)) {
+ availableToWrite = bytes - totalWritten;
+ }
+
+ if (!mDataMQ->write(static_cast<const uint8_t*>(buffer) + totalWritten,
+ availableToWrite)) {
+ ALOGE("FMQ datapath writing %zu/%zu failed", totalWritten, bytes);
+ return totalWritten;
+ }
+ totalWritten += availableToWrite;
+ } else if (ms_timeout >= kWritePollMs) {
+ lock.unlock();
+ usleep(kWritePollMs * 1000);
+ ms_timeout -= kWritePollMs;
+ } else {
+ ALOGD("out data %zu/%zu overflow %d ms", totalWritten, bytes,
+ (kFmqSendTimeoutMs - ms_timeout));
+ return totalWritten;
+ }
+ } while (totalWritten < bytes);
+ return totalWritten;
+}
+
+// The control function reads stream from FMQ
+size_t BluetoothAudioSession::InReadPcmData(void* buffer, size_t bytes) {
+ if (buffer == nullptr || !bytes) return 0;
+ size_t totalRead = 0;
+ int ms_timeout = kFmqReceiveTimeoutMs;
+ do {
+ std::unique_lock<std::recursive_mutex> lock(mutex_);
+ if (!IsSessionReady()) break;
+ size_t availableToRead = mDataMQ->availableToRead();
+ if (availableToRead) {
+ if (availableToRead > (bytes - totalRead)) {
+ availableToRead = bytes - totalRead;
+ }
+ if (!mDataMQ->read(static_cast<uint8_t*>(buffer) + totalRead,
+ availableToRead)) {
+ ALOGE("FMQ datapath reading %zu/%zu failed", totalRead, bytes);
+ return totalRead;
+ }
+ totalRead += availableToRead;
+ } else if (ms_timeout >= kReadPollMs) {
+ lock.unlock();
+ usleep(kReadPollMs * 1000);
+ ms_timeout -= kReadPollMs;
+ continue;
+ } else {
+ ALOGD("in data %zu/%zu overflow %d ms", totalRead, bytes,
+ (kFmqReceiveTimeoutMs - ms_timeout));
+ return totalRead;
+ }
+ } while (totalRead < bytes);
+ return totalRead;
+}
+
+std::unique_ptr<BluetoothAudioSessionInstance>
+ BluetoothAudioSessionInstance::instance_ptr =
+ std::unique_ptr<BluetoothAudioSessionInstance>(
+ new BluetoothAudioSessionInstance());
+
+// API to fetch the session
+std::shared_ptr<BluetoothAudioSession>
+BluetoothAudioSessionInstance::GetSessionInstance(
+ const SessionType& session_type) {
+ std::lock_guard<std::mutex> 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<BluetoothAudioSession> session_ptr =
+ std::make_shared<BluetoothAudioSession>(session_type);
+ instance_ptr->sessions_map_[session_type] = session_ptr;
+ return session_ptr;
+}
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/session/BluetoothAudioSession.h b/bluetooth/audio/2.1/default/session/BluetoothAudioSession.h
new file mode 100644
index 0000000..7bc12e6
--- /dev/null
+++ b/bluetooth/audio/2.1/default/session/BluetoothAudioSession.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <mutex>
+#include <unordered_map>
+
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
+#include <android/hardware/bluetooth/audio/2.1/types.h>
+#include <fmq/MessageQueue.h>
+#include <hardware/audio.h>
+#include <hidl/MQDescriptor.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::sp;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
+using ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration;
+using ::android::hardware::bluetooth::audio::V2_1::PcmParameters;
+using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
+using ::android::hardware::bluetooth::audio::V2_1::SessionType;
+
+using BluetoothAudioStatus =
+ ::android::hardware::bluetooth::audio::V2_0::Status;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+static constexpr uint16_t kObserversCookieSize = 0x0010; // 0x0000 ~ 0x000f
+constexpr uint16_t kObserversCookieUndefined =
+ (static_cast<uint16_t>(SessionType::UNKNOWN) << 8 & 0xff00);
+inline SessionType ObserversCookieGetSessionType(uint16_t cookie) {
+ return static_cast<SessionType>(cookie >> 8 & 0x00ff);
+}
+inline uint16_t ObserversCookieGetInitValue(SessionType session_type) {
+ return (static_cast<uint16_t>(session_type) << 8 & 0xff00);
+}
+inline uint16_t ObserversCookieGetUpperBound(SessionType session_type) {
+ return (static_cast<uint16_t>(session_type) << 8 & 0xff00) +
+ kObserversCookieSize;
+}
+
+// This presents the callbacks of started / suspended and session changed,
+// and the bluetooth_audio module uses to receive the status notification
+struct PortStatusCallbacks {
+ // control_result_cb_ - when the Bluetooth stack reports results of
+ // streamStarted or streamSuspended, the BluetoothAudioProvider will invoke
+ // this callback to report to the bluetooth_audio module.
+ // @param: cookie - indicates which bluetooth_audio output should handle
+ // @param: start_resp - this report is for startStream or not
+ // @param: status - the result of startStream
+ std::function<void(uint16_t cookie, bool start_resp,
+ const BluetoothAudioStatus& status)>
+ control_result_cb_;
+ // session_changed_cb_ - when the Bluetooth stack start / end session, the
+ // BluetoothAudioProvider will invoke this callback to notify to the
+ // bluetooth_audio module.
+ // @param: cookie - indicates which bluetooth_audio output should handle
+ std::function<void(uint16_t cookie)> session_changed_cb_;
+};
+
+class BluetoothAudioSession {
+ private:
+ // using recursive_mutex to allow hwbinder to re-enter again.
+ std::recursive_mutex mutex_;
+ SessionType session_type_;
+
+ // audio control path to use for both software and offloading
+ sp<IBluetoothAudioPort> stack_iface_;
+ // Audio path (FMQ) for software encoding/decoded data
+ std::unique_ptr<DataMQ> mDataMQ;
+ // audio data configuration for both software and offloading
+ AudioConfiguration audio_config_;
+
+ static AudioConfiguration invalidSoftwareAudioConfiguration;
+ static AudioConfiguration invalidOffloadAudioConfiguration;
+
+ // saving those registered bluetooth_audio's callbacks
+ std::unordered_map<uint16_t, std::shared_ptr<struct PortStatusCallbacks>>
+ observers_;
+
+ bool UpdateDataPath(const DataMQ::Descriptor* dataMQ);
+ bool UpdateAudioConfig(const AudioConfiguration& audio_config);
+ // invoking the registered session_changed_cb_
+ void ReportSessionStatus();
+
+ public:
+ BluetoothAudioSession(const 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();
+
+ // 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<IBluetoothAudioPort> stack_iface,
+ const DataMQ::Descriptor* dataMQ,
+ const AudioConfiguration& audio_config);
+
+ // The report function is used to report that the Bluetooth stack has ended
+ // the session, and will invoke session_changed_cb_ to notify registered
+ // bluetooth_audio outputs
+ void OnSessionEnded();
+
+ // The report function is used to report that the Bluetooth stack has notified
+ // the result of startStream or suspendStream, and will invoke
+ // control_result_cb_ to notify registered bluetooth_audio outputs
+ void ReportControlStatus(bool start_resp, const BluetoothAudioStatus& status);
+
+ // The control function helps the bluetooth_audio module to register
+ // PortStatusCallbacks
+ // @return: cookie - the assigned number to this bluetooth_audio output
+ uint16_t RegisterStatusCback(const PortStatusCallbacks& cbacks);
+
+ // The control function helps the bluetooth_audio module to unregister
+ // PortStatusCallbacks
+ // @param: cookie - indicates which bluetooth_audio output is
+ void UnregisterStatusCback(uint16_t cookie);
+
+ // The control function is for the bluetooth_audio module to get the current
+ // AudioConfiguration
+ const AudioConfiguration& GetAudioConfig();
+
+ // Those control functions are for the bluetooth_audio module to start,
+ // suspend, stop stream, to check position, and to update metadata.
+ bool StartStream();
+ bool SuspendStream();
+ void StopStream();
+ bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
+ uint64_t* total_bytes_readed,
+ timespec* data_position);
+ void UpdateTracksMetadata(const struct source_metadata* source_metadata);
+
+ // The control function writes stream to FMQ
+ size_t OutWritePcmData(const void* buffer, size_t bytes);
+ // The control function read stream from FMQ
+ size_t InReadPcmData(void* buffer, size_t bytes);
+
+ static constexpr PcmParameters kInvalidPcmParameters = {
+ .sampleRate = SampleRate::RATE_UNKNOWN,
+ .channelMode = ChannelMode::UNKNOWN,
+ .bitsPerSample = BitsPerSample::BITS_UNKNOWN,
+ .dataIntervalUs = 0,
+ };
+ // can't be constexpr because of non-literal type
+ static const CodecConfiguration kInvalidCodecConfiguration;
+
+ static constexpr AudioConfiguration& kInvalidSoftwareAudioConfiguration =
+ invalidSoftwareAudioConfiguration;
+ static constexpr AudioConfiguration& kInvalidOffloadAudioConfiguration =
+ invalidOffloadAudioConfiguration;
+};
+
+class BluetoothAudioSessionInstance {
+ public:
+ // The API is to fetch the specified session
+ static std::shared_ptr<BluetoothAudioSession> GetSessionInstance(
+ const SessionType& session_type);
+
+ private:
+ static std::unique_ptr<BluetoothAudioSessionInstance> instance_ptr;
+ std::mutex mutex_;
+ std::unordered_map<SessionType, std::shared_ptr<BluetoothAudioSession>>
+ sessions_map_;
+};
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/session/BluetoothAudioSessionControl.h b/bluetooth/audio/2.1/default/session/BluetoothAudioSessionControl.h
new file mode 100644
index 0000000..017a611
--- /dev/null
+++ b/bluetooth/audio/2.1/default/session/BluetoothAudioSessionControl.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BluetoothAudioSession.h"
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+class BluetoothAudioSessionControl {
+ public:
+ // The control API helps to check if session is ready or not
+ // @return: true if the Bluetooth stack has started th specified session
+ static bool IsSessionReady(const SessionType& session_type) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->IsSessionReady();
+ }
+ return false;
+ }
+
+ // The control API helps the bluetooth_audio module to register
+ // PortStatusCallbacks
+ // @return: cookie - the assigned number to this bluetooth_audio output
+ static uint16_t RegisterControlResultCback(
+ const SessionType& session_type, const PortStatusCallbacks& cbacks) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->RegisterStatusCback(cbacks);
+ }
+ return kObserversCookieUndefined;
+ }
+
+ // The control API helps the bluetooth_audio module to unregister
+ // PortStatusCallbacks
+ // @param: cookie - indicates which bluetooth_audio output is
+ static void UnregisterControlResultCback(const SessionType& session_type,
+ uint16_t cookie) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->UnregisterStatusCback(cookie);
+ }
+ }
+
+ // The control API for the bluetooth_audio module to get current
+ // AudioConfiguration
+ static const AudioConfiguration& GetAudioConfig(
+ const SessionType& session_type) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->GetAudioConfig();
+ } else if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ return BluetoothAudioSession::kInvalidOffloadAudioConfiguration;
+ } else {
+ return BluetoothAudioSession::kInvalidSoftwareAudioConfiguration;
+ }
+ }
+
+ // Those control APIs for the bluetooth_audio module to start / suspend / stop
+ // stream, to check position, and to update metadata.
+ static bool StartStream(const SessionType& session_type) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->StartStream();
+ }
+ return false;
+ }
+
+ static bool SuspendStream(const SessionType& session_type) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->SuspendStream();
+ }
+ return false;
+ }
+
+ static void StopStream(const SessionType& session_type) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->StopStream();
+ }
+ }
+
+ static bool GetPresentationPosition(const SessionType& session_type,
+ uint64_t* remote_delay_report_ns,
+ uint64_t* total_bytes_readed,
+ timespec* data_position) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->GetPresentationPosition(
+ remote_delay_report_ns, total_bytes_readed, data_position);
+ }
+ return false;
+ }
+
+ static void UpdateTracksMetadata(
+ const SessionType& session_type,
+ const struct source_metadata* source_metadata) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->UpdateTracksMetadata(source_metadata);
+ }
+ }
+
+ // The control API writes stream to FMQ
+ static size_t OutWritePcmData(const SessionType& session_type,
+ const void* buffer, size_t bytes) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->OutWritePcmData(buffer, bytes);
+ }
+ return 0;
+ }
+
+ // The control API reads stream from FMQ
+ static size_t InReadPcmData(const SessionType& session_type, void* buffer,
+ size_t bytes) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->InReadPcmData(buffer, bytes);
+ }
+ return 0;
+ }
+};
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/session/BluetoothAudioSessionReport.h b/bluetooth/audio/2.1/default/session/BluetoothAudioSessionReport.h
new file mode 100644
index 0000000..267bf8f
--- /dev/null
+++ b/bluetooth/audio/2.1/default/session/BluetoothAudioSessionReport.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BluetoothAudioSession.h"
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+class BluetoothAudioSessionReport {
+ public:
+ // The API reports the Bluetooth stack has started the session, and will
+ // inform registered bluetooth_audio session
+ static void OnSessionStarted(const SessionType& session_type,
+ const sp<IBluetoothAudioPort> host_iface,
+ const DataMQ::Descriptor* dataMQ,
+ const AudioConfiguration& audio_config) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::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 SessionType& session_type) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->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 SessionType& session_type,
+ const bool& start_resp,
+ const BluetoothAudioStatus& status) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->ReportControlStatus(start_resp, status);
+ }
+ }
+};
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/session/BluetoothAudioSupportedCodecsDB.cpp b/bluetooth/audio/2.1/default/session/BluetoothAudioSupportedCodecsDB.cpp
new file mode 100644
index 0000000..d15db49
--- /dev/null
+++ b/bluetooth/audio/2.1/default/session/BluetoothAudioSupportedCodecsDB.cpp
@@ -0,0 +1,487 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderSessionCodecsDB"
+
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
+using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
+using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
+using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
+using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
+using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
+using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
+using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
+
+// Default Supported PCM Parameters
+static const ::android::hardware::bluetooth::audio::V2_0::PcmParameters
+ kDefaultSoftwarePcmCapabilities = {
+ .sampleRate = static_cast<
+ android::hardware::bluetooth::audio::V2_0::SampleRate>(
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 |
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000 |
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_88200 |
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_96000 |
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_16000),
+ .channelMode =
+ static_cast<ChannelMode>(ChannelMode::MONO | ChannelMode::STEREO),
+ .bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
+ BitsPerSample::BITS_24 |
+ BitsPerSample::BITS_32)};
+
+static const PcmParameters kDefaultSoftwarePcmCapabilities_2_1 = {
+ .sampleRate = static_cast<SampleRate>(
+ SampleRate::RATE_48000 | SampleRate::RATE_44100 |
+ SampleRate::RATE_32000 | SampleRate::RATE_24000 |
+ SampleRate::RATE_16000 | SampleRate::RATE_8000),
+ .channelMode =
+ static_cast<ChannelMode>(ChannelMode::MONO | ChannelMode::STEREO),
+ .bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16)};
+
+// Default Supported Codecs
+// SBC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(MONO|STEREO)
+// all blocks | subbands 8 | Loudness
+static const SbcParameters kDefaultOffloadSbcCapability = {
+ .sampleRate =
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100,
+ .channelMode = static_cast<SbcChannelMode>(SbcChannelMode::MONO |
+ SbcChannelMode::JOINT_STEREO),
+ .blockLength = static_cast<SbcBlockLength>(
+ SbcBlockLength::BLOCKS_4 | SbcBlockLength::BLOCKS_8 |
+ SbcBlockLength::BLOCKS_12 | SbcBlockLength::BLOCKS_16),
+ .numSubbands = SbcNumSubbands::SUBBAND_8,
+ .allocMethod = SbcAllocMethod::ALLOC_MD_L,
+ .bitsPerSample = BitsPerSample::BITS_16,
+ .minBitpool = 2,
+ .maxBitpool = 53};
+
+// AAC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(STEREO)
+static const AacParameters kDefaultOffloadAacCapability = {
+ .objectType = AacObjectType::MPEG2_LC,
+ .sampleRate =
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100,
+ .channelMode = ChannelMode::STEREO,
+ .variableBitRateEnabled = AacVariableBitRate::ENABLED,
+ .bitsPerSample = BitsPerSample::BITS_16};
+
+// LDAC: mSampleRate:(44100|48000|88200|96000), mBitsPerSample:(16|24|32),
+// mChannelMode:(DUAL|STEREO)
+static const LdacParameters kDefaultOffloadLdacCapability = {
+ .sampleRate =
+ static_cast<android::hardware::bluetooth::audio::V2_0::SampleRate>(
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 |
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000 |
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_88200 |
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_96000),
+ .channelMode = static_cast<LdacChannelMode>(LdacChannelMode::DUAL |
+ LdacChannelMode::STEREO),
+ .qualityIndex = LdacQualityIndex::QUALITY_HIGH,
+ .bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
+ BitsPerSample::BITS_24 |
+ BitsPerSample::BITS_32)};
+
+// aptX: mSampleRate:(44100|48000), mBitsPerSample:(16), mChannelMode:(STEREO)
+static const AptxParameters kDefaultOffloadAptxCapability = {
+ .sampleRate =
+ static_cast<android::hardware::bluetooth::audio::V2_0::SampleRate>(
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 |
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000),
+ .channelMode = ChannelMode::STEREO,
+ .bitsPerSample = BitsPerSample::BITS_16,
+};
+
+// aptX HD: mSampleRate:(44100|48000), mBitsPerSample:(24),
+// mChannelMode:(STEREO)
+static const AptxParameters kDefaultOffloadAptxHdCapability = {
+ .sampleRate =
+ static_cast<android::hardware::bluetooth::audio::V2_0::SampleRate>(
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 |
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000),
+ .channelMode = ChannelMode::STEREO,
+ .bitsPerSample = BitsPerSample::BITS_24,
+};
+
+const std::vector<CodecCapabilities> kDefaultOffloadA2dpCodecCapabilities = {
+ {.codecType = CodecType::SBC, .capabilities = {}},
+ {.codecType = CodecType::AAC, .capabilities = {}},
+ {.codecType = CodecType::LDAC, .capabilities = {}},
+ {.codecType = CodecType::APTX, .capabilities = {}},
+ {.codecType = CodecType::APTX_HD, .capabilities = {}}};
+
+static bool IsSingleBit(uint32_t bitmasks, uint32_t bitfield) {
+ bool single = false;
+ uint32_t test_bit = 0x00000001;
+ while (test_bit <= bitmasks && test_bit <= bitfield) {
+ if (bitfield & test_bit && bitmasks & test_bit) {
+ if (single) return false;
+ single = true;
+ }
+ if (test_bit == 0x80000000) break;
+ test_bit <<= 1;
+ }
+ return single;
+}
+
+static bool IsOffloadSbcConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific);
+static bool IsOffloadAacConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific);
+static bool IsOffloadLdacConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific);
+static bool IsOffloadAptxConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific);
+static bool IsOffloadAptxHdConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific);
+
+static bool IsOffloadSbcConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific) {
+ if (codec_specific.getDiscriminator() !=
+ CodecConfiguration::CodecSpecific::hidl_discriminator::sbcConfig) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ }
+ const SbcParameters sbc_data = codec_specific.sbcConfig();
+ if (!IsSingleBit(static_cast<uint32_t>(sbc_data.sampleRate), 0xff) ||
+ !IsSingleBit(static_cast<uint32_t>(sbc_data.channelMode), 0x0f) ||
+ !IsSingleBit(static_cast<uint32_t>(sbc_data.blockLength), 0xf0) ||
+ !IsSingleBit(static_cast<uint32_t>(sbc_data.numSubbands), 0x0c) ||
+ !IsSingleBit(static_cast<uint32_t>(sbc_data.allocMethod), 0x03) ||
+ !IsSingleBit(static_cast<uint32_t>(sbc_data.bitsPerSample), 0x07) ||
+ sbc_data.minBitpool > sbc_data.maxBitpool) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ } else if ((sbc_data.sampleRate & kDefaultOffloadSbcCapability.sampleRate) &&
+ (sbc_data.channelMode &
+ kDefaultOffloadSbcCapability.channelMode) &&
+ (sbc_data.blockLength &
+ kDefaultOffloadSbcCapability.blockLength) &&
+ (sbc_data.numSubbands &
+ kDefaultOffloadSbcCapability.numSubbands) &&
+ (sbc_data.allocMethod &
+ kDefaultOffloadSbcCapability.allocMethod) &&
+ (sbc_data.bitsPerSample &
+ kDefaultOffloadSbcCapability.bitsPerSample) &&
+ (kDefaultOffloadSbcCapability.minBitpool <= sbc_data.minBitpool &&
+ sbc_data.maxBitpool <= kDefaultOffloadSbcCapability.maxBitpool)) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported CodecSpecific=" << toString(codec_specific);
+ return false;
+}
+
+static bool IsOffloadAacConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific) {
+ if (codec_specific.getDiscriminator() !=
+ CodecConfiguration::CodecSpecific::hidl_discriminator::aacConfig) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ }
+ const AacParameters aac_data = codec_specific.aacConfig();
+ if (!IsSingleBit(static_cast<uint32_t>(aac_data.objectType), 0xf0) ||
+ !IsSingleBit(static_cast<uint32_t>(aac_data.sampleRate), 0xff) ||
+ !IsSingleBit(static_cast<uint32_t>(aac_data.channelMode), 0x03) ||
+ !IsSingleBit(static_cast<uint32_t>(aac_data.bitsPerSample), 0x07)) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ } else if ((aac_data.objectType & kDefaultOffloadAacCapability.objectType) &&
+ (aac_data.sampleRate & kDefaultOffloadAacCapability.sampleRate) &&
+ (aac_data.channelMode &
+ kDefaultOffloadAacCapability.channelMode) &&
+ (aac_data.variableBitRateEnabled == AacVariableBitRate::DISABLED ||
+ kDefaultOffloadAacCapability.variableBitRateEnabled ==
+ AacVariableBitRate::ENABLED) &&
+ (aac_data.bitsPerSample &
+ kDefaultOffloadAacCapability.bitsPerSample)) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported CodecSpecific=" << toString(codec_specific);
+ return false;
+}
+
+static bool IsOffloadLdacConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific) {
+ if (codec_specific.getDiscriminator() !=
+ CodecConfiguration::CodecSpecific::hidl_discriminator::ldacConfig) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ }
+ const LdacParameters ldac_data = codec_specific.ldacConfig();
+ if (!IsSingleBit(static_cast<uint32_t>(ldac_data.sampleRate), 0xff) ||
+ !IsSingleBit(static_cast<uint32_t>(ldac_data.channelMode), 0x07) ||
+ (ldac_data.qualityIndex > LdacQualityIndex::QUALITY_LOW &&
+ ldac_data.qualityIndex != LdacQualityIndex::QUALITY_ABR) ||
+ !IsSingleBit(static_cast<uint32_t>(ldac_data.bitsPerSample), 0x07)) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ } else if ((ldac_data.sampleRate &
+ kDefaultOffloadLdacCapability.sampleRate) &&
+ (ldac_data.channelMode &
+ kDefaultOffloadLdacCapability.channelMode) &&
+ (ldac_data.bitsPerSample &
+ kDefaultOffloadLdacCapability.bitsPerSample)) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported CodecSpecific=" << toString(codec_specific);
+ return false;
+}
+
+static bool IsOffloadAptxConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific) {
+ if (codec_specific.getDiscriminator() !=
+ CodecConfiguration::CodecSpecific::hidl_discriminator::aptxConfig) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ }
+ const AptxParameters aptx_data = codec_specific.aptxConfig();
+ if (!IsSingleBit(static_cast<uint32_t>(aptx_data.sampleRate), 0xff) ||
+ !IsSingleBit(static_cast<uint32_t>(aptx_data.channelMode), 0x03) ||
+ !IsSingleBit(static_cast<uint32_t>(aptx_data.bitsPerSample), 0x07)) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ } else if ((aptx_data.sampleRate &
+ kDefaultOffloadAptxCapability.sampleRate) &&
+ (aptx_data.channelMode &
+ kDefaultOffloadAptxCapability.channelMode) &&
+ (aptx_data.bitsPerSample &
+ kDefaultOffloadAptxCapability.bitsPerSample)) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported CodecSpecific=" << toString(codec_specific);
+ return false;
+}
+
+static bool IsOffloadAptxHdConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific) {
+ if (codec_specific.getDiscriminator() !=
+ CodecConfiguration::CodecSpecific::hidl_discriminator::aptxConfig) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ }
+ const AptxParameters aptx_data = codec_specific.aptxConfig();
+ if (!IsSingleBit(static_cast<uint32_t>(aptx_data.sampleRate), 0xff) ||
+ !IsSingleBit(static_cast<uint32_t>(aptx_data.channelMode), 0x03) ||
+ !IsSingleBit(static_cast<uint32_t>(aptx_data.bitsPerSample), 0x07)) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ } else if ((aptx_data.sampleRate &
+ kDefaultOffloadAptxHdCapability.sampleRate) &&
+ (aptx_data.channelMode &
+ kDefaultOffloadAptxHdCapability.channelMode) &&
+ (aptx_data.bitsPerSample &
+ kDefaultOffloadAptxHdCapability.bitsPerSample)) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported CodecSpecific=" << toString(codec_specific);
+ return false;
+}
+
+std::vector<::android::hardware::bluetooth::audio::V2_0::PcmParameters>
+GetSoftwarePcmCapabilities() {
+ return std::vector<
+ ::android::hardware::bluetooth::audio::V2_0::PcmParameters>(
+ 1, kDefaultSoftwarePcmCapabilities);
+}
+
+std::vector<PcmParameters> GetSoftwarePcmCapabilities_2_1() {
+ return std::vector<PcmParameters>(1, kDefaultSoftwarePcmCapabilities_2_1);
+}
+
+std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
+ const ::android::hardware::bluetooth::audio::V2_0::SessionType&
+ session_type) {
+ return GetOffloadCodecCapabilities(static_cast<SessionType>(session_type));
+}
+
+std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
+ const SessionType& session_type) {
+ if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ return std::vector<CodecCapabilities>(0);
+ }
+ std::vector<CodecCapabilities> offload_a2dp_codec_capabilities =
+ kDefaultOffloadA2dpCodecCapabilities;
+ for (auto& codec_capability : offload_a2dp_codec_capabilities) {
+ switch (codec_capability.codecType) {
+ case CodecType::SBC:
+ codec_capability.capabilities.sbcCapabilities(
+ kDefaultOffloadSbcCapability);
+ break;
+ case CodecType::AAC:
+ codec_capability.capabilities.aacCapabilities(
+ kDefaultOffloadAacCapability);
+ break;
+ case CodecType::LDAC:
+ codec_capability.capabilities.ldacCapabilities(
+ kDefaultOffloadLdacCapability);
+ break;
+ case CodecType::APTX:
+ codec_capability.capabilities.aptxCapabilities(
+ kDefaultOffloadAptxCapability);
+ break;
+ case CodecType::APTX_HD:
+ codec_capability.capabilities.aptxCapabilities(
+ kDefaultOffloadAptxHdCapability);
+ break;
+ case CodecType::UNKNOWN:
+ codec_capability = {};
+ break;
+ }
+ }
+ return offload_a2dp_codec_capabilities;
+}
+
+bool IsSoftwarePcmConfigurationValid(
+ const ::android::hardware::bluetooth::audio::V2_0::PcmParameters&
+ pcm_config) {
+ if ((pcm_config.sampleRate !=
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 &&
+ pcm_config.sampleRate !=
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000 &&
+ pcm_config.sampleRate !=
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_88200 &&
+ pcm_config.sampleRate !=
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_96000 &&
+ pcm_config.sampleRate !=
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_16000 &&
+ pcm_config.sampleRate !=
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_24000) ||
+ (pcm_config.bitsPerSample != BitsPerSample::BITS_16 &&
+ pcm_config.bitsPerSample != BitsPerSample::BITS_24 &&
+ pcm_config.bitsPerSample != BitsPerSample::BITS_32) ||
+ (pcm_config.channelMode != ChannelMode::MONO &&
+ pcm_config.channelMode != ChannelMode::STEREO)) {
+ LOG(WARNING) << __func__
+ << ": Invalid PCM Configuration=" << toString(pcm_config);
+ return false;
+ } else if (pcm_config.sampleRate &
+ kDefaultSoftwarePcmCapabilities.sampleRate &&
+ pcm_config.bitsPerSample &
+ kDefaultSoftwarePcmCapabilities.bitsPerSample &&
+ pcm_config.channelMode &
+ kDefaultSoftwarePcmCapabilities.channelMode) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported PCM Configuration=" << toString(pcm_config);
+ return false;
+}
+
+bool IsSoftwarePcmConfigurationValid_2_1(const PcmParameters& pcm_config) {
+ if ((pcm_config.sampleRate != SampleRate::RATE_44100 &&
+ pcm_config.sampleRate != SampleRate::RATE_48000 &&
+ pcm_config.sampleRate != SampleRate::RATE_88200 &&
+ pcm_config.sampleRate != SampleRate::RATE_96000 &&
+ pcm_config.sampleRate != SampleRate::RATE_16000 &&
+ pcm_config.sampleRate != SampleRate::RATE_24000) ||
+ (pcm_config.bitsPerSample != BitsPerSample::BITS_16 &&
+ pcm_config.bitsPerSample != BitsPerSample::BITS_24 &&
+ pcm_config.bitsPerSample != BitsPerSample::BITS_32) ||
+ (pcm_config.channelMode != ChannelMode::MONO &&
+ pcm_config.channelMode != ChannelMode::STEREO)) {
+ LOG(WARNING) << __func__
+ << ": Invalid PCM Configuration=" << toString(pcm_config);
+ return false;
+ } else if (pcm_config.sampleRate &
+ kDefaultSoftwarePcmCapabilities_2_1.sampleRate &&
+ pcm_config.bitsPerSample &
+ kDefaultSoftwarePcmCapabilities_2_1.bitsPerSample &&
+ pcm_config.channelMode &
+ kDefaultSoftwarePcmCapabilities_2_1.channelMode &&
+ pcm_config.dataIntervalUs != 0) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported PCM Configuration=" << toString(pcm_config);
+ return false;
+}
+
+bool IsOffloadCodecConfigurationValid(const SessionType& session_type,
+ const CodecConfiguration& codec_config) {
+ if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ LOG(ERROR) << __func__
+ << ": Invalid SessionType=" << toString(session_type);
+ return false;
+ } else if (codec_config.encodedAudioBitrate < 0x00000001 ||
+ 0x00ffffff < codec_config.encodedAudioBitrate) {
+ LOG(ERROR) << __func__ << ": Unsupported Codec Configuration="
+ << toString(codec_config);
+ return false;
+ }
+ const CodecConfiguration::CodecSpecific& codec_specific = codec_config.config;
+ switch (codec_config.codecType) {
+ case CodecType::SBC:
+ if (IsOffloadSbcConfigurationValid(codec_specific)) {
+ return true;
+ }
+ return false;
+ case CodecType::AAC:
+ if (IsOffloadAacConfigurationValid(codec_specific)) {
+ return true;
+ }
+ return false;
+ case CodecType::LDAC:
+ if (IsOffloadLdacConfigurationValid(codec_specific)) {
+ return true;
+ }
+ return false;
+ case CodecType::APTX:
+ if (IsOffloadAptxConfigurationValid(codec_specific)) {
+ return true;
+ }
+ return false;
+ case CodecType::APTX_HD:
+ if (IsOffloadAptxHdConfigurationValid(codec_specific)) {
+ return true;
+ }
+ return false;
+ case CodecType::UNKNOWN:
+ return false;
+ }
+ return false;
+}
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/2.1/default/session/BluetoothAudioSupportedCodecsDB.h b/bluetooth/audio/2.1/default/session/BluetoothAudioSupportedCodecsDB.h
new file mode 100644
index 0000000..9b2f680
--- /dev/null
+++ b/bluetooth/audio/2.1/default/session/BluetoothAudioSupportedCodecsDB.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.0/types.h>
+#include <android/hardware/bluetooth/audio/2.1/types.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_1::PcmParameters;
+using ::android::hardware::bluetooth::audio::V2_1::SessionType;
+
+std::vector<::android::hardware::bluetooth::audio::V2_0::PcmParameters>
+GetSoftwarePcmCapabilities();
+std::vector<PcmParameters> GetSoftwarePcmCapabilities_2_1();
+std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
+ const SessionType& session_type);
+std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
+ const ::android::hardware::bluetooth::audio::V2_0::SessionType&
+ session_type);
+
+bool IsSoftwarePcmConfigurationValid_2_1(const PcmParameters& pcm_config);
+bool IsSoftwarePcmConfigurationValid(
+ const ::android::hardware::bluetooth::audio::V2_0::PcmParameters&
+ pcm_config);
+bool IsOffloadCodecConfigurationValid(const SessionType& session_type,
+ const CodecConfiguration& codec_config);
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/2.1/types.hal b/bluetooth/audio/2.1/types.hal
new file mode 100644
index 0000000..5604c38
--- /dev/null
+++ b/bluetooth/audio/2.1/types.hal
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.bluetooth.audio@2.1;
+
+import @2.0::PcmParameters;
+import @2.0::SessionType;
+import @2.0::SampleRate;
+import @2.0::ChannelMode;
+import @2.0::BitsPerSample;
+import @2.0::CodecConfiguration;
+import @2.0::CodecCapabilities;
+
+enum SessionType : @2.0::SessionType {
+ /** Used when encoded by Bluetooth Stack and streaming to LE Audio device */
+ LE_AUDIO_SOFTWARE_ENCODING_DATAPATH,
+ /** Used when decoded by Bluetooth Stack and streaming to audio framework */
+ LE_AUDIO_SOFTWARE_DECODED_DATAPATH,
+ /** Encoding is done by HW an there is control only */
+ LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH,
+ /** Decoding is done by HW an there is control only */
+ LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH,
+};
+
+enum SampleRate : @2.0::SampleRate {
+ RATE_8000 = 0x100,
+ RATE_32000 = 0x200,
+};
+
+/** Used for Software Encoding audio feed parameters */
+struct PcmParameters {
+ SampleRate sampleRate;
+ ChannelMode channelMode;
+ BitsPerSample bitsPerSample;
+ /** Data interval for data transfer */
+ uint32_t dataIntervalUs;
+};
+
+/** Used to configure either a Hardware or Software Encoding session based on session type */
+safe_union AudioConfiguration {
+ PcmParameters pcmConfig;
+ CodecConfiguration codecConfig;
+};
+
+/** Used to specify the capabilities of the different session types */
+safe_union AudioCapabilities {
+ PcmParameters pcmCapabilities;
+ CodecCapabilities codecCapabilities;
+};
diff --git a/bluetooth/audio/2.1/vts/OWNERS b/bluetooth/audio/2.1/vts/OWNERS
new file mode 100644
index 0000000..b6c0813
--- /dev/null
+++ b/bluetooth/audio/2.1/vts/OWNERS
@@ -0,0 +1,3 @@
+include platform/system/bt:/OWNERS
+
+cheneyni@google.com
diff --git a/bluetooth/audio/2.1/vts/functional/Android.bp b/bluetooth/audio/2.1/vts/functional/Android.bp
new file mode 100644
index 0000000..6ec5537
--- /dev/null
+++ b/bluetooth/audio/2.1/vts/functional/Android.bp
@@ -0,0 +1,14 @@
+cc_test {
+ name: "VtsHalBluetoothAudioV2_1TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalBluetoothAudioV2_1TargetTest.cpp"],
+ static_libs: [
+ "android.hardware.audio.common@5.0",
+ "android.hardware.bluetooth.audio@2.1",
+ "android.hardware.bluetooth.audio@2.0",
+ ],
+ shared_libs: [
+ "libfmq",
+ ],
+ test_suites: ["general-tests", "vts"],
+}
diff --git a/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp b/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
new file mode 100644
index 0000000..c0ec907
--- /dev/null
+++ b/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
@@ -0,0 +1,1209 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bluetooth_audio_hidl_hal_test"
+
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/logging.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
+#include <android/hardware/bluetooth/audio/2.1/IBluetoothAudioProvider.h>
+#include <android/hardware/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.h>
+#include <fmq/MessageQueue.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/ServiceManagement.h>
+#include <utils/Log.h>
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::audio::common::V5_0::SourceMetadata;
+using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
+using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
+using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AudioCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioProvider;
+using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
+using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
+using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
+using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
+using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+using ::android::hardware::bluetooth::audio::V2_1::
+ IBluetoothAudioProvidersFactory;
+using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using BluetoothAudioStatus =
+ ::android::hardware::bluetooth::audio::V2_0::Status;
+using CodecSpecificConfig = ::android::hardware::bluetooth::audio::V2_0::
+ CodecConfiguration::CodecSpecific;
+
+namespace {
+constexpr android::hardware::bluetooth::audio::V2_0::SampleRate
+ a2dp_sample_rates[5] = {
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_UNKNOWN,
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100,
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000,
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_88200,
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_96000};
+constexpr BitsPerSample a2dp_bits_per_samples[4] = {
+ BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16, BitsPerSample::BITS_24,
+ BitsPerSample::BITS_32};
+constexpr ChannelMode a2dp_channel_modes[3] = {
+ ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+constexpr CodecType a2dp_codec_types[6] = {CodecType::UNKNOWN, CodecType::SBC,
+ CodecType::AAC, CodecType::APTX,
+ CodecType::APTX_HD, CodecType::LDAC};
+
+template <typename T>
+std::vector<T> ExtractValuesFromBitmask(T bitmasks, uint32_t bitfield,
+ bool supported) {
+ std::vector<T> retval;
+ if (!supported) {
+ retval.push_back(static_cast<T>(bitfield));
+ }
+ uint32_t test_bit = 0x00000001;
+ while (test_bit <= static_cast<uint32_t>(bitmasks) && test_bit <= bitfield) {
+ if ((bitfield & test_bit)) {
+ if ((!(bitmasks & test_bit) && !supported) ||
+ ((bitmasks & test_bit) && supported)) {
+ retval.push_back(static_cast<T>(test_bit));
+ }
+ }
+ if (test_bit == 0x80000000) {
+ break;
+ }
+ test_bit <<= 1;
+ }
+ return retval;
+}
+} // namespace
+
+// The base test class for Bluetooth Audio HAL.
+class BluetoothAudioProvidersFactoryHidlTest
+ : public ::testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ providers_factory_ =
+ IBluetoothAudioProvidersFactory::getService(GetParam());
+ ASSERT_NE(providers_factory_, nullptr);
+ }
+
+ virtual void TearDown() override { providers_factory_ = nullptr; }
+
+ // A simple test implementation of IBluetoothAudioPort.
+ class BluetoothAudioPort : public ::testing::VtsHalHidlTargetCallbackBase<
+ BluetoothAudioProvidersFactoryHidlTest>,
+ public IBluetoothAudioPort {
+ BluetoothAudioProvidersFactoryHidlTest& parent_;
+
+ public:
+ BluetoothAudioPort(BluetoothAudioProvidersFactoryHidlTest& parent)
+ : parent_(parent) {}
+ virtual ~BluetoothAudioPort() = default;
+
+ Return<void> startStream() override {
+ parent_.audio_provider_->streamStarted(BluetoothAudioStatus::SUCCESS);
+ return Void();
+ }
+
+ Return<void> suspendStream() override {
+ parent_.audio_provider_->streamSuspended(BluetoothAudioStatus::SUCCESS);
+ return Void();
+ }
+
+ Return<void> stopStream() override { return Void(); }
+
+ Return<void> getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
+ _hidl_cb(BluetoothAudioStatus::SUCCESS, 0, 0, {.tvSec = 0, .tvNSec = 0});
+ return Void();
+ }
+
+ Return<void> updateMetadata(const SourceMetadata& sourceMetadata __unused) {
+ return Void();
+ }
+ };
+
+ void GetProviderCapabilitiesHelper(const SessionType& session_type) {
+ temp_provider_capabilities_.clear();
+ auto hidl_cb = [&temp_capabilities = this->temp_provider_capabilities_](
+ const hidl_vec<AudioCapabilities>& audioCapabilities) {
+ for (auto audioCapability : audioCapabilities)
+ temp_capabilities.push_back(audioCapability);
+ };
+ auto hidl_retval =
+ providers_factory_->getProviderCapabilities(session_type, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ if (session_type == SessionType::UNKNOWN) {
+ ASSERT_TRUE(temp_provider_capabilities_.empty());
+ } else if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ // All software paths are mandatory and must have exact 1 "PcmParameters"
+ ASSERT_EQ(temp_provider_capabilities_.size(), 1);
+ ASSERT_EQ(temp_provider_capabilities_[0].getDiscriminator(),
+ AudioCapabilities::hidl_discriminator::pcmCapabilities);
+ } else {
+ uint32_t codec_type_bitmask = 0x00000000;
+ // empty capability means offload is unsupported
+ for (auto audio_capability : temp_provider_capabilities_) {
+ ASSERT_EQ(audio_capability.getDiscriminator(),
+ AudioCapabilities::hidl_discriminator::codecCapabilities);
+ const CodecCapabilities& codec_capabilities =
+ audio_capability.codecCapabilities();
+ // Every codec can present once at most
+ ASSERT_EQ(codec_type_bitmask &
+ static_cast<uint32_t>(codec_capabilities.codecType),
+ 0);
+ switch (codec_capabilities.codecType) {
+ case CodecType::SBC:
+ ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+ CodecCapabilities::Capabilities::hidl_discriminator::
+ sbcCapabilities);
+ break;
+ case CodecType::AAC:
+ ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+ CodecCapabilities::Capabilities::hidl_discriminator::
+ aacCapabilities);
+ break;
+ case CodecType::APTX:
+ FALLTHROUGH_INTENDED;
+ case CodecType::APTX_HD:
+ ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+ CodecCapabilities::Capabilities::hidl_discriminator::
+ aptxCapabilities);
+ break;
+ case CodecType::LDAC:
+ ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+ CodecCapabilities::Capabilities::hidl_discriminator::
+ ldacCapabilities);
+ break;
+ case CodecType::UNKNOWN:
+ break;
+ }
+ codec_type_bitmask |= codec_capabilities.codecType;
+ }
+ }
+ }
+
+ void GetProviderCapabilitiesHelper_2_1(
+ const android::hardware::bluetooth::audio::V2_1::SessionType&
+ session_type) {
+ temp_provider_capabilities_2_1_.clear();
+ auto hidl_cb =
+ [&temp_capabilities = this->temp_provider_capabilities_2_1_](
+ const hidl_vec<
+ android::hardware::bluetooth::audio::V2_1::AudioCapabilities>&
+ audioCapabilities) {
+ for (auto audioCapability : audioCapabilities)
+ temp_capabilities.push_back(audioCapability);
+ };
+ auto hidl_retval =
+ providers_factory_->getProviderCapabilities_2_1(session_type, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+
+ // All software paths are mandatory and must have exact 1 "PcmParameters"
+ ASSERT_EQ(temp_provider_capabilities_2_1_.size(), 1);
+ ASSERT_EQ(temp_provider_capabilities_2_1_[0].getDiscriminator(),
+ android::hardware::bluetooth::audio::V2_1::AudioCapabilities::
+ hidl_discriminator::pcmCapabilities);
+ }
+
+ // This helps to open the specified provider and check the openProvider()
+ // has corruct return values. BUT, to keep it simple, it does not consider
+ // the capability, and please do so at the SetUp of each session's test.
+ void OpenProviderHelper(const SessionType& session_type) {
+ BluetoothAudioStatus cb_status;
+ auto hidl_cb = [&cb_status, &local_provider = this->audio_provider_](
+ BluetoothAudioStatus status,
+ const sp<IBluetoothAudioProvider>& provider) {
+ cb_status = status;
+ local_provider = provider;
+ };
+ auto hidl_retval = providers_factory_->openProvider(session_type, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ if (cb_status == BluetoothAudioStatus::SUCCESS) {
+ ASSERT_NE(session_type, SessionType::UNKNOWN);
+ ASSERT_NE(audio_provider_, nullptr);
+ audio_port_ = new BluetoothAudioPort(*this);
+ } else {
+ // A2DP_HARDWARE_OFFLOAD_DATAPATH is optional
+ ASSERT_TRUE(session_type == SessionType::UNKNOWN ||
+ session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+ ASSERT_EQ(cb_status, BluetoothAudioStatus::FAILURE);
+ ASSERT_EQ(audio_provider_, nullptr);
+ }
+ }
+
+ // This helps to open the specified provider and check the openProvider_2_1()
+ // has corruct return values. BUT, to keep it simple, it does not consider
+ // the capability, and please do so at the SetUp of each session's test.
+ void OpenProviderHelper_2_1(
+ const android::hardware::bluetooth::audio::V2_1::SessionType&
+ session_type) {
+ BluetoothAudioStatus cb_status;
+ auto hidl_cb = [&cb_status, &local_provider = this->audio_provider_2_1_](
+ BluetoothAudioStatus status,
+ const sp<android::hardware::bluetooth::audio::V2_1::
+ IBluetoothAudioProvider>& provider) {
+ cb_status = status;
+ local_provider = provider;
+ };
+ auto hidl_retval =
+ providers_factory_->openProvider_2_1(session_type, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ if (cb_status == BluetoothAudioStatus::SUCCESS) {
+ ASSERT_NE(
+ session_type,
+ android::hardware::bluetooth::audio::V2_1::SessionType::UNKNOWN);
+ ASSERT_NE(audio_provider_2_1_, nullptr);
+ audio_port_ = new BluetoothAudioPort(*this);
+ } else {
+ ASSERT_TRUE(
+ session_type ==
+ android::hardware::bluetooth::audio::V2_1::SessionType::UNKNOWN);
+ ASSERT_EQ(cb_status, BluetoothAudioStatus::FAILURE);
+ ASSERT_EQ(audio_provider_2_1_, nullptr);
+ }
+ }
+
+ bool IsPcmParametersSupported(const PcmParameters& pcm_parameters) {
+ if (temp_provider_capabilities_.size() != 1 ||
+ temp_provider_capabilities_[0].getDiscriminator() !=
+ AudioCapabilities::hidl_discriminator::pcmCapabilities) {
+ return false;
+ }
+ auto pcm_capability = temp_provider_capabilities_[0].pcmCapabilities();
+ bool is_parameter_valid =
+ (pcm_parameters.sampleRate != android::hardware::bluetooth::audio::
+ V2_0::SampleRate::RATE_UNKNOWN &&
+ pcm_parameters.channelMode != ChannelMode::UNKNOWN &&
+ pcm_parameters.bitsPerSample != BitsPerSample::BITS_UNKNOWN);
+ bool is_parameter_in_capability =
+ (pcm_capability.sampleRate & pcm_parameters.sampleRate &&
+ pcm_capability.channelMode & pcm_parameters.channelMode &&
+ pcm_capability.bitsPerSample & pcm_parameters.bitsPerSample);
+ return is_parameter_valid && is_parameter_in_capability;
+ }
+
+ bool IsPcmParametersSupported_2_1(
+ const android::hardware::bluetooth::audio::V2_1::PcmParameters&
+ pcm_parameters) {
+ if (temp_provider_capabilities_2_1_.size() != 1 ||
+ temp_provider_capabilities_2_1_[0].getDiscriminator() !=
+ android::hardware::bluetooth::audio::V2_1::AudioCapabilities::
+ hidl_discriminator::pcmCapabilities) {
+ return false;
+ }
+ auto pcm_capability = temp_provider_capabilities_2_1_[0].pcmCapabilities();
+ bool is_parameter_valid =
+ (pcm_parameters.sampleRate != SampleRate::RATE_UNKNOWN &&
+ pcm_parameters.channelMode != ChannelMode::UNKNOWN &&
+ pcm_parameters.bitsPerSample != BitsPerSample::BITS_UNKNOWN &&
+ pcm_parameters.dataIntervalUs != 0);
+ bool is_parameter_in_capability =
+ (pcm_capability.sampleRate & pcm_parameters.sampleRate &&
+ pcm_capability.channelMode & pcm_parameters.channelMode &&
+ pcm_capability.bitsPerSample & pcm_parameters.bitsPerSample);
+ return is_parameter_valid && is_parameter_in_capability;
+ }
+
+ sp<IBluetoothAudioProvidersFactory> providers_factory_;
+
+ // temp storage saves the specified provider capability by
+ // GetProviderCapabilitiesHelper()
+ std::vector<AudioCapabilities> temp_provider_capabilities_;
+ std::vector<android::hardware::bluetooth::audio::V2_1::AudioCapabilities>
+ temp_provider_capabilities_2_1_;
+
+ // audio_provider_ is for the Bluetooth stack to report session started/ended
+ // and handled audio stream started / suspended
+ sp<IBluetoothAudioProvider> audio_provider_;
+ sp<android::hardware::bluetooth::audio::V2_1::IBluetoothAudioProvider>
+ audio_provider_2_1_;
+
+ // audio_port_ is for the Audio HAL to send stream start/suspend/stop commands
+ // to Bluetooth stack
+ sp<IBluetoothAudioPort> audio_port_;
+
+ static constexpr SessionType session_types_[4] = {
+ SessionType::UNKNOWN, SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH,
+ SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH,
+ SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH};
+};
+
+/**
+ * Test whether we can get the FactoryService from HIDL
+ */
+TEST_P(BluetoothAudioProvidersFactoryHidlTest, GetProvidersFactoryService) {}
+
+/**
+ * Test whether we can open a provider for each provider returned by
+ * getProviderCapabilities() with non-empty capabalities
+ */
+TEST_P(BluetoothAudioProvidersFactoryHidlTest,
+ OpenProviderAndCheckCapabilitiesBySession) {
+ for (auto session_type : session_types_) {
+ GetProviderCapabilitiesHelper(session_type);
+ OpenProviderHelper(session_type);
+ // We must be able to open a provider if its getProviderCapabilities()
+ // returns non-empty list.
+ EXPECT_TRUE(temp_provider_capabilities_.empty() ||
+ audio_provider_ != nullptr);
+ }
+}
+
+/**
+ * openProvider A2DP_SOFTWARE_ENCODING_DATAPATH
+ */
+class BluetoothAudioProviderA2dpSoftwareHidlTest
+ : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+ virtual void SetUp() override {
+ BluetoothAudioProvidersFactoryHidlTest::SetUp();
+ GetProviderCapabilitiesHelper(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
+ OpenProviderHelper(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
+ ASSERT_NE(audio_provider_, nullptr);
+ }
+
+ virtual void TearDown() override {
+ audio_port_ = nullptr;
+ audio_provider_ = nullptr;
+ BluetoothAudioProvidersFactoryHidlTest::TearDown();
+ }
+};
+
+/**
+ * Test whether we can open a provider of type
+ */
+TEST_P(BluetoothAudioProviderA2dpSoftwareHidlTest, OpenA2dpSoftwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH can be started and stopped with
+ * different PCM config
+ */
+TEST_P(BluetoothAudioProviderA2dpSoftwareHidlTest,
+ StartAndEndA2dpSoftwareSessionWithPossiblePcmConfig) {
+ bool is_codec_config_valid;
+ std::unique_ptr<DataMQ> tempDataMQ;
+ auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
+ BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ if (is_codec_config_valid) {
+ ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ ASSERT_TRUE(dataMQ.isHandleValid());
+ tempDataMQ.reset(new DataMQ(dataMQ));
+ } else {
+ EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ }
+ };
+ AudioConfiguration audio_config = {};
+ PcmParameters pcm_parameters = {};
+ for (auto sample_rate : a2dp_sample_rates) {
+ pcm_parameters.sampleRate = sample_rate;
+ for (auto bits_per_sample : a2dp_bits_per_samples) {
+ pcm_parameters.bitsPerSample = bits_per_sample;
+ for (auto channel_mode : a2dp_channel_modes) {
+ pcm_parameters.channelMode = channel_mode;
+ is_codec_config_valid = IsPcmParametersSupported(pcm_parameters);
+ audio_config.pcmConfig(pcm_parameters);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ if (is_codec_config_valid) {
+ EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+ }
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ } // ChannelMode
+ } // BitsPerSampple
+ } // SampleRate
+}
+
+/**
+ * openProvider A2DP_HARDWARE_OFFLOAD_DATAPATH
+ */
+class BluetoothAudioProviderA2dpHardwareHidlTest
+ : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+ virtual void SetUp() override {
+ BluetoothAudioProvidersFactoryHidlTest::SetUp();
+ GetProviderCapabilitiesHelper(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+ OpenProviderHelper(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+ ASSERT_TRUE(temp_provider_capabilities_.empty() ||
+ audio_provider_ != nullptr);
+ }
+
+ virtual void TearDown() override {
+ audio_port_ = nullptr;
+ audio_provider_ = nullptr;
+ BluetoothAudioProvidersFactoryHidlTest::TearDown();
+ }
+
+ bool IsOffloadSupported() { return (temp_provider_capabilities_.size() > 0); }
+
+ void GetOffloadCodecCapabilityHelper(const CodecType& codec_type) {
+ temp_codec_capabilities_ = {};
+ for (auto codec_capability : temp_provider_capabilities_) {
+ if (codec_capability.codecCapabilities().codecType != codec_type) {
+ continue;
+ }
+ temp_codec_capabilities_ = codec_capability.codecCapabilities();
+ }
+ }
+
+ std::vector<CodecSpecificConfig> GetSbcCodecSpecificSupportedList(
+ bool supported) {
+ std::vector<CodecSpecificConfig> sbc_codec_specifics;
+ GetOffloadCodecCapabilityHelper(CodecType::SBC);
+ if (temp_codec_capabilities_.codecType != CodecType::SBC) {
+ return sbc_codec_specifics;
+ }
+ // parse the capability
+ SbcParameters sbc_capability =
+ temp_codec_capabilities_.capabilities.sbcCapabilities();
+ if (sbc_capability.minBitpool > sbc_capability.maxBitpool) {
+ return sbc_codec_specifics;
+ }
+ std::vector<android::hardware::bluetooth::audio::V2_0::SampleRate>
+ sample_rates = ExtractValuesFromBitmask<
+ android::hardware::bluetooth::audio::V2_0::SampleRate>(
+ sbc_capability.sampleRate, 0xff, supported);
+ std::vector<SbcChannelMode> channel_modes =
+ ExtractValuesFromBitmask<SbcChannelMode>(sbc_capability.channelMode,
+ 0x0f, supported);
+ std::vector<SbcBlockLength> block_lengths =
+ ExtractValuesFromBitmask<SbcBlockLength>(sbc_capability.blockLength,
+ 0xf0, supported);
+ std::vector<SbcNumSubbands> num_subbandss =
+ ExtractValuesFromBitmask<SbcNumSubbands>(sbc_capability.numSubbands,
+ 0x0c, supported);
+ std::vector<SbcAllocMethod> alloc_methods =
+ ExtractValuesFromBitmask<SbcAllocMethod>(sbc_capability.allocMethod,
+ 0x03, supported);
+ std::vector<BitsPerSample> bits_per_samples =
+ ExtractValuesFromBitmask<BitsPerSample>(sbc_capability.bitsPerSample,
+ 0x07, supported);
+ // combine those parameters into one list of
+ // CodecConfiguration::CodecSpecific
+ CodecSpecificConfig codec_specific = {};
+ SbcParameters sbc_data;
+ for (auto sample_rate : sample_rates) {
+ for (auto channel_mode : channel_modes) {
+ for (auto block_length : block_lengths) {
+ for (auto num_subbands : num_subbandss) {
+ for (auto alloc_method : alloc_methods) {
+ for (auto bits_per_sample : bits_per_samples) {
+ sbc_data = {.sampleRate = sample_rate,
+ .channelMode = channel_mode,
+ .blockLength = block_length,
+ .numSubbands = num_subbands,
+ .allocMethod = alloc_method,
+ .bitsPerSample = bits_per_sample,
+ .minBitpool = sbc_capability.minBitpool,
+ .maxBitpool = sbc_capability.maxBitpool};
+ codec_specific.sbcConfig(sbc_data);
+ sbc_codec_specifics.push_back(codec_specific);
+ }
+ }
+ }
+ }
+ }
+ }
+ return sbc_codec_specifics;
+ }
+
+ std::vector<CodecSpecificConfig> GetAacCodecSpecificSupportedList(
+ bool supported) {
+ std::vector<CodecSpecificConfig> aac_codec_specifics;
+ GetOffloadCodecCapabilityHelper(CodecType::AAC);
+ if (temp_codec_capabilities_.codecType != CodecType::AAC) {
+ return aac_codec_specifics;
+ }
+ // parse the capability
+ AacParameters aac_capability =
+ temp_codec_capabilities_.capabilities.aacCapabilities();
+ std::vector<AacObjectType> object_types =
+ ExtractValuesFromBitmask<AacObjectType>(aac_capability.objectType, 0xf0,
+ supported);
+ std::vector<android::hardware::bluetooth::audio::V2_0::SampleRate>
+ sample_rates = ExtractValuesFromBitmask<
+ android::hardware::bluetooth::audio::V2_0::SampleRate>(
+ aac_capability.sampleRate, 0xff, supported);
+ std::vector<ChannelMode> channel_modes =
+ ExtractValuesFromBitmask<ChannelMode>(aac_capability.channelMode, 0x03,
+ supported);
+ std::vector<AacVariableBitRate> variable_bit_rate_enableds = {
+ AacVariableBitRate::DISABLED};
+ if (aac_capability.variableBitRateEnabled == AacVariableBitRate::ENABLED) {
+ variable_bit_rate_enableds.push_back(AacVariableBitRate::ENABLED);
+ }
+ std::vector<BitsPerSample> bits_per_samples =
+ ExtractValuesFromBitmask<BitsPerSample>(aac_capability.bitsPerSample,
+ 0x07, supported);
+ // combine those parameters into one list of
+ // CodecConfiguration::CodecSpecific
+ CodecSpecificConfig codec_specific = {};
+ AacParameters aac_data;
+ for (auto object_type : object_types) {
+ for (auto sample_rate : sample_rates) {
+ for (auto channel_mode : channel_modes) {
+ for (auto variable_bit_rate_enabled : variable_bit_rate_enableds) {
+ for (auto bits_per_sample : bits_per_samples) {
+ aac_data = {.objectType = object_type,
+ .sampleRate = sample_rate,
+ .channelMode = channel_mode,
+ .variableBitRateEnabled = variable_bit_rate_enabled,
+ .bitsPerSample = bits_per_sample};
+ codec_specific.aacConfig(aac_data);
+ aac_codec_specifics.push_back(codec_specific);
+ }
+ }
+ }
+ }
+ }
+ return aac_codec_specifics;
+ }
+
+ std::vector<CodecSpecificConfig> GetLdacCodecSpecificSupportedList(
+ bool supported) {
+ std::vector<CodecSpecificConfig> ldac_codec_specifics;
+ GetOffloadCodecCapabilityHelper(CodecType::LDAC);
+ if (temp_codec_capabilities_.codecType != CodecType::LDAC) {
+ return ldac_codec_specifics;
+ }
+ // parse the capability
+ LdacParameters ldac_capability =
+ temp_codec_capabilities_.capabilities.ldacCapabilities();
+ std::vector<android::hardware::bluetooth::audio::V2_0::SampleRate>
+ sample_rates = ExtractValuesFromBitmask<
+ android::hardware::bluetooth::audio::V2_0::SampleRate>(
+ ldac_capability.sampleRate, 0xff, supported);
+ std::vector<LdacChannelMode> channel_modes =
+ ExtractValuesFromBitmask<LdacChannelMode>(ldac_capability.channelMode,
+ 0x07, supported);
+ std::vector<LdacQualityIndex> quality_indexes = {
+ LdacQualityIndex::QUALITY_HIGH, LdacQualityIndex::QUALITY_MID,
+ LdacQualityIndex::QUALITY_LOW, LdacQualityIndex::QUALITY_ABR};
+ std::vector<BitsPerSample> bits_per_samples =
+ ExtractValuesFromBitmask<BitsPerSample>(ldac_capability.bitsPerSample,
+ 0x07, supported);
+ // combine those parameters into one list of
+ // CodecConfiguration::CodecSpecific
+ CodecSpecificConfig codec_specific = {};
+ LdacParameters ldac_data;
+ for (auto sample_rate : sample_rates) {
+ for (auto channel_mode : channel_modes) {
+ for (auto quality_index : quality_indexes) {
+ for (auto bits_per_sample : bits_per_samples) {
+ ldac_data = {.sampleRate = sample_rate,
+ .channelMode = channel_mode,
+ .qualityIndex = quality_index,
+ .bitsPerSample = bits_per_sample};
+ codec_specific.ldacConfig(ldac_data);
+ ldac_codec_specifics.push_back(codec_specific);
+ }
+ }
+ }
+ }
+ return ldac_codec_specifics;
+ }
+
+ std::vector<CodecSpecificConfig> GetAptxCodecSpecificSupportedList(
+ bool is_hd, bool supported) {
+ std::vector<CodecSpecificConfig> aptx_codec_specifics;
+ GetOffloadCodecCapabilityHelper(
+ (is_hd ? CodecType::APTX_HD : CodecType::APTX));
+ if ((is_hd && temp_codec_capabilities_.codecType != CodecType::APTX_HD) ||
+ (!is_hd && temp_codec_capabilities_.codecType != CodecType::APTX)) {
+ return aptx_codec_specifics;
+ }
+ // parse the capability
+ AptxParameters aptx_capability =
+ temp_codec_capabilities_.capabilities.aptxCapabilities();
+ std::vector<android::hardware::bluetooth::audio::V2_0::SampleRate>
+ sample_rates = ExtractValuesFromBitmask<
+ android::hardware::bluetooth::audio::V2_0::SampleRate>(
+ aptx_capability.sampleRate, 0xff, supported);
+ std::vector<ChannelMode> channel_modes =
+ ExtractValuesFromBitmask<ChannelMode>(aptx_capability.channelMode, 0x03,
+ supported);
+ std::vector<BitsPerSample> bits_per_samples =
+ ExtractValuesFromBitmask<BitsPerSample>(aptx_capability.bitsPerSample,
+ 0x07, supported);
+ // combine those parameters into one list of
+ // CodecConfiguration::CodecSpecific
+ CodecSpecificConfig codec_specific = {};
+ AptxParameters aptx_data;
+ for (auto sample_rate : sample_rates) {
+ for (auto channel_mode : channel_modes) {
+ for (auto bits_per_sample : bits_per_samples) {
+ aptx_data = {.sampleRate = sample_rate,
+ .channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample};
+ codec_specific.aptxConfig(aptx_data);
+ aptx_codec_specifics.push_back(codec_specific);
+ }
+ }
+ }
+ return aptx_codec_specifics;
+ }
+
+ // temp storage saves the specified codec capability by
+ // GetOffloadCodecCapabilityHelper()
+ CodecCapabilities temp_codec_capabilities_;
+};
+
+/**
+ * Test whether we can open a provider of type
+ */
+TEST_P(BluetoothAudioProviderA2dpHardwareHidlTest, OpenA2dpHardwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * SBC hardware encoding config
+ */
+TEST_P(BluetoothAudioProviderA2dpHardwareHidlTest,
+ StartAndEndA2dpSbcHardwareSession) {
+ if (!IsOffloadSupported()) {
+ return;
+ }
+
+ CodecConfiguration codec_config = {};
+ codec_config.codecType = CodecType::SBC;
+ codec_config.encodedAudioBitrate = 328000;
+ codec_config.peerMtu = 1005;
+ codec_config.isScmstEnabled = false;
+ AudioConfiguration audio_config = {};
+ std::vector<CodecSpecificConfig> sbc_codec_specifics =
+ GetSbcCodecSpecificSupportedList(true);
+ auto hidl_cb = [](BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ };
+ for (auto codec_specific : sbc_codec_specifics) {
+ codec_config.config = codec_specific;
+ audio_config.codecConfig(codec_config);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * AAC hardware encoding config
+ */
+TEST_P(BluetoothAudioProviderA2dpHardwareHidlTest,
+ StartAndEndA2dpAacHardwareSession) {
+ if (!IsOffloadSupported()) {
+ return;
+ }
+
+ CodecConfiguration codec_config = {};
+ codec_config.codecType = CodecType::AAC;
+ codec_config.encodedAudioBitrate = 320000;
+ codec_config.peerMtu = 1005;
+ codec_config.isScmstEnabled = false;
+ AudioConfiguration audio_config = {};
+ std::vector<CodecSpecificConfig> aac_codec_specifics =
+ GetAacCodecSpecificSupportedList(true);
+ auto hidl_cb = [](BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ };
+ for (auto codec_specific : aac_codec_specifics) {
+ codec_config.config = codec_specific;
+ audio_config.codecConfig(codec_config);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * LDAC hardware encoding config
+ */
+TEST_P(BluetoothAudioProviderA2dpHardwareHidlTest,
+ StartAndEndA2dpLdacHardwareSession) {
+ if (!IsOffloadSupported()) {
+ return;
+ }
+
+ CodecConfiguration codec_config = {};
+ codec_config.codecType = CodecType::LDAC;
+ codec_config.encodedAudioBitrate = 990000;
+ codec_config.peerMtu = 1005;
+ codec_config.isScmstEnabled = false;
+ AudioConfiguration audio_config = {};
+ std::vector<CodecSpecificConfig> ldac_codec_specifics =
+ GetLdacCodecSpecificSupportedList(true);
+ auto hidl_cb = [](BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ };
+ for (auto codec_specific : ldac_codec_specifics) {
+ codec_config.config = codec_specific;
+ audio_config.codecConfig(codec_config);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * AptX hardware encoding config
+ */
+TEST_P(BluetoothAudioProviderA2dpHardwareHidlTest,
+ StartAndEndA2dpAptxHardwareSession) {
+ if (!IsOffloadSupported()) {
+ return;
+ }
+
+ for (auto codec_type : {CodecType::APTX, CodecType::APTX_HD}) {
+ CodecConfiguration codec_config = {};
+ codec_config.codecType = codec_type;
+ codec_config.encodedAudioBitrate =
+ (codec_type == CodecType::APTX ? 352000 : 576000);
+ codec_config.peerMtu = 1005;
+ codec_config.isScmstEnabled = false;
+ AudioConfiguration audio_config = {};
+ std::vector<CodecSpecificConfig> aptx_codec_specifics =
+ GetAptxCodecSpecificSupportedList(
+ (codec_type == CodecType::APTX_HD ? true : false), true);
+ auto hidl_cb = [](BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ };
+ for (auto codec_specific : aptx_codec_specifics) {
+ codec_config.config = codec_specific;
+ audio_config.codecConfig(codec_config);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ }
+ }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * an invalid codec config
+ */
+TEST_P(BluetoothAudioProviderA2dpHardwareHidlTest,
+ StartAndEndA2dpHardwareSessionInvalidCodecConfig) {
+ if (!IsOffloadSupported()) {
+ return;
+ }
+ ASSERT_NE(audio_provider_, nullptr);
+
+ std::vector<CodecSpecificConfig> codec_specifics;
+ for (auto codec_type : a2dp_codec_types) {
+ switch (codec_type) {
+ case CodecType::SBC:
+ codec_specifics = GetSbcCodecSpecificSupportedList(false);
+ break;
+ case CodecType::AAC:
+ codec_specifics = GetAacCodecSpecificSupportedList(false);
+ break;
+ case CodecType::LDAC:
+ codec_specifics = GetLdacCodecSpecificSupportedList(false);
+ break;
+ case CodecType::APTX:
+ codec_specifics = GetAptxCodecSpecificSupportedList(false, false);
+ break;
+ case CodecType::APTX_HD:
+ codec_specifics = GetAptxCodecSpecificSupportedList(true, false);
+ break;
+ case CodecType::UNKNOWN:
+ codec_specifics.clear();
+ break;
+ }
+ if (codec_specifics.empty()) {
+ continue;
+ }
+
+ CodecConfiguration codec_config = {};
+ codec_config.codecType = codec_type;
+ codec_config.encodedAudioBitrate = 328000;
+ codec_config.peerMtu = 1005;
+ codec_config.isScmstEnabled = false;
+ AudioConfiguration audio_config = {};
+ auto hidl_cb = [](BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ };
+ for (auto codec_specific : codec_specifics) {
+ codec_config.config = codec_specific;
+ audio_config.codecConfig(codec_config);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ }
+ }
+}
+
+/**
+ * openProvider HEARING_AID_SOFTWARE_ENCODING_DATAPATH
+ */
+class BluetoothAudioProviderHearingAidSoftwareHidlTest
+ : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+ virtual void SetUp() override {
+ BluetoothAudioProvidersFactoryHidlTest::SetUp();
+ GetProviderCapabilitiesHelper(
+ SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
+ OpenProviderHelper(SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
+ ASSERT_NE(audio_provider_, nullptr);
+ }
+
+ virtual void TearDown() override {
+ audio_port_ = nullptr;
+ audio_provider_ = nullptr;
+ BluetoothAudioProvidersFactoryHidlTest::TearDown();
+ }
+
+ static constexpr android::hardware::bluetooth::audio::V2_0::SampleRate
+ hearing_aid_sample_rates_[3] = {
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_UNKNOWN,
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_16000,
+ android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_24000};
+ static constexpr BitsPerSample hearing_aid_bits_per_samples_[3] = {
+ BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16,
+ BitsPerSample::BITS_24};
+ static constexpr ChannelMode hearing_aid_channel_modes_[3] = {
+ ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+};
+
+/**
+ * Test whether each provider of type
+ * SessionType::HEARING_AID_HARDWARE_ENCODING_DATAPATH can be started and
+ * stopped with SBC hardware encoding config
+ */
+TEST_P(BluetoothAudioProviderHearingAidSoftwareHidlTest,
+ OpenHearingAidSoftwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH can be started and
+ * stopped with different PCM config
+ */
+TEST_P(BluetoothAudioProviderHearingAidSoftwareHidlTest,
+ StartAndEndHearingAidSessionWithPossiblePcmConfig) {
+ bool is_codec_config_valid;
+ std::unique_ptr<DataMQ> tempDataMQ;
+ auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
+ BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ if (is_codec_config_valid) {
+ ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ ASSERT_TRUE(dataMQ.isHandleValid());
+ tempDataMQ.reset(new DataMQ(dataMQ));
+ } else {
+ EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ }
+ };
+ AudioConfiguration audio_config = {};
+ PcmParameters pcm_parameters = {};
+ for (auto sample_rate : hearing_aid_sample_rates_) {
+ pcm_parameters.sampleRate = sample_rate;
+ for (auto bits_per_sample : hearing_aid_bits_per_samples_) {
+ pcm_parameters.bitsPerSample = bits_per_sample;
+ for (auto channel_mode : hearing_aid_channel_modes_) {
+ pcm_parameters.channelMode = channel_mode;
+ is_codec_config_valid = IsPcmParametersSupported(pcm_parameters);
+ audio_config.pcmConfig(pcm_parameters);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ if (is_codec_config_valid) {
+ EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+ }
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ } // ChannelMode
+ } // BitsPerSampple
+ } // SampleRate
+}
+
+/**
+ * openProvider LE_AUDIO_SOFTWARE_ENCODING_DATAPATH
+ */
+class BluetoothAudioProviderLeAudioOutputSoftwareHidlTest
+ : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+ virtual void SetUp() override {
+ BluetoothAudioProvidersFactoryHidlTest::SetUp();
+ GetProviderCapabilitiesHelper_2_1(
+ android::hardware::bluetooth::audio::V2_1::SessionType::
+ LE_AUDIO_SOFTWARE_ENCODING_DATAPATH);
+ OpenProviderHelper_2_1(
+ android::hardware::bluetooth::audio::V2_1::SessionType::
+ LE_AUDIO_SOFTWARE_ENCODING_DATAPATH);
+ ASSERT_NE(audio_provider_2_1_, nullptr);
+ }
+
+ virtual void TearDown() override {
+ audio_port_ = nullptr;
+ audio_provider_2_1_ = nullptr;
+ BluetoothAudioProvidersFactoryHidlTest::TearDown();
+ }
+
+ static constexpr SampleRate le_audio_output_sample_rates_[3] = {
+ SampleRate::RATE_UNKNOWN, SampleRate::RATE_16000, SampleRate::RATE_24000};
+ static constexpr BitsPerSample le_audio_output_bits_per_samples_[3] = {
+ BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16,
+ BitsPerSample::BITS_24};
+ static constexpr ChannelMode le_audio_output_channel_modes_[3] = {
+ ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+ static constexpr uint32_t le_audio_output_data_interval_us_[2] = {
+ 0 /* Invalid */, 10000 /* Valid 10ms */};
+};
+
+/**
+ * Test whether each provider of type
+ * SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH can be started and
+ * stopped
+ */
+TEST_P(BluetoothAudioProviderLeAudioOutputSoftwareHidlTest,
+ OpenLeAudioOutputSoftwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH can be started and
+ * stopped with different PCM config
+ */
+TEST_P(BluetoothAudioProviderLeAudioOutputSoftwareHidlTest,
+ StartAndEndLeAudioOutputSessionWithPossiblePcmConfig) {
+ bool is_codec_config_valid;
+ std::unique_ptr<DataMQ> tempDataMQ;
+ auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
+ BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ if (is_codec_config_valid) {
+ ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ ASSERT_TRUE(dataMQ.isHandleValid());
+ tempDataMQ.reset(new DataMQ(dataMQ));
+ } else {
+ EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ }
+ };
+ android::hardware::bluetooth::audio::V2_1::AudioConfiguration audio_config =
+ {};
+ android::hardware::bluetooth::audio::V2_1::PcmParameters pcm_parameters = {};
+ for (auto sample_rate : le_audio_output_sample_rates_) {
+ pcm_parameters.sampleRate = sample_rate;
+ for (auto bits_per_sample : le_audio_output_bits_per_samples_) {
+ pcm_parameters.bitsPerSample = bits_per_sample;
+ for (auto channel_mode : le_audio_output_channel_modes_) {
+ pcm_parameters.channelMode = channel_mode;
+ for (auto data_interval_us : le_audio_output_data_interval_us_) {
+ pcm_parameters.dataIntervalUs = data_interval_us;
+ is_codec_config_valid = IsPcmParametersSupported_2_1(pcm_parameters);
+ audio_config.pcmConfig(pcm_parameters);
+ auto hidl_retval = audio_provider_2_1_->startSession_2_1(
+ audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ if (is_codec_config_valid) {
+ EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+ }
+ EXPECT_TRUE(audio_provider_2_1_->endSession().isOk());
+ } // uint32_t (data interval in microseconds)
+ } // ChannelMode
+ } // BitsPerSampple
+ } // SampleRate
+}
+
+/**
+ * openProvider LE_AUDIO_SOFTWARE_DECODED_DATAPATH
+ */
+class BluetoothAudioProviderLeAudioInputSoftwareHidlTest
+ : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+ virtual void SetUp() override {
+ BluetoothAudioProvidersFactoryHidlTest::SetUp();
+ GetProviderCapabilitiesHelper_2_1(
+ android::hardware::bluetooth::audio::V2_1::SessionType::
+ LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
+ OpenProviderHelper_2_1(android::hardware::bluetooth::audio::V2_1::
+ SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
+ ASSERT_NE(audio_provider_2_1_, nullptr);
+ }
+
+ virtual void TearDown() override {
+ audio_port_ = nullptr;
+ audio_provider_2_1_ = nullptr;
+ BluetoothAudioProvidersFactoryHidlTest::TearDown();
+ }
+
+ static constexpr SampleRate le_audio_output_sample_rates_[3] = {
+ SampleRate::RATE_UNKNOWN, SampleRate::RATE_16000, SampleRate::RATE_24000};
+ static constexpr BitsPerSample le_audio_output_bits_per_samples_[3] = {
+ BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16,
+ BitsPerSample::BITS_24};
+ static constexpr ChannelMode le_audio_output_channel_modes_[3] = {
+ ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+ static constexpr uint32_t le_audio_output_data_interval_us_[2] = {
+ 0 /* Invalid */, 10000 /* Valid 10ms */};
+};
+
+/**
+ * Test whether each provider of type
+ * SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH can be started and
+ * stopped
+ */
+TEST_P(BluetoothAudioProviderLeAudioInputSoftwareHidlTest,
+ OpenLeAudioInputSoftwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH can be started and
+ * stopped with different PCM config
+ */
+TEST_P(BluetoothAudioProviderLeAudioInputSoftwareHidlTest,
+ StartAndEndLeAudioInputSessionWithPossiblePcmConfig) {
+ bool is_codec_config_valid;
+ std::unique_ptr<DataMQ> tempDataMQ;
+ auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
+ BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ if (is_codec_config_valid) {
+ ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ ASSERT_TRUE(dataMQ.isHandleValid());
+ tempDataMQ.reset(new DataMQ(dataMQ));
+ } else {
+ EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ }
+ };
+ android::hardware::bluetooth::audio::V2_1::AudioConfiguration audio_config =
+ {};
+ android::hardware::bluetooth::audio::V2_1::PcmParameters pcm_parameters = {};
+ for (auto sample_rate : le_audio_output_sample_rates_) {
+ pcm_parameters.sampleRate = sample_rate;
+ for (auto bits_per_sample : le_audio_output_bits_per_samples_) {
+ pcm_parameters.bitsPerSample = bits_per_sample;
+ for (auto channel_mode : le_audio_output_channel_modes_) {
+ pcm_parameters.channelMode = channel_mode;
+ for (auto data_interval_us : le_audio_output_data_interval_us_) {
+ pcm_parameters.dataIntervalUs = data_interval_us;
+ is_codec_config_valid = IsPcmParametersSupported_2_1(pcm_parameters);
+ audio_config.pcmConfig(pcm_parameters);
+ auto hidl_retval = audio_provider_2_1_->startSession_2_1(
+ audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ if (is_codec_config_valid) {
+ EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+ } else {
+ EXPECT_TRUE(tempDataMQ == nullptr);
+ }
+ EXPECT_TRUE(audio_provider_2_1_->endSession().isOk());
+ } // uint32_t (data interval in microseconds)
+ } // ChannelMode
+ } // BitsPerSampple
+ } // SampleRate
+}
+
+static const std::vector<std::string> kAudioInstances =
+ android::hardware::getAllHalInstanceNames(
+ IBluetoothAudioProvidersFactory::descriptor);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+ BluetoothAudioProvidersFactoryHidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, BluetoothAudioProvidersFactoryHidlTest,
+ testing::ValuesIn(kAudioInstances),
+ android::hardware::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+ BluetoothAudioProviderA2dpSoftwareHidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance,
+ BluetoothAudioProviderA2dpSoftwareHidlTest,
+ testing::ValuesIn(kAudioInstances),
+ android::hardware::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+ BluetoothAudioProviderA2dpHardwareHidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance,
+ BluetoothAudioProviderA2dpHardwareHidlTest,
+ testing::ValuesIn(kAudioInstances),
+ android::hardware::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+ BluetoothAudioProviderHearingAidSoftwareHidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance,
+ BluetoothAudioProviderHearingAidSoftwareHidlTest,
+ testing::ValuesIn(kAudioInstances),
+ android::hardware::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+ BluetoothAudioProviderLeAudioOutputSoftwareHidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance,
+ BluetoothAudioProviderLeAudioOutputSoftwareHidlTest,
+ testing::ValuesIn(kAudioInstances),
+ android::hardware::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+ BluetoothAudioProviderLeAudioInputSoftwareHidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance,
+ BluetoothAudioProviderLeAudioInputSoftwareHidlTest,
+ testing::ValuesIn(kAudioInstances),
+ android::hardware::PrintInstanceNameToString);