diff --git a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java index 052019fc80..665c41cdf6 100644 --- a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java +++ b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java @@ -221,4 +221,52 @@ public class NetworkNsdReportedMetrics { builder.setEventDurationMillisec(durationMs); mDependencies.statsWrite(builder.build()); } + + /** + * Report service info callback registered metric data. + * + * @param transactionId The transaction id of service info callback registration. + */ + public void reportServiceInfoCallbackRegistered(int transactionId) { + final Builder builder = makeReportedBuilder(); + builder.setTransactionId(transactionId); + builder.setType(NsdEventType.NET_SERVICE_INFO_CALLBACK); + builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_INFO_CALLBACK_REGISTERED); + mDependencies.statsWrite(builder.build()); + } + + /** + * Report service info callback registration failed metric data. + * + * @param transactionId The transaction id of service callback registration. + */ + public void reportServiceInfoCallbackRegistrationFailed(int transactionId) { + final Builder builder = makeReportedBuilder(); + builder.setTransactionId(transactionId); + builder.setType(NsdEventType.NET_SERVICE_INFO_CALLBACK); + builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_INFO_CALLBACK_REGISTRATION_FAILED); + mDependencies.statsWrite(builder.build()); + } + + /** + * Report service callback unregistered metric data. + * + * @param transactionId The transaction id of service callback registration. + * @param durationMs The duration of service callback stayed registered. + * @param updateCallbackCount The count of service update callbacks during this registration. + * @param lostCallbackCount The count of service lost callbacks during this registration. + * @param isServiceFromCache Whether the resolved service is from cache. + */ + public void reportServiceInfoCallbackUnregistered(int transactionId, long durationMs, + int updateCallbackCount, int lostCallbackCount, boolean isServiceFromCache) { + final Builder builder = makeReportedBuilder(); + builder.setTransactionId(transactionId); + builder.setType(NsdEventType.NET_SERVICE_INFO_CALLBACK); + builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_INFO_CALLBACK_UNREGISTERED); + builder.setEventDurationMillisec(durationMs); + builder.setFoundCallbackCount(updateCallbackCount); + builder.setLostCallbackCount(lostCallbackCount); + builder.setIsKnownService(isServiceFromCache); + 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 0a7ac9b94b..e189d69aca 100644 --- a/service-t/src/com/android/server/NsdService.java +++ b/service-t/src/com/android/server/NsdService.java @@ -1114,6 +1114,7 @@ public class NsdService extends INsdManager.Stub { resolveServiceType, listener, options); storeDiscoveryManagerRequestMap(clientRequestId, transactionId, listener, clientInfo, info.getNetwork()); + clientInfo.onServiceInfoCallbackRegistered(transactionId); clientInfo.log("Register a ServiceInfoListener " + transactionId + " for service type:" + resolveServiceType); break; @@ -1140,7 +1141,7 @@ public class NsdService extends INsdManager.Stub { if (request instanceof DiscoveryManagerRequest) { stopDiscoveryManagerRequest( request, clientRequestId, transactionId, clientInfo); - clientInfo.onServiceInfoCallbackUnregistered(clientRequestId); + clientInfo.onServiceInfoCallbackUnregistered(clientRequestId, request); clientInfo.log("Unregister the ServiceInfoListener " + transactionId); } else { loge("Unregister failed with non-DiscoveryManagerRequest."); @@ -1478,11 +1479,17 @@ public class NsdService extends INsdManager.Stub { final List addresses = getInetAddresses(serviceInfo); info.setHostAddresses(addresses); - clientInfo.onServiceUpdated(clientRequestId, info); + clientInfo.onServiceUpdated(clientRequestId, info, request); + // Set the ServiceFromCache flag only if the service is actually being + // retrieved from the cache. This flag should not be overridden by later + // service updates, which may not be cached. + if (event.mIsServiceFromCache) { + request.setServiceFromCache(true); + } break; } case NsdManager.SERVICE_UPDATED_LOST: - clientInfo.onServiceUpdatedLost(clientRequestId); + clientInfo.onServiceUpdatedLost(clientRequestId, request); break; default: return false; @@ -2396,6 +2403,12 @@ public class NsdService extends INsdManager.Stub { } else if (listener instanceof ResolutionListener) { mMetrics.reportServiceResolutionStop(transactionId, request.calculateRequestDurationMs(mClock.elapsedRealtime())); + } else if (listener instanceof ServiceInfoListener) { + mMetrics.reportServiceInfoCallbackUnregistered(transactionId, + request.calculateRequestDurationMs(mClock.elapsedRealtime()), + request.getFoundServiceCount(), + request.getLostServiceCount(), + request.isServiceFromCache()); } continue; } @@ -2622,6 +2635,7 @@ public class NsdService extends INsdManager.Stub { } void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) { + mMetrics.reportServiceInfoCallbackRegistrationFailed(NO_TRANSACTION); try { mCb.onServiceInfoCallbackRegistrationFailed(listenerKey, error); } catch (RemoteException e) { @@ -2629,7 +2643,12 @@ public class NsdService extends INsdManager.Stub { } } - void onServiceUpdated(int listenerKey, NsdServiceInfo info) { + void onServiceInfoCallbackRegistered(int transactionId) { + mMetrics.reportServiceInfoCallbackRegistered(transactionId); + } + + void onServiceUpdated(int listenerKey, NsdServiceInfo info, ClientRequest request) { + request.onServiceFound(info.getServiceName()); try { mCb.onServiceUpdated(listenerKey, info); } catch (RemoteException e) { @@ -2637,7 +2656,8 @@ public class NsdService extends INsdManager.Stub { } } - void onServiceUpdatedLost(int listenerKey) { + void onServiceUpdatedLost(int listenerKey, ClientRequest request) { + request.onServiceLost(); try { mCb.onServiceUpdatedLost(listenerKey); } catch (RemoteException e) { @@ -2645,7 +2665,13 @@ public class NsdService extends INsdManager.Stub { } } - void onServiceInfoCallbackUnregistered(int listenerKey) { + void onServiceInfoCallbackUnregistered(int listenerKey, ClientRequest request) { + mMetrics.reportServiceInfoCallbackUnregistered( + request.mTransactionId, + request.calculateRequestDurationMs(mClock.elapsedRealtime()), + request.getFoundServiceCount(), + request.getLostServiceCount(), + request.isServiceFromCache()); try { mCb.onServiceInfoCallbackUnregistered(listenerKey); } catch (RemoteException e) { diff --git a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt index 97aa5759cb..740aa883a2 100644 --- a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt +++ b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt @@ -221,4 +221,67 @@ class NetworkNsdReportedMetricsTest { assertEquals(durationMs, it.eventDurationMillisec) } } + + @Test + fun testReportServiceInfoCallbackRegistered() { + val clientId = 99 + val transactionId = 100 + val metrics = NetworkNsdReportedMetrics(false /* isLegacy */, clientId, deps) + metrics.reportServiceInfoCallbackRegistered(transactionId) + + 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_SERVICE_INFO_CALLBACK, it.type) + assertEquals(MdnsQueryResult.MQR_SERVICE_INFO_CALLBACK_REGISTERED, it.queryResult) + } + } + + @Test + fun testReportServiceInfoCallbackRegistrationFailed() { + val clientId = 99 + val transactionId = 100 + val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps) + metrics.reportServiceInfoCallbackRegistrationFailed(transactionId) + + 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_SERVICE_INFO_CALLBACK, it.type) + assertEquals( + MdnsQueryResult.MQR_SERVICE_INFO_CALLBACK_REGISTRATION_FAILED, it.queryResult) + } + } + + @Test + fun testReportServiceInfoCallbackUnregistered() { + val clientId = 99 + val transactionId = 100 + val durationMs = 10L + val updateCallbackCount = 100 + val lostCallbackCount = 10 + val metrics = NetworkNsdReportedMetrics(false /* isLegacy */, clientId, deps) + metrics.reportServiceInfoCallbackUnregistered(transactionId, durationMs, + updateCallbackCount, lostCallbackCount, false /* isServiceFromCache */) + + 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_SERVICE_INFO_CALLBACK, it.type) + assertEquals(MdnsQueryResult.MQR_SERVICE_INFO_CALLBACK_UNREGISTERED, it.queryResult) + assertEquals(durationMs, it.eventDurationMillisec) + assertEquals(updateCallbackCount, it.foundCallbackCount) + assertEquals(lostCallbackCount, it.lostCallbackCount) + assertFalse(it.isKnownService) + } + } } diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java index c715c8e7af..ffc7904cbf 100644 --- a/tests/unit/java/com/android/server/NsdServiceTest.java +++ b/tests/unit/java/com/android/server/NsdServiceTest.java @@ -822,13 +822,17 @@ public class NsdServiceTest { client.registerServiceInfoCallback(request, Runnable::run, serviceInfoCallback); waitForIdle(); // Verify the registration callback start. - final ArgumentCaptor listenerCaptor = - ArgumentCaptor.forClass(MdnsServiceBrowserListener.class); + final ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(MdnsListener.class); verify(mSocketProvider).startMonitoringSockets(); verify(mDiscoveryManager).registerListener(eq(serviceTypeWithLocalDomain), listenerCaptor.capture(), argThat(options -> network.equals(options.getNetwork()))); - final MdnsServiceBrowserListener listener = listenerCaptor.getValue(); + final MdnsListener listener = listenerCaptor.getValue(); + final int servInfoId = listener.mTransactionId; + // Verify the service info callback registered. + verify(mMetrics).reportServiceInfoCallbackRegistered(servInfoId); + final MdnsServiceInfo mdnsServiceInfo = new MdnsServiceInfo( SERVICE_NAME, serviceTypeWithLocalDomain.split("\\."), @@ -842,8 +846,11 @@ public class NsdServiceTest { 1234, network); + // Callbacks for query sent. + listener.onDiscoveryQuerySent(Collections.emptyList(), 1 /* transactionId */); + // Verify onServiceFound callback - listener.onServiceFound(mdnsServiceInfo, false /* isServiceFromCache */); + listener.onServiceFound(mdnsServiceInfo, true /* isServiceFromCache */); final ArgumentCaptor updateInfoCaptor = ArgumentCaptor.forClass(NsdServiceInfo.class); verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(1)) @@ -878,10 +885,18 @@ public class NsdServiceTest { List.of(parseNumericAddress(v4Address), parseNumericAddress(v6Address)), PORT, IFACE_IDX_ANY, new Network(999)); + // Service lost then recovered. + listener.onServiceRemoved(updatedServiceInfo); + listener.onServiceFound(updatedServiceInfo, false /* isServiceFromCache */); + // Verify service callback unregistration. + doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); client.unregisterServiceInfoCallback(serviceInfoCallback); waitForIdle(); verify(serviceInfoCallback, timeout(TIMEOUT_MS)).onServiceInfoCallbackUnregistered(); + verify(mMetrics).reportServiceInfoCallbackUnregistered(servInfoId, 10L /* durationMs */, + 3 /* updateCallbackCount */, 1 /* lostCallbackCount */, + true /* isServiceFromCache */); } @Test @@ -897,6 +912,7 @@ public class NsdServiceTest { // Fail to register service callback. verify(serviceInfoCallback, timeout(TIMEOUT_MS)) .onServiceInfoCallbackRegistrationFailed(eq(FAILURE_BAD_PARAMETERS)); + verify(mMetrics).reportServiceInfoCallbackRegistrationFailed(NO_TRANSACTION); } @Test