Dynamic audio policies: multi-zone through uid/device affinity

Setting audio device affinity for a given uid augments all
 audio mix criteria that do not route to the given devices
 to exclude the uid.
AudioPolicyManager: after changing the device affinity,
 check the outputs addressing the devices to re-evaluate
 audio routing

Bug: 111647296
Test: requires device with routing policy started by CarService
Change-Id: I72de54ae067151fe6ac2ec43b78fe544a9fd9888
gugelfrei
Jean-Michel Trivi 6 years ago
parent abf2929e8d
commit bda70da1f0

@ -21,6 +21,22 @@
namespace android {
//
// AudioDeviceTypeAddr implementation
//
status_t AudioDeviceTypeAddr::readFromParcel(Parcel *parcel) {
mType = (audio_devices_t) parcel->readInt32();
mAddress = parcel->readString8();
return NO_ERROR;
}
status_t AudioDeviceTypeAddr::writeToParcel(Parcel *parcel) const {
parcel->writeInt32((int32_t) mType);
parcel->writeString8(mAddress);
return NO_ERROR;
}
//
// AudioMixMatchCriterion implementation
//
@ -40,11 +56,22 @@ AudioMixMatchCriterion::AudioMixMatchCriterion(audio_usage_t usage,
status_t AudioMixMatchCriterion::readFromParcel(Parcel *parcel)
{
mRule = parcel->readInt32();
if (mRule == RULE_MATCH_ATTRIBUTE_USAGE ||
mRule == RULE_EXCLUDE_ATTRIBUTE_USAGE) {
mValue.mUsage = (audio_usage_t)parcel->readInt32();
} else {
mValue.mSource = (audio_source_t)parcel->readInt32();
switch (mRule) {
case RULE_MATCH_ATTRIBUTE_USAGE:
case RULE_EXCLUDE_ATTRIBUTE_USAGE:
mValue.mUsage = (audio_usage_t) parcel->readInt32();
break;
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
case RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET:
mValue.mSource = (audio_source_t) parcel->readInt32();
break;
case RULE_MATCH_UID:
case RULE_EXCLUDE_UID:
mValue.mUid = (uid_t) parcel->readInt32();
break;
default:
ALOGE("Trying to build AudioMixMatchCriterion from unknown rule %d", mRule);
return BAD_VALUE;
}
return NO_ERROR;
}
@ -116,4 +143,11 @@ status_t AudioMix::writeToParcel(Parcel *parcel) const
return NO_ERROR;
}
void AudioMix::excludeUid(uid_t uid) const {
AudioMixMatchCriterion crit;
crit.mRule = RULE_EXCLUDE_UID;
crit.mValue.mUid = uid;
mCriteria.add(crit);
}
} // namespace android

@ -1236,6 +1236,19 @@ status_t AudioSystem::registerPolicyMixes(const Vector<AudioMix>& mixes, bool re
return aps->registerPolicyMixes(mixes, registration);
}
status_t AudioSystem::setUidDeviceAffinities(uid_t uid, const Vector<AudioDeviceTypeAddr>& devices)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->setUidDeviceAffinities(uid, devices);
}
status_t AudioSystem::removeUidDeviceAffinities(uid_t uid) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->removeUidDeviceAffinities(uid);
}
status_t AudioSystem::startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId)

@ -90,6 +90,8 @@ enum {
SET_ASSISTANT_UID,
SET_A11Y_SERVICES_UIDS,
IS_HAPTIC_PLAYBACK_SUPPORTED,
SET_UID_DEVICE_AFFINITY,
REMOVE_UID_DEVICE_AFFINITY,
};
#define MAX_ITEMS_PER_LIST 1024
@ -990,6 +992,50 @@ public:
return reply.readBool();
}
virtual status_t setUidDeviceAffinities(uid_t uid, const Vector<AudioDeviceTypeAddr>& devices)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
data.writeInt32((int32_t) uid);
size_t size = devices.size();
size_t sizePosition = data.dataPosition();
data.writeInt32((int32_t) size);
size_t finalSize = size;
for (size_t i = 0; i < size; i++) {
size_t position = data.dataPosition();
if (devices[i].writeToParcel(&data) != NO_ERROR) {
data.setDataPosition(position);
finalSize--;
}
}
if (size != finalSize) {
size_t position = data.dataPosition();
data.setDataPosition(sizePosition);
data.writeInt32(finalSize);
data.setDataPosition(position);
}
status_t status = remote()->transact(SET_UID_DEVICE_AFFINITY, data, &reply);
if (status == NO_ERROR) {
status = (status_t)reply.readInt32();
}
return status;
}
virtual status_t removeUidDeviceAffinities(uid_t uid)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
data.writeInt32((int32_t) uid);
status_t status = remote()->transact(REMOVE_UID_DEVICE_AFFINITY, data, &reply);
if (status == NO_ERROR) {
status = (status_t)reply.readInt32();
}
return status;
}
};
IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@ -1048,7 +1094,9 @@ status_t BnAudioPolicyService::onTransact(
case GET_SURROUND_FORMATS:
case SET_SURROUND_FORMAT_ENABLED:
case SET_ASSISTANT_UID:
case SET_A11Y_SERVICES_UIDS: {
case SET_A11Y_SERVICES_UIDS:
case SET_UID_DEVICE_AFFINITY:
case REMOVE_UID_DEVICE_AFFINITY: {
if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
__func__, code, IPCThreadState::self()->getCallingPid(),
@ -1811,6 +1859,30 @@ status_t BnAudioPolicyService::onTransact(
CHECK_INTERFACE(IAudioPolicyService, data, reply);
bool isSupported = isHapticPlaybackSupported();
reply->writeBool(isSupported);
return NO_ERROR;
}
case SET_UID_DEVICE_AFFINITY: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
const uid_t uid = (uid_t) data.readInt32();
Vector<AudioDeviceTypeAddr> devices;
size_t size = (size_t)data.readInt32();
for (size_t i = 0; i < size; i++) {
AudioDeviceTypeAddr device;
if (device.readFromParcel((Parcel*)&data) == NO_ERROR) {
devices.add(device);
}
}
status_t status = setUidDeviceAffinities(uid, devices);
reply->writeInt32(status);
return NO_ERROR;
}
case REMOVE_UID_DEVICE_AFFINITY: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
const uid_t uid = (uid_t) data.readInt32();
status_t status = removeUidDeviceAffinities(uid);
reply->writeInt32(status);
return NO_ERROR;
}

@ -56,6 +56,19 @@ namespace android {
#define MAX_MIXES_PER_POLICY 10
#define MAX_CRITERIA_PER_MIX 20
class AudioDeviceTypeAddr {
public:
AudioDeviceTypeAddr() {}
AudioDeviceTypeAddr(audio_devices_t type, String8 address) :
mType(type), mAddress(address) {}
status_t readFromParcel(Parcel *parcel);
status_t writeToParcel(Parcel *parcel) const;
audio_devices_t mType;
String8 mAddress;
};
class AudioMixMatchCriterion {
public:
AudioMixMatchCriterion() {}
@ -87,7 +100,9 @@ public:
status_t readFromParcel(Parcel *parcel);
status_t writeToParcel(Parcel *parcel) const;
Vector<AudioMixMatchCriterion> mCriteria;
void excludeUid(uid_t uid) const;
mutable Vector<AudioMixMatchCriterion> mCriteria;
uint32_t mMixType;
audio_config_t mFormat;
uint32_t mRouteFlags;

@ -325,6 +325,10 @@ public:
static status_t registerPolicyMixes(const Vector<AudioMix>& mixes, bool registration);
static status_t setUidDeviceAffinities(uid_t uid, const Vector<AudioDeviceTypeAddr>& devices);
static status_t removeUidDeviceAffinities(uid_t uid);
static status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId);

@ -167,6 +167,11 @@ public:
virtual status_t registerPolicyMixes(const Vector<AudioMix>& mixes, bool registration) = 0;
virtual status_t setUidDeviceAffinities(uid_t uid, const Vector<AudioDeviceTypeAddr>& devices)
= 0;
virtual status_t removeUidDeviceAffinities(uid_t uid) = 0;
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId) = 0;

@ -51,6 +51,7 @@ LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
liblog \
libaudioclient \
libsoundtrigger
ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1)

@ -210,6 +210,10 @@ public:
virtual status_t registerPolicyMixes(const Vector<AudioMix>& mixes) = 0;
virtual status_t unregisterPolicyMixes(Vector<AudioMix> mixes) = 0;
virtual status_t setUidDeviceAffinities(uid_t uid, const Vector<AudioDeviceTypeAddr>& devices)
= 0;
virtual status_t removeUidDeviceAffinities(uid_t uid) = 0;
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId,

@ -80,6 +80,10 @@ public:
status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix);
status_t setUidDeviceAffinities(uid_t uid, const Vector<AudioDeviceTypeAddr>& devices);
status_t removeUidDeviceAffinities(uid_t uid);
status_t getDevicesForUid(uid_t uid, Vector<AudioDeviceTypeAddr>& devices) const;
void dump(String8 *dst) const;
};

@ -340,6 +340,87 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A
return NO_ERROR;
}
status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid,
const Vector<AudioDeviceTypeAddr>& devices) {
// remove existing rules for this uid
removeUidDeviceAffinities(uid);
// for each player mix: add a rule to match or exclude the uid based on the device
for (size_t i = 0; i < size(); i++) {
const AudioMix *mix = valueAt(i)->getMix();
if (mix->mMixType != MIX_TYPE_PLAYERS) {
continue;
}
// check if this mix goes to a device in the list of devices
bool deviceMatch = false;
for (size_t j = 0; j < devices.size(); j++) {
if (devices[j].mType == mix->mDeviceType
&& devices[j].mAddress == mix->mDeviceAddress) {
deviceMatch = true;
break;
}
}
if (!deviceMatch) {
// this mix doesn't go to one of the listed devices for the given uid,
// modify its rules to exclude the uid
mix->excludeUid(uid);
}
}
return NO_ERROR;
}
status_t AudioPolicyMixCollection::removeUidDeviceAffinities(uid_t uid) {
// for each player mix: remove existing rules that match or exclude this uid
for (size_t i = 0; i < size(); i++) {
bool foundUidRule = false;
AudioMix *mix = valueAt(i)->getMix();
if (mix->mMixType != MIX_TYPE_PLAYERS) {
continue;
}
std::vector<size_t> criteriaToRemove;
for (size_t j = 0; j < mix->mCriteria.size(); j++) {
const uint32_t rule = mix->mCriteria[j].mRule;
// is this rule affecting the uid?
if (rule == RULE_EXCLUDE_UID
&& uid == mix->mCriteria[j].mValue.mUid) {
foundUidRule = true;
criteriaToRemove.push_back(j);
}
}
if (foundUidRule) {
for (size_t j = criteriaToRemove.size() - 1; j >= 0; j--) {
mix->mCriteria.removeAt(criteriaToRemove[j]);
}
}
}
return NO_ERROR;
}
status_t AudioPolicyMixCollection::getDevicesForUid(uid_t uid,
Vector<AudioDeviceTypeAddr>& devices) const {
// for each player mix: find rules that don't exclude this uid, and add the device to the list
for (size_t i = 0; i < size(); i++) {
bool ruleAllowsUid = true;
AudioMix *mix = valueAt(i)->getMix();
if (mix->mMixType != MIX_TYPE_PLAYERS) {
continue;
}
for (size_t j = 0; j < mix->mCriteria.size(); j++) {
const uint32_t rule = mix->mCriteria[j].mRule;
if (rule == RULE_EXCLUDE_UID
&& uid == mix->mCriteria[j].mValue.mUid) {
ruleAllowsUid = false;
break;
}
}
if (ruleAllowsUid) {
devices.add(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress));
}
}
return NO_ERROR;
}
void AudioPolicyMixCollection::dump(String8 *dst) const
{
dst->append("\nAudio Policy Mix:\n");

@ -2674,6 +2674,59 @@ void AudioPolicyManager::dumpManualSurroundFormats(String8 *dst) const
}
}
status_t AudioPolicyManager::setUidDeviceAffinities(uid_t uid,
const Vector<AudioDeviceTypeAddr>& devices) {
ALOGV("%s() uid=%d num devices %zu", __FUNCTION__, uid, devices.size());
// uid/device affinity is only for output devices
for (size_t i = 0; i < devices.size(); i++) {
if (!audio_is_output_device(devices[i].mType)) {
ALOGE("setUidDeviceAffinities() device=%08x is NOT an output device",
devices[i].mType);
return BAD_VALUE;
}
}
status_t res = mPolicyMixes.setUidDeviceAffinities(uid, devices);
if (res == NO_ERROR) {
// reevaluate outputs for all given devices
for (size_t i = 0; i < devices.size(); i++) {
sp<DeviceDescriptor> devDesc = mHwModules.getDeviceDescriptor(
devices[i].mType, devices[i].mAddress, String8());
SortedVector<audio_io_handle_t> outputs;
if (checkOutputsForDevice(devDesc, AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
outputs,
devDesc->address()) != NO_ERROR) {
ALOGE("setUidDeviceAffinities() error in checkOutputsForDevice for device=%08x"
" addr=%s", devices[i].mType, devices[i].mAddress.string());
return INVALID_OPERATION;
}
}
}
return res;
}
status_t AudioPolicyManager::removeUidDeviceAffinities(uid_t uid) {
ALOGV("%s() uid=%d", __FUNCTION__, uid);
Vector<AudioDeviceTypeAddr> devices;
status_t res = mPolicyMixes.getDevicesForUid(uid, devices);
if (res == NO_ERROR) {
// reevaluate outputs for all found devices
for (size_t i = 0; i < devices.size(); i++) {
sp<DeviceDescriptor> devDesc = mHwModules.getDeviceDescriptor(
devices[i].mType, devices[i].mAddress, String8());
SortedVector<audio_io_handle_t> outputs;
if (checkOutputsForDevice(devDesc, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
outputs,
devDesc->address()) != NO_ERROR) {
ALOGE("%s() error in checkOutputsForDevice for device=%08x addr=%s",
__FUNCTION__, devices[i].mType, devices[i].mAddress.string());
return INVALID_OPERATION;
}
}
}
return res;
}
void AudioPolicyManager::dump(String8 *dst) const
{
dst->appendFormat("\nAudioPolicyManager Dump: %p\n", this);

@ -218,6 +218,9 @@ public:
virtual status_t registerPolicyMixes(const Vector<AudioMix>& mixes);
virtual status_t unregisterPolicyMixes(Vector<AudioMix> mixes);
virtual status_t setUidDeviceAffinities(uid_t uid,
const Vector<AudioDeviceTypeAddr>& devices);
virtual status_t removeUidDeviceAffinities(uid_t uid);
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,

@ -1037,6 +1037,31 @@ status_t AudioPolicyService::registerPolicyMixes(const Vector<AudioMix>& mixes,
}
}
status_t AudioPolicyService::setUidDeviceAffinities(uid_t uid,
const Vector<AudioDeviceTypeAddr>& devices) {
Mutex::Autolock _l(mLock);
if(!modifyAudioRoutingAllowed()) {
return PERMISSION_DENIED;
}
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
AutoCallerClear acc;
return mAudioPolicyManager->setUidDeviceAffinities(uid, devices);
}
status_t AudioPolicyService::removeUidDeviceAffinities(uid_t uid) {
Mutex::Autolock _l(mLock);
if(!modifyAudioRoutingAllowed()) {
return PERMISSION_DENIED;
}
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
AutoCallerClear acc;
return mAudioPolicyManager->removeUidDeviceAffinities(uid);
}
status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId)

@ -199,6 +199,10 @@ public:
virtual status_t registerPolicyMixes(const Vector<AudioMix>& mixes, bool registration);
virtual status_t setUidDeviceAffinities(uid_t uid, const Vector<AudioDeviceTypeAddr>& devices);
virtual status_t removeUidDeviceAffinities(uid_t uid);
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId);

Loading…
Cancel
Save