Report resolution metrics data
Collect information on resolution success and failure events,
then use this data to build metrics and report them.
Bug: 287546772
Test: atest FrameworksNetTestCases NsdManagerTest
Change-Id: I373ce91fea9d5a278f53fc0c505be90dd39b2d5b
diff --git a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
index 71788d2..c3b5086 100644
--- a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
+++ b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
@@ -173,4 +173,37 @@
builder.setFoundServiceCount(servicesCount);
mDependencies.statsWrite(builder.build());
}
+
+ /**
+ * Report service resolution success metric data.
+ *
+ * @param transactionId The transaction id of service resolution.
+ * @param durationMs The duration of resolving services.
+ * @param isServiceFromCache Whether the resolved service is from cache.
+ */
+ public void reportServiceResolved(int transactionId, long durationMs,
+ boolean isServiceFromCache) {
+ final Builder builder = makeReportedBuilder();
+ builder.setTransactionId(transactionId);
+ builder.setType(NsdEventType.NET_RESOLVE);
+ builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_RESOLVED);
+ builder.setEventDurationMillisec(durationMs);
+ builder.setIsKnownService(isServiceFromCache);
+ mDependencies.statsWrite(builder.build());
+ }
+
+ /**
+ * Report service resolution failed metric data.
+ *
+ * @param transactionId The transaction id of service resolution.
+ * @param durationMs The duration of service resolution failed.
+ */
+ public void reportServiceResolutionFailed(int transactionId, long durationMs) {
+ final Builder builder = makeReportedBuilder();
+ builder.setTransactionId(transactionId);
+ builder.setType(NsdEventType.NET_RESOLVE);
+ builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_RESOLUTION_FAILED);
+ builder.setEventDurationMillisec(durationMs);
+ mDependencies.statsWrite(builder.build());
+ }
}
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 786a181..b975a1b 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -265,7 +265,8 @@
}
@Override
- public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo) { }
+ public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
+ boolean isServiceFromCache) { }
@Override
public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { }
@@ -274,7 +275,8 @@
public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
@Override
- public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo) { }
+ public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
+ boolean isServiceFromCache) { }
@Override
public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
@@ -300,10 +302,11 @@
}
@Override
- public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo) {
+ public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
+ boolean isServiceFromCache) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.SERVICE_FOUND,
- new MdnsEvent(mClientRequestId, serviceInfo));
+ new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
}
@Override
@@ -322,10 +325,10 @@
}
@Override
- public void onServiceFound(MdnsServiceInfo serviceInfo) {
+ public void onServiceFound(MdnsServiceInfo serviceInfo, boolean isServiceFromCache) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.RESOLVE_SERVICE_SUCCEEDED,
- new MdnsEvent(mClientRequestId, serviceInfo));
+ new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
}
}
@@ -337,10 +340,11 @@
}
@Override
- public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo) {
+ public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
+ boolean isServiceFromCache) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.SERVICE_UPDATED,
- new MdnsEvent(mClientRequestId, serviceInfo));
+ new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
}
@Override
@@ -463,10 +467,17 @@
final int mClientRequestId;
@NonNull
final MdnsServiceInfo mMdnsServiceInfo;
+ final boolean mIsServiceFromCache;
MdnsEvent(int clientRequestId, @NonNull MdnsServiceInfo mdnsServiceInfo) {
+ this(clientRequestId, mdnsServiceInfo, false /* isServiceFromCache */);
+ }
+
+ MdnsEvent(int clientRequestId, @NonNull MdnsServiceInfo mdnsServiceInfo,
+ boolean isServiceFromCache) {
mClientRequestId = clientRequestId;
mMdnsServiceInfo = mdnsServiceInfo;
+ mIsServiceFromCache = isServiceFromCache;
}
}
@@ -615,7 +626,7 @@
case NsdManager.RESOLVE_SERVICE:
cInfo = getClientInfoForReply(msg);
if (cInfo != null) {
- cInfo.onResolveServiceFailed(
+ cInfo.onResolveServiceFailedImmediately(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
@@ -682,9 +693,9 @@
}
private void storeLegacyRequestMap(int clientRequestId, int transactionId,
- ClientInfo clientInfo, int what) {
- clientInfo.mClientRequests.put(clientRequestId, new LegacyClientRequest(
- transactionId, what, mClock.elapsedRealtime()));
+ ClientInfo clientInfo, int what, long startTimeMs) {
+ clientInfo.mClientRequests.put(clientRequestId,
+ new LegacyClientRequest(transactionId, what, startTimeMs));
mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
// Remove the cleanup event because here comes a new request.
cancelStop();
@@ -810,8 +821,8 @@
Log.d(TAG, "Discover " + msg.arg2 + " " + transactionId
+ info.getServiceType());
}
- storeLegacyRequestMap(
- clientRequestId, transactionId, clientInfo, msg.what);
+ storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
+ msg.what, mClock.elapsedRealtime());
clientInfo.onDiscoverServicesStarted(
clientRequestId, info, transactionId);
} else {
@@ -912,8 +923,8 @@
Log.d(TAG, "Register " + clientRequestId
+ " " + transactionId);
}
- storeLegacyRequestMap(
- clientRequestId, transactionId, clientInfo, msg.what);
+ storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
+ msg.what, mClock.elapsedRealtime());
// Return success after mDns reports success
} else {
unregisterService(transactionId);
@@ -986,8 +997,8 @@
|| mDeps.isMdnsDiscoveryManagerEnabled(mContext)
|| useDiscoveryManagerForType(serviceType)) {
if (serviceType == null) {
- clientInfo.onResolveServiceFailed(clientRequestId,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailedImmediately(
+ clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
}
final String resolveServiceType = serviceType + ".local";
@@ -1009,7 +1020,7 @@
+ " for service type:" + resolveServiceType);
} else {
if (clientInfo.mResolvedService != null) {
- clientInfo.onResolveServiceFailed(
+ clientInfo.onResolveServiceFailedImmediately(
clientRequestId, NsdManager.FAILURE_ALREADY_ACTIVE);
break;
}
@@ -1017,10 +1028,10 @@
maybeStartDaemon();
if (resolveService(transactionId, info)) {
clientInfo.mResolvedService = new NsdServiceInfo();
- storeLegacyRequestMap(
- clientRequestId, transactionId, clientInfo, msg.what);
+ storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
+ msg.what, mClock.elapsedRealtime());
} else {
- clientInfo.onResolveServiceFailed(
+ clientInfo.onResolveServiceFailedImmediately(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
}
}
@@ -1279,10 +1290,11 @@
final int transactionId2 = getUniqueId();
if (getAddrInfo(transactionId2, info.hostname, info.interfaceIdx)) {
storeLegacyRequestMap(clientRequestId, transactionId2, clientInfo,
- NsdManager.RESOLVE_SERVICE);
+ NsdManager.RESOLVE_SERVICE, request.mStartTimeMs);
} else {
- clientInfo.onResolveServiceFailed(
- clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailed(clientRequestId,
+ NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
clientInfo.mResolvedService = null;
}
break;
@@ -1291,16 +1303,18 @@
/* NNN resolveId errorCode */
stopResolveService(transactionId);
removeRequestMap(clientRequestId, transactionId, clientInfo);
- clientInfo.onResolveServiceFailed(
- clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailed(clientRequestId,
+ NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
clientInfo.mResolvedService = null;
break;
case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
/* NNN resolveId errorCode */
stopGetAddrInfo(transactionId);
removeRequestMap(clientRequestId, transactionId, clientInfo);
- clientInfo.onResolveServiceFailed(
- clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailed(clientRequestId,
+ NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
clientInfo.mResolvedService = null;
break;
case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
@@ -1323,10 +1337,11 @@
setServiceNetworkForCallback(clientInfo.mResolvedService,
netId, info.interfaceIdx);
clientInfo.onResolveServiceSucceeded(
- clientRequestId, clientInfo.mResolvedService);
+ clientRequestId, clientInfo.mResolvedService, request);
} else {
- clientInfo.onResolveServiceFailed(
- clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailed(clientRequestId,
+ NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
}
stopGetAddrInfo(transactionId);
removeRequestMap(clientRequestId, transactionId, clientInfo);
@@ -1429,11 +1444,13 @@
final List<InetAddress> addresses = getInetAddresses(serviceInfo);
if (addresses.size() != 0) {
info.setHostAddresses(addresses);
- clientInfo.onResolveServiceSucceeded(clientRequestId, info);
+ request.setServiceFromCache(event.mIsServiceFromCache);
+ clientInfo.onResolveServiceSucceeded(clientRequestId, info, request);
} else {
// No address. Notify resolution failure.
- clientInfo.onResolveServiceFailed(
- clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailed(clientRequestId,
+ NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()));
}
// Unregister the listener immediately like IMDnsEventListener design
@@ -2200,6 +2217,7 @@
private int mFoundServiceCount = 0;
private int mLostServiceCount = 0;
private final Set<String> mServices = new ArraySet<>();
+ private boolean mIsServiceFromCache = false;
private ClientRequest(int transactionId, long startTimeMs) {
mTransactionId = transactionId;
@@ -2232,6 +2250,14 @@
public int getServicesCount() {
return mServices.size();
}
+
+ public void setServiceFromCache(boolean isServiceFromCache) {
+ mIsServiceFromCache = isServiceFromCache;
+ }
+
+ public boolean isServiceFromCache() {
+ return mIsServiceFromCache;
+ }
}
private static class LegacyClientRequest extends ClientRequest {
@@ -2544,7 +2570,13 @@
}
}
- void onResolveServiceFailed(int listenerKey, int error) {
+ void onResolveServiceFailedImmediately(int listenerKey, int error) {
+ onResolveServiceFailed(listenerKey, error, NO_TRANSACTION, 0L /* durationMs */);
+ }
+
+ void onResolveServiceFailed(int listenerKey, int error, int transactionId,
+ long durationMs) {
+ mMetrics.reportServiceResolutionFailed(transactionId, durationMs);
try {
mCb.onResolveServiceFailed(listenerKey, error);
} catch (RemoteException e) {
@@ -2552,7 +2584,12 @@
}
}
- void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
+ void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info,
+ ClientRequest request) {
+ mMetrics.reportServiceResolved(
+ request.mTransactionId,
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()),
+ request.isServiceFromCache());
try {
mCb.onResolveServiceSucceeded(listenerKey, info);
} catch (RemoteException e) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java
index 7c19359..4c3cbc0 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java
@@ -32,8 +32,9 @@
* service records (PTR, SRV, TXT, A or AAAA) are received .
*
* @param serviceInfo The found mDNS service instance.
+ * @param isServiceFromCache Whether the found mDNS service is from cache.
*/
- void onServiceFound(@NonNull MdnsServiceInfo serviceInfo);
+ void onServiceFound(@NonNull MdnsServiceInfo serviceInfo, boolean isServiceFromCache);
/**
* Called when an mDNS service instance is updated. This method would be called only if all
@@ -84,8 +85,9 @@
* record has been received.
*
* @param serviceInfo The discovered mDNS service instance.
+ * @param isServiceFromCache Whether the discovered mDNS service is from cache.
*/
- void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo);
+ void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo, boolean isServiceFromCache);
/**
* Called when a discovered mDNS service instance is no longer valid and removed.
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 53a7ab9..861d8d1 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -297,9 +297,9 @@
if (!responseMatchesOptions(existingResponse, searchOptions)) continue;
final MdnsServiceInfo info =
buildMdnsServiceInfoFromResponse(existingResponse, serviceTypeLabels);
- listener.onServiceNameDiscovered(info);
+ listener.onServiceNameDiscovered(info, true /* isServiceFromCache */);
if (existingResponse.isComplete()) {
- listener.onServiceFound(info);
+ listener.onServiceFound(info, true /* isServiceFromCache */);
hadReply = true;
}
}
@@ -512,13 +512,13 @@
final MdnsServiceBrowserListener listener = listeners.keyAt(i);
if (newServiceFound) {
sharedLog.log("onServiceNameDiscovered: " + serviceInfo);
- listener.onServiceNameDiscovered(serviceInfo);
+ listener.onServiceNameDiscovered(serviceInfo, false /* isServiceFromCache */);
}
if (response.isComplete()) {
if (newServiceFound || serviceBecomesComplete) {
sharedLog.log("onServiceFound: " + serviceInfo);
- listener.onServiceFound(serviceInfo);
+ listener.onServiceFound(serviceInfo, false /* isServiceFromCache */);
} else {
sharedLog.log("onServiceUpdated: " + serviceInfo);
listener.onServiceUpdated(serviceInfo);
diff --git a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
index fdc12c9..872326e 100644
--- a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
+++ b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
@@ -160,4 +160,45 @@
assertEquals(durationMs, it.eventDurationMillisec)
}
}
+
+ @Test
+ fun testReportServiceResolved() {
+ val clientId = 99
+ val transactionId = 100
+ val durationMs = 10L
+ val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps)
+ metrics.reportServiceResolved(transactionId, durationMs, true /* isServiceFromCache */)
+
+ val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
+ verify(deps).statsWrite(eventCaptor.capture())
+ eventCaptor.value.let {
+ assertTrue(it.isLegacy)
+ assertEquals(clientId, it.clientId)
+ assertEquals(transactionId, it.transactionId)
+ assertEquals(NsdEventType.NET_RESOLVE, it.type)
+ assertEquals(MdnsQueryResult.MQR_SERVICE_RESOLVED, it.queryResult)
+ assertTrue(it.isKnownService)
+ assertEquals(durationMs, it.eventDurationMillisec)
+ }
+ }
+
+ @Test
+ fun testReportServiceResolutionFailed() {
+ val clientId = 99
+ val transactionId = 100
+ val durationMs = 10L
+ val metrics = NetworkNsdReportedMetrics(false /* isLegacy */, clientId, deps)
+ metrics.reportServiceResolutionFailed(transactionId, durationMs)
+
+ val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
+ verify(deps).statsWrite(eventCaptor.capture())
+ eventCaptor.value.let {
+ assertFalse(it.isLegacy)
+ assertEquals(clientId, it.clientId)
+ assertEquals(transactionId, it.transactionId)
+ assertEquals(NsdEventType.NET_RESOLVE, it.type)
+ assertEquals(MdnsQueryResult.MQR_SERVICE_RESOLUTION_FAILED, it.queryResult)
+ assertEquals(durationMs, it.eventDurationMillisec)
+ }
+ }
}
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 8ed065f..2789c9a 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -457,19 +457,24 @@
eq(interfaceIdx));
final String serviceAddress = "192.0.2.123";
+ final int getAddrId = getAddrIdCaptor.getValue();
final GetAddressInfo addressInfo = new GetAddressInfo(
- getAddrIdCaptor.getValue(),
+ getAddrId,
IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS,
SERVICE_FULL_NAME,
serviceAddress,
interfaceIdx,
INetd.LOCAL_NET_ID);
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
eventListener.onGettingServiceAddressStatus(addressInfo);
waitForIdle();
final ArgumentCaptor<NsdServiceInfo> resInfoCaptor =
ArgumentCaptor.forClass(NsdServiceInfo.class);
verify(resolveListener, timeout(TIMEOUT_MS)).onServiceResolved(resInfoCaptor.capture());
+ verify(mMetrics).reportServiceResolved(
+ getAddrId, 10L /* durationMs */, false /* isServiceFromCache */);
+
final NsdServiceInfo resolvedService = resInfoCaptor.getValue();
assertEquals(SERVICE_NAME, resolvedService.getServiceName());
assertEquals("." + SERVICE_TYPE, resolvedService.getServiceType());
@@ -609,8 +614,9 @@
eq("local.") /* domain */, eq(IFACE_IDX_ANY));
// Fail to resolve service.
+ final int resolvId = resolvIdCaptor.getValue();
final ResolutionInfo resolutionFailedInfo = new ResolutionInfo(
- resolvIdCaptor.getValue(),
+ resolvId,
IMDnsEventListener.SERVICE_RESOLUTION_FAILED,
null /* serviceName */,
null /* serviceType */,
@@ -620,9 +626,11 @@
0 /* port */,
new byte[0] /* txtRecord */,
IFACE_IDX_ANY);
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
eventListener.onServiceResolutionStatus(resolutionFailedInfo);
verify(resolveListener, timeout(TIMEOUT_MS))
.onResolveFailed(any(), eq(FAILURE_INTERNAL_ERROR));
+ verify(mMetrics).reportServiceResolutionFailed(resolvId, 10L /* durationMs */);
}
@Test
@@ -660,16 +668,19 @@
eq(IFACE_IDX_ANY));
// Fail to get service address.
+ final int getAddrId = getAddrIdCaptor.getValue();
final GetAddressInfo gettingAddrFailedInfo = new GetAddressInfo(
- getAddrIdCaptor.getValue(),
+ getAddrId,
IMDnsEventListener.SERVICE_GET_ADDR_FAILED,
null /* hostname */,
null /* address */,
IFACE_IDX_ANY,
0 /* netId */);
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
eventListener.onGettingServiceAddressStatus(gettingAddrFailedInfo);
verify(resolveListener, timeout(TIMEOUT_MS))
.onResolveFailed(any(), eq(FAILURE_INTERNAL_ERROR));
+ verify(mMetrics).reportServiceResolutionFailed(getAddrId, 10L /* durationMs */);
}
@Test
@@ -828,7 +839,7 @@
network);
// Verify onServiceFound callback
- listener.onServiceFound(mdnsServiceInfo);
+ listener.onServiceFound(mdnsServiceInfo, false /* isServiceFromCache */);
final ArgumentCaptor<NsdServiceInfo> updateInfoCaptor =
ArgumentCaptor.forClass(NsdServiceInfo.class);
verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(1))
@@ -972,7 +983,7 @@
network);
// Verify onServiceNameDiscovered callback
- listener.onServiceNameDiscovered(foundInfo);
+ listener.onServiceNameDiscovered(foundInfo, false /* isServiceFromCache */);
verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(argThat(info ->
info.getServiceName().equals(SERVICE_NAME)
// Service type in discovery callbacks has a dot at the end
@@ -1081,8 +1092,8 @@
final Network network = new Network(999);
final String serviceType = "_nsd._service._tcp";
final String constructedServiceType = "_service._tcp.local";
- final ArgumentCaptor<MdnsServiceBrowserListener> listenerCaptor =
- ArgumentCaptor.forClass(MdnsServiceBrowserListener.class);
+ final ArgumentCaptor<MdnsListener> listenerCaptor =
+ ArgumentCaptor.forClass(MdnsListener.class);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, serviceType);
request.setNetwork(network);
client.resolveService(request, resolveListener);
@@ -1097,7 +1108,7 @@
// Subtypes are not used for resolution, only for discovery
assertEquals(Collections.emptyList(), optionsCaptor.getValue().getSubtypes());
- final MdnsServiceBrowserListener listener = listenerCaptor.getValue();
+ final MdnsListener listener = listenerCaptor.getValue();
final MdnsServiceInfo mdnsServiceInfo = new MdnsServiceInfo(
SERVICE_NAME,
constructedServiceType.split("\\."),
@@ -1113,10 +1124,14 @@
network);
// Verify onServiceFound callback
- listener.onServiceFound(mdnsServiceInfo);
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
+ listener.onServiceFound(mdnsServiceInfo, true /* isServiceFromCache */);
final ArgumentCaptor<NsdServiceInfo> infoCaptor =
ArgumentCaptor.forClass(NsdServiceInfo.class);
verify(resolveListener, timeout(TIMEOUT_MS)).onServiceResolved(infoCaptor.capture());
+ verify(mMetrics).reportServiceResolved(
+ listener.mTransactionId, 10 /* durationMs */, true /* isServiceFromCache */);
+
final NsdServiceInfo info = infoCaptor.getValue();
assertEquals(SERVICE_NAME, info.getServiceName());
assertEquals("._service._tcp", info.getServiceType());
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 1fdfe09..fde5abd 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -26,6 +26,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
@@ -645,14 +646,14 @@
SERVICE_TYPE_LABELS,
Collections.emptyMap(), TEST_TTL), socketKey);
- verify(mockListenerOne).onServiceNameDiscovered(any());
- verify(mockListenerOne).onServiceFound(any());
+ verify(mockListenerOne).onServiceNameDiscovered(any(), eq(false) /* isServiceFromCache */);
+ verify(mockListenerOne).onServiceFound(any(), eq(false) /* isServiceFromCache */);
// File another identical query
startSendAndReceive(mockListenerTwo, searchOptions);
- verify(mockListenerTwo).onServiceNameDiscovered(any());
- verify(mockListenerTwo).onServiceFound(any());
+ verify(mockListenerTwo).onServiceNameDiscovered(any(), eq(true) /* isServiceFromCache */);
+ verify(mockListenerTwo).onServiceFound(any(), eq(true) /* isServiceFromCache */);
// This time no query is submitted, only scheduled
assertNull(currentThreadExecutor.getAndClearSubmittedRunnable());
@@ -686,7 +687,8 @@
"service-instance-1", null /* host */, 0 /* port */,
SERVICE_TYPE_LABELS,
Collections.emptyMap(), TEST_TTL), socketKey);
- verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
"service-instance-1",
SERVICE_TYPE_LABELS,
@@ -697,7 +699,7 @@
Collections.emptyMap(),
socketKey);
- verify(mockListenerOne, never()).onServiceFound(any(MdnsServiceInfo.class));
+ verify(mockListenerOne, never()).onServiceFound(any(MdnsServiceInfo.class), anyBoolean());
verify(mockListenerOne, never()).onServiceUpdated(any(MdnsServiceInfo.class));
}
@@ -718,7 +720,8 @@
socketKey);
// Verify onServiceNameDiscovered was called once for the initial response.
- verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
"service-instance-1",
SERVICE_TYPE_LABELS,
@@ -730,7 +733,8 @@
socketKey);
// Verify onServiceFound was called once for the initial response.
- verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceFound(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
MdnsServiceInfo initialServiceInfo = serviceInfoCaptor.getAllValues().get(1);
assertEquals(initialServiceInfo.getServiceInstanceName(), "service-instance-1");
assertEquals(initialServiceInfo.getIpv4Address(), ipV4Address);
@@ -770,7 +774,8 @@
socketKey);
// Verify onServiceNameDiscovered was called once for the initial response.
- verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
"service-instance-1",
SERVICE_TYPE_LABELS,
@@ -782,7 +787,8 @@
socketKey);
// Verify onServiceFound was called once for the initial response.
- verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceFound(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
MdnsServiceInfo initialServiceInfo = serviceInfoCaptor.getAllValues().get(1);
assertEquals(initialServiceInfo.getServiceInstanceName(), "service-instance-1");
assertEquals(initialServiceInfo.getIpv6Address(), ipV6Address);
@@ -867,7 +873,8 @@
startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
// Verify onServiceNameDiscovered was called once for the existing response.
- verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(true) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
"service-instance-1",
SERVICE_TYPE_LABELS,
@@ -879,7 +886,8 @@
socketKey);
// Verify onServiceFound was called once for the existing response.
- verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ verify(mockListenerOne).onServiceFound(
+ serviceInfoCaptor.capture(), eq(true) /* isServiceFromCache */);
MdnsServiceInfo existingServiceInfo = serviceInfoCaptor.getAllValues().get(1);
assertEquals(existingServiceInfo.getServiceInstanceName(), "service-instance-1");
assertEquals(existingServiceInfo.getIpv4Address(), "192.168.1.1");
@@ -897,8 +905,9 @@
// Verify onServiceFound was not called on the newly registered listener after the existing
// response is gone.
- verify(mockListenerTwo, never()).onServiceNameDiscovered(any(MdnsServiceInfo.class));
- verify(mockListenerTwo, never()).onServiceFound(any(MdnsServiceInfo.class));
+ verify(mockListenerTwo, never()).onServiceNameDiscovered(
+ any(MdnsServiceInfo.class), eq(false));
+ verify(mockListenerTwo, never()).onServiceFound(any(MdnsServiceInfo.class), anyBoolean());
}
@Test
@@ -1044,7 +1053,8 @@
socketKey);
// Verify onServiceNameDiscovered was first called for the initial response.
- inOrder.verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ inOrder.verify(mockListenerOne).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
serviceName,
SERVICE_TYPE_LABELS,
@@ -1056,7 +1066,8 @@
socketKey);
// Verify onServiceFound was second called for the second response.
- inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ inOrder.verify(mockListenerOne).onServiceFound(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(1),
serviceName,
SERVICE_TYPE_LABELS,
@@ -1183,10 +1194,11 @@
Collections.emptyList() /* authorityRecords */,
Collections.emptyList() /* additionalRecords */);
- inOrder.verify(mockListenerOne, never()).onServiceNameDiscovered(any());
+ inOrder.verify(mockListenerOne, never()).onServiceNameDiscovered(any(), anyBoolean());
processResponse(addressResponse, socketKey);
- inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ inOrder.verify(mockListenerOne).onServiceFound(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getValue(),
instanceName,
SERVICE_TYPE_LABELS,
@@ -1253,8 +1265,10 @@
Collections.emptyList() /* additionalRecords */);
processResponse(srvTxtResponse, socketKey);
dispatchMessage();
- inOrder.verify(mockListenerOne).onServiceNameDiscovered(any());
- inOrder.verify(mockListenerOne).onServiceFound(any());
+ inOrder.verify(mockListenerOne).onServiceNameDiscovered(
+ any(), eq(false) /* isServiceFromCache */);
+ inOrder.verify(mockListenerOne).onServiceFound(
+ any(), eq(false) /* isServiceFromCache */);
// Expect no query on the next run
currentThreadExecutor.getAndClearLastScheduledRunnable().run();
@@ -1355,24 +1369,29 @@
// mockListenerOne gets notified for the requested instance
verify(mockListenerOne).onServiceNameDiscovered(
- matchServiceName(capitalizedRequestInstance));
- verify(mockListenerOne).onServiceFound(matchServiceName(capitalizedRequestInstance));
+ matchServiceName(capitalizedRequestInstance), eq(false) /* isServiceFromCache */);
+ verify(mockListenerOne).onServiceFound(
+ matchServiceName(capitalizedRequestInstance), eq(false) /* isServiceFromCache */);
// ...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()).onServiceFound(
+ matchServiceName(otherInstance), anyBoolean());
+ verify(mockListenerOne, never()).onServiceNameDiscovered(
+ matchServiceName(otherInstance), anyBoolean());
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(capitalizedRequestInstance));
+ matchServiceName(capitalizedRequestInstance), eq(false) /* isServiceFromCache */);
inOrder.verify(mockListenerTwo).onServiceFound(
- matchServiceName(capitalizedRequestInstance));
+ matchServiceName(capitalizedRequestInstance), eq(false) /* isServiceFromCache */);
- inOrder.verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
- inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
+ inOrder.verify(mockListenerTwo).onServiceNameDiscovered(
+ matchServiceName(otherInstance), eq(false) /* isServiceFromCache */);
+ inOrder.verify(mockListenerTwo).onServiceFound(
+ matchServiceName(otherInstance), eq(false) /* isServiceFromCache */);
inOrder.verify(mockListenerTwo).onServiceUpdated(matchServiceName(otherInstance));
inOrder.verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
}
@@ -1439,22 +1458,30 @@
final ArgumentMatcher<MdnsServiceInfo> subtypeInstanceMatcher = info ->
info.getServiceInstanceName().equals(matchingInstance)
&& info.getSubtypes().equals(Collections.singletonList(subtype));
- verify(mockListenerOne).onServiceNameDiscovered(argThat(subtypeInstanceMatcher));
- verify(mockListenerOne).onServiceFound(argThat(subtypeInstanceMatcher));
+ verify(mockListenerOne).onServiceNameDiscovered(
+ argThat(subtypeInstanceMatcher), eq(false) /* isServiceFromCache */);
+ verify(mockListenerOne).onServiceFound(
+ argThat(subtypeInstanceMatcher), eq(false) /* isServiceFromCache */);
// ...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()).onServiceFound(
+ matchServiceName(otherInstance), anyBoolean());
+ verify(mockListenerOne, never()).onServiceNameDiscovered(
+ matchServiceName(otherInstance), anyBoolean());
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(argThat(subtypeInstanceMatcher));
- inOrder.verify(mockListenerTwo).onServiceFound(argThat(subtypeInstanceMatcher));
+ inOrder.verify(mockListenerTwo).onServiceNameDiscovered(
+ argThat(subtypeInstanceMatcher), eq(false) /* isServiceFromCache */);
+ inOrder.verify(mockListenerTwo).onServiceFound(
+ argThat(subtypeInstanceMatcher), eq(false) /* isServiceFromCache */);
- inOrder.verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
- inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
+ inOrder.verify(mockListenerTwo).onServiceNameDiscovered(
+ matchServiceName(otherInstance), eq(false) /* isServiceFromCache */);
+ inOrder.verify(mockListenerTwo).onServiceFound(
+ matchServiceName(otherInstance), eq(false) /* isServiceFromCache */);
inOrder.verify(mockListenerTwo).onServiceUpdated(matchServiceName(otherInstance));
inOrder.verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
}
@@ -1508,24 +1535,30 @@
// mockListenerOne gets notified for the requested instance
final InOrder inOrder1 = inOrder(mockListenerOne);
inOrder1.verify(mockListenerOne).onServiceNameDiscovered(
- matchServiceName(requestedInstance));
- inOrder1.verify(mockListenerOne).onServiceFound(matchServiceName(requestedInstance));
+ matchServiceName(requestedInstance), eq(false) /* isServiceFromCache */);
+ inOrder1.verify(mockListenerOne).onServiceFound(
+ matchServiceName(requestedInstance), eq(false) /* isServiceFromCache */);
inOrder1.verify(mockListenerOne).onServiceRemoved(matchServiceName(requestedInstance));
inOrder1.verify(mockListenerOne).onServiceNameRemoved(matchServiceName(requestedInstance));
- verify(mockListenerOne, never()).onServiceFound(matchServiceName(otherInstance));
- verify(mockListenerOne, never()).onServiceNameDiscovered(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceFound(
+ matchServiceName(otherInstance), anyBoolean());
+ verify(mockListenerOne, never()).onServiceNameDiscovered(
+ matchServiceName(otherInstance), anyBoolean());
verify(mockListenerOne, never()).onServiceRemoved(matchServiceName(otherInstance));
verify(mockListenerOne, never()).onServiceNameRemoved(matchServiceName(otherInstance));
// mockListenerTwo gets notified for both though
final InOrder inOrder2 = inOrder(mockListenerTwo);
inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(
- matchServiceName(requestedInstance));
- inOrder2.verify(mockListenerTwo).onServiceFound(matchServiceName(requestedInstance));
+ matchServiceName(requestedInstance), eq(false) /* isServiceFromCache */);
+ inOrder2.verify(mockListenerTwo).onServiceFound(
+ matchServiceName(requestedInstance), eq(false) /* isServiceFromCache */);
inOrder2.verify(mockListenerTwo).onServiceRemoved(matchServiceName(requestedInstance));
inOrder2.verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(requestedInstance));
- verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
- verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
+ verify(mockListenerTwo).onServiceNameDiscovered(
+ matchServiceName(otherInstance), eq(false) /* isServiceFromCache */);
+ verify(mockListenerTwo).onServiceFound(
+ matchServiceName(otherInstance), eq(false) /* isServiceFromCache */);
verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(otherInstance));
}
@@ -1547,7 +1580,8 @@
socketKey);
// Verify that onServiceNameDiscovered is called.
- inOrder.verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ inOrder.verify(mockListenerOne).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
serviceName,
SERVICE_TYPE_LABELS,
@@ -1559,7 +1593,8 @@
socketKey);
// Verify that onServiceFound is called.
- inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ inOrder.verify(mockListenerOne).onServiceFound(
+ serviceInfoCaptor.capture(), eq(false) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(1),
serviceName,
SERVICE_TYPE_LABELS,
@@ -1581,7 +1616,8 @@
// The services are cached in MdnsServiceCache, verify that onServiceNameDiscovered is
// called immediately.
- inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(
+ serviceInfoCaptor.capture(), eq(true) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(2),
serviceName,
SERVICE_TYPE_LABELS,
@@ -1594,7 +1630,8 @@
// The services are cached in MdnsServiceCache, verify that onServiceFound is
// called immediately.
- inOrder2.verify(mockListenerTwo).onServiceFound(serviceInfoCaptor.capture());
+ inOrder2.verify(mockListenerTwo).onServiceFound(
+ serviceInfoCaptor.capture(), eq(true) /* isServiceFromCache */);
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(3),
serviceName,
SERVICE_TYPE_LABELS,