AIDL effect: Initial IEffect interface implementation and vts test

Bug: 238913361
Test: atest VtsHalAudioEffectTargetTest
Merged-In: Id64d28af9122e82acd96e3349cf37c3d9728069a
Change-Id: Id64d28af9122e82acd96e3349cf37c3d9728069a
This commit is contained in:
Shunkai Yao 2022-08-24 18:14:02 +00:00
parent 84efa03de3
commit 4590517a96
11 changed files with 426 additions and 49 deletions

View file

@ -166,3 +166,19 @@ aidl_interface {
},
},
}
latest_android_hardware_audio_effect = "android.hardware.audio.effect-V1"
cc_defaults {
name: "latest_android_hardware_audio_effect_ndk_shared",
shared_libs: [
latest_android_hardware_audio_effect + "-ndk",
],
}
cc_defaults {
name: "latest_android_hardware_audio_effect_ndk_static",
static_libs: [
latest_android_hardware_audio_effect + "-ndk",
],
}

View file

@ -68,6 +68,7 @@ cc_defaults {
"libbase",
"libbinder_ndk",
"android.hardware.audio.effect-V1-ndk",
"libequalizer",
],
cflags: [
"-Wall",
@ -80,7 +81,10 @@ cc_defaults {
cc_library_static {
name: "libaudioeffectserviceexampleimpl",
defaults: ["aidlaudioeffectservice_defaults"],
export_include_dirs: ["include"],
export_include_dirs: [
"include",
"include/equalizer-impl/",
],
srcs: [
"EffectFactory.cpp",
],

View file

@ -36,6 +36,8 @@ Factory::Factory() {
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
id.uuid = EqualizerUUID;
mIdentityList.push_back(id);
// TODO: Add visualizer with default implementation later
#if 0
id.type = {static_cast<int32_t>(0xd3467faa),
0xacc7,
0x4d34,
@ -43,6 +45,7 @@ Factory::Factory() {
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
id.uuid = VisualizerUUID;
mIdentityList.push_back(id);
#endif
}
ndk::ScopedAStatus Factory::queryEffects(const std::optional<AudioUuid>& in_type,
@ -56,4 +59,28 @@ ndk::ScopedAStatus Factory::queryEffects(const std::optional<AudioUuid>& in_type
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Factory::createEffect(
const AudioUuid& in_impl_uuid,
std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>* _aidl_return) {
LOG(DEBUG) << __func__ << ": UUID " << in_impl_uuid.toString();
if (in_impl_uuid == EqualizerUUID) {
*_aidl_return = ndk::SharedRefBase::make<Equalizer>();
} else {
LOG(ERROR) << __func__ << ": UUID "
<< " not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Factory::destroyEffect(
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_handle) {
if (in_handle) {
// TODO: b/245393900 need check the instance state with IEffect.getState before destroy.
return ndk::ScopedAStatus::ok();
} else {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}
} // namespace aidl::android::hardware::audio::effect

View file

@ -23,10 +23,11 @@
int main() {
// This is a debug implementation, always enable debug logging.
android::base::SetMinimumLogSeverity(::android::base::DEBUG);
ABinderProcess_setThreadPoolMaxThreadCount(16);
ABinderProcess_setThreadPoolMaxThreadCount(1);
auto effectFactory =
ndk::SharedRefBase::make<aidl::android::hardware::audio::effect::Factory>();
std::string serviceName = std::string() + effectFactory->descriptor + "/default";
binder_status_t status =
AServiceManager_addService(effectFactory->asBinder().get(), serviceName.c_str());

View file

@ -2,7 +2,7 @@ service vendor.audio-effect-hal-aidl /vendor/bin/hw/android.hardware.audio.effec
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
group audio media
capabilities BLOCK_SUSPEND
ioprio rt 4
task_profiles ProcessCapacityHigh HighPerformance

View file

@ -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.
*/
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_shared {
name: "libequalizer",
vendor: true,
shared_libs: [
"libbase",
"libbinder_ndk",
"libstagefright_foundation",
],
defaults: [
"latest_android_media_audio_common_types_ndk_shared",
"latest_android_hardware_audio_effect_ndk_shared",
],
include_dirs: ["hardware/interfaces/audio/aidl/default/include/equalizer-impl"],
srcs: [
"Equalizer.cpp",
],
visibility: [
"//hardware/interfaces/audio/aidl/default",
],
}

View file

@ -0,0 +1,49 @@
/*
* 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_Equalizer"
#include <android-base/logging.h>
#include "Equalizer.h"
namespace aidl::android::hardware::audio::effect {
Equalizer::Equalizer() {
// Implementation UUID
mDesc.common.id.uuid = {static_cast<int32_t>(0xce772f20),
0x847d,
0x11df,
0xbb17,
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
}
ndk::ScopedAStatus Equalizer::open() {
LOG(DEBUG) << __func__;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Equalizer::close() {
LOG(DEBUG) << __func__;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Equalizer::getDescriptor(Descriptor* _aidl_return) {
LOG(DEBUG) << __func__ << "descriptor " << mDesc.toString();
*_aidl_return = mDesc;
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::audio::effect

View file

@ -40,6 +40,28 @@ class Factory : public BnFactory {
const std::optional<::aidl::android::media::audio::common::AudioUuid>& in_instance,
std::vector<Descriptor::Identity>* out_descriptor) override;
/**
* @brief Create an effect instance for a certain implementation (identified by UUID).
*
* @param in_impl_uuid Effect implementation UUID.
* @param _aidl_return A pointer to created effect instance.
* @return ndk::ScopedAStatus
*/
ndk::ScopedAStatus createEffect(
const ::aidl::android::media::audio::common::AudioUuid& in_impl_uuid,
std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>* _aidl_return)
override;
/**
* @brief Destroy an effect instance.
*
* @param in_handle Effect instance handle.
* @return ndk::ScopedAStatus
*/
ndk::ScopedAStatus destroyEffect(
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_handle)
override;
private:
// List of effect descriptors supported by the devices.
std::vector<Descriptor::Identity> mIdentityList;

View file

@ -16,16 +16,28 @@
#pragma once
#include <aidl/android/hardware/audio/effect/BnEffect.h>
#include <cstdlib>
namespace aidl::android::hardware::audio::effect {
// Equalizer implementation UUID.
static const ::aidl::android::media::audio::common::AudioUuid EqualizerUUID = {
static_cast<int32_t>(0xce772f20),
0x847d,
0x11df,
0xbb17,
static_cast<int32_t>(0x0bed4300),
0xddd6,
0x11db,
0x8f34,
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
class Equalizer : public BnEffect {
public:
Equalizer();
ndk::ScopedAStatus open() override;
ndk::ScopedAStatus close() override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
private:
// Effect descriptor.
Descriptor mDesc = {.common.id.type = EqualizerUUID};
};
} // namespace aidl::android::hardware::audio::effect

View file

@ -14,7 +14,11 @@
* limitations under the License.
*/
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#define LOG_TAG "VtsHalAudioEffect"
@ -35,37 +39,88 @@ using namespace android;
using ndk::ScopedAStatus;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::media::audio::common::AudioUuid;
namespace ndk {
std::ostream& operator<<(std::ostream& str, const ScopedAStatus& status) {
str << status.getDescription();
return str;
}
} // namespace ndk
class EffectFactory : public testing::TestWithParam<std::string> {
class EffectFactoryHelper {
public:
void SetUp() override { ASSERT_NO_FATAL_FAILURE(ConnectToService()); }
EffectFactoryHelper(const std::string& name) : mServiceName(name) {}
void TearDown() override {}
void ConnectToService() {
serviceName = GetParam();
factory = IFactory::fromBinder(binderUtil.connectToService(serviceName));
ASSERT_NE(factory, nullptr);
void ConnectToFactoryService() {
mEffectFactory = IFactory::fromBinder(binderUtil.connectToService(mServiceName));
ASSERT_NE(mEffectFactory, nullptr);
}
void RestartService() {
ASSERT_NE(factory, nullptr);
factory = IFactory::fromBinder(binderUtil.restartService());
ASSERT_NE(factory, nullptr);
void RestartFactoryService() {
ASSERT_NE(mEffectFactory, nullptr);
mEffectFactory = IFactory::fromBinder(binderUtil.restartService());
ASSERT_NE(mEffectFactory, nullptr);
}
std::shared_ptr<IFactory> factory;
std::string serviceName;
void QueryAllEffects() {
EXPECT_NE(mEffectFactory, nullptr);
ScopedAStatus status =
mEffectFactory->queryEffects(std::nullopt, std::nullopt, &mCompleteIds);
EXPECT_EQ(status.getExceptionCode(), EX_NONE);
}
void QueryEffects(const std::optional<AudioUuid>& in_type,
const std::optional<AudioUuid>& in_instance,
std::vector<Descriptor::Identity>* _aidl_return) {
EXPECT_NE(mEffectFactory, nullptr);
ScopedAStatus status = mEffectFactory->queryEffects(in_type, in_instance, _aidl_return);
EXPECT_EQ(status.getExceptionCode(), EX_NONE);
mIds = *_aidl_return;
}
void CreateEffects() {
EXPECT_NE(mEffectFactory, nullptr);
ScopedAStatus status;
for (const auto& id : mIds) {
std::shared_ptr<IEffect> effect;
status = mEffectFactory->createEffect(id.uuid, &effect);
EXPECT_EQ(status.getExceptionCode(), EX_NONE) << id.toString();
EXPECT_NE(effect, nullptr) << id.toString();
mEffectIdMap[effect] = id;
}
}
void DestroyEffects() {
EXPECT_NE(mEffectFactory, nullptr);
ScopedAStatus status;
for (const auto& it : mEffectIdMap) {
status = mEffectFactory->destroyEffect(it.first);
EXPECT_EQ(status.getExceptionCode(), EX_NONE) << it.second.toString();
}
mEffectIdMap.clear();
}
std::shared_ptr<IFactory> GetFactory() { return mEffectFactory; }
const std::vector<Descriptor::Identity>& GetEffectIds() { return mIds; }
const std::vector<Descriptor::Identity>& GetCompleteEffectIdList() { return mCompleteIds; }
const std::unordered_map<std::shared_ptr<IEffect>, Descriptor::Identity>& GetEffectMap() {
return mEffectIdMap;
}
private:
std::shared_ptr<IFactory> mEffectFactory;
std::string mServiceName;
AudioHalBinderServiceUtil binderUtil;
std::vector<Descriptor::Identity> mIds;
std::vector<Descriptor::Identity> mCompleteIds;
std::unordered_map<std::shared_ptr<IEffect>, Descriptor::Identity> mEffectIdMap;
};
/// Effect factory testing.
class EffectFactoryTest : public testing::TestWithParam<std::string> {
public:
void SetUp() override { ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService()); }
void TearDown() override { mFactory.DestroyEffects(); }
EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam());
// TODO: these UUID can get from config file
// ec7178ec-e5e1-4432-a3f4-4657e6795210
const AudioUuid nullUuid = {static_cast<int32_t>(0xec7178ec),
@ -77,25 +132,23 @@ class EffectFactory : public testing::TestWithParam<std::string> {
static_cast<int32_t>(0x0), 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
};
TEST_P(EffectFactory, SetupAndTearDown) {
TEST_P(EffectFactoryTest, SetupAndTearDown) {
// Intentionally empty test body.
}
TEST_P(EffectFactory, CanBeRestarted) {
ASSERT_NO_FATAL_FAILURE(RestartService());
TEST_P(EffectFactoryTest, CanBeRestarted) {
ASSERT_NO_FATAL_FAILURE(mFactory.RestartFactoryService());
}
TEST_P(EffectFactory, QueriedDescriptorList) {
TEST_P(EffectFactoryTest, QueriedDescriptorList) {
std::vector<Descriptor::Identity> descriptors;
ScopedAStatus status = factory->queryEffects(std::nullopt, std::nullopt, &descriptors);
EXPECT_EQ(EX_NONE, status.getExceptionCode());
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
EXPECT_NE(static_cast<int>(descriptors.size()), 0);
}
TEST_P(EffectFactory, DescriptorUUIDNotNull) {
TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) {
std::vector<Descriptor::Identity> descriptors;
ScopedAStatus status = factory->queryEffects(std::nullopt, std::nullopt, &descriptors);
EXPECT_EQ(EX_NONE, status.getExceptionCode());
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
// TODO: Factory eventually need to return the full list of MUST supported AOSP effects.
for (auto& desc : descriptors) {
EXPECT_NE(desc.type, zeroUuid);
@ -103,24 +156,172 @@ TEST_P(EffectFactory, DescriptorUUIDNotNull) {
}
}
TEST_P(EffectFactory, QueriedDescriptorNotExistType) {
TEST_P(EffectFactoryTest, QueriedDescriptorNotExistType) {
std::vector<Descriptor::Identity> descriptors;
ScopedAStatus status = factory->queryEffects(nullUuid, std::nullopt, &descriptors);
EXPECT_EQ(EX_NONE, status.getExceptionCode());
mFactory.QueryEffects(nullUuid, std::nullopt, &descriptors);
EXPECT_EQ(static_cast<int>(descriptors.size()), 0);
}
TEST_P(EffectFactory, QueriedDescriptorNotExistInstance) {
TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) {
std::vector<Descriptor::Identity> descriptors;
ScopedAStatus status = factory->queryEffects(std::nullopt, nullUuid, &descriptors);
EXPECT_EQ(EX_NONE, status.getExceptionCode());
mFactory.QueryEffects(std::nullopt, nullUuid, &descriptors);
EXPECT_EQ(static_cast<int>(descriptors.size()), 0);
}
INSTANTIATE_TEST_SUITE_P(EffectFactoryTest, EffectFactory,
TEST_P(EffectFactoryTest, CreateAndDestroyRepeat) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
int numIds = static_cast<int>(mFactory.GetEffectIds().size());
EXPECT_NE(numIds, 0);
EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 0);
mFactory.CreateEffects();
EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), numIds);
mFactory.DestroyEffects();
EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 0);
// Create and destroy again
mFactory.CreateEffects();
EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), numIds);
mFactory.DestroyEffects();
EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 0);
}
TEST_P(EffectFactoryTest, CreateMultipleInstanceOfSameEffect) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
int numIds = static_cast<int>(mFactory.GetEffectIds().size());
EXPECT_NE(numIds, 0);
EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 0);
mFactory.CreateEffects();
EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), numIds);
// Create effect instances of same implementation
mFactory.CreateEffects();
EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 2 * numIds);
mFactory.CreateEffects();
EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 3 * numIds);
mFactory.DestroyEffects();
EXPECT_EQ(static_cast<int>(mFactory.GetEffectMap().size()), 0);
}
INSTANTIATE_TEST_SUITE_P(EffectFactoryTest, EffectFactoryTest,
testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
android::PrintInstanceNameToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EffectFactory);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EffectFactoryTest);
/// Effect testing.
class AudioEffect : public testing::TestWithParam<std::string> {
public:
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService());
ASSERT_NO_FATAL_FAILURE(mFactory.CreateEffects());
}
void TearDown() override {
CloseEffects();
ASSERT_NO_FATAL_FAILURE(mFactory.DestroyEffects());
}
void OpenEffects() {
auto open = [](const std::shared_ptr<IEffect>& effect) {
ScopedAStatus status = effect->open();
EXPECT_EQ(status.getExceptionCode(), EX_NONE);
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(open));
}
void CloseEffects() {
auto close = [](const std::shared_ptr<IEffect>& effect) {
ScopedAStatus status = effect->close();
EXPECT_EQ(status.getExceptionCode(), EX_NONE);
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
}
void GetEffectDescriptors() {
auto get = [](const std::shared_ptr<IEffect>& effect) {
Descriptor desc;
ScopedAStatus status = effect->getDescriptor(&desc);
EXPECT_EQ(status.getExceptionCode(), EX_NONE);
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
}
template <typename Functor>
void ForEachEffect(Functor functor) {
auto effectMap = mFactory.GetEffectMap();
ScopedAStatus status;
for (const auto& it : effectMap) {
SCOPED_TRACE(it.second.toString());
functor(it.first);
}
}
EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam());
};
TEST_P(AudioEffect, OpenEffectTest) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
}
TEST_P(AudioEffect, OpenAndCloseEffect) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
TEST_P(AudioEffect, CloseUnopenedEffectTest) {
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
TEST_P(AudioEffect, DoubleOpenCloseEffects) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
TEST_P(AudioEffect, GetDescriptors) {
EXPECT_NO_FATAL_FAILURE(GetEffectDescriptors());
}
TEST_P(AudioEffect, DescriptorIdExistAndUnique) {
auto checker = [&](const std::shared_ptr<IEffect>& effect) {
Descriptor desc;
std::vector<Descriptor::Identity> idList;
ScopedAStatus status = effect->getDescriptor(&desc);
EXPECT_EQ(status.getExceptionCode(), EX_NONE);
mFactory.QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList);
EXPECT_EQ(static_cast<int>(idList.size()), 1);
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(checker));
// Check unique with a set
auto stringHash = [](const Descriptor::Identity& id) {
return std::hash<std::string>()(id.toString());
};
auto vec = mFactory.GetCompleteEffectIdList();
std::unordered_set<Descriptor::Identity, decltype(stringHash)> idSet(0, stringHash);
for (auto it : vec) {
EXPECT_EQ(static_cast<int>(idSet.count(it)), 0);
idSet.insert(it);
}
}
INSTANTIATE_TEST_SUITE_P(AudioEffectTest, AudioEffect,
testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
android::PrintInstanceNameToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffect);
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);

View file

@ -16,7 +16,7 @@
<instance>default</instance>
</interface>
</hal>
<hal format="hidl" optional="false">
<hal format="hidl" optional="true">
<name>android.hardware.audio.effect</name>
<version>6.0</version>
<version>7.0</version>
@ -37,7 +37,7 @@
<instance>default</instance>
</interface>
</hal>
<hal format="aidl" optional="false">
<hal format="aidl" optional="true">
<name>android.hardware.audio.effect</name>
<version>1</version>
<interface>