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

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

audiopolicy: fix dynamic device & explicit routing management



While a playback is ongoing using explicit routing
and the device is disconnected, setOutputDevices() will bail out
if the new device is incompatible with the io profile. So the device
will not be reset in the output.
Moreover, if another device of the same type/address is connected,
this device will have a new unique ID.
If a track wants to use it as explicit, as the first playback/capture is still
on going, the function findPreferredDevice will return the previous device id.

This CL enforces:
-setOutputDevices() to check if new device is not compatible AND previous devices
are still available
- When a device is disconnected, go over all clients to clear any preferred device
referring to the device being removed.
- Sanitize preferred device received by get<Output|Input>ForAttr() according to
connected devices.

Test: AudioPolicyTests --gtest_filter=DynamicAddressOutputDevice/DeviceConnectionTest.DeviceConnectionState/*
Test: audio smoke tests
Test: CTS tests for AudioTrack, AudioRecord, AudioRouting

Change-Id: Ib3a2d54d902def0fcfc2a623c25bb92d65b1ab98
Signed-off-by: default avatarFrancois Gaffie <francois.gaffie@renault.com>
parent ffb2516c
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -143,6 +143,16 @@ public:

    void trackEffectEnabled(const sp<EffectDescriptor> &effect, bool enabled);

    /**
    * @brief clearSessionRoutesForDevice: when a device is disconnected, and if this device has
    * been chosen as the preferred device by any client, the policy manager shall
    * prevent from using this device any more by clearing all the session routes involving this
    * device.
    * In other words, the preferred device port id of these clients will be resetted to NONE.
    * @param disconnectedDevice device to be disconnected
    */
    void clearSessionRoutesForDevice(const sp<DeviceDescriptor> &disconnectedDevice);

    void dump(String8 *dst) const;
};

+10 −0
Original line number Diff line number Diff line
@@ -379,6 +379,16 @@ public:
                                      const sp<SwAudioOutputDescriptor>& desc,
                                      uint32_t inPastMs = 0, nsecs_t sysTime = 0) const;

    /**
     * @brief clearSessionRoutesForDevice: when a device is disconnected, and if this device has
     * been chosen as the preferred device by any client, the policy manager shall
     * prevent from using this device any more by clearing all the session routes involving this
     * device.
     * In other words, the preferred device port id of these clients will be resetted to NONE.
     * @param disconnectedDevice device to be disconnected
     */
    void clearSessionRoutesForDevice(const sp<DeviceDescriptor> &disconnectedDevice);

    /**
     * returns the A2DP output handle if it is open or 0 otherwise
     */
+13 −0
Original line number Diff line number Diff line
@@ -511,6 +511,19 @@ void AudioInputCollection::trackEffectEnabled(const sp<EffectDescriptor> &effect
    }
}

void AudioInputCollection::clearSessionRoutesForDevice(
    const sp<DeviceDescriptor> &disconnectedDevice)
{
    for (size_t i = 0; i < size(); i++) {
        sp<AudioInputDescriptor> inputDesc = valueAt(i);
        for (const auto& client : inputDesc->getClientIterable()) {
            if (client->preferredDeviceId() == disconnectedDevice->getId()) {
                client->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE);
            }
        }
    }
}

void AudioInputCollection::dump(String8 *dst) const
{
    dst->append("\nInputs dump:\n");
+13 −0
Original line number Diff line number Diff line
@@ -802,6 +802,19 @@ sp<SwAudioOutputDescriptor> SwAudioOutputCollection::getOutputForClient(audio_po
    return 0;
}

void SwAudioOutputCollection::clearSessionRoutesForDevice(
        const sp<DeviceDescriptor> &disconnectedDevice)
{
    for (size_t i = 0; i < size(); i++) {
        sp<AudioOutputDescriptor> outputDesc = valueAt(i);
        for (const auto& client : outputDesc->getClientIterable()) {
            if (client->preferredDeviceId() == disconnectedDevice->getId()) {
                client->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE);
            }
        }
    }
}

void SwAudioOutputCollection::dump(String8 *dst) const
{
    dst->append("\nOutputs dump:\n");
+22 −9
Original line number Diff line number Diff line
@@ -193,6 +193,8 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT
            // remove device from available output devices
            mAvailableOutputDevices.remove(device);

            mOutputs.clearSessionRoutesForDevice(device);

            checkOutputsForDevice(device, state, outputs);

            // Reset active device codec
@@ -929,8 +931,6 @@ status_t AudioPolicyManager::getOutputForAttrInt(
    const sp<DeviceDescriptor> requestedDevice =
        mAvailableOutputDevices.getDeviceFromId(requestedPortId);



    status_t status = getAudioAttributes(resultAttr, attr, *stream);
    if (status != NO_ERROR) {
        return status;
@@ -1045,6 +1045,14 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
    audio_attributes_t resultAttr;
    bool isRequestedDeviceForExclusiveUse = false;
    std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputDescs;
    const sp<DeviceDescriptor> requestedDevice =
      mAvailableOutputDevices.getDeviceFromId(requestedPortId);

    // Prevent from storing invalid requested device id in clients
    const audio_port_handle_t sanitizedRequestedPortId =
      requestedDevice != nullptr ? requestedPortId : AUDIO_PORT_HANDLE_NONE;
    *selectedDeviceId = sanitizedRequestedPortId;

    status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid,
            config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse,
            &secondaryOutputDescs);
@@ -1064,15 +1072,15 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,

    sp<TrackClientDescriptor> clientDesc =
        new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
                                  requestedPortId, *stream,
                                  sanitizedRequestedPortId, *stream,
                                  mEngine->getProductStrategyForAttributes(resultAttr),
                                  *flags, isRequestedDeviceForExclusiveUse,
                                  std::move(weakSecondaryOutputDescs));
    sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
    outputDesc->addClient(clientDesc);

    ALOGV("%s() returns output %d selectedDeviceId %d for port ID %d", __func__,
          *output, *selectedDeviceId, *portId);
    ALOGV("%s() returns output %d requestedPortId %d selectedDeviceId %d for port ID %d", __func__,
          *output, requestedPortId, *selectedDeviceId, *portId);

    return NO_ERROR;
}
@@ -1953,6 +1961,8 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,
        if (explicitRoutingDevice != nullptr) {
            device = explicitRoutingDevice;
        } else {
            // Prevent from storing invalid requested device id in clients
            requestedDeviceId = AUDIO_PORT_HANDLE_NONE;
            device = mEngine->getInputDeviceForAttributes(attributes, &policyMix);
        }
        if (device == nullptr) {
@@ -5295,16 +5305,17 @@ uint32_t AudioPolicyManager::setOutputDevices(const sp<SwAudioOutputDescriptor>&

    // filter devices according to output selected
    DeviceVector filteredDevices = outputDesc->filterSupportedDevices(devices);
    DeviceVector prevDevices = outputDesc->devices();

    // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
    // output profile
    if (!devices.isEmpty() && filteredDevices.isEmpty()) {
    // output profile or if new device is not supported AND previous device(s) is(are) still
    // available (otherwise reset device must be done on the output)
    if (!devices.isEmpty() && filteredDevices.isEmpty() &&
            !mAvailableOutputDevices.filter(prevDevices).empty()) {
        ALOGV("%s: unsupported device %s for output", __func__, devices.toString().c_str());
        return 0;
    }

    DeviceVector prevDevices = outputDesc->devices();

    ALOGV("setOutputDevices() prevDevice %s", prevDevices.toString().c_str());

    if (!filteredDevices.isEmpty()) {
@@ -5819,6 +5830,8 @@ void AudioPolicyManager::cleanUpForDevice(const sp<DeviceDescriptor>& deviceDesc
        }
    }

    mInputs.clearSessionRoutesForDevice(deviceDesc);

    mHwModules.cleanUpForDevice(deviceDesc);
}