From c337a8799b8cc3029aaee2addee9c4212e812b3c Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 7 Jul 2023 12:01:17 -0700 Subject: [PATCH 1/3] audio: Move tinyALSA-specific code to Module/StreamAlsa Extract code interacting with tinyALSA which is not specific to USB into "abstract" module and stream implementations ModuleAlsa and StreamAlsa. Also, move utility code which does not need module or stream context into Utils. This facilitates implementation of the CF core HAL which also uses tinyALSA, allowing to share common code. Bug: 264712385 Test: atest VtsHalAudioCoreTargetTest Change-Id: I2134b15e970c78e8a48b254e15199b8207a8ab34 --- audio/aidl/default/Android.bp | 5 +- audio/aidl/default/alsa/Mixer.cpp | 154 +++++++++ audio/aidl/default/alsa/Mixer.h | 82 +++++ audio/aidl/default/alsa/ModuleAlsa.cpp | 67 ++++ audio/aidl/default/alsa/StreamAlsa.cpp | 103 ++++++ audio/aidl/default/alsa/Utils.cpp | 293 ++++++++++++++++++ audio/aidl/default/alsa/Utils.h | 70 +++++ .../default/include/core-impl/ModuleAlsa.h | 38 +++ .../default/include/core-impl/ModuleUsb.h | 6 +- .../default/include/core-impl/StreamAlsa.h | 54 ++++ .../default/include/core-impl/StreamUsb.h | 33 +- audio/aidl/default/usb/ModuleUsb.cpp | 103 +----- audio/aidl/default/usb/StreamUsb.cpp | 160 ++-------- .../aidl/default/usb/UsbAlsaMixerControl.cpp | 140 +-------- audio/aidl/default/usb/UsbAlsaMixerControl.h | 62 +--- audio/aidl/default/usb/UsbAlsaUtils.cpp | 181 ----------- audio/aidl/default/usb/UsbAlsaUtils.h | 39 --- 17 files changed, 923 insertions(+), 667 deletions(-) create mode 100644 audio/aidl/default/alsa/Mixer.cpp create mode 100644 audio/aidl/default/alsa/Mixer.h create mode 100644 audio/aidl/default/alsa/ModuleAlsa.cpp create mode 100644 audio/aidl/default/alsa/StreamAlsa.cpp create mode 100644 audio/aidl/default/alsa/Utils.cpp create mode 100644 audio/aidl/default/alsa/Utils.h create mode 100644 audio/aidl/default/include/core-impl/ModuleAlsa.h create mode 100644 audio/aidl/default/include/core-impl/StreamAlsa.h delete mode 100644 audio/aidl/default/usb/UsbAlsaUtils.cpp delete mode 100644 audio/aidl/default/usb/UsbAlsaUtils.h diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp index e9294cf930..65bbe9e9a1 100644 --- a/audio/aidl/default/Android.bp +++ b/audio/aidl/default/Android.bp @@ -77,6 +77,10 @@ cc_library { "Stream.cpp", "StreamStub.cpp", "Telephony.cpp", + "alsa/Mixer.cpp", + "alsa/ModuleAlsa.cpp", + "alsa/StreamAlsa.cpp", + "alsa/Utils.cpp", "r_submix/ModuleRemoteSubmix.cpp", "r_submix/RemoteSubmixUtils.cpp", "r_submix/SubmixRoute.cpp", @@ -84,7 +88,6 @@ cc_library { "usb/ModuleUsb.cpp", "usb/StreamUsb.cpp", "usb/UsbAlsaMixerControl.cpp", - "usb/UsbAlsaUtils.cpp", ], generated_sources: [ "audio_policy_configuration_aidl_default", diff --git a/audio/aidl/default/alsa/Mixer.cpp b/audio/aidl/default/alsa/Mixer.cpp new file mode 100644 index 0000000000..f0393e3a7e --- /dev/null +++ b/audio/aidl/default/alsa/Mixer.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AHAL_AlsaMixer" +#include + +#include + +#include + +#include "Mixer.h" + +namespace aidl::android::hardware::audio::core::alsa { + +//----------------------------------------------------------------------------- + +MixerControl::MixerControl(struct mixer_ctl* ctl) + : mCtl(ctl), + mNumValues(mixer_ctl_get_num_values(ctl)), + mMinValue(mixer_ctl_get_range_min(ctl)), + mMaxValue(mixer_ctl_get_range_max(ctl)) {} + +unsigned int MixerControl::getNumValues() const { + return mNumValues; +} + +int MixerControl::getMaxValue() const { + return mMaxValue; +} + +int MixerControl::getMinValue() const { + return mMinValue; +} + +int MixerControl::setArray(const void* array, size_t count) { + const std::lock_guard guard(mLock); + return mixer_ctl_set_array(mCtl, array, count); +} + +//----------------------------------------------------------------------------- + +// static +const std::map> + Mixer::kPossibleControls = { + {Mixer::MASTER_SWITCH, {{"Master Playback Switch", MIXER_CTL_TYPE_BOOL}}}, + {Mixer::MASTER_VOLUME, {{"Master Playback Volume", MIXER_CTL_TYPE_INT}}}, + {Mixer::HW_VOLUME, + {{"Headphone Playback Volume", MIXER_CTL_TYPE_INT}, + {"Headset Playback Volume", MIXER_CTL_TYPE_INT}, + {"PCM Playback Volume", MIXER_CTL_TYPE_INT}}}}; + +// static +std::map> Mixer::initializeMixerControls( + struct mixer* mixer) { + std::map> mixerControls; + std::string mixerCtlNames; + for (const auto& [control, possibleCtls] : kPossibleControls) { + for (const auto& [ctlName, expectedCtlType] : possibleCtls) { + struct mixer_ctl* ctl = mixer_get_ctl_by_name(mixer, ctlName.c_str()); + if (ctl != nullptr && mixer_ctl_get_type(ctl) == expectedCtlType) { + mixerControls.emplace(control, std::make_unique(ctl)); + if (!mixerCtlNames.empty()) { + mixerCtlNames += ","; + } + mixerCtlNames += ctlName; + break; + } + } + } + LOG(DEBUG) << __func__ << ": available mixer control names=[" << mixerCtlNames << "]"; + return mixerControls; +} + +Mixer::Mixer(struct mixer* mixer) + : mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {} + +Mixer::~Mixer() { + mixer_close(mMixer); +} + +namespace { + +int volumeFloatToInteger(float fValue, int maxValue, int minValue) { + return minValue + std::ceil((maxValue - minValue) * fValue); +} + +} // namespace + +ndk::ScopedAStatus Mixer::setMasterMute(bool muted) { + auto it = mMixerControls.find(Mixer::MASTER_SWITCH); + if (it == mMixerControls.end()) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + const int numValues = it->second->getNumValues(); + std::vector values(numValues, muted ? 0 : 1); + if (int err = it->second->setArray(values.data(), numValues); err != 0) { + LOG(ERROR) << __func__ << ": failed to set master mute, err=" << err; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Mixer::setMasterVolume(float volume) { + auto it = mMixerControls.find(Mixer::MASTER_VOLUME); + if (it == mMixerControls.end()) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + const int numValues = it->second->getNumValues(); + std::vector values(numValues, volumeFloatToInteger(volume, it->second->getMaxValue(), + it->second->getMinValue())); + if (int err = it->second->setArray(values.data(), numValues); err != 0) { + LOG(ERROR) << __func__ << ": failed to set master volume, err=" << err; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Mixer::setVolumes(const std::vector& volumes) { + auto it = mMixerControls.find(Mixer::HW_VOLUME); + if (it == mMixerControls.end()) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + const int numValues = it->second->getNumValues(); + if (numValues < 0) { + LOG(FATAL) << __func__ << ": negative number of values: " << numValues; + } + const int maxValue = it->second->getMaxValue(); + const int minValue = it->second->getMinValue(); + std::vector values; + size_t i = 0; + for (; i < static_cast(numValues) && i < values.size(); ++i) { + values.emplace_back(volumeFloatToInteger(volumes[i], maxValue, minValue)); + } + if (int err = it->second->setArray(values.data(), values.size()); err != 0) { + LOG(ERROR) << __func__ << ": failed to set volume, err=" << err; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + return ndk::ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::audio::core::alsa diff --git a/audio/aidl/default/alsa/Mixer.h b/audio/aidl/default/alsa/Mixer.h new file mode 100644 index 0000000000..de9e6f42cd --- /dev/null +++ b/audio/aidl/default/alsa/Mixer.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +extern "C" { +#include +} + +namespace aidl::android::hardware::audio::core::alsa { + +class MixerControl { + public: + explicit MixerControl(struct mixer_ctl* ctl); + + unsigned int getNumValues() const; + int getMaxValue() const; + int getMinValue() const; + int setArray(const void* array, size_t count); + + private: + std::mutex mLock; + // The mixer_ctl object is owned by ALSA and will be released when the mixer is closed. + struct mixer_ctl* mCtl GUARDED_BY(mLock); + const unsigned int mNumValues; + const int mMinValue; + const int mMaxValue; +}; + +class Mixer { + public: + explicit Mixer(struct mixer* mixer); + + ~Mixer(); + + bool isValid() const { return mMixer != nullptr; } + + ndk::ScopedAStatus setMasterMute(bool muted); + ndk::ScopedAStatus setMasterVolume(float volume); + ndk::ScopedAStatus setVolumes(const std::vector& volumes); + + private: + enum Control { + MASTER_SWITCH, + MASTER_VOLUME, + HW_VOLUME, + }; + using ControlNamesAndExpectedCtlType = std::pair; + static const std::map> kPossibleControls; + static std::map> initializeMixerControls( + struct mixer* mixer); + + // The mixer object is owned by ALSA and will be released when the mixer is closed. + struct mixer* mMixer; + // `mMixerControls` will only be initialized in constructor. After that, it wil only be + // read but not be modified. + const std::map> mMixerControls; +}; + +} // namespace aidl::android::hardware::audio::core::alsa diff --git a/audio/aidl/default/alsa/ModuleAlsa.cpp b/audio/aidl/default/alsa/ModuleAlsa.cpp new file mode 100644 index 0000000000..8e75d56843 --- /dev/null +++ b/audio/aidl/default/alsa/ModuleAlsa.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AHAL_ModuleAlsa" + +#include + +#include + +#include "Utils.h" +#include "core-impl/ModuleAlsa.h" + +extern "C" { +#include "alsa_device_profile.h" +} + +using aidl::android::media::audio::common::AudioChannelLayout; +using aidl::android::media::audio::common::AudioFormatType; +using aidl::android::media::audio::common::AudioPort; +using aidl::android::media::audio::common::AudioProfile; + +namespace aidl::android::hardware::audio::core { + +ndk::ScopedAStatus ModuleAlsa::populateConnectedDevicePort(AudioPort* audioPort) { + auto deviceProfile = alsa::getDeviceProfile(*audioPort); + if (!deviceProfile.has_value()) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + auto profile = alsa::readAlsaDeviceInfo(*deviceProfile); + if (!profile.has_value()) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + + std::vector channels = alsa::getChannelMasksFromProfile(&profile.value()); + std::vector sampleRates = alsa::getSampleRatesFromProfile(&profile.value()); + + for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) && + profile->formats[i] != PCM_FORMAT_INVALID; + ++i) { + auto audioFormatDescription = + alsa::c2aidl_pcm_format_AudioFormatDescription(profile->formats[i]); + if (audioFormatDescription.type == AudioFormatType::DEFAULT) { + LOG(WARNING) << __func__ << ": unknown pcm type=" << profile->formats[i]; + continue; + } + AudioProfile audioProfile = {.format = audioFormatDescription, + .channelMasks = channels, + .sampleRates = sampleRates}; + audioPort->profiles.push_back(std::move(audioProfile)); + } + return ndk::ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/alsa/StreamAlsa.cpp b/audio/aidl/default/alsa/StreamAlsa.cpp new file mode 100644 index 0000000000..ecb3c78a65 --- /dev/null +++ b/audio/aidl/default/alsa/StreamAlsa.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#define LOG_TAG "AHAL_StreamAlsa" +#include + +#include +#include + +#include "core-impl/StreamAlsa.h" + +namespace aidl::android::hardware::audio::core { + +StreamAlsa::StreamAlsa(const Metadata& metadata, StreamContext&& context) + : StreamCommonImpl(metadata, std::move(context)), + mFrameSizeBytes(getContext().getFrameSize()), + mIsInput(isInput(metadata)), + mConfig(alsa::getPcmConfig(getContext(), mIsInput)) {} + +::android::status_t StreamAlsa::init() { + return mConfig.has_value() ? ::android::OK : ::android::NO_INIT; +} + +::android::status_t StreamAlsa::standby() { + mAlsaDeviceProxies.clear(); + return ::android::OK; +} + +::android::status_t StreamAlsa::start() { + decltype(mAlsaDeviceProxies) alsaDeviceProxies; + for (const auto& device : getDeviceProfiles()) { + auto profile = alsa::readAlsaDeviceInfo(device); + if (!profile.has_value()) { + LOG(ERROR) << __func__ << ": unable to read device info, device address=" << device; + return ::android::UNKNOWN_ERROR; + } + + auto proxy = alsa::makeDeviceProxy(); + // Always ask for alsa configure as required since the configuration should be supported + // by the connected device. That is guaranteed by `setAudioPortConfig` and `setAudioPatch`. + if (int err = proxy_prepare(proxy.get(), &profile.value(), + const_cast(&mConfig.value()), + true /*require_exact_match*/); + err != 0) { + LOG(ERROR) << __func__ << ": fail to prepare for device address=" << device + << " error=" << err; + return ::android::UNKNOWN_ERROR; + } + if (int err = proxy_open(proxy.get()); err != 0) { + LOG(ERROR) << __func__ << ": failed to open device, address=" << device + << " error=" << err; + return ::android::UNKNOWN_ERROR; + } + alsaDeviceProxies.push_back(std::move(proxy)); + } + mAlsaDeviceProxies = std::move(alsaDeviceProxies); + return ::android::OK; +} + +::android::status_t StreamAlsa::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, + int32_t* latencyMs) { + const size_t bytesToTransfer = frameCount * mFrameSizeBytes; + unsigned maxLatency = 0; + if (mIsInput) { + if (mAlsaDeviceProxies.empty()) { + LOG(FATAL) << __func__ << ": no input devices"; + return ::android::NO_INIT; + } + // For input case, only support single device. + proxy_read(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer); + maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get()); + } else { + for (auto& proxy : mAlsaDeviceProxies) { + proxy_write(proxy.get(), buffer, bytesToTransfer); + maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get())); + } + } + *actualFrameCount = frameCount; + maxLatency = std::min(maxLatency, static_cast(std::numeric_limits::max())); + *latencyMs = maxLatency; + return ::android::OK; +} + +void StreamAlsa::shutdown() { + mAlsaDeviceProxies.clear(); +} + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/alsa/Utils.cpp b/audio/aidl/default/alsa/Utils.cpp new file mode 100644 index 0000000000..162f8529cf --- /dev/null +++ b/audio/aidl/default/alsa/Utils.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#define LOG_TAG "AHAL_AlsaUtils" +#include +#include +#include +#include + +#include "Utils.h" +#include "core-impl/utils.h" + +using aidl::android::hardware::audio::common::getChannelCount; +using aidl::android::media::audio::common::AudioChannelLayout; +using aidl::android::media::audio::common::AudioDeviceAddress; +using aidl::android::media::audio::common::AudioFormatDescription; +using aidl::android::media::audio::common::AudioFormatType; +using aidl::android::media::audio::common::AudioIoFlags; +using aidl::android::media::audio::common::AudioPortExt; +using aidl::android::media::audio::common::PcmType; + +namespace aidl::android::hardware::audio::core::alsa { + +namespace { + +using AudioChannelCountToMaskMap = std::map; +using AudioFormatDescToPcmFormatMap = std::map; +using PcmFormatToAudioFormatDescMap = std::map; + +AudioChannelLayout getInvalidChannelLayout() { + static const AudioChannelLayout invalidChannelLayout = + AudioChannelLayout::make(0); + return invalidChannelLayout; +} + +static AudioChannelCountToMaskMap make_ChannelCountToMaskMap( + const std::set& channelMasks) { + AudioChannelCountToMaskMap channelMaskToCountMap; + for (const auto& channelMask : channelMasks) { + channelMaskToCountMap.emplace(getChannelCount(channelMask), channelMask); + } + return channelMaskToCountMap; +} + +#define DEFINE_CHANNEL_LAYOUT_MASK(n) \ + AudioChannelLayout::make(AudioChannelLayout::LAYOUT_##n) + +const AudioChannelCountToMaskMap& getSupportedChannelOutLayoutMap() { + static const std::set supportedOutChannelLayouts = { + DEFINE_CHANNEL_LAYOUT_MASK(MONO), DEFINE_CHANNEL_LAYOUT_MASK(STEREO), + DEFINE_CHANNEL_LAYOUT_MASK(2POINT1), DEFINE_CHANNEL_LAYOUT_MASK(QUAD), + DEFINE_CHANNEL_LAYOUT_MASK(PENTA), DEFINE_CHANNEL_LAYOUT_MASK(5POINT1), + DEFINE_CHANNEL_LAYOUT_MASK(6POINT1), DEFINE_CHANNEL_LAYOUT_MASK(7POINT1), + DEFINE_CHANNEL_LAYOUT_MASK(7POINT1POINT4), DEFINE_CHANNEL_LAYOUT_MASK(22POINT2), + }; + static const AudioChannelCountToMaskMap outLayouts = + make_ChannelCountToMaskMap(supportedOutChannelLayouts); + return outLayouts; +} + +const AudioChannelCountToMaskMap& getSupportedChannelInLayoutMap() { + static const std::set supportedInChannelLayouts = { + DEFINE_CHANNEL_LAYOUT_MASK(MONO), + DEFINE_CHANNEL_LAYOUT_MASK(STEREO), + }; + static const AudioChannelCountToMaskMap inLayouts = + make_ChannelCountToMaskMap(supportedInChannelLayouts); + return inLayouts; +} + +#undef DEFINE_CHANNEL_LAYOUT_MASK +#define DEFINE_CHANNEL_INDEX_MASK(n) \ + AudioChannelLayout::make(AudioChannelLayout::INDEX_MASK_##n) + +const AudioChannelCountToMaskMap& getSupportedChannelIndexLayoutMap() { + static const std::set supportedIndexChannelLayouts = { + DEFINE_CHANNEL_INDEX_MASK(1), DEFINE_CHANNEL_INDEX_MASK(2), + DEFINE_CHANNEL_INDEX_MASK(3), DEFINE_CHANNEL_INDEX_MASK(4), + DEFINE_CHANNEL_INDEX_MASK(5), DEFINE_CHANNEL_INDEX_MASK(6), + DEFINE_CHANNEL_INDEX_MASK(7), DEFINE_CHANNEL_INDEX_MASK(8), + DEFINE_CHANNEL_INDEX_MASK(9), DEFINE_CHANNEL_INDEX_MASK(10), + DEFINE_CHANNEL_INDEX_MASK(11), DEFINE_CHANNEL_INDEX_MASK(12), + DEFINE_CHANNEL_INDEX_MASK(13), DEFINE_CHANNEL_INDEX_MASK(14), + DEFINE_CHANNEL_INDEX_MASK(15), DEFINE_CHANNEL_INDEX_MASK(16), + DEFINE_CHANNEL_INDEX_MASK(17), DEFINE_CHANNEL_INDEX_MASK(18), + DEFINE_CHANNEL_INDEX_MASK(19), DEFINE_CHANNEL_INDEX_MASK(20), + DEFINE_CHANNEL_INDEX_MASK(21), DEFINE_CHANNEL_INDEX_MASK(22), + DEFINE_CHANNEL_INDEX_MASK(23), DEFINE_CHANNEL_INDEX_MASK(24), + }; + static const AudioChannelCountToMaskMap indexLayouts = + make_ChannelCountToMaskMap(supportedIndexChannelLayouts); + return indexLayouts; +} + +#undef DEFINE_CHANNEL_INDEX_MASK + +AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) { + AudioFormatDescription result; + result.type = type; + return result; +} + +AudioFormatDescription make_AudioFormatDescription(PcmType pcm) { + auto result = make_AudioFormatDescription(AudioFormatType::PCM); + result.pcm = pcm; + return result; +} + +const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() { + static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = { + {make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8}, + {make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE}, + {make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE}, + {make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE}, + {make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE}, + {make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE}, + }; + return formatDescToPcmFormatMap; +} + +static PcmFormatToAudioFormatDescMap make_PcmFormatToAudioFormatDescMap( + const AudioFormatDescToPcmFormatMap& formatDescToPcmFormatMap) { + PcmFormatToAudioFormatDescMap result; + for (const auto& formatPair : formatDescToPcmFormatMap) { + result.emplace(formatPair.second, formatPair.first); + } + return result; +} + +const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() { + static const PcmFormatToAudioFormatDescMap pcmFormatToFormatDescMap = + make_PcmFormatToAudioFormatDescMap(getAudioFormatDescriptorToPcmFormatMap()); + return pcmFormatToFormatDescMap; +} + +} // namespace + +std::ostream& operator<<(std::ostream& os, const DeviceProfile& device) { + return os << "<" << device.card << "," << device.device << ">"; +} + +AudioChannelLayout getChannelLayoutMaskFromChannelCount(unsigned int channelCount, int isInput) { + return findValueOrDefault( + isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(), + channelCount, getInvalidChannelLayout()); +} + +AudioChannelLayout getChannelIndexMaskFromChannelCount(unsigned int channelCount) { + return findValueOrDefault(getSupportedChannelIndexLayoutMap(), channelCount, + getInvalidChannelLayout()); +} + +unsigned int getChannelCountFromChannelMask(const AudioChannelLayout& channelMask, bool isInput) { + switch (channelMask.getTag()) { + case AudioChannelLayout::Tag::layoutMask: { + return findKeyOrDefault( + isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(), + static_cast(getChannelCount(channelMask)), 0u /*defaultValue*/); + } + case AudioChannelLayout::Tag::indexMask: { + return findKeyOrDefault(getSupportedChannelIndexLayoutMap(), + static_cast(getChannelCount(channelMask)), + 0u /*defaultValue*/); + } + case AudioChannelLayout::Tag::none: + case AudioChannelLayout::Tag::invalid: + case AudioChannelLayout::Tag::voiceMask: + default: + return 0; + } +} + +std::vector getChannelMasksFromProfile(const alsa_device_profile* profile) { + const bool isInput = profile->direction == PCM_IN; + std::vector channels; + for (size_t i = 0; i < AUDIO_PORT_MAX_CHANNEL_MASKS && profile->channel_counts[i] != 0; ++i) { + auto layoutMask = + alsa::getChannelLayoutMaskFromChannelCount(profile->channel_counts[i], isInput); + if (layoutMask.getTag() == AudioChannelLayout::Tag::layoutMask) { + channels.push_back(layoutMask); + } + auto indexMask = alsa::getChannelIndexMaskFromChannelCount(profile->channel_counts[i]); + if (indexMask.getTag() == AudioChannelLayout::Tag::indexMask) { + channels.push_back(indexMask); + } + } + return channels; +} + +std::optional getDeviceProfile( + const ::aidl::android::media::audio::common::AudioDevice& audioDevice, bool isInput) { + if (audioDevice.address.getTag() != AudioDeviceAddress::Tag::alsa) { + LOG(ERROR) << __func__ << ": not alsa address: " << audioDevice.toString(); + return std::nullopt; + } + auto& alsaAddress = audioDevice.address.get(); + if (alsaAddress.size() != 2 || alsaAddress[0] < 0 || alsaAddress[1] < 0) { + LOG(ERROR) << __func__ + << ": malformed alsa address: " << ::android::internal::ToString(alsaAddress); + return std::nullopt; + } + return DeviceProfile{.card = alsaAddress[0], + .device = alsaAddress[1], + .direction = isInput ? PCM_IN : PCM_OUT}; +} + +std::optional getDeviceProfile( + const ::aidl::android::media::audio::common::AudioPort& audioPort) { + if (audioPort.ext.getTag() != AudioPortExt::Tag::device) { + LOG(ERROR) << __func__ << ": port id " << audioPort.id << " is not a device port"; + return std::nullopt; + } + auto& devicePort = audioPort.ext.get(); + return getDeviceProfile(devicePort.device, audioPort.flags.getTag() == AudioIoFlags::input); +} + +std::optional getPcmConfig(const StreamContext& context, bool isInput) { + struct pcm_config config; + config.channels = alsa::getChannelCountFromChannelMask(context.getChannelLayout(), isInput); + if (config.channels == 0) { + LOG(ERROR) << __func__ << ": invalid channel=" << context.getChannelLayout().toString(); + return std::nullopt; + } + config.format = alsa::aidl2c_AudioFormatDescription_pcm_format(context.getFormat()); + if (config.format == PCM_FORMAT_INVALID) { + LOG(ERROR) << __func__ << ": invalid format=" << context.getFormat().toString(); + return std::nullopt; + } + config.rate = context.getSampleRate(); + if (config.rate == 0) { + LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate; + return std::nullopt; + } + return config; +} + +std::vector getSampleRatesFromProfile(const alsa_device_profile* profile) { + std::vector sampleRates; + for (int i = 0; i < std::min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) && + profile->sample_rates[i] != 0; + i++) { + sampleRates.push_back(profile->sample_rates[i]); + } + return sampleRates; +} + +DeviceProxy makeDeviceProxy() { + return DeviceProxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) { + if (proxy != nullptr) { + proxy_close(proxy); + delete proxy; + } + }); +} + +std::optional readAlsaDeviceInfo(const DeviceProfile& deviceProfile) { + alsa_device_profile profile; + profile_init(&profile, deviceProfile.direction); + profile.card = deviceProfile.card; + profile.device = deviceProfile.device; + if (!profile_read_device_info(&profile)) { + LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card + << ", device=" << profile.device; + return std::nullopt; + } + return profile; +} + +AudioFormatDescription c2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy) { + return findValueOrDefault(getPcmFormatToAudioFormatDescMap(), legacy, AudioFormatDescription()); +} + +pcm_format aidl2c_AudioFormatDescription_pcm_format(const AudioFormatDescription& aidl) { + return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID); +} + +} // namespace aidl::android::hardware::audio::core::alsa diff --git a/audio/aidl/default/alsa/Utils.h b/audio/aidl/default/alsa/Utils.h new file mode 100644 index 0000000000..c1b9b380c0 --- /dev/null +++ b/audio/aidl/default/alsa/Utils.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "core-impl/Stream.h" + +extern "C" { +#include +#include "alsa_device_profile.h" +#include "alsa_device_proxy.h" +} + +namespace aidl::android::hardware::audio::core::alsa { + +struct DeviceProfile { + int card; + int device; + int direction; /* PCM_OUT or PCM_IN */ +}; +std::ostream& operator<<(std::ostream& os, const DeviceProfile& device); +using DeviceProxyDeleter = std::function; +using DeviceProxy = std::unique_ptr; + +::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount( + unsigned int channelCount, int isInput); +::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount( + unsigned int channelCount); +unsigned int getChannelCountFromChannelMask( + const ::aidl::android::media::audio::common::AudioChannelLayout& channelMask, bool isInput); +std::vector<::aidl::android::media::audio::common::AudioChannelLayout> getChannelMasksFromProfile( + const alsa_device_profile* profile); +std::optional getDeviceProfile( + const ::aidl::android::media::audio::common::AudioDevice& audioDevice, bool isInput); +std::optional getDeviceProfile( + const ::aidl::android::media::audio::common::AudioPort& audioPort); +std::optional getPcmConfig(const StreamContext& context, bool isInput); +std::vector getSampleRatesFromProfile(const alsa_device_profile* profile); +DeviceProxy makeDeviceProxy(); +std::optional readAlsaDeviceInfo(const DeviceProfile& deviceProfile); + +::aidl::android::media::audio::common::AudioFormatDescription +c2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy); +pcm_format aidl2c_AudioFormatDescription_pcm_format( + const ::aidl::android::media::audio::common::AudioFormatDescription& aidl); + +} // namespace aidl::android::hardware::audio::core::alsa diff --git a/audio/aidl/default/include/core-impl/ModuleAlsa.h b/audio/aidl/default/include/core-impl/ModuleAlsa.h new file mode 100644 index 0000000000..5815961f7c --- /dev/null +++ b/audio/aidl/default/include/core-impl/ModuleAlsa.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "core-impl/Module.h" + +namespace aidl::android::hardware::audio::core { + +// This class is intended to be used as a base class for implementations +// that use TinyAlsa. This can be either a primary module or a USB Audio +// module. This class does not define a complete module implementation, +// and should never be used on its own. Derived classes are expected to +// provide necessary overrides for all interface methods omitted here. +class ModuleAlsa : public Module { + public: + explicit ModuleAlsa(Module::Type type) : Module(type) {} + + protected: + // Extension methods of 'Module'. + ndk::ScopedAStatus populateConnectedDevicePort( + ::aidl::android::media::audio::common::AudioPort* audioPort) override; +}; + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/ModuleUsb.h b/audio/aidl/default/include/core-impl/ModuleUsb.h index 5a5429db64..ea7cc48214 100644 --- a/audio/aidl/default/include/core-impl/ModuleUsb.h +++ b/audio/aidl/default/include/core-impl/ModuleUsb.h @@ -16,13 +16,13 @@ #pragma once -#include "core-impl/Module.h" +#include "core-impl/ModuleAlsa.h" namespace aidl::android::hardware::audio::core { -class ModuleUsb : public Module { +class ModuleUsb final : public ModuleAlsa { public: - explicit ModuleUsb(Module::Type type) : Module(type) {} + explicit ModuleUsb(Module::Type type) : ModuleAlsa(type) {} private: // IModule interfaces diff --git a/audio/aidl/default/include/core-impl/StreamAlsa.h b/audio/aidl/default/include/core-impl/StreamAlsa.h new file mode 100644 index 0000000000..edfc72860c --- /dev/null +++ b/audio/aidl/default/include/core-impl/StreamAlsa.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "Stream.h" +#include "alsa/Utils.h" + +namespace aidl::android::hardware::audio::core { + +// This class is intended to be used as a base class for implementations +// that use TinyAlsa. +// This class does not define a complete stream implementation, +// and should never be used on its own. Derived classes are expected to +// provide necessary overrides for all interface methods omitted here. +class StreamAlsa : public StreamCommonImpl { + public: + StreamAlsa(const Metadata& metadata, StreamContext&& context); + // Methods of 'DriverInterface'. + ::android::status_t init() override; + ::android::status_t standby() override; + ::android::status_t start() override; + ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, + int32_t* latencyMs) override; + void shutdown() override; + + protected: + // Called from 'start' to initialize 'mAlsaDeviceProxies', the vector must be non-empty. + virtual std::vector getDeviceProfiles() = 0; + + const size_t mFrameSizeBytes; + const bool mIsInput; + const std::optional mConfig; + // All fields below are only used on the worker thread. + std::vector mAlsaDeviceProxies; +}; + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/StreamUsb.h b/audio/aidl/default/include/core-impl/StreamUsb.h index 8c40782db1..44f742a4dd 100644 --- a/audio/aidl/default/include/core-impl/StreamUsb.h +++ b/audio/aidl/default/include/core-impl/StreamUsb.h @@ -17,55 +17,34 @@ #pragma once #include -#include #include -#include #include #include -#include "core-impl/Stream.h" - -extern "C" { -#include -#include "alsa_device_proxy.h" -} +#include "StreamAlsa.h" namespace aidl::android::hardware::audio::core { -class StreamUsb : public StreamCommonImpl { +class StreamUsb : public StreamAlsa { public: StreamUsb(const Metadata& metadata, StreamContext&& context); // Methods of 'DriverInterface'. - ::android::status_t init() override; ::android::status_t drain(StreamDescriptor::DrainMode) override; ::android::status_t flush() override; ::android::status_t pause() override; - ::android::status_t standby() override; - ::android::status_t start() override; ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, int32_t* latencyMs) override; - void shutdown() override; // Overridden methods of 'StreamCommonImpl', called on a Binder thread. - const ConnectedDevices& getConnectedDevices() const override; ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override; - private: - using AlsaDeviceProxyDeleter = std::function; - using AlsaDeviceProxy = std::unique_ptr; - - static std::optional maybePopulateConfig(const StreamContext& context, - bool isInput); + protected: + std::vector getDeviceProfiles() override; mutable std::mutex mLock; - - const size_t mFrameSizeBytes; - const bool mIsInput; - const std::optional mConfig; + std::vector mConnectedDeviceProfiles GUARDED_BY(mLock); std::atomic mConnectedDevicesUpdated = false; - // All fields below are only used on the worker thread. - std::vector mAlsaDeviceProxies; }; class StreamInUsb final : public StreamUsb, public StreamIn { @@ -94,7 +73,7 @@ class StreamOutUsb final : public StreamUsb, public StreamOut { ndk::ScopedAStatus getHwVolume(std::vector* _aidl_return) override; ndk::ScopedAStatus setHwVolume(const std::vector& in_channelVolumes) override; - int mChannelCount; + const int mChannelCount; std::vector mHwVolumes; }; diff --git a/audio/aidl/default/usb/ModuleUsb.cpp b/audio/aidl/default/usb/ModuleUsb.cpp index 5c9d4776ee..a812e4d3a1 100644 --- a/audio/aidl/default/usb/ModuleUsb.cpp +++ b/audio/aidl/default/usb/ModuleUsb.cpp @@ -14,68 +14,34 @@ * limitations under the License. */ -#define LOG_TAG "AHAL_ModuleUsb" - #include +#define LOG_TAG "AHAL_ModuleUsb" #include #include -#include #include "UsbAlsaMixerControl.h" -#include "UsbAlsaUtils.h" +#include "alsa/Utils.h" #include "core-impl/ModuleUsb.h" #include "core-impl/StreamUsb.h" -extern "C" { -#include "alsa_device_profile.h" -} - using aidl::android::hardware::audio::common::SinkMetadata; using aidl::android::hardware::audio::common::SourceMetadata; -using aidl::android::media::audio::common::AudioChannelLayout; -using aidl::android::media::audio::common::AudioDeviceAddress; using aidl::android::media::audio::common::AudioDeviceDescription; -using aidl::android::media::audio::common::AudioDeviceType; -using aidl::android::media::audio::common::AudioFormatDescription; -using aidl::android::media::audio::common::AudioFormatType; -using aidl::android::media::audio::common::AudioIoFlags; using aidl::android::media::audio::common::AudioOffloadInfo; using aidl::android::media::audio::common::AudioPort; using aidl::android::media::audio::common::AudioPortConfig; using aidl::android::media::audio::common::AudioPortExt; -using aidl::android::media::audio::common::AudioProfile; using aidl::android::media::audio::common::MicrophoneInfo; namespace aidl::android::hardware::audio::core { namespace { -std::vector populateChannelMasksFromProfile(const alsa_device_profile* profile, - bool isInput) { - std::vector channels; - for (size_t i = 0; i < AUDIO_PORT_MAX_CHANNEL_MASKS && profile->channel_counts[i] != 0; ++i) { - auto layoutMask = - usb::getChannelLayoutMaskFromChannelCount(profile->channel_counts[i], isInput); - if (layoutMask.getTag() == AudioChannelLayout::Tag::layoutMask) { - channels.push_back(layoutMask); - } - auto indexMask = usb::getChannelIndexMaskFromChannelCount(profile->channel_counts[i]); - if (indexMask.getTag() == AudioChannelLayout::Tag::indexMask) { - channels.push_back(indexMask); - } - } - return channels; -} - -std::vector populateSampleRatesFromProfile(const alsa_device_profile* profile) { - std::vector sampleRates; - for (int i = 0; i < std::min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) && - profile->sample_rates[i] != 0; - i++) { - sampleRates.push_back(profile->sample_rates[i]); - } - return sampleRates; +bool isUsbDevicePort(const AudioPort& audioPort) { + return audioPort.ext.getTag() == AudioPortExt::Tag::device && + audioPort.ext.get().device.type.connection == + AudioDeviceDescription::CONNECTION_USB; } } // namespace @@ -122,55 +88,11 @@ ndk::ScopedAStatus ModuleUsb::createOutputStream(const SourceMetadata& sourceMet } ndk::ScopedAStatus ModuleUsb::populateConnectedDevicePort(AudioPort* audioPort) { - if (audioPort->ext.getTag() != AudioPortExt::Tag::device) { - LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a device port"; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); - } - auto& devicePort = audioPort->ext.get(); - if (devicePort.device.type.connection != AudioDeviceDescription::CONNECTION_USB) { + if (!isUsbDevicePort(*audioPort)) { LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a usb device port"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } - if (devicePort.device.address.getTag() != AudioDeviceAddress::Tag::alsa) { - LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not using alsa address"; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); - } - auto& alsaAddress = devicePort.device.address.get(); - if (alsaAddress.size() != 2 || alsaAddress[0] < 0 || alsaAddress[1] < 0) { - LOG(ERROR) << __func__ << ": port id " << audioPort->id << " invalid alsa address"; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); - } - - const bool isInput = audioPort->flags.getTag() == AudioIoFlags::input; - alsa_device_profile profile; - profile_init(&profile, isInput ? PCM_IN : PCM_OUT); - profile.card = alsaAddress[0]; - profile.device = alsaAddress[1]; - if (!profile_read_device_info(&profile)) { - LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card - << ", device=" << profile.device; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - } - - std::vector channels = populateChannelMasksFromProfile(&profile, isInput); - std::vector sampleRates = populateSampleRatesFromProfile(&profile); - - for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) && - profile.formats[i] != PCM_FORMAT_INVALID; - ++i) { - auto audioFormatDescription = - usb::legacy2aidl_pcm_format_AudioFormatDescription(profile.formats[i]); - if (audioFormatDescription.type == AudioFormatType::DEFAULT) { - LOG(WARNING) << __func__ << ": unknown pcm type=" << profile.formats[i]; - continue; - } - AudioProfile audioProfile = {.format = audioFormatDescription, - .channelMasks = channels, - .sampleRates = sampleRates}; - audioPort->profiles.push_back(std::move(audioProfile)); - } - - return ndk::ScopedAStatus::ok(); + return ModuleAlsa::populateConnectedDevicePort(audioPort); } ndk::ScopedAStatus ModuleUsb::checkAudioPatchEndpointsMatch( @@ -191,15 +113,14 @@ ndk::ScopedAStatus ModuleUsb::checkAudioPatchEndpointsMatch( void ModuleUsb::onExternalDeviceConnectionChanged( const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected) { - if (audioPort.ext.getTag() != AudioPortExt::Tag::device) { + if (!isUsbDevicePort(audioPort)) { return; } - const auto& address = audioPort.ext.get().device.address; - if (address.getTag() != AudioDeviceAddress::alsa) { + auto profile = alsa::getDeviceProfile(audioPort); + if (!profile.has_value()) { return; } - const int card = address.get()[0]; - usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(card, getMasterMute(), + usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(profile->card, getMasterMute(), getMasterVolume(), connected); } diff --git a/audio/aidl/default/usb/StreamUsb.cpp b/audio/aidl/default/usb/StreamUsb.cpp index 17e1ab42f1..da0ad11be2 100644 --- a/audio/aidl/default/usb/StreamUsb.cpp +++ b/audio/aidl/default/usb/StreamUsb.cpp @@ -23,64 +23,20 @@ #include #include "UsbAlsaMixerControl.h" -#include "UsbAlsaUtils.h" -#include "core-impl/Module.h" #include "core-impl/StreamUsb.h" -extern "C" { -#include "alsa_device_profile.h" -} - using aidl::android::hardware::audio::common::getChannelCount; using aidl::android::hardware::audio::common::SinkMetadata; using aidl::android::hardware::audio::common::SourceMetadata; using aidl::android::media::audio::common::AudioDevice; -using aidl::android::media::audio::common::AudioDeviceAddress; using aidl::android::media::audio::common::AudioOffloadInfo; -using aidl::android::media::audio::common::AudioPortExt; using aidl::android::media::audio::common::MicrophoneDynamicInfo; using aidl::android::media::audio::common::MicrophoneInfo; -using android::OK; -using android::status_t; namespace aidl::android::hardware::audio::core { StreamUsb::StreamUsb(const Metadata& metadata, StreamContext&& context) - : StreamCommonImpl(metadata, std::move(context)), - mFrameSizeBytes(getContext().getFrameSize()), - mIsInput(isInput(metadata)), - mConfig(maybePopulateConfig(getContext(), mIsInput)) {} - -// static -std::optional StreamUsb::maybePopulateConfig(const StreamContext& context, - bool isInput) { - struct pcm_config config; - config.channels = usb::getChannelCountFromChannelMask(context.getChannelLayout(), isInput); - if (config.channels == 0) { - LOG(ERROR) << __func__ << ": invalid channel=" << context.getChannelLayout().toString(); - return std::nullopt; - } - config.format = usb::aidl2legacy_AudioFormatDescription_pcm_format(context.getFormat()); - if (config.format == PCM_FORMAT_INVALID) { - LOG(ERROR) << __func__ << ": invalid format=" << context.getFormat().toString(); - return std::nullopt; - } - config.rate = context.getSampleRate(); - if (config.rate == 0) { - LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate; - return std::nullopt; - } - return config; -} - -::android::status_t StreamUsb::init() { - return mConfig.has_value() ? ::android::OK : ::android::NO_INIT; -} - -const StreamCommonInterface::ConnectedDevices& StreamUsb::getConnectedDevices() const { - std::lock_guard guard(mLock); - return mConnectedDevices; -} + : StreamAlsa(metadata, std::move(context)) {} ndk::ScopedAStatus StreamUsb::setConnectedDevices( const std::vector& connectedDevices) { @@ -89,14 +45,19 @@ ndk::ScopedAStatus StreamUsb::setConnectedDevices( << ") for input stream"; return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } + std::vector connectedDeviceProfiles; for (const auto& connectedDevice : connectedDevices) { - if (connectedDevice.address.getTag() != AudioDeviceAddress::alsa) { - LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString(); + auto profile = alsa::getDeviceProfile(connectedDevice, mIsInput); + if (!profile.has_value()) { + LOG(ERROR) << __func__ + << ": unsupported device address=" << connectedDevice.address.toString(); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } + connectedDeviceProfiles.push_back(*profile); } - std::lock_guard guard(mLock); RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices)); + std::lock_guard guard(mLock); + mConnectedDeviceProfiles = std::move(connectedDeviceProfiles); mConnectedDevicesUpdated.store(true, std::memory_order_release); return ndk::ScopedAStatus::ok(); } @@ -119,87 +80,22 @@ ndk::ScopedAStatus StreamUsb::setConnectedDevices( ::android::status_t StreamUsb::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, int32_t* latencyMs) { if (mConnectedDevicesUpdated.load(std::memory_order_acquire)) { - // 'setConnectedDevices' has been called. I/O will be restarted. + // 'setConnectedDevices' was called. I/O will be restarted. *actualFrameCount = 0; *latencyMs = StreamDescriptor::LATENCY_UNKNOWN; return ::android::OK; } - const size_t bytesToTransfer = frameCount * mFrameSizeBytes; - unsigned maxLatency = 0; - if (mIsInput) { - if (mAlsaDeviceProxies.empty()) { - LOG(FATAL) << __func__ << ": no input devices"; - return ::android::NO_INIT; - } - // For input case, only support single device. - proxy_read(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer); - maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get()); - } else { - for (auto& proxy : mAlsaDeviceProxies) { - proxy_write(proxy.get(), buffer, bytesToTransfer); - maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get())); - } - } - *actualFrameCount = frameCount; - maxLatency = std::min(maxLatency, static_cast(std::numeric_limits::max())); - *latencyMs = maxLatency; - return ::android::OK; + return StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs); } -::android::status_t StreamUsb::standby() { - mAlsaDeviceProxies.clear(); - return ::android::OK; -} - -void StreamUsb::shutdown() { - mAlsaDeviceProxies.clear(); -} - -::android::status_t StreamUsb::start() { - std::vector connectedDevices; +std::vector StreamUsb::getDeviceProfiles() { + std::vector connectedDevices; { std::lock_guard guard(mLock); - std::transform(mConnectedDevices.begin(), mConnectedDevices.end(), - std::back_inserter(connectedDevices), - [](const auto& device) { return device.address; }); + connectedDevices = mConnectedDeviceProfiles; mConnectedDevicesUpdated.store(false, std::memory_order_release); } - decltype(mAlsaDeviceProxies) alsaDeviceProxies; - for (const auto& device : connectedDevices) { - alsa_device_profile profile; - profile_init(&profile, mIsInput ? PCM_IN : PCM_OUT); - profile.card = device.get()[0]; - profile.device = device.get()[1]; - if (!profile_read_device_info(&profile)) { - LOG(ERROR) << __func__ - << ": unable to read device info, device address=" << device.toString(); - return ::android::UNKNOWN_ERROR; - } - - AlsaDeviceProxy proxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) { - proxy_close(proxy); - free(proxy); - }); - // Always ask for alsa configure as required since the configuration should be supported - // by the connected device. That is guaranteed by `setAudioPortConfig` and - // `setAudioPatch`. - if (int err = proxy_prepare(proxy.get(), &profile, - const_cast(&mConfig.value()), - true /*is_bit_perfect*/); - err != 0) { - LOG(ERROR) << __func__ << ": fail to prepare for device address=" << device.toString() - << " error=" << err; - return ::android::UNKNOWN_ERROR; - } - if (int err = proxy_open(proxy.get()); err != 0) { - LOG(ERROR) << __func__ << ": failed to open device, address=" << device.toString() - << " error=" << err; - return ::android::UNKNOWN_ERROR; - } - alsaDeviceProxies.push_back(std::move(proxy)); - } - mAlsaDeviceProxies = std::move(alsaDeviceProxies); - return ::android::OK; + return connectedDevices; } StreamInUsb::StreamInUsb(const SinkMetadata& sinkMetadata, StreamContext&& context, @@ -214,9 +110,9 @@ ndk::ScopedAStatus StreamInUsb::getActiveMicrophones( StreamOutUsb::StreamOutUsb(const SourceMetadata& sourceMetadata, StreamContext&& context, const std::optional& offloadInfo) - : StreamUsb(sourceMetadata, std::move(context)), StreamOut(offloadInfo) { - mChannelCount = getChannelCount(getContext().getChannelLayout()); -} + : StreamUsb(sourceMetadata, std::move(context)), + StreamOut(offloadInfo), + mChannelCount(getChannelCount(getContext().getChannelLayout())) {} ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector* _aidl_return) { *_aidl_return = mHwVolumes; @@ -224,17 +120,17 @@ ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector* _aidl_return) { } ndk::ScopedAStatus StreamOutUsb::setHwVolume(const std::vector& in_channelVolumes) { + // Avoid using mConnectedDeviceProfiles because it requires a lock. for (const auto& device : getConnectedDevices()) { - if (device.address.getTag() != AudioDeviceAddress::alsa) { - LOG(DEBUG) << __func__ << ": skip as the device address is not alsa"; - continue; - } - const int card = device.address.get()[0]; - if (auto result = - usb::UsbAlsaMixerControl::getInstance().setVolumes(card, in_channelVolumes); - !result.isOk()) { - LOG(ERROR) << __func__ << ": failed to set volume for device, card=" << card; - return result; + if (auto deviceProfile = alsa::getDeviceProfile(device, mIsInput); + deviceProfile.has_value()) { + if (auto result = usb::UsbAlsaMixerControl::getInstance().setVolumes( + deviceProfile->card, in_channelVolumes); + !result.isOk()) { + LOG(ERROR) << __func__ + << ": failed to set volume for device address=" << *deviceProfile; + return result; + } } } mHwVolumes = in_channelVolumes; diff --git a/audio/aidl/default/usb/UsbAlsaMixerControl.cpp b/audio/aidl/default/usb/UsbAlsaMixerControl.cpp index 6c0c24bc09..769d739969 100644 --- a/audio/aidl/default/usb/UsbAlsaMixerControl.cpp +++ b/audio/aidl/default/usb/UsbAlsaMixerControl.cpp @@ -17,144 +17,12 @@ #define LOG_TAG "AHAL_UsbAlsaMixerControl" #include -#include -#include -#include - #include #include "UsbAlsaMixerControl.h" namespace aidl::android::hardware::audio::core::usb { -//----------------------------------------------------------------------------- - -MixerControl::MixerControl(struct mixer_ctl* ctl) - : mCtl(ctl), - mNumValues(mixer_ctl_get_num_values(ctl)), - mMinValue(mixer_ctl_get_range_min(ctl)), - mMaxValue(mixer_ctl_get_range_max(ctl)) {} - -unsigned int MixerControl::getNumValues() const { - return mNumValues; -} - -int MixerControl::getMaxValue() const { - return mMaxValue; -} - -int MixerControl::getMinValue() const { - return mMinValue; -} - -int MixerControl::setArray(const void* array, size_t count) { - const std::lock_guard guard(mLock); - return mixer_ctl_set_array(mCtl, array, count); -} - -//----------------------------------------------------------------------------- - -// static -const std::map> - AlsaMixer::kPossibleControls = { - {AlsaMixer::MASTER_SWITCH, {{"Master Playback Switch", MIXER_CTL_TYPE_BOOL}}}, - {AlsaMixer::MASTER_VOLUME, {{"Master Playback Volume", MIXER_CTL_TYPE_INT}}}, - {AlsaMixer::HW_VOLUME, - {{"Headphone Playback Volume", MIXER_CTL_TYPE_INT}, - {"Headset Playback Volume", MIXER_CTL_TYPE_INT}, - {"PCM Playback Volume", MIXER_CTL_TYPE_INT}}}}; - -// static -std::map> AlsaMixer::initializeMixerControls( - struct mixer* mixer) { - std::map> mixerControls; - std::string mixerCtlNames; - for (const auto& [control, possibleCtls] : kPossibleControls) { - for (const auto& [ctlName, expectedCtlType] : possibleCtls) { - struct mixer_ctl* ctl = mixer_get_ctl_by_name(mixer, ctlName.c_str()); - if (ctl != nullptr && mixer_ctl_get_type(ctl) == expectedCtlType) { - mixerControls.emplace(control, std::make_unique(ctl)); - if (!mixerCtlNames.empty()) { - mixerCtlNames += ","; - } - mixerCtlNames += ctlName; - break; - } - } - } - LOG(DEBUG) << __func__ << ": available mixer control names=[" << mixerCtlNames << "]"; - return mixerControls; -} - -AlsaMixer::AlsaMixer(struct mixer* mixer) - : mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {} - -AlsaMixer::~AlsaMixer() { - mixer_close(mMixer); -} - -namespace { - -int volumeFloatToInteger(float fValue, int maxValue, int minValue) { - return minValue + std::ceil((maxValue - minValue) * fValue); -} - -} // namespace - -ndk::ScopedAStatus AlsaMixer::setMasterMute(bool muted) { - auto it = mMixerControls.find(AlsaMixer::MASTER_SWITCH); - if (it == mMixerControls.end()) { - return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); - } - const int numValues = it->second->getNumValues(); - std::vector values(numValues, muted ? 0 : 1); - if (int err = it->second->setArray(values.data(), numValues); err != 0) { - LOG(ERROR) << __func__ << ": failed to set master mute, err=" << err; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - } - return ndk::ScopedAStatus::ok(); -} - -ndk::ScopedAStatus AlsaMixer::setMasterVolume(float volume) { - auto it = mMixerControls.find(AlsaMixer::MASTER_VOLUME); - if (it == mMixerControls.end()) { - return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); - } - const int numValues = it->second->getNumValues(); - std::vector values(numValues, volumeFloatToInteger(volume, it->second->getMaxValue(), - it->second->getMinValue())); - if (int err = it->second->setArray(values.data(), numValues); err != 0) { - LOG(ERROR) << __func__ << ": failed to set master volume, err=" << err; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - } - return ndk::ScopedAStatus::ok(); -} - -ndk::ScopedAStatus AlsaMixer::setVolumes(std::vector volumes) { - auto it = mMixerControls.find(AlsaMixer::HW_VOLUME); - if (it == mMixerControls.end()) { - return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); - } - const int numValues = it->second->getNumValues(); - if (numValues < 0) { - LOG(FATAL) << __func__ << ": negative number of values: " << numValues; - } - const int maxValue = it->second->getMaxValue(); - const int minValue = it->second->getMinValue(); - std::vector values; - size_t i = 0; - for (; i < static_cast(numValues) && i < values.size(); ++i) { - values.emplace_back(volumeFloatToInteger(volumes[i], maxValue, minValue)); - } - if (int err = it->second->setArray(values.data(), values.size()); err != 0) { - LOG(ERROR) << __func__ << ": failed to set volume, err=" << err; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - } - return ndk::ScopedAStatus::ok(); -} - -//----------------------------------------------------------------------------- - // static UsbAlsaMixerControl& UsbAlsaMixerControl::getInstance() { static UsbAlsaMixerControl gInstance; @@ -170,7 +38,7 @@ void UsbAlsaMixerControl::setDeviceConnectionState(int card, bool masterMuted, f PLOG(ERROR) << __func__ << ": failed to open mixer for card=" << card; return; } - auto alsaMixer = std::make_shared(mixer); + auto alsaMixer = std::make_shared(mixer); alsaMixer->setMasterMute(masterMuted); alsaMixer->setMasterVolume(masterVolume); const std::lock_guard guard(mLock); @@ -209,7 +77,7 @@ ndk::ScopedAStatus UsbAlsaMixerControl::setMasterVolume(float volume) { return ndk::ScopedAStatus::ok(); } -ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, std::vector volumes) { +ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, const std::vector& volumes) { auto alsaMixer = getAlsaMixer(card); if (alsaMixer == nullptr) { LOG(ERROR) << __func__ << ": no mixer control found for card=" << card; @@ -218,13 +86,13 @@ ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, std::vector return alsaMixer->setVolumes(volumes); } -std::shared_ptr UsbAlsaMixerControl::getAlsaMixer(int card) { +std::shared_ptr UsbAlsaMixerControl::getAlsaMixer(int card) { const std::lock_guard guard(mLock); const auto it = mMixerControls.find(card); return it == mMixerControls.end() ? nullptr : it->second; } -std::map> UsbAlsaMixerControl::getAlsaMixers() { +std::map> UsbAlsaMixerControl::getAlsaMixers() { const std::lock_guard guard(mLock); return mMixerControls; } diff --git a/audio/aidl/default/usb/UsbAlsaMixerControl.h b/audio/aidl/default/usb/UsbAlsaMixerControl.h index cbcddd82c6..c3265f8fd3 100644 --- a/audio/aidl/default/usb/UsbAlsaMixerControl.h +++ b/audio/aidl/default/usb/UsbAlsaMixerControl.h @@ -19,67 +19,15 @@ #include #include #include -#include -#include #include #include #include -extern "C" { -#include -} +#include "alsa/Mixer.h" namespace aidl::android::hardware::audio::core::usb { -class MixerControl { - public: - explicit MixerControl(struct mixer_ctl* ctl); - - unsigned int getNumValues() const; - int getMaxValue() const; - int getMinValue() const; - int setArray(const void* array, size_t count); - - private: - std::mutex mLock; - // The mixer_ctl object is owned by ALSA and will be released when the mixer is closed. - struct mixer_ctl* mCtl GUARDED_BY(mLock); - const unsigned int mNumValues; - const int mMinValue; - const int mMaxValue; -}; - -class AlsaMixer { - public: - explicit AlsaMixer(struct mixer* mixer); - - ~AlsaMixer(); - - bool isValid() const { return mMixer != nullptr; } - - ndk::ScopedAStatus setMasterMute(bool muted); - ndk::ScopedAStatus setMasterVolume(float volume); - ndk::ScopedAStatus setVolumes(std::vector volumes); - - private: - enum Control { - MASTER_SWITCH, - MASTER_VOLUME, - HW_VOLUME, - }; - using ControlNamesAndExpectedCtlType = std::pair; - static const std::map> kPossibleControls; - static std::map> initializeMixerControls( - struct mixer* mixer); - - // The mixer object is owned by ALSA and will be released when the mixer is closed. - struct mixer* mMixer; - // `mMixerControls` will only be initialized in constructor. After that, it wil only be - // read but not be modified. - const std::map> mMixerControls; -}; - class UsbAlsaMixerControl { public: static UsbAlsaMixerControl& getInstance(); @@ -91,16 +39,16 @@ class UsbAlsaMixerControl { ndk::ScopedAStatus setMasterMute(bool muted); ndk::ScopedAStatus setMasterVolume(float volume); // The volume settings can be different on sound cards. It is controlled by streams. - ndk::ScopedAStatus setVolumes(int card, std::vector volumes); + ndk::ScopedAStatus setVolumes(int card, const std::vector& volumes); private: - std::shared_ptr getAlsaMixer(int card); - std::map> getAlsaMixers(); + std::shared_ptr getAlsaMixer(int card); + std::map> getAlsaMixers(); std::mutex mLock; // A map whose key is the card number and value is a shared pointer to corresponding // AlsaMixer object. - std::map> mMixerControls GUARDED_BY(mLock); + std::map> mMixerControls GUARDED_BY(mLock); }; } // namespace aidl::android::hardware::audio::core::usb diff --git a/audio/aidl/default/usb/UsbAlsaUtils.cpp b/audio/aidl/default/usb/UsbAlsaUtils.cpp deleted file mode 100644 index 74d9c289a5..0000000000 --- a/audio/aidl/default/usb/UsbAlsaUtils.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include -#include -#include - -#include "UsbAlsaUtils.h" -#include "core-impl/utils.h" - -using aidl::android::hardware::audio::common::getChannelCount; -using aidl::android::media::audio::common::AudioChannelLayout; -using aidl::android::media::audio::common::AudioFormatDescription; -using aidl::android::media::audio::common::AudioFormatType; -using aidl::android::media::audio::common::PcmType; - -namespace aidl::android::hardware::audio::core::usb { - -namespace { - -using AudioChannelCountToMaskMap = std::map; -using AudioFormatDescToPcmFormatMap = std::map; -using PcmFormatToAudioFormatDescMap = std::map; - -static const AudioChannelLayout INVALID_CHANNEL_LAYOUT = - AudioChannelLayout::make(0); - -#define DEFINE_CHANNEL_LAYOUT_MASK(n) \ - AudioChannelLayout::make(AudioChannelLayout::LAYOUT_##n) - -static const std::set SUPPORTED_OUT_CHANNEL_LAYOUTS = { - DEFINE_CHANNEL_LAYOUT_MASK(MONO), DEFINE_CHANNEL_LAYOUT_MASK(STEREO), - DEFINE_CHANNEL_LAYOUT_MASK(2POINT1), DEFINE_CHANNEL_LAYOUT_MASK(QUAD), - DEFINE_CHANNEL_LAYOUT_MASK(PENTA), DEFINE_CHANNEL_LAYOUT_MASK(5POINT1), - DEFINE_CHANNEL_LAYOUT_MASK(6POINT1), DEFINE_CHANNEL_LAYOUT_MASK(7POINT1), - DEFINE_CHANNEL_LAYOUT_MASK(7POINT1POINT4), DEFINE_CHANNEL_LAYOUT_MASK(22POINT2), -}; - -static const std::set SUPPORTED_IN_CHANNEL_LAYOUTS = { - DEFINE_CHANNEL_LAYOUT_MASK(MONO), - DEFINE_CHANNEL_LAYOUT_MASK(STEREO), -}; - -#define DEFINE_CHANNEL_INDEX_MASK(n) \ - AudioChannelLayout::make(AudioChannelLayout::INDEX_MASK_##n) - -static const std::set SUPPORTED_INDEX_CHANNEL_LAYOUTS = { - DEFINE_CHANNEL_INDEX_MASK(1), DEFINE_CHANNEL_INDEX_MASK(2), DEFINE_CHANNEL_INDEX_MASK(3), - DEFINE_CHANNEL_INDEX_MASK(4), DEFINE_CHANNEL_INDEX_MASK(5), DEFINE_CHANNEL_INDEX_MASK(6), - DEFINE_CHANNEL_INDEX_MASK(7), DEFINE_CHANNEL_INDEX_MASK(8), DEFINE_CHANNEL_INDEX_MASK(9), - DEFINE_CHANNEL_INDEX_MASK(10), DEFINE_CHANNEL_INDEX_MASK(11), DEFINE_CHANNEL_INDEX_MASK(12), - DEFINE_CHANNEL_INDEX_MASK(13), DEFINE_CHANNEL_INDEX_MASK(14), DEFINE_CHANNEL_INDEX_MASK(15), - DEFINE_CHANNEL_INDEX_MASK(16), DEFINE_CHANNEL_INDEX_MASK(17), DEFINE_CHANNEL_INDEX_MASK(18), - DEFINE_CHANNEL_INDEX_MASK(19), DEFINE_CHANNEL_INDEX_MASK(20), DEFINE_CHANNEL_INDEX_MASK(21), - DEFINE_CHANNEL_INDEX_MASK(22), DEFINE_CHANNEL_INDEX_MASK(23), DEFINE_CHANNEL_INDEX_MASK(24), -}; - -static AudioChannelCountToMaskMap make_ChannelCountToMaskMap( - const std::set& channelMasks) { - AudioChannelCountToMaskMap channelMaskToCountMap; - for (const auto& channelMask : channelMasks) { - channelMaskToCountMap.emplace(getChannelCount(channelMask), channelMask); - } - return channelMaskToCountMap; -} - -const AudioChannelCountToMaskMap& getSupportedChannelOutLayoutMap() { - static const AudioChannelCountToMaskMap outLayouts = - make_ChannelCountToMaskMap(SUPPORTED_OUT_CHANNEL_LAYOUTS); - return outLayouts; -} - -const AudioChannelCountToMaskMap& getSupportedChannelInLayoutMap() { - static const AudioChannelCountToMaskMap inLayouts = - make_ChannelCountToMaskMap(SUPPORTED_IN_CHANNEL_LAYOUTS); - return inLayouts; -} - -const AudioChannelCountToMaskMap& getSupportedChannelIndexLayoutMap() { - static const AudioChannelCountToMaskMap indexLayouts = - make_ChannelCountToMaskMap(SUPPORTED_INDEX_CHANNEL_LAYOUTS); - return indexLayouts; -} - -AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) { - AudioFormatDescription result; - result.type = type; - return result; -} - -AudioFormatDescription make_AudioFormatDescription(PcmType pcm) { - auto result = make_AudioFormatDescription(AudioFormatType::PCM); - result.pcm = pcm; - return result; -} - -const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() { - static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = { - {make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8}, - {make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE}, - {make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE}, - {make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE}, - {make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE}, - {make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE}, - }; - return formatDescToPcmFormatMap; -} - -static PcmFormatToAudioFormatDescMap make_PcmFormatToAudioFormatDescMap( - const AudioFormatDescToPcmFormatMap& formatDescToPcmFormatMap) { - PcmFormatToAudioFormatDescMap result; - for (const auto& formatPair : formatDescToPcmFormatMap) { - result.emplace(formatPair.second, formatPair.first); - } - return result; -} - -const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() { - static const PcmFormatToAudioFormatDescMap pcmFormatToFormatDescMap = - make_PcmFormatToAudioFormatDescMap(getAudioFormatDescriptorToPcmFormatMap()); - return pcmFormatToFormatDescMap; -} - -} // namespace - -AudioChannelLayout getChannelLayoutMaskFromChannelCount(unsigned int channelCount, int isInput) { - return findValueOrDefault( - isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(), - channelCount, INVALID_CHANNEL_LAYOUT); -} - -AudioChannelLayout getChannelIndexMaskFromChannelCount(unsigned int channelCount) { - return findValueOrDefault(getSupportedChannelIndexLayoutMap(), channelCount, - INVALID_CHANNEL_LAYOUT); -} - -unsigned int getChannelCountFromChannelMask(const AudioChannelLayout& channelMask, bool isInput) { - switch (channelMask.getTag()) { - case AudioChannelLayout::Tag::layoutMask: { - return findKeyOrDefault( - isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(), - (unsigned int)getChannelCount(channelMask), 0u /*defaultValue*/); - } - case AudioChannelLayout::Tag::indexMask: { - return findKeyOrDefault(getSupportedChannelIndexLayoutMap(), - (unsigned int)getChannelCount(channelMask), - 0u /*defaultValue*/); - } - case AudioChannelLayout::Tag::none: - case AudioChannelLayout::Tag::invalid: - case AudioChannelLayout::Tag::voiceMask: - default: - return 0; - } -} - -AudioFormatDescription legacy2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy) { - return findValueOrDefault(getPcmFormatToAudioFormatDescMap(), legacy, AudioFormatDescription()); -} - -pcm_format aidl2legacy_AudioFormatDescription_pcm_format(const AudioFormatDescription& aidl) { - return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID); -} - -} // namespace aidl::android::hardware::audio::core::usb diff --git a/audio/aidl/default/usb/UsbAlsaUtils.h b/audio/aidl/default/usb/UsbAlsaUtils.h deleted file mode 100644 index 2d2f0f4342..0000000000 --- a/audio/aidl/default/usb/UsbAlsaUtils.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -extern "C" { -#include -} - -namespace aidl::android::hardware::audio::core::usb { - -::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount( - unsigned int channelCount, int isInput); -::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount( - unsigned int channelCount); -unsigned int getChannelCountFromChannelMask( - const ::aidl::android::media::audio::common::AudioChannelLayout& channelMask, bool isInput); -::aidl::android::media::audio::common::AudioFormatDescription -legacy2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy); -pcm_format aidl2legacy_AudioFormatDescription_pcm_format( - const ::aidl::android::media::audio::common::AudioFormatDescription& aidl); - -} // namespace aidl::android::hardware::audio::core::usb \ No newline at end of file From 521fc49fba3de4f0843a04625ebc369933b47352 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 11 Jul 2023 17:24:08 -0700 Subject: [PATCH 2/3] audio: Create ModulePrimary and ModuleStub Make 'Module' more abstract by moving stream creation methods to more concrete 'ModulePrimary' and 'ModuleStub'. 'ModulePrimary' is now closer to the CF primary module: it was stripped off USB devices from its configuration, these got moved to 'ModuleUsb', and got rid of BT A2DP and LE interfaces, these will be on 'ModuleBluetooth'. Note that 'ModulePrimary' still uses stub streams, this will be changed in subsequent patches. 'ModuleStub' is what 'Module' used to be, just a module for improving test coverage. It includes simulation of offload streams and dummy BT objects. Bug: 264712385 Test: atest VtsHalAudioCoreTargetTest Change-Id: I5e4da0c32c00d65688f2eda78b2c79594e4e4671 --- audio/aidl/default/Android.bp | 4 +- audio/aidl/default/Configuration.cpp | 171 +++++++++++++----- audio/aidl/default/Module.cpp | 68 +++---- audio/aidl/default/ModulePrimary.cpp | 60 ++++++ .../android.hardware.audio.service-aidl.xml | 5 + .../include/core-impl/ChildInterface.h | 48 +++++ .../default/include/core-impl/Configuration.h | 1 + audio/aidl/default/include/core-impl/Module.h | 34 +--- .../default/include/core-impl/ModulePrimary.h | 46 +++++ .../include/core-impl/ModuleRemoteSubmix.h | 2 +- .../default/include/core-impl/ModuleStub.h | 50 +++++ .../default/include/core-impl/ModuleUsb.h | 2 +- audio/aidl/default/main.cpp | 18 +- audio/aidl/default/stub/ModuleStub.cpp | 78 ++++++++ audio/aidl/default/{ => stub}/StreamStub.cpp | 0 15 files changed, 463 insertions(+), 124 deletions(-) create mode 100644 audio/aidl/default/ModulePrimary.cpp create mode 100644 audio/aidl/default/include/core-impl/ChildInterface.h create mode 100644 audio/aidl/default/include/core-impl/ModulePrimary.h create mode 100644 audio/aidl/default/include/core-impl/ModuleStub.h create mode 100644 audio/aidl/default/stub/ModuleStub.cpp rename audio/aidl/default/{ => stub}/StreamStub.cpp (100%) diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp index 65bbe9e9a1..934c7ee39d 100644 --- a/audio/aidl/default/Android.bp +++ b/audio/aidl/default/Android.bp @@ -73,9 +73,9 @@ cc_library { "Configuration.cpp", "EngineConfigXmlConverter.cpp", "Module.cpp", + "ModulePrimary.cpp", "SoundDose.cpp", "Stream.cpp", - "StreamStub.cpp", "Telephony.cpp", "alsa/Mixer.cpp", "alsa/ModuleAlsa.cpp", @@ -85,6 +85,8 @@ cc_library { "r_submix/RemoteSubmixUtils.cpp", "r_submix/SubmixRoute.cpp", "r_submix/StreamRemoteSubmix.cpp", + "stub/ModuleStub.cpp", + "stub/StreamStub.cpp", "usb/ModuleUsb.cpp", "usb/StreamUsb.cpp", "usb/UsbAlsaMixerControl.cpp", diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp index d41ea67329..a71c6ea734 100644 --- a/audio/aidl/default/Configuration.cpp +++ b/audio/aidl/default/Configuration.cpp @@ -144,10 +144,6 @@ static AudioRoute createRoute(const std::vector& sources, const Audio // - no profiles specified // * "FM Tuner", IN_FM_TUNER // - no profiles specified -// * "USB Out", OUT_DEVICE, CONNECTION_USB -// - no profiles specified -// * "USB In", IN_DEVICE, CONNECTION_USB -// - no profiles specified // // Mix ports: // * "primary output", PRIMARY, 1 max open, 1 max active stream @@ -172,8 +168,7 @@ static AudioRoute createRoute(const std::vector& sources, const Audio // // Routes: // "primary out", "compressed offload" -> "Speaker" -// "primary out", "compressed offload" -> "USB Out" -// "Built-in Mic", "USB In" -> "primary input" +// "Built-in Mic" -> "primary input" // "telephony_tx" -> "Telephony Tx" // "Telephony Rx" -> "telephony_rx" // "FM Tuner" -> "fm_tuner" @@ -185,14 +180,6 @@ static AudioRoute createRoute(const std::vector& sources, const Audio // * "Telephony Rx" device port: PCM 24-bit; MONO; 48000 // * "FM Tuner" device port: PCM 24-bit; STEREO; 48000 // -// Profiles for device port connected state: -// * USB Out": -// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 -// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 -// * USB In": -// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 -// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 -// std::unique_ptr getPrimaryConfiguration() { static const Configuration configuration = []() { const std::vector standardPcmAudioProfiles = { @@ -252,19 +239,6 @@ std::unique_ptr getPrimaryConfiguration() { AudioChannelLayout::LAYOUT_STEREO, 48000, 0, true, createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0))); - AudioPort usbOutDevice = - createPort(c.nextPortId++, "USB Out", 0, false, - createDeviceExt(AudioDeviceType::OUT_DEVICE, 0, - AudioDeviceDescription::CONNECTION_USB)); - c.ports.push_back(usbOutDevice); - c.connectedProfiles[usbOutDevice.id] = standardPcmAudioProfiles; - - AudioPort usbInDevice = createPort(c.nextPortId++, "USB In", 0, true, - createDeviceExt(AudioDeviceType::IN_DEVICE, 0, - AudioDeviceDescription::CONNECTION_USB)); - c.ports.push_back(usbInDevice); - c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles; - // Mix ports AudioPort primaryOutMix = createPort(c.nextPortId++, "primary output", @@ -323,8 +297,7 @@ std::unique_ptr getPrimaryConfiguration() { c.ports.push_back(fmTunerInMix); c.routes.push_back(createRoute({primaryOutMix, compressedOffloadOutMix}, speakerOutDevice)); - c.routes.push_back(createRoute({primaryOutMix, compressedOffloadOutMix}, usbOutDevice)); - c.routes.push_back(createRoute({micInDevice, usbInDevice}, primaryInMix)); + c.routes.push_back(createRoute({micInDevice}, primaryInMix)); c.routes.push_back(createRoute({telephonyTxOutMix}, telephonyTxOutDevice)); c.routes.push_back(createRoute({telephonyRxInDevice}, telephonyRxInMix)); c.routes.push_back(createRoute({fmTunerInDevice}, fmTunerInMix)); @@ -406,22 +379,31 @@ std::unique_ptr getRSubmixConfiguration() { // Usb configuration: // // Device ports: +// * "USB Device Out", OUT_DEVICE, CONNECTION_USB +// - no profiles specified // * "USB Headset Out", OUT_HEADSET, CONNECTION_USB // - no profiles specified +// * "USB Device In", IN_DEVICE, CONNECTION_USB +// - no profiles specified // * "USB Headset In", IN_HEADSET, CONNECTION_USB // - no profiles specified // // Mix ports: -// * "usb_headset output", 1 max open, 1 max active stream +// * "usb_device output", 1 max open, 1 max active stream // - no profiles specified -// * "usb_headset input", 1 max open, 1 max active stream +// * "usb_device input", 1 max open, 1 max active stream // - no profiles specified // +// Routes: +// * "usb_device output" -> "USB Device Out" +// * "usb_device output" -> "USB Headset Out" +// * "USB Device In", "USB Headset In" -> "usb_device input" +// // Profiles for device port connected state: -// * USB Headset Out": +// * "USB Device Out", "USB Headset Out": // - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000 // - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000 -// * USB Headset In": +// * "USB Device In", "USB Headset In": // - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000 // - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000 // @@ -440,6 +422,13 @@ std::unique_ptr getUsbConfiguration() { // Device ports + AudioPort usbOutDevice = + createPort(c.nextPortId++, "USB Device Out", 0, false, + createDeviceExt(AudioDeviceType::OUT_DEVICE, 0, + AudioDeviceDescription::CONNECTION_USB)); + c.ports.push_back(usbOutDevice); + c.connectedProfiles[usbOutDevice.id] = standardPcmAudioProfiles; + AudioPort usbOutHeadset = createPort(c.nextPortId++, "USB Headset Out", 0, false, createDeviceExt(AudioDeviceType::OUT_HEADSET, 0, @@ -447,6 +436,12 @@ std::unique_ptr getUsbConfiguration() { c.ports.push_back(usbOutHeadset); c.connectedProfiles[usbOutHeadset.id] = standardPcmAudioProfiles; + AudioPort usbInDevice = createPort(c.nextPortId++, "USB Device In", 0, true, + createDeviceExt(AudioDeviceType::IN_DEVICE, 0, + AudioDeviceDescription::CONNECTION_USB)); + c.ports.push_back(usbInDevice); + c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles; + AudioPort usbInHeadset = createPort(c.nextPortId++, "USB Headset In", 0, true, createDeviceExt(AudioDeviceType::IN_HEADSET, 0, @@ -456,16 +451,110 @@ std::unique_ptr getUsbConfiguration() { // Mix ports - AudioPort usbHeadsetOutMix = - createPort(c.nextPortId++, "usb_headset output", 0, false, createPortMixExt(1, 1)); - c.ports.push_back(usbHeadsetOutMix); + AudioPort usbDeviceOutMix = + createPort(c.nextPortId++, "usb_device output", 0, false, createPortMixExt(1, 1)); + c.ports.push_back(usbDeviceOutMix); - AudioPort usbHeadsetInMix = - createPort(c.nextPortId++, "usb_headset input", 0, true, createPortMixExt(1, 1)); - c.ports.push_back(usbHeadsetInMix); + AudioPort usbDeviceInMix = + createPort(c.nextPortId++, "usb_device input", 0, true, createPortMixExt(1, 1)); + c.ports.push_back(usbDeviceInMix); - c.routes.push_back(createRoute({usbHeadsetOutMix}, usbOutHeadset)); - c.routes.push_back(createRoute({usbInHeadset}, usbHeadsetInMix)); + c.routes.push_back(createRoute({usbDeviceOutMix}, usbOutDevice)); + c.routes.push_back(createRoute({usbDeviceOutMix}, usbOutHeadset)); + c.routes.push_back(createRoute({usbInDevice, usbInHeadset}, usbDeviceInMix)); + + return c; + }(); + return std::make_unique(configuration); +} + +// Stub configuration: +// +// Device ports: +// * "Test Out", OUT_AFE_PROXY +// - no profiles specified +// * "Test In", IN_AFE_PROXY +// - no profiles specified +// +// Mix ports: +// * "test output", 1 max open, 1 max active stream +// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000 +// * "compressed offload", DIRECT|COMPRESS_OFFLOAD|NON_BLOCKING, 1 max open, 1 max active stream +// - profile MP3; MONO, STEREO; 44100, 48000 +// * "test input", 2 max open, 2 max active streams +// - profile PCM 24-bit; MONO, STEREO, FRONT_BACK; +// 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 +// +// Routes: +// "test output", "compressed offload" -> "Test Out" +// "Test In" -> "test input" +// +// Initial port configs: +// * "Test Out" device port: PCM 24-bit; STEREO; 48000 +// * "Test In" device port: PCM 24-bit; MONO; 48000 +// +std::unique_ptr getStubConfiguration() { + static const Configuration configuration = []() { + Configuration c; + + // Device ports + + AudioPort testOutDevice = createPort(c.nextPortId++, "Test Out", 0, false, + createDeviceExt(AudioDeviceType::OUT_AFE_PROXY, 0)); + c.ports.push_back(testOutDevice); + c.initialConfigs.push_back( + createPortConfig(testOutDevice.id, testOutDevice.id, PcmType::INT_24_BIT, + AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false, + createDeviceExt(AudioDeviceType::OUT_AFE_PROXY, 0))); + + AudioPort testInDevice = createPort(c.nextPortId++, "Test In", 0, true, + createDeviceExt(AudioDeviceType::IN_AFE_PROXY, 0)); + c.ports.push_back(testInDevice); + c.initialConfigs.push_back( + createPortConfig(testInDevice.id, testInDevice.id, PcmType::INT_24_BIT, + AudioChannelLayout::LAYOUT_MONO, 48000, 0, true, + createDeviceExt(AudioDeviceType::IN_AFE_PROXY, 0))); + + // Mix ports + + AudioPort testOutMix = + createPort(c.nextPortId++, "test output", 0, false, createPortMixExt(1, 1)); + testOutMix.profiles.push_back( + createProfile(PcmType::INT_24_BIT, + {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, + {8000, 11025, 16000, 32000, 44100, 48000})); + c.ports.push_back(testOutMix); + + AudioPort compressedOffloadOutMix = + createPort(c.nextPortId++, "compressed offload", + makeBitPositionFlagMask({AudioOutputFlags::DIRECT, + AudioOutputFlags::COMPRESS_OFFLOAD, + AudioOutputFlags::NON_BLOCKING}), + false, createPortMixExt(1, 1)); + compressedOffloadOutMix.profiles.push_back( + createProfile(::android::MEDIA_MIMETYPE_AUDIO_MPEG, + {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, + {44100, 48000})); + c.ports.push_back(compressedOffloadOutMix); + + AudioPort testInMIx = + createPort(c.nextPortId++, "test input", 0, true, createPortMixExt(2, 2)); + testInMIx.profiles.push_back( + createProfile(PcmType::INT_16_BIT, + {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO, + AudioChannelLayout::LAYOUT_FRONT_BACK}, + {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000})); + testInMIx.profiles.push_back( + createProfile(PcmType::INT_24_BIT, + {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO, + AudioChannelLayout::LAYOUT_FRONT_BACK}, + {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000})); + c.ports.push_back(testInMIx); + + c.routes.push_back(createRoute({testOutMix, compressedOffloadOutMix}, testOutDevice)); + c.routes.push_back(createRoute({testInDevice}, testInMIx)); + + c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end()); return c; }(); diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp index 48d14580a7..12bbbb0cba 100644 --- a/audio/aidl/default/Module.cpp +++ b/audio/aidl/default/Module.cpp @@ -25,13 +25,12 @@ #include #include -#include "core-impl/Bluetooth.h" #include "core-impl/Module.h" +#include "core-impl/ModulePrimary.h" #include "core-impl/ModuleRemoteSubmix.h" +#include "core-impl/ModuleStub.h" #include "core-impl/ModuleUsb.h" #include "core-impl/SoundDose.h" -#include "core-impl/StreamStub.h" -#include "core-impl/Telephony.h" #include "core-impl/utils.h" using aidl::android::hardware::audio::common::getFrameSizeInBytes; @@ -110,13 +109,14 @@ bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& forma // static std::shared_ptr Module::createInstance(Type type) { switch (type) { - case Module::Type::USB: - return ndk::SharedRefBase::make(type); - case Type::R_SUBMIX: - return ndk::SharedRefBase::make(type); case Type::DEFAULT: - default: - return ndk::SharedRefBase::make(type); + return ndk::SharedRefBase::make(); + case Type::R_SUBMIX: + return ndk::SharedRefBase::make(); + case Type::STUB: + return ndk::SharedRefBase::make(); + case Type::USB: + return ndk::SharedRefBase::make(); } } @@ -128,6 +128,9 @@ std::ostream& operator<<(std::ostream& os, Module::Type t) { case Module::Type::R_SUBMIX: os << "r_submix"; break; + case Module::Type::STUB: + os << "stub"; + break; case Module::Type::USB: os << "usb"; break; @@ -292,6 +295,9 @@ internal::Configuration& Module::getConfig() { case Type::R_SUBMIX: mConfig = std::move(internal::getRSubmixConfiguration()); break; + case Type::STUB: + mConfig = std::move(internal::getStubConfiguration()); + break; case Type::USB: mConfig = std::move(internal::getUsbConfiguration()); break; @@ -395,38 +401,26 @@ ndk::ScopedAStatus Module::setModuleDebug( } ndk::ScopedAStatus Module::getTelephony(std::shared_ptr* _aidl_return) { - if (!mTelephony) { - mTelephony = ndk::SharedRefBase::make(); - } - *_aidl_return = mTelephony.getPtr(); - LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get(); + *_aidl_return = nullptr; + LOG(DEBUG) << __func__ << ": returning null"; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Module::getBluetooth(std::shared_ptr* _aidl_return) { - if (!mBluetooth) { - mBluetooth = ndk::SharedRefBase::make(); - } - *_aidl_return = mBluetooth.getPtr(); - LOG(DEBUG) << __func__ << ": returning instance of IBluetooth: " << _aidl_return->get(); + *_aidl_return = nullptr; + LOG(DEBUG) << __func__ << ": returning null"; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Module::getBluetoothA2dp(std::shared_ptr* _aidl_return) { - if (!mBluetoothA2dp) { - mBluetoothA2dp = ndk::SharedRefBase::make(); - } - *_aidl_return = mBluetoothA2dp.getPtr(); - LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get(); + *_aidl_return = nullptr; + LOG(DEBUG) << __func__ << ": returning null"; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Module::getBluetoothLe(std::shared_ptr* _aidl_return) { - if (!mBluetoothLe) { - mBluetoothLe = ndk::SharedRefBase::make(); - } - *_aidl_return = mBluetoothLe.getPtr(); - LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get(); + *_aidl_return = nullptr; + LOG(DEBUG) << __func__ << ": returning null"; return ndk::ScopedAStatus::ok(); } @@ -1334,22 +1328,6 @@ bool Module::isMmapSupported() { return mIsMmapSupported.value(); } -ndk::ScopedAStatus Module::createInputStream(const SinkMetadata& sinkMetadata, - StreamContext&& context, - const std::vector& microphones, - std::shared_ptr* result) { - return createStreamInstance(result, sinkMetadata, std::move(context), - microphones); -} - -ndk::ScopedAStatus Module::createOutputStream(const SourceMetadata& sourceMetadata, - StreamContext&& context, - const std::optional& offloadInfo, - std::shared_ptr* result) { - return createStreamInstance(result, sourceMetadata, std::move(context), - offloadInfo); -} - ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort __unused) { LOG(VERBOSE) << __func__ << ": do nothing and return ok"; return ndk::ScopedAStatus::ok(); diff --git a/audio/aidl/default/ModulePrimary.cpp b/audio/aidl/default/ModulePrimary.cpp new file mode 100644 index 0000000000..cbb6730372 --- /dev/null +++ b/audio/aidl/default/ModulePrimary.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#define LOG_TAG "AHAL_ModulePrimary" +#include +#include + +#include "core-impl/ModulePrimary.h" +#include "core-impl/StreamStub.h" +#include "core-impl/Telephony.h" + +using aidl::android::hardware::audio::common::SinkMetadata; +using aidl::android::hardware::audio::common::SourceMetadata; +using aidl::android::media::audio::common::AudioOffloadInfo; +using aidl::android::media::audio::common::AudioPort; +using aidl::android::media::audio::common::AudioPortConfig; +using aidl::android::media::audio::common::MicrophoneInfo; + +namespace aidl::android::hardware::audio::core { + +ndk::ScopedAStatus ModulePrimary::getTelephony(std::shared_ptr* _aidl_return) { + if (!mTelephony) { + mTelephony = ndk::SharedRefBase::make(); + } + *_aidl_return = mTelephony.getPtr(); + LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus ModulePrimary::createInputStream(const SinkMetadata& sinkMetadata, + StreamContext&& context, + const std::vector& microphones, + std::shared_ptr* result) { + return createStreamInstance(result, sinkMetadata, std::move(context), + microphones); +} + +ndk::ScopedAStatus ModulePrimary::createOutputStream( + const SourceMetadata& sourceMetadata, StreamContext&& context, + const std::optional& offloadInfo, std::shared_ptr* result) { + return createStreamInstance(result, sourceMetadata, std::move(context), + offloadInfo); +} + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/android.hardware.audio.service-aidl.xml b/audio/aidl/default/android.hardware.audio.service-aidl.xml index 9636a58d48..c9d6314e32 100644 --- a/audio/aidl/default/android.hardware.audio.service-aidl.xml +++ b/audio/aidl/default/android.hardware.audio.service-aidl.xml @@ -9,6 +9,11 @@ 1 IModule/r_submix + + android.hardware.audio.core + 1 + IModule/stub + android.hardware.audio.core 1 diff --git a/audio/aidl/default/include/core-impl/ChildInterface.h b/audio/aidl/default/include/core-impl/ChildInterface.h new file mode 100644 index 0000000000..1b31691556 --- /dev/null +++ b/audio/aidl/default/include/core-impl/ChildInterface.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace aidl::android::hardware::audio::core { + +// Helper used for interfaces that require a persistent instance. We hold them via a strong +// pointer. The binder token is retained for a call to 'setMinSchedulerPolicy'. +template +struct ChildInterface : private std::pair, ndk::SpAIBinder> { + ChildInterface() = default; + ChildInterface& operator=(const std::shared_ptr& c) { + return operator=(std::shared_ptr(c)); + } + ChildInterface& operator=(std::shared_ptr&& c) { + this->first = std::move(c); + this->second = this->first->asBinder(); + AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO); + return *this; + } + explicit operator bool() const { return !!this->first; } + C& operator*() const { return *(this->first); } + C* operator->() const { return this->first; } + std::shared_ptr getPtr() const { return this->first; } +}; + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/Configuration.h b/audio/aidl/default/include/core-impl/Configuration.h index 70320e46aa..25bf7afa81 100644 --- a/audio/aidl/default/include/core-impl/Configuration.h +++ b/audio/aidl/default/include/core-impl/Configuration.h @@ -45,6 +45,7 @@ struct Configuration { std::unique_ptr getPrimaryConfiguration(); std::unique_ptr getRSubmixConfiguration(); +std::unique_ptr getStubConfiguration(); std::unique_ptr getUsbConfiguration(); } // namespace aidl::android::hardware::audio::core::internal diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h index 4a23637d86..ee0458be49 100644 --- a/audio/aidl/default/include/core-impl/Module.h +++ b/audio/aidl/default/include/core-impl/Module.h @@ -16,12 +16,14 @@ #pragma once +#include #include #include #include #include +#include "core-impl/ChildInterface.h" #include "core-impl/Configuration.h" #include "core-impl/Stream.h" @@ -31,7 +33,7 @@ class Module : public BnModule { public: // This value is used for all AudioPatches and reported by all streams. static constexpr int32_t kLatencyMs = 10; - enum Type : int { DEFAULT, R_SUBMIX, USB }; + enum Type : int { DEFAULT, R_SUBMIX, STUB, USB }; static std::shared_ptr createInstance(Type type); @@ -132,26 +134,6 @@ class Module : public BnModule { bool forceTransientBurst = false; bool forceSynchronousDrain = false; }; - // Helper used for interfaces that require a persistent instance. We hold them via a strong - // pointer. The binder token is retained for a call to 'setMinSchedulerPolicy'. - template - struct ChildInterface : private std::pair, ndk::SpAIBinder> { - ChildInterface() {} - ChildInterface& operator=(const std::shared_ptr& c) { - return operator=(std::shared_ptr(c)); - } - ChildInterface& operator=(std::shared_ptr&& c) { - this->first = std::move(c); - this->second = this->first->asBinder(); - AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL, - ANDROID_PRIORITY_AUDIO); - return *this; - } - explicit operator bool() const { return !!this->first; } - C& operator*() const { return *(this->first); } - C* operator->() const { return this->first; } - std::shared_ptr getPtr() const { return this->first; } - }; // ids of device ports created at runtime via 'connectExternalDevice'. // Also stores a list of ids of mix ports with dynamic profiles that were populated from // the connected port. This list can be empty, thus an int->int multimap can't be used. @@ -164,10 +146,6 @@ class Module : public BnModule { std::unique_ptr mConfig; ModuleDebug mDebug; VendorDebug mVendorDebug; - ChildInterface mTelephony; - ChildInterface mBluetooth; - ChildInterface mBluetoothA2dp; - ChildInterface mBluetoothLe; ConnectedDevicePorts mConnectedDevicePorts; Streams mStreams; Patches mPatches; @@ -184,13 +162,13 @@ class Module : public BnModule { const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata, StreamContext&& context, const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones, - std::shared_ptr* result); + std::shared_ptr* result) = 0; virtual ndk::ScopedAStatus createOutputStream( const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata, StreamContext&& context, const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>& offloadInfo, - std::shared_ptr* result); + std::shared_ptr* result) = 0; // If the module is unable to populate the connected device port correctly, the returned error // code must correspond to the errors of `IModule.connectedExternalDevice` method. virtual ndk::ScopedAStatus populateConnectedDevicePort( @@ -232,4 +210,6 @@ class Module : public BnModule { const AudioPatch& newPatch); }; +std::ostream& operator<<(std::ostream& os, Module::Type t); + } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/ModulePrimary.h b/audio/aidl/default/include/core-impl/ModulePrimary.h new file mode 100644 index 0000000000..bc808ab03d --- /dev/null +++ b/audio/aidl/default/include/core-impl/ModulePrimary.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "core-impl/Module.h" + +namespace aidl::android::hardware::audio::core { + +class ModulePrimary final : public Module { + public: + ModulePrimary() : Module(Type::DEFAULT) {} + + protected: + ndk::ScopedAStatus getTelephony(std::shared_ptr* _aidl_return) override; + + ndk::ScopedAStatus createInputStream( + const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata, + StreamContext&& context, + const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones, + std::shared_ptr* result) override; + ndk::ScopedAStatus createOutputStream( + const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata, + StreamContext&& context, + const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>& + offloadInfo, + std::shared_ptr* result) override; + + private: + ChildInterface mTelephony; +}; + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h index 7b1d375117..ccfcdd9462 100644 --- a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h +++ b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h @@ -22,7 +22,7 @@ namespace aidl::android::hardware::audio::core { class ModuleRemoteSubmix : public Module { public: - explicit ModuleRemoteSubmix(Module::Type type) : Module(type) {} + ModuleRemoteSubmix() : Module(Type::R_SUBMIX) {} private: // IModule interfaces diff --git a/audio/aidl/default/include/core-impl/ModuleStub.h b/audio/aidl/default/include/core-impl/ModuleStub.h new file mode 100644 index 0000000000..59c343f4b7 --- /dev/null +++ b/audio/aidl/default/include/core-impl/ModuleStub.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "core-impl/Module.h" + +namespace aidl::android::hardware::audio::core { + +class ModuleStub final : public Module { + public: + ModuleStub() : Module(Type::STUB) {} + + protected: + ndk::ScopedAStatus getBluetooth(std::shared_ptr* _aidl_return) override; + ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr* _aidl_return) override; + ndk::ScopedAStatus getBluetoothLe(std::shared_ptr* _aidl_return) override; + + ndk::ScopedAStatus createInputStream( + const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata, + StreamContext&& context, + const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones, + std::shared_ptr* result) override; + ndk::ScopedAStatus createOutputStream( + const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata, + StreamContext&& context, + const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>& + offloadInfo, + std::shared_ptr* result) override; + + private: + ChildInterface mBluetooth; + ChildInterface mBluetoothA2dp; + ChildInterface mBluetoothLe; +}; + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/ModuleUsb.h b/audio/aidl/default/include/core-impl/ModuleUsb.h index ea7cc48214..e6b3e66e41 100644 --- a/audio/aidl/default/include/core-impl/ModuleUsb.h +++ b/audio/aidl/default/include/core-impl/ModuleUsb.h @@ -22,7 +22,7 @@ namespace aidl::android::hardware::audio::core { class ModuleUsb final : public ModuleAlsa { public: - explicit ModuleUsb(Module::Type type) : ModuleAlsa(type) {} + ModuleUsb() : ModuleAlsa(Type::USB) {} private: // IModule interfaces diff --git a/audio/aidl/default/main.cpp b/audio/aidl/default/main.cpp index 12c0c4b53e..93fb36607e 100644 --- a/audio/aidl/default/main.cpp +++ b/audio/aidl/default/main.cpp @@ -16,20 +16,21 @@ #include #include +#include #include +#include #include +#include #include #include #include #include "core-impl/Config.h" #include "core-impl/Module.h" -#include "core-impl/ModuleUsb.h" using aidl::android::hardware::audio::core::Config; using aidl::android::hardware::audio::core::Module; -using aidl::android::hardware::audio::core::ModuleUsb; int main() { // Random values are used in the implementation. @@ -52,18 +53,19 @@ int main() { CHECK_EQ(STATUS_OK, status); // Make modules - auto createModule = [](Module::Type type, const std::string& instance) { + auto createModule = [](Module::Type type) { auto module = Module::createInstance(type); ndk::SpAIBinder moduleBinder = module->asBinder(); - const std::string moduleName = std::string(Module::descriptor).append("/").append(instance); + std::stringstream moduleName; + moduleName << Module::descriptor << "/" << type; AIBinder_setMinSchedulerPolicy(moduleBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO); - binder_status_t status = AServiceManager_addService(moduleBinder.get(), moduleName.c_str()); + binder_status_t status = + AServiceManager_addService(moduleBinder.get(), moduleName.str().c_str()); CHECK_EQ(STATUS_OK, status); return std::make_pair(module, moduleBinder); }; - auto modules = {createModule(Module::Type::DEFAULT, "default"), - createModule(Module::Type::R_SUBMIX, "r_submix"), - createModule(Module::Type::USB, "usb")}; + auto modules = {createModule(Module::Type::DEFAULT), createModule(Module::Type::R_SUBMIX), + createModule(Module::Type::USB), createModule(Module::Type::STUB)}; (void)modules; ABinderProcess_joinThreadPool(); diff --git a/audio/aidl/default/stub/ModuleStub.cpp b/audio/aidl/default/stub/ModuleStub.cpp new file mode 100644 index 0000000000..a60075221d --- /dev/null +++ b/audio/aidl/default/stub/ModuleStub.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#define LOG_TAG "AHAL_ModuleStub" +#include +#include + +#include "core-impl/Bluetooth.h" +#include "core-impl/ModuleStub.h" +#include "core-impl/StreamStub.h" + +using aidl::android::hardware::audio::common::SinkMetadata; +using aidl::android::hardware::audio::common::SourceMetadata; +using aidl::android::media::audio::common::AudioOffloadInfo; +using aidl::android::media::audio::common::AudioPort; +using aidl::android::media::audio::common::AudioPortConfig; +using aidl::android::media::audio::common::MicrophoneInfo; + +namespace aidl::android::hardware::audio::core { + +ndk::ScopedAStatus ModuleStub::getBluetooth(std::shared_ptr* _aidl_return) { + if (!mBluetooth) { + mBluetooth = ndk::SharedRefBase::make(); + } + *_aidl_return = mBluetooth.getPtr(); + LOG(DEBUG) << __func__ << ": returning instance of IBluetooth: " << _aidl_return->get(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus ModuleStub::getBluetoothA2dp(std::shared_ptr* _aidl_return) { + if (!mBluetoothA2dp) { + mBluetoothA2dp = ndk::SharedRefBase::make(); + } + *_aidl_return = mBluetoothA2dp.getPtr(); + LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus ModuleStub::getBluetoothLe(std::shared_ptr* _aidl_return) { + if (!mBluetoothLe) { + mBluetoothLe = ndk::SharedRefBase::make(); + } + *_aidl_return = mBluetoothLe.getPtr(); + LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus ModuleStub::createInputStream(const SinkMetadata& sinkMetadata, + StreamContext&& context, + const std::vector& microphones, + std::shared_ptr* result) { + return createStreamInstance(result, sinkMetadata, std::move(context), + microphones); +} + +ndk::ScopedAStatus ModuleStub::createOutputStream( + const SourceMetadata& sourceMetadata, StreamContext&& context, + const std::optional& offloadInfo, std::shared_ptr* result) { + return createStreamInstance(result, sourceMetadata, std::move(context), + offloadInfo); +} + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/StreamStub.cpp b/audio/aidl/default/stub/StreamStub.cpp similarity index 100% rename from audio/aidl/default/StreamStub.cpp rename to audio/aidl/default/stub/StreamStub.cpp From 704aec434e921c3cee833f9e113d89c3216c1f14 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 13 Jul 2023 11:08:29 -0700 Subject: [PATCH 3/3] audio: Add optional 'DriverInterface::getPosition' method. This is a method which can be optionally implemented by a stream in case it can provide more exact position, for example by taking into account data in intermediate buffers. Implemented this method for StreamAlsa and StreamRemoteSubmix. Bug: 264712385 Test: atest VtsHalAudioCoreTargetTest Change-Id: I392933f8f6b22d784726925199db00dcb0313648 --- audio/aidl/default/Stream.cpp | 8 +++-- audio/aidl/default/alsa/StreamAlsa.cpp | 32 +++++++++++++++++++ audio/aidl/default/include/core-impl/Stream.h | 6 ++++ .../default/include/core-impl/StreamAlsa.h | 1 + .../include/core-impl/StreamRemoteSubmix.h | 1 + .../default/r_submix/StreamRemoteSubmix.cpp | 21 ++++++++++++ 6 files changed, 66 insertions(+), 3 deletions(-) diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp index 251dea09e5..215de94a31 100644 --- a/audio/aidl/default/Stream.cpp +++ b/audio/aidl/default/Stream.cpp @@ -110,10 +110,12 @@ void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply, if (isConnected) { reply->observable.frames = mFrameCount; reply->observable.timeNs = ::android::elapsedRealtimeNano(); - } else { - reply->observable.frames = StreamDescriptor::Position::UNKNOWN; - reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN; + if (auto status = mDriver->getPosition(&reply->observable); status == ::android::OK) { + return; + } } + reply->observable.frames = StreamDescriptor::Position::UNKNOWN; + reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN; } void StreamWorkerCommonLogic::populateReplyWrongState( diff --git a/audio/aidl/default/alsa/StreamAlsa.cpp b/audio/aidl/default/alsa/StreamAlsa.cpp index ecb3c78a65..17c7febdd1 100644 --- a/audio/aidl/default/alsa/StreamAlsa.cpp +++ b/audio/aidl/default/alsa/StreamAlsa.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include "core-impl/StreamAlsa.h" @@ -96,6 +97,37 @@ StreamAlsa::StreamAlsa(const Metadata& metadata, StreamContext&& context) return ::android::OK; } +::android::status_t StreamAlsa::getPosition(StreamDescriptor::Position* position) { + if (mAlsaDeviceProxies.empty()) { + LOG(FATAL) << __func__ << ": no input devices"; + return ::android::NO_INIT; + } + if (mIsInput) { + if (int ret = proxy_get_capture_position(mAlsaDeviceProxies[0].get(), &position->frames, + &position->timeNs); + ret != 0) { + LOG(WARNING) << __func__ << ": failed to retrieve capture position: " << ret; + return ::android::INVALID_OPERATION; + } + } else { + uint64_t hwFrames; + struct timespec timestamp; + if (int ret = proxy_get_presentation_position(mAlsaDeviceProxies[0].get(), &hwFrames, + ×tamp); + ret == 0) { + if (hwFrames > std::numeric_limits::max()) { + hwFrames -= std::numeric_limits::max(); + } + position->frames = static_cast(hwFrames); + position->timeNs = audio_utils_ns_from_timespec(×tamp); + } else { + LOG(WARNING) << __func__ << ": failed to retrieve presentation position: " << ret; + return ::android::INVALID_OPERATION; + } + } + return ::android::OK; +} + void StreamAlsa::shutdown() { mAlsaDeviceProxies.clear(); } diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h index aaf58601b5..e64c578b68 100644 --- a/audio/aidl/default/include/core-impl/Stream.h +++ b/audio/aidl/default/include/core-impl/Stream.h @@ -184,6 +184,12 @@ struct DriverInterface { virtual ::android::status_t start() = 0; virtual ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, int32_t* latencyMs) = 0; + // No need to implement 'getPosition' unless the driver can provide more precise + // data than just total frame count. For example, the driver may correctly account + // for any intermediate buffers. + virtual ::android::status_t getPosition(StreamDescriptor::Position* /*position*/) { + return ::android::OK; + } virtual void shutdown() = 0; // This function is only called once. }; diff --git a/audio/aidl/default/include/core-impl/StreamAlsa.h b/audio/aidl/default/include/core-impl/StreamAlsa.h index edfc72860c..5744d665f3 100644 --- a/audio/aidl/default/include/core-impl/StreamAlsa.h +++ b/audio/aidl/default/include/core-impl/StreamAlsa.h @@ -38,6 +38,7 @@ class StreamAlsa : public StreamCommonImpl { ::android::status_t start() override; ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, int32_t* latencyMs) override; + ::android::status_t getPosition(StreamDescriptor::Position* position) override; void shutdown() override; protected: diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h index 2253ec76cd..1bca910995 100644 --- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h +++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h @@ -39,6 +39,7 @@ class StreamRemoteSubmix : public StreamCommonImpl { ::android::status_t start() override; ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, int32_t* latencyMs) override; + ::android::status_t getPosition(StreamDescriptor::Position* position) override; void shutdown() override; // Overridden methods of 'StreamCommonImpl', called on a Binder thread. diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp index 5af0d914b8..6d5185b346 100644 --- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp +++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp @@ -179,6 +179,27 @@ void StreamRemoteSubmix::shutdown() { : outWrite(buffer, frameCount, actualFrameCount)); } +::android::status_t StreamRemoteSubmix::getPosition(StreamDescriptor::Position* position) { + sp source = mCurrentRoute->getSource(); + if (source == nullptr) { + return ::android::NO_INIT; + } + const ssize_t framesInPipe = source->availableToRead(); + if (framesInPipe < 0) { + return ::android::INVALID_OPERATION; + } + if (mIsInput) { + position->frames += framesInPipe; + } else { + if (position->frames > framesInPipe) { + position->frames -= framesInPipe; + } else { + position->frames = 0; + } + } + return ::android::OK; +} + // Calculate the maximum size of the pipe buffer in frames for the specified stream. size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() { auto pipeConfig = mCurrentRoute->mPipeConfig;