aosp aidl bluetooth audio hal implementation
Bug: 228804498 Test: atest VtsHalAudioCoreTargetTest Change-Id: Id5fb60fe53ead9f5d2e6ecbb9988a23835cf2509
This commit is contained in:
parent
b93626c412
commit
b03b5c4a06
13 changed files with 1408 additions and 2 deletions
|
@ -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",
|
||||
],
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
562
audio/aidl/default/bluetooth/DevicePortProxy.cpp
Normal file
562
audio/aidl/default/bluetooth/DevicePortProxy.cpp
Normal file
|
@ -0,0 +1,562 @@
|
|||
/*
|
||||
* 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::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
|
65
audio/aidl/default/bluetooth/ModuleBluetooth.cpp
Normal file
65
audio/aidl/default/bluetooth/ModuleBluetooth.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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::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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
298
audio/aidl/default/bluetooth/StreamBluetooth.cpp
Normal file
298
audio/aidl/default/bluetooth/StreamBluetooth.cpp
Normal file
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
* 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)
|
||||
: StreamCommonImpl(context, metadata),
|
||||
mSampleRate(getContext().getSampleRate()),
|
||||
mChannelLayout(getContext().getChannelLayout()),
|
||||
mFormat(getContext().getFormat()),
|
||||
mFrameSizeBytes(getContext().getFrameSize()),
|
||||
mIsInput(isInput(metadata)) {
|
||||
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);
|
||||
}
|
||||
|
||||
StreamInBluetooth::StreamInBluetooth(StreamContext&& context, const SinkMetadata& sinkMetadata,
|
||||
const std::vector<MicrophoneInfo>& microphones)
|
||||
: StreamIn(std::move(context), microphones),
|
||||
StreamBluetooth(&(StreamIn::mContext), sinkMetadata) {}
|
||||
|
||||
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)
|
||||
: StreamOut(std::move(context), offloadInfo),
|
||||
StreamBluetooth(&(StreamOut::mContext), sourceMetadata) {}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
|
@ -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
|
||||
|
|
233
audio/aidl/default/include/core-impl/DevicePortProxy.h
Normal file
233
audio/aidl/default/include/core-impl/DevicePortProxy.h
Normal file
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* 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 3 functions to control the Bluetooth stack (Audio Control Path).
|
||||
* Note: Both start() and suspend() will return true when there are no errors.
|
||||
* 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 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,7 +33,7 @@ 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 };
|
||||
|
||||
static std::shared_ptr<Module> createInstance(Type type);
|
||||
|
||||
|
|
46
audio/aidl/default/include/core-impl/ModuleBluetooth.h
Normal file
46
audio/aidl/default/include/core-impl/ModuleBluetooth.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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/Module.h"
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
class ModuleBluetooth final : public Module {
|
||||
public:
|
||||
ModuleBluetooth() : Module(Type::BLUETOOTH) {}
|
||||
|
||||
private:
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
97
audio/aidl/default/include/core-impl/StreamBluetooth.h
Normal file
97
audio/aidl/default/include/core-impl/StreamBluetooth.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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 "core-impl/DevicePortProxy.h"
|
||||
#include "core-impl/Stream.h"
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
class StreamBluetooth : public StreamCommonImpl {
|
||||
public:
|
||||
StreamBluetooth(StreamContext* context, const Metadata& metadata);
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
|
||||
};
|
||||
|
||||
} // 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();
|
||||
|
|
Loading…
Reference in a new issue