diff --git a/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.cpp new file mode 100644 index 0000000000..84fba34ca9 --- /dev/null +++ b/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BTAudioProviderA2dpOffload" + +#include +#include +#include + +#include "A2dpOffloadAudioProvider.h" +#include "BluetoothAudioSessionReport.h" +#include "BluetoothAudioSupportedCodecsDB.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_0 { +namespace implementation { + +using ::android::bluetooth::audio::BluetoothAudioSessionReport; +using ::android::hardware::kSynchronizedReadWrite; +using ::android::hardware::MessageQueue; +using ::android::hardware::Void; + +using DataMQ = MessageQueue; + +A2dpOffloadAudioProvider::A2dpOffloadAudioProvider() + : BluetoothAudioProvider() { + session_type_ = SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH; +} + +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 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_0 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.h b/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.h new file mode 100644 index 0000000000..9f40278a4b --- /dev/null +++ b/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.h @@ -0,0 +1,47 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "BluetoothAudioProvider.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_0 { +namespace implementation { + +class A2dpOffloadAudioProvider : public BluetoothAudioProvider { + public: + A2dpOffloadAudioProvider(); + + bool isValid(const SessionType& sessionType) override; + + Return startSession(const sp& hostIf, + const AudioConfiguration& audioConfig, + startSession_cb _hidl_cb) override; + + private: + Return onSessionReady(startSession_cb _hidl_cb) override; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.cpp b/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.cpp new file mode 100644 index 0000000000..f71a73e233 --- /dev/null +++ b/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BTAudioProviderA2dpSoftware" + +#include + +#include "A2dpSoftwareAudioProvider.h" +#include "BluetoothAudioSessionReport.h" +#include "BluetoothAudioSupportedCodecsDB.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_0 { +namespace implementation { + +using ::android::bluetooth::audio::BluetoothAudioSessionReport; +using ::android::hardware::Void; + +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 SessionType& sessionType) { + return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid()); +} + +Return A2dpSoftwareAudioProvider::startSession( + const sp& hostIf, + const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + /** + * Initialize the audio platform if audioConfiguration is supported. + * Save the 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_0 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.h b/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.h new file mode 100644 index 0000000000..228a928c30 --- /dev/null +++ b/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.h @@ -0,0 +1,58 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "BluetoothAudioProvider.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_0 { +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; + + Return startSession(const sp& hostIf, + const 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_0 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.0/default/Android.bp b/bluetooth/audio/2.0/default/Android.bp new file mode 100644 index 0000000000..1dfc05dfaa --- /dev/null +++ b/bluetooth/audio/2.0/default/Android.bp @@ -0,0 +1,48 @@ +cc_library_shared { + name: "android.hardware.bluetooth.audio@2.0-impl", + defaults: ["hidl_defaults"], + vendor: true, + relative_install_path: "hw", + srcs: [ + "BluetoothAudioProvidersFactory.cpp", + "BluetoothAudioProvider.cpp", + "A2dpOffloadAudioProvider.cpp", + "A2dpSoftwareAudioProvider.cpp", + "HearingAidAudioProvider.cpp", + ], + header_libs: ["libhardware_headers"], + shared_libs: [ + "android.hardware.audio.common@5.0", + "android.hardware.bluetooth.audio@2.0", + "libbase", + "libbluetooth_audio_session", + "libcutils", + "libfmq", + "libhidlbase", + "libhidltransport", + "liblog", + "libutils", + ], +} + +cc_library_shared { + name: "libbluetooth_audio_session", + 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", + "libbase", + "libcutils", + "libfmq", + "libhidlbase", + "libhidltransport", + "liblog", + "libutils", + ], +} diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.0/default/BluetoothAudioProvider.cpp new file mode 100644 index 0000000000..ab8973e52e --- /dev/null +++ b/bluetooth/audio/2.0/default/BluetoothAudioProvider.cpp @@ -0,0 +1,143 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BTAudioProviderStub" + +#include + +#include "BluetoothAudioProvider.h" +#include "BluetoothAudioSessionReport.h" +#include "BluetoothAudioSupportedCodecsDB.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_0 { +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 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 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_0 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvider.h b/bluetooth/audio/2.0/default/BluetoothAudioProvider.h new file mode 100644 index 0000000000..02bf2b992a --- /dev/null +++ b/bluetooth/audio/2.0/default/BluetoothAudioProvider.h @@ -0,0 +1,79 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_0 { +namespace implementation { + +using ::android::sp; +using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration; +using ::android::hardware::bluetooth::audio::V2_0::SessionType; + +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; + + Return startSession(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_0 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp new file mode 100644 index 0000000000..df89cc8bae --- /dev/null +++ b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BTAudioProvidersFactory" + +#include + +#include "BluetoothAudioProvidersFactory.h" +#include "BluetoothAudioSupportedCodecsDB.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_0 { +namespace implementation { + +using ::android::hardware::hidl_vec; +using ::android::hardware::Void; + +A2dpSoftwareAudioProvider + BluetoothAudioProvidersFactory::a2dp_software_provider_instance_; +A2dpOffloadAudioProvider + BluetoothAudioProvidersFactory::a2dp_offload_provider_instance_; +HearingAidAudioProvider + BluetoothAudioProvidersFactory::hearing_aid_provider_instance_; + +Return BluetoothAudioProvidersFactory::openProvider( + const SessionType sessionType, openProvider_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; + 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 SessionType sessionType, getProviderCapabilities_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(); + 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_0 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.h b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.h new file mode 100644 index 0000000000..0b515365bb --- /dev/null +++ b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.h @@ -0,0 +1,58 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "A2dpOffloadAudioProvider.h" +#include "A2dpSoftwareAudioProvider.h" +#include "BluetoothAudioProvider.h" +#include "HearingAidAudioProvider.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_0 { +namespace implementation { + +class BluetoothAudioProvidersFactory : public IBluetoothAudioProvidersFactory { + public: + BluetoothAudioProvidersFactory() {} + + Return openProvider(const SessionType sessionType, + openProvider_cb _hidl_cb) override; + + Return getProviderCapabilities( + const SessionType sessionType, + getProviderCapabilities_cb _hidl_cb) override; + + private: + static A2dpSoftwareAudioProvider a2dp_software_provider_instance_; + static A2dpOffloadAudioProvider a2dp_offload_provider_instance_; + static HearingAidAudioProvider hearing_aid_provider_instance_; +}; + +extern "C" IBluetoothAudioProvidersFactory* +HIDL_FETCH_IBluetoothAudioProvidersFactory(const char* name); + +} // namespace implementation +} // namespace V2_0 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.0/default/HearingAidAudioProvider.cpp b/bluetooth/audio/2.0/default/HearingAidAudioProvider.cpp new file mode 100644 index 0000000000..e91cf8ad44 --- /dev/null +++ b/bluetooth/audio/2.0/default/HearingAidAudioProvider.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BTAudioProviderHearingAid" + +#include + +#include "BluetoothAudioSessionReport.h" +#include "BluetoothAudioSupportedCodecsDB.h" +#include "HearingAidAudioProvider.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_0 { +namespace implementation { + +using ::android::bluetooth::audio::BluetoothAudioSessionReport; +using ::android::hardware::Void; + +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 SessionType& sessionType) { + return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid()); +} + +Return HearingAidAudioProvider::startSession( + const sp& hostIf, + const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) { + /** + * Initialize the audio platform if audioConfiguration is supported. + * Save the 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_0 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.0/default/HearingAidAudioProvider.h b/bluetooth/audio/2.0/default/HearingAidAudioProvider.h new file mode 100644 index 0000000000..117eb32c9a --- /dev/null +++ b/bluetooth/audio/2.0/default/HearingAidAudioProvider.h @@ -0,0 +1,58 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "BluetoothAudioProvider.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace audio { +namespace V2_0 { +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; + + Return startSession(const sp& hostIf, + const 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_0 +} // namespace audio +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp new file mode 100644 index 0000000000..d60e7324f3 --- /dev/null +++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp @@ -0,0 +1,427 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BTAudioProviderSession" + +#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 kWritePollMs = 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_); + if (IsSessionReady()) { + ReportSessionStatus(); + } + audio_config_ = (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH + ? kInvalidOffloadAudioConfiguration + : kInvalidSoftwareAudioConfiguration); + stack_iface_ = nullptr; + UpdateDataPath(nullptr); +} + +// 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); + 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 writting %zu/%zu failed", totalWritten, bytes); + return totalWritten; + } + totalWritten += availableToWrite; + } else if (ms_timeout >= kWritePollMs) { + lock.unlock(); + usleep(kWritePollMs * 1000); + ms_timeout -= kWritePollMs; + } else { + ALOGD("data %zu/%zu overflow %d ms", totalWritten, bytes, + (kFmqSendTimeoutMs - ms_timeout)); + return totalWritten; + } + } while (totalWritten < bytes); + return totalWritten; +} + +std::unique_ptr + BluetoothAudioSessionInstance::instance_ptr = + std::unique_ptr( + new BluetoothAudioSessionInstance()); + +// API to fetch the session of A2DP / Hearing Aid +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.0/default/session/BluetoothAudioSession.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h new file mode 100644 index 0000000000..85e8742d07 --- /dev/null +++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h @@ -0,0 +1,185 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include +#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::AudioConfiguration; +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_0::PcmParameters; +using ::android::hardware::bluetooth::audio::V2_0::SampleRate; +using ::android::hardware::bluetooth::audio::V2_0::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 agian. + std::recursive_mutex mutex_; + SessionType session_type_; + + // audio control path to use for both software and offloading + sp stack_iface_; + // audio data path (FMQ) for software encoding + 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); + + static constexpr PcmParameters kInvalidPcmParameters = { + .sampleRate = SampleRate::RATE_UNKNOWN, + .bitsPerSample = BitsPerSample::BITS_UNKNOWN, + .channelMode = ChannelMode::UNKNOWN}; + // 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 of A2DP / Hearing Aid + 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.0/default/session/BluetoothAudioSessionControl.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSessionControl.h new file mode 100644 index 0000000000..6707765638 --- /dev/null +++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSessionControl.h @@ -0,0 +1,143 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "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; + } +}; + +} // namespace audio +} // namespace bluetooth +} // namespace android diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSessionReport.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSessionReport.h new file mode 100644 index 0000000000..5a83ae2d1f --- /dev/null +++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSessionReport.h @@ -0,0 +1,63 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "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 outputs + 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.0/default/session/BluetoothAudioSupportedCodecsDB.cpp b/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.cpp new file mode 100644 index 0000000000..292e28b3b2 --- /dev/null +++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.cpp @@ -0,0 +1,413 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "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::SampleRate; +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; + +// Default Supported PCM Parameters +static const PcmParameters kDefaultSoftwarePcmCapabilities = { + .sampleRate = static_cast( + SampleRate::RATE_44100 | SampleRate::RATE_48000 | + SampleRate::RATE_88200 | SampleRate::RATE_96000 | + SampleRate::RATE_16000 | SampleRate::RATE_24000), + .channelMode = + static_cast(ChannelMode::MONO | ChannelMode::STEREO), + .bitsPerSample = static_cast(BitsPerSample::BITS_16 | + BitsPerSample::BITS_24 | + BitsPerSample::BITS_32)}; + +// Default Supported Codecs +// SBC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(MONO|STEREO) +// all blocks | subbands 8 | Loudness +static const SbcParameters kDefaultOffloadSbcCapability = { + .sampleRate = 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 = SampleRate::RATE_44100, + .channelMode = ChannelMode::STEREO, + .variableBitRateEnabled = AacVariableBitRate::DISABLED, + .bitsPerSample = BitsPerSample::BITS_16}; + +// LDAC: mSampleRate:(44100|48000|88200|96000), mBitsPerSample:(16|24|32), +// mChannelMode:(DUAL|STEREO) +static const LdacParameters kDefaultOffloadLdacCapability = { + .sampleRate = static_cast( + SampleRate::RATE_44100 | SampleRate::RATE_48000 | + SampleRate::RATE_88200 | 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(SampleRate::RATE_44100 | + SampleRate::RATE_48000), + .bitsPerSample = BitsPerSample::BITS_16, + .channelMode = ChannelMode::STEREO}; + +// aptX HD: mSampleRate:(44100|48000), mBitsPerSample:(24), +// mChannelMode:(STEREO) +static const AptxParameters kDefaultOffloadAptxHdCapability = { + .sampleRate = static_cast(SampleRate::RATE_44100 | + SampleRate::RATE_48000), + .bitsPerSample = BitsPerSample::BITS_24, + .channelMode = ChannelMode::STEREO}; + +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 GetSoftwarePcmCapabilities() { + return std::vector(1, kDefaultSoftwarePcmCapabilities); +} + +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 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.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 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.0/default/session/BluetoothAudioSupportedCodecsDB.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.h new file mode 100644 index 0000000000..e71dc8a183 --- /dev/null +++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.h @@ -0,0 +1,40 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +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_0::PcmParameters; +using ::android::hardware::bluetooth::audio::V2_0::SessionType; + +std::vector GetSoftwarePcmCapabilities(); +std::vector GetOffloadCodecCapabilities( + const SessionType& session_type); + +bool IsSoftwarePcmConfigurationValid(const PcmParameters& pcm_config); +bool IsOffloadCodecConfigurationValid(const SessionType& session_type, + const CodecConfiguration& codec_config); + +} // namespace audio +} // namespace bluetooth +} // namespace android