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

Commit ddd48982 authored by Shunkai Yao's avatar Shunkai Yao Committed by Gerrit Code Review
Browse files

Merge changes from topic "fix-b-273252382-connect-external-device"

* changes:
  audio: Clarify profiles management for external devices
  audio: Add some utility methods, improve logging
parents 73c82441 7b2d12b1
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -192,6 +192,19 @@ interface IModule {
     * device address is specified for a point-to-multipoint external device
     * connection.
     *
     * Since not all modules have a DSP that could perform sample rate and
     * format conversions, behavior related to mix port configurations may vary.
     * For modules with a DSP, mix ports can be pre-configured and have a fixed
     * set of audio profiles supported by the DSP. For modules without a DSP,
     * audio profiles of mix ports may change after connecting an external
     * device. The typical case is that the mix port has an empty set of
     * profiles when no external devices are connected, and after external
     * device connection it receives the same set of profiles as the device
     * ports that they can be routed to. The client will re-query current port
     * configurations using 'getAudioPorts'. All mix ports that can be routed to
     * the connected device port must have a non-empty set of audio profiles
     * after successful connection of an external device.
     *
     * Handling of a disconnect is done in a reverse order:
     *  1. Reset port configuration using the 'resetAudioPortConfig' method.
     *  2. Release the connected device port by calling the 'disconnectExternalDevice'
+6 −0
Original line number Diff line number Diff line
@@ -99,6 +99,12 @@ constexpr size_t getFrameSizeInBytes(
    return 0;
}

constexpr bool isDefaultAudioFormat(
        const ::aidl::android::media::audio::common::AudioFormatDescription& desc) {
    return desc.type == ::aidl::android::media::audio::common::AudioFormatType::DEFAULT &&
           desc.pcm == ::aidl::android::media::audio::common::PcmType::DEFAULT;
}

constexpr bool isTelephonyDeviceType(
        ::aidl::android::media::audio::common::AudioDeviceType device) {
    return device == ::aidl::android::media::audio::common::AudioDeviceType::IN_TELEPHONY_RX ||
+66 −20
Original line number Diff line number Diff line
@@ -143,6 +143,21 @@ StreamOut::CreateInstance Module::getStreamOutCreator(Type type) {
    }
}

std::ostream& operator<<(std::ostream& os, Module::Type t) {
    switch (t) {
        case Module::Type::DEFAULT:
            os << "default";
            break;
        case Module::Type::R_SUBMIX:
            os << "r_submix";
            break;
        case Module::Type::USB:
            os << "usb";
            break;
    }
    return os;
}

void Module::cleanUpPatch(int32_t patchId) {
    erase_all_values(mPatches, std::set<int32_t>{patchId});
}
@@ -352,16 +367,17 @@ void Module::updateStreamsConnectedState(const AudioPatch& oldPatch, const Audio

ndk::ScopedAStatus Module::setModuleDebug(
        const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) {
    LOG(DEBUG) << __func__ << ": old flags:" << mDebug.toString()
    LOG(DEBUG) << __func__ << ": " << mType << ": old flags:" << mDebug.toString()
               << ", new flags: " << in_debug.toString();
    if (mDebug.simulateDeviceConnections != in_debug.simulateDeviceConnections &&
        !mConnectedDevicePorts.empty()) {
        LOG(ERROR) << __func__ << ": attempting to change device connections simulation "
                   << "while having external devices connected";
        LOG(ERROR) << __func__ << ": " << mType
                   << ": attempting to change device connections simulation while having external "
                   << "devices connected";
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
    }
    if (in_debug.streamTransientStateDelayMs < 0) {
        LOG(ERROR) << __func__ << ": streamTransientStateDelayMs is negative: "
        LOG(ERROR) << __func__ << ": " << mType << ": streamTransientStateDelayMs is negative: "
                   << in_debug.streamTransientStateDelayMs;
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
@@ -440,38 +456,45 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
        LOG(DEBUG) << __func__ << ": device port " << connectedPort.id << " device set to "
                   << connectedDevicePort.device.toString();
        // Check if there is already a connected port with for the same external device.
        for (auto connectedPortId : mConnectedDevicePorts) {
            auto connectedPortIt = findById<AudioPort>(ports, connectedPortId);
        for (auto connectedPortPair : mConnectedDevicePorts) {
            auto connectedPortIt = findById<AudioPort>(ports, connectedPortPair.first);
            if (connectedPortIt->ext.get<AudioPortExt::Tag::device>().device ==
                connectedDevicePort.device) {
                LOG(ERROR) << __func__ << ": device " << connectedDevicePort.device.toString()
                           << " is already connected at the device port id " << connectedPortId;
                           << " is already connected at the device port id "
                           << connectedPortPair.first;
                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
            }
        }
    }

    if (!mDebug.simulateDeviceConnections) {
        // In a real HAL here we would attempt querying the profiles from the device.
        LOG(ERROR) << __func__ << ": failed to query supported device profiles";
        // TODO: Check the return value when it is ready for actual devices.
        populateConnectedDevicePort(&connectedPort);
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
        if (ndk::ScopedAStatus status = populateConnectedDevicePort(&connectedPort);
            !status.isOk()) {
            return status;
        }

    connectedPort.id = ++getConfig().nextPortId;
    mConnectedDevicePorts.insert(connectedPort.id);
    LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, "
               << "connected port ID " << connectedPort.id;
    } else {
        auto& connectedProfiles = getConfig().connectedProfiles;
        if (auto connectedProfilesIt = connectedProfiles.find(templateId);
            connectedProfilesIt != connectedProfiles.end()) {
            connectedPort.profiles = connectedProfilesIt->second;
        }
    }
    if (connectedPort.profiles.empty()) {
        LOG(ERROR) << "Profiles of a connected port still empty after connecting external device "
                   << connectedPort.toString();
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
    }

    connectedPort.id = ++getConfig().nextPortId;
    auto [connectedPortsIt, _] =
            mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::vector<int32_t>()));
    LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, "
               << "connected port ID " << connectedPort.id;
    ports.push_back(connectedPort);
    onExternalDeviceConnectionChanged(connectedPort, true /*connected*/);
    *_aidl_return = std::move(connectedPort);

    std::vector<int32_t> routablePortIds;
    std::vector<AudioRoute> newRoutes;
    auto& routes = getConfig().routes;
    for (auto& r : routes) {
@@ -481,15 +504,30 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
            newRoute.sinkPortId = connectedPort.id;
            newRoute.isExclusive = r.isExclusive;
            newRoutes.push_back(std::move(newRoute));
            routablePortIds.insert(routablePortIds.end(), r.sourcePortIds.begin(),
                                   r.sourcePortIds.end());
        } else {
            auto& srcs = r.sourcePortIds;
            if (std::find(srcs.begin(), srcs.end(), templateId) != srcs.end()) {
                srcs.push_back(connectedPort.id);
                routablePortIds.push_back(r.sinkPortId);
            }
        }
    }
    routes.insert(routes.end(), newRoutes.begin(), newRoutes.end());

    // Note: this is a simplistic approach assuming that a mix port can only be populated
    // from a single device port. Implementing support for stuffing dynamic profiles with a superset
    // 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()) {
            portsIt->profiles = connectedPort.profiles;
            connectedPortsIt->second.push_back(portsIt->id);
        }
    }
    *_aidl_return = std::move(connectedPort);

    return ndk::ScopedAStatus::ok();
}

@@ -504,7 +542,8 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) {
        LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a device port";
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
    if (mConnectedDevicePorts.count(in_portId) == 0) {
    auto connectedPortsIt = mConnectedDevicePorts.find(in_portId);
    if (connectedPortsIt == mConnectedDevicePorts.end()) {
        LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a connected device port";
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
@@ -525,7 +564,6 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) {
    }
    onExternalDeviceConnectionChanged(*portIt, false /*connected*/);
    ports.erase(portIt);
    mConnectedDevicePorts.erase(in_portId);
    LOG(DEBUG) << __func__ << ": connected device port " << in_portId << " released";

    auto& routes = getConfig().routes;
@@ -540,6 +578,14 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) {
        }
    }

    for (const auto mixPortId : connectedPortsIt->second) {
        auto mixPortIt = findById<AudioPort>(ports, mixPortId);
        if (mixPortIt != ports.end()) {
            mixPortIt->profiles = {};
        }
    }
    mConnectedDevicePorts.erase(connectedPortsIt);

    return ndk::ScopedAStatus::ok();
}

+2 −1
Original line number Diff line number Diff line
@@ -33,7 +33,8 @@ struct Configuration {
    std::vector<::aidl::android::media::audio::common::AudioPort> ports;
    std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs;
    std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs;
    // Port id -> List of profiles to use when the device port state is set to 'connected'.
    // Port id -> List of profiles to use when the device port state is set to 'connected'
    // in connection simulation mode.
    std::map<int32_t, std::vector<::aidl::android::media::audio::common::AudioProfile>>
            connectedProfiles;
    std::vector<AudioRoute> routes;
+4 −2
Original line number Diff line number Diff line
@@ -177,8 +177,10 @@ class Module : public BnModule {
    ChildInterface<IBluetooth> mBluetooth;
    ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
    ChildInterface<IBluetoothLe> mBluetoothLe;
    // ids of ports created at runtime via 'connectExternalDevice'.
    std::set<int32_t> mConnectedDevicePorts;
    // ids of device ports created at runtime via 'connectExternalDevice'.
    // Also stores ids of mix ports with dynamic profiles which got populated from the connected
    // port.
    std::map<int32_t, std::vector<int32_t>> mConnectedDevicePorts;
    Streams mStreams;
    // Maps port ids and port config ids to patch ids.
    // Multimap because both ports and configs can be used by multiple patches.
Loading