audiopolicy: Load the engine library dynamically

Android provides 2 audio policy engines:
libaudiopolicyenginedefault and
libaudiopolicyengineconfigurable. This change makes the engine
to be loaded dynamically based on the configuration (currently
the engine name is hardcoded into AudioPolicyConfig). Dynamic
loading allows building and installing of both libraries without
any conflicts.

Technical changes:

 - AudioPolicyManagerInterface renamed to EngineInterface
   for clarity;

 - For the purpose of dynamic loading, APM does not depend
   anymore on the EngineInstance class. The class got removed
   from the default AP engine, but left in the configurable engine
   because it is also used by its plugins;

 - Added EngineLibrary class to encapsulate dynamic loading
   of the AP engine. The class name EngineInstance is repurposed
   for a smart pointer to EngineInterface;

 - services/audiopolicy/managerdefault/Android.mk converted
   into Android.bp;

 - Added engine loading failure test;

Bug: 132639720
Test: sanity tests for audio; audiopolicy_tests
Change-Id: I0581569a172f810e030aec879225e817bfa7851a
Merged-In: I0581569a172f810e030aec879225e817bfa7851a
gugelfrei
Mikhail Naganov 5 years ago committed by jiabin
parent 3e0b02e336
commit e13c679e4a

@ -40,7 +40,8 @@ public:
DeviceVector &availableOutputDevices,
DeviceVector &availableInputDevices,
sp<DeviceDescriptor> &defaultOutputDevice)
: mHwModules(hwModules),
: mEngineLibraryNameSuffix(kDefaultEngineLibraryNameSuffix),
mHwModules(hwModules),
mAvailableOutputDevices(availableOutputDevices),
mAvailableInputDevices(availableInputDevices),
mDefaultOutputDevice(defaultOutputDevice),
@ -55,6 +56,14 @@ public:
mSource = file;
}
const std::string& getEngineLibraryNameSuffix() const {
return mEngineLibraryNameSuffix;
}
void setEngineLibraryNameSuffix(const std::string& suffix) {
mEngineLibraryNameSuffix = suffix;
}
void setHwModules(const HwModuleCollection &hwModules)
{
mHwModules = hwModules;
@ -108,6 +117,7 @@ public:
void setDefault(void)
{
mSource = "AudioPolicyConfig::setDefault";
mEngineLibraryNameSuffix = kDefaultEngineLibraryNameSuffix;
mDefaultOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER);
mDefaultOutputDevice->addAudioProfile(AudioProfile::createFullDynamic());
sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC);
@ -167,7 +177,10 @@ public:
}
private:
static const constexpr char* const kDefaultEngineLibraryNameSuffix = "default";
std::string mSource;
std::string mEngineLibraryNameSuffix;
HwModuleCollection &mHwModules; /**< Collection of Module, with Profiles, i.e. Mix Ports. */
DeviceVector &mAvailableOutputDevices;
DeviceVector &mAvailableInputDevices;

@ -17,18 +17,18 @@
#pragma once
#include <EngineConfig.h>
#include <AudioPolicyManagerInterface.h>
#include <EngineInterface.h>
#include <ProductStrategy.h>
#include <VolumeGroup.h>
namespace android {
namespace audio_policy {
class EngineBase : public AudioPolicyManagerInterface
class EngineBase : public EngineInterface
{
public:
///
/// from AudioPolicyManagerInterface
/// from EngineInterface
///
android::status_t initCheck() override;

@ -19,7 +19,6 @@
#include "VolumeGroup.h"
#include <system/audio.h>
#include <AudioPolicyManagerInterface.h>
#include <utils/RefBase.h>
#include <HandleGenerator.h>
#include <string>
@ -27,6 +26,7 @@
#include <map>
#include <utils/Errors.h>
#include <utils/String8.h>
#include <media/AudioAttributes.h>
namespace android {

@ -18,7 +18,6 @@
#include "IVolumeCurves.h"
#include <policy.h>
#include <AudioPolicyManagerInterface.h>
#include <utils/RefBase.h>
#include <HandleGenerator.h>
#include <utils/String8.h>

@ -16,7 +16,6 @@
#pragma once
#include <AudioPolicyManagerInterface.h>
#include <VolumeCurve.h>
#include <system/audio.h>
#include <utils/RefBase.h>

@ -19,6 +19,7 @@
#include "ProductStrategy.h"
#include <media/AudioProductStrategy.h>
#include <media/TypeConverter.h>
#include <utils/String8.h>
#include <cstdint>

@ -32,9 +32,9 @@
#include <istream>
#include <cstdint>
#include <stdarg.h>
#include <string>
namespace android {
using utilities::convertTo;
@ -603,7 +603,39 @@ static status_t deserializeLegacyVolumeCollection(_xmlDoc *doc, const _xmlNode *
return NO_ERROR;
}
namespace {
class XmlErrorHandler {
public:
XmlErrorHandler() {
xmlSetGenericErrorFunc(this, &xmlErrorHandler);
}
XmlErrorHandler(const XmlErrorHandler&) = delete;
XmlErrorHandler(XmlErrorHandler&&) = delete;
XmlErrorHandler& operator=(const XmlErrorHandler&) = delete;
XmlErrorHandler& operator=(XmlErrorHandler&&) = delete;
~XmlErrorHandler() {
xmlSetGenericErrorFunc(NULL, NULL);
if (!mErrorMessage.empty()) {
ALOG(LOG_ERROR, "libxml2", "%s", mErrorMessage.c_str());
}
}
static void xmlErrorHandler(void* ctx, const char* msg, ...) {
char buffer[256];
va_list args;
va_start(args, msg);
vsnprintf(buffer, sizeof(buffer), msg, args);
va_end(args);
static_cast<XmlErrorHandler*>(ctx)->mErrorMessage += buffer;
}
private:
std::string mErrorMessage;
};
} // namespace
ParsingResult parse(const char* path) {
XmlErrorHandler errorHandler;
xmlDocPtr doc;
doc = xmlParseFile(path);
if (doc == NULL) {
@ -641,6 +673,7 @@ ParsingResult parse(const char* path) {
}
android::status_t parseLegacyVolumeFile(const char* path, VolumeGroups &volumeGroups) {
XmlErrorHandler errorHandler;
xmlDocPtr doc;
doc = xmlParseFile(path);
if (doc == NULL) {

@ -38,7 +38,7 @@ using VolumeGroupVector = std::vector<volume_group_t>;
/**
* This interface is dedicated to the policy manager that a Policy Engine shall implement.
*/
class AudioPolicyManagerInterface
class EngineInterface
{
public:
/**
@ -295,7 +295,13 @@ public:
virtual void dump(String8 *dst) const = 0;
protected:
virtual ~AudioPolicyManagerInterface() {}
virtual ~EngineInterface() {}
};
__attribute__((visibility("default")))
extern "C" EngineInterface* createEngineInstance();
__attribute__((visibility("default")))
extern "C" void destroyEngineInstance(EngineInterface *engine);
} // namespace android

@ -16,7 +16,7 @@
#pragma once
class AudioPolicyManagerInterface;
class EngineInterface;
class AudioPolicyPluginInterface;
namespace android {
@ -69,7 +69,7 @@ private:
* Compile time error will claim if invalid interface is requested.
*/
template <>
AudioPolicyManagerInterface *EngineInstance::queryInterface() const;
EngineInterface *EngineInstance::queryInterface() const;
template <>
AudioPolicyPluginInterface *EngineInstance::queryInterface() const;

@ -361,7 +361,7 @@ bool Engine::setDeviceTypesForProductStrategy(product_strategy_t strategy, audio
}
template <>
AudioPolicyManagerInterface *Engine::queryInterface()
EngineInterface *Engine::queryInterface()
{
return this;
}

@ -17,7 +17,7 @@
#pragma once
#include "EngineBase.h"
#include <AudioPolicyManagerInterface.h>
#include <EngineInterface.h>
#include <AudioPolicyPluginInterface.h>
#include "Collection.h"

@ -14,7 +14,7 @@
* limitations under the License.
*/
#include <AudioPolicyManagerInterface.h>
#include <EngineInterface.h>
#include <AudioPolicyPluginInterface.h>
#include "AudioPolicyEngineInstance.h"
#include "Engine.h"
@ -45,9 +45,9 @@ Engine *EngineInstance::getEngine() const
}
template <>
AudioPolicyManagerInterface *EngineInstance::queryInterface() const
EngineInterface *EngineInstance::queryInterface() const
{
return getEngine()->queryInterface<AudioPolicyManagerInterface>();
return getEngine()->queryInterface<EngineInterface>();
}
template <>
@ -57,5 +57,16 @@ AudioPolicyPluginInterface *EngineInstance::queryInterface() const
}
} // namespace audio_policy
extern "C" EngineInterface* createEngineInstance()
{
return audio_policy::EngineInstance::getInstance()->queryInterface<EngineInterface>();
}
extern "C" void destroyEngineInstance(EngineInterface*)
{
// The engine is a singleton.
}
} // namespace android

@ -1,16 +1,15 @@
cc_library_shared {
name: "libaudiopolicyenginedefault",
export_include_dirs: ["include"],
srcs: [
"src/Engine.cpp",
"src/EngineInstance.cpp",
],
cflags: [
"-fvisibility=hidden",
"-Wall",
"-Werror",
"-Wextra",
],
local_include_dirs: ["include"],
header_libs: [
"libbase_headers",
"libaudiopolicycommon",

@ -1,76 +0,0 @@
/*
* Copyright (C) 2015 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
class AudioPolicyManagerInterface;
namespace android
{
namespace audio_policy
{
class Engine;
class EngineInstance
{
protected:
EngineInstance();
public:
virtual ~EngineInstance();
/**
* Get Audio Policy Engine instance.
*
* @return pointer to Route Manager Instance object.
*/
static EngineInstance *getInstance();
/**
* Interface query.
* The first client of an interface of the policy engine will start the singleton.
*
* @tparam RequestedInterface: interface that the client is wishing to retrieve.
*
* @return interface handle.
*/
template <class RequestedInterface>
RequestedInterface *queryInterface() const;
protected:
/**
* Get Audio Policy Engine instance.
*
* @return Audio Policy Engine singleton.
*/
Engine *getEngine() const;
private:
/* Copy facilities are put private to disable copy. */
EngineInstance(const EngineInstance &object);
EngineInstance &operator=(const EngineInstance &object);
};
/**
* Limit template instantation to supported type interfaces.
* Compile time error will claim if invalid interface is requested.
*/
template <>
AudioPolicyManagerInterface *EngineInstance::queryInterface() const;
} // namespace audio_policy
} // namespace android

@ -762,12 +762,6 @@ sp<DeviceDescriptor> Engine::getInputDeviceForAttributes(const audio_attributes_
AUDIO_FORMAT_DEFAULT);
}
template <>
AudioPolicyManagerInterface *Engine::queryInterface()
{
return this;
}
} // namespace audio_policy
} // namespace android

@ -17,7 +17,7 @@
#pragma once
#include "EngineBase.h"
#include "AudioPolicyManagerInterface.h"
#include "EngineInterface.h"
#include <AudioGain.h>
#include <policy.h>
@ -48,12 +48,9 @@ public:
Engine();
virtual ~Engine() = default;
template <class RequestedInterface>
RequestedInterface *queryInterface();
private:
///
/// from EngineBase, so from AudioPolicyManagerInterface
/// from EngineBase, so from EngineInterface
///
status_t setForceUse(audio_policy_force_use_t usage,
audio_policy_forced_cfg_t config) override;

@ -14,41 +14,21 @@
* limitations under the License.
*/
#include <AudioPolicyManagerInterface.h>
#include "AudioPolicyEngineInstance.h"
#include <EngineInterface.h>
#include "Engine.h"
namespace android
{
namespace audio_policy
{
EngineInstance::EngineInstance()
{
}
EngineInstance *EngineInstance::getInstance()
{
static EngineInstance instance;
return &instance;
}
EngineInstance::~EngineInstance()
{
}
namespace android {
namespace audio_policy {
Engine *EngineInstance::getEngine() const
extern "C" EngineInterface* createEngineInstance()
{
static Engine engine;
return &engine;
return new (std::nothrow) Engine();
}
template <>
AudioPolicyManagerInterface *EngineInstance::queryInterface() const
extern "C" void destroyEngineInstance(EngineInterface *engine)
{
return getEngine()->queryInterface<AudioPolicyManagerInterface>();
delete static_cast<Engine*>(engine);
}
} // namespace audio_policy
} // namespace android

@ -0,0 +1,43 @@
cc_library_shared {
name: "libaudiopolicymanagerdefault",
srcs: [
"AudioPolicyManager.cpp",
"EngineLibrary.cpp",
],
export_include_dirs: ["."],
shared_libs: [
"libcutils",
"libdl",
"libutils",
"liblog",
"libaudiopolicy",
"libsoundtrigger",
"libmedia_helper",
"libmediametrics",
"libbinder",
"libhidlbase",
"libxml2",
// The default audio policy engine is always present in the system image.
// libaudiopolicyengineconfigurable can be built in addition by specifying
// a dependency on it in the device makefile. There will be no build time
// conflict with libaudiopolicyenginedefault.
"libaudiopolicyenginedefault",
],
header_libs: [
"libaudiopolicycommon",
"libaudiopolicyengine_interface_headers",
"libaudiopolicymanager_interface_headers",
],
static_libs: ["libaudiopolicycomponents"],
cflags: [
"-Wall",
"-Werror",
],
}

@ -1,48 +0,0 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= AudioPolicyManager.cpp
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
liblog \
libaudiopolicy \
libsoundtrigger
ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1)
LOCAL_SHARED_LIBRARIES += libaudiopolicyengineconfigurable
else
LOCAL_SHARED_LIBRARIES += libaudiopolicyenginedefault
endif # ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1)
LOCAL_C_INCLUDES += \
$(call include-path-for, audio-utils)
LOCAL_HEADER_LIBRARIES := \
libaudiopolicycommon \
libaudiopolicyengine_interface_headers \
libaudiopolicymanager_interface_headers
LOCAL_STATIC_LIBRARIES := \
libaudiopolicycomponents
LOCAL_SHARED_LIBRARIES += libmedia_helper
LOCAL_SHARED_LIBRARIES += libmediametrics
LOCAL_SHARED_LIBRARIES += libbinder libhidlbase libxml2
LOCAL_CFLAGS += -Wall -Werror
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
LOCAL_MODULE:= libaudiopolicymanagerdefault
include $(BUILD_SHARED_LIBRARY)

@ -42,8 +42,6 @@
#include <set>
#include <unordered_set>
#include <vector>
#include <AudioPolicyManagerInterface.h>
#include <AudioPolicyEngineInstance.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <media/AudioParameter.h>
@ -4304,17 +4302,18 @@ void AudioPolicyManager::loadConfig() {
}
status_t AudioPolicyManager::initialize() {
// Once policy config has been parsed, retrieve an instance of the engine and initialize it.
audio_policy::EngineInstance *engineInstance = audio_policy::EngineInstance::getInstance();
if (!engineInstance) {
ALOGE("%s: Could not get an instance of policy engine", __FUNCTION__);
return NO_INIT;
}
// Retrieve the Policy Manager Interface
mEngine = engineInstance->queryInterface<AudioPolicyManagerInterface>();
if (mEngine == NULL) {
ALOGE("%s: Failed to get Policy Engine Interface", __FUNCTION__);
return NO_INIT;
{
auto engLib = EngineLibrary::load(
"libaudiopolicyengine" + getConfig().getEngineLibraryNameSuffix() + ".so");
if (!engLib) {
ALOGE("%s: Failed to load the engine library", __FUNCTION__);
return NO_INIT;
}
mEngine = engLib->createEngine();
if (mEngine == nullptr) {
ALOGE("%s: Failed to instantiate the APM engine", __FUNCTION__);
return NO_INIT;
}
}
mEngine->setObserver(this);
status_t status = mEngine->initCheck();

@ -34,7 +34,6 @@
#include <media/PatchBuilder.h>
#include "AudioPolicyInterface.h"
#include <AudioPolicyManagerInterface.h>
#include <AudioPolicyManagerObserver.h>
#include <AudioGain.h>
#include <AudioPolicyConfig.h>
@ -49,6 +48,7 @@
#include <AudioPolicyMix.h>
#include <EffectDescriptor.h>
#include <SoundTriggerSession.h>
#include "EngineLibrary.h"
#include "TypeConverter.h"
namespace android {
@ -752,7 +752,7 @@ protected:
uint32_t nextAudioPortGeneration();
// Audio Policy Engine Interface.
AudioPolicyManagerInterface *mEngine;
EngineInstance mEngine;
// Surround formats that are enabled manually. Taken into account when
// "encoded surround" is forced into "manual" mode.

@ -0,0 +1,78 @@
/*
* Copyright 2019 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 "APM_EngineLoader"
#include <dlfcn.h>
#include <utils/Log.h>
#include "EngineLibrary.h"
namespace android {
// static
std::shared_ptr<EngineLibrary> EngineLibrary::load(std::string libraryPath)
{
std::shared_ptr<EngineLibrary> engLib(new EngineLibrary());
return engLib->init(std::move(libraryPath)) ? engLib : nullptr;
}
EngineLibrary::~EngineLibrary()
{
close();
}
bool EngineLibrary::init(std::string libraryPath)
{
mLibraryHandle = dlopen(libraryPath.c_str(), 0);
if (mLibraryHandle == nullptr) {
ALOGE("Could not dlopen %s: %s", libraryPath.c_str(), dlerror());
return false;
}
mCreateEngineInstance = (EngineInterface* (*)())dlsym(mLibraryHandle, "createEngineInstance");
mDestroyEngineInstance = (void (*)(EngineInterface*))dlsym(
mLibraryHandle, "destroyEngineInstance");
if (mCreateEngineInstance == nullptr || mDestroyEngineInstance == nullptr) {
ALOGE("Could not find engine interface functions in %s", libraryPath.c_str());
close();
return false;
}
ALOGD("Loaded engine from %s", libraryPath.c_str());
return true;
}
EngineInstance EngineLibrary::createEngine()
{
if (mCreateEngineInstance == nullptr || mDestroyEngineInstance == nullptr) {
return EngineInstance();
}
return EngineInstance(mCreateEngineInstance(),
[lib = shared_from_this(), destroy = mDestroyEngineInstance] (EngineInterface* e) {
destroy(e);
});
}
void EngineLibrary::close()
{
if (mLibraryHandle != nullptr) {
dlclose(mLibraryHandle);
}
mLibraryHandle = nullptr;
mCreateEngineInstance = nullptr;
mDestroyEngineInstance = nullptr;
}
} // namespace android

@ -0,0 +1,51 @@
/*
* Copyright 2019 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 <functional>
#include <memory>
#include <string>
#include <EngineInterface.h>
namespace android {
using EngineInstance = std::unique_ptr<EngineInterface, std::function<void (EngineInterface*)>>;
class EngineLibrary : public std::enable_shared_from_this<EngineLibrary> {
public:
static std::shared_ptr<EngineLibrary> load(std::string libraryPath);
~EngineLibrary();
EngineLibrary(const EngineLibrary&) = delete;
EngineLibrary(EngineLibrary&&) = delete;
EngineLibrary& operator=(const EngineLibrary&) = delete;
EngineLibrary& operator=(EngineLibrary&&) = delete;
EngineInstance createEngine();
private:
EngineLibrary() = default;
bool init(std::string libraryPath);
void close();
void *mLibraryHandle = nullptr;
EngineInterface* (*mCreateEngineInstance)() = nullptr;
void (*mDestroyEngineInstance)(EngineInterface*) = nullptr;
};
} // namespace android

@ -30,7 +30,16 @@
using namespace android;
TEST(AudioPolicyManagerTestInit, Failure) {
TEST(AudioPolicyManagerTestInit, EngineFailure) {
AudioPolicyTestClient client;
AudioPolicyTestManager manager(&client);
manager.getConfig().setDefault();
manager.getConfig().setEngineLibraryNameSuffix("non-existent");
ASSERT_EQ(NO_INIT, manager.initialize());
ASSERT_EQ(NO_INIT, manager.initCheck());
}
TEST(AudioPolicyManagerTestInit, ClientFailure) {
AudioPolicyTestClient client;
AudioPolicyTestManager manager(&client);
manager.getConfig().setDefault();

Loading…
Cancel
Save