Merge changes I47b91c0c,I630c0e49 into main

* changes:
  Report sent query count
  Report service info callback metrics data
diff --git a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
index 052019f..597c06f 100644
--- a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
+++ b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
@@ -160,9 +160,10 @@
      * @param foundCallbackCount The count of found service callbacks before stop discovery.
      * @param lostCallbackCount The count of lost service callbacks before stop discovery.
      * @param servicesCount The count of found services.
+     * @param sentQueryCount The count of sent queries before stop discovery.
      */
     public void reportServiceDiscoveryStop(int transactionId, long durationMs,
-            int foundCallbackCount, int lostCallbackCount, int servicesCount) {
+            int foundCallbackCount, int lostCallbackCount, int servicesCount, int sentQueryCount) {
         final Builder builder = makeReportedBuilder();
         builder.setTransactionId(transactionId);
         builder.setType(NsdEventType.NET_DISCOVER);
@@ -171,6 +172,7 @@
         builder.setFoundCallbackCount(foundCallbackCount);
         builder.setLostCallbackCount(lostCallbackCount);
         builder.setFoundServiceCount(servicesCount);
+        builder.setSentQueryCount(sentQueryCount);
         mDependencies.statsWrite(builder.build());
     }
 
@@ -180,15 +182,17 @@
      * @param transactionId The transaction id of service resolution.
      * @param durationMs The duration of resolving services.
      * @param isServiceFromCache Whether the resolved service is from cache.
+     * @param sentQueryCount The count of sent queries during resolving.
      */
     public void reportServiceResolved(int transactionId, long durationMs,
-            boolean isServiceFromCache) {
+            boolean isServiceFromCache, int sentQueryCount) {
         final Builder builder = makeReportedBuilder();
         builder.setTransactionId(transactionId);
         builder.setType(NsdEventType.NET_RESOLVE);
         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_RESOLVED);
         builder.setEventDurationMillisec(durationMs);
         builder.setIsKnownService(isServiceFromCache);
+        builder.setSentQueryCount(sentQueryCount);
         mDependencies.statsWrite(builder.build());
     }
 
@@ -221,4 +225,55 @@
         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.
+     * @param sentQueryCount The count of sent queries during this registration.
+     */
+    public void reportServiceInfoCallbackUnregistered(int transactionId, long durationMs,
+            int updateCallbackCount, int lostCallbackCount, boolean isServiceFromCache,
+            int sentQueryCount) {
+        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);
+        builder.setSentQueryCount(sentQueryCount);
+        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 eb73ee5..6485e99 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -172,6 +172,8 @@
     private static final int MAX_SERVICES_COUNT_METRIC_PER_CLIENT = 100;
     @VisibleForTesting
     static final int NO_TRANSACTION = -1;
+    private static final int NO_SENT_QUERY_COUNT = 0;
+    private static final int DISCOVERY_QUERY_SENT_CALLBACK = 1000;
     private static final SharedLog LOGGER = new SharedLog("serviceDiscovery");
 
     private final Context mContext;
@@ -288,7 +290,8 @@
         public void onSearchFailedToStart() { }
 
         @Override
-        public void onDiscoveryQuerySent(@NonNull List<String> subtypes, int transactionId) { }
+        public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
+                int sentQueryTransactionId) { }
 
         @Override
         public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode) { }
@@ -315,6 +318,13 @@
                     NsdManager.SERVICE_LOST,
                     new MdnsEvent(mClientRequestId, serviceInfo));
         }
+
+        @Override
+        public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
+                int sentQueryTransactionId) {
+            mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
+                    DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
+        }
     }
 
     private class ResolutionListener extends MdnsListener {
@@ -330,6 +340,13 @@
                     NsdManager.RESOLVE_SERVICE_SUCCEEDED,
                     new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
         }
+
+        @Override
+        public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
+                int sentQueryTransactionId) {
+            mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
+                    DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
+        }
     }
 
     private class ServiceInfoListener extends MdnsListener {
@@ -360,6 +377,13 @@
                     NsdManager.SERVICE_UPDATED_LOST,
                     new MdnsEvent(mClientRequestId, serviceInfo));
         }
+
+        @Override
+        public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
+                int sentQueryTransactionId) {
+            mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
+                    DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
+        }
     }
 
     private class SocketRequestMonitor implements MdnsSocketProvider.SocketRequestMonitor {
@@ -465,15 +489,19 @@
      */
     private static class MdnsEvent {
         final int mClientRequestId;
-        @NonNull
+        @Nullable
         final MdnsServiceInfo mMdnsServiceInfo;
         final boolean mIsServiceFromCache;
 
-        MdnsEvent(int clientRequestId, @NonNull MdnsServiceInfo mdnsServiceInfo) {
+        MdnsEvent(int clientRequestId) {
+            this(clientRequestId, null /* mdnsServiceInfo */, false /* isServiceFromCache */);
+        }
+
+        MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo) {
             this(clientRequestId, mdnsServiceInfo, false /* isServiceFromCache */);
         }
 
-        MdnsEvent(int clientRequestId, @NonNull MdnsServiceInfo mdnsServiceInfo,
+        MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo,
                 boolean isServiceFromCache) {
             mClientRequestId = clientRequestId;
             mMdnsServiceInfo = mdnsServiceInfo;
@@ -1114,6 +1142,7 @@
                                 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 +1169,7 @@
                         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.");
@@ -1410,17 +1439,25 @@
 
                 final MdnsEvent event = (MdnsEvent) obj;
                 final int clientRequestId = event.mClientRequestId;
+                final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
+                if (request == null) {
+                    Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
+                    return false;
+                }
+
+                // Deal with the discovery sent callback
+                if (code == DISCOVERY_QUERY_SENT_CALLBACK) {
+                    request.onQuerySent();
+                    return true;
+                }
+
+                // Deal with other callbacks.
                 final NsdServiceInfo info = buildNsdServiceInfoFromMdnsEvent(event, code);
                 // Errors are already logged if null
                 if (info == null) return false;
                 mServiceLogs.log(String.format(
                         "MdnsDiscoveryManager event code=%s transactionId=%d",
                         NsdManager.nameOf(code), transactionId));
-                final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
-                if (request == null) {
-                    Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
-                    return false;
-                }
                 switch (code) {
                     case NsdManager.SERVICE_FOUND:
                         clientInfo.onServiceFound(clientRequestId, info, request);
@@ -1478,11 +1515,17 @@
 
                         final List<InetAddress> 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;
@@ -2218,6 +2261,7 @@
         private int mLostServiceCount = 0;
         private final Set<String> mServices = new ArraySet<>();
         private boolean mIsServiceFromCache = false;
+        private int mSentQueryCount = NO_SENT_QUERY_COUNT;
 
         private ClientRequest(int transactionId, long startTimeMs) {
             mTransactionId = transactionId;
@@ -2258,6 +2302,14 @@
         public boolean isServiceFromCache() {
             return mIsServiceFromCache;
         }
+
+        public void onQuerySent() {
+            mSentQueryCount++;
+        }
+
+        public int getSentQueryCount() {
+            return mSentQueryCount;
+        }
     }
 
     private static class LegacyClientRequest extends ClientRequest {
@@ -2392,10 +2444,18 @@
                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
                                 request.getFoundServiceCount(),
                                 request.getLostServiceCount(),
-                                request.getServicesCount());
+                                request.getServicesCount(),
+                                request.getSentQueryCount());
                     } 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(),
+                                request.getSentQueryCount());
                     }
                     continue;
                 }
@@ -2418,7 +2478,8 @@
                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
                                 request.getFoundServiceCount(),
                                 request.getLostServiceCount(),
-                                request.getServicesCount());
+                                request.getServicesCount(),
+                                NO_SENT_QUERY_COUNT);
                         break;
                     case NsdManager.RESOLVE_SERVICE:
                         stopResolveService(transactionId);
@@ -2526,7 +2587,8 @@
                     request.calculateRequestDurationMs(mClock.elapsedRealtime()),
                     request.getFoundServiceCount(),
                     request.getLostServiceCount(),
-                    request.getServicesCount());
+                    request.getServicesCount(),
+                    request.getSentQueryCount());
             try {
                 mCb.onStopDiscoverySucceeded(listenerKey);
             } catch (RemoteException e) {
@@ -2594,7 +2656,8 @@
             mMetrics.reportServiceResolved(
                     request.mTransactionId,
                     request.calculateRequestDurationMs(mClock.elapsedRealtime()),
-                    request.isServiceFromCache());
+                    request.isServiceFromCache(),
+                    request.getSentQueryCount());
             try {
                 mCb.onResolveServiceSucceeded(listenerKey, info);
             } catch (RemoteException e) {
@@ -2622,6 +2685,7 @@
         }
 
         void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) {
+            mMetrics.reportServiceInfoCallbackRegistrationFailed(NO_TRANSACTION);
             try {
                 mCb.onServiceInfoCallbackRegistrationFailed(listenerKey, error);
             } catch (RemoteException e) {
@@ -2629,7 +2693,12 @@
             }
         }
 
-        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 +2706,8 @@
             }
         }
 
-        void onServiceUpdatedLost(int listenerKey) {
+        void onServiceUpdatedLost(int listenerKey, ClientRequest request) {
+            request.onServiceLost();
             try {
                 mCb.onServiceUpdatedLost(listenerKey);
             } catch (RemoteException e) {
@@ -2645,7 +2715,14 @@
             }
         }
 
-        void onServiceInfoCallbackUnregistered(int listenerKey) {
+        void onServiceInfoCallbackUnregistered(int listenerKey, ClientRequest request) {
+            mMetrics.reportServiceInfoCallbackUnregistered(
+                    request.mTransactionId,
+                    request.calculateRequestDurationMs(mClock.elapsedRealtime()),
+                    request.getFoundServiceCount(),
+                    request.getLostServiceCount(),
+                    request.isServiceFromCache(),
+                    request.getSentQueryCount());
             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 97aa575..7f893df 100644
--- a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
+++ b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
@@ -141,9 +141,10 @@
         val foundCallbackCount = 100
         val lostCallbackCount = 49
         val servicesCount = 75
+        val sentQueryCount = 150
         val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps)
-        metrics.reportServiceDiscoveryStop(
-                transactionId, durationMs, foundCallbackCount, lostCallbackCount, servicesCount)
+        metrics.reportServiceDiscoveryStop(transactionId, durationMs, foundCallbackCount,
+                lostCallbackCount, servicesCount, sentQueryCount)
 
         val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
         verify(deps).statsWrite(eventCaptor.capture())
@@ -158,6 +159,7 @@
             assertEquals(lostCallbackCount, it.lostCallbackCount)
             assertEquals(servicesCount, it.foundServiceCount)
             assertEquals(durationMs, it.eventDurationMillisec)
+            assertEquals(sentQueryCount, it.sentQueryCount)
         }
     }
 
@@ -166,8 +168,10 @@
         val clientId = 99
         val transactionId = 100
         val durationMs = 10L
+        val sentQueryCount = 0
         val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps)
-        metrics.reportServiceResolved(transactionId, durationMs, true /* isServiceFromCache */)
+        metrics.reportServiceResolved(transactionId, durationMs, true /* isServiceFromCache */,
+                sentQueryCount)
 
         val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
         verify(deps).statsWrite(eventCaptor.capture())
@@ -179,6 +183,7 @@
             assertEquals(MdnsQueryResult.MQR_SERVICE_RESOLVED, it.queryResult)
             assertTrue(it.isKnownService)
             assertEquals(durationMs, it.eventDurationMillisec)
+            assertEquals(sentQueryCount, it.sentQueryCount)
         }
     }
 
@@ -221,4 +226,70 @@
             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 sentQueryCount = 150
+        val metrics = NetworkNsdReportedMetrics(false /* isLegacy */, clientId, deps)
+        metrics.reportServiceInfoCallbackUnregistered(transactionId, durationMs,
+                updateCallbackCount, lostCallbackCount, false /* isServiceFromCache */,
+                sentQueryCount)
+
+        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)
+            assertEquals(sentQueryCount, it.sentQueryCount)
+        }
+    }
 }
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index dbd4e4e..f0c7dcc 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -472,8 +472,8 @@
         final ArgumentCaptor<NsdServiceInfo> resInfoCaptor =
                 ArgumentCaptor.forClass(NsdServiceInfo.class);
         verify(resolveListener, timeout(TIMEOUT_MS)).onServiceResolved(resInfoCaptor.capture());
-        verify(mMetrics).reportServiceResolved(
-                getAddrId, 10L /* durationMs */, false /* isServiceFromCache */);
+        verify(mMetrics).reportServiceResolved(getAddrId, 10L /* durationMs */,
+                false /* isServiceFromCache */, 0 /* sentQueryCount */);
 
         final NsdServiceInfo resolvedService = resInfoCaptor.getValue();
         assertEquals(SERVICE_NAME, resolvedService.getServiceName());
@@ -822,13 +822,17 @@
         client.registerServiceInfoCallback(request, Runnable::run, serviceInfoCallback);
         waitForIdle();
         // Verify the registration callback start.
-        final ArgumentCaptor<MdnsServiceBrowserListener> listenerCaptor =
-                ArgumentCaptor.forClass(MdnsServiceBrowserListener.class);
+        final ArgumentCaptor<MdnsListener> 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 @@
                 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<NsdServiceInfo> updateInfoCaptor =
                 ArgumentCaptor.forClass(NsdServiceInfo.class);
         verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(1))
@@ -878,10 +885,18 @@
                 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 */, 1 /* sentQueryCount */);
     }
 
     @Test
@@ -897,6 +912,7 @@
         // Fail to register service callback.
         verify(serviceInfoCallback, timeout(TIMEOUT_MS))
                 .onServiceInfoCallbackRegistrationFailed(eq(FAILURE_BAD_PARAMETERS));
+        verify(mMetrics).reportServiceInfoCallbackRegistrationFailed(NO_TRANSACTION);
     }
 
     @Test
@@ -973,6 +989,11 @@
         final int discId = listener.mTransactionId;
         verify(mMetrics).reportServiceDiscoveryStarted(discId);
 
+        // Callbacks for query sent.
+        listener.onDiscoveryQuerySent(Collections.emptyList(), 1 /* transactionId */);
+        listener.onDiscoveryQuerySent(Collections.emptyList(), 2 /* transactionId */);
+        listener.onDiscoveryQuerySent(Collections.emptyList(), 3 /* transactionId */);
+
         final MdnsServiceInfo foundInfo = new MdnsServiceInfo(
                 SERVICE_NAME, /* serviceInstanceName */
                 serviceTypeWithLocalDomain.split("\\."), /* serviceType */
@@ -1021,7 +1042,8 @@
         verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStopped(SERVICE_TYPE);
         verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).requestStopWhenInactive();
         verify(mMetrics).reportServiceDiscoveryStop(discId, 10L /* durationMs */,
-                1 /* foundCallbackCount */, 1 /* lostCallbackCount */, 1 /* servicesCount */);
+                1 /* foundCallbackCount */, 1 /* lostCallbackCount */, 1 /* servicesCount */,
+                3 /* sentQueryCount */);
     }
 
     @Test
@@ -1133,8 +1155,8 @@
         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 */);
+        verify(mMetrics).reportServiceResolved(listener.mTransactionId, 10 /* durationMs */,
+                true /* isServiceFromCache */, 0 /* sendQueryCount */);
 
         final NsdServiceInfo info = infoCaptor.getValue();
         assertEquals(SERVICE_NAME, info.getServiceName());