Only send resolve callbacks for subscribed service

Resolve callbacks should only be sent for the service name that was
requested.

Bug: 267570781
Test: atest MdnsServiceTypeClientTests
Change-Id: I3d8eb5c06bcf54312e20a39b8575555d40528bca
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
index f87804b..5298aef 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -174,6 +174,7 @@
             this.searchOptions = searchOptions;
             if (listeners.put(listener, searchOptions) == null) {
                 for (MdnsResponse existingResponse : instanceNameToResponse.values()) {
+                    if (!responseMatchesOptions(existingResponse, searchOptions)) continue;
                     final MdnsServiceInfo info =
                             buildMdnsServiceInfoFromResponse(existingResponse, serviceTypeLabels);
                     listener.onServiceNameDiscovered(info);
@@ -199,6 +200,13 @@
         }
     }
 
+    private boolean responseMatchesOptions(@NonNull MdnsResponse response,
+            @NonNull MdnsSearchOptions options) {
+        if (options.getResolveInstanceName() == null) return true;
+        // DNS is case-insensitive, so ignore case in the comparison
+        return options.getResolveInstanceName().equalsIgnoreCase(response.getServiceInstanceName());
+    }
+
     /**
      * Unregisters {@code listener} from receiving discovery event of mDNS service instances.
      *
@@ -274,6 +282,7 @@
                 buildMdnsServiceInfoFromResponse(response, serviceTypeLabels);
 
         for (int i = 0; i < listeners.size(); i++) {
+            if (!responseMatchesOptions(response, listeners.valueAt(i))) continue;
             final MdnsServiceBrowserListener listener = listeners.keyAt(i);
             if (newServiceFound) {
                 listener.onServiceNameDiscovered(serviceInfo);
@@ -295,6 +304,7 @@
             return;
         }
         for (int i = 0; i < listeners.size(); i++) {
+            if (!responseMatchesOptions(response, listeners.valueAt(i))) continue;
             final MdnsServiceBrowserListener listener = listeners.keyAt(i);
             final MdnsServiceInfo serviceInfo =
                     buildMdnsServiceInfoFromResponse(response, serviceTypeLabels);
@@ -512,6 +522,10 @@
                                 == 0) {
                             iter.remove();
                             for (int i = 0; i < listeners.size(); i++) {
+                                if (!responseMatchesOptions(existingResponse,
+                                        listeners.valueAt(i)))  {
+                                    continue;
+                                }
                                 final MdnsServiceBrowserListener listener = listeners.keyAt(i);
                                 String serviceInstanceName =
                                         existingResponse.getServiceInstanceName();
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
index 5d58f5d..746994f 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -992,6 +992,71 @@
                 mockNetwork);
     }
 
+    @Test
+    public void testProcessResponse_ResolveExcludesOtherServices() {
+        client = new MdnsServiceTypeClient(
+                SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork);
+
+        final String requestedInstance = "instance1";
+        final String otherInstance = "instance2";
+        final String ipV4Address = "192.0.2.0";
+        final String ipV6Address = "2001:db8::";
+
+        final MdnsSearchOptions resolveOptions = MdnsSearchOptions.newBuilder()
+                // Use different case in the options
+                .setResolveInstanceName("Instance1").build();
+
+        client.startSendAndReceive(mockListenerOne, resolveOptions);
+        client.startSendAndReceive(mockListenerTwo, MdnsSearchOptions.getDefaultOptions());
+
+        // Complete response from instanceName
+        client.processResponse(createResponse(
+                requestedInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
+                        Collections.emptyMap() /* textAttributes */, TEST_TTL),
+                INTERFACE_INDEX, mockNetwork);
+
+        // Complete response from otherInstanceName
+        client.processResponse(createResponse(
+                otherInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
+                        Collections.emptyMap() /* textAttributes */, TEST_TTL),
+                INTERFACE_INDEX, mockNetwork);
+
+        // Address update from otherInstanceName
+        client.processResponse(createResponse(
+                otherInstance, ipV6Address, 5353, SERVICE_TYPE_LABELS,
+                Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+
+        // Goodbye from otherInstanceName
+        client.processResponse(createResponse(
+                otherInstance, ipV6Address, 5353, SERVICE_TYPE_LABELS,
+                Collections.emptyMap(), 0L /* ttl */), INTERFACE_INDEX, mockNetwork);
+
+        // mockListenerOne gets notified for the requested instance
+        verify(mockListenerOne).onServiceNameDiscovered(matchServiceName(requestedInstance));
+        verify(mockListenerOne).onServiceFound(matchServiceName(requestedInstance));
+
+        // ...but does not get any callback for the other instance
+        verify(mockListenerOne, never()).onServiceFound(matchServiceName(otherInstance));
+        verify(mockListenerOne, never()).onServiceNameDiscovered(matchServiceName(otherInstance));
+        verify(mockListenerOne, never()).onServiceUpdated(matchServiceName(otherInstance));
+        verify(mockListenerOne, never()).onServiceRemoved(matchServiceName(otherInstance));
+
+        // mockListenerTwo gets notified for both though
+        final InOrder inOrder = inOrder(mockListenerTwo);
+        inOrder.verify(mockListenerTwo).onServiceNameDiscovered(
+                matchServiceName(requestedInstance));
+        inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(requestedInstance));
+
+        inOrder.verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
+        inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
+        inOrder.verify(mockListenerTwo).onServiceUpdated(matchServiceName(otherInstance));
+        inOrder.verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
+    }
+
+    private static MdnsServiceInfo matchServiceName(String name) {
+        return argThat(info -> info.getServiceInstanceName().equals(name));
+    }
+
     // verifies that the right query was enqueued with the right delay, and send query by executing
     // the runnable.
     private void verifyAndSendQuery(int index, long timeInMs, boolean expectsUnicastResponse) {