From ecdc6ca8e872a0478fae7d76b1ad4d805b024eed Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 11 Nov 2021 22:09:22 +0000 Subject: [PATCH] audio HAL: Minimal example implementation Implements basic functionality for enumerating capabilities of an audio module, audio patches creation, and opening of I/O streams. Bug: 205884982 Test: atest VtsHalAudioCoreTargetTest Change-Id: Ie5d67e9192a598260e762ae9368f99592c8ad97e --- audio/aidl/default/Android.bp | 45 ++ audio/aidl/default/Config.cpp | 19 + audio/aidl/default/Configuration.cpp | 196 +++++++ audio/aidl/default/Module.cpp | 522 ++++++++++++++++++ audio/aidl/default/Stream.cpp | 76 +++ ...oid.hardware.audio.service-aidl.example.rc | 9 + .../android.hardware.audio.service-aidl.xml | 12 + audio/aidl/default/include/core-impl/Config.h | 25 + .../default/include/core-impl/Configuration.h | 40 ++ audio/aidl/default/include/core-impl/Module.h | 72 +++ audio/aidl/default/include/core-impl/Stream.h | 103 ++++ audio/aidl/default/include/core-impl/utils.h | 104 ++++ audio/aidl/default/main.cpp | 45 ++ .../compatibility_matrix.current.xml | 12 + 14 files changed, 1280 insertions(+) create mode 100644 audio/aidl/default/Android.bp create mode 100644 audio/aidl/default/Config.cpp create mode 100644 audio/aidl/default/Configuration.cpp create mode 100644 audio/aidl/default/Module.cpp create mode 100644 audio/aidl/default/Stream.cpp create mode 100644 audio/aidl/default/android.hardware.audio.service-aidl.example.rc create mode 100644 audio/aidl/default/android.hardware.audio.service-aidl.xml create mode 100644 audio/aidl/default/include/core-impl/Config.h create mode 100644 audio/aidl/default/include/core-impl/Configuration.h create mode 100644 audio/aidl/default/include/core-impl/Module.h create mode 100644 audio/aidl/default/include/core-impl/Stream.h create mode 100644 audio/aidl/default/include/core-impl/utils.h create mode 100644 audio/aidl/default/main.cpp diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp new file mode 100644 index 0000000000..0a6fe60c39 --- /dev/null +++ b/audio/aidl/default/Android.bp @@ -0,0 +1,45 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +cc_library_static { + name: "libaudioserviceexampleimpl", + vendor: true, + shared_libs: [ + "libbase", + "libbinder_ndk", + "android.hardware.audio.core-V1-ndk", + ], + export_include_dirs: ["include"], + srcs: [ + "Config.cpp", + "Configuration.cpp", + "Module.cpp", + "Stream.cpp", + ], + visibility: [ + ":__subpackages__", + ], +} + +cc_binary { + name: "android.hardware.audio.service-aidl.example", + relative_install_path: "hw", + init_rc: ["android.hardware.audio.service-aidl.example.rc"], + vintf_fragments: ["android.hardware.audio.service-aidl.xml"], + vendor: true, + shared_libs: [ + "libbase", + "libbinder_ndk", + "android.hardware.audio.core-V1-ndk", + ], + static_libs: [ + "libaudioserviceexampleimpl", + ], + srcs: ["main.cpp"], +} diff --git a/audio/aidl/default/Config.cpp b/audio/aidl/default/Config.cpp new file mode 100644 index 0000000000..3f7a3d3e6f --- /dev/null +++ b/audio/aidl/default/Config.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "core-impl/Config.h" + +namespace aidl::android::hardware::audio::core {} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp new file mode 100644 index 0000000000..1104caadcf --- /dev/null +++ b/audio/aidl/default/Configuration.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include "aidl/android/media/audio/common/AudioFormatDescription.h" +#include "core-impl/Configuration.h" + +using aidl::android::media::audio::common::AudioChannelLayout; +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::AudioGainConfig; +using aidl::android::media::audio::common::AudioIoFlags; +using aidl::android::media::audio::common::AudioOutputFlags; +using aidl::android::media::audio::common::AudioPort; +using aidl::android::media::audio::common::AudioPortConfig; +using aidl::android::media::audio::common::AudioPortDeviceExt; +using aidl::android::media::audio::common::AudioPortExt; +using aidl::android::media::audio::common::AudioPortMixExt; +using aidl::android::media::audio::common::AudioProfile; +using aidl::android::media::audio::common::Int; +using aidl::android::media::audio::common::PcmType; + +namespace aidl::android::hardware::audio::core::internal { + +static AudioProfile createProfile(PcmType pcmType, const std::vector& channelLayouts, + const std::vector& sampleRates) { + AudioProfile profile; + profile.format.type = AudioFormatType::PCM; + profile.format.pcm = pcmType; + for (auto layout : channelLayouts) { + profile.channelMasks.push_back( + AudioChannelLayout::make(layout)); + } + profile.sampleRates.insert(profile.sampleRates.end(), sampleRates.begin(), sampleRates.end()); + return profile; +} + +static AudioPortExt createDeviceExt(AudioDeviceType devType, int32_t flags) { + AudioPortDeviceExt deviceExt; + deviceExt.device.type.type = devType; + deviceExt.flags = flags; + return AudioPortExt::make(deviceExt); +} + +static AudioPortExt createPortMixExt(int32_t maxOpenStreamCount, int32_t maxActiveStreamCount) { + AudioPortMixExt mixExt; + mixExt.maxOpenStreamCount = maxOpenStreamCount; + mixExt.maxActiveStreamCount = maxActiveStreamCount; + return AudioPortExt::make(mixExt); +} + +static AudioPort createPort(int32_t id, const std::string& name, int32_t flags, bool isInput, + const AudioPortExt& ext) { + AudioPort port; + port.id = id; + port.name = name; + port.flags = isInput ? AudioIoFlags::make(flags) + : AudioIoFlags::make(flags); + port.ext = ext; + return port; +} + +static AudioPortConfig createPortConfig(int32_t id, int32_t portId, PcmType pcmType, int32_t layout, + int32_t sampleRate, int32_t flags, bool isInput, + const AudioPortExt& ext) { + AudioPortConfig config; + config.id = id; + config.portId = portId; + config.sampleRate = Int{.value = sampleRate}; + config.channelMask = AudioChannelLayout::make(layout); + config.format = AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = pcmType}; + config.gain = AudioGainConfig(); + config.flags = isInput ? AudioIoFlags::make(flags) + : AudioIoFlags::make(flags); + config.ext = ext; + return config; +} + +static AudioRoute createRoute(const std::vector& sources, int32_t sink) { + AudioRoute route; + route.sinkPortId = sink; + route.sourcePortIds.insert(route.sourcePortIds.end(), sources.begin(), sources.end()); + return route; +} + +Configuration& getNullPrimaryConfiguration() { + static Configuration configuration = []() { + Configuration c; + + AudioPort nullOutDevice = + createPort(c.nextPortId++, "Null", 0, false, + createDeviceExt(AudioDeviceType::OUT_SPEAKER, + 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE)); + c.ports.push_back(nullOutDevice); + + AudioPort primaryOutMix = createPort(c.nextPortId++, "primary output", + 1 << static_cast(AudioOutputFlags::PRIMARY), + false, createPortMixExt(1, 1)); + primaryOutMix.profiles.push_back( + createProfile(PcmType::INT_16_BIT, + {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, + {44100, 48000})); + primaryOutMix.profiles.push_back( + createProfile(PcmType::INT_24_BIT, + {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, + {44100, 48000})); + c.ports.push_back(primaryOutMix); + + c.routes.push_back(createRoute({primaryOutMix.id}, nullOutDevice.id)); + + c.initialConfigs.push_back( + createPortConfig(nullOutDevice.id, nullOutDevice.id, PcmType::INT_24_BIT, + AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false, + createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0))); + + AudioPort loopOutDevice = createPort(c.nextPortId++, "Loopback Out", 0, false, + createDeviceExt(AudioDeviceType::OUT_SUBMIX, 0)); + loopOutDevice.profiles.push_back( + createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000})); + c.ports.push_back(loopOutDevice); + + AudioPort loopOutMix = + createPort(c.nextPortId++, "loopback output", 0, false, createPortMixExt(0, 0)); + loopOutMix.profiles.push_back( + createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000})); + c.ports.push_back(loopOutMix); + + c.routes.push_back(createRoute({loopOutMix.id}, loopOutDevice.id)); + + AudioPort zeroInDevice = + createPort(c.nextPortId++, "Zero", 0, true, + createDeviceExt(AudioDeviceType::IN_MICROPHONE, + 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE)); + c.ports.push_back(zeroInDevice); + + AudioPort primaryInMix = + createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(2, 2)); + primaryInMix.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})); + primaryInMix.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(primaryInMix); + + c.routes.push_back(createRoute({zeroInDevice.id}, primaryInMix.id)); + + c.initialConfigs.push_back( + createPortConfig(zeroInDevice.id, zeroInDevice.id, PcmType::INT_24_BIT, + AudioChannelLayout::LAYOUT_MONO, 48000, 0, true, + createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0))); + + AudioPort loopInDevice = createPort(c.nextPortId++, "Loopback In", 0, true, + createDeviceExt(AudioDeviceType::IN_SUBMIX, 0)); + loopInDevice.profiles.push_back( + createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000})); + c.ports.push_back(loopInDevice); + + AudioPort loopInMix = + createPort(c.nextPortId++, "loopback input", 0, true, createPortMixExt(0, 0)); + loopInMix.profiles.push_back( + createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000})); + c.ports.push_back(loopInMix); + + c.routes.push_back(createRoute({loopInDevice.id}, loopInMix.id)); + + c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end()); + return c; + }(); + return configuration; +} + +} // namespace aidl::android::hardware::audio::core::internal diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp new file mode 100644 index 0000000000..e0a68a5fcd --- /dev/null +++ b/audio/aidl/default/Module.cpp @@ -0,0 +1,522 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#define LOG_TAG "AHAL_Module" +#define LOG_NDEBUG 0 +#include + +#include + +#include "core-impl/Module.h" +#include "core-impl/utils.h" + +using aidl::android::hardware::audio::common::SinkMetadata; +using aidl::android::hardware::audio::common::SourceMetadata; +using aidl::android::media::audio::common::AudioFormatDescription; +using aidl::android::media::audio::common::AudioIoFlags; +using aidl::android::media::audio::common::AudioOffloadInfo; +using aidl::android::media::audio::common::AudioOutputFlags; +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::Int; + +namespace aidl::android::hardware::audio::core { + +namespace { + +bool generateDefaultPortConfig(const AudioPort& port, AudioPortConfig* config) { + *config = {}; + config->portId = port.id; + if (port.profiles.empty()) { + LOG(ERROR) << __func__ << ": port " << port.id << " has no profiles"; + return false; + } + const auto& profile = port.profiles.begin(); + config->format = profile->format; + if (profile->channelMasks.empty()) { + LOG(ERROR) << __func__ << ": the first profile in port " << port.id + << " has no channel masks"; + return false; + } + config->channelMask = *profile->channelMasks.begin(); + if (profile->sampleRates.empty()) { + LOG(ERROR) << __func__ << ": the first profile in port " << port.id + << " has no sample rates"; + return false; + } + Int sampleRate; + sampleRate.value = *profile->sampleRates.begin(); + config->sampleRate = sampleRate; + config->flags = port.flags; + config->ext = port.ext; + return true; +} + +bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& format, + AudioProfile* profile) { + if (auto profilesIt = + find_if(port.profiles.begin(), port.profiles.end(), + [&format](const auto& profile) { return profile.format == format; }); + profilesIt != port.profiles.end()) { + *profile = *profilesIt; + return true; + } + return false; +} +} // namespace + +void Module::cleanUpPatch(int32_t patchId) { + erase_all_values(mPatches, std::set{patchId}); +} + +void Module::cleanUpPatches(int32_t portConfigId) { + auto& patches = getConfig().patches; + if (patches.size() == 0) return; + auto range = mPatches.equal_range(portConfigId); + for (auto it = range.first; it != range.second; ++it) { + auto patchIt = findById(patches, it->second); + if (patchIt != patches.end()) { + erase_if(patchIt->sourcePortConfigIds, + [portConfigId](auto e) { return e == portConfigId; }); + erase_if(patchIt->sinkPortConfigIds, + [portConfigId](auto e) { return e == portConfigId; }); + } + } + std::set erasedPatches; + for (size_t i = patches.size() - 1; i != 0; --i) { + const auto& patch = patches[i]; + if (patch.sourcePortConfigIds.empty() || patch.sinkPortConfigIds.empty()) { + erasedPatches.insert(patch.id); + patches.erase(patches.begin() + i); + } + } + erase_all_values(mPatches, erasedPatches); +} + +internal::Configuration& Module::getConfig() { + if (!mConfig) { + mConfig.reset(new internal::Configuration(internal::getNullPrimaryConfiguration())); + } + return *mConfig; +} + +void Module::registerPatch(const AudioPatch& patch) { + auto& configs = getConfig().portConfigs; + auto do_insert = [&](const std::vector& portConfigIds) { + for (auto portConfigId : portConfigIds) { + auto configIt = findById(configs, portConfigId); + if (configIt != configs.end()) { + mPatches.insert(std::pair{portConfigId, patch.id}); + if (configIt->portId != portConfigId) { + mPatches.insert(std::pair{configIt->portId, patch.id}); + } + } + }; + }; + do_insert(patch.sourcePortConfigIds); + do_insert(patch.sinkPortConfigIds); +} + +ndk::ScopedAStatus Module::getAudioPatches(std::vector* _aidl_return) { + *_aidl_return = getConfig().patches; + LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " patches"; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Module::getAudioPort(int32_t in_portId, AudioPort* _aidl_return) { + auto& ports = getConfig().ports; + auto portIt = findById(ports, in_portId); + if (portIt != ports.end()) { + *_aidl_return = *portIt; + LOG(DEBUG) << __func__ << ": returning port by id " << in_portId; + return ndk::ScopedAStatus::ok(); + } + LOG(ERROR) << __func__ << ": port id " << in_portId << " not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); +} + +ndk::ScopedAStatus Module::getAudioPortConfigs(std::vector* _aidl_return) { + *_aidl_return = getConfig().portConfigs; + LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " port configs"; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Module::getAudioPorts(std::vector* _aidl_return) { + *_aidl_return = getConfig().ports; + LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " ports"; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Module::getAudioRoutes(std::vector* _aidl_return) { + *_aidl_return = getConfig().routes; + LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " routes"; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Module::openInputStream(int32_t in_portConfigId, + const SinkMetadata& in_sinkMetadata, + std::shared_ptr* _aidl_return) { + auto& configs = getConfig().portConfigs; + auto portConfigIt = findById(configs, in_portConfigId); + if (portConfigIt == configs.end()) { + LOG(ERROR) << __func__ << ": existing port config id " << in_portConfigId << " not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + const int32_t portId = portConfigIt->portId; + // In our implementation, configs of mix ports always have unique IDs. + CHECK(portId != in_portConfigId); + auto& ports = getConfig().ports; + auto portIt = findById(ports, portId); + if (portIt == ports.end()) { + LOG(ERROR) << __func__ << ": port id " << portId << " used by port config id " + << in_portConfigId << " not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (portIt->flags.getTag() != AudioIoFlags::Tag::input || + portIt->ext.getTag() != AudioPortExt::Tag::mix) { + LOG(ERROR) << __func__ << ": port config id " << in_portConfigId + << " does not correspond to an input mix port"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (mStreams.count(in_portConfigId) != 0) { + LOG(ERROR) << __func__ << ": port config id " << in_portConfigId + << " already has a stream opened on it"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + const int32_t maxOpenStreamCount = portIt->ext.get().maxOpenStreamCount; + if (maxOpenStreamCount != 0 && mStreams.count(portId) >= maxOpenStreamCount) { + LOG(ERROR) << __func__ << ": port id " << portId + << " has already reached maximum allowed opened stream count: " + << maxOpenStreamCount; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + auto stream = ndk::SharedRefBase::make(in_sinkMetadata); + mStreams.insert(portId, in_portConfigId, StreamWrapper(stream)); + *_aidl_return = std::move(stream); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Module::openOutputStream(int32_t in_portConfigId, + const SourceMetadata& in_sourceMetadata, + const std::optional& in_offloadInfo, + std::shared_ptr* _aidl_return) { + auto& configs = getConfig().portConfigs; + auto portConfigIt = findById(configs, in_portConfigId); + if (portConfigIt == configs.end()) { + LOG(ERROR) << __func__ << ": existing port config id " << in_portConfigId << " not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + const int32_t portId = portConfigIt->portId; + // In our implementation, configs of mix ports always have unique IDs. + CHECK(portId != in_portConfigId); + auto& ports = getConfig().ports; + auto portIt = findById(ports, portId); + if (portIt == ports.end()) { + LOG(ERROR) << __func__ << ": port id " << portId << " used by port config id " + << in_portConfigId << " not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (portIt->flags.getTag() != AudioIoFlags::Tag::output || + portIt->ext.getTag() != AudioPortExt::Tag::mix) { + LOG(ERROR) << __func__ << ": port config id " << in_portConfigId + << " does not correspond to an output mix port"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (mStreams.count(in_portConfigId) != 0) { + LOG(ERROR) << __func__ << ": port config id " << in_portConfigId + << " already has a stream opened on it"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + const int32_t maxOpenStreamCount = portIt->ext.get().maxOpenStreamCount; + if (maxOpenStreamCount != 0 && mStreams.count(portId) >= maxOpenStreamCount) { + LOG(ERROR) << __func__ << ": port id " << portId + << " has already reached maximum allowed opened stream count: " + << maxOpenStreamCount; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + auto stream = ndk::SharedRefBase::make(in_sourceMetadata, in_offloadInfo); + mStreams.insert(portId, in_portConfigId, StreamWrapper(stream)); + *_aidl_return = std::move(stream); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Module::setAudioPatch(const AudioPatch& in_requested, AudioPatch* _aidl_return) { + if (in_requested.sourcePortConfigIds.empty()) { + LOG(ERROR) << __func__ << ": requested patch has empty sources list"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (!all_unique(in_requested.sourcePortConfigIds)) { + LOG(ERROR) << __func__ << ": requested patch has duplicate ids in the sources list"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (in_requested.sinkPortConfigIds.empty()) { + LOG(ERROR) << __func__ << ": requested patch has empty sinks list"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (!all_unique(in_requested.sinkPortConfigIds)) { + LOG(ERROR) << __func__ << ": requested patch has duplicate ids in the sinks list"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + auto& configs = getConfig().portConfigs; + std::vector missingIds; + auto sources = + selectByIds(configs, in_requested.sourcePortConfigIds, &missingIds); + if (!missingIds.empty()) { + LOG(ERROR) << __func__ << ": following source port config ids not found: " + << ::android::internal::ToString(missingIds); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + auto sinks = selectByIds(configs, in_requested.sinkPortConfigIds, &missingIds); + if (!missingIds.empty()) { + LOG(ERROR) << __func__ << ": following sink port config ids not found: " + << ::android::internal::ToString(missingIds); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + // bool indicates whether a non-exclusive route is available. + // If only an exclusive route is available, that means the patch can not be + // established if there is any other patch which currently uses the sink port. + std::map allowedSinkPorts; + auto& routes = getConfig().routes; + for (auto src : sources) { + for (const auto& r : routes) { + const auto& srcs = r.sourcePortIds; + if (std::find(srcs.begin(), srcs.end(), src->portId) != srcs.end()) { + if (!allowedSinkPorts[r.sinkPortId]) { // prefer non-exclusive + allowedSinkPorts[r.sinkPortId] = !r.isExclusive; + } + } + } + } + for (auto sink : sinks) { + if (allowedSinkPorts.count(sink->portId) == 0) { + LOG(ERROR) << __func__ << ": there is no route to the sink port id " << sink->portId; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + } + + auto& patches = getConfig().patches; + auto existing = patches.end(); + std::optional patchesBackup; + if (in_requested.id != 0) { + existing = findById(patches, in_requested.id); + if (existing != patches.end()) { + patchesBackup = mPatches; + cleanUpPatch(existing->id); + } else { + LOG(ERROR) << __func__ << ": not found existing patch id " << in_requested.id; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + } + // Validate the requested patch. + for (const auto& [sinkPortId, nonExclusive] : allowedSinkPorts) { + if (!nonExclusive && mPatches.count(sinkPortId) != 0) { + LOG(ERROR) << __func__ << ": sink port id " << sinkPortId + << "is exclusive and is already used by some other patch"; + if (patchesBackup.has_value()) { + mPatches = std::move(*patchesBackup); + } + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + } + *_aidl_return = in_requested; + if (existing == patches.end()) { + _aidl_return->id = getConfig().nextPatchId++; + patches.push_back(*_aidl_return); + existing = patches.begin() + (patches.size() - 1); + } else { + *existing = *_aidl_return; + } + registerPatch(*existing); + LOG(DEBUG) << __func__ << ": created or updated patch id " << _aidl_return->id; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requested, + AudioPortConfig* out_suggested, bool* _aidl_return) { + LOG(DEBUG) << __func__ << ": requested " << in_requested.toString(); + auto& configs = getConfig().portConfigs; + auto existing = configs.end(); + if (in_requested.id != 0) { + if (existing = findById(configs, in_requested.id); + existing == configs.end()) { + LOG(ERROR) << __func__ << ": existing port config id " << in_requested.id + << " not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + } + + const int portId = existing != configs.end() ? existing->portId : in_requested.portId; + if (portId == 0) { + LOG(ERROR) << __func__ << ": input port config does not specify portId"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + auto& ports = getConfig().ports; + auto portIt = findById(ports, portId); + if (portIt == ports.end()) { + LOG(ERROR) << __func__ << ": input port config points to non-existent portId " << portId; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (existing != configs.end()) { + *out_suggested = *existing; + } else { + AudioPortConfig newConfig; + if (generateDefaultPortConfig(*portIt, &newConfig)) { + *out_suggested = newConfig; + } else { + LOG(ERROR) << __func__ << ": unable generate a default config for port " << portId; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + } + // From this moment, 'out_suggested' is either an existing port config, + // or a new generated config. Now attempt to update it according to the specified + // fields of 'in_requested'. + + bool requestedIsValid = true, requestedIsFullySpecified = true; + + AudioIoFlags portFlags = portIt->flags; + if (in_requested.flags.has_value()) { + if (in_requested.flags.value() != portFlags) { + LOG(WARNING) << __func__ << ": requested flags " + << in_requested.flags.value().toString() << " do not match port's " + << portId << " flags " << portFlags.toString(); + requestedIsValid = false; + } + } else { + requestedIsFullySpecified = false; + } + + AudioProfile portProfile; + if (in_requested.format.has_value()) { + const auto& format = in_requested.format.value(); + if (findAudioProfile(*portIt, format, &portProfile)) { + out_suggested->format = format; + } else { + LOG(WARNING) << __func__ << ": requested format " << format.toString() + << " is not found in port's " << portId << " profiles"; + requestedIsValid = false; + } + } else { + requestedIsFullySpecified = false; + } + if (!findAudioProfile(*portIt, out_suggested->format.value(), &portProfile)) { + LOG(ERROR) << __func__ << ": port " << portId << " does not support format " + << out_suggested->format.value().toString() << " anymore"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + if (in_requested.channelMask.has_value()) { + const auto& channelMask = in_requested.channelMask.value(); + if (find(portProfile.channelMasks.begin(), portProfile.channelMasks.end(), channelMask) != + portProfile.channelMasks.end()) { + out_suggested->channelMask = channelMask; + } else { + LOG(WARNING) << __func__ << ": requested channel mask " << channelMask.toString() + << " is not supported for the format " << portProfile.format.toString() + << " by the port " << portId; + requestedIsValid = false; + } + } else { + requestedIsFullySpecified = false; + } + + if (in_requested.sampleRate.has_value()) { + const auto& sampleRate = in_requested.sampleRate.value(); + if (find(portProfile.sampleRates.begin(), portProfile.sampleRates.end(), + sampleRate.value) != portProfile.sampleRates.end()) { + out_suggested->sampleRate = sampleRate; + } else { + LOG(WARNING) << __func__ << ": requested sample rate " << sampleRate.value + << " is not supported for the format " << portProfile.format.toString() + << " by the port " << portId; + requestedIsValid = false; + } + } else { + requestedIsFullySpecified = false; + } + + if (in_requested.gain.has_value()) { + // Let's pretend that gain can always be applied. + out_suggested->gain = in_requested.gain.value(); + } + + if (existing == configs.end() && requestedIsValid && requestedIsFullySpecified) { + out_suggested->id = getConfig().nextPortId++; + configs.push_back(*out_suggested); + *_aidl_return = true; + LOG(DEBUG) << __func__ << ": created new port config " << out_suggested->toString(); + } else if (existing != configs.end() && requestedIsValid) { + *existing = *out_suggested; + *_aidl_return = true; + LOG(DEBUG) << __func__ << ": updated port config " << out_suggested->toString(); + } else { + LOG(DEBUG) << __func__ << ": not applied; existing config ? " << (existing != configs.end()) + << "; requested is valid? " << requestedIsValid << ", fully specified? " + << requestedIsFullySpecified; + *_aidl_return = false; + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Module::resetAudioPatch(int32_t in_patchId) { + auto& patches = getConfig().patches; + auto patchIt = findById(patches, in_patchId); + if (patchIt != patches.end()) { + cleanUpPatch(patchIt->id); + patches.erase(patchIt); + LOG(DEBUG) << __func__ << ": erased patch " << in_patchId; + return ndk::ScopedAStatus::ok(); + } + LOG(ERROR) << __func__ << ": patch id " << in_patchId << " not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); +} + +ndk::ScopedAStatus Module::resetAudioPortConfig(int32_t in_portConfigId) { + auto& configs = getConfig().portConfigs; + auto configIt = findById(configs, in_portConfigId); + if (configIt != configs.end()) { + if (mStreams.count(in_portConfigId) != 0) { + LOG(ERROR) << __func__ << ": port config id " << in_portConfigId + << " has a stream opened on it"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + auto patchIt = mPatches.find(in_portConfigId); + if (patchIt != mPatches.end()) { + LOG(ERROR) << __func__ << ": port config id " << in_portConfigId + << " is used by the patch with id " << patchIt->second; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + auto& initials = getConfig().initialConfigs; + auto initialIt = findById(initials, in_portConfigId); + if (initialIt == initials.end()) { + configs.erase(configIt); + LOG(DEBUG) << __func__ << ": erased port config " << in_portConfigId; + } else if (*configIt != *initialIt) { + *configIt = *initialIt; + LOG(DEBUG) << __func__ << ": reset port config " << in_portConfigId; + } + return ndk::ScopedAStatus::ok(); + } + LOG(ERROR) << __func__ << ": port config id " << in_portConfigId << " not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); +} + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp new file mode 100644 index 0000000000..e16b2c62b9 --- /dev/null +++ b/audio/aidl/default/Stream.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AHAL_Stream" +#define LOG_NDEBUG 0 +#include + +#include "core-impl/Stream.h" + +using aidl::android::hardware::audio::common::SinkMetadata; +using aidl::android::hardware::audio::common::SourceMetadata; +using aidl::android::media::audio::common::AudioOffloadInfo; + +namespace aidl::android::hardware::audio::core { + +StreamIn::StreamIn(const SinkMetadata& sinkMetadata) : mMetadata(sinkMetadata) {} + +ndk::ScopedAStatus StreamIn::close() { + LOG(DEBUG) << __func__; + if (!mIsClosed) { + mIsClosed = true; + return ndk::ScopedAStatus::ok(); + } else { + LOG(ERROR) << __func__ << ": stream was already closed"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } +} + +ndk::ScopedAStatus StreamIn::updateMetadata(const SinkMetadata& in_sinkMetadata) { + LOG(DEBUG) << __func__; + if (!mIsClosed) { + mMetadata = in_sinkMetadata; + return ndk::ScopedAStatus::ok(); + } + LOG(ERROR) << __func__ << ": stream was closed"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); +} + +StreamOut::StreamOut(const SourceMetadata& sourceMetadata, + const std::optional& offloadInfo) + : mMetadata(sourceMetadata), mOffloadInfo(offloadInfo) {} + +ndk::ScopedAStatus StreamOut::close() { + LOG(DEBUG) << __func__; + if (!mIsClosed) { + mIsClosed = true; + return ndk::ScopedAStatus::ok(); + } + LOG(ERROR) << __func__ << ": stream was already closed"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); +} + +ndk::ScopedAStatus StreamOut::updateMetadata(const SourceMetadata& in_sourceMetadata) { + LOG(DEBUG) << __func__; + if (!mIsClosed) { + mMetadata = in_sourceMetadata; + return ndk::ScopedAStatus::ok(); + } + LOG(ERROR) << __func__ << ": stream was closed"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); +} + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/android.hardware.audio.service-aidl.example.rc b/audio/aidl/default/android.hardware.audio.service-aidl.example.rc new file mode 100644 index 0000000000..02a9c37d42 --- /dev/null +++ b/audio/aidl/default/android.hardware.audio.service-aidl.example.rc @@ -0,0 +1,9 @@ +service vendor.audio-hal-aidl /vendor/bin/hw/android.hardware.audio.service-aidl.example + class hal + user audioserver + # media gid needed for /dev/fm (radio) and for /data/misc/media (tee) + group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub + capabilities BLOCK_SUSPEND + ioprio rt 4 + task_profiles ProcessCapacityHigh HighPerformance + onrestart restart audioserver diff --git a/audio/aidl/default/android.hardware.audio.service-aidl.xml b/audio/aidl/default/android.hardware.audio.service-aidl.xml new file mode 100644 index 0000000000..bb4b01a737 --- /dev/null +++ b/audio/aidl/default/android.hardware.audio.service-aidl.xml @@ -0,0 +1,12 @@ + + + android.hardware.audio.core + 1 + IModule/default + + + android.hardware.audio.core + 1 + IConfig/default + + diff --git a/audio/aidl/default/include/core-impl/Config.h b/audio/aidl/default/include/core-impl/Config.h new file mode 100644 index 0000000000..b62a14be4d --- /dev/null +++ b/audio/aidl/default/include/core-impl/Config.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace aidl::android::hardware::audio::core { + +class Config : public BnConfig {}; + +} // 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 new file mode 100644 index 0000000000..17e342d80a --- /dev/null +++ b/audio/aidl/default/include/core-impl/Configuration.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace aidl::android::hardware::audio::core::internal { + +struct Configuration { + std::vector<::aidl::android::media::audio::common::AudioPort> ports; + std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs; + std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs; + std::vector routes; + std::vector patches; + int32_t nextPortId = 1; + int32_t nextPatchId = 1; +}; + +Configuration& getNullPrimaryConfiguration(); + +} // 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 new file mode 100644 index 0000000000..359626cc22 --- /dev/null +++ b/audio/aidl/default/include/core-impl/Module.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include + +#include "core-impl/Configuration.h" +#include "core-impl/Stream.h" + +namespace aidl::android::hardware::audio::core { + +class Module : public BnModule { + ndk::ScopedAStatus getAudioPatches(std::vector* _aidl_return) override; + ndk::ScopedAStatus getAudioPort( + int32_t in_portId, + ::aidl::android::media::audio::common::AudioPort* _aidl_return) override; + ndk::ScopedAStatus getAudioPortConfigs( + std::vector<::aidl::android::media::audio::common::AudioPortConfig>* _aidl_return) + override; + ndk::ScopedAStatus getAudioPorts( + std::vector<::aidl::android::media::audio::common::AudioPort>* _aidl_return) override; + ndk::ScopedAStatus getAudioRoutes(std::vector* _aidl_return) override; + ndk::ScopedAStatus openInputStream( + int32_t in_portConfigId, + const ::aidl::android::hardware::audio::common::SinkMetadata& in_sinkMetadata, + std::shared_ptr* _aidl_return) override; + ndk::ScopedAStatus openOutputStream( + int32_t in_portConfigId, + const ::aidl::android::hardware::audio::common::SourceMetadata& in_sourceMetadata, + const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>& + in_offloadInfo, + std::shared_ptr* _aidl_return) override; + ndk::ScopedAStatus setAudioPatch(const AudioPatch& in_requested, + AudioPatch* _aidl_return) override; + ndk::ScopedAStatus setAudioPortConfig( + const ::aidl::android::media::audio::common::AudioPortConfig& in_requested, + ::aidl::android::media::audio::common::AudioPortConfig* out_suggested, + bool* _aidl_return) override; + ndk::ScopedAStatus resetAudioPatch(int32_t in_patchId) override; + ndk::ScopedAStatus resetAudioPortConfig(int32_t in_portConfigId) override; + + private: + void cleanUpPatch(int32_t patchId); + void cleanUpPatches(int32_t portConfigId); + internal::Configuration& getConfig(); + void registerPatch(const AudioPatch& patch); + + std::unique_ptr mConfig; + Streams mStreams; + // Maps port ids and port config ids to patch ids. + // Multimap because both ports and configs can be used by multiple patches. + std::multimap mPatches; +}; + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h new file mode 100644 index 0000000000..87104dd1c1 --- /dev/null +++ b/audio/aidl/default/include/core-impl/Stream.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "core-impl/utils.h" + +namespace aidl::android::hardware::audio::core { + +class StreamIn : public BnStreamIn { + ndk::ScopedAStatus close() override; + ndk::ScopedAStatus updateMetadata( + const ::aidl::android::hardware::audio::common::SinkMetadata& in_sinkMetadata) override; + + public: + explicit StreamIn(const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata); + bool isClosed() const { return mIsClosed; } + + private: + ::aidl::android::hardware::audio::common::SinkMetadata mMetadata; + bool mIsClosed = false; +}; + +class StreamOut : public BnStreamOut { + ndk::ScopedAStatus close() override; + ndk::ScopedAStatus updateMetadata( + const ::aidl::android::hardware::audio::common::SourceMetadata& in_sourceMetadata) + override; + + public: + StreamOut(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata, + const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>& + offloadInfo); + bool isClosed() const { return mIsClosed; } + + private: + ::aidl::android::hardware::audio::common::SourceMetadata mMetadata; + std::optional<::aidl::android::media::audio::common::AudioOffloadInfo> mOffloadInfo; + bool mIsClosed = false; +}; + +class StreamWrapper { + public: + explicit StreamWrapper(std::shared_ptr streamIn) : mStream(streamIn) {} + explicit StreamWrapper(std::shared_ptr streamOut) : mStream(streamOut) {} + bool isStreamOpen() const { + return std::visit( + [](auto&& ws) -> bool { + auto s = ws.lock(); + return s && !s->isClosed(); + }, + mStream); + } + + private: + std::variant, std::weak_ptr> mStream; +}; + +class Streams { + public: + Streams() = default; + Streams(const Streams&) = delete; + Streams& operator=(const Streams&) = delete; + size_t count(int32_t id) { + // Streams do not remove themselves from the collection on close. + erase_if(mStreams, [](const auto& pair) { return !pair.second.isStreamOpen(); }); + return mStreams.count(id); + } + void insert(int32_t portId, int32_t portConfigId, StreamWrapper sw) { + mStreams.insert(std::pair{portConfigId, sw}); + mStreams.insert(std::pair{portId, sw}); + } + + private: + // Maps port ids and port config ids to streams. Multimap because a port + // (not port config) can have multiple streams opened on it. + std::multimap mStreams; +}; + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/include/core-impl/utils.h b/audio/aidl/default/include/core-impl/utils.h new file mode 100644 index 0000000000..7101012639 --- /dev/null +++ b/audio/aidl/default/include/core-impl/utils.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace aidl::android::hardware::audio::core { + +// Return whether all the elements in the vector are unique. +template +bool all_unique(const std::vector& v) { + return std::set(v.begin(), v.end()).size() == v.size(); +} + +// Erase all the specified elements from a map. +template +auto erase_all(C& c, const V& keys) { + auto oldSize = c.size(); + for (auto& k : keys) { + c.erase(k); + } + return oldSize - c.size(); +} + +// Erase all the elements in the map that satisfy the provided predicate. +template +auto erase_if(C& c, P pred) { + auto oldSize = c.size(); + for (auto it = c.begin(), last = c.end(); it != last;) { + if (pred(*it)) { + it = c.erase(it); + } else { + ++it; + } + } + return oldSize - c.size(); +} + +// Erase all the elements in the map that have specified values. +template +auto erase_all_values(C& c, const V& values) { + return erase_if(c, [values](const auto& pair) { return values.count(pair.second) != 0; }); +} + +// Return non-zero count of elements for any of the provided keys. +template +size_t count_any(const M& m, const V& keys) { + for (auto& k : keys) { + if (size_t c = m.count(k); c != 0) return c; + } + return 0; +} + +// Assuming that M is a map whose values have an 'id' field, +// find an element with the specified id. +template +auto findById(M& m, int32_t id) { + return std::find_if(m.begin(), m.end(), [&](const auto& p) { return p.second.id == id; }); +} + +// Assuming that the vector contains elements with an 'id' field, +// find an element with the specified id. +template +auto findById(std::vector& v, int32_t id) { + return std::find_if(v.begin(), v.end(), [&](const auto& e) { return e.id == id; }); +} + +// Return elements from the vector that have specified ids, also +// optionally return which ids were not found. +template +std::vector selectByIds(std::vector& v, const std::vector& ids, + std::vector* missingIds = nullptr) { + std::vector result; + std::set idsSet(ids.begin(), ids.end()); + for (size_t i = 0; i < v.size(); ++i) { + T& e = v[i]; + if (idsSet.count(e.id) != 0) { + result.push_back(&v[i]); + idsSet.erase(e.id); + } + } + if (missingIds) { + *missingIds = std::vector(idsSet.begin(), idsSet.end()); + } + return result; +} + +} // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/main.cpp b/audio/aidl/default/main.cpp new file mode 100644 index 0000000000..0de6047f6e --- /dev/null +++ b/audio/aidl/default/main.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "core-impl/Config.h" +#include "core-impl/Module.h" + +#include +#include +#include + +using aidl::android::hardware::audio::core::Config; +using aidl::android::hardware::audio::core::Module; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(16); + + // make the default config service + auto config = ndk::SharedRefBase::make(); + const std::string configName = std::string() + Config::descriptor + "/default"; + binder_status_t status = + AServiceManager_addService(config->asBinder().get(), configName.c_str()); + CHECK(status == STATUS_OK); + + // make the default module + auto moduleDefault = ndk::SharedRefBase::make(); + const std::string moduleDefaultName = std::string() + Module::descriptor + "/default"; + status = AServiceManager_addService(moduleDefault->asBinder().get(), moduleDefaultName.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index 8b3830a373..ff84558a55 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -25,6 +25,18 @@ default + + android.hardware.audio.core + 1 + + IModule + default + + + IConfig + default + + android.hardware.authsecret 1