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

Commit 10009fad authored by Mikhail Naganov's avatar Mikhail Naganov Committed by Gerrit Code Review
Browse files

Merge "AIDL: Release patches on external device disconnection" into main

parents 555e6b88 a317a80c
Loading
Loading
Loading
Loading
+32 −5
Original line number Diff line number Diff line
@@ -589,7 +589,6 @@ status_t DeviceHalAidl::createAudioPatch(unsigned int num_sources,
    // that the HAL module uses `int32_t` for patch IDs. The following assert ensures
    // that both the framework and the HAL use the same value for "no ID":
    static_assert(AUDIO_PATCH_HANDLE_NONE == 0);
    int32_t aidlPatchId = static_cast<int32_t>(*patch);

    // Upon conversion, mix port configs contain audio configuration, while
    // device port configs contain device address. This data is used to find
@@ -611,12 +610,28 @@ status_t DeviceHalAidl::createAudioPatch(unsigned int num_sources,
                        ::aidl::android::legacy2aidl_audio_port_config_AudioPortConfig(
                                sinks[i], isInput, 0)));
    }
    int32_t aidlPatchId = static_cast<int32_t>(*patch);
    Hal2AidlMapper::Cleanups cleanups(mMapperAccessor);
    {
        std::lock_guard l(mLock);
        // Check for patches that only exist for the framework, or have different HAL patch ID.
        if (int32_t aidlHalPatchId = mMapper.findFwkPatch(aidlPatchId); aidlHalPatchId != 0) {
            if (aidlHalPatchId == aidlPatchId) {
                // This patch was previously released by the HAL. Thus we need to pass '0'
                // to the HAL to obtain a new patch.
                int32_t newAidlPatchId = 0;
                RETURN_STATUS_IF_ERROR(mMapper.createOrUpdatePatch(
                                aidlSources, aidlSinks, &newAidlPatchId, &cleanups));
                mMapper.updateFwkPatch(aidlPatchId, newAidlPatchId);
            } else {
                RETURN_STATUS_IF_ERROR(mMapper.createOrUpdatePatch(
                                aidlSources, aidlSinks, &aidlHalPatchId, &cleanups));
            }
        } else {
            RETURN_STATUS_IF_ERROR(mMapper.createOrUpdatePatch(
                            aidlSources, aidlSinks, &aidlPatchId, &cleanups));
        }
    }
    *patch = static_cast<audio_patch_handle_t>(aidlPatchId);
    cleanups.disarmAll();
    return OK;
@@ -631,7 +646,19 @@ status_t DeviceHalAidl::releaseAudioPatch(audio_patch_handle_t patch) {
        return BAD_VALUE;
    }
    std::lock_guard l(mLock);
    RETURN_STATUS_IF_ERROR(mMapper.releaseAudioPatch(static_cast<int32_t>(patch)));
    // Check for patches that only exist for the framework, or have different HAL patch ID.
    int32_t aidlPatchId = static_cast<int32_t>(patch);
    if (int32_t aidlHalPatchId = mMapper.findFwkPatch(aidlPatchId); aidlHalPatchId != 0) {
        if (aidlHalPatchId == aidlPatchId) {
            // This patch was previously released by the HAL, just need to finish its removal.
            mMapper.eraseFwkPatch(aidlPatchId);
            return OK;
        } else {
            // This patch has a HAL patch ID which is different
            aidlPatchId = aidlHalPatchId;
        }
    }
    RETURN_STATUS_IF_ERROR(mMapper.releaseAudioPatch(aidlPatchId));
    return OK;
}

@@ -988,7 +1015,7 @@ status_t DeviceHalAidl::setSimulateDeviceConnections(bool enabled) {
    if (mModule == nullptr) return NO_INIT;
    {
        std::lock_guard l(mLock);
        mMapper.resetUnusedPatchesPortConfigsAndPorts();
        mMapper.resetUnusedPatchesAndPortConfigs();
    }
    ModuleDebug debug{ .simulateDeviceConnections = enabled };
    status_t status = statusTFromBinderStatus(mModule->setModuleDebug(debug));
+70 −61
Original line number Diff line number Diff line
@@ -102,8 +102,8 @@ Hal2AidlMapper::Hal2AidlMapper(const std::string& instance, const std::shared_pt
}

void Hal2AidlMapper::addStream(
        const sp<StreamHalInterface>& stream, int32_t portConfigId, int32_t patchId) {
    mStreams.insert(std::pair(stream, std::pair(portConfigId, patchId)));
        const sp<StreamHalInterface>& stream, int32_t mixPortConfigId, int32_t patchId) {
    mStreams.insert(std::pair(stream, std::pair(mixPortConfigId, patchId)));
}

bool Hal2AidlMapper::audioDeviceMatches(const AudioDevice& device, const AudioPort& p) {
@@ -698,20 +698,23 @@ status_t Hal2AidlMapper::initialize() {
    return OK;
}

bool Hal2AidlMapper::isPortBeingHeld(int32_t portId) {
    // It is assumed that mStreams has already been cleaned up.
    for (const auto& s : mStreams) {
        if (portConfigBelongsToPort(s.second.first, portId)) return true;
    }
    for (const auto& [_, patch] : mPatches) {
std::set<int32_t> Hal2AidlMapper::getPatchIdsByPortId(int32_t portId) {
    std::set<int32_t> result;
    for (const auto& [patchId, patch] : mPatches) {
        for (int32_t id : patch.sourcePortConfigIds) {
            if (portConfigBelongsToPort(id, portId)) return true;
            if (portConfigBelongsToPort(id, portId)) {
                result.insert(patchId);
                break;
            }
        }
        for (int32_t id : patch.sinkPortConfigIds) {
            if (portConfigBelongsToPort(id, portId)) return true;
            if (portConfigBelongsToPort(id, portId)) {
                result.insert(patchId);
                break;
            }
        }
    return false;
    }
    return result;
}

status_t Hal2AidlMapper::prepareToDisconnectExternalDevice(const AudioPort& devicePort) {
@@ -730,7 +733,7 @@ status_t Hal2AidlMapper::prepareToOpenStream(
            this, __func__, ioHandle, device.toString().c_str(),
            flags.toString().c_str(), toString(source).c_str(),
            config->toString().c_str(), mixPortConfig->toString().c_str());
    resetUnusedPatchesPortConfigsAndPorts();
    resetUnusedPatchesAndPortConfigs();
    const AudioConfig initialConfig = *config;
    // Find / create AudioPortConfigs for the device port and the mix port,
    // then find / create a patch between them, and open a stream on the mix port.
@@ -843,39 +846,52 @@ status_t Hal2AidlMapper::releaseAudioPatch(int32_t patchId) {
    return releaseAudioPatches({patchId});
}

status_t Hal2AidlMapper::releaseAudioPatches(const std::set<int32_t>& patchIds) {
    status_t result = OK;
    for (const auto patchId : patchIds) {
        if (auto it = mPatches.find(patchId); it != mPatches.end()) {
            mPatches.erase(it);
// Note: does not reset port configs.
status_t Hal2AidlMapper::releaseAudioPatch(Patches::iterator it) {
    const int32_t patchId = it->first;
    if (ndk::ScopedAStatus status = mModule->resetAudioPatch(patchId); !status.isOk()) {
        ALOGE("%s: error while resetting patch %d: %s",
                __func__, patchId, status.getDescription().c_str());
                result = statusTFromBinderStatus(status);
        return statusTFromBinderStatus(status);
    }
    mPatches.erase(it);
    for (auto it = mFwkPatches.begin(); it != mFwkPatches.end(); ++it) {
        if (it->second == patchId) {
            mFwkPatches.erase(it);
            break;
        }
    }
    return OK;
}

status_t Hal2AidlMapper::releaseAudioPatches(const std::set<int32_t>& patchIds) {
    status_t result = OK;
    for (const auto patchId : patchIds) {
        if (auto it = mPatches.find(patchId); it != mPatches.end()) {
            releaseAudioPatch(it);
        } else {
            ALOGE("%s: patch id %d not found", __func__, patchId);
            result = BAD_VALUE;
        }
    }
    resetUnusedPortConfigsAndPorts();
    resetUnusedPortConfigs();
    return result;
}

void Hal2AidlMapper::resetPortConfig(int32_t portConfigId) {
    if (auto it = mPortConfigs.find(portConfigId); it != mPortConfigs.end()) {
        mPortConfigs.erase(it);
        if (ndk::ScopedAStatus status = mModule->resetAudioPortConfig(portConfigId);
                !status.isOk()) {
            ALOGE("%s: error while resetting port config %d: %s",
                    __func__, portConfigId, status.getDescription().c_str());
        }
        mPortConfigs.erase(it);
        return;
    }
    ALOGE("%s: port config id %d not found", __func__, portConfigId);
}

void Hal2AidlMapper::resetUnusedPatchesPortConfigsAndPorts() {
void Hal2AidlMapper::resetUnusedPatchesAndPortConfigs() {
    // Since patches can be created independently of streams via 'createOrUpdatePatch',
    // here we only clean up patches for released streams.
    std::set<int32_t> patchesToRelease;
@@ -889,52 +905,35 @@ void Hal2AidlMapper::resetUnusedPatchesPortConfigsAndPorts() {
            it = mStreams.erase(it);
        }
    }
    // 'releaseAudioPatches' also resets unused port configs and ports.
    // 'releaseAudioPatches' also resets unused port configs.
    releaseAudioPatches(patchesToRelease);
}

void Hal2AidlMapper::resetUnusedPortConfigsAndPorts() {
void Hal2AidlMapper::resetUnusedPortConfigs() {
    // The assumption is that port configs are used to create patches
    // (or to open streams, but that involves creation of patches, too). Thus,
    // orphaned port configs can and should be reset.
    std::map<int32_t, int32_t /*portID*/> portConfigIds;
    std::set<int32_t> portConfigIdsToReset;
    std::transform(mPortConfigs.begin(), mPortConfigs.end(),
            std::inserter(portConfigIds, portConfigIds.end()),
            [](const auto& pcPair) { return std::make_pair(pcPair.first, pcPair.second.portId); });
            std::inserter(portConfigIdsToReset, portConfigIdsToReset.end()),
            [](const auto& pcPair) { return pcPair.first; });
    for (const auto& p : mPatches) {
        for (int32_t id : p.second.sourcePortConfigIds) portConfigIds.erase(id);
        for (int32_t id : p.second.sinkPortConfigIds) portConfigIds.erase(id);
        for (int32_t id : p.second.sourcePortConfigIds) portConfigIdsToReset.erase(id);
        for (int32_t id : p.second.sinkPortConfigIds) portConfigIdsToReset.erase(id);
    }
    for (int32_t id : mInitialPortConfigIds) {
        portConfigIds.erase(id);
        portConfigIdsToReset.erase(id);
    }
    for (const auto& s : mStreams) {
        portConfigIds.erase(s.second.first);
    }
    std::set<int32_t> retryDeviceDisconnection;
    for (const auto& portConfigAndIdPair : portConfigIds) {
        resetPortConfig(portConfigAndIdPair.first);
        if (const auto it = mConnectedPorts.find(portConfigAndIdPair.second);
                it != mConnectedPorts.end() && it->second) {
            retryDeviceDisconnection.insert(portConfigAndIdPair.second);
        }
    }
    for (int32_t portId : retryDeviceDisconnection) {
        if (!isPortBeingHeld(portId)) {
            if (auto status = mModule->disconnectExternalDevice(portId); status.isOk()) {
                eraseConnectedPort(portId);
                ALOGD("%s: executed postponed external device disconnection for port ID %d",
                        __func__, portId);
            }
        portConfigIdsToReset.erase(s.second.first);
    }
    }
    if (!retryDeviceDisconnection.empty()) {
        updateRoutes();
    for (const auto& portConfigId : portConfigIdsToReset) {
        resetPortConfig(portConfigId);
    }
}

status_t Hal2AidlMapper::setDevicePortConnectedState(const AudioPort& devicePort, bool connected) {
    resetUnusedPatchesPortConfigsAndPorts();
    resetUnusedPatchesAndPortConfigs();
    if (connected) {
        AudioDevice matchDevice = devicePort.ext.get<AudioPortExt::device>().device;
        std::optional<AudioPort> templatePort;
@@ -980,7 +979,7 @@ status_t Hal2AidlMapper::setDevicePortConnectedState(const AudioPort& devicePort
                "%s: module %s, duplicate port ID received from HAL: %s, existing port: %s",
                __func__, mInstance.c_str(), connectedPort.toString().c_str(),
                it->second.toString().c_str());
        mConnectedPorts[connectedPort.id] = false;
        mConnectedPorts.insert(connectedPort.id);
        if (erasePortAfterConnectionIt != mPorts.end()) {
            mPorts.erase(erasePortAfterConnectionIt);
        }
@@ -1007,17 +1006,27 @@ status_t Hal2AidlMapper::setDevicePortConnectedState(const AudioPort& devicePort
            port.ext.get<AudioPortExt::Tag::device>().device = matchDevice;
            port.profiles = portsIt->second.profiles;
        }
        // Streams are closed by AudioFlinger independently from device disconnections.
        // It is possible that the stream has not been closed yet.
        if (!isPortBeingHeld(portId)) {
            RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
                            mModule->disconnectExternalDevice(portId)));
            eraseConnectedPort(portId);
        } else {
            ALOGD("%s: since device port ID %d is used by a stream, "
                    "external device disconnection postponed", __func__, portId);
            mConnectedPorts[portId] = true;

        // Patches may still exist, the framework may reset or update them later.
        // For disconnection to succeed, need to release these patches first.
        if (std::set<int32_t> patchIdsToRelease = getPatchIdsByPortId(portId);
                !patchIdsToRelease.empty()) {
            FwkPatches releasedPatches;
            status_t status = OK;
            for (int32_t patchId : patchIdsToRelease) {
                if (auto it = mPatches.find(patchId); it != mPatches.end()) {
                    if (status = releaseAudioPatch(it); status != OK) break;
                    releasedPatches.insert(std::make_pair(patchId, patchId));
                }
            }
            resetUnusedPortConfigs();
            mFwkPatches.merge(releasedPatches);
            LOG_ALWAYS_FATAL_IF(!releasedPatches.empty(),
                    "mFwkPatches already contains some of released patches");
            if (status != OK) return status;
        }
        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->disconnectExternalDevice(portId)));
        eraseConnectedPort(portId);
    }
    return updateRoutes();
}
+30 −9
Original line number Diff line number Diff line
@@ -49,7 +49,7 @@ class Hal2AidlMapper {
            const std::string& instance,
            const std::shared_ptr<::aidl::android::hardware::audio::core::IModule>& module);

    void addStream(const sp<StreamHalInterface>& stream, int32_t portConfigId, int32_t patchId);
    void addStream(const sp<StreamHalInterface>& stream, int32_t mixPortConfigId, int32_t patchId);
    status_t createOrUpdatePatch(
            const std::vector<::aidl::android::media::audio::common::AudioPortConfig>& sources,
            const std::vector<::aidl::android::media::audio::common::AudioPortConfig>& sinks,
@@ -91,13 +91,32 @@ class Hal2AidlMapper {
        ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
        Cleanups* cleanups = nullptr);
    status_t releaseAudioPatch(int32_t patchId);
    void resetUnusedPatchesPortConfigsAndPorts();
    void resetUnusedPatchesAndPortConfigs();
    status_t setDevicePortConnectedState(
            const ::aidl::android::media::audio::common::AudioPort& devicePort, bool connected);

    // Methods to work with FwkPatches.
    void eraseFwkPatch(int32_t fwkPatchId) { mFwkPatches.erase(fwkPatchId); }
    int32_t findFwkPatch(int32_t fwkPatchId) {
        const auto it = mFwkPatches.find(fwkPatchId);
        return it != mFwkPatches.end() ? it->second : 0;
    }
    void updateFwkPatch(int32_t fwkPatchId, int32_t halPatchId) {
        mFwkPatches[fwkPatchId] = halPatchId;
    }

  private:
    // IDs of ports for connected external devices, and whether they are held by streams.
    using ConnectedPorts = std::map<int32_t /*port ID*/, bool>;
    // 'FwkPatches' is used to store patches that diverge from the framework's state.
    // Uses framework patch ID (aka audio_patch_handle_t) values for indexing.
    // When the 'key == value', that means Hal2AidlMapper has removed this patch, and it is absent
    // from 'mPatches', but it still "exists" for the framework. It will either remove it or
    // re-patch. If the framework re-patches, it will continue to use the same patch handle,
    // but the HAL will use the new one (since the old patch was reset), thus 'key != value'
    // for such patches. Since they "exist" both for the framework and the HAL, 'mPatches'
    // contains their data under HAL patch ID ('value' of 'FwkPatches').
    // To avoid confusion, all patchIDs used by Hal2AidlMapper are HAL IDs. Mapping between
    // framework patch IDs and HAL patch IDs is done by DeviceHalAidl.
    using FwkPatches = std::map<int32_t /*audio_patch_handle_t*/, int32_t /*patch ID*/>;
    using Patches = std::map<int32_t /*patch ID*/,
            ::aidl::android::hardware::audio::core::AudioPatch>;
    using PortConfigs = std::map<int32_t /*port config ID*/,
@@ -107,12 +126,12 @@ class Hal2AidlMapper {
    // Answers the question "whether portID 'first' is reachable from portID 'second'?"
    // It's not a map because both portIDs are known. The matrix is symmetric.
    using RoutingMatrix = std::set<std::pair<int32_t, int32_t>>;
    // There is always a port config ID set. The patch ID is set after stream
    // There is always a mix port config ID set. The patch ID is set after stream
    // creation, and can be set to '-1' later if the framework happens to create
    // a patch between the same endpoints. In that case, the ownership of the patch
    // is on the framework.
    using Streams = std::map<wp<StreamHalInterface>,
            std::pair<int32_t /*port config ID*/, int32_t /*patch ID*/>>;
            std::pair<int32_t /*mix port config ID*/, int32_t /*patch ID*/>>;

    const std::string mInstance;
    const std::shared_ptr<::aidl::android::hardware::audio::core::IModule> mModule;
@@ -168,7 +187,7 @@ class Hal2AidlMapper {
            const std::optional<::aidl::android::media::audio::common::AudioConfig>& config,
            const std::optional<::aidl::android::media::audio::common::AudioIoFlags>& flags,
            int32_t ioHandle);
    bool isPortBeingHeld(int32_t portId);
    std::set<int32_t> getPatchIdsByPortId(int32_t portId);
    status_t prepareToOpenStreamHelper(
        int32_t ioHandle, int32_t devicePortId, int32_t devicePortConfigId,
        const ::aidl::android::media::audio::common::AudioIoFlags& flags,
@@ -181,10 +200,11 @@ class Hal2AidlMapper {
        auto it = mPortConfigs.find(portConfigId);
        return it != mPortConfigs.end() && it->second.portId == portId;
    }
    status_t releaseAudioPatch(Patches::iterator it);
    status_t releaseAudioPatches(const std::set<int32_t>& patchIds);
    void resetPatch(int32_t patchId) { (void)releaseAudioPatch(patchId); }
    void resetPortConfig(int32_t portConfigId);
    void resetUnusedPortConfigsAndPorts();
    void resetUnusedPortConfigs();
    status_t updateAudioPort(
            int32_t portId, ::aidl::android::media::audio::common::AudioPort* port);
    status_t updateRoutes();
@@ -197,13 +217,14 @@ class Hal2AidlMapper {
    std::optional<::aidl::android::media::audio::common::AudioPort> mRemoteSubmixOut;
    int32_t mDefaultInputPortId = -1;
    int32_t mDefaultOutputPortId = -1;
    FwkPatches mFwkPatches;
    PortConfigs mPortConfigs;
    std::set<int32_t> mInitialPortConfigIds;
    Patches mPatches;
    Routes mRoutes;
    RoutingMatrix mRoutingMatrix;
    Streams mStreams;
    ConnectedPorts mConnectedPorts;
    std::set<int32_t> mConnectedPorts;
    std::pair<int32_t, ::aidl::android::media::audio::common::AudioPort>
            mDisconnectedPortReplacement;
    std::set<int32_t> mDynamicMixPortIds;