diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp index 78b59d4efe..19b2397de6 100644 --- a/audio/aidl/default/Android.bp +++ b/audio/aidl/default/Android.bp @@ -73,18 +73,23 @@ cc_library { "Configuration.cpp", "EngineConfigXmlConverter.cpp", "Module.cpp", + "ModulePrimary.cpp", "SoundDose.cpp", "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", "r_submix/StreamRemoteSubmix.cpp", + "stub/ModuleStub.cpp", + "stub/StreamStub.cpp", "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/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/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/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..17c7febdd1 --- /dev/null +++ b/audio/aidl/default/alsa/StreamAlsa.cpp @@ -0,0 +1,135 @@ +/* + * 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 + +#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; +} + +::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(); +} + +} // 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/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/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/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 5a5429db64..e6b3e66e41 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) {} + ModuleUsb() : ModuleAlsa(Type::USB) {} private: // IModule interfaces 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 new file mode 100644 index 0000000000..5744d665f3 --- /dev/null +++ b/audio/aidl/default/include/core-impl/StreamAlsa.h @@ -0,0 +1,55 @@ +/* + * 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; + ::android::status_t getPosition(StreamDescriptor::Position* position) 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/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/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/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/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; 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 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