From bda70da1f0fb32d37abd704ff8618e55661be0a9 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Wed, 19 Dec 2018 07:30:15 -0800 Subject: [PATCH] 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 --- media/libaudioclient/AudioPolicy.cpp | 44 ++++++++-- media/libaudioclient/AudioSystem.cpp | 13 +++ media/libaudioclient/IAudioPolicyService.cpp | 74 ++++++++++++++++- .../include/media/AudioPolicy.h | 17 +++- .../include/media/AudioSystem.h | 4 + .../include/media/IAudioPolicyService.h | 5 ++ services/audiopolicy/Android.mk | 1 + services/audiopolicy/AudioPolicyInterface.h | 4 + .../include/AudioPolicyMix.h | 4 + .../managerdefinitions/src/AudioPolicyMix.cpp | 81 +++++++++++++++++++ .../managerdefault/AudioPolicyManager.cpp | 53 ++++++++++++ .../managerdefault/AudioPolicyManager.h | 3 + .../service/AudioPolicyInterfaceImpl.cpp | 25 ++++++ .../audiopolicy/service/AudioPolicyService.h | 4 + 14 files changed, 325 insertions(+), 7 deletions(-) diff --git a/media/libaudioclient/AudioPolicy.cpp b/media/libaudioclient/AudioPolicy.cpp index d1f752587e..9601d6dd87 100644 --- a/media/libaudioclient/AudioPolicy.cpp +++ b/media/libaudioclient/AudioPolicy.cpp @@ -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 diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index dc7531ceb0..baeae8b709 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1236,6 +1236,19 @@ status_t AudioSystem::registerPolicyMixes(const Vector& mixes, bool re return aps->registerPolicyMixes(mixes, registration); } +status_t AudioSystem::setUidDeviceAffinities(uid_t uid, const Vector& devices) +{ + const sp& 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& 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) diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 0ce8b16f47..272415c59f 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -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& 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 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; } diff --git a/media/libaudioclient/include/media/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h index 8da0069ccb..96e1235e9b 100644 --- a/media/libaudioclient/include/media/AudioPolicy.h +++ b/media/libaudioclient/include/media/AudioPolicy.h @@ -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 mCriteria; + void excludeUid(uid_t uid) const; + + mutable Vector mCriteria; uint32_t mMixType; audio_config_t mFormat; uint32_t mRouteFlags; diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index b0da5b8e03..781e9dfc41 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -325,6 +325,10 @@ public: static status_t registerPolicyMixes(const Vector& mixes, bool registration); + static status_t setUidDeviceAffinities(uid_t uid, const Vector& 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); diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index 61f3b27d6f..fb4fe9302f 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -167,6 +167,11 @@ public: virtual status_t registerPolicyMixes(const Vector& mixes, bool registration) = 0; + virtual status_t setUidDeviceAffinities(uid_t uid, const Vector& 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; diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index bfa1b5e92f..d71aa15832 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -51,6 +51,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ liblog \ + libaudioclient \ libsoundtrigger ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1) diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index ad12a904a9..1c2b9d7c8b 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -210,6 +210,10 @@ public: virtual status_t registerPolicyMixes(const Vector& mixes) = 0; virtual status_t unregisterPolicyMixes(Vector mixes) = 0; + virtual status_t setUidDeviceAffinities(uid_t uid, const Vector& 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, diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 96c00ea7a7..955e87bd04 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -80,6 +80,10 @@ public: status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix); + status_t setUidDeviceAffinities(uid_t uid, const Vector& devices); + status_t removeUidDeviceAffinities(uid_t uid); + status_t getDevicesForUid(uid_t uid, Vector& devices) const; + void dump(String8 *dst) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 3cf80146e4..776d98f850 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -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& 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 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& 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"); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index aa205f0bae..baa5eb385b 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2674,6 +2674,59 @@ void AudioPolicyManager::dumpManualSurroundFormats(String8 *dst) const } } +status_t AudioPolicyManager::setUidDeviceAffinities(uid_t uid, + const Vector& 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 devDesc = mHwModules.getDeviceDescriptor( + devices[i].mType, devices[i].mAddress, String8()); + SortedVector 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 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 devDesc = mHwModules.getDeviceDescriptor( + devices[i].mType, devices[i].mAddress, String8()); + SortedVector 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); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 35dd87ca47..9eb1dcfaa6 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -218,6 +218,9 @@ public: virtual status_t registerPolicyMixes(const Vector& mixes); virtual status_t unregisterPolicyMixes(Vector mixes); + virtual status_t setUidDeviceAffinities(uid_t uid, + const Vector& devices); + virtual status_t removeUidDeviceAffinities(uid_t uid); virtual status_t startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 439764be89..80503fdaa1 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -1037,6 +1037,31 @@ status_t AudioPolicyService::registerPolicyMixes(const Vector& mixes, } } +status_t AudioPolicyService::setUidDeviceAffinities(uid_t uid, + const Vector& 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) diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index c44d816cd3..959e757229 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -199,6 +199,10 @@ public: virtual status_t registerPolicyMixes(const Vector& mixes, bool registration); + virtual status_t setUidDeviceAffinities(uid_t uid, const Vector& 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);