Dynamic audio policies on UID
When looking for an output, take attributes and UID into account.
To find the matching mix: for each mix
- inspect which rules are used
- find a candidate mix for usage
- check UID compatibility
Rename AttributeMatchCriterion to AudioMixMatchCriterion to be consistent
with the fact that now mixing rules are not always about audio attributes.
Bug 26798796
Change-Id: I1520b0df190a98f197ea8e0144b770e1e6d97888
diff --git a/include/media/AudioPolicy.h b/include/media/AudioPolicy.h
index ff07b08..a171493 100644
--- a/include/media/AudioPolicy.h
+++ b/include/media/AudioPolicy.h
@@ -28,11 +28,13 @@
// Keep in sync with AudioMix.java, AudioMixingRule.java, AudioPolicyConfig.java
#define RULE_EXCLUSION_MASK 0x8000
-#define RULE_MATCH_ATTRIBUTE_USAGE 0x1
+#define RULE_MATCH_ATTRIBUTE_USAGE 0x1
#define RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET (0x1 << 1)
-#define RULE_EXCLUDE_ATTRIBUTE_USAGE (RULE_EXCLUSION_MASK|RULE_MATCH_ATTRIBUTE_USAGE)
+#define RULE_MATCH_UID (0x1 << 2)
+#define RULE_EXCLUDE_ATTRIBUTE_USAGE (RULE_EXCLUSION_MASK|RULE_MATCH_ATTRIBUTE_USAGE)
#define RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET \
- (RULE_EXCLUSION_MASK|RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET)
+ (RULE_EXCLUSION_MASK|RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET)
+#define RULE_EXCLUDE_UID (RULE_EXCLUSION_MASK|RULE_MATCH_UID)
#define MIX_TYPE_INVALID -1
#define MIX_TYPE_PLAYERS 0
@@ -53,10 +55,10 @@
#define MAX_MIXES_PER_POLICY 10
#define MAX_CRITERIA_PER_MIX 20
-class AttributeMatchCriterion {
+class AudioMixMatchCriterion {
public:
- AttributeMatchCriterion() {}
- AttributeMatchCriterion(audio_usage_t usage, audio_source_t source, uint32_t rule);
+ AudioMixMatchCriterion() {}
+ AudioMixMatchCriterion(audio_usage_t usage, audio_source_t source, uint32_t rule);
status_t readFromParcel(Parcel *parcel);
status_t writeToParcel(Parcel *parcel) const;
@@ -64,7 +66,8 @@
union {
audio_usage_t mUsage;
audio_source_t mSource;
- } mAttr;
+ uid_t mUid;
+ } mValue;
uint32_t mRule;
};
@@ -75,7 +78,7 @@
static const uint32_t kCbFlagNotifyActivity = 0x1;
AudioMix() {}
- AudioMix(Vector<AttributeMatchCriterion> criteria, uint32_t mixType, audio_config_t format,
+ AudioMix(Vector<AudioMixMatchCriterion> criteria, uint32_t mixType, audio_config_t format,
uint32_t routeFlags, String8 registrationId, uint32_t flags) :
mCriteria(criteria), mMixType(mixType), mFormat(format),
mRouteFlags(routeFlags), mRegistrationId(registrationId), mCbFlags(flags){}
@@ -83,7 +86,7 @@
status_t readFromParcel(Parcel *parcel);
status_t writeToParcel(Parcel *parcel) const;
- Vector<AttributeMatchCriterion> mCriteria;
+ Vector<AudioMixMatchCriterion> mCriteria;
uint32_t mMixType;
audio_config_t mFormat;
uint32_t mRouteFlags;
diff --git a/media/libmedia/AudioPolicy.cpp b/media/libmedia/AudioPolicy.cpp
index 9d07011..ea22b6c 100644
--- a/media/libmedia/AudioPolicy.cpp
+++ b/media/libmedia/AudioPolicy.cpp
@@ -22,37 +22,37 @@
namespace android {
//
-// AttributeMatchCriterion implementation
+// AudioMixMatchCriterion implementation
//
-AttributeMatchCriterion::AttributeMatchCriterion(audio_usage_t usage,
+AudioMixMatchCriterion::AudioMixMatchCriterion(audio_usage_t usage,
audio_source_t source,
uint32_t rule)
: mRule(rule)
{
if (mRule == RULE_MATCH_ATTRIBUTE_USAGE ||
mRule == RULE_EXCLUDE_ATTRIBUTE_USAGE) {
- mAttr.mUsage = usage;
+ mValue.mUsage = usage;
} else {
- mAttr.mSource = source;
+ mValue.mSource = source;
}
}
-status_t AttributeMatchCriterion::readFromParcel(Parcel *parcel)
+status_t AudioMixMatchCriterion::readFromParcel(Parcel *parcel)
{
mRule = parcel->readInt32();
if (mRule == RULE_MATCH_ATTRIBUTE_USAGE ||
mRule == RULE_EXCLUDE_ATTRIBUTE_USAGE) {
- mAttr.mUsage = (audio_usage_t)parcel->readInt32();
+ mValue.mUsage = (audio_usage_t)parcel->readInt32();
} else {
- mAttr.mSource = (audio_source_t)parcel->readInt32();
+ mValue.mSource = (audio_source_t)parcel->readInt32();
}
return NO_ERROR;
}
-status_t AttributeMatchCriterion::writeToParcel(Parcel *parcel) const
+status_t AudioMixMatchCriterion::writeToParcel(Parcel *parcel) const
{
parcel->writeInt32(mRule);
- parcel->writeInt32(mAttr.mUsage);
+ parcel->writeInt32(mValue.mUsage);
return NO_ERROR;
}
@@ -74,7 +74,7 @@
size = MAX_CRITERIA_PER_MIX;
}
for (size_t i = 0; i < size; i++) {
- AttributeMatchCriterion criterion;
+ AudioMixMatchCriterion criterion;
if (criterion.readFromParcel(parcel) == NO_ERROR) {
mCriteria.add(criterion);
}
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index d51f4e1..c952831 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -69,7 +69,8 @@
* @return NO_ERROR if an output was found for the given attribute (in this case, the
* descriptor output param is initialized), error code otherwise.
*/
- status_t getOutputForAttr(audio_attributes_t attributes, sp<SwAudioOutputDescriptor> &desc);
+ status_t getOutputForAttr(audio_attributes_t attributes, uid_t uid,
+ sp<SwAudioOutputDescriptor> &desc);
audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource,
audio_devices_t availableDeviceTypes,
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 6f1998c..3735c05 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -98,30 +98,111 @@
}
}
-status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes,
+status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes, uid_t uid,
sp<SwAudioOutputDescriptor> &desc)
{
+ desc = 0;
for (size_t i = 0; i < size(); i++) {
sp<AudioPolicyMix> policyMix = valueAt(i);
AudioMix *mix = policyMix->getMix();
if (mix->mMixType == MIX_TYPE_PLAYERS) {
+ // TODO if adding more player rules (currently only 2), make rule handling "generic"
+ // as there is no difference in the treatment of usage- or uid-based rules
+ bool hasUsageMatchRules = false;
+ bool hasUsageExcludeRules = false;
+ bool usageMatchFound = false;
+ bool usageExclusionFound = false;
+
+ bool hasUidMatchRules = false;
+ bool hasUidExcludeRules = false;
+ bool uidMatchFound = false;
+ bool uidExclusionFound = false;
+
+ bool hasAddrMatch = false;
+
+ // iterate over all mix criteria to list what rules this mix contains
for (size_t j = 0; j < mix->mCriteria.size(); j++) {
- if ((RULE_MATCH_ATTRIBUTE_USAGE == mix->mCriteria[j].mRule &&
- mix->mCriteria[j].mAttr.mUsage == attributes.usage) ||
- (RULE_EXCLUDE_ATTRIBUTE_USAGE == mix->mCriteria[j].mRule &&
- mix->mCriteria[j].mAttr.mUsage != attributes.usage)) {
- desc = policyMix->getOutput();
- break;
- }
+ ALOGV("getOutputForAttr: inspecting mix %zu of %zu", i, mix->mCriteria.size());
+
+ // if there is an address match, prioritize that match
if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
strncmp(attributes.tags + strlen("addr="),
mix->mRegistrationId.string(),
AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
- desc = policyMix->getOutput();
+ hasAddrMatch = true;
break;
}
+
+ switch (mix->mCriteria[j].mRule) {
+ case RULE_MATCH_ATTRIBUTE_USAGE:
+ ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
+ mix->mCriteria[j].mValue.mUsage);
+ hasUsageMatchRules = true;
+ if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
+ // found one match against all allowed usages
+ usageMatchFound = true;
+ }
+ break;
+ case RULE_EXCLUDE_ATTRIBUTE_USAGE:
+ ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
+ mix->mCriteria[j].mValue.mUsage);
+ hasUsageExcludeRules = true;
+ if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
+ // found this usage is to be excluded
+ usageExclusionFound = true;
+ }
+ break;
+ case RULE_MATCH_UID:
+ ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
+ hasUidMatchRules = true;
+ if (mix->mCriteria[j].mValue.mUid == uid) {
+ // found one UID match against all allowed UIDs
+ uidMatchFound = true;
+ }
+ break;
+ case RULE_EXCLUDE_UID:
+ ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
+ hasUidExcludeRules = true;
+ if (mix->mCriteria[j].mValue.mUid == uid) {
+ // found this UID is to be excluded
+ uidExclusionFound = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // consistency checks: for each "dimension" of rules (usage, uid...), we can
+ // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
+ if (hasUsageMatchRules && hasUsageExcludeRules) {
+ ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
+ " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", i);
+ return BAD_VALUE;
+ }
+ if (hasUidMatchRules && hasUidExcludeRules) {
+ ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
+ " and RULE_EXCLUDE_UID in mix %zu", i);
+ return BAD_VALUE;
+ }
+
+ if ((hasUsageExcludeRules && usageExclusionFound)
+ || (hasUidExcludeRules && uidExclusionFound)) {
+ break; // stop iterating on criteria because an exclusion was found (will fail)
+ }
+
+ }//iterate on mix criteria
+
+ // determine if exiting on success (or implicit failure as desc is 0)
+ if (hasAddrMatch ||
+ !((hasUsageExcludeRules && usageExclusionFound) ||
+ (hasUsageMatchRules && !usageMatchFound) ||
+ (hasUidExcludeRules && uidExclusionFound) ||
+ (hasUidMatchRules && !uidMatchFound))) {
+ ALOGV("\tgetOutputForAttr will use mix %zu", i);
+ desc = policyMix->getOutput();
}
+
} else if (mix->mMixType == MIX_TYPE_RECORDERS) {
if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
@@ -151,9 +232,9 @@
}
for (size_t j = 0; j < mix->mCriteria.size(); j++) {
if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
- mix->mCriteria[j].mAttr.mSource == inputSource) ||
+ mix->mCriteria[j].mValue.mSource == inputSource) ||
(RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
- mix->mCriteria[j].mAttr.mSource != inputSource)) {
+ mix->mCriteria[j].mValue.mSource != inputSource)) {
if (availDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
if (policyMix != NULL) {
*policyMix = mix;
@@ -174,6 +255,15 @@
}
String8 address(attr.tags + strlen("addr="));
+#ifdef LOG_NDEBUG
+ ALOGV("getInputMixForAttr looking for address %s\n mixes available:", address.string());
+ for (size_t i = 0; i < size(); i++) {
+ sp<AudioPolicyMix> policyMix = valueAt(i);
+ AudioMix *mix = policyMix->getMix();
+ ALOGV("\tmix %zu address=%s", i, mix->mRegistrationId.string());
+ }
+#endif
+
ssize_t index = indexOfKey(address);
if (index < 0) {
ALOGW("getInputMixForAttr() no policy for address %s", address.string());
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index ec70ed4..60ec27e 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -709,7 +709,7 @@
stream_type_to_audio_attributes(*stream, &attributes);
}
sp<SwAudioOutputDescriptor> desc;
- if (mPolicyMixes.getOutputForAttr(attributes, desc) == NO_ERROR) {
+ if (mPolicyMixes.getOutputForAttr(attributes, uid, desc) == NO_ERROR) {
ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
if (!audio_is_linear_pcm(format)) {
return BAD_VALUE;