diff --git a/audio/common/all-versions/default/service/service.cpp b/audio/common/all-versions/default/service/service.cpp index 147d062204..710ddcea11 100644 --- a/audio/common/all-versions/default/service/service.cpp +++ b/audio/common/all-versions/default/service/service.cpp @@ -87,7 +87,8 @@ int main(int /* argc */, char* /* argv */ []) { }, { "Bluetooth Audio API", - "android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvidersFactory" + "android.hardware.bluetooth.audio@2.1::IBluetoothAudioProvidersFactory", + "android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvidersFactory", }, // remove the old HIDL when Bluetooth Audio Hal V2 has offloading supported { diff --git a/bluetooth/audio/2.1/Android.bp b/bluetooth/audio/2.1/Android.bp new file mode 100644 index 0000000000..9af8add19b --- /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 0000000000..c462b9e15e --- /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 dataMQ); +}; diff --git a/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.hal b/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.hal new file mode 100644 index 0000000000..5593c7c0f5 --- /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); +}; diff --git a/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.cpp new file mode 100644 index 0000000000..b4a61b6ce9 --- /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 +#include +#include + +#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; + +A2dpOffloadAudioProvider::A2dpOffloadAudioProvider() + : BluetoothAudioProvider() { + session_type_ = SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH; +} + +bool A2dpOffloadAudioProvider::isValid(const V2_0::SessionType& sessionType) { + return isValid(static_cast(sessionType)); +} + +bool A2dpOffloadAudioProvider::isValid(const SessionType& sessionType) { + return (sessionType == session_type_); +} + +Return A2dpOffloadAudioProvider::startSession( + const sp& hostIf, + const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + /** + * Initialize the audio platform if audioConfiguration is supported. + * Save the IBluetoothAudioPort interface, so that it can be used + * later to send stream control commands to the HAL client, based on + * interaction with Audio framework. + */ + if (audioConfig.getDiscriminator() != + AudioConfiguration::hidl_discriminator::codecConfig) { + LOG(WARNING) << __func__ + << " - Invalid Audio Configuration=" << toString(audioConfig); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } else if (!android::bluetooth::audio::IsOffloadCodecConfigurationValid( + session_type_, audioConfig.codecConfig())) { + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } + + return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb); +} + +Return A2dpOffloadAudioProvider::onSessionReady( + startSession_cb _hidl_cb) { + BluetoothAudioSessionReport::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 0000000000..13e0b9ab62 --- /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 startSession(const sp& hostIf, + const V2_0::AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + + private: + Return onSessionReady(startSession_cb _hidl_cb) override; +}; + +} // namespace implementation +} // namespace V2_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 0000000000..a67c341abb --- /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 + +#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 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)); +} + +bool A2dpSoftwareAudioProvider::isValid(const SessionType& sessionType) { + return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid()); +} + +Return A2dpSoftwareAudioProvider::startSession( + const sp& 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 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 0000000000..20566d19e8 --- /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 +#include + +#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; + +class A2dpSoftwareAudioProvider : public BluetoothAudioProvider { + public: + A2dpSoftwareAudioProvider(); + + bool isValid(const SessionType& sessionType) override; + bool isValid(const V2_0::SessionType& sessionType) override; + + Return startSession(const sp& hostIf, + const V2_0::AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + + private: + // audio data queue for software encoding + std::unique_ptr mDataMQ; + + Return onSessionReady(startSession_cb _hidl_cb) override; +}; + +} // namespace implementation +} // namespace V2_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 0000000000..5381fec43e --- /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 0000000000..0f349a4650 --- /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 + +#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; + +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 BluetoothAudioProvider::startSession( + const sp& 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(audioConfig.pcmConfig().sampleRate), + .channelMode = audioConfig.pcmConfig().channelMode, + .bitsPerSample = audioConfig.pcmConfig().bitsPerSample, + .dataIntervalUs = 0}; + + return startSession_2_1(hostIf, audioConfig_2_1, _hidl_cb); +} + +Return BluetoothAudioProvider::startSession_2_1( + const sp& hostIf, + const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + if (hostIf == nullptr) { + _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor()); + return Void(); + } + + /** + * Initialize the audio platform if audioConfiguration is supported. + * Save the IBluetoothAudioPort interface, so that it can be used + * later to send stream control commands to the HAL client, based on + * interaction with Audio framework. + */ + audio_config_ = audioConfig; + stack_iface_ = hostIf; + stack_iface_->linkToDeath(death_recipient_, 0); + + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << ", AudioConfiguration=[" << toString(audio_config_) << "]"; + + onSessionReady(_hidl_cb); + return Void(); +} + +Return BluetoothAudioProvider::streamStarted( + BluetoothAudioStatus status) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << ", status=" << toString(status); + + /** + * Streaming on control path has started, + * HAL server should start the streaming on data path. + */ + if (stack_iface_) { + BluetoothAudioSessionReport::ReportControlStatus(session_type_, true, + status); + } else { + LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_) + << ", status=" << toString(status) << " has NO session"; + } + + return Void(); +} + +Return BluetoothAudioProvider::streamSuspended( + BluetoothAudioStatus status) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << ", status=" << toString(status); + + /** + * Streaming on control path has suspend, + * HAL server should suspend the streaming on data path. + */ + if (stack_iface_) { + BluetoothAudioSessionReport::ReportControlStatus(session_type_, false, + status); + } else { + LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_) + << ", status=" << toString(status) << " has NO session"; + } + + return Void(); +} + +Return BluetoothAudioProvider::endSession() { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_); + + if (stack_iface_) { + BluetoothAudioSessionReport::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 0000000000..a2e611f01e --- /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 + +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 startSession(const sp& hostIf, + const V2_0::AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + Return startSession_2_1(const sp& hostIf, + const AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + Return streamStarted(BluetoothAudioStatus status) override; + Return streamSuspended(BluetoothAudioStatus status) override; + Return endSession() override; + + protected: + sp death_recipient_; + + SessionType session_type_; + AudioConfiguration audio_config_; + sp stack_iface_; + + virtual Return onSessionReady(startSession_cb _hidl_cb) = 0; +}; + +class BluetoothAudioDeathRecipient : public hidl_death_recipient { + public: + BluetoothAudioDeathRecipient(const sp provider) + : provider_(provider) {} + + virtual void serviceDied( + uint64_t cookie, + const wp<::android::hidl::base::V1_0::IBase>& who) override; + + private: + sp provider_; +}; + +} // namespace implementation +} // namespace V2_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 0000000000..adf27170d7 --- /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 + +#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 BluetoothAudioProvidersFactory::openProvider( + const V2_0::SessionType sessionType, openProvider_cb _hidl_cb) { + LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType); + BluetoothAudioStatus status = BluetoothAudioStatus::SUCCESS; + BluetoothAudioProvider* provider = nullptr; + switch (sessionType) { + case V2_0::SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH: + provider = &a2dp_software_provider_instance_; + break; + case V2_0::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH: + provider = &a2dp_offload_provider_instance_; + break; + case V2_0::SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH: + provider = &hearing_aid_provider_instance_; + break; + default: + status = BluetoothAudioStatus::FAILURE; + } + if (provider == nullptr || !provider->isValid(sessionType)) { + provider = nullptr; + status = BluetoothAudioStatus::FAILURE; + LOG(ERROR) << __func__ << " - SessionType=" << toString(sessionType) + << ", status=" << toString(status); + } + _hidl_cb(status, provider); + return Void(); +} + +Return BluetoothAudioProvidersFactory::openProvider_2_1( + const 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 BluetoothAudioProvidersFactory::getProviderCapabilities( + const V2_0::SessionType sessionType, getProviderCapabilities_cb _hidl_cb) { + hidl_vec audio_capabilities = + hidl_vec(0); + if (sessionType == V2_0::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) { + std::vector db_codec_capabilities = + android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType); + if (db_codec_capabilities.size()) { + audio_capabilities.resize(db_codec_capabilities.size()); + for (int i = 0; i < db_codec_capabilities.size(); ++i) { + audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]); + } + } + } else if (sessionType != V2_0::SessionType::UNKNOWN) { + std::vector<::android::hardware::bluetooth::audio::V2_0::PcmParameters> + db_pcm_capabilities = + android::bluetooth::audio::GetSoftwarePcmCapabilities(); + if (db_pcm_capabilities.size() == 1) { + audio_capabilities.resize(1); + audio_capabilities[0].pcmCapabilities(db_pcm_capabilities[0]); + } + } + LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType) + << " supports " << audio_capabilities.size() << " codecs"; + _hidl_cb(audio_capabilities); + return Void(); +} + +Return BluetoothAudioProvidersFactory::getProviderCapabilities_2_1( + const SessionType sessionType, getProviderCapabilities_2_1_cb _hidl_cb) { + hidl_vec audio_capabilities = + hidl_vec(0); + if (sessionType == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) { + std::vector db_codec_capabilities = + android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType); + if (db_codec_capabilities.size()) { + audio_capabilities.resize(db_codec_capabilities.size()); + for (int i = 0; i < db_codec_capabilities.size(); ++i) { + audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]); + } + } + } else if (sessionType != SessionType::UNKNOWN) { + std::vector db_pcm_capabilities = + android::bluetooth::audio::GetSoftwarePcmCapabilities_2_1(); + if (db_pcm_capabilities.size() == 1) { + audio_capabilities.resize(1); + audio_capabilities[0].pcmCapabilities(db_pcm_capabilities[0]); + } + } + LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType) + << " supports " << audio_capabilities.size() << " codecs"; + _hidl_cb(audio_capabilities); + return Void(); +} + +IBluetoothAudioProvidersFactory* HIDL_FETCH_IBluetoothAudioProvidersFactory( + const char* /* name */) { + return new BluetoothAudioProvidersFactory(); +} + +} // namespace implementation +} // namespace V2_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 0000000000..fd836945d6 --- /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 + +#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 openProvider(const V2_0::SessionType sessionType, + openProvider_cb _hidl_cb) override; + + Return getProviderCapabilities( + const V2_0::SessionType sessionType, + getProviderCapabilities_cb _hidl_cb) override; + + Return openProvider_2_1(const SessionType sessionType, + openProvider_2_1_cb _hidl_cb) override; + + Return 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 0000000000..aded7e157a --- /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 + +#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 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)); +} + +bool HearingAidAudioProvider::isValid(const SessionType& sessionType) { + return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid()); +} + +Return HearingAidAudioProvider::startSession( + const sp& 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 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 0000000000..c9492570d9 --- /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 +#include + +#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; + +class HearingAidAudioProvider : public BluetoothAudioProvider { + public: + HearingAidAudioProvider(); + + bool isValid(const SessionType& sessionType) override; + bool isValid(const V2_0::SessionType& sessionType) override; + + Return startSession(const sp& hostIf, + const V2_0::AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + + private: + // audio data queue for software encoding + std::unique_ptr mDataMQ; + + Return onSessionReady(startSession_cb _hidl_cb) override; +}; + +} // namespace implementation +} // namespace V2_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 0000000000..1fa2dcea1f --- /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 + +#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 LeAudioAudioProvider::startSession_2_1( + const sp& hostIf, + const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + /** + * Initialize the audio platform if audioConfiguration is supported. + * Save the IBluetoothAudioPort interface, so that it can be used + * later to send stream control commands to the HAL client, based on + * interaction with Audio framework. + */ + if (audioConfig.getDiscriminator() != + AudioConfiguration::hidl_discriminator::pcmConfig) { + LOG(WARNING) << __func__ + << " - Invalid Audio Configuration=" << toString(audioConfig); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid_2_1( + audioConfig.pcmConfig())) { + LOG(WARNING) << __func__ << " - Unsupported PCM Configuration=" + << toString(audioConfig.pcmConfig()); + _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION, + DataMQ::Descriptor()); + return Void(); + } + + uint32_t kDataMqSize = 0; + switch (audioConfig.pcmConfig().sampleRate) { + case SampleRate::RATE_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 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 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 0000000000..09b2b54f46 --- /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 +#include +#include + +#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; + +class LeAudioAudioProvider : public BluetoothAudioProvider { + public: + LeAudioAudioProvider(); + + bool isValid(const SessionType& sessionType) override; + bool isValid(const V2_0::SessionType& sessionType) override; + + Return startSession_2_1(const sp& hostIf, + const AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + + private: + /** queue for software encodec/decoded audio data */ + std::unique_ptr mDataMQ; + + Return onSessionReady(startSession_cb _hidl_cb) override; +}; + +class LeAudioOutputAudioProvider : public LeAudioAudioProvider { + public: + LeAudioOutputAudioProvider(); +}; + +class LeAudioInputAudioProvider : public LeAudioAudioProvider { + public: + LeAudioInputAudioProvider(); +}; + +} // namespace implementation +} // namespace V2_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 0000000000..ea2c54afe1 --- /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 +#include + +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(TS.tvSec), + .tv_nsec = static_cast(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 stack_iface, const DataMQ::Descriptor* dataMQ, + const AudioConfiguration& audio_config) { + std::lock_guard 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 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 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 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 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 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 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 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 cb = + std::make_shared(); + *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 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 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 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 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 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 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 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(track->usage); + halMetadata->contentType = + static_cast(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 lock(mutex_); + if (!IsSessionReady()) break; + size_t availableToWrite = mDataMQ->availableToWrite(); + if (availableToWrite) { + if (availableToWrite > (bytes - totalWritten)) { + availableToWrite = bytes - totalWritten; + } + + if (!mDataMQ->write(static_cast(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 lock(mutex_); + if (!IsSessionReady()) break; + size_t availableToRead = mDataMQ->availableToRead(); + if (availableToRead) { + if (availableToRead > (bytes - totalRead)) { + availableToRead = bytes - totalRead; + } + if (!mDataMQ->read(static_cast(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::instance_ptr = + std::unique_ptr( + new BluetoothAudioSessionInstance()); + +// API to fetch the session +std::shared_ptr +BluetoothAudioSessionInstance::GetSessionInstance( + const SessionType& session_type) { + std::lock_guard guard(instance_ptr->mutex_); + if (!instance_ptr->sessions_map_.empty()) { + auto entry = instance_ptr->sessions_map_.find(session_type); + if (entry != instance_ptr->sessions_map_.end()) { + return entry->second; + } + } + std::shared_ptr session_ptr = + std::make_shared(session_type); + instance_ptr->sessions_map_[session_type] = session_ptr; + return session_ptr; +} + +} // namespace audio +} // namespace bluetooth +} // namespace android diff --git a/bluetooth/audio/2.1/default/session/BluetoothAudioSession.h b/bluetooth/audio/2.1/default/session/BluetoothAudioSession.h new file mode 100644 index 0000000000..7bc12e6337 --- /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 +#include + +#include +#include +#include +#include +#include + +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; + +static constexpr uint16_t kObserversCookieSize = 0x0010; // 0x0000 ~ 0x000f +constexpr uint16_t kObserversCookieUndefined = + (static_cast(SessionType::UNKNOWN) << 8 & 0xff00); +inline SessionType ObserversCookieGetSessionType(uint16_t cookie) { + return static_cast(cookie >> 8 & 0x00ff); +} +inline uint16_t ObserversCookieGetInitValue(SessionType session_type) { + return (static_cast(session_type) << 8 & 0xff00); +} +inline uint16_t ObserversCookieGetUpperBound(SessionType session_type) { + return (static_cast(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 + 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 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 stack_iface_; + // Audio path (FMQ) for software encoding/decoded data + std::unique_ptr 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> + 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 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 GetSessionInstance( + const SessionType& session_type); + + private: + static std::unique_ptr instance_ptr; + std::mutex mutex_; + std::unordered_map> + 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 0000000000..017a6115cb --- /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 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 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 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 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 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 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 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 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 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 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 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 0000000000..267bf8fa92 --- /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 host_iface, + const DataMQ::Descriptor* dataMQ, + const AudioConfiguration& audio_config) { + std::shared_ptr 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 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 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 0000000000..d15db49ff1 --- /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 + +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::MONO | ChannelMode::STEREO), + .bitsPerSample = static_cast(BitsPerSample::BITS_16 | + BitsPerSample::BITS_24 | + BitsPerSample::BITS_32)}; + +static const PcmParameters kDefaultSoftwarePcmCapabilities_2_1 = { + .sampleRate = static_cast( + SampleRate::RATE_48000 | SampleRate::RATE_44100 | + SampleRate::RATE_32000 | SampleRate::RATE_24000 | + SampleRate::RATE_16000 | SampleRate::RATE_8000), + .channelMode = + static_cast(ChannelMode::MONO | ChannelMode::STEREO), + .bitsPerSample = static_cast(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::MONO | + SbcChannelMode::JOINT_STEREO), + .blockLength = static_cast( + 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::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::DUAL | + LdacChannelMode::STEREO), + .qualityIndex = LdacQualityIndex::QUALITY_HIGH, + .bitsPerSample = static_cast(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::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::RATE_44100 | + android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000), + .channelMode = ChannelMode::STEREO, + .bitsPerSample = BitsPerSample::BITS_24, +}; + +const std::vector 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(sbc_data.sampleRate), 0xff) || + !IsSingleBit(static_cast(sbc_data.channelMode), 0x0f) || + !IsSingleBit(static_cast(sbc_data.blockLength), 0xf0) || + !IsSingleBit(static_cast(sbc_data.numSubbands), 0x0c) || + !IsSingleBit(static_cast(sbc_data.allocMethod), 0x03) || + !IsSingleBit(static_cast(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(aac_data.objectType), 0xf0) || + !IsSingleBit(static_cast(aac_data.sampleRate), 0xff) || + !IsSingleBit(static_cast(aac_data.channelMode), 0x03) || + !IsSingleBit(static_cast(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(ldac_data.sampleRate), 0xff) || + !IsSingleBit(static_cast(ldac_data.channelMode), 0x07) || + (ldac_data.qualityIndex > LdacQualityIndex::QUALITY_LOW && + ldac_data.qualityIndex != LdacQualityIndex::QUALITY_ABR) || + !IsSingleBit(static_cast(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(aptx_data.sampleRate), 0xff) || + !IsSingleBit(static_cast(aptx_data.channelMode), 0x03) || + !IsSingleBit(static_cast(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(aptx_data.sampleRate), 0xff) || + !IsSingleBit(static_cast(aptx_data.channelMode), 0x03) || + !IsSingleBit(static_cast(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 GetSoftwarePcmCapabilities_2_1() { + return std::vector(1, kDefaultSoftwarePcmCapabilities_2_1); +} + +std::vector GetOffloadCodecCapabilities( + const ::android::hardware::bluetooth::audio::V2_0::SessionType& + session_type) { + return GetOffloadCodecCapabilities(static_cast(session_type)); +} + +std::vector GetOffloadCodecCapabilities( + const SessionType& session_type) { + if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) { + return std::vector(0); + } + std::vector 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 0000000000..9b2f68092f --- /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 +#include + +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 GetSoftwarePcmCapabilities_2_1(); +std::vector GetOffloadCodecCapabilities( + const SessionType& session_type); +std::vector 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 0000000000..5604c386d2 --- /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 0000000000..b6c0813977 --- /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 0000000000..6ec5537775 --- /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 0000000000..c0ec907e42 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +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 +std::vector ExtractValuesFromBitmask(T bitmasks, uint32_t bitfield, + bool supported) { + std::vector retval; + if (!supported) { + retval.push_back(static_cast(bitfield)); + } + uint32_t test_bit = 0x00000001; + while (test_bit <= static_cast(bitmasks) && test_bit <= bitfield) { + if ((bitfield & test_bit)) { + if ((!(bitmasks & test_bit) && !supported) || + ((bitmasks & test_bit) && supported)) { + retval.push_back(static_cast(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 { + 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 startStream() override { + parent_.audio_provider_->streamStarted(BluetoothAudioStatus::SUCCESS); + return Void(); + } + + Return suspendStream() override { + parent_.audio_provider_->streamSuspended(BluetoothAudioStatus::SUCCESS); + return Void(); + } + + Return stopStream() override { return Void(); } + + Return getPresentationPosition(getPresentationPosition_cb _hidl_cb) { + _hidl_cb(BluetoothAudioStatus::SUCCESS, 0, 0, {.tvSec = 0, .tvNSec = 0}); + return Void(); + } + + Return 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) { + 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(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& 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& 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 providers_factory_; + + // temp storage saves the specified provider capability by + // GetProviderCapabilitiesHelper() + std::vector temp_provider_capabilities_; + std::vector + temp_provider_capabilities_2_1_; + + // audio_provider_ is for the Bluetooth stack to report session started/ended + // and handled audio stream started / suspended + sp audio_provider_; + sp + audio_provider_2_1_; + + // audio_port_ is for the Audio HAL to send stream start/suspend/stop commands + // to Bluetooth stack + sp 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 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 GetSbcCodecSpecificSupportedList( + bool supported) { + std::vector 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 + sample_rates = ExtractValuesFromBitmask< + android::hardware::bluetooth::audio::V2_0::SampleRate>( + sbc_capability.sampleRate, 0xff, supported); + std::vector channel_modes = + ExtractValuesFromBitmask(sbc_capability.channelMode, + 0x0f, supported); + std::vector block_lengths = + ExtractValuesFromBitmask(sbc_capability.blockLength, + 0xf0, supported); + std::vector num_subbandss = + ExtractValuesFromBitmask(sbc_capability.numSubbands, + 0x0c, supported); + std::vector alloc_methods = + ExtractValuesFromBitmask(sbc_capability.allocMethod, + 0x03, supported); + std::vector bits_per_samples = + ExtractValuesFromBitmask(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 GetAacCodecSpecificSupportedList( + bool supported) { + std::vector 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 object_types = + ExtractValuesFromBitmask(aac_capability.objectType, 0xf0, + supported); + std::vector + sample_rates = ExtractValuesFromBitmask< + android::hardware::bluetooth::audio::V2_0::SampleRate>( + aac_capability.sampleRate, 0xff, supported); + std::vector channel_modes = + ExtractValuesFromBitmask(aac_capability.channelMode, 0x03, + supported); + std::vector variable_bit_rate_enableds = { + AacVariableBitRate::DISABLED}; + if (aac_capability.variableBitRateEnabled == AacVariableBitRate::ENABLED) { + variable_bit_rate_enableds.push_back(AacVariableBitRate::ENABLED); + } + std::vector bits_per_samples = + ExtractValuesFromBitmask(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 GetLdacCodecSpecificSupportedList( + bool supported) { + std::vector 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 + sample_rates = ExtractValuesFromBitmask< + android::hardware::bluetooth::audio::V2_0::SampleRate>( + ldac_capability.sampleRate, 0xff, supported); + std::vector channel_modes = + ExtractValuesFromBitmask(ldac_capability.channelMode, + 0x07, supported); + std::vector quality_indexes = { + LdacQualityIndex::QUALITY_HIGH, LdacQualityIndex::QUALITY_MID, + LdacQualityIndex::QUALITY_LOW, LdacQualityIndex::QUALITY_ABR}; + std::vector bits_per_samples = + ExtractValuesFromBitmask(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 GetAptxCodecSpecificSupportedList( + bool is_hd, bool supported) { + std::vector 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 + sample_rates = ExtractValuesFromBitmask< + android::hardware::bluetooth::audio::V2_0::SampleRate>( + aptx_capability.sampleRate, 0xff, supported); + std::vector channel_modes = + ExtractValuesFromBitmask(aptx_capability.channelMode, 0x03, + supported); + std::vector bits_per_samples = + ExtractValuesFromBitmask(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 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 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 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 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 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 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 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 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 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); diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index f60d557db0..77b83b93d2 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -110,7 +110,7 @@ android.hardware.bluetooth.audio - 2.0 + 2.0-1 IBluetoothAudioProvidersFactory default