Deal with responses on MdnsServiceCache

To cache services and respond quickly, the MdnsServiceTypeClient
should add all services to the MdnsServiceCache and remove any
services from there as well.

Bug: 265787401
Test: atest FrameworksNetTests
Change-Id: If0a9e6b563a0992ac25b8cde7f3beb00700f1c11
This commit is contained in:
Paul Hu
2023-07-06 01:23:59 +00:00
parent 2118d33da8
commit 639e3c70bb
4 changed files with 170 additions and 44 deletions

View File

@@ -51,6 +51,7 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
@NonNull private final PerSocketServiceTypeClients perSocketServiceTypeClients; @NonNull private final PerSocketServiceTypeClients perSocketServiceTypeClients;
@NonNull private final Handler handler; @NonNull private final Handler handler;
@Nullable private final HandlerThread handlerThread; @Nullable private final HandlerThread handlerThread;
@NonNull private final MdnsServiceCache serviceCache;
private static class PerSocketServiceTypeClients { private static class PerSocketServiceTypeClients {
private final ArrayMap<Pair<String, SocketKey>, MdnsServiceTypeClient> clients = private final ArrayMap<Pair<String, SocketKey>, MdnsServiceTypeClient> clients =
@@ -119,10 +120,12 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
if (socketClient.getLooper() != null) { if (socketClient.getLooper() != null) {
this.handlerThread = null; this.handlerThread = null;
this.handler = new Handler(socketClient.getLooper()); this.handler = new Handler(socketClient.getLooper());
this.serviceCache = new MdnsServiceCache(socketClient.getLooper());
} else { } else {
this.handlerThread = new HandlerThread(MdnsDiscoveryManager.class.getSimpleName()); this.handlerThread = new HandlerThread(MdnsDiscoveryManager.class.getSimpleName());
this.handlerThread.start(); this.handlerThread.start();
this.handler = new Handler(handlerThread.getLooper()); this.handler = new Handler(handlerThread.getLooper());
this.serviceCache = new MdnsServiceCache(handlerThread.getLooper());
} }
} }
@@ -289,6 +292,6 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
return new MdnsServiceTypeClient( return new MdnsServiceTypeClient(
serviceType, socketClient, serviceType, socketClient,
executorProvider.newServiceTypeClientSchedulerExecutor(), socketKey, executorProvider.newServiceTypeClientSchedulerExecutor(), socketKey,
sharedLog.forSubComponent(tag), handler.getLooper()); sharedLog.forSubComponent(tag), handler.getLooper(), serviceCache);
} }
} }

View File

@@ -96,7 +96,14 @@ public class MdnsServiceCache {
: Collections.emptyList(); : Collections.emptyList();
} }
private MdnsResponse findMatchedResponse(@NonNull List<MdnsResponse> responses, /**
* Find a matched response for given service name
*
* @param responses the responses to be searched.
* @param serviceName the target service name
* @return the response which matches the given service name or null if not found.
*/
public static MdnsResponse findMatchedResponse(@NonNull List<MdnsResponse> responses,
@NonNull String serviceName) { @NonNull String serviceName) {
for (MdnsResponse response : responses) { for (MdnsResponse response : responses) {
if (equalsIgnoreDnsCase(serviceName, response.getServiceInstanceName())) { if (equalsIgnoreDnsCase(serviceName, response.getServiceInstanceName())) {

View File

@@ -16,6 +16,7 @@
package com.android.server.connectivity.mdns; package com.android.server.connectivity.mdns;
import static com.android.server.connectivity.mdns.MdnsServiceCache.findMatchedResponse;
import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread; import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
import android.annotation.NonNull; import android.annotation.NonNull;
@@ -40,10 +41,8 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
/** /**
@@ -67,10 +66,12 @@ public class MdnsServiceTypeClient {
@NonNull private final SharedLog sharedLog; @NonNull private final SharedLog sharedLog;
@NonNull private final Handler handler; @NonNull private final Handler handler;
@NonNull private final Dependencies dependencies; @NonNull private final Dependencies dependencies;
/**
* The service caches for each socket. It should be accessed from looper thread only.
*/
@NonNull private final MdnsServiceCache serviceCache;
private final ArrayMap<MdnsServiceBrowserListener, MdnsSearchOptions> listeners = private final ArrayMap<MdnsServiceBrowserListener, MdnsSearchOptions> listeners =
new ArrayMap<>(); new ArrayMap<>();
// TODO: change instanceNameToResponse to TreeMap with case insensitive comparator.
private final Map<String, MdnsResponse> instanceNameToResponse = new HashMap<>();
private final boolean removeServiceAfterTtlExpires = private final boolean removeServiceAfterTtlExpires =
MdnsConfigs.removeServiceAfterTtlExpires(); MdnsConfigs.removeServiceAfterTtlExpires();
private final MdnsResponseDecoder.Clock clock; private final MdnsResponseDecoder.Clock clock;
@@ -190,9 +191,10 @@ public class MdnsServiceTypeClient {
@NonNull ScheduledExecutorService executor, @NonNull ScheduledExecutorService executor,
@NonNull SocketKey socketKey, @NonNull SocketKey socketKey,
@NonNull SharedLog sharedLog, @NonNull SharedLog sharedLog,
@NonNull Looper looper) { @NonNull Looper looper,
@NonNull MdnsServiceCache serviceCache) {
this(serviceType, socketClient, executor, new MdnsResponseDecoder.Clock(), socketKey, this(serviceType, socketClient, executor, new MdnsResponseDecoder.Clock(), socketKey,
sharedLog, looper, new Dependencies()); sharedLog, looper, new Dependencies(), serviceCache);
} }
@VisibleForTesting @VisibleForTesting
@@ -204,7 +206,8 @@ public class MdnsServiceTypeClient {
@NonNull SocketKey socketKey, @NonNull SocketKey socketKey,
@NonNull SharedLog sharedLog, @NonNull SharedLog sharedLog,
@NonNull Looper looper, @NonNull Looper looper,
@NonNull Dependencies dependencies) { @NonNull Dependencies dependencies,
@NonNull MdnsServiceCache serviceCache) {
this.serviceType = serviceType; this.serviceType = serviceType;
this.socketClient = socketClient; this.socketClient = socketClient;
this.executor = executor; this.executor = executor;
@@ -215,6 +218,7 @@ public class MdnsServiceTypeClient {
this.sharedLog = sharedLog; this.sharedLog = sharedLog;
this.handler = new QueryTaskHandler(looper); this.handler = new QueryTaskHandler(looper);
this.dependencies = dependencies; this.dependencies = dependencies;
this.serviceCache = serviceCache;
} }
private static MdnsServiceInfo buildMdnsServiceInfoFromResponse( private static MdnsServiceInfo buildMdnsServiceInfoFromResponse(
@@ -281,7 +285,8 @@ public class MdnsServiceTypeClient {
this.searchOptions = searchOptions; this.searchOptions = searchOptions;
boolean hadReply = false; boolean hadReply = false;
if (listeners.put(listener, searchOptions) == null) { if (listeners.put(listener, searchOptions) == null) {
for (MdnsResponse existingResponse : instanceNameToResponse.values()) { for (MdnsResponse existingResponse :
serviceCache.getCachedServices(serviceType, socketKey)) {
if (!responseMatchesOptions(existingResponse, searchOptions)) continue; if (!responseMatchesOptions(existingResponse, searchOptions)) continue;
final MdnsServiceInfo info = final MdnsServiceInfo info =
buildMdnsServiceInfoFromResponse(existingResponse, serviceTypeLabels); buildMdnsServiceInfoFromResponse(existingResponse, serviceTypeLabels);
@@ -377,11 +382,13 @@ public class MdnsServiceTypeClient {
ensureRunningOnHandlerThread(handler); ensureRunningOnHandlerThread(handler);
// Augment the list of current known responses, and generated responses for resolve // Augment the list of current known responses, and generated responses for resolve
// requests if there is no known response // requests if there is no known response
final List<MdnsResponse> currentList = new ArrayList<>(instanceNameToResponse.values()); final List<MdnsResponse> cachedList =
serviceCache.getCachedServices(serviceType, socketKey);
final List<MdnsResponse> currentList = new ArrayList<>(cachedList);
List<MdnsResponse> additionalResponses = makeResponsesForResolve(socketKey); List<MdnsResponse> additionalResponses = makeResponsesForResolve(socketKey);
for (MdnsResponse additionalResponse : additionalResponses) { for (MdnsResponse additionalResponse : additionalResponses) {
if (!instanceNameToResponse.containsKey( if (findMatchedResponse(
additionalResponse.getServiceInstanceName())) { cachedList, additionalResponse.getServiceInstanceName()) == null) {
currentList.add(additionalResponse); currentList.add(additionalResponse);
} }
} }
@@ -393,16 +400,17 @@ public class MdnsServiceTypeClient {
final ArrayList<MdnsResponse> allResponses = augmentedResult.second; final ArrayList<MdnsResponse> allResponses = augmentedResult.second;
for (MdnsResponse response : allResponses) { for (MdnsResponse response : allResponses) {
final String serviceInstanceName = response.getServiceInstanceName();
if (modifiedResponse.contains(response)) { if (modifiedResponse.contains(response)) {
if (response.isGoodbye()) { if (response.isGoodbye()) {
onGoodbyeReceived(response.getServiceInstanceName()); onGoodbyeReceived(serviceInstanceName);
} else { } else {
onResponseModified(response); onResponseModified(response);
} }
} else if (instanceNameToResponse.containsKey(response.getServiceInstanceName())) { } else if (findMatchedResponse(cachedList, serviceInstanceName) != null) {
// If the response is not modified and already in the cache. The cache will // If the response is not modified and already in the cache. The cache will
// need to be updated to refresh the last receipt time. // need to be updated to refresh the last receipt time.
instanceNameToResponse.put(response.getServiceInstanceName(), response); serviceCache.addOrUpdateService(serviceType, socketKey, response);
} }
} }
if (dependencies.hasMessages(handler, EVENT_START_QUERYTASK) if (dependencies.hasMessages(handler, EVENT_START_QUERYTASK)
@@ -431,7 +439,7 @@ public class MdnsServiceTypeClient {
/** Notify all services are removed because the socket is destroyed. */ /** Notify all services are removed because the socket is destroyed. */
public void notifySocketDestroyed() { public void notifySocketDestroyed() {
ensureRunningOnHandlerThread(handler); ensureRunningOnHandlerThread(handler);
for (MdnsResponse response : instanceNameToResponse.values()) { for (MdnsResponse response : serviceCache.getCachedServices(serviceType, socketKey)) {
final String name = response.getServiceInstanceName(); final String name = response.getServiceInstanceName();
if (name == null) continue; if (name == null) continue;
for (int i = 0; i < listeners.size(); i++) { for (int i = 0; i < listeners.size(); i++) {
@@ -453,18 +461,18 @@ public class MdnsServiceTypeClient {
private void onResponseModified(@NonNull MdnsResponse response) { private void onResponseModified(@NonNull MdnsResponse response) {
final String serviceInstanceName = response.getServiceInstanceName(); final String serviceInstanceName = response.getServiceInstanceName();
final MdnsResponse currentResponse = final MdnsResponse currentResponse =
instanceNameToResponse.get(serviceInstanceName); serviceCache.getCachedService(serviceInstanceName, serviceType, socketKey);
boolean newServiceFound = false; boolean newServiceFound = false;
boolean serviceBecomesComplete = false; boolean serviceBecomesComplete = false;
if (currentResponse == null) { if (currentResponse == null) {
newServiceFound = true; newServiceFound = true;
if (serviceInstanceName != null) { if (serviceInstanceName != null) {
instanceNameToResponse.put(serviceInstanceName, response); serviceCache.addOrUpdateService(serviceType, socketKey, response);
} }
} else { } else {
boolean before = currentResponse.isComplete(); boolean before = currentResponse.isComplete();
instanceNameToResponse.put(serviceInstanceName, response); serviceCache.addOrUpdateService(serviceType, socketKey, response);
boolean after = response.isComplete(); boolean after = response.isComplete();
serviceBecomesComplete = !before && after; serviceBecomesComplete = !before && after;
} }
@@ -497,7 +505,8 @@ public class MdnsServiceTypeClient {
} }
private void onGoodbyeReceived(@Nullable String serviceInstanceName) { private void onGoodbyeReceived(@Nullable String serviceInstanceName) {
final MdnsResponse response = instanceNameToResponse.remove(serviceInstanceName); final MdnsResponse response =
serviceCache.removeService(serviceInstanceName, serviceType, socketKey);
if (response == null) { if (response == null) {
return; return;
} }
@@ -673,7 +682,8 @@ public class MdnsServiceTypeClient {
if (resolveName == null) { if (resolveName == null) {
continue; continue;
} }
MdnsResponse knownResponse = instanceNameToResponse.get(resolveName); MdnsResponse knownResponse =
serviceCache.getCachedService(resolveName, serviceType, socketKey);
if (knownResponse == null) { if (knownResponse == null) {
final ArrayList<String> instanceFullName = new ArrayList<>( final ArrayList<String> instanceFullName = new ArrayList<>(
serviceTypeLabels.length + 1); serviceTypeLabels.length + 1);
@@ -691,19 +701,21 @@ public class MdnsServiceTypeClient {
private void tryRemoveServiceAfterTtlExpires() { private void tryRemoveServiceAfterTtlExpires() {
if (!shouldRemoveServiceAfterTtlExpires()) return; if (!shouldRemoveServiceAfterTtlExpires()) return;
Iterator<MdnsResponse> iter = instanceNameToResponse.values().iterator(); Iterator<MdnsResponse> iter =
serviceCache.getCachedServices(serviceType, socketKey).iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
MdnsResponse existingResponse = iter.next(); MdnsResponse existingResponse = iter.next();
final String serviceInstanceName = existingResponse.getServiceInstanceName();
if (existingResponse.hasServiceRecord() if (existingResponse.hasServiceRecord()
&& existingResponse.getServiceRecord() && existingResponse.getServiceRecord()
.getRemainingTTL(clock.elapsedRealtime()) == 0) { .getRemainingTTL(clock.elapsedRealtime()) == 0) {
iter.remove(); serviceCache.removeService(serviceInstanceName, serviceType, socketKey);
for (int i = 0; i < listeners.size(); i++) { for (int i = 0; i < listeners.size(); i++) {
if (!responseMatchesOptions(existingResponse, listeners.valueAt(i))) { if (!responseMatchesOptions(existingResponse, listeners.valueAt(i))) {
continue; continue;
} }
final MdnsServiceBrowserListener listener = listeners.keyAt(i); final MdnsServiceBrowserListener listener = listeners.keyAt(i);
if (existingResponse.getServiceInstanceName() != null) { if (serviceInstanceName != null) {
final MdnsServiceInfo serviceInfo = buildMdnsServiceInfoFromResponse( final MdnsServiceInfo serviceInfo = buildMdnsServiceInfoFromResponse(
existingResponse, serviceTypeLabels); existingResponse, serviceTypeLabels);
if (existingResponse.isComplete()) { if (existingResponse.isComplete()) {
@@ -812,7 +824,7 @@ public class MdnsServiceTypeClient {
private long getMinRemainingTtl(long now) { private long getMinRemainingTtl(long now) {
long minRemainingTtl = Long.MAX_VALUE; long minRemainingTtl = Long.MAX_VALUE;
for (MdnsResponse response : instanceNameToResponse.values()) { for (MdnsResponse response : serviceCache.getCachedServices(serviceType, socketKey)) {
if (!response.isComplete()) { if (!response.isComplete()) {
continue; continue;
} }

View File

@@ -131,6 +131,7 @@ public class MdnsServiceTypeClientTests {
private SocketKey socketKey; private SocketKey socketKey;
private HandlerThread thread; private HandlerThread thread;
private Handler handler; private Handler handler;
private MdnsServiceCache serviceCache;
private long latestDelayMs = 0; private long latestDelayMs = 0;
private Message delayMessage = null; private Message delayMessage = null;
private Handler realHandler = null; private Handler realHandler = null;
@@ -190,6 +191,7 @@ public class MdnsServiceTypeClientTests {
thread = new HandlerThread("MdnsServiceTypeClientTests"); thread = new HandlerThread("MdnsServiceTypeClientTests");
thread.start(); thread.start();
handler = new Handler(thread.getLooper()); handler = new Handler(thread.getLooper());
serviceCache = new MdnsServiceCache(thread.getLooper());
doAnswer(inv -> { doAnswer(inv -> {
latestDelayMs = 0; latestDelayMs = 0;
@@ -213,7 +215,8 @@ public class MdnsServiceTypeClientTests {
client = client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps) { mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
serviceCache) {
@Override @Override
MdnsPacketWriter createMdnsPacketWriter() { MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter; return mockPacketWriter;
@@ -908,7 +911,8 @@ public class MdnsServiceTypeClientTests {
final String serviceInstanceName = "service-instance-1"; final String serviceInstanceName = "service-instance-1";
client = client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps) { mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
serviceCache) {
@Override @Override
MdnsPacketWriter createMdnsPacketWriter() { MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter; return mockPacketWriter;
@@ -953,7 +957,8 @@ public class MdnsServiceTypeClientTests {
final String serviceInstanceName = "service-instance-1"; final String serviceInstanceName = "service-instance-1";
client = client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps) { mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
serviceCache) {
@Override @Override
MdnsPacketWriter createMdnsPacketWriter() { MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter; return mockPacketWriter;
@@ -986,7 +991,8 @@ public class MdnsServiceTypeClientTests {
final String serviceInstanceName = "service-instance-1"; final String serviceInstanceName = "service-instance-1";
client = client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps) { mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
serviceCache) {
@Override @Override
MdnsPacketWriter createMdnsPacketWriter() { MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter; return mockPacketWriter;
@@ -1106,7 +1112,8 @@ public class MdnsServiceTypeClientTests {
@Test @Test
public void testProcessResponse_Resolve() throws Exception { public void testProcessResponse_Resolve() throws Exception {
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps); mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
serviceCache);
final String instanceName = "service-instance"; final String instanceName = "service-instance";
final String[] hostname = new String[] { "testhost "}; final String[] hostname = new String[] { "testhost "};
@@ -1199,7 +1206,8 @@ public class MdnsServiceTypeClientTests {
@Test @Test
public void testRenewTxtSrvInResolve() throws Exception { public void testRenewTxtSrvInResolve() throws Exception {
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps); mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
serviceCache);
final String instanceName = "service-instance"; final String instanceName = "service-instance";
final String[] hostname = new String[] { "testhost "}; final String[] hostname = new String[] { "testhost "};
@@ -1312,7 +1320,8 @@ public class MdnsServiceTypeClientTests {
@Test @Test
public void testProcessResponse_ResolveExcludesOtherServices() { public void testProcessResponse_ResolveExcludesOtherServices() {
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps); mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
serviceCache);
final String requestedInstance = "instance1"; final String requestedInstance = "instance1";
final String otherInstance = "instance2"; final String otherInstance = "instance2";
@@ -1376,7 +1385,8 @@ public class MdnsServiceTypeClientTests {
@Test @Test
public void testProcessResponse_SubtypeDiscoveryLimitedToSubtype() { public void testProcessResponse_SubtypeDiscoveryLimitedToSubtype() {
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps); mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
serviceCache);
final String matchingInstance = "instance1"; final String matchingInstance = "instance1";
final String subtype = "_subtype"; final String subtype = "_subtype";
@@ -1457,7 +1467,8 @@ public class MdnsServiceTypeClientTests {
@Test @Test
public void testNotifySocketDestroyed() throws Exception { public void testNotifySocketDestroyed() throws Exception {
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps); mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
serviceCache);
final String requestedInstance = "instance1"; final String requestedInstance = "instance1";
final String otherInstance = "instance2"; final String otherInstance = "instance2";
@@ -1512,16 +1523,109 @@ public class MdnsServiceTypeClientTests {
verify(mockListenerOne, never()).onServiceNameRemoved(matchServiceName(otherInstance)); verify(mockListenerOne, never()).onServiceNameRemoved(matchServiceName(otherInstance));
// mockListenerTwo gets notified for both though // mockListenerTwo gets notified for both though
final InOrder inOrder2 = inOrder(mockListenerTwo); verify(mockListenerTwo).onServiceNameDiscovered(
inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(
matchServiceName(requestedInstance)); matchServiceName(requestedInstance));
inOrder2.verify(mockListenerTwo).onServiceFound(matchServiceName(requestedInstance)); verify(mockListenerTwo).onServiceFound(matchServiceName(requestedInstance));
inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance)); verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
inOrder2.verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance)); verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
inOrder2.verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance)); verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
inOrder2.verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(otherInstance)); verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(otherInstance));
inOrder2.verify(mockListenerTwo).onServiceRemoved(matchServiceName(requestedInstance)); verify(mockListenerTwo).onServiceRemoved(matchServiceName(requestedInstance));
inOrder2.verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(requestedInstance)); verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(requestedInstance));
}
@Test
public void testServicesAreCached() throws Exception {
final String serviceName = "service-instance";
final String ipV4Address = "192.0.2.0";
// Register a listener
startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
verify(mockDeps, times(1)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
InOrder inOrder = inOrder(mockListenerOne);
// Process a response which has ip address to make response become complete.
final String subtype = "ABCDE";
processResponse(createResponse(
serviceName, ipV4Address, 5353, subtype,
Collections.emptyMap(), TEST_TTL),
socketKey);
// Verify that onServiceNameDiscovered is called.
inOrder.verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
serviceName,
SERVICE_TYPE_LABELS,
List.of(ipV4Address) /* ipv4Address */,
List.of() /* ipv6Address */,
5353 /* port */,
Collections.singletonList(subtype) /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
socketKey);
// Verify that onServiceFound is called.
inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(1),
serviceName,
SERVICE_TYPE_LABELS,
List.of(ipV4Address) /* ipv4Address */,
List.of() /* ipv6Address */,
5353 /* port */,
Collections.singletonList(subtype) /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
socketKey);
// Unregister the listener
stopSendAndReceive(mockListenerOne);
verify(mockDeps, times(2)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
// Register another listener.
startSendAndReceive(mockListenerTwo, MdnsSearchOptions.getDefaultOptions());
verify(mockDeps, times(3)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
InOrder inOrder2 = inOrder(mockListenerTwo);
// The services are cached in MdnsServiceCache, verify that onServiceNameDiscovered is
// called immediately.
inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(serviceInfoCaptor.capture());
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(2),
serviceName,
SERVICE_TYPE_LABELS,
List.of(ipV4Address) /* ipv4Address */,
List.of() /* ipv6Address */,
5353 /* port */,
Collections.singletonList(subtype) /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
socketKey);
// The services are cached in MdnsServiceCache, verify that onServiceFound is
// called immediately.
inOrder2.verify(mockListenerTwo).onServiceFound(serviceInfoCaptor.capture());
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(3),
serviceName,
SERVICE_TYPE_LABELS,
List.of(ipV4Address) /* ipv4Address */,
List.of() /* ipv6Address */,
5353 /* port */,
Collections.singletonList(subtype) /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
socketKey);
// Process a response with a different ip address, port and updated text attributes.
final String ipV6Address = "2001:db8::";
processResponse(createResponse(
serviceName, ipV6Address, 5354, subtype,
Collections.singletonMap("key", "value"), TEST_TTL), socketKey);
// Verify the onServiceUpdated is called.
inOrder2.verify(mockListenerTwo).onServiceUpdated(serviceInfoCaptor.capture());
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(4),
serviceName,
SERVICE_TYPE_LABELS,
List.of(ipV4Address) /* ipv4Address */,
List.of(ipV6Address) /* ipv6Address */,
5354 /* port */,
Collections.singletonList(subtype) /* subTypes */,
Collections.singletonMap("key", "value") /* attributes */,
socketKey);
} }
private static MdnsServiceInfo matchServiceName(String name) { private static MdnsServiceInfo matchServiceName(String name) {