diff --git a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java index 6c8e273686..bcedbefe0e 100644 --- a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java +++ b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java @@ -16,6 +16,14 @@ package com.android.metrics; +import static com.android.metrics.NetworkNsdReported.Builder; + +import android.stats.connectivity.MdnsQueryResult; +import android.stats.connectivity.NsdEventType; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.ConnectivityStatsLog; + /** * Class to record the NetworkNsdReported into statsd. Each client should create this class to * report its data. @@ -25,11 +33,93 @@ public class NetworkNsdReportedMetrics { private final boolean mIsLegacy; // The client id. private final int mClientId; + private final Dependencies mDependencies; public NetworkNsdReportedMetrics(boolean isLegacy, int clientId) { - mIsLegacy = isLegacy; - mClientId = clientId; + this(isLegacy, clientId, new Dependencies()); } - // TODO: Report metrics data. + @VisibleForTesting + NetworkNsdReportedMetrics(boolean isLegacy, int clientId, Dependencies dependencies) { + mIsLegacy = isLegacy; + mClientId = clientId; + mDependencies = dependencies; + } + + /** + * Dependencies of NetworkNsdReportedMetrics, for injection in tests. + */ + public static class Dependencies { + + /** + * @see ConnectivityStatsLog + */ + public void statsWrite(NetworkNsdReported event) { + ConnectivityStatsLog.write(ConnectivityStatsLog.NETWORK_NSD_REPORTED, + event.getIsLegacy(), + event.getClientId(), + event.getTransactionId(), + event.getIsKnownService(), + event.getType().getNumber(), + event.getEventDurationMillisec(), + event.getQueryResult().getNumber(), + event.getFoundServiceCount(), + event.getFoundCallbackCount(), + event.getLostCallbackCount(), + event.getRepliedRequestsCount()); + } + } + + private Builder makeReportedBuilder() { + final Builder builder = NetworkNsdReported.newBuilder(); + builder.setIsLegacy(mIsLegacy); + builder.setClientId(mClientId); + return builder; + } + + /** + * Report service registration succeeded metric data. + * + * @param transactionId The transaction id of service registration. + * @param durationMs The duration of service registration success. + */ + public void reportServiceRegistrationSucceeded(int transactionId, long durationMs) { + final Builder builder = makeReportedBuilder(); + builder.setTransactionId(transactionId); + builder.setType(NsdEventType.NET_REGISTER); + builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_REGISTERED); + builder.setEventDurationMillisec(durationMs); + mDependencies.statsWrite(builder.build()); + } + + /** + * Report service registration failed metric data. + * + * @param transactionId The transaction id of service registration. + * @param durationMs The duration of service registration failed. + */ + public void reportServiceRegistrationFailed(int transactionId, long durationMs) { + final Builder builder = makeReportedBuilder(); + builder.setTransactionId(transactionId); + builder.setType(NsdEventType.NET_REGISTER); + builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_REGISTRATION_FAILED); + builder.setEventDurationMillisec(durationMs); + mDependencies.statsWrite(builder.build()); + } + + /** + * Report service unregistration success metric data. + * + * @param transactionId The transaction id of service registration. + * @param durationMs The duration of service stayed registered. + */ + public void reportServiceUnregistration(int transactionId, long durationMs) { + final Builder builder = makeReportedBuilder(); + builder.setTransactionId(transactionId); + builder.setType(NsdEventType.NET_REGISTER); + builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_UNREGISTERED); + builder.setEventDurationMillisec(durationMs); + // TODO: Report repliedRequestsCount + 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 95717c2cdb..745c5bc705 100644 --- a/service-t/src/com/android/server/NsdService.java +++ b/service-t/src/com/android/server/NsdService.java @@ -162,6 +162,7 @@ public class NsdService extends INsdManager.Stub { public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final long CLEANUP_DELAY_MS = 10000; private static final int IFACE_IDX_ANY = 0; + private static final int NO_TRANSACTION = -1; private static final SharedLog LOGGER = new SharedLog("serviceDiscovery"); private final Context mContext; @@ -178,6 +179,8 @@ public class NsdService extends INsdManager.Stub { private final MdnsSocketProvider mMdnsSocketProvider; @NonNull private final MdnsAdvertiser mAdvertiser; + @NonNull + private final Clock mClock; private final SharedLog mServiceLogs = LOGGER.forSubComponent(TAG); // WARNING : Accessing these values in any thread is not safe, it must only be changed in the // state machine thread. If change this outside state machine, it will need to introduce @@ -530,8 +533,9 @@ public class NsdService extends INsdManager.Stub { try { cb.asBinder().linkToDeath(arg.connector, 0); final String tag = "Client" + arg.uid + "-" + mClientNumberId++; - final NetworkNsdReportedMetrics metrics = new NetworkNsdReportedMetrics( - !arg.useJavaBackend, (int) new Clock().elapsedRealtime()); + final NetworkNsdReportedMetrics metrics = + mDeps.makeNetworkNsdReportedMetrics( + !arg.useJavaBackend, (int) mClock.elapsedRealtime()); cInfo = new ClientInfo(cb, arg.uid, arg.useJavaBackend, mServiceLogs.forSubComponent(tag), metrics); mClients.put(arg.connector, cInfo); @@ -569,7 +573,7 @@ public class NsdService extends INsdManager.Stub { case NsdManager.REGISTER_SERVICE: cInfo = getClientInfoForReply(msg); if (cInfo != null) { - cInfo.onRegisterServiceFailed( + cInfo.onRegisterServiceFailedImmediately( clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR); } break; @@ -651,8 +655,8 @@ public class NsdService extends INsdManager.Stub { private void storeLegacyRequestMap(int clientRequestId, int transactionId, ClientInfo clientInfo, int what) { - clientInfo.mClientRequests.put( - clientRequestId, new LegacyClientRequest(transactionId, what)); + clientInfo.mClientRequests.put(clientRequestId, new LegacyClientRequest( + transactionId, what, mClock, mClock.elapsedRealtime())); mTransactionIdToClientInfoMap.put(transactionId, clientInfo); // Remove the cleanup event because here comes a new request. cancelStop(); @@ -660,8 +664,8 @@ public class NsdService extends INsdManager.Stub { private void storeAdvertiserRequestMap(int clientRequestId, int transactionId, ClientInfo clientInfo, @Nullable Network requestedNetwork) { - clientInfo.mClientRequests.put(clientRequestId, - new AdvertiserClientRequest(transactionId, requestedNetwork)); + clientInfo.mClientRequests.put(clientRequestId, new AdvertiserClientRequest( + transactionId, requestedNetwork, mClock, mClock.elapsedRealtime())); mTransactionIdToClientInfoMap.put(transactionId, clientInfo); updateMulticastLock(); } @@ -684,8 +688,9 @@ public class NsdService extends INsdManager.Stub { private void storeDiscoveryManagerRequestMap(int clientRequestId, int transactionId, MdnsListener listener, ClientInfo clientInfo, @Nullable Network requestedNetwork) { - clientInfo.mClientRequests.put(clientRequestId, - new DiscoveryManagerRequest(transactionId, listener, requestedNetwork)); + clientInfo.mClientRequests.put(clientRequestId, new DiscoveryManagerRequest( + transactionId, listener, requestedNetwork, mClock, + mClock.elapsedRealtime())); mTransactionIdToClientInfoMap.put(transactionId, clientInfo); updateMulticastLock(); } @@ -838,7 +843,7 @@ public class NsdService extends INsdManager.Stub { } if (requestLimitReached(clientInfo)) { - clientInfo.onRegisterServiceFailed( + clientInfo.onRegisterServiceFailedImmediately( clientRequestId, NsdManager.FAILURE_MAX_LIMIT); break; } @@ -854,8 +859,8 @@ public class NsdService extends INsdManager.Stub { || useAdvertiserForType(registerServiceType)) { if (registerServiceType == null) { Log.e(TAG, "Invalid service type: " + serviceType); - clientInfo.onRegisterServiceFailed(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR); + clientInfo.onRegisterServiceFailedImmediately( + clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR); break; } serviceInfo.setServiceType(registerServiceType); @@ -882,7 +887,7 @@ public class NsdService extends INsdManager.Stub { // Return success after mDns reports success } else { unregisterService(transactionId); - clientInfo.onRegisterServiceFailed( + clientInfo.onRegisterServiceFailedImmediately( clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR); } @@ -914,10 +919,12 @@ public class NsdService extends INsdManager.Stub { // instead of looking at the flag value. if (request instanceof AdvertiserClientRequest) { mAdvertiser.removeService(transactionId); - clientInfo.onUnregisterServiceSucceeded(clientRequestId); + clientInfo.onUnregisterServiceSucceeded(clientRequestId, transactionId, + request.calculateRequestDurationMs()); } else { if (unregisterService(transactionId)) { - clientInfo.onUnregisterServiceSucceeded(clientRequestId); + clientInfo.onUnregisterServiceSucceeded(clientRequestId, + transactionId, request.calculateRequestDurationMs()); } else { clientInfo.onUnregisterServiceFailed( clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR); @@ -1181,12 +1188,18 @@ public class NsdService extends INsdManager.Stub { final RegistrationInfo info = (RegistrationInfo) obj; final String name = info.serviceName; servInfo = new NsdServiceInfo(name, null /* serviceType */); - clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo); + final ClientRequest request = + clientInfo.mClientRequests.get(clientRequestId); + clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo, + transactionId, request.calculateRequestDurationMs()); break; } case IMDnsEventListener.SERVICE_REGISTRATION_FAILED: - clientInfo.onRegisterServiceFailed( - clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR); + final ClientRequest request = + clientInfo.mClientRequests.get(clientRequestId); + clientInfo.onRegisterServiceFailed(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, transactionId, + request.calculateRequestDurationMs()); break; case IMDnsEventListener.SERVICE_RESOLVED: { final ResolutionInfo info = (ResolutionInfo) obj; @@ -1566,6 +1579,7 @@ public class NsdService extends INsdManager.Stub { handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager)); mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider, new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser")); + mClock = deps.makeClock(); } /** @@ -1655,6 +1669,21 @@ public class NsdService extends INsdManager.Stub { public int getCallingUid() { return Binder.getCallingUid(); } + + /** + * @see NetworkNsdReportedMetrics + */ + public NetworkNsdReportedMetrics makeNetworkNsdReportedMetrics( + boolean isLegacy, int clientId) { + return new NetworkNsdReportedMetrics(isLegacy, clientId); + } + + /** + * @see MdnsUtils.Clock + */ + public Clock makeClock() { + return new Clock(); + } } /** @@ -1755,7 +1784,9 @@ public class NsdService extends INsdManager.Stub { // onRegisterServiceSucceeded only has the service name in its info. This aligns with // historical behavior. final NsdServiceInfo cbInfo = new NsdServiceInfo(registeredInfo.getServiceName(), null); - clientInfo.onRegisterServiceSucceeded(clientRequestId, cbInfo); + final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId); + clientInfo.onRegisterServiceSucceeded( + clientRequestId, cbInfo, transactionId, request.calculateRequestDurationMs()); } @Override @@ -1765,8 +1796,9 @@ public class NsdService extends INsdManager.Stub { final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId); if (clientRequestId < 0) return; - - clientInfo.onRegisterServiceFailed(clientRequestId, errorCode); + final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId); + clientInfo.onRegisterServiceFailed(clientRequestId, errorCode, transactionId, + request.calculateRequestDurationMs()); } private ClientInfo getClientInfoOrLog(int transactionId) { @@ -2026,17 +2058,27 @@ public class NsdService extends INsdManager.Stub { private abstract static class ClientRequest { private final int mTransactionId; + private final Clock mClock; + private final long mStartTimeMs; - private ClientRequest(int transactionId) { + private ClientRequest(int transactionId, @NonNull Clock clock, long startTimeMs) { mTransactionId = transactionId; + mClock = clock; + mStartTimeMs = startTimeMs; + } + + public long calculateRequestDurationMs() { + final long stopTimeMs = mClock.elapsedRealtime(); + return stopTimeMs - mStartTimeMs; } } private static class LegacyClientRequest extends ClientRequest { private final int mRequestCode; - private LegacyClientRequest(int transactionId, int requestCode) { - super(transactionId); + private LegacyClientRequest(int transactionId, int requestCode, @NonNull Clock clock, + long startTimeMs) { + super(transactionId, clock, startTimeMs); mRequestCode = requestCode; } } @@ -2045,8 +2087,9 @@ public class NsdService extends INsdManager.Stub { @Nullable private final Network mRequestedNetwork; - private JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork) { - super(transactionId); + private JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork, + @NonNull Clock clock, long startTimeMs) { + super(transactionId, clock, startTimeMs); mRequestedNetwork = requestedNetwork; } @@ -2057,8 +2100,9 @@ public class NsdService extends INsdManager.Stub { } private static class AdvertiserClientRequest extends JavaBackendClientRequest { - private AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork) { - super(transactionId, requestedNetwork); + private AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork, + @NonNull Clock clock, long startTimeMs) { + super(transactionId, requestedNetwork, clock, startTimeMs); } } @@ -2067,8 +2111,8 @@ public class NsdService extends INsdManager.Stub { private final MdnsListener mListener; private DiscoveryManagerRequest(int transactionId, @NonNull MdnsListener listener, - @Nullable Network requestedNetwork) { - super(transactionId, requestedNetwork); + @Nullable Network requestedNetwork, @NonNull Clock clock, long startTimeMs) { + super(transactionId, requestedNetwork, clock, startTimeMs); mListener = listener; } } @@ -2161,6 +2205,8 @@ public class NsdService extends INsdManager.Stub { if (request instanceof AdvertiserClientRequest) { mAdvertiser.removeService(transactionId); + mMetrics.reportServiceUnregistration( + transactionId, request.calculateRequestDurationMs()); continue; } @@ -2177,6 +2223,8 @@ public class NsdService extends INsdManager.Stub { break; case NsdManager.REGISTER_SERVICE: unregisterService(transactionId); + mMetrics.reportServiceUnregistration( + transactionId, request.calculateRequestDurationMs()); break; default: break; @@ -2268,7 +2316,13 @@ public class NsdService extends INsdManager.Stub { } } - void onRegisterServiceFailed(int listenerKey, int error) { + void onRegisterServiceFailedImmediately(int listenerKey, int error) { + onRegisterServiceFailed(listenerKey, error, NO_TRANSACTION, 0 /* durationMs */); + } + + void onRegisterServiceFailed(int listenerKey, int error, int transactionId, + long durationMs) { + mMetrics.reportServiceRegistrationFailed(transactionId, durationMs); try { mCb.onRegisterServiceFailed(listenerKey, error); } catch (RemoteException e) { @@ -2276,7 +2330,9 @@ public class NsdService extends INsdManager.Stub { } } - void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) { + void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info, int transactionId, + long durationMs) { + mMetrics.reportServiceRegistrationSucceeded(transactionId, durationMs); try { mCb.onRegisterServiceSucceeded(listenerKey, info); } catch (RemoteException e) { @@ -2292,7 +2348,8 @@ public class NsdService extends INsdManager.Stub { } } - void onUnregisterServiceSucceeded(int listenerKey) { + void onUnregisterServiceSucceeded(int listenerKey, int transactionId, long durationMs) { + mMetrics.reportServiceUnregistration(transactionId, durationMs); try { mCb.onUnregisterServiceSucceeded(listenerKey); } catch (RemoteException e) { diff --git a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt new file mode 100644 index 0000000000..961c4222b4 --- /dev/null +++ b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.metrics + +import android.os.Build +import android.stats.connectivity.MdnsQueryResult +import android.stats.connectivity.NsdEventType +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRunner +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify + +@RunWith(DevSdkIgnoreRunner::class) +@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) +class NetworkNsdReportedMetricsTest { + private val deps = mock(NetworkNsdReportedMetrics.Dependencies::class.java) + + @Test + fun testReportServiceRegistrationSucceeded() { + val clientId = 99 + val transactionId = 100 + val durationMs = 10L + val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps) + metrics.reportServiceRegistrationSucceeded(transactionId, durationMs) + + 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_REGISTER, it.type) + assertEquals(MdnsQueryResult.MQR_SERVICE_REGISTERED, it.queryResult) + assertEquals(durationMs, it.eventDurationMillisec) + } + } + + @Test + fun testReportServiceRegistrationFailed() { + val clientId = 99 + val transactionId = 100 + val durationMs = 10L + val metrics = NetworkNsdReportedMetrics(false /* isLegacy */, clientId, deps) + metrics.reportServiceRegistrationFailed(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_REGISTER, it.type) + assertEquals(MdnsQueryResult.MQR_SERVICE_REGISTRATION_FAILED, it.queryResult) + assertEquals(durationMs, it.eventDurationMillisec) + } + } + + @Test + fun testReportServiceUnregistration() { + val clientId = 99 + val transactionId = 100 + val durationMs = 10L + val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps) + metrics.reportServiceUnregistration(transactionId, durationMs) + + 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_REGISTER, it.type) + assertEquals(MdnsQueryResult.MQR_SERVICE_UNREGISTERED, 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 f51b28d515..55384b3d6a 100644 --- a/tests/unit/java/com/android/server/NsdServiceTest.java +++ b/tests/unit/java/com/android/server/NsdServiceTest.java @@ -43,7 +43,9 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.any; @@ -95,6 +97,7 @@ import android.util.Pair; import androidx.annotation.NonNull; import androidx.test.filters.SmallTest; +import com.android.metrics.NetworkNsdReportedMetrics; import com.android.server.NsdService.Dependencies; import com.android.server.connectivity.mdns.MdnsAdvertiser; import com.android.server.connectivity.mdns.MdnsDiscoveryManager; @@ -104,6 +107,7 @@ import com.android.server.connectivity.mdns.MdnsServiceBrowserListener; import com.android.server.connectivity.mdns.MdnsServiceInfo; import com.android.server.connectivity.mdns.MdnsSocketProvider; import com.android.server.connectivity.mdns.MdnsSocketProvider.SocketRequestMonitor; +import com.android.server.connectivity.mdns.util.MdnsUtils; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRunner; import com.android.testutils.HandlerUtils; @@ -138,6 +142,7 @@ public class NsdServiceTest { static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD; private static final long CLEANUP_DELAY_MS = 500; private static final long TIMEOUT_MS = 500; + private static final long TEST_TIME_MS = 123L; private static final String SERVICE_NAME = "a_name"; private static final String SERVICE_TYPE = "_test._tcp"; private static final String SERVICE_FULL_NAME = SERVICE_NAME + "." + SERVICE_TYPE; @@ -164,6 +169,8 @@ public class NsdServiceTest { @Mock WifiManager mWifiManager; @Mock WifiManager.MulticastLock mMulticastLock; @Mock ActivityManager mActivityManager; + @Mock NetworkNsdReportedMetrics mMetrics; + @Mock MdnsUtils.Clock mClock; SocketRequestMonitor mSocketRequestMonitor; OnUidImportanceListener mUidImportanceListener; HandlerThread mThread; @@ -210,6 +217,9 @@ public class NsdServiceTest { doReturn(DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF).when(mDeps).getDeviceConfigInt( eq(NsdService.MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF), anyInt()); doReturn(mAdvertiser).when(mDeps).makeMdnsAdvertiser(any(), any(), any(), any()); + doReturn(mMetrics).when(mDeps).makeNetworkNsdReportedMetrics(anyBoolean(), anyInt()); + doReturn(mClock).when(mDeps).makeClock(); + doReturn(TEST_TIME_MS).when(mClock).elapsedRealtime(); mService = makeService(); final ArgumentCaptor cbMonitorCaptor = ArgumentCaptor.forClass(SocketRequestMonitor.class); @@ -512,14 +522,16 @@ public class NsdServiceTest { eq(SERVICE_NAME), eq(SERVICE_TYPE), eq(PORT), any(), eq(IFACE_IDX_ANY)); // Register service successfully. + final int regId = regIdCaptor.getValue(); final RegistrationInfo registrationInfo = new RegistrationInfo( - regIdCaptor.getValue(), + regId, IMDnsEventListener.SERVICE_REGISTERED, SERVICE_NAME, SERVICE_TYPE, PORT, new byte[0] /* txtRecord */, IFACE_IDX_ANY); + doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); eventListener.onServiceRegistrationStatus(registrationInfo); final ArgumentCaptor registeredInfoCaptor = @@ -528,19 +540,22 @@ public class NsdServiceTest { .onServiceRegistered(registeredInfoCaptor.capture()); final NsdServiceInfo registeredInfo = registeredInfoCaptor.getValue(); assertEquals(SERVICE_NAME, registeredInfo.getServiceName()); + verify(mMetrics).reportServiceRegistrationSucceeded(regId, 10L /* durationMs */); // Fail to register service. final RegistrationInfo registrationFailedInfo = new RegistrationInfo( - regIdCaptor.getValue(), + regId, IMDnsEventListener.SERVICE_REGISTRATION_FAILED, null /* serviceName */, null /* registrationType */, 0 /* port */, new byte[0] /* txtRecord */, IFACE_IDX_ANY); + doReturn(TEST_TIME_MS + 20L).when(mClock).elapsedRealtime(); eventListener.onServiceRegistrationStatus(registrationFailedInfo); verify(regListener, timeout(TIMEOUT_MS)) .onRegistrationFailed(any(), eq(FAILURE_INTERNAL_ERROR)); + verify(mMetrics).reportServiceRegistrationFailed(regId, 20L /* durationMs */); } @Test @@ -1215,17 +1230,22 @@ public class NsdServiceTest { // Verify onServiceRegistered callback final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue(); - cb.onRegisterServiceSucceeded(idCaptor.getValue(), regInfo); + final int regId = idCaptor.getValue(); + doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); + cb.onRegisterServiceSucceeded(regId, regInfo); verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered(argThat(info -> matches(info, new NsdServiceInfo(regInfo.getServiceName(), null)))); + verify(mMetrics).reportServiceRegistrationSucceeded(regId, 10L /* durationMs */); + doReturn(TEST_TIME_MS + 100L).when(mClock).elapsedRealtime(); client.unregisterService(regListener); waitForIdle(); verify(mAdvertiser).removeService(idCaptor.getValue()); verify(regListener, timeout(TIMEOUT_MS)).onServiceUnregistered( argThat(info -> matches(info, regInfo))); verify(mSocketProvider, timeout(TIMEOUT_MS)).requestStopWhenInactive(); + verify(mMetrics).reportServiceUnregistration(regId, 100L /* durationMs */); } @Test @@ -1251,6 +1271,7 @@ public class NsdServiceTest { verify(regListener, timeout(TIMEOUT_MS)).onRegistrationFailed( argThat(info -> matches(info, regInfo)), eq(FAILURE_INTERNAL_ERROR)); + verify(mMetrics).reportServiceRegistrationFailed(anyInt(), anyLong()); } @Test @@ -1280,10 +1301,13 @@ public class NsdServiceTest { // Verify onServiceRegistered callback final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue(); - cb.onRegisterServiceSucceeded(idCaptor.getValue(), regInfo); + final int regId = idCaptor.getValue(); + doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); + cb.onRegisterServiceSucceeded(regId, regInfo); verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered( argThat(info -> matches(info, new NsdServiceInfo(regInfo.getServiceName(), null)))); + verify(mMetrics).reportServiceRegistrationSucceeded(regId, 10L /* durationMs */); } @Test