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

Commit b2e5cb5a authored by Francois Gaffie's avatar Francois Gaffie Committed by Eric Laurent
Browse files

[BUG] AudioPolicyManager: prevent patch leak on SwOutput used for bridges



When using either SwBridge or HwBridge, a SwOutput is always involved.
SwBridge: for playback, HwBridge: for volume control (it hosts the
Source Client).

When the bridge ends, the SwOutput shall be rerouted (Sw) or
unrouted (Hw bridge to prevent patch leak).

This CL generalizes the ClientDescriptor usage for all audio patches.
For patches created from APM::createAudioPatch API or call patches witch
are not associated to an active client, it creates an internal client descriptor.
Sink device is expressed as a preferredDevices. Then APM forbids to change
it using UID enforcement in getNewOutputDevices.

It alos allows to track client hence volume sources active on a SwOutput,
even for HwBridge. Waiting for cleaner HwAudioOutputDescriptor, we will use
the mandatory SwOutput declared in configuration file for a given sink device
for HwBridge to attach the Audio Source and control activity, thus volume.
This SwOutput will be routed (aka setOutputDevices to be called) and unrouted
if not needed anymore.

Bug: 187173302

Test: build & audio UC involving bridges
(functional on emulator):
adb shell ./data/AudioPolicyEmulatorTests --gtest_filter=*AudioPatchTest*
adb shell ./data/AudioPolicyEmulatorTests --gtest_filter=*AudioSourceTest*

Signed-off-by: default avatarFrancois Gaffie <francois.gaffie@renault.com>
Change-Id: Ib4dc2ae32a89464bd8ac3b96833f324760012799
parent f68caf6e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -305,6 +305,7 @@ public:
    {
        return !devices().isEmpty() ? devices().itemAt(0)->hasGainController() : false;
    }
    bool isRouted() const { return mPatchHandle != AUDIO_PATCH_HANDLE_NONE; }

    DeviceVector mDevices; /**< current devices this output is routed to */
    wp<AudioPolicyMix> mPolicyMix;  // non NULL when used by a dynamic policy
+47 −2
Original line number Diff line number Diff line
@@ -56,7 +56,13 @@ public:

    virtual void dump(String8 *dst, int spaces) const;
    virtual std::string toShortString() const;

    /**
     * @brief isInternal
     * @return true if the client corresponds to an audio patch created from createAudioPatch API or
     * for call audio routing, or false if the client corresponds to an AudioTrack, AudioRecord or
     * HW Audio Source.
     */
    virtual bool isInternal() const { return false; }
    audio_port_handle_t portId() const { return mPortId; }
    uid_t uid() const { return mUid; }
    audio_session_t session() const { return mSessionId; };
@@ -69,8 +75,16 @@ public:
    bool isPreferredDeviceForExclusiveUse() const { return mPreferredDeviceForExclusiveUse; }
    virtual void setActive(bool active) { mActive = active; }
    bool active() const { return mActive; }
    /**
     * @brief hasPreferredDevice Note that as internal clients use preferred device for convenience,
     * we do hide this internal behavior to prevent from regression (like invalidating track for
     * clients following same strategies...)
     * @param activeOnly
     * @return
     */
    bool hasPreferredDevice(bool activeOnly = false) const {
        return mPreferredDeviceId != AUDIO_PORT_HANDLE_NONE && (!activeOnly || mActive);
        return !isInternal() &&
                mPreferredDeviceId != AUDIO_PORT_HANDLE_NONE && (!activeOnly || mActive);
    }

private:
@@ -211,6 +225,8 @@ public:
        mPatchHandle = AUDIO_PATCH_HANDLE_NONE;
        mSinkDevice = nullptr;
    }
    void setUseSwBridge() { mUseSwBridge = true; }
    bool useSwBridge() const { return mUseSwBridge; }
    bool isConnected() const { return mPatchHandle != AUDIO_PATCH_HANDLE_NONE; }
    audio_patch_handle_t getPatchHandle() const { return mPatchHandle; }
    sp<DeviceDescriptor> srcDevice() const { return mSrcDevice; }
@@ -229,6 +245,35 @@ public:
    sp<DeviceDescriptor> mSinkDevice;
    wp<SwAudioOutputDescriptor> mSwOutput;
    wp<HwAudioOutputDescriptor> mHwOutput;
    bool mUseSwBridge = false;
};

/**
 * @brief The InternalSourceClientDescriptor class
 * Specialized Client Descriptor for either a raw patch created from @see createAudioPatch API
 * or for internal audio patches managed by APM (e.g. phone call patches).
 * Whatever the bridge created (software or hardware), we need a client to track the activity
 * and manage volumes.
 * The Audio Patch requested sink is expressed as a preferred device which allows to route
 * the SwOutput. Then APM will performs checks on the UID (against UID of Audioserver) of the
 * requester to prevent rerouting SwOutput involved in raw patches.
 */
class InternalSourceClientDescriptor: public SourceClientDescriptor
{
public:
    InternalSourceClientDescriptor(
            audio_port_handle_t portId, uid_t uid, audio_attributes_t attributes,
            const struct audio_port_config &config, const sp<DeviceDescriptor>& srcDevice,
             const sp<DeviceDescriptor>& sinkDevice,
            product_strategy_t strategy, VolumeSource volumeSource) :
        SourceClientDescriptor(
            portId, uid, attributes, config, srcDevice, AUDIO_STREAM_PATCH, strategy,
            volumeSource)
    {
        setPreferredDeviceId(sinkDevice->getId());
    }
    bool isInternal() const override { return true; }
    ~InternalSourceClientDescriptor() override = default;
};

class SourceClientCollection :
+2 −1
Original line number Diff line number Diff line
@@ -100,7 +100,8 @@ SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t
    TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes,
        {config.sample_rate, config.channel_mask, config.format}, AUDIO_PORT_HANDLE_NONE,
        stream, strategy, volumeSource, AUDIO_OUTPUT_FLAG_NONE, false,
        {} /* Sources do not support secondary outputs*/, nullptr), mSrcDevice(srcDevice)
        {} /* Sources do not support secondary outputs*/, nullptr),
    mSrcDevice(srcDevice)
{
}