[Feature sync] Fix issue where multiple SRV records with same host are not fully resolved

For Matter, a device may have a single host with multiple A/AAAA
records, but multiple PTR/SRV pairs that represent different
operational identities for the same device.

Currently, MdnsResponseDecoder looks for only a single matching
MdnsResponse by hostname when looping through the A/AAAA records
(which will leave the second response incomplete). Updated with a
flag that will allow returning all MdnsResponses which match the
given hostname and update the Inet records for all responses.

Bug: 254155029
Test: atest FrameworksNetTests
      Verified with a Matter device with two identities that the
      second response is incomplete without this change.
      Additionally, added unit tests to verify the before and
      after behavior with a real packet capture from the device.
Change-Id: Ic4a101b305c039f6cd7fd557246a2bfdaf0c781a
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsConfigs.java b/service/mdns/com/android/server/connectivity/mdns/MdnsConfigs.java
index 41abba7..75c7e6c 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsConfigs.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsConfigs.java
@@ -101,4 +101,8 @@
     public static boolean allowNetworkInterfaceIndexPropagation() {
         return true;
     }
+
+    public static boolean allowMultipleSrvRecordsPerHost() {
+        return true;
+    }
 }
\ No newline at end of file
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java b/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
index 57b241e..e73de55 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
@@ -25,6 +25,7 @@
 import java.io.EOFException;
 import java.io.IOException;
 import java.net.DatagramPacket;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
@@ -37,6 +38,8 @@
     public static final int SUCCESS = 0;
     private static final String TAG = "MdnsResponseDecoder";
     private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
+    private final boolean allowMultipleSrvRecordsPerHost =
+            MdnsConfigs.allowMultipleSrvRecordsPerHost();
     private final String[] serviceType;
     private final Clock clock;
 
@@ -281,13 +284,18 @@
         for (MdnsRecord record : records) {
             if (record instanceof MdnsInetAddressRecord) {
                 MdnsInetAddressRecord inetRecord = (MdnsInetAddressRecord) record;
-                MdnsResponse response = findResponseWithHostName(responses, inetRecord.getName());
-                if (inetRecord.getInet4Address() != null && response != null) {
-                    response.setInet4AddressRecord(inetRecord);
-                    response.setInterfaceIndex(interfaceIndex);
-                } else if (inetRecord.getInet6Address() != null && response != null) {
-                    response.setInet6AddressRecord(inetRecord);
-                    response.setInterfaceIndex(interfaceIndex);
+                if (allowMultipleSrvRecordsPerHost) {
+                    List<MdnsResponse> matchingResponses =
+                            findResponsesWithHostName(responses, inetRecord.getName());
+                    for (MdnsResponse response : matchingResponses) {
+                        assignInetRecord(response, inetRecord, interfaceIndex);
+                    }
+                } else {
+                    MdnsResponse response =
+                            findResponseWithHostName(responses, inetRecord.getName());
+                    if (response != null) {
+                        assignInetRecord(response, inetRecord, interfaceIndex);
+                    }
                 }
             }
         }
@@ -295,6 +303,39 @@
         return SUCCESS;
     }
 
+    private static void assignInetRecord(
+            MdnsResponse response, MdnsInetAddressRecord inetRecord, int interfaceIndex) {
+        if (inetRecord.getInet4Address() != null) {
+            response.setInet4AddressRecord(inetRecord);
+            response.setInterfaceIndex(interfaceIndex);
+        } else if (inetRecord.getInet6Address() != null) {
+            response.setInet6AddressRecord(inetRecord);
+            response.setInterfaceIndex(interfaceIndex);
+        }
+    }
+
+    private static List<MdnsResponse> findResponsesWithHostName(
+            @Nullable List<MdnsResponse> responses, String[] hostName) {
+        if (responses == null || responses.isEmpty()) {
+            return List.of();
+        }
+
+        List<MdnsResponse> result = null;
+        for (MdnsResponse response : responses) {
+            MdnsServiceRecord serviceRecord = response.getServiceRecord();
+            if (serviceRecord == null) {
+                continue;
+            }
+            if (Arrays.equals(serviceRecord.getServiceHost(), hostName)) {
+                if (result == null) {
+                    result = new ArrayList<>(/* initialCapacity= */ responses.size());
+                }
+                result.add(response);
+            }
+        }
+        return result == null ? List.of() : result;
+    }
+
     public static class Clock {
         public long elapsedRealtime() {
             return SystemClock.elapsedRealtime();
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
index 8d0ace5..02e00c2 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
@@ -26,11 +26,14 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 
+import android.net.InetAddresses;
+
 import com.android.net.module.util.HexDump;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -106,9 +109,49 @@
             + "63616C0000018001000000780004C0A8010A000001800100000078"
             + "0004C0A8010A00000000000000");
 
+    // Expected to contain two SRV records which point to the same hostname.
+    private static final byte[] matterDuplicateHostname = HexDump.hexStringToByteArray(
+            "00008000000000080000000A095F7365727669636573075F646E732D73"
+            + "64045F756470056C6F63616C00000C000100000078000F075F6D61"
+            + "74746572045F746370C023C00C000C000100000078001A125F4943"
+            + "324639453337374632454139463430045F737562C034C034000C00"
+            + "0100000078002421433246394533373746324541394634302D3030"
+            + "3030303030304534443041334641C034C04F000C00010000007800"
+            + "02C075C00C000C0001000000780002C034C00C000C000100000078"
+            + "0015125F4941413035363731333439334135343144C062C034000C"
+            + "000100000078002421414130353637313334393341353431442D30"
+            + "303030303030304331324446303344C034C0C1000C000100000078"
+            + "0002C0E2C075002100010000007800150000000015A40C33433631"
+            + "3035304338394638C023C07500100001000011940015084352493D"
+            + "35303030074352413D33303003543D31C126001C00010000007800"
+            + "10FE800000000000003E6105FFFE0C89F8C126001C000100000078"
+            + "00102605A601A84657003E6105FFFE0C89F8C12600010001000000"
+            + "780004C0A8018AC0E2002100010000007800080000000015A4C126"
+            + "C0E200100001000011940015084352493D35303030074352413D33"
+            + "303003543D31C126001C0001000000780010FE800000000000003E"
+            + "6105FFFE0C89F8C126001C00010000007800102605A601A8465700"
+            + "3E6105FFFE0C89F8C12600010001000000780004C0A8018A313035"
+            + "304338394638C02300010001000000780004C0A8018AC0A0001000"
+            + "0100001194003A0E56503D36353532312B3332373639084352493D"
+            + "35303030074352413D33303003543D3106443D3236353704434D3D"
+            + "320550483D33360350493D21433246394533373746324541394634"
+            + "302D30303030303030304534443041334641C0F700210001000000"
+            + "7800150000000015A40C334336313035304338394638C023214332"
+            + "46394533373746324541394634302D303030303030303045344430"
+            + "41334641C0F700100001000011940015084352493D353030300743"
+            + "52413D33303003543D310C334336313035304338394638C023001C"
+            + "0001000000780010FE800000000000003E6105FFFE0C89F80C3343"
+            + "36313035304338394638C023001C00010000007800102605A601A8"
+            + "4657003E6105FFFE0C89F80C334336313035304338394638C02300"
+            + "010001000000780004C0A8018A0000000000000000000000000000"
+            + "000000");
+
     private static final String CAST_SERVICE_NAME = "_googlecast";
     private static final String[] CAST_SERVICE_TYPE =
             new String[] {CAST_SERVICE_NAME, "_tcp", "local"};
+    private static final String MATTER_SERVICE_NAME = "_matter";
+    private static final String[] MATTER_SERVICE_TYPE =
+            new String[] {MATTER_SERVICE_NAME, "_tcp", "local"};
 
     private final List<MdnsResponse> responses = new LinkedList<>();
 
@@ -249,4 +292,62 @@
         assertEquals(responses.size(), 1);
         assertEquals(responses.get(0).getInterfaceIndex(), 10);
     }
+
+    @Test
+    public void decode_singleHostname_multipleSrvRecords_flagEnabled_multipleCompleteResponses() {
+        //MdnsScannerConfigsFlagsImpl.allowMultipleSrvRecordsPerHost.override(true);
+        MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, MATTER_SERVICE_TYPE);
+        assertNotNull(matterDuplicateHostname);
+
+        DatagramPacket packet =
+                new DatagramPacket(matterDuplicateHostname, matterDuplicateHostname.length);
+
+        packet.setSocketAddress(
+                new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
+
+        responses.clear();
+        int errorCode = decoder.decode(packet, responses, /* interfaceIndex= */ 0);
+        assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
+
+        // This should emit two records:
+        assertEquals(2, responses.size());
+
+        MdnsResponse response1 = responses.get(0);
+        MdnsResponse response2 = responses.get(0);
+
+        // Both of which are complete:
+        assertTrue(response1.isComplete());
+        assertTrue(response2.isComplete());
+
+        // And should both have the same IPv6 address:
+        assertEquals(InetAddresses.parseNumericAddress("2605:a601:a846:5700:3e61:5ff:fe0c:89f8"),
+                response1.getInet6AddressRecord().getInet6Address());
+        assertEquals(InetAddresses.parseNumericAddress("2605:a601:a846:5700:3e61:5ff:fe0c:89f8"),
+                response2.getInet6AddressRecord().getInet6Address());
+    }
+
+    @Test
+    @Ignore("MdnsConfigs is not configurable currently.")
+    public void decode_singleHostname_multipleSrvRecords_flagDisabled_singleCompleteResponse() {
+        //MdnsScannerConfigsFlagsImpl.allowMultipleSrvRecordsPerHost.override(false);
+        MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, MATTER_SERVICE_TYPE);
+        assertNotNull(matterDuplicateHostname);
+
+        DatagramPacket packet =
+                new DatagramPacket(matterDuplicateHostname, matterDuplicateHostname.length);
+
+        packet.setSocketAddress(
+                new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
+
+        responses.clear();
+        int errorCode = decoder.decode(packet, responses, /* interfaceIndex= */ 0);
+        assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
+
+        // This should emit only two records:
+        assertEquals(2, responses.size());
+
+        // But only the first is complete:
+        assertTrue(responses.get(0).isComplete());
+        assertFalse(responses.get(1).isComplete());
+    }
 }
\ No newline at end of file