Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 873d10f7 authored by Oscar Azucena's avatar Oscar Azucena
Browse files

Disallowed explicit routing for user device affinities conflicts

If user id device affinities for a particular device
disallows playback for a user id, then the explicit audio
routing (#setPreferredDevice) is disallowed. Otherwise,
the explicity routing is allowed.

Bug: 237733218
Test: atest audiopolicy_tests
Change-Id: I823e7e59e86303f80808d4953b52efad5da8e779
parent 931e6959
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -203,9 +203,10 @@ bool AudioMix::hasUserIdRule(bool match, int userId) const {
    return false;
}

bool AudioMix::hasMatchUserIdRule() const {
bool AudioMix::hasUserIdRule(bool match) const {
    const uint32_t rule = match ? RULE_MATCH_USERID : RULE_EXCLUDE_USERID;
    for (size_t i = 0; i < mCriteria.size(); i++) {
        if (mCriteria[i].mRule == RULE_MATCH_USERID) {
        if (mCriteria[i].mRule == rule) {
            return true;
        }
    }
+12 −4
Original line number Diff line number Diff line
@@ -61,7 +61,10 @@ namespace android {
#define MIX_ROUTE_FLAG_LOOP_BACK (0x1 << 1)
/** Loop back some audio while it is rendered */
#define MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER (MIX_ROUTE_FLAG_RENDER | MIX_ROUTE_FLAG_LOOP_BACK)
#define MIX_ROUTE_FLAG_ALL (MIX_ROUTE_FLAG_RENDER | MIX_ROUTE_FLAG_LOOP_BACK)
/** Control if audio routing disallows preferred device routing **/
#define MIX_ROUTE_FLAG_DISALLOWS_PREFERRED_DEVICE (0x1 << 2)
#define MIX_ROUTE_FLAG_ALL (MIX_ROUTE_FLAG_RENDER | MIX_ROUTE_FLAG_LOOP_BACK | \
    MIX_ROUTE_FLAG_DISALLOWS_PREFERRED_DEVICE)

#define MAX_MIXES_PER_POLICY 10
#define MAX_CRITERIA_PER_MIX 20
@@ -112,9 +115,9 @@ public:
    void setMatchUserId(int userId);
    /** returns true if this mix has a rule to match or exclude the given userId */
    bool hasUserIdRule(bool match, int userId) const;
    /** returns true if this mix has a rule for userId match (any userId) */
    bool hasMatchUserIdRule() const;
    /** returns true if this mix can be used for uid-device affinity routing */
    /** returns true if this mix has a rule to match or exclude (any userId) */
    bool hasUserIdRule(bool match) const;
    /** returns true if this mix has a rule for userId exclude (any userId) */
    bool isDeviceAffinityCompatible() const;

    std::vector<AudioMixMatchCriterion> mCriteria;
@@ -147,6 +150,11 @@ static inline bool is_mix_loopback(uint32_t routeFlags) {
           == MIX_ROUTE_FLAG_LOOP_BACK;
}

static inline bool is_mix_disallows_preferred_device(uint32_t routeFlags) {
    return (routeFlags & MIX_ROUTE_FLAG_DISALLOWS_PREFERRED_DEVICE)
           == MIX_ROUTE_FLAG_DISALLOWS_PREFERRED_DEVICE;
}

}; // namespace android

#endif  // ANDROID_AUDIO_POLICY_H
+28 −5
Original line number Diff line number Diff line
@@ -62,11 +62,24 @@ public:
    void closeOutput(sp<SwAudioOutputDescriptor> &desc);

    /**
     * Try to find an output descriptor for the given attributes.
     * Tries to find the best matching audio policy mix
     *
     * @param[in] attributes to consider for the research of the audio policy mix.
     * @param[in] config to consider for the research of the audio policy mix.
     * @param[in] uid to consider for the research of the audio policy mix.
     * @param[in] session to consider for the research of the audio policy mix.
     * @param[in] flags to consider for the research of the audio policy mix.
     * @param[in] availableOutputDevices available output devices can be use during the research
     *      of the audio policy mix
     * @param[in] requestedDevice currently requested device that can be used determined if the
     *      matching audio policy mix should be used instead of the currently set preferred device.
     * @param[out] primaryMix to return in case a matching audio plicy mix could be found.
     * @param[out] secondaryMixes that audio should be routed to in case a matching
     *      secondary mixes could be found.
     * @param[out] usePrimaryOutputFromPolicyMixes to return in case the audio policy mix
     *      should be use, including the case where the requested device is explicitly disallowed
     *      by the audio policy.
     *
     * @param[in] attributes to consider fowr the research of output descriptor.
     * @param[out] desc to return if an primary output could be found.
     * @param[out] secondaryDesc other desc that the audio should be routed to.
     * @return OK if the request is valid
     *         otherwise if the request is not supported
     */
@@ -75,8 +88,11 @@ public:
                              uid_t uid,
                              audio_session_t session,
                              audio_output_flags_t flags,
                              const DeviceVector &availableOutputDevices,
                              const sp<DeviceDescriptor>& requestedDevice,
                              sp<AudioPolicyMix> &primaryMix,
                              std::vector<sp<AudioPolicyMix>> *secondaryMixes);
                              std::vector<sp<AudioPolicyMix>> *secondaryMixes,
                              bool& usePrimaryOutputFromPolicyMixes);

    sp<DeviceDescriptor> getDeviceAndMixForInputSource(const audio_attributes_t& attributes,
                                                       const DeviceVector &availableDeviceTypes,
@@ -132,6 +148,13 @@ private:
                            const audio_config_base_t& config,
                            uid_t uid,
                            audio_session_t session);
    bool mixDisallowsRequestedDevice(const AudioMix* mix,
                            const sp<DeviceDescriptor>& requestedDevice,
                            const sp<DeviceDescriptor>& mixDevice,
                            const uid_t uid);

    sp<DeviceDescriptor> getOutputDeviceForMix(const AudioMix* mix,
                            const DeviceVector& availableOutputDevices);
};

std::optional<std::string> extractAddressFromAudioAttributes(const audio_attributes_t& attr);
+53 −9
Original line number Diff line number Diff line
@@ -259,14 +259,25 @@ status_t AudioPolicyMixCollection::getOutputForAttr(
        const audio_attributes_t& attributes, const audio_config_base_t& config, const uid_t uid,
        const audio_session_t session,
        audio_output_flags_t flags,
        const DeviceVector &availableOutputDevices,
        const sp<DeviceDescriptor>& requestedDevice,
        sp<AudioPolicyMix> &primaryMix,
        std::vector<sp<AudioPolicyMix>> *secondaryMixes)
        std::vector<sp<AudioPolicyMix>> *secondaryMixes,
        bool& usePrimaryOutputFromPolicyMixes)
{
    ALOGV("getOutputForAttr() querying %zu mixes:", size());
    primaryMix.clear();
    bool mixesDisallowsRequestedDevice = false;
    for (size_t i = 0; i < size(); i++) {
        sp<AudioPolicyMix> policyMix = itemAt(i);
        const bool primaryOutputMix = !is_mix_loopback_render(policyMix->mRouteFlags);
        sp<DeviceDescriptor> mixDevice = getOutputDeviceForMix(policyMix.get(),
            availableOutputDevices);
        if (mixDisallowsRequestedDevice(policyMix.get(), requestedDevice, mixDevice, uid)) {
            ALOGV("%s: Mix %zu: does not allows device", __func__, i);
            mixesDisallowsRequestedDevice = true;
        }

        if (!primaryOutputMix && (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ)) {
            // AAudio does not support MMAP_NO_IRQ loopback render, and there is no way with
            // the current MmapStreamInterface::start to reject a specific client added to a shared
@@ -288,6 +299,11 @@ status_t AudioPolicyMixCollection::getOutputForAttr(
            continue; // skip the mix
        }

        if (mixDevice != nullptr && mixDevice->equals(requestedDevice)) {
            ALOGV("%s: Mix %zu: requested device mathches", __func__, i);
            mixesDisallowsRequestedDevice = false;
        }

        if (primaryOutputMix) {
            primaryMix = policyMix;
            ALOGV("%s: Mix %zu: set primary desc", __func__, i);
@@ -298,9 +314,36 @@ status_t AudioPolicyMixCollection::getOutputForAttr(
            }
        }
    }

    // Explicit routing is higher priority than dynamic policy primary output, but policy may
    // explicitly deny it
    usePrimaryOutputFromPolicyMixes =
        (mixesDisallowsRequestedDevice || requestedDevice == nullptr) && primaryMix != nullptr;

    return NO_ERROR;
}

sp<DeviceDescriptor> AudioPolicyMixCollection::getOutputDeviceForMix(const AudioMix* mix,
                                                    const DeviceVector& availableOutputDevices) {
    ALOGV("%s: device (0x%x, addr=%s) forced by mix", __func__, mix->mDeviceType,
        mix->mDeviceAddress.c_str());
    return availableOutputDevices.getDevice(mix->mDeviceType, mix->mDeviceAddress,
        AUDIO_FORMAT_DEFAULT);
}

bool AudioPolicyMixCollection::mixDisallowsRequestedDevice(const AudioMix* mix,
                                                     const sp<DeviceDescriptor>& requestedDevice,
                                                     const sp<DeviceDescriptor>& mixDevice,
                                                     const uid_t uid) {
    if (requestedDevice == nullptr || mixDevice == nullptr) {
        return false;
    }

    return is_mix_disallows_preferred_device(mix->mRouteFlags)
        && requestedDevice->equals(mixDevice)
        && mix->hasUserIdRule(false /* match */, multiuser_get_user_id(uid));
}

bool AudioPolicyMixCollection::mixMatch(const AudioMix* mix, size_t mixIndex,
    const audio_attributes_t& attributes, const audio_config_base_t& config,
    uid_t uid, audio_session_t session) {
@@ -361,11 +404,7 @@ sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForOutput(
    for (size_t i = 0; i < size(); i++) {
        if (itemAt(i)->getOutput() == output) {
            // This Desc is involved in a Mix, which has the highest prio
            audio_devices_t deviceType = itemAt(i)->mDeviceType;
            String8 address = itemAt(i)->mDeviceAddress;
            ALOGV("%s: device (0x%x, addr=%s) forced by mix",
                  __FUNCTION__, deviceType, address.c_str());
            return availableOutputDevices.getDevice(deviceType, address, AUDIO_FORMAT_DEFAULT);
            return getOutputDeviceForMix(itemAt(i).get(), availableOutputDevices);
        }
    }
    return nullptr;
@@ -568,13 +607,14 @@ status_t AudioPolicyMixCollection::setUserIdDeviceAffinities(int userId,
                break;
            }
        }
        if (!deviceMatch && !mix->hasMatchUserIdRule()) {
        if (!deviceMatch && !mix->hasUserIdRule(true /*match*/)) {
            // 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);
                mix->mRouteFlags = mix->mRouteFlags | MIX_ROUTE_FLAG_DISALLOWS_PREFERRED_DEVICE;
            }
        }
    }
@@ -595,6 +635,10 @@ status_t AudioPolicyMixCollection::removeUserIdDeviceAffinities(int userId) {
        EraseCriteriaIf(mix->mCriteria, [userId](const AudioMixMatchCriterion& c) {
            return c.mRule == RULE_EXCLUDE_USERID && c.mValue.mUserId == userId;
        });

        if (!mix->hasUserIdRule(false /* match */)) {
            mix->mRouteFlags = mix->mRouteFlags & ~MIX_ROUTE_FLAG_DISALLOWS_PREFERRED_DEVICE;
        }
    }
    return NO_ERROR;
}
+16 −8
Original line number Diff line number Diff line
@@ -1168,6 +1168,8 @@ status_t AudioPolicyManager::getOutputForAttrInt(
    ALOGV("%s() attributes=%s stream=%s session %d selectedDeviceId %d", __func__,
          toString(*resultAttr).c_str(), toString(*stream).c_str(), session, requestedPortId);

    bool usePrimaryOutputFromPolicyMixes = false;

    // The primary output is the explicit routing (eg. setPreferredDevice) if specified,
    //       otherwise, fallback to the dynamic policies, if none match, query the engine.
    // Secondary outputs are always found by dynamic policies as the engine do not support them
@@ -1177,14 +1179,12 @@ status_t AudioPolicyManager::getOutputForAttrInt(
        .format = config->format,
    };
    status = mPolicyMixes.getOutputForAttr(*resultAttr, clientConfig, uid, session, *flags,
                                           primaryMix, secondaryMixes);
                                           mAvailableOutputDevices, requestedDevice, primaryMix,
                                           secondaryMixes, usePrimaryOutputFromPolicyMixes);
    if (status != OK) {
        return status;
    }

    // Explicit routing is higher priority then any dynamic policy primary output
    bool usePrimaryOutputFromPolicyMixes = requestedDevice == nullptr && primaryMix != nullptr;

    // FIXME: in case of RENDER policy, the output capabilities should be checked
    if ((secondaryMixes != nullptr && !secondaryMixes->empty())
            && !audio_is_linear_pcm(config->format)) {
@@ -6730,6 +6730,7 @@ void AudioPolicyManager::checkOutputForAttributes(const audio_attributes_t &attr
    SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevices(newDevices, mOutputs);

    uint32_t maxLatency = 0;
    bool unneededUsePrimaryOutputFromPolicyMixes = false;
    std::vector<sp<SwAudioOutputDescriptor>> invalidatedOutputs;
    // take into account dynamic audio policies related changes: if a client is now associated
    // to a different policy mix than at creation time, invalidate corresponding stream
@@ -6744,7 +6745,9 @@ void AudioPolicyManager::checkOutputForAttributes(const audio_attributes_t &attr
            }
            sp<AudioPolicyMix> primaryMix;
            status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->config(),
                    client->uid(), client->session(), client->flags(), primaryMix, nullptr);
                    client->uid(), client->session(), client->flags(), mAvailableOutputDevices,
                    nullptr /* requestedDevice */, primaryMix, nullptr /* secondaryMixes */,
                    unneededUsePrimaryOutputFromPolicyMixes);
            if (status != OK) {
                continue;
            }
@@ -6842,13 +6845,16 @@ void AudioPolicyManager::checkOutputForAllStrategies()
void AudioPolicyManager::checkSecondaryOutputs() {
    PortHandleVector clientsToInvalidate;
    TrackSecondaryOutputsMap trackSecondaryOutputs;
    bool unneededUsePrimaryOutputFromPolicyMixes = false;
    for (size_t i = 0; i < mOutputs.size(); i++) {
        const sp<SwAudioOutputDescriptor>& outputDescriptor = mOutputs[i];
        for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
            sp<AudioPolicyMix> primaryMix;
            std::vector<sp<AudioPolicyMix>> secondaryMixes;
            status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->config(),
                    client->uid(), client->session(), client->flags(), primaryMix, &secondaryMixes);
                    client->uid(), client->session(), client->flags(), mAvailableOutputDevices,
                    nullptr /* requestedDevice */, primaryMix, &secondaryMixes,
                    unneededUsePrimaryOutputFromPolicyMixes);
            std::vector<sp<SwAudioOutputDescriptor>> secondaryDescs;
            for (auto &secondaryMix : secondaryMixes) {
                sp<SwAudioOutputDescriptor> outputDesc = secondaryMix->getOutput();
@@ -8282,9 +8288,11 @@ status_t AudioPolicyManager::getDevicesForAttributes(
    // check dynamic policies but only for primary descriptors (secondary not used for audible
    // audio routing, only used for duplication for playback capture)
    sp<AudioPolicyMix> policyMix;
    bool unneededUsePrimaryOutputFromPolicyMixes = false;
    status_t status = mPolicyMixes.getOutputForAttr(attr, AUDIO_CONFIG_BASE_INITIALIZER,
            0 /*uid unknown here*/, AUDIO_SESSION_NONE, AUDIO_OUTPUT_FLAG_NONE, policyMix,
            nullptr /* secondaryMixes */);
            0 /*uid unknown here*/, AUDIO_SESSION_NONE, AUDIO_OUTPUT_FLAG_NONE,
            mAvailableOutputDevices, nullptr /* requestedDevice */, policyMix,
            nullptr /* secondaryMixes */, unneededUsePrimaryOutputFromPolicyMixes);
    if (status != OK) {
        return status;
    }
Loading