Added audio policy api to do route based on user.
Added new API to audio policy to route audio output streams based on
Android user id. The routing remains similar to uid rules, with only
using USERID_EXCLUDE_USERID to decide on the user id base routing.
Application uid and stream usage routing remain the same with the added
difference that user id will be taken in to consideration if available.
Bug: 139365417
Test: atest
com.google.android.gts.audio.AudioHostTest#testUserIdDeviceAffinity
Test: atest AudioPlaybackCaptureTest
Change-Id: Ib817eb8ae19ed7dea8451e8a8584a20b9b6f5042
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index dd0cd9b..4d53be4 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -250,6 +250,10 @@
= 0;
virtual status_t removeUidDeviceAffinities(uid_t uid) = 0;
+ virtual status_t setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices) = 0;
+ virtual status_t removeUserIdDeviceAffinities(int userId) = 0;
+
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId,
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index fc79ab1..a757551 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -105,12 +105,27 @@
status_t removeUidDeviceAffinities(uid_t uid);
status_t getDevicesForUid(uid_t uid, Vector<AudioDeviceTypeAddr>& devices) const;
+ /**
+ * Updates the mix rules in order to make streams associated with the given user
+ * be routed to the given audio devices.
+ * @param userId the userId for which the device affinity is set
+ * @param devices the vector of devices that this userId may be routed to. A typical
+ * use is to pass the devices associated with a given zone in a multi-zone setup.
+ * @return NO_ERROR if the update was successful, INVALID_OPERATION otherwise.
+ * An example of failure is when there are already rules in place to restrict
+ * a mix to the given userId (i.e. when a MATCH_USERID rule was set for it).
+ */
+ status_t setUserIdDeviceAffinities(int userId, const Vector<AudioDeviceTypeAddr>& devices);
+ status_t removeUserIdDeviceAffinities(int userId);
+ status_t getDevicesForUserId(int userId, Vector<AudioDeviceTypeAddr>& devices) const;
+
void dump(String8 *dst) const;
private:
enum class MixMatchStatus { MATCH, NO_MATCH, INVALID_MIX };
MixMatchStatus mixMatch(const AudioMix* mix, size_t mixIndex,
- const audio_attributes_t& attributes, uid_t uid);
+ const audio_attributes_t& attributes,
+ uid_t uid);
};
} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index b1103ab..202cd67 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -60,6 +60,9 @@
case RULE_MATCH_UID:
ruleValue = std::to_string(criterion.mValue.mUid);
break;
+ case RULE_MATCH_USERID:
+ ruleValue = std::to_string(criterion.mValue.mUserId);
+ break;
default:
unknownRule = true;
}
@@ -227,6 +230,9 @@
return MixMatchStatus::NO_MATCH;
}
}
+
+ int userId = (int) multiuser_get_user_id(uid);
+
// 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;
@@ -239,6 +245,12 @@
bool uidMatchFound = false;
bool uidExclusionFound = false;
+ bool hasUserIdExcludeRules = false;
+ bool userIdExclusionFound = false;
+ bool hasUserIdMatchRules = false;
+ bool userIdMatchFound = false;
+
+
bool hasAddrMatch = false;
// iterate over all mix criteria to list what rules this mix contains
@@ -290,6 +302,24 @@
uidExclusionFound = true;
}
break;
+ case RULE_MATCH_USERID:
+ ALOGV("\tmix has RULE_MATCH_USERID for userId %d",
+ mix->mCriteria[j].mValue.mUserId);
+ hasUserIdMatchRules = true;
+ if (mix->mCriteria[j].mValue.mUserId == userId) {
+ // found one userId match against all allowed userIds
+ userIdMatchFound = true;
+ }
+ break;
+ case RULE_EXCLUDE_USERID:
+ ALOGV("\tmix has RULE_EXCLUDE_USERID for userId %d",
+ mix->mCriteria[j].mValue.mUserId);
+ hasUserIdExcludeRules = true;
+ if (mix->mCriteria[j].mValue.mUserId == userId) {
+ // found this userId is to be excluded
+ userIdExclusionFound = true;
+ }
+ break;
default:
break;
}
@@ -306,20 +336,27 @@
" and RULE_EXCLUDE_UID in mix %zu", mixIndex);
return MixMatchStatus::INVALID_MIX;
}
-
- if ((hasUsageExcludeRules && usageExclusionFound)
- || (hasUidExcludeRules && uidExclusionFound)) {
- break; // stop iterating on criteria because an exclusion was found (will fail)
+ if (hasUserIdMatchRules && hasUserIdExcludeRules) {
+ ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_USERID"
+ " and RULE_EXCLUDE_USERID in mix %zu", mixIndex);
+ return MixMatchStatus::INVALID_MIX;
}
+ if ((hasUsageExcludeRules && usageExclusionFound)
+ || (hasUidExcludeRules && uidExclusionFound)
+ || (hasUserIdExcludeRules && userIdExclusionFound)) {
+ 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) ||
+ (hasUserIdExcludeRules && userIdExclusionFound) ||
(hasUsageMatchRules && !usageMatchFound) ||
(hasUidExcludeRules && uidExclusionFound) ||
- (hasUidMatchRules && !uidMatchFound))) {
+ (hasUidMatchRules && !uidMatchFound)) ||
+ (hasUserIdMatchRules && !userIdMatchFound)) {
ALOGV("\tgetOutputForAttr will use mix %zu", mixIndex);
return MixMatchStatus::MATCH;
}
@@ -530,6 +567,109 @@
return NO_ERROR;
}
+status_t AudioPolicyMixCollection::setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices) {
+ // verify feasibility: for each player mix: if it already contains a
+ // "match userId" rule for this userId, return an error
+ // (adding a userId-device affinity would result in contradictory rules)
+ for (size_t i = 0; i < size(); i++) {
+ const AudioPolicyMix* mix = itemAt(i).get();
+ if (!mix->isDeviceAffinityCompatible()) {
+ continue;
+ }
+ if (mix->hasUserIdRule(true /*match*/, userId)) {
+ return INVALID_OPERATION;
+ }
+ }
+
+ // remove existing rules for this userId
+ removeUserIdDeviceAffinities(userId);
+
+ // for each player mix:
+ // IF device is not a target for the mix,
+ // AND it doesn't have a "match userId" rule
+ // THEN add a rule to exclude the userId
+ for (size_t i = 0; i < size(); i++) {
+ const AudioPolicyMix *mix = itemAt(i).get();
+ if (!mix->isDeviceAffinityCompatible()) {
+ continue;
+ }
+ // check if this mix goes to a device in the list of devices
+ bool deviceMatch = false;
+ const AudioDeviceTypeAddr mixDevice(mix->mDeviceType, mix->mDeviceAddress.string());
+ for (size_t j = 0; j < devices.size(); j++) {
+ if (mixDevice.equals(devices[j])) {
+ deviceMatch = true;
+ break;
+ }
+ }
+ if (!deviceMatch && !mix->hasMatchUserIdRule()) {
+ // this mix doesn't go to one of the listed devices for the given userId,
+ // and it's not already restricting the mix on a userId,
+ // modify its rules to exclude the userId
+ if (!mix->hasUserIdRule(false /*match*/, userId)) {
+ // no need to do it again if userId is already excluded
+ mix->setExcludeUserId(userId);
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t AudioPolicyMixCollection::removeUserIdDeviceAffinities(int userId) {
+ // for each player mix: remove existing rules that match or exclude this userId
+ for (size_t i = 0; i < size(); i++) {
+ bool foundUserIdRule = false;
+ const AudioPolicyMix *mix = itemAt(i).get();
+ if (!mix->isDeviceAffinityCompatible()) {
+ 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 excluding the userId? (not considering userId match rules
+ // as those are not used for userId-device affinity)
+ if (rule == RULE_EXCLUDE_USERID
+ && userId == mix->mCriteria[j].mValue.mUserId) {
+ foundUserIdRule = true;
+ criteriaToRemove.insert(criteriaToRemove.begin(), j);
+ }
+ }
+ if (foundUserIdRule) {
+ for (size_t j = 0; j < criteriaToRemove.size(); j++) {
+ mix->mCriteria.removeAt(criteriaToRemove[j]);
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t AudioPolicyMixCollection::getDevicesForUserId(int userId,
+ Vector<AudioDeviceTypeAddr>& devices) const {
+ // for each player mix:
+ // find rules that don't exclude this userId, and add the device to the list
+ for (size_t i = 0; i < size(); i++) {
+ bool ruleAllowsUserId = true;
+ const AudioPolicyMix *mix = itemAt(i).get();
+ 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_USERID
+ && userId == mix->mCriteria[j].mValue.mUserId) {
+ ruleAllowsUserId = false;
+ break;
+ }
+ }
+ if (ruleAllowsUserId) {
+ devices.add(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress.string()));
+ }
+ }
+ return NO_ERROR;
+}
+
void AudioPolicyMixCollection::dump(String8 *dst) const
{
dst->append("\nAudio Policy Mix:\n");
diff --git a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
index 2b5455e..c5b3546 100644
--- a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
@@ -55,9 +55,11 @@
MAKE_STRING_FROM_ENUM(RULE_MATCH_ATTRIBUTE_USAGE),
MAKE_STRING_FROM_ENUM(RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET),
MAKE_STRING_FROM_ENUM(RULE_MATCH_UID),
+ MAKE_STRING_FROM_ENUM(RULE_MATCH_USERID),
MAKE_STRING_FROM_ENUM(RULE_EXCLUDE_ATTRIBUTE_USAGE),
MAKE_STRING_FROM_ENUM(RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET),
MAKE_STRING_FROM_ENUM(RULE_EXCLUDE_UID),
+ MAKE_STRING_FROM_ENUM(RULE_EXCLUDE_USERID),
TERMINATOR
};
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 82c4e47..bd8c3d1 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3150,6 +3150,49 @@
return mEngine->getPreferredDeviceForStrategy(strategy, device);
}
+status_t AudioPolicyManager::setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices) {
+ ALOGI("%s() userId=%d num devices %zu", __FUNCTION__, userId, devices.size());
+ // userId/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("%s() device=%08x is NOT an output device",
+ __FUNCTION__,
+ devices[i].mType);
+ return BAD_VALUE;
+ }
+ }
+
+ status_t status = mPolicyMixes.setUserIdDeviceAffinities(userId, devices);
+ if (status != NO_ERROR) {
+ ALOGE("%s() could not set device affinity for userId %d",
+ __FUNCTION__, userId);
+ return status;
+ }
+
+ // reevaluate outputs for all devices
+ checkForDeviceAndOutputChanges();
+ updateCallAndOutputRouting();
+
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::removeUserIdDeviceAffinities(int userId) {
+ ALOGI("%s() userId=%d", __FUNCTION__, userId);
+ status_t status = mPolicyMixes.removeUserIdDeviceAffinities(userId);
+ if (status != NO_ERROR) {
+ ALOGE("%s() Could not remove all device affinities fo userId = %d",
+ __FUNCTION__, userId);
+ return status;
+ }
+
+ // reevaluate outputs for all devices
+ checkForDeviceAndOutputChanges();
+ updateCallAndOutputRouting();
+
+ return NO_ERROR;
+}
+
void AudioPolicyManager::dump(String8 *dst) const
{
dst->appendFormat("\nAudioPolicyManager Dump: %p\n", this);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 7e0e16f..10adeb4 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -262,6 +262,9 @@
virtual status_t setUidDeviceAffinities(uid_t uid,
const Vector<AudioDeviceTypeAddr>& devices);
virtual status_t removeUidDeviceAffinities(uid_t uid);
+ virtual status_t setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices);
+ virtual status_t removeUserIdDeviceAffinities(int userId);
virtual status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device);
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index c865063..c38c11a 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -1264,6 +1264,31 @@
return mAudioPolicyManager->removeUidDeviceAffinities(uid);
}
+status_t AudioPolicyService::setUserIdDeviceAffinities(int userId,
+ const Vector<AudioDeviceTypeAddr>& devices) {
+ Mutex::Autolock _l(mLock);
+ if(!modifyAudioRoutingAllowed()) {
+ return PERMISSION_DENIED;
+ }
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ AutoCallerClear acc;
+ return mAudioPolicyManager->setUserIdDeviceAffinities(userId, devices);
+}
+
+status_t AudioPolicyService::removeUserIdDeviceAffinities(int userId) {
+ Mutex::Autolock _l(mLock);
+ if(!modifyAudioRoutingAllowed()) {
+ return PERMISSION_DENIED;
+ }
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ AutoCallerClear acc;
+ return mAudioPolicyManager->removeUserIdDeviceAffinities(userId);
+}
+
status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId)
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index d9fec9a..e297f34 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -235,6 +235,9 @@
virtual status_t getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device);
+ virtual status_t setUserIdDeviceAffinities(int userId, const Vector<AudioDeviceTypeAddr>& devices);
+
+ virtual status_t removeUserIdDeviceAffinities(int userId);
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,