gsi: Initial implementation of sysbta, a system-side bluetooth audio HAL
On platform release T, the legacy `audio.a2dp.default` HAL no longer exists, and cannot be trivially restored for generic system-side A2DP audio support. Rather than trying to cling on to its existence (which does not even work anymore even if one did so), it is time to come up with a new solution. This commit introduces a system-side implementation of the generic bluetooth audio HAL. The HAL itself is modelled after the default bluetooth audio HAL, while substantial components of the default audio HAL are also included as it has to also implement the audio HAL interfaces. The audio HAL implementation is delegated to `audio.sysbta.default`, forked from `audio.bluetooth.default` from the Bluetooth apex package. It then communicates with the bluetooth audio HAL interfaces, which need to live in the same process (in this case, the process of the sysbta HAL). This is why we cannot just load `audio.sysbta.default` into the default audio HAL or the audioserver process directly, but rather have to include our own simplistic audio HAL implementation. For now, the audio HAL implementation only includes one for the 6.0 version, but technically, an interface for *every* audio HAL version needs to exist. This, along with other messiness in the sysbta implementation, will be addressed in a future commit. Note that to actually make use of the sysbta audio hal, patches in frameworks/av and packages/modules/Bluetooth are required to introduce support for the property `persist.bluetooth.system_audio_hal.enabled`.
This commit is contained in:
parent
a7009ad4ad
commit
978ccd40f9
9
base.mk
9
base.mk
@ -8,6 +8,15 @@ PRODUCT_COPY_FILES := \
|
||||
frameworks/native/data/etc/android.hardware.bluetooth_le.xml:system/etc/permissions/android.hardware.bluetooth_le.xml \
|
||||
frameworks/native/data/etc/android.hardware.usb.host.xml:system/etc/permissions/android.hardware.usb.host.xml \
|
||||
|
||||
# Bluetooth Audio (System-side HAL, sysbta)
|
||||
PRODUCT_PACKAGES += \
|
||||
audio.sysbta.default \
|
||||
android.hardware.bluetooth.audio-service-system
|
||||
|
||||
PRODUCT_COPY_FILES += \
|
||||
device/phh/treble/bluetooth/audio/config/sysbta_audio_policy_configuration.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/sysbta_audio_policy_configuration.xml \
|
||||
device/phh/treble/bluetooth/audio/config/sysbta_audio_policy_configuration_7_0.xml:$(TARGET_COPY_OUTY_SYSTEM)/etc/sysbta_audio_policy_configuration_7_0.xml
|
||||
|
||||
#Use a more decent APN config
|
||||
PRODUCT_COPY_FILES += \
|
||||
device/sample/etc/apns-full-conf.xml:system/etc/apns-conf.xml
|
||||
|
44
bluetooth/audio/config/sysbta_audio_policy_configuration.xml
Normal file
44
bluetooth/audio/config/sysbta_audio_policy_configuration.xml
Normal file
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Bluetooth Audio HAL Audio Policy Configuration file -->
|
||||
<module name="sysbta" halVersion="2.0">
|
||||
<mixPorts>
|
||||
<!-- A2DP Audio Ports -->
|
||||
<mixPort name="a2dp output" role="source"/>
|
||||
<!-- Hearing AIDs Audio Ports -->
|
||||
<mixPort name="hearing aid output" role="source">
|
||||
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
|
||||
samplingRates="24000,16000"
|
||||
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
|
||||
</mixPort>
|
||||
</mixPorts>
|
||||
<devicePorts>
|
||||
<!-- A2DP Audio Ports -->
|
||||
<devicePort tagName="BT A2DP Out" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP" role="sink">
|
||||
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
|
||||
samplingRates="44100,48000,88200,96000"
|
||||
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
|
||||
</devicePort>
|
||||
<devicePort tagName="BT A2DP Headphones" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES" role="sink">
|
||||
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
|
||||
samplingRates="44100,48000,88200,96000"
|
||||
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
|
||||
</devicePort>
|
||||
<devicePort tagName="BT A2DP Speaker" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER" role="sink">
|
||||
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
|
||||
samplingRates="44100,48000,88200,96000"
|
||||
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
|
||||
</devicePort>
|
||||
<!-- Hearing AIDs Audio Ports -->
|
||||
<devicePort tagName="BT Hearing Aid Out" type="AUDIO_DEVICE_OUT_HEARING_AID" role="sink"/>
|
||||
</devicePorts>
|
||||
<routes>
|
||||
<route type="mix" sink="BT A2DP Out"
|
||||
sources="a2dp output"/>
|
||||
<route type="mix" sink="BT A2DP Headphones"
|
||||
sources="a2dp output"/>
|
||||
<route type="mix" sink="BT A2DP Speaker"
|
||||
sources="a2dp output"/>
|
||||
<route type="mix" sink="BT Hearing Aid Out"
|
||||
sources="hearing aid output"/>
|
||||
</routes>
|
||||
</module>
|
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Bluetooth Audio HAL Audio Policy Configuration file -->
|
||||
<module name="sysbta" halVersion="2.0">
|
||||
<mixPorts>
|
||||
<!-- A2DP Audio Ports -->
|
||||
<mixPort name="a2dp output" role="source"/>
|
||||
<!-- Hearing AIDs Audio Ports -->
|
||||
<mixPort name="hearing aid output" role="source">
|
||||
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
|
||||
samplingRates="24000 16000"
|
||||
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
|
||||
</mixPort>
|
||||
</mixPorts>
|
||||
<devicePorts>
|
||||
<!-- A2DP Audio Ports -->
|
||||
<devicePort tagName="BT A2DP Out" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP" role="sink">
|
||||
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
|
||||
samplingRates="44100 48000 88200 96000"
|
||||
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
|
||||
</devicePort>
|
||||
<devicePort tagName="BT A2DP Headphones" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES" role="sink">
|
||||
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
|
||||
samplingRates="44100 48000 88200 96000"
|
||||
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
|
||||
</devicePort>
|
||||
<devicePort tagName="BT A2DP Speaker" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER" role="sink">
|
||||
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
|
||||
samplingRates="44100 48000 88200 96000"
|
||||
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
|
||||
</devicePort>
|
||||
<!-- Hearing AIDs Audio Ports -->
|
||||
<devicePort tagName="BT Hearing Aid Out" type="AUDIO_DEVICE_OUT_HEARING_AID" role="sink"/>
|
||||
</devicePorts>
|
||||
<routes>
|
||||
<route type="mix" sink="BT A2DP Out"
|
||||
sources="a2dp output"/>
|
||||
<route type="mix" sink="BT A2DP Headphones"
|
||||
sources="a2dp output"/>
|
||||
<route type="mix" sink="BT A2DP Speaker"
|
||||
sources="a2dp output"/>
|
||||
<route type="mix" sink="BT Hearing Aid Out"
|
||||
sources="hearing aid output"/>
|
||||
</routes>
|
||||
</module>
|
80
bluetooth/audio/hal/A2dpOffloadAudioProvider.cpp
Normal file
80
bluetooth/audio/hal/A2dpOffloadAudioProvider.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 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 "BTAudioProviderA2dpHW"
|
||||
|
||||
#include "A2dpOffloadAudioProvider.h"
|
||||
|
||||
#include <BluetoothAudioCodecs.h>
|
||||
#include <BluetoothAudioSessionReport.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
A2dpOffloadEncodingAudioProvider::A2dpOffloadEncodingAudioProvider()
|
||||
: A2dpOffloadAudioProvider() {
|
||||
session_type_ = SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
|
||||
}
|
||||
|
||||
A2dpOffloadDecodingAudioProvider::A2dpOffloadDecodingAudioProvider()
|
||||
: A2dpOffloadAudioProvider() {
|
||||
session_type_ = SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH;
|
||||
}
|
||||
|
||||
A2dpOffloadAudioProvider::A2dpOffloadAudioProvider() {}
|
||||
|
||||
bool A2dpOffloadAudioProvider::isValid(const SessionType& session_type) {
|
||||
return (session_type == session_type_);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus A2dpOffloadAudioProvider::startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
|
||||
if (audio_config.getTag() != AudioConfiguration::a2dpConfig) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
if (!BluetoothAudioCodecs::IsOffloadCodecConfigurationValid(
|
||||
session_type_, audio_config.get<AudioConfiguration::a2dpConfig>())) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
return BluetoothAudioProvider::startSession(
|
||||
host_if, audio_config, latency_modes, _aidl_return);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus A2dpOffloadAudioProvider::onSessionReady(
|
||||
DataMQDesc* _aidl_return) {
|
||||
*_aidl_return = DataMQDesc();
|
||||
BluetoothAudioSessionReport::OnSessionStarted(
|
||||
session_type_, stack_iface_, nullptr, *audio_config_, latency_modes_);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
57
bluetooth/audio/hal/A2dpOffloadAudioProvider.h
Normal file
57
bluetooth/audio/hal/A2dpOffloadAudioProvider.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioProvider.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class A2dpOffloadAudioProvider : public BluetoothAudioProvider {
|
||||
public:
|
||||
A2dpOffloadAudioProvider();
|
||||
|
||||
bool isValid(const SessionType& session_type) override;
|
||||
|
||||
ndk::ScopedAStatus startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes,
|
||||
DataMQDesc* _aidl_return);
|
||||
|
||||
private:
|
||||
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
|
||||
};
|
||||
|
||||
class A2dpOffloadEncodingAudioProvider : public A2dpOffloadAudioProvider {
|
||||
public:
|
||||
A2dpOffloadEncodingAudioProvider();
|
||||
};
|
||||
|
||||
class A2dpOffloadDecodingAudioProvider : public A2dpOffloadAudioProvider {
|
||||
public:
|
||||
A2dpOffloadDecodingAudioProvider();
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
111
bluetooth/audio/hal/A2dpSoftwareAudioProvider.cpp
Normal file
111
bluetooth/audio/hal/A2dpSoftwareAudioProvider.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 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 "BTAudioProviderA2dpSW"
|
||||
|
||||
#include "A2dpSoftwareAudioProvider.h"
|
||||
|
||||
#include <BluetoothAudioCodecs.h>
|
||||
#include <BluetoothAudioSessionReport.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
// Here the buffer size is based on SBC
|
||||
static constexpr uint32_t kPcmFrameSize = 4; // 16 bits per sample / stereo
|
||||
// SBC is 128, and here we choose the LCM of 16, 24, and 32
|
||||
static constexpr uint32_t kPcmFrameCount = 96;
|
||||
static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
|
||||
// The max counts by 1 tick (20ms) for SBC is about 7. Since using 96 for the
|
||||
// PCM counts, here we just choose a greater number
|
||||
static constexpr uint32_t kRtpFrameCount = 10;
|
||||
static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
|
||||
static constexpr uint32_t kBufferCount = 2; // double buffer
|
||||
static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
|
||||
|
||||
A2dpSoftwareEncodingAudioProvider::A2dpSoftwareEncodingAudioProvider()
|
||||
: A2dpSoftwareAudioProvider() {
|
||||
session_type_ = SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
|
||||
}
|
||||
|
||||
A2dpSoftwareDecodingAudioProvider::A2dpSoftwareDecodingAudioProvider()
|
||||
: A2dpSoftwareAudioProvider() {
|
||||
session_type_ = SessionType::A2DP_SOFTWARE_DECODING_DATAPATH;
|
||||
}
|
||||
|
||||
A2dpSoftwareAudioProvider::A2dpSoftwareAudioProvider()
|
||||
: BluetoothAudioProvider(), data_mq_(nullptr) {
|
||||
LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
|
||||
<< " byte(s)";
|
||||
std::unique_ptr<DataMQ> data_mq(
|
||||
new DataMQ(kDataMqSize, /* EventFlag */ true));
|
||||
if (data_mq && data_mq->isValid()) {
|
||||
data_mq_ = std::move(data_mq);
|
||||
} else {
|
||||
ALOGE_IF(!data_mq, "failed to allocate data MQ");
|
||||
ALOGE_IF(data_mq && !data_mq->isValid(), "data MQ is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
bool A2dpSoftwareAudioProvider::isValid(const SessionType& sessionType) {
|
||||
return (sessionType == session_type_ && data_mq_ && data_mq_->isValid());
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus A2dpSoftwareAudioProvider::startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
|
||||
if (audio_config.getTag() != AudioConfiguration::pcmConfig) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
const PcmConfiguration& pcm_config =
|
||||
audio_config.get<AudioConfiguration::pcmConfig>();
|
||||
if (!BluetoothAudioCodecs::IsSoftwarePcmConfigurationValid(pcm_config)) {
|
||||
LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
|
||||
<< pcm_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
return BluetoothAudioProvider::startSession(
|
||||
host_if, audio_config, latency_modes, _aidl_return);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus A2dpSoftwareAudioProvider::onSessionReady(
|
||||
DataMQDesc* _aidl_return) {
|
||||
if (data_mq_ == nullptr || !data_mq_->isValid()) {
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
*_aidl_return = data_mq_->dupeDesc();
|
||||
auto desc = data_mq_->dupeDesc();
|
||||
BluetoothAudioSessionReport::OnSessionStarted(
|
||||
session_type_, stack_iface_, &desc, *audio_config_, latency_modes_);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
60
bluetooth/audio/hal/A2dpSoftwareAudioProvider.h
Normal file
60
bluetooth/audio/hal/A2dpSoftwareAudioProvider.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioProvider.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class A2dpSoftwareAudioProvider : public BluetoothAudioProvider {
|
||||
public:
|
||||
A2dpSoftwareAudioProvider();
|
||||
|
||||
bool isValid(const SessionType& sessionType) override;
|
||||
|
||||
ndk::ScopedAStatus startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes,
|
||||
DataMQDesc* _aidl_return);
|
||||
|
||||
private:
|
||||
// audio data queue for software encoding
|
||||
std::unique_ptr<DataMQ> data_mq_;
|
||||
|
||||
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
|
||||
};
|
||||
|
||||
class A2dpSoftwareEncodingAudioProvider : public A2dpSoftwareAudioProvider {
|
||||
public:
|
||||
A2dpSoftwareEncodingAudioProvider();
|
||||
};
|
||||
|
||||
class A2dpSoftwareDecodingAudioProvider : public A2dpSoftwareAudioProvider {
|
||||
public:
|
||||
A2dpSoftwareDecodingAudioProvider();
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
92
bluetooth/audio/hal/Android.bp
Normal file
92
bluetooth/audio/hal/Android.bp
Normal file
@ -0,0 +1,92 @@
|
||||
cc_library_shared {
|
||||
name: "android.hardware.bluetooth.audio-system-impl",
|
||||
vintf_fragments: ["bluetooth_audio.xml"],
|
||||
srcs: [
|
||||
"BluetoothAudioProvider.cpp",
|
||||
"BluetoothAudioProviderFactory.cpp",
|
||||
"A2dpOffloadAudioProvider.cpp",
|
||||
"A2dpSoftwareAudioProvider.cpp",
|
||||
"HearingAidAudioProvider.cpp",
|
||||
"LeAudioOffloadAudioProvider.cpp",
|
||||
"LeAudioSoftwareAudioProvider.cpp",
|
||||
"service.cpp",
|
||||
],
|
||||
export_include_dirs: ["."],
|
||||
header_libs: ["libhardware_headers"],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"libcutils",
|
||||
"libfmq",
|
||||
"liblog",
|
||||
"android.hardware.bluetooth.audio-V2-ndk",
|
||||
"libbluetooth_audio_session_aidl_system",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "android.hardware.bluetooth.audio-service-system",
|
||||
vintf_fragments: ["bluetooth_audio_system.xml"],
|
||||
init_rc: ["android.hardware.bluetooth.audio-service-system.rc"],
|
||||
relative_install_path: "hw",
|
||||
srcs: [
|
||||
"BluetoothAudioProvider.cpp",
|
||||
"BluetoothAudioProviderFactory.cpp",
|
||||
"A2dpOffloadAudioProvider.cpp",
|
||||
"A2dpSoftwareAudioProvider.cpp",
|
||||
"HearingAidAudioProvider.cpp",
|
||||
"LeAudioOffloadAudioProvider.cpp",
|
||||
"LeAudioSoftwareAudioProvider.cpp",
|
||||
"Device.cpp",
|
||||
"DevicesFactory.cpp",
|
||||
"ParametersUtil.cpp",
|
||||
"PrimaryDevice.cpp",
|
||||
"Stream.cpp",
|
||||
"StreamIn.cpp",
|
||||
"StreamOut.cpp",
|
||||
"service_system.cpp",
|
||||
],
|
||||
header_libs: [
|
||||
"libhardware_headers",
|
||||
"android.hardware.audio.common.util@all-versions",
|
||||
"libaudioutils_headers",
|
||||
"libaudio_system_headers",
|
||||
"libmedia_headers",
|
||||
"libmediautils_headers",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder",
|
||||
"libbinder_ndk",
|
||||
"libcutils",
|
||||
"libfmq",
|
||||
"liblog",
|
||||
"android.hardware.bluetooth.audio-V2-ndk",
|
||||
"libbluetooth_audio_session_aidl_system",
|
||||
"libfmq",
|
||||
"libhardware",
|
||||
"libhidlbase",
|
||||
"liblog",
|
||||
"libmedia_helper",
|
||||
"libmediautils_vendor",
|
||||
"libmemunreachable",
|
||||
"libutils",
|
||||
"android.hardware.audio.common-util",
|
||||
"android.hardware.audio@6.0",
|
||||
// "android.hardware.audio@7.1",
|
||||
"android.hardware.audio@6.0-util",
|
||||
"android.hardware.audio.common@6.0",
|
||||
// "android.hardware.audio.common@6.0-enums",
|
||||
"android.hardware.audio.common@6.0-util",
|
||||
],
|
||||
static_libs: [
|
||||
"libaudiofoundation",
|
||||
],
|
||||
cflags: [
|
||||
"-DMAJOR_VERSION=6",
|
||||
"-DMINOR_VERSION=0",
|
||||
"-DCOMMON_TYPES_MINOR_VERSION=0",
|
||||
"-DCORE_TYPES_MINOR_VERSION=0",
|
||||
"-include common/all-versions/VersionMacro.h",
|
||||
],
|
||||
}
|
161
bluetooth/audio/hal/BluetoothAudioProvider.cpp
Normal file
161
bluetooth/audio/hal/BluetoothAudioProvider.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (C) 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 "BTAudioProviderStub"
|
||||
|
||||
#include "BluetoothAudioProvider.h"
|
||||
|
||||
#include <BluetoothAudioSessionReport.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
BluetoothAudioProvider::BluetoothAudioProvider() {
|
||||
death_recipient_ = ::ndk::ScopedAIBinder_DeathRecipient(
|
||||
AIBinder_DeathRecipient_new(binderDiedCallbackAidl));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latencyModes,
|
||||
DataMQDesc* _aidl_return) {
|
||||
if (host_if == nullptr) {
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
latency_modes_ = latencyModes;
|
||||
audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
|
||||
stack_iface_ = host_if;
|
||||
is_binder_died = false;
|
||||
|
||||
AIBinder_linkToDeath(stack_iface_->asBinder().get(), death_recipient_.get(),
|
||||
this);
|
||||
|
||||
onSessionReady(_aidl_return);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::endSession() {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
|
||||
|
||||
if (stack_iface_ != nullptr) {
|
||||
BluetoothAudioSessionReport::OnSessionEnded(session_type_);
|
||||
|
||||
if (!is_binder_died) {
|
||||
AIBinder_unlinkToDeath(stack_iface_->asBinder().get(),
|
||||
death_recipient_.get(), this);
|
||||
}
|
||||
} else {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
}
|
||||
|
||||
stack_iface_ = nullptr;
|
||||
audio_config_ = nullptr;
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::streamStarted(
|
||||
BluetoothAudioStatus status) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", status=" << toString(status);
|
||||
|
||||
if (stack_iface_ != nullptr) {
|
||||
BluetoothAudioSessionReport::ReportControlStatus(session_type_, true,
|
||||
status);
|
||||
} else {
|
||||
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", status=" << toString(status) << " has NO session";
|
||||
}
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::streamSuspended(
|
||||
BluetoothAudioStatus status) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", status=" << toString(status);
|
||||
|
||||
if (stack_iface_ != nullptr) {
|
||||
BluetoothAudioSessionReport::ReportControlStatus(session_type_, false,
|
||||
status);
|
||||
} else {
|
||||
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", status=" << toString(status) << " has NO session";
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::updateAudioConfiguration(
|
||||
const AudioConfiguration& audio_config) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
|
||||
|
||||
if (stack_iface_ == nullptr || audio_config_ == nullptr) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
if (audio_config.getTag() != audio_config_->getTag()) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " audio config type is not match";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
|
||||
BluetoothAudioSessionReport::ReportAudioConfigChanged(session_type_,
|
||||
*audio_config_);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProvider::setLowLatencyModeAllowed(
|
||||
bool allowed) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
|
||||
|
||||
if (stack_iface_ == nullptr) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
LOG(INFO) << __func__ << " - allowed " << allowed;
|
||||
BluetoothAudioSessionReport::ReportLowLatencyModeAllowedChanged(
|
||||
session_type_, allowed);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
void BluetoothAudioProvider::binderDiedCallbackAidl(void* ptr) {
|
||||
LOG(ERROR) << __func__ << " - BluetoothAudio Service died";
|
||||
auto provider = static_cast<BluetoothAudioProvider*>(ptr);
|
||||
if (provider == nullptr) {
|
||||
LOG(ERROR) << __func__ << ": Null AudioProvider HAL died";
|
||||
return;
|
||||
}
|
||||
provider->is_binder_died = true;
|
||||
provider->endSession();
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
72
bluetooth/audio/hal/BluetoothAudioProvider.h
Normal file
72
bluetooth/audio/hal/BluetoothAudioProvider.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/bluetooth/audio/BnBluetoothAudioProvider.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/LatencyMode.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
|
||||
#include <fmq/AidlMessageQueue.h>
|
||||
|
||||
using ::aidl::android::hardware::common::fmq::MQDescriptor;
|
||||
using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
|
||||
using ::android::AidlMessageQueue;
|
||||
|
||||
using MqDataType = int8_t;
|
||||
using MqDataMode = SynchronizedReadWrite;
|
||||
using DataMQ = AidlMessageQueue<MqDataType, MqDataMode>;
|
||||
using DataMQDesc = MQDescriptor<MqDataType, MqDataMode>;
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class BluetoothAudioProvider : public BnBluetoothAudioProvider {
|
||||
public:
|
||||
BluetoothAudioProvider();
|
||||
ndk::ScopedAStatus startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes,
|
||||
DataMQDesc* _aidl_return);
|
||||
ndk::ScopedAStatus endSession();
|
||||
ndk::ScopedAStatus streamStarted(BluetoothAudioStatus status);
|
||||
ndk::ScopedAStatus streamSuspended(BluetoothAudioStatus status);
|
||||
ndk::ScopedAStatus updateAudioConfiguration(
|
||||
const AudioConfiguration& audio_config);
|
||||
ndk::ScopedAStatus setLowLatencyModeAllowed(bool allowed);
|
||||
|
||||
virtual bool isValid(const SessionType& sessionType) = 0;
|
||||
|
||||
protected:
|
||||
virtual ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) = 0;
|
||||
static void binderDiedCallbackAidl(void* cookie_ptr);
|
||||
|
||||
::ndk::ScopedAIBinder_DeathRecipient death_recipient_;
|
||||
|
||||
std::shared_ptr<IBluetoothAudioPort> stack_iface_;
|
||||
std::unique_ptr<AudioConfiguration> audio_config_ = nullptr;
|
||||
SessionType session_type_;
|
||||
std::vector<LatencyMode> latency_modes_;
|
||||
bool is_binder_died = false;
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
142
bluetooth/audio/hal/BluetoothAudioProviderFactory.cpp
Normal file
142
bluetooth/audio/hal/BluetoothAudioProviderFactory.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 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 "BTAudioProviderFactoryAIDL"
|
||||
|
||||
#include "BluetoothAudioProviderFactory.h"
|
||||
|
||||
#include <BluetoothAudioCodecs.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "A2dpOffloadAudioProvider.h"
|
||||
#include "A2dpSoftwareAudioProvider.h"
|
||||
#include "BluetoothAudioProvider.h"
|
||||
#include "HearingAidAudioProvider.h"
|
||||
#include "LeAudioOffloadAudioProvider.h"
|
||||
#include "LeAudioSoftwareAudioProvider.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
BluetoothAudioProviderFactory::BluetoothAudioProviderFactory() {}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProviderFactory::openProvider(
|
||||
const SessionType session_type,
|
||||
std::shared_ptr<IBluetoothAudioProvider>* _aidl_return) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type);
|
||||
std::shared_ptr<BluetoothAudioProvider> provider = nullptr;
|
||||
|
||||
switch (session_type) {
|
||||
case SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<A2dpSoftwareEncodingAudioProvider>();
|
||||
break;
|
||||
case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<A2dpOffloadEncodingAudioProvider>();
|
||||
break;
|
||||
case SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<HearingAidAudioProvider>();
|
||||
break;
|
||||
case SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<LeAudioSoftwareOutputAudioProvider>();
|
||||
break;
|
||||
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<LeAudioOffloadOutputAudioProvider>();
|
||||
break;
|
||||
case SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<LeAudioSoftwareInputAudioProvider>();
|
||||
break;
|
||||
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<LeAudioOffloadInputAudioProvider>();
|
||||
break;
|
||||
case SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH:
|
||||
provider =
|
||||
ndk::SharedRefBase::make<LeAudioSoftwareBroadcastAudioProvider>();
|
||||
break;
|
||||
case SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
provider =
|
||||
ndk::SharedRefBase::make<LeAudioOffloadBroadcastAudioProvider>();
|
||||
break;
|
||||
case SessionType::A2DP_SOFTWARE_DECODING_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<A2dpSoftwareDecodingAudioProvider>();
|
||||
break;
|
||||
case SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH:
|
||||
provider = ndk::SharedRefBase::make<A2dpOffloadDecodingAudioProvider>();
|
||||
break;
|
||||
default:
|
||||
provider = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (provider == nullptr || !provider->isValid(session_type)) {
|
||||
provider = nullptr;
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type);
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
*_aidl_return = provider;
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus BluetoothAudioProviderFactory::getProviderCapabilities(
|
||||
const SessionType session_type,
|
||||
std::vector<AudioCapabilities>* _aidl_return) {
|
||||
if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
auto codec_capabilities =
|
||||
BluetoothAudioCodecs::GetA2dpOffloadCodecCapabilities(session_type);
|
||||
_aidl_return->resize(codec_capabilities.size());
|
||||
for (int i = 0; i < codec_capabilities.size(); i++) {
|
||||
_aidl_return->at(i).set<AudioCapabilities::a2dpCapabilities>(
|
||||
codec_capabilities[i]);
|
||||
}
|
||||
} else if (session_type ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
|
||||
session_type ==
|
||||
SessionType::
|
||||
LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
|
||||
std::vector<LeAudioCodecCapabilitiesSetting> db_codec_capabilities =
|
||||
BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities(session_type);
|
||||
if (db_codec_capabilities.size()) {
|
||||
_aidl_return->resize(db_codec_capabilities.size());
|
||||
for (int i = 0; i < db_codec_capabilities.size(); ++i) {
|
||||
_aidl_return->at(i).set<AudioCapabilities::leAudioCapabilities>(
|
||||
db_codec_capabilities[i]);
|
||||
}
|
||||
}
|
||||
} else if (session_type != SessionType::UNKNOWN) {
|
||||
auto pcm_capabilities = BluetoothAudioCodecs::GetSoftwarePcmCapabilities();
|
||||
_aidl_return->resize(pcm_capabilities.size());
|
||||
for (int i = 0; i < pcm_capabilities.size(); i++) {
|
||||
_aidl_return->at(i).set<AudioCapabilities::pcmCapabilities>(
|
||||
pcm_capabilities[i]);
|
||||
}
|
||||
}
|
||||
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type)
|
||||
<< " supports " << _aidl_return->size() << " codecs";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
44
bluetooth/audio/hal/BluetoothAudioProviderFactory.h
Normal file
44
bluetooth/audio/hal/BluetoothAudioProviderFactory.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/bluetooth/audio/BnBluetoothAudioProviderFactory.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class BluetoothAudioProviderFactory : public BnBluetoothAudioProviderFactory {
|
||||
public:
|
||||
BluetoothAudioProviderFactory();
|
||||
|
||||
ndk::ScopedAStatus openProvider(
|
||||
const SessionType session_type,
|
||||
std::shared_ptr<IBluetoothAudioProvider>* _aidl_return) override;
|
||||
|
||||
ndk::ScopedAStatus getProviderCapabilities(
|
||||
const SessionType session_type,
|
||||
std::vector<AudioCapabilities>* _aidl_return) override;
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
628
bluetooth/audio/hal/Device.cpp
Normal file
628
bluetooth/audio/hal/Device.cpp
Normal file
@ -0,0 +1,628 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "DeviceHAL"
|
||||
|
||||
#include "Device.h"
|
||||
#include "common/all-versions/default/EffectMap.h"
|
||||
#include "StreamIn.h"
|
||||
#include "StreamOut.h"
|
||||
#include "Util.h"
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <mediautils/MemoryLeakTrackUtil.h>
|
||||
#include <memunreachable/memunreachable.h>
|
||||
|
||||
#include <HidlUtils.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::implementation::HidlUtils;
|
||||
namespace util {
|
||||
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION::implementation::util;
|
||||
}
|
||||
|
||||
Device::Device(audio_hw_device_t* device) : mIsClosed(false), mDevice(device) {}
|
||||
|
||||
Device::~Device() {
|
||||
(void)doClose();
|
||||
mDevice = nullptr;
|
||||
}
|
||||
|
||||
Result Device::analyzeStatus(const char* funcName, int status,
|
||||
const std::vector<int>& ignoreErrors) {
|
||||
return util::analyzeStatus("Device", funcName, status, ignoreErrors);
|
||||
}
|
||||
|
||||
void Device::closeInputStream(audio_stream_in_t* stream) {
|
||||
mDevice->close_input_stream(mDevice, stream);
|
||||
LOG_ALWAYS_FATAL_IF(mOpenedStreamsCount == 0, "mOpenedStreamsCount is already 0");
|
||||
--mOpenedStreamsCount;
|
||||
}
|
||||
|
||||
void Device::closeOutputStream(audio_stream_out_t* stream) {
|
||||
mDevice->close_output_stream(mDevice, stream);
|
||||
LOG_ALWAYS_FATAL_IF(mOpenedStreamsCount == 0, "mOpenedStreamsCount is already 0");
|
||||
--mOpenedStreamsCount;
|
||||
}
|
||||
|
||||
char* Device::halGetParameters(const char* keys) {
|
||||
return mDevice->get_parameters(mDevice, keys);
|
||||
}
|
||||
|
||||
int Device::halSetParameters(const char* keysAndValues) {
|
||||
return mDevice->set_parameters(mDevice, keysAndValues);
|
||||
}
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IDevice follow.
|
||||
Return<Result> Device::initCheck() {
|
||||
return analyzeStatus("init_check", mDevice->init_check(mDevice));
|
||||
}
|
||||
|
||||
Return<Result> Device::setMasterVolume(float volume) {
|
||||
if (mDevice->set_master_volume == NULL) {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
if (!util::isGainNormalized(volume)) {
|
||||
ALOGW("Can not set a master volume (%f) outside [0,1]", volume);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
return analyzeStatus("set_master_volume", mDevice->set_master_volume(mDevice, volume),
|
||||
{ENOSYS} /*ignore*/);
|
||||
}
|
||||
|
||||
Return<void> Device::getMasterVolume(getMasterVolume_cb _hidl_cb) {
|
||||
Result retval(Result::NOT_SUPPORTED);
|
||||
float volume = 0;
|
||||
if (mDevice->get_master_volume != NULL) {
|
||||
retval = analyzeStatus("get_master_volume", mDevice->get_master_volume(mDevice, &volume),
|
||||
{ENOSYS} /*ignore*/);
|
||||
}
|
||||
_hidl_cb(retval, volume);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Device::setMicMute(bool mute) {
|
||||
return analyzeStatus("set_mic_mute", mDevice->set_mic_mute(mDevice, mute), {ENOSYS} /*ignore*/);
|
||||
}
|
||||
|
||||
Return<void> Device::getMicMute(getMicMute_cb _hidl_cb) {
|
||||
bool mute = false;
|
||||
Result retval = analyzeStatus("get_mic_mute", mDevice->get_mic_mute(mDevice, &mute),
|
||||
{ENOSYS} /*ignore*/);
|
||||
_hidl_cb(retval, mute);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Device::setMasterMute(bool mute) {
|
||||
Result retval(Result::NOT_SUPPORTED);
|
||||
if (mDevice->set_master_mute != NULL) {
|
||||
retval = analyzeStatus("set_master_mute", mDevice->set_master_mute(mDevice, mute),
|
||||
{ENOSYS} /*ignore*/);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
Return<void> Device::getMasterMute(getMasterMute_cb _hidl_cb) {
|
||||
Result retval(Result::NOT_SUPPORTED);
|
||||
bool mute = false;
|
||||
if (mDevice->get_master_mute != NULL) {
|
||||
retval = analyzeStatus("get_master_mute", mDevice->get_master_mute(mDevice, &mute),
|
||||
{ENOSYS} /*ignore*/);
|
||||
}
|
||||
_hidl_cb(retval, mute);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::getInputBufferSize(const AudioConfig& config, getInputBufferSize_cb _hidl_cb) {
|
||||
audio_config_t halConfig;
|
||||
Result retval(Result::INVALID_ARGUMENTS);
|
||||
uint64_t bufferSize = 0;
|
||||
if (HidlUtils::audioConfigToHal(config, &halConfig) == NO_ERROR) {
|
||||
size_t halBufferSize = mDevice->get_input_buffer_size(mDevice, &halConfig);
|
||||
if (halBufferSize != 0) {
|
||||
retval = Result::OK;
|
||||
bufferSize = halBufferSize;
|
||||
}
|
||||
}
|
||||
_hidl_cb(retval, bufferSize);
|
||||
return Void();
|
||||
}
|
||||
|
||||
std::tuple<Result, sp<IStreamOut>> Device::openOutputStreamCore(int32_t ioHandle,
|
||||
const DeviceAddress& device,
|
||||
const AudioConfig& config,
|
||||
const AudioOutputFlags& flags,
|
||||
AudioConfig* suggestedConfig) {
|
||||
audio_config_t halConfig;
|
||||
if (HidlUtils::audioConfigToHal(config, &halConfig) != NO_ERROR) {
|
||||
return {Result::INVALID_ARGUMENTS, nullptr};
|
||||
}
|
||||
audio_stream_out_t* halStream;
|
||||
audio_devices_t halDevice;
|
||||
char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
|
||||
if (CoreUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
|
||||
return {Result::INVALID_ARGUMENTS, nullptr};
|
||||
}
|
||||
audio_output_flags_t halFlags;
|
||||
if (CoreUtils::audioOutputFlagsToHal(flags, &halFlags) != NO_ERROR) {
|
||||
return {Result::INVALID_ARGUMENTS, nullptr};
|
||||
}
|
||||
ALOGV("open_output_stream handle: %d devices: %x flags: %#x "
|
||||
"srate: %d format %#x channels %x address %s",
|
||||
ioHandle, halDevice, halFlags, halConfig.sample_rate, halConfig.format,
|
||||
halConfig.channel_mask, halDeviceAddress);
|
||||
int status = mDevice->open_output_stream(mDevice, ioHandle, halDevice, halFlags, &halConfig,
|
||||
&halStream, halDeviceAddress);
|
||||
ALOGV("open_output_stream status %d stream %p", status, halStream);
|
||||
sp<IStreamOut> streamOut;
|
||||
if (status == OK) {
|
||||
streamOut = new StreamOut(this, halStream);
|
||||
++mOpenedStreamsCount;
|
||||
}
|
||||
status_t convertStatus =
|
||||
HidlUtils::audioConfigFromHal(halConfig, false /*isInput*/, suggestedConfig);
|
||||
ALOGW_IF(convertStatus != OK, "%s: suggested config with incompatible fields", __func__);
|
||||
return {analyzeStatus("open_output_stream", status, {EINVAL} /*ignore*/), streamOut};
|
||||
}
|
||||
|
||||
std::tuple<Result, sp<IStreamIn>> Device::openInputStreamCore(
|
||||
int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
|
||||
const AudioInputFlags& flags, AudioSource source, AudioConfig* suggestedConfig) {
|
||||
audio_config_t halConfig;
|
||||
if (HidlUtils::audioConfigToHal(config, &halConfig) != NO_ERROR) {
|
||||
return {Result::INVALID_ARGUMENTS, nullptr};
|
||||
}
|
||||
audio_stream_in_t* halStream;
|
||||
audio_devices_t halDevice;
|
||||
char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
|
||||
if (CoreUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
|
||||
return {Result::INVALID_ARGUMENTS, nullptr};
|
||||
}
|
||||
audio_input_flags_t halFlags;
|
||||
audio_source_t halSource;
|
||||
if (CoreUtils::audioInputFlagsToHal(flags, &halFlags) != NO_ERROR ||
|
||||
HidlUtils::audioSourceToHal(source, &halSource) != NO_ERROR) {
|
||||
return {Result::INVALID_ARGUMENTS, nullptr};
|
||||
}
|
||||
ALOGV("open_input_stream handle: %d devices: %x flags: %#x "
|
||||
"srate: %d format %#x channels %x address %s source %d",
|
||||
ioHandle, halDevice, halFlags, halConfig.sample_rate, halConfig.format,
|
||||
halConfig.channel_mask, halDeviceAddress, halSource);
|
||||
int status = mDevice->open_input_stream(mDevice, ioHandle, halDevice, &halConfig, &halStream,
|
||||
halFlags, halDeviceAddress, halSource);
|
||||
ALOGV("open_input_stream status %d stream %p", status, halStream);
|
||||
sp<IStreamIn> streamIn;
|
||||
if (status == OK) {
|
||||
streamIn = new StreamIn(this, halStream);
|
||||
++mOpenedStreamsCount;
|
||||
}
|
||||
status_t convertStatus =
|
||||
HidlUtils::audioConfigFromHal(halConfig, true /*isInput*/, suggestedConfig);
|
||||
ALOGW_IF(convertStatus != OK, "%s: suggested config with incompatible fields", __func__);
|
||||
return {analyzeStatus("open_input_stream", status, {EINVAL} /*ignore*/), streamIn};
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> Device::openOutputStream(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config, AudioOutputFlags flags,
|
||||
openOutputStream_cb _hidl_cb) {
|
||||
AudioConfig suggestedConfig;
|
||||
auto [result, streamOut] =
|
||||
openOutputStreamCore(ioHandle, device, config, flags, &suggestedConfig);
|
||||
_hidl_cb(result, streamOut, suggestedConfig);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Device::openInputStream(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config, AudioInputFlags flags,
|
||||
AudioSource source, openInputStream_cb _hidl_cb) {
|
||||
AudioConfig suggestedConfig;
|
||||
auto [result, streamIn] =
|
||||
openInputStreamCore(ioHandle, device, config, flags, source, &suggestedConfig);
|
||||
_hidl_cb(result, streamIn, suggestedConfig);
|
||||
return Void();
|
||||
}
|
||||
|
||||
#elif MAJOR_VERSION >= 4
|
||||
std::tuple<Result, sp<IStreamOut>, AudioConfig> Device::openOutputStreamImpl(
|
||||
int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
|
||||
const SourceMetadata& sourceMetadata,
|
||||
#if MAJOR_VERSION <= 6
|
||||
AudioOutputFlags flags) {
|
||||
if (status_t status = CoreUtils::sourceMetadataToHal(sourceMetadata, nullptr);
|
||||
status != NO_ERROR) {
|
||||
#else
|
||||
const AudioOutputFlags& flags) {
|
||||
if (status_t status = CoreUtils::sourceMetadataToHalV7(sourceMetadata,
|
||||
false /*ignoreNonVendorTags*/, nullptr);
|
||||
status != NO_ERROR) {
|
||||
#endif
|
||||
return {analyzeStatus("sourceMetadataToHal", status), nullptr, {}};
|
||||
}
|
||||
AudioConfig suggestedConfig;
|
||||
auto [result, streamOut] =
|
||||
openOutputStreamCore(ioHandle, device, config, flags, &suggestedConfig);
|
||||
if (streamOut) {
|
||||
streamOut->updateSourceMetadata(sourceMetadata);
|
||||
}
|
||||
return {result, streamOut, suggestedConfig};
|
||||
}
|
||||
|
||||
Return<void> Device::openOutputStream(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config,
|
||||
#if MAJOR_VERSION <= 6
|
||||
AudioOutputFlags flags,
|
||||
#else
|
||||
const AudioOutputFlags& flags,
|
||||
#endif
|
||||
const SourceMetadata& sourceMetadata,
|
||||
openOutputStream_cb _hidl_cb) {
|
||||
auto [result, streamOut, suggestedConfig] =
|
||||
openOutputStreamImpl(ioHandle, device, config, sourceMetadata, flags);
|
||||
_hidl_cb(result, streamOut, suggestedConfig);
|
||||
return Void();
|
||||
}
|
||||
|
||||
std::tuple<Result, sp<IStreamIn>, AudioConfig> Device::openInputStreamImpl(
|
||||
int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
|
||||
#if MAJOR_VERSION <= 6
|
||||
AudioInputFlags flags,
|
||||
#else
|
||||
const AudioInputFlags& flags,
|
||||
#endif
|
||||
const SinkMetadata& sinkMetadata) {
|
||||
if (sinkMetadata.tracks.size() == 0) {
|
||||
// This should never happen, the framework must not create as stream
|
||||
// if there is no client
|
||||
ALOGE("openInputStream called without tracks connected");
|
||||
return {Result::INVALID_ARGUMENTS, nullptr, AudioConfig{}};
|
||||
}
|
||||
#if MAJOR_VERSION <= 6
|
||||
if (status_t status = CoreUtils::sinkMetadataToHal(sinkMetadata, nullptr); status != NO_ERROR) {
|
||||
#else
|
||||
if (status_t status = CoreUtils::sinkMetadataToHalV7(sinkMetadata,
|
||||
false /*ignoreNonVendorTags*/, nullptr);
|
||||
status != NO_ERROR) {
|
||||
#endif
|
||||
return {analyzeStatus("sinkMetadataToHal", status), nullptr, AudioConfig{}};
|
||||
}
|
||||
// Pick the first one as the main.
|
||||
AudioSource source = sinkMetadata.tracks[0].source;
|
||||
AudioConfig suggestedConfig;
|
||||
auto [result, streamIn] =
|
||||
openInputStreamCore(ioHandle, device, config, flags, source, &suggestedConfig);
|
||||
if (streamIn) {
|
||||
streamIn->updateSinkMetadata(sinkMetadata);
|
||||
}
|
||||
return {result, streamIn, suggestedConfig};
|
||||
}
|
||||
|
||||
Return<void> Device::openInputStream(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config,
|
||||
#if MAJOR_VERSION <= 6
|
||||
AudioInputFlags flags,
|
||||
#else
|
||||
const AudioInputFlags& flags,
|
||||
#endif
|
||||
const SinkMetadata& sinkMetadata,
|
||||
openInputStream_cb _hidl_cb) {
|
||||
auto [result, streamIn, suggestedConfig] =
|
||||
openInputStreamImpl(ioHandle, device, config, flags, sinkMetadata);
|
||||
_hidl_cb(result, streamIn, suggestedConfig);
|
||||
return Void();
|
||||
}
|
||||
#endif /* MAJOR_VERSION */
|
||||
|
||||
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
|
||||
Return<void> Device::openOutputStream_7_1(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config, const AudioOutputFlags& flags,
|
||||
const SourceMetadata& sourceMetadata,
|
||||
openOutputStream_7_1_cb _hidl_cb) {
|
||||
auto [result, streamOut, suggestedConfig] =
|
||||
openOutputStreamImpl(ioHandle, device, config, sourceMetadata, flags);
|
||||
_hidl_cb(result, streamOut, suggestedConfig);
|
||||
return Void();
|
||||
}
|
||||
#endif // V7.1
|
||||
|
||||
Return<bool> Device::supportsAudioPatches() {
|
||||
return version() >= AUDIO_DEVICE_API_VERSION_3_0;
|
||||
}
|
||||
|
||||
Return<void> Device::createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
|
||||
const hidl_vec<AudioPortConfig>& sinks,
|
||||
createAudioPatch_cb _hidl_cb) {
|
||||
auto [retval, patch] = createOrUpdateAudioPatch(AudioPatchHandle{}, sources, sinks);
|
||||
_hidl_cb(retval, patch);
|
||||
return Void();
|
||||
}
|
||||
|
||||
std::tuple<Result, AudioPatchHandle> Device::createOrUpdateAudioPatch(
|
||||
AudioPatchHandle patch, const hidl_vec<AudioPortConfig>& sources,
|
||||
const hidl_vec<AudioPortConfig>& sinks) {
|
||||
Result retval(Result::NOT_SUPPORTED);
|
||||
if (version() >= AUDIO_DEVICE_API_VERSION_3_0) {
|
||||
audio_patch_handle_t halPatch = static_cast<audio_patch_handle_t>(patch);
|
||||
std::unique_ptr<audio_port_config[]> halSources;
|
||||
if (status_t status = HidlUtils::audioPortConfigsToHal(sources, &halSources);
|
||||
status != NO_ERROR) {
|
||||
return {analyzeStatus("audioPortConfigsToHal;sources", status), patch};
|
||||
}
|
||||
std::unique_ptr<audio_port_config[]> halSinks;
|
||||
if (status_t status = HidlUtils::audioPortConfigsToHal(sinks, &halSinks);
|
||||
status != NO_ERROR) {
|
||||
return {analyzeStatus("audioPortConfigsToHal;sinks", status), patch};
|
||||
}
|
||||
retval = analyzeStatus("create_audio_patch",
|
||||
mDevice->create_audio_patch(mDevice, sources.size(), &halSources[0],
|
||||
sinks.size(), &halSinks[0], &halPatch));
|
||||
if (retval == Result::OK) {
|
||||
patch = static_cast<AudioPatchHandle>(halPatch);
|
||||
}
|
||||
}
|
||||
return {retval, patch};
|
||||
}
|
||||
|
||||
Return<Result> Device::releaseAudioPatch(int32_t patch) {
|
||||
if (version() >= AUDIO_DEVICE_API_VERSION_3_0) {
|
||||
return analyzeStatus(
|
||||
"release_audio_patch",
|
||||
mDevice->release_audio_patch(mDevice, static_cast<audio_patch_handle_t>(patch)));
|
||||
}
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
template <typename HalPort>
|
||||
Return<void> Device::getAudioPortImpl(const AudioPort& port, getAudioPort_cb _hidl_cb,
|
||||
int (*halGetter)(audio_hw_device_t*, HalPort*),
|
||||
const char* halGetterName) {
|
||||
HalPort halPort;
|
||||
if (status_t status = HidlUtils::audioPortToHal(port, &halPort); status != NO_ERROR) {
|
||||
_hidl_cb(analyzeStatus("audioPortToHal", status), port);
|
||||
return Void();
|
||||
}
|
||||
Result retval = analyzeStatus(halGetterName, halGetter(mDevice, &halPort));
|
||||
AudioPort resultPort = port;
|
||||
if (retval == Result::OK) {
|
||||
if (status_t status = HidlUtils::audioPortFromHal(halPort, &resultPort);
|
||||
status != NO_ERROR) {
|
||||
_hidl_cb(analyzeStatus("audioPortFromHal", status), port);
|
||||
return Void();
|
||||
}
|
||||
}
|
||||
_hidl_cb(retval, resultPort);
|
||||
return Void();
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION <= 6
|
||||
Return<void> Device::getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) {
|
||||
return getAudioPortImpl(port, _hidl_cb, mDevice->get_audio_port, "get_audio_port");
|
||||
}
|
||||
#else
|
||||
Return<void> Device::getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) {
|
||||
if (version() >= AUDIO_DEVICE_API_VERSION_3_2) {
|
||||
// get_audio_port_v7 is mandatory if legacy HAL support this API version.
|
||||
return getAudioPortImpl(port, _hidl_cb, mDevice->get_audio_port_v7, "get_audio_port_v7");
|
||||
} else {
|
||||
return getAudioPortImpl(port, _hidl_cb, mDevice->get_audio_port, "get_audio_port");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<Result> Device::setAudioPortConfig(const AudioPortConfig& config) {
|
||||
if (version() >= AUDIO_DEVICE_API_VERSION_3_0) {
|
||||
struct audio_port_config halPortConfig;
|
||||
if (status_t status = HidlUtils::audioPortConfigToHal(config, &halPortConfig);
|
||||
status != NO_ERROR) {
|
||||
return analyzeStatus("audioPortConfigToHal", status);
|
||||
}
|
||||
return analyzeStatus("set_audio_port_config",
|
||||
mDevice->set_audio_port_config(mDevice, &halPortConfig));
|
||||
}
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<AudioHwSync> Device::getHwAvSync() {
|
||||
int halHwAvSync;
|
||||
Result retval = getParam(AudioParameter::keyHwAvSync, &halHwAvSync);
|
||||
return retval == Result::OK ? halHwAvSync : AUDIO_HW_SYNC_INVALID;
|
||||
}
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> Device::getHwAvSync(getHwAvSync_cb _hidl_cb) {
|
||||
int halHwAvSync;
|
||||
Result retval = getParam(AudioParameter::keyHwAvSync, &halHwAvSync);
|
||||
_hidl_cb(retval, halHwAvSync);
|
||||
return Void();
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<Result> Device::setScreenState(bool turnedOn) {
|
||||
return setParam(AudioParameter::keyScreenState, turnedOn);
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> Device::getParameters(const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
|
||||
getParametersImpl({}, keys, _hidl_cb);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Device::setParameters(const hidl_vec<ParameterValue>& parameters) {
|
||||
return setParametersImpl({} /* context */, parameters);
|
||||
}
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> Device::getParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
|
||||
getParametersImpl(context, keys, _hidl_cb);
|
||||
return Void();
|
||||
}
|
||||
Return<Result> Device::setParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<ParameterValue>& parameters) {
|
||||
return setParametersImpl(context, parameters);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> Device::debugDump(const hidl_handle& fd) {
|
||||
return debug(fd, {});
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<void> Device::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
|
||||
if (fd.getNativeHandle() != nullptr && fd->numFds == 1) {
|
||||
const int fd0 = fd->data[0];
|
||||
bool dumpMem = false;
|
||||
bool unreachableMemory = false;
|
||||
for (const auto& option : options) {
|
||||
if (option == "-m") {
|
||||
dumpMem = true;
|
||||
} else if (option == "--unreachable") {
|
||||
unreachableMemory = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (dumpMem) {
|
||||
dprintf(fd0, "\nDumping memory:\n");
|
||||
std::string s = dumpMemoryAddresses(100 /* limit */);
|
||||
write(fd0, s.c_str(), s.size());
|
||||
}
|
||||
if (unreachableMemory) {
|
||||
dprintf(fd0, "\nDumping unreachable memory:\n");
|
||||
// TODO - should limit be an argument parameter?
|
||||
std::string s = GetUnreachableMemoryString(true /* contents */, 100 /* limit */);
|
||||
write(fd0, s.c_str(), s.size());
|
||||
}
|
||||
|
||||
analyzeStatus("dump", mDevice->dump(mDevice, fd0));
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION >= 4
|
||||
Return<void> Device::getMicrophones(getMicrophones_cb _hidl_cb) {
|
||||
Result retval = Result::NOT_SUPPORTED;
|
||||
size_t actual_mics = AUDIO_MICROPHONE_MAX_COUNT;
|
||||
audio_microphone_characteristic_t mic_array[AUDIO_MICROPHONE_MAX_COUNT];
|
||||
|
||||
hidl_vec<MicrophoneInfo> microphones;
|
||||
if (mDevice->get_microphones != NULL &&
|
||||
mDevice->get_microphones(mDevice, &mic_array[0], &actual_mics) == 0) {
|
||||
microphones.resize(actual_mics);
|
||||
for (size_t i = 0; i < actual_mics; ++i) {
|
||||
(void)CoreUtils::microphoneInfoFromHal(mic_array[i], µphones[i]);
|
||||
}
|
||||
retval = Result::OK;
|
||||
}
|
||||
_hidl_cb(retval, microphones);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Device::setConnectedState(const DeviceAddress& address, bool connected) {
|
||||
auto key = connected ? AudioParameter::keyDeviceConnect : AudioParameter::keyDeviceDisconnect;
|
||||
return setParam(key, address);
|
||||
}
|
||||
#endif
|
||||
|
||||
Result Device::doClose() {
|
||||
if (mIsClosed || mOpenedStreamsCount != 0) return Result::INVALID_STATE;
|
||||
mIsClosed = true;
|
||||
return analyzeStatus("close", audio_hw_device_close(mDevice));
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION >= 6
|
||||
Return<Result> Device::close() {
|
||||
return doClose();
|
||||
}
|
||||
|
||||
Return<Result> Device::addDeviceEffect(AudioPortHandle device, uint64_t effectId) {
|
||||
if (version() < AUDIO_DEVICE_API_VERSION_3_1 || mDevice->add_device_effect == nullptr) {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
effect_handle_t halEffect = EffectMap::getInstance().get(effectId);
|
||||
if (halEffect != NULL) {
|
||||
return analyzeStatus("add_device_effect",
|
||||
mDevice->add_device_effect(
|
||||
mDevice, static_cast<audio_port_handle_t>(device), halEffect));
|
||||
} else {
|
||||
ALOGW("%s Invalid effect ID passed from client: %" PRIu64 "", __func__, effectId);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
}
|
||||
|
||||
Return<Result> Device::removeDeviceEffect(AudioPortHandle device, uint64_t effectId) {
|
||||
if (version() < AUDIO_DEVICE_API_VERSION_3_1 || mDevice->remove_device_effect == nullptr) {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
effect_handle_t halEffect = EffectMap::getInstance().get(effectId);
|
||||
if (halEffect != NULL) {
|
||||
return analyzeStatus("remove_device_effect",
|
||||
mDevice->remove_device_effect(
|
||||
mDevice, static_cast<audio_port_handle_t>(device), halEffect));
|
||||
} else {
|
||||
ALOGW("%s Invalid effect ID passed from client: %" PRIu64 "", __func__, effectId);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
}
|
||||
|
||||
Return<void> Device::updateAudioPatch(int32_t previousPatch,
|
||||
const hidl_vec<AudioPortConfig>& sources,
|
||||
const hidl_vec<AudioPortConfig>& sinks,
|
||||
createAudioPatch_cb _hidl_cb) {
|
||||
if (previousPatch != static_cast<int32_t>(AudioPatchHandle{})) {
|
||||
auto [retval, patch] = createOrUpdateAudioPatch(previousPatch, sources, sinks);
|
||||
_hidl_cb(retval, patch);
|
||||
} else {
|
||||
_hidl_cb(Result::INVALID_ARGUMENTS, previousPatch);
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
|
||||
Return<Result> Device::setConnectedState_7_1(const AudioPort& devicePort, bool connected) {
|
||||
if (version() >= AUDIO_DEVICE_API_VERSION_3_2 &&
|
||||
mDevice->set_device_connected_state_v7 != nullptr) {
|
||||
audio_port_v7 halPort;
|
||||
if (status_t status = HidlUtils::audioPortToHal(devicePort, &halPort); status != NO_ERROR) {
|
||||
return analyzeStatus("audioPortToHal", status);
|
||||
}
|
||||
return analyzeStatus("set_device_connected_state_v7",
|
||||
mDevice->set_device_connected_state_v7(mDevice, &halPort, connected));
|
||||
}
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
203
bluetooth/audio/hal/Device.h
Normal file
203
bluetooth/audio/hal/Device.h
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_AUDIO_DEVICE_H
|
||||
#define ANDROID_HARDWARE_AUDIO_DEVICE_H
|
||||
|
||||
#include PATH(android/hardware/audio/FILE_VERSION/IDevice.h)
|
||||
|
||||
#include "ParametersUtil.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <hardware/audio.h>
|
||||
#include <media/AudioParameter.h>
|
||||
|
||||
#include <hidl/Status.h>
|
||||
|
||||
#include <hidl/MQDescriptor.h>
|
||||
|
||||
#include <VersionUtils.h>
|
||||
#include <util/CoreUtils.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::Void;
|
||||
using ::android::hardware::audio::CORE_TYPES_CPP_VERSION::implementation::CoreUtils;
|
||||
using ::android::hardware::audio::CORE_TYPES_CPP_VERSION::implementation::ParametersUtil;
|
||||
using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION;
|
||||
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION;
|
||||
using namespace ::android::hardware::audio::CPP_VERSION;
|
||||
using AudioInputFlags = CoreUtils::AudioInputFlags;
|
||||
using AudioOutputFlags = CoreUtils::AudioOutputFlags;
|
||||
|
||||
struct Device : public IDevice, public ParametersUtil {
|
||||
explicit Device(audio_hw_device_t* device);
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IDevice follow.
|
||||
Return<Result> initCheck() override;
|
||||
Return<Result> setMasterVolume(float volume) override;
|
||||
Return<void> getMasterVolume(getMasterVolume_cb _hidl_cb) override;
|
||||
Return<Result> setMicMute(bool mute) override;
|
||||
Return<void> getMicMute(getMicMute_cb _hidl_cb) override;
|
||||
Return<Result> setMasterMute(bool mute) override;
|
||||
Return<void> getMasterMute(getMasterMute_cb _hidl_cb) override;
|
||||
Return<void> getInputBufferSize(const AudioConfig& config,
|
||||
getInputBufferSize_cb _hidl_cb) override;
|
||||
|
||||
std::tuple<Result, sp<IStreamOut>> openOutputStreamCore(int32_t ioHandle,
|
||||
const DeviceAddress& device,
|
||||
const AudioConfig& config,
|
||||
const AudioOutputFlags& flags,
|
||||
AudioConfig* suggestedConfig);
|
||||
std::tuple<Result, sp<IStreamIn>> openInputStreamCore(
|
||||
int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
|
||||
const AudioInputFlags& flags, AudioSource source, AudioConfig* suggestedConfig);
|
||||
#if MAJOR_VERSION >= 4
|
||||
std::tuple<Result, sp<IStreamOut>, AudioConfig> openOutputStreamImpl(
|
||||
int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
|
||||
const SourceMetadata& sourceMetadata,
|
||||
#if MAJOR_VERSION <= 6
|
||||
AudioOutputFlags flags);
|
||||
#else
|
||||
const AudioOutputFlags& flags);
|
||||
#endif
|
||||
std::tuple<Result, sp<IStreamIn>, AudioConfig> openInputStreamImpl(
|
||||
int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
|
||||
#if MAJOR_VERSION <= 6
|
||||
AudioInputFlags flags,
|
||||
#else
|
||||
const AudioInputFlags& flags,
|
||||
#endif
|
||||
const SinkMetadata& sinkMetadata);
|
||||
#endif // MAJOR_VERSION >= 4
|
||||
|
||||
Return<void> openOutputStream(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config,
|
||||
#if MAJOR_VERSION <= 6
|
||||
AudioOutputFlags flags,
|
||||
#else
|
||||
const AudioOutputFlags& flags,
|
||||
#endif
|
||||
#if MAJOR_VERSION >= 4
|
||||
const SourceMetadata& sourceMetadata,
|
||||
#endif
|
||||
openOutputStream_cb _hidl_cb) override;
|
||||
Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config,
|
||||
#if MAJOR_VERSION <= 6
|
||||
AudioInputFlags flags,
|
||||
#else
|
||||
const AudioInputFlags& flags,
|
||||
#endif
|
||||
#if MAJOR_VERSION == 2
|
||||
AudioSource source,
|
||||
#elif MAJOR_VERSION >= 4
|
||||
const SinkMetadata& sinkMetadata,
|
||||
#endif
|
||||
openInputStream_cb _hidl_cb) override;
|
||||
|
||||
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
|
||||
Return<void> openOutputStream_7_1(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config, const AudioOutputFlags& flags,
|
||||
const SourceMetadata& sourceMetadata,
|
||||
openOutputStream_7_1_cb _hidl_cb) override;
|
||||
#endif
|
||||
|
||||
Return<bool> supportsAudioPatches() override;
|
||||
Return<void> createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
|
||||
const hidl_vec<AudioPortConfig>& sinks,
|
||||
createAudioPatch_cb _hidl_cb) override;
|
||||
Return<Result> releaseAudioPatch(int32_t patch) override;
|
||||
Return<void> getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) override;
|
||||
Return<Result> setAudioPortConfig(const AudioPortConfig& config) override;
|
||||
|
||||
Return<Result> setScreenState(bool turnedOn) override;
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<AudioHwSync> getHwAvSync() override;
|
||||
Return<void> getParameters(const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) override;
|
||||
Return<Result> setParameters(const hidl_vec<ParameterValue>& parameters) override;
|
||||
Return<void> debugDump(const hidl_handle& fd) override;
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> getHwAvSync(getHwAvSync_cb _hidl_cb) override;
|
||||
Return<void> getParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) override;
|
||||
Return<Result> setParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<ParameterValue>& parameters) override;
|
||||
Return<void> getMicrophones(getMicrophones_cb _hidl_cb) override;
|
||||
Return<Result> setConnectedState(const DeviceAddress& address, bool connected) override;
|
||||
#endif
|
||||
#if MAJOR_VERSION >= 6
|
||||
Return<Result> close() override;
|
||||
Return<Result> addDeviceEffect(AudioPortHandle device, uint64_t effectId) override;
|
||||
Return<Result> removeDeviceEffect(AudioPortHandle device, uint64_t effectId) override;
|
||||
Return<void> updateAudioPatch(int32_t previousPatch, const hidl_vec<AudioPortConfig>& sources,
|
||||
const hidl_vec<AudioPortConfig>& sinks,
|
||||
createAudioPatch_cb _hidl_cb) override;
|
||||
#endif
|
||||
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
|
||||
Return<Result> setConnectedState_7_1(const AudioPort& devicePort, bool connected) override;
|
||||
#endif
|
||||
Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
|
||||
|
||||
// Utility methods for extending interfaces.
|
||||
Result analyzeStatus(const char* funcName, int status,
|
||||
const std::vector<int>& ignoreErrors = {});
|
||||
void closeInputStream(audio_stream_in_t* stream);
|
||||
void closeOutputStream(audio_stream_out_t* stream);
|
||||
audio_hw_device_t* device() const { return mDevice; }
|
||||
|
||||
uint32_t version() const { return mDevice->common.version; }
|
||||
|
||||
private:
|
||||
bool mIsClosed;
|
||||
audio_hw_device_t* mDevice;
|
||||
int mOpenedStreamsCount = 0;
|
||||
|
||||
virtual ~Device();
|
||||
|
||||
Result doClose();
|
||||
std::tuple<Result, AudioPatchHandle> createOrUpdateAudioPatch(
|
||||
AudioPatchHandle patch, const hidl_vec<AudioPortConfig>& sources,
|
||||
const hidl_vec<AudioPortConfig>& sinks);
|
||||
template <typename HalPort>
|
||||
Return<void> getAudioPortImpl(const AudioPort& port, getAudioPort_cb _hidl_cb,
|
||||
int (*halGetter)(audio_hw_device_t*, HalPort*),
|
||||
const char* halGetterName);
|
||||
|
||||
// Methods from ParametersUtil.
|
||||
char* halGetParameters(const char* keys) override;
|
||||
int halSetParameters(const char* keysAndValues) override;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUDIO_DEVICE_H
|
152
bluetooth/audio/hal/DevicesFactory.cpp
Normal file
152
bluetooth/audio/hal/DevicesFactory.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "DevicesFactoryHAL"
|
||||
|
||||
#include "DevicesFactory.h"
|
||||
#include "Device.h"
|
||||
#include "PrimaryDevice.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> DevicesFactory::openDevice(IDevicesFactory::Device device, openDevice_cb _hidl_cb) {
|
||||
switch (device) {
|
||||
case IDevicesFactory::Device::PRIMARY:
|
||||
return openDevice<PrimaryDevice>(AUDIO_HARDWARE_MODULE_ID_PRIMARY, _hidl_cb);
|
||||
case IDevicesFactory::Device::A2DP:
|
||||
return openDevice(AUDIO_HARDWARE_MODULE_ID_A2DP, _hidl_cb);
|
||||
case IDevicesFactory::Device::USB:
|
||||
return openDevice(AUDIO_HARDWARE_MODULE_ID_USB, _hidl_cb);
|
||||
case IDevicesFactory::Device::R_SUBMIX:
|
||||
return openDevice(AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX, _hidl_cb);
|
||||
case IDevicesFactory::Device::STUB:
|
||||
return openDevice(AUDIO_HARDWARE_MODULE_ID_STUB, _hidl_cb);
|
||||
}
|
||||
_hidl_cb(Result::INVALID_ARGUMENTS, nullptr);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> DevicesFactory::openDevice(const char* moduleName, openDevice_cb _hidl_cb) {
|
||||
return openDevice<implementation::Device>(moduleName, _hidl_cb);
|
||||
}
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> DevicesFactory::openDevice(const hidl_string& moduleName, openDevice_cb _hidl_cb) {
|
||||
if (moduleName == AUDIO_HARDWARE_MODULE_ID_PRIMARY) {
|
||||
return openDevice<PrimaryDevice>(moduleName.c_str(), _hidl_cb);
|
||||
}
|
||||
return openDevice<implementation::Device>(moduleName.c_str(), _hidl_cb);
|
||||
}
|
||||
Return<void> DevicesFactory::openPrimaryDevice(openPrimaryDevice_cb _hidl_cb) {
|
||||
return openDevice<PrimaryDevice>(AUDIO_HARDWARE_MODULE_ID_PRIMARY, _hidl_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
|
||||
Return<void> DevicesFactory::openDevice_7_1(const hidl_string& moduleName,
|
||||
openDevice_7_1_cb _hidl_cb) {
|
||||
if (moduleName == AUDIO_HARDWARE_MODULE_ID_PRIMARY) {
|
||||
Result result;
|
||||
sp<IPrimaryDevice> primary;
|
||||
auto ret = openDevice<PrimaryDevice>(
|
||||
AUDIO_HARDWARE_MODULE_ID_PRIMARY,
|
||||
[&result, &primary](Result r, const sp<IPrimaryDevice>& p) {
|
||||
result = r;
|
||||
primary = p;
|
||||
});
|
||||
if (ret.isOk() && result == Result::OK && primary != nullptr) {
|
||||
auto getDeviceRet = primary->getDevice();
|
||||
if (getDeviceRet.isOk()) {
|
||||
_hidl_cb(result, getDeviceRet);
|
||||
} else {
|
||||
_hidl_cb(Result::NOT_INITIALIZED, nullptr);
|
||||
}
|
||||
} else {
|
||||
_hidl_cb(result, nullptr);
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
return openDevice<implementation::Device>(moduleName.c_str(), _hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> DevicesFactory::openPrimaryDevice_7_1(openPrimaryDevice_7_1_cb _hidl_cb) {
|
||||
return openDevice<PrimaryDevice>(AUDIO_HARDWARE_MODULE_ID_PRIMARY, _hidl_cb);
|
||||
}
|
||||
#endif // V7.1
|
||||
|
||||
template <class DeviceShim, class Callback>
|
||||
Return<void> DevicesFactory::openDevice(const char* moduleName, Callback _hidl_cb) {
|
||||
audio_hw_device_t* halDevice;
|
||||
Result retval(Result::INVALID_ARGUMENTS);
|
||||
sp<DeviceShim> result;
|
||||
int halStatus = loadAudioInterface(moduleName, &halDevice);
|
||||
if (halStatus == OK) {
|
||||
result = new DeviceShim(halDevice);
|
||||
retval = Result::OK;
|
||||
} else if (halStatus == -EINVAL) {
|
||||
retval = Result::NOT_INITIALIZED;
|
||||
}
|
||||
_hidl_cb(retval, result);
|
||||
return Void();
|
||||
}
|
||||
|
||||
// static
|
||||
int DevicesFactory::loadAudioInterface(const char* if_name, audio_hw_device_t** dev) {
|
||||
const hw_module_t* mod;
|
||||
int rc;
|
||||
|
||||
rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
|
||||
if (rc) {
|
||||
ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID,
|
||||
if_name, strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
rc = audio_hw_device_open(mod, dev);
|
||||
if (rc) {
|
||||
ALOGE("%s couldn't open audio hw device in %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID,
|
||||
if_name, strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) {
|
||||
ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version);
|
||||
rc = -EINVAL;
|
||||
audio_hw_device_close(*dev);
|
||||
goto out;
|
||||
}
|
||||
return OK;
|
||||
|
||||
out:
|
||||
*dev = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
IDevicesFactory* HIDL_FETCH_IDevicesFactory(const char* name) {
|
||||
return strcmp(name, "default") == 0 ? new DevicesFactory() : nullptr;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
70
bluetooth/audio/hal/DevicesFactory.h
Normal file
70
bluetooth/audio/hal/DevicesFactory.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_AUDIO_DEVICESFACTORY_H
|
||||
#define ANDROID_HARDWARE_AUDIO_DEVICESFACTORY_H
|
||||
|
||||
#include PATH(android/hardware/audio/FILE_VERSION/IDevicesFactory.h)
|
||||
|
||||
#include <hardware/audio.h>
|
||||
|
||||
#include <hidl/Status.h>
|
||||
|
||||
#include <hidl/MQDescriptor.h>
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::Void;
|
||||
using namespace ::android::hardware::audio::CPP_VERSION;
|
||||
|
||||
struct DevicesFactory : public IDevicesFactory {
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> openDevice(IDevicesFactory::Device device, openDevice_cb _hidl_cb) override;
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> openDevice(const hidl_string& device, openDevice_cb _hidl_cb) override;
|
||||
Return<void> openPrimaryDevice(openPrimaryDevice_cb _hidl_cb) override;
|
||||
#endif
|
||||
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
|
||||
Return<void> openDevice_7_1(const hidl_string& device, openDevice_7_1_cb _hidl_cb) override;
|
||||
Return<void> openPrimaryDevice_7_1(openPrimaryDevice_7_1_cb _hidl_cb) override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
template <class DeviceShim, class Callback>
|
||||
Return<void> openDevice(const char* moduleName, Callback _hidl_cb);
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> openDevice(const char* moduleName, openDevice_cb _hidl_cb);
|
||||
#endif
|
||||
|
||||
static int loadAudioInterface(const char* if_name, audio_hw_device_t** dev);
|
||||
};
|
||||
|
||||
extern "C" IDevicesFactory* HIDL_FETCH_IDevicesFactory(const char* name);
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUDIO_DEVICESFACTORY_H
|
96
bluetooth/audio/hal/HearingAidAudioProvider.cpp
Normal file
96
bluetooth/audio/hal/HearingAidAudioProvider.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 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 "BTAudioProviderHearingAid"
|
||||
|
||||
#include "HearingAidAudioProvider.h"
|
||||
|
||||
#include <BluetoothAudioCodecs.h>
|
||||
#include <BluetoothAudioSessionReport.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
static constexpr uint32_t kPcmFrameSize = 4; // 16 bits per sample / stereo
|
||||
static constexpr uint32_t kPcmFrameCount = 128;
|
||||
static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
|
||||
static constexpr uint32_t kRtpFrameCount = 7; // max counts by 1 tick (20ms)
|
||||
static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
|
||||
static constexpr uint32_t kBufferCount = 1; // single buffer
|
||||
static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
|
||||
|
||||
HearingAidAudioProvider::HearingAidAudioProvider()
|
||||
: BluetoothAudioProvider(), data_mq_(nullptr) {
|
||||
LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
|
||||
<< " byte(s)";
|
||||
std::unique_ptr<DataMQ> data_mq(
|
||||
new DataMQ(kDataMqSize, /* EventFlag */ true));
|
||||
if (data_mq && data_mq->isValid()) {
|
||||
data_mq_ = std::move(data_mq);
|
||||
session_type_ = SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH;
|
||||
} else {
|
||||
ALOGE_IF(!data_mq, "failed to allocate data MQ");
|
||||
ALOGE_IF(data_mq && !data_mq->isValid(), "data MQ is invalid");
|
||||
}
|
||||
}
|
||||
bool HearingAidAudioProvider::isValid(const SessionType& sessionType) {
|
||||
return (sessionType == session_type_ && data_mq_ && data_mq_->isValid());
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus HearingAidAudioProvider::startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
|
||||
if (audio_config.getTag() != AudioConfiguration::pcmConfig) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
const auto& pcm_config = audio_config.get<AudioConfiguration::pcmConfig>();
|
||||
if (!BluetoothAudioCodecs::IsSoftwarePcmConfigurationValid(pcm_config)) {
|
||||
LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
|
||||
<< pcm_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
return BluetoothAudioProvider::startSession(
|
||||
host_if, audio_config, latency_modes, _aidl_return);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus HearingAidAudioProvider::onSessionReady(
|
||||
DataMQDesc* _aidl_return) {
|
||||
if (data_mq_ == nullptr || !data_mq_->isValid()) {
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
*_aidl_return = data_mq_->dupeDesc();
|
||||
auto desc = data_mq_->dupeDesc();
|
||||
BluetoothAudioSessionReport::OnSessionStarted(
|
||||
session_type_, stack_iface_, &desc, *audio_config_, latency_modes_);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
50
bluetooth/audio/hal/HearingAidAudioProvider.h
Normal file
50
bluetooth/audio/hal/HearingAidAudioProvider.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioProvider.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class HearingAidAudioProvider : public BluetoothAudioProvider {
|
||||
public:
|
||||
HearingAidAudioProvider();
|
||||
|
||||
bool isValid(const SessionType& sessionType) override;
|
||||
|
||||
ndk::ScopedAStatus startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes,
|
||||
DataMQDesc* _aidl_return);
|
||||
|
||||
private:
|
||||
// audio data queue for software encoding
|
||||
std::unique_ptr<DataMQ> data_mq_;
|
||||
|
||||
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
90
bluetooth/audio/hal/LeAudioOffloadAudioProvider.cpp
Normal file
90
bluetooth/audio/hal/LeAudioOffloadAudioProvider.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 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 "BTAudioProviderLeAudioHW"
|
||||
|
||||
#include "LeAudioOffloadAudioProvider.h"
|
||||
|
||||
#include <BluetoothAudioCodecs.h>
|
||||
#include <BluetoothAudioSessionReport.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
LeAudioOffloadOutputAudioProvider::LeAudioOffloadOutputAudioProvider()
|
||||
: LeAudioOffloadAudioProvider() {
|
||||
session_type_ = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
|
||||
}
|
||||
|
||||
LeAudioOffloadInputAudioProvider::LeAudioOffloadInputAudioProvider()
|
||||
: LeAudioOffloadAudioProvider() {
|
||||
session_type_ = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH;
|
||||
}
|
||||
|
||||
LeAudioOffloadBroadcastAudioProvider::LeAudioOffloadBroadcastAudioProvider()
|
||||
: LeAudioOffloadAudioProvider() {
|
||||
session_type_ =
|
||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
|
||||
}
|
||||
|
||||
LeAudioOffloadAudioProvider::LeAudioOffloadAudioProvider()
|
||||
: BluetoothAudioProvider() {}
|
||||
|
||||
bool LeAudioOffloadAudioProvider::isValid(const SessionType& sessionType) {
|
||||
return (sessionType == session_type_);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus LeAudioOffloadAudioProvider::startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
|
||||
if (audio_config.getTag() != AudioConfiguration::leAudioConfig) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
const auto& le_audio_config =
|
||||
audio_config.get<AudioConfiguration::leAudioConfig>();
|
||||
if (!BluetoothAudioCodecs::IsOffloadLeAudioConfigurationValid(
|
||||
session_type_, le_audio_config)) {
|
||||
LOG(WARNING) << __func__ << " - Unsupported LC3 Offloaded Configuration="
|
||||
<< le_audio_config.toString();
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
return BluetoothAudioProvider::startSession(
|
||||
host_if, audio_config, latency_modes, _aidl_return);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSessionReady(
|
||||
DataMQDesc* _aidl_return) {
|
||||
BluetoothAudioSessionReport::OnSessionStarted(
|
||||
session_type_, stack_iface_, nullptr, *audio_config_, latency_modes_);
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
63
bluetooth/audio/hal/LeAudioOffloadAudioProvider.h
Normal file
63
bluetooth/audio/hal/LeAudioOffloadAudioProvider.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioProvider.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
|
||||
public:
|
||||
LeAudioOffloadAudioProvider();
|
||||
|
||||
bool isValid(const SessionType& sessionType) override;
|
||||
|
||||
ndk::ScopedAStatus startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes,
|
||||
DataMQDesc* _aidl_return);
|
||||
|
||||
private:
|
||||
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
|
||||
};
|
||||
|
||||
class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {
|
||||
public:
|
||||
LeAudioOffloadOutputAudioProvider();
|
||||
};
|
||||
|
||||
class LeAudioOffloadInputAudioProvider : public LeAudioOffloadAudioProvider {
|
||||
public:
|
||||
LeAudioOffloadInputAudioProvider();
|
||||
};
|
||||
|
||||
class LeAudioOffloadBroadcastAudioProvider
|
||||
: public LeAudioOffloadAudioProvider {
|
||||
public:
|
||||
LeAudioOffloadBroadcastAudioProvider();
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
148
bluetooth/audio/hal/LeAudioSoftwareAudioProvider.cpp
Normal file
148
bluetooth/audio/hal/LeAudioSoftwareAudioProvider.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 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 "BTAudioProviderLeAudioSW"
|
||||
|
||||
#include "LeAudioSoftwareAudioProvider.h"
|
||||
|
||||
#include <BluetoothAudioCodecs.h>
|
||||
#include <BluetoothAudioSessionReport.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
static constexpr uint32_t kBufferOutCount = 2; // two frame buffer
|
||||
static constexpr uint32_t kBufferInCount = 2; // two frame buffer
|
||||
|
||||
inline uint32_t channel_mode_to_channel_count(ChannelMode channel_mode) {
|
||||
switch (channel_mode) {
|
||||
case ChannelMode::MONO:
|
||||
return 1;
|
||||
case ChannelMode::STEREO:
|
||||
return 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LeAudioSoftwareOutputAudioProvider::LeAudioSoftwareOutputAudioProvider()
|
||||
: LeAudioSoftwareAudioProvider() {
|
||||
session_type_ = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
|
||||
}
|
||||
|
||||
LeAudioSoftwareInputAudioProvider::LeAudioSoftwareInputAudioProvider()
|
||||
: LeAudioSoftwareAudioProvider() {
|
||||
session_type_ = SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH;
|
||||
}
|
||||
|
||||
LeAudioSoftwareBroadcastAudioProvider::LeAudioSoftwareBroadcastAudioProvider()
|
||||
: LeAudioSoftwareAudioProvider() {
|
||||
session_type_ = SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH;
|
||||
}
|
||||
|
||||
LeAudioSoftwareAudioProvider::LeAudioSoftwareAudioProvider()
|
||||
: BluetoothAudioProvider(), data_mq_(nullptr) {}
|
||||
|
||||
bool LeAudioSoftwareAudioProvider::isValid(const SessionType& sessionType) {
|
||||
return (sessionType == session_type_);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus LeAudioSoftwareAudioProvider::startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
|
||||
if (audio_config.getTag() != AudioConfiguration::pcmConfig) {
|
||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||
<< audio_config.toString();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
const auto& pcm_config = audio_config.get<AudioConfiguration::pcmConfig>();
|
||||
if (!BluetoothAudioCodecs::IsSoftwarePcmConfigurationValid(pcm_config)) {
|
||||
LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
|
||||
<< pcm_config.toString();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
uint32_t buffer_modifier = 0;
|
||||
if (session_type_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH)
|
||||
buffer_modifier = kBufferOutCount;
|
||||
else if (session_type_ == SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH)
|
||||
buffer_modifier = kBufferInCount;
|
||||
|
||||
// 24 bit audio stream is sent as unpacked
|
||||
int bytes_per_sample =
|
||||
(pcm_config.bitsPerSample == 24) ? 4 : (pcm_config.bitsPerSample / 8);
|
||||
|
||||
uint32_t data_mq_size =
|
||||
(ceil(pcm_config.sampleRateHz) / 1000) *
|
||||
channel_mode_to_channel_count(pcm_config.channelMode) * bytes_per_sample *
|
||||
(pcm_config.dataIntervalUs / 1000) * buffer_modifier;
|
||||
if (data_mq_size <= 0) {
|
||||
LOG(ERROR) << __func__ << "Unexpected audio buffer size: " << data_mq_size
|
||||
<< ", SampleRateHz: " << pcm_config.sampleRateHz
|
||||
<< ", ChannelMode: " << toString(pcm_config.channelMode)
|
||||
<< ", BitsPerSample: "
|
||||
<< static_cast<int>(pcm_config.bitsPerSample)
|
||||
<< ", BytesPerSample: " << bytes_per_sample
|
||||
<< ", DataIntervalUs: " << pcm_config.dataIntervalUs
|
||||
<< ", SessionType: " << toString(session_type_);
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
LOG(INFO) << __func__ << " - size of audio buffer " << data_mq_size
|
||||
<< " byte(s)";
|
||||
|
||||
std::unique_ptr<DataMQ> temp_data_mq(
|
||||
new DataMQ(data_mq_size, /* EventFlag */ true));
|
||||
if (temp_data_mq == nullptr || !temp_data_mq->isValid()) {
|
||||
ALOGE_IF(!temp_data_mq, "failed to allocate data MQ");
|
||||
ALOGE_IF(temp_data_mq && !temp_data_mq->isValid(), "data MQ is invalid");
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
data_mq_ = std::move(temp_data_mq);
|
||||
|
||||
return BluetoothAudioProvider::startSession(
|
||||
host_if, audio_config, latency_modes, _aidl_return);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus LeAudioSoftwareAudioProvider::onSessionReady(
|
||||
DataMQDesc* _aidl_return) {
|
||||
if (data_mq_ == nullptr || !data_mq_->isValid()) {
|
||||
*_aidl_return = DataMQDesc();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
*_aidl_return = data_mq_->dupeDesc();
|
||||
auto desc = data_mq_->dupeDesc();
|
||||
BluetoothAudioSessionReport::OnSessionStarted(
|
||||
session_type_, stack_iface_, &desc, *audio_config_, latency_modes_);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
66
bluetooth/audio/hal/LeAudioSoftwareAudioProvider.h
Normal file
66
bluetooth/audio/hal/LeAudioSoftwareAudioProvider.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioProvider.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class LeAudioSoftwareAudioProvider : public BluetoothAudioProvider {
|
||||
public:
|
||||
LeAudioSoftwareAudioProvider();
|
||||
|
||||
bool isValid(const SessionType& sessionType) override;
|
||||
|
||||
ndk::ScopedAStatus startSession(
|
||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes,
|
||||
DataMQDesc* _aidl_return);
|
||||
|
||||
private:
|
||||
// audio data queue for software encoding
|
||||
std::unique_ptr<DataMQ> data_mq_;
|
||||
|
||||
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
|
||||
};
|
||||
|
||||
class LeAudioSoftwareOutputAudioProvider : public LeAudioSoftwareAudioProvider {
|
||||
public:
|
||||
LeAudioSoftwareOutputAudioProvider();
|
||||
};
|
||||
|
||||
class LeAudioSoftwareInputAudioProvider : public LeAudioSoftwareAudioProvider {
|
||||
public:
|
||||
LeAudioSoftwareInputAudioProvider();
|
||||
};
|
||||
|
||||
class LeAudioSoftwareBroadcastAudioProvider
|
||||
: public LeAudioSoftwareAudioProvider {
|
||||
public:
|
||||
LeAudioSoftwareBroadcastAudioProvider();
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
174
bluetooth/audio/hal/ParametersUtil.cpp
Normal file
174
bluetooth/audio/hal/ParametersUtil.cpp
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ParametersUtil.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include <system/audio.h>
|
||||
|
||||
#include <util/CoreUtils.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CORE_TYPES_CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
/** Converts a status_t in Result according to the rules of AudioParameter::get*
|
||||
* Note: Static method and not private method to avoid leaking status_t dependency
|
||||
*/
|
||||
static Result getHalStatusToResult(status_t status) {
|
||||
switch (status) {
|
||||
case OK:
|
||||
return Result::OK;
|
||||
case BAD_VALUE: // Nothing was returned, probably because the HAL does
|
||||
// not handle it
|
||||
return Result::NOT_SUPPORTED;
|
||||
case INVALID_OPERATION: // Conversion from string to the requested type
|
||||
// failed
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
default: // Should not happen
|
||||
ALOGW("Unexpected status returned by getParam: %u", status);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
}
|
||||
|
||||
Result ParametersUtil::getParam(const char* name, bool* value) {
|
||||
String8 halValue;
|
||||
Result retval = getParam(name, &halValue);
|
||||
*value = false;
|
||||
if (retval == Result::OK) {
|
||||
if (halValue.empty()) {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
*value = !(halValue == AudioParameter::valueOff);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
Result ParametersUtil::getParam(const char* name, int* value) {
|
||||
const String8 halName(name);
|
||||
AudioParameter keys;
|
||||
keys.addKey(halName);
|
||||
std::unique_ptr<AudioParameter> params = getParams(keys);
|
||||
return getHalStatusToResult(params->getInt(halName, *value));
|
||||
}
|
||||
|
||||
Result ParametersUtil::getParam(const char* name, String8* value, AudioParameter context) {
|
||||
const String8 halName(name);
|
||||
context.addKey(halName);
|
||||
std::unique_ptr<AudioParameter> params = getParams(context);
|
||||
return getHalStatusToResult(params->get(halName, *value));
|
||||
}
|
||||
|
||||
void ParametersUtil::getParametersImpl(
|
||||
const hidl_vec<ParameterValue>& context, const hidl_vec<hidl_string>& keys,
|
||||
std::function<void(Result retval, const hidl_vec<ParameterValue>& parameters)> cb) {
|
||||
AudioParameter halKeys;
|
||||
for (auto& pair : context) {
|
||||
halKeys.add(String8(pair.key.c_str()), String8(pair.value.c_str()));
|
||||
}
|
||||
for (size_t i = 0; i < keys.size(); ++i) {
|
||||
halKeys.addKey(String8(keys[i].c_str()));
|
||||
}
|
||||
std::unique_ptr<AudioParameter> halValues = getParams(halKeys);
|
||||
Result retval =
|
||||
(keys.size() == 0 || halValues->size() != 0) ? Result::OK : Result::NOT_SUPPORTED;
|
||||
hidl_vec<ParameterValue> result;
|
||||
result.resize(halValues->size());
|
||||
String8 halKey, halValue;
|
||||
for (size_t i = 0; i < halValues->size(); ++i) {
|
||||
status_t status = halValues->getAt(i, halKey, halValue);
|
||||
if (status != OK) {
|
||||
result.resize(0);
|
||||
retval = getHalStatusToResult(status);
|
||||
break;
|
||||
}
|
||||
result[i].key = halKey.string();
|
||||
result[i].value = halValue.string();
|
||||
}
|
||||
cb(retval, result);
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioParameter> ParametersUtil::getParams(const AudioParameter& keys) {
|
||||
String8 paramsAndValues;
|
||||
char* halValues = halGetParameters(keys.keysToString().string());
|
||||
if (halValues != NULL) {
|
||||
paramsAndValues.setTo(halValues);
|
||||
free(halValues);
|
||||
} else {
|
||||
paramsAndValues.clear();
|
||||
}
|
||||
return std::unique_ptr<AudioParameter>(new AudioParameter(paramsAndValues));
|
||||
}
|
||||
|
||||
Result ParametersUtil::setParam(const char* name, const char* value) {
|
||||
AudioParameter param;
|
||||
param.add(String8(name), String8(value));
|
||||
return setParams(param);
|
||||
}
|
||||
|
||||
Result ParametersUtil::setParam(const char* name, bool value) {
|
||||
AudioParameter param;
|
||||
param.add(String8(name), String8(value ? AudioParameter::valueOn : AudioParameter::valueOff));
|
||||
return setParams(param);
|
||||
}
|
||||
|
||||
Result ParametersUtil::setParam(const char* name, int value) {
|
||||
AudioParameter param;
|
||||
param.addInt(String8(name), value);
|
||||
return setParams(param);
|
||||
}
|
||||
|
||||
Result ParametersUtil::setParam(const char* name, float value) {
|
||||
AudioParameter param;
|
||||
param.addFloat(String8(name), value);
|
||||
return setParams(param);
|
||||
}
|
||||
|
||||
Result ParametersUtil::setParametersImpl(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<ParameterValue>& parameters) {
|
||||
AudioParameter params;
|
||||
for (auto& pair : context) {
|
||||
params.add(String8(pair.key.c_str()), String8(pair.value.c_str()));
|
||||
}
|
||||
for (size_t i = 0; i < parameters.size(); ++i) {
|
||||
params.add(String8(parameters[i].key.c_str()), String8(parameters[i].value.c_str()));
|
||||
}
|
||||
return setParams(params);
|
||||
}
|
||||
|
||||
Result ParametersUtil::setParam(const char* name, const DeviceAddress& address) {
|
||||
audio_devices_t halDeviceType;
|
||||
char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
|
||||
if (CoreUtils::deviceAddressToHal(address, &halDeviceType, halDeviceAddress) != NO_ERROR) {
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
AudioParameter params{String8(halDeviceAddress)};
|
||||
params.addInt(String8(name), halDeviceType);
|
||||
return setParams(params);
|
||||
}
|
||||
|
||||
Result ParametersUtil::setParams(const AudioParameter& param) {
|
||||
int halStatus = halSetParameters(param.toString().string());
|
||||
return util::analyzeStatus(halStatus);
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CORE_TYPES_CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
73
bluetooth/audio/hal/ParametersUtil.h
Normal file
73
bluetooth/audio/hal/ParametersUtil.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_AUDIO_PARAMETERS_UTIL_H_
|
||||
#define ANDROID_HARDWARE_AUDIO_PARAMETERS_UTIL_H_
|
||||
|
||||
// clang-format off
|
||||
#include PATH(android/hardware/audio/common/COMMON_TYPES_FILE_VERSION/types.h)
|
||||
#include PATH(android/hardware/audio/CORE_TYPES_FILE_VERSION/types.h)
|
||||
// clang-format on
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <hidl/HidlSupport.h>
|
||||
#include <media/AudioParameter.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CORE_TYPES_CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION;
|
||||
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION;
|
||||
|
||||
class ParametersUtil {
|
||||
public:
|
||||
Result setParam(const char* name, const char* value);
|
||||
Result getParam(const char* name, bool* value);
|
||||
Result getParam(const char* name, int* value);
|
||||
Result getParam(const char* name, String8* value, AudioParameter context = {});
|
||||
void getParametersImpl(
|
||||
const hidl_vec<ParameterValue>& context, const hidl_vec<hidl_string>& keys,
|
||||
std::function<void(Result retval, const hidl_vec<ParameterValue>& parameters)> cb);
|
||||
std::unique_ptr<AudioParameter> getParams(const AudioParameter& keys);
|
||||
Result setParam(const char* name, bool value);
|
||||
Result setParam(const char* name, int value);
|
||||
Result setParam(const char* name, float value);
|
||||
Result setParametersImpl(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<ParameterValue>& parameters);
|
||||
Result setParams(const AudioParameter& param);
|
||||
Result setParam(const char* name, const DeviceAddress& address);
|
||||
|
||||
protected:
|
||||
virtual ~ParametersUtil() {}
|
||||
|
||||
virtual char* halGetParameters(const char* keys) = 0;
|
||||
virtual int halSetParameters(const char* keysAndValues) = 0;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CORE_TYPES_CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUDIO_PARAMETERS_UTIL_H_
|
355
bluetooth/audio/hal/PrimaryDevice.cpp
Normal file
355
bluetooth/audio/hal/PrimaryDevice.cpp
Normal file
@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "PrimaryDeviceHAL"
|
||||
|
||||
#include "PrimaryDevice.h"
|
||||
#include "Util.h"
|
||||
|
||||
#if MAJOR_VERSION >= 4
|
||||
#include <cmath>
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
namespace util {
|
||||
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION::implementation::util;
|
||||
}
|
||||
|
||||
PrimaryDevice::PrimaryDevice(audio_hw_device_t* device) : mDevice(new Device(device)) {}
|
||||
|
||||
PrimaryDevice::~PrimaryDevice() {
|
||||
// Do not call mDevice->close here. If there are any unclosed streams,
|
||||
// they only hold IDevice instance, not IPrimaryDevice, thus IPrimaryDevice
|
||||
// "part" of a device can be destroyed before the streams.
|
||||
}
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IDevice follow.
|
||||
Return<Result> PrimaryDevice::initCheck() {
|
||||
return mDevice->initCheck();
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::setMasterVolume(float volume) {
|
||||
return mDevice->setMasterVolume(volume);
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::getMasterVolume(getMasterVolume_cb _hidl_cb) {
|
||||
return mDevice->getMasterVolume(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::setMicMute(bool mute) {
|
||||
return mDevice->setMicMute(mute);
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::getMicMute(getMicMute_cb _hidl_cb) {
|
||||
return mDevice->getMicMute(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::setMasterMute(bool mute) {
|
||||
return mDevice->setMasterMute(mute);
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::getMasterMute(getMasterMute_cb _hidl_cb) {
|
||||
return mDevice->getMasterMute(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::getInputBufferSize(const AudioConfig& config,
|
||||
getInputBufferSize_cb _hidl_cb) {
|
||||
return mDevice->getInputBufferSize(config, _hidl_cb);
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> PrimaryDevice::openOutputStream(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config, AudioOutputFlags flags,
|
||||
openOutputStream_cb _hidl_cb) {
|
||||
return mDevice->openOutputStream(ioHandle, device, config, flags, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::openInputStream(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config, AudioInputFlags flags,
|
||||
AudioSource source, openInputStream_cb _hidl_cb) {
|
||||
return mDevice->openInputStream(ioHandle, device, config, flags, source, _hidl_cb);
|
||||
}
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> PrimaryDevice::openOutputStream(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config,
|
||||
#if MAJOR_VERSION <= 6
|
||||
AudioOutputFlags flags,
|
||||
#else
|
||||
const AudioOutputFlags& flags,
|
||||
#endif
|
||||
const SourceMetadata& sourceMetadata,
|
||||
openOutputStream_cb _hidl_cb) {
|
||||
return mDevice->openOutputStream(ioHandle, device, config, flags, sourceMetadata, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::openInputStream(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config,
|
||||
#if MAJOR_VERSION <= 6
|
||||
AudioInputFlags flags,
|
||||
#else
|
||||
const AudioInputFlags& flags,
|
||||
#endif
|
||||
const SinkMetadata& sinkMetadata,
|
||||
openInputStream_cb _hidl_cb) {
|
||||
return mDevice->openInputStream(ioHandle, device, config, flags, sinkMetadata, _hidl_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<bool> PrimaryDevice::supportsAudioPatches() {
|
||||
return mDevice->supportsAudioPatches();
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
|
||||
const hidl_vec<AudioPortConfig>& sinks,
|
||||
createAudioPatch_cb _hidl_cb) {
|
||||
return mDevice->createAudioPatch(sources, sinks, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::releaseAudioPatch(int32_t patch) {
|
||||
return mDevice->releaseAudioPatch(patch);
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) {
|
||||
return mDevice->getAudioPort(port, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::setAudioPortConfig(const AudioPortConfig& config) {
|
||||
return mDevice->setAudioPortConfig(config);
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::setScreenState(bool turnedOn) {
|
||||
return mDevice->setScreenState(turnedOn);
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<AudioHwSync> PrimaryDevice::getHwAvSync() {
|
||||
return mDevice->getHwAvSync();
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::getParameters(const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) {
|
||||
return mDevice->getParameters(keys, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::setParameters(const hidl_vec<ParameterValue>& parameters) {
|
||||
return mDevice->setParameters(parameters);
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::debugDump(const hidl_handle& fd) {
|
||||
return mDevice->debugDump(fd);
|
||||
}
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> PrimaryDevice::getHwAvSync(getHwAvSync_cb _hidl_cb) {
|
||||
return mDevice->getHwAvSync(_hidl_cb);
|
||||
}
|
||||
Return<void> PrimaryDevice::getParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) {
|
||||
return mDevice->getParameters(context, keys, _hidl_cb);
|
||||
}
|
||||
Return<Result> PrimaryDevice::setParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<ParameterValue>& parameters) {
|
||||
return mDevice->setParameters(context, parameters);
|
||||
}
|
||||
Return<void> PrimaryDevice::getMicrophones(getMicrophones_cb _hidl_cb) {
|
||||
return mDevice->getMicrophones(_hidl_cb);
|
||||
}
|
||||
Return<Result> PrimaryDevice::setConnectedState(const DeviceAddress& address, bool connected) {
|
||||
return mDevice->setConnectedState(address, connected);
|
||||
}
|
||||
#endif
|
||||
#if MAJOR_VERSION >= 6
|
||||
Return<Result> PrimaryDevice::close() {
|
||||
return mDevice->close();
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::addDeviceEffect(AudioPortHandle device, uint64_t effectId) {
|
||||
return mDevice->addDeviceEffect(device, effectId);
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::removeDeviceEffect(AudioPortHandle device, uint64_t effectId) {
|
||||
return mDevice->removeDeviceEffect(device, effectId);
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::updateAudioPatch(int32_t previousPatch,
|
||||
const hidl_vec<AudioPortConfig>& sources,
|
||||
const hidl_vec<AudioPortConfig>& sinks,
|
||||
updateAudioPatch_cb _hidl_cb) {
|
||||
return mDevice->updateAudioPatch(previousPatch, sources, sinks, _hidl_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IPrimaryDevice follow.
|
||||
Return<Result> PrimaryDevice::setVoiceVolume(float volume) {
|
||||
if (!util::isGainNormalized(volume)) {
|
||||
ALOGW("Can not set a voice volume (%f) outside [0,1]", volume);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
return mDevice->analyzeStatus("set_voice_volume",
|
||||
mDevice->device()->set_voice_volume(mDevice->device(), volume));
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::setMode(AudioMode mode) {
|
||||
// INVALID, CURRENT, CNT, MAX are reserved for internal use.
|
||||
// TODO: remove the values from the HIDL interface
|
||||
switch (mode) {
|
||||
case AudioMode::NORMAL:
|
||||
case AudioMode::RINGTONE:
|
||||
case AudioMode::IN_CALL:
|
||||
case AudioMode::IN_COMMUNICATION:
|
||||
#if MAJOR_VERSION >= 6
|
||||
case AudioMode::CALL_SCREEN:
|
||||
#endif
|
||||
break; // Valid values
|
||||
default:
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
};
|
||||
|
||||
return mDevice->analyzeStatus(
|
||||
"set_mode",
|
||||
mDevice->device()->set_mode(mDevice->device(), static_cast<audio_mode_t>(mode)));
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::getBtScoNrecEnabled(getBtScoNrecEnabled_cb _hidl_cb) {
|
||||
bool enabled;
|
||||
Result retval = mDevice->getParam(AudioParameter::keyBtNrec, &enabled);
|
||||
_hidl_cb(retval, enabled);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::setBtScoNrecEnabled(bool enabled) {
|
||||
return mDevice->setParam(AudioParameter::keyBtNrec, enabled);
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::getBtScoWidebandEnabled(getBtScoWidebandEnabled_cb _hidl_cb) {
|
||||
bool enabled;
|
||||
Result retval = mDevice->getParam(AUDIO_PARAMETER_KEY_BT_SCO_WB, &enabled);
|
||||
_hidl_cb(retval, enabled);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::setBtScoWidebandEnabled(bool enabled) {
|
||||
return mDevice->setParam(AUDIO_PARAMETER_KEY_BT_SCO_WB, enabled);
|
||||
}
|
||||
|
||||
static const char* convertTtyModeFromHIDL(IPrimaryDevice::TtyMode mode) {
|
||||
switch (mode) {
|
||||
case IPrimaryDevice::TtyMode::OFF:
|
||||
return AUDIO_PARAMETER_VALUE_TTY_OFF;
|
||||
case IPrimaryDevice::TtyMode::VCO:
|
||||
return AUDIO_PARAMETER_VALUE_TTY_VCO;
|
||||
case IPrimaryDevice::TtyMode::HCO:
|
||||
return AUDIO_PARAMETER_VALUE_TTY_HCO;
|
||||
case IPrimaryDevice::TtyMode::FULL:
|
||||
return AUDIO_PARAMETER_VALUE_TTY_FULL;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
static IPrimaryDevice::TtyMode convertTtyModeToHIDL(const char* halMode) {
|
||||
if (strcmp(halMode, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0)
|
||||
return IPrimaryDevice::TtyMode::OFF;
|
||||
else if (strcmp(halMode, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0)
|
||||
return IPrimaryDevice::TtyMode::VCO;
|
||||
else if (strcmp(halMode, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0)
|
||||
return IPrimaryDevice::TtyMode::HCO;
|
||||
else if (strcmp(halMode, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0)
|
||||
return IPrimaryDevice::TtyMode::FULL;
|
||||
return IPrimaryDevice::TtyMode(-1);
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::getTtyMode(getTtyMode_cb _hidl_cb) {
|
||||
String8 halMode;
|
||||
Result retval = mDevice->getParam(AUDIO_PARAMETER_KEY_TTY_MODE, &halMode);
|
||||
if (retval != Result::OK) {
|
||||
_hidl_cb(retval, TtyMode::OFF);
|
||||
return Void();
|
||||
}
|
||||
TtyMode mode = convertTtyModeToHIDL(halMode);
|
||||
if (mode == TtyMode(-1)) {
|
||||
ALOGE("HAL returned invalid TTY value: %s", halMode.c_str());
|
||||
_hidl_cb(Result::INVALID_STATE, TtyMode::OFF);
|
||||
return Void();
|
||||
}
|
||||
_hidl_cb(Result::OK, mode);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::setTtyMode(IPrimaryDevice::TtyMode mode) {
|
||||
const char* modeStr = convertTtyModeFromHIDL(mode);
|
||||
if (modeStr == nullptr) {
|
||||
ALOGW("Can not set an invalid TTY value: %d", mode);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
return mDevice->setParam(AUDIO_PARAMETER_KEY_TTY_MODE, modeStr);
|
||||
}
|
||||
|
||||
Return<void> PrimaryDevice::getHacEnabled(getHacEnabled_cb _hidl_cb) {
|
||||
bool enabled;
|
||||
Result retval = mDevice->getParam(AUDIO_PARAMETER_KEY_HAC, &enabled);
|
||||
_hidl_cb(retval, enabled);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> PrimaryDevice::setHacEnabled(bool enabled) {
|
||||
return mDevice->setParam(AUDIO_PARAMETER_KEY_HAC, enabled);
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION >= 4
|
||||
Return<Result> PrimaryDevice::setBtScoHeadsetDebugName(const hidl_string& name) {
|
||||
return mDevice->setParam(AUDIO_PARAMETER_KEY_BT_SCO_HEADSET_NAME, name.c_str());
|
||||
}
|
||||
Return<void> PrimaryDevice::getBtHfpEnabled(getBtHfpEnabled_cb _hidl_cb) {
|
||||
bool enabled;
|
||||
Result retval = mDevice->getParam(AUDIO_PARAMETER_KEY_HFP_ENABLE, &enabled);
|
||||
_hidl_cb(retval, enabled);
|
||||
return Void();
|
||||
}
|
||||
Return<Result> PrimaryDevice::setBtHfpEnabled(bool enabled) {
|
||||
return mDevice->setParam(AUDIO_PARAMETER_KEY_HFP_ENABLE, enabled);
|
||||
}
|
||||
Return<Result> PrimaryDevice::setBtHfpSampleRate(uint32_t sampleRateHz) {
|
||||
return mDevice->setParam(AUDIO_PARAMETER_KEY_HFP_SET_SAMPLING_RATE, int(sampleRateHz));
|
||||
}
|
||||
Return<Result> PrimaryDevice::setBtHfpVolume(float volume) {
|
||||
if (!util::isGainNormalized(volume)) {
|
||||
ALOGW("Can not set BT HFP volume (%f) outside [0,1]", volume);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
// Map the normalized volume onto the range of [0, 15]
|
||||
return mDevice->setParam(AUDIO_PARAMETER_KEY_HFP_VOLUME,
|
||||
static_cast<int>(std::round(volume * 15)));
|
||||
}
|
||||
Return<Result> PrimaryDevice::updateRotation(IPrimaryDevice::Rotation rotation) {
|
||||
// legacy API expects the rotation in degree
|
||||
return mDevice->setParam(AUDIO_PARAMETER_KEY_ROTATION, int(rotation) * 90);
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<void> PrimaryDevice::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
|
||||
return mDevice->debug(fd, options);
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
154
bluetooth/audio/hal/PrimaryDevice.h
Normal file
154
bluetooth/audio/hal/PrimaryDevice.h
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_AUDIO_PRIMARYDEVICE_H
|
||||
#define ANDROID_HARDWARE_AUDIO_PRIMARYDEVICE_H
|
||||
|
||||
#include PATH(android/hardware/audio/FILE_VERSION/IPrimaryDevice.h)
|
||||
|
||||
#include "Device.h"
|
||||
|
||||
#include <hidl/Status.h>
|
||||
|
||||
#include <hidl/MQDescriptor.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::Void;
|
||||
using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION;
|
||||
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION;
|
||||
using namespace ::android::hardware::audio::CPP_VERSION;
|
||||
|
||||
struct PrimaryDevice : public IPrimaryDevice {
|
||||
explicit PrimaryDevice(audio_hw_device_t* device);
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IDevice follow.
|
||||
Return<Result> initCheck() override;
|
||||
Return<Result> setMasterVolume(float volume) override;
|
||||
Return<void> getMasterVolume(getMasterVolume_cb _hidl_cb) override;
|
||||
Return<Result> setMicMute(bool mute) override;
|
||||
Return<void> getMicMute(getMicMute_cb _hidl_cb) override;
|
||||
Return<Result> setMasterMute(bool mute) override;
|
||||
Return<void> getMasterMute(getMasterMute_cb _hidl_cb) override;
|
||||
Return<void> getInputBufferSize(const AudioConfig& config,
|
||||
getInputBufferSize_cb _hidl_cb) override;
|
||||
|
||||
Return<void> openOutputStream(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config,
|
||||
#if MAJOR_VERSION <= 6
|
||||
AudioOutputFlags flags,
|
||||
#else
|
||||
const AudioOutputFlags& flags,
|
||||
#endif
|
||||
#if MAJOR_VERSION >= 4
|
||||
const SourceMetadata& sourceMetadata,
|
||||
#endif
|
||||
openOutputStream_cb _hidl_cb) override;
|
||||
Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
|
||||
const AudioConfig& config,
|
||||
#if MAJOR_VERSION <= 6
|
||||
AudioInputFlags flags,
|
||||
#else
|
||||
const AudioInputFlags& flags,
|
||||
#endif
|
||||
#if MAJOR_VERSION == 2
|
||||
AudioSource source,
|
||||
#elif MAJOR_VERSION >= 4
|
||||
const SinkMetadata& sinkMetadata,
|
||||
#endif
|
||||
openInputStream_cb _hidl_cb) override;
|
||||
|
||||
Return<bool> supportsAudioPatches() override;
|
||||
Return<void> createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
|
||||
const hidl_vec<AudioPortConfig>& sinks,
|
||||
createAudioPatch_cb _hidl_cb) override;
|
||||
Return<Result> releaseAudioPatch(int32_t patch) override;
|
||||
Return<void> getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) override;
|
||||
Return<Result> setAudioPortConfig(const AudioPortConfig& config) override;
|
||||
|
||||
Return<Result> setScreenState(bool turnedOn) override;
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<AudioHwSync> getHwAvSync() override;
|
||||
Return<void> getParameters(const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) override;
|
||||
Return<Result> setParameters(const hidl_vec<ParameterValue>& parameters) override;
|
||||
Return<void> debugDump(const hidl_handle& fd) override;
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> getHwAvSync(getHwAvSync_cb _hidl_cb) override;
|
||||
Return<void> getParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) override;
|
||||
Return<Result> setParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<ParameterValue>& parameters) override;
|
||||
Return<void> getMicrophones(getMicrophones_cb _hidl_cb) override;
|
||||
Return<Result> setConnectedState(const DeviceAddress& address, bool connected) override;
|
||||
#endif
|
||||
#if MAJOR_VERSION >= 6
|
||||
Return<Result> close() override;
|
||||
Return<Result> addDeviceEffect(AudioPortHandle device, uint64_t effectId) override;
|
||||
Return<Result> removeDeviceEffect(AudioPortHandle device, uint64_t effectId) override;
|
||||
Return<void> updateAudioPatch(int32_t previousPatch, const hidl_vec<AudioPortConfig>& sources,
|
||||
const hidl_vec<AudioPortConfig>& sinks,
|
||||
updateAudioPatch_cb _hidl_cb) override;
|
||||
#endif
|
||||
|
||||
Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IPrimaryDevice follow.
|
||||
Return<Result> setVoiceVolume(float volume) override;
|
||||
Return<Result> setMode(AudioMode mode) override;
|
||||
Return<void> getBtScoNrecEnabled(getBtScoNrecEnabled_cb _hidl_cb) override;
|
||||
Return<Result> setBtScoNrecEnabled(bool enabled) override;
|
||||
Return<void> getBtScoWidebandEnabled(getBtScoWidebandEnabled_cb _hidl_cb) override;
|
||||
Return<Result> setBtScoWidebandEnabled(bool enabled) override;
|
||||
Return<void> getTtyMode(getTtyMode_cb _hidl_cb) override;
|
||||
Return<Result> setTtyMode(IPrimaryDevice::TtyMode mode) override;
|
||||
Return<void> getHacEnabled(getHacEnabled_cb _hidl_cb) override;
|
||||
Return<Result> setHacEnabled(bool enabled) override;
|
||||
|
||||
#if MAJOR_VERSION >= 4
|
||||
Return<Result> setBtScoHeadsetDebugName(const hidl_string& name) override;
|
||||
Return<void> getBtHfpEnabled(getBtHfpEnabled_cb _hidl_cb) override;
|
||||
Return<Result> setBtHfpEnabled(bool enabled) override;
|
||||
Return<Result> setBtHfpSampleRate(uint32_t sampleRateHz) override;
|
||||
Return<Result> setBtHfpVolume(float volume) override;
|
||||
Return<Result> updateRotation(IPrimaryDevice::Rotation rotation) override;
|
||||
#endif
|
||||
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
|
||||
Return<sp<::android::hardware::audio::V7_1::IDevice>> getDevice() override { return mDevice; }
|
||||
#endif
|
||||
private:
|
||||
sp<Device> mDevice;
|
||||
|
||||
virtual ~PrimaryDevice();
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUDIO_PRIMARYDEVICE_H
|
457
bluetooth/audio/hal/Stream.cpp
Normal file
457
bluetooth/audio/hal/Stream.cpp
Normal file
@ -0,0 +1,457 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "StreamHAL"
|
||||
|
||||
#include "Stream.h"
|
||||
#include "common/all-versions/HidlSupport.h"
|
||||
#include "common/all-versions/default/EffectMap.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <HidlUtils.h>
|
||||
#include <android/log.h>
|
||||
#include <hardware/audio.h>
|
||||
#include <hardware/audio_effect.h>
|
||||
#include <media/AudioContainers.h>
|
||||
#include <media/TypeConverter.h>
|
||||
#include <util/CoreUtils.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::implementation::HidlUtils;
|
||||
using ::android::hardware::audio::common::utils::splitString;
|
||||
using ::android::hardware::audio::CORE_TYPES_CPP_VERSION::implementation::CoreUtils;
|
||||
namespace util {
|
||||
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION::implementation::util;
|
||||
}
|
||||
|
||||
Stream::Stream(bool isInput, audio_stream_t* stream) : mIsInput(isInput), mStream(stream) {
|
||||
(void)mIsInput; // prevent 'unused field' warnings in pre-V7 versions.
|
||||
}
|
||||
|
||||
Stream::~Stream() {
|
||||
mStream = nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
Result Stream::analyzeStatus(const char* funcName, int status) {
|
||||
return util::analyzeStatus("stream", funcName, status);
|
||||
}
|
||||
|
||||
// static
|
||||
Result Stream::analyzeStatus(const char* funcName, int status,
|
||||
const std::vector<int>& ignoreErrors) {
|
||||
return util::analyzeStatus("stream", funcName, status, ignoreErrors);
|
||||
}
|
||||
|
||||
char* Stream::halGetParameters(const char* keys) {
|
||||
return mStream->get_parameters(mStream, keys);
|
||||
}
|
||||
|
||||
int Stream::halSetParameters(const char* keysAndValues) {
|
||||
return mStream->set_parameters(mStream, keysAndValues);
|
||||
}
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IStream follow.
|
||||
Return<uint64_t> Stream::getFrameSize() {
|
||||
// Needs to be implemented by interface subclasses. But can't be declared as pure virtual,
|
||||
// since interface subclasses implementation do not inherit from this class.
|
||||
LOG_ALWAYS_FATAL("Stream::getFrameSize is pure abstract");
|
||||
return uint64_t{};
|
||||
}
|
||||
|
||||
Return<uint64_t> Stream::getFrameCount() {
|
||||
int halFrameCount;
|
||||
Result retval = getParam(AudioParameter::keyFrameCount, &halFrameCount);
|
||||
return retval == Result::OK ? halFrameCount : 0;
|
||||
}
|
||||
|
||||
Return<uint64_t> Stream::getBufferSize() {
|
||||
return mStream->get_buffer_size(mStream);
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION <= 6
|
||||
Return<uint32_t> Stream::getSampleRate() {
|
||||
return mStream->get_sample_rate(mStream);
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> Stream::getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) {
|
||||
return getSupportedSampleRates(getFormat(), _hidl_cb);
|
||||
}
|
||||
Return<void> Stream::getSupportedChannelMasks(getSupportedChannelMasks_cb _hidl_cb) {
|
||||
return getSupportedChannelMasks(getFormat(), _hidl_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<void> Stream::getSupportedSampleRates(AudioFormat format,
|
||||
getSupportedSampleRates_cb _hidl_cb) {
|
||||
AudioParameter context;
|
||||
context.addInt(String8(AUDIO_PARAMETER_STREAM_FORMAT), int(format));
|
||||
String8 halListValue;
|
||||
Result result =
|
||||
getParam(AudioParameter::keyStreamSupportedSamplingRates, &halListValue, context);
|
||||
hidl_vec<uint32_t> sampleRates;
|
||||
SampleRateSet halSampleRates;
|
||||
if (result == Result::OK) {
|
||||
halSampleRates =
|
||||
samplingRatesFromString(halListValue.string(), AudioParameter::valueListSeparator);
|
||||
sampleRates = hidl_vec<uint32_t>(halSampleRates.begin(), halSampleRates.end());
|
||||
// Legacy get_parameter does not return a status_t, thus can not advertise of failure.
|
||||
// Note that this method must succeed (non empty list) if the format is supported.
|
||||
if (sampleRates.size() == 0) {
|
||||
result = Result::NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
#if MAJOR_VERSION == 2
|
||||
_hidl_cb(sampleRates);
|
||||
#elif MAJOR_VERSION >= 4
|
||||
_hidl_cb(result, sampleRates);
|
||||
#endif
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Stream::getSupportedChannelMasks(AudioFormat format,
|
||||
getSupportedChannelMasks_cb _hidl_cb) {
|
||||
AudioParameter context;
|
||||
context.addInt(String8(AUDIO_PARAMETER_STREAM_FORMAT), int(format));
|
||||
String8 halListValue;
|
||||
Result result = getParam(AudioParameter::keyStreamSupportedChannels, &halListValue, context);
|
||||
hidl_vec<AudioChannelBitfield> channelMasks;
|
||||
ChannelMaskSet halChannelMasks;
|
||||
if (result == Result::OK) {
|
||||
halChannelMasks =
|
||||
channelMasksFromString(halListValue.string(), AudioParameter::valueListSeparator);
|
||||
channelMasks.resize(halChannelMasks.size());
|
||||
size_t i = 0;
|
||||
for (auto channelMask : halChannelMasks) {
|
||||
channelMasks[i++] = AudioChannelBitfield(channelMask);
|
||||
}
|
||||
// Legacy get_parameter does not return a status_t, thus can not advertise of failure.
|
||||
// Note that this method must succeed (non empty list) if the format is supported.
|
||||
if (channelMasks.size() == 0) {
|
||||
result = Result::NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
#if MAJOR_VERSION == 2
|
||||
_hidl_cb(channelMasks);
|
||||
#elif MAJOR_VERSION >= 4
|
||||
_hidl_cb(result, channelMasks);
|
||||
#endif
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Stream::setSampleRate(uint32_t sampleRateHz) {
|
||||
return setParam(AudioParameter::keySamplingRate, static_cast<int>(sampleRateHz));
|
||||
}
|
||||
|
||||
Return<AudioChannelBitfield> Stream::getChannelMask() {
|
||||
return AudioChannelBitfield(mStream->get_channels(mStream));
|
||||
}
|
||||
|
||||
Return<Result> Stream::setChannelMask(AudioChannelBitfield mask) {
|
||||
return setParam(AudioParameter::keyChannels, static_cast<int>(mask));
|
||||
}
|
||||
|
||||
Return<AudioFormat> Stream::getFormat() {
|
||||
return AudioFormat(mStream->get_format(mStream));
|
||||
}
|
||||
|
||||
Return<void> Stream::getSupportedFormats(getSupportedFormats_cb _hidl_cb) {
|
||||
String8 halListValue;
|
||||
Result result = getParam(AudioParameter::keyStreamSupportedFormats, &halListValue);
|
||||
hidl_vec<AudioFormat> formats;
|
||||
FormatVector halFormats;
|
||||
if (result == Result::OK) {
|
||||
halFormats = formatsFromString(halListValue.string(), AudioParameter::valueListSeparator);
|
||||
formats.resize(halFormats.size());
|
||||
for (size_t i = 0; i < halFormats.size(); ++i) {
|
||||
formats[i] = AudioFormat(halFormats[i]);
|
||||
}
|
||||
// Legacy get_parameter does not return a status_t, thus can not advertise of failure.
|
||||
// Note that the method must not return an empty list if this capability is supported.
|
||||
if (formats.size() == 0) {
|
||||
result = Result::NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
#if MAJOR_VERSION <= 5
|
||||
_hidl_cb(formats);
|
||||
#elif MAJOR_VERSION >= 6
|
||||
_hidl_cb(result, formats);
|
||||
#endif
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Stream::setFormat(AudioFormat format) {
|
||||
return setParam(AudioParameter::keyFormat, static_cast<int>(format));
|
||||
}
|
||||
|
||||
Return<void> Stream::getAudioProperties(getAudioProperties_cb _hidl_cb) {
|
||||
uint32_t halSampleRate = mStream->get_sample_rate(mStream);
|
||||
audio_channel_mask_t halMask = mStream->get_channels(mStream);
|
||||
audio_format_t halFormat = mStream->get_format(mStream);
|
||||
_hidl_cb(halSampleRate, AudioChannelBitfield(halMask), AudioFormat(halFormat));
|
||||
return Void();
|
||||
}
|
||||
|
||||
#else // MAJOR_VERSION <= 6
|
||||
|
||||
Return<void> Stream::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
|
||||
String8 halListValue;
|
||||
Result result = getParam(AudioParameter::keyStreamSupportedFormats, &halListValue);
|
||||
hidl_vec<AudioProfile> profiles;
|
||||
if (result != Result::OK) {
|
||||
_hidl_cb(result, profiles);
|
||||
return Void();
|
||||
}
|
||||
// Ensure that the separator is one character, despite that it's defined as a C string.
|
||||
static_assert(sizeof(AUDIO_PARAMETER_VALUE_LIST_SEPARATOR) == 2);
|
||||
std::vector<std::string> halFormats =
|
||||
splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
|
||||
hidl_vec<AudioFormat> formats;
|
||||
(void)HidlUtils::audioFormatsFromHal(halFormats, &formats);
|
||||
std::vector<AudioProfile> tempProfiles;
|
||||
for (const auto& format : formats) {
|
||||
audio_format_t halFormat;
|
||||
if (status_t status = HidlUtils::audioFormatToHal(format, &halFormat); status != NO_ERROR) {
|
||||
continue;
|
||||
}
|
||||
AudioParameter context;
|
||||
context.addInt(String8(AUDIO_PARAMETER_STREAM_FORMAT), int(halFormat));
|
||||
// Query supported sample rates for the format.
|
||||
result = getParam(AudioParameter::keyStreamSupportedSamplingRates, &halListValue, context);
|
||||
if (result != Result::OK) break;
|
||||
std::vector<std::string> halSampleRates =
|
||||
splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
|
||||
hidl_vec<uint32_t> sampleRates;
|
||||
sampleRates.resize(halSampleRates.size());
|
||||
for (size_t i = 0; i < sampleRates.size(); ++i) {
|
||||
sampleRates[i] = std::stoi(halSampleRates[i]);
|
||||
}
|
||||
// Query supported channel masks for the format.
|
||||
result = getParam(AudioParameter::keyStreamSupportedChannels, &halListValue, context);
|
||||
if (result != Result::OK) break;
|
||||
std::vector<std::string> halChannelMasks =
|
||||
splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
|
||||
hidl_vec<AudioChannelMask> channelMasks;
|
||||
(void)HidlUtils::audioChannelMasksFromHal(halChannelMasks, &channelMasks);
|
||||
// Create a profile.
|
||||
if (channelMasks.size() != 0 && sampleRates.size() != 0) {
|
||||
tempProfiles.push_back({.format = format,
|
||||
.sampleRates = std::move(sampleRates),
|
||||
.channelMasks = std::move(channelMasks)});
|
||||
}
|
||||
}
|
||||
// Legacy get_parameter does not return a status_t, thus can not advertise of failure.
|
||||
// Note that the method must not return an empty list if this capability is supported.
|
||||
if (!tempProfiles.empty()) {
|
||||
profiles = tempProfiles;
|
||||
} else {
|
||||
result = Result::NOT_SUPPORTED;
|
||||
}
|
||||
_hidl_cb(result, profiles);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Stream::getAudioProperties(getAudioProperties_cb _hidl_cb) {
|
||||
audio_config_base_t halConfigBase = {mStream->get_sample_rate(mStream),
|
||||
mStream->get_channels(mStream),
|
||||
mStream->get_format(mStream)};
|
||||
AudioConfigBase configBase = {};
|
||||
status_t status = HidlUtils::audioConfigBaseFromHal(halConfigBase, mIsInput, &configBase);
|
||||
_hidl_cb(Stream::analyzeStatus("get_audio_properties", status), configBase);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Stream::setAudioProperties(const AudioConfigBaseOptional& config) {
|
||||
audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
|
||||
bool formatSpecified, sRateSpecified, channelMaskSpecified;
|
||||
status_t status = HidlUtils::audioConfigBaseOptionalToHal(
|
||||
config, &halConfigBase, &formatSpecified, &sRateSpecified, &channelMaskSpecified);
|
||||
if (status != NO_ERROR) {
|
||||
return Stream::analyzeStatus("set_audio_properties", status);
|
||||
}
|
||||
if (sRateSpecified) {
|
||||
if (Result result = setParam(AudioParameter::keySamplingRate,
|
||||
static_cast<int>(halConfigBase.sample_rate));
|
||||
result != Result::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (channelMaskSpecified) {
|
||||
if (Result result = setParam(AudioParameter::keyChannels,
|
||||
static_cast<int>(halConfigBase.channel_mask));
|
||||
result != Result::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (formatSpecified) {
|
||||
if (Result result =
|
||||
setParam(AudioParameter::keyFormat, static_cast<int>(halConfigBase.format));
|
||||
result != Result::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
#endif // MAJOR_VERSION <= 6
|
||||
|
||||
Return<Result> Stream::addEffect(uint64_t effectId) {
|
||||
effect_handle_t halEffect = EffectMap::getInstance().get(effectId);
|
||||
if (halEffect != NULL) {
|
||||
return analyzeStatus("add_audio_effect", mStream->add_audio_effect(mStream, halEffect));
|
||||
} else {
|
||||
ALOGW("Invalid effect ID passed from client: %" PRIu64, effectId);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
}
|
||||
|
||||
Return<Result> Stream::removeEffect(uint64_t effectId) {
|
||||
effect_handle_t halEffect = EffectMap::getInstance().get(effectId);
|
||||
if (halEffect != NULL) {
|
||||
return analyzeStatus("remove_audio_effect",
|
||||
mStream->remove_audio_effect(mStream, halEffect));
|
||||
} else {
|
||||
ALOGW("Invalid effect ID passed from client: %" PRIu64, effectId);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
}
|
||||
|
||||
Return<Result> Stream::standby() {
|
||||
return analyzeStatus("standby", mStream->standby(mStream));
|
||||
}
|
||||
|
||||
Return<Result> Stream::setHwAvSync(uint32_t hwAvSync) {
|
||||
return setParam(AudioParameter::keyStreamHwAvSync, static_cast<int>(hwAvSync));
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<AudioDevice> Stream::getDevice() {
|
||||
int device = 0;
|
||||
Result retval = getParam(AudioParameter::keyRouting, &device);
|
||||
return retval == Result::OK ? static_cast<AudioDevice>(device) : AudioDevice::NONE;
|
||||
}
|
||||
|
||||
Return<Result> Stream::setDevice(const DeviceAddress& address) {
|
||||
return setParam(AudioParameter::keyRouting, address);
|
||||
}
|
||||
|
||||
Return<void> Stream::getParameters(const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
|
||||
getParametersImpl({} /* context */, keys, _hidl_cb);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Stream::setParameters(const hidl_vec<ParameterValue>& parameters) {
|
||||
return setParametersImpl({} /* context */, parameters);
|
||||
}
|
||||
|
||||
Return<Result> Stream::setConnectedState(const DeviceAddress& address, bool connected) {
|
||||
return setParam(
|
||||
connected ? AudioParameter::keyDeviceConnect : AudioParameter::keyDeviceDisconnect,
|
||||
address);
|
||||
}
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> Stream::getDevices(getDevices_cb _hidl_cb) {
|
||||
int halDevice = 0;
|
||||
Result retval = getParam(AudioParameter::keyRouting, &halDevice);
|
||||
hidl_vec<DeviceAddress> devices;
|
||||
if (retval == Result::OK) {
|
||||
devices.resize(1);
|
||||
retval = Stream::analyzeStatus(
|
||||
"get_devices",
|
||||
CoreUtils::deviceAddressFromHal(static_cast<audio_devices_t>(halDevice), nullptr,
|
||||
&devices[0]));
|
||||
}
|
||||
_hidl_cb(retval, devices);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Stream::setDevices(const hidl_vec<DeviceAddress>& devices) {
|
||||
// FIXME: can the legacy API set multiple device with address ?
|
||||
if (devices.size() > 1) {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
DeviceAddress address{};
|
||||
if (devices.size() == 1) {
|
||||
address = devices[0];
|
||||
}
|
||||
return setParam(AudioParameter::keyRouting, address);
|
||||
}
|
||||
|
||||
Return<void> Stream::getParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
|
||||
getParametersImpl(context, keys, _hidl_cb);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Stream::setParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<ParameterValue>& parameters) {
|
||||
return setParametersImpl(context, parameters);
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<Result> Stream::start() {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Return<Result> Stream::stop() {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Return<void> Stream::createMmapBuffer(int32_t minSizeFrames __unused,
|
||||
createMmapBuffer_cb _hidl_cb) {
|
||||
Result retval(Result::NOT_SUPPORTED);
|
||||
MmapBufferInfo info;
|
||||
_hidl_cb(retval, info);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Stream::getMmapPosition(getMmapPosition_cb _hidl_cb) {
|
||||
Result retval(Result::NOT_SUPPORTED);
|
||||
MmapPosition position;
|
||||
_hidl_cb(retval, position);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Stream::close() {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Return<void> Stream::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& /* options */) {
|
||||
if (fd.getNativeHandle() != nullptr && fd->numFds == 1) {
|
||||
analyzeStatus("dump", mStream->dump(mStream, fd->data[0]));
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> Stream::debugDump(const hidl_handle& fd) {
|
||||
return debug(fd, {} /* options */);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
236
bluetooth/audio/hal/Stream.h
Normal file
236
bluetooth/audio/hal/Stream.h
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_AUDIO_STREAM_H
|
||||
#define ANDROID_HARDWARE_AUDIO_STREAM_H
|
||||
|
||||
// clang-format off
|
||||
#include PATH(android/hardware/audio/COMMON_TYPES_FILE_VERSION/IStream.h)
|
||||
// clang-format on
|
||||
|
||||
#include "ParametersUtil.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <hardware/audio.h>
|
||||
#include <hidl/Status.h>
|
||||
|
||||
#include <hidl/MQDescriptor.h>
|
||||
|
||||
#include <VersionUtils.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::Void;
|
||||
using ::android::hardware::audio::CORE_TYPES_CPP_VERSION::implementation::ParametersUtil;
|
||||
#if MAJOR_VERSION <= 6
|
||||
using ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::implementation::
|
||||
AudioChannelBitfield;
|
||||
#endif
|
||||
using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION;
|
||||
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION;
|
||||
using namespace ::android::hardware::audio::CPP_VERSION;
|
||||
|
||||
struct Stream : public IStream, public ParametersUtil {
|
||||
Stream(bool isInput, audio_stream_t* stream);
|
||||
|
||||
/** 1GiB is the maximum buffer size the HAL client is allowed to request.
|
||||
* This value has been chosen to be under SIZE_MAX and still big enough
|
||||
* for all audio use case.
|
||||
* Keep private for 2.0, put in .hal in 2.1
|
||||
*/
|
||||
static constexpr uint32_t MAX_BUFFER_SIZE = 2 << 30 /* == 1GiB */;
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IStream follow.
|
||||
Return<uint64_t> getFrameSize() override;
|
||||
Return<uint64_t> getFrameCount() override;
|
||||
Return<uint64_t> getBufferSize() override;
|
||||
#if MAJOR_VERSION <= 6
|
||||
Return<uint32_t> getSampleRate() override;
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) override;
|
||||
Return<void> getSupportedChannelMasks(getSupportedChannelMasks_cb _hidl_cb) override;
|
||||
#endif
|
||||
Return<void> getSupportedSampleRates(AudioFormat format, getSupportedSampleRates_cb _hidl_cb);
|
||||
Return<void> getSupportedChannelMasks(AudioFormat format, getSupportedChannelMasks_cb _hidl_cb);
|
||||
Return<Result> setSampleRate(uint32_t sampleRateHz) override;
|
||||
Return<AudioChannelBitfield> getChannelMask() override;
|
||||
Return<Result> setChannelMask(AudioChannelBitfield mask) override;
|
||||
Return<AudioFormat> getFormat() override;
|
||||
Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
|
||||
Return<Result> setFormat(AudioFormat format) override;
|
||||
#else
|
||||
Return<void> getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) override;
|
||||
Return<Result> setAudioProperties(const AudioConfigBaseOptional& config) override;
|
||||
#endif // MAJOR_VERSION <= 6
|
||||
Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
|
||||
Return<Result> addEffect(uint64_t effectId) override;
|
||||
Return<Result> removeEffect(uint64_t effectId) override;
|
||||
Return<Result> standby() override;
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<AudioDevice> getDevice() override;
|
||||
Return<Result> setDevice(const DeviceAddress& address) override;
|
||||
Return<void> getParameters(const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) override;
|
||||
Return<Result> setParameters(const hidl_vec<ParameterValue>& parameters) override;
|
||||
Return<Result> setConnectedState(const DeviceAddress& address, bool connected) override;
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> getDevices(getDevices_cb _hidl_cb) override;
|
||||
Return<Result> setDevices(const hidl_vec<DeviceAddress>& devices) override;
|
||||
Return<void> getParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) override;
|
||||
Return<Result> setParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<ParameterValue>& parameters) override;
|
||||
#endif
|
||||
Return<Result> setHwAvSync(uint32_t hwAvSync) override;
|
||||
Return<Result> start() override;
|
||||
Return<Result> stop() override;
|
||||
Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
|
||||
Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
|
||||
Return<Result> close() override;
|
||||
|
||||
Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> debugDump(const hidl_handle& fd) override;
|
||||
#endif
|
||||
|
||||
// Utility methods for extending interfaces.
|
||||
static Result analyzeStatus(const char* funcName, int status);
|
||||
static Result analyzeStatus(const char* funcName, int status,
|
||||
const std::vector<int>& ignoreErrors);
|
||||
|
||||
private:
|
||||
const bool mIsInput;
|
||||
audio_stream_t* mStream;
|
||||
|
||||
virtual ~Stream();
|
||||
|
||||
// Methods from ParametersUtil.
|
||||
char* halGetParameters(const char* keys) override;
|
||||
int halSetParameters(const char* keysAndValues) override;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct StreamMmap : public RefBase {
|
||||
explicit StreamMmap(T* stream) : mStream(stream) {}
|
||||
|
||||
Return<Result> start();
|
||||
Return<Result> stop();
|
||||
Return<void> createMmapBuffer(int32_t minSizeFrames, size_t frameSize,
|
||||
IStream::createMmapBuffer_cb _hidl_cb);
|
||||
Return<void> getMmapPosition(IStream::getMmapPosition_cb _hidl_cb);
|
||||
|
||||
private:
|
||||
StreamMmap() {}
|
||||
|
||||
T* mStream;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Return<Result> StreamMmap<T>::start() {
|
||||
if (mStream->start == NULL) return Result::NOT_SUPPORTED;
|
||||
int result = mStream->start(mStream);
|
||||
return Stream::analyzeStatus("start", result);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Return<Result> StreamMmap<T>::stop() {
|
||||
if (mStream->stop == NULL) return Result::NOT_SUPPORTED;
|
||||
int result = mStream->stop(mStream);
|
||||
return Stream::analyzeStatus("stop", result);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Return<void> StreamMmap<T>::createMmapBuffer(int32_t minSizeFrames, size_t frameSize,
|
||||
IStream::createMmapBuffer_cb _hidl_cb) {
|
||||
Result retval(Result::NOT_SUPPORTED);
|
||||
MmapBufferInfo info;
|
||||
native_handle_t* hidlHandle = nullptr;
|
||||
|
||||
if (mStream->create_mmap_buffer != NULL) {
|
||||
if (minSizeFrames <= 0) {
|
||||
retval = Result::INVALID_ARGUMENTS;
|
||||
goto exit;
|
||||
}
|
||||
struct audio_mmap_buffer_info halInfo;
|
||||
retval = Stream::analyzeStatus(
|
||||
"create_mmap_buffer", mStream->create_mmap_buffer(mStream, minSizeFrames, &halInfo));
|
||||
if (retval == Result::OK) {
|
||||
hidlHandle = native_handle_create(1, 0);
|
||||
hidlHandle->data[0] = halInfo.shared_memory_fd;
|
||||
|
||||
// Negative buffer size frame is a legacy hack to indicate that the buffer
|
||||
// is shareable to applications before the relevant flag was introduced
|
||||
bool applicationShareable =
|
||||
halInfo.flags & AUDIO_MMAP_APPLICATION_SHAREABLE || halInfo.buffer_size_frames < 0;
|
||||
halInfo.buffer_size_frames = abs(halInfo.buffer_size_frames);
|
||||
info.sharedMemory = // hidl_memory size must always be positive
|
||||
hidl_memory("audio_buffer", hidlHandle, frameSize * halInfo.buffer_size_frames);
|
||||
#if MAJOR_VERSION == 2
|
||||
if (applicationShareable) {
|
||||
halInfo.buffer_size_frames *= -1;
|
||||
}
|
||||
#else
|
||||
info.flags =
|
||||
halInfo.flags | (applicationShareable ? MmapBufferFlag::APPLICATION_SHAREABLE
|
||||
: MmapBufferFlag::NONE);
|
||||
#endif
|
||||
info.bufferSizeFrames = halInfo.buffer_size_frames;
|
||||
info.burstSizeFrames = halInfo.burst_size_frames;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
_hidl_cb(retval, info);
|
||||
if (hidlHandle != nullptr) {
|
||||
native_handle_delete(hidlHandle);
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Return<void> StreamMmap<T>::getMmapPosition(IStream::getMmapPosition_cb _hidl_cb) {
|
||||
Result retval(Result::NOT_SUPPORTED);
|
||||
MmapPosition position;
|
||||
|
||||
if (mStream->get_mmap_position != NULL) {
|
||||
struct audio_mmap_position halPosition;
|
||||
retval = Stream::analyzeStatus("get_mmap_position",
|
||||
mStream->get_mmap_position(mStream, &halPosition));
|
||||
if (retval == Result::OK) {
|
||||
position.timeNanoseconds = halPosition.time_nanoseconds;
|
||||
position.positionFrames = halPosition.position_frames;
|
||||
}
|
||||
}
|
||||
_hidl_cb(retval, position);
|
||||
return Void();
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUDIO_STREAM_H
|
607
bluetooth/audio/hal/StreamIn.cpp
Normal file
607
bluetooth/audio/hal/StreamIn.cpp
Normal file
@ -0,0 +1,607 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "StreamInHAL"
|
||||
|
||||
#include "StreamIn.h"
|
||||
#include "Util.h"
|
||||
#include "common/all-versions/HidlSupport.h"
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
#define ATRACE_TAG ATRACE_TAG_AUDIO
|
||||
|
||||
#include <HidlUtils.h>
|
||||
#include <android/log.h>
|
||||
#include <hardware/audio.h>
|
||||
#include <util/CoreUtils.h>
|
||||
#include <utils/Trace.h>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::implementation::HidlUtils;
|
||||
using ::android::hardware::audio::CORE_TYPES_CPP_VERSION::implementation::CoreUtils;
|
||||
namespace util {
|
||||
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION::implementation::util;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class ReadThread : public Thread {
|
||||
public:
|
||||
// ReadThread's lifespan never exceeds StreamIn's lifespan.
|
||||
ReadThread(std::atomic<bool>* stop, audio_stream_in_t* stream, StreamIn::CommandMQ* commandMQ,
|
||||
StreamIn::DataMQ* dataMQ, StreamIn::StatusMQ* statusMQ, EventFlag* efGroup)
|
||||
: Thread(false /*canCallJava*/),
|
||||
mStop(stop),
|
||||
mStream(stream),
|
||||
mCommandMQ(commandMQ),
|
||||
mDataMQ(dataMQ),
|
||||
mStatusMQ(statusMQ),
|
||||
mEfGroup(efGroup),
|
||||
mBuffer(nullptr) {}
|
||||
bool init() {
|
||||
mBuffer.reset(new (std::nothrow) uint8_t[mDataMQ->getQuantumCount()]);
|
||||
return mBuffer != nullptr;
|
||||
}
|
||||
virtual ~ReadThread() {}
|
||||
|
||||
private:
|
||||
std::atomic<bool>* mStop;
|
||||
audio_stream_in_t* mStream;
|
||||
StreamIn::CommandMQ* mCommandMQ;
|
||||
StreamIn::DataMQ* mDataMQ;
|
||||
StreamIn::StatusMQ* mStatusMQ;
|
||||
EventFlag* mEfGroup;
|
||||
std::unique_ptr<uint8_t[]> mBuffer;
|
||||
IStreamIn::ReadParameters mParameters;
|
||||
IStreamIn::ReadStatus mStatus;
|
||||
|
||||
bool threadLoop() override;
|
||||
|
||||
void doGetCapturePosition();
|
||||
void doRead();
|
||||
};
|
||||
|
||||
void ReadThread::doRead() {
|
||||
size_t availableToWrite = mDataMQ->availableToWrite();
|
||||
size_t requestedToRead = mParameters.params.read;
|
||||
if (requestedToRead > availableToWrite) {
|
||||
ALOGW(
|
||||
"truncating read data from %d to %d due to insufficient data queue "
|
||||
"space",
|
||||
(int32_t)requestedToRead, (int32_t)availableToWrite);
|
||||
requestedToRead = availableToWrite;
|
||||
}
|
||||
ssize_t readResult = mStream->read(mStream, &mBuffer[0], requestedToRead);
|
||||
mStatus.retval = Result::OK;
|
||||
if (readResult >= 0) {
|
||||
mStatus.reply.read = readResult;
|
||||
if (!mDataMQ->write(&mBuffer[0], readResult)) {
|
||||
ALOGW("data message queue write failed");
|
||||
}
|
||||
} else {
|
||||
mStatus.retval = Stream::analyzeStatus("read", readResult);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadThread::doGetCapturePosition() {
|
||||
mStatus.retval = StreamIn::getCapturePositionImpl(
|
||||
mStream, &mStatus.reply.capturePosition.frames, &mStatus.reply.capturePosition.time);
|
||||
}
|
||||
|
||||
bool ReadThread::threadLoop() {
|
||||
// This implementation doesn't return control back to the Thread until it
|
||||
// decides to stop,
|
||||
// as the Thread uses mutexes, and this can lead to priority inversion.
|
||||
while (!std::atomic_load_explicit(mStop, std::memory_order_acquire)) {
|
||||
uint32_t efState = 0;
|
||||
mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL), &efState);
|
||||
if (!(efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL))) {
|
||||
continue; // Nothing to do.
|
||||
}
|
||||
if (!mCommandMQ->read(&mParameters)) {
|
||||
continue; // Nothing to do.
|
||||
}
|
||||
mStatus.replyTo = mParameters.command;
|
||||
switch (mParameters.command) {
|
||||
case IStreamIn::ReadCommand::READ:
|
||||
doRead();
|
||||
break;
|
||||
case IStreamIn::ReadCommand::GET_CAPTURE_POSITION:
|
||||
doGetCapturePosition();
|
||||
break;
|
||||
default:
|
||||
ALOGE("Unknown read thread command code %d", mParameters.command);
|
||||
mStatus.retval = Result::NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
if (!mStatusMQ->write(&mStatus)) {
|
||||
ALOGW("status message queue write failed");
|
||||
}
|
||||
mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
StreamIn::StreamIn(const sp<Device>& device, audio_stream_in_t* stream)
|
||||
: mDevice(device),
|
||||
mStream(stream),
|
||||
mStreamCommon(new Stream(true /*isInput*/, &stream->common)),
|
||||
mStreamMmap(new StreamMmap<audio_stream_in_t>(stream)),
|
||||
mEfGroup(nullptr),
|
||||
mStopReadThread(false) {}
|
||||
|
||||
StreamIn::~StreamIn() {
|
||||
ATRACE_CALL();
|
||||
close();
|
||||
if (mReadThread.get()) {
|
||||
ATRACE_NAME("mReadThread->join");
|
||||
status_t status = mReadThread->join();
|
||||
ALOGE_IF(status, "read thread exit error: %s", strerror(-status));
|
||||
}
|
||||
if (mEfGroup) {
|
||||
status_t status = EventFlag::deleteEventFlag(&mEfGroup);
|
||||
ALOGE_IF(status, "read MQ event flag deletion error: %s", strerror(-status));
|
||||
}
|
||||
#if MAJOR_VERSION <= 5
|
||||
mDevice->closeInputStream(mStream);
|
||||
#endif
|
||||
mStream = nullptr;
|
||||
}
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IStream follow.
|
||||
Return<uint64_t> StreamIn::getFrameSize() {
|
||||
return audio_stream_in_frame_size(mStream);
|
||||
}
|
||||
|
||||
Return<uint64_t> StreamIn::getFrameCount() {
|
||||
return mStreamCommon->getFrameCount();
|
||||
}
|
||||
|
||||
Return<uint64_t> StreamIn::getBufferSize() {
|
||||
return mStreamCommon->getBufferSize();
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION <= 6
|
||||
Return<uint32_t> StreamIn::getSampleRate() {
|
||||
return mStreamCommon->getSampleRate();
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> StreamIn::getSupportedChannelMasks(getSupportedChannelMasks_cb _hidl_cb) {
|
||||
return mStreamCommon->getSupportedChannelMasks(_hidl_cb);
|
||||
}
|
||||
Return<void> StreamIn::getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) {
|
||||
return mStreamCommon->getSupportedSampleRates(_hidl_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<void> StreamIn::getSupportedChannelMasks(AudioFormat format,
|
||||
getSupportedChannelMasks_cb _hidl_cb) {
|
||||
return mStreamCommon->getSupportedChannelMasks(format, _hidl_cb);
|
||||
}
|
||||
Return<void> StreamIn::getSupportedSampleRates(AudioFormat format,
|
||||
getSupportedSampleRates_cb _hidl_cb) {
|
||||
return mStreamCommon->getSupportedSampleRates(format, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::setSampleRate(uint32_t sampleRateHz) {
|
||||
return mStreamCommon->setSampleRate(sampleRateHz);
|
||||
}
|
||||
|
||||
Return<AudioChannelBitfield> StreamIn::getChannelMask() {
|
||||
return mStreamCommon->getChannelMask();
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::setChannelMask(AudioChannelBitfield mask) {
|
||||
return mStreamCommon->setChannelMask(mask);
|
||||
}
|
||||
|
||||
Return<AudioFormat> StreamIn::getFormat() {
|
||||
return mStreamCommon->getFormat();
|
||||
}
|
||||
|
||||
Return<void> StreamIn::getSupportedFormats(getSupportedFormats_cb _hidl_cb) {
|
||||
return mStreamCommon->getSupportedFormats(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::setFormat(AudioFormat format) {
|
||||
return mStreamCommon->setFormat(format);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
Return<void> StreamIn::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
|
||||
return mStreamCommon->getSupportedProfiles(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::setAudioProperties(const AudioConfigBaseOptional& config) {
|
||||
return mStreamCommon->setAudioProperties(config);
|
||||
}
|
||||
|
||||
#endif // MAJOR_VERSION <= 6
|
||||
|
||||
Return<void> StreamIn::getAudioProperties(getAudioProperties_cb _hidl_cb) {
|
||||
return mStreamCommon->getAudioProperties(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::addEffect(uint64_t effectId) {
|
||||
return mStreamCommon->addEffect(effectId);
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::removeEffect(uint64_t effectId) {
|
||||
return mStreamCommon->removeEffect(effectId);
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::standby() {
|
||||
return mStreamCommon->standby();
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::setHwAvSync(uint32_t hwAvSync) {
|
||||
return mStreamCommon->setHwAvSync(hwAvSync);
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<Result> StreamIn::setConnectedState(const DeviceAddress& address, bool connected) {
|
||||
return mStreamCommon->setConnectedState(address, connected);
|
||||
}
|
||||
|
||||
Return<AudioDevice> StreamIn::getDevice() {
|
||||
return mStreamCommon->getDevice();
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::setDevice(const DeviceAddress& address) {
|
||||
return mStreamCommon->setDevice(address);
|
||||
}
|
||||
|
||||
Return<void> StreamIn::getParameters(const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
|
||||
return mStreamCommon->getParameters(keys, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::setParameters(const hidl_vec<ParameterValue>& parameters) {
|
||||
return mStreamCommon->setParameters(parameters);
|
||||
}
|
||||
|
||||
Return<void> StreamIn::debugDump(const hidl_handle& fd) {
|
||||
return mStreamCommon->debugDump(fd);
|
||||
}
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> StreamIn::getDevices(getDevices_cb _hidl_cb) {
|
||||
return mStreamCommon->getDevices(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::setDevices(const hidl_vec<DeviceAddress>& devices) {
|
||||
return mStreamCommon->setDevices(devices);
|
||||
}
|
||||
Return<void> StreamIn::getParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
|
||||
return mStreamCommon->getParameters(context, keys, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::setParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<ParameterValue>& parameters) {
|
||||
return mStreamCommon->setParameters(context, parameters);
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<Result> StreamIn::start() {
|
||||
return mStreamMmap->start();
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::stop() {
|
||||
return mStreamMmap->stop();
|
||||
}
|
||||
|
||||
Return<void> StreamIn::createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) {
|
||||
return mStreamMmap->createMmapBuffer(minSizeFrames, audio_stream_in_frame_size(mStream),
|
||||
_hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> StreamIn::getMmapPosition(getMmapPosition_cb _hidl_cb) {
|
||||
return mStreamMmap->getMmapPosition(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::close() {
|
||||
if (mStopReadThread.load(std::memory_order_relaxed)) { // only this thread writes
|
||||
return Result::INVALID_STATE;
|
||||
}
|
||||
mStopReadThread.store(true, std::memory_order_release);
|
||||
if (mEfGroup) {
|
||||
mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
|
||||
}
|
||||
#if MAJOR_VERSION >= 6
|
||||
mDevice->closeInputStream(mStream);
|
||||
#endif
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IStreamIn follow.
|
||||
Return<void> StreamIn::getAudioSource(getAudioSource_cb _hidl_cb) {
|
||||
int halSource;
|
||||
Result retval = mStreamCommon->getParam(AudioParameter::keyInputSource, &halSource);
|
||||
AudioSource source = {};
|
||||
if (retval == Result::OK) {
|
||||
retval = Stream::analyzeStatus(
|
||||
"get_audio_source",
|
||||
HidlUtils::audioSourceFromHal(static_cast<audio_source_t>(halSource), &source));
|
||||
}
|
||||
_hidl_cb(retval, source);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::setGain(float gain) {
|
||||
if (!util::isGainNormalized(gain)) {
|
||||
ALOGW("Can not set a stream input gain (%f) outside [0,1]", gain);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
return Stream::analyzeStatus("set_gain", mStream->set_gain(mStream, gain));
|
||||
}
|
||||
|
||||
Return<void> StreamIn::prepareForReading(uint32_t frameSize, uint32_t framesCount,
|
||||
prepareForReading_cb _hidl_cb) {
|
||||
status_t status;
|
||||
#if MAJOR_VERSION <= 6
|
||||
ThreadInfo threadInfo = {0, 0};
|
||||
#else
|
||||
int32_t threadInfo = 0;
|
||||
#endif
|
||||
|
||||
// Wrap the _hidl_cb to return an error
|
||||
auto sendError = [&threadInfo, &_hidl_cb](Result result) {
|
||||
_hidl_cb(result, CommandMQ::Descriptor(), DataMQ::Descriptor(), StatusMQ::Descriptor(),
|
||||
threadInfo);
|
||||
};
|
||||
|
||||
// Create message queues.
|
||||
if (mDataMQ) {
|
||||
ALOGE("the client attempts to call prepareForReading twice");
|
||||
sendError(Result::INVALID_STATE);
|
||||
return Void();
|
||||
}
|
||||
std::unique_ptr<CommandMQ> tempCommandMQ(new CommandMQ(1));
|
||||
|
||||
// Check frameSize and framesCount
|
||||
if (frameSize == 0 || framesCount == 0) {
|
||||
ALOGE("Null frameSize (%u) or framesCount (%u)", frameSize, framesCount);
|
||||
sendError(Result::INVALID_ARGUMENTS);
|
||||
return Void();
|
||||
}
|
||||
|
||||
if (frameSize > Stream::MAX_BUFFER_SIZE / framesCount) {
|
||||
ALOGE("Buffer too big: %u*%u bytes > MAX_BUFFER_SIZE (%u)", frameSize, framesCount,
|
||||
Stream::MAX_BUFFER_SIZE);
|
||||
sendError(Result::INVALID_ARGUMENTS);
|
||||
return Void();
|
||||
}
|
||||
std::unique_ptr<DataMQ> tempDataMQ(new DataMQ(frameSize * framesCount, true /* EventFlag */));
|
||||
|
||||
std::unique_ptr<StatusMQ> tempStatusMQ(new StatusMQ(1));
|
||||
if (!tempCommandMQ->isValid() || !tempDataMQ->isValid() || !tempStatusMQ->isValid()) {
|
||||
ALOGE_IF(!tempCommandMQ->isValid(), "command MQ is invalid");
|
||||
ALOGE_IF(!tempDataMQ->isValid(), "data MQ is invalid");
|
||||
ALOGE_IF(!tempStatusMQ->isValid(), "status MQ is invalid");
|
||||
sendError(Result::INVALID_ARGUMENTS);
|
||||
return Void();
|
||||
}
|
||||
EventFlag* tempRawEfGroup{};
|
||||
status = EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &tempRawEfGroup);
|
||||
std::unique_ptr<EventFlag, void (*)(EventFlag*)> tempElfGroup(
|
||||
tempRawEfGroup, [](auto* ef) { EventFlag::deleteEventFlag(&ef); });
|
||||
if (status != OK || !tempElfGroup) {
|
||||
ALOGE("failed creating event flag for data MQ: %s", strerror(-status));
|
||||
sendError(Result::INVALID_ARGUMENTS);
|
||||
return Void();
|
||||
}
|
||||
|
||||
// Create and launch the thread.
|
||||
auto tempReadThread =
|
||||
sp<ReadThread>::make(&mStopReadThread, mStream, tempCommandMQ.get(), tempDataMQ.get(),
|
||||
tempStatusMQ.get(), tempElfGroup.get());
|
||||
if (!tempReadThread->init()) {
|
||||
ALOGW("failed to start reader thread: %s", strerror(-status));
|
||||
sendError(Result::INVALID_ARGUMENTS);
|
||||
return Void();
|
||||
}
|
||||
status = tempReadThread->run("reader", PRIORITY_URGENT_AUDIO);
|
||||
if (status != OK) {
|
||||
ALOGW("failed to start reader thread: %s", strerror(-status));
|
||||
sendError(Result::INVALID_ARGUMENTS);
|
||||
return Void();
|
||||
}
|
||||
|
||||
mCommandMQ = std::move(tempCommandMQ);
|
||||
mDataMQ = std::move(tempDataMQ);
|
||||
mStatusMQ = std::move(tempStatusMQ);
|
||||
mReadThread = tempReadThread;
|
||||
mEfGroup = tempElfGroup.release();
|
||||
#if MAJOR_VERSION <= 6
|
||||
threadInfo.pid = getpid();
|
||||
threadInfo.tid = mReadThread->getTid();
|
||||
#else
|
||||
threadInfo = mReadThread->getTid();
|
||||
#endif
|
||||
_hidl_cb(Result::OK, *mCommandMQ->getDesc(), *mDataMQ->getDesc(), *mStatusMQ->getDesc(),
|
||||
threadInfo);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<uint32_t> StreamIn::getInputFramesLost() {
|
||||
return mStream->get_input_frames_lost(mStream);
|
||||
}
|
||||
|
||||
// static
|
||||
Result StreamIn::getCapturePositionImpl(audio_stream_in_t* stream, uint64_t* frames,
|
||||
uint64_t* time) {
|
||||
// HAL may have a stub function, always returning ENOSYS, don't
|
||||
// spam the log in this case.
|
||||
static const std::vector<int> ignoredErrors{ENOSYS};
|
||||
Result retval(Result::NOT_SUPPORTED);
|
||||
if (stream->get_capture_position == NULL) return retval;
|
||||
int64_t halFrames, halTime;
|
||||
retval = Stream::analyzeStatus("get_capture_position",
|
||||
stream->get_capture_position(stream, &halFrames, &halTime),
|
||||
ignoredErrors);
|
||||
if (retval == Result::OK) {
|
||||
*frames = halFrames;
|
||||
*time = halTime;
|
||||
}
|
||||
return retval;
|
||||
};
|
||||
|
||||
Return<void> StreamIn::getCapturePosition(getCapturePosition_cb _hidl_cb) {
|
||||
uint64_t frames = 0, time = 0;
|
||||
Result retval = getCapturePositionImpl(mStream, &frames, &time);
|
||||
_hidl_cb(retval, frames, time);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> StreamIn::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
|
||||
return mStreamCommon->debug(fd, options);
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION >= 4
|
||||
Result StreamIn::doUpdateSinkMetadata(const SinkMetadata& sinkMetadata) {
|
||||
std::vector<record_track_metadata> halTracks;
|
||||
#if MAJOR_VERSION <= 6
|
||||
(void)CoreUtils::sinkMetadataToHal(sinkMetadata, &halTracks);
|
||||
#else
|
||||
// Validate whether a conversion to V7 is possible. This is needed
|
||||
// to have a consistent behavior of the HAL regardless of the API
|
||||
// version of the legacy HAL (and also to be consistent with openInputStream).
|
||||
std::vector<record_track_metadata_v7> halTracksV7;
|
||||
if (status_t status = CoreUtils::sinkMetadataToHalV7(
|
||||
sinkMetadata, false /*ignoreNonVendorTags*/, &halTracksV7);
|
||||
status == NO_ERROR) {
|
||||
halTracks.reserve(halTracksV7.size());
|
||||
for (auto metadata_v7 : halTracksV7) {
|
||||
halTracks.push_back(std::move(metadata_v7.base));
|
||||
}
|
||||
} else {
|
||||
return Stream::analyzeStatus("sinkMetadataToHal", status);
|
||||
}
|
||||
#endif // MAJOR_VERSION <= 6
|
||||
const sink_metadata_t halMetadata = {
|
||||
.track_count = halTracks.size(),
|
||||
.tracks = halTracks.data(),
|
||||
};
|
||||
mStream->update_sink_metadata(mStream, &halMetadata);
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION >= 7
|
||||
Result StreamIn::doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata) {
|
||||
std::vector<record_track_metadata_v7> halTracks;
|
||||
if (status_t status = CoreUtils::sinkMetadataToHalV7(sinkMetadata,
|
||||
false /*ignoreNonVendorTags*/, &halTracks);
|
||||
status != NO_ERROR) {
|
||||
return Stream::analyzeStatus("sinkMetadataToHal", status);
|
||||
}
|
||||
const sink_metadata_v7_t halMetadata = {
|
||||
.track_count = halTracks.size(),
|
||||
.tracks = halTracks.data(),
|
||||
};
|
||||
mStream->update_sink_metadata_v7(mStream, &halMetadata);
|
||||
return Result::OK;
|
||||
}
|
||||
#endif // MAJOR_VERSION >= 7
|
||||
|
||||
#if MAJOR_VERSION <= 6
|
||||
Return<void> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
|
||||
if (mStream->update_sink_metadata == nullptr) {
|
||||
return Void(); // not supported by the HAL
|
||||
}
|
||||
(void)doUpdateSinkMetadata(sinkMetadata);
|
||||
return Void();
|
||||
}
|
||||
#elif MAJOR_VERSION >= 7
|
||||
Return<Result> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
|
||||
if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) {
|
||||
if (mStream->update_sink_metadata == nullptr) {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
return doUpdateSinkMetadata(sinkMetadata);
|
||||
} else {
|
||||
if (mStream->update_sink_metadata_v7 == nullptr) {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
return doUpdateSinkMetadataV7(sinkMetadata);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<void> StreamIn::getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) {
|
||||
Result retval = Result::NOT_SUPPORTED;
|
||||
size_t actual_mics = AUDIO_MICROPHONE_MAX_COUNT;
|
||||
audio_microphone_characteristic_t mic_array[AUDIO_MICROPHONE_MAX_COUNT];
|
||||
|
||||
hidl_vec<MicrophoneInfo> microphones;
|
||||
if (mStream->get_active_microphones != NULL &&
|
||||
mStream->get_active_microphones(mStream, &mic_array[0], &actual_mics) == 0) {
|
||||
microphones.resize(actual_mics);
|
||||
for (size_t i = 0; i < actual_mics; ++i) {
|
||||
(void)CoreUtils::microphoneInfoFromHal(mic_array[i], µphones[i]);
|
||||
}
|
||||
retval = Result::OK;
|
||||
}
|
||||
|
||||
_hidl_cb(retval, microphones);
|
||||
return Void();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MAJOR_VERSION >= 5
|
||||
Return<Result> StreamIn::setMicrophoneDirection(MicrophoneDirection direction) {
|
||||
if (mStream->set_microphone_direction == nullptr) {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
if (!common::utils::isValidHidlEnum(direction)) {
|
||||
ALOGE("%s: Invalid direction %d", __func__, direction);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
return Stream::analyzeStatus(
|
||||
"set_microphone_direction",
|
||||
mStream->set_microphone_direction(
|
||||
mStream, static_cast<audio_microphone_direction_t>(direction)));
|
||||
}
|
||||
|
||||
Return<Result> StreamIn::setMicrophoneFieldDimension(float zoom) {
|
||||
if (mStream->set_microphone_field_dimension == nullptr) {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
if (std::isnan(zoom) || zoom < -1 || zoom > 1) {
|
||||
ALOGE("%s: Invalid zoom %f", __func__, zoom);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
return Stream::analyzeStatus("set_microphone_field_dimension",
|
||||
mStream->set_microphone_field_dimension(mStream, zoom));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
162
bluetooth/audio/hal/StreamIn.h
Normal file
162
bluetooth/audio/hal/StreamIn.h
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_AUDIO_STREAMIN_H
|
||||
#define ANDROID_HARDWARE_AUDIO_STREAMIN_H
|
||||
|
||||
// clang-format off
|
||||
#include PATH(android/hardware/audio/CORE_TYPES_FILE_VERSION/IStreamIn.h)
|
||||
// clang-format on
|
||||
|
||||
#include "Device.h"
|
||||
#include "Stream.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include <fmq/EventFlag.h>
|
||||
#include <fmq/MessageQueue.h>
|
||||
#include <hidl/MQDescriptor.h>
|
||||
#include <hidl/Status.h>
|
||||
#include <utils/Thread.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::Void;
|
||||
using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION;
|
||||
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION;
|
||||
using namespace ::android::hardware::audio::CPP_VERSION;
|
||||
|
||||
struct StreamIn : public IStreamIn {
|
||||
typedef MessageQueue<ReadParameters, kSynchronizedReadWrite> CommandMQ;
|
||||
typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
|
||||
typedef MessageQueue<ReadStatus, kSynchronizedReadWrite> StatusMQ;
|
||||
|
||||
StreamIn(const sp<Device>& device, audio_stream_in_t* stream);
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IStream follow.
|
||||
Return<uint64_t> getFrameSize() override;
|
||||
Return<uint64_t> getFrameCount() override;
|
||||
Return<uint64_t> getBufferSize() override;
|
||||
#if MAJOR_VERSION <= 6
|
||||
Return<uint32_t> getSampleRate() override;
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) override;
|
||||
Return<void> getSupportedChannelMasks(getSupportedChannelMasks_cb _hidl_cb) override;
|
||||
#endif
|
||||
Return<void> getSupportedSampleRates(AudioFormat format, getSupportedSampleRates_cb _hidl_cb);
|
||||
Return<void> getSupportedChannelMasks(AudioFormat format, getSupportedChannelMasks_cb _hidl_cb);
|
||||
Return<Result> setSampleRate(uint32_t sampleRateHz) override;
|
||||
Return<AudioChannelBitfield> getChannelMask() override;
|
||||
Return<Result> setChannelMask(AudioChannelBitfield mask) override;
|
||||
Return<AudioFormat> getFormat() override;
|
||||
Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
|
||||
Return<Result> setFormat(AudioFormat format) override;
|
||||
#else
|
||||
Return<void> getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) override;
|
||||
Return<Result> setAudioProperties(const AudioConfigBaseOptional& config) override;
|
||||
#endif // MAJOR_VERSION <= 6
|
||||
Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
|
||||
Return<Result> addEffect(uint64_t effectId) override;
|
||||
Return<Result> removeEffect(uint64_t effectId) override;
|
||||
Return<Result> standby() override;
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<AudioDevice> getDevice() override;
|
||||
Return<Result> setDevice(const DeviceAddress& address) override;
|
||||
Return<void> getParameters(const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) override;
|
||||
Return<Result> setParameters(const hidl_vec<ParameterValue>& parameters) override;
|
||||
Return<Result> setConnectedState(const DeviceAddress& address, bool connected) override;
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> getDevices(getDevices_cb _hidl_cb) override;
|
||||
Return<Result> setDevices(const hidl_vec<DeviceAddress>& devices) override;
|
||||
Return<void> getParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) override;
|
||||
Return<Result> setParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<ParameterValue>& parameters) override;
|
||||
#endif
|
||||
Return<Result> setHwAvSync(uint32_t hwAvSync) override;
|
||||
Return<Result> close() override;
|
||||
|
||||
Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> debugDump(const hidl_handle& fd) override;
|
||||
#endif
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IStreamIn follow.
|
||||
Return<void> getAudioSource(getAudioSource_cb _hidl_cb) override;
|
||||
Return<Result> setGain(float gain) override;
|
||||
Return<void> prepareForReading(uint32_t frameSize, uint32_t framesCount,
|
||||
prepareForReading_cb _hidl_cb) override;
|
||||
Return<uint32_t> getInputFramesLost() override;
|
||||
Return<void> getCapturePosition(getCapturePosition_cb _hidl_cb) override;
|
||||
Return<Result> start() override;
|
||||
Return<Result> stop() override;
|
||||
Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
|
||||
Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
|
||||
#if MAJOR_VERSION >= 4
|
||||
#if MAJOR_VERSION <= 6
|
||||
Return<void> updateSinkMetadata(const SinkMetadata& sinkMetadata) override;
|
||||
#else
|
||||
Return<Result> updateSinkMetadata(const SinkMetadata& sinkMetadata) override;
|
||||
#endif
|
||||
Return<void> getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) override;
|
||||
#endif // MAJOR_VERSION >= 4
|
||||
#if MAJOR_VERSION >= 5
|
||||
Return<Result> setMicrophoneDirection(MicrophoneDirection direction) override;
|
||||
Return<Result> setMicrophoneFieldDimension(float zoom) override;
|
||||
#endif
|
||||
static Result getCapturePositionImpl(audio_stream_in_t* stream, uint64_t* frames,
|
||||
uint64_t* time);
|
||||
|
||||
private:
|
||||
#if MAJOR_VERSION >= 4
|
||||
Result doUpdateSinkMetadata(const SinkMetadata& sinkMetadata);
|
||||
#if MAJOR_VERSION >= 7
|
||||
Result doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata);
|
||||
#endif
|
||||
#endif // MAJOR_VERSION >= 4
|
||||
|
||||
const sp<Device> mDevice;
|
||||
audio_stream_in_t* mStream;
|
||||
const sp<Stream> mStreamCommon;
|
||||
const sp<StreamMmap<audio_stream_in_t>> mStreamMmap;
|
||||
std::unique_ptr<CommandMQ> mCommandMQ;
|
||||
std::unique_ptr<DataMQ> mDataMQ;
|
||||
std::unique_ptr<StatusMQ> mStatusMQ;
|
||||
EventFlag* mEfGroup;
|
||||
std::atomic<bool> mStopReadThread;
|
||||
sp<Thread> mReadThread;
|
||||
|
||||
virtual ~StreamIn();
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUDIO_STREAMIN_H
|
837
bluetooth/audio/hal/StreamOut.cpp
Normal file
837
bluetooth/audio/hal/StreamOut.cpp
Normal file
@ -0,0 +1,837 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "StreamOutHAL"
|
||||
|
||||
#include "StreamOut.h"
|
||||
#include "Util.h"
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
#define ATRACE_TAG ATRACE_TAG_AUDIO
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <HidlUtils.h>
|
||||
#include <android/log.h>
|
||||
#include <audio_utils/Metadata.h>
|
||||
#include <hardware/audio.h>
|
||||
#include <util/CoreUtils.h>
|
||||
#include <utils/Trace.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::implementation::HidlUtils;
|
||||
using ::android::hardware::audio::CORE_TYPES_CPP_VERSION::implementation::CoreUtils;
|
||||
namespace util {
|
||||
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION::implementation::util;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class WriteThread : public Thread {
|
||||
public:
|
||||
// WriteThread's lifespan never exceeds StreamOut's lifespan.
|
||||
WriteThread(std::atomic<bool>* stop, audio_stream_out_t* stream,
|
||||
StreamOut::CommandMQ* commandMQ, StreamOut::DataMQ* dataMQ,
|
||||
StreamOut::StatusMQ* statusMQ, EventFlag* efGroup)
|
||||
: Thread(false /*canCallJava*/),
|
||||
mStop(stop),
|
||||
mStream(stream),
|
||||
mCommandMQ(commandMQ),
|
||||
mDataMQ(dataMQ),
|
||||
mStatusMQ(statusMQ),
|
||||
mEfGroup(efGroup),
|
||||
mBuffer(nullptr) {}
|
||||
bool init() {
|
||||
mBuffer.reset(new (std::nothrow) uint8_t[mDataMQ->getQuantumCount()]);
|
||||
return mBuffer != nullptr;
|
||||
}
|
||||
virtual ~WriteThread() {}
|
||||
|
||||
private:
|
||||
std::atomic<bool>* mStop;
|
||||
audio_stream_out_t* mStream;
|
||||
StreamOut::CommandMQ* mCommandMQ;
|
||||
StreamOut::DataMQ* mDataMQ;
|
||||
StreamOut::StatusMQ* mStatusMQ;
|
||||
EventFlag* mEfGroup;
|
||||
std::unique_ptr<uint8_t[]> mBuffer;
|
||||
IStreamOut::WriteStatus mStatus;
|
||||
|
||||
bool threadLoop() override;
|
||||
|
||||
void doGetLatency();
|
||||
void doGetPresentationPosition();
|
||||
void doWrite();
|
||||
};
|
||||
|
||||
void WriteThread::doWrite() {
|
||||
const size_t availToRead = mDataMQ->availableToRead();
|
||||
mStatus.retval = Result::OK;
|
||||
mStatus.reply.written = 0;
|
||||
if (mDataMQ->read(&mBuffer[0], availToRead)) {
|
||||
ssize_t writeResult = mStream->write(mStream, &mBuffer[0], availToRead);
|
||||
if (writeResult >= 0) {
|
||||
mStatus.reply.written = writeResult;
|
||||
} else {
|
||||
mStatus.retval = Stream::analyzeStatus("write", writeResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteThread::doGetPresentationPosition() {
|
||||
mStatus.retval =
|
||||
StreamOut::getPresentationPositionImpl(mStream, &mStatus.reply.presentationPosition.frames,
|
||||
&mStatus.reply.presentationPosition.timeStamp);
|
||||
}
|
||||
|
||||
void WriteThread::doGetLatency() {
|
||||
mStatus.retval = Result::OK;
|
||||
mStatus.reply.latencyMs = mStream->get_latency(mStream);
|
||||
}
|
||||
|
||||
bool WriteThread::threadLoop() {
|
||||
// This implementation doesn't return control back to the Thread until it
|
||||
// decides to stop,
|
||||
// as the Thread uses mutexes, and this can lead to priority inversion.
|
||||
while (!std::atomic_load_explicit(mStop, std::memory_order_acquire)) {
|
||||
uint32_t efState = 0;
|
||||
mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY), &efState);
|
||||
if (!(efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY))) {
|
||||
continue; // Nothing to do.
|
||||
}
|
||||
if (!mCommandMQ->read(&mStatus.replyTo)) {
|
||||
continue; // Nothing to do.
|
||||
}
|
||||
switch (mStatus.replyTo) {
|
||||
case IStreamOut::WriteCommand::WRITE:
|
||||
doWrite();
|
||||
break;
|
||||
case IStreamOut::WriteCommand::GET_PRESENTATION_POSITION:
|
||||
doGetPresentationPosition();
|
||||
break;
|
||||
case IStreamOut::WriteCommand::GET_LATENCY:
|
||||
doGetLatency();
|
||||
break;
|
||||
default:
|
||||
ALOGE("Unknown write thread command code %d", mStatus.replyTo);
|
||||
mStatus.retval = Result::NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
if (!mStatusMQ->write(&mStatus)) {
|
||||
ALOGE("status message queue write failed");
|
||||
}
|
||||
mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
StreamOut::StreamOut(const sp<Device>& device, audio_stream_out_t* stream)
|
||||
: mDevice(device),
|
||||
mStream(stream),
|
||||
mStreamCommon(new Stream(false /*isInput*/, &stream->common)),
|
||||
mStreamMmap(new StreamMmap<audio_stream_out_t>(stream)),
|
||||
mEfGroup(nullptr),
|
||||
mStopWriteThread(false) {}
|
||||
|
||||
StreamOut::~StreamOut() {
|
||||
ATRACE_CALL();
|
||||
(void)close();
|
||||
if (mWriteThread.get()) {
|
||||
ATRACE_NAME("mWriteThread->join");
|
||||
status_t status = mWriteThread->join();
|
||||
ALOGE_IF(status, "write thread exit error: %s", strerror(-status));
|
||||
}
|
||||
if (mEfGroup) {
|
||||
status_t status = EventFlag::deleteEventFlag(&mEfGroup);
|
||||
ALOGE_IF(status, "write MQ event flag deletion error: %s", strerror(-status));
|
||||
}
|
||||
mCallback = nullptr;
|
||||
#if MAJOR_VERSION <= 5
|
||||
mDevice->closeOutputStream(mStream);
|
||||
// Closing the output stream in the HAL waits for the callback to finish,
|
||||
// and joins the callback thread. Thus is it guaranteed that the callback
|
||||
// thread will not be accessing our object anymore.
|
||||
#endif
|
||||
mStream = nullptr;
|
||||
}
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IStream follow.
|
||||
Return<uint64_t> StreamOut::getFrameSize() {
|
||||
return audio_stream_out_frame_size(mStream);
|
||||
}
|
||||
|
||||
Return<uint64_t> StreamOut::getFrameCount() {
|
||||
return mStreamCommon->getFrameCount();
|
||||
}
|
||||
|
||||
Return<uint64_t> StreamOut::getBufferSize() {
|
||||
return mStreamCommon->getBufferSize();
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION <= 6
|
||||
Return<uint32_t> StreamOut::getSampleRate() {
|
||||
return mStreamCommon->getSampleRate();
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> StreamOut::getSupportedChannelMasks(getSupportedChannelMasks_cb _hidl_cb) {
|
||||
return mStreamCommon->getSupportedChannelMasks(_hidl_cb);
|
||||
}
|
||||
Return<void> StreamOut::getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) {
|
||||
return mStreamCommon->getSupportedSampleRates(_hidl_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<void> StreamOut::getSupportedChannelMasks(AudioFormat format,
|
||||
getSupportedChannelMasks_cb _hidl_cb) {
|
||||
return mStreamCommon->getSupportedChannelMasks(format, _hidl_cb);
|
||||
}
|
||||
Return<void> StreamOut::getSupportedSampleRates(AudioFormat format,
|
||||
getSupportedSampleRates_cb _hidl_cb) {
|
||||
return mStreamCommon->getSupportedSampleRates(format, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setSampleRate(uint32_t sampleRateHz) {
|
||||
return mStreamCommon->setSampleRate(sampleRateHz);
|
||||
}
|
||||
|
||||
Return<AudioChannelBitfield> StreamOut::getChannelMask() {
|
||||
return mStreamCommon->getChannelMask();
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setChannelMask(AudioChannelBitfield mask) {
|
||||
return mStreamCommon->setChannelMask(mask);
|
||||
}
|
||||
|
||||
Return<AudioFormat> StreamOut::getFormat() {
|
||||
return mStreamCommon->getFormat();
|
||||
}
|
||||
|
||||
Return<void> StreamOut::getSupportedFormats(getSupportedFormats_cb _hidl_cb) {
|
||||
return mStreamCommon->getSupportedFormats(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setFormat(AudioFormat format) {
|
||||
return mStreamCommon->setFormat(format);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
Return<void> StreamOut::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
|
||||
return mStreamCommon->getSupportedProfiles(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setAudioProperties(const AudioConfigBaseOptional& config) {
|
||||
return mStreamCommon->setAudioProperties(config);
|
||||
}
|
||||
|
||||
#endif // MAJOR_VERSION <= 6
|
||||
|
||||
Return<void> StreamOut::getAudioProperties(getAudioProperties_cb _hidl_cb) {
|
||||
return mStreamCommon->getAudioProperties(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::addEffect(uint64_t effectId) {
|
||||
return mStreamCommon->addEffect(effectId);
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::removeEffect(uint64_t effectId) {
|
||||
return mStreamCommon->removeEffect(effectId);
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::standby() {
|
||||
return mStreamCommon->standby();
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setHwAvSync(uint32_t hwAvSync) {
|
||||
return mStreamCommon->setHwAvSync(hwAvSync);
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<Result> StreamOut::setConnectedState(const DeviceAddress& address, bool connected) {
|
||||
return mStreamCommon->setConnectedState(address, connected);
|
||||
}
|
||||
|
||||
Return<AudioDevice> StreamOut::getDevice() {
|
||||
return mStreamCommon->getDevice();
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setDevice(const DeviceAddress& address) {
|
||||
return mStreamCommon->setDevice(address);
|
||||
}
|
||||
|
||||
Return<void> StreamOut::getParameters(const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) {
|
||||
return mStreamCommon->getParameters(keys, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setParameters(const hidl_vec<ParameterValue>& parameters) {
|
||||
return mStreamCommon->setParameters(parameters);
|
||||
}
|
||||
|
||||
Return<void> StreamOut::debugDump(const hidl_handle& fd) {
|
||||
return mStreamCommon->debugDump(fd);
|
||||
}
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> StreamOut::getDevices(getDevices_cb _hidl_cb) {
|
||||
return mStreamCommon->getDevices(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setDevices(const hidl_vec<DeviceAddress>& devices) {
|
||||
return mStreamCommon->setDevices(devices);
|
||||
}
|
||||
Return<void> StreamOut::getParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) {
|
||||
return mStreamCommon->getParameters(context, keys, _hidl_cb);
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<ParameterValue>& parameters) {
|
||||
return mStreamCommon->setParameters(context, parameters);
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<Result> StreamOut::close() {
|
||||
if (mStopWriteThread.load(std::memory_order_relaxed)) { // only this thread writes
|
||||
return Result::INVALID_STATE;
|
||||
}
|
||||
mStopWriteThread.store(true, std::memory_order_release);
|
||||
if (mEfGroup) {
|
||||
mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
|
||||
}
|
||||
#if MAJOR_VERSION >= 6
|
||||
mDevice->closeOutputStream(mStream);
|
||||
#endif
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IStreamOut follow.
|
||||
Return<uint32_t> StreamOut::getLatency() {
|
||||
return mStream->get_latency(mStream);
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setVolume(float left, float right) {
|
||||
if (mStream->set_volume == NULL) {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
if (!util::isGainNormalized(left)) {
|
||||
ALOGW("Can not set a stream output volume {%f, %f} outside [0,1]", left, right);
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
}
|
||||
return Stream::analyzeStatus("set_volume", mStream->set_volume(mStream, left, right),
|
||||
{ENOSYS} /*ignore*/);
|
||||
}
|
||||
|
||||
Return<void> StreamOut::prepareForWriting(uint32_t frameSize, uint32_t framesCount,
|
||||
prepareForWriting_cb _hidl_cb) {
|
||||
status_t status;
|
||||
#if MAJOR_VERSION <= 6
|
||||
ThreadInfo threadInfo = {0, 0};
|
||||
#else
|
||||
int32_t threadInfo = 0;
|
||||
#endif
|
||||
|
||||
// Wrap the _hidl_cb to return an error
|
||||
auto sendError = [&threadInfo, &_hidl_cb](Result result) {
|
||||
_hidl_cb(result, CommandMQ::Descriptor(), DataMQ::Descriptor(), StatusMQ::Descriptor(),
|
||||
threadInfo);
|
||||
};
|
||||
|
||||
// Create message queues.
|
||||
if (mDataMQ) {
|
||||
ALOGE("the client attempts to call prepareForWriting twice");
|
||||
sendError(Result::INVALID_STATE);
|
||||
return Void();
|
||||
}
|
||||
std::unique_ptr<CommandMQ> tempCommandMQ(new CommandMQ(1));
|
||||
|
||||
// Check frameSize and framesCount
|
||||
if (frameSize == 0 || framesCount == 0) {
|
||||
ALOGE("Null frameSize (%u) or framesCount (%u)", frameSize, framesCount);
|
||||
sendError(Result::INVALID_ARGUMENTS);
|
||||
return Void();
|
||||
}
|
||||
if (frameSize > Stream::MAX_BUFFER_SIZE / framesCount) {
|
||||
ALOGE("Buffer too big: %u*%u bytes > MAX_BUFFER_SIZE (%u)", frameSize, framesCount,
|
||||
Stream::MAX_BUFFER_SIZE);
|
||||
sendError(Result::INVALID_ARGUMENTS);
|
||||
return Void();
|
||||
}
|
||||
std::unique_ptr<DataMQ> tempDataMQ(new DataMQ(frameSize * framesCount, true /* EventFlag */));
|
||||
|
||||
std::unique_ptr<StatusMQ> tempStatusMQ(new StatusMQ(1));
|
||||
if (!tempCommandMQ->isValid() || !tempDataMQ->isValid() || !tempStatusMQ->isValid()) {
|
||||
ALOGE_IF(!tempCommandMQ->isValid(), "command MQ is invalid");
|
||||
ALOGE_IF(!tempDataMQ->isValid(), "data MQ is invalid");
|
||||
ALOGE_IF(!tempStatusMQ->isValid(), "status MQ is invalid");
|
||||
sendError(Result::INVALID_ARGUMENTS);
|
||||
return Void();
|
||||
}
|
||||
EventFlag* tempRawEfGroup{};
|
||||
status = EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &tempRawEfGroup);
|
||||
std::unique_ptr<EventFlag, void (*)(EventFlag*)> tempElfGroup(
|
||||
tempRawEfGroup, [](auto* ef) { EventFlag::deleteEventFlag(&ef); });
|
||||
if (status != OK || !tempElfGroup) {
|
||||
ALOGE("failed creating event flag for data MQ: %s", strerror(-status));
|
||||
sendError(Result::INVALID_ARGUMENTS);
|
||||
return Void();
|
||||
}
|
||||
|
||||
// Create and launch the thread.
|
||||
auto tempWriteThread =
|
||||
sp<WriteThread>::make(&mStopWriteThread, mStream, tempCommandMQ.get(), tempDataMQ.get(),
|
||||
tempStatusMQ.get(), tempElfGroup.get());
|
||||
if (!tempWriteThread->init()) {
|
||||
ALOGW("failed to start writer thread: %s", strerror(-status));
|
||||
sendError(Result::INVALID_ARGUMENTS);
|
||||
return Void();
|
||||
}
|
||||
status = tempWriteThread->run("writer", PRIORITY_URGENT_AUDIO);
|
||||
if (status != OK) {
|
||||
ALOGW("failed to start writer thread: %s", strerror(-status));
|
||||
sendError(Result::INVALID_ARGUMENTS);
|
||||
return Void();
|
||||
}
|
||||
|
||||
mCommandMQ = std::move(tempCommandMQ);
|
||||
mDataMQ = std::move(tempDataMQ);
|
||||
mStatusMQ = std::move(tempStatusMQ);
|
||||
mWriteThread = tempWriteThread;
|
||||
mEfGroup = tempElfGroup.release();
|
||||
#if MAJOR_VERSION <= 6
|
||||
threadInfo.pid = getpid();
|
||||
threadInfo.tid = mWriteThread->getTid();
|
||||
#else
|
||||
threadInfo = mWriteThread->getTid();
|
||||
#endif
|
||||
_hidl_cb(Result::OK, *mCommandMQ->getDesc(), *mDataMQ->getDesc(), *mStatusMQ->getDesc(),
|
||||
threadInfo);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> StreamOut::getRenderPosition(getRenderPosition_cb _hidl_cb) {
|
||||
uint32_t halDspFrames;
|
||||
Result retval = Stream::analyzeStatus("get_render_position",
|
||||
mStream->get_render_position(mStream, &halDspFrames),
|
||||
{ENOSYS} /*ignore*/);
|
||||
_hidl_cb(retval, halDspFrames);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> StreamOut::getNextWriteTimestamp(getNextWriteTimestamp_cb _hidl_cb) {
|
||||
Result retval(Result::NOT_SUPPORTED);
|
||||
int64_t timestampUs = 0;
|
||||
if (mStream->get_next_write_timestamp != NULL) {
|
||||
retval = Stream::analyzeStatus("get_next_write_timestamp",
|
||||
mStream->get_next_write_timestamp(mStream, ×tampUs),
|
||||
{ENOSYS} /*ignore*/);
|
||||
}
|
||||
_hidl_cb(retval, timestampUs);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setCallback(const sp<IStreamOutCallback>& callback) {
|
||||
if (mStream->set_callback == NULL) return Result::NOT_SUPPORTED;
|
||||
// Safe to pass 'this' because it is guaranteed that the callback thread
|
||||
// is joined prior to exit from StreamOut's destructor.
|
||||
int result = mStream->set_callback(mStream, StreamOut::asyncCallback, this);
|
||||
if (result == 0) {
|
||||
mCallback = callback;
|
||||
}
|
||||
return Stream::analyzeStatus("set_callback", result, {ENOSYS} /*ignore*/);
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::clearCallback() {
|
||||
if (mStream->set_callback == NULL) return Result::NOT_SUPPORTED;
|
||||
mCallback = nullptr;
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
// static
|
||||
int StreamOut::asyncCallback(stream_callback_event_t event, void*, void* cookie) {
|
||||
// It is guaranteed that the callback thread is joined prior
|
||||
// to exiting from StreamOut's destructor. Must *not* use sp<StreamOut>
|
||||
// here because it can make this code the last owner of StreamOut,
|
||||
// and an attempt to run the destructor on the callback thread
|
||||
// will cause a deadlock in the legacy HAL code.
|
||||
StreamOut* self = reinterpret_cast<StreamOut*>(cookie);
|
||||
// It's correct to hold an sp<> to callback because the reference
|
||||
// in the StreamOut instance can be cleared in the meantime. There is
|
||||
// no difference on which thread to run IStreamOutCallback's destructor.
|
||||
sp<IStreamOutCallback> callback = self->mCallback.load();
|
||||
if (callback.get() == nullptr) return 0;
|
||||
ALOGV("asyncCallback() event %d", event);
|
||||
Return<void> result;
|
||||
switch (event) {
|
||||
case STREAM_CBK_EVENT_WRITE_READY:
|
||||
result = callback->onWriteReady();
|
||||
break;
|
||||
case STREAM_CBK_EVENT_DRAIN_READY:
|
||||
result = callback->onDrainReady();
|
||||
break;
|
||||
case STREAM_CBK_EVENT_ERROR:
|
||||
result = callback->onError();
|
||||
break;
|
||||
default:
|
||||
ALOGW("asyncCallback() unknown event %d", event);
|
||||
break;
|
||||
}
|
||||
ALOGW_IF(!result.isOk(), "Client callback failed: %s", result.description().c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
Return<void> StreamOut::supportsPauseAndResume(supportsPauseAndResume_cb _hidl_cb) {
|
||||
_hidl_cb(mStream->pause != NULL, mStream->resume != NULL);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::pause() {
|
||||
return mStream->pause != NULL
|
||||
? Stream::analyzeStatus("pause", mStream->pause(mStream), {ENOSYS} /*ignore*/)
|
||||
: Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::resume() {
|
||||
return mStream->resume != NULL
|
||||
? Stream::analyzeStatus("resume", mStream->resume(mStream), {ENOSYS} /*ignore*/)
|
||||
: Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Return<bool> StreamOut::supportsDrain() {
|
||||
return mStream->drain != NULL;
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::drain(AudioDrain type) {
|
||||
audio_drain_type_t halDrainType =
|
||||
type == AudioDrain::EARLY_NOTIFY ? AUDIO_DRAIN_EARLY_NOTIFY : AUDIO_DRAIN_ALL;
|
||||
return mStream->drain != NULL
|
||||
? Stream::analyzeStatus("drain", mStream->drain(mStream, halDrainType),
|
||||
{ENOSYS} /*ignore*/)
|
||||
: Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::flush() {
|
||||
return mStream->flush != NULL
|
||||
? Stream::analyzeStatus("flush", mStream->flush(mStream), {ENOSYS} /*ignore*/)
|
||||
: Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// static
|
||||
Result StreamOut::getPresentationPositionImpl(audio_stream_out_t* stream, uint64_t* frames,
|
||||
TimeSpec* timeStamp) {
|
||||
// Don't logspam on EINVAL--it's normal for get_presentation_position
|
||||
// to return it sometimes. EAGAIN may be returned by A2DP audio HAL
|
||||
// implementation. ENODATA can also be reported while the writer is
|
||||
// continuously querying it, but the stream has been stopped.
|
||||
static const std::vector<int> ignoredErrors{EINVAL, EAGAIN, ENODATA, ENOSYS};
|
||||
Result retval(Result::NOT_SUPPORTED);
|
||||
if (stream->get_presentation_position == NULL) return retval;
|
||||
struct timespec halTimeStamp;
|
||||
retval = Stream::analyzeStatus("get_presentation_position",
|
||||
stream->get_presentation_position(stream, frames, &halTimeStamp),
|
||||
ignoredErrors);
|
||||
if (retval == Result::OK) {
|
||||
timeStamp->tvSec = halTimeStamp.tv_sec;
|
||||
timeStamp->tvNSec = halTimeStamp.tv_nsec;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
Return<void> StreamOut::getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
|
||||
uint64_t frames = 0;
|
||||
TimeSpec timeStamp = {0, 0};
|
||||
Result retval = getPresentationPositionImpl(mStream, &frames, &timeStamp);
|
||||
_hidl_cb(retval, frames, timeStamp);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::start() {
|
||||
return mStreamMmap->start();
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::stop() {
|
||||
return mStreamMmap->stop();
|
||||
}
|
||||
|
||||
Return<void> StreamOut::createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) {
|
||||
return mStreamMmap->createMmapBuffer(minSizeFrames, audio_stream_out_frame_size(mStream),
|
||||
_hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> StreamOut::getMmapPosition(getMmapPosition_cb _hidl_cb) {
|
||||
return mStreamMmap->getMmapPosition(_hidl_cb);
|
||||
}
|
||||
|
||||
Return<void> StreamOut::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
|
||||
return mStreamCommon->debug(fd, options);
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION >= 4
|
||||
Result StreamOut::doUpdateSourceMetadata(const SourceMetadata& sourceMetadata) {
|
||||
std::vector<playback_track_metadata_t> halTracks;
|
||||
#if MAJOR_VERSION <= 6
|
||||
(void)CoreUtils::sourceMetadataToHal(sourceMetadata, &halTracks);
|
||||
#else
|
||||
// Validate whether a conversion to V7 is possible. This is needed
|
||||
// to have a consistent behavior of the HAL regardless of the API
|
||||
// version of the legacy HAL (and also to be consistent with openOutputStream).
|
||||
std::vector<playback_track_metadata_v7> halTracksV7;
|
||||
if (status_t status = CoreUtils::sourceMetadataToHalV7(
|
||||
sourceMetadata, false /*ignoreNonVendorTags*/, &halTracksV7);
|
||||
status == NO_ERROR) {
|
||||
halTracks.reserve(halTracksV7.size());
|
||||
for (auto metadata_v7 : halTracksV7) {
|
||||
halTracks.push_back(std::move(metadata_v7.base));
|
||||
}
|
||||
} else {
|
||||
return Stream::analyzeStatus("sourceMetadataToHal", status);
|
||||
}
|
||||
#endif // MAJOR_VERSION <= 6
|
||||
const source_metadata_t halMetadata = {
|
||||
.track_count = halTracks.size(),
|
||||
.tracks = halTracks.data(),
|
||||
};
|
||||
mStream->update_source_metadata(mStream, &halMetadata);
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION >= 7
|
||||
Result StreamOut::doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata) {
|
||||
std::vector<playback_track_metadata_v7> halTracks;
|
||||
if (status_t status = CoreUtils::sourceMetadataToHalV7(
|
||||
sourceMetadata, false /*ignoreNonVendorTags*/, &halTracks);
|
||||
status != NO_ERROR) {
|
||||
return Stream::analyzeStatus("sourceMetadataToHal", status);
|
||||
}
|
||||
const source_metadata_v7_t halMetadata = {
|
||||
.track_count = halTracks.size(),
|
||||
.tracks = halTracks.data(),
|
||||
};
|
||||
mStream->update_source_metadata_v7(mStream, &halMetadata);
|
||||
return Result::OK;
|
||||
}
|
||||
#endif // MAJOR_VERSION >= 7
|
||||
|
||||
#if MAJOR_VERSION <= 6
|
||||
Return<void> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
|
||||
if (mStream->update_source_metadata == nullptr) {
|
||||
return Void(); // not supported by the HAL
|
||||
}
|
||||
(void)doUpdateSourceMetadata(sourceMetadata);
|
||||
return Void();
|
||||
}
|
||||
#elif MAJOR_VERSION >= 7
|
||||
Return<Result> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
|
||||
if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) {
|
||||
if (mStream->update_source_metadata == nullptr) {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
return doUpdateSourceMetadata(sourceMetadata);
|
||||
} else {
|
||||
if (mStream->update_source_metadata_v7 == nullptr) {
|
||||
return Result::NOT_SUPPORTED;
|
||||
}
|
||||
return doUpdateSourceMetadataV7(sourceMetadata);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Return<Result> StreamOut::selectPresentation(int32_t /*presentationId*/, int32_t /*programId*/) {
|
||||
return Result::NOT_SUPPORTED; // TODO: propagate to legacy
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MAJOR_VERSION >= 6
|
||||
Return<void> StreamOut::getDualMonoMode(getDualMonoMode_cb _hidl_cb) {
|
||||
audio_dual_mono_mode_t mode = AUDIO_DUAL_MONO_MODE_OFF;
|
||||
Result retval = mStream->get_dual_mono_mode != nullptr
|
||||
? Stream::analyzeStatus("get_dual_mono_mode",
|
||||
mStream->get_dual_mono_mode(mStream, &mode))
|
||||
: Result::NOT_SUPPORTED;
|
||||
_hidl_cb(retval, DualMonoMode(mode));
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setDualMonoMode(DualMonoMode mode) {
|
||||
return mStream->set_dual_mono_mode != nullptr
|
||||
? Stream::analyzeStatus(
|
||||
"set_dual_mono_mode",
|
||||
mStream->set_dual_mono_mode(mStream,
|
||||
static_cast<audio_dual_mono_mode_t>(mode)))
|
||||
: Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Return<void> StreamOut::getAudioDescriptionMixLevel(getAudioDescriptionMixLevel_cb _hidl_cb) {
|
||||
float leveldB = -std::numeric_limits<float>::infinity();
|
||||
Result retval = mStream->get_audio_description_mix_level != nullptr
|
||||
? Stream::analyzeStatus(
|
||||
"get_audio_description_mix_level",
|
||||
mStream->get_audio_description_mix_level(mStream, &leveldB))
|
||||
: Result::NOT_SUPPORTED;
|
||||
_hidl_cb(retval, leveldB);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setAudioDescriptionMixLevel(float leveldB) {
|
||||
return mStream->set_audio_description_mix_level != nullptr
|
||||
? Stream::analyzeStatus(
|
||||
"set_audio_description_mix_level",
|
||||
mStream->set_audio_description_mix_level(mStream, leveldB))
|
||||
: Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Return<void> StreamOut::getPlaybackRateParameters(getPlaybackRateParameters_cb _hidl_cb) {
|
||||
audio_playback_rate_t rate = AUDIO_PLAYBACK_RATE_INITIALIZER;
|
||||
Result retval =
|
||||
mStream->get_playback_rate_parameters != nullptr
|
||||
? Stream::analyzeStatus("get_playback_rate_parameters",
|
||||
mStream->get_playback_rate_parameters(mStream, &rate))
|
||||
: Result::NOT_SUPPORTED;
|
||||
_hidl_cb(retval,
|
||||
PlaybackRate{rate.mSpeed, rate.mPitch, static_cast<TimestretchMode>(rate.mStretchMode),
|
||||
static_cast<TimestretchFallbackMode>(rate.mFallbackMode)});
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setPlaybackRateParameters(const PlaybackRate& playbackRate) {
|
||||
audio_playback_rate_t rate = {
|
||||
playbackRate.speed, playbackRate.pitch,
|
||||
static_cast<audio_timestretch_stretch_mode_t>(playbackRate.timestretchMode),
|
||||
static_cast<audio_timestretch_fallback_mode_t>(playbackRate.fallbackMode)};
|
||||
return mStream->set_playback_rate_parameters != nullptr
|
||||
? Stream::analyzeStatus("set_playback_rate_parameters",
|
||||
mStream->set_playback_rate_parameters(mStream, &rate))
|
||||
: Result::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setEventCallback(const sp<IStreamOutEventCallback>& callback) {
|
||||
if (mStream->set_event_callback == nullptr) return Result::NOT_SUPPORTED;
|
||||
int result = mStream->set_event_callback(mStream, StreamOut::asyncEventCallback, this);
|
||||
if (result == 0) {
|
||||
mEventCallback = callback;
|
||||
}
|
||||
return Stream::analyzeStatus("set_stream_out_callback", result, {ENOSYS} /*ignore*/);
|
||||
}
|
||||
|
||||
// static
|
||||
int StreamOut::asyncEventCallback(stream_event_callback_type_t event, void* param, void* cookie) {
|
||||
StreamOut* self = reinterpret_cast<StreamOut*>(cookie);
|
||||
sp<IStreamOutEventCallback> eventCallback = self->mEventCallback.load();
|
||||
if (eventCallback.get() == nullptr) return 0;
|
||||
ALOGV("%s event %d", __func__, event);
|
||||
Return<void> result;
|
||||
switch (event) {
|
||||
case STREAM_EVENT_CBK_TYPE_CODEC_FORMAT_CHANGED: {
|
||||
hidl_vec<uint8_t> audioMetadata;
|
||||
// void* param is the byte string buffer from byte_string_from_audio_metadata().
|
||||
// As the byte string buffer may have embedded zeroes, we cannot use strlen()
|
||||
// but instead use audio_utils::metadata::dataByteStringLen().
|
||||
audioMetadata.setToExternal((uint8_t*)param, audio_utils::metadata::dataByteStringLen(
|
||||
(const uint8_t*)param));
|
||||
result = eventCallback->onCodecFormatChanged(audioMetadata);
|
||||
} break;
|
||||
default:
|
||||
ALOGW("%s unknown event %d", __func__, event);
|
||||
break;
|
||||
}
|
||||
ALOGW_IF(!result.isOk(), "Client callback failed: %s", result.description().c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
|
||||
Return<Result> StreamOut::setLatencyMode(LatencyMode mode) {
|
||||
return mStream->set_latency_mode != nullptr
|
||||
? Stream::analyzeStatus(
|
||||
"set_latency_mode",
|
||||
mStream->set_latency_mode(mStream,
|
||||
static_cast<audio_latency_mode_t>(mode)))
|
||||
: Result::NOT_SUPPORTED;
|
||||
};
|
||||
|
||||
Return<void> StreamOut::getRecommendedLatencyModes(getRecommendedLatencyModes_cb _hidl_cb) {
|
||||
Result retval = Result::NOT_SUPPORTED;
|
||||
hidl_vec<LatencyMode> hidlModes;
|
||||
size_t num_modes = AUDIO_LATENCY_MODE_CNT;
|
||||
audio_latency_mode_t modes[AUDIO_LATENCY_MODE_CNT];
|
||||
|
||||
if (mStream->get_recommended_latency_modes != nullptr &&
|
||||
mStream->get_recommended_latency_modes(mStream, &modes[0], &num_modes) == 0) {
|
||||
if (num_modes == 0 || num_modes > AUDIO_LATENCY_MODE_CNT) {
|
||||
ALOGW("%s invalid number of modes returned: %zu", __func__, num_modes);
|
||||
retval = Result::INVALID_STATE;
|
||||
} else {
|
||||
hidlModes.resize(num_modes);
|
||||
for (size_t i = 0; i < num_modes; ++i) {
|
||||
hidlModes[i] = static_cast<LatencyMode>(modes[i]);
|
||||
}
|
||||
retval = Result::OK;
|
||||
}
|
||||
}
|
||||
_hidl_cb(retval, hidlModes);
|
||||
return Void();
|
||||
};
|
||||
|
||||
// static
|
||||
void StreamOut::latencyModeCallback(audio_latency_mode_t* modes, size_t num_modes, void* cookie) {
|
||||
StreamOut* self = reinterpret_cast<StreamOut*>(cookie);
|
||||
sp<IStreamOutLatencyModeCallback> callback = self->mLatencyModeCallback.load();
|
||||
if (callback.get() == nullptr) return;
|
||||
|
||||
ALOGV("%s", __func__);
|
||||
|
||||
if (num_modes == 0 || num_modes > AUDIO_LATENCY_MODE_CNT) {
|
||||
ALOGW("%s invalid number of modes returned: %zu", __func__, num_modes);
|
||||
return;
|
||||
}
|
||||
|
||||
hidl_vec<LatencyMode> hidlModes(num_modes);
|
||||
for (size_t i = 0; i < num_modes; ++i) {
|
||||
hidlModes[i] = static_cast<LatencyMode>(modes[i]);
|
||||
}
|
||||
Return<void> result = callback->onRecommendedLatencyModeChanged(hidlModes);
|
||||
ALOGW_IF(!result.isOk(), "Client callback failed: %s", result.description().c_str());
|
||||
}
|
||||
|
||||
Return<Result> StreamOut::setLatencyModeCallback(
|
||||
const sp<IStreamOutLatencyModeCallback>& callback) {
|
||||
if (mStream->set_latency_mode_callback == nullptr) return Result::NOT_SUPPORTED;
|
||||
int result = mStream->set_latency_mode_callback(mStream, StreamOut::latencyModeCallback, this);
|
||||
if (result == 0) {
|
||||
mLatencyModeCallback = callback;
|
||||
}
|
||||
return Stream::analyzeStatus("set_latency_mode_callback", result, {ENOSYS} /*ignore*/);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
201
bluetooth/audio/hal/StreamOut.h
Normal file
201
bluetooth/audio/hal/StreamOut.h
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_AUDIO_STREAMOUT_H
|
||||
#define ANDROID_HARDWARE_AUDIO_STREAMOUT_H
|
||||
|
||||
#include PATH(android/hardware/audio/FILE_VERSION/IStreamOut.h)
|
||||
|
||||
#include "Device.h"
|
||||
#include "Stream.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include <fmq/EventFlag.h>
|
||||
#include <fmq/MessageQueue.h>
|
||||
#include <hidl/MQDescriptor.h>
|
||||
#include <hidl/Status.h>
|
||||
#include <mediautils/Synchronization.h>
|
||||
#include <utils/Thread.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::Void;
|
||||
using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION;
|
||||
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION;
|
||||
using namespace ::android::hardware::audio::CPP_VERSION;
|
||||
|
||||
struct StreamOut : public IStreamOut {
|
||||
typedef MessageQueue<WriteCommand, kSynchronizedReadWrite> CommandMQ;
|
||||
typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
|
||||
typedef MessageQueue<WriteStatus, kSynchronizedReadWrite> StatusMQ;
|
||||
|
||||
StreamOut(const sp<Device>& device, audio_stream_out_t* stream);
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IStream follow.
|
||||
Return<uint64_t> getFrameSize() override;
|
||||
Return<uint64_t> getFrameCount() override;
|
||||
Return<uint64_t> getBufferSize() override;
|
||||
#if MAJOR_VERSION <= 6
|
||||
Return<uint32_t> getSampleRate() override;
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) override;
|
||||
Return<void> getSupportedChannelMasks(getSupportedChannelMasks_cb _hidl_cb) override;
|
||||
#endif
|
||||
Return<void> getSupportedSampleRates(AudioFormat format, getSupportedSampleRates_cb _hidl_cb);
|
||||
Return<void> getSupportedChannelMasks(AudioFormat format, getSupportedChannelMasks_cb _hidl_cb);
|
||||
Return<Result> setSampleRate(uint32_t sampleRateHz) override;
|
||||
Return<AudioChannelBitfield> getChannelMask() override;
|
||||
Return<Result> setChannelMask(AudioChannelBitfield mask) override;
|
||||
Return<AudioFormat> getFormat() override;
|
||||
Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
|
||||
Return<Result> setFormat(AudioFormat format) override;
|
||||
#else
|
||||
Return<void> getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) override;
|
||||
Return<Result> setAudioProperties(const AudioConfigBaseOptional& config) override;
|
||||
#endif // MAJOR_VERSION <= 6
|
||||
Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
|
||||
Return<Result> addEffect(uint64_t effectId) override;
|
||||
Return<Result> removeEffect(uint64_t effectId) override;
|
||||
Return<Result> standby() override;
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<AudioDevice> getDevice() override;
|
||||
Return<Result> setDevice(const DeviceAddress& address) override;
|
||||
Return<void> getParameters(const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) override;
|
||||
Return<Result> setParameters(const hidl_vec<ParameterValue>& parameters) override;
|
||||
Return<Result> setConnectedState(const DeviceAddress& address, bool connected) override;
|
||||
#elif MAJOR_VERSION >= 4
|
||||
Return<void> getDevices(getDevices_cb _hidl_cb) override;
|
||||
Return<Result> setDevices(const hidl_vec<DeviceAddress>& devices) override;
|
||||
Return<void> getParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<hidl_string>& keys,
|
||||
getParameters_cb _hidl_cb) override;
|
||||
Return<Result> setParameters(const hidl_vec<ParameterValue>& context,
|
||||
const hidl_vec<ParameterValue>& parameters) override;
|
||||
#endif
|
||||
Return<Result> setHwAvSync(uint32_t hwAvSync) override;
|
||||
Return<Result> close() override;
|
||||
|
||||
Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
|
||||
#if MAJOR_VERSION == 2
|
||||
Return<void> debugDump(const hidl_handle& fd) override;
|
||||
#endif
|
||||
|
||||
// Methods from ::android::hardware::audio::CPP_VERSION::IStreamOut follow.
|
||||
Return<uint32_t> getLatency() override;
|
||||
Return<Result> setVolume(float left, float right) override;
|
||||
Return<void> prepareForWriting(uint32_t frameSize, uint32_t framesCount,
|
||||
prepareForWriting_cb _hidl_cb) override;
|
||||
Return<void> getRenderPosition(getRenderPosition_cb _hidl_cb) override;
|
||||
Return<void> getNextWriteTimestamp(getNextWriteTimestamp_cb _hidl_cb) override;
|
||||
Return<Result> setCallback(const sp<IStreamOutCallback>& callback) override;
|
||||
Return<Result> clearCallback() override;
|
||||
Return<void> supportsPauseAndResume(supportsPauseAndResume_cb _hidl_cb) override;
|
||||
Return<Result> pause() override;
|
||||
Return<Result> resume() override;
|
||||
Return<bool> supportsDrain() override;
|
||||
Return<Result> drain(AudioDrain type) override;
|
||||
Return<Result> flush() override;
|
||||
Return<void> getPresentationPosition(getPresentationPosition_cb _hidl_cb) override;
|
||||
Return<Result> start() override;
|
||||
Return<Result> stop() override;
|
||||
Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
|
||||
Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
|
||||
#if MAJOR_VERSION >= 4
|
||||
Return<Result> selectPresentation(int32_t presentationId, int32_t programId) override;
|
||||
#if MAJOR_VERSION <= 6
|
||||
Return<void> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
|
||||
#else
|
||||
Return<Result> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
|
||||
#endif
|
||||
#endif // MAJOR_VERSION >= 4
|
||||
#if MAJOR_VERSION >= 6
|
||||
Return<void> getDualMonoMode(getDualMonoMode_cb _hidl_cb) override;
|
||||
Return<Result> setDualMonoMode(DualMonoMode mode) override;
|
||||
Return<void> getAudioDescriptionMixLevel(getAudioDescriptionMixLevel_cb _hidl_cb) override;
|
||||
Return<Result> setAudioDescriptionMixLevel(float leveldB) override;
|
||||
Return<void> getPlaybackRateParameters(getPlaybackRateParameters_cb _hidl_cb) override;
|
||||
Return<Result> setPlaybackRateParameters(const PlaybackRate& playbackRate) override;
|
||||
#endif
|
||||
|
||||
static Result getPresentationPositionImpl(audio_stream_out_t* stream, uint64_t* frames,
|
||||
TimeSpec* timeStamp);
|
||||
|
||||
#if MAJOR_VERSION >= 6
|
||||
Return<Result> setEventCallback(const sp<IStreamOutEventCallback>& callback) override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
#if MAJOR_VERSION >= 4
|
||||
Result doUpdateSourceMetadata(const SourceMetadata& sourceMetadata);
|
||||
#if MAJOR_VERSION >= 7
|
||||
Result doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata);
|
||||
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
|
||||
Return<Result> setLatencyMode(LatencyMode mode) override;
|
||||
Return<void> getRecommendedLatencyModes(getRecommendedLatencyModes_cb _hidl_cb) override;
|
||||
Return<Result> setLatencyModeCallback(
|
||||
const sp<IStreamOutLatencyModeCallback>& callback) override;
|
||||
#endif
|
||||
#endif
|
||||
#endif // MAJOR_VERSION >= 4
|
||||
|
||||
const sp<Device> mDevice;
|
||||
audio_stream_out_t* mStream;
|
||||
const sp<Stream> mStreamCommon;
|
||||
const sp<StreamMmap<audio_stream_out_t>> mStreamMmap;
|
||||
mediautils::atomic_sp<IStreamOutCallback> mCallback; // for non-blocking write and drain
|
||||
#if MAJOR_VERSION >= 6
|
||||
mediautils::atomic_sp<IStreamOutEventCallback> mEventCallback;
|
||||
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
|
||||
mediautils::atomic_sp<IStreamOutLatencyModeCallback> mLatencyModeCallback;
|
||||
#endif
|
||||
#endif
|
||||
std::unique_ptr<CommandMQ> mCommandMQ;
|
||||
std::unique_ptr<DataMQ> mDataMQ;
|
||||
std::unique_ptr<StatusMQ> mStatusMQ;
|
||||
EventFlag* mEfGroup;
|
||||
std::atomic<bool> mStopWriteThread;
|
||||
sp<Thread> mWriteThread;
|
||||
|
||||
virtual ~StreamOut();
|
||||
|
||||
static int asyncCallback(stream_callback_event_t event, void* param, void* cookie);
|
||||
|
||||
#if MAJOR_VERSION >= 6
|
||||
static int asyncEventCallback(stream_event_callback_type_t event, void* param, void* cookie);
|
||||
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
|
||||
static void latencyModeCallback(audio_latency_mode_t* modes, size_t num_modes, void* cookie);
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUDIO_STREAMOUT_H
|
82
bluetooth/audio/hal/Util.h
Normal file
82
bluetooth/audio/hal/Util.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_AUDIO_UTIL_H
|
||||
#define ANDROID_HARDWARE_AUDIO_UTIL_H
|
||||
|
||||
// clang-format off
|
||||
#include PATH(android/hardware/audio/CORE_TYPES_FILE_VERSION/types.h)
|
||||
// clang-format on
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <system/audio.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace audio {
|
||||
namespace CORE_TYPES_CPP_VERSION {
|
||||
namespace implementation {
|
||||
|
||||
using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION;
|
||||
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION;
|
||||
|
||||
namespace util {
|
||||
|
||||
/** @return true if gain is between 0 and 1 included. */
|
||||
constexpr bool isGainNormalized(float gain) {
|
||||
return gain >= 0.0 && gain <= 1.0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool element_in(T e, const std::vector<T>& v) {
|
||||
return std::find(v.begin(), v.end(), e) != v.end();
|
||||
}
|
||||
|
||||
static inline Result analyzeStatus(status_t status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return Result::OK;
|
||||
case -EINVAL:
|
||||
return Result::INVALID_ARGUMENTS;
|
||||
case -ENODATA:
|
||||
return Result::INVALID_STATE;
|
||||
case -ENODEV:
|
||||
return Result::NOT_INITIALIZED;
|
||||
case -ENOSYS:
|
||||
return Result::NOT_SUPPORTED;
|
||||
default:
|
||||
return Result::INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline Result analyzeStatus(const char* className, const char* funcName, status_t status,
|
||||
const std::vector<int>& ignoreErrors = {}) {
|
||||
if (status != 0 && !element_in(-status, ignoreErrors)) {
|
||||
ALOGW("Error from HAL %s in function %s: %s", className, funcName, strerror(-status));
|
||||
}
|
||||
return analyzeStatus(status);
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace implementation
|
||||
} // namespace CORE_TYPES_CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUDIO_UTIL_H
|
@ -0,0 +1,9 @@
|
||||
service system.bt-audio-hal /system/bin/hw/android.hardware.bluetooth.audio-service-system
|
||||
class hal
|
||||
user audioserver
|
||||
# media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
|
||||
group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub
|
||||
capabilities BLOCK_SUSPEND
|
||||
ioprio rt 4
|
||||
task_profiles ProcessCapacityHigh HighPerformance
|
||||
onrestart restart audioserver
|
7
bluetooth/audio/hal/bluetooth_audio.xml
Normal file
7
bluetooth/audio/hal/bluetooth_audio.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.bluetooth.audio</name>
|
||||
<version>2</version>
|
||||
<fqname>IBluetoothAudioProviderFactory/default</fqname>
|
||||
</hal>
|
||||
</manifest>
|
12
bluetooth/audio/hal/bluetooth_audio_system.xml
Normal file
12
bluetooth/audio/hal/bluetooth_audio_system.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<manifest version="1.0" type="framework">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.bluetooth.audio</name>
|
||||
<version>2</version>
|
||||
<fqname>IBluetoothAudioProviderFactory/sysbta</fqname>
|
||||
</hal>
|
||||
<hal>
|
||||
<name>android.hardware.audio</name>
|
||||
<transport>hwbinder</transport>
|
||||
<fqname>@6.0::IDevicesFactory/sysbta</fqname>
|
||||
</hal>
|
||||
</manifest>
|
38
bluetooth/audio/hal/service.cpp
Normal file
38
bluetooth/audio/hal/service.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 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 "BtAudioAIDLService"
|
||||
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/binder_process.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include "BluetoothAudioProviderFactory.h"
|
||||
|
||||
using ::aidl::android::hardware::bluetooth::audio::
|
||||
BluetoothAudioProviderFactory;
|
||||
|
||||
extern "C" __attribute__((visibility("default"))) binder_status_t
|
||||
createIBluetoothAudioProviderFactory() {
|
||||
auto factory = ::ndk::SharedRefBase::make<BluetoothAudioProviderFactory>();
|
||||
const std::string instance_name =
|
||||
std::string() + BluetoothAudioProviderFactory::descriptor + "/default";
|
||||
binder_status_t aidl_status = AServiceManager_addService(
|
||||
factory->asBinder().get(), instance_name.c_str());
|
||||
ALOGW_IF(aidl_status != STATUS_OK, "Could not register %s, status=%d",
|
||||
instance_name.c_str(), aidl_status);
|
||||
return aidl_status;
|
||||
}
|
65
bluetooth/audio/hal/service_system.cpp
Normal file
65
bluetooth/audio/hal/service_system.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 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 "BtAudioAIDLServiceSystem"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/binder_process.h>
|
||||
#include <utils/Log.h>
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
#include <hidl/LegacySupport.h>
|
||||
#include <hwbinder/ProcessState.h>
|
||||
#include <binder/ProcessState.h>
|
||||
|
||||
#include PATH(android/hardware/audio/FILE_VERSION/IDevicesFactory.h)
|
||||
|
||||
#include <hardware/audio.h>
|
||||
|
||||
#include "BluetoothAudioProviderFactory.h"
|
||||
#include "DevicesFactory.h"
|
||||
|
||||
//using namespace android::hardware;
|
||||
using ::aidl::android::hardware::bluetooth::audio::
|
||||
BluetoothAudioProviderFactory;
|
||||
|
||||
using ::android::hardware::audio::CPP_VERSION::implementation::DevicesFactory;
|
||||
using namespace ::android::hardware::audio::CPP_VERSION;
|
||||
|
||||
int main() {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
::android::hardware::configureRpcThreadpool(16, true);
|
||||
::android::ProcessState::initWithDriver("/dev/binder");
|
||||
// start a threadpool for binder / hwbinder interactions
|
||||
::android::ProcessState::self()->startThreadPool();
|
||||
::android::hardware::ProcessState::self()->startThreadPool();
|
||||
|
||||
auto factory = ::ndk::SharedRefBase::make<BluetoothAudioProviderFactory>();
|
||||
const std::string instance_name =
|
||||
std::string() + BluetoothAudioProviderFactory::descriptor + "/sysbta";
|
||||
binder_status_t aidl_status = AServiceManager_addService(
|
||||
factory->asBinder().get(), instance_name.c_str());
|
||||
ALOGW_IF(aidl_status != STATUS_OK, "Could not register %s, status=%d",
|
||||
instance_name.c_str(), aidl_status);
|
||||
|
||||
::android::sp<IDevicesFactory> audio_factory = new DevicesFactory();
|
||||
::android::status_t hidl_status = audio_factory->registerAsService("sysbta");
|
||||
ALOGW_IF(hidl_status != STATUS_OK, "Could not register sysbta, status=%d", hidl_status);
|
||||
|
||||
::android::hardware::joinRpcThreadpool();
|
||||
}
|
33
bluetooth/audio/hw/Android.bp
Normal file
33
bluetooth/audio/hw/Android.bp
Normal file
@ -0,0 +1,33 @@
|
||||
cc_library_shared {
|
||||
name: "audio.sysbta.default",
|
||||
relative_install_path: "hw",
|
||||
srcs: [
|
||||
"audio_bluetooth_hw.cc",
|
||||
"stream_apis.cc",
|
||||
"device_port_proxy.cc",
|
||||
"device_port_proxy_hidl.cc",
|
||||
"utils.cc",
|
||||
],
|
||||
header_libs: ["libhardware_headers"],
|
||||
shared_libs: [
|
||||
"android.hardware.bluetooth.audio-V2-ndk",
|
||||
"libbluetooth_audio_session_aidl_system",
|
||||
"libaudioutils",
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"libcutils",
|
||||
"libfmq",
|
||||
"liblog",
|
||||
"libutils",
|
||||
// HIDL dependencies
|
||||
"android.hardware.bluetooth.audio@2.0",
|
||||
"android.hardware.bluetooth.audio@2.1",
|
||||
"libbluetooth_audio_session_system",
|
||||
"libhidlbase",
|
||||
],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wno-unused-parameter",
|
||||
],
|
||||
}
|
161
bluetooth/audio/hw/audio_bluetooth_hw.cc
Normal file
161
bluetooth/audio/hw/audio_bluetooth_hw.cc
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2019 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 "BTAudioHw"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <errno.h>
|
||||
#include <hardware/hardware.h>
|
||||
#include <log/log.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <system/audio.h>
|
||||
|
||||
#include "stream_apis.h"
|
||||
#include "utils.h"
|
||||
|
||||
using ::android::bluetooth::audio::utils::GetAudioParamString;
|
||||
using ::android::bluetooth::audio::utils::ParseAudioParams;
|
||||
|
||||
static int adev_set_parameters(struct audio_hw_device* dev,
|
||||
const char* kvpairs) {
|
||||
LOG(VERBOSE) << __func__ << ": kevpairs=[" << kvpairs << "]";
|
||||
std::unordered_map<std::string, std::string> params =
|
||||
ParseAudioParams(kvpairs);
|
||||
if (params.empty()) return 0;
|
||||
|
||||
LOG(VERBOSE) << __func__ << ": ParamsMap=[" << GetAudioParamString(params)
|
||||
<< "]";
|
||||
if (params.find("A2dpSuspended") == params.end()) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(dev);
|
||||
std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
|
||||
for (auto sout : bluetooth_device->opened_stream_outs_) {
|
||||
if (sout->stream_out_.common.set_parameters != nullptr) {
|
||||
sout->stream_out_.common.set_parameters(&sout->stream_out_.common,
|
||||
kvpairs);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char* adev_get_parameters(const struct audio_hw_device* dev,
|
||||
const char* keys) {
|
||||
LOG(VERBOSE) << __func__ << ": keys=[" << keys << "]";
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
static int adev_init_check(const struct audio_hw_device* dev) { return 0; }
|
||||
|
||||
static int adev_set_voice_volume(struct audio_hw_device* dev, float volume) {
|
||||
LOG(VERBOSE) << __func__ << ": volume=" << volume;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int adev_set_master_volume(struct audio_hw_device* dev, float volume) {
|
||||
LOG(VERBOSE) << __func__ << ": volume=" << volume;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int adev_get_master_volume(struct audio_hw_device* dev, float* volume) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int adev_set_master_mute(struct audio_hw_device* dev, bool muted) {
|
||||
LOG(VERBOSE) << __func__ << ": mute=" << muted;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int adev_get_master_mute(struct audio_hw_device* dev, bool* muted) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int adev_set_mode(struct audio_hw_device* dev, audio_mode_t mode) {
|
||||
LOG(VERBOSE) << __func__ << ": mode=" << mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adev_set_mic_mute(struct audio_hw_device* dev, bool state) {
|
||||
LOG(VERBOSE) << __func__ << ": state=" << state;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int adev_get_mic_mute(const struct audio_hw_device* dev, bool* state) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int adev_dump(const audio_hw_device_t* device, int fd) { return 0; }
|
||||
|
||||
static int adev_close(hw_device_t* device) {
|
||||
auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(device);
|
||||
delete bluetooth_device;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adev_open(const hw_module_t* module, const char* name,
|
||||
hw_device_t** device) {
|
||||
LOG(VERBOSE) << __func__ << ": name=[" << name << "]";
|
||||
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL;
|
||||
|
||||
auto bluetooth_audio_device = new BluetoothAudioDevice{};
|
||||
struct audio_hw_device* adev = &bluetooth_audio_device->audio_device_;
|
||||
if (!adev) return -ENOMEM;
|
||||
|
||||
adev->common.tag = HARDWARE_DEVICE_TAG;
|
||||
adev->common.version = AUDIO_DEVICE_API_VERSION_2_0;
|
||||
adev->common.module = (struct hw_module_t*)module;
|
||||
adev->common.close = adev_close;
|
||||
|
||||
adev->init_check = adev_init_check;
|
||||
adev->set_voice_volume = adev_set_voice_volume;
|
||||
adev->set_master_volume = adev_set_master_volume;
|
||||
adev->get_master_volume = adev_get_master_volume;
|
||||
adev->set_mode = adev_set_mode;
|
||||
adev->set_mic_mute = adev_set_mic_mute;
|
||||
adev->get_mic_mute = adev_get_mic_mute;
|
||||
adev->set_parameters = adev_set_parameters;
|
||||
adev->get_parameters = adev_get_parameters;
|
||||
adev->get_input_buffer_size = adev_get_input_buffer_size;
|
||||
adev->open_output_stream = adev_open_output_stream;
|
||||
adev->close_output_stream = adev_close_output_stream;
|
||||
adev->open_input_stream = adev_open_input_stream;
|
||||
adev->close_input_stream = adev_close_input_stream;
|
||||
adev->dump = adev_dump;
|
||||
adev->set_master_mute = adev_set_master_mute;
|
||||
adev->get_master_mute = adev_get_master_mute;
|
||||
|
||||
*device = &adev->common;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hw_module_methods_t hal_module_methods = {
|
||||
.open = adev_open,
|
||||
};
|
||||
|
||||
struct audio_module HAL_MODULE_INFO_SYM = {
|
||||
.common =
|
||||
{
|
||||
.tag = HARDWARE_MODULE_TAG,
|
||||
.module_api_version = AUDIO_MODULE_API_VERSION_0_1,
|
||||
.hal_api_version = HARDWARE_HAL_API_VERSION,
|
||||
.id = AUDIO_HARDWARE_MODULE_ID,
|
||||
.name = "Bluetooth Audio HW HAL",
|
||||
.author = "The Android Open Source Project",
|
||||
.methods = &hal_module_methods,
|
||||
},
|
||||
};
|
618
bluetooth/audio/hw/device_port_proxy.cc
Normal file
618
bluetooth/audio/hw/device_port_proxy.cc
Normal file
@ -0,0 +1,618 @@
|
||||
/*
|
||||
* 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
|
213
bluetooth/audio/hw/device_port_proxy.h
Normal file
213
bluetooth/audio/hw/device_port_proxy.h
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/bluetooth/audio/BluetoothAudioStatus.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
|
||||
#include <hardware/audio.h>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
enum class BluetoothStreamState : uint8_t;
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
/***
|
||||
* 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, in stream_apis.cc, if GetState() is only used for
|
||||
* verbose logging, it is not locked, so the state may not be synchronized.
|
||||
***/
|
||||
class BluetoothAudioPort {
|
||||
public:
|
||||
BluetoothAudioPort(){};
|
||||
virtual ~BluetoothAudioPort() = default;
|
||||
|
||||
/***
|
||||
* Fetch output control / data path of BluetoothAudioPort and setup
|
||||
* callbacks into BluetoothAudioProvider. If SetUp() returns false, the audio
|
||||
* HAL must delete this BluetoothAudioPort and return EINVAL to caller
|
||||
***/
|
||||
virtual bool SetUp(audio_devices_t) { return false; }
|
||||
|
||||
/***
|
||||
* Unregister this BluetoothAudioPort from BluetoothAudioSessionControl.
|
||||
* Audio HAL must delete this BluetoothAudioPort after calling this.
|
||||
***/
|
||||
virtual void TearDown() {}
|
||||
|
||||
/***
|
||||
* 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(audio_config_t*) const { return false; };
|
||||
|
||||
/***
|
||||
* WAR to support Mono mode / 16 bits per sample
|
||||
***/
|
||||
virtual void ForcePcmStereoToMono(bool) {}
|
||||
|
||||
/***
|
||||
* 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() { return false; }
|
||||
|
||||
/***
|
||||
* Called by Audio framework / HAL to suspend the stream
|
||||
***/
|
||||
virtual bool Suspend() { return false; };
|
||||
|
||||
/***
|
||||
virtual bool Suspend() { return false; }
|
||||
* Called by Audio framework / HAL to stop the stream
|
||||
***/
|
||||
virtual void Stop() {}
|
||||
|
||||
/***
|
||||
* 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(uint64_t*, uint64_t*, timespec*) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/***
|
||||
* Called by the Audio framework / HAL when the metadata of the stream's
|
||||
* source has been changed.
|
||||
***/
|
||||
virtual void UpdateSourceMetadata(const source_metadata*) const {};
|
||||
|
||||
/***
|
||||
* Return the current BluetoothStreamState
|
||||
***/
|
||||
virtual BluetoothStreamState GetState() const {
|
||||
return static_cast<BluetoothStreamState>(0);
|
||||
}
|
||||
|
||||
/***
|
||||
* Set the current BluetoothStreamState
|
||||
***/
|
||||
virtual void SetState(BluetoothStreamState state) {}
|
||||
|
||||
virtual bool IsA2dp() const { return false; }
|
||||
|
||||
virtual bool GetPreferredDataIntervalUs(size_t* interval_us) const {
|
||||
return false;
|
||||
};
|
||||
|
||||
virtual size_t WriteData(const void* buffer, size_t bytes) const {
|
||||
return 0;
|
||||
};
|
||||
virtual size_t ReadData(void* buffer, size_t bytes) const { return 0; };
|
||||
};
|
||||
|
||||
namespace aidl {
|
||||
|
||||
using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus;
|
||||
using ::aidl::android::hardware::bluetooth::audio::SessionType;
|
||||
|
||||
class BluetoothAudioPortAidl : public BluetoothAudioPort {
|
||||
public:
|
||||
BluetoothAudioPortAidl();
|
||||
virtual ~BluetoothAudioPortAidl() = default;
|
||||
|
||||
bool SetUp(audio_devices_t devices) override;
|
||||
|
||||
void TearDown() override;
|
||||
|
||||
void ForcePcmStereoToMono(bool force) override { is_stereo_to_mono_ = force; }
|
||||
|
||||
bool Start() override;
|
||||
bool Suspend() override;
|
||||
void Stop() override;
|
||||
|
||||
bool GetPresentationPosition(uint64_t* delay_ns, uint64_t* byte,
|
||||
timespec* timestamp) const override;
|
||||
|
||||
void UpdateSourceMetadata(
|
||||
const source_metadata* source_metadata) const override;
|
||||
|
||||
/***
|
||||
* Called by the Audio framework / HAL when the metadata of the stream's
|
||||
* sink has been changed.
|
||||
***/
|
||||
virtual void UpdateSinkMetadata(const sink_metadata* sink_metadata) const;
|
||||
|
||||
BluetoothStreamState GetState() const override;
|
||||
|
||||
void SetState(BluetoothStreamState state) override;
|
||||
|
||||
bool IsA2dp() const override {
|
||||
return session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
|
||||
}
|
||||
|
||||
bool GetPreferredDataIntervalUs(size_t* interval_us) const override;
|
||||
|
||||
protected:
|
||||
uint16_t cookie_;
|
||||
BluetoothStreamState state_;
|
||||
SessionType session_type_;
|
||||
// WR to support Mono: True if fetching Stereo and mixing into Mono
|
||||
bool is_stereo_to_mono_ = false;
|
||||
virtual bool in_use() const;
|
||||
|
||||
private:
|
||||
mutable std::mutex cv_mutex_;
|
||||
std::condition_variable internal_cv_;
|
||||
|
||||
// Check and initialize session type for |devices| If failed, this
|
||||
// BluetoothAudioPortAidl is not initialized and must be deleted.
|
||||
bool init_session_type(audio_devices_t device);
|
||||
|
||||
bool CondwaitState(BluetoothStreamState state);
|
||||
|
||||
void ControlResultHandler(const BluetoothAudioStatus& status);
|
||||
void SessionChangedHandler();
|
||||
};
|
||||
|
||||
class BluetoothAudioPortAidlOut : public BluetoothAudioPortAidl {
|
||||
public:
|
||||
~BluetoothAudioPortAidlOut();
|
||||
|
||||
// The audio data path to the Bluetooth stack (Software encoding)
|
||||
size_t WriteData(const void* buffer, size_t bytes) const override;
|
||||
bool LoadAudioConfig(audio_config_t* audio_cfg) const override;
|
||||
};
|
||||
|
||||
class BluetoothAudioPortAidlIn : public BluetoothAudioPortAidl {
|
||||
public:
|
||||
~BluetoothAudioPortAidlIn();
|
||||
|
||||
// The audio data path from the Bluetooth stack (Software decoded)
|
||||
size_t ReadData(void* buffer, size_t bytes) const override;
|
||||
bool LoadAudioConfig(audio_config_t* audio_cfg) const override;
|
||||
};
|
||||
|
||||
} // namespace aidl
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
621
bluetooth/audio/hw/device_port_proxy_hidl.cc
Normal file
621
bluetooth/audio/hw/device_port_proxy_hidl.cc
Normal file
@ -0,0 +1,621 @@
|
||||
/*
|
||||
* 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 "BTAudioHalDeviceProxyHIDL"
|
||||
|
||||
#include "device_port_proxy_hidl.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_2_1.h"
|
||||
#include "stream_apis.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
namespace hidl {
|
||||
|
||||
using ::android::base::StringPrintf;
|
||||
using ::android::bluetooth::audio::BluetoothAudioSessionControl_2_1;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
|
||||
using SampleRate = ::android::hardware::bluetooth::audio::V2_0::SampleRate;
|
||||
using SampleRate_2_1 = ::android::hardware::bluetooth::audio::V2_1::SampleRate;
|
||||
using BluetoothAudioStatusHidl =
|
||||
::android::hardware::bluetooth::audio::V2_0::Status;
|
||||
using ControlResultCallback = std::function<void(
|
||||
uint16_t cookie, bool start_resp, const BluetoothAudioStatusHidl& status)>;
|
||||
using SessionChangedCallback = std::function<void(uint16_t cookie)>;
|
||||
|
||||
namespace {
|
||||
|
||||
unsigned int SampleRateToAudioFormat(SampleRate_2_1 sample_rate) {
|
||||
switch (sample_rate) {
|
||||
case SampleRate_2_1::RATE_8000:
|
||||
return 8000;
|
||||
case SampleRate_2_1::RATE_16000:
|
||||
return 16000;
|
||||
case SampleRate_2_1::RATE_24000:
|
||||
return 24000;
|
||||
case SampleRate_2_1::RATE_32000:
|
||||
return 32000;
|
||||
case SampleRate_2_1::RATE_44100:
|
||||
return 44100;
|
||||
case SampleRate_2_1::RATE_48000:
|
||||
return 48000;
|
||||
case SampleRate_2_1::RATE_88200:
|
||||
return 88200;
|
||||
case SampleRate_2_1::RATE_96000:
|
||||
return 96000;
|
||||
case SampleRate_2_1::RATE_176400:
|
||||
return 176400;
|
||||
case SampleRate_2_1::RATE_192000:
|
||||
return 192000;
|
||||
default:
|
||||
return kBluetoothDefaultSampleRate;
|
||||
}
|
||||
}
|
||||
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(BitsPerSample bits_per_sample) {
|
||||
switch (bits_per_sample) {
|
||||
case BitsPerSample::BITS_16:
|
||||
return AUDIO_FORMAT_PCM_16_BIT;
|
||||
case BitsPerSample::BITS_24:
|
||||
return AUDIO_FORMAT_PCM_24_BIT_PACKED;
|
||||
case BitsPerSample::BITS_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
|
||||
|
||||
BluetoothAudioPortHidl::BluetoothAudioPortHidl()
|
||||
: session_type_hidl_(SessionType_2_1::UNKNOWN),
|
||||
cookie_(android::bluetooth::audio::kObserversCookieUndefined),
|
||||
state_(BluetoothStreamState::DISABLED) {}
|
||||
|
||||
BluetoothAudioPortHidlOut::~BluetoothAudioPortHidlOut() {
|
||||
if (BluetoothAudioPortHidl::in_use()) BluetoothAudioPortHidl::TearDown();
|
||||
}
|
||||
|
||||
BluetoothAudioPortHidlIn::~BluetoothAudioPortHidlIn() {
|
||||
if (BluetoothAudioPortHidl::in_use()) BluetoothAudioPortHidl::TearDown();
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortHidl::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 BluetoothAudioStatusHidl& status) {
|
||||
if (!port->in_use()) {
|
||||
LOG(ERROR) << "control_result_cb: BluetoothAudioPort 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: BluetoothAudioPort 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();
|
||||
};
|
||||
::android::bluetooth::audio::PortStatusCallbacks cbacks = {
|
||||
.control_result_cb_ = control_result_cb,
|
||||
.session_changed_cb_ = session_changed_cb};
|
||||
cookie_ = BluetoothAudioSessionControl_2_1::RegisterControlResultCback(
|
||||
session_type_hidl_, cbacks);
|
||||
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_);
|
||||
|
||||
return (cookie_ != android::bluetooth::audio::kObserversCookieUndefined);
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortHidl::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_hidl_ = SessionType_2_1::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_hidl_ =
|
||||
SessionType_2_1::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_hidl_ = SessionType_2_1::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_hidl_ = SessionType_2_1::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_hidl_ = SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH;
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << __func__
|
||||
<< ": unknown device=" << StringPrintf("%#x", device);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!BluetoothAudioSessionControl_2_1::IsSessionReady(session_type_hidl_)) {
|
||||
LOG(ERROR) << __func__ << ": device=" << StringPrintf("%#x", device)
|
||||
<< ", session_type=" << toString(session_type_hidl_)
|
||||
<< " is not ready";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BluetoothAudioPortHidl::TearDown() {
|
||||
if (!in_use()) {
|
||||
LOG(ERROR) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< " unknown monitor";
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_);
|
||||
BluetoothAudioSessionControl_2_1::UnregisterControlResultCback(
|
||||
session_type_hidl_, cookie_);
|
||||
cookie_ = android::bluetooth::audio::kObserversCookieUndefined;
|
||||
}
|
||||
|
||||
void BluetoothAudioPortHidl::ControlResultHandler(
|
||||
const BluetoothAudioStatusHidl& status) {
|
||||
if (!in_use()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortis 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_hidl_)
|
||||
<< ", 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::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_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", previous_state=" << previous_state;
|
||||
}
|
||||
break;
|
||||
case BluetoothStreamState::STARTING:
|
||||
if (status == BluetoothAudioStatusHidl::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_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", previous_state=" << previous_state;
|
||||
state_ = BluetoothStreamState::STANDBY;
|
||||
}
|
||||
break;
|
||||
case BluetoothStreamState::SUSPENDING:
|
||||
if (status == BluetoothAudioStatusHidl::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_hidl_)
|
||||
<< ", 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_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", previous_state=" << previous_state;
|
||||
return;
|
||||
}
|
||||
port_lock.unlock();
|
||||
internal_cv_.notify_all();
|
||||
}
|
||||
|
||||
void BluetoothAudioPortHidl::SessionChangedHandler() {
|
||||
if (!in_use()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPort 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_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", previous_state=" << previous_state;
|
||||
state_ = BluetoothStreamState::DISABLED;
|
||||
port_lock.unlock();
|
||||
internal_cv_.notify_all();
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortHidl::in_use() const {
|
||||
return (cookie_ != android::bluetooth::audio::kObserversCookieUndefined);
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortHidl::GetPreferredDataIntervalUs(
|
||||
size_t* interval_us) const {
|
||||
if (!in_use()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||
hal_audio_cfg =
|
||||
BluetoothAudioSessionControl_2_1::GetAudioConfig(session_type_hidl_);
|
||||
if (hal_audio_cfg.getDiscriminator() !=
|
||||
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
|
||||
hidl_discriminator::pcmConfig) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ::android::hardware::bluetooth::audio::V2_1::PcmParameters& pcm_cfg =
|
||||
hal_audio_cfg.pcmConfig();
|
||||
*interval_us = pcm_cfg.dataIntervalUs;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortHidl::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_hidl_)
|
||||
<< ", 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_hidl_)
|
||||
<< ", 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_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< " waiting for KNOWN";
|
||||
return false;
|
||||
}
|
||||
|
||||
return retval; // false if any failure like timeout
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortHidl::Start() {
|
||||
if (!in_use()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPort is not in use";
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", 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_2_1::StartStream(session_type_hidl_)) {
|
||||
retval = CondwaitState(BluetoothStreamState::STARTING);
|
||||
} else {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", state=" << state_ << " Hal fails";
|
||||
}
|
||||
}
|
||||
|
||||
if (retval) {
|
||||
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", state=" << state_
|
||||
<< ", mono=" << (is_stereo_to_mono_ ? "true" : "false")
|
||||
<< " done";
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", state=" << state_ << " failure";
|
||||
}
|
||||
|
||||
return retval; // false if any failure like timeout
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortHidl::Suspend() {
|
||||
if (!in_use()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPort is not in use";
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", state=" << state_ << " request";
|
||||
bool retval = false;
|
||||
if (state_ == BluetoothStreamState::STARTED) {
|
||||
state_ = BluetoothStreamState::SUSPENDING;
|
||||
if (BluetoothAudioSessionControl_2_1::SuspendStream(session_type_hidl_)) {
|
||||
retval = CondwaitState(BluetoothStreamState::SUSPENDING);
|
||||
} else {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", state=" << state_ << " Hal fails";
|
||||
}
|
||||
}
|
||||
|
||||
if (retval) {
|
||||
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", state=" << state_ << " done";
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", state=" << state_ << " failure";
|
||||
}
|
||||
|
||||
return retval; // false if any failure like timeout
|
||||
}
|
||||
|
||||
void BluetoothAudioPortHidl::Stop() {
|
||||
if (!in_use()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPort is not in use";
|
||||
return;
|
||||
}
|
||||
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", state=" << state_ << " request";
|
||||
state_ = BluetoothStreamState::DISABLED;
|
||||
BluetoothAudioSessionControl_2_1::StopStream(session_type_hidl_);
|
||||
LOG(INFO) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", state=" << state_ << " done";
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortHidl::GetPresentationPosition(
|
||||
uint64_t* delay_ns, uint64_t* bytes, timespec* timestamp) const {
|
||||
if (!in_use()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPort is not in use";
|
||||
return false;
|
||||
}
|
||||
bool retval = BluetoothAudioSessionControl_2_1::GetPresentationPosition(
|
||||
session_type_hidl_, delay_ns, bytes, timestamp);
|
||||
LOG(VERBOSE) << __func__
|
||||
<< ": session_type=" << StringPrintf("%#hhx", session_type_hidl_)
|
||||
<< ", 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 BluetoothAudioPortHidl::UpdateSourceMetadata(
|
||||
const source_metadata* source_metadata) const {
|
||||
if (!in_use()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPort is not in use";
|
||||
return;
|
||||
}
|
||||
LOG(DEBUG) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie=" << StringPrintf("%#hx", cookie_)
|
||||
<< ", state=" << state_ << ", " << source_metadata->track_count
|
||||
<< " track(s)";
|
||||
if (source_metadata->track_count == 0) return;
|
||||
BluetoothAudioSessionControl_2_1::UpdateTracksMetadata(session_type_hidl_,
|
||||
source_metadata);
|
||||
}
|
||||
|
||||
BluetoothStreamState BluetoothAudioPortHidl::GetState() const { return state_; }
|
||||
|
||||
void BluetoothAudioPortHidl::SetState(BluetoothStreamState state) {
|
||||
state_ = state;
|
||||
}
|
||||
|
||||
size_t BluetoothAudioPortHidlOut::WriteData(const void* buffer,
|
||||
size_t bytes) const {
|
||||
if (!BluetoothAudioPortHidl::in_use()) return 0;
|
||||
if (!BluetoothAudioPortHidl::is_stereo_to_mono_) {
|
||||
return BluetoothAudioSessionControl_2_1::OutWritePcmData(session_type_hidl_,
|
||||
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_2_1::OutWritePcmData(
|
||||
session_type_hidl_, dst.get(), write_frames * 2) *
|
||||
2;
|
||||
}
|
||||
|
||||
size_t BluetoothAudioPortHidlIn::ReadData(void* buffer, size_t bytes) const {
|
||||
if (!BluetoothAudioPortHidl::in_use()) return 0;
|
||||
return BluetoothAudioSessionControl_2_1::InReadPcmData(session_type_hidl_,
|
||||
buffer, bytes);
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortHidlIn::LoadAudioConfig(
|
||||
audio_config_t* audio_cfg) const {
|
||||
if (!BluetoothAudioPortHidl::in_use()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortIn is not in use";
|
||||
audio_cfg->sample_rate = kBluetoothDefaultSampleRate;
|
||||
audio_cfg->channel_mask = kBluetoothDefaultInputChannelModeMask;
|
||||
audio_cfg->format = kBluetoothDefaultAudioFormatBitsPerSample;
|
||||
return false;
|
||||
}
|
||||
|
||||
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||
hal_audio_cfg =
|
||||
BluetoothAudioSessionControl_2_1::GetAudioConfig(session_type_hidl_);
|
||||
if (hal_audio_cfg.getDiscriminator() !=
|
||||
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
|
||||
hidl_discriminator::pcmConfig) {
|
||||
audio_cfg->sample_rate = kBluetoothDefaultSampleRate;
|
||||
audio_cfg->channel_mask = kBluetoothDefaultInputChannelModeMask;
|
||||
audio_cfg->format = kBluetoothDefaultAudioFormatBitsPerSample;
|
||||
return false;
|
||||
}
|
||||
const ::android::hardware::bluetooth::audio::V2_1::PcmParameters& pcm_cfg =
|
||||
hal_audio_cfg.pcmConfig();
|
||||
LOG(VERBOSE) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie="
|
||||
<< StringPrintf("%#hx", BluetoothAudioPortHidl::cookie_)
|
||||
<< ", state=" << BluetoothAudioPortHidl::state_
|
||||
<< ", PcmConfig=[" << toString(pcm_cfg) << "]";
|
||||
if (pcm_cfg.sampleRate == SampleRate_2_1::RATE_UNKNOWN ||
|
||||
pcm_cfg.channelMode == ChannelMode::UNKNOWN ||
|
||||
pcm_cfg.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
audio_cfg->sample_rate = SampleRateToAudioFormat(pcm_cfg.sampleRate);
|
||||
audio_cfg->channel_mask = InputChannelModeToAudioFormat(pcm_cfg.channelMode);
|
||||
audio_cfg->format = BitsPerSampleToAudioFormat(pcm_cfg.bitsPerSample);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothAudioPortHidlOut::LoadAudioConfig(
|
||||
audio_config_t* audio_cfg) const {
|
||||
if (!BluetoothAudioPortHidl::in_use()) {
|
||||
LOG(ERROR) << __func__ << ": BluetoothAudioPortOut is not in use";
|
||||
audio_cfg->sample_rate = kBluetoothDefaultSampleRate;
|
||||
audio_cfg->channel_mask = kBluetoothDefaultOutputChannelModeMask;
|
||||
audio_cfg->format = kBluetoothDefaultAudioFormatBitsPerSample;
|
||||
return false;
|
||||
}
|
||||
|
||||
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||
hal_audio_cfg =
|
||||
BluetoothAudioSessionControl_2_1::GetAudioConfig(session_type_hidl_);
|
||||
if (hal_audio_cfg.getDiscriminator() !=
|
||||
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
|
||||
hidl_discriminator::pcmConfig) {
|
||||
audio_cfg->sample_rate = kBluetoothDefaultSampleRate;
|
||||
audio_cfg->channel_mask = kBluetoothDefaultOutputChannelModeMask;
|
||||
audio_cfg->format = kBluetoothDefaultAudioFormatBitsPerSample;
|
||||
return false;
|
||||
}
|
||||
const ::android::hardware::bluetooth::audio::V2_1::PcmParameters& pcm_cfg =
|
||||
hal_audio_cfg.pcmConfig();
|
||||
LOG(VERBOSE) << __func__ << ": session_type=" << toString(session_type_hidl_)
|
||||
<< ", cookie="
|
||||
<< StringPrintf("%#hx", BluetoothAudioPortHidl::cookie_)
|
||||
<< ", state=" << BluetoothAudioPortHidl::state_
|
||||
<< ", PcmConfig=[" << toString(pcm_cfg) << "]";
|
||||
if (pcm_cfg.sampleRate == SampleRate_2_1::RATE_UNKNOWN ||
|
||||
pcm_cfg.channelMode == ChannelMode::UNKNOWN ||
|
||||
pcm_cfg.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
audio_cfg->sample_rate = SampleRateToAudioFormat(pcm_cfg.sampleRate);
|
||||
audio_cfg->channel_mask =
|
||||
(BluetoothAudioPortHidl::is_stereo_to_mono_
|
||||
? AUDIO_CHANNEL_OUT_STEREO
|
||||
: OutputChannelModeToAudioFormat(pcm_cfg.channelMode));
|
||||
audio_cfg->format = BitsPerSampleToAudioFormat(pcm_cfg.bitsPerSample);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace hidl
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
116
bluetooth/audio/hw/device_port_proxy_hidl.h
Normal file
116
bluetooth/audio/hw/device_port_proxy_hidl.h
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/hardware/bluetooth/audio/2.1/types.h>
|
||||
#include <hardware/audio.h>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "device_port_proxy.h"
|
||||
|
||||
enum class BluetoothStreamState : uint8_t;
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
namespace hidl {
|
||||
|
||||
using SessionType_2_1 =
|
||||
::android::hardware::bluetooth::audio::V2_1::SessionType;
|
||||
|
||||
class BluetoothAudioPortHidl : public BluetoothAudioPort {
|
||||
public:
|
||||
BluetoothAudioPortHidl();
|
||||
virtual ~BluetoothAudioPortHidl() = default;
|
||||
|
||||
bool SetUp(audio_devices_t devices) override;
|
||||
|
||||
void TearDown() override;
|
||||
|
||||
void ForcePcmStereoToMono(bool force) override { is_stereo_to_mono_ = force; }
|
||||
|
||||
bool Start() override;
|
||||
|
||||
bool Suspend() override;
|
||||
|
||||
void Stop() override;
|
||||
|
||||
bool GetPresentationPosition(uint64_t* delay_ns, uint64_t* bytes,
|
||||
timespec* timestamp) const override;
|
||||
|
||||
void UpdateSourceMetadata(
|
||||
const source_metadata* source_metadata) const override;
|
||||
|
||||
BluetoothStreamState GetState() const override;
|
||||
|
||||
void SetState(BluetoothStreamState state) override;
|
||||
|
||||
bool IsA2dp() const override {
|
||||
return session_type_hidl_ ==
|
||||
SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_hidl_ ==
|
||||
SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH;
|
||||
}
|
||||
|
||||
bool GetPreferredDataIntervalUs(size_t* interval_us) const override;
|
||||
|
||||
protected:
|
||||
SessionType_2_1 session_type_hidl_;
|
||||
uint16_t cookie_;
|
||||
BluetoothStreamState state_;
|
||||
// WR to support Mono: True if fetching Stereo and mixing into Mono
|
||||
bool is_stereo_to_mono_ = false;
|
||||
|
||||
bool in_use() const;
|
||||
|
||||
private:
|
||||
mutable std::mutex cv_mutex_;
|
||||
std::condition_variable internal_cv_;
|
||||
|
||||
bool init_session_type(audio_devices_t device);
|
||||
|
||||
bool CondwaitState(BluetoothStreamState state);
|
||||
|
||||
void ControlResultHandler(
|
||||
const ::android::hardware::bluetooth::audio::V2_0::Status& status);
|
||||
|
||||
void SessionChangedHandler();
|
||||
};
|
||||
|
||||
class BluetoothAudioPortHidlOut : public BluetoothAudioPortHidl {
|
||||
public:
|
||||
~BluetoothAudioPortHidlOut();
|
||||
|
||||
size_t WriteData(const void* buffer, size_t bytes) const override;
|
||||
bool LoadAudioConfig(audio_config_t* audio_cfg) const override;
|
||||
};
|
||||
|
||||
class BluetoothAudioPortHidlIn : public BluetoothAudioPortHidl {
|
||||
public:
|
||||
~BluetoothAudioPortHidlIn();
|
||||
|
||||
size_t ReadData(void* buffer, size_t bytes) const override;
|
||||
bool LoadAudioConfig(audio_config_t* audio_cfg) const override;
|
||||
};
|
||||
|
||||
} // namespace hidl
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
1308
bluetooth/audio/hw/stream_apis.cc
Normal file
1308
bluetooth/audio/hw/stream_apis.cc
Normal file
File diff suppressed because it is too large
Load Diff
129
bluetooth/audio/hw/stream_apis.h
Normal file
129
bluetooth/audio/hw/stream_apis.h
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2019 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 <hardware/audio.h>
|
||||
#include <system/audio.h>
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "device_port_proxy.h"
|
||||
#include "device_port_proxy_hidl.h"
|
||||
|
||||
constexpr unsigned int kBluetoothDefaultSampleRate = 44100;
|
||||
constexpr audio_format_t kBluetoothDefaultAudioFormatBitsPerSample =
|
||||
AUDIO_FORMAT_PCM_16_BIT;
|
||||
|
||||
constexpr unsigned int kBluetoothDefaultInputBufferMs = 20;
|
||||
constexpr unsigned int kBluetoothDefaultInputStateTimeoutMs = 20;
|
||||
|
||||
constexpr unsigned int kBluetoothDefaultOutputBufferMs = 10;
|
||||
constexpr unsigned int kBluetoothSpatializerOutputBufferMs = 10;
|
||||
|
||||
constexpr audio_channel_mask_t kBluetoothDefaultOutputChannelModeMask =
|
||||
AUDIO_CHANNEL_OUT_STEREO;
|
||||
constexpr audio_channel_mask_t kBluetoothDefaultInputChannelModeMask =
|
||||
AUDIO_CHANNEL_IN_MONO;
|
||||
|
||||
enum class BluetoothStreamState : uint8_t {
|
||||
DISABLED = 0, // This stream is closing or set param "suspend=true"
|
||||
STANDBY,
|
||||
STARTING,
|
||||
STARTED,
|
||||
SUSPENDING,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state);
|
||||
|
||||
struct BluetoothStreamOut {
|
||||
// Must be the first member so it can be cast from audio_stream
|
||||
// or audio_stream_out pointer
|
||||
audio_stream_out stream_out_{};
|
||||
std::unique_ptr<::android::bluetooth::audio::BluetoothAudioPort>
|
||||
bluetooth_output_;
|
||||
bool is_aidl;
|
||||
int64_t last_write_time_us_;
|
||||
// Audio PCM Configs
|
||||
uint32_t sample_rate_;
|
||||
audio_channel_mask_t channel_mask_;
|
||||
audio_format_t format_;
|
||||
size_t preferred_data_interval_us;
|
||||
// frame is the number of samples per channel
|
||||
// frames count per tick
|
||||
size_t frames_count_;
|
||||
// total frames written, reset on standby
|
||||
uint64_t frames_rendered_;
|
||||
// total frames written after opened, never reset
|
||||
uint64_t frames_presented_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
struct BluetoothAudioDevice {
|
||||
// Important: device must be first as an audio_hw_device* may be cast to
|
||||
// BluetoothAudioDevice* when the type is implicitly known.
|
||||
audio_hw_device audio_device_{};
|
||||
// protect against device->output and stream_out from being inconsistent
|
||||
std::mutex mutex_;
|
||||
std::list<BluetoothStreamOut*> opened_stream_outs_ =
|
||||
std::list<BluetoothStreamOut*>(0);
|
||||
};
|
||||
|
||||
struct BluetoothStreamIn {
|
||||
// Must be the first member so it can be cast from audio_stream
|
||||
// or audio_stream_in pointer
|
||||
audio_stream_in stream_in_;
|
||||
std::unique_ptr<::android::bluetooth::audio::BluetoothAudioPort>
|
||||
bluetooth_input_;
|
||||
bool is_aidl;
|
||||
int64_t last_read_time_us_;
|
||||
// Audio PCM Configs
|
||||
uint32_t sample_rate_;
|
||||
audio_channel_mask_t channel_mask_;
|
||||
audio_format_t format_;
|
||||
size_t preferred_data_interval_us;
|
||||
// frame is the number of samples per channel
|
||||
// frames count per tick
|
||||
size_t frames_count_;
|
||||
// total frames read after opened, never reset
|
||||
uint64_t frames_presented_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
int adev_open_output_stream(struct audio_hw_device* dev,
|
||||
audio_io_handle_t handle, audio_devices_t devices,
|
||||
audio_output_flags_t flags,
|
||||
struct audio_config* config,
|
||||
struct audio_stream_out** stream_out,
|
||||
const char* address __unused);
|
||||
|
||||
void adev_close_output_stream(struct audio_hw_device* dev,
|
||||
struct audio_stream_out* stream);
|
||||
|
||||
size_t adev_get_input_buffer_size(const struct audio_hw_device* dev,
|
||||
const struct audio_config* config);
|
||||
|
||||
int adev_open_input_stream(struct audio_hw_device* dev,
|
||||
audio_io_handle_t handle, audio_devices_t devices,
|
||||
struct audio_config* config,
|
||||
struct audio_stream_in** stream_in,
|
||||
audio_input_flags_t flags __unused,
|
||||
const char* address __unused,
|
||||
audio_source_t source __unused);
|
||||
|
||||
void adev_close_input_stream(struct audio_hw_device* dev,
|
||||
struct audio_stream_in* in);
|
63
bluetooth/audio/hw/utils.cc
Normal file
63
bluetooth/audio/hw/utils.cc
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2019 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 "BTAudioHalUtils"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <log/log.h>
|
||||
#include <stdlib.h>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
namespace utils {
|
||||
|
||||
std::unordered_map<std::string, std::string> ParseAudioParams(
|
||||
const std::string& params) {
|
||||
std::vector<std::string> segments = android::base::Split(params, ";");
|
||||
std::unordered_map<std::string, std::string> params_map;
|
||||
for (const auto& segment : segments) {
|
||||
if (segment.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
std::vector<std::string> kv = android::base::Split(segment, "=");
|
||||
if (kv[0].empty()) {
|
||||
LOG(WARNING) << __func__ << ": Invalid audio parameter " << segment;
|
||||
continue;
|
||||
}
|
||||
params_map[kv[0]] = (kv.size() > 1 ? kv[1] : "");
|
||||
}
|
||||
return params_map;
|
||||
}
|
||||
|
||||
std::string GetAudioParamString(
|
||||
std::unordered_map<std::string, std::string>& params_map) {
|
||||
std::ostringstream sout;
|
||||
for (const auto& ptr : params_map) {
|
||||
sout << "key: '" << ptr.first << "' value: '" << ptr.second << "'\n";
|
||||
}
|
||||
return sout.str();
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
48
bluetooth/audio/hw/utils.h
Normal file
48
bluetooth/audio/hw/utils.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2019 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 <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
namespace utils {
|
||||
|
||||
// Creates a hash map based on the |params| string containing key and value
|
||||
// pairs. Pairs are expected in the form "key=value" separated by the ';'
|
||||
// character. Both ';' and '=' characters are invalid in keys or values.
|
||||
// Examples:
|
||||
// "key0" -> map: [key0]=""
|
||||
// "key0=value0;key1=value1;" -> map: [key0]="value0" [key1]="value1"
|
||||
// "key0=;key1=value1;" -> map: [key0]="" [key1]="value1"
|
||||
// "=value0;key1=value1;" -> map: [key1]="value1"
|
||||
std::unordered_map<std::string, std::string> ParseAudioParams(
|
||||
const std::string& params);
|
||||
|
||||
// Dumps the contents of the hash_map to the log for debugging purposes.
|
||||
// If |map| is not NULL, all entries of |map| will be dumped, otherwise
|
||||
// nothing will be dumped. Note that this function does not take the ownership
|
||||
// of the |map|.
|
||||
std::string GetAudioParamString(
|
||||
std::unordered_map<std::string, std::string>& params_map);
|
||||
|
||||
} // namespace utils
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
46
bluetooth/audio/utils/Android.bp
Normal file
46
bluetooth/audio/utils/Android.bp
Normal file
@ -0,0 +1,46 @@
|
||||
cc_library_shared {
|
||||
name: "libbluetooth_audio_session_system",
|
||||
defaults: ["hidl_defaults"],
|
||||
srcs: [
|
||||
"session/BluetoothAudioSession.cpp",
|
||||
"session/BluetoothAudioSession_2_1.cpp",
|
||||
"session/BluetoothAudioSupportedCodecsDB.cpp",
|
||||
"session/BluetoothAudioSupportedCodecsDB_2_1.cpp",
|
||||
],
|
||||
export_include_dirs: ["session/"],
|
||||
header_libs: ["libhardware_headers"],
|
||||
shared_libs: [
|
||||
"android.hardware.audio.common@5.0",
|
||||
"android.hardware.bluetooth.audio@2.0",
|
||||
"android.hardware.bluetooth.audio@2.1",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libfmq",
|
||||
"libhidlbase",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"libbluetooth_audio_session_aidl_system",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_shared {
|
||||
name: "libbluetooth_audio_session_aidl_system",
|
||||
srcs: [
|
||||
"aidl_session/BluetoothAudioCodecs.cpp",
|
||||
"aidl_session/BluetoothAudioSession.cpp",
|
||||
"aidl_session/HidlToAidlMiddleware.cpp",
|
||||
],
|
||||
export_include_dirs: ["aidl_session/"],
|
||||
header_libs: ["libhardware_headers"],
|
||||
shared_libs: [
|
||||
"android.hardware.bluetooth.audio@2.0",
|
||||
"android.hardware.bluetooth.audio@2.1",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libbinder_ndk",
|
||||
"libfmq",
|
||||
"liblog",
|
||||
"android.hardware.bluetooth.audio-V2-ndk",
|
||||
"libhidlbase",
|
||||
],
|
||||
}
|
523
bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
Normal file
523
bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
Normal file
@ -0,0 +1,523 @@
|
||||
/*
|
||||
* Copyright (C) 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 "BTAudioCodecsAidl"
|
||||
|
||||
#include "BluetoothAudioCodecs.h"
|
||||
|
||||
#include <aidl/android/hardware/bluetooth/audio/AacCapabilities.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/AacObjectType.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/AptxCapabilities.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/ChannelMode.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/LdacCapabilities.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/LdacChannelMode.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/LdacQualityIndex.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/LeAudioConfiguration.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/OpusCapabilities.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/OpusConfiguration.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/SbcCapabilities.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/SbcChannelMode.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
static const PcmCapabilities kDefaultSoftwarePcmCapabilities = {
|
||||
.sampleRateHz = {16000, 24000, 32000, 44100, 48000, 88200, 96000},
|
||||
.channelMode = {ChannelMode::MONO, ChannelMode::STEREO},
|
||||
.bitsPerSample = {16, 24, 32},
|
||||
.dataIntervalUs = {},
|
||||
};
|
||||
|
||||
static const SbcCapabilities kDefaultOffloadSbcCapability = {
|
||||
.sampleRateHz = {44100},
|
||||
.channelMode = {SbcChannelMode::MONO, SbcChannelMode::JOINT_STEREO},
|
||||
.blockLength = {4, 8, 12, 16},
|
||||
.numSubbands = {8},
|
||||
.allocMethod = {SbcAllocMethod::ALLOC_MD_L},
|
||||
.bitsPerSample = {16},
|
||||
.minBitpool = 2,
|
||||
.maxBitpool = 53};
|
||||
|
||||
static const AacCapabilities kDefaultOffloadAacCapability = {
|
||||
.objectType = {AacObjectType::MPEG2_LC},
|
||||
.sampleRateHz = {44100},
|
||||
.channelMode = {ChannelMode::STEREO},
|
||||
.variableBitRateSupported = true,
|
||||
.bitsPerSample = {16}};
|
||||
|
||||
static const LdacCapabilities kDefaultOffloadLdacCapability = {
|
||||
.sampleRateHz = {44100, 48000, 88200, 96000},
|
||||
.channelMode = {LdacChannelMode::DUAL, LdacChannelMode::STEREO},
|
||||
.qualityIndex = {LdacQualityIndex::HIGH},
|
||||
.bitsPerSample = {16, 24, 32}};
|
||||
|
||||
static const AptxCapabilities kDefaultOffloadAptxCapability = {
|
||||
.sampleRateHz = {44100, 48000},
|
||||
.channelMode = {ChannelMode::STEREO},
|
||||
.bitsPerSample = {16},
|
||||
};
|
||||
|
||||
static const AptxCapabilities kDefaultOffloadAptxHdCapability = {
|
||||
.sampleRateHz = {44100, 48000},
|
||||
.channelMode = {ChannelMode::STEREO},
|
||||
.bitsPerSample = {24},
|
||||
};
|
||||
|
||||
static const OpusCapabilities kDefaultOffloadOpusCapability = {
|
||||
.samplingFrequencyHz = {48000},
|
||||
.frameDurationUs = {10000, 20000},
|
||||
.channelMode = {ChannelMode::MONO, ChannelMode::STEREO},
|
||||
};
|
||||
|
||||
const std::vector<CodecCapabilities> kDefaultOffloadA2dpCodecCapabilities = {
|
||||
{.codecType = CodecType::SBC, .capabilities = {}},
|
||||
{.codecType = CodecType::AAC, .capabilities = {}},
|
||||
{.codecType = CodecType::LDAC, .capabilities = {}},
|
||||
{.codecType = CodecType::APTX, .capabilities = {}},
|
||||
{.codecType = CodecType::APTX_HD, .capabilities = {}},
|
||||
{.codecType = CodecType::OPUS, .capabilities = {}}};
|
||||
|
||||
std::vector<LeAudioCodecCapabilitiesSetting> kDefaultOffloadLeAudioCapabilities;
|
||||
|
||||
static const UnicastCapability kInvalidUnicastCapability = {
|
||||
.codecType = CodecType::UNKNOWN};
|
||||
|
||||
static const BroadcastCapability kInvalidBroadcastCapability = {
|
||||
.codecType = CodecType::UNKNOWN};
|
||||
|
||||
// Default Supported Codecs
|
||||
// LC3 16_1: sample rate: 16 kHz, frame duration: 7.5 ms, octets per frame: 30
|
||||
static const Lc3Capabilities kLc3Capability_16_1 = {
|
||||
.samplingFrequencyHz = {16000},
|
||||
.frameDurationUs = {7500},
|
||||
.octetsPerFrame = {30}};
|
||||
|
||||
// Default Supported Codecs
|
||||
// LC3 16_2: sample rate: 16 kHz, frame duration: 10 ms, octets per frame: 40
|
||||
static const Lc3Capabilities kLc3Capability_16_2 = {
|
||||
.samplingFrequencyHz = {16000},
|
||||
.frameDurationUs = {10000},
|
||||
.octetsPerFrame = {40}};
|
||||
|
||||
// Default Supported Codecs
|
||||
// LC3 24_2: sample rate: 24 kHz, frame duration: 10 ms, octets per frame: 60
|
||||
static const Lc3Capabilities kLc3Capability_24_2 = {
|
||||
.samplingFrequencyHz = {24000},
|
||||
.frameDurationUs = {10000},
|
||||
.octetsPerFrame = {60}};
|
||||
|
||||
// Default Supported Codecs
|
||||
// LC3 32_2: sample rate: 32 kHz, frame duration: 10 ms, octets per frame: 80
|
||||
static const Lc3Capabilities kLc3Capability_32_2 = {
|
||||
.samplingFrequencyHz = {32000},
|
||||
.frameDurationUs = {10000},
|
||||
.octetsPerFrame = {80}};
|
||||
|
||||
// Default Supported Codecs
|
||||
// LC3 48_4: sample rate: 48 kHz, frame duration: 10 ms, octets per frame: 120
|
||||
static const Lc3Capabilities kLc3Capability_48_4 = {
|
||||
.samplingFrequencyHz = {48000},
|
||||
.frameDurationUs = {10000},
|
||||
.octetsPerFrame = {120}};
|
||||
|
||||
static const std::vector<Lc3Capabilities> supportedLc3CapabilityList = {
|
||||
kLc3Capability_48_4, kLc3Capability_32_2, kLc3Capability_24_2,
|
||||
kLc3Capability_16_2, kLc3Capability_16_1};
|
||||
|
||||
static AudioLocation stereoAudio = static_cast<AudioLocation>(
|
||||
static_cast<uint8_t>(AudioLocation::FRONT_LEFT) |
|
||||
static_cast<uint8_t>(AudioLocation::FRONT_RIGHT));
|
||||
static AudioLocation monoAudio = AudioLocation::UNKNOWN;
|
||||
|
||||
// Stores the supported setting of audio location, connected device, and the
|
||||
// channel count for each device
|
||||
std::vector<std::tuple<AudioLocation, uint8_t, uint8_t>>
|
||||
supportedDeviceSetting = {
|
||||
// Stereo, two connected device, one for L one for R
|
||||
std::make_tuple(stereoAudio, 2, 1),
|
||||
// Stereo, one connected device for both L and R
|
||||
std::make_tuple(stereoAudio, 1, 2),
|
||||
// Mono
|
||||
std::make_tuple(monoAudio, 1, 1)};
|
||||
|
||||
template <class T>
|
||||
bool BluetoothAudioCodecs::ContainedInVector(
|
||||
const std::vector<T>& vector, const typename identity<T>::type& target) {
|
||||
return std::find(vector.begin(), vector.end(), target) != vector.end();
|
||||
}
|
||||
|
||||
bool BluetoothAudioCodecs::IsOffloadSbcConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific) {
|
||||
if (codec_specific.getTag() != CodecConfiguration::CodecSpecific::sbcConfig) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << codec_specific.toString();
|
||||
return false;
|
||||
}
|
||||
const SbcConfiguration sbc_data =
|
||||
codec_specific.get<CodecConfiguration::CodecSpecific::sbcConfig>();
|
||||
|
||||
if (ContainedInVector(kDefaultOffloadSbcCapability.sampleRateHz,
|
||||
sbc_data.sampleRateHz) &&
|
||||
ContainedInVector(kDefaultOffloadSbcCapability.blockLength,
|
||||
sbc_data.blockLength) &&
|
||||
ContainedInVector(kDefaultOffloadSbcCapability.numSubbands,
|
||||
sbc_data.numSubbands) &&
|
||||
ContainedInVector(kDefaultOffloadSbcCapability.bitsPerSample,
|
||||
sbc_data.bitsPerSample) &&
|
||||
ContainedInVector(kDefaultOffloadSbcCapability.channelMode,
|
||||
sbc_data.channelMode) &&
|
||||
ContainedInVector(kDefaultOffloadSbcCapability.allocMethod,
|
||||
sbc_data.allocMethod) &&
|
||||
sbc_data.minBitpool <= sbc_data.maxBitpool &&
|
||||
kDefaultOffloadSbcCapability.minBitpool <= sbc_data.minBitpool &&
|
||||
kDefaultOffloadSbcCapability.maxBitpool >= sbc_data.maxBitpool) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported CodecSpecific=" << codec_specific.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BluetoothAudioCodecs::IsOffloadAacConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific) {
|
||||
if (codec_specific.getTag() != CodecConfiguration::CodecSpecific::aacConfig) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << codec_specific.toString();
|
||||
return false;
|
||||
}
|
||||
const AacConfiguration aac_data =
|
||||
codec_specific.get<CodecConfiguration::CodecSpecific::aacConfig>();
|
||||
|
||||
if (ContainedInVector(kDefaultOffloadAacCapability.sampleRateHz,
|
||||
aac_data.sampleRateHz) &&
|
||||
ContainedInVector(kDefaultOffloadAacCapability.bitsPerSample,
|
||||
aac_data.bitsPerSample) &&
|
||||
ContainedInVector(kDefaultOffloadAacCapability.channelMode,
|
||||
aac_data.channelMode) &&
|
||||
ContainedInVector(kDefaultOffloadAacCapability.objectType,
|
||||
aac_data.objectType) &&
|
||||
(!aac_data.variableBitRateEnabled ||
|
||||
kDefaultOffloadAacCapability.variableBitRateSupported)) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported CodecSpecific=" << codec_specific.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BluetoothAudioCodecs::IsOffloadLdacConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific) {
|
||||
if (codec_specific.getTag() !=
|
||||
CodecConfiguration::CodecSpecific::ldacConfig) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << codec_specific.toString();
|
||||
return false;
|
||||
}
|
||||
const LdacConfiguration ldac_data =
|
||||
codec_specific.get<CodecConfiguration::CodecSpecific::ldacConfig>();
|
||||
|
||||
if (ContainedInVector(kDefaultOffloadLdacCapability.sampleRateHz,
|
||||
ldac_data.sampleRateHz) &&
|
||||
ContainedInVector(kDefaultOffloadLdacCapability.bitsPerSample,
|
||||
ldac_data.bitsPerSample) &&
|
||||
ContainedInVector(kDefaultOffloadLdacCapability.channelMode,
|
||||
ldac_data.channelMode) &&
|
||||
ContainedInVector(kDefaultOffloadLdacCapability.qualityIndex,
|
||||
ldac_data.qualityIndex)) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported CodecSpecific=" << codec_specific.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BluetoothAudioCodecs::IsOffloadAptxConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific) {
|
||||
if (codec_specific.getTag() !=
|
||||
CodecConfiguration::CodecSpecific::aptxConfig) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << codec_specific.toString();
|
||||
return false;
|
||||
}
|
||||
const AptxConfiguration aptx_data =
|
||||
codec_specific.get<CodecConfiguration::CodecSpecific::aptxConfig>();
|
||||
|
||||
if (ContainedInVector(kDefaultOffloadAptxCapability.sampleRateHz,
|
||||
aptx_data.sampleRateHz) &&
|
||||
ContainedInVector(kDefaultOffloadAptxCapability.bitsPerSample,
|
||||
aptx_data.bitsPerSample) &&
|
||||
ContainedInVector(kDefaultOffloadAptxCapability.channelMode,
|
||||
aptx_data.channelMode)) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported CodecSpecific=" << codec_specific.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BluetoothAudioCodecs::IsOffloadAptxHdConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific) {
|
||||
if (codec_specific.getTag() !=
|
||||
CodecConfiguration::CodecSpecific::aptxConfig) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << codec_specific.toString();
|
||||
return false;
|
||||
}
|
||||
const AptxConfiguration aptx_data =
|
||||
codec_specific.get<CodecConfiguration::CodecSpecific::aptxConfig>();
|
||||
|
||||
if (ContainedInVector(kDefaultOffloadAptxHdCapability.sampleRateHz,
|
||||
aptx_data.sampleRateHz) &&
|
||||
ContainedInVector(kDefaultOffloadAptxHdCapability.bitsPerSample,
|
||||
aptx_data.bitsPerSample) &&
|
||||
ContainedInVector(kDefaultOffloadAptxHdCapability.channelMode,
|
||||
aptx_data.channelMode)) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported CodecSpecific=" << codec_specific.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BluetoothAudioCodecs::IsOffloadOpusConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific) {
|
||||
if (codec_specific.getTag() !=
|
||||
CodecConfiguration::CodecSpecific::opusConfig) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << codec_specific.toString();
|
||||
return false;
|
||||
}
|
||||
std::optional<OpusConfiguration> opus_data =
|
||||
codec_specific.get<CodecConfiguration::CodecSpecific::opusConfig>();
|
||||
|
||||
if (opus_data.has_value() &&
|
||||
ContainedInVector(kDefaultOffloadOpusCapability.samplingFrequencyHz,
|
||||
opus_data->samplingFrequencyHz) &&
|
||||
ContainedInVector(kDefaultOffloadOpusCapability.frameDurationUs,
|
||||
opus_data->frameDurationUs) &&
|
||||
ContainedInVector(kDefaultOffloadOpusCapability.channelMode,
|
||||
opus_data->channelMode)) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported CodecSpecific=" << codec_specific.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BluetoothAudioCodecs::IsOffloadLeAudioConfigurationValid(
|
||||
const SessionType& session_type, const LeAudioConfiguration&) {
|
||||
if (session_type !=
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
|
||||
session_type !=
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH &&
|
||||
session_type !=
|
||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<PcmCapabilities>
|
||||
BluetoothAudioCodecs::GetSoftwarePcmCapabilities() {
|
||||
return {kDefaultSoftwarePcmCapabilities};
|
||||
}
|
||||
|
||||
std::vector<CodecCapabilities>
|
||||
BluetoothAudioCodecs::GetA2dpOffloadCodecCapabilities(
|
||||
const SessionType& session_type) {
|
||||
if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
|
||||
session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
return {};
|
||||
}
|
||||
std::vector<CodecCapabilities> offload_a2dp_codec_capabilities =
|
||||
kDefaultOffloadA2dpCodecCapabilities;
|
||||
for (auto& codec_capability : offload_a2dp_codec_capabilities) {
|
||||
switch (codec_capability.codecType) {
|
||||
case CodecType::SBC:
|
||||
codec_capability.capabilities
|
||||
.set<CodecCapabilities::Capabilities::sbcCapabilities>(
|
||||
kDefaultOffloadSbcCapability);
|
||||
break;
|
||||
case CodecType::AAC:
|
||||
codec_capability.capabilities
|
||||
.set<CodecCapabilities::Capabilities::aacCapabilities>(
|
||||
kDefaultOffloadAacCapability);
|
||||
break;
|
||||
case CodecType::LDAC:
|
||||
codec_capability.capabilities
|
||||
.set<CodecCapabilities::Capabilities::ldacCapabilities>(
|
||||
kDefaultOffloadLdacCapability);
|
||||
break;
|
||||
case CodecType::APTX:
|
||||
codec_capability.capabilities
|
||||
.set<CodecCapabilities::Capabilities::aptxCapabilities>(
|
||||
kDefaultOffloadAptxCapability);
|
||||
break;
|
||||
case CodecType::APTX_HD:
|
||||
codec_capability.capabilities
|
||||
.set<CodecCapabilities::Capabilities::aptxCapabilities>(
|
||||
kDefaultOffloadAptxHdCapability);
|
||||
break;
|
||||
case CodecType::OPUS:
|
||||
codec_capability.capabilities
|
||||
.set<CodecCapabilities::Capabilities::opusCapabilities>(
|
||||
kDefaultOffloadOpusCapability);
|
||||
break;
|
||||
case CodecType::UNKNOWN:
|
||||
case CodecType::VENDOR:
|
||||
case CodecType::LC3:
|
||||
case CodecType::APTX_ADAPTIVE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return offload_a2dp_codec_capabilities;
|
||||
}
|
||||
|
||||
bool BluetoothAudioCodecs::IsSoftwarePcmConfigurationValid(
|
||||
const PcmConfiguration& pcm_config) {
|
||||
if (ContainedInVector(kDefaultSoftwarePcmCapabilities.sampleRateHz,
|
||||
pcm_config.sampleRateHz) &&
|
||||
ContainedInVector(kDefaultSoftwarePcmCapabilities.bitsPerSample,
|
||||
pcm_config.bitsPerSample) &&
|
||||
ContainedInVector(kDefaultSoftwarePcmCapabilities.channelMode,
|
||||
pcm_config.channelMode)
|
||||
// data interval is not checked for now
|
||||
// && pcm_config.dataIntervalUs != 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported CodecSpecific=" << pcm_config.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BluetoothAudioCodecs::IsOffloadCodecConfigurationValid(
|
||||
const SessionType& session_type, const CodecConfiguration& codec_config) {
|
||||
if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
|
||||
session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": Invalid SessionType=" << toString(session_type);
|
||||
return false;
|
||||
}
|
||||
const CodecConfiguration::CodecSpecific& codec_specific = codec_config.config;
|
||||
switch (codec_config.codecType) {
|
||||
case CodecType::SBC:
|
||||
if (IsOffloadSbcConfigurationValid(codec_specific)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case CodecType::AAC:
|
||||
if (IsOffloadAacConfigurationValid(codec_specific)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case CodecType::LDAC:
|
||||
if (IsOffloadLdacConfigurationValid(codec_specific)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case CodecType::APTX:
|
||||
if (IsOffloadAptxConfigurationValid(codec_specific)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case CodecType::APTX_HD:
|
||||
if (IsOffloadAptxHdConfigurationValid(codec_specific)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case CodecType::OPUS:
|
||||
if (IsOffloadOpusConfigurationValid(codec_specific)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case CodecType::APTX_ADAPTIVE:
|
||||
case CodecType::LC3:
|
||||
case CodecType::UNKNOWN:
|
||||
case CodecType::VENDOR:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UnicastCapability composeUnicastLc3Capability(
|
||||
AudioLocation audioLocation, uint8_t deviceCnt, uint8_t channelCount,
|
||||
const Lc3Capabilities& capability) {
|
||||
return {
|
||||
.codecType = CodecType::LC3,
|
||||
.supportedChannel = audioLocation,
|
||||
.deviceCount = deviceCnt,
|
||||
.channelCountPerDevice = channelCount,
|
||||
.leAudioCodecCapabilities =
|
||||
UnicastCapability::LeAudioCodecCapabilities(capability),
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<LeAudioCodecCapabilitiesSetting>
|
||||
BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities(
|
||||
const SessionType& session_type) {
|
||||
if (session_type !=
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
|
||||
session_type !=
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH &&
|
||||
session_type !=
|
||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
|
||||
return std::vector<LeAudioCodecCapabilitiesSetting>(0);
|
||||
}
|
||||
|
||||
if (kDefaultOffloadLeAudioCapabilities.empty()) {
|
||||
for (auto [audioLocation, deviceCnt, channelCount] :
|
||||
supportedDeviceSetting) {
|
||||
for (auto capability : supportedLc3CapabilityList) {
|
||||
UnicastCapability lc3Capability = composeUnicastLc3Capability(
|
||||
audioLocation, deviceCnt, channelCount, capability);
|
||||
UnicastCapability lc3MonoDecodeCapability =
|
||||
composeUnicastLc3Capability(monoAudio, 1, 1, capability);
|
||||
|
||||
// Adds the capability for encode only
|
||||
kDefaultOffloadLeAudioCapabilities.push_back(
|
||||
{.unicastEncodeCapability = lc3Capability,
|
||||
.unicastDecodeCapability = kInvalidUnicastCapability,
|
||||
.broadcastCapability = kInvalidBroadcastCapability});
|
||||
|
||||
// Adds the capability for decode only
|
||||
kDefaultOffloadLeAudioCapabilities.push_back(
|
||||
{.unicastEncodeCapability = kInvalidUnicastCapability,
|
||||
.unicastDecodeCapability = lc3Capability,
|
||||
.broadcastCapability = kInvalidBroadcastCapability});
|
||||
|
||||
// Adds the capability for the case that encode and decode exist at the
|
||||
// same time
|
||||
kDefaultOffloadLeAudioCapabilities.push_back(
|
||||
{.unicastEncodeCapability = lc3Capability,
|
||||
.unicastDecodeCapability = lc3MonoDecodeCapability,
|
||||
.broadcastCapability = kInvalidBroadcastCapability});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kDefaultOffloadLeAudioCapabilities;
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
82
bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h
Normal file
82
bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/bluetooth/audio/CodecCapabilities.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/CodecConfiguration.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/LeAudioCodecCapabilitiesSetting.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/LeAudioConfiguration.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/OpusConfiguration.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/PcmCapabilities.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/PcmConfiguration.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class BluetoothAudioCodecs {
|
||||
public:
|
||||
static std::vector<PcmCapabilities> GetSoftwarePcmCapabilities();
|
||||
static std::vector<CodecCapabilities> GetA2dpOffloadCodecCapabilities(
|
||||
const SessionType& session_type);
|
||||
|
||||
static bool IsSoftwarePcmConfigurationValid(
|
||||
const PcmConfiguration& pcm_config);
|
||||
static bool IsOffloadCodecConfigurationValid(
|
||||
const SessionType& session_type, const CodecConfiguration& codec_config);
|
||||
|
||||
static bool IsOffloadLeAudioConfigurationValid(
|
||||
const SessionType& session_type, const LeAudioConfiguration&);
|
||||
|
||||
static std::vector<LeAudioCodecCapabilitiesSetting>
|
||||
GetLeAudioOffloadCodecCapabilities(const SessionType& session_type);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
struct identity {
|
||||
typedef T type;
|
||||
};
|
||||
template <class T>
|
||||
static bool ContainedInVector(const std::vector<T>& vector,
|
||||
const typename identity<T>::type& target);
|
||||
template <class T>
|
||||
static bool ContainedInBitmask(const T& bitmask, const T& target);
|
||||
static bool IsSingleBit(uint32_t bitmasks, uint32_t bitfield);
|
||||
static bool IsOffloadSbcConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific);
|
||||
static bool IsOffloadAacConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific);
|
||||
static bool IsOffloadLdacConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific);
|
||||
static bool IsOffloadAptxConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific);
|
||||
static bool IsOffloadAptxHdConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific);
|
||||
static bool IsOffloadOpusConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific);
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
618
bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
Normal file
618
bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
Normal file
@ -0,0 +1,618 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#define LOG_TAG "BTAudioSessionAidl"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android/binder_manager.h>
|
||||
|
||||
#include "BluetoothAudioSession.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
static constexpr int kFmqSendTimeoutMs = 1000; // 1000 ms timeout for sending
|
||||
static constexpr int kFmqReceiveTimeoutMs =
|
||||
1000; // 1000 ms timeout for receiving
|
||||
static constexpr int kWritePollMs = 1; // polled non-blocking interval
|
||||
static constexpr int kReadPollMs = 1; // polled non-blocking interval
|
||||
|
||||
BluetoothAudioSession::BluetoothAudioSession(const SessionType& session_type)
|
||||
: session_type_(session_type), stack_iface_(nullptr), data_mq_(nullptr) {}
|
||||
|
||||
/***
|
||||
*
|
||||
* Callback methods
|
||||
*
|
||||
***/
|
||||
|
||||
void BluetoothAudioSession::OnSessionStarted(
|
||||
const std::shared_ptr<IBluetoothAudioPort> stack_iface,
|
||||
const DataMQDesc* mq_desc, const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (stack_iface == nullptr) {
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", IBluetoothAudioPort Invalid";
|
||||
} else if (!UpdateAudioConfig(audio_config)) {
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", AudioConfiguration=" << audio_config.toString()
|
||||
<< " Invalid";
|
||||
} else if (!UpdateDataPath(mq_desc)) {
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " MqDescriptor Invalid";
|
||||
audio_config_ = nullptr;
|
||||
} else {
|
||||
stack_iface_ = stack_iface;
|
||||
latency_modes_ = latency_modes;
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", AudioConfiguration=" << audio_config.toString();
|
||||
ReportSessionStatus();
|
||||
}
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::OnSessionEnded() {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
bool toggled = IsSessionReady();
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
|
||||
audio_config_ = nullptr;
|
||||
stack_iface_ = nullptr;
|
||||
UpdateDataPath(nullptr);
|
||||
if (toggled) {
|
||||
ReportSessionStatus();
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* Util methods
|
||||
*
|
||||
***/
|
||||
|
||||
const AudioConfiguration BluetoothAudioSession::GetAudioConfig() {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
switch (session_type_) {
|
||||
case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
case SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH:
|
||||
return AudioConfiguration(CodecConfiguration{});
|
||||
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
|
||||
return AudioConfiguration(LeAudioConfiguration{});
|
||||
case SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
return AudioConfiguration(LeAudioBroadcastConfiguration{});
|
||||
default:
|
||||
return AudioConfiguration(PcmConfiguration{});
|
||||
}
|
||||
}
|
||||
return *audio_config_;
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::ReportAudioConfigChanged(
|
||||
const AudioConfiguration& audio_config) {
|
||||
if (session_type_ !=
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
|
||||
session_type_ !=
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
|
||||
if (observers_.empty()) {
|
||||
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO port state observer";
|
||||
return;
|
||||
}
|
||||
for (auto& observer : observers_) {
|
||||
uint16_t cookie = observer.first;
|
||||
std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
|
||||
LOG(INFO) << __func__ << " for SessionType=" << toString(session_type_)
|
||||
<< ", bluetooth_audio=0x"
|
||||
<< ::android::base::StringPrintf("%04x", cookie);
|
||||
if (cb->audio_configuration_changed_cb_ != nullptr) {
|
||||
cb->audio_configuration_changed_cb_(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BluetoothAudioSession::IsSessionReady() {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
|
||||
bool is_mq_valid =
|
||||
(session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
|
||||
(data_mq_ != nullptr && data_mq_->isValid()));
|
||||
return stack_iface_ != nullptr && is_mq_valid && audio_config_ != nullptr;
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* Status callback methods
|
||||
*
|
||||
***/
|
||||
|
||||
uint16_t BluetoothAudioSession::RegisterStatusCback(
|
||||
const PortStatusCallbacks& callbacks) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
uint16_t cookie = ObserversCookieGetInitValue(session_type_);
|
||||
uint16_t cookie_upper_bound = ObserversCookieGetUpperBound(session_type_);
|
||||
|
||||
while (cookie < cookie_upper_bound) {
|
||||
if (observers_.find(cookie) == observers_.end()) {
|
||||
break;
|
||||
}
|
||||
++cookie;
|
||||
}
|
||||
if (cookie >= cookie_upper_bound) {
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has " << observers_.size()
|
||||
<< " observers already (No Resource)";
|
||||
return kObserversCookieUndefined;
|
||||
}
|
||||
std::shared_ptr<PortStatusCallbacks> cb =
|
||||
std::make_shared<PortStatusCallbacks>();
|
||||
*cb = callbacks;
|
||||
observers_[cookie] = cb;
|
||||
return cookie;
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::UnregisterStatusCback(uint16_t cookie) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (observers_.erase(cookie) != 1) {
|
||||
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " no such provider=0x"
|
||||
<< ::android::base::StringPrintf("%04x", cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* Stream methods
|
||||
*
|
||||
***/
|
||||
|
||||
bool BluetoothAudioSession::StartStream(bool is_low_latency) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return false;
|
||||
}
|
||||
auto hal_retval = stack_iface_->startStream(is_low_latency);
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothAudioSession::SuspendStream() {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return false;
|
||||
}
|
||||
auto hal_retval = stack_iface_->suspendStream();
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::StopStream() {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
return;
|
||||
}
|
||||
auto hal_retval = stack_iface_->stopStream();
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* Private methods
|
||||
*
|
||||
***/
|
||||
|
||||
bool BluetoothAudioSession::UpdateDataPath(const DataMQDesc* mq_desc) {
|
||||
if (mq_desc == nullptr) {
|
||||
// usecase of reset by nullptr
|
||||
data_mq_ = nullptr;
|
||||
return true;
|
||||
}
|
||||
std::unique_ptr<DataMQ> temp_mq;
|
||||
temp_mq.reset(new DataMQ(*mq_desc));
|
||||
if (!temp_mq || !temp_mq->isValid()) {
|
||||
data_mq_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
data_mq_ = std::move(temp_mq);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothAudioSession::UpdateAudioConfig(
|
||||
const AudioConfiguration& audio_config) {
|
||||
bool is_software_session =
|
||||
(session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH ||
|
||||
session_type_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH);
|
||||
bool is_offload_a2dp_session =
|
||||
(session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH);
|
||||
bool is_offload_le_audio_session =
|
||||
(session_type_ ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ ==
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH);
|
||||
auto audio_config_tag = audio_config.getTag();
|
||||
bool is_software_audio_config =
|
||||
(is_software_session &&
|
||||
audio_config_tag == AudioConfiguration::pcmConfig);
|
||||
bool is_a2dp_offload_audio_config =
|
||||
(is_offload_a2dp_session &&
|
||||
audio_config_tag == AudioConfiguration::a2dpConfig);
|
||||
bool is_le_audio_offload_audio_config =
|
||||
(is_offload_le_audio_session &&
|
||||
audio_config_tag == AudioConfiguration::leAudioConfig);
|
||||
if (!is_software_audio_config && !is_a2dp_offload_audio_config &&
|
||||
!is_le_audio_offload_audio_config) {
|
||||
return false;
|
||||
}
|
||||
audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
|
||||
return true;
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::ReportSessionStatus() {
|
||||
// This is locked already by OnSessionStarted / OnSessionEnded
|
||||
if (observers_.empty()) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO port state observer";
|
||||
return;
|
||||
}
|
||||
for (auto& observer : observers_) {
|
||||
uint16_t cookie = observer.first;
|
||||
std::shared_ptr<PortStatusCallbacks> callback = observer.second;
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " notify to bluetooth_audio=0x"
|
||||
<< ::android::base::StringPrintf("%04x", cookie);
|
||||
callback->session_changed_cb_(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* PCM methods
|
||||
*
|
||||
***/
|
||||
|
||||
size_t BluetoothAudioSession::OutWritePcmData(const void* buffer,
|
||||
size_t bytes) {
|
||||
if (buffer == nullptr || bytes <= 0) {
|
||||
return 0;
|
||||
}
|
||||
size_t total_written = 0;
|
||||
int timeout_ms = kFmqSendTimeoutMs;
|
||||
do {
|
||||
std::unique_lock<std::recursive_mutex> lock(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
break;
|
||||
}
|
||||
size_t num_bytes_to_write = data_mq_->availableToWrite();
|
||||
if (num_bytes_to_write) {
|
||||
if (num_bytes_to_write > (bytes - total_written)) {
|
||||
num_bytes_to_write = bytes - total_written;
|
||||
}
|
||||
|
||||
if (!data_mq_->write(
|
||||
static_cast<const MQDataType*>(buffer) + total_written,
|
||||
num_bytes_to_write)) {
|
||||
LOG(ERROR) << "FMQ datapath writing " << total_written << "/" << bytes
|
||||
<< " failed";
|
||||
return total_written;
|
||||
}
|
||||
total_written += num_bytes_to_write;
|
||||
} else if (timeout_ms >= kWritePollMs) {
|
||||
lock.unlock();
|
||||
usleep(kWritePollMs * 1000);
|
||||
timeout_ms -= kWritePollMs;
|
||||
} else {
|
||||
LOG(DEBUG) << "Data " << total_written << "/" << bytes << " overflow "
|
||||
<< (kFmqSendTimeoutMs - timeout_ms) << " ms";
|
||||
return total_written;
|
||||
}
|
||||
} while (total_written < bytes);
|
||||
return total_written;
|
||||
}
|
||||
|
||||
size_t BluetoothAudioSession::InReadPcmData(void* buffer, size_t bytes) {
|
||||
if (buffer == nullptr || bytes <= 0) {
|
||||
return 0;
|
||||
}
|
||||
size_t total_read = 0;
|
||||
int timeout_ms = kFmqReceiveTimeoutMs;
|
||||
do {
|
||||
std::unique_lock<std::recursive_mutex> lock(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
break;
|
||||
}
|
||||
size_t num_bytes_to_read = data_mq_->availableToRead();
|
||||
if (num_bytes_to_read) {
|
||||
if (num_bytes_to_read > (bytes - total_read)) {
|
||||
num_bytes_to_read = bytes - total_read;
|
||||
}
|
||||
if (!data_mq_->read(static_cast<MQDataType*>(buffer) + total_read,
|
||||
num_bytes_to_read)) {
|
||||
LOG(ERROR) << "FMQ datapath reading " << total_read << "/" << bytes
|
||||
<< " failed";
|
||||
return total_read;
|
||||
}
|
||||
total_read += num_bytes_to_read;
|
||||
} else if (timeout_ms >= kReadPollMs) {
|
||||
lock.unlock();
|
||||
usleep(kReadPollMs * 1000);
|
||||
timeout_ms -= kReadPollMs;
|
||||
continue;
|
||||
} else {
|
||||
LOG(DEBUG) << "Data " << total_read << "/" << bytes << " overflow "
|
||||
<< (kFmqReceiveTimeoutMs - timeout_ms) << " ms";
|
||||
return total_read;
|
||||
}
|
||||
} while (total_read < bytes);
|
||||
return total_read;
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* Other methods
|
||||
*
|
||||
***/
|
||||
|
||||
void BluetoothAudioSession::ReportControlStatus(bool start_resp,
|
||||
BluetoothAudioStatus status) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (observers_.empty()) {
|
||||
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO port state observer";
|
||||
return;
|
||||
}
|
||||
for (auto& observer : observers_) {
|
||||
uint16_t cookie = observer.first;
|
||||
std::shared_ptr<PortStatusCallbacks> callback = observer.second;
|
||||
LOG(INFO) << __func__ << " - status=" << toString(status)
|
||||
<< " for SessionType=" << toString(session_type_)
|
||||
<< ", bluetooth_audio=0x"
|
||||
<< ::android::base::StringPrintf("%04x", cookie)
|
||||
<< (start_resp ? " started" : " suspended");
|
||||
callback->control_result_cb_(cookie, start_resp, status);
|
||||
}
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::ReportLowLatencyModeAllowedChanged(bool allowed) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
low_latency_allowed_ = allowed;
|
||||
if (observers_.empty()) {
|
||||
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO port state observer";
|
||||
return;
|
||||
}
|
||||
for (auto& observer : observers_) {
|
||||
uint16_t cookie = observer.first;
|
||||
std::shared_ptr<PortStatusCallbacks> callback = observer.second;
|
||||
LOG(INFO) << __func__
|
||||
<< " - allowed=" << (allowed ? " allowed" : " disallowed");
|
||||
if (callback->low_latency_mode_allowed_cb_ != nullptr) {
|
||||
callback->low_latency_mode_allowed_cb_(cookie, allowed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BluetoothAudioSession::GetPresentationPosition(
|
||||
PresentationPosition& presentation_position) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return false;
|
||||
}
|
||||
bool retval = false;
|
||||
|
||||
if (!stack_iface_->getPresentationPosition(&presentation_position).isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
return false;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::UpdateSourceMetadata(
|
||||
const struct source_metadata& source_metadata) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t track_count = source_metadata.track_count;
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ","
|
||||
<< track_count << " track(s)";
|
||||
if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
SourceMetadata hal_source_metadata;
|
||||
hal_source_metadata.tracks.resize(track_count);
|
||||
for (int i = 0; i < track_count; i++) {
|
||||
hal_source_metadata.tracks[i].usage =
|
||||
static_cast<media::audio::common::AudioUsage>(
|
||||
source_metadata.tracks[i].usage);
|
||||
hal_source_metadata.tracks[i].contentType =
|
||||
static_cast<media::audio::common::AudioContentType>(
|
||||
source_metadata.tracks[i].content_type);
|
||||
hal_source_metadata.tracks[i].gain = source_metadata.tracks[i].gain;
|
||||
LOG(VERBOSE) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", usage=" << toString(hal_source_metadata.tracks[i].usage)
|
||||
<< ", content="
|
||||
<< toString(hal_source_metadata.tracks[i].contentType)
|
||||
<< ", gain=" << hal_source_metadata.tracks[i].gain;
|
||||
}
|
||||
|
||||
auto hal_retval = stack_iface_->updateSourceMetadata(hal_source_metadata);
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
}
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::UpdateSinkMetadata(
|
||||
const struct sink_metadata& sink_metadata) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t track_count = sink_metadata.track_count;
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ","
|
||||
<< track_count << " track(s)";
|
||||
if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_SOFTWARE_DECODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
SinkMetadata hal_sink_metadata;
|
||||
hal_sink_metadata.tracks.resize(track_count);
|
||||
for (int i = 0; i < track_count; i++) {
|
||||
hal_sink_metadata.tracks[i].source =
|
||||
static_cast<media::audio::common::AudioSource>(
|
||||
sink_metadata.tracks[i].source);
|
||||
hal_sink_metadata.tracks[i].gain = sink_metadata.tracks[i].gain;
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", source=" << sink_metadata.tracks[i].source
|
||||
<< ", dest_device=" << sink_metadata.tracks[i].dest_device
|
||||
<< ", gain=" << sink_metadata.tracks[i].gain
|
||||
<< ", dest_device_address="
|
||||
<< sink_metadata.tracks[i].dest_device_address;
|
||||
}
|
||||
|
||||
auto hal_retval = stack_iface_->updateSinkMetadata(hal_sink_metadata);
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<LatencyMode> BluetoothAudioSession::GetSupportedLatencyModes() {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return std::vector<LatencyMode>();
|
||||
}
|
||||
if (low_latency_allowed_) return latency_modes_;
|
||||
std::vector<LatencyMode> modes;
|
||||
for (LatencyMode mode : latency_modes_) {
|
||||
if (mode == LatencyMode::LOW_LATENCY)
|
||||
// ignore those low latency mode if Bluetooth stack doesn't allow
|
||||
continue;
|
||||
modes.push_back(mode);
|
||||
}
|
||||
return modes;
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::SetLatencyMode(const LatencyMode& latency_mode) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return;
|
||||
}
|
||||
|
||||
auto hal_retval = stack_iface_->setLatencyMode(latency_mode);
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
}
|
||||
}
|
||||
|
||||
bool BluetoothAudioSession::IsAidlAvailable() {
|
||||
if (is_aidl_checked) return is_aidl_available;
|
||||
is_aidl_available =
|
||||
(AServiceManager_checkService(
|
||||
kDefaultAudioProviderFactoryInterface.c_str()) != nullptr);
|
||||
is_aidl_checked = true;
|
||||
return is_aidl_available;
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* BluetoothAudioSessionInstance
|
||||
*
|
||||
***/
|
||||
std::mutex BluetoothAudioSessionInstance::mutex_;
|
||||
std::unordered_map<SessionType, std::shared_ptr<BluetoothAudioSession>>
|
||||
BluetoothAudioSessionInstance::sessions_map_;
|
||||
|
||||
std::shared_ptr<BluetoothAudioSession>
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(
|
||||
const SessionType& session_type) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
|
||||
if (!sessions_map_.empty()) {
|
||||
auto entry = sessions_map_.find(session_type);
|
||||
if (entry != sessions_map_.end()) {
|
||||
return entry->second;
|
||||
}
|
||||
}
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
std::make_shared<BluetoothAudioSession>(session_type);
|
||||
sessions_map_[session_type] = session_ptr;
|
||||
return session_ptr;
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
243
bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h
Normal file
243
bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/audio/common/SinkMetadata.h>
|
||||
#include <aidl/android/hardware/audio/common/SourceMetadata.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/IBluetoothAudioProvider.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/IBluetoothAudioProviderFactory.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/LatencyMode.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
|
||||
#include <fmq/AidlMessageQueue.h>
|
||||
#include <hardware/audio.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
using ::aidl::android::hardware::common::fmq::MQDescriptor;
|
||||
using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
|
||||
using ::android::AidlMessageQueue;
|
||||
|
||||
using ::aidl::android::hardware::audio::common::SinkMetadata;
|
||||
using ::aidl::android::hardware::audio::common::SourceMetadata;
|
||||
|
||||
using MQDataType = int8_t;
|
||||
using MQDataMode = SynchronizedReadWrite;
|
||||
using DataMQ = AidlMessageQueue<MQDataType, MQDataMode>;
|
||||
using DataMQDesc =
|
||||
::aidl::android::hardware::common::fmq::MQDescriptor<MQDataType,
|
||||
MQDataMode>;
|
||||
|
||||
static constexpr uint16_t kObserversCookieSize = 0x0010; // 0x0000 ~ 0x000f
|
||||
static constexpr uint16_t kObserversCookieUndefined =
|
||||
(static_cast<uint16_t>(SessionType::UNKNOWN) << 8 & 0xff00);
|
||||
inline SessionType ObserversCookieGetSessionType(uint16_t cookie) {
|
||||
return static_cast<SessionType>(cookie >> 8 & 0x00ff);
|
||||
}
|
||||
inline uint16_t ObserversCookieGetInitValue(SessionType session_type) {
|
||||
return (static_cast<uint16_t>(session_type) << 8 & 0xff00);
|
||||
}
|
||||
inline uint16_t ObserversCookieGetUpperBound(SessionType session_type) {
|
||||
return (static_cast<uint16_t>(session_type) << 8 & 0xff00) +
|
||||
kObserversCookieSize;
|
||||
}
|
||||
|
||||
/***
|
||||
* This presents the callbacks of started / suspended and session changed,
|
||||
* and the bluetooth_audio module uses to receive the status notification
|
||||
***/
|
||||
struct PortStatusCallbacks {
|
||||
/***
|
||||
* control_result_cb_ - when the Bluetooth stack reports results of
|
||||
* streamStarted or streamSuspended, the BluetoothAudioProvider will invoke
|
||||
* this callback to report to the bluetooth_audio module.
|
||||
* @param: cookie - indicates which bluetooth_audio output should handle
|
||||
* @param: start_resp - this report is for startStream or not
|
||||
* @param: status - the result of startStream
|
||||
***/
|
||||
std::function<void(uint16_t cookie, bool start_resp,
|
||||
BluetoothAudioStatus status)>
|
||||
control_result_cb_;
|
||||
/***
|
||||
* session_changed_cb_ - when the Bluetooth stack start / end session, the
|
||||
* BluetoothAudioProvider will invoke this callback to notify to the
|
||||
* bluetooth_audio module.
|
||||
* @param: cookie - indicates which bluetooth_audio output should handle
|
||||
***/
|
||||
std::function<void(uint16_t cookie)> session_changed_cb_;
|
||||
/***
|
||||
* audio_configuration_changed_cb_ - when the Bluetooth stack change the audio
|
||||
* configuration, the BluetoothAudioProvider will invoke this callback to
|
||||
* notify to the bluetooth_audio module.
|
||||
* @param: cookie - indicates which bluetooth_audio output should handle
|
||||
***/
|
||||
std::function<void(uint16_t cookie)> audio_configuration_changed_cb_;
|
||||
/***
|
||||
* low_latency_mode_allowed_cb_ - when the Bluetooth stack low latency mode
|
||||
* allowed or disallowed, the BluetoothAudioProvider will invoke
|
||||
* this callback to report to the bluetooth_audio module.
|
||||
* @param: cookie - indicates which bluetooth_audio output should handle
|
||||
* @param: allowed - indicates if low latency mode is allowed
|
||||
***/
|
||||
std::function<void(uint16_t cookie, bool allowed)>
|
||||
low_latency_mode_allowed_cb_;
|
||||
};
|
||||
|
||||
class BluetoothAudioSession {
|
||||
public:
|
||||
BluetoothAudioSession(const SessionType& session_type);
|
||||
|
||||
/***
|
||||
* The function helps to check if this session is ready or not
|
||||
* @return: true if the Bluetooth stack has started the specified session
|
||||
***/
|
||||
bool IsSessionReady();
|
||||
|
||||
/***
|
||||
* The report function is used to report that the Bluetooth stack has started
|
||||
* this session without any failure, and will invoke session_changed_cb_ to
|
||||
* notify those registered bluetooth_audio outputs
|
||||
***/
|
||||
void OnSessionStarted(const std::shared_ptr<IBluetoothAudioPort> stack_iface,
|
||||
const DataMQDesc* mq_desc,
|
||||
const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes);
|
||||
|
||||
/***
|
||||
* The report function is used to report that the Bluetooth stack has ended
|
||||
* the session, and will invoke session_changed_cb_ to notify registered
|
||||
* bluetooth_audio outputs
|
||||
***/
|
||||
void OnSessionEnded();
|
||||
|
||||
/***
|
||||
* The report function is used to report that the Bluetooth stack has notified
|
||||
* the result of startStream or suspendStream, and will invoke
|
||||
* control_result_cb_ to notify registered bluetooth_audio outputs
|
||||
***/
|
||||
void ReportControlStatus(bool start_resp, BluetoothAudioStatus status);
|
||||
|
||||
/***
|
||||
* The control function helps the bluetooth_audio module to register
|
||||
* PortStatusCallbacks
|
||||
* @return: cookie - the assigned number to this bluetooth_audio output
|
||||
***/
|
||||
uint16_t RegisterStatusCback(const PortStatusCallbacks& cbacks);
|
||||
|
||||
/***
|
||||
* The control function helps the bluetooth_audio module to unregister
|
||||
* PortStatusCallbacks
|
||||
* @param: cookie - indicates which bluetooth_audio output is
|
||||
***/
|
||||
void UnregisterStatusCback(uint16_t cookie);
|
||||
|
||||
/***
|
||||
* The control function is for the bluetooth_audio module to get the current
|
||||
* AudioConfiguration
|
||||
***/
|
||||
const AudioConfiguration GetAudioConfig();
|
||||
|
||||
/***
|
||||
* The report function is used to report that the Bluetooth stack has notified
|
||||
* the audio configuration changed, and will invoke
|
||||
* audio_configuration_changed_cb_ to notify registered bluetooth_audio
|
||||
* outputs
|
||||
***/
|
||||
void ReportAudioConfigChanged(const AudioConfiguration& audio_config);
|
||||
|
||||
/***
|
||||
* The report function is used to report that the Bluetooth stack has notified
|
||||
* the low latency mode allowed changed, and will invoke
|
||||
* low_latency_mode_allowed_changed_cb to notify registered bluetooth_audio
|
||||
* outputs
|
||||
***/
|
||||
void ReportLowLatencyModeAllowedChanged(bool allowed);
|
||||
/***
|
||||
* Those control functions are for the bluetooth_audio module to start,
|
||||
* suspend, stop stream, to check position, and to update metadata.
|
||||
***/
|
||||
bool StartStream(bool low_latency);
|
||||
bool SuspendStream();
|
||||
void StopStream();
|
||||
bool GetPresentationPosition(PresentationPosition& presentation_position);
|
||||
void UpdateSourceMetadata(const struct source_metadata& source_metadata);
|
||||
void UpdateSinkMetadata(const struct sink_metadata& sink_metadata);
|
||||
|
||||
std::vector<LatencyMode> GetSupportedLatencyModes();
|
||||
void SetLatencyMode(const LatencyMode& latency_mode);
|
||||
|
||||
// The control function writes stream to FMQ
|
||||
size_t OutWritePcmData(const void* buffer, size_t bytes);
|
||||
// The control function read stream from FMQ
|
||||
size_t InReadPcmData(void* buffer, size_t bytes);
|
||||
|
||||
// Return if IBluetoothAudioProviderFactory implementation existed
|
||||
static bool IsAidlAvailable();
|
||||
|
||||
private:
|
||||
// using recursive_mutex to allow hwbinder to re-enter again.
|
||||
std::recursive_mutex mutex_;
|
||||
SessionType session_type_;
|
||||
|
||||
// audio control path to use for both software and offloading
|
||||
std::shared_ptr<IBluetoothAudioPort> stack_iface_;
|
||||
// audio data path (FMQ) for software encoding
|
||||
std::unique_ptr<DataMQ> data_mq_;
|
||||
// audio data configuration for both software and offloading
|
||||
std::unique_ptr<AudioConfiguration> audio_config_;
|
||||
std::vector<LatencyMode> latency_modes_;
|
||||
bool low_latency_allowed_ = true;
|
||||
|
||||
// saving those registered bluetooth_audio's callbacks
|
||||
std::unordered_map<uint16_t, std::shared_ptr<struct PortStatusCallbacks>>
|
||||
observers_;
|
||||
|
||||
bool UpdateDataPath(const DataMQDesc* mq_desc);
|
||||
bool UpdateAudioConfig(const AudioConfiguration& audio_config);
|
||||
// invoking the registered session_changed_cb_
|
||||
void ReportSessionStatus();
|
||||
|
||||
static inline std::atomic<bool> is_aidl_checked = false;
|
||||
static inline std::atomic<bool> is_aidl_available = false;
|
||||
static inline const std::string kDefaultAudioProviderFactoryInterface =
|
||||
std::string() + IBluetoothAudioProviderFactory::descriptor + "/sysbta";
|
||||
};
|
||||
|
||||
class BluetoothAudioSessionInstance {
|
||||
public:
|
||||
// The API is to fetch the specified session of A2DP / Hearing Aid
|
||||
static std::shared_ptr<BluetoothAudioSession> GetSessionInstance(
|
||||
const SessionType& session_type);
|
||||
|
||||
private:
|
||||
static std::mutex mutex_;
|
||||
static std::unordered_map<SessionType, std::shared_ptr<BluetoothAudioSession>>
|
||||
sessions_map_;
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioSession.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class BluetoothAudioSessionControl {
|
||||
public:
|
||||
/***
|
||||
* The control API helps to check if session is ready or not
|
||||
* @return: true if the Bluetooth stack has started th specified session
|
||||
***/
|
||||
static bool IsSessionReady(const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->IsSessionReady();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/***
|
||||
* The control API helps the bluetooth_audio module to register
|
||||
* PortStatusCallbacks
|
||||
* @return: cookie - the assigned number to this bluetooth_audio output
|
||||
***/
|
||||
static uint16_t RegisterControlResultCback(
|
||||
const SessionType& session_type, const PortStatusCallbacks& cbacks) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->RegisterStatusCback(cbacks);
|
||||
}
|
||||
return kObserversCookieUndefined;
|
||||
}
|
||||
|
||||
/***
|
||||
* The control API helps the bluetooth_audio module to unregister
|
||||
* PortStatusCallbacks
|
||||
* @param: cookie - indicates which bluetooth_audio output is
|
||||
***/
|
||||
static void UnregisterControlResultCback(const SessionType& session_type,
|
||||
uint16_t cookie) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->UnregisterStatusCback(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* The control API for the bluetooth_audio module to get current
|
||||
* AudioConfiguration
|
||||
***/
|
||||
static const AudioConfiguration GetAudioConfig(
|
||||
const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetAudioConfig();
|
||||
}
|
||||
switch (session_type) {
|
||||
case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
case SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH:
|
||||
return AudioConfiguration(CodecConfiguration{});
|
||||
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
|
||||
return AudioConfiguration(LeAudioConfiguration{});
|
||||
case SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
|
||||
return AudioConfiguration(LeAudioBroadcastConfiguration{});
|
||||
default:
|
||||
return AudioConfiguration(PcmConfiguration{});
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* Those control APIs for the bluetooth_audio module to start / suspend /
|
||||
stop
|
||||
* stream, to check position, and to update metadata.
|
||||
***/
|
||||
static bool StartStream(const SessionType& session_type,
|
||||
bool low_latency = false) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->StartStream(low_latency);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool SuspendStream(const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->SuspendStream();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void StopStream(const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->StopStream();
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetPresentationPosition(
|
||||
const SessionType& session_type,
|
||||
PresentationPosition& presentation_position) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetPresentationPosition(presentation_position);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void UpdateSourceMetadata(
|
||||
const SessionType& session_type,
|
||||
const struct source_metadata& source_metadata) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->UpdateSourceMetadata(source_metadata);
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateSinkMetadata(const SessionType& session_type,
|
||||
const struct sink_metadata& sink_metadata) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->UpdateSinkMetadata(sink_metadata);
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<LatencyMode> GetSupportedLatencyModes(
|
||||
const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetSupportedLatencyModes();
|
||||
}
|
||||
return std::vector<LatencyMode>();
|
||||
}
|
||||
|
||||
static void SetLatencyMode(const SessionType& session_type,
|
||||
const LatencyMode& latency_mode) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->SetLatencyMode(latency_mode);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* The control API writes stream to FMQ
|
||||
***/
|
||||
static size_t OutWritePcmData(const SessionType& session_type,
|
||||
const void* buffer, size_t bytes) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->OutWritePcmData(buffer, bytes);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
* The control API reads stream from FMQ
|
||||
***/
|
||||
static size_t InReadPcmData(const SessionType& session_type, void* buffer,
|
||||
size_t bytes) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->InReadPcmData(buffer, bytes);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
101
bluetooth/audio/utils/aidl_session/BluetoothAudioSessionReport.h
Normal file
101
bluetooth/audio/utils/aidl_session/BluetoothAudioSessionReport.h
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioSession.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class BluetoothAudioSessionReport {
|
||||
public:
|
||||
/***
|
||||
* The API reports the Bluetooth stack has started the session, and will
|
||||
* inform registered bluetooth_audio outputs
|
||||
***/
|
||||
static void OnSessionStarted(
|
||||
const SessionType& session_type,
|
||||
const std::shared_ptr<IBluetoothAudioPort> host_iface,
|
||||
const DataMQDesc* data_mq, const AudioConfiguration& audio_config,
|
||||
const std::vector<LatencyMode>& latency_modes) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->OnSessionStarted(host_iface, data_mq, audio_config,
|
||||
latency_modes);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* The API reports the Bluetooth stack has ended the session, and will
|
||||
* inform registered bluetooth_audio outputs
|
||||
***/
|
||||
static void OnSessionEnded(const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->OnSessionEnded();
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* The API reports the Bluetooth stack has replied the result of startStream
|
||||
* or suspendStream, and will inform registered bluetooth_audio outputs
|
||||
***/
|
||||
static void ReportControlStatus(const SessionType& session_type,
|
||||
const bool& start_resp,
|
||||
BluetoothAudioStatus status) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->ReportControlStatus(start_resp, status);
|
||||
}
|
||||
}
|
||||
/***
|
||||
* The API reports the Bluetooth stack has replied the changed of the audio
|
||||
* configuration, and will inform registered bluetooth_audio outputs
|
||||
***/
|
||||
static void ReportAudioConfigChanged(const SessionType& session_type,
|
||||
const AudioConfiguration& audio_config) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->ReportAudioConfigChanged(audio_config);
|
||||
}
|
||||
}
|
||||
/***
|
||||
* The API reports the Bluetooth stack has replied the changed of the low
|
||||
* latency audio allowed, and will inform registered bluetooth_audio outputs
|
||||
***/
|
||||
static void ReportLowLatencyModeAllowedChanged(
|
||||
const SessionType& session_type, bool allowed) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->ReportLowLatencyModeAllowedChanged(allowed);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
632
bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp
Normal file
632
bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp
Normal file
@ -0,0 +1,632 @@
|
||||
/*
|
||||
* Copyright (C) 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 "BtAudioNakahara"
|
||||
|
||||
#include <aidl/android/hardware/bluetooth/audio/AudioConfiguration.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/BluetoothAudioStatus.h>
|
||||
#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../aidl_session/BluetoothAudioSession.h"
|
||||
#include "../aidl_session/BluetoothAudioSessionControl.h"
|
||||
#include "HidlToAidlMiddleware_2_0.h"
|
||||
#include "HidlToAidlMiddleware_2_1.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
using HidlStatus = ::android::hardware::bluetooth::audio::V2_0::Status;
|
||||
using PcmConfig_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::PcmParameters;
|
||||
using SampleRate_2_0 = ::android::hardware::bluetooth::audio::V2_0::SampleRate;
|
||||
using ChannelMode_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::ChannelMode;
|
||||
using BitsPerSample_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
|
||||
using CodecConfig_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
|
||||
using CodecType_2_0 = ::android::hardware::bluetooth::audio::V2_0::CodecType;
|
||||
using SbcConfig_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::SbcParameters;
|
||||
using AacConfig_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::AacParameters;
|
||||
using LdacConfig_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::LdacParameters;
|
||||
using AptxConfig_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::AptxParameters;
|
||||
using SbcAllocMethod_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
|
||||
using SbcBlockLength_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
|
||||
using SbcChannelMode_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
|
||||
using SbcNumSubbands_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
|
||||
using AacObjectType_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::AacObjectType;
|
||||
using AacVarBitRate_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
|
||||
using LdacChannelMode_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
|
||||
using LdacQualityIndex_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
|
||||
|
||||
using PcmConfig_2_1 =
|
||||
::android::hardware::bluetooth::audio::V2_1::PcmParameters;
|
||||
using SampleRate_2_1 = ::android::hardware::bluetooth::audio::V2_1::SampleRate;
|
||||
using Lc3CodecConfig_2_1 =
|
||||
::android::hardware::bluetooth::audio::V2_1::Lc3CodecConfiguration;
|
||||
using Lc3Config_2_1 =
|
||||
::android::hardware::bluetooth::audio::V2_1::Lc3Parameters;
|
||||
using Lc3FrameDuration_2_1 =
|
||||
::android::hardware::bluetooth::audio::V2_1::Lc3FrameDuration;
|
||||
|
||||
std::mutex legacy_callback_lock;
|
||||
std::unordered_map<
|
||||
SessionType,
|
||||
std::unordered_map<uint16_t, std::shared_ptr<PortStatusCallbacks_2_0>>>
|
||||
legacy_callback_table;
|
||||
|
||||
const static std::unordered_map<SessionType_2_1, SessionType>
|
||||
session_type_2_1_to_aidl_map{
|
||||
{SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH,
|
||||
SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH},
|
||||
{SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH,
|
||||
SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH},
|
||||
{SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH,
|
||||
SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH},
|
||||
{SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH,
|
||||
SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH},
|
||||
{SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH,
|
||||
SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH},
|
||||
{SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH,
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH},
|
||||
{SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH,
|
||||
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH},
|
||||
};
|
||||
|
||||
const static std::unordered_map<int32_t, SampleRate_2_1>
|
||||
sample_rate_to_hidl_2_1_map{
|
||||
{44100, SampleRate_2_1::RATE_44100},
|
||||
{48000, SampleRate_2_1::RATE_48000},
|
||||
{88200, SampleRate_2_1::RATE_88200},
|
||||
{96000, SampleRate_2_1::RATE_96000},
|
||||
{176400, SampleRate_2_1::RATE_176400},
|
||||
{192000, SampleRate_2_1::RATE_192000},
|
||||
{16000, SampleRate_2_1::RATE_16000},
|
||||
{24000, SampleRate_2_1::RATE_24000},
|
||||
{8000, SampleRate_2_1::RATE_8000},
|
||||
{32000, SampleRate_2_1::RATE_32000},
|
||||
};
|
||||
|
||||
const static std::unordered_map<CodecType, CodecType_2_0>
|
||||
codec_type_to_hidl_2_0_map{
|
||||
{CodecType::UNKNOWN, CodecType_2_0::UNKNOWN},
|
||||
{CodecType::SBC, CodecType_2_0::SBC},
|
||||
{CodecType::AAC, CodecType_2_0::AAC},
|
||||
{CodecType::APTX, CodecType_2_0::APTX},
|
||||
{CodecType::APTX_HD, CodecType_2_0::APTX_HD},
|
||||
{CodecType::LDAC, CodecType_2_0::LDAC},
|
||||
{CodecType::LC3, CodecType_2_0::UNKNOWN},
|
||||
};
|
||||
|
||||
const static std::unordered_map<SbcChannelMode, SbcChannelMode_2_0>
|
||||
sbc_channel_mode_to_hidl_2_0_map{
|
||||
{SbcChannelMode::UNKNOWN, SbcChannelMode_2_0::UNKNOWN},
|
||||
{SbcChannelMode::JOINT_STEREO, SbcChannelMode_2_0::JOINT_STEREO},
|
||||
{SbcChannelMode::STEREO, SbcChannelMode_2_0::STEREO},
|
||||
{SbcChannelMode::DUAL, SbcChannelMode_2_0::DUAL},
|
||||
{SbcChannelMode::MONO, SbcChannelMode_2_0::MONO},
|
||||
};
|
||||
|
||||
const static std::unordered_map<int8_t, SbcBlockLength_2_0>
|
||||
sbc_block_length_to_hidl_map{
|
||||
{4, SbcBlockLength_2_0::BLOCKS_4},
|
||||
{8, SbcBlockLength_2_0::BLOCKS_8},
|
||||
{12, SbcBlockLength_2_0::BLOCKS_12},
|
||||
{16, SbcBlockLength_2_0::BLOCKS_16},
|
||||
};
|
||||
|
||||
const static std::unordered_map<int8_t, SbcNumSubbands_2_0>
|
||||
sbc_subbands_to_hidl_map{
|
||||
{4, SbcNumSubbands_2_0::SUBBAND_4},
|
||||
{8, SbcNumSubbands_2_0::SUBBAND_8},
|
||||
};
|
||||
|
||||
const static std::unordered_map<SbcAllocMethod, SbcAllocMethod_2_0>
|
||||
sbc_alloc_method_to_hidl_map{
|
||||
{SbcAllocMethod::ALLOC_MD_S, SbcAllocMethod_2_0::ALLOC_MD_S},
|
||||
{SbcAllocMethod::ALLOC_MD_L, SbcAllocMethod_2_0::ALLOC_MD_L},
|
||||
};
|
||||
|
||||
const static std::unordered_map<AacObjectType, AacObjectType_2_0>
|
||||
aac_object_type_to_hidl_map{
|
||||
{AacObjectType::MPEG2_LC, AacObjectType_2_0::MPEG2_LC},
|
||||
{AacObjectType::MPEG4_LC, AacObjectType_2_0::MPEG4_LC},
|
||||
{AacObjectType::MPEG4_LTP, AacObjectType_2_0::MPEG4_LTP},
|
||||
{AacObjectType::MPEG4_SCALABLE, AacObjectType_2_0::MPEG4_SCALABLE},
|
||||
};
|
||||
|
||||
const static std::unordered_map<LdacChannelMode, LdacChannelMode_2_0>
|
||||
ldac_channel_mode_to_hidl_map{
|
||||
{LdacChannelMode::UNKNOWN, LdacChannelMode_2_0::UNKNOWN},
|
||||
{LdacChannelMode::STEREO, LdacChannelMode_2_0::STEREO},
|
||||
{LdacChannelMode::DUAL, LdacChannelMode_2_0::DUAL},
|
||||
{LdacChannelMode::MONO, LdacChannelMode_2_0::MONO},
|
||||
};
|
||||
|
||||
const static std::unordered_map<LdacQualityIndex, LdacQualityIndex_2_0>
|
||||
ldac_qindex_to_hidl_map{
|
||||
{LdacQualityIndex::HIGH, LdacQualityIndex_2_0::QUALITY_HIGH},
|
||||
{LdacQualityIndex::MID, LdacQualityIndex_2_0::QUALITY_MID},
|
||||
{LdacQualityIndex::LOW, LdacQualityIndex_2_0::QUALITY_LOW},
|
||||
{LdacQualityIndex::ABR, LdacQualityIndex_2_0::QUALITY_ABR},
|
||||
};
|
||||
|
||||
inline SessionType from_session_type_2_1(
|
||||
const SessionType_2_1& session_type_hidl) {
|
||||
auto it = session_type_2_1_to_aidl_map.find(session_type_hidl);
|
||||
if (it != session_type_2_1_to_aidl_map.end()) return it->second;
|
||||
return SessionType::UNKNOWN;
|
||||
}
|
||||
|
||||
inline SessionType from_session_type_2_0(
|
||||
const SessionType_2_0& session_type_hidl) {
|
||||
return from_session_type_2_1(static_cast<SessionType_2_1>(session_type_hidl));
|
||||
}
|
||||
|
||||
inline HidlStatus to_hidl_status(const BluetoothAudioStatus& status) {
|
||||
switch (status) {
|
||||
case BluetoothAudioStatus::SUCCESS:
|
||||
return HidlStatus::SUCCESS;
|
||||
case BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION:
|
||||
return HidlStatus::UNSUPPORTED_CODEC_CONFIGURATION;
|
||||
default:
|
||||
return HidlStatus::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
inline SampleRate_2_1 to_hidl_sample_rate_2_1(const int32_t sample_rate_hz) {
|
||||
auto it = sample_rate_to_hidl_2_1_map.find(sample_rate_hz);
|
||||
if (it != sample_rate_to_hidl_2_1_map.end()) return it->second;
|
||||
return SampleRate_2_1::RATE_UNKNOWN;
|
||||
}
|
||||
|
||||
inline SampleRate_2_0 to_hidl_sample_rate_2_0(const int32_t sample_rate_hz) {
|
||||
auto it = sample_rate_to_hidl_2_1_map.find(sample_rate_hz);
|
||||
if (it != sample_rate_to_hidl_2_1_map.end())
|
||||
return static_cast<SampleRate_2_0>(it->second);
|
||||
return SampleRate_2_0::RATE_UNKNOWN;
|
||||
}
|
||||
|
||||
inline BitsPerSample_2_0 to_hidl_bits_per_sample(const int8_t bit_per_sample) {
|
||||
switch (bit_per_sample) {
|
||||
case 16:
|
||||
return BitsPerSample_2_0::BITS_16;
|
||||
case 24:
|
||||
return BitsPerSample_2_0::BITS_24;
|
||||
case 32:
|
||||
return BitsPerSample_2_0::BITS_32;
|
||||
default:
|
||||
return BitsPerSample_2_0::BITS_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
inline ChannelMode_2_0 to_hidl_channel_mode(const ChannelMode channel_mode) {
|
||||
switch (channel_mode) {
|
||||
case ChannelMode::MONO:
|
||||
return ChannelMode_2_0::MONO;
|
||||
case ChannelMode::STEREO:
|
||||
return ChannelMode_2_0::STEREO;
|
||||
default:
|
||||
return ChannelMode_2_0::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
inline PcmConfig_2_0 to_hidl_pcm_config_2_0(
|
||||
const PcmConfiguration& pcm_config) {
|
||||
PcmConfig_2_0 hidl_pcm_config;
|
||||
hidl_pcm_config.sampleRate = to_hidl_sample_rate_2_0(pcm_config.sampleRateHz);
|
||||
hidl_pcm_config.channelMode = to_hidl_channel_mode(pcm_config.channelMode);
|
||||
hidl_pcm_config.bitsPerSample =
|
||||
to_hidl_bits_per_sample(pcm_config.bitsPerSample);
|
||||
return hidl_pcm_config;
|
||||
}
|
||||
|
||||
inline CodecType_2_0 to_hidl_codec_type_2_0(const CodecType codec_type) {
|
||||
auto it = codec_type_to_hidl_2_0_map.find(codec_type);
|
||||
if (it != codec_type_to_hidl_2_0_map.end()) return it->second;
|
||||
return CodecType_2_0::UNKNOWN;
|
||||
}
|
||||
|
||||
inline SbcConfig_2_0 to_hidl_sbc_config(const SbcConfiguration sbc_config) {
|
||||
SbcConfig_2_0 hidl_sbc_config;
|
||||
hidl_sbc_config.minBitpool = sbc_config.minBitpool;
|
||||
hidl_sbc_config.maxBitpool = sbc_config.maxBitpool;
|
||||
hidl_sbc_config.sampleRate = to_hidl_sample_rate_2_0(sbc_config.sampleRateHz);
|
||||
hidl_sbc_config.bitsPerSample =
|
||||
to_hidl_bits_per_sample(sbc_config.bitsPerSample);
|
||||
if (sbc_channel_mode_to_hidl_2_0_map.find(sbc_config.channelMode) !=
|
||||
sbc_channel_mode_to_hidl_2_0_map.end()) {
|
||||
hidl_sbc_config.channelMode =
|
||||
sbc_channel_mode_to_hidl_2_0_map.at(sbc_config.channelMode);
|
||||
}
|
||||
if (sbc_block_length_to_hidl_map.find(sbc_config.blockLength) !=
|
||||
sbc_block_length_to_hidl_map.end()) {
|
||||
hidl_sbc_config.blockLength =
|
||||
sbc_block_length_to_hidl_map.at(sbc_config.blockLength);
|
||||
}
|
||||
if (sbc_subbands_to_hidl_map.find(sbc_config.numSubbands) !=
|
||||
sbc_subbands_to_hidl_map.end()) {
|
||||
hidl_sbc_config.numSubbands =
|
||||
sbc_subbands_to_hidl_map.at(sbc_config.numSubbands);
|
||||
}
|
||||
if (sbc_alloc_method_to_hidl_map.find(sbc_config.allocMethod) !=
|
||||
sbc_alloc_method_to_hidl_map.end()) {
|
||||
hidl_sbc_config.allocMethod =
|
||||
sbc_alloc_method_to_hidl_map.at(sbc_config.allocMethod);
|
||||
}
|
||||
return hidl_sbc_config;
|
||||
}
|
||||
|
||||
inline AacConfig_2_0 to_hidl_aac_config(const AacConfiguration aac_config) {
|
||||
AacConfig_2_0 hidl_aac_config;
|
||||
hidl_aac_config.sampleRate = to_hidl_sample_rate_2_0(aac_config.sampleRateHz);
|
||||
hidl_aac_config.bitsPerSample =
|
||||
to_hidl_bits_per_sample(aac_config.bitsPerSample);
|
||||
hidl_aac_config.channelMode = to_hidl_channel_mode(aac_config.channelMode);
|
||||
if (aac_object_type_to_hidl_map.find(aac_config.objectType) !=
|
||||
aac_object_type_to_hidl_map.end()) {
|
||||
hidl_aac_config.objectType =
|
||||
aac_object_type_to_hidl_map.at(aac_config.objectType);
|
||||
}
|
||||
hidl_aac_config.variableBitRateEnabled = aac_config.variableBitRateEnabled
|
||||
? AacVarBitRate_2_0::ENABLED
|
||||
: AacVarBitRate_2_0::DISABLED;
|
||||
return hidl_aac_config;
|
||||
}
|
||||
|
||||
inline LdacConfig_2_0 to_hidl_ldac_config(const LdacConfiguration ldac_config) {
|
||||
LdacConfig_2_0 hidl_ldac_config;
|
||||
hidl_ldac_config.sampleRate =
|
||||
to_hidl_sample_rate_2_0(ldac_config.sampleRateHz);
|
||||
hidl_ldac_config.bitsPerSample =
|
||||
to_hidl_bits_per_sample(ldac_config.bitsPerSample);
|
||||
if (ldac_channel_mode_to_hidl_map.find(ldac_config.channelMode) !=
|
||||
ldac_channel_mode_to_hidl_map.end()) {
|
||||
hidl_ldac_config.channelMode =
|
||||
ldac_channel_mode_to_hidl_map.at(ldac_config.channelMode);
|
||||
}
|
||||
if (ldac_qindex_to_hidl_map.find(ldac_config.qualityIndex) !=
|
||||
ldac_qindex_to_hidl_map.end()) {
|
||||
hidl_ldac_config.qualityIndex =
|
||||
ldac_qindex_to_hidl_map.at(ldac_config.qualityIndex);
|
||||
}
|
||||
return hidl_ldac_config;
|
||||
}
|
||||
|
||||
inline AptxConfig_2_0 to_hidl_aptx_config(const AptxConfiguration aptx_config) {
|
||||
AptxConfig_2_0 hidl_aptx_config;
|
||||
hidl_aptx_config.sampleRate =
|
||||
to_hidl_sample_rate_2_0(aptx_config.sampleRateHz);
|
||||
hidl_aptx_config.bitsPerSample =
|
||||
to_hidl_bits_per_sample(aptx_config.bitsPerSample);
|
||||
hidl_aptx_config.channelMode = to_hidl_channel_mode(aptx_config.channelMode);
|
||||
return hidl_aptx_config;
|
||||
}
|
||||
|
||||
inline CodecConfig_2_0 to_hidl_codec_config_2_0(
|
||||
const CodecConfiguration& codec_config) {
|
||||
CodecConfig_2_0 hidl_codec_config;
|
||||
hidl_codec_config.codecType = to_hidl_codec_type_2_0(codec_config.codecType);
|
||||
hidl_codec_config.encodedAudioBitrate =
|
||||
static_cast<uint32_t>(codec_config.encodedAudioBitrate);
|
||||
hidl_codec_config.peerMtu = static_cast<uint32_t>(codec_config.peerMtu);
|
||||
hidl_codec_config.isScmstEnabled = codec_config.isScmstEnabled;
|
||||
switch (codec_config.config.getTag()) {
|
||||
case CodecConfiguration::CodecSpecific::sbcConfig:
|
||||
hidl_codec_config.config.sbcConfig(to_hidl_sbc_config(
|
||||
codec_config.config
|
||||
.get<CodecConfiguration::CodecSpecific::sbcConfig>()));
|
||||
break;
|
||||
case CodecConfiguration::CodecSpecific::aacConfig:
|
||||
hidl_codec_config.config.aacConfig(to_hidl_aac_config(
|
||||
codec_config.config
|
||||
.get<CodecConfiguration::CodecSpecific::aacConfig>()));
|
||||
break;
|
||||
case CodecConfiguration::CodecSpecific::ldacConfig:
|
||||
hidl_codec_config.config.ldacConfig(to_hidl_ldac_config(
|
||||
codec_config.config
|
||||
.get<CodecConfiguration::CodecSpecific::ldacConfig>()));
|
||||
break;
|
||||
case CodecConfiguration::CodecSpecific::aptxConfig:
|
||||
hidl_codec_config.config.aptxConfig(to_hidl_aptx_config(
|
||||
codec_config.config
|
||||
.get<CodecConfiguration::CodecSpecific::aptxConfig>()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return hidl_codec_config;
|
||||
}
|
||||
|
||||
inline AudioConfig_2_0 to_hidl_audio_config_2_0(
|
||||
const AudioConfiguration& audio_config) {
|
||||
AudioConfig_2_0 hidl_audio_config;
|
||||
if (audio_config.getTag() == AudioConfiguration::pcmConfig) {
|
||||
hidl_audio_config.pcmConfig(to_hidl_pcm_config_2_0(
|
||||
audio_config.get<AudioConfiguration::pcmConfig>()));
|
||||
} else if (audio_config.getTag() == AudioConfiguration::a2dpConfig) {
|
||||
hidl_audio_config.codecConfig(to_hidl_codec_config_2_0(
|
||||
audio_config.get<AudioConfiguration::a2dpConfig>()));
|
||||
}
|
||||
return hidl_audio_config;
|
||||
}
|
||||
|
||||
inline PcmConfig_2_1 to_hidl_pcm_config_2_1(
|
||||
const PcmConfiguration& pcm_config) {
|
||||
PcmConfig_2_1 hidl_pcm_config;
|
||||
hidl_pcm_config.sampleRate = to_hidl_sample_rate_2_1(pcm_config.sampleRateHz);
|
||||
hidl_pcm_config.channelMode = to_hidl_channel_mode(pcm_config.channelMode);
|
||||
hidl_pcm_config.bitsPerSample =
|
||||
to_hidl_bits_per_sample(pcm_config.bitsPerSample);
|
||||
hidl_pcm_config.dataIntervalUs =
|
||||
static_cast<uint32_t>(pcm_config.dataIntervalUs);
|
||||
return hidl_pcm_config;
|
||||
}
|
||||
|
||||
inline Lc3Config_2_1 to_hidl_lc3_config_2_1(
|
||||
const Lc3Configuration& lc3_config) {
|
||||
Lc3Config_2_1 hidl_lc3_config;
|
||||
hidl_lc3_config.pcmBitDepth = to_hidl_bits_per_sample(lc3_config.pcmBitDepth);
|
||||
hidl_lc3_config.samplingFrequency =
|
||||
to_hidl_sample_rate_2_1(lc3_config.samplingFrequencyHz);
|
||||
if (lc3_config.samplingFrequencyHz == 10000)
|
||||
hidl_lc3_config.frameDuration = Lc3FrameDuration_2_1::DURATION_10000US;
|
||||
else if (lc3_config.samplingFrequencyHz == 7500)
|
||||
hidl_lc3_config.frameDuration = Lc3FrameDuration_2_1::DURATION_7500US;
|
||||
hidl_lc3_config.octetsPerFrame =
|
||||
static_cast<uint32_t>(lc3_config.octetsPerFrame);
|
||||
hidl_lc3_config.blocksPerSdu = static_cast<uint32_t>(lc3_config.blocksPerSdu);
|
||||
return hidl_lc3_config;
|
||||
}
|
||||
|
||||
inline Lc3CodecConfig_2_1 to_hidl_leaudio_config_2_1(
|
||||
const LeAudioConfiguration& unicast_config) {
|
||||
Lc3CodecConfig_2_1 hidl_lc3_codec_config = {
|
||||
.audioChannelAllocation = 0,
|
||||
};
|
||||
if (unicast_config.leAudioCodecConfig.getTag() ==
|
||||
LeAudioCodecConfiguration::lc3Config) {
|
||||
LOG(FATAL) << __func__ << ": unexpected codec type(vendor?)";
|
||||
}
|
||||
auto& le_codec_config = unicast_config.leAudioCodecConfig
|
||||
.get<LeAudioCodecConfiguration::lc3Config>();
|
||||
|
||||
hidl_lc3_codec_config.lc3Config = to_hidl_lc3_config_2_1(le_codec_config);
|
||||
|
||||
for (const auto& map : unicast_config.streamMap) {
|
||||
hidl_lc3_codec_config.audioChannelAllocation |= map.audioChannelAllocation;
|
||||
}
|
||||
return hidl_lc3_codec_config;
|
||||
}
|
||||
|
||||
inline Lc3CodecConfig_2_1 to_hidl_leaudio_broadcast_config_2_1(
|
||||
const LeAudioBroadcastConfiguration& broadcast_config) {
|
||||
Lc3CodecConfig_2_1 hidl_lc3_codec_config = {
|
||||
.audioChannelAllocation = 0,
|
||||
};
|
||||
// NOTE: Broadcast is not officially supported in HIDL
|
||||
if (broadcast_config.streamMap.empty()) {
|
||||
return hidl_lc3_codec_config;
|
||||
}
|
||||
if (broadcast_config.streamMap[0].leAudioCodecConfig.getTag() !=
|
||||
LeAudioCodecConfiguration::lc3Config) {
|
||||
LOG(FATAL) << __func__ << ": unexpected codec type(vendor?)";
|
||||
}
|
||||
auto& le_codec_config =
|
||||
broadcast_config.streamMap[0]
|
||||
.leAudioCodecConfig.get<LeAudioCodecConfiguration::lc3Config>();
|
||||
hidl_lc3_codec_config.lc3Config = to_hidl_lc3_config_2_1(le_codec_config);
|
||||
|
||||
for (const auto& map : broadcast_config.streamMap) {
|
||||
hidl_lc3_codec_config.audioChannelAllocation |= map.audioChannelAllocation;
|
||||
}
|
||||
return hidl_lc3_codec_config;
|
||||
}
|
||||
|
||||
inline AudioConfig_2_1 to_hidl_audio_config_2_1(
|
||||
const AudioConfiguration& audio_config) {
|
||||
AudioConfig_2_1 hidl_audio_config;
|
||||
switch (audio_config.getTag()) {
|
||||
case AudioConfiguration::pcmConfig:
|
||||
hidl_audio_config.pcmConfig(to_hidl_pcm_config_2_1(
|
||||
audio_config.get<AudioConfiguration::pcmConfig>()));
|
||||
break;
|
||||
case AudioConfiguration::a2dpConfig:
|
||||
hidl_audio_config.codecConfig(to_hidl_codec_config_2_0(
|
||||
audio_config.get<AudioConfiguration::a2dpConfig>()));
|
||||
break;
|
||||
case AudioConfiguration::leAudioConfig:
|
||||
hidl_audio_config.leAudioCodecConfig(to_hidl_leaudio_config_2_1(
|
||||
audio_config.get<AudioConfiguration::leAudioConfig>()));
|
||||
break;
|
||||
case AudioConfiguration::leAudioBroadcastConfig:
|
||||
hidl_audio_config.leAudioCodecConfig(to_hidl_leaudio_broadcast_config_2_1(
|
||||
audio_config.get<AudioConfiguration::leAudioBroadcastConfig>()));
|
||||
break;
|
||||
}
|
||||
return hidl_audio_config;
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* 2.0
|
||||
*
|
||||
***/
|
||||
|
||||
bool HidlToAidlMiddleware_2_0::IsSessionReady(
|
||||
const SessionType_2_0& session_type) {
|
||||
return BluetoothAudioSessionControl::IsSessionReady(
|
||||
from_session_type_2_0(session_type));
|
||||
}
|
||||
|
||||
uint16_t HidlToAidlMiddleware_2_0::RegisterControlResultCback(
|
||||
const SessionType_2_0& session_type,
|
||||
const PortStatusCallbacks_2_0& cbacks) {
|
||||
LOG(INFO) << __func__ << ": " << toString(session_type);
|
||||
auto aidl_session_type = from_session_type_2_0(session_type);
|
||||
// Pass the exact reference to the lambda
|
||||
auto& session_legacy_callback_table =
|
||||
legacy_callback_table[aidl_session_type];
|
||||
PortStatusCallbacks aidl_callbacks{};
|
||||
if (cbacks.control_result_cb_) {
|
||||
aidl_callbacks.control_result_cb_ =
|
||||
[&session_legacy_callback_table](uint16_t cookie, bool start_resp,
|
||||
const BluetoothAudioStatus& status) {
|
||||
if (session_legacy_callback_table.find(cookie) ==
|
||||
session_legacy_callback_table.end()) {
|
||||
LOG(ERROR) << __func__ << ": Unknown callback invoked!";
|
||||
return;
|
||||
}
|
||||
auto& cback = session_legacy_callback_table[cookie];
|
||||
cback->control_result_cb_(cookie, start_resp, to_hidl_status(status));
|
||||
};
|
||||
}
|
||||
if (cbacks.session_changed_cb_) {
|
||||
aidl_callbacks.session_changed_cb_ =
|
||||
[&session_legacy_callback_table](uint16_t cookie) {
|
||||
if (session_legacy_callback_table.find(cookie) ==
|
||||
session_legacy_callback_table.end()) {
|
||||
LOG(ERROR) << __func__ << ": Unknown callback invoked!";
|
||||
return;
|
||||
}
|
||||
auto& cback = session_legacy_callback_table[cookie];
|
||||
cback->session_changed_cb_(cookie);
|
||||
};
|
||||
};
|
||||
auto cookie = BluetoothAudioSessionControl::RegisterControlResultCback(
|
||||
aidl_session_type, aidl_callbacks);
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(legacy_callback_lock);
|
||||
session_legacy_callback_table[cookie] =
|
||||
std::make_shared<PortStatusCallbacks_2_0>(cbacks);
|
||||
}
|
||||
return cookie;
|
||||
}
|
||||
|
||||
void HidlToAidlMiddleware_2_0::UnregisterControlResultCback(
|
||||
const SessionType_2_0& session_type, uint16_t cookie) {
|
||||
LOG(INFO) << __func__ << ": " << toString(session_type);
|
||||
auto aidl_session_type = from_session_type_2_0(session_type);
|
||||
BluetoothAudioSessionControl::UnregisterControlResultCback(aidl_session_type,
|
||||
cookie);
|
||||
auto& session_callback_table = legacy_callback_table[aidl_session_type];
|
||||
if (session_callback_table.find(cookie) != session_callback_table.end()) {
|
||||
std::lock_guard<std::mutex> guard(legacy_callback_lock);
|
||||
session_callback_table.erase(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
const AudioConfig_2_0 HidlToAidlMiddleware_2_0::GetAudioConfig(
|
||||
const SessionType_2_0& session_type) {
|
||||
return to_hidl_audio_config_2_0(BluetoothAudioSessionControl::GetAudioConfig(
|
||||
from_session_type_2_0(session_type)));
|
||||
}
|
||||
|
||||
bool HidlToAidlMiddleware_2_0::StartStream(
|
||||
const SessionType_2_0& session_type) {
|
||||
return BluetoothAudioSessionControl::StartStream(
|
||||
from_session_type_2_0(session_type));
|
||||
}
|
||||
|
||||
void HidlToAidlMiddleware_2_0::StopStream(const SessionType_2_0& session_type) {
|
||||
return BluetoothAudioSessionControl::StopStream(
|
||||
from_session_type_2_0(session_type));
|
||||
}
|
||||
|
||||
bool HidlToAidlMiddleware_2_0::SuspendStream(
|
||||
const SessionType_2_0& session_type) {
|
||||
return BluetoothAudioSessionControl::SuspendStream(
|
||||
from_session_type_2_0(session_type));
|
||||
}
|
||||
|
||||
bool HidlToAidlMiddleware_2_0::GetPresentationPosition(
|
||||
const SessionType_2_0& session_type, uint64_t* remote_delay_report_ns,
|
||||
uint64_t* total_bytes_readed, timespec* data_position) {
|
||||
PresentationPosition presentation_position;
|
||||
auto ret_val = BluetoothAudioSessionControl::GetPresentationPosition(
|
||||
from_session_type_2_0(session_type), presentation_position);
|
||||
if (remote_delay_report_ns)
|
||||
*remote_delay_report_ns = presentation_position.remoteDeviceAudioDelayNanos;
|
||||
if (total_bytes_readed)
|
||||
*total_bytes_readed = presentation_position.transmittedOctets;
|
||||
if (data_position)
|
||||
*data_position = {
|
||||
.tv_sec = static_cast<__kernel_old_time_t>(
|
||||
presentation_position.transmittedOctetsTimestamp.tvSec),
|
||||
.tv_nsec = static_cast<long>(
|
||||
presentation_position.transmittedOctetsTimestamp.tvNSec)};
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
void HidlToAidlMiddleware_2_0::UpdateTracksMetadata(
|
||||
const SessionType_2_0& session_type,
|
||||
const struct source_metadata* source_metadata) {
|
||||
return BluetoothAudioSessionControl::UpdateSourceMetadata(
|
||||
from_session_type_2_0(session_type), *source_metadata);
|
||||
}
|
||||
|
||||
size_t HidlToAidlMiddleware_2_0::OutWritePcmData(
|
||||
const SessionType_2_0& session_type, const void* buffer, size_t bytes) {
|
||||
return BluetoothAudioSessionControl::OutWritePcmData(
|
||||
from_session_type_2_0(session_type), buffer, bytes);
|
||||
}
|
||||
|
||||
size_t HidlToAidlMiddleware_2_0::InReadPcmData(
|
||||
const SessionType_2_0& session_type, void* buffer, size_t bytes) {
|
||||
return BluetoothAudioSessionControl::InReadPcmData(
|
||||
from_session_type_2_0(session_type), buffer, bytes);
|
||||
}
|
||||
|
||||
bool HidlToAidlMiddleware_2_0::IsAidlAvailable() {
|
||||
return BluetoothAudioSession::IsAidlAvailable();
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* 2.1
|
||||
*
|
||||
***/
|
||||
|
||||
const AudioConfig_2_1 HidlToAidlMiddleware_2_1::GetAudioConfig(
|
||||
const SessionType_2_1& session_type) {
|
||||
return to_hidl_audio_config_2_1(BluetoothAudioSessionControl::GetAudioConfig(
|
||||
from_session_type_2_1(session_type)));
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#include <android/hardware/bluetooth/audio/2.0/types.h>
|
||||
|
||||
#include "../session/BluetoothAudioSession.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
using SessionType_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::SessionType;
|
||||
using PortStatusCallbacks_2_0 =
|
||||
::android::bluetooth::audio::PortStatusCallbacks;
|
||||
using AudioConfig_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
|
||||
|
||||
class HidlToAidlMiddleware_2_0 {
|
||||
public:
|
||||
static bool IsAidlAvailable();
|
||||
|
||||
static bool IsSessionReady(const SessionType_2_0& session_type);
|
||||
|
||||
static uint16_t RegisterControlResultCback(
|
||||
const SessionType_2_0& session_type,
|
||||
const PortStatusCallbacks_2_0& cbacks);
|
||||
|
||||
static void UnregisterControlResultCback(const SessionType_2_0& session_type,
|
||||
uint16_t cookie);
|
||||
|
||||
static const AudioConfig_2_0 GetAudioConfig(
|
||||
const SessionType_2_0& session_type);
|
||||
|
||||
static bool StartStream(const SessionType_2_0& session_type);
|
||||
|
||||
static void StopStream(const SessionType_2_0& session_type);
|
||||
|
||||
static bool SuspendStream(const SessionType_2_0& session_type);
|
||||
|
||||
static bool GetPresentationPosition(const SessionType_2_0& session_type,
|
||||
uint64_t* remote_delay_report_ns,
|
||||
uint64_t* total_bytes_readed,
|
||||
timespec* data_position);
|
||||
|
||||
static void UpdateTracksMetadata(
|
||||
const SessionType_2_0& session_type,
|
||||
const struct source_metadata* source_metadata);
|
||||
|
||||
static size_t OutWritePcmData(const SessionType_2_0& session_type,
|
||||
const void* buffer, size_t bytes);
|
||||
|
||||
static size_t InReadPcmData(const SessionType_2_0& session_type, void* buffer,
|
||||
size_t bytes);
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#include <android/hardware/bluetooth/audio/2.1/types.h>
|
||||
|
||||
#include "../session/BluetoothAudioSession.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
using SessionType_2_1 =
|
||||
::android::hardware::bluetooth::audio::V2_1::SessionType;
|
||||
using AudioConfig_2_1 =
|
||||
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration;
|
||||
|
||||
class HidlToAidlMiddleware_2_1 {
|
||||
public:
|
||||
static const AudioConfig_2_1 GetAudioConfig(
|
||||
const SessionType_2_1& session_type);
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
498
bluetooth/audio/utils/session/BluetoothAudioSession.cpp
Normal file
498
bluetooth/audio/utils/session/BluetoothAudioSession.cpp
Normal file
@ -0,0 +1,498 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "BTAudioProviderSession"
|
||||
|
||||
#include "BluetoothAudioSession.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include "../aidl_session/HidlToAidlMiddleware_2_0.h"
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
using ::aidl::android::hardware::bluetooth::audio::HidlToAidlMiddleware_2_0;
|
||||
using ::android::hardware::audio::common::V5_0::AudioContentType;
|
||||
using ::android::hardware::audio::common::V5_0::AudioUsage;
|
||||
using ::android::hardware::audio::common::V5_0::PlaybackTrackMetadata;
|
||||
using ::android::hardware::audio::common::V5_0::SourceMetadata;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::CodecType;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::TimeSpec;
|
||||
|
||||
const CodecConfiguration BluetoothAudioSession::kInvalidCodecConfiguration = {
|
||||
.codecType = CodecType::UNKNOWN,
|
||||
.encodedAudioBitrate = 0x00000000,
|
||||
.peerMtu = 0xffff,
|
||||
.isScmstEnabled = false,
|
||||
.config = {}};
|
||||
AudioConfiguration BluetoothAudioSession::invalidSoftwareAudioConfiguration =
|
||||
{};
|
||||
AudioConfiguration BluetoothAudioSession::invalidOffloadAudioConfiguration = {};
|
||||
|
||||
static constexpr int kFmqSendTimeoutMs = 1000; // 1000 ms timeout for sending
|
||||
static constexpr int kFmqReceiveTimeoutMs =
|
||||
1000; // 1000 ms timeout for receiving
|
||||
static constexpr int kWritePollMs = 1; // polled non-blocking interval
|
||||
static constexpr int kReadPollMs = 1; // polled non-blocking interval
|
||||
|
||||
static inline timespec timespec_convert_from_hal(const TimeSpec& TS) {
|
||||
return {.tv_sec = static_cast<long>(TS.tvSec),
|
||||
.tv_nsec = static_cast<long>(TS.tvNSec)};
|
||||
}
|
||||
|
||||
BluetoothAudioSession::BluetoothAudioSession(const SessionType& session_type)
|
||||
: session_type_(session_type), stack_iface_(nullptr), mDataMQ(nullptr) {
|
||||
invalidSoftwareAudioConfiguration.pcmConfig(kInvalidPcmParameters);
|
||||
invalidOffloadAudioConfiguration.codecConfig(kInvalidCodecConfiguration);
|
||||
}
|
||||
|
||||
// The report function is used to report that the Bluetooth stack has started
|
||||
// this session without any failure, and will invoke session_changed_cb_ to
|
||||
// notify those registered bluetooth_audio outputs
|
||||
void BluetoothAudioSession::OnSessionStarted(
|
||||
const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
|
||||
const AudioConfiguration& audio_config) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (stack_iface == nullptr) {
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", IBluetoothAudioPort Invalid";
|
||||
} else if (!UpdateAudioConfig(audio_config)) {
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", AudioConfiguration=" << toString(audio_config)
|
||||
<< " Invalid";
|
||||
} else if (!UpdateDataPath(dataMQ)) {
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " DataMQ Invalid";
|
||||
audio_config_ =
|
||||
(session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
|
||||
? kInvalidOffloadAudioConfiguration
|
||||
: kInvalidSoftwareAudioConfiguration);
|
||||
} else {
|
||||
stack_iface_ = stack_iface;
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", AudioConfiguration=" << toString(audio_config);
|
||||
ReportSessionStatus();
|
||||
}
|
||||
}
|
||||
|
||||
// The report function is used to report that the Bluetooth stack has ended the
|
||||
// session, and will invoke session_changed_cb_ to notify registered
|
||||
// bluetooth_audio outputs
|
||||
void BluetoothAudioSession::OnSessionEnded() {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
bool toggled = IsSessionReady();
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
|
||||
audio_config_ = (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
|
||||
? kInvalidOffloadAudioConfiguration
|
||||
: kInvalidSoftwareAudioConfiguration);
|
||||
stack_iface_ = nullptr;
|
||||
UpdateDataPath(nullptr);
|
||||
if (toggled) {
|
||||
ReportSessionStatus();
|
||||
}
|
||||
}
|
||||
|
||||
// invoking the registered session_changed_cb_
|
||||
void BluetoothAudioSession::ReportSessionStatus() {
|
||||
// This is locked already by OnSessionStarted / OnSessionEnded
|
||||
if (observers_.empty()) {
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO port state observer";
|
||||
return;
|
||||
}
|
||||
for (auto& observer : observers_) {
|
||||
uint16_t cookie = observer.first;
|
||||
std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " notify to bluetooth_audio=0x"
|
||||
<< android::base::StringPrintf("%04x", cookie);
|
||||
cb->session_changed_cb_(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
// The report function is used to report that the Bluetooth stack has notified
|
||||
// the result of startStream or suspendStream, and will invoke
|
||||
// control_result_cb_ to notify registered bluetooth_audio outputs
|
||||
void BluetoothAudioSession::ReportControlStatus(
|
||||
bool start_resp, const BluetoothAudioStatus& status) {
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (observers_.empty()) {
|
||||
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO port state observer";
|
||||
return;
|
||||
}
|
||||
for (auto& observer : observers_) {
|
||||
uint16_t cookie = observer.first;
|
||||
std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
|
||||
LOG(INFO) << __func__ << " - status=" << toString(status)
|
||||
<< " for SessionType=" << toString(session_type_)
|
||||
<< ", bluetooth_audio=0x"
|
||||
<< android::base::StringPrintf("%04x", cookie)
|
||||
<< (start_resp ? " started" : " suspended");
|
||||
cb->control_result_cb_(cookie, start_resp, status);
|
||||
}
|
||||
}
|
||||
|
||||
// The function helps to check if this session is ready or not
|
||||
// @return: true if the Bluetooth stack has started the specified session
|
||||
bool BluetoothAudioSession::IsSessionReady() {
|
||||
if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
|
||||
return HidlToAidlMiddleware_2_0::IsSessionReady(session_type_);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
bool dataMQ_valid =
|
||||
(session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
|
||||
(mDataMQ != nullptr && mDataMQ->isValid()));
|
||||
return stack_iface_ != nullptr && dataMQ_valid;
|
||||
}
|
||||
|
||||
bool BluetoothAudioSession::UpdateDataPath(const DataMQ::Descriptor* dataMQ) {
|
||||
if (dataMQ == nullptr) {
|
||||
// usecase of reset by nullptr
|
||||
mDataMQ = nullptr;
|
||||
return true;
|
||||
}
|
||||
std::unique_ptr<DataMQ> tempDataMQ;
|
||||
tempDataMQ.reset(new DataMQ(*dataMQ));
|
||||
if (!tempDataMQ || !tempDataMQ->isValid()) {
|
||||
mDataMQ = nullptr;
|
||||
return false;
|
||||
}
|
||||
mDataMQ = std::move(tempDataMQ);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothAudioSession::UpdateAudioConfig(
|
||||
const AudioConfiguration& audio_config) {
|
||||
bool is_software_session =
|
||||
(session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
|
||||
bool is_offload_session =
|
||||
(session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
|
||||
auto audio_config_discriminator = audio_config.getDiscriminator();
|
||||
bool is_software_audio_config =
|
||||
(is_software_session &&
|
||||
audio_config_discriminator ==
|
||||
AudioConfiguration::hidl_discriminator::pcmConfig);
|
||||
bool is_offload_audio_config =
|
||||
(is_offload_session &&
|
||||
audio_config_discriminator ==
|
||||
AudioConfiguration::hidl_discriminator::codecConfig);
|
||||
if (!is_software_audio_config && !is_offload_audio_config) {
|
||||
return false;
|
||||
}
|
||||
audio_config_ = audio_config;
|
||||
return true;
|
||||
}
|
||||
|
||||
// The control function helps the bluetooth_audio module to register
|
||||
// PortStatusCallbacks
|
||||
// @return: cookie - the assigned number to this bluetooth_audio output
|
||||
uint16_t BluetoothAudioSession::RegisterStatusCback(
|
||||
const PortStatusCallbacks& cbacks) {
|
||||
if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
|
||||
return HidlToAidlMiddleware_2_0::RegisterControlResultCback(session_type_,
|
||||
cbacks);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
uint16_t cookie = ObserversCookieGetInitValue(session_type_);
|
||||
uint16_t cookie_upper_bound = ObserversCookieGetUpperBound(session_type_);
|
||||
|
||||
while (cookie < cookie_upper_bound) {
|
||||
if (observers_.find(cookie) == observers_.end()) {
|
||||
break;
|
||||
}
|
||||
++cookie;
|
||||
}
|
||||
if (cookie >= cookie_upper_bound) {
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has " << observers_.size()
|
||||
<< " observers already (No Resource)";
|
||||
return kObserversCookieUndefined;
|
||||
}
|
||||
std::shared_ptr<struct PortStatusCallbacks> cb =
|
||||
std::make_shared<struct PortStatusCallbacks>();
|
||||
*cb = cbacks;
|
||||
observers_[cookie] = cb;
|
||||
return cookie;
|
||||
}
|
||||
|
||||
// The control function helps the bluetooth_audio module to unregister
|
||||
// PortStatusCallbacks
|
||||
// @param: cookie - indicates which bluetooth_audio output is
|
||||
void BluetoothAudioSession::UnregisterStatusCback(uint16_t cookie) {
|
||||
if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
|
||||
return HidlToAidlMiddleware_2_0::UnregisterControlResultCback(session_type_,
|
||||
cookie);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (observers_.erase(cookie) != 1) {
|
||||
LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " no such provider=0x"
|
||||
<< android::base::StringPrintf("%04x", cookie);
|
||||
}
|
||||
}
|
||||
|
||||
// The control function is for the bluetooth_audio module to get the current
|
||||
// AudioConfiguration
|
||||
const AudioConfiguration& BluetoothAudioSession::GetAudioConfig() {
|
||||
if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
|
||||
return (audio_config_ =
|
||||
HidlToAidlMiddleware_2_0::GetAudioConfig(session_type_));
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (IsSessionReady()) {
|
||||
return audio_config_;
|
||||
} else if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
|
||||
return kInvalidOffloadAudioConfiguration;
|
||||
} else {
|
||||
return kInvalidSoftwareAudioConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
// Those control functions are for the bluetooth_audio module to start, suspend,
|
||||
// stop stream, to check position, and to update metadata.
|
||||
bool BluetoothAudioSession::StartStream() {
|
||||
if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
|
||||
return HidlToAidlMiddleware_2_0::StartStream(session_type_);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return false;
|
||||
}
|
||||
auto hal_retval = stack_iface_->startStream();
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothAudioSession::SuspendStream() {
|
||||
if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
|
||||
return HidlToAidlMiddleware_2_0::SuspendStream(session_type_);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return false;
|
||||
}
|
||||
auto hal_retval = stack_iface_->suspendStream();
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::StopStream() {
|
||||
if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
|
||||
return HidlToAidlMiddleware_2_0::StopStream(session_type_);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
return;
|
||||
}
|
||||
auto hal_retval = stack_iface_->stopStream();
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
}
|
||||
}
|
||||
|
||||
bool BluetoothAudioSession::GetPresentationPosition(
|
||||
uint64_t* remote_delay_report_ns, uint64_t* total_bytes_readed,
|
||||
timespec* data_position) {
|
||||
if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
|
||||
return HidlToAidlMiddleware_2_0::GetPresentationPosition(
|
||||
session_type_, remote_delay_report_ns, total_bytes_readed,
|
||||
data_position);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return false;
|
||||
}
|
||||
bool retval = false;
|
||||
auto hal_retval = stack_iface_->getPresentationPosition(
|
||||
[&retval, &remote_delay_report_ns, &total_bytes_readed, &data_position](
|
||||
BluetoothAudioStatus status,
|
||||
const uint64_t& remoteDeviceAudioDelayNanos,
|
||||
uint64_t transmittedOctets,
|
||||
const TimeSpec& transmittedOctetsTimeStamp) {
|
||||
if (status == BluetoothAudioStatus::SUCCESS) {
|
||||
if (remote_delay_report_ns)
|
||||
*remote_delay_report_ns = remoteDeviceAudioDelayNanos;
|
||||
if (total_bytes_readed) *total_bytes_readed = transmittedOctets;
|
||||
if (data_position)
|
||||
*data_position =
|
||||
timespec_convert_from_hal(transmittedOctetsTimeStamp);
|
||||
retval = true;
|
||||
}
|
||||
});
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
return false;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void BluetoothAudioSession::UpdateTracksMetadata(
|
||||
const struct source_metadata* source_metadata) {
|
||||
if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
|
||||
return HidlToAidlMiddleware_2_0::UpdateTracksMetadata(session_type_,
|
||||
source_metadata);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex_);
|
||||
if (!IsSessionReady()) {
|
||||
LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< " has NO session";
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t track_count = source_metadata->track_count;
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ", "
|
||||
<< track_count << " track(s)";
|
||||
if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct playback_track_metadata* track = source_metadata->tracks;
|
||||
SourceMetadata sourceMetadata;
|
||||
PlaybackTrackMetadata* halMetadata;
|
||||
|
||||
sourceMetadata.tracks.resize(track_count);
|
||||
halMetadata = sourceMetadata.tracks.data();
|
||||
while (track_count && track) {
|
||||
halMetadata->usage = static_cast<AudioUsage>(track->usage);
|
||||
halMetadata->contentType =
|
||||
static_cast<AudioContentType>(track->content_type);
|
||||
halMetadata->gain = track->gain;
|
||||
LOG(VERBOSE) << __func__ << " - SessionType=" << toString(session_type_)
|
||||
<< ", usage=" << toString(halMetadata->usage)
|
||||
<< ", content=" << toString(halMetadata->contentType)
|
||||
<< ", gain=" << halMetadata->gain;
|
||||
--track_count;
|
||||
++track;
|
||||
++halMetadata;
|
||||
}
|
||||
auto hal_retval = stack_iface_->updateMetadata(sourceMetadata);
|
||||
if (!hal_retval.isOk()) {
|
||||
LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
|
||||
<< toString(session_type_) << " failed";
|
||||
}
|
||||
}
|
||||
|
||||
// The control function writes stream to FMQ
|
||||
size_t BluetoothAudioSession::OutWritePcmData(const void* buffer,
|
||||
size_t bytes) {
|
||||
if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
|
||||
return HidlToAidlMiddleware_2_0::OutWritePcmData(session_type_, buffer,
|
||||
bytes);
|
||||
if (buffer == nullptr || !bytes) return 0;
|
||||
size_t totalWritten = 0;
|
||||
int ms_timeout = kFmqSendTimeoutMs;
|
||||
do {
|
||||
std::unique_lock<std::recursive_mutex> lock(mutex_);
|
||||
if (!IsSessionReady()) break;
|
||||
size_t availableToWrite = mDataMQ->availableToWrite();
|
||||
if (availableToWrite) {
|
||||
if (availableToWrite > (bytes - totalWritten)) {
|
||||
availableToWrite = bytes - totalWritten;
|
||||
}
|
||||
|
||||
if (!mDataMQ->write(static_cast<const uint8_t*>(buffer) + totalWritten,
|
||||
availableToWrite)) {
|
||||
ALOGE("FMQ datapath writting %zu/%zu failed", totalWritten, bytes);
|
||||
return totalWritten;
|
||||
}
|
||||
totalWritten += availableToWrite;
|
||||
} else if (ms_timeout >= kWritePollMs) {
|
||||
lock.unlock();
|
||||
usleep(kWritePollMs * 1000);
|
||||
ms_timeout -= kWritePollMs;
|
||||
} else {
|
||||
ALOGD("data %zu/%zu overflow %d ms", totalWritten, bytes,
|
||||
(kFmqSendTimeoutMs - ms_timeout));
|
||||
return totalWritten;
|
||||
}
|
||||
} while (totalWritten < bytes);
|
||||
return totalWritten;
|
||||
}
|
||||
|
||||
// The control function reads stream from FMQ
|
||||
size_t BluetoothAudioSession::InReadPcmData(void* buffer, size_t bytes) {
|
||||
if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
|
||||
return HidlToAidlMiddleware_2_0::InReadPcmData(session_type_, buffer,
|
||||
bytes);
|
||||
if (buffer == nullptr || !bytes) return 0;
|
||||
size_t totalRead = 0;
|
||||
int ms_timeout = kFmqReceiveTimeoutMs;
|
||||
do {
|
||||
std::unique_lock<std::recursive_mutex> lock(mutex_);
|
||||
if (!IsSessionReady()) break;
|
||||
size_t availableToRead = mDataMQ->availableToRead();
|
||||
if (availableToRead) {
|
||||
if (availableToRead > (bytes - totalRead)) {
|
||||
availableToRead = bytes - totalRead;
|
||||
}
|
||||
if (!mDataMQ->read(static_cast<uint8_t*>(buffer) + totalRead,
|
||||
availableToRead)) {
|
||||
ALOGE("FMQ datapath reading %zu/%zu failed", totalRead, bytes);
|
||||
return totalRead;
|
||||
}
|
||||
totalRead += availableToRead;
|
||||
} else if (ms_timeout >= kReadPollMs) {
|
||||
lock.unlock();
|
||||
usleep(kReadPollMs * 1000);
|
||||
ms_timeout -= kReadPollMs;
|
||||
continue;
|
||||
} else {
|
||||
ALOGD("in data %zu/%zu overflow %d ms", totalRead, bytes,
|
||||
(kFmqReceiveTimeoutMs - ms_timeout));
|
||||
return totalRead;
|
||||
}
|
||||
} while (totalRead < bytes);
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
std::unique_ptr<BluetoothAudioSessionInstance>
|
||||
BluetoothAudioSessionInstance::instance_ptr =
|
||||
std::unique_ptr<BluetoothAudioSessionInstance>(
|
||||
new BluetoothAudioSessionInstance());
|
||||
|
||||
// API to fetch the session of A2DP / Hearing Aid
|
||||
std::shared_ptr<BluetoothAudioSession>
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(
|
||||
const SessionType& session_type) {
|
||||
std::lock_guard<std::mutex> guard(instance_ptr->mutex_);
|
||||
if (!instance_ptr->sessions_map_.empty()) {
|
||||
auto entry = instance_ptr->sessions_map_.find(session_type);
|
||||
if (entry != instance_ptr->sessions_map_.end()) {
|
||||
return entry->second;
|
||||
}
|
||||
}
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
std::make_shared<BluetoothAudioSession>(session_type);
|
||||
instance_ptr->sessions_map_[session_type] = session_ptr;
|
||||
return session_ptr;
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
191
bluetooth/audio/utils/session/BluetoothAudioSession.h
Normal file
191
bluetooth/audio/utils/session/BluetoothAudioSession.h
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
|
||||
#include <fmq/MessageQueue.h>
|
||||
#include <hardware/audio.h>
|
||||
#include <hidl/MQDescriptor.h>
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::hardware::kSynchronizedReadWrite;
|
||||
using ::android::hardware::MessageQueue;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::SessionType;
|
||||
|
||||
using BluetoothAudioStatus =
|
||||
::android::hardware::bluetooth::audio::V2_0::Status;
|
||||
|
||||
using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
|
||||
|
||||
static constexpr uint16_t kObserversCookieSize = 0x0010; // 0x0000 ~ 0x000f
|
||||
constexpr uint16_t kObserversCookieUndefined =
|
||||
(static_cast<uint16_t>(SessionType::UNKNOWN) << 8 & 0xff00);
|
||||
inline SessionType ObserversCookieGetSessionType(uint16_t cookie) {
|
||||
return static_cast<SessionType>(cookie >> 8 & 0x00ff);
|
||||
}
|
||||
inline uint16_t ObserversCookieGetInitValue(SessionType session_type) {
|
||||
return (static_cast<uint16_t>(session_type) << 8 & 0xff00);
|
||||
}
|
||||
inline uint16_t ObserversCookieGetUpperBound(SessionType session_type) {
|
||||
return (static_cast<uint16_t>(session_type) << 8 & 0xff00) +
|
||||
kObserversCookieSize;
|
||||
}
|
||||
|
||||
// This presents the callbacks of started / suspended and session changed,
|
||||
// and the bluetooth_audio module uses to receive the status notification
|
||||
struct PortStatusCallbacks {
|
||||
// control_result_cb_ - when the Bluetooth stack reports results of
|
||||
// streamStarted or streamSuspended, the BluetoothAudioProvider will invoke
|
||||
// this callback to report to the bluetooth_audio module.
|
||||
// @param: cookie - indicates which bluetooth_audio output should handle
|
||||
// @param: start_resp - this report is for startStream or not
|
||||
// @param: status - the result of startStream
|
||||
std::function<void(uint16_t cookie, bool start_resp,
|
||||
const BluetoothAudioStatus& status)>
|
||||
control_result_cb_;
|
||||
// session_changed_cb_ - when the Bluetooth stack start / end session, the
|
||||
// BluetoothAudioProvider will invoke this callback to notify to the
|
||||
// bluetooth_audio module.
|
||||
// @param: cookie - indicates which bluetooth_audio output should handle
|
||||
std::function<void(uint16_t cookie)> session_changed_cb_;
|
||||
};
|
||||
|
||||
class BluetoothAudioSession {
|
||||
friend class BluetoothAudioSession_2_1;
|
||||
friend class BluetoothAudioSession_2_2;
|
||||
|
||||
private:
|
||||
// using recursive_mutex to allow hwbinder to re-enter agian.
|
||||
std::recursive_mutex mutex_;
|
||||
SessionType session_type_;
|
||||
|
||||
// audio control path to use for both software and offloading
|
||||
sp<IBluetoothAudioPort> stack_iface_;
|
||||
// audio data path (FMQ) for software encoding
|
||||
std::unique_ptr<DataMQ> mDataMQ;
|
||||
// audio data configuration for both software and offloading
|
||||
AudioConfiguration audio_config_;
|
||||
|
||||
static AudioConfiguration invalidSoftwareAudioConfiguration;
|
||||
static AudioConfiguration invalidOffloadAudioConfiguration;
|
||||
|
||||
// saving those registered bluetooth_audio's callbacks
|
||||
std::unordered_map<uint16_t, std::shared_ptr<struct PortStatusCallbacks>>
|
||||
observers_;
|
||||
|
||||
bool UpdateDataPath(const DataMQ::Descriptor* dataMQ);
|
||||
bool UpdateAudioConfig(const AudioConfiguration& audio_config);
|
||||
// invoking the registered session_changed_cb_
|
||||
void ReportSessionStatus();
|
||||
|
||||
public:
|
||||
BluetoothAudioSession(const SessionType& session_type);
|
||||
|
||||
// The function helps to check if this session is ready or not
|
||||
// @return: true if the Bluetooth stack has started the specified session
|
||||
bool IsSessionReady();
|
||||
|
||||
// The report function is used to report that the Bluetooth stack has started
|
||||
// this session without any failure, and will invoke session_changed_cb_ to
|
||||
// notify those registered bluetooth_audio outputs
|
||||
void OnSessionStarted(const sp<IBluetoothAudioPort> stack_iface,
|
||||
const DataMQ::Descriptor* dataMQ,
|
||||
const AudioConfiguration& audio_config);
|
||||
|
||||
// The report function is used to report that the Bluetooth stack has ended
|
||||
// the session, and will invoke session_changed_cb_ to notify registered
|
||||
// bluetooth_audio outputs
|
||||
void OnSessionEnded();
|
||||
|
||||
// The report function is used to report that the Bluetooth stack has notified
|
||||
// the result of startStream or suspendStream, and will invoke
|
||||
// control_result_cb_ to notify registered bluetooth_audio outputs
|
||||
void ReportControlStatus(bool start_resp, const BluetoothAudioStatus& status);
|
||||
|
||||
// The control function helps the bluetooth_audio module to register
|
||||
// PortStatusCallbacks
|
||||
// @return: cookie - the assigned number to this bluetooth_audio output
|
||||
uint16_t RegisterStatusCback(const PortStatusCallbacks& cbacks);
|
||||
|
||||
// The control function helps the bluetooth_audio module to unregister
|
||||
// PortStatusCallbacks
|
||||
// @param: cookie - indicates which bluetooth_audio output is
|
||||
void UnregisterStatusCback(uint16_t cookie);
|
||||
|
||||
// The control function is for the bluetooth_audio module to get the current
|
||||
// AudioConfiguration
|
||||
const AudioConfiguration& GetAudioConfig();
|
||||
|
||||
// Those control functions are for the bluetooth_audio module to start,
|
||||
// suspend, stop stream, to check position, and to update metadata.
|
||||
bool StartStream();
|
||||
bool SuspendStream();
|
||||
void StopStream();
|
||||
bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
|
||||
uint64_t* total_bytes_readed,
|
||||
timespec* data_position);
|
||||
void UpdateTracksMetadata(const struct source_metadata* source_metadata);
|
||||
|
||||
// The control function writes stream to FMQ
|
||||
size_t OutWritePcmData(const void* buffer, size_t bytes);
|
||||
// The control function read stream from FMQ
|
||||
size_t InReadPcmData(void* buffer, size_t bytes);
|
||||
|
||||
static constexpr PcmParameters kInvalidPcmParameters = {
|
||||
.sampleRate = SampleRate::RATE_UNKNOWN,
|
||||
.channelMode = ChannelMode::UNKNOWN,
|
||||
.bitsPerSample = BitsPerSample::BITS_UNKNOWN,
|
||||
};
|
||||
// can't be constexpr because of non-literal type
|
||||
static const CodecConfiguration kInvalidCodecConfiguration;
|
||||
|
||||
static constexpr AudioConfiguration& kInvalidSoftwareAudioConfiguration =
|
||||
invalidSoftwareAudioConfiguration;
|
||||
static constexpr AudioConfiguration& kInvalidOffloadAudioConfiguration =
|
||||
invalidOffloadAudioConfiguration;
|
||||
};
|
||||
|
||||
class BluetoothAudioSessionInstance {
|
||||
public:
|
||||
// The API is to fetch the specified session of A2DP / Hearing Aid
|
||||
static std::shared_ptr<BluetoothAudioSession> GetSessionInstance(
|
||||
const SessionType& session_type);
|
||||
|
||||
private:
|
||||
static std::unique_ptr<BluetoothAudioSessionInstance> instance_ptr;
|
||||
std::mutex mutex_;
|
||||
std::unordered_map<SessionType, std::shared_ptr<BluetoothAudioSession>>
|
||||
sessions_map_;
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
143
bluetooth/audio/utils/session/BluetoothAudioSessionControl.h
Normal file
143
bluetooth/audio/utils/session/BluetoothAudioSessionControl.h
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioSession.h"
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class BluetoothAudioSessionControl {
|
||||
public:
|
||||
// The control API helps to check if session is ready or not
|
||||
// @return: true if the Bluetooth stack has started th specified session
|
||||
static bool IsSessionReady(const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->IsSessionReady();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The control API helps the bluetooth_audio module to register
|
||||
// PortStatusCallbacks
|
||||
// @return: cookie - the assigned number to this bluetooth_audio output
|
||||
static uint16_t RegisterControlResultCback(
|
||||
const SessionType& session_type, const PortStatusCallbacks& cbacks) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->RegisterStatusCback(cbacks);
|
||||
}
|
||||
return kObserversCookieUndefined;
|
||||
}
|
||||
|
||||
// The control API helps the bluetooth_audio module to unregister
|
||||
// PortStatusCallbacks
|
||||
// @param: cookie - indicates which bluetooth_audio output is
|
||||
static void UnregisterControlResultCback(const SessionType& session_type,
|
||||
uint16_t cookie) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->UnregisterStatusCback(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
// The control API for the bluetooth_audio module to get current
|
||||
// AudioConfiguration
|
||||
static const AudioConfiguration& GetAudioConfig(
|
||||
const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetAudioConfig();
|
||||
} else if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
|
||||
return BluetoothAudioSession::kInvalidOffloadAudioConfiguration;
|
||||
} else {
|
||||
return BluetoothAudioSession::kInvalidSoftwareAudioConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
// Those control APIs for the bluetooth_audio module to start / suspend / stop
|
||||
// stream, to check position, and to update metadata.
|
||||
static bool StartStream(const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->StartStream();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool SuspendStream(const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->SuspendStream();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void StopStream(const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->StopStream();
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetPresentationPosition(const SessionType& session_type,
|
||||
uint64_t* remote_delay_report_ns,
|
||||
uint64_t* total_bytes_readed,
|
||||
timespec* data_position) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetPresentationPosition(
|
||||
remote_delay_report_ns, total_bytes_readed, data_position);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void UpdateTracksMetadata(
|
||||
const SessionType& session_type,
|
||||
const struct source_metadata* source_metadata) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->UpdateTracksMetadata(source_metadata);
|
||||
}
|
||||
}
|
||||
|
||||
// The control API writes stream to FMQ
|
||||
static size_t OutWritePcmData(const SessionType& session_type,
|
||||
const void* buffer, size_t bytes) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->OutWritePcmData(buffer, bytes);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
160
bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_1.h
Normal file
160
bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_1.h
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioSession_2_1.h"
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class BluetoothAudioSessionControl_2_1 {
|
||||
using SessionType_2_1 =
|
||||
::android::hardware::bluetooth::audio::V2_1::SessionType;
|
||||
using AudioConfiguration_2_1 =
|
||||
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration;
|
||||
|
||||
public:
|
||||
// The control API helps to check if session is ready or not
|
||||
// @return: true if the Bluetooth stack has started th specified session
|
||||
static bool IsSessionReady(const SessionType_2_1& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetAudioSession()->IsSessionReady();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The control API helps the bluetooth_audio module to register
|
||||
// PortStatusCallbacks
|
||||
// @return: cookie - the assigned number to this bluetooth_audio output
|
||||
static uint16_t RegisterControlResultCback(
|
||||
const SessionType_2_1& session_type, const PortStatusCallbacks& cbacks) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetAudioSession()->RegisterStatusCback(cbacks);
|
||||
}
|
||||
return kObserversCookieUndefined;
|
||||
}
|
||||
|
||||
// The control API helps the bluetooth_audio module to unregister
|
||||
// PortStatusCallbacks
|
||||
// @param: cookie - indicates which bluetooth_audio output is
|
||||
static void UnregisterControlResultCback(const SessionType_2_1& session_type,
|
||||
uint16_t cookie) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->GetAudioSession()->UnregisterStatusCback(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
// The control API for the bluetooth_audio module to get current
|
||||
// AudioConfiguration
|
||||
static const AudioConfiguration_2_1 GetAudioConfig(
|
||||
const SessionType_2_1& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetAudioConfig();
|
||||
} else if (session_type ==
|
||||
SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
|
||||
return BluetoothAudioSession_2_1::kInvalidOffloadAudioConfiguration;
|
||||
} else {
|
||||
return BluetoothAudioSession_2_1::kInvalidSoftwareAudioConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
// Those control APIs for the bluetooth_audio module to start / suspend / stop
|
||||
// stream, to check position, and to update metadata.
|
||||
static bool StartStream(const SessionType_2_1& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetAudioSession()->StartStream();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool SuspendStream(const SessionType_2_1& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetAudioSession()->SuspendStream();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void StopStream(const SessionType_2_1& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->GetAudioSession()->StopStream();
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetPresentationPosition(const SessionType_2_1& session_type,
|
||||
uint64_t* remote_delay_report_ns,
|
||||
uint64_t* total_bytes_readed,
|
||||
timespec* data_position) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetAudioSession()->GetPresentationPosition(
|
||||
remote_delay_report_ns, total_bytes_readed, data_position);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void UpdateTracksMetadata(
|
||||
const SessionType_2_1& session_type,
|
||||
const struct source_metadata* source_metadata) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->GetAudioSession()->UpdateTracksMetadata(source_metadata);
|
||||
}
|
||||
}
|
||||
|
||||
// The control API writes stream to FMQ
|
||||
static size_t OutWritePcmData(const SessionType_2_1& session_type,
|
||||
const void* buffer, size_t bytes) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetAudioSession()->OutWritePcmData(buffer, bytes);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The control API reads stream from FMQ
|
||||
static size_t InReadPcmData(const SessionType_2_1& session_type, void* buffer,
|
||||
size_t bytes) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
return session_ptr->GetAudioSession()->InReadPcmData(buffer, bytes);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
63
bluetooth/audio/utils/session/BluetoothAudioSessionReport.h
Normal file
63
bluetooth/audio/utils/session/BluetoothAudioSessionReport.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioSession.h"
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class BluetoothAudioSessionReport {
|
||||
public:
|
||||
// The API reports the Bluetooth stack has started the session, and will
|
||||
// inform registered bluetooth_audio outputs
|
||||
static void OnSessionStarted(const SessionType& session_type,
|
||||
const sp<IBluetoothAudioPort> host_iface,
|
||||
const DataMQ::Descriptor* dataMQ,
|
||||
const AudioConfiguration& audio_config) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->OnSessionStarted(host_iface, dataMQ, audio_config);
|
||||
}
|
||||
}
|
||||
// The API reports the Bluetooth stack has ended the session, and will
|
||||
// inform registered bluetooth_audio outputs
|
||||
static void OnSessionEnded(const SessionType& session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->OnSessionEnded();
|
||||
}
|
||||
}
|
||||
// The API reports the Bluetooth stack has replied the result of startStream
|
||||
// or suspendStream, and will inform registered bluetooth_audio outputs
|
||||
static void ReportControlStatus(const SessionType& session_type,
|
||||
const bool& start_resp,
|
||||
const BluetoothAudioStatus& status) {
|
||||
std::shared_ptr<BluetoothAudioSession> session_ptr =
|
||||
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->ReportControlStatus(start_resp, status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioSession_2_1.h"
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class BluetoothAudioSessionReport_2_1 {
|
||||
public:
|
||||
// The API reports the Bluetooth stack has started the session, and will
|
||||
// inform registered bluetooth_audio outputs
|
||||
static void OnSessionStarted(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type,
|
||||
const sp<IBluetoothAudioPort> host_iface,
|
||||
const DataMQ::Descriptor* dataMQ,
|
||||
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||
audio_config) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->OnSessionStarted(host_iface, dataMQ, audio_config);
|
||||
}
|
||||
}
|
||||
// The API reports the Bluetooth stack has ended the session, and will
|
||||
// inform registered bluetooth_audio outputs
|
||||
static void OnSessionEnded(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->GetAudioSession()->OnSessionEnded();
|
||||
}
|
||||
}
|
||||
// The API reports the Bluetooth stack has replied the result of startStream
|
||||
// or suspendStream, and will inform registered bluetooth_audio outputs
|
||||
static void ReportControlStatus(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type,
|
||||
const bool& start_resp, const BluetoothAudioStatus& status) {
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
|
||||
if (session_ptr != nullptr) {
|
||||
session_ptr->GetAudioSession()->ReportControlStatus(start_resp, status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
229
bluetooth/audio/utils/session/BluetoothAudioSession_2_1.cpp
Normal file
229
bluetooth/audio/utils/session/BluetoothAudioSession_2_1.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "BTAudioProviderSession_2_1"
|
||||
|
||||
#include "BluetoothAudioSession_2_1.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include "../aidl_session/HidlToAidlMiddleware_2_0.h"
|
||||
#include "../aidl_session/HidlToAidlMiddleware_2_1.h"
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
using ::aidl::android::hardware::bluetooth::audio::HidlToAidlMiddleware_2_0;
|
||||
using ::aidl::android::hardware::bluetooth::audio::HidlToAidlMiddleware_2_1;
|
||||
using SessionType_2_1 =
|
||||
::android::hardware::bluetooth::audio::V2_1::SessionType;
|
||||
using SessionType_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::SessionType;
|
||||
|
||||
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
|
||||
BluetoothAudioSession_2_1::invalidSoftwareAudioConfiguration = {};
|
||||
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
|
||||
BluetoothAudioSession_2_1::invalidOffloadAudioConfiguration = {};
|
||||
|
||||
namespace {
|
||||
bool is_2_0_session_type(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type) {
|
||||
if (session_type == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
|
||||
session_type == SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_unsupported_2_1_session_type(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type) {
|
||||
if (session_type ==
|
||||
SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
|
||||
session_type ==
|
||||
SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
BluetoothAudioSession_2_1::BluetoothAudioSession_2_1(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type)
|
||||
: audio_session(BluetoothAudioSessionInstance::GetSessionInstance(
|
||||
static_cast<SessionType_2_0>(session_type))) {
|
||||
if (is_2_0_session_type(session_type) ||
|
||||
is_unsupported_2_1_session_type(session_type)) {
|
||||
session_type_2_1_ = (SessionType_2_1::UNKNOWN);
|
||||
} else {
|
||||
session_type_2_1_ = (session_type);
|
||||
}
|
||||
raw_session_type_ = session_type;
|
||||
}
|
||||
|
||||
std::shared_ptr<BluetoothAudioSession>
|
||||
BluetoothAudioSession_2_1::GetAudioSession() {
|
||||
return audio_session;
|
||||
}
|
||||
|
||||
// The control function is for the bluetooth_audio module to get the current
|
||||
// AudioConfiguration
|
||||
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
|
||||
BluetoothAudioSession_2_1::GetAudioConfig() {
|
||||
if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
|
||||
return HidlToAidlMiddleware_2_1::GetAudioConfig(raw_session_type_);
|
||||
std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
|
||||
if (audio_session->IsSessionReady()) {
|
||||
// If session is unknown it means it should be 2.0 type
|
||||
if (session_type_2_1_ != SessionType_2_1::UNKNOWN)
|
||||
return audio_config_2_1_;
|
||||
|
||||
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration toConf;
|
||||
const AudioConfiguration fromConf = GetAudioSession()->GetAudioConfig();
|
||||
// pcmConfig only differs between 2.0 and 2.1 in AudioConfiguration
|
||||
if (fromConf.getDiscriminator() ==
|
||||
AudioConfiguration::hidl_discriminator::codecConfig) {
|
||||
toConf.codecConfig(fromConf.codecConfig());
|
||||
} else {
|
||||
toConf.pcmConfig() = {
|
||||
.sampleRate = static_cast<
|
||||
::android::hardware::bluetooth::audio::V2_1::SampleRate>(
|
||||
fromConf.pcmConfig().sampleRate),
|
||||
.channelMode = fromConf.pcmConfig().channelMode,
|
||||
.bitsPerSample = fromConf.pcmConfig().bitsPerSample,
|
||||
.dataIntervalUs = 0};
|
||||
}
|
||||
return toConf;
|
||||
} else if (session_type_2_1_ ==
|
||||
SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
|
||||
return kInvalidOffloadAudioConfiguration;
|
||||
} else {
|
||||
return kInvalidSoftwareAudioConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
bool BluetoothAudioSession_2_1::UpdateAudioConfig(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||
audio_config) {
|
||||
bool is_software_session =
|
||||
(session_type_2_1_ == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_2_1_ ==
|
||||
SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_2_1_ ==
|
||||
SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type_2_1_ ==
|
||||
SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
|
||||
bool is_offload_a2dp_session =
|
||||
(session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH);
|
||||
auto audio_config_discriminator = audio_config.getDiscriminator();
|
||||
bool is_software_audio_config =
|
||||
(is_software_session &&
|
||||
audio_config_discriminator ==
|
||||
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
|
||||
hidl_discriminator::pcmConfig);
|
||||
bool is_a2dp_offload_audio_config =
|
||||
(is_offload_a2dp_session &&
|
||||
audio_config_discriminator ==
|
||||
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
|
||||
hidl_discriminator::codecConfig);
|
||||
if (!is_software_audio_config && !is_a2dp_offload_audio_config) {
|
||||
return false;
|
||||
}
|
||||
audio_config_2_1_ = audio_config;
|
||||
return true;
|
||||
}
|
||||
|
||||
// The report function is used to report that the Bluetooth stack has started
|
||||
// this session without any failure, and will invoke session_changed_cb_ to
|
||||
// notify those registered bluetooth_audio outputs
|
||||
void BluetoothAudioSession_2_1::OnSessionStarted(
|
||||
const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
|
||||
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||
audio_config) {
|
||||
if (session_type_2_1_ == SessionType_2_1::UNKNOWN) {
|
||||
::android::hardware::bluetooth::audio::V2_0::AudioConfiguration config;
|
||||
if (audio_config.getDiscriminator() ==
|
||||
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
|
||||
hidl_discriminator::codecConfig) {
|
||||
config.codecConfig(audio_config.codecConfig());
|
||||
} else {
|
||||
auto& tmpPcm = audio_config.pcmConfig();
|
||||
config.pcmConfig(
|
||||
::android::hardware::bluetooth::audio::V2_0::PcmParameters{
|
||||
.sampleRate = static_cast<SampleRate>(tmpPcm.sampleRate),
|
||||
.channelMode = tmpPcm.channelMode,
|
||||
.bitsPerSample = tmpPcm.bitsPerSample
|
||||
/*dataIntervalUs is not passed to 2.0 */
|
||||
});
|
||||
}
|
||||
|
||||
audio_session->OnSessionStarted(stack_iface, dataMQ, config);
|
||||
} else {
|
||||
std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
|
||||
if (stack_iface == nullptr) {
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
|
||||
<< ", IBluetoothAudioPort Invalid";
|
||||
} else if (!UpdateAudioConfig(audio_config)) {
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
|
||||
<< ", AudioConfiguration=" << toString(audio_config)
|
||||
<< " Invalid";
|
||||
} else if (!audio_session->UpdateDataPath(dataMQ)) {
|
||||
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
|
||||
<< " DataMQ Invalid";
|
||||
audio_config_2_1_ =
|
||||
(session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH
|
||||
? kInvalidOffloadAudioConfiguration
|
||||
: kInvalidSoftwareAudioConfiguration);
|
||||
} else {
|
||||
audio_session->stack_iface_ = stack_iface;
|
||||
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_)
|
||||
<< ", AudioConfiguration=" << toString(audio_config);
|
||||
audio_session->ReportSessionStatus();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<BluetoothAudioSessionInstance_2_1>
|
||||
BluetoothAudioSessionInstance_2_1::instance_ptr =
|
||||
std::unique_ptr<BluetoothAudioSessionInstance_2_1>(
|
||||
new BluetoothAudioSessionInstance_2_1());
|
||||
|
||||
// API to fetch the session of A2DP / Hearing Aid
|
||||
std::shared_ptr<BluetoothAudioSession_2_1>
|
||||
BluetoothAudioSessionInstance_2_1::GetSessionInstance(
|
||||
const SessionType_2_1& session_type) {
|
||||
std::lock_guard<std::mutex> guard(instance_ptr->mutex_);
|
||||
if (!instance_ptr->sessions_map_.empty()) {
|
||||
auto entry = instance_ptr->sessions_map_.find(session_type);
|
||||
if (entry != instance_ptr->sessions_map_.end()) {
|
||||
return entry->second;
|
||||
}
|
||||
}
|
||||
std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
|
||||
std::make_shared<BluetoothAudioSession_2_1>(session_type);
|
||||
instance_ptr->sessions_map_[session_type] = session_ptr;
|
||||
return session_ptr;
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
95
bluetooth/audio/utils/session/BluetoothAudioSession_2_1.h
Normal file
95
bluetooth/audio/utils/session/BluetoothAudioSession_2_1.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/hardware/bluetooth/audio/2.1/types.h>
|
||||
#include "BluetoothAudioSession.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
class BluetoothAudioSession_2_1 {
|
||||
private:
|
||||
std::shared_ptr<BluetoothAudioSession> audio_session;
|
||||
|
||||
::android::hardware::bluetooth::audio::V2_1::SessionType session_type_2_1_;
|
||||
::android::hardware::bluetooth::audio::V2_1::SessionType raw_session_type_;
|
||||
|
||||
// audio data configuration for both software and offloading
|
||||
::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
|
||||
audio_config_2_1_;
|
||||
|
||||
bool UpdateAudioConfig(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||
audio_config);
|
||||
|
||||
static ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
|
||||
invalidSoftwareAudioConfiguration;
|
||||
static ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
|
||||
invalidOffloadAudioConfiguration;
|
||||
|
||||
public:
|
||||
BluetoothAudioSession_2_1(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type);
|
||||
|
||||
std::shared_ptr<BluetoothAudioSession> GetAudioSession();
|
||||
|
||||
// The report function is used to report that the Bluetooth stack has started
|
||||
// this session without any failure, and will invoke session_changed_cb_ to
|
||||
// notify those registered bluetooth_audio outputs
|
||||
void OnSessionStarted(
|
||||
const sp<IBluetoothAudioPort> stack_iface,
|
||||
const DataMQ::Descriptor* dataMQ,
|
||||
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
|
||||
audio_config);
|
||||
|
||||
// The control function is for the bluetooth_audio module to get the current
|
||||
// AudioConfiguration
|
||||
const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
|
||||
GetAudioConfig();
|
||||
|
||||
static constexpr ::android::hardware::bluetooth::audio::V2_1::
|
||||
AudioConfiguration& kInvalidSoftwareAudioConfiguration =
|
||||
invalidSoftwareAudioConfiguration;
|
||||
static constexpr ::android::hardware::bluetooth::audio::V2_1::
|
||||
AudioConfiguration& kInvalidOffloadAudioConfiguration =
|
||||
invalidOffloadAudioConfiguration;
|
||||
};
|
||||
|
||||
class BluetoothAudioSessionInstance_2_1 {
|
||||
public:
|
||||
// The API is to fetch the specified session of A2DP / Hearing Aid
|
||||
static std::shared_ptr<BluetoothAudioSession_2_1> GetSessionInstance(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type);
|
||||
|
||||
private:
|
||||
static std::unique_ptr<BluetoothAudioSessionInstance_2_1> instance_ptr;
|
||||
std::mutex mutex_;
|
||||
std::unordered_map<::android::hardware::bluetooth::audio::V2_1::SessionType,
|
||||
std::shared_ptr<BluetoothAudioSession_2_1>>
|
||||
sessions_map_;
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
@ -0,0 +1,415 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "BTAudioProviderSessionCodecsDB"
|
||||
|
||||
#include "BluetoothAudioSupportedCodecsDB.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::CodecType;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
|
||||
|
||||
// Default Supported PCM Parameters
|
||||
static const PcmParameters kDefaultSoftwarePcmCapabilities = {
|
||||
.sampleRate = static_cast<SampleRate>(
|
||||
SampleRate::RATE_44100 | SampleRate::RATE_48000 |
|
||||
SampleRate::RATE_88200 | SampleRate::RATE_96000 |
|
||||
SampleRate::RATE_16000 | SampleRate::RATE_24000),
|
||||
.channelMode =
|
||||
static_cast<ChannelMode>(ChannelMode::MONO | ChannelMode::STEREO),
|
||||
.bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
|
||||
BitsPerSample::BITS_24 |
|
||||
BitsPerSample::BITS_32)};
|
||||
|
||||
// Default Supported Codecs
|
||||
// SBC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(MONO|STEREO)
|
||||
// all blocks | subbands 8 | Loudness
|
||||
static const SbcParameters kDefaultOffloadSbcCapability = {
|
||||
.sampleRate = SampleRate::RATE_44100,
|
||||
.channelMode = static_cast<SbcChannelMode>(SbcChannelMode::MONO |
|
||||
SbcChannelMode::JOINT_STEREO),
|
||||
.blockLength = static_cast<SbcBlockLength>(
|
||||
SbcBlockLength::BLOCKS_4 | SbcBlockLength::BLOCKS_8 |
|
||||
SbcBlockLength::BLOCKS_12 | SbcBlockLength::BLOCKS_16),
|
||||
.numSubbands = SbcNumSubbands::SUBBAND_8,
|
||||
.allocMethod = SbcAllocMethod::ALLOC_MD_L,
|
||||
.bitsPerSample = BitsPerSample::BITS_16,
|
||||
.minBitpool = 2,
|
||||
.maxBitpool = 53};
|
||||
|
||||
// AAC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(STEREO)
|
||||
static const AacParameters kDefaultOffloadAacCapability = {
|
||||
.objectType = AacObjectType::MPEG2_LC,
|
||||
.sampleRate = SampleRate::RATE_44100,
|
||||
.channelMode = ChannelMode::STEREO,
|
||||
.variableBitRateEnabled = AacVariableBitRate::ENABLED,
|
||||
.bitsPerSample = BitsPerSample::BITS_16};
|
||||
|
||||
// LDAC: mSampleRate:(44100|48000|88200|96000), mBitsPerSample:(16|24|32),
|
||||
// mChannelMode:(DUAL|STEREO)
|
||||
static const LdacParameters kDefaultOffloadLdacCapability = {
|
||||
.sampleRate = static_cast<SampleRate>(
|
||||
SampleRate::RATE_44100 | SampleRate::RATE_48000 |
|
||||
SampleRate::RATE_88200 | SampleRate::RATE_96000),
|
||||
.channelMode = static_cast<LdacChannelMode>(LdacChannelMode::DUAL |
|
||||
LdacChannelMode::STEREO),
|
||||
.qualityIndex = LdacQualityIndex::QUALITY_HIGH,
|
||||
.bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
|
||||
BitsPerSample::BITS_24 |
|
||||
BitsPerSample::BITS_32)};
|
||||
|
||||
// aptX: mSampleRate:(44100|48000), mBitsPerSample:(16), mChannelMode:(STEREO)
|
||||
static const AptxParameters kDefaultOffloadAptxCapability = {
|
||||
.sampleRate = static_cast<SampleRate>(SampleRate::RATE_44100 |
|
||||
SampleRate::RATE_48000),
|
||||
.channelMode = ChannelMode::STEREO,
|
||||
.bitsPerSample = BitsPerSample::BITS_16,
|
||||
};
|
||||
|
||||
// aptX HD: mSampleRate:(44100|48000), mBitsPerSample:(24),
|
||||
// mChannelMode:(STEREO)
|
||||
static const AptxParameters kDefaultOffloadAptxHdCapability = {
|
||||
.sampleRate = static_cast<SampleRate>(SampleRate::RATE_44100 |
|
||||
SampleRate::RATE_48000),
|
||||
.channelMode = ChannelMode::STEREO,
|
||||
.bitsPerSample = BitsPerSample::BITS_24,
|
||||
};
|
||||
|
||||
const std::vector<CodecCapabilities> kDefaultOffloadA2dpCodecCapabilities = {
|
||||
{.codecType = CodecType::SBC, .capabilities = {}},
|
||||
{.codecType = CodecType::AAC, .capabilities = {}},
|
||||
{.codecType = CodecType::LDAC, .capabilities = {}},
|
||||
{.codecType = CodecType::APTX, .capabilities = {}},
|
||||
{.codecType = CodecType::APTX_HD, .capabilities = {}}};
|
||||
|
||||
static bool IsSingleBit(uint32_t bitmasks, uint32_t bitfield) {
|
||||
bool single = false;
|
||||
uint32_t test_bit = 0x00000001;
|
||||
while (test_bit <= bitmasks && test_bit <= bitfield) {
|
||||
if (bitfield & test_bit && bitmasks & test_bit) {
|
||||
if (single) return false;
|
||||
single = true;
|
||||
}
|
||||
if (test_bit == 0x80000000) break;
|
||||
test_bit <<= 1;
|
||||
}
|
||||
return single;
|
||||
}
|
||||
|
||||
static bool IsOffloadSbcConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific);
|
||||
static bool IsOffloadAacConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific);
|
||||
static bool IsOffloadLdacConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific);
|
||||
static bool IsOffloadAptxConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific);
|
||||
static bool IsOffloadAptxHdConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific);
|
||||
|
||||
static bool IsOffloadSbcConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific) {
|
||||
if (codec_specific.getDiscriminator() !=
|
||||
CodecConfiguration::CodecSpecific::hidl_discriminator::sbcConfig) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
}
|
||||
const SbcParameters sbc_data = codec_specific.sbcConfig();
|
||||
if (!IsSingleBit(static_cast<uint32_t>(sbc_data.sampleRate), 0xff) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(sbc_data.channelMode), 0x0f) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(sbc_data.blockLength), 0xf0) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(sbc_data.numSubbands), 0x0c) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(sbc_data.allocMethod), 0x03) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(sbc_data.bitsPerSample), 0x07) ||
|
||||
sbc_data.minBitpool > sbc_data.maxBitpool) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
} else if ((sbc_data.sampleRate & kDefaultOffloadSbcCapability.sampleRate) &&
|
||||
(sbc_data.channelMode &
|
||||
kDefaultOffloadSbcCapability.channelMode) &&
|
||||
(sbc_data.blockLength &
|
||||
kDefaultOffloadSbcCapability.blockLength) &&
|
||||
(sbc_data.numSubbands &
|
||||
kDefaultOffloadSbcCapability.numSubbands) &&
|
||||
(sbc_data.allocMethod &
|
||||
kDefaultOffloadSbcCapability.allocMethod) &&
|
||||
(sbc_data.bitsPerSample &
|
||||
kDefaultOffloadSbcCapability.bitsPerSample) &&
|
||||
(kDefaultOffloadSbcCapability.minBitpool <= sbc_data.minBitpool &&
|
||||
sbc_data.maxBitpool <= kDefaultOffloadSbcCapability.maxBitpool)) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsOffloadAacConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific) {
|
||||
if (codec_specific.getDiscriminator() !=
|
||||
CodecConfiguration::CodecSpecific::hidl_discriminator::aacConfig) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
}
|
||||
const AacParameters aac_data = codec_specific.aacConfig();
|
||||
if (!IsSingleBit(static_cast<uint32_t>(aac_data.objectType), 0xf0) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(aac_data.sampleRate), 0xff) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(aac_data.channelMode), 0x03) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(aac_data.bitsPerSample), 0x07)) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
} else if ((aac_data.objectType & kDefaultOffloadAacCapability.objectType) &&
|
||||
(aac_data.sampleRate & kDefaultOffloadAacCapability.sampleRate) &&
|
||||
(aac_data.channelMode &
|
||||
kDefaultOffloadAacCapability.channelMode) &&
|
||||
(aac_data.variableBitRateEnabled == AacVariableBitRate::DISABLED ||
|
||||
kDefaultOffloadAacCapability.variableBitRateEnabled ==
|
||||
AacVariableBitRate::ENABLED) &&
|
||||
(aac_data.bitsPerSample &
|
||||
kDefaultOffloadAacCapability.bitsPerSample)) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsOffloadLdacConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific) {
|
||||
if (codec_specific.getDiscriminator() !=
|
||||
CodecConfiguration::CodecSpecific::hidl_discriminator::ldacConfig) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
}
|
||||
const LdacParameters ldac_data = codec_specific.ldacConfig();
|
||||
if (!IsSingleBit(static_cast<uint32_t>(ldac_data.sampleRate), 0xff) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(ldac_data.channelMode), 0x07) ||
|
||||
(ldac_data.qualityIndex > LdacQualityIndex::QUALITY_LOW &&
|
||||
ldac_data.qualityIndex != LdacQualityIndex::QUALITY_ABR) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(ldac_data.bitsPerSample), 0x07)) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
} else if ((ldac_data.sampleRate &
|
||||
kDefaultOffloadLdacCapability.sampleRate) &&
|
||||
(ldac_data.channelMode &
|
||||
kDefaultOffloadLdacCapability.channelMode) &&
|
||||
(ldac_data.bitsPerSample &
|
||||
kDefaultOffloadLdacCapability.bitsPerSample)) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsOffloadAptxConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific) {
|
||||
if (codec_specific.getDiscriminator() !=
|
||||
CodecConfiguration::CodecSpecific::hidl_discriminator::aptxConfig) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
}
|
||||
const AptxParameters aptx_data = codec_specific.aptxConfig();
|
||||
if (!IsSingleBit(static_cast<uint32_t>(aptx_data.sampleRate), 0xff) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(aptx_data.channelMode), 0x03) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(aptx_data.bitsPerSample), 0x07)) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
} else if ((aptx_data.sampleRate &
|
||||
kDefaultOffloadAptxCapability.sampleRate) &&
|
||||
(aptx_data.channelMode &
|
||||
kDefaultOffloadAptxCapability.channelMode) &&
|
||||
(aptx_data.bitsPerSample &
|
||||
kDefaultOffloadAptxCapability.bitsPerSample)) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsOffloadAptxHdConfigurationValid(
|
||||
const CodecConfiguration::CodecSpecific& codec_specific) {
|
||||
if (codec_specific.getDiscriminator() !=
|
||||
CodecConfiguration::CodecSpecific::hidl_discriminator::aptxConfig) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
}
|
||||
const AptxParameters aptx_data = codec_specific.aptxConfig();
|
||||
if (!IsSingleBit(static_cast<uint32_t>(aptx_data.sampleRate), 0xff) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(aptx_data.channelMode), 0x03) ||
|
||||
!IsSingleBit(static_cast<uint32_t>(aptx_data.bitsPerSample), 0x07)) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
} else if ((aptx_data.sampleRate &
|
||||
kDefaultOffloadAptxHdCapability.sampleRate) &&
|
||||
(aptx_data.channelMode &
|
||||
kDefaultOffloadAptxHdCapability.channelMode) &&
|
||||
(aptx_data.bitsPerSample &
|
||||
kDefaultOffloadAptxHdCapability.bitsPerSample)) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported CodecSpecific=" << toString(codec_specific);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<PcmParameters> GetSoftwarePcmCapabilities() {
|
||||
return std::vector<PcmParameters>(1, kDefaultSoftwarePcmCapabilities);
|
||||
}
|
||||
|
||||
std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
|
||||
const SessionType& session_type) {
|
||||
if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
|
||||
return std::vector<CodecCapabilities>(0);
|
||||
}
|
||||
std::vector<CodecCapabilities> offload_a2dp_codec_capabilities =
|
||||
kDefaultOffloadA2dpCodecCapabilities;
|
||||
for (auto& codec_capability : offload_a2dp_codec_capabilities) {
|
||||
switch (codec_capability.codecType) {
|
||||
case CodecType::SBC:
|
||||
codec_capability.capabilities.sbcCapabilities(
|
||||
kDefaultOffloadSbcCapability);
|
||||
break;
|
||||
case CodecType::AAC:
|
||||
codec_capability.capabilities.aacCapabilities(
|
||||
kDefaultOffloadAacCapability);
|
||||
break;
|
||||
case CodecType::LDAC:
|
||||
codec_capability.capabilities.ldacCapabilities(
|
||||
kDefaultOffloadLdacCapability);
|
||||
break;
|
||||
case CodecType::APTX:
|
||||
codec_capability.capabilities.aptxCapabilities(
|
||||
kDefaultOffloadAptxCapability);
|
||||
break;
|
||||
case CodecType::APTX_HD:
|
||||
codec_capability.capabilities.aptxCapabilities(
|
||||
kDefaultOffloadAptxHdCapability);
|
||||
break;
|
||||
case CodecType::UNKNOWN:
|
||||
codec_capability = {};
|
||||
break;
|
||||
}
|
||||
}
|
||||
return offload_a2dp_codec_capabilities;
|
||||
}
|
||||
|
||||
bool IsSoftwarePcmConfigurationValid(const PcmParameters& pcm_config) {
|
||||
if ((pcm_config.sampleRate != SampleRate::RATE_44100 &&
|
||||
pcm_config.sampleRate != SampleRate::RATE_48000 &&
|
||||
pcm_config.sampleRate != SampleRate::RATE_88200 &&
|
||||
pcm_config.sampleRate != SampleRate::RATE_96000 &&
|
||||
pcm_config.sampleRate != SampleRate::RATE_16000 &&
|
||||
pcm_config.sampleRate != SampleRate::RATE_24000) ||
|
||||
(pcm_config.bitsPerSample != BitsPerSample::BITS_16 &&
|
||||
pcm_config.bitsPerSample != BitsPerSample::BITS_24 &&
|
||||
pcm_config.bitsPerSample != BitsPerSample::BITS_32) ||
|
||||
(pcm_config.channelMode != ChannelMode::MONO &&
|
||||
pcm_config.channelMode != ChannelMode::STEREO)) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid PCM Configuration=" << toString(pcm_config);
|
||||
return false;
|
||||
} else if (pcm_config.sampleRate &
|
||||
kDefaultSoftwarePcmCapabilities.sampleRate &&
|
||||
pcm_config.bitsPerSample &
|
||||
kDefaultSoftwarePcmCapabilities.bitsPerSample &&
|
||||
pcm_config.channelMode &
|
||||
kDefaultSoftwarePcmCapabilities.channelMode) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported PCM Configuration=" << toString(pcm_config);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsOffloadCodecConfigurationValid(const SessionType& session_type,
|
||||
const CodecConfiguration& codec_config) {
|
||||
if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": Invalid SessionType=" << toString(session_type);
|
||||
return false;
|
||||
} else if (codec_config.encodedAudioBitrate < 0x00000001 ||
|
||||
0x00ffffff < codec_config.encodedAudioBitrate) {
|
||||
LOG(ERROR) << __func__ << ": Unsupported Codec Configuration="
|
||||
<< toString(codec_config);
|
||||
return false;
|
||||
}
|
||||
const CodecConfiguration::CodecSpecific& codec_specific = codec_config.config;
|
||||
switch (codec_config.codecType) {
|
||||
case CodecType::SBC:
|
||||
if (IsOffloadSbcConfigurationValid(codec_specific)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case CodecType::AAC:
|
||||
if (IsOffloadAacConfigurationValid(codec_specific)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case CodecType::LDAC:
|
||||
if (IsOffloadLdacConfigurationValid(codec_specific)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case CodecType::APTX:
|
||||
if (IsOffloadAptxConfigurationValid(codec_specific)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case CodecType::APTX_HD:
|
||||
if (IsOffloadAptxHdConfigurationValid(codec_specific)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case CodecType::UNKNOWN:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/hardware/bluetooth/audio/2.0/types.h>
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::SessionType;
|
||||
|
||||
std::vector<PcmParameters> GetSoftwarePcmCapabilities();
|
||||
std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
|
||||
const SessionType& session_type);
|
||||
|
||||
bool IsSoftwarePcmConfigurationValid(const PcmParameters& pcm_config);
|
||||
bool IsOffloadCodecConfigurationValid(const SessionType& session_type,
|
||||
const CodecConfiguration& codec_config);
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "BTAudioProviderSessionCodecsDB_2_1"
|
||||
|
||||
#include "BluetoothAudioSupportedCodecsDB_2_1.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
|
||||
using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
|
||||
|
||||
using SampleRate_2_0 = ::android::hardware::bluetooth::audio::V2_0::SampleRate;
|
||||
using SampleRate_2_1 = ::android::hardware::bluetooth::audio::V2_1::SampleRate;
|
||||
|
||||
using SessionType_2_1 =
|
||||
::android::hardware::bluetooth::audio::V2_1::SessionType;
|
||||
using SessionType_2_0 =
|
||||
::android::hardware::bluetooth::audio::V2_0::SessionType;
|
||||
|
||||
namespace {
|
||||
bool is_2_0_session_type(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type) {
|
||||
if (session_type == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
|
||||
session_type == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
|
||||
session_type == SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static const ::android::hardware::bluetooth::audio::V2_1::PcmParameters
|
||||
kDefaultSoftwarePcmCapabilities_2_1 = {
|
||||
.sampleRate = static_cast<SampleRate_2_1>(
|
||||
SampleRate_2_1::RATE_44100 | SampleRate_2_1::RATE_48000 |
|
||||
SampleRate_2_1::RATE_88200 | SampleRate_2_1::RATE_96000 |
|
||||
SampleRate_2_1::RATE_16000 | SampleRate_2_1::RATE_24000),
|
||||
.channelMode =
|
||||
static_cast<ChannelMode>(ChannelMode::MONO | ChannelMode::STEREO),
|
||||
.bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
|
||||
BitsPerSample::BITS_24 |
|
||||
BitsPerSample::BITS_32)};
|
||||
|
||||
std::vector<::android::hardware::bluetooth::audio::V2_1::PcmParameters>
|
||||
GetSoftwarePcmCapabilities_2_1() {
|
||||
return std::vector<
|
||||
::android::hardware::bluetooth::audio::V2_1::PcmParameters>(
|
||||
1, kDefaultSoftwarePcmCapabilities_2_1);
|
||||
}
|
||||
|
||||
std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type) {
|
||||
if (is_2_0_session_type(session_type)) {
|
||||
return GetOffloadCodecCapabilities(
|
||||
static_cast<SessionType_2_0>(session_type));
|
||||
}
|
||||
return std::vector<CodecCapabilities>(0);
|
||||
}
|
||||
|
||||
bool IsSoftwarePcmConfigurationValid_2_1(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::PcmParameters&
|
||||
pcm_config) {
|
||||
if ((pcm_config.sampleRate != SampleRate_2_1::RATE_44100 &&
|
||||
pcm_config.sampleRate != SampleRate_2_1::RATE_48000 &&
|
||||
pcm_config.sampleRate != SampleRate_2_1::RATE_88200 &&
|
||||
pcm_config.sampleRate != SampleRate_2_1::RATE_96000 &&
|
||||
pcm_config.sampleRate != SampleRate_2_1::RATE_16000 &&
|
||||
pcm_config.sampleRate != SampleRate_2_1::RATE_24000) ||
|
||||
(pcm_config.bitsPerSample != BitsPerSample::BITS_16 &&
|
||||
pcm_config.bitsPerSample != BitsPerSample::BITS_24 &&
|
||||
pcm_config.bitsPerSample != BitsPerSample::BITS_32) ||
|
||||
(pcm_config.channelMode != ChannelMode::MONO &&
|
||||
pcm_config.channelMode != ChannelMode::STEREO)) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Invalid PCM Configuration=" << toString(pcm_config);
|
||||
return false;
|
||||
} else if (pcm_config.sampleRate &
|
||||
kDefaultSoftwarePcmCapabilities_2_1.sampleRate &&
|
||||
pcm_config.bitsPerSample &
|
||||
kDefaultSoftwarePcmCapabilities_2_1.bitsPerSample &&
|
||||
pcm_config.channelMode &
|
||||
kDefaultSoftwarePcmCapabilities_2_1.channelMode &&
|
||||
pcm_config.dataIntervalUs != 0) {
|
||||
return true;
|
||||
}
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Unsupported PCM Configuration=" << toString(pcm_config);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsOffloadCodecConfigurationValid(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type,
|
||||
const ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration&
|
||||
codec_config) {
|
||||
if (is_2_0_session_type(session_type)) {
|
||||
return IsOffloadCodecConfigurationValid(
|
||||
static_cast<SessionType_2_0>(session_type), codec_config);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsOffloadLeAudioConfigurationValid(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type,
|
||||
const ::android::hardware::bluetooth::audio::V2_1::Lc3CodecConfiguration&) {
|
||||
|
||||
if (session_type != SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
|
||||
session_type != SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO: perform checks on le_audio_codec_config once we know supported parameters
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothAudioSupportedCodecsDB.h"
|
||||
|
||||
#include <android/hardware/bluetooth/audio/2.1/types.h>
|
||||
|
||||
namespace android {
|
||||
namespace bluetooth {
|
||||
namespace audio {
|
||||
|
||||
std::vector<::android::hardware::bluetooth::audio::V2_1::PcmParameters>
|
||||
GetSoftwarePcmCapabilities_2_1();
|
||||
std::vector<::android::hardware::bluetooth::audio::V2_0::CodecCapabilities>
|
||||
GetOffloadCodecCapabilities(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type);
|
||||
|
||||
bool IsSoftwarePcmConfigurationValid_2_1(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::PcmParameters&
|
||||
pcm_config);
|
||||
|
||||
bool IsOffloadCodecConfigurationValid(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type,
|
||||
const ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration&
|
||||
codec_config);
|
||||
|
||||
bool IsOffloadLeAudioConfigurationValid(
|
||||
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
|
||||
session_type,
|
||||
const ::android::hardware::bluetooth::audio::V2_1::Lc3CodecConfiguration&
|
||||
le_audio_codec_config);
|
||||
} // namespace audio
|
||||
} // namespace bluetooth
|
||||
} // namespace android
|
@ -1,5 +1,8 @@
|
||||
TARGET_SYSTEM_PROP := device/phh/treble/system.prop $(TARGET_SYSTEM_PROP)
|
||||
BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := device/phh/treble/bluetooth
|
||||
|
||||
# Bluetooth
|
||||
BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := device/phh/treble/bluetooth/include
|
||||
|
||||
TARGET_EXFAT_DRIVER := exfat
|
||||
DEVICE_FRAMEWORK_MANIFEST_FILE := device/phh/treble/framework_manifest.xml
|
||||
|
||||
|
@ -21,3 +21,4 @@
|
||||
/efs u:object_r:efs_file:s0
|
||||
|
||||
/dev/smcinvoke u:object_r:smcinvoke_device:s0
|
||||
/system/bin/hw/android\.hardware\.bluetooth\.audio-service-system u:object_r:hal_audio_sysbta_exec:s0
|
||||
|
10
sepolicy/hal_audio_sysbta.te
Normal file
10
sepolicy/hal_audio_sysbta.te
Normal file
@ -0,0 +1,10 @@
|
||||
type hal_audio_sysbta, domain, coredomain;
|
||||
hal_server_domain(hal_audio_sysbta, hal_audio)
|
||||
|
||||
type hal_audio_sysbta_exec, exec_type, system_file_type, file_type;
|
||||
init_daemon_domain(hal_audio_sysbta)
|
||||
|
||||
hal_client_domain(hal_audio_sysbta, hal_allocator)
|
||||
|
||||
# allow audioserver to call hal_audio dump with its own fd to retrieve status
|
||||
allow hal_audio_sysbta audioserver:fifo_file write;
|
@ -8,3 +8,6 @@ irit u:object_r:radio_service:s0
|
||||
# MTK IMS
|
||||
mwis u:object_r:radio_service:s0
|
||||
mtkIms u:object_r:radio_service:s0
|
||||
|
||||
# Audio AIDL interface
|
||||
android.hardware.bluetooth.audio.IBluetoothAudioProviderFactory/sysbta u:object_r:hal_audio_service:s0
|
||||
|
@ -15,6 +15,8 @@ ro.boot.realme.lockstate=0
|
||||
#Fixes fingerprint unlock delay
|
||||
persist.wm.enable_remote_keyguard_animation=0
|
||||
|
||||
# Enable system-side generic bluetooth audio HAL
|
||||
persist.bluetooth.system_audio_hal.enabled=1
|
||||
# Set commonly-supported Bluetooth profiles to enabled
|
||||
# TODO: Enable BLE Audio profiles on newer vendors
|
||||
bluetooth.profile.asha.central.enabled?=true
|
||||
|
Loading…
Reference in New Issue
Block a user