Change-Id: I37dac8d63dff454a396e9c14a1062db7f4b2588dfourteen-wip
parent
2f752116d0
commit
1c47751006
@ -0,0 +1,145 @@ |
|||||||
|
filegroup { |
||||||
|
name: "android.hardware.audio-impl_sm7125_srcs", |
||||||
|
srcs: [ |
||||||
|
"Device.cpp", |
||||||
|
"DevicesFactory.cpp", |
||||||
|
"ParametersUtil.cpp", |
||||||
|
"PrimaryDevice.cpp", |
||||||
|
"Stream.cpp", |
||||||
|
"StreamIn.cpp", |
||||||
|
"StreamOut.cpp", |
||||||
|
], |
||||||
|
} |
||||||
|
|
||||||
|
cc_library_headers { |
||||||
|
name: "android.hardware.audio-impl_sm7125_headers", |
||||||
|
proprietary: true, |
||||||
|
vendor: true, |
||||||
|
export_include_dirs: ["include"], |
||||||
|
} |
||||||
|
|
||||||
|
cc_defaults { |
||||||
|
name: "android.hardware.audio-impl_sm7125", |
||||||
|
relative_install_path: "hw", |
||||||
|
proprietary: true, |
||||||
|
vendor: true, |
||||||
|
srcs: [":android.hardware.audio-impl_sm7125_srcs"], |
||||||
|
|
||||||
|
defaults: ["hidl_defaults"], |
||||||
|
|
||||||
|
static_libs: [ |
||||||
|
"libaudiofoundation", |
||||||
|
], |
||||||
|
|
||||||
|
shared_libs: [ |
||||||
|
"libbase", |
||||||
|
"libcutils", |
||||||
|
"libfmq", |
||||||
|
"libhardware", |
||||||
|
"libhidlbase", |
||||||
|
"liblog", |
||||||
|
"libmedia_helper", |
||||||
|
"libmediautils_vendor", |
||||||
|
"libmemunreachable", |
||||||
|
"libutils", |
||||||
|
"android.hardware.audio.common-util", |
||||||
|
], |
||||||
|
|
||||||
|
header_libs: [ |
||||||
|
"android.hardware.audio-impl_sm7125_headers", |
||||||
|
"android.hardware.audio.common.util@all-versions", |
||||||
|
"libaudioclient_headers", |
||||||
|
"libaudioutils_headers", |
||||||
|
"libaudio_system_headers", |
||||||
|
"libhardware_headers", |
||||||
|
"libmedia_headers", |
||||||
|
"libmediautils_headers", |
||||||
|
], |
||||||
|
|
||||||
|
export_header_lib_headers: [ |
||||||
|
"android.hardware.audio-impl_sm7125_headers", |
||||||
|
], |
||||||
|
} |
||||||
|
|
||||||
|
cc_library_shared { |
||||||
|
name: "android.hardware.audio@2.0-impl.sm7125", |
||||||
|
defaults: ["android.hardware.audio-impl_sm7125"], |
||||||
|
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.sm7125", |
||||||
|
defaults: ["android.hardware.audio-impl_sm7125"], |
||||||
|
|
||||||
|
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.sm7125", |
||||||
|
defaults: ["android.hardware.audio-impl_sm7125"], |
||||||
|
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.sm7125", |
||||||
|
defaults: ["android.hardware.audio-impl_sm7125"], |
||||||
|
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.sm7125", |
||||||
|
defaults: ["android.hardware.audio-impl_sm7125"], |
||||||
|
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", |
||||||
|
], |
||||||
|
} |
@ -0,0 +1,582 @@ |
|||||||
|
/*
|
||||||
|
* 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 "core/default/Device.h" |
||||||
|
#include "common/all-versions/default/EffectMap.h" |
||||||
|
#include "core/default/StreamIn.h" |
||||||
|
#include "core/default/StreamOut.h" |
||||||
|
#include "core/default/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::CPP_VERSION::implementation::HidlUtils; |
||||||
|
|
||||||
|
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 (!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::openOutputStreamImpl(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::openInputStreamImpl( |
||||||
|
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] = |
||||||
|
openOutputStreamImpl(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] = |
||||||
|
openInputStreamImpl(ioHandle, device, config, flags, source, &suggestedConfig); |
||||||
|
_hidl_cb(result, streamIn, suggestedConfig); |
||||||
|
return Void(); |
||||||
|
} |
||||||
|
|
||||||
|
#elif MAJOR_VERSION >= 4 |
||||||
|
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) { |
||||||
|
#if MAJOR_VERSION <= 6 |
||||||
|
if (status_t status = CoreUtils::sourceMetadataToHal(sourceMetadata, nullptr); |
||||||
|
status != NO_ERROR) { |
||||||
|
#else |
||||||
|
if (status_t status = CoreUtils::sourceMetadataToHalV7(sourceMetadata, |
||||||
|
false /*ignoreNonVendorTags*/, nullptr); |
||||||
|
status != NO_ERROR) { |
||||||
|
#endif |
||||||
|
_hidl_cb(analyzeStatus("sourceMetadataToHal", status), nullptr, AudioConfig{}); |
||||||
|
return Void(); |
||||||
|
} |
||||||
|
AudioConfig suggestedConfig; |
||||||
|
auto [result, streamOut] = |
||||||
|
openOutputStreamImpl(ioHandle, device, config, flags, &suggestedConfig); |
||||||
|
if (streamOut) { |
||||||
|
streamOut->updateSourceMetadata(sourceMetadata); |
||||||
|
} |
||||||
|
_hidl_cb(result, streamOut, suggestedConfig); |
||||||
|
return Void(); |
||||||
|
} |
||||||
|
|
||||||
|
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) { |
||||||
|
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"); |
||||||
|
_hidl_cb(Result::INVALID_ARGUMENTS, nullptr, AudioConfig{}); |
||||||
|
return Void(); |
||||||
|
} |
||||||
|
#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 |
||||||
|
_hidl_cb(analyzeStatus("sinkMetadataToHal", status), nullptr, AudioConfig{}); |
||||||
|
return Void(); |
||||||
|
} |
||||||
|
// Pick the first one as the main.
|
||||||
|
AudioSource source = sinkMetadata.tracks[0].source; |
||||||
|
AudioConfig suggestedConfig; |
||||||
|
auto [result, streamIn] = |
||||||
|
openInputStreamImpl(ioHandle, device, config, flags, source, &suggestedConfig); |
||||||
|
if (streamIn) { |
||||||
|
streamIn->updateSinkMetadata(sinkMetadata); |
||||||
|
} |
||||||
|
_hidl_cb(result, streamIn, suggestedConfig); |
||||||
|
return Void(); |
||||||
|
} |
||||||
|
#endif /* MAJOR_VERSION */ |
||||||
|
|
||||||
|
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) { |
||||||
|
if (halGetter == nullptr) { |
||||||
|
_hidl_cb(Result::NOT_SUPPORTED, port); |
||||||
|
return Void(); |
||||||
|
} |
||||||
|
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 |
||||||
|
|
||||||
|
} // namespace implementation
|
||||||
|
} // namespace CPP_VERSION
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace hardware
|
||||||
|
} // namespace android
|
@ -0,0 +1,120 @@ |
|||||||
|
/*
|
||||||
|
* 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 "core/default/DevicesFactory.h" |
||||||
|
#include "core/default/Device.h" |
||||||
|
#include "core/default/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(); |
||||||
|
} |
||||||
|
#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(moduleName.c_str(), _hidl_cb); |
||||||
|
} |
||||||
|
Return<void> DevicesFactory::openPrimaryDevice(openPrimaryDevice_cb _hidl_cb) { |
||||||
|
return openDevice<PrimaryDevice>(AUDIO_HARDWARE_MODULE_ID_PRIMARY, _hidl_cb); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
Return<void> DevicesFactory::openDevice(const char* moduleName, openDevice_cb _hidl_cb) { |
||||||
|
return openDevice<implementation::Device>(moduleName, _hidl_cb); |
||||||
|
} |
||||||
|
|
||||||
|
template <class DeviceShim, class Callback> |
||||||
|
Return<void> DevicesFactory::openDevice(const char* moduleName, Callback _hidl_cb) { |
||||||
|
audio_hw_device_t* halDevice; |
||||||
|
Result retval(Result::INVALID_ARGUMENTS); |
||||||
|
sp<DeviceShim> result; |
||||||
|
int halStatus = loadAudioInterface(moduleName, &halDevice); |
||||||
|
if (halStatus == OK) { |
||||||
|
result = new DeviceShim(halDevice); |
||||||
|
retval = Result::OK; |
||||||
|
} else if (halStatus == -EINVAL) { |
||||||
|
retval = Result::NOT_INITIALIZED; |
||||||
|
} |
||||||
|
_hidl_cb(retval, result); |
||||||
|
return Void(); |
||||||
|
} |
||||||
|
|
||||||
|
// static
|
||||||
|
int DevicesFactory::loadAudioInterface(const char* if_name, audio_hw_device_t** dev) { |
||||||
|
const hw_module_t* mod; |
||||||
|
int rc; |
||||||
|
|
||||||
|
rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod); |
||||||
|
if (rc) { |
||||||
|
ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID, |
||||||
|
if_name, strerror(-rc)); |
||||||
|
goto out; |
||||||
|
} |
||||||
|
rc = audio_hw_device_open(mod, dev); |
||||||
|
if (rc) { |
||||||
|
ALOGE("%s couldn't open audio hw device in %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID, |
||||||
|
if_name, strerror(-rc)); |
||||||
|
goto out; |
||||||
|
} |
||||||
|
if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) { |
||||||
|
ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version); |
||||||
|
rc = -EINVAL; |
||||||
|
audio_hw_device_close(*dev); |
||||||
|
goto out; |
||||||
|
} |
||||||
|
return OK; |
||||||
|
|
||||||
|
out: |
||||||
|
*dev = NULL; |
||||||
|
return rc; |
||||||
|
} |
||||||
|
|
||||||
|
IDevicesFactory* HIDL_FETCH_IDevicesFactory(const char* name) { |
||||||
|
return strcmp(name, "default") == 0 ? new DevicesFactory() : nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace implementation
|
||||||
|
} // namespace CPP_VERSION
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace hardware
|
||||||
|
} // namespace android
|
@ -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 "core/default/ParametersUtil.h" |
||||||
|
#include "core/default/Util.h" |
||||||
|
|
||||||
|
#include <system/audio.h> |
||||||
|
|
||||||
|
#include <util/CoreUtils.h> |
||||||
|
|
||||||
|
namespace android { |
||||||
|
namespace hardware { |
||||||
|
namespace audio { |
||||||
|
namespace 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 CPP_VERSION
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace hardware
|
||||||
|
} // namespace android
|
@ -0,0 +1,351 @@ |
|||||||
|
/*
|
||||||
|
* 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 "core/default/PrimaryDevice.h" |
||||||
|
#include "core/default/Util.h" |
||||||
|
|
||||||
|
#if MAJOR_VERSION >= 4 |
||||||
|
#include <cmath> |
||||||
|
#endif |
||||||
|
|
||||||
|
namespace android { |
||||||
|
namespace hardware { |
||||||
|
namespace audio { |
||||||
|
namespace CPP_VERSION { |
||||||
|
namespace implementation { |
||||||
|
|
||||||
|
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 (!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 (!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
|
@ -0,0 +1,453 @@ |
|||||||
|
/*
|
||||||
|
* 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 "core/default/Stream.h" |
||||||
|
#include "common/all-versions/HidlSupport.h" |
||||||
|
#include "common/all-versions/default/EffectMap.h" |
||||||
|
#include "core/default/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::CPP_VERSION::implementation::HidlUtils; |
||||||
|
using ::android::hardware::audio::common::utils::splitString; |
||||||
|
|
||||||
|
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
|
@ -0,0 +1,603 @@ |
|||||||
|
/*
|
||||||
|
* 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 "core/default/StreamIn.h" |
||||||
|
#include "core/default/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::CPP_VERSION::implementation::HidlUtils; |
||||||
|
|
||||||
|
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 (!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
|
@ -0,0 +1,766 @@ |
|||||||
|
/*
|
||||||
|
* 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 "core/default/StreamOut.h" |
||||||
|
#include "core/default/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::CPP_VERSION::implementation::HidlUtils; |
||||||
|
|
||||||
|
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 (!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; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
} // namespace implementation
|
||||||
|
} // namespace CPP_VERSION
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace hardware
|
||||||
|
} // namespace android
|
@ -0,0 +1,48 @@ |
|||||||
|
/*
|
||||||
|
* 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_CONVERSIONS_H_ |
||||||
|
#define ANDROID_HARDWARE_AUDIO_CONVERSIONS_H_ |
||||||
|
|
||||||
|
#include PATH(android/hardware/audio/FILE_VERSION/types.h) |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <system/audio.h> |
||||||
|
|
||||||
|
namespace android { |
||||||
|
namespace hardware { |
||||||
|
namespace audio { |
||||||
|
namespace CPP_VERSION { |
||||||
|
namespace implementation { |
||||||
|
|
||||||
|
using namespace ::android::hardware::audio::common::CPP_VERSION; |
||||||
|
using namespace ::android::hardware::audio::CPP_VERSION; |
||||||
|
|
||||||
|
std::string deviceAddressToHal(const DeviceAddress& address); |
||||||
|
|
||||||
|
#if MAJOR_VERSION >= 4 |
||||||
|
bool halToMicrophoneCharacteristics(MicrophoneInfo* pDst, |
||||||
|
const struct audio_microphone_characteristic_t& src); |
||||||
|
#endif |
||||||
|
|
||||||
|
} // namespace implementation
|
||||||
|
} // namespace CPP_VERSION
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace hardware
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
|
#endif // ANDROID_HARDWARE_AUDIO_CONVERSIONS_H_
|
@ -0,0 +1,172 @@ |
|||||||
|
/*
|
||||||
|
* 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 namespace ::android::hardware::audio::common::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>> openOutputStreamImpl(int32_t ioHandle, |
||||||
|
const DeviceAddress& device, |
||||||
|
const AudioConfig& config, |
||||||
|
const AudioOutputFlags& flags, |
||||||
|
AudioConfig* suggestedConfig); |
||||||
|
std::tuple<Result, sp<IStreamIn>> openInputStreamImpl( |
||||||
|
int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config, |
||||||
|
const AudioInputFlags& flags, AudioSource source, AudioConfig* suggestedConfig); |
||||||
|
|
||||||
|
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, |
||||||
|
createAudioPatch_cb _hidl_cb) 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
|
@ -0,0 +1,64 @@ |
|||||||
|
/*
|
||||||
|
* 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 |
||||||
|
|
||||||
|
private: |
||||||
|
template <class DeviceShim, class Callback> |
||||||
|
Return<void> openDevice(const char* moduleName, Callback _hidl_cb); |
||||||
|
Return<void> openDevice(const char* moduleName, openDevice_cb _hidl_cb); |
||||||
|
|
||||||
|
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
|
@ -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_PARAMETERS_UTIL_H_ |
||||||
|
#define ANDROID_HARDWARE_AUDIO_PARAMETERS_UTIL_H_ |
||||||
|
|
||||||
|
#include PATH(android/hardware/audio/FILE_VERSION/types.h) |
||||||
|
|
||||||
|
#include <functional> |
||||||
|
#include <memory> |
||||||
|
|
||||||
|
#include <hidl/HidlSupport.h> |
||||||
|
#include <media/AudioParameter.h> |
||||||
|
|
||||||
|
namespace android { |
||||||
|
namespace hardware { |
||||||
|
namespace audio { |
||||||
|
namespace CPP_VERSION { |
||||||
|
namespace implementation { |
||||||
|
|
||||||
|
using ::android::hardware::hidl_string; |
||||||
|
using ::android::hardware::hidl_vec; |
||||||
|
using namespace ::android::hardware::audio::common::CPP_VERSION; |
||||||
|
using namespace ::android::hardware::audio::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 CPP_VERSION
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace hardware
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
|
#endif // ANDROID_HARDWARE_AUDIO_PARAMETERS_UTIL_H_
|
@ -0,0 +1,151 @@ |
|||||||
|
/*
|
||||||
|
* 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::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 |
||||||
|
|
||||||
|
private: |
||||||
|
sp<Device> mDevice; |
||||||
|
|
||||||
|
virtual ~PrimaryDevice(); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace implementation
|
||||||
|
} // namespace CPP_VERSION
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace hardware
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
|
#endif // ANDROID_HARDWARE_AUDIO_PRIMARYDEVICE_H
|
@ -0,0 +1,231 @@ |
|||||||
|
/*
|
||||||
|
* 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 |
||||||
|
|
||||||
|
#include PATH(android/hardware/audio/FILE_VERSION/IStream.h) |
||||||
|
|
||||||
|
#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; |
||||||
|
#if MAJOR_VERSION <= 6 |
||||||
|
using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioChannelBitfield; |
||||||
|
#endif |
||||||
|
using namespace ::android::hardware::audio::common::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
|
@ -0,0 +1,159 @@ |
|||||||
|
/*
|
||||||
|
* 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 |
||||||
|
|
||||||
|
#include PATH(android/hardware/audio/FILE_VERSION/IStreamIn.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 <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::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
|
@ -0,0 +1,188 @@ |
|||||||
|
/*
|
||||||
|
* 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::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); |
||||||
|
#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; |
||||||
|
#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); |
||||||
|
#endif |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace implementation
|
||||||
|
} // namespace CPP_VERSION
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace hardware
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
|
#endif // ANDROID_HARDWARE_AUDIO_STREAMOUT_H
|
@ -0,0 +1,80 @@ |
|||||||
|
/*
|
||||||
|
* 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 |
||||||
|
|
||||||
|
#include PATH(android/hardware/audio/FILE_VERSION/types.h) |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include <system/audio.h> |
||||||
|
|
||||||
|
namespace android { |
||||||
|
namespace hardware { |
||||||
|
namespace audio { |
||||||
|
namespace CPP_VERSION { |
||||||
|
namespace implementation { |
||||||
|
|
||||||
|
using namespace ::android::hardware::audio::common::CPP_VERSION; |
||||||
|
using namespace ::android::hardware::audio::CPP_VERSION; |
||||||
|
|
||||||
|
/** @return true if gain is between 0 and 1 included. */ |
||||||
|
constexpr bool isGainNormalized(float gain) { |
||||||
|
return gain >= 0.0 && gain <= 1.0; |
||||||
|
} |
||||||
|
|
||||||
|
namespace util { |
||||||
|
|
||||||
|
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 CPP_VERSION
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace hardware
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
|
#endif // ANDROID_HARDWARE_AUDIO_UTIL_H
|
Loading…
Reference in new issue