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

Commit 0e128dd3 authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

audio: Fix handling of external devices disconnection

A mix port can be patched to multiple connected device ports. Thus, when
disconnecting an external device and removing the connected port, the
profiles of the mix port can only be cleared iff there are no more
connected device ports patched to it, and it did not have profiles prior to
connection of the first device.

Enhanced VTS tests to catch this problem in the HAL implementations. Also,
ensure that audio ports and audio routes do not change after the test
finishes. This ensures that tests can't affect each other.

Bug: 298175108
Test: atest audiosystem_tests
Test: atest VtsHalAudioCoreTargetTest
Change-Id: Ia666b874958fb260513fc2b8cd20a823953ec679
parent ecc3e1af
Loading
Loading
Loading
Loading
+25 −6
Original line number Diff line number Diff line
@@ -517,7 +517,7 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA

    connectedPort.id = ++getConfig().nextPortId;
    auto [connectedPortsIt, _] =
            mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::vector<int32_t>()));
            mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::set<int32_t>()));
    LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, "
               << "connected port ID " << connectedPort.id;
    ports.push_back(connectedPort);
@@ -550,9 +550,21 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
    // of all profiles from all routable dynamic device ports would be more involved.
    for (const auto mixPortId : routablePortIds) {
        auto portsIt = findById<AudioPort>(ports, mixPortId);
        if (portsIt != ports.end() && portsIt->profiles.empty()) {
        if (portsIt != ports.end()) {
            if (portsIt->profiles.empty()) {
                portsIt->profiles = connectedPort.profiles;
            connectedPortsIt->second.push_back(portsIt->id);
                connectedPortsIt->second.insert(portsIt->id);
            } else {
                // Check if profiles are non empty because they were populated by
                // a previous connection. Otherwise, it means that they are not empty because
                // the mix port has static profiles.
                for (const auto cp : mConnectedDevicePorts) {
                    if (cp.second.count(portsIt->id) > 0) {
                        connectedPortsIt->second.insert(portsIt->id);
                        break;
                    }
                }
            }
        }
    }
    *_aidl_return = std::move(connectedPort);
@@ -607,13 +619,20 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) {
        }
    }

    for (const auto mixPortId : connectedPortsIt->second) {
    // Clear profiles for mix ports that are not connected to any other ports.
    std::set<int32_t> mixPortsToClear = std::move(connectedPortsIt->second);
    mConnectedDevicePorts.erase(connectedPortsIt);
    for (const auto& connectedPort : mConnectedDevicePorts) {
        for (int32_t mixPortId : connectedPort.second) {
            mixPortsToClear.erase(mixPortId);
        }
    }
    for (int32_t mixPortId : mixPortsToClear) {
        auto mixPortIt = findById<AudioPort>(ports, mixPortId);
        if (mixPortIt != ports.end()) {
            mixPortIt->profiles = {};
        }
    }
    mConnectedDevicePorts.erase(connectedPortsIt);

    return ndk::ScopedAStatus::ok();
}
+1 −1
Original line number Diff line number Diff line
@@ -142,7 +142,7 @@ class Module : public BnModule {
    // ids of device ports created at runtime via 'connectExternalDevice'.
    // Also stores a list of ids of mix ports with dynamic profiles that were populated from
    // the connected port. This list can be empty, thus an int->int multimap can't be used.
    using ConnectedDevicePorts = std::map<int32_t, std::vector<int32_t>>;
    using ConnectedDevicePorts = std::map<int32_t, std::set<int32_t>>;
    // Maps port ids and port config ids to patch ids.
    // Multimap because both ports and configs can be used by multiple patches.
    using Patches = std::multimap<int32_t, int32_t>;
+4 −1
Original line number Diff line number Diff line
@@ -26,7 +26,10 @@ cc_defaults {
        "libaudioaidlcommon",
        "libaidlcommonsupport",
    ],
    header_libs: ["libaudioaidl_headers"],
    header_libs: [
        "libaudioaidl_headers",
        "libexpectedutils_headers",
    ],
    cflags: [
        "-Wall",
        "-Wextra",
+11 −18
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <aidl/android/media/audio/common/AudioInputFlags.h>
#include <aidl/android/media/audio/common/AudioIoFlags.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#include <error/expected_utils.h>

#include "ModuleConfig.h"

@@ -499,18 +500,13 @@ std::vector<AudioPortConfig> ModuleConfig::generateAudioDevicePortConfigs(
    return result;
}

const ndk::ScopedAStatus& ModuleConfig::onExternalDeviceConnected(IModule* module,
                                                                  const AudioPort& port) {
    // Update ports and routes
    mStatus = module->getAudioPorts(&mPorts);
    if (!mStatus.isOk()) return mStatus;
    mStatus = module->getAudioRoutes(&mRoutes);
    if (!mStatus.isOk()) return mStatus;
ndk::ScopedAStatus ModuleConfig::onExternalDeviceConnected(IModule* module, const AudioPort& port) {
    RETURN_STATUS_IF_ERROR(module->getAudioPorts(&mPorts));
    RETURN_STATUS_IF_ERROR(module->getAudioRoutes(&mRoutes));

    // Validate port is present in module
    if (std::find(mPorts.begin(), mPorts.end(), port) == mPorts.end()) {
        mStatus = ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
        return mStatus;
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }

    if (port.flags.getTag() == aidl::android::media::audio::common::AudioIoFlags::Tag::input) {
@@ -518,23 +514,20 @@ const ndk::ScopedAStatus& ModuleConfig::onExternalDeviceConnected(IModule* modul
    } else {
        mConnectedExternalSinkDevicePorts.insert(port.id);
    }
    return mStatus;
    return ndk::ScopedAStatus::ok();
}

const ndk::ScopedAStatus& ModuleConfig::onExternalDeviceDisconnected(IModule* module,
ndk::ScopedAStatus ModuleConfig::onExternalDeviceDisconnected(IModule* module,
                                                              const AudioPort& port) {
    // Update ports and routes
    mStatus = module->getAudioPorts(&mPorts);
    if (!mStatus.isOk()) return mStatus;
    mStatus = module->getAudioRoutes(&mRoutes);
    if (!mStatus.isOk()) return mStatus;
    RETURN_STATUS_IF_ERROR(module->getAudioPorts(&mPorts));
    RETURN_STATUS_IF_ERROR(module->getAudioRoutes(&mRoutes));

    if (port.flags.getTag() == aidl::android::media::audio::common::AudioIoFlags::Tag::input) {
        mConnectedExternalSourceDevicePorts.erase(port.id);
    } else {
        mConnectedExternalSinkDevicePorts.erase(port.id);
    }
    return mStatus;
    return ndk::ScopedAStatus::ok();
}

bool ModuleConfig::isMmapSupported() const {
+2 −2
Original line number Diff line number Diff line
@@ -157,10 +157,10 @@ class ModuleConfig {
        return *config.begin();
    }

    const ndk::ScopedAStatus& onExternalDeviceConnected(
    ndk::ScopedAStatus onExternalDeviceConnected(
            aidl::android::hardware::audio::core::IModule* module,
            const aidl::android::media::audio::common::AudioPort& port);
    const ndk::ScopedAStatus& onExternalDeviceDisconnected(
    ndk::ScopedAStatus onExternalDeviceDisconnected(
            aidl::android::hardware::audio::core::IModule* module,
            const aidl::android::media::audio::common::AudioPort& port);

Loading