618 lines
24 KiB
C++
618 lines
24 KiB
C++
|
/*
|
||
|
* Copyright 2022 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 "BTAudioHalDeviceProxyAIDL"
|
||
|
|
||
|
#include "device_port_proxy.h"
|
||
|
|
||
|
#include <android-base/logging.h>
|
||
|
#include <android-base/stringprintf.h>
|
||
|
#include <audio_utils/primitives.h>
|
||
|
#include <inttypes.h>
|
||
|
#include <log/log.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include "BluetoothAudioSessionControl.h"
|
||
|
#include "stream_apis.h"
|
||
|
#include "utils.h"
|
||
|
|
||
|
namespace android {
|
||
|
namespace bluetooth {
|
||
|
namespace audio {
|
||
|
namespace aidl {
|
||
|
|
||
|
using ::aidl::android::hardware::bluetooth::audio::AudioConfiguration;
|
||
|
using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioSessionControl;
|
||
|
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 ::android::base::StringPrintf;
|
||
|
using ControlResultCallback = std::function<void(
|
||
|
uint16_t cookie, bool start_resp, const BluetoothAudioStatus& status)>;
|
||
|
using SessionChangedCallback = std::function<void(uint16_t cookie)>;
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
audio_channel_mask_t OutputChannelModeToAudioFormat(ChannelMode channel_mode) {
|
||
|
switch (channel_mode) {
|
||
|
case ChannelMode::MONO:
|
||
|
return AUDIO_CHANNEL_OUT_MONO;
|
||
|
case ChannelMode::STEREO:
|
||
|
return AUDIO_CHANNEL_OUT_STEREO;
|
||
|
default:
|
||
|
return kBluetoothDefaultOutputChannelModeMask;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
audio_channel_mask_t InputChannelModeToAudioFormat(ChannelMode channel_mode) {
|
||
|
switch (channel_mode) {
|
||
|
case ChannelMode::MONO:
|
||
|
return AUDIO_CHANNEL_IN_MONO;
|
||
|
case ChannelMode::STEREO:
|
||
|
return AUDIO_CHANNEL_IN_STEREO;
|
||
|
default:
|
||
|
return kBluetoothDefaultInputChannelModeMask;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
audio_format_t BitsPerSampleToAudioFormat(uint8_t bits_per_sample,
|
||
|
const SessionType& session_type) {
|
||
|
switch (bits_per_sample) {
|
||
|
case 16:
|
||
|
return AUDIO_FORMAT_PCM_16_BIT;
|
||
|
case 24:
|
||
|
/* Now we use knowledge that Classic sessions used packed, and LE Audio
|
||
|
* LC3 encoder uses unpacked as input. This should be passed as parameter
|
||
|
* from BT stack through AIDL, but it would require new interface version,
|
||
|
* so sticking with this workaround for now. */
|
||
|
if (session_type ==
|
||
|
SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||
|
session_type == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH) {
|
||
|
return AUDIO_FORMAT_PCM_24_BIT_PACKED;
|
||
|
} else {
|
||
|
return AUDIO_FORMAT_PCM_8_24_BIT;
|
||
|
}
|
||
|
case 32:
|
||
|
return AUDIO_FORMAT_PCM_32_BIT;
|
||
|
default:
|
||
|
return kBluetoothDefaultAudioFormatBitsPerSample;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The maximum time to wait in std::condition_variable::wait_for()
|
||
|
constexpr unsigned int kMaxWaitingTimeMs = 4500;
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
BluetoothAudioPortAidl::BluetoothAudioPortAidl()
|
||
|
: cookie_(::aidl::android::hardware::bluetooth::audio::
|
||
|
kObserversCookieUndefined),
|
||
|
state_(BluetoothStreamState::DISABLED),
|
||
|
session_type_(SessionType::UNKNOWN) {}
|
||
|
|
||
|
BluetoothAudioPortAidlOut::~BluetoothAudioPortAidlOut() {
|
||
|
if (in_use()) TearDown();
|
||
|
}
|
||
|
|
||
|
BluetoothAudioPortAidlIn::~BluetoothAudioPortAidlIn() {
|
||
|
if (in_use()) TearDown();
|
||
|
}
|
||
|
|
||
|
bool BluetoothAudioPortAidl::SetUp(audio_devices_t devices) {
|
||
|
if (!init_session_type(devices)) return false;
|
||
|
|
||
|
state_ = BluetoothStreamState::STANDBY;
|
||
|
|
||
|
auto control_result_cb = [port = this](uint16_t cookie, bool start_resp,
|
||
|
const BluetoothAudioStatus& status) {
|
||
|
if (!port->in_use()) {
|
||
|
LOG(ERROR) << "control_result_cb: BluetoothAudioPortAidl is not in use";
|
||
|
return;
|
||
|
}
|
||
|
if (port->cookie_ != cookie) {
|
||
|
LOG(ERROR) << "control_result_cb: proxy of device port (cookie="
|
||
|
<< StringPrintf("%#hx", cookie) << ") is corrupted";
|
||
|
return;
|
||
|
}
|
||
|
port->ControlResultHandler(status);
|
||
|
};
|
||
|
auto session_changed_cb = [port = this](uint16_t cookie) {
|
||
|
if (!port->in_use()) {
|
||
|
LOG(ERROR) << "session_changed_cb: BluetoothAudioPortAidl is not in use";
|
||
|
return;
|
||
|
}
|
||
|
if (port->cookie_ != cookie) {
|
||
|
LOG(ERROR) << "session_changed_cb: proxy of device port (cookie="
|
||
|
<< StringPrintf("%#hx", cookie) << ") is corrupted";
|
||
|
return;
|
||
|
}
|
||
|
port->SessionChangedHandler();
|
||
|
};
|
||
|
// TODO: Add audio_config_changed_cb
|
||
|
PortStatusCallbacks cbacks = {
|
||
|
.control_result_cb_ = control_result_cb,
|
||
|
.session_changed_cb_ = session_changed_cb,
|
||
|
};
|
||
|
cookie_ = BluetoothAudioSessionControl::RegisterControlResultCback(
|
||
|
session_type_, cbacks);
|
||
|
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_);
|
||
|
|
||
|
return (
|
||
|
cookie_ !=
|
||
|
::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined);
|
||
|
}
|
||
|
|
||
|
bool BluetoothAudioPortAidl::init_session_type(audio_devices_t device) {
|
||
|
switch (device) {
|
||
|
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
|
||
|
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
|
||
|
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
|
||
|
LOG(VERBOSE)
|
||
|
<< __func__
|
||
|
<< ": device=AUDIO_DEVICE_OUT_BLUETOOTH_A2DP (HEADPHONES/SPEAKER) ("
|
||
|
<< StringPrintf("%#x", device) << ")";
|
||
|
session_type_ = SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
|
||
|
break;
|
||
|
case AUDIO_DEVICE_OUT_HEARING_AID:
|
||
|
LOG(VERBOSE) << __func__
|
||
|
<< ": device=AUDIO_DEVICE_OUT_HEARING_AID (MEDIA/VOICE) ("
|
||
|
<< StringPrintf("%#x", device) << ")";
|
||
|
session_type_ = SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH;
|
||
|
break;
|
||
|
case AUDIO_DEVICE_OUT_BLE_HEADSET:
|
||
|
LOG(VERBOSE) << __func__
|
||
|
<< ": device=AUDIO_DEVICE_OUT_BLE_HEADSET (MEDIA/VOICE) ("
|
||
|
<< StringPrintf("%#x", device) << ")";
|
||
|
session_type_ = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
|
||
|
break;
|
||
|
case AUDIO_DEVICE_OUT_BLE_SPEAKER:
|
||
|
LOG(VERBOSE) << __func__
|
||
|
<< ": device=AUDIO_DEVICE_OUT_BLE_SPEAKER (MEDIA) ("
|
||
|
<< StringPrintf("%#x", device) << ")";
|
||
|
session_type_ = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
|
||
|
break;
|
||
|
case AUDIO_DEVICE_IN_BLE_HEADSET:
|
||
|
LOG(VERBOSE) << __func__
|
||
|
<< ": device=AUDIO_DEVICE_IN_BLE_HEADSET (VOICE) ("
|
||
|
<< StringPrintf("%#x", device) << ")";
|
||
|
session_type_ = SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH;
|
||
|
break;
|
||
|
case AUDIO_DEVICE_OUT_BLE_BROADCAST:
|
||
|
LOG(VERBOSE) << __func__
|
||
|
<< ": device=AUDIO_DEVICE_OUT_BLE_BROADCAST (MEDIA) ("
|
||
|
<< StringPrintf("%#x", device) << ")";
|
||
|
session_type_ =
|
||
|
SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH;
|
||
|
break;
|
||
|
default:
|
||
|
LOG(ERROR) << __func__
|
||
|
<< ": unknown device=" << StringPrintf("%#x", device);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!BluetoothAudioSessionControl::IsSessionReady(session_type_)) {
|
||
|
LOG(ERROR) << __func__ << ": device=" << StringPrintf("%#x", device)
|
||
|
<< ", session_type=" << toString(session_type_)
|
||
|
<< " is not ready";
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void BluetoothAudioPortAidl::TearDown() {
|
||
|
if (!in_use()) {
|
||
|
LOG(ERROR) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< " unknown monitor";
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_);
|
||
|
BluetoothAudioSessionControl::UnregisterControlResultCback(session_type_,
|
||
|
cookie_);
|
||
|
cookie_ =
|
||
|
::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined;
|
||
|
}
|
||
|
|
||
|
void BluetoothAudioPortAidl::ControlResultHandler(
|
||
|
const BluetoothAudioStatus& status) {
|
||
|
if (!in_use()) {
|
||
|
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidlis not in use";
|
||
|
return;
|
||
|
}
|
||
|
std::unique_lock<std::mutex> port_lock(cv_mutex_);
|
||
|
BluetoothStreamState previous_state = state_;
|
||
|
LOG(INFO) << "control_result_cb: session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", 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) {
|
||
|
state_ = BluetoothStreamState::STANDBY;
|
||
|
} else {
|
||
|
// Set to standby since the stack may be busy switching between outputs
|
||
|
LOG(WARNING) << "control_result_cb: status=" << toString(status)
|
||
|
<< " failure for session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", previous_state=" << previous_state;
|
||
|
}
|
||
|
break;
|
||
|
case BluetoothStreamState::STARTING:
|
||
|
if (status == BluetoothAudioStatus::SUCCESS) {
|
||
|
state_ = BluetoothStreamState::STARTED;
|
||
|
} else {
|
||
|
// Set to standby since the stack may be busy switching between outputs
|
||
|
LOG(WARNING) << "control_result_cb: status=" << toString(status)
|
||
|
<< " failure for session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", previous_state=" << previous_state;
|
||
|
state_ = BluetoothStreamState::STANDBY;
|
||
|
}
|
||
|
break;
|
||
|
case BluetoothStreamState::SUSPENDING:
|
||
|
if (status == BluetoothAudioStatus::SUCCESS) {
|
||
|
state_ = BluetoothStreamState::STANDBY;
|
||
|
} else {
|
||
|
// It will be failed if the headset is disconnecting, and set to disable
|
||
|
// to wait for re-init again
|
||
|
LOG(WARNING) << "control_result_cb: status=" << toString(status)
|
||
|
<< " failure for session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", previous_state=" << previous_state;
|
||
|
state_ = BluetoothStreamState::DISABLED;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
LOG(ERROR) << "control_result_cb: unexpected status=" << toString(status)
|
||
|
<< " for session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", previous_state=" << previous_state;
|
||
|
return;
|
||
|
}
|
||
|
port_lock.unlock();
|
||
|
internal_cv_.notify_all();
|
||
|
}
|
||
|
|
||
|
void BluetoothAudioPortAidl::SessionChangedHandler() {
|
||
|
if (!in_use()) {
|
||
|
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||
|
return;
|
||
|
}
|
||
|
std::unique_lock<std::mutex> port_lock(cv_mutex_);
|
||
|
BluetoothStreamState previous_state = state_;
|
||
|
LOG(INFO) << "session_changed_cb: session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", previous_state=" << previous_state;
|
||
|
state_ = BluetoothStreamState::DISABLED;
|
||
|
port_lock.unlock();
|
||
|
internal_cv_.notify_all();
|
||
|
}
|
||
|
|
||
|
bool BluetoothAudioPortAidl::in_use() const {
|
||
|
return (
|
||
|
cookie_ !=
|
||
|
::aidl::android::hardware::bluetooth::audio::kObserversCookieUndefined);
|
||
|
}
|
||
|
|
||
|
bool BluetoothAudioPortAidl::GetPreferredDataIntervalUs(
|
||
|
size_t* interval_us) const {
|
||
|
if (!in_use()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const AudioConfiguration& hal_audio_cfg =
|
||
|
BluetoothAudioSessionControl::GetAudioConfig(session_type_);
|
||
|
if (hal_audio_cfg.getTag() != AudioConfiguration::pcmConfig) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const PcmConfiguration& pcm_cfg =
|
||
|
hal_audio_cfg.get<AudioConfiguration::pcmConfig>();
|
||
|
*interval_us = pcm_cfg.dataIntervalUs;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool BluetoothAudioPortAidlOut::LoadAudioConfig(
|
||
|
audio_config_t* audio_cfg) const {
|
||
|
if (!in_use()) {
|
||
|
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidlOut is not in use";
|
||
|
audio_cfg->sample_rate = kBluetoothDefaultSampleRate;
|
||
|
audio_cfg->channel_mask = kBluetoothDefaultOutputChannelModeMask;
|
||
|
audio_cfg->format = kBluetoothDefaultAudioFormatBitsPerSample;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const AudioConfiguration& hal_audio_cfg =
|
||
|
BluetoothAudioSessionControl::GetAudioConfig(session_type_);
|
||
|
if (hal_audio_cfg.getTag() != AudioConfiguration::pcmConfig) {
|
||
|
audio_cfg->sample_rate = kBluetoothDefaultSampleRate;
|
||
|
audio_cfg->channel_mask = kBluetoothDefaultOutputChannelModeMask;
|
||
|
audio_cfg->format = kBluetoothDefaultAudioFormatBitsPerSample;
|
||
|
return false;
|
||
|
}
|
||
|
const PcmConfiguration& pcm_cfg =
|
||
|
hal_audio_cfg.get<AudioConfiguration::pcmConfig>();
|
||
|
LOG(VERBOSE) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << ", PcmConfig=[" << pcm_cfg.toString()
|
||
|
<< "]";
|
||
|
if (pcm_cfg.channelMode == ChannelMode::UNKNOWN) {
|
||
|
return false;
|
||
|
}
|
||
|
audio_cfg->sample_rate = pcm_cfg.sampleRateHz;
|
||
|
audio_cfg->channel_mask =
|
||
|
(is_stereo_to_mono_
|
||
|
? AUDIO_CHANNEL_OUT_STEREO
|
||
|
: OutputChannelModeToAudioFormat(pcm_cfg.channelMode));
|
||
|
audio_cfg->format =
|
||
|
BitsPerSampleToAudioFormat(pcm_cfg.bitsPerSample, session_type_);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool BluetoothAudioPortAidlIn::LoadAudioConfig(
|
||
|
audio_config_t* audio_cfg) const {
|
||
|
if (!in_use()) {
|
||
|
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidlIn is not in use";
|
||
|
audio_cfg->sample_rate = kBluetoothDefaultSampleRate;
|
||
|
audio_cfg->channel_mask = kBluetoothDefaultInputChannelModeMask;
|
||
|
audio_cfg->format = kBluetoothDefaultAudioFormatBitsPerSample;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const AudioConfiguration& hal_audio_cfg =
|
||
|
BluetoothAudioSessionControl::GetAudioConfig(session_type_);
|
||
|
if (hal_audio_cfg.getTag() != AudioConfiguration::pcmConfig) {
|
||
|
audio_cfg->sample_rate = kBluetoothDefaultSampleRate;
|
||
|
audio_cfg->channel_mask = kBluetoothDefaultInputChannelModeMask;
|
||
|
audio_cfg->format = kBluetoothDefaultAudioFormatBitsPerSample;
|
||
|
return false;
|
||
|
}
|
||
|
const PcmConfiguration& pcm_cfg =
|
||
|
hal_audio_cfg.get<AudioConfiguration::pcmConfig>();
|
||
|
LOG(VERBOSE) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << ", PcmConfig=[" << pcm_cfg.toString()
|
||
|
<< "]";
|
||
|
if (pcm_cfg.channelMode == ChannelMode::UNKNOWN) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
audio_cfg->sample_rate = pcm_cfg.sampleRateHz;
|
||
|
audio_cfg->channel_mask = InputChannelModeToAudioFormat(pcm_cfg.channelMode);
|
||
|
audio_cfg->format =
|
||
|
BitsPerSampleToAudioFormat(pcm_cfg.bitsPerSample, session_type_);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool BluetoothAudioPortAidl::CondwaitState(BluetoothStreamState state) {
|
||
|
bool retval;
|
||
|
std::unique_lock<std::mutex> port_lock(cv_mutex_);
|
||
|
switch (state) {
|
||
|
case BluetoothStreamState::STARTING:
|
||
|
LOG(VERBOSE) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< " waiting for STARTED";
|
||
|
retval = internal_cv_.wait_for(
|
||
|
port_lock, std::chrono::milliseconds(kMaxWaitingTimeMs),
|
||
|
[this] { return this->state_ != BluetoothStreamState::STARTING; });
|
||
|
retval = retval && state_ == BluetoothStreamState::STARTED;
|
||
|
break;
|
||
|
case BluetoothStreamState::SUSPENDING:
|
||
|
LOG(VERBOSE) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< " waiting for SUSPENDED";
|
||
|
retval = internal_cv_.wait_for(
|
||
|
port_lock, std::chrono::milliseconds(kMaxWaitingTimeMs),
|
||
|
[this] { return this->state_ != BluetoothStreamState::SUSPENDING; });
|
||
|
retval = retval && state_ == BluetoothStreamState::STANDBY;
|
||
|
break;
|
||
|
default:
|
||
|
LOG(WARNING) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< " waiting for KNOWN";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return retval; // false if any failure like timeout
|
||
|
}
|
||
|
|
||
|
bool BluetoothAudioPortAidl::Start() {
|
||
|
if (!in_use()) {
|
||
|
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_
|
||
|
<< ", mono=" << (is_stereo_to_mono_ ? "true" : "false")
|
||
|
<< " request";
|
||
|
bool retval = false;
|
||
|
if (state_ == BluetoothStreamState::STANDBY) {
|
||
|
state_ = BluetoothStreamState::STARTING;
|
||
|
if (BluetoothAudioSessionControl::StartStream(session_type_)) {
|
||
|
retval = CondwaitState(BluetoothStreamState::STARTING);
|
||
|
} else {
|
||
|
LOG(ERROR) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << " Hal fails";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (retval) {
|
||
|
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_
|
||
|
<< ", mono=" << (is_stereo_to_mono_ ? "true" : "false")
|
||
|
<< " done";
|
||
|
} else {
|
||
|
LOG(ERROR) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << " failure";
|
||
|
}
|
||
|
|
||
|
return retval; // false if any failure like timeout
|
||
|
}
|
||
|
|
||
|
bool BluetoothAudioPortAidl::Suspend() {
|
||
|
if (!in_use()) {
|
||
|
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << " request";
|
||
|
bool retval = false;
|
||
|
if (state_ == BluetoothStreamState::STARTED) {
|
||
|
state_ = BluetoothStreamState::SUSPENDING;
|
||
|
if (BluetoothAudioSessionControl::SuspendStream(session_type_)) {
|
||
|
retval = CondwaitState(BluetoothStreamState::SUSPENDING);
|
||
|
} else {
|
||
|
LOG(ERROR) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << " Hal fails";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (retval) {
|
||
|
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << " done";
|
||
|
} else {
|
||
|
LOG(ERROR) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << " failure";
|
||
|
}
|
||
|
|
||
|
return retval; // false if any failure like timeout
|
||
|
}
|
||
|
|
||
|
void BluetoothAudioPortAidl::Stop() {
|
||
|
if (!in_use()) {
|
||
|
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||
|
return;
|
||
|
}
|
||
|
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << " request";
|
||
|
state_ = BluetoothStreamState::DISABLED;
|
||
|
BluetoothAudioSessionControl::StopStream(session_type_);
|
||
|
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << " done";
|
||
|
}
|
||
|
|
||
|
size_t BluetoothAudioPortAidlOut::WriteData(const void* buffer,
|
||
|
size_t bytes) const {
|
||
|
if (!in_use()) return 0;
|
||
|
if (!is_stereo_to_mono_) {
|
||
|
return BluetoothAudioSessionControl::OutWritePcmData(session_type_, 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.
|
||
|
return BluetoothAudioSessionControl::OutWritePcmData(session_type_, dst.get(),
|
||
|
write_frames * 2) *
|
||
|
2;
|
||
|
}
|
||
|
|
||
|
size_t BluetoothAudioPortAidlIn::ReadData(void* buffer, size_t bytes) const {
|
||
|
if (!in_use()) return 0;
|
||
|
return BluetoothAudioSessionControl::InReadPcmData(session_type_, buffer,
|
||
|
bytes);
|
||
|
}
|
||
|
|
||
|
bool BluetoothAudioPortAidl::GetPresentationPosition(
|
||
|
uint64_t* delay_ns, uint64_t* bytes, timespec* timestamp) const {
|
||
|
if (!in_use()) {
|
||
|
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||
|
return false;
|
||
|
}
|
||
|
PresentationPosition presentation_position;
|
||
|
bool retval = BluetoothAudioSessionControl::GetPresentationPosition(
|
||
|
session_type_, presentation_position);
|
||
|
*delay_ns = presentation_position.remoteDeviceAudioDelayNanos;
|
||
|
*bytes = presentation_position.transmittedOctets;
|
||
|
*timestamp = {.tv_sec = static_cast<__kernel_old_time_t>(
|
||
|
presentation_position.transmittedOctetsTimestamp.tvSec),
|
||
|
.tv_nsec = static_cast<long>(
|
||
|
presentation_position.transmittedOctetsTimestamp.tvNSec)};
|
||
|
LOG(VERBOSE) << __func__
|
||
|
<< ": session_type=" << StringPrintf("%#hhx", session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << ", delay=" << *delay_ns
|
||
|
<< "ns, data=" << *bytes
|
||
|
<< " bytes, timestamp=" << timestamp->tv_sec << "."
|
||
|
<< StringPrintf("%09ld", timestamp->tv_nsec) << "s";
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
void BluetoothAudioPortAidl::UpdateSourceMetadata(
|
||
|
const source_metadata* source_metadata) const {
|
||
|
if (!in_use()) {
|
||
|
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||
|
return;
|
||
|
}
|
||
|
LOG(DEBUG) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << ", " << source_metadata->track_count
|
||
|
<< " track(s)";
|
||
|
if (source_metadata->track_count == 0) return;
|
||
|
BluetoothAudioSessionControl::UpdateSourceMetadata(session_type_,
|
||
|
*source_metadata);
|
||
|
}
|
||
|
|
||
|
void BluetoothAudioPortAidl::UpdateSinkMetadata(
|
||
|
const sink_metadata* sink_metadata) const {
|
||
|
if (!in_use()) {
|
||
|
LOG(ERROR) << __func__ << ": BluetoothAudioPortAidl is not in use";
|
||
|
return;
|
||
|
}
|
||
|
LOG(DEBUG) << __func__ << ": session_type=" << toString(session_type_)
|
||
|
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||
|
<< ", state=" << state_ << ", " << sink_metadata->track_count
|
||
|
<< " track(s)";
|
||
|
if (sink_metadata->track_count == 0) return;
|
||
|
BluetoothAudioSessionControl::UpdateSinkMetadata(session_type_,
|
||
|
*sink_metadata);
|
||
|
}
|
||
|
|
||
|
BluetoothStreamState BluetoothAudioPortAidl::GetState() const { return state_; }
|
||
|
|
||
|
void BluetoothAudioPortAidl::SetState(BluetoothStreamState state) {
|
||
|
state_ = state;
|
||
|
}
|
||
|
|
||
|
} // namespace aidl
|
||
|
} // namespace audio
|
||
|
} // namespace bluetooth
|
||
|
} // namespace android
|