Compare commits
35 Commits
49ae7b2188
...
fa82b1365f
Author | SHA1 | Date | |
---|---|---|---|
fa82b1365f | |||
5e2329940f | |||
0dcef21299 | |||
52743e0475 | |||
|
0b26ad42ab | ||
|
7a6d658f5d | ||
|
948497d23a | ||
|
747f281b9f | ||
|
9f0e48ddcd | ||
|
420f583758 | ||
|
4f1e4824dc | ||
|
323ef8d915 | ||
|
8636827cf6 | ||
|
978ccd40f9 | ||
|
a7009ad4ad | ||
|
26fc8430a4 | ||
|
091321c413 | ||
|
8bd41df06c | ||
|
75f7c5db31 | ||
|
8c28e09626 | ||
|
f6e059b8e4 | ||
|
09ae57bf02 | ||
|
53c51cfa88 | ||
|
5fd63a4229 | ||
|
41c8ec4ee0 | ||
|
64c6844aca | ||
|
9d488d04b5 | ||
|
7a36effb45 | ||
|
5b3edfaf9d | ||
|
0c5eaf7d25 | ||
|
cb054bed36 | ||
|
f0c6140a9b | ||
|
6c3350b249 | ||
|
809d83671f | ||
|
1fb1f6346b |
139
audio/hal/Android.bp
Normal file
139
audio/hal/Android.bp
Normal file
@ -0,0 +1,139 @@
|
||||
cc_defaults {
|
||||
name: "android.hardware.audio-impl-system_default",
|
||||
relative_install_path: "hw",
|
||||
srcs: [
|
||||
"Device.cpp",
|
||||
"DevicesFactory.cpp",
|
||||
"ParametersUtil.cpp",
|
||||
"PrimaryDevice.cpp",
|
||||
"Stream.cpp",
|
||||
"StreamIn.cpp",
|
||||
"StreamOut.cpp",
|
||||
"service.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libfmq",
|
||||
"libhardware",
|
||||
"libhidlbase",
|
||||
"liblog",
|
||||
"libmedia_helper",
|
||||
"libmediautils_vendor",
|
||||
"libmemunreachable",
|
||||
"libutils",
|
||||
"android.hardware.audio.common-util",
|
||||
],
|
||||
static_libs: [
|
||||
"libaudiofoundation",
|
||||
],
|
||||
header_libs: [
|
||||
"android.hardware.audio.common.util@all-versions",
|
||||
"libaudioutils_headers",
|
||||
"libaudio_system_headers",
|
||||
"libmedia_headers",
|
||||
"libmediautils_headers",
|
||||
"libhardware_headers",
|
||||
]
|
||||
}
|
||||
|
||||
cc_library_shared {
|
||||
name: "android.hardware.audio@2.0-impl-system",
|
||||
defaults: ["android.hardware.audio-impl-system_default"],
|
||||
shared_libs: [
|
||||
"android.hardware.audio@2.0",
|
||||
"android.hardware.audio@2.0-util",
|
||||
"android.hardware.audio.common@2.0",
|
||||
"android.hardware.audio.common@2.0-util",
|
||||
],
|
||||
cflags: [
|
||||
"-DMAJOR_VERSION=2",
|
||||
"-DMINOR_VERSION=0",
|
||||
"-include common/all-versions/VersionMacro.h",
|
||||
],
|
||||
}
|
||||
cc_library_shared {
|
||||
name: "android.hardware.audio@4.0-impl-system",
|
||||
defaults: ["android.hardware.audio-impl-system_default"],
|
||||
shared_libs: [
|
||||
"android.hardware.audio@4.0",
|
||||
"android.hardware.audio@4.0-util",
|
||||
"android.hardware.audio.common@4.0",
|
||||
"android.hardware.audio.common@4.0-util",
|
||||
],
|
||||
cflags: [
|
||||
"-DMAJOR_VERSION=4",
|
||||
"-DMINOR_VERSION=0",
|
||||
"-include common/all-versions/VersionMacro.h",
|
||||
],
|
||||
}
|
||||
cc_library_shared {
|
||||
name: "android.hardware.audio@5.0-impl-system",
|
||||
defaults: ["android.hardware.audio-impl-system_default"],
|
||||
shared_libs: [
|
||||
"android.hardware.audio@5.0",
|
||||
"android.hardware.audio@5.0-util",
|
||||
"android.hardware.audio.common@5.0",
|
||||
"android.hardware.audio.common@5.0-util",
|
||||
],
|
||||
cflags: [
|
||||
"-DMAJOR_VERSION=5",
|
||||
"-DMINOR_VERSION=0",
|
||||
"-include common/all-versions/VersionMacro.h",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_shared {
|
||||
name: "android.hardware.audio@6.0-impl-system",
|
||||
defaults: ["android.hardware.audio-impl-system_default"],
|
||||
shared_libs: [
|
||||
"android.hardware.audio@6.0",
|
||||
"android.hardware.audio@6.0-util",
|
||||
"android.hardware.audio.common@6.0",
|
||||
"android.hardware.audio.common@6.0-util",
|
||||
],
|
||||
cflags: [
|
||||
"-DMAJOR_VERSION=6",
|
||||
"-DMINOR_VERSION=0",
|
||||
"-include common/all-versions/VersionMacro.h",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_shared {
|
||||
name: "android.hardware.audio@7.0-impl-system",
|
||||
defaults: ["android.hardware.audio-impl-system_default"],
|
||||
shared_libs: [
|
||||
"android.hardware.audio@7.0",
|
||||
"android.hardware.audio@7.0-util",
|
||||
"android.hardware.audio.common@7.0",
|
||||
"android.hardware.audio.common@7.0-enums",
|
||||
"android.hardware.audio.common@7.0-util",
|
||||
"libbase",
|
||||
],
|
||||
cflags: [
|
||||
"-DMAJOR_VERSION=7",
|
||||
"-DMINOR_VERSION=0",
|
||||
"-include common/all-versions/VersionMacro.h",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_shared {
|
||||
name: "android.hardware.audio@7.1-impl-system",
|
||||
defaults: ["android.hardware.audio-impl-system_default"],
|
||||
shared_libs: [
|
||||
"android.hardware.audio@7.0",
|
||||
"android.hardware.audio@7.1",
|
||||
"android.hardware.audio@7.1-util",
|
||||
"android.hardware.audio.common@7.0",
|
||||
"android.hardware.audio.common@7.1-enums",
|
||||
"android.hardware.audio.common@7.1-util",
|
||||
"libbase",
|
||||
],
|
||||
cflags: [
|
||||
"-DMAJOR_VERSION=7",
|
||||
"-DMINOR_VERSION=1",
|
||||
"-DCOMMON_TYPES_MINOR_VERSION=0",
|
||||
"-DCORE_TYPES_MINOR_VERSION=0",
|
||||
"-include common/all-versions/VersionMacro.h",
|
||||
],
|
||||
}
|
628
audio/hal/Device.cpp
Normal file
628
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
audio/hal/Device.h
Normal file
203
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
audio/hal/DevicesFactory.cpp
Normal file
152
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 new DevicesFactory();
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace CPP_VERSION
|
||||
} // namespace audio
|
||||
} // namespace hardware
|
||||
} // namespace android
|
70
audio/hal/DevicesFactory.h
Normal file
70
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
|
174
audio/hal/ParametersUtil.cpp
Normal file
174
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
audio/hal/ParametersUtil.h
Normal file
73
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
audio/hal/PrimaryDevice.cpp
Normal file
355
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
audio/hal/PrimaryDevice.h
Normal file
154
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
audio/hal/Stream.cpp
Normal file
457
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
audio/hal/Stream.h
Normal file
236
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
audio/hal/StreamIn.cpp
Normal file
607
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
audio/hal/StreamIn.h
Normal file
162
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
audio/hal/StreamOut.cpp
Normal file
837
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
audio/hal/StreamOut.h
Normal file
201
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
audio/hal/Util.h
Normal file
82
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
|
15
audio/hal/service.cpp
Normal file
15
audio/hal/service.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include "DevicesFactory.h"
|
||||
#include PATH(android/hardware/audio/FILE_VERSION/IDevicesFactory.h)
|
||||
|
||||
#include <hidl/Status.h>
|
||||
|
||||
using ::android::status_t;
|
||||
using ::android::hardware::audio::CPP_VERSION::implementation::DevicesFactory;
|
||||
using namespace ::android::hardware::audio::CPP_VERSION;
|
||||
|
||||
extern "C" __attribute__((visibility("default")))
|
||||
status_t createIDevicesFactory(const char *instance_name) {
|
||||
::android::sp<IDevicesFactory> audio_factory = new DevicesFactory();
|
||||
::android::status_t hidl_status = audio_factory->registerAsService(instance_name);
|
||||
return hidl_status;
|
||||
}
|
14
base.mk
14
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
|
||||
@ -135,6 +144,7 @@ PRODUCT_COPY_FILES += \
|
||||
device/phh/treble/files/bv9500plus-mtk-kpd.kl:system/phh/bv9500plus-mtk-kpd.kl \
|
||||
device/phh/treble/files/moto-liber-gpio-keys.kl:system/phh/moto-liber-gpio-keys.kl \
|
||||
device/phh/treble/files/tecno-touchpanel.kl:system/phh/tecno-touchpanel.kl \
|
||||
device/phh/treble/files/rosemary-excluded-input-devices.xml:system/phh/rosemary-excluded-input-devices.xml
|
||||
|
||||
SELINUX_IGNORE_NEVERALLOWS := true
|
||||
|
||||
@ -214,6 +224,10 @@ PRODUCT_PRODUCT_PROPERTIES += \
|
||||
PRODUCT_PACKAGES += \
|
||||
NavigationBarMode2ButtonOverlay
|
||||
|
||||
# ThemePicker
|
||||
PRODUCT_PACKAGES += \
|
||||
ThemePicker
|
||||
|
||||
PRODUCT_PACKAGES += \
|
||||
oplus-alert-slider
|
||||
|
||||
|
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
|
40
bluetooth/audio/hal/Android.bp
Normal file
40
bluetooth/audio/hal/Android.bp
Normal file
@ -0,0 +1,40 @@
|
||||
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",
|
||||
"service_system.cpp",
|
||||
],
|
||||
header_libs: [
|
||||
"libhardware_headers",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder",
|
||||
"libbinder_ndk",
|
||||
"libcutils",
|
||||
"libfmq",
|
||||
"libhidlbase",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"android.hardware.bluetooth.audio-V2-ndk",
|
||||
"libbluetooth_audio_session_aidl_system",
|
||||
],
|
||||
required: [
|
||||
// Audio HAL impls
|
||||
"android.hardware.audio@2.0-impl-system",
|
||||
"android.hardware.audio@4.0-impl-system",
|
||||
"android.hardware.audio@5.0-impl-system",
|
||||
"android.hardware.audio@6.0-impl-system",
|
||||
"android.hardware.audio@7.0-impl-system",
|
||||
"android.hardware.audio@7.1-impl-system",
|
||||
],
|
||||
}
|
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
|
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
|
@ -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
|
17
bluetooth/audio/hal/bluetooth_audio_system.xml
Normal file
17
bluetooth/audio/hal/bluetooth_audio_system.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<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>@2.0::IDevicesFactory/sysbta</fqname>
|
||||
<fqname>@4.0::IDevicesFactory/sysbta</fqname>
|
||||
<fqname>@5.0::IDevicesFactory/sysbta</fqname>
|
||||
<fqname>@6.0::IDevicesFactory/sysbta</fqname>
|
||||
<fqname>@7.0::IDevicesFactory/sysbta</fqname>
|
||||
<fqname>@7.1::IDevicesFactory/sysbta</fqname>
|
||||
</hal>
|
||||
</manifest>
|
92
bluetooth/audio/hal/service_system.cpp
Normal file
92
bluetooth/audio/hal/service_system.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 <dlfcn.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/binder_process.h>
|
||||
#include <utils/Log.h>
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
#include <hwbinder/ProcessState.h>
|
||||
#include <binder/ProcessState.h>
|
||||
|
||||
#include "BluetoothAudioProviderFactory.h"
|
||||
|
||||
using ::aidl::android::hardware::bluetooth::audio::
|
||||
BluetoothAudioProviderFactory;
|
||||
|
||||
#if defined(__LP64__)
|
||||
#define HAL_LIBRARY_PATH "/system/lib64/hw"
|
||||
#else
|
||||
#define HAL_LIBRARY_PATH "/system/lib/hw"
|
||||
#endif
|
||||
|
||||
void registerAudioInterfaces() {
|
||||
const char *interface_libs[] = {
|
||||
"android.hardware.audio@2.0-impl-system.so",
|
||||
"android.hardware.audio@4.0-impl-system.so",
|
||||
"android.hardware.audio@5.0-impl-system.so",
|
||||
"android.hardware.audio@6.0-impl-system.so",
|
||||
"android.hardware.audio@7.0-impl-system.so",
|
||||
"android.hardware.audio@7.1-impl-system.so",
|
||||
};
|
||||
|
||||
for (auto& lib : interface_libs) {
|
||||
void* handle = dlopen((std::string() + HAL_LIBRARY_PATH + "/" + lib).c_str(), RTLD_NOW);
|
||||
if (handle == nullptr) {
|
||||
ALOGW("Failed to load %s, skipping", lib);
|
||||
continue;
|
||||
}
|
||||
|
||||
::android::status_t (*entry_func)(const char*);
|
||||
entry_func = reinterpret_cast<::android::status_t (*)(const char*)>(dlsym(handle, "createIDevicesFactory"));
|
||||
|
||||
if (entry_func == nullptr) {
|
||||
ALOGW("Cannot find entry symbol in %s, skipping", lib);
|
||||
continue;
|
||||
}
|
||||
|
||||
::android::status_t status = entry_func("sysbta");
|
||||
ALOGW_IF(status != STATUS_OK, "Could not register sysbta for %s, status=%d", lib, status);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// We must also implement audio HAL interfaces in order to serve audio.sysbta.default.so
|
||||
// It must be served in the *same* process to access the same libbluetooth_audio_session
|
||||
registerAudioInterfaces();
|
||||
|
||||
::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
|
||||
|
||||
|
31
files/rosemary-excluded-input-devices.xml
Normal file
31
files/rosemary-excluded-input-devices.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<!-- Copyright (c) 2018 The Linux Foundation. All rights reserved. -->
|
||||
<!-- -->
|
||||
<!-- Redistribution and use in source and binary forms, with or without -->
|
||||
<!-- modification, are permitted provided that the following conditions are -->
|
||||
<!-- met: -->
|
||||
<!-- * Redistributions of source code must retain the above copyright -->
|
||||
<!-- notice, this list of conditions and the following disclaimer. -->
|
||||
<!-- * Redistributions in binary form must reproduce the above -->
|
||||
<!-- copyright notice, this list of conditions and the following -->
|
||||
<!-- disclaimer in the documentation and/or other materials provided -->
|
||||
<!-- with the distribution. -->
|
||||
<!-- * Neither the name of The Linux Foundation nor the names of its -->
|
||||
<!-- contributors may be used to endorse or promote products derived -->
|
||||
<!-- from this software without specific prior written permission. -->
|
||||
<!-- -->
|
||||
<!-- THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED -->
|
||||
<!-- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -->
|
||||
<!-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT -->
|
||||
<!-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS -->
|
||||
<!-- BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -->
|
||||
<!-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -->
|
||||
<!-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -->
|
||||
<!-- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -->
|
||||
<!-- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -->
|
||||
<!-- OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -->
|
||||
<!-- IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -->
|
||||
<devices>
|
||||
<device name="uinput-fpc" />
|
||||
<device name="uinput-goodix" />
|
||||
</devices>
|
5
gapps.mk
5
gapps.mk
@ -4,14 +4,15 @@ ifneq ($(wildcard vendor/google)$(wildcard vendor/partner_gms),)
|
||||
-include vendor/partner_gms/products/gms.mk
|
||||
PRODUCT_SHIPPING_API_LEVEL :=
|
||||
|
||||
PRODUCT_PACKAGES := $(filter-out CalendarGoogle, $(PRODUCT_PACKAGES))
|
||||
PRODUCT_PACKAGES := $(filter-out GoogleContacts, $(PRODUCT_PACKAGES))
|
||||
#PRODUCT_PACKAGES := $(filter-out CalendarGoogle, $(PRODUCT_PACKAGES))
|
||||
#PRODUCT_PACKAGES := $(filter-out GoogleContacts, $(PRODUCT_PACKAGES))
|
||||
|
||||
PRODUCT_PACKAGES += \
|
||||
phh-gapps-overrides \
|
||||
Chrome \
|
||||
GoogleContactsSyncAdapter \
|
||||
talkback \
|
||||
Photos \
|
||||
|
||||
PRODUCT_SYSTEM_DEFAULT_PROPERTIES := $(filter-out ro.boot.vendor.overlay.theme=%,$(PRODUCT_SYSTEM_DEFAULT_PROPERTIES))
|
||||
endif
|
||||
|
@ -92,7 +92,7 @@ PRODUCT_SYSTEM_BRAND := google
|
||||
PRODUCT_MODEL := Phh-Treble $apps_name
|
||||
|
||||
# Overwrite the inherited "emulator" characteristics
|
||||
PRODUCT_CHARACTERISTICS := device
|
||||
PRODUCT_CHARACTERISTICS := tablet
|
||||
|
||||
PRODUCT_PACKAGES += $extra_packages
|
||||
|
||||
|
@ -14,6 +14,17 @@ using namespace std::chrono_literals;
|
||||
using namespace std::string_literals;
|
||||
using android::fiemap::IImageManager;
|
||||
|
||||
|
||||
std::string getNextSlot() {
|
||||
std::string current_slot;
|
||||
std::string next_slot = "a";
|
||||
if(android::base::ReadFileToString("/metadata/phh/img", ¤t_slot) &&
|
||||
current_slot.c_str()[0] == 'a') {
|
||||
next_slot = "b";
|
||||
}
|
||||
return next_slot;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
mkdir("/metadata/gsi/phh", 0771);
|
||||
chown("/metadata/gsi/phh", 0, 1000);
|
||||
@ -27,27 +38,13 @@ int main(int argc, char **argv) {
|
||||
return 0;
|
||||
}
|
||||
if(argc>=2 && strcmp(argv[1], "switch-slot") == 0) {
|
||||
std::string current_slot;
|
||||
std::string next_slot;
|
||||
if(!android::base::ReadFileToString("/metadata/phh/img", ¤t_slot)) {
|
||||
next_slot = "a";
|
||||
} else {
|
||||
if(current_slot.c_str()[0] == 'a')
|
||||
next_slot = "b";
|
||||
}
|
||||
std::string next_slot = getNextSlot();
|
||||
mkdir("/metadata/phh", 0700);
|
||||
android::base::WriteStringToFile(next_slot, "/metadata/phh/img");
|
||||
return 0;
|
||||
}
|
||||
if(argc>=2 && strcmp(argv[1], "new-slot") == 0) {
|
||||
std::string current_slot;
|
||||
std::string next_slot;
|
||||
if(!android::base::ReadFileToString("/metadata/phh/img", ¤t_slot)) {
|
||||
next_slot = "a";
|
||||
} else {
|
||||
if(current_slot.c_str()[0] == 'a')
|
||||
next_slot = "b";
|
||||
}
|
||||
std::string next_slot = getNextSlot();
|
||||
|
||||
std::string imageName = "system_otaphh_"s + next_slot;
|
||||
|
||||
|
@ -22,7 +22,6 @@
|
||||
<string name="config_icon_mask" translatable="false">"M50 0C77.6 0 100 22.4 100 50C100 77.6 77.6 100 50 100C22.4 100 0 77.6 0 50C0 22.4 22.4 0 50 0Z"</string>
|
||||
<bool name="config_useRoundIcon">true</bool>
|
||||
|
||||
<string name="config_wallpaperCropperPackage">com.android.wallpaperpicker</string>
|
||||
<bool name="config_unplugTurnsOnScreen">true</bool>
|
||||
<integer name="config_multiuserMaximumUsers">5</integer>
|
||||
<bool name="config_enableMultiUserUI">true</bool>
|
||||
|
21
overlay/packages/apps/Settings/res/values/config.xml
Normal file
21
overlay/packages/apps/Settings/res/values/config.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Package name and fully-qualified class name for the wallpaper picker activity. -->
|
||||
<string name="config_wallpaper_picker_package" translatable="false">com.android.wallpaper</string>
|
||||
<string name="config_wallpaper_picker_class" translatable="false">com.android.customization.picker.CustomizationPickerActivity</string>
|
||||
|
||||
<!-- Fully-qualified class name for the styles & wallpaper picker activity. -->
|
||||
<string name="config_styles_and_wallpaper_picker_class" translatable="false">com.android.customization.picker.CustomizationPickerActivity</string>
|
||||
</resources>
|
@ -1,8 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<permissions>
|
||||
<privapp-permissions package="me.phh.treble.app">
|
||||
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
|
||||
<permission name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<permission name="android.permission.DIAGNOSTIC"/>
|
||||
<permission name="android.permission.INSTALL_PACKAGES"/>
|
||||
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
|
||||
<permission name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
</privapp-permissions>
|
||||
|
||||
<!-- fix voice channels in calls (stop unloading treble-app) -->
|
||||
<allow-in-power-save package="me.phh.treble.app" />
|
||||
</permissions>
|
||||
|
54
rw-system.sh
54
rw-system.sh
@ -147,7 +147,7 @@ changeKeylayout() {
|
||||
fi
|
||||
|
||||
if getprop ro.vendor.build.fingerprint | grep -iq \
|
||||
-e poco/ -e redmi/ -e xiaomi/ ; then
|
||||
-e poco/ -e POCO/ -e redmi/ -e xiaomi/ ; then
|
||||
if [ ! -f /mnt/phh/keylayout/uinput-goodix.kl ]; then
|
||||
cp /system/phh/empty /mnt/phh/keylayout/uinput-goodix.kl
|
||||
chmod 0644 /mnt/phh/keylayout/uinput-goodix.kl
|
||||
@ -392,9 +392,9 @@ if getprop ro.vendor.build.fingerprint | grep -iq \
|
||||
setprop persist.sys.qcom-brightness -1
|
||||
fi
|
||||
|
||||
# Lenovo Z5s brightness flickers without this setting
|
||||
# Lenovo Z5s & Xiaomi Mi10TLite brightness flickers without this setting
|
||||
if getprop ro.vendor.build.fingerprint | grep -iq \
|
||||
-e Lenovo/jd2019; then
|
||||
-e Lenovo/jd2019 -e Xiaomi/gauguin -e Redmi/gauguin; then
|
||||
setprop persist.sys.qcom-brightness -1
|
||||
fi
|
||||
|
||||
@ -424,7 +424,7 @@ if getprop ro.vendor.build.fingerprint | grep -q -i \
|
||||
-e xiaomi/nitrogen -e xiaomi/whyred -e xiaomi/platina \
|
||||
-e xiaomi/ysl -e nubia/nx60 -e nubia/nx61 -e xiaomi/tulip \
|
||||
-e xiaomi/lavender -e xiaomi/olive -e xiaomi/olivelite -e xiaomi/pine \
|
||||
-e Redmi/lancelot -e Redmi/galahad; then
|
||||
-e Redmi/lancelot -e Redmi/galahad -e POCO/evergreen; then
|
||||
setprop persist.sys.qcom-brightness "$(cat /sys/class/leds/lcd-backlight/max_brightness)"
|
||||
fi
|
||||
|
||||
@ -457,7 +457,7 @@ if getprop ro.vendor.build.fingerprint | grep -iq \
|
||||
-e motorola/hannah -e motorola/james -e motorola/pettyl -e xiaomi/cepheus \
|
||||
-e xiaomi/grus -e xiaomi/cereus -e xiaomi/cactus -e xiaomi/raphael -e xiaomi/davinci \
|
||||
-e xiaomi/ginkgo -e xiaomi/willow -e xiaomi/laurel_sprout -e xiaomi/andromeda \
|
||||
-e redmi/curtana -e redmi/picasso \
|
||||
-e xiaomi/gauguin -e redmi/gauguin -e redmi/curtana -e redmi/picasso \
|
||||
-e bq/Aquaris_M10 ; then
|
||||
mount -o bind /mnt/phh/empty_dir /vendor/lib64/soundfx
|
||||
mount -o bind /mnt/phh/empty_dir /vendor/lib/soundfx
|
||||
@ -581,10 +581,17 @@ if getprop ro.vendor.build.fingerprint | grep -iq -e Redmi/merlin; then
|
||||
setprop debug.sf.enable_hwc_vds 0
|
||||
fi
|
||||
|
||||
if getprop ro.vendor.build.fingerprint | grep -iq -e Redmi/lancelot; then
|
||||
setprop debug.sf.enable_hwc_vds 1
|
||||
fi
|
||||
|
||||
if getprop ro.vendor.build.fingerprint | grep -iq -e Redmi/rosemary \
|
||||
-e Redmi/secret -e Redmi/maltose; then
|
||||
setprop debug.sf.latch_unsignaled 1
|
||||
setprop debug.sf.enable_hwc_vds 0
|
||||
|
||||
# Exclude FP input devices
|
||||
mount -o bind /system/phh/rosemary-excluded-input-devices.xml /system/etc/excluded-input-devices.xml
|
||||
fi
|
||||
|
||||
if getprop ro.vendor.build.fingerprint | grep -iq -E -e 'huawei|honor' || getprop persist.sys.overlay.huawei | grep -iq -E -e 'true'; then
|
||||
@ -975,22 +982,39 @@ fi
|
||||
|
||||
resetprop_phh ro.bluetooth.library_name libbluetooth.so
|
||||
|
||||
if getprop ro.vendor.build.fingerprint |grep -iq xiaomi/cepheus -e xiaomi/nabu;then
|
||||
setprop ro.netflix.bsp_rev Q855-16947-1
|
||||
board="$(getprop ro.board.platform)"
|
||||
|
||||
if [ "$board" = atoll ] || [ "$board" = sm6250 ]; then
|
||||
setprop ro.netflix.bsp_rev Q6250-19132-1
|
||||
fi
|
||||
|
||||
if getprop ro.vendor.build.fingerprint |grep -iq xiaomi/elish;then
|
||||
setprop ro.netflix.bsp_rev Q8250-19134-1
|
||||
if [ "$board" = msmnile ]; then
|
||||
setprop ro.netflix.bsp_rev Q855-16947-1
|
||||
fi
|
||||
|
||||
if getprop ro.vendor.build.fingerprint |grep -qi redmi/curtana;then
|
||||
setprop ro.netflix.bsp_rev Q6250-19132-1
|
||||
if [ "$board" = sm6150 ]; then
|
||||
setprop ro.netflix.bsp_rev Q6150-17263-1
|
||||
fi
|
||||
|
||||
if getprop ro.vendor.build.fingerprint |grep -iq xiaomi/renoir;then
|
||||
setprop ro.netflix.bsp_rev Q875-32774-1
|
||||
resetprop_phh ro.config.media_vol_steps 25
|
||||
resetprop_phh ro.config.media_vol_default 15
|
||||
if [ "$board" = mt6768 ]; then
|
||||
setprop ro.netflix.bsp_rev MTK6768-19055-1
|
||||
fi
|
||||
|
||||
if [ "$board" = lahaina ]; then
|
||||
setprop ro.netflix.bsp_rev Q875-32774-1
|
||||
resetprop_phh ro.config.media_vol_steps 25
|
||||
resetprop_phh ro.config.media_vol_default 15
|
||||
fi
|
||||
|
||||
if [ "$board" = universal8825 ];then
|
||||
setprop ro.netflix.bsp_rev EXYNOS1280-34993-1
|
||||
fi
|
||||
|
||||
if getprop ro.vendor.build.fingerprint |grep -qi Nokia/Phoenix;then
|
||||
setprop ro.netflix.bsp_rev Q845-05000-1
|
||||
setprop debug.sf.latch_unsignaled 1
|
||||
setprop sys.use_fifo_ui 1
|
||||
setprop media.settings.xml "/vendor/etc/media_profiles_vendor.xml"
|
||||
fi
|
||||
|
||||
# Set props for Vsmart Live's fod
|
||||
|
@ -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;
|
2
sepolicy/property_contexts
Normal file
2
sepolicy/property_contexts
Normal file
@ -0,0 +1,2 @@
|
||||
persist.sys.sf.hs_mode u:object_r:sf_hs_mode_prop:s0
|
||||
hwc.exynos.vsync_mode u:object_r:vendor_hwc_prop:s0
|
@ -2,3 +2,14 @@ type boot_prop, property_type;
|
||||
|
||||
set_prop(system_server, boot_prop);
|
||||
|
||||
type hal_graphics_composer_default, domain;
|
||||
|
||||
type sf_hs_mode_prop, property_type;
|
||||
|
||||
get_prop(hal_graphics_composer_default, sf_hs_mode_prop);
|
||||
|
||||
type vendor_hwc_prop, property_type;
|
||||
|
||||
get_prop(hal_graphics_composer_default, vendor_hwc_prop);
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
19
system.prop
19
system.prop
@ -14,3 +14,22 @@ 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
|
||||
bluetooth.profile.a2dp.source.enabled?=true
|
||||
bluetooth.profile.avrcp.target.enabled?=true
|
||||
bluetooth.profile.bas.client.enabled?=true
|
||||
bluetooth.profile.gatt.enabled?=true
|
||||
bluetooth.profile.hfp.ag.enabled?=true
|
||||
bluetooth.profile.hid.device.enabled?=true
|
||||
bluetooth.profile.hid.host.enabled?=true
|
||||
bluetooth.profile.map.server.enabled?=true
|
||||
bluetooth.profile.opp.enabled?=true
|
||||
bluetooth.profile.pan.nap.enabled?=true
|
||||
bluetooth.profile.pan.panu.enabled?=true
|
||||
bluetooth.profile.pbap.server.enabled?=true
|
||||
bluetooth.profile.sap.server.enabled?=true
|
||||
|
1
vndk.rc
1
vndk.rc
@ -3,7 +3,6 @@ on post-fs
|
||||
exec - root -- /system/bin/rw-system.sh
|
||||
mount none /system/etc/usb_audio_policy_configuration.xml /vendor/etc/usb_audio_policy_configuration.xml bind
|
||||
setprop ro.vndk.version ${persist.sys.vndk}
|
||||
export LD_PRELOAD :
|
||||
|
||||
on property:vold.decrypt=trigger_restart_framework
|
||||
exec - root -- /system/bin/phh-on-data.sh
|
||||
|
Loading…
Reference in New Issue
Block a user