mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
Merge changes from topic "bluetoothmodule" into main
* changes: aosp aidl bluetooth parameter support aosp aidl bluetooth audio hal implementation
This commit is contained in:
@@ -82,6 +82,9 @@ cc_library {
|
||||
"alsa/ModuleAlsa.cpp",
|
||||
"alsa/StreamAlsa.cpp",
|
||||
"alsa/Utils.cpp",
|
||||
"bluetooth/DevicePortProxy.cpp",
|
||||
"bluetooth/ModuleBluetooth.cpp",
|
||||
"bluetooth/StreamBluetooth.cpp",
|
||||
"r_submix/ModuleRemoteSubmix.cpp",
|
||||
"r_submix/RemoteSubmixUtils.cpp",
|
||||
"r_submix/SubmixRoute.cpp",
|
||||
@@ -105,7 +108,9 @@ cc_library {
|
||||
"audio_policy_engine_configuration_aidl_default",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.bluetooth.audio-V3-ndk",
|
||||
"libaudio_aidl_conversion_common_ndk",
|
||||
"libbluetooth_audio_session_aidl",
|
||||
"libmedia_helper",
|
||||
"libstagefright_foundation",
|
||||
],
|
||||
@@ -136,7 +141,9 @@ cc_binary {
|
||||
"libaudioserviceexampleimpl",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.bluetooth.audio-V3-ndk",
|
||||
"libaudio_aidl_conversion_common_ndk",
|
||||
"libbluetooth_audio_session_aidl",
|
||||
"libmedia_helper",
|
||||
"libstagefright_foundation",
|
||||
],
|
||||
|
||||
@@ -82,19 +82,18 @@ ndk::ScopedAStatus Bluetooth::setHfpConfig(const HfpConfig& in_config, HfpConfig
|
||||
|
||||
ndk::ScopedAStatus BluetoothA2dp::isEnabled(bool* _aidl_return) {
|
||||
*_aidl_return = mEnabled;
|
||||
LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothA2dp::setEnabled(bool in_enabled) {
|
||||
mEnabled = in_enabled;
|
||||
LOG(DEBUG) << __func__ << ": " << mEnabled;
|
||||
if (mHandler) return mHandler();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothA2dp::supportsOffloadReconfiguration(bool* _aidl_return) {
|
||||
*_aidl_return = true;
|
||||
LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
|
||||
*_aidl_return = false;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
@@ -102,24 +101,22 @@ ndk::ScopedAStatus BluetoothA2dp::reconfigureOffload(
|
||||
const std::vector<::aidl::android::hardware::audio::core::VendorParameter>& in_parameters
|
||||
__unused) {
|
||||
LOG(DEBUG) << __func__ << ": " << ::android::internal::ToString(in_parameters);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothLe::isEnabled(bool* _aidl_return) {
|
||||
*_aidl_return = mEnabled;
|
||||
LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothLe::setEnabled(bool in_enabled) {
|
||||
mEnabled = in_enabled;
|
||||
LOG(DEBUG) << __func__ << ": " << mEnabled;
|
||||
if (mHandler) return mHandler();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothLe::supportsOffloadReconfiguration(bool* _aidl_return) {
|
||||
*_aidl_return = true;
|
||||
LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
|
||||
*_aidl_return = false;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
@@ -127,7 +124,7 @@ ndk::ScopedAStatus BluetoothLe::reconfigureOffload(
|
||||
const std::vector<::aidl::android::hardware::audio::core::VendorParameter>& in_parameters
|
||||
__unused) {
|
||||
LOG(DEBUG) << __func__ << ": " << ::android::internal::ToString(in_parameters);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
||||
@@ -561,4 +561,86 @@ std::unique_ptr<Configuration> getStubConfiguration() {
|
||||
return std::make_unique<Configuration>(configuration);
|
||||
}
|
||||
|
||||
// Bluetooth configuration:
|
||||
//
|
||||
// Device ports:
|
||||
// * "BT A2DP Out", OUT_DEVICE, CONNECTION_BT_A2DP
|
||||
// - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
|
||||
// * "BT A2DP Headphones", OUT_HEADSET, CONNECTION_BT_A2DP
|
||||
// - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
|
||||
// * "BT A2DP Speaker", OUT_SPEAKER, CONNECTION_BT_A2DP
|
||||
// - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
|
||||
// * "BT Hearing Aid Out", OUT_HEARING_AID, CONNECTION_WIRELESS
|
||||
// - no profiles specified
|
||||
//
|
||||
// Mix ports:
|
||||
// * "a2dp output", 1 max open, 1 max active stream
|
||||
// - no profiles specified
|
||||
// * "hearing aid output", 1 max open, 1 max active stream
|
||||
// - profile PCM 16-bit; STEREO; 16000, 24000
|
||||
//
|
||||
// Routes:
|
||||
// "a2dp output" -> "BT A2DP Out"
|
||||
// "a2dp output" -> "BT A2DP Headphones"
|
||||
// "a2dp output" -> "BT A2DP Speaker"
|
||||
// "hearing aid output" -> "BT Hearing Aid Out"
|
||||
//
|
||||
std::unique_ptr<Configuration> getBluetoothConfiguration() {
|
||||
static const Configuration configuration = []() {
|
||||
const std::vector<AudioProfile> standardPcmAudioProfiles = {
|
||||
createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO},
|
||||
{44100, 48000, 88200, 96000})};
|
||||
Configuration c;
|
||||
|
||||
// Device ports
|
||||
AudioPort btOutDevice =
|
||||
createPort(c.nextPortId++, "BT A2DP Out", 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
|
||||
AudioDeviceDescription::CONNECTION_BT_A2DP));
|
||||
c.ports.push_back(btOutDevice);
|
||||
c.connectedProfiles[btOutDevice.id] = standardPcmAudioProfiles;
|
||||
|
||||
AudioPort btOutHeadphone =
|
||||
createPort(c.nextPortId++, "BT A2DP Headphones", 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_HEADSET, 0,
|
||||
AudioDeviceDescription::CONNECTION_BT_A2DP));
|
||||
c.ports.push_back(btOutHeadphone);
|
||||
c.connectedProfiles[btOutHeadphone.id] = standardPcmAudioProfiles;
|
||||
|
||||
AudioPort btOutSpeaker =
|
||||
createPort(c.nextPortId++, "BT A2DP Speaker", 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0,
|
||||
AudioDeviceDescription::CONNECTION_BT_A2DP));
|
||||
c.ports.push_back(btOutSpeaker);
|
||||
c.connectedProfiles[btOutSpeaker.id] = standardPcmAudioProfiles;
|
||||
|
||||
AudioPort btOutHearingAid =
|
||||
createPort(c.nextPortId++, "BT Hearing Aid Out", 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_HEARING_AID, 0,
|
||||
AudioDeviceDescription::CONNECTION_WIRELESS));
|
||||
c.ports.push_back(btOutHearingAid);
|
||||
c.connectedProfiles[btOutHearingAid.id] = std::vector<AudioProfile>(
|
||||
{createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000})});
|
||||
|
||||
// Mix ports
|
||||
AudioPort btInMix =
|
||||
createPort(c.nextPortId++, "a2dp output", 0, true, createPortMixExt(1, 1));
|
||||
c.ports.push_back(btInMix);
|
||||
|
||||
AudioPort btHeadsetInMix =
|
||||
createPort(c.nextPortId++, "hearing aid output", 0, true, createPortMixExt(1, 1));
|
||||
btHeadsetInMix.profiles.push_back(createProfile(
|
||||
PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000, 24000}));
|
||||
c.ports.push_back(btHeadsetInMix);
|
||||
|
||||
c.routes.push_back(createRoute({btInMix}, btOutDevice));
|
||||
c.routes.push_back(createRoute({btInMix}, btOutHeadphone));
|
||||
c.routes.push_back(createRoute({btInMix}, btOutSpeaker));
|
||||
c.routes.push_back(createRoute({btHeadsetInMix}, btOutHearingAid));
|
||||
|
||||
return c;
|
||||
}();
|
||||
return std::make_unique<Configuration>(configuration);
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core::internal
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <error/expected_utils.h>
|
||||
|
||||
#include "core-impl/Module.h"
|
||||
#include "core-impl/ModuleBluetooth.h"
|
||||
#include "core-impl/ModulePrimary.h"
|
||||
#include "core-impl/ModuleRemoteSubmix.h"
|
||||
#include "core-impl/ModuleStub.h"
|
||||
@@ -117,6 +118,8 @@ std::shared_ptr<Module> Module::createInstance(Type type) {
|
||||
return ndk::SharedRefBase::make<ModuleStub>();
|
||||
case Type::USB:
|
||||
return ndk::SharedRefBase::make<ModuleUsb>();
|
||||
case Type::BLUETOOTH:
|
||||
return ndk::SharedRefBase::make<ModuleBluetooth>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +137,9 @@ std::ostream& operator<<(std::ostream& os, Module::Type t) {
|
||||
case Module::Type::USB:
|
||||
os << "usb";
|
||||
break;
|
||||
case Module::Type::BLUETOOTH:
|
||||
os << "bluetooth";
|
||||
break;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
@@ -301,6 +307,9 @@ std::unique_ptr<internal::Configuration> Module::initializeConfig() {
|
||||
case Type::USB:
|
||||
config = std::move(internal::getUsbConfiguration());
|
||||
break;
|
||||
case Type::BLUETOOTH:
|
||||
config = std::move(internal::getBluetoothConfiguration());
|
||||
break;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
@@ -1362,4 +1371,13 @@ ndk::ScopedAStatus Module::onMasterVolumeChanged(float volume __unused) {
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
Module::BtProfileHandles Module::getBtProfileManagerHandles() {
|
||||
return std::make_tuple(std::weak_ptr<IBluetooth>(), std::weak_ptr<IBluetoothA2dp>(),
|
||||
std::weak_ptr<IBluetoothLe>());
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::bluetoothParametersUpdated() {
|
||||
return mStreams.bluetoothParametersUpdated();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
||||
@@ -723,6 +723,11 @@ ndk::ScopedAStatus StreamCommonImpl::setConnectedDevices(
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus StreamCommonImpl::bluetoothParametersUpdated() {
|
||||
LOG(DEBUG) << __func__;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
namespace {
|
||||
static std::map<AudioDevice, std::string> transformMicrophones(
|
||||
const std::vector<MicrophoneInfo>& microphones) {
|
||||
|
||||
@@ -233,8 +233,28 @@ ndk::ScopedAStatus StreamSwitcher::setConnectedDevices(const std::vector<AudioDe
|
||||
<< status.getDescription();
|
||||
}
|
||||
}
|
||||
if (mBluetoothParametersUpdated) {
|
||||
if (auto status = mStream->bluetoothParametersUpdated(); !status.isOk()) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": error while updating BT parameters for a new stream: "
|
||||
<< status.getDescription();
|
||||
}
|
||||
}
|
||||
mBluetoothParametersUpdated = false;
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus StreamSwitcher::bluetoothParametersUpdated() {
|
||||
if (mStream == nullptr) {
|
||||
LOG(ERROR) << __func__ << ": stream was closed";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
if (mIsStubStream) {
|
||||
mBluetoothParametersUpdated = true;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
return mStream->bluetoothParametersUpdated();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
<version>1</version>
|
||||
<fqname>IModule/usb</fqname>
|
||||
</hal>
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.audio.core</name>
|
||||
<version>1</version>
|
||||
<fqname>IModule/bluetooth</fqname>
|
||||
</hal>
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.audio.core</name>
|
||||
<version>1</version>
|
||||
|
||||
577
audio/aidl/default/bluetooth/DevicePortProxy.cpp
Normal file
577
audio/aidl/default/bluetooth/DevicePortProxy.cpp
Normal file
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
* Copyright 2023 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 "AHAL_BluetoothPortProxy"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <audio_utils/primitives.h>
|
||||
#include <inttypes.h>
|
||||
#include <log/log.h>
|
||||
|
||||
#include "core-impl/DevicePortProxy.h"
|
||||
|
||||
namespace android::bluetooth::audio::aidl {
|
||||
|
||||
namespace {
|
||||
|
||||
// The maximum time to wait in std::condition_variable::wait_for()
|
||||
constexpr unsigned int kMaxWaitingTimeMs = 4500;
|
||||
|
||||
} // namespace
|
||||
|
||||
using ::aidl::android::hardware::audio::common::SinkMetadata;
|
||||
using ::aidl::android::hardware::audio::common::SourceMetadata;
|
||||
using ::aidl::android::hardware::bluetooth::audio::AudioConfiguration;
|
||||
using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioSessionControl;
|
||||
using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus;
|
||||
using ::aidl::android::hardware::bluetooth::audio::ChannelMode;
|
||||
using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration;
|
||||
using ::aidl::android::hardware::bluetooth::audio::PortStatusCallbacks;
|
||||
using ::aidl::android::hardware::bluetooth::audio::PresentationPosition;
|
||||
using ::aidl::android::hardware::bluetooth::audio::SessionType;
|
||||
using ::aidl::android::media::audio::common::AudioDeviceDescription;
|
||||
using ::aidl::android::media::audio::common::AudioDeviceType;
|
||||
using ::android::base::StringPrintf;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state) {
|
||||
switch (state) {
|
||||
case BluetoothStreamState::DISABLED:
|
||||
return os << "DISABLED";
|
||||
case BluetoothStreamState::STANDBY:
|
||||
return os << "STANDBY";
|
||||
case BluetoothStreamState::STARTING:
|
||||
return os << "STARTING";
|
||||
case BluetoothStreamState::STARTED:
|
||||
return os << "STARTED";
|
||||
case BluetoothStreamState::SUSPENDING:
|
||||
return os << "SUSPENDING";
|
||||
case BluetoothStreamState::UNKNOWN:
|
||||
return os << "UNKNOWN";
|
||||
default:
|
||||
return os << android::base::StringPrintf("%#hhx", state);
|
||||
}
|
||||
}
|
||||
|
||||
BluetoothAudioPortAidl::BluetoothAudioPortAidl()
|
||||
: mCookie(::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined),
|
||||
mState(BluetoothStreamState::DISABLED),
|
||||
mSessionType(SessionType::UNKNOWN) {}
|
||||
|
||||
BluetoothAudioPortAidl::~BluetoothAudioPortAidl() {
|
||||
unregisterPort();
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::registerPort(const AudioDeviceDescription& description) {
|
||||
if (inUse()) {
|
||||
LOG(ERROR) << __func__ << debugMessage() << " already in use";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initSessionType(description)) return false;
|
||||
|
||||
auto control_result_cb = [port = this](uint16_t cookie, bool start_resp,
|
||||
const BluetoothAudioStatus& status) {
|
||||
(void)start_resp;
|
||||
port->controlResultHandler(cookie, status);
|
||||
};
|
||||
auto session_changed_cb = [port = this](uint16_t cookie) {
|
||||
port->sessionChangedHandler(cookie);
|
||||
};
|
||||
// TODO: Add audio_config_changed_cb
|
||||
PortStatusCallbacks cbacks = {
|
||||
.control_result_cb_ = control_result_cb,
|
||||
.session_changed_cb_ = session_changed_cb,
|
||||
};
|
||||
mCookie = BluetoothAudioSessionControl::RegisterControlResultCback(mSessionType, cbacks);
|
||||
auto isOk = (mCookie != ::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined);
|
||||
if (isOk) {
|
||||
std::lock_guard guard(mCvMutex);
|
||||
mState = BluetoothStreamState::STANDBY;
|
||||
}
|
||||
LOG(DEBUG) << __func__ << debugMessage();
|
||||
return isOk;
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::initSessionType(const AudioDeviceDescription& description) {
|
||||
if (description.connection == AudioDeviceDescription::CONNECTION_BT_A2DP &&
|
||||
(description.type == AudioDeviceType::OUT_DEVICE ||
|
||||
description.type == AudioDeviceType::OUT_HEADPHONE ||
|
||||
description.type == AudioDeviceType::OUT_SPEAKER)) {
|
||||
LOG(VERBOSE) << __func__
|
||||
<< ": device=AUDIO_DEVICE_OUT_BLUETOOTH_A2DP (HEADPHONES/SPEAKER) ("
|
||||
<< description.toString() << ")";
|
||||
mSessionType = SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
|
||||
} else if (description.connection == AudioDeviceDescription::CONNECTION_WIRELESS &&
|
||||
description.type == AudioDeviceType::OUT_HEARING_AID) {
|
||||
LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_HEARING_AID (MEDIA/VOICE) ("
|
||||
<< description.toString() << ")";
|
||||
mSessionType = SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH;
|
||||
} else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE &&
|
||||
description.type == AudioDeviceType::OUT_HEADSET) {
|
||||
LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_BLE_HEADSET (MEDIA/VOICE) ("
|
||||
<< description.toString() << ")";
|
||||
mSessionType = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
|
||||
} else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE &&
|
||||
description.type == AudioDeviceType::OUT_SPEAKER) {
|
||||
LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_BLE_SPEAKER (MEDIA) ("
|
||||
<< description.toString() << ")";
|
||||
mSessionType = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
|
||||
} else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE &&
|
||||
description.type == AudioDeviceType::IN_HEADSET) {
|
||||
LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_IN_BLE_HEADSET (VOICE) ("
|
||||
<< description.toString() << ")";
|
||||
mSessionType = SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH;
|
||||
} else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE &&
|
||||
description.type == AudioDeviceType::OUT_BROADCAST) {
|
||||
LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_BLE_BROADCAST (MEDIA) ("
|
||||
<< description.toString() << ")";
|
||||
mSessionType = SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH;
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << ": unknown device=" << description.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!BluetoothAudioSessionControl::IsSessionReady(mSessionType)) {
|
||||
LOG(ERROR) << __func__ << ": device=" << description.toString()
|
||||
<< ", session_type=" << toString(mSessionType) << " is not ready";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BluetoothAudioPortAidl::unregisterPort() {
|
||||
if (!inUse()) {
|
||||
LOG(WARNING) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return;
|
||||
}
|
||||
BluetoothAudioSessionControl::UnregisterControlResultCback(mSessionType, mCookie);
|
||||
mCookie = ::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined;
|
||||
LOG(VERBOSE) << __func__ << debugMessage() << " port unregistered";
|
||||
}
|
||||
|
||||
void BluetoothAudioPortAidl::controlResultHandler(uint16_t cookie,
|
||||
const BluetoothAudioStatus& status) {
|
||||
std::lock_guard guard(mCvMutex);
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << "control_result_cb: BluetoothAudioPortAidl is not in use";
|
||||
return;
|
||||
}
|
||||
if (mCookie != cookie) {
|
||||
LOG(ERROR) << "control_result_cb: proxy of device port (cookie="
|
||||
<< StringPrintf("%#hx", cookie) << ") is corrupted";
|
||||
return;
|
||||
}
|
||||
BluetoothStreamState previous_state = mState;
|
||||
LOG(INFO) << "control_result_cb:" << debugMessage() << ", previous_state=" << previous_state
|
||||
<< ", status=" << toString(status);
|
||||
|
||||
switch (previous_state) {
|
||||
case BluetoothStreamState::STARTED:
|
||||
/* Only Suspend signal can be send in STARTED state*/
|
||||
if (status == BluetoothAudioStatus::RECONFIGURATION ||
|
||||
status == BluetoothAudioStatus::SUCCESS) {
|
||||
mState = BluetoothStreamState::STANDBY;
|
||||
} else {
|
||||
LOG(WARNING) << StringPrintf(
|
||||
"control_result_cb: status=%s failure for session_type= %s, cookie=%#hx, "
|
||||
"previous_state=%#hhx",
|
||||
toString(status).c_str(), toString(mSessionType).c_str(), mCookie,
|
||||
previous_state);
|
||||
}
|
||||
break;
|
||||
case BluetoothStreamState::STARTING:
|
||||
if (status == BluetoothAudioStatus::SUCCESS) {
|
||||
mState = BluetoothStreamState::STARTED;
|
||||
} else {
|
||||
// Set to standby since the stack may be busy switching between outputs
|
||||
LOG(WARNING) << StringPrintf(
|
||||
"control_result_cb: status=%s failure for session_type= %s, cookie=%#hx, "
|
||||
"previous_state=%#hhx",
|
||||
toString(status).c_str(), toString(mSessionType).c_str(), mCookie,
|
||||
previous_state);
|
||||
mState = BluetoothStreamState::STANDBY;
|
||||
}
|
||||
break;
|
||||
case BluetoothStreamState::SUSPENDING:
|
||||
if (status == BluetoothAudioStatus::SUCCESS) {
|
||||
mState = BluetoothStreamState::STANDBY;
|
||||
} else {
|
||||
// It will be failed if the headset is disconnecting, and set to disable
|
||||
// to wait for re-init again
|
||||
LOG(WARNING) << StringPrintf(
|
||||
"control_result_cb: status=%s failure for session_type= %s, cookie=%#hx, "
|
||||
"previous_state=%#hhx",
|
||||
toString(status).c_str(), toString(mSessionType).c_str(), mCookie,
|
||||
previous_state);
|
||||
mState = BluetoothStreamState::DISABLED;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "control_result_cb: unexpected previous_state="
|
||||
<< StringPrintf(
|
||||
"control_result_cb: status=%s failure for session_type= %s, "
|
||||
"cookie=%#hx, previous_state=%#hhx",
|
||||
toString(status).c_str(), toString(mSessionType).c_str(), mCookie,
|
||||
previous_state);
|
||||
return;
|
||||
}
|
||||
mInternalCv.notify_all();
|
||||
}
|
||||
|
||||
void BluetoothAudioPortAidl::sessionChangedHandler(uint16_t cookie) {
|
||||
std::lock_guard guard(mCvMutex);
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << "session_changed_cb: BluetoothAudioPortAidl is not in use";
|
||||
return;
|
||||
}
|
||||
if (mCookie != cookie) {
|
||||
LOG(ERROR) << "session_changed_cb: proxy of device port (cookie="
|
||||
<< StringPrintf("%#hx", cookie) << ") is corrupted";
|
||||
return;
|
||||
}
|
||||
BluetoothStreamState previous_state = mState;
|
||||
LOG(VERBOSE) << "session_changed_cb:" << debugMessage()
|
||||
<< ", previous_state=" << previous_state;
|
||||
mState = BluetoothStreamState::DISABLED;
|
||||
mInternalCv.notify_all();
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::inUse() const {
|
||||
return (mCookie != ::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined);
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::getPreferredDataIntervalUs(size_t* interval_us) const {
|
||||
if (!interval_us) {
|
||||
LOG(ERROR) << __func__ << ": bad input arg";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return false;
|
||||
}
|
||||
|
||||
const AudioConfiguration& hal_audio_cfg =
|
||||
BluetoothAudioSessionControl::GetAudioConfig(mSessionType);
|
||||
if (hal_audio_cfg.getTag() != AudioConfiguration::pcmConfig) {
|
||||
LOG(ERROR) << __func__ << ": unsupported audio cfg tag";
|
||||
return false;
|
||||
}
|
||||
|
||||
*interval_us = hal_audio_cfg.get<AudioConfiguration::pcmConfig>().dataIntervalUs;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::loadAudioConfig(PcmConfiguration* audio_cfg) const {
|
||||
if (!audio_cfg) {
|
||||
LOG(ERROR) << __func__ << ": bad input arg";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return false;
|
||||
}
|
||||
|
||||
const AudioConfiguration& hal_audio_cfg =
|
||||
BluetoothAudioSessionControl::GetAudioConfig(mSessionType);
|
||||
if (hal_audio_cfg.getTag() != AudioConfiguration::pcmConfig) {
|
||||
LOG(ERROR) << __func__ << ": unsupported audio cfg tag";
|
||||
return false;
|
||||
}
|
||||
*audio_cfg = hal_audio_cfg.get<AudioConfiguration::pcmConfig>();
|
||||
LOG(VERBOSE) << __func__ << debugMessage() << ", state*=" << getState() << ", PcmConfig=["
|
||||
<< audio_cfg->toString() << "]";
|
||||
if (audio_cfg->channelMode == ChannelMode::UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::standby() {
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return false;
|
||||
}
|
||||
std::lock_guard guard(mCvMutex);
|
||||
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState() << " request";
|
||||
if (mState == BluetoothStreamState::DISABLED) {
|
||||
mState = BluetoothStreamState::STANDBY;
|
||||
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState() << " done";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::condWaitState(BluetoothStreamState state) {
|
||||
const auto waitTime = std::chrono::milliseconds(kMaxWaitingTimeMs);
|
||||
std::unique_lock lock(mCvMutex);
|
||||
base::ScopedLockAssertion lock_assertion(mCvMutex);
|
||||
switch (state) {
|
||||
case BluetoothStreamState::STARTING: {
|
||||
LOG(VERBOSE) << __func__ << debugMessage() << " waiting for STARTED";
|
||||
mInternalCv.wait_for(lock, waitTime, [this] {
|
||||
base::ScopedLockAssertion lock_assertion(mCvMutex);
|
||||
return mState != BluetoothStreamState::STARTING;
|
||||
});
|
||||
return mState == BluetoothStreamState::STARTED;
|
||||
}
|
||||
case BluetoothStreamState::SUSPENDING: {
|
||||
LOG(VERBOSE) << __func__ << debugMessage() << " waiting for SUSPENDED";
|
||||
mInternalCv.wait_for(lock, waitTime, [this] {
|
||||
base::ScopedLockAssertion lock_assertion(mCvMutex);
|
||||
return mState != BluetoothStreamState::SUSPENDING;
|
||||
});
|
||||
return mState == BluetoothStreamState::STANDBY;
|
||||
}
|
||||
default:
|
||||
LOG(WARNING) << __func__ << debugMessage() << " waiting for KNOWN";
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::start() {
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return false;
|
||||
}
|
||||
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState()
|
||||
<< ", mono=" << (mIsStereoToMono ? "true" : "false") << " request";
|
||||
|
||||
{
|
||||
std::unique_lock lock(mCvMutex);
|
||||
base::ScopedLockAssertion lock_assertion(mCvMutex);
|
||||
if (mState == BluetoothStreamState::STARTED) {
|
||||
return true; // nop, return
|
||||
} else if (mState == BluetoothStreamState::SUSPENDING ||
|
||||
mState == BluetoothStreamState::STARTING) {
|
||||
/* If port is in transient state, give some time to respond */
|
||||
auto state_ = mState;
|
||||
lock.unlock();
|
||||
if (!condWaitState(state_)) {
|
||||
LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() << " failure";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool retval = false;
|
||||
{
|
||||
std::unique_lock lock(mCvMutex);
|
||||
base::ScopedLockAssertion lock_assertion(mCvMutex);
|
||||
if (mState == BluetoothStreamState::STARTED) {
|
||||
retval = true;
|
||||
} else if (mState == BluetoothStreamState::STANDBY) {
|
||||
mState = BluetoothStreamState::STARTING;
|
||||
lock.unlock();
|
||||
if (BluetoothAudioSessionControl::StartStream(mSessionType)) {
|
||||
retval = condWaitState(BluetoothStreamState::STARTING);
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState()
|
||||
<< " Hal fails";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (retval) {
|
||||
LOG(INFO) << __func__ << debugMessage() << ", state=" << getState()
|
||||
<< ", mono=" << (mIsStereoToMono ? "true" : "false") << " done";
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() << " failure";
|
||||
}
|
||||
|
||||
return retval; // false if any failure like timeout
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::suspend() {
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return false;
|
||||
}
|
||||
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState() << " request";
|
||||
|
||||
{
|
||||
std::unique_lock lock(mCvMutex);
|
||||
base::ScopedLockAssertion lock_assertion(mCvMutex);
|
||||
if (mState == BluetoothStreamState::STANDBY) {
|
||||
return true; // nop, return
|
||||
} else if (mState == BluetoothStreamState::SUSPENDING ||
|
||||
mState == BluetoothStreamState::STARTING) {
|
||||
/* If port is in transient state, give some time to respond */
|
||||
auto state_ = mState;
|
||||
lock.unlock();
|
||||
if (!condWaitState(state_)) {
|
||||
LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() << " failure";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool retval = false;
|
||||
{
|
||||
std::unique_lock lock(mCvMutex);
|
||||
base::ScopedLockAssertion lock_assertion(mCvMutex);
|
||||
if (mState == BluetoothStreamState::STANDBY) {
|
||||
retval = true;
|
||||
} else if (mState == BluetoothStreamState::STARTED) {
|
||||
mState = BluetoothStreamState::SUSPENDING;
|
||||
lock.unlock();
|
||||
if (BluetoothAudioSessionControl::SuspendStream(mSessionType)) {
|
||||
retval = condWaitState(BluetoothStreamState::SUSPENDING);
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState()
|
||||
<< " Hal fails";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (retval) {
|
||||
LOG(INFO) << __func__ << debugMessage() << ", state=" << getState() << " done";
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << debugMessage() << ", state=" << getState() << " failure";
|
||||
}
|
||||
|
||||
return retval; // false if any failure like timeout
|
||||
}
|
||||
|
||||
void BluetoothAudioPortAidl::stop() {
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return;
|
||||
}
|
||||
std::lock_guard guard(mCvMutex);
|
||||
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState() << " request";
|
||||
if (mState != BluetoothStreamState::DISABLED) {
|
||||
BluetoothAudioSessionControl::StopStream(mSessionType);
|
||||
mState = BluetoothStreamState::DISABLED;
|
||||
}
|
||||
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState() << " done";
|
||||
}
|
||||
|
||||
size_t BluetoothAudioPortAidlOut::writeData(const void* buffer, size_t bytes) const {
|
||||
if (!buffer) {
|
||||
LOG(ERROR) << __func__ << ": bad input arg";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!mIsStereoToMono) {
|
||||
return BluetoothAudioSessionControl::OutWritePcmData(mSessionType, buffer, bytes);
|
||||
}
|
||||
|
||||
// WAR to mix the stereo into Mono (16 bits per sample)
|
||||
const size_t write_frames = bytes >> 2;
|
||||
if (write_frames == 0) return 0;
|
||||
auto src = static_cast<const int16_t*>(buffer);
|
||||
std::unique_ptr<int16_t[]> dst{new int16_t[write_frames]};
|
||||
downmix_to_mono_i16_from_stereo_i16(dst.get(), src, write_frames);
|
||||
// a frame is 16 bits, and the size of a mono frame is equal to half a stereo.
|
||||
auto totalWrite = BluetoothAudioSessionControl::OutWritePcmData(mSessionType, dst.get(),
|
||||
write_frames * 2);
|
||||
return totalWrite * 2;
|
||||
}
|
||||
|
||||
size_t BluetoothAudioPortAidlIn::readData(void* buffer, size_t bytes) const {
|
||||
if (!buffer) {
|
||||
LOG(ERROR) << __func__ << ": bad input arg";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return 0;
|
||||
}
|
||||
|
||||
return BluetoothAudioSessionControl::InReadPcmData(mSessionType, buffer, bytes);
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::getPresentationPosition(
|
||||
PresentationPosition& presentation_position) const {
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return false;
|
||||
}
|
||||
bool retval = BluetoothAudioSessionControl::GetPresentationPosition(mSessionType,
|
||||
presentation_position);
|
||||
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << getState()
|
||||
<< presentation_position.toString();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::updateSourceMetadata(const SourceMetadata& source_metadata) const {
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return false;
|
||||
}
|
||||
LOG(DEBUG) << __func__ << debugMessage() << ", state=" << getState() << ", "
|
||||
<< source_metadata.tracks.size() << " track(s)";
|
||||
if (source_metadata.tracks.size() == 0) return true;
|
||||
return BluetoothAudioSessionControl::UpdateSourceMetadata(mSessionType, source_metadata);
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::updateSinkMetadata(const SinkMetadata& sink_metadata) const {
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return false;
|
||||
}
|
||||
LOG(DEBUG) << __func__ << debugMessage() << ", state=" << getState() << ", "
|
||||
<< sink_metadata.tracks.size() << " track(s)";
|
||||
if (sink_metadata.tracks.size() == 0) return true;
|
||||
return BluetoothAudioSessionControl::UpdateSinkMetadata(mSessionType, sink_metadata);
|
||||
}
|
||||
|
||||
BluetoothStreamState BluetoothAudioPortAidl::getState() const {
|
||||
return mState;
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::setState(BluetoothStreamState state) {
|
||||
if (!inUse()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||||
return false;
|
||||
}
|
||||
std::lock_guard guard(mCvMutex);
|
||||
LOG(DEBUG) << __func__ << ": BluetoothAudioPortAidl old state = " << mState
|
||||
<< " new state = " << state;
|
||||
mState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::isA2dp() const {
|
||||
return mSessionType == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
mSessionType == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortAidl::isLeAudio() const {
|
||||
return mSessionType == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
|
||||
mSessionType == SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH ||
|
||||
mSessionType == SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
mSessionType == SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
|
||||
mSessionType == SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH ||
|
||||
mSessionType == SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
|
||||
}
|
||||
|
||||
std::string BluetoothAudioPortAidl::debugMessage() const {
|
||||
return StringPrintf(": session_type=%s, cookie=%#hx", toString(mSessionType).c_str(), mCookie);
|
||||
}
|
||||
|
||||
} // namespace android::bluetooth::audio::aidl
|
||||
93
audio/aidl/default/bluetooth/ModuleBluetooth.cpp
Normal file
93
audio/aidl/default/bluetooth/ModuleBluetooth.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 "AHAL_ModuleBluetooth"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "core-impl/ModuleBluetooth.h"
|
||||
#include "core-impl/StreamBluetooth.h"
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
using aidl::android::hardware::audio::common::SinkMetadata;
|
||||
using aidl::android::hardware::audio::common::SourceMetadata;
|
||||
using aidl::android::media::audio::common::AudioOffloadInfo;
|
||||
using aidl::android::media::audio::common::MicrophoneInfo;
|
||||
|
||||
ndk::ScopedAStatus ModuleBluetooth::getBluetoothA2dp(
|
||||
std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
|
||||
if (!mBluetoothA2dp) {
|
||||
auto handle = ndk::SharedRefBase::make<BluetoothA2dp>();
|
||||
handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this));
|
||||
mBluetoothA2dp = handle;
|
||||
}
|
||||
*_aidl_return = mBluetoothA2dp.getInstance();
|
||||
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleBluetooth::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
|
||||
if (!mBluetoothLe) {
|
||||
auto handle = ndk::SharedRefBase::make<BluetoothLe>();
|
||||
handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this));
|
||||
mBluetoothLe = handle;
|
||||
}
|
||||
*_aidl_return = mBluetoothLe.getInstance();
|
||||
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
Module::BtProfileHandles ModuleBluetooth::getBtProfileManagerHandles() {
|
||||
return std::make_tuple(std::weak_ptr<IBluetooth>(), mBluetoothA2dp.getInstance(),
|
||||
mBluetoothLe.getInstance());
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleBluetooth::getMicMute(bool* _aidl_return __unused) {
|
||||
LOG(DEBUG) << __func__ << ": is not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleBluetooth::setMicMute(bool in_mute __unused) {
|
||||
LOG(DEBUG) << __func__ << ": is not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleBluetooth::createInputStream(
|
||||
StreamContext&& context, const SinkMetadata& sinkMetadata,
|
||||
const std::vector<MicrophoneInfo>& microphones, std::shared_ptr<StreamIn>* result) {
|
||||
return createStreamInstance<StreamInBluetooth>(result, std::move(context), sinkMetadata,
|
||||
microphones, getBtProfileManagerHandles());
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleBluetooth::createOutputStream(
|
||||
StreamContext&& context, const SourceMetadata& sourceMetadata,
|
||||
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
|
||||
return createStreamInstance<StreamOutBluetooth>(result, std::move(context), sourceMetadata,
|
||||
offloadInfo, getBtProfileManagerHandles());
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleBluetooth::onMasterMuteChanged(bool) {
|
||||
LOG(DEBUG) << __func__ << ": is not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleBluetooth::onMasterVolumeChanged(float) {
|
||||
LOG(DEBUG) << __func__ << ": is not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
338
audio/aidl/default/bluetooth/StreamBluetooth.cpp
Normal file
338
audio/aidl/default/bluetooth/StreamBluetooth.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 "AHAL_StreamBluetooth"
|
||||
|
||||
#include <Utils.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <audio_utils/clock.h>
|
||||
|
||||
#include "BluetoothAudioSessionControl.h"
|
||||
#include "core-impl/StreamBluetooth.h"
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
using ::aidl::android::hardware::audio::common::SinkMetadata;
|
||||
using ::aidl::android::hardware::audio::common::SourceMetadata;
|
||||
using ::aidl::android::hardware::audio::core::VendorParameter;
|
||||
using ::aidl::android::hardware::bluetooth::audio::ChannelMode;
|
||||
using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration;
|
||||
using ::aidl::android::hardware::bluetooth::audio::PresentationPosition;
|
||||
using ::aidl::android::media::audio::common::AudioChannelLayout;
|
||||
using ::aidl::android::media::audio::common::AudioDevice;
|
||||
using ::aidl::android::media::audio::common::AudioDeviceAddress;
|
||||
using ::aidl::android::media::audio::common::AudioFormatDescription;
|
||||
using ::aidl::android::media::audio::common::AudioFormatType;
|
||||
using ::aidl::android::media::audio::common::AudioOffloadInfo;
|
||||
using ::aidl::android::media::audio::common::MicrophoneDynamicInfo;
|
||||
using ::aidl::android::media::audio::common::MicrophoneInfo;
|
||||
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
|
||||
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn;
|
||||
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
|
||||
using ::android::bluetooth::audio::aidl::BluetoothStreamState;
|
||||
|
||||
constexpr int kBluetoothDefaultInputBufferMs = 20;
|
||||
constexpr int kBluetoothDefaultOutputBufferMs = 10;
|
||||
// constexpr int kBluetoothSpatializerOutputBufferMs = 10;
|
||||
|
||||
size_t getFrameCount(uint64_t durationUs, uint32_t sampleRate) {
|
||||
return (durationUs * sampleRate) / 1000000;
|
||||
}
|
||||
|
||||
// pcm configuration params are not really used by the module
|
||||
StreamBluetooth::StreamBluetooth(StreamContext* context, const Metadata& metadata,
|
||||
Module::BtProfileHandles&& btHandles)
|
||||
: StreamCommonImpl(context, metadata),
|
||||
mSampleRate(getContext().getSampleRate()),
|
||||
mChannelLayout(getContext().getChannelLayout()),
|
||||
mFormat(getContext().getFormat()),
|
||||
mFrameSizeBytes(getContext().getFrameSize()),
|
||||
mIsInput(isInput(metadata)),
|
||||
mBluetoothA2dp(std::move(std::get<Module::BtInterface::BTA2DP>(btHandles))),
|
||||
mBluetoothLe(std::move(std::get<Module::BtInterface::BTLE>(btHandles))) {
|
||||
mPreferredDataIntervalUs =
|
||||
mIsInput ? kBluetoothDefaultInputBufferMs : kBluetoothDefaultOutputBufferMs;
|
||||
mPreferredFrameCount = getFrameCount(mPreferredDataIntervalUs, mSampleRate);
|
||||
mIsInitialized = false;
|
||||
mIsReadyToClose = false;
|
||||
}
|
||||
|
||||
::android::status_t StreamBluetooth::init() {
|
||||
return ::android::OK; // defering this till we get AudioDeviceDescription
|
||||
}
|
||||
|
||||
const StreamCommonInterface::ConnectedDevices& StreamBluetooth::getConnectedDevices() const {
|
||||
std::lock_guard guard(mLock);
|
||||
return StreamCommonImpl::getConnectedDevices();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus StreamBluetooth::setConnectedDevices(
|
||||
const std::vector<AudioDevice>& connectedDevices) {
|
||||
if (mIsInput && connectedDevices.size() > 1) {
|
||||
LOG(ERROR) << __func__ << ": wrong device size(" << connectedDevices.size()
|
||||
<< ") for input stream";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
for (const auto& connectedDevice : connectedDevices) {
|
||||
if (connectedDevice.address.getTag() != AudioDeviceAddress::mac) {
|
||||
LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
}
|
||||
std::lock_guard guard(mLock);
|
||||
RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices));
|
||||
mIsInitialized = false; // updated connected device list, need initialization
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
::android::status_t StreamBluetooth::drain(StreamDescriptor::DrainMode) {
|
||||
usleep(1000);
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
::android::status_t StreamBluetooth::flush() {
|
||||
usleep(1000);
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
::android::status_t StreamBluetooth::pause() {
|
||||
return standby();
|
||||
}
|
||||
|
||||
::android::status_t StreamBluetooth::transfer(void* buffer, size_t frameCount,
|
||||
size_t* actualFrameCount, int32_t* latencyMs) {
|
||||
std::lock_guard guard(mLock);
|
||||
if (!mIsInitialized || mIsReadyToClose) {
|
||||
// 'setConnectedDevices' has been called or stream is ready to close, so no transfers
|
||||
*actualFrameCount = 0;
|
||||
*latencyMs = StreamDescriptor::LATENCY_UNKNOWN;
|
||||
return ::android::OK;
|
||||
}
|
||||
*actualFrameCount = 0;
|
||||
*latencyMs = 0;
|
||||
for (auto proxy : mBtDeviceProxies) {
|
||||
if (!proxy->start()) {
|
||||
LOG(ERROR) << __func__ << ": state = " << proxy->getState() << " failed to start ";
|
||||
return -EIO;
|
||||
}
|
||||
const size_t fc = std::min(frameCount, mPreferredFrameCount);
|
||||
const size_t bytesToTransfer = fc * mFrameSizeBytes;
|
||||
if (mIsInput) {
|
||||
const size_t totalRead = proxy->readData(buffer, bytesToTransfer);
|
||||
*actualFrameCount = std::max(*actualFrameCount, totalRead / mFrameSizeBytes);
|
||||
} else {
|
||||
const size_t totalWrite = proxy->writeData(buffer, bytesToTransfer);
|
||||
*actualFrameCount = std::max(*actualFrameCount, totalWrite / mFrameSizeBytes);
|
||||
}
|
||||
PresentationPosition presentation_position;
|
||||
if (!proxy->getPresentationPosition(presentation_position)) {
|
||||
LOG(ERROR) << __func__ << ": getPresentationPosition returned error ";
|
||||
return ::android::UNKNOWN_ERROR;
|
||||
}
|
||||
*latencyMs =
|
||||
std::max(*latencyMs, (int32_t)(presentation_position.remoteDeviceAudioDelayNanos /
|
||||
NANOS_PER_MILLISECOND));
|
||||
}
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
::android::status_t StreamBluetooth::initialize() {
|
||||
if (!::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::IsAidlAvailable()) {
|
||||
LOG(ERROR) << __func__ << ": IBluetoothAudioProviderFactory service not available";
|
||||
return ::android::UNKNOWN_ERROR;
|
||||
}
|
||||
if (StreamCommonImpl::getConnectedDevices().empty()) {
|
||||
LOG(ERROR) << __func__ << ", has no connected devices";
|
||||
return ::android::NO_INIT;
|
||||
}
|
||||
// unregister older proxies (if any)
|
||||
for (auto proxy : mBtDeviceProxies) {
|
||||
proxy->stop();
|
||||
proxy->unregisterPort();
|
||||
}
|
||||
mBtDeviceProxies.clear();
|
||||
for (auto it = StreamCommonImpl::getConnectedDevices().begin();
|
||||
it != StreamCommonImpl::getConnectedDevices().end(); ++it) {
|
||||
std::shared_ptr<BluetoothAudioPortAidl> proxy =
|
||||
mIsInput ? std::shared_ptr<BluetoothAudioPortAidl>(
|
||||
std::make_shared<BluetoothAudioPortAidlIn>())
|
||||
: std::shared_ptr<BluetoothAudioPortAidl>(
|
||||
std::make_shared<BluetoothAudioPortAidlOut>());
|
||||
if (proxy->registerPort(it->type)) {
|
||||
LOG(ERROR) << __func__ << ": cannot init HAL";
|
||||
return ::android::UNKNOWN_ERROR;
|
||||
}
|
||||
PcmConfiguration config;
|
||||
if (!proxy->loadAudioConfig(&config)) {
|
||||
LOG(ERROR) << __func__ << ": state=" << proxy->getState()
|
||||
<< " failed to get audio config";
|
||||
return ::android::UNKNOWN_ERROR;
|
||||
}
|
||||
// TODO: Ensure minimum duration for spatialized output?
|
||||
// WAR to support Mono / 16 bits per sample as the Bluetooth stack required
|
||||
if (!mIsInput && config.channelMode == ChannelMode::MONO && config.bitsPerSample == 16) {
|
||||
proxy->forcePcmStereoToMono(true);
|
||||
config.channelMode = ChannelMode::STEREO;
|
||||
LOG(INFO) << __func__ << ": force channels = to be AUDIO_CHANNEL_OUT_STEREO";
|
||||
}
|
||||
if (!checkConfigParams(config)) {
|
||||
LOG(ERROR) << __func__ << " checkConfigParams failed";
|
||||
return ::android::UNKNOWN_ERROR;
|
||||
}
|
||||
mBtDeviceProxies.push_back(std::move(proxy));
|
||||
}
|
||||
mIsInitialized = true;
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
bool StreamBluetooth::checkConfigParams(
|
||||
::aidl::android::hardware::bluetooth::audio::PcmConfiguration& config) {
|
||||
if ((int)mSampleRate != config.sampleRateHz) {
|
||||
LOG(ERROR) << __func__ << ": Sample Rate mismatch, stream val = " << mSampleRate
|
||||
<< " hal val = " << config.sampleRateHz;
|
||||
return false;
|
||||
}
|
||||
auto channelCount = aidl::android::hardware::audio::common::getChannelCount(mChannelLayout);
|
||||
if ((config.channelMode == ChannelMode::MONO && channelCount != 1) ||
|
||||
(config.channelMode == ChannelMode::STEREO && channelCount != 2)) {
|
||||
LOG(ERROR) << __func__ << ": Channel count mismatch, stream val = " << channelCount
|
||||
<< " hal val = " << toString(config.channelMode);
|
||||
return false;
|
||||
}
|
||||
if (mFormat.type != AudioFormatType::PCM) {
|
||||
LOG(ERROR) << __func__ << ": unexpected format type "
|
||||
<< aidl::android::media::audio::common::toString(mFormat.type);
|
||||
return false;
|
||||
}
|
||||
int8_t bps = aidl::android::hardware::audio::common::getPcmSampleSizeInBytes(mFormat.pcm) * 8;
|
||||
if (bps != config.bitsPerSample) {
|
||||
LOG(ERROR) << __func__ << ": bits per sample mismatch, stream val = " << bps
|
||||
<< " hal val = " << config.bitsPerSample;
|
||||
return false;
|
||||
}
|
||||
if (config.dataIntervalUs > 0) {
|
||||
mPreferredDataIntervalUs =
|
||||
std::min((int32_t)mPreferredDataIntervalUs, config.dataIntervalUs);
|
||||
mPreferredFrameCount = getFrameCount(mPreferredDataIntervalUs, mSampleRate);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus StreamBluetooth::prepareToClose() {
|
||||
std::lock_guard guard(mLock);
|
||||
mIsReadyToClose = true;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
::android::status_t StreamBluetooth::standby() {
|
||||
std::lock_guard guard(mLock);
|
||||
if (!mIsInitialized) {
|
||||
if (auto status = initialize(); status != ::android::OK) return status;
|
||||
}
|
||||
for (auto proxy : mBtDeviceProxies) {
|
||||
if (!proxy->suspend()) {
|
||||
LOG(ERROR) << __func__ << ": state = " << proxy->getState() << " failed to stand by ";
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
::android::status_t StreamBluetooth::start() {
|
||||
std::lock_guard guard(mLock);
|
||||
if (!mIsInitialized) return initialize();
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
void StreamBluetooth::shutdown() {
|
||||
std::lock_guard guard(mLock);
|
||||
for (auto proxy : mBtDeviceProxies) {
|
||||
proxy->stop();
|
||||
proxy->unregisterPort();
|
||||
}
|
||||
mBtDeviceProxies.clear();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus StreamBluetooth::updateMetadataCommon(const Metadata& metadata) {
|
||||
std::lock_guard guard(mLock);
|
||||
if (!mIsInitialized) return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
bool isOk = true;
|
||||
if (isInput(metadata)) {
|
||||
isOk = mBtDeviceProxies[0]->updateSinkMetadata(std::get<SinkMetadata>(metadata));
|
||||
} else {
|
||||
for (auto proxy : mBtDeviceProxies) {
|
||||
if (!proxy->updateSourceMetadata(std::get<SourceMetadata>(metadata))) isOk = false;
|
||||
}
|
||||
}
|
||||
return isOk ? ndk::ScopedAStatus::ok()
|
||||
: ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus StreamBluetooth::bluetoothParametersUpdated() {
|
||||
if (mIsInput) {
|
||||
LOG(WARNING) << __func__ << ": not handled";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
auto applyParam = [](const std::shared_ptr<BluetoothAudioPortAidl>& proxy,
|
||||
bool isEnabled) -> bool {
|
||||
if (!isEnabled) {
|
||||
if (proxy->suspend()) return proxy->setState(BluetoothStreamState::DISABLED);
|
||||
return false;
|
||||
}
|
||||
return proxy->standby();
|
||||
};
|
||||
bool hasA2dpParam, enableA2dp;
|
||||
auto btA2dp = mBluetoothA2dp.lock();
|
||||
hasA2dpParam = btA2dp != nullptr && btA2dp->isEnabled(&enableA2dp).isOk();
|
||||
bool hasLeParam, enableLe;
|
||||
auto btLe = mBluetoothLe.lock();
|
||||
hasLeParam = btLe != nullptr && btLe->isEnabled(&enableLe).isOk();
|
||||
std::unique_lock lock(mLock);
|
||||
::android::base::ScopedLockAssertion lock_assertion(mLock);
|
||||
if (!mIsInitialized) {
|
||||
LOG(WARNING) << __func__ << ": init not done";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
for (auto proxy : mBtDeviceProxies) {
|
||||
if ((hasA2dpParam && proxy->isA2dp() && !applyParam(proxy, enableA2dp)) ||
|
||||
(hasLeParam && proxy->isLeAudio() && !applyParam(proxy, enableLe))) {
|
||||
LOG(DEBUG) << __func__ << ": applyParam failed";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
StreamInBluetooth::StreamInBluetooth(StreamContext&& context, const SinkMetadata& sinkMetadata,
|
||||
const std::vector<MicrophoneInfo>& microphones,
|
||||
Module::BtProfileHandles&& btProfileHandles)
|
||||
: StreamIn(std::move(context), microphones),
|
||||
StreamBluetooth(&(StreamIn::mContext), sinkMetadata, std::move(btProfileHandles)) {}
|
||||
|
||||
ndk::ScopedAStatus StreamInBluetooth::getActiveMicrophones(
|
||||
std::vector<MicrophoneDynamicInfo>* _aidl_return __unused) {
|
||||
LOG(DEBUG) << __func__ << ": not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
StreamOutBluetooth::StreamOutBluetooth(StreamContext&& context,
|
||||
const SourceMetadata& sourceMetadata,
|
||||
const std::optional<AudioOffloadInfo>& offloadInfo,
|
||||
Module::BtProfileHandles&& btProfileHandles)
|
||||
: StreamOut(std::move(context), offloadInfo),
|
||||
StreamBluetooth(&(StreamOut::mContext), sourceMetadata, std::move(btProfileHandles)) {}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
@@ -22,6 +22,15 @@
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
class ParamChangeHandler {
|
||||
public:
|
||||
ParamChangeHandler() = default;
|
||||
void registerHandler(std::function<ndk::ScopedAStatus()> handler) { mHandler = handler; }
|
||||
|
||||
protected:
|
||||
std::function<ndk::ScopedAStatus()> mHandler = nullptr;
|
||||
};
|
||||
|
||||
class Bluetooth : public BnBluetooth {
|
||||
public:
|
||||
Bluetooth();
|
||||
@@ -34,7 +43,7 @@ class Bluetooth : public BnBluetooth {
|
||||
HfpConfig mHfpConfig;
|
||||
};
|
||||
|
||||
class BluetoothA2dp : public BnBluetoothA2dp {
|
||||
class BluetoothA2dp : public BnBluetoothA2dp, public ParamChangeHandler {
|
||||
public:
|
||||
BluetoothA2dp() = default;
|
||||
|
||||
@@ -49,7 +58,7 @@ class BluetoothA2dp : public BnBluetoothA2dp {
|
||||
bool mEnabled = false;
|
||||
};
|
||||
|
||||
class BluetoothLe : public BnBluetoothLe {
|
||||
class BluetoothLe : public BnBluetoothLe, public ParamChangeHandler {
|
||||
public:
|
||||
BluetoothLe() = default;
|
||||
|
||||
|
||||
@@ -47,5 +47,6 @@ std::unique_ptr<Configuration> getPrimaryConfiguration();
|
||||
std::unique_ptr<Configuration> getRSubmixConfiguration();
|
||||
std::unique_ptr<Configuration> getStubConfiguration();
|
||||
std::unique_ptr<Configuration> getUsbConfiguration();
|
||||
std::unique_ptr<Configuration> getBluetoothConfiguration();
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core::internal
|
||||
|
||||
242
audio/aidl/default/include/core-impl/DevicePortProxy.h
Normal file
242
audio/aidl/default/include/core-impl/DevicePortProxy.h
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright 2023 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 <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include <android-base/thread_annotations.h>
|
||||
|
||||
#include <aidl/android/hardware/audio/common/SinkMetadata.h>
|
||||
#include <aidl/android/hardware/audio/common/SourceMetadata.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/BluetoothAudioStatus.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/PcmConfiguration.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
|
||||
#include <aidl/android/media/audio/common/AudioDeviceDescription.h>
|
||||
|
||||
#include "BluetoothAudioSessionControl.h"
|
||||
|
||||
namespace android::bluetooth::audio::aidl {
|
||||
|
||||
enum class BluetoothStreamState : uint8_t {
|
||||
DISABLED = 0, // This stream is closing or Bluetooth profiles (A2DP/LE) is disabled
|
||||
STANDBY,
|
||||
STARTING,
|
||||
STARTED,
|
||||
SUSPENDING,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state);
|
||||
|
||||
/**
|
||||
* Proxy for Bluetooth Audio HW Module to communicate with Bluetooth Audio
|
||||
* Session Control. All methods are not thread safe, so users must acquire a
|
||||
* lock. Note: currently, getState() of DevicePortProxy is only used for
|
||||
* verbose logging, it is not locked, so the state may not be synchronized.
|
||||
*/
|
||||
class BluetoothAudioPort {
|
||||
public:
|
||||
BluetoothAudioPort() = default;
|
||||
virtual ~BluetoothAudioPort() = default;
|
||||
|
||||
/**
|
||||
* Fetch output control / data path of BluetoothAudioPort and setup
|
||||
* callbacks into BluetoothAudioProvider. If registerPort() returns false, the audio
|
||||
* HAL must delete this BluetoothAudioPort and return EINVAL to caller
|
||||
*/
|
||||
virtual bool registerPort(
|
||||
const ::aidl::android::media::audio::common::AudioDeviceDescription&) = 0;
|
||||
|
||||
/**
|
||||
* Unregister this BluetoothAudioPort from BluetoothAudioSessionControl.
|
||||
* Audio HAL must delete this BluetoothAudioPort after calling this.
|
||||
*/
|
||||
virtual void unregisterPort() = 0;
|
||||
|
||||
/**
|
||||
* When the Audio framework / HAL tries to query audio config about format,
|
||||
* channel mask and sample rate, it uses this function to fetch from the
|
||||
* Bluetooth stack
|
||||
*/
|
||||
virtual bool loadAudioConfig(
|
||||
::aidl::android::hardware::bluetooth::audio::PcmConfiguration*) const = 0;
|
||||
|
||||
/**
|
||||
* WAR to support Mono mode / 16 bits per sample
|
||||
*/
|
||||
virtual void forcePcmStereoToMono(bool) = 0;
|
||||
|
||||
/**
|
||||
* When the Audio framework / HAL wants to change the stream state, it invokes
|
||||
* these 4 functions to control the Bluetooth stack (Audio Control Path).
|
||||
* Note: standby(), start() and suspend() will return true when there are no errors.
|
||||
|
||||
* Called by Audio framework / HAL to change the state to stand by. When A2DP/LE profile is
|
||||
* disabled, the port is first set to STANDBY by calling suspend and then mState is set to
|
||||
* DISABLED. To reset the state back to STANDBY this method is called.
|
||||
*/
|
||||
virtual bool standby() = 0;
|
||||
|
||||
/**
|
||||
* Called by Audio framework / HAL to start the stream
|
||||
*/
|
||||
virtual bool start() = 0;
|
||||
|
||||
/**
|
||||
* Called by Audio framework / HAL to suspend the stream
|
||||
*/
|
||||
virtual bool suspend() = 0;
|
||||
|
||||
/**
|
||||
* Called by Audio framework / HAL to stop the stream
|
||||
*/
|
||||
virtual void stop() = 0;
|
||||
|
||||
/**
|
||||
* Called by the Audio framework / HAL to fetch information about audio frames
|
||||
* presented to an external sink, or frames presented fror an internal sink
|
||||
*/
|
||||
virtual bool getPresentationPosition(
|
||||
::aidl::android::hardware::bluetooth::audio::PresentationPosition&) const = 0;
|
||||
|
||||
/**
|
||||
* Called by the Audio framework / HAL when the metadata of the stream's
|
||||
* source has been changed.
|
||||
*/
|
||||
virtual bool updateSourceMetadata(
|
||||
const ::aidl::android::hardware::audio::common::SourceMetadata&) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the Audio framework / HAL when the metadata of the stream's
|
||||
* sink has been changed.
|
||||
*/
|
||||
virtual bool updateSinkMetadata(
|
||||
const ::aidl::android::hardware::audio::common::SinkMetadata&) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current BluetoothStreamState
|
||||
*/
|
||||
virtual BluetoothStreamState getState() const = 0;
|
||||
|
||||
/**
|
||||
* Set the current BluetoothStreamState
|
||||
*/
|
||||
virtual bool setState(BluetoothStreamState) = 0;
|
||||
|
||||
virtual bool isA2dp() const = 0;
|
||||
|
||||
virtual bool isLeAudio() const = 0;
|
||||
|
||||
virtual bool getPreferredDataIntervalUs(size_t*) const = 0;
|
||||
|
||||
virtual size_t writeData(const void*, size_t) const { return 0; }
|
||||
|
||||
virtual size_t readData(void*, size_t) const { return 0; }
|
||||
};
|
||||
|
||||
class BluetoothAudioPortAidl : public BluetoothAudioPort {
|
||||
public:
|
||||
BluetoothAudioPortAidl();
|
||||
virtual ~BluetoothAudioPortAidl();
|
||||
|
||||
bool registerPort(const ::aidl::android::media::audio::common::AudioDeviceDescription&
|
||||
description) override;
|
||||
|
||||
void unregisterPort() override;
|
||||
|
||||
bool loadAudioConfig(::aidl::android::hardware::bluetooth::audio::PcmConfiguration* audio_cfg)
|
||||
const override;
|
||||
|
||||
void forcePcmStereoToMono(bool force) override { mIsStereoToMono = force; }
|
||||
|
||||
bool standby() override;
|
||||
bool start() override;
|
||||
bool suspend() override;
|
||||
void stop() override;
|
||||
|
||||
bool getPresentationPosition(::aidl::android::hardware::bluetooth::audio::PresentationPosition&
|
||||
presentation_position) const override;
|
||||
|
||||
bool updateSourceMetadata(const ::aidl::android::hardware::audio::common::SourceMetadata&
|
||||
sourceMetadata) const override;
|
||||
|
||||
bool updateSinkMetadata(const ::aidl::android::hardware::audio::common::SinkMetadata&
|
||||
sinkMetadata) const override;
|
||||
|
||||
/**
|
||||
* Return the current BluetoothStreamState
|
||||
* Note: This method is used for logging, does not lock, so value returned may not be latest
|
||||
*/
|
||||
BluetoothStreamState getState() const override NO_THREAD_SAFETY_ANALYSIS;
|
||||
|
||||
bool setState(BluetoothStreamState state) override;
|
||||
|
||||
bool isA2dp() const override;
|
||||
|
||||
bool isLeAudio() const override;
|
||||
|
||||
bool getPreferredDataIntervalUs(size_t* interval_us) const override;
|
||||
|
||||
protected:
|
||||
uint16_t mCookie;
|
||||
BluetoothStreamState mState GUARDED_BY(mCvMutex);
|
||||
::aidl::android::hardware::bluetooth::audio::SessionType mSessionType;
|
||||
// WR to support Mono: True if fetching Stereo and mixing into Mono
|
||||
bool mIsStereoToMono = false;
|
||||
|
||||
bool inUse() const;
|
||||
|
||||
std::string debugMessage() const;
|
||||
|
||||
private:
|
||||
// start()/suspend() report state change status via callback. Wait until kMaxWaitingTimeMs or a
|
||||
// state change after a call to start()/suspend() and analyse the returned status. Below mutex,
|
||||
// conditional variable serves this purpose.
|
||||
mutable std::mutex mCvMutex;
|
||||
std::condition_variable mInternalCv GUARDED_BY(mCvMutex);
|
||||
|
||||
// Check and initialize session type for |devices| If failed, this
|
||||
// BluetoothAudioPortAidl is not initialized and must be deleted.
|
||||
bool initSessionType(
|
||||
const ::aidl::android::media::audio::common::AudioDeviceDescription& description);
|
||||
|
||||
bool condWaitState(BluetoothStreamState state);
|
||||
|
||||
void controlResultHandler(
|
||||
uint16_t cookie,
|
||||
const ::aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus& status);
|
||||
void sessionChangedHandler(uint16_t cookie);
|
||||
};
|
||||
|
||||
class BluetoothAudioPortAidlOut : public BluetoothAudioPortAidl {
|
||||
public:
|
||||
// The audio data path to the Bluetooth stack (Software encoding)
|
||||
size_t writeData(const void* buffer, size_t bytes) const override;
|
||||
};
|
||||
|
||||
class BluetoothAudioPortAidlIn : public BluetoothAudioPortAidl {
|
||||
public:
|
||||
// The audio data path from the Bluetooth stack (Software decoded)
|
||||
size_t readData(void* buffer, size_t bytes) const override;
|
||||
};
|
||||
|
||||
} // namespace android::bluetooth::audio::aidl
|
||||
@@ -33,12 +33,17 @@ class Module : public BnModule {
|
||||
public:
|
||||
// This value is used for all AudioPatches and reported by all streams.
|
||||
static constexpr int32_t kLatencyMs = 10;
|
||||
enum Type : int { DEFAULT, R_SUBMIX, STUB, USB };
|
||||
enum Type : int { DEFAULT, R_SUBMIX, STUB, USB, BLUETOOTH };
|
||||
enum BtInterface : int { BTCONF, BTA2DP, BTLE };
|
||||
|
||||
static std::shared_ptr<Module> createInstance(Type type);
|
||||
|
||||
explicit Module(Type type) : mType(type) {}
|
||||
|
||||
typedef std::tuple<std::weak_ptr<IBluetooth>, std::weak_ptr<IBluetoothA2dp>,
|
||||
std::weak_ptr<IBluetoothLe>>
|
||||
BtProfileHandles;
|
||||
|
||||
protected:
|
||||
// The vendor extension done via inheritance can override interface methods and augment
|
||||
// a call to the base implementation.
|
||||
@@ -185,6 +190,7 @@ class Module : public BnModule {
|
||||
virtual std::unique_ptr<internal::Configuration> initializeConfig();
|
||||
|
||||
// Utility and helper functions accessible to subclasses.
|
||||
ndk::ScopedAStatus bluetoothParametersUpdated();
|
||||
void cleanUpPatch(int32_t patchId);
|
||||
ndk::ScopedAStatus createStreamContext(
|
||||
int32_t in_portConfigId, int64_t in_bufferSizeFrames,
|
||||
@@ -196,6 +202,7 @@ class Module : public BnModule {
|
||||
std::set<int32_t> findConnectedPortConfigIds(int32_t portConfigId);
|
||||
ndk::ScopedAStatus findPortIdForNewStream(
|
||||
int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port);
|
||||
virtual BtProfileHandles getBtProfileManagerHandles();
|
||||
internal::Configuration& getConfig();
|
||||
const ConnectedDevicePorts& getConnectedDevicePorts() const { return mConnectedDevicePorts; }
|
||||
bool getMasterMute() const { return mMasterMute; }
|
||||
|
||||
54
audio/aidl/default/include/core-impl/ModuleBluetooth.h
Normal file
54
audio/aidl/default/include/core-impl/ModuleBluetooth.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 "core-impl/Bluetooth.h"
|
||||
#include "core-impl/Module.h"
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
class ModuleBluetooth final : public Module {
|
||||
public:
|
||||
ModuleBluetooth() : Module(Type::BLUETOOTH) {}
|
||||
|
||||
private:
|
||||
BtProfileHandles getBtProfileManagerHandles() override;
|
||||
|
||||
ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) override;
|
||||
ndk::ScopedAStatus getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) override;
|
||||
ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
|
||||
ndk::ScopedAStatus setMicMute(bool in_mute) override;
|
||||
|
||||
ndk::ScopedAStatus createInputStream(
|
||||
StreamContext&& context,
|
||||
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
|
||||
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
|
||||
std::shared_ptr<StreamIn>* result) override;
|
||||
ndk::ScopedAStatus createOutputStream(
|
||||
StreamContext&& context,
|
||||
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
|
||||
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
|
||||
offloadInfo,
|
||||
std::shared_ptr<StreamOut>* result) override;
|
||||
ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
|
||||
ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
|
||||
|
||||
ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
|
||||
ChildInterface<IBluetoothLe> mBluetoothLe;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
@@ -354,6 +354,7 @@ struct StreamCommonInterface {
|
||||
virtual const ConnectedDevices& getConnectedDevices() const = 0;
|
||||
virtual ndk::ScopedAStatus setConnectedDevices(
|
||||
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) = 0;
|
||||
virtual ndk::ScopedAStatus bluetoothParametersUpdated() = 0;
|
||||
};
|
||||
|
||||
// This is equivalent to automatically generated 'IStreamCommonDelegator' but uses
|
||||
@@ -454,6 +455,7 @@ class StreamCommonImpl : virtual public StreamCommonInterface, virtual public Dr
|
||||
ndk::ScopedAStatus setConnectedDevices(
|
||||
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
|
||||
override;
|
||||
ndk::ScopedAStatus bluetoothParametersUpdated() override;
|
||||
|
||||
protected:
|
||||
static StreamWorkerInterface::CreateInstance getDefaultInWorkerCreator() {
|
||||
@@ -584,6 +586,11 @@ class StreamWrapper {
|
||||
if (s) return s->setConnectedDevices(devices);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
ndk::ScopedAStatus bluetoothParametersUpdated() {
|
||||
auto s = mStream.lock();
|
||||
if (s) return s->bluetoothParametersUpdated();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<StreamCommonInterface> mStream;
|
||||
@@ -612,6 +619,14 @@ class Streams {
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
ndk::ScopedAStatus bluetoothParametersUpdated() {
|
||||
bool isOk = true;
|
||||
for (auto& it : mStreams) {
|
||||
if (!it.second.bluetoothParametersUpdated().isOk()) isOk = false;
|
||||
}
|
||||
return isOk ? ndk::ScopedAStatus::ok()
|
||||
: ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
private:
|
||||
// Maps port ids and port config ids to streams. Multimap because a port
|
||||
|
||||
106
audio/aidl/default/include/core-impl/StreamBluetooth.h
Normal file
106
audio/aidl/default/include/core-impl/StreamBluetooth.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 <vector>
|
||||
|
||||
#include <aidl/android/hardware/audio/core/IBluetooth.h>
|
||||
#include <aidl/android/hardware/audio/core/IBluetoothA2dp.h>
|
||||
#include <aidl/android/hardware/audio/core/IBluetoothLe.h>
|
||||
|
||||
#include "core-impl/DevicePortProxy.h"
|
||||
#include "core-impl/Module.h"
|
||||
#include "core-impl/Stream.h"
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
class StreamBluetooth : public StreamCommonImpl {
|
||||
public:
|
||||
StreamBluetooth(StreamContext* context, const Metadata& metadata,
|
||||
Module::BtProfileHandles&& btHandles);
|
||||
// Methods of 'DriverInterface'.
|
||||
::android::status_t init() override;
|
||||
::android::status_t drain(StreamDescriptor::DrainMode) override;
|
||||
::android::status_t flush() override;
|
||||
::android::status_t pause() override;
|
||||
::android::status_t standby() override;
|
||||
::android::status_t start() override;
|
||||
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
|
||||
int32_t* latencyMs) override;
|
||||
void shutdown() override;
|
||||
|
||||
// Overridden methods of 'StreamCommonImpl', called on a Binder thread.
|
||||
ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) override;
|
||||
ndk::ScopedAStatus prepareToClose() override;
|
||||
const ConnectedDevices& getConnectedDevices() const override;
|
||||
ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override;
|
||||
ndk::ScopedAStatus bluetoothParametersUpdated() override;
|
||||
|
||||
private:
|
||||
// Audio Pcm Config
|
||||
const uint32_t mSampleRate;
|
||||
const ::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout;
|
||||
const ::aidl::android::media::audio::common::AudioFormatDescription mFormat;
|
||||
const size_t mFrameSizeBytes;
|
||||
const bool mIsInput;
|
||||
const std::weak_ptr<IBluetoothA2dp> mBluetoothA2dp;
|
||||
const std::weak_ptr<IBluetoothLe> mBluetoothLe;
|
||||
size_t mPreferredDataIntervalUs;
|
||||
size_t mPreferredFrameCount;
|
||||
|
||||
mutable std::mutex mLock;
|
||||
bool mIsInitialized GUARDED_BY(mLock);
|
||||
bool mIsReadyToClose GUARDED_BY(mLock);
|
||||
std::vector<std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl>>
|
||||
mBtDeviceProxies GUARDED_BY(mLock);
|
||||
|
||||
::android::status_t initialize() REQUIRES(mLock);
|
||||
bool checkConfigParams(::aidl::android::hardware::bluetooth::audio::PcmConfiguration& config);
|
||||
};
|
||||
|
||||
class StreamInBluetooth final : public StreamIn, public StreamBluetooth {
|
||||
public:
|
||||
friend class ndk::SharedRefBase;
|
||||
StreamInBluetooth(
|
||||
StreamContext&& context,
|
||||
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
|
||||
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
|
||||
Module::BtProfileHandles&& btHandles);
|
||||
|
||||
private:
|
||||
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
|
||||
ndk::ScopedAStatus getActiveMicrophones(
|
||||
std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
|
||||
override;
|
||||
};
|
||||
|
||||
class StreamOutBluetooth final : public StreamOut, public StreamBluetooth {
|
||||
public:
|
||||
friend class ndk::SharedRefBase;
|
||||
StreamOutBluetooth(
|
||||
StreamContext&& context,
|
||||
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
|
||||
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
|
||||
offloadInfo,
|
||||
Module::BtProfileHandles&& btHandles);
|
||||
|
||||
private:
|
||||
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
@@ -129,6 +129,7 @@ class StreamSwitcher : virtual public StreamCommonInterface {
|
||||
ndk::ScopedAStatus setConnectedDevices(
|
||||
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
|
||||
override;
|
||||
ndk::ScopedAStatus bluetoothParametersUpdated() override;
|
||||
|
||||
protected:
|
||||
// Since switching a stream requires closing down the current stream, StreamSwitcher
|
||||
@@ -187,6 +188,7 @@ class StreamSwitcher : virtual public StreamCommonInterface {
|
||||
std::optional<int32_t> mHwAvSyncId;
|
||||
std::vector<VndParam> mMissedParameters;
|
||||
std::vector<std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>> mEffects;
|
||||
bool mBluetoothParametersUpdated = false;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
||||
@@ -65,7 +65,8 @@ int main() {
|
||||
return std::make_pair(module, moduleBinder);
|
||||
};
|
||||
auto modules = {createModule(Module::Type::DEFAULT), createModule(Module::Type::R_SUBMIX),
|
||||
createModule(Module::Type::USB), createModule(Module::Type::STUB)};
|
||||
createModule(Module::Type::USB), createModule(Module::Type::STUB),
|
||||
createModule(Module::Type::BLUETOOTH)};
|
||||
(void)modules;
|
||||
|
||||
ABinderProcess_joinThreadPool();
|
||||
|
||||
Reference in New Issue
Block a user