Audio : Add 3 VTS test cases for remote submix module

1. OutputDoesNotBlockWhenNoInput
2. OutputDoesNotBlockWhenInputStuck
3. OutputAndInput

Bug: 286914845
Test: atest AudioModuleRemoteSubmix

Change-Id: I19a08bf2bf39131a70a867280c758b5ef001c024
diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp
index 7213034..8fdb155 100644
--- a/audio/aidl/vts/ModuleConfig.cpp
+++ b/audio/aidl/vts/ModuleConfig.cpp
@@ -66,15 +66,36 @@
     return {};
 }
 
+std::vector<aidl::android::media::audio::common::AudioPort>
+ModuleConfig::getAudioPortsForDeviceTypes(const std::vector<AudioDeviceType>& deviceTypes,
+                                          const std::string& connection) {
+    return getAudioPortsForDeviceTypes(mPorts, deviceTypes, connection);
+}
+
 // static
 std::vector<aidl::android::media::audio::common::AudioPort> ModuleConfig::getBuiltInMicPorts(
         const std::vector<aidl::android::media::audio::common::AudioPort>& ports) {
+    return getAudioPortsForDeviceTypes(
+            ports, std::vector<AudioDeviceType>{AudioDeviceType::IN_MICROPHONE,
+                                                AudioDeviceType::IN_MICROPHONE_BACK});
+}
+
+std::vector<aidl::android::media::audio::common::AudioPort>
+ModuleConfig::getAudioPortsForDeviceTypes(
+        const std::vector<aidl::android::media::audio::common::AudioPort>& ports,
+        const std::vector<AudioDeviceType>& deviceTypes, const std::string& connection) {
     std::vector<AudioPort> result;
-    std::copy_if(ports.begin(), ports.end(), std::back_inserter(result), [](const auto& port) {
-        const auto type = port.ext.template get<AudioPortExt::Tag::device>().device.type;
-        return type.connection.empty() && (type.type == AudioDeviceType::IN_MICROPHONE ||
-                                           type.type == AudioDeviceType::IN_MICROPHONE_BACK);
-    });
+    for (const auto& port : ports) {
+        if (port.ext.getTag() != AudioPortExt::Tag::device) continue;
+        const auto type = port.ext.get<AudioPortExt::Tag::device>().device.type;
+        if (type.connection == connection) {
+            for (auto deviceType : deviceTypes) {
+                if (type.type == deviceType) {
+                    result.push_back(port);
+                }
+            }
+        }
+    }
     return result;
 }
 
@@ -119,6 +140,31 @@
     return result;
 }
 
+std::vector<AudioPort> ModuleConfig::getConnectedExternalDevicePorts() const {
+    std::vector<AudioPort> result;
+    std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
+        return mConnectedExternalSinkDevicePorts.count(port.id) != 0 ||
+               mConnectedExternalSourceDevicePorts.count(port.id) != 0;
+    });
+    return result;
+}
+
+std::set<int32_t> ModuleConfig::getConnectedSinkDevicePorts() const {
+    std::set<int32_t> result;
+    result.insert(mAttachedSinkDevicePorts.begin(), mAttachedSinkDevicePorts.end());
+    result.insert(mConnectedExternalSinkDevicePorts.begin(),
+                  mConnectedExternalSinkDevicePorts.end());
+    return result;
+}
+
+std::set<int32_t> ModuleConfig::getConnectedSourceDevicePorts() const {
+    std::set<int32_t> result;
+    result.insert(mAttachedSourceDevicePorts.begin(), mAttachedSourceDevicePorts.end());
+    result.insert(mConnectedExternalSourceDevicePorts.begin(),
+                  mConnectedExternalSourceDevicePorts.end());
+    return result;
+}
+
 std::vector<AudioPort> ModuleConfig::getExternalDevicePorts() const {
     std::vector<AudioPort> result;
     std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result),
@@ -126,76 +172,77 @@
     return result;
 }
 
-std::vector<AudioPort> ModuleConfig::getInputMixPorts(bool attachedOnly) const {
+std::vector<AudioPort> ModuleConfig::getInputMixPorts(bool connectedOnly) const {
     std::vector<AudioPort> result;
     std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
         return port.ext.getTag() == AudioPortExt::Tag::mix &&
                port.flags.getTag() == AudioIoFlags::Tag::input &&
-               (!attachedOnly || !getAttachedSourceDevicesPortsForMixPort(port).empty());
+               (!connectedOnly || !getConnectedSourceDevicesPortsForMixPort(port).empty());
     });
     return result;
 }
 
-std::vector<AudioPort> ModuleConfig::getOutputMixPorts(bool attachedOnly) const {
+std::vector<AudioPort> ModuleConfig::getOutputMixPorts(bool connectedOnly) const {
     std::vector<AudioPort> result;
     std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
         return port.ext.getTag() == AudioPortExt::Tag::mix &&
                port.flags.getTag() == AudioIoFlags::Tag::output &&
-               (!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty());
+               (!connectedOnly || !getConnectedSinkDevicesPortsForMixPort(port).empty());
     });
     return result;
 }
 
-std::vector<AudioPort> ModuleConfig::getNonBlockingMixPorts(bool attachedOnly,
+std::vector<AudioPort> ModuleConfig::getNonBlockingMixPorts(bool connectedOnly,
                                                             bool singlePort) const {
-    return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
+    return findMixPorts(false /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) {
         return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
                                     AudioOutputFlags::NON_BLOCKING);
     });
 }
 
-std::vector<AudioPort> ModuleConfig::getOffloadMixPorts(bool attachedOnly, bool singlePort) const {
-    return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
+std::vector<AudioPort> ModuleConfig::getOffloadMixPorts(bool connectedOnly, bool singlePort) const {
+    return findMixPorts(false /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) {
         return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
                                     AudioOutputFlags::COMPRESS_OFFLOAD);
     });
 }
 
-std::vector<AudioPort> ModuleConfig::getPrimaryMixPorts(bool attachedOnly, bool singlePort) const {
-    return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
+std::vector<AudioPort> ModuleConfig::getPrimaryMixPorts(bool connectedOnly, bool singlePort) const {
+    return findMixPorts(false /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) {
         return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
                                     AudioOutputFlags::PRIMARY);
     });
 }
 
-std::vector<AudioPort> ModuleConfig::getMmapOutMixPorts(bool attachedOnly, bool singlePort) const {
-    return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
+std::vector<AudioPort> ModuleConfig::getMmapOutMixPorts(bool connectedOnly, bool singlePort) const {
+    return findMixPorts(false /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) {
         return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
                                     AudioOutputFlags::MMAP_NOIRQ);
     });
 }
 
-std::vector<AudioPort> ModuleConfig::getMmapInMixPorts(bool attachedOnly, bool singlePort) const {
-    return findMixPorts(true /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
+std::vector<AudioPort> ModuleConfig::getMmapInMixPorts(bool connectedOnly, bool singlePort) const {
+    return findMixPorts(true /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) {
         return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::input>(),
                                     AudioInputFlags::MMAP_NOIRQ);
     });
 }
 
-std::vector<AudioPort> ModuleConfig::getAttachedDevicesPortsForMixPort(
+std::vector<AudioPort> ModuleConfig::getConnectedDevicesPortsForMixPort(
         bool isInput, const AudioPortConfig& mixPortConfig) const {
     const auto mixPortIt = findById<AudioPort>(mPorts, mixPortConfig.portId);
     if (mixPortIt != mPorts.end()) {
-        return getAttachedDevicesPortsForMixPort(isInput, *mixPortIt);
+        return getConnectedDevicesPortsForMixPort(isInput, *mixPortIt);
     }
     return {};
 }
 
-std::vector<AudioPort> ModuleConfig::getAttachedSinkDevicesPortsForMixPort(
+std::vector<AudioPort> ModuleConfig::getConnectedSinkDevicesPortsForMixPort(
         const AudioPort& mixPort) const {
     std::vector<AudioPort> result;
+    std::set<int32_t> connectedSinkDevicePorts = getConnectedSinkDevicePorts();
     for (const auto& route : mRoutes) {
-        if (mAttachedSinkDevicePorts.count(route.sinkPortId) != 0 &&
+        if ((connectedSinkDevicePorts.count(route.sinkPortId) != 0) &&
             std::find(route.sourcePortIds.begin(), route.sourcePortIds.end(), mixPort.id) !=
                     route.sourcePortIds.end()) {
             const auto devicePortIt = findById<AudioPort>(mPorts, route.sinkPortId);
@@ -205,13 +252,14 @@
     return result;
 }
 
-std::vector<AudioPort> ModuleConfig::getAttachedSourceDevicesPortsForMixPort(
+std::vector<AudioPort> ModuleConfig::getConnectedSourceDevicesPortsForMixPort(
         const AudioPort& mixPort) const {
     std::vector<AudioPort> result;
+    std::set<int32_t> connectedSourceDevicePorts = getConnectedSourceDevicePorts();
     for (const auto& route : mRoutes) {
         if (route.sinkPortId == mixPort.id) {
             for (const auto srcId : route.sourcePortIds) {
-                if (mAttachedSourceDevicePorts.count(srcId) != 0) {
+                if (connectedSourceDevicePorts.count(srcId) != 0) {
                     const auto devicePortIt = findById<AudioPort>(mPorts, srcId);
                     if (devicePortIt != mPorts.end()) result.push_back(*devicePortIt);
                 }
@@ -221,9 +269,10 @@
     return result;
 }
 
-std::optional<AudioPort> ModuleConfig::getSourceMixPortForAttachedDevice() const {
+std::optional<AudioPort> ModuleConfig::getSourceMixPortForConnectedDevice() const {
+    std::set<int32_t> connectedSinkDevicePorts = getConnectedSinkDevicePorts();
     for (const auto& route : mRoutes) {
-        if (mAttachedSinkDevicePorts.count(route.sinkPortId) != 0) {
+        if (connectedSinkDevicePorts.count(route.sinkPortId) != 0) {
             const auto mixPortIt = findById<AudioPort>(mPorts, route.sourcePortIds[0]);
             if (mixPortIt != mPorts.end()) return *mixPortIt;
         }
@@ -233,7 +282,7 @@
 
 std::optional<ModuleConfig::SrcSinkPair> ModuleConfig::getNonRoutableSrcSinkPair(
         bool isInput) const {
-    const auto mixPorts = getMixPorts(isInput, false /*attachedOnly*/);
+    const auto mixPorts = getMixPorts(isInput, false /*connectedOnly*/);
     std::set<std::pair<int32_t, int32_t>> allowedRoutes;
     for (const auto& route : mRoutes) {
         for (const auto srcPortId : route.sourcePortIds) {
@@ -243,7 +292,8 @@
     auto make_pair = [isInput](auto& device, auto& mix) {
         return isInput ? std::make_pair(device, mix) : std::make_pair(mix, device);
     };
-    for (const auto portId : isInput ? mAttachedSourceDevicePorts : mAttachedSinkDevicePorts) {
+    for (const auto portId :
+         isInput ? getConnectedSourceDevicePorts() : getConnectedSinkDevicePorts()) {
         const auto devicePortIt = findById<AudioPort>(mPorts, portId);
         if (devicePortIt == mPorts.end()) continue;
         auto devicePortConfig = getSingleConfigForDevicePort(*devicePortIt);
@@ -262,10 +312,11 @@
 
 std::optional<ModuleConfig::SrcSinkPair> ModuleConfig::getRoutableSrcSinkPair(bool isInput) const {
     if (isInput) {
+        std::set<int32_t> connectedSourceDevicePorts = getConnectedSourceDevicePorts();
         for (const auto& route : mRoutes) {
             auto srcPortIdIt = std::find_if(
                     route.sourcePortIds.begin(), route.sourcePortIds.end(),
-                    [&](const auto& portId) { return mAttachedSourceDevicePorts.count(portId); });
+                    [&](const auto& portId) { return connectedSourceDevicePorts.count(portId); });
             if (srcPortIdIt == route.sourcePortIds.end()) continue;
             const auto devicePortIt = findById<AudioPort>(mPorts, *srcPortIdIt);
             const auto mixPortIt = findById<AudioPort>(mPorts, route.sinkPortId);
@@ -276,8 +327,9 @@
             return std::make_pair(devicePortConfig, mixPortConfig.value());
         }
     } else {
+        std::set<int32_t> connectedSinkDevicePorts = getConnectedSinkDevicePorts();
         for (const auto& route : mRoutes) {
-            if (mAttachedSinkDevicePorts.count(route.sinkPortId) == 0) continue;
+            if (connectedSinkDevicePorts.count(route.sinkPortId) == 0) continue;
             const auto mixPortIt = findById<AudioPort>(mPorts, route.sourcePortIds[0]);
             const auto devicePortIt = findById<AudioPort>(mPorts, route.sinkPortId);
             if (devicePortIt == mPorts.end() || mixPortIt == mPorts.end()) continue;
@@ -293,11 +345,12 @@
 std::vector<ModuleConfig::SrcSinkGroup> ModuleConfig::getRoutableSrcSinkGroups(bool isInput) const {
     std::vector<SrcSinkGroup> result;
     if (isInput) {
+        std::set<int32_t> connectedSourceDevicePorts = getConnectedSourceDevicePorts();
         for (const auto& route : mRoutes) {
             std::vector<int32_t> srcPortIds;
             std::copy_if(route.sourcePortIds.begin(), route.sourcePortIds.end(),
                          std::back_inserter(srcPortIds), [&](const auto& portId) {
-                             return mAttachedSourceDevicePorts.count(portId);
+                             return connectedSourceDevicePorts.count(portId);
                          });
             if (srcPortIds.empty()) continue;
             const auto mixPortIt = findById<AudioPort>(mPorts, route.sinkPortId);
@@ -317,8 +370,9 @@
             }
         }
     } else {
+        std::set<int32_t> connectedSinkDevicePorts = getConnectedSinkDevicePorts();
         for (const auto& route : mRoutes) {
-            if (mAttachedSinkDevicePorts.count(route.sinkPortId) == 0) continue;
+            if (connectedSinkDevicePorts.count(route.sinkPortId) == 0) continue;
             const auto devicePortIt = findById<AudioPort>(mPorts, route.sinkPortId);
             if (devicePortIt == mPorts.end()) continue;
             auto devicePortConfig = getSingleConfigForDevicePort(*devicePortIt);
@@ -352,6 +406,8 @@
     result.append(android::internal::ToString(mAttachedSourceDevicePorts));
     result.append("\nExternal device ports: ");
     result.append(android::internal::ToString(mExternalDevicePorts));
+    result.append("\nConnected external device ports: ");
+    result.append(android::internal::ToString(getConnectedExternalDevicePorts()));
     result.append("\nRoutes: ");
     result.append(android::internal::ToString(mRoutes));
     return result;
@@ -384,10 +440,10 @@
 }
 
 std::vector<AudioPort> ModuleConfig::findMixPorts(
-        bool isInput, bool attachedOnly, bool singlePort,
+        bool isInput, bool connectedOnly, bool singlePort,
         const std::function<bool(const AudioPort&)>& pred) const {
     std::vector<AudioPort> result;
-    const auto mixPorts = getMixPorts(isInput, attachedOnly);
+    const auto mixPorts = getMixPorts(isInput, connectedOnly);
     for (auto mixPortIt = mixPorts.begin(); mixPortIt != mixPorts.end();) {
         mixPortIt = std::find_if(mixPortIt, mixPorts.end(), pred);
         if (mixPortIt == mixPorts.end()) break;
@@ -401,7 +457,7 @@
         const std::vector<AudioPort>& ports, bool isInput, bool singleProfile) const {
     std::vector<AudioPortConfig> result;
     for (const auto& mixPort : ports) {
-        if (getAttachedDevicesPortsForMixPort(isInput, mixPort).empty()) {
+        if (getConnectedDevicesPortsForMixPort(isInput, mixPort).empty()) {
             continue;
         }
         for (const auto& profile : mixPort.profiles) {
@@ -443,10 +499,48 @@
     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;
+
+    // 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;
+    }
+
+    if (port.flags.getTag() == aidl::android::media::audio::common::AudioIoFlags::Tag::input) {
+        mConnectedExternalSourceDevicePorts.insert(port.id);
+    } else {
+        mConnectedExternalSinkDevicePorts.insert(port.id);
+    }
+    return mStatus;
+}
+
+const 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;
+
+    if (port.flags.getTag() == aidl::android::media::audio::common::AudioIoFlags::Tag::input) {
+        mConnectedExternalSourceDevicePorts.erase(port.id);
+    } else {
+        mConnectedExternalSinkDevicePorts.erase(port.id);
+    }
+    return mStatus;
+}
+
 bool ModuleConfig::isMmapSupported() const {
     const std::vector<AudioPort> mmapOutMixPorts =
-            getMmapOutMixPorts(false /*attachedOnly*/, false /*singlePort*/);
+            getMmapOutMixPorts(false /*connectedOnly*/, false /*singlePort*/);
     const std::vector<AudioPort> mmapInMixPorts =
-            getMmapInMixPorts(false /*attachedOnly*/, false /*singlePort*/);
+            getMmapInMixPorts(false /*connectedOnly*/, false /*singlePort*/);
     return !mmapOutMixPorts.empty() || !mmapInMixPorts.empty();
 }