Add dynamic stream default effects

Allows runtime modification of what effects should be default
attached to streams of different types/usages. The core functionality
was already in the AudioPolicyEffects system, this allows
the dynamic modification of the lists.

Bug: 78527120
Test: Builds, manually tested an app adding a stream effect,
tested both media on the specified stream and on a different stream.
Also tested by Android Things integration tests.
Change-Id: Ie0426b17ff7daece58b8c85d72fbef620844325b
gugelfrei
Ari Hausman-Cohen 6 years ago
parent 5c00d01c85
commit 433722eeb7

@ -447,6 +447,55 @@ status_t AudioEffect::queryDefaultPreProcessing(audio_session_t audioSession,
if (aps == 0) return PERMISSION_DENIED;
return aps->queryDefaultPreProcessing(audioSession, descriptors, count);
}
status_t AudioEffect::newEffectUniqueId(audio_unique_id_t* id)
{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
*id = af->newAudioUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT);
return NO_ERROR;
}
status_t AudioEffect::addStreamDefaultEffect(const char *typeStr,
const String16& opPackageName,
const char *uuidStr,
int32_t priority,
audio_usage_t usage,
audio_unique_id_t *id)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
if (typeStr == NULL && uuidStr == NULL) return BAD_VALUE;
// Convert type & uuid from string to effect_uuid_t.
effect_uuid_t type;
if (typeStr != NULL) {
status_t res = stringToGuid(typeStr, &type);
if (res != OK) return res;
} else {
type = *EFFECT_UUID_NULL;
}
effect_uuid_t uuid;
if (uuidStr != NULL) {
status_t res = stringToGuid(uuidStr, &uuid);
if (res != OK) return res;
} else {
uuid = *EFFECT_UUID_NULL;
}
return aps->addStreamDefaultEffect(&type, opPackageName, &uuid, priority, usage, id);
}
status_t AudioEffect::removeStreamDefaultEffect(audio_unique_id_t id)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->removeStreamDefaultEffect(id);
}
// -------------------------------------------------------------------------
status_t AudioEffect::stringToGuid(const char *str, effect_uuid_t *guid)

@ -81,7 +81,9 @@ enum {
GET_MASTER_MONO,
GET_STREAM_VOLUME_DB,
GET_SURROUND_FORMATS,
SET_SURROUND_FORMAT_ENABLED
SET_SURROUND_FORMAT_ENABLED,
ADD_STREAM_DEFAULT_EFFECT,
REMOVE_STREAM_DEFAULT_EFFECT
};
#define MAX_ITEMS_PER_LIST 1024
@ -866,6 +868,42 @@ public:
}
return reply.readInt32();
}
virtual status_t addStreamDefaultEffect(const effect_uuid_t *type,
const String16& opPackageName,
const effect_uuid_t *uuid,
int32_t priority,
audio_usage_t usage,
audio_unique_id_t* id)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
data.write(type, sizeof(effect_uuid_t));
data.writeString16(opPackageName);
data.write(uuid, sizeof(effect_uuid_t));
data.writeInt32(priority);
data.writeInt32((int32_t) usage);
status_t status = remote()->transact(ADD_STREAM_DEFAULT_EFFECT, data, &reply);
if (status != NO_ERROR) {
return status;
}
status = static_cast <status_t> (reply.readInt32());
*id = reply.readInt32();
return status;
}
virtual status_t removeStreamDefaultEffect(audio_unique_id_t id)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
data.writeInt32(id);
status_t status = remote()->transact(REMOVE_STREAM_DEFAULT_EFFECT, data, &reply);
if (status != NO_ERROR) {
return status;
}
return static_cast <status_t> (reply.readInt32());
}
};
IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@ -1561,6 +1599,43 @@ status_t BnAudioPolicyService::onTransact(
return NO_ERROR;
}
case ADD_STREAM_DEFAULT_EFFECT: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
effect_uuid_t type;
status_t status = data.read(&type, sizeof(effect_uuid_t));
if (status != NO_ERROR) {
return status;
}
String16 opPackageName;
status = data.readString16(&opPackageName);
if (status != NO_ERROR) {
return status;
}
effect_uuid_t uuid;
status = data.read(&uuid, sizeof(effect_uuid_t));
if (status != NO_ERROR) {
return status;
}
int32_t priority = data.readInt32();
audio_usage_t usage = (audio_usage_t) data.readInt32();
audio_unique_id_t id = 0;
reply->writeInt32(static_cast <int32_t>(addStreamDefaultEffect(&type,
opPackageName,
&uuid,
priority,
usage,
&id)));
reply->writeInt32(id);
return NO_ERROR;
}
case REMOVE_STREAM_DEFAULT_EFFECT: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
audio_unique_id_t id = static_cast<audio_unique_id_t>(data.readInt32());
reply->writeInt32(static_cast <int32_t>(removeStreamDefaultEffect(id)));
return NO_ERROR;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}

@ -150,6 +150,79 @@ public:
effect_descriptor_t *descriptors,
uint32_t *count);
/*
* Gets a new system-wide unique effect id.
*
* Parameters:
* id: The address to return the generated id.
*
* Returned status (from utils/Errors.h) can be:
* NO_ERROR successful operation.
* PERMISSION_DENIED could not get AudioFlinger interface
* or caller lacks required permissions.
* Returned value
* *id: The new unique system-wide effect id.
*/
static status_t newEffectUniqueId(audio_unique_id_t* id);
/*
* Static methods for adding/removing system-wide effects.
*/
/*
* Adds an effect to the list of default output effects for a given stream type.
*
* If the effect is no longer available when a stream of the given type
* is created, the system will continue without adding it.
*
* Parameters:
* typeStr: Type uuid of effect to be a default: can be null if uuidStr is specified.
* This may correspond to the OpenSL ES interface implemented by this effect,
* or could be some vendor-defined type.
* opPackageName: The package name used for app op checks.
* uuidStr: Uuid of effect to be a default: can be null if type is specified.
* This uuid corresponds to a particular implementation of an effect type.
* Note if both uuidStr and typeStr are specified, typeStr is ignored.
* priority: Requested priority for effect control: the priority level corresponds to the
* value of priority parameter: negative values indicate lower priorities, positive
* values higher priorities, 0 being the normal priority.
* usage: The usage this effect should be a default for. Unrecognized values will be
* treated as AUDIO_USAGE_UNKNOWN.
* id: Address where the system-wide unique id of the default effect should be returned.
*
* Returned status (from utils/Errors.h) can be:
* NO_ERROR successful operation.
* PERMISSION_DENIED could not get AudioFlinger interface
* or caller lacks required permissions.
* NO_INIT effect library failed to initialize.
* BAD_VALUE invalid type uuid or implementation uuid.
* NAME_NOT_FOUND no effect with this uuid or type found.
*
* Returned value
* *id: The system-wide unique id of the added default effect.
*/
static status_t addStreamDefaultEffect(const char* typeStr,
const String16& opPackageName,
const char* uuidStr,
int32_t priority,
audio_usage_t usage,
audio_unique_id_t* id);
/*
* Removes an effect from the list of default output effects for a given stream type.
*
* Parameters:
* id: The system-wide unique id of the effect that should no longer be a default.
*
* Returned status (from utils/Errors.h) can be:
* NO_ERROR successful operation.
* PERMISSION_DENIED could not get AudioFlinger interface
* or caller lacks required permissions.
* NO_INIT effect library failed to initialize.
* BAD_VALUE invalid id.
*/
static status_t removeStreamDefaultEffect(audio_unique_id_t id);
/*
* Events used by callback function (effect_callback_t).
*/

@ -109,6 +109,13 @@ public:
virtual status_t queryDefaultPreProcessing(audio_session_t audioSession,
effect_descriptor_t *descriptors,
uint32_t *count) = 0;
virtual status_t addStreamDefaultEffect(const effect_uuid_t *type,
const String16& opPackageName,
const effect_uuid_t *uuid,
int32_t priority,
audio_usage_t usage,
audio_unique_id_t* id) = 0;
virtual status_t removeStreamDefaultEffect(audio_unique_id_t id) = 0;
// Check if offload is possible for given format, stream type, sample rate,
// bit rate, duration, video and streaming or offload property is enabled
virtual bool isOffloadSupported(const audio_offload_info_t& info) = 0;

@ -40,6 +40,12 @@ cc_library {
"-Werror",
],
product_variables: {
product_is_iot: {
cflags: ["-DTARGET_ANDROID_THINGS"],
},
},
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}

@ -158,6 +158,27 @@ bool modifyAudioRoutingAllowed() {
return ok;
}
bool modifyDefaultAudioEffectsAllowed() {
static const String16 sModifyDefaultAudioEffectsAllowed(
"android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS");
// IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
bool ok = PermissionCache::checkCallingPermission(sModifyDefaultAudioEffectsAllowed);
#ifdef TARGET_ANDROID_THINGS
if (!ok) {
// Use a secondary permission on Android Things to allow a more lenient level of protection.
static const String16 sModifyDefaultAudioEffectsAndroidThingsAllowed(
"com.google.android.things.permission.MODIFY_DEFAULT_AUDIO_EFFECTS");
ok = PermissionCache::checkCallingPermission(
sModifyDefaultAudioEffectsAndroidThingsAllowed);
}
if (!ok) ALOGE("com.google.android.things.permission.MODIFY_DEFAULT_AUDIO_EFFECTS");
#else
if (!ok) ALOGE("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS");
#endif
return ok;
}
bool dumpAllowed() {
static const String16 sDump("android.permission.DUMP");
// IMPORTANT: Use PermissionCache - not a runtime permission and may not change.

@ -68,6 +68,7 @@ bool captureAudioOutputAllowed(pid_t pid, uid_t uid);
bool captureHotwordAllowed(pid_t pid, uid_t uid);
bool settingsAllowed();
bool modifyAudioRoutingAllowed();
bool modifyDefaultAudioEffectsAllowed();
bool dumpAllowed();
bool modifyPhoneStateAllowed(pid_t pid, uid_t uid);
status_t checkIMemory(const sp<IMemory>& iMemory);

@ -23,6 +23,7 @@
#include <memory>
#include <cutils/misc.h>
#include <media/AudioEffect.h>
#include <media/AudioPolicyHelper.h>
#include <media/EffectsConfig.h>
#include <mediautils/ServiceUtilities.h>
#include <system/audio.h>
@ -317,6 +318,102 @@ status_t AudioPolicyEffects::releaseOutputSessionEffects(audio_io_handle_t outpu
return status;
}
status_t AudioPolicyEffects::addStreamDefaultEffect(const effect_uuid_t *type,
const String16& opPackageName,
const effect_uuid_t *uuid,
int32_t priority,
audio_usage_t usage,
audio_unique_id_t* id)
{
if (uuid == NULL || type == NULL) {
ALOGE("addStreamDefaultEffect(): Null uuid or type uuid pointer");
return BAD_VALUE;
}
audio_stream_type_t stream = audio_usage_to_stream_type(usage);
if (stream < AUDIO_STREAM_MIN || stream >= AUDIO_STREAM_PUBLIC_CNT) {
ALOGE("addStreamDefaultEffect(): Unsupported stream type %d", stream);
return BAD_VALUE;
}
// Check that |uuid| or |type| corresponds to an effect on the system.
effect_descriptor_t descriptor = {};
status_t res = AudioEffect::getEffectDescriptor(
uuid, type, EFFECT_FLAG_TYPE_INSERT, &descriptor);
if (res != OK) {
ALOGE("addStreamDefaultEffect(): Failed to find effect descriptor matching uuid/type.");
return res;
}
// Only insert effects can be added dynamically as stream defaults.
if ((descriptor.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_INSERT) {
ALOGE("addStreamDefaultEffect(): Desired effect cannot be attached "
"as a stream default effect.");
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
// Find the EffectDescVector for the given stream type, or create a new one if necessary.
ssize_t index = mOutputStreams.indexOfKey(stream);
EffectDescVector *desc = NULL;
if (index < 0) {
// No effects for this stream type yet.
desc = new EffectDescVector();
mOutputStreams.add(stream, desc);
} else {
desc = mOutputStreams.valueAt(index);
}
// Create a new effect and add it to the vector.
res = AudioEffect::newEffectUniqueId(id);
if (res != OK) {
ALOGE("addStreamDefaultEffect(): failed to get new unique id.");
return res;
}
EffectDesc *effect = new EffectDesc(
descriptor.name, *type, opPackageName, *uuid, priority, *id);
desc->mEffects.add(effect);
// TODO(b/71813697): Support setting params as well.
// TODO(b/71814300): Retroactively attach to any existing streams of the given type.
// This requires tracking the stream type of each session id in addition to what is
// already being tracked.
return NO_ERROR;
}
status_t AudioPolicyEffects::removeStreamDefaultEffect(audio_unique_id_t id)
{
if (id == AUDIO_UNIQUE_ID_ALLOCATE) {
// ALLOCATE is not a unique identifier, but rather a reserved value indicating
// a real id has not been assigned. For default effects, this value is only used
// by system-owned defaults from the loaded config, which cannot be removed.
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
// Check each stream type.
size_t numStreams = mOutputStreams.size();
for (size_t i = 0; i < numStreams; ++i) {
// Check each effect for each stream.
EffectDescVector* descVector = mOutputStreams[i];
for (auto desc = descVector->mEffects.begin(); desc != descVector->mEffects.end(); ++desc) {
if ((*desc)->mId == id) {
// Found it!
// TODO(b/71814300): Remove from any streams the effect was attached to.
descVector->mEffects.erase(desc);
// Handles are unique; there can only be one match, so return early.
return NO_ERROR;
}
}
}
// Effect wasn't found, so it's been trivially removed successfully.
return NO_ERROR;
}
void AudioPolicyEffects::EffectVector::setProcessorEnabled(bool enabled)
{

@ -64,7 +64,6 @@ public:
status_t releaseInputEffects(audio_io_handle_t input,
audio_session_t audioSession);
// Return a list of effect descriptors for default output effects
// associated with audioSession
status_t queryDefaultOutputSessionEffects(audio_session_t audioSession,
@ -82,18 +81,49 @@ public:
audio_stream_type_t stream,
audio_session_t audioSession);
// Add the effect to the list of default effects for streams of type |stream|.
status_t addStreamDefaultEffect(const effect_uuid_t *type,
const String16& opPackageName,
const effect_uuid_t *uuid,
int32_t priority,
audio_usage_t usage,
audio_unique_id_t* id);
// Remove the default stream effect from wherever it's attached.
status_t removeStreamDefaultEffect(audio_unique_id_t id);
private:
// class to store the description of an effects and its parameters
// as defined in audio_effects.conf
class EffectDesc {
public:
EffectDesc(const char *name, const effect_uuid_t& uuid) :
EffectDesc(const char *name,
const effect_uuid_t& typeUuid,
const String16& opPackageName,
const effect_uuid_t& uuid,
uint32_t priority,
audio_unique_id_t id) :
mName(strdup(name)),
mUuid(uuid) { }
mTypeUuid(typeUuid),
mOpPackageName(opPackageName),
mUuid(uuid),
mPriority(priority),
mId(id) { }
EffectDesc(const char *name, const effect_uuid_t& uuid) :
EffectDesc(name,
*EFFECT_UUID_NULL,
String16(""),
uuid,
0,
AUDIO_UNIQUE_ID_ALLOCATE) { }
EffectDesc(const EffectDesc& orig) :
mName(strdup(orig.mName)),
mUuid(orig.mUuid) {
mTypeUuid(orig.mTypeUuid),
mOpPackageName(orig.mOpPackageName),
mUuid(orig.mUuid),
mPriority(orig.mPriority),
mId(orig.mId) {
// deep copy mParams
for (size_t k = 0; k < orig.mParams.size(); k++) {
effect_param_t *origParam = orig.mParams[k];
@ -116,7 +146,11 @@ private:
}
}
char *mName;
effect_uuid_t mTypeUuid;
String16 mOpPackageName;
effect_uuid_t mUuid;
int32_t mPriority;
audio_unique_id_t mId;
Vector <effect_param_t *> mParams;
};

@ -859,6 +859,50 @@ status_t AudioPolicyService::queryDefaultPreProcessing(audio_session_t audioSess
(audio_session_t)audioSession, descriptors, count);
}
status_t AudioPolicyService::addStreamDefaultEffect(const effect_uuid_t *type,
const String16& opPackageName,
const effect_uuid_t *uuid,
int32_t priority,
audio_usage_t usage,
audio_unique_id_t* id)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
if (!modifyDefaultAudioEffectsAllowed()) {
return PERMISSION_DENIED;
}
sp<AudioPolicyEffects>audioPolicyEffects;
{
Mutex::Autolock _l(mLock);
audioPolicyEffects = mAudioPolicyEffects;
}
if (audioPolicyEffects == 0) {
return NO_INIT;
}
return audioPolicyEffects->addStreamDefaultEffect(
type, opPackageName, uuid, priority, usage, id);
}
status_t AudioPolicyService::removeStreamDefaultEffect(audio_unique_id_t id)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
if (!modifyDefaultAudioEffectsAllowed()) {
return PERMISSION_DENIED;
}
sp<AudioPolicyEffects>audioPolicyEffects;
{
Mutex::Autolock _l(mLock);
audioPolicyEffects = mAudioPolicyEffects;
}
if (audioPolicyEffects == 0) {
return NO_INIT;
}
return audioPolicyEffects->removeStreamDefaultEffect(id);
}
bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info)
{
if (mAudioPolicyManager == NULL) {

@ -126,6 +126,14 @@ public:
virtual status_t queryDefaultPreProcessing(audio_session_t audioSession,
effect_descriptor_t *descriptors,
uint32_t *count);
virtual status_t addStreamDefaultEffect(const effect_uuid_t *type,
const String16& opPackageName,
const effect_uuid_t *uuid,
int32_t priority,
audio_usage_t usage,
audio_unique_id_t* id);
virtual status_t removeStreamDefaultEffect(audio_unique_id_t id);
virtual status_t onTransact(
uint32_t code,
const Parcel& data,

Loading…
Cancel
Save