One libbluetooth_audio_session
libbluetooth_audio_session must be version agnostic, and support both 2.0 and 2.1 bluetooth.audio vendor libraries Test: compilation Bug: 150670922 Change-Id: I6a4a10b37201da9a5fb289262530f9d9cd5041ac
This commit is contained in:
parent
964f323023
commit
3c8dc61462
20 changed files with 619 additions and 1461 deletions
|
@ -22,8 +22,8 @@
|
||||||
#include <fmq/MessageQueue.h>
|
#include <fmq/MessageQueue.h>
|
||||||
#include <hidl/MQDescriptor.h>
|
#include <hidl/MQDescriptor.h>
|
||||||
|
|
||||||
#include "BluetoothAudioSessionReport.h"
|
#include "BluetoothAudioSessionReport_2_1.h"
|
||||||
#include "BluetoothAudioSupportedCodecsDB.h"
|
#include "BluetoothAudioSupportedCodecsDB_2_1.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace hardware {
|
namespace hardware {
|
||||||
|
@ -32,7 +32,7 @@ namespace audio {
|
||||||
namespace V2_1 {
|
namespace V2_1 {
|
||||||
namespace implementation {
|
namespace implementation {
|
||||||
|
|
||||||
using ::android::bluetooth::audio::BluetoothAudioSessionReport;
|
using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
|
||||||
using ::android::hardware::kSynchronizedReadWrite;
|
using ::android::hardware::kSynchronizedReadWrite;
|
||||||
using ::android::hardware::MessageQueue;
|
using ::android::hardware::MessageQueue;
|
||||||
using ::android::hardware::Void;
|
using ::android::hardware::Void;
|
||||||
|
@ -81,8 +81,8 @@ Return<void> A2dpOffloadAudioProvider::startSession(
|
||||||
|
|
||||||
Return<void> A2dpOffloadAudioProvider::onSessionReady(
|
Return<void> A2dpOffloadAudioProvider::onSessionReady(
|
||||||
startSession_cb _hidl_cb) {
|
startSession_cb _hidl_cb) {
|
||||||
BluetoothAudioSessionReport::OnSessionStarted(session_type_, stack_iface_,
|
BluetoothAudioSessionReport_2_1::OnSessionStarted(session_type_, stack_iface_,
|
||||||
nullptr, audio_config_);
|
nullptr, audio_config_);
|
||||||
_hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor());
|
_hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor());
|
||||||
return Void();
|
return Void();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
|
||||||
#include "BluetoothAudioSessionReport.h"
|
#include "BluetoothAudioSessionReport_2_1.h"
|
||||||
#include "BluetoothAudioSupportedCodecsDB.h"
|
#include "BluetoothAudioSupportedCodecsDB_2_1.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace hardware {
|
namespace hardware {
|
||||||
|
@ -30,7 +30,7 @@ namespace audio {
|
||||||
namespace V2_1 {
|
namespace V2_1 {
|
||||||
namespace implementation {
|
namespace implementation {
|
||||||
|
|
||||||
using ::android::bluetooth::audio::BluetoothAudioSessionReport;
|
using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
|
||||||
using ::android::hardware::Void;
|
using ::android::hardware::Void;
|
||||||
using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
|
using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ Return<void> A2dpSoftwareAudioProvider::startSession(
|
||||||
Return<void> A2dpSoftwareAudioProvider::onSessionReady(
|
Return<void> A2dpSoftwareAudioProvider::onSessionReady(
|
||||||
startSession_cb _hidl_cb) {
|
startSession_cb _hidl_cb) {
|
||||||
if (mDataMQ && mDataMQ->isValid()) {
|
if (mDataMQ && mDataMQ->isValid()) {
|
||||||
BluetoothAudioSessionReport::OnSessionStarted(
|
BluetoothAudioSessionReport_2_1::OnSessionStarted(
|
||||||
session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
|
session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
|
||||||
_hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
|
_hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,29 +16,7 @@ cc_library_shared {
|
||||||
"android.hardware.bluetooth.audio@2.0",
|
"android.hardware.bluetooth.audio@2.0",
|
||||||
"android.hardware.bluetooth.audio@2.1",
|
"android.hardware.bluetooth.audio@2.1",
|
||||||
"libbase",
|
"libbase",
|
||||||
"libbluetooth_audio_session_2_1",
|
"libbluetooth_audio_session",
|
||||||
"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",
|
"libcutils",
|
||||||
"libfmq",
|
"libfmq",
|
||||||
"libhidlbase",
|
"libhidlbase",
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
|
||||||
#include "BluetoothAudioSessionReport.h"
|
#include "BluetoothAudioSessionReport_2_1.h"
|
||||||
#include "BluetoothAudioSupportedCodecsDB.h"
|
#include "BluetoothAudioSupportedCodecsDB_2_1.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace hardware {
|
namespace hardware {
|
||||||
|
@ -30,7 +30,7 @@ namespace audio {
|
||||||
namespace V2_1 {
|
namespace V2_1 {
|
||||||
namespace implementation {
|
namespace implementation {
|
||||||
|
|
||||||
using ::android::bluetooth::audio::BluetoothAudioSessionReport;
|
using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
|
||||||
using ::android::hardware::kSynchronizedReadWrite;
|
using ::android::hardware::kSynchronizedReadWrite;
|
||||||
using ::android::hardware::MessageQueue;
|
using ::android::hardware::MessageQueue;
|
||||||
using ::android::hardware::Void;
|
using ::android::hardware::Void;
|
||||||
|
@ -105,8 +105,8 @@ Return<void> BluetoothAudioProvider::streamStarted(
|
||||||
* HAL server should start the streaming on data path.
|
* HAL server should start the streaming on data path.
|
||||||
*/
|
*/
|
||||||
if (stack_iface_) {
|
if (stack_iface_) {
|
||||||
BluetoothAudioSessionReport::ReportControlStatus(session_type_, true,
|
BluetoothAudioSessionReport_2_1::ReportControlStatus(session_type_, true,
|
||||||
status);
|
status);
|
||||||
} else {
|
} else {
|
||||||
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
||||||
<< ", status=" << toString(status) << " has NO session";
|
<< ", status=" << toString(status) << " has NO session";
|
||||||
|
@ -125,8 +125,8 @@ Return<void> BluetoothAudioProvider::streamSuspended(
|
||||||
* HAL server should suspend the streaming on data path.
|
* HAL server should suspend the streaming on data path.
|
||||||
*/
|
*/
|
||||||
if (stack_iface_) {
|
if (stack_iface_) {
|
||||||
BluetoothAudioSessionReport::ReportControlStatus(session_type_, false,
|
BluetoothAudioSessionReport_2_1::ReportControlStatus(session_type_, false,
|
||||||
status);
|
status);
|
||||||
} else {
|
} else {
|
||||||
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
||||||
<< ", status=" << toString(status) << " has NO session";
|
<< ", status=" << toString(status) << " has NO session";
|
||||||
|
@ -139,7 +139,7 @@ Return<void> BluetoothAudioProvider::endSession() {
|
||||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
|
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
|
||||||
|
|
||||||
if (stack_iface_) {
|
if (stack_iface_) {
|
||||||
BluetoothAudioSessionReport::OnSessionEnded(session_type_);
|
BluetoothAudioSessionReport_2_1::OnSessionEnded(session_type_);
|
||||||
stack_iface_->unlinkToDeath(death_recipient_);
|
stack_iface_->unlinkToDeath(death_recipient_);
|
||||||
} else {
|
} else {
|
||||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
|
||||||
#include "BluetoothAudioSupportedCodecsDB.h"
|
#include "BluetoothAudioSupportedCodecsDB_2_1.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace hardware {
|
namespace hardware {
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
|
||||||
#include "BluetoothAudioSessionReport.h"
|
#include "BluetoothAudioSessionReport_2_1.h"
|
||||||
#include "BluetoothAudioSupportedCodecsDB.h"
|
#include "BluetoothAudioSupportedCodecsDB_2_1.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace hardware {
|
namespace hardware {
|
||||||
|
@ -30,7 +30,7 @@ namespace audio {
|
||||||
namespace V2_1 {
|
namespace V2_1 {
|
||||||
namespace implementation {
|
namespace implementation {
|
||||||
|
|
||||||
using ::android::bluetooth::audio::BluetoothAudioSessionReport;
|
using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
|
||||||
using ::android::hardware::Void;
|
using ::android::hardware::Void;
|
||||||
using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
|
using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ Return<void> HearingAidAudioProvider::startSession(
|
||||||
|
|
||||||
Return<void> HearingAidAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
|
Return<void> HearingAidAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
|
||||||
if (mDataMQ && mDataMQ->isValid()) {
|
if (mDataMQ && mDataMQ->isValid()) {
|
||||||
BluetoothAudioSessionReport::OnSessionStarted(
|
BluetoothAudioSessionReport_2_1::OnSessionStarted(
|
||||||
session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
|
session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
|
||||||
_hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
|
_hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
|
||||||
#include "BluetoothAudioSessionReport.h"
|
#include "BluetoothAudioSessionReport_2_1.h"
|
||||||
#include "BluetoothAudioSupportedCodecsDB.h"
|
#include "BluetoothAudioSupportedCodecsDB_2_1.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace hardware {
|
namespace hardware {
|
||||||
|
@ -31,7 +31,7 @@ namespace audio {
|
||||||
namespace V2_1 {
|
namespace V2_1 {
|
||||||
namespace implementation {
|
namespace implementation {
|
||||||
|
|
||||||
using ::android::bluetooth::audio::BluetoothAudioSessionReport;
|
using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
|
||||||
using ::android::hardware::Void;
|
using ::android::hardware::Void;
|
||||||
using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
|
using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
|
||||||
using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
|
using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
|
||||||
|
@ -179,7 +179,7 @@ Return<void> LeAudioAudioProvider::startSession_2_1(
|
||||||
|
|
||||||
Return<void> LeAudioAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
|
Return<void> LeAudioAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
|
||||||
if (mDataMQ && mDataMQ->isValid()) {
|
if (mDataMQ && mDataMQ->isValid()) {
|
||||||
BluetoothAudioSessionReport::OnSessionStarted(
|
BluetoothAudioSessionReport_2_1::OnSessionStarted(
|
||||||
session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
|
session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
|
||||||
_hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
|
_hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,467 +0,0 @@
|
||||||
/*
|
|
||||||
* 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
|
|
|
@ -1,190 +0,0 @@
|
||||||
/*
|
|
||||||
* 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
|
|
|
@ -1,154 +0,0 @@
|
||||||
/*
|
|
||||||
* 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
|
|
|
@ -1,63 +0,0 @@
|
||||||
/*
|
|
||||||
* 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
|
|
|
@ -1,489 +0,0 @@
|
||||||
/*
|
|
||||||
* 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_96000 &&
|
|
||||||
pcm_config.sampleRate != SampleRate::RATE_88200 &&
|
|
||||||
pcm_config.sampleRate != SampleRate::RATE_48000 &&
|
|
||||||
pcm_config.sampleRate != SampleRate::RATE_44100 &&
|
|
||||||
pcm_config.sampleRate != SampleRate::RATE_32000 &&
|
|
||||||
pcm_config.sampleRate != SampleRate::RATE_24000 &&
|
|
||||||
pcm_config.sampleRate != SampleRate::RATE_16000 &&
|
|
||||||
pcm_config.sampleRate != SampleRate::RATE_8000) ||
|
|
||||||
(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
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* 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
|
|
|
@ -4,12 +4,15 @@ cc_library_shared {
|
||||||
vendor: true,
|
vendor: true,
|
||||||
srcs: [
|
srcs: [
|
||||||
"session/BluetoothAudioSession.cpp",
|
"session/BluetoothAudioSession.cpp",
|
||||||
|
"session/BluetoothAudioSession_2_1.cpp",
|
||||||
"session/BluetoothAudioSupportedCodecsDB.cpp",
|
"session/BluetoothAudioSupportedCodecsDB.cpp",
|
||||||
|
"session/BluetoothAudioSupportedCodecsDB_2_1.cpp",
|
||||||
],
|
],
|
||||||
export_include_dirs: ["session/"],
|
export_include_dirs: ["session/"],
|
||||||
header_libs: ["libhardware_headers"],
|
header_libs: ["libhardware_headers"],
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
"android.hardware.bluetooth.audio@2.0",
|
"android.hardware.bluetooth.audio@2.0",
|
||||||
|
"android.hardware.bluetooth.audio@2.1",
|
||||||
"libbase",
|
"libbase",
|
||||||
"libcutils",
|
"libcutils",
|
||||||
"libfmq",
|
"libfmq",
|
||||||
|
|
148
bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_1.h
Normal file
148
bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_1.h
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BluetoothAudioSession_2_1.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace bluetooth {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
|
class BluetoothAudioSessionControl_2_1 {
|
||||||
|
using SessionType_2_1 =
|
||||||
|
::android::hardware::bluetooth::audio::V2_1::SessionType;
|
||||||
|
|
||||||
|
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_2_1& session_type) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
return session_ptr->GetAudioSession()->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_2_1& session_type, const PortStatusCallbacks& cbacks) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
return session_ptr->GetAudioSession()->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_2_1& session_type,
|
||||||
|
uint16_t cookie) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
session_ptr->GetAudioSession()->UnregisterStatusCback(cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The control API for the bluetooth_audio module to get current
|
||||||
|
// AudioConfiguration
|
||||||
|
static const AudioConfiguration& GetAudioConfig(
|
||||||
|
const SessionType_2_1& session_type) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
// TODO: map 2.1 to 2.0 audio config inside GetAudioConfig?
|
||||||
|
return session_ptr->GetAudioSession()->GetAudioConfig();
|
||||||
|
} else if (session_type ==
|
||||||
|
SessionType_2_1::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_2_1& session_type) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
return session_ptr->GetAudioSession()->StartStream();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool SuspendStream(const SessionType_2_1& session_type) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
return session_ptr->GetAudioSession()->SuspendStream();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void StopStream(const SessionType_2_1& session_type) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
session_ptr->GetAudioSession()->StopStream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GetPresentationPosition(const SessionType_2_1& session_type,
|
||||||
|
uint64_t* remote_delay_report_ns,
|
||||||
|
uint64_t* total_bytes_readed,
|
||||||
|
timespec* data_position) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
return session_ptr->GetAudioSession()->GetPresentationPosition(
|
||||||
|
remote_delay_report_ns, total_bytes_readed, data_position);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UpdateTracksMetadata(
|
||||||
|
const SessionType_2_1& session_type,
|
||||||
|
const struct source_metadata* source_metadata) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
session_ptr->GetAudioSession()->UpdateTracksMetadata(source_metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The control API writes stream to FMQ
|
||||||
|
static size_t OutWritePcmData(const SessionType_2_1& session_type,
|
||||||
|
const void* buffer, size_t bytes) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
return session_ptr->GetAudioSession()->OutWritePcmData(buffer, bytes);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace bluetooth
|
||||||
|
} // namespace android
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BluetoothAudioSession_2_1.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace bluetooth {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
|
class BluetoothAudioSessionReport_2_1 {
|
||||||
|
public:
|
||||||
|
// The API reports the Bluetooth stack has started the session, and will
|
||||||
|
// inform registered bluetooth_audio outputs
|
||||||
|
static void OnSessionStarted(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||||
|
session_type,
|
||||||
|
const sp<IBluetoothAudioPort> host_iface,
|
||||||
|
const DataMQ::Descriptor* dataMQ,
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||||
|
audio_config) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
session_ptr->OnSessionStarted(host_iface, dataMQ, audio_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The API reports the Bluetooth stack has ended the session, and will
|
||||||
|
// inform registered bluetooth_audio outputs
|
||||||
|
static void OnSessionEnded(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||||
|
session_type) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
session_ptr->GetAudioSession()->OnSessionEnded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The API reports the Bluetooth stack has replied the result of startStream
|
||||||
|
// or suspendStream, and will inform registered bluetooth_audio outputs
|
||||||
|
static void ReportControlStatus(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||||
|
session_type,
|
||||||
|
const bool& start_resp, const BluetoothAudioStatus& status) {
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||||
|
if (session_ptr != nullptr) {
|
||||||
|
session_ptr->GetAudioSession()->ReportControlStatus(start_resp, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace bluetooth
|
||||||
|
} // namespace android
|
117
bluetooth/audio/utils/session/BluetoothAudioSession_2_1.cpp
Normal file
117
bluetooth/audio/utils/session/BluetoothAudioSession_2_1.cpp
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_TAG "BTAudioProviderSession_2_1"
|
||||||
|
|
||||||
|
#include "BluetoothAudioSession_2_1.h"
|
||||||
|
|
||||||
|
#include <android-base/logging.h>
|
||||||
|
#include <android-base/stringprintf.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace bluetooth {
|
||||||
|
namespace audio {
|
||||||
|
using SessionType_2_1 =
|
||||||
|
::android::hardware::bluetooth::audio::V2_1::SessionType;
|
||||||
|
using SessionType_2_0 =
|
||||||
|
::android::hardware::bluetooth::audio::V2_0::SessionType;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
bool is_2_0_session_type(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||||
|
session_type) {
|
||||||
|
if (session_type == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||||
|
session_type == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
|
||||||
|
session_type == SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
BluetoothAudioSession_2_1::BluetoothAudioSession_2_1(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||||
|
session_type)
|
||||||
|
: audio_session(BluetoothAudioSessionInstance::GetSessionInstance(
|
||||||
|
static_cast<SessionType_2_0>(session_type))) {
|
||||||
|
if (is_2_0_session_type(session_type)) {
|
||||||
|
session_type_2_1_ = (SessionType_2_1::UNKNOWN);
|
||||||
|
} else {
|
||||||
|
session_type_2_1_ = (session_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<BluetoothAudioSession>
|
||||||
|
BluetoothAudioSession_2_1::GetAudioSession() {
|
||||||
|
return audio_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The report function is used to report that the Bluetooth stack has started
|
||||||
|
// this session without any failure, and will invoke session_changed_cb_ to
|
||||||
|
// notify those registered bluetooth_audio outputs
|
||||||
|
void BluetoothAudioSession_2_1::OnSessionStarted(
|
||||||
|
const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||||
|
audio_config) {
|
||||||
|
if (session_type_2_1_ == SessionType_2_1::UNKNOWN) {
|
||||||
|
::android::hardware::bluetooth::audio::V2_0::AudioConfiguration config;
|
||||||
|
if (audio_config.getDiscriminator() ==
|
||||||
|
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
|
||||||
|
hidl_discriminator::codecConfig) {
|
||||||
|
config.codecConfig(audio_config.codecConfig());
|
||||||
|
} else {
|
||||||
|
auto& tmpPcm = audio_config.pcmConfig();
|
||||||
|
config.pcmConfig(
|
||||||
|
::android::hardware::bluetooth::audio::V2_0::PcmParameters{
|
||||||
|
.sampleRate = static_cast<SampleRate>(tmpPcm.sampleRate),
|
||||||
|
.channelMode = tmpPcm.channelMode,
|
||||||
|
.bitsPerSample = tmpPcm.bitsPerSample
|
||||||
|
/*dataIntervalUs is not passed to 2.0 */
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_session->OnSessionStarted(stack_iface, dataMQ, config);
|
||||||
|
} else {
|
||||||
|
LOG(FATAL) << " Not implemented yet!!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<BluetoothAudioSessionInstance_2_1>
|
||||||
|
BluetoothAudioSessionInstance_2_1::instance_ptr =
|
||||||
|
std::unique_ptr<BluetoothAudioSessionInstance_2_1>(
|
||||||
|
new BluetoothAudioSessionInstance_2_1());
|
||||||
|
|
||||||
|
// API to fetch the session of A2DP / Hearing Aid
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1>
|
||||||
|
BluetoothAudioSessionInstance_2_1::GetSessionInstance(
|
||||||
|
const SessionType_2_1& 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_2_1> session_ptr =
|
||||||
|
std::make_shared<BluetoothAudioSession_2_1>(session_type);
|
||||||
|
instance_ptr->sessions_map_[session_type] = session_ptr;
|
||||||
|
return session_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace bluetooth
|
||||||
|
} // namespace android
|
82
bluetooth/audio/utils/session/BluetoothAudioSession_2_1.h
Normal file
82
bluetooth/audio/utils/session/BluetoothAudioSession_2_1.h
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <android/hardware/bluetooth/audio/2.1/types.h>
|
||||||
|
#include "BluetoothAudioSession.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace bluetooth {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
|
class BluetoothAudioSession_2_1 {
|
||||||
|
private:
|
||||||
|
std::shared_ptr<BluetoothAudioSession> audio_session;
|
||||||
|
|
||||||
|
::android::hardware::bluetooth::audio::V2_1::SessionType session_type_2_1_;
|
||||||
|
|
||||||
|
// audio data configuration for both software and offloading
|
||||||
|
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
|
||||||
|
audio_config_2_1_;
|
||||||
|
|
||||||
|
bool UpdateAudioConfig(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||||
|
audio_config);
|
||||||
|
|
||||||
|
public:
|
||||||
|
BluetoothAudioSession_2_1(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||||
|
session_type);
|
||||||
|
|
||||||
|
std::shared_ptr<BluetoothAudioSession> GetAudioSession();
|
||||||
|
|
||||||
|
// The report function is used to report that the Bluetooth stack has started
|
||||||
|
// this session without any failure, and will invoke session_changed_cb_ to
|
||||||
|
// notify those registered bluetooth_audio outputs
|
||||||
|
void OnSessionStarted(
|
||||||
|
const sp<IBluetoothAudioPort> stack_iface,
|
||||||
|
const DataMQ::Descriptor* dataMQ,
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||||
|
audio_config);
|
||||||
|
|
||||||
|
// The control function is for the bluetooth_audio module to get the current
|
||||||
|
// AudioConfiguration
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||||
|
GetAudioConfig();
|
||||||
|
};
|
||||||
|
|
||||||
|
class BluetoothAudioSessionInstance_2_1 {
|
||||||
|
public:
|
||||||
|
// The API is to fetch the specified session of A2DP / Hearing Aid
|
||||||
|
static std::shared_ptr<BluetoothAudioSession_2_1> GetSessionInstance(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||||
|
session_type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::unique_ptr<BluetoothAudioSessionInstance_2_1> instance_ptr;
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::unordered_map<::android::hardware::bluetooth::audio::V2_1::SessionType,
|
||||||
|
std::shared_ptr<BluetoothAudioSession_2_1>>
|
||||||
|
sessions_map_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace bluetooth
|
||||||
|
} // namespace android
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_TAG "BTAudioProviderSessionCodecsDB_2_1"
|
||||||
|
|
||||||
|
#include "BluetoothAudioSupportedCodecsDB_2_1.h"
|
||||||
|
|
||||||
|
#include <android-base/logging.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace bluetooth {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
|
using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
|
||||||
|
using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
|
||||||
|
|
||||||
|
using SampleRate_2_0 = ::android::hardware::bluetooth::audio::V2_0::SampleRate;
|
||||||
|
using SampleRate_2_1 = ::android::hardware::bluetooth::audio::V2_1::SampleRate;
|
||||||
|
|
||||||
|
using SessionType_2_1 =
|
||||||
|
::android::hardware::bluetooth::audio::V2_1::SessionType;
|
||||||
|
using SessionType_2_0 =
|
||||||
|
::android::hardware::bluetooth::audio::V2_0::SessionType;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
bool is_2_0_session_type(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||||
|
session_type) {
|
||||||
|
if (session_type == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||||
|
session_type == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
|
||||||
|
session_type == SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
static const ::android::hardware::bluetooth::audio::V2_1::PcmParameters
|
||||||
|
kDefaultSoftwarePcmCapabilities_2_1 = {
|
||||||
|
.sampleRate = static_cast<SampleRate_2_1>(
|
||||||
|
SampleRate_2_1::RATE_44100 | SampleRate_2_1::RATE_48000 |
|
||||||
|
SampleRate_2_1::RATE_88200 | SampleRate_2_1::RATE_96000 |
|
||||||
|
SampleRate_2_1::RATE_16000 | SampleRate_2_1::RATE_24000),
|
||||||
|
.channelMode =
|
||||||
|
static_cast<ChannelMode>(ChannelMode::MONO | ChannelMode::STEREO),
|
||||||
|
.bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
|
||||||
|
BitsPerSample::BITS_24 |
|
||||||
|
BitsPerSample::BITS_32)};
|
||||||
|
|
||||||
|
std::vector<::android::hardware::bluetooth::audio::V2_1::PcmParameters>
|
||||||
|
GetSoftwarePcmCapabilities_2_1() {
|
||||||
|
return std::vector<
|
||||||
|
::android::hardware::bluetooth::audio::V2_1::PcmParameters>(
|
||||||
|
1, kDefaultSoftwarePcmCapabilities_2_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||||
|
session_type) {
|
||||||
|
if (is_2_0_session_type(session_type)) {
|
||||||
|
return GetOffloadCodecCapabilities(
|
||||||
|
static_cast<SessionType_2_0>(session_type));
|
||||||
|
}
|
||||||
|
return std::vector<CodecCapabilities>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSoftwarePcmConfigurationValid_2_1(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::PcmParameters&
|
||||||
|
pcm_config) {
|
||||||
|
if ((pcm_config.sampleRate != SampleRate_2_1::RATE_44100 &&
|
||||||
|
pcm_config.sampleRate != SampleRate_2_1::RATE_48000 &&
|
||||||
|
pcm_config.sampleRate != SampleRate_2_1::RATE_88200 &&
|
||||||
|
pcm_config.sampleRate != SampleRate_2_1::RATE_96000 &&
|
||||||
|
pcm_config.sampleRate != SampleRate_2_1::RATE_16000 &&
|
||||||
|
pcm_config.sampleRate != SampleRate_2_1::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 ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||||
|
session_type,
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration&
|
||||||
|
codec_config) {
|
||||||
|
if (is_2_0_session_type(session_type)) {
|
||||||
|
return IsOffloadCodecConfigurationValid(
|
||||||
|
static_cast<SessionType_2_0>(session_type), codec_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace bluetooth
|
||||||
|
} // namespace android
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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 "BluetoothAudioSupportedCodecsDB.h"
|
||||||
|
|
||||||
|
#include <android/hardware/bluetooth/audio/2.1/types.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace bluetooth {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
|
std::vector<::android::hardware::bluetooth::audio::V2_1::PcmParameters>
|
||||||
|
GetSoftwarePcmCapabilities_2_1();
|
||||||
|
std::vector<::android::hardware::bluetooth::audio::V2_0::CodecCapabilities>
|
||||||
|
GetOffloadCodecCapabilities(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||||
|
session_type);
|
||||||
|
|
||||||
|
bool IsSoftwarePcmConfigurationValid_2_1(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::PcmParameters&
|
||||||
|
pcm_config);
|
||||||
|
|
||||||
|
bool IsOffloadCodecConfigurationValid(
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||||
|
session_type,
|
||||||
|
const ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration&
|
||||||
|
codec_config);
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace bluetooth
|
||||||
|
} // namespace android
|
Loading…
Reference in a new issue