Introduce LE Audio software provider for bluetooth audio 2.1 am: b5f2d23e26 am: a1663edb70

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/1404810

Change-Id: I0d1d2a08de4b5a8184ff0457f7587a30905b5a46
This commit is contained in:
Grzegorz Kolodziejczyk
2020-10-09 18:41:41 +00:00
committed by Automerger Merge Worker
28 changed files with 4147 additions and 2 deletions

View File

@@ -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
{

View File

@@ -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,
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.bluetooth.audio@2.1;
import @2.0::IBluetoothAudioProvider;
import @2.0::IBluetoothAudioPort;
import @2.0::Status;
/**
* HAL interface from the Bluetooth stack to the Audio HAL
*
* The Bluetooth stack calls methods in this interface to start and end audio
* sessions and sends callback events to the Audio HAL.
*
* Note: For HIDL APIs with a "generates" statement, the callback parameter used
* for return value must be invoked synchronously before the API call returns.
*/
interface IBluetoothAudioProvider extends @2.0::IBluetoothAudioProvider {
/**
* This method indicates that the Bluetooth stack is ready to stream audio.
* It registers an instance of IBluetoothAudioPort with and provides the
* current negotiated codec to the Audio HAL. After this method is called,
* the Audio HAL can invoke IBluetoothAudioPort.startStream().
*
* Note: endSession() must be called to unregister this IBluetoothAudioPort
*
* @param hostIf An instance of IBluetoothAudioPort for stream control
* @param audioConfig The audio configuration negotiated with the remote
* device. The PCM parameters are set if software based encoding,
* otherwise the correct codec configuration is used for hardware
* encoding.
*
* @return status One of the following
* SUCCESS if this IBluetoothAudioPort was successfully registered with
* the Audio HAL
* UNSUPPORTED_CODEC_CONFIGURATION if the Audio HAL cannot register this
* IBluetoothAudioPort with the given codec configuration
* FAILURE if the Audio HAL cannot register this IBluetoothAudioPort for
* any other reason
* @return dataMQ The fast message queue for audio data from/to this
* provider. Audio data will be in PCM format as specified by the
* audioConfig.pcmConfig parameter. Invalid if streaming is offloaded
* from/to hardware or on failure.
*/
startSession_2_1(IBluetoothAudioPort hostIf, AudioConfiguration audioConfig)
generates (Status status, fmq_sync<uint8_t> dataMQ);
};

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.bluetooth.audio@2.1;
import IBluetoothAudioProvider;
import @2.0::IBluetoothAudioProvidersFactory;
import @2.0::Status;
/**
* This factory allows a HAL implementation to be split into multiple
* independent providers.
*
* When the Bluetooth stack is ready to create an audio session, it must first
* obtain the IBluetoothAudioProvider for that session type by calling
* openProvider().
*
* Note: For HIDL APIs with a "generates" statement, the callback parameter used
* for return value must be invoked synchronously before the API call returns.
*/
interface IBluetoothAudioProvidersFactory extends @2.0::IBluetoothAudioProvidersFactory {
/**
* Opens an audio provider for a session type. To close the provider, it is
* necessary to release references to the returned provider object.
*
* @param sessionType The session type (e.g.
* LE_AUDIO_SOFTWARE_ENCODING_DATAPATH).
*
* @return status One of the following
* SUCCESS if the Audio HAL successfully opens the provider with the
* given session type
* FAILURE if the Audio HAL cannot open the provider
* @return provider The provider of the specified session type
*/
openProvider_2_1(SessionType sessionType)
generates (Status status, IBluetoothAudioProvider provider);
/**
* Gets a list of audio capabilities for a session type.
*
* For software encoding, the PCM capabilities are returned.
* For hardware encoding, the supported codecs and their capabilities are
* returned.
*
* @param sessionType The session type (e.g.
* A2DP_SOFTWARE_ENCODING_DATAPATH).
* @return audioCapabilities A list containing all the capabilities
* supported by the sesson type. The capabilities is a list of
* available options when configuring the codec for the session.
* For software encoding it is the PCM data rate.
* For hardware encoding it is the list of supported codecs and their
* capabilities.
* If a provider isn't supported, an empty list should be returned.
* Note: Only one entry should exist per codec when using hardware
* encoding.
*/
getProviderCapabilities_2_1(SessionType sessionType)
generates (vec<AudioCapabilities> audioCapabilities);
};

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "BTAudioProviderA2dpOffload"
#include "A2dpOffloadAudioProvider.h"
#include <android-base/logging.h>
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
#include "BluetoothAudioSessionReport.h"
#include "BluetoothAudioSupportedCodecsDB.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
namespace V2_1 {
namespace implementation {
using ::android::bluetooth::audio::BluetoothAudioSessionReport;
using ::android::hardware::kSynchronizedReadWrite;
using ::android::hardware::MessageQueue;
using ::android::hardware::Void;
using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
A2dpOffloadAudioProvider::A2dpOffloadAudioProvider()
: BluetoothAudioProvider() {
session_type_ = SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH;
}
bool A2dpOffloadAudioProvider::isValid(const V2_0::SessionType& sessionType) {
return isValid(static_cast<SessionType>(sessionType));
}
bool A2dpOffloadAudioProvider::isValid(const SessionType& sessionType) {
return (sessionType == session_type_);
}
Return<void> A2dpOffloadAudioProvider::startSession(
const sp<IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
/**
* Initialize the audio platform if audioConfiguration is supported.
* Save the IBluetoothAudioPort interface, so that it can be used
* later to send stream control commands to the HAL client, based on
* interaction with Audio framework.
*/
if (audioConfig.getDiscriminator() !=
AudioConfiguration::hidl_discriminator::codecConfig) {
LOG(WARNING) << __func__
<< " - Invalid Audio Configuration=" << toString(audioConfig);
_hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
DataMQ::Descriptor());
return Void();
} else if (!android::bluetooth::audio::IsOffloadCodecConfigurationValid(
session_type_, audioConfig.codecConfig())) {
_hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
DataMQ::Descriptor());
return Void();
}
return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
}
Return<void> A2dpOffloadAudioProvider::onSessionReady(
startSession_cb _hidl_cb) {
BluetoothAudioSessionReport::OnSessionStarted(session_type_, stack_iface_,
nullptr, audio_config_);
_hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor());
return Void();
}
} // namespace implementation
} // namespace V2_1
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "BluetoothAudioProvider.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
namespace V2_1 {
namespace implementation {
class A2dpOffloadAudioProvider : public BluetoothAudioProvider {
public:
A2dpOffloadAudioProvider();
bool isValid(const SessionType& sessionType) override;
bool isValid(const V2_0::SessionType& sessionType) override;
Return<void> startSession(const sp<V2_0::IBluetoothAudioPort>& hostIf,
const V2_0::AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
private:
Return<void> onSessionReady(startSession_cb _hidl_cb) override;
};
} // namespace implementation
} // namespace V2_1
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "BTAudioProviderA2dpSoftware"
#include "A2dpSoftwareAudioProvider.h"
#include <android-base/logging.h>
#include "BluetoothAudioSessionReport.h"
#include "BluetoothAudioSupportedCodecsDB.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
namespace V2_1 {
namespace implementation {
using ::android::bluetooth::audio::BluetoothAudioSessionReport;
using ::android::hardware::Void;
using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
static constexpr uint32_t kPcmFrameSize = 4; // 16 bits per sample / stereo
static constexpr uint32_t kPcmFrameCount = 128;
static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
static constexpr uint32_t kRtpFrameCount = 7; // max counts by 1 tick (20ms)
static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
static constexpr uint32_t kBufferCount = 2; // double buffer
static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
A2dpSoftwareAudioProvider::A2dpSoftwareAudioProvider()
: BluetoothAudioProvider(), mDataMQ(nullptr) {
LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
<< " byte(s)";
std::unique_ptr<DataMQ> tempDataMQ(
new DataMQ(kDataMqSize, /* EventFlag */ true));
if (tempDataMQ && tempDataMQ->isValid()) {
mDataMQ = std::move(tempDataMQ);
session_type_ = SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
} else {
ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
}
}
bool A2dpSoftwareAudioProvider::isValid(const V2_0::SessionType& sessionType) {
return isValid(static_cast<SessionType>(sessionType));
}
bool A2dpSoftwareAudioProvider::isValid(const SessionType& sessionType) {
return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid());
}
Return<void> A2dpSoftwareAudioProvider::startSession(
const sp<IBluetoothAudioPort>& hostIf,
const V2_0::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
/**
* Initialize the audio platform if audioConfiguration is supported.
* Save the IBluetoothAudioPort interface, so that it can be used
* later to send stream control commands to the HAL client, based on
* interaction with Audio framework.
*/
if (audioConfig.getDiscriminator() !=
AudioConfiguration::hidl_discriminator::pcmConfig) {
LOG(WARNING) << __func__
<< " - Invalid Audio Configuration=" << toString(audioConfig);
_hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
DataMQ::Descriptor());
return Void();
} else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid(
audioConfig.pcmConfig())) {
LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
<< toString(audioConfig.pcmConfig());
_hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
DataMQ::Descriptor());
return Void();
}
return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
}
Return<void> A2dpSoftwareAudioProvider::onSessionReady(
startSession_cb _hidl_cb) {
if (mDataMQ && mDataMQ->isValid()) {
BluetoothAudioSessionReport::OnSessionStarted(
session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
_hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
} else {
_hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
}
return Void();
}
} // namespace implementation
} // namespace V2_1
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
#include "BluetoothAudioProvider.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
namespace V2_1 {
namespace implementation {
using ::android::hardware::kSynchronizedReadWrite;
using ::android::hardware::MessageQueue;
using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
class A2dpSoftwareAudioProvider : public BluetoothAudioProvider {
public:
A2dpSoftwareAudioProvider();
bool isValid(const SessionType& sessionType) override;
bool isValid(const V2_0::SessionType& sessionType) override;
Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
const V2_0::AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
private:
// audio data queue for software encoding
std::unique_ptr<DataMQ> mDataMQ;
Return<void> onSessionReady(startSession_cb _hidl_cb) override;
};
} // namespace implementation
} // namespace V2_1
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android

View File

@@ -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",
],
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "BTAudioProviderStub"
#include "BluetoothAudioProvider.h"
#include <android-base/logging.h>
#include "BluetoothAudioSessionReport.h"
#include "BluetoothAudioSupportedCodecsDB.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
namespace V2_1 {
namespace implementation {
using ::android::bluetooth::audio::BluetoothAudioSessionReport;
using ::android::hardware::kSynchronizedReadWrite;
using ::android::hardware::MessageQueue;
using ::android::hardware::Void;
using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
void BluetoothAudioDeathRecipient::serviceDied(
uint64_t cookie __unused,
const wp<::android::hidl::base::V1_0::IBase>& who __unused) {
LOG(ERROR) << "BluetoothAudioDeathRecipient::" << __func__
<< " - BluetoothAudio Service died";
provider_->endSession();
}
BluetoothAudioProvider::BluetoothAudioProvider()
: death_recipient_(new BluetoothAudioDeathRecipient(this)),
session_type_(SessionType::UNKNOWN),
audio_config_({}) {}
Return<void> BluetoothAudioProvider::startSession(
const sp<IBluetoothAudioPort>& hostIf,
const V2_0::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
AudioConfiguration audioConfig_2_1;
audioConfig_2_1.codecConfig() = audioConfig.codecConfig();
audioConfig_2_1.pcmConfig() = {
.sampleRate = static_cast<SampleRate>(audioConfig.pcmConfig().sampleRate),
.channelMode = audioConfig.pcmConfig().channelMode,
.bitsPerSample = audioConfig.pcmConfig().bitsPerSample,
.dataIntervalUs = 0};
return startSession_2_1(hostIf, audioConfig_2_1, _hidl_cb);
}
Return<void> BluetoothAudioProvider::startSession_2_1(
const sp<IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
if (hostIf == nullptr) {
_hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
return Void();
}
/**
* Initialize the audio platform if audioConfiguration is supported.
* Save the IBluetoothAudioPort interface, so that it can be used
* later to send stream control commands to the HAL client, based on
* interaction with Audio framework.
*/
audio_config_ = audioConfig;
stack_iface_ = hostIf;
stack_iface_->linkToDeath(death_recipient_, 0);
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< ", AudioConfiguration=[" << toString(audio_config_) << "]";
onSessionReady(_hidl_cb);
return Void();
}
Return<void> BluetoothAudioProvider::streamStarted(
BluetoothAudioStatus status) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< ", status=" << toString(status);
/**
* Streaming on control path has started,
* HAL server should start the streaming on data path.
*/
if (stack_iface_) {
BluetoothAudioSessionReport::ReportControlStatus(session_type_, true,
status);
} else {
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
<< ", status=" << toString(status) << " has NO session";
}
return Void();
}
Return<void> BluetoothAudioProvider::streamSuspended(
BluetoothAudioStatus status) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< ", status=" << toString(status);
/**
* Streaming on control path has suspend,
* HAL server should suspend the streaming on data path.
*/
if (stack_iface_) {
BluetoothAudioSessionReport::ReportControlStatus(session_type_, false,
status);
} else {
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
<< ", status=" << toString(status) << " has NO session";
}
return Void();
}
Return<void> BluetoothAudioProvider::endSession() {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
if (stack_iface_) {
BluetoothAudioSessionReport::OnSessionEnded(session_type_);
stack_iface_->unlinkToDeath(death_recipient_);
} else {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO session";
}
/**
* Clean up the audio platform as remote audio device is no
* longer active
*/
stack_iface_ = nullptr;
audio_config_ = {};
return Void();
}
} // namespace implementation
} // namespace V2_1
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <android/hardware/bluetooth/audio/2.1/IBluetoothAudioProvider.h>
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
namespace V2_1 {
namespace implementation {
using ::android::sp;
using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
using BluetoothAudioStatus =
::android::hardware::bluetooth::audio::V2_0::Status;
class BluetoothAudioDeathRecipient;
class BluetoothAudioProvider : public IBluetoothAudioProvider {
public:
BluetoothAudioProvider();
~BluetoothAudioProvider() = default;
virtual bool isValid(const SessionType& sessionType) = 0;
virtual bool isValid(const V2_0::SessionType& sessionType) = 0;
Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
const V2_0::AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
Return<void> startSession_2_1(const sp<IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
Return<void> streamStarted(BluetoothAudioStatus status) override;
Return<void> streamSuspended(BluetoothAudioStatus status) override;
Return<void> endSession() override;
protected:
sp<BluetoothAudioDeathRecipient> death_recipient_;
SessionType session_type_;
AudioConfiguration audio_config_;
sp<V2_0::IBluetoothAudioPort> stack_iface_;
virtual Return<void> onSessionReady(startSession_cb _hidl_cb) = 0;
};
class BluetoothAudioDeathRecipient : public hidl_death_recipient {
public:
BluetoothAudioDeathRecipient(const sp<BluetoothAudioProvider> provider)
: provider_(provider) {}
virtual void serviceDied(
uint64_t cookie,
const wp<::android::hidl::base::V1_0::IBase>& who) override;
private:
sp<BluetoothAudioProvider> provider_;
};
} // namespace implementation
} // namespace V2_1
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,174 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "BTAudioProvidersFactory"
#include "BluetoothAudioProvidersFactory.h"
#include <android-base/logging.h>
#include "BluetoothAudioSupportedCodecsDB.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
namespace V2_1 {
namespace implementation {
using ::android::hardware::hidl_vec;
using ::android::hardware::Void;
using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
A2dpSoftwareAudioProvider
BluetoothAudioProvidersFactory::a2dp_software_provider_instance_;
A2dpOffloadAudioProvider
BluetoothAudioProvidersFactory::a2dp_offload_provider_instance_;
HearingAidAudioProvider
BluetoothAudioProvidersFactory::hearing_aid_provider_instance_;
LeAudioOutputAudioProvider
BluetoothAudioProvidersFactory::leaudio_output_provider_instance_;
LeAudioInputAudioProvider
BluetoothAudioProvidersFactory::leaudio_input_provider_instance_;
Return<void> BluetoothAudioProvidersFactory::openProvider(
const V2_0::SessionType sessionType, openProvider_cb _hidl_cb) {
LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType);
BluetoothAudioStatus status = BluetoothAudioStatus::SUCCESS;
BluetoothAudioProvider* provider = nullptr;
switch (sessionType) {
case V2_0::SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH:
provider = &a2dp_software_provider_instance_;
break;
case V2_0::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH:
provider = &a2dp_offload_provider_instance_;
break;
case V2_0::SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH:
provider = &hearing_aid_provider_instance_;
break;
default:
status = BluetoothAudioStatus::FAILURE;
}
if (provider == nullptr || !provider->isValid(sessionType)) {
provider = nullptr;
status = BluetoothAudioStatus::FAILURE;
LOG(ERROR) << __func__ << " - SessionType=" << toString(sessionType)
<< ", status=" << toString(status);
}
_hidl_cb(status, provider);
return Void();
}
Return<void> BluetoothAudioProvidersFactory::openProvider_2_1(
const SessionType sessionType, openProvider_2_1_cb _hidl_cb) {
LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType);
BluetoothAudioStatus status = BluetoothAudioStatus::SUCCESS;
BluetoothAudioProvider* provider = nullptr;
switch (sessionType) {
case SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH:
provider = &a2dp_software_provider_instance_;
break;
case SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH:
provider = &a2dp_offload_provider_instance_;
break;
case SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH:
provider = &hearing_aid_provider_instance_;
break;
case SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH:
provider = &leaudio_output_provider_instance_;
break;
case SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH:
provider = &leaudio_input_provider_instance_;
break;
default:
status = BluetoothAudioStatus::FAILURE;
}
if (provider == nullptr || !provider->isValid(sessionType)) {
provider = nullptr;
status = BluetoothAudioStatus::FAILURE;
LOG(ERROR) << __func__ << " - SessionType=" << toString(sessionType)
<< ", status=" << toString(status);
}
_hidl_cb(status, provider);
return Void();
}
Return<void> BluetoothAudioProvidersFactory::getProviderCapabilities(
const V2_0::SessionType sessionType, getProviderCapabilities_cb _hidl_cb) {
hidl_vec<V2_0::AudioCapabilities> audio_capabilities =
hidl_vec<V2_0::AudioCapabilities>(0);
if (sessionType == V2_0::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
std::vector<CodecCapabilities> db_codec_capabilities =
android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType);
if (db_codec_capabilities.size()) {
audio_capabilities.resize(db_codec_capabilities.size());
for (int i = 0; i < db_codec_capabilities.size(); ++i) {
audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]);
}
}
} else if (sessionType != V2_0::SessionType::UNKNOWN) {
std::vector<::android::hardware::bluetooth::audio::V2_0::PcmParameters>
db_pcm_capabilities =
android::bluetooth::audio::GetSoftwarePcmCapabilities();
if (db_pcm_capabilities.size() == 1) {
audio_capabilities.resize(1);
audio_capabilities[0].pcmCapabilities(db_pcm_capabilities[0]);
}
}
LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType)
<< " supports " << audio_capabilities.size() << " codecs";
_hidl_cb(audio_capabilities);
return Void();
}
Return<void> BluetoothAudioProvidersFactory::getProviderCapabilities_2_1(
const SessionType sessionType, getProviderCapabilities_2_1_cb _hidl_cb) {
hidl_vec<AudioCapabilities> audio_capabilities =
hidl_vec<AudioCapabilities>(0);
if (sessionType == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
std::vector<CodecCapabilities> db_codec_capabilities =
android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType);
if (db_codec_capabilities.size()) {
audio_capabilities.resize(db_codec_capabilities.size());
for (int i = 0; i < db_codec_capabilities.size(); ++i) {
audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]);
}
}
} else if (sessionType != SessionType::UNKNOWN) {
std::vector<PcmParameters> db_pcm_capabilities =
android::bluetooth::audio::GetSoftwarePcmCapabilities_2_1();
if (db_pcm_capabilities.size() == 1) {
audio_capabilities.resize(1);
audio_capabilities[0].pcmCapabilities(db_pcm_capabilities[0]);
}
}
LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType)
<< " supports " << audio_capabilities.size() << " codecs";
_hidl_cb(audio_capabilities);
return Void();
}
IBluetoothAudioProvidersFactory* HIDL_FETCH_IBluetoothAudioProvidersFactory(
const char* /* name */) {
return new BluetoothAudioProvidersFactory();
}
} // namespace implementation
} // namespace V2_1
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <android/hardware/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.h>
#include "A2dpOffloadAudioProvider.h"
#include "A2dpSoftwareAudioProvider.h"
#include "BluetoothAudioProvider.h"
#include "HearingAidAudioProvider.h"
#include "LeAudioAudioProvider.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
namespace V2_1 {
namespace implementation {
class BluetoothAudioProvidersFactory : public IBluetoothAudioProvidersFactory {
public:
BluetoothAudioProvidersFactory() {}
Return<void> openProvider(const V2_0::SessionType sessionType,
openProvider_cb _hidl_cb) override;
Return<void> getProviderCapabilities(
const V2_0::SessionType sessionType,
getProviderCapabilities_cb _hidl_cb) override;
Return<void> openProvider_2_1(const SessionType sessionType,
openProvider_2_1_cb _hidl_cb) override;
Return<void> getProviderCapabilities_2_1(
const SessionType sessionType,
getProviderCapabilities_2_1_cb _hidl_cb) override;
private:
static A2dpSoftwareAudioProvider a2dp_software_provider_instance_;
static A2dpOffloadAudioProvider a2dp_offload_provider_instance_;
static HearingAidAudioProvider hearing_aid_provider_instance_;
static LeAudioOutputAudioProvider leaudio_output_provider_instance_;
static LeAudioInputAudioProvider leaudio_input_provider_instance_;
};
extern "C" IBluetoothAudioProvidersFactory*
HIDL_FETCH_IBluetoothAudioProvidersFactory(const char* name);
} // namespace implementation
} // namespace V2_1
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "BTAudioProviderHearingAid"
#include "HearingAidAudioProvider.h"
#include <android-base/logging.h>
#include "BluetoothAudioSessionReport.h"
#include "BluetoothAudioSupportedCodecsDB.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
namespace V2_1 {
namespace implementation {
using ::android::bluetooth::audio::BluetoothAudioSessionReport;
using ::android::hardware::Void;
using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
static constexpr uint32_t kPcmFrameSize = 4; // 16 bits per sample / stereo
static constexpr uint32_t kPcmFrameCount = 128;
static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
static constexpr uint32_t kRtpFrameCount = 7; // max counts by 1 tick (20ms)
static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
static constexpr uint32_t kBufferCount = 1; // single buffer
static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
HearingAidAudioProvider::HearingAidAudioProvider()
: BluetoothAudioProvider(), mDataMQ(nullptr) {
LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
<< " byte(s)";
std::unique_ptr<DataMQ> tempDataMQ(
new DataMQ(kDataMqSize, /* EventFlag */ true));
if (tempDataMQ && tempDataMQ->isValid()) {
mDataMQ = std::move(tempDataMQ);
session_type_ = SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH;
} else {
ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
}
}
bool HearingAidAudioProvider::isValid(const V2_0::SessionType& sessionType) {
return isValid(static_cast<SessionType>(sessionType));
}
bool HearingAidAudioProvider::isValid(const SessionType& sessionType) {
return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid());
}
Return<void> HearingAidAudioProvider::startSession(
const sp<IBluetoothAudioPort>& hostIf,
const V2_0::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
/**
* Initialize the audio platform if audioConfiguration is supported.
* Save the IBluetoothAudioPort interface, so that it can be used
* later to send stream control commands to the HAL client, based on
* interaction with Audio framework.
*/
if (audioConfig.getDiscriminator() !=
AudioConfiguration::hidl_discriminator::pcmConfig) {
LOG(WARNING) << __func__
<< " - Invalid Audio Configuration=" << toString(audioConfig);
_hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
DataMQ::Descriptor());
return Void();
} else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid(
audioConfig.pcmConfig())) {
LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
<< toString(audioConfig.pcmConfig());
_hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
DataMQ::Descriptor());
return Void();
}
return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
}
Return<void> HearingAidAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
if (mDataMQ && mDataMQ->isValid()) {
BluetoothAudioSessionReport::OnSessionStarted(
session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
_hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
} else {
_hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
}
return Void();
}
} // namespace implementation
} // namespace V2_1
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
#include "BluetoothAudioProvider.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
namespace V2_1 {
namespace implementation {
using ::android::hardware::kSynchronizedReadWrite;
using ::android::hardware::MessageQueue;
using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
class HearingAidAudioProvider : public BluetoothAudioProvider {
public:
HearingAidAudioProvider();
bool isValid(const SessionType& sessionType) override;
bool isValid(const V2_0::SessionType& sessionType) override;
Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
const V2_0::AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
private:
// audio data queue for software encoding
std::unique_ptr<DataMQ> mDataMQ;
Return<void> onSessionReady(startSession_cb _hidl_cb) override;
};
} // namespace implementation
} // namespace V2_1
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,201 @@
/*
* Copyright 2020 HIMSA II K/S - www.himsa.com. Represented by EHIMA -
* www.ehima.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "BTAudioProviderLeAudio"
#include "LeAudioAudioProvider.h"
#include <android-base/logging.h>
#include "BluetoothAudioSessionReport.h"
#include "BluetoothAudioSupportedCodecsDB.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
namespace V2_1 {
namespace implementation {
using ::android::bluetooth::audio::BluetoothAudioSessionReport;
using ::android::hardware::Void;
using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
static constexpr uint32_t kBufferOutCount = 2; // two frame buffer
static constexpr uint32_t kBufferInCount = 2; // two frame buffer
LeAudioOutputAudioProvider::LeAudioOutputAudioProvider()
: LeAudioAudioProvider() {
session_type_ = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
}
LeAudioInputAudioProvider::LeAudioInputAudioProvider()
: LeAudioAudioProvider() {
session_type_ = SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH;
}
LeAudioAudioProvider::LeAudioAudioProvider()
: BluetoothAudioProvider(), mDataMQ(nullptr) {}
bool LeAudioAudioProvider::isValid(const V2_0::SessionType& sessionType) {
LOG(ERROR) << __func__ << ", invalid session type for Le Audio provider: "
<< toString(sessionType);
return false;
}
bool LeAudioAudioProvider::isValid(const SessionType& sessionType) {
return (sessionType == session_type_);
}
Return<void> LeAudioAudioProvider::startSession_2_1(
const sp<V2_0::IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
/**
* Initialize the audio platform if audioConfiguration is supported.
* Save the IBluetoothAudioPort interface, so that it can be used
* later to send stream control commands to the HAL client, based on
* interaction with Audio framework.
*/
if (audioConfig.getDiscriminator() !=
AudioConfiguration::hidl_discriminator::pcmConfig) {
LOG(WARNING) << __func__
<< " - Invalid Audio Configuration=" << toString(audioConfig);
_hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
DataMQ::Descriptor());
return Void();
} else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid_2_1(
audioConfig.pcmConfig())) {
LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
<< toString(audioConfig.pcmConfig());
_hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
DataMQ::Descriptor());
return Void();
}
uint32_t kDataMqSize = 0;
switch (audioConfig.pcmConfig().sampleRate) {
case SampleRate::RATE_16000:
kDataMqSize = 16000;
break;
case SampleRate::RATE_24000:
kDataMqSize = 24000;
break;
case SampleRate::RATE_44100:
kDataMqSize = 44100;
break;
case SampleRate::RATE_48000:
kDataMqSize = 48000;
break;
case SampleRate::RATE_88200:
kDataMqSize = 88200;
break;
case SampleRate::RATE_96000:
kDataMqSize = 96000;
break;
case SampleRate::RATE_176400:
kDataMqSize = 176400;
break;
case SampleRate::RATE_192000:
kDataMqSize = 192000;
break;
default:
/* This should never happen it would be caught while validating
* parameters.
*/
break;
}
/* Number of samples per millisecond */
kDataMqSize = ceil(kDataMqSize / 1000);
switch (audioConfig.pcmConfig().channelMode) {
case ChannelMode::MONO:
break;
case ChannelMode::STEREO:
kDataMqSize *= 2;
break;
default:
/* This should never happen it would be caught while validating
* parameters.
*/
break;
}
switch (audioConfig.pcmConfig().bitsPerSample) {
case BitsPerSample::BITS_16:
kDataMqSize *= 2;
break;
case BitsPerSample::BITS_24:
kDataMqSize *= 3;
break;
case BitsPerSample::BITS_32:
kDataMqSize *= 4;
break;
default:
/* This should never happen it would be caught while validating
* parameters.
*/
break;
}
if (session_type_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH)
kDataMqSize *= kBufferOutCount;
else if (session_type_ == SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH)
kDataMqSize *= kBufferInCount;
else
LOG(WARNING) << __func__ << ", default single buffer used";
kDataMqSize *= audioConfig.pcmConfig().dataIntervalUs / 1000;
LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
<< " byte(s)";
std::unique_ptr<DataMQ> tempDataMQ(
new DataMQ(kDataMqSize, /* EventFlag */ true));
if (tempDataMQ && tempDataMQ->isValid()) {
mDataMQ = std::move(tempDataMQ);
} else {
ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
_hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
return Void();
}
return BluetoothAudioProvider::startSession_2_1(hostIf, audioConfig,
_hidl_cb);
}
Return<void> LeAudioAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
if (mDataMQ && mDataMQ->isValid()) {
BluetoothAudioSessionReport::OnSessionStarted(
session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
_hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
} else {
_hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
}
return Void();
}
} // namespace implementation
} // namespace V2_1
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2020 HIMSA II K/S - www.himsa.com. Represented by EHIMA -
* www.ehima.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <android/hardware/bluetooth/audio/2.1/types.h>
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
#include "BluetoothAudioProvider.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {
namespace V2_1 {
namespace implementation {
using ::android::hardware::kSynchronizedReadWrite;
using ::android::hardware::MessageQueue;
using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
class LeAudioAudioProvider : public BluetoothAudioProvider {
public:
LeAudioAudioProvider();
bool isValid(const SessionType& sessionType) override;
bool isValid(const V2_0::SessionType& sessionType) override;
Return<void> startSession_2_1(const sp<V2_0::IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
private:
/** queue for software encodec/decoded audio data */
std::unique_ptr<DataMQ> mDataMQ;
Return<void> onSessionReady(startSession_cb _hidl_cb) override;
};
class LeAudioOutputAudioProvider : public LeAudioAudioProvider {
public:
LeAudioOutputAudioProvider();
};
class LeAudioInputAudioProvider : public LeAudioAudioProvider {
public:
LeAudioInputAudioProvider();
};
} // namespace implementation
} // namespace V2_1
} // namespace audio
} // namespace bluetooth
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,467 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "BTAudioProviderSession"
#include "BluetoothAudioSession.h"
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
namespace android {
namespace bluetooth {
namespace audio {
using ::android::hardware::audio::common::V5_0::AudioContentType;
using ::android::hardware::audio::common::V5_0::AudioUsage;
using ::android::hardware::audio::common::V5_0::PlaybackTrackMetadata;
using ::android::hardware::audio::common::V5_0::SourceMetadata;
using ::android::hardware::bluetooth::audio::V2_0::CodecType;
using ::android::hardware::bluetooth::audio::V2_0::TimeSpec;
const CodecConfiguration BluetoothAudioSession::kInvalidCodecConfiguration = {
.codecType = CodecType::UNKNOWN,
.encodedAudioBitrate = 0x00000000,
.peerMtu = 0xffff,
.isScmstEnabled = false,
.config = {}};
AudioConfiguration BluetoothAudioSession::invalidSoftwareAudioConfiguration =
{};
AudioConfiguration BluetoothAudioSession::invalidOffloadAudioConfiguration = {};
static constexpr int kFmqSendTimeoutMs = 1000; // 1000 ms timeout for sending
static constexpr int kFmqReceiveTimeoutMs =
1000; // 1000 ms timeout for receiving
static constexpr int kWritePollMs = 1; // polled non-blocking interval
static constexpr int kReadPollMs = 1; // polled non-blocking interval
static inline timespec timespec_convert_from_hal(const TimeSpec& TS) {
return {.tv_sec = static_cast<long>(TS.tvSec),
.tv_nsec = static_cast<long>(TS.tvNSec)};
}
BluetoothAudioSession::BluetoothAudioSession(const SessionType& session_type)
: session_type_(session_type), stack_iface_(nullptr), mDataMQ(nullptr) {
invalidSoftwareAudioConfiguration.pcmConfig(kInvalidPcmParameters);
invalidOffloadAudioConfiguration.codecConfig(kInvalidCodecConfiguration);
}
// The report function is used to report that the Bluetooth stack has started
// this session without any failure, and will invoke session_changed_cb_ to
// notify those registered bluetooth_audio outputs
void BluetoothAudioSession::OnSessionStarted(
const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
const AudioConfiguration& audio_config) {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (stack_iface == nullptr) {
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
<< ", IBluetoothAudioPort Invalid";
} else if (!UpdateAudioConfig(audio_config)) {
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
<< ", AudioConfiguration=" << toString(audio_config)
<< " Invalid";
} else if (!UpdateDataPath(dataMQ)) {
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
<< " DataMQ Invalid";
audio_config_ =
(session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
? kInvalidOffloadAudioConfiguration
: kInvalidSoftwareAudioConfiguration);
} else {
stack_iface_ = stack_iface;
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< ", AudioConfiguration=" << toString(audio_config);
ReportSessionStatus();
}
}
// The report function is used to report that the Bluetooth stack has ended the
// session, and will invoke session_changed_cb_ to notify registered
// bluetooth_audio outputs
void BluetoothAudioSession::OnSessionEnded() {
std::lock_guard<std::recursive_mutex> guard(mutex_);
bool toggled = IsSessionReady();
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
audio_config_ = (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
? kInvalidOffloadAudioConfiguration
: kInvalidSoftwareAudioConfiguration);
stack_iface_ = nullptr;
UpdateDataPath(nullptr);
if (toggled) {
ReportSessionStatus();
}
}
// invoking the registered session_changed_cb_
void BluetoothAudioSession::ReportSessionStatus() {
// This is locked already by OnSessionStarted / OnSessionEnded
if (observers_.empty()) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO port state observer";
return;
}
for (auto& observer : observers_) {
uint16_t cookie = observer.first;
std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
<< " notify to bluetooth_audio=0x"
<< android::base::StringPrintf("%04x", cookie);
cb->session_changed_cb_(cookie);
}
}
// The report function is used to report that the Bluetooth stack has notified
// the result of startStream or suspendStream, and will invoke
// control_result_cb_ to notify registered bluetooth_audio outputs
void BluetoothAudioSession::ReportControlStatus(
bool start_resp, const BluetoothAudioStatus& status) {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (observers_.empty()) {
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO port state observer";
return;
}
for (auto& observer : observers_) {
uint16_t cookie = observer.first;
std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
LOG(INFO) << __func__ << " - status=" << toString(status)
<< " for SessionType=" << toString(session_type_)
<< ", bluetooth_audio=0x"
<< android::base::StringPrintf("%04x", cookie)
<< (start_resp ? " started" : " suspended");
cb->control_result_cb_(cookie, start_resp, status);
}
}
// The function helps to check if this session is ready or not
// @return: true if the Bluetooth stack has started the specified session
bool BluetoothAudioSession::IsSessionReady() {
std::lock_guard<std::recursive_mutex> guard(mutex_);
bool dataMQ_valid =
(session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
(mDataMQ != nullptr && mDataMQ->isValid()));
return stack_iface_ != nullptr && dataMQ_valid;
}
bool BluetoothAudioSession::UpdateDataPath(const DataMQ::Descriptor* dataMQ) {
if (dataMQ == nullptr) {
// usecase of reset by nullptr
mDataMQ = nullptr;
return true;
}
std::unique_ptr<DataMQ> tempDataMQ;
tempDataMQ.reset(new DataMQ(*dataMQ));
if (!tempDataMQ || !tempDataMQ->isValid()) {
mDataMQ = nullptr;
return false;
}
mDataMQ = std::move(tempDataMQ);
return true;
}
bool BluetoothAudioSession::UpdateAudioConfig(
const AudioConfiguration& audio_config) {
bool is_software_session =
(session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
session_type_ == SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH ||
session_type_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
session_type_ == SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
bool is_offload_session =
(session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
auto audio_config_discriminator = audio_config.getDiscriminator();
bool is_software_audio_config =
(is_software_session &&
audio_config_discriminator ==
AudioConfiguration::hidl_discriminator::pcmConfig);
bool is_offload_audio_config =
(is_offload_session &&
audio_config_discriminator ==
AudioConfiguration::hidl_discriminator::codecConfig);
if (!is_software_audio_config && !is_offload_audio_config) {
return false;
}
audio_config_ = audio_config;
return true;
}
// The control function helps the bluetooth_audio module to register
// PortStatusCallbacks
// @return: cookie - the assigned number to this bluetooth_audio output
uint16_t BluetoothAudioSession::RegisterStatusCback(
const PortStatusCallbacks& cbacks) {
std::lock_guard<std::recursive_mutex> guard(mutex_);
uint16_t cookie = ObserversCookieGetInitValue(session_type_);
uint16_t cookie_upper_bound = ObserversCookieGetUpperBound(session_type_);
while (cookie < cookie_upper_bound) {
if (observers_.find(cookie) == observers_.end()) {
break;
}
++cookie;
}
if (cookie >= cookie_upper_bound) {
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
<< " has " << observers_.size()
<< " observers already (No Resource)";
return kObserversCookieUndefined;
}
std::shared_ptr<struct PortStatusCallbacks> cb =
std::make_shared<struct PortStatusCallbacks>();
*cb = cbacks;
observers_[cookie] = cb;
return cookie;
}
// The control function helps the bluetooth_audio module to unregister
// PortStatusCallbacks
// @param: cookie - indicates which bluetooth_audio output is
void BluetoothAudioSession::UnregisterStatusCback(uint16_t cookie) {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (observers_.erase(cookie) != 1) {
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
<< " no such provider=0x"
<< android::base::StringPrintf("%04x", cookie);
}
}
// The control function is for the bluetooth_audio module to get the current
// AudioConfiguration
const AudioConfiguration& BluetoothAudioSession::GetAudioConfig() {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (IsSessionReady()) {
return audio_config_;
} else if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
return kInvalidOffloadAudioConfiguration;
} else {
return kInvalidSoftwareAudioConfiguration;
}
}
// Those control functions are for the bluetooth_audio module to start, suspend,
// stop stream, to check position, and to update metadata.
bool BluetoothAudioSession::StartStream() {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (!IsSessionReady()) {
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO session";
return false;
}
auto hal_retval = stack_iface_->startStream();
if (!hal_retval.isOk()) {
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
<< toString(session_type_) << " failed";
return false;
}
return true;
}
bool BluetoothAudioSession::SuspendStream() {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (!IsSessionReady()) {
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO session";
return false;
}
auto hal_retval = stack_iface_->suspendStream();
if (!hal_retval.isOk()) {
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
<< toString(session_type_) << " failed";
return false;
}
return true;
}
void BluetoothAudioSession::StopStream() {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (!IsSessionReady()) {
return;
}
auto hal_retval = stack_iface_->stopStream();
if (!hal_retval.isOk()) {
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
<< toString(session_type_) << " failed";
}
}
bool BluetoothAudioSession::GetPresentationPosition(
uint64_t* remote_delay_report_ns, uint64_t* total_bytes_readed,
timespec* data_position) {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (!IsSessionReady()) {
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO session";
return false;
}
bool retval = false;
auto hal_retval = stack_iface_->getPresentationPosition(
[&retval, &remote_delay_report_ns, &total_bytes_readed, &data_position](
BluetoothAudioStatus status,
const uint64_t& remoteDeviceAudioDelayNanos,
uint64_t transmittedOctets,
const TimeSpec& transmittedOctetsTimeStamp) {
if (status == BluetoothAudioStatus::SUCCESS) {
if (remote_delay_report_ns)
*remote_delay_report_ns = remoteDeviceAudioDelayNanos;
if (total_bytes_readed) *total_bytes_readed = transmittedOctets;
if (data_position)
*data_position =
timespec_convert_from_hal(transmittedOctetsTimeStamp);
retval = true;
}
});
if (!hal_retval.isOk()) {
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
<< toString(session_type_) << " failed";
return false;
}
return retval;
}
void BluetoothAudioSession::UpdateTracksMetadata(
const struct source_metadata* source_metadata) {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (!IsSessionReady()) {
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
<< " has NO session";
return;
}
ssize_t track_count = source_metadata->track_count;
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ", "
<< track_count << " track(s)";
if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
return;
}
struct playback_track_metadata* track = source_metadata->tracks;
SourceMetadata sourceMetadata;
PlaybackTrackMetadata* halMetadata;
sourceMetadata.tracks.resize(track_count);
halMetadata = sourceMetadata.tracks.data();
while (track_count && track) {
halMetadata->usage = static_cast<AudioUsage>(track->usage);
halMetadata->contentType =
static_cast<AudioContentType>(track->content_type);
halMetadata->gain = track->gain;
LOG(VERBOSE) << __func__ << " - SessionType=" << toString(session_type_)
<< ", usage=" << toString(halMetadata->usage)
<< ", content=" << toString(halMetadata->contentType)
<< ", gain=" << halMetadata->gain;
--track_count;
++track;
++halMetadata;
}
auto hal_retval = stack_iface_->updateMetadata(sourceMetadata);
if (!hal_retval.isOk()) {
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
<< toString(session_type_) << " failed";
}
}
// The control function writes stream to FMQ
size_t BluetoothAudioSession::OutWritePcmData(const void* buffer,
size_t bytes) {
if (buffer == nullptr || !bytes) return 0;
size_t totalWritten = 0;
int ms_timeout = kFmqSendTimeoutMs;
do {
std::unique_lock<std::recursive_mutex> lock(mutex_);
if (!IsSessionReady()) break;
size_t availableToWrite = mDataMQ->availableToWrite();
if (availableToWrite) {
if (availableToWrite > (bytes - totalWritten)) {
availableToWrite = bytes - totalWritten;
}
if (!mDataMQ->write(static_cast<const uint8_t*>(buffer) + totalWritten,
availableToWrite)) {
ALOGE("FMQ datapath writing %zu/%zu failed", totalWritten, bytes);
return totalWritten;
}
totalWritten += availableToWrite;
} else if (ms_timeout >= kWritePollMs) {
lock.unlock();
usleep(kWritePollMs * 1000);
ms_timeout -= kWritePollMs;
} else {
ALOGD("out data %zu/%zu overflow %d ms", totalWritten, bytes,
(kFmqSendTimeoutMs - ms_timeout));
return totalWritten;
}
} while (totalWritten < bytes);
return totalWritten;
}
// The control function reads stream from FMQ
size_t BluetoothAudioSession::InReadPcmData(void* buffer, size_t bytes) {
if (buffer == nullptr || !bytes) return 0;
size_t totalRead = 0;
int ms_timeout = kFmqReceiveTimeoutMs;
do {
std::unique_lock<std::recursive_mutex> lock(mutex_);
if (!IsSessionReady()) break;
size_t availableToRead = mDataMQ->availableToRead();
if (availableToRead) {
if (availableToRead > (bytes - totalRead)) {
availableToRead = bytes - totalRead;
}
if (!mDataMQ->read(static_cast<uint8_t*>(buffer) + totalRead,
availableToRead)) {
ALOGE("FMQ datapath reading %zu/%zu failed", totalRead, bytes);
return totalRead;
}
totalRead += availableToRead;
} else if (ms_timeout >= kReadPollMs) {
lock.unlock();
usleep(kReadPollMs * 1000);
ms_timeout -= kReadPollMs;
continue;
} else {
ALOGD("in data %zu/%zu overflow %d ms", totalRead, bytes,
(kFmqReceiveTimeoutMs - ms_timeout));
return totalRead;
}
} while (totalRead < bytes);
return totalRead;
}
std::unique_ptr<BluetoothAudioSessionInstance>
BluetoothAudioSessionInstance::instance_ptr =
std::unique_ptr<BluetoothAudioSessionInstance>(
new BluetoothAudioSessionInstance());
// API to fetch the session
std::shared_ptr<BluetoothAudioSession>
BluetoothAudioSessionInstance::GetSessionInstance(
const SessionType& session_type) {
std::lock_guard<std::mutex> guard(instance_ptr->mutex_);
if (!instance_ptr->sessions_map_.empty()) {
auto entry = instance_ptr->sessions_map_.find(session_type);
if (entry != instance_ptr->sessions_map_.end()) {
return entry->second;
}
}
std::shared_ptr<BluetoothAudioSession> session_ptr =
std::make_shared<BluetoothAudioSession>(session_type);
instance_ptr->sessions_map_[session_type] = session_ptr;
return session_ptr;
}
} // namespace audio
} // namespace bluetooth
} // namespace android

View File

@@ -0,0 +1,190 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <mutex>
#include <unordered_map>
#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
#include <android/hardware/bluetooth/audio/2.1/types.h>
#include <fmq/MessageQueue.h>
#include <hardware/audio.h>
#include <hidl/MQDescriptor.h>
namespace android {
namespace bluetooth {
namespace audio {
using ::android::sp;
using ::android::hardware::kSynchronizedReadWrite;
using ::android::hardware::MessageQueue;
using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
using ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration;
using ::android::hardware::bluetooth::audio::V2_1::PcmParameters;
using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
using ::android::hardware::bluetooth::audio::V2_1::SessionType;
using BluetoothAudioStatus =
::android::hardware::bluetooth::audio::V2_0::Status;
using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
static constexpr uint16_t kObserversCookieSize = 0x0010; // 0x0000 ~ 0x000f
constexpr uint16_t kObserversCookieUndefined =
(static_cast<uint16_t>(SessionType::UNKNOWN) << 8 & 0xff00);
inline SessionType ObserversCookieGetSessionType(uint16_t cookie) {
return static_cast<SessionType>(cookie >> 8 & 0x00ff);
}
inline uint16_t ObserversCookieGetInitValue(SessionType session_type) {
return (static_cast<uint16_t>(session_type) << 8 & 0xff00);
}
inline uint16_t ObserversCookieGetUpperBound(SessionType session_type) {
return (static_cast<uint16_t>(session_type) << 8 & 0xff00) +
kObserversCookieSize;
}
// This presents the callbacks of started / suspended and session changed,
// and the bluetooth_audio module uses to receive the status notification
struct PortStatusCallbacks {
// control_result_cb_ - when the Bluetooth stack reports results of
// streamStarted or streamSuspended, the BluetoothAudioProvider will invoke
// this callback to report to the bluetooth_audio module.
// @param: cookie - indicates which bluetooth_audio output should handle
// @param: start_resp - this report is for startStream or not
// @param: status - the result of startStream
std::function<void(uint16_t cookie, bool start_resp,
const BluetoothAudioStatus& status)>
control_result_cb_;
// session_changed_cb_ - when the Bluetooth stack start / end session, the
// BluetoothAudioProvider will invoke this callback to notify to the
// bluetooth_audio module.
// @param: cookie - indicates which bluetooth_audio output should handle
std::function<void(uint16_t cookie)> session_changed_cb_;
};
class BluetoothAudioSession {
private:
// using recursive_mutex to allow hwbinder to re-enter again.
std::recursive_mutex mutex_;
SessionType session_type_;
// audio control path to use for both software and offloading
sp<IBluetoothAudioPort> stack_iface_;
// Audio path (FMQ) for software encoding/decoded data
std::unique_ptr<DataMQ> mDataMQ;
// audio data configuration for both software and offloading
AudioConfiguration audio_config_;
static AudioConfiguration invalidSoftwareAudioConfiguration;
static AudioConfiguration invalidOffloadAudioConfiguration;
// saving those registered bluetooth_audio's callbacks
std::unordered_map<uint16_t, std::shared_ptr<struct PortStatusCallbacks>>
observers_;
bool UpdateDataPath(const DataMQ::Descriptor* dataMQ);
bool UpdateAudioConfig(const AudioConfiguration& audio_config);
// invoking the registered session_changed_cb_
void ReportSessionStatus();
public:
BluetoothAudioSession(const SessionType& session_type);
// The function helps to check if this session is ready or not
// @return: true if the Bluetooth stack has started the specified session
bool IsSessionReady();
// The report function is used to report that the Bluetooth stack has started
// this session without any failure, and will invoke session_changed_cb_ to
// notify those registered bluetooth_audio outputs
void OnSessionStarted(const sp<IBluetoothAudioPort> stack_iface,
const DataMQ::Descriptor* dataMQ,
const AudioConfiguration& audio_config);
// The report function is used to report that the Bluetooth stack has ended
// the session, and will invoke session_changed_cb_ to notify registered
// bluetooth_audio outputs
void OnSessionEnded();
// The report function is used to report that the Bluetooth stack has notified
// the result of startStream or suspendStream, and will invoke
// control_result_cb_ to notify registered bluetooth_audio outputs
void ReportControlStatus(bool start_resp, const BluetoothAudioStatus& status);
// The control function helps the bluetooth_audio module to register
// PortStatusCallbacks
// @return: cookie - the assigned number to this bluetooth_audio output
uint16_t RegisterStatusCback(const PortStatusCallbacks& cbacks);
// The control function helps the bluetooth_audio module to unregister
// PortStatusCallbacks
// @param: cookie - indicates which bluetooth_audio output is
void UnregisterStatusCback(uint16_t cookie);
// The control function is for the bluetooth_audio module to get the current
// AudioConfiguration
const AudioConfiguration& GetAudioConfig();
// Those control functions are for the bluetooth_audio module to start,
// suspend, stop stream, to check position, and to update metadata.
bool StartStream();
bool SuspendStream();
void StopStream();
bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
uint64_t* total_bytes_readed,
timespec* data_position);
void UpdateTracksMetadata(const struct source_metadata* source_metadata);
// The control function writes stream to FMQ
size_t OutWritePcmData(const void* buffer, size_t bytes);
// The control function read stream from FMQ
size_t InReadPcmData(void* buffer, size_t bytes);
static constexpr PcmParameters kInvalidPcmParameters = {
.sampleRate = SampleRate::RATE_UNKNOWN,
.channelMode = ChannelMode::UNKNOWN,
.bitsPerSample = BitsPerSample::BITS_UNKNOWN,
.dataIntervalUs = 0,
};
// can't be constexpr because of non-literal type
static const CodecConfiguration kInvalidCodecConfiguration;
static constexpr AudioConfiguration& kInvalidSoftwareAudioConfiguration =
invalidSoftwareAudioConfiguration;
static constexpr AudioConfiguration& kInvalidOffloadAudioConfiguration =
invalidOffloadAudioConfiguration;
};
class BluetoothAudioSessionInstance {
public:
// The API is to fetch the specified session
static std::shared_ptr<BluetoothAudioSession> GetSessionInstance(
const SessionType& session_type);
private:
static std::unique_ptr<BluetoothAudioSessionInstance> instance_ptr;
std::mutex mutex_;
std::unordered_map<SessionType, std::shared_ptr<BluetoothAudioSession>>
sessions_map_;
};
} // namespace audio
} // namespace bluetooth
} // namespace android

View File

@@ -0,0 +1,154 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "BluetoothAudioSession.h"
namespace android {
namespace bluetooth {
namespace audio {
class BluetoothAudioSessionControl {
public:
// The control API helps to check if session is ready or not
// @return: true if the Bluetooth stack has started th specified session
static bool IsSessionReady(const SessionType& session_type) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
return session_ptr->IsSessionReady();
}
return false;
}
// The control API helps the bluetooth_audio module to register
// PortStatusCallbacks
// @return: cookie - the assigned number to this bluetooth_audio output
static uint16_t RegisterControlResultCback(
const SessionType& session_type, const PortStatusCallbacks& cbacks) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
return session_ptr->RegisterStatusCback(cbacks);
}
return kObserversCookieUndefined;
}
// The control API helps the bluetooth_audio module to unregister
// PortStatusCallbacks
// @param: cookie - indicates which bluetooth_audio output is
static void UnregisterControlResultCback(const SessionType& session_type,
uint16_t cookie) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
session_ptr->UnregisterStatusCback(cookie);
}
}
// The control API for the bluetooth_audio module to get current
// AudioConfiguration
static const AudioConfiguration& GetAudioConfig(
const SessionType& session_type) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
return session_ptr->GetAudioConfig();
} else if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
return BluetoothAudioSession::kInvalidOffloadAudioConfiguration;
} else {
return BluetoothAudioSession::kInvalidSoftwareAudioConfiguration;
}
}
// Those control APIs for the bluetooth_audio module to start / suspend / stop
// stream, to check position, and to update metadata.
static bool StartStream(const SessionType& session_type) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
return session_ptr->StartStream();
}
return false;
}
static bool SuspendStream(const SessionType& session_type) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
return session_ptr->SuspendStream();
}
return false;
}
static void StopStream(const SessionType& session_type) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
session_ptr->StopStream();
}
}
static bool GetPresentationPosition(const SessionType& session_type,
uint64_t* remote_delay_report_ns,
uint64_t* total_bytes_readed,
timespec* data_position) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
return session_ptr->GetPresentationPosition(
remote_delay_report_ns, total_bytes_readed, data_position);
}
return false;
}
static void UpdateTracksMetadata(
const SessionType& session_type,
const struct source_metadata* source_metadata) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
session_ptr->UpdateTracksMetadata(source_metadata);
}
}
// The control API writes stream to FMQ
static size_t OutWritePcmData(const SessionType& session_type,
const void* buffer, size_t bytes) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
return session_ptr->OutWritePcmData(buffer, bytes);
}
return 0;
}
// The control API reads stream from FMQ
static size_t InReadPcmData(const SessionType& session_type, void* buffer,
size_t bytes) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
return session_ptr->InReadPcmData(buffer, bytes);
}
return 0;
}
};
} // namespace audio
} // namespace bluetooth
} // namespace android

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "BluetoothAudioSession.h"
namespace android {
namespace bluetooth {
namespace audio {
class BluetoothAudioSessionReport {
public:
// The API reports the Bluetooth stack has started the session, and will
// inform registered bluetooth_audio session
static void OnSessionStarted(const SessionType& session_type,
const sp<IBluetoothAudioPort> host_iface,
const DataMQ::Descriptor* dataMQ,
const AudioConfiguration& audio_config) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
session_ptr->OnSessionStarted(host_iface, dataMQ, audio_config);
}
}
// The API reports the Bluetooth stack has ended the session, and will
// inform registered bluetooth_audio outputs
static void OnSessionEnded(const SessionType& session_type) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
session_ptr->OnSessionEnded();
}
}
// The API reports the Bluetooth stack has replied the result of startStream
// or suspendStream, and will inform registered bluetooth_audio outputs
static void ReportControlStatus(const SessionType& session_type,
const bool& start_resp,
const BluetoothAudioStatus& status) {
std::shared_ptr<BluetoothAudioSession> session_ptr =
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
session_ptr->ReportControlStatus(start_resp, status);
}
}
};
} // namespace audio
} // namespace bluetooth
} // namespace android

View File

@@ -0,0 +1,487 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "BTAudioProviderSessionCodecsDB"
#include "BluetoothAudioSupportedCodecsDB.h"
#include <android-base/logging.h>
namespace android {
namespace bluetooth {
namespace audio {
using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
using ::android::hardware::bluetooth::audio::V2_0::CodecType;
using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
// Default Supported PCM Parameters
static const ::android::hardware::bluetooth::audio::V2_0::PcmParameters
kDefaultSoftwarePcmCapabilities = {
.sampleRate = static_cast<
android::hardware::bluetooth::audio::V2_0::SampleRate>(
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 |
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000 |
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_88200 |
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_96000 |
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_16000),
.channelMode =
static_cast<ChannelMode>(ChannelMode::MONO | ChannelMode::STEREO),
.bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
BitsPerSample::BITS_24 |
BitsPerSample::BITS_32)};
static const PcmParameters kDefaultSoftwarePcmCapabilities_2_1 = {
.sampleRate = static_cast<SampleRate>(
SampleRate::RATE_48000 | SampleRate::RATE_44100 |
SampleRate::RATE_32000 | SampleRate::RATE_24000 |
SampleRate::RATE_16000 | SampleRate::RATE_8000),
.channelMode =
static_cast<ChannelMode>(ChannelMode::MONO | ChannelMode::STEREO),
.bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16)};
// Default Supported Codecs
// SBC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(MONO|STEREO)
// all blocks | subbands 8 | Loudness
static const SbcParameters kDefaultOffloadSbcCapability = {
.sampleRate =
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100,
.channelMode = static_cast<SbcChannelMode>(SbcChannelMode::MONO |
SbcChannelMode::JOINT_STEREO),
.blockLength = static_cast<SbcBlockLength>(
SbcBlockLength::BLOCKS_4 | SbcBlockLength::BLOCKS_8 |
SbcBlockLength::BLOCKS_12 | SbcBlockLength::BLOCKS_16),
.numSubbands = SbcNumSubbands::SUBBAND_8,
.allocMethod = SbcAllocMethod::ALLOC_MD_L,
.bitsPerSample = BitsPerSample::BITS_16,
.minBitpool = 2,
.maxBitpool = 53};
// AAC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(STEREO)
static const AacParameters kDefaultOffloadAacCapability = {
.objectType = AacObjectType::MPEG2_LC,
.sampleRate =
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100,
.channelMode = ChannelMode::STEREO,
.variableBitRateEnabled = AacVariableBitRate::ENABLED,
.bitsPerSample = BitsPerSample::BITS_16};
// LDAC: mSampleRate:(44100|48000|88200|96000), mBitsPerSample:(16|24|32),
// mChannelMode:(DUAL|STEREO)
static const LdacParameters kDefaultOffloadLdacCapability = {
.sampleRate =
static_cast<android::hardware::bluetooth::audio::V2_0::SampleRate>(
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 |
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000 |
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_88200 |
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_96000),
.channelMode = static_cast<LdacChannelMode>(LdacChannelMode::DUAL |
LdacChannelMode::STEREO),
.qualityIndex = LdacQualityIndex::QUALITY_HIGH,
.bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
BitsPerSample::BITS_24 |
BitsPerSample::BITS_32)};
// aptX: mSampleRate:(44100|48000), mBitsPerSample:(16), mChannelMode:(STEREO)
static const AptxParameters kDefaultOffloadAptxCapability = {
.sampleRate =
static_cast<android::hardware::bluetooth::audio::V2_0::SampleRate>(
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 |
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000),
.channelMode = ChannelMode::STEREO,
.bitsPerSample = BitsPerSample::BITS_16,
};
// aptX HD: mSampleRate:(44100|48000), mBitsPerSample:(24),
// mChannelMode:(STEREO)
static const AptxParameters kDefaultOffloadAptxHdCapability = {
.sampleRate =
static_cast<android::hardware::bluetooth::audio::V2_0::SampleRate>(
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 |
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000),
.channelMode = ChannelMode::STEREO,
.bitsPerSample = BitsPerSample::BITS_24,
};
const std::vector<CodecCapabilities> kDefaultOffloadA2dpCodecCapabilities = {
{.codecType = CodecType::SBC, .capabilities = {}},
{.codecType = CodecType::AAC, .capabilities = {}},
{.codecType = CodecType::LDAC, .capabilities = {}},
{.codecType = CodecType::APTX, .capabilities = {}},
{.codecType = CodecType::APTX_HD, .capabilities = {}}};
static bool IsSingleBit(uint32_t bitmasks, uint32_t bitfield) {
bool single = false;
uint32_t test_bit = 0x00000001;
while (test_bit <= bitmasks && test_bit <= bitfield) {
if (bitfield & test_bit && bitmasks & test_bit) {
if (single) return false;
single = true;
}
if (test_bit == 0x80000000) break;
test_bit <<= 1;
}
return single;
}
static bool IsOffloadSbcConfigurationValid(
const CodecConfiguration::CodecSpecific& codec_specific);
static bool IsOffloadAacConfigurationValid(
const CodecConfiguration::CodecSpecific& codec_specific);
static bool IsOffloadLdacConfigurationValid(
const CodecConfiguration::CodecSpecific& codec_specific);
static bool IsOffloadAptxConfigurationValid(
const CodecConfiguration::CodecSpecific& codec_specific);
static bool IsOffloadAptxHdConfigurationValid(
const CodecConfiguration::CodecSpecific& codec_specific);
static bool IsOffloadSbcConfigurationValid(
const CodecConfiguration::CodecSpecific& codec_specific) {
if (codec_specific.getDiscriminator() !=
CodecConfiguration::CodecSpecific::hidl_discriminator::sbcConfig) {
LOG(WARNING) << __func__
<< ": Invalid CodecSpecific=" << toString(codec_specific);
return false;
}
const SbcParameters sbc_data = codec_specific.sbcConfig();
if (!IsSingleBit(static_cast<uint32_t>(sbc_data.sampleRate), 0xff) ||
!IsSingleBit(static_cast<uint32_t>(sbc_data.channelMode), 0x0f) ||
!IsSingleBit(static_cast<uint32_t>(sbc_data.blockLength), 0xf0) ||
!IsSingleBit(static_cast<uint32_t>(sbc_data.numSubbands), 0x0c) ||
!IsSingleBit(static_cast<uint32_t>(sbc_data.allocMethod), 0x03) ||
!IsSingleBit(static_cast<uint32_t>(sbc_data.bitsPerSample), 0x07) ||
sbc_data.minBitpool > sbc_data.maxBitpool) {
LOG(WARNING) << __func__
<< ": Invalid CodecSpecific=" << toString(codec_specific);
return false;
} else if ((sbc_data.sampleRate & kDefaultOffloadSbcCapability.sampleRate) &&
(sbc_data.channelMode &
kDefaultOffloadSbcCapability.channelMode) &&
(sbc_data.blockLength &
kDefaultOffloadSbcCapability.blockLength) &&
(sbc_data.numSubbands &
kDefaultOffloadSbcCapability.numSubbands) &&
(sbc_data.allocMethod &
kDefaultOffloadSbcCapability.allocMethod) &&
(sbc_data.bitsPerSample &
kDefaultOffloadSbcCapability.bitsPerSample) &&
(kDefaultOffloadSbcCapability.minBitpool <= sbc_data.minBitpool &&
sbc_data.maxBitpool <= kDefaultOffloadSbcCapability.maxBitpool)) {
return true;
}
LOG(WARNING) << __func__
<< ": Unsupported CodecSpecific=" << toString(codec_specific);
return false;
}
static bool IsOffloadAacConfigurationValid(
const CodecConfiguration::CodecSpecific& codec_specific) {
if (codec_specific.getDiscriminator() !=
CodecConfiguration::CodecSpecific::hidl_discriminator::aacConfig) {
LOG(WARNING) << __func__
<< ": Invalid CodecSpecific=" << toString(codec_specific);
return false;
}
const AacParameters aac_data = codec_specific.aacConfig();
if (!IsSingleBit(static_cast<uint32_t>(aac_data.objectType), 0xf0) ||
!IsSingleBit(static_cast<uint32_t>(aac_data.sampleRate), 0xff) ||
!IsSingleBit(static_cast<uint32_t>(aac_data.channelMode), 0x03) ||
!IsSingleBit(static_cast<uint32_t>(aac_data.bitsPerSample), 0x07)) {
LOG(WARNING) << __func__
<< ": Invalid CodecSpecific=" << toString(codec_specific);
return false;
} else if ((aac_data.objectType & kDefaultOffloadAacCapability.objectType) &&
(aac_data.sampleRate & kDefaultOffloadAacCapability.sampleRate) &&
(aac_data.channelMode &
kDefaultOffloadAacCapability.channelMode) &&
(aac_data.variableBitRateEnabled == AacVariableBitRate::DISABLED ||
kDefaultOffloadAacCapability.variableBitRateEnabled ==
AacVariableBitRate::ENABLED) &&
(aac_data.bitsPerSample &
kDefaultOffloadAacCapability.bitsPerSample)) {
return true;
}
LOG(WARNING) << __func__
<< ": Unsupported CodecSpecific=" << toString(codec_specific);
return false;
}
static bool IsOffloadLdacConfigurationValid(
const CodecConfiguration::CodecSpecific& codec_specific) {
if (codec_specific.getDiscriminator() !=
CodecConfiguration::CodecSpecific::hidl_discriminator::ldacConfig) {
LOG(WARNING) << __func__
<< ": Invalid CodecSpecific=" << toString(codec_specific);
return false;
}
const LdacParameters ldac_data = codec_specific.ldacConfig();
if (!IsSingleBit(static_cast<uint32_t>(ldac_data.sampleRate), 0xff) ||
!IsSingleBit(static_cast<uint32_t>(ldac_data.channelMode), 0x07) ||
(ldac_data.qualityIndex > LdacQualityIndex::QUALITY_LOW &&
ldac_data.qualityIndex != LdacQualityIndex::QUALITY_ABR) ||
!IsSingleBit(static_cast<uint32_t>(ldac_data.bitsPerSample), 0x07)) {
LOG(WARNING) << __func__
<< ": Invalid CodecSpecific=" << toString(codec_specific);
return false;
} else if ((ldac_data.sampleRate &
kDefaultOffloadLdacCapability.sampleRate) &&
(ldac_data.channelMode &
kDefaultOffloadLdacCapability.channelMode) &&
(ldac_data.bitsPerSample &
kDefaultOffloadLdacCapability.bitsPerSample)) {
return true;
}
LOG(WARNING) << __func__
<< ": Unsupported CodecSpecific=" << toString(codec_specific);
return false;
}
static bool IsOffloadAptxConfigurationValid(
const CodecConfiguration::CodecSpecific& codec_specific) {
if (codec_specific.getDiscriminator() !=
CodecConfiguration::CodecSpecific::hidl_discriminator::aptxConfig) {
LOG(WARNING) << __func__
<< ": Invalid CodecSpecific=" << toString(codec_specific);
return false;
}
const AptxParameters aptx_data = codec_specific.aptxConfig();
if (!IsSingleBit(static_cast<uint32_t>(aptx_data.sampleRate), 0xff) ||
!IsSingleBit(static_cast<uint32_t>(aptx_data.channelMode), 0x03) ||
!IsSingleBit(static_cast<uint32_t>(aptx_data.bitsPerSample), 0x07)) {
LOG(WARNING) << __func__
<< ": Invalid CodecSpecific=" << toString(codec_specific);
return false;
} else if ((aptx_data.sampleRate &
kDefaultOffloadAptxCapability.sampleRate) &&
(aptx_data.channelMode &
kDefaultOffloadAptxCapability.channelMode) &&
(aptx_data.bitsPerSample &
kDefaultOffloadAptxCapability.bitsPerSample)) {
return true;
}
LOG(WARNING) << __func__
<< ": Unsupported CodecSpecific=" << toString(codec_specific);
return false;
}
static bool IsOffloadAptxHdConfigurationValid(
const CodecConfiguration::CodecSpecific& codec_specific) {
if (codec_specific.getDiscriminator() !=
CodecConfiguration::CodecSpecific::hidl_discriminator::aptxConfig) {
LOG(WARNING) << __func__
<< ": Invalid CodecSpecific=" << toString(codec_specific);
return false;
}
const AptxParameters aptx_data = codec_specific.aptxConfig();
if (!IsSingleBit(static_cast<uint32_t>(aptx_data.sampleRate), 0xff) ||
!IsSingleBit(static_cast<uint32_t>(aptx_data.channelMode), 0x03) ||
!IsSingleBit(static_cast<uint32_t>(aptx_data.bitsPerSample), 0x07)) {
LOG(WARNING) << __func__
<< ": Invalid CodecSpecific=" << toString(codec_specific);
return false;
} else if ((aptx_data.sampleRate &
kDefaultOffloadAptxHdCapability.sampleRate) &&
(aptx_data.channelMode &
kDefaultOffloadAptxHdCapability.channelMode) &&
(aptx_data.bitsPerSample &
kDefaultOffloadAptxHdCapability.bitsPerSample)) {
return true;
}
LOG(WARNING) << __func__
<< ": Unsupported CodecSpecific=" << toString(codec_specific);
return false;
}
std::vector<::android::hardware::bluetooth::audio::V2_0::PcmParameters>
GetSoftwarePcmCapabilities() {
return std::vector<
::android::hardware::bluetooth::audio::V2_0::PcmParameters>(
1, kDefaultSoftwarePcmCapabilities);
}
std::vector<PcmParameters> GetSoftwarePcmCapabilities_2_1() {
return std::vector<PcmParameters>(1, kDefaultSoftwarePcmCapabilities_2_1);
}
std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
const ::android::hardware::bluetooth::audio::V2_0::SessionType&
session_type) {
return GetOffloadCodecCapabilities(static_cast<SessionType>(session_type));
}
std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
const SessionType& session_type) {
if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
return std::vector<CodecCapabilities>(0);
}
std::vector<CodecCapabilities> offload_a2dp_codec_capabilities =
kDefaultOffloadA2dpCodecCapabilities;
for (auto& codec_capability : offload_a2dp_codec_capabilities) {
switch (codec_capability.codecType) {
case CodecType::SBC:
codec_capability.capabilities.sbcCapabilities(
kDefaultOffloadSbcCapability);
break;
case CodecType::AAC:
codec_capability.capabilities.aacCapabilities(
kDefaultOffloadAacCapability);
break;
case CodecType::LDAC:
codec_capability.capabilities.ldacCapabilities(
kDefaultOffloadLdacCapability);
break;
case CodecType::APTX:
codec_capability.capabilities.aptxCapabilities(
kDefaultOffloadAptxCapability);
break;
case CodecType::APTX_HD:
codec_capability.capabilities.aptxCapabilities(
kDefaultOffloadAptxHdCapability);
break;
case CodecType::UNKNOWN:
codec_capability = {};
break;
}
}
return offload_a2dp_codec_capabilities;
}
bool IsSoftwarePcmConfigurationValid(
const ::android::hardware::bluetooth::audio::V2_0::PcmParameters&
pcm_config) {
if ((pcm_config.sampleRate !=
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100 &&
pcm_config.sampleRate !=
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000 &&
pcm_config.sampleRate !=
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_88200 &&
pcm_config.sampleRate !=
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_96000 &&
pcm_config.sampleRate !=
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_16000 &&
pcm_config.sampleRate !=
android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_24000) ||
(pcm_config.bitsPerSample != BitsPerSample::BITS_16 &&
pcm_config.bitsPerSample != BitsPerSample::BITS_24 &&
pcm_config.bitsPerSample != BitsPerSample::BITS_32) ||
(pcm_config.channelMode != ChannelMode::MONO &&
pcm_config.channelMode != ChannelMode::STEREO)) {
LOG(WARNING) << __func__
<< ": Invalid PCM Configuration=" << toString(pcm_config);
return false;
} else if (pcm_config.sampleRate &
kDefaultSoftwarePcmCapabilities.sampleRate &&
pcm_config.bitsPerSample &
kDefaultSoftwarePcmCapabilities.bitsPerSample &&
pcm_config.channelMode &
kDefaultSoftwarePcmCapabilities.channelMode) {
return true;
}
LOG(WARNING) << __func__
<< ": Unsupported PCM Configuration=" << toString(pcm_config);
return false;
}
bool IsSoftwarePcmConfigurationValid_2_1(const PcmParameters& pcm_config) {
if ((pcm_config.sampleRate != SampleRate::RATE_44100 &&
pcm_config.sampleRate != SampleRate::RATE_48000 &&
pcm_config.sampleRate != SampleRate::RATE_88200 &&
pcm_config.sampleRate != SampleRate::RATE_96000 &&
pcm_config.sampleRate != SampleRate::RATE_16000 &&
pcm_config.sampleRate != SampleRate::RATE_24000) ||
(pcm_config.bitsPerSample != BitsPerSample::BITS_16 &&
pcm_config.bitsPerSample != BitsPerSample::BITS_24 &&
pcm_config.bitsPerSample != BitsPerSample::BITS_32) ||
(pcm_config.channelMode != ChannelMode::MONO &&
pcm_config.channelMode != ChannelMode::STEREO)) {
LOG(WARNING) << __func__
<< ": Invalid PCM Configuration=" << toString(pcm_config);
return false;
} else if (pcm_config.sampleRate &
kDefaultSoftwarePcmCapabilities_2_1.sampleRate &&
pcm_config.bitsPerSample &
kDefaultSoftwarePcmCapabilities_2_1.bitsPerSample &&
pcm_config.channelMode &
kDefaultSoftwarePcmCapabilities_2_1.channelMode &&
pcm_config.dataIntervalUs != 0) {
return true;
}
LOG(WARNING) << __func__
<< ": Unsupported PCM Configuration=" << toString(pcm_config);
return false;
}
bool IsOffloadCodecConfigurationValid(const SessionType& session_type,
const CodecConfiguration& codec_config) {
if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
LOG(ERROR) << __func__
<< ": Invalid SessionType=" << toString(session_type);
return false;
} else if (codec_config.encodedAudioBitrate < 0x00000001 ||
0x00ffffff < codec_config.encodedAudioBitrate) {
LOG(ERROR) << __func__ << ": Unsupported Codec Configuration="
<< toString(codec_config);
return false;
}
const CodecConfiguration::CodecSpecific& codec_specific = codec_config.config;
switch (codec_config.codecType) {
case CodecType::SBC:
if (IsOffloadSbcConfigurationValid(codec_specific)) {
return true;
}
return false;
case CodecType::AAC:
if (IsOffloadAacConfigurationValid(codec_specific)) {
return true;
}
return false;
case CodecType::LDAC:
if (IsOffloadLdacConfigurationValid(codec_specific)) {
return true;
}
return false;
case CodecType::APTX:
if (IsOffloadAptxConfigurationValid(codec_specific)) {
return true;
}
return false;
case CodecType::APTX_HD:
if (IsOffloadAptxHdConfigurationValid(codec_specific)) {
return true;
}
return false;
case CodecType::UNKNOWN:
return false;
}
return false;
}
} // namespace audio
} // namespace bluetooth
} // namespace android

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <android/hardware/bluetooth/audio/2.0/types.h>
#include <android/hardware/bluetooth/audio/2.1/types.h>
namespace android {
namespace bluetooth {
namespace audio {
using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
using ::android::hardware::bluetooth::audio::V2_1::PcmParameters;
using ::android::hardware::bluetooth::audio::V2_1::SessionType;
std::vector<::android::hardware::bluetooth::audio::V2_0::PcmParameters>
GetSoftwarePcmCapabilities();
std::vector<PcmParameters> GetSoftwarePcmCapabilities_2_1();
std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
const SessionType& session_type);
std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
const ::android::hardware::bluetooth::audio::V2_0::SessionType&
session_type);
bool IsSoftwarePcmConfigurationValid_2_1(const PcmParameters& pcm_config);
bool IsSoftwarePcmConfigurationValid(
const ::android::hardware::bluetooth::audio::V2_0::PcmParameters&
pcm_config);
bool IsOffloadCodecConfigurationValid(const SessionType& session_type,
const CodecConfiguration& codec_config);
} // namespace audio
} // namespace bluetooth
} // namespace android

View File

@@ -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;
};

View File

@@ -0,0 +1,3 @@
include platform/system/bt:/OWNERS
cheneyni@google.com

View File

@@ -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"],
}

File diff suppressed because it is too large Load Diff

View File

@@ -110,7 +110,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.bluetooth.audio</name>
<version>2.0</version>
<version>2.0-1</version>
<interface>
<name>IBluetoothAudioProvidersFactory</name>
<instance>default</instance>