Files
pawlet_rpi4/audio/bluetooth/DevicePortProxy.cpp
T
oxmc 8d5f56fd05 Sync to upstream android-17.0 (AOSP17)
Pull in raspberry-vanilla's AOSP17 device tree changes for rpi4 while
keeping all PawletOS customizations (A/B partition layout, sepolicy,
zram, TWRP recovery scaffolding, ALSA HDMI pre-warm, product/overlay
structure) intact on top.

Upstream changes absorbed:
- AOSP17 audio HAL refactor (StreamAlsa split into StreamAlsaBase/
  StreamAlsaMonoPipe, Utils.cpp -> UtilsAlsa.cpp, new Bluetooth/stub
  helper classes, libeffects AIDL rebuild via *_ndk_shared defaults)
- API level 36 -> 37, FCM matrix level 202504 -> 202604
- QuickSettings tile default split (SystemUIRpiOverlay, ported into
  pawlet_rpi_common where this overlay now lives)
- vendor_tracing_descriptors added to enforce-product-packages-exist
- misc AOSP17 logging header switch (android-base/logging.h -> Log.h)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 00:16:17 -07:00

855 lines
36 KiB
C++

/*
* 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_BluetoothAudioPort"
#include <Log.h>
#include <android-base/stringprintf.h>
#include <audio_utils/primitives.h>
#include <log/log.h>
#include "BluetoothAudioSessionControl.h"
#include "core-impl/DevicePortProxy.h"
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::LatencyMode;
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;
namespace android::bluetooth::audio::aidl {
class BluetoothSession {
public:
explicit BluetoothSession(SessionType session_type) : mSessionType(session_type) {}
uint16_t registerControlResultCback(const PortStatusCallbacks& cbacks) {
return BluetoothAudioSessionControl::RegisterControlResultCback(mSessionType, cbacks);
}
void unregisterControlResultCback(uint16_t cookie) {
BluetoothAudioSessionControl::UnregisterControlResultCback(mSessionType, cookie);
}
bool isSessionReady(bool is_primary_hal = true) const {
return BluetoothAudioSessionControl::IsSessionReady(mSessionType, is_primary_hal);
}
const AudioConfiguration getAudioConfig() const {
return BluetoothAudioSessionControl::GetAudioConfig(mSessionType);
}
std::vector<LatencyMode> getSupportedLatencyModes() const {
return BluetoothAudioSessionControl::GetSupportedLatencyModes(mSessionType);
}
void setLatencyMode(LatencyMode latency_mode) const {
BluetoothAudioSessionControl::SetLatencyMode(mSessionType, latency_mode);
}
bool startStream(bool low_latency) const {
return BluetoothAudioSessionControl::StartStream(mSessionType, low_latency);
}
bool suspendStream() const { return BluetoothAudioSessionControl::SuspendStream(mSessionType); }
void stopStream() const { BluetoothAudioSessionControl::StopStream(mSessionType); }
size_t outWritePcmData(const void* buffer, size_t bytes) const {
return BluetoothAudioSessionControl::OutWritePcmData(mSessionType, buffer, bytes);
}
size_t inReadPcmData(void* buffer, size_t bytes) const {
return BluetoothAudioSessionControl::InReadPcmData(mSessionType, buffer, bytes);
}
bool getPresentationPosition(PresentationPosition& presentation_position) const {
return BluetoothAudioSessionControl::GetPresentationPosition(mSessionType,
presentation_position);
}
bool updateSourceMetadata(const SourceMetadata& source_metadata) const {
return BluetoothAudioSessionControl::UpdateSourceMetadata(mSessionType, source_metadata);
}
bool updateSinkMetadata(const SinkMetadata& sink_metadata) const {
return BluetoothAudioSessionControl::UpdateSinkMetadata(mSessionType, sink_metadata);
}
SessionType getSessionType() const { return mSessionType; }
std::string toString() const {
return ::aidl::android::hardware::bluetooth::audio::toString(mSessionType);
}
bool isA2dp() const {
return mSessionType == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
mSessionType == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
}
bool isHfp() const {
switch (mSessionType) {
case SessionType::HFP_SOFTWARE_ENCODING_DATAPATH:
case SessionType::HFP_SOFTWARE_DECODING_DATAPATH:
case SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH:
return true;
default:
return false;
}
}
bool 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;
}
private:
const SessionType mSessionType;
};
namespace {
// The maximum time to wait in std::condition_variable::wait_for()
constexpr unsigned int kMaxWaitingTimeMs = 4500;
constexpr unsigned int kScoMixPortFixedSampleRate = 32000;
} // namespace
BluetoothAudioPortAidl::BluetoothAudioPortAidl(std::optional<bool> supportsLowLatency)
: mCookie(::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined),
mState(BluetoothStreamState::DISABLED),
mSupportsLowLatency(supportsLowLatency) {}
BluetoothAudioPortAidl::~BluetoothAudioPortAidl() {
unregisterPort();
}
bool BluetoothAudioPortAidl::registerPort(const AudioDeviceDescription& description) {
if (inUse()) {
LOG(ERROR) << __func__ << debugMessage() << " already in use";
return false;
}
if (!initSession(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);
};
auto low_latency_allowed_cb = [port = this](uint16_t cookie, bool allowed) {
port->lowLatencyAllowedHandler(cookie, allowed);
};
PortStatusCallbacks cbacks = {
.control_result_cb_ = control_result_cb,
.session_changed_cb_ = session_changed_cb,
.low_latency_mode_allowed_cb_ = low_latency_allowed_cb,
};
mCookie = getSession()->registerControlResultCback(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::initSession(const AudioDeviceDescription& description) {
SessionType sessionType = SessionType::UNKNOWN;
SessionType fallbackSessionType = SessionType::UNKNOWN;
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() << ")";
sessionType = SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
fallbackSessionType = SessionType::A2DP_HARDWARE_OFFLOAD_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() << ")";
sessionType = 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() << ")";
sessionType = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
fallbackSessionType = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_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() << ")";
sessionType = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
fallbackSessionType = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_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() << ")";
sessionType = 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() << ")";
sessionType = SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH;
} else if (description.connection == AudioDeviceDescription::CONNECTION_BT_SCO &&
description.type == AudioDeviceType::IN_HEADSET) {
LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_IN_SCO_HEADSET (VOICE) ("
<< description.toString() << ")";
sessionType = SessionType::HFP_SOFTWARE_DECODING_DATAPATH;
} else if (description.connection == AudioDeviceDescription::CONNECTION_BT_SCO &&
(description.type == AudioDeviceType::OUT_DEVICE ||
description.type == AudioDeviceType::OUT_HEADSET ||
description.type == AudioDeviceType::OUT_CARKIT)) {
LOG(VERBOSE) << __func__ << ": device=AUDIO_DEVICE_OUT_SCO (HEADSET/OUT_CARKIT) ("
<< description.toString() << ")";
sessionType = SessionType::HFP_SOFTWARE_ENCODING_DATAPATH;
} else {
LOG(ERROR) << __func__ << ": unknown device=" << description.toString();
return false;
}
if (!BluetoothAudioSessionControl::IsSessionReady(sessionType)) {
if (fallbackSessionType != SessionType::UNKNOWN) {
LOG(WARNING) << __func__
<< ": Retry fallback session_type=" << toString(fallbackSessionType)
<< " for session_type=" << toString(sessionType);
if (BluetoothAudioSessionControl::IsSessionReady(fallbackSessionType, false)) {
mSession = std::make_unique<BluetoothSession>(fallbackSessionType);
return true;
} else {
LOG(ERROR) << __func__
<< ": fallback session_type=" << toString(fallbackSessionType)
<< " is not ready";
}
}
LOG(ERROR) << __func__ << ": device=" << description.toString()
<< ", session_type=" << toString(sessionType) << " is not ready";
return false;
}
mSession = std::make_unique<BluetoothSession>(sessionType);
return true;
}
void BluetoothAudioPortAidl::unregisterPort() {
if (!inUse()) {
LOG(WARNING) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return;
}
getSession()->unregisterControlResultCback(mCookie);
mCookie = ::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined;
mSession.reset();
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 is corrupted "
<< "cookie=" << StringPrintf("%#hx", cookie) << ", expected "
<< StringPrintf("%#hx", mCookie);
return;
}
BluetoothStreamState previous_state = mState;
::android::base::LogSeverity severity = ::android::base::FATAL;
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;
severity = ::android::base::INFO;
} else {
severity = ::android::base::WARNING;
}
break;
case BluetoothStreamState::STARTING:
if (status == BluetoothAudioStatus::SUCCESS) {
mState = BluetoothStreamState::STARTED;
severity = ::android::base::INFO;
} else {
// Set to standby since the stack may be busy switching between outputs
mState = BluetoothStreamState::STANDBY;
severity = ::android::base::WARNING;
}
break;
case BluetoothStreamState::SUSPENDING:
if (status == BluetoothAudioStatus::SUCCESS) {
mState = BluetoothStreamState::STANDBY;
severity = ::android::base::INFO;
} else {
// Will fail if the headset is disconnecting, so set to disable
// to wait for re-init again
mState = BluetoothStreamState::DISABLED;
severity = ::android::base::WARNING;
}
break;
default:
severity = ::android::base::ERROR;
}
if (previous_state != mState) {
LOG(severity) << "control_result_cb" << debugMessage() << ", status=" << toString(status)
<< ", " << previous_state << " -> " << mState;
} else {
LOG(severity) << "control_result_cb" << debugMessage() << ", status=" << toString(status)
<< ", " << previous_state;
}
if (severity != ::android::base::ERROR) {
mInternalCv.notify_all();
}
}
void BluetoothAudioPortAidl::lowLatencyAllowedHandler(uint16_t cookie, bool allowed) {
if (mCookie != cookie) {
LOG(ERROR) << "low_latency_allowed_cb: proxy of device port (cookie="
<< StringPrintf("%#hx", cookie) << ") is corrupted";
return;
}
LOG(INFO) << "low_latency_allowed_cb:" << debugMessage() << ", allowed=" << allowed;
std::vector<LatencyMode> latency_modes;
if (!getRecommendedLatencyModes(&latency_modes)) return;
std::shared_ptr<BluetoothAudioPortCallbacks> callbacks;
{
std::lock_guard guard(mCvMutex);
callbacks = mCallbacks;
}
if (callbacks) {
callbacks->onRecommendedLatencyModeChanged(latency_modes);
}
}
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 is corrupted "
<< "cookie=" << StringPrintf("%#hx", cookie) << ", expected "
<< StringPrintf("%#hx", mCookie);
return;
}
BluetoothStreamState previous_state = mState;
mState = BluetoothStreamState::DISABLED;
LOG(DEBUG) << "session_changed_cb" << debugMessage() << ", " << previous_state << " -> "
<< mState;
mInternalCv.notify_all();
}
bool BluetoothAudioPortAidl::inUse() const {
return (mCookie != ::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined);
}
BluetoothSession* BluetoothAudioPortAidl::getSession() const {
return mSession.get();
}
bool BluetoothAudioPortAidl::getPreferredDataIntervalUs(size_t& interval_us) const {
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return false;
}
const AudioConfiguration& hal_audio_cfg = getSession()->getAudioConfig();
if (hal_audio_cfg.getTag() != AudioConfiguration::pcmConfig) {
LOG(ERROR) << __func__ << debugMessage() << ": unsupported audio cfg tag";
return false;
}
interval_us = hal_audio_cfg.get<AudioConfiguration::pcmConfig>().dataIntervalUs;
return true;
}
bool BluetoothAudioPortAidl::getRecommendedLatencyModes(std::vector<LatencyMode>* latency_modes,
std::optional<bool>* supports_low_latency) {
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return false;
}
*latency_modes = getSession()->getSupportedLatencyModes();
LOG(INFO) << __func__ << debugMessage() << ": "
<< ::android::internal::ToString(*latency_modes);
*supports_low_latency = std::find(latency_modes->begin(), latency_modes->end(),
LatencyMode::LOW_LATENCY) != latency_modes->end();
return true;
}
bool BluetoothAudioPortAidl::getRecommendedLatencyModes(std::vector<LatencyMode>* latency_modes) {
std::optional<bool> supportsLowLatency;
if (getRecommendedLatencyModes(latency_modes, &supportsLowLatency)) {
std::lock_guard guard(mCvMutex);
mSupportsLowLatency = supportsLowLatency;
return true;
}
return false;
}
bool BluetoothAudioPortAidl::loadAudioConfig(PcmConfiguration& audio_cfg) {
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return false;
}
const AudioConfiguration& hal_audio_cfg = getSession()->getAudioConfig();
if (hal_audio_cfg.getTag() != AudioConfiguration::pcmConfig) {
LOG(ERROR) << __func__ << debugMessage()
<< ": unsupported audio cfg tag: " << toString(hal_audio_cfg.getTag());
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) {
LOG(ERROR) << __func__ << debugMessage()
<< ": unsupported channel mode: " << toString(audio_cfg.channelMode);
return false;
}
if (mSession->isHfp()) {
if (audio_cfg.sampleRateHz == kScoMixPortFixedSampleRate) {
mResampler = {nullptr, nullptr};
mResampleRatio = 0;
} else {
uint32_t resampleTargetRate = audio_cfg.sampleRateHz;
switch (audio_cfg.sampleRateHz) {
case 8000:
case 16000:
case 32000:
break;
default:
LOG(FATAL) << __func__ << debugMessage()
<< ": Resample target rate must be a divisor of "
<< kScoMixPortFixedSampleRate << ", but is "
<< audio_cfg.sampleRateHz;
}
mResampleRatio = kScoMixPortFixedSampleRate / resampleTargetRate;
audio_cfg.sampleRateHz = kScoMixPortFixedSampleRate;
int in_rate, out_rate;
switch (mSession->getSessionType()) {
case SessionType::HFP_SOFTWARE_ENCODING_DATAPATH:
in_rate = kScoMixPortFixedSampleRate;
out_rate = resampleTargetRate;
break;
case SessionType::HFP_SOFTWARE_DECODING_DATAPATH:
in_rate = resampleTargetRate;
out_rate = kScoMixPortFixedSampleRate;
break;
default:
LOG(ERROR) << __func__
<< ": cannot create resampler for unsupported session type "
<< toString(mSession->getSessionType());
return false;
}
struct resampler_itfe* resampler = nullptr;
if (int rc = create_resampler(/* in_sample_rate= */ in_rate,
/* outSampleRate= */ out_rate,
/* channelCount= */ 1,
/* quality= */ RESAMPLER_QUALITY_VOIP,
/* provider= */ nullptr,
/* resampler= */ &resampler);
rc != 0) {
LOG(ERROR) << __func__ << ": failed to create resampler, rc=" << rc;
return false;
}
mResampler = {resampler, release_resampler};
LOG(DEBUG) << __func__ << ": created resampler with in_rate=" << in_rate
<< ", out_rate=" << out_rate << ", for session type "
<< toString(mSession->getSessionType());
}
}
return true;
}
bool BluetoothAudioPortAidlOut::loadAudioConfig(PcmConfiguration& audio_cfg) {
if (!BluetoothAudioPortAidl::loadAudioConfig(audio_cfg)) return false;
// WAR to support Mono / 16 bits per sample as the Bluetooth stack requires
if (audio_cfg.channelMode == ChannelMode::MONO && audio_cfg.bitsPerSample == 16) {
mIsStereoToMono = true;
audio_cfg.channelMode = ChannelMode::STEREO;
LOG(INFO) << __func__ << debugMessage()
<< ": force channels = to be AUDIO_CHANNEL_OUT_STEREO";
} else {
mIsStereoToMono = false;
}
return true;
}
bool BluetoothAudioPortAidl::standby() {
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return false;
}
std::lock_guard guard(mCvMutex);
BluetoothStreamState previous_state = mState;
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << mState << " request";
if (mState == BluetoothStreamState::DISABLED) {
mState = BluetoothStreamState::STANDBY;
LOG(INFO) << __func__ << debugMessage() << ", " << previous_state << " -> " << mState;
return true;
}
return false;
}
bool BluetoothAudioPortAidl::condWaitState(std::unique_lock<std::mutex>* lock) {
const auto waitTime = std::chrono::milliseconds(kMaxWaitingTimeMs);
const auto state = mState;
if (state == BluetoothStreamState::STARTING || state == BluetoothStreamState::SUSPENDING) {
LOG(DEBUG) << __func__ << debugMessage() << " waiting to change from " << state;
mInternalCv.wait_for(*lock, waitTime, [this, state] {
// No aliasing support in thread safety analysis, specify lock name explicitly.
base::ScopedLockAssertion lock_assertion(mCvMutex);
return mState != state;
});
const bool expected =
mState == (state == BluetoothStreamState::STARTING ? BluetoothStreamState::STARTED
: BluetoothStreamState::STANDBY);
LOG(expected ? INFO : WARNING)
<< __func__ << debugMessage() << ", " << state << " -> " << mState;
return expected;
}
LOG(ERROR) << __func__ << debugMessage() << " called to wait when in " << state;
return false;
}
bool BluetoothAudioPortAidl::start() {
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return false;
}
bool retval = false;
{
std::unique_lock lock(mCvMutex);
base::ScopedLockAssertion lock_assertion(mCvMutex);
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << mState
<< ", mono=" << (mIsStereoToMono ? "true" : "false") << " request";
if (mState == BluetoothStreamState::STARTED) {
return true; // nop, return
} else if (mState == BluetoothStreamState::DISABLED) {
return false; // avoid logspam when called from `transfer`
} else if (mState == BluetoothStreamState::SUSPENDING ||
mState == BluetoothStreamState::STARTING) {
/* If port is in transient state, give some time to respond */
if (!condWaitState(&lock)) {
LOG(ERROR) << __func__ << debugMessage() << ", state=" << mState << " failure";
return false;
}
}
if (mState == BluetoothStreamState::STARTED) {
retval = true;
} else if (mState == BluetoothStreamState::STANDBY) {
if (!mSupportsLowLatency.has_value()) {
std::vector<LatencyMode> latency_modes;
getRecommendedLatencyModes(&latency_modes, &mSupportsLowLatency);
}
const bool low_latency = mSupportsLowLatency.value_or(false);
mState = BluetoothStreamState::STARTING;
lock.unlock();
const bool startSuccess = mSession->startStream(low_latency);
lock.lock();
if (startSuccess && mState == BluetoothStreamState::STARTING) {
retval = condWaitState(&lock);
} else if (startSuccess && mState == BluetoothStreamState::STARTED) {
retval = true;
} else {
// !startSuccess => no session instance
const BluetoothStreamState newState = startSuccess ? BluetoothStreamState::STANDBY
: BluetoothStreamState::DISABLED;
LOG(ERROR) << __func__ << debugMessage() << ", startSuccess=" << startSuccess
<< ", state=" << mState << " -> " << newState;
mState = newState;
}
}
if (retval) {
LOG(INFO) << __func__ << debugMessage() << ", state=" << mState
<< ", mono=" << (mIsStereoToMono ? "true" : "false") << " done";
// Reload audio config.
PcmConfiguration pcm_config;
loadAudioConfig(pcm_config);
} else {
LOG(ERROR) << __func__ << debugMessage() << ", state=" << mState << " failure";
}
}
return retval; // false if any failure like timeout
}
bool BluetoothAudioPortAidl::suspend() {
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return false;
}
bool retval = false;
{
std::unique_lock lock(mCvMutex);
base::ScopedLockAssertion lock_assertion(mCvMutex);
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << mState << " request";
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 */
if (!condWaitState(&lock)) {
LOG(ERROR) << __func__ << debugMessage() << ", state=" << mState << " failure";
return false;
}
}
if (mState == BluetoothStreamState::STANDBY) {
retval = true;
} else if (mState == BluetoothStreamState::STARTED) {
mState = BluetoothStreamState::SUSPENDING;
lock.unlock();
const bool suspendSuccess = mSession->suspendStream();
lock.lock();
if (suspendSuccess && mState == BluetoothStreamState::SUSPENDING) {
retval = condWaitState(&lock);
} else if (suspendSuccess && mState == BluetoothStreamState::STANDBY) {
retval = true;
} else {
LOG(ERROR) << __func__ << debugMessage() << ", suspendSuccess=" << suspendSuccess
<< ", state=" << mState << " -> DISABLED";
mState = BluetoothStreamState::DISABLED;
}
}
if (retval) {
LOG(INFO) << __func__ << debugMessage() << ", state=" << mState << " done";
} else {
LOG(ERROR) << __func__ << debugMessage() << ", state=" << mState << " failure";
}
}
return retval; // false if any failure like timeout
}
void BluetoothAudioPortAidl::stop() {
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return;
}
std::unique_lock lock(mCvMutex);
base::ScopedLockAssertion lock_assertion(mCvMutex);
BluetoothStreamState previous_state = mState;
LOG(VERBOSE) << __func__ << debugMessage() << ", state=" << mState << " request";
if (mState != BluetoothStreamState::DISABLED) {
lock.unlock();
mSession->stopStream();
lock.lock();
mState = BluetoothStreamState::DISABLED;
LOG(INFO) << __func__ << debugMessage() << ", " << previous_state << " -> " << mState;
}
}
size_t BluetoothAudioPortAidlOut::writeData(const void* buffer, size_t bytes) const {
if (!buffer) {
LOG(ERROR) << __func__ << debugMessage() << ": bad input arg";
return 0;
}
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return 0;
}
size_t multiplier = 1;
std::vector<int16_t> src(static_cast<const int16_t*>(buffer),
static_cast<const int16_t*>(buffer) + bytes / 2);
if (mIsStereoToMono) {
// WAR to mix the stereo into Mono (16 bits per sample)
std::vector<int16_t> dst(src.size() / 2);
if (dst.empty()) {
LOG(ERROR) << __func__ << debugMessage() << ": No frame to write";
return 0;
}
downmix_to_mono_i16_from_stereo_i16(dst.data(), src.data(), dst.size());
src = std::move(dst);
multiplier *= 2;
}
if (mResampler) {
// WAR to resample audio data from BT HAL to expected rate in mix port
size_t in_frames = src.size();
if (in_frames == 0) {
LOG(ERROR) << __func__ << debugMessage() << ": No frame to write";
return 0;
}
size_t out_frames = in_frames / mResampleRatio;
std::vector<int16_t> dst(out_frames);
int rc = mResampler->resample_from_input(mResampler.get(), src.data(), &in_frames,
dst.data(), &out_frames);
if (rc) {
LOG(ERROR) << __func__ << ": failed to resample, rc=" << rc;
}
dst.resize(out_frames);
src = std::move(dst);
multiplier *= mResampleRatio;
}
auto totalWrite = getSession()->outWritePcmData(src.data(), src.size() * 2);
return totalWrite * multiplier;
}
bool BluetoothAudioPortAidlOut::setLatencyMode(
::aidl::android::hardware::bluetooth::audio::LatencyMode latency_mode) {
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return false;
}
LOG(INFO) << __func__ << debugMessage() << ": " << toString(latency_mode);
getSession()->setLatencyMode(latency_mode);
return true;
}
size_t BluetoothAudioPortAidlIn::readData(void* buffer, size_t bytes) const {
if (!buffer) {
LOG(ERROR) << __func__ << debugMessage() << ": bad input arg";
return 0;
}
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return 0;
}
if (!mResampler) {
return getSession()->inReadPcmData(buffer, bytes);
} else {
std::vector<int16_t> input(bytes / mResampleRatio / 2);
auto totalRead = getSession()->inReadPcmData(input.data(), input.size() * 2);
size_t in_frames = totalRead / 2;
if (in_frames == 0) return 0;
size_t out_frames = in_frames * mResampleRatio;
int rc = mResampler->resample_from_input(mResampler.get(), input.data(), &in_frames,
static_cast<int16_t*>(buffer), &out_frames);
if (rc) {
LOG(ERROR) << __func__ << ": failed to resample, rc=" << rc;
}
return out_frames * 2;
}
}
bool BluetoothAudioPortAidl::getPresentationPosition(
PresentationPosition& presentation_position) const {
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return false;
}
bool retval = getSession()->getPresentationPosition(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__ << debugMessage() << ": 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 getSession()->updateSourceMetadata(source_metadata);
}
bool BluetoothAudioPortAidl::updateSinkMetadata(const SinkMetadata& sink_metadata) const {
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": 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 getSession()->updateSinkMetadata(sink_metadata);
}
BluetoothStreamState BluetoothAudioPortAidl::getState() const {
std::lock_guard guard(mCvMutex);
return mState;
}
bool BluetoothAudioPortAidl::setState(BluetoothStreamState state) {
if (!inUse()) {
LOG(ERROR) << __func__ << debugMessage() << ": BluetoothAudioPortAidl is not in use";
return false;
}
std::lock_guard guard(mCvMutex);
LOG(INFO) << __func__ << debugMessage() << ": " << mState << " -> " << state;
mState = state;
return true;
}
void BluetoothAudioPortAidl::setCallbacks(
const std::shared_ptr<BluetoothAudioPortCallbacks>& callbacks) {
std::lock_guard l(mCvMutex);
mCallbacks = callbacks;
}
bool BluetoothAudioPortAidl::isA2dp() const {
return mSession ? mSession->isA2dp() : false;
}
bool BluetoothAudioPortAidl::isHfp() const {
return mSession ? mSession->isHfp() : false;
}
bool BluetoothAudioPortAidl::isLeAudio() const {
return mSession ? mSession->isLeAudio() : false;
}
std::string BluetoothAudioPortAidl::debugMessage() const {
const auto session_type_str = getSessionNameForDebug();
return StringPrintf(": session_type=%s, cookie=%#hx", session_type_str.c_str(), mCookie);
}
std::string BluetoothAudioPortAidl::getSessionNameForDebug() const {
return mSession ? mSession->toString() : toString(SessionType::UNKNOWN);
}
} // namespace android::bluetooth::audio::aidl