1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.metrics;
18 
19 import static com.android.metrics.NetworkNsdReported.Builder;
20 
21 import android.stats.connectivity.MdnsQueryResult;
22 import android.stats.connectivity.NsdEventType;
23 
24 import com.android.internal.annotations.VisibleForTesting;
25 import com.android.server.ConnectivityStatsLog;
26 
27 import java.util.Random;
28 
29 /**
30  * Class to record the NetworkNsdReported into statsd. Each client should create this class to
31  * report its data.
32  */
33 public class NetworkNsdReportedMetrics {
34     // The upper bound for the random number used in metrics data sampling determines the possible
35     // sample rate.
36     private static final int RANDOM_NUMBER_UPPER_BOUND = 1000;
37     // The client id.
38     private final int mClientId;
39     private final Dependencies mDependencies;
40     private final Random mRandom;
41 
NetworkNsdReportedMetrics(int clientId)42     public NetworkNsdReportedMetrics(int clientId) {
43         this(clientId, new Dependencies());
44     }
45 
46     @VisibleForTesting
NetworkNsdReportedMetrics(int clientId, Dependencies dependencies)47     NetworkNsdReportedMetrics(int clientId, Dependencies dependencies) {
48         mClientId = clientId;
49         mDependencies = dependencies;
50         mRandom = dependencies.makeRandomGenerator();
51     }
52 
53     /**
54      * Dependencies of NetworkNsdReportedMetrics, for injection in tests.
55      */
56     public static class Dependencies {
57 
58         /**
59          * @see ConnectivityStatsLog
60          */
statsWrite(NetworkNsdReported event)61         public void statsWrite(NetworkNsdReported event) {
62             ConnectivityStatsLog.write(ConnectivityStatsLog.NETWORK_NSD_REPORTED,
63                     event.getIsLegacy(),
64                     event.getClientId(),
65                     event.getTransactionId(),
66                     event.getIsKnownService(),
67                     event.getType().getNumber(),
68                     event.getEventDurationMillisec(),
69                     event.getQueryResult().getNumber(),
70                     event.getFoundServiceCount(),
71                     event.getFoundCallbackCount(),
72                     event.getLostCallbackCount(),
73                     event.getRepliedRequestsCount(),
74                     event.getSentQueryCount(),
75                     event.getSentPacketCount(),
76                     event.getConflictDuringProbingCount(),
77                     event.getConflictAfterProbingCount(),
78                     event.getRandomNumber());
79         }
80 
81         /**
82          * @see Random
83          */
makeRandomGenerator()84         public Random makeRandomGenerator() {
85             return new Random();
86         }
87     }
88 
makeReportedBuilder(boolean isLegacy, int transactionId)89     private Builder makeReportedBuilder(boolean isLegacy, int transactionId) {
90         final Builder builder = NetworkNsdReported.newBuilder();
91         builder.setIsLegacy(isLegacy);
92         builder.setClientId(mClientId);
93         builder.setRandomNumber(mRandom.nextInt(RANDOM_NUMBER_UPPER_BOUND));
94         builder.setTransactionId(transactionId);
95         return builder;
96     }
97 
98     /**
99      * Report service registration succeeded metric data.
100      *
101      * @param isLegacy Whether this call is using legacy backend.
102      * @param transactionId The transaction id of service registration.
103      * @param durationMs The duration of service registration success.
104      */
reportServiceRegistrationSucceeded(boolean isLegacy, int transactionId, long durationMs)105     public void reportServiceRegistrationSucceeded(boolean isLegacy, int transactionId,
106             long durationMs) {
107         final Builder builder = makeReportedBuilder(isLegacy, transactionId);
108         builder.setType(NsdEventType.NET_REGISTER);
109         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_REGISTERED);
110         builder.setEventDurationMillisec(durationMs);
111         mDependencies.statsWrite(builder.build());
112     }
113 
114     /**
115      * Report service registration failed metric data.
116      *
117      * @param isLegacy Whether this call is using legacy backend.
118      * @param transactionId The transaction id of service registration.
119      * @param durationMs The duration of service registration failed.
120      */
reportServiceRegistrationFailed(boolean isLegacy, int transactionId, long durationMs)121     public void reportServiceRegistrationFailed(boolean isLegacy, int transactionId,
122             long durationMs) {
123         final Builder builder = makeReportedBuilder(isLegacy, transactionId);
124         builder.setType(NsdEventType.NET_REGISTER);
125         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_REGISTRATION_FAILED);
126         builder.setEventDurationMillisec(durationMs);
127         mDependencies.statsWrite(builder.build());
128     }
129 
130     /**
131      * Report service unregistration success metric data.
132      *
133      * @param isLegacy Whether this call is using legacy backend.
134      * @param transactionId The transaction id of service registration.
135      * @param durationMs The duration of service stayed registered.
136      * @param repliedRequestsCount The replied request count of this service before unregistered it.
137      * @param sentPacketCount The total sent packet count of this service before unregistered it.
138      * @param conflictDuringProbingCount The number of conflict during probing.
139      * @param conflictAfterProbingCount The number of conflict after probing.
140      */
reportServiceUnregistration(boolean isLegacy, int transactionId, long durationMs, int repliedRequestsCount, int sentPacketCount, int conflictDuringProbingCount, int conflictAfterProbingCount)141     public void reportServiceUnregistration(boolean isLegacy, int transactionId, long durationMs,
142             int repliedRequestsCount, int sentPacketCount, int conflictDuringProbingCount,
143             int conflictAfterProbingCount) {
144         final Builder builder = makeReportedBuilder(isLegacy, transactionId);
145         builder.setType(NsdEventType.NET_REGISTER);
146         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_UNREGISTERED);
147         builder.setEventDurationMillisec(durationMs);
148         builder.setRepliedRequestsCount(repliedRequestsCount);
149         builder.setSentPacketCount(sentPacketCount);
150         builder.setConflictDuringProbingCount(conflictDuringProbingCount);
151         builder.setConflictAfterProbingCount(conflictAfterProbingCount);
152         mDependencies.statsWrite(builder.build());
153     }
154 
155     /**
156      * Report service discovery started metric data.
157      *
158      * @param isLegacy Whether this call is using legacy backend.
159      * @param transactionId The transaction id of service discovery.
160      */
reportServiceDiscoveryStarted(boolean isLegacy, int transactionId)161     public void reportServiceDiscoveryStarted(boolean isLegacy, int transactionId) {
162         final Builder builder = makeReportedBuilder(isLegacy, transactionId);
163         builder.setType(NsdEventType.NET_DISCOVER);
164         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_DISCOVERY_STARTED);
165         mDependencies.statsWrite(builder.build());
166     }
167 
168     /**
169      * Report service discovery failed metric data.
170      *
171      * @param isLegacy Whether this call is using legacy backend.
172      * @param transactionId The transaction id of service discovery.
173      * @param durationMs The duration of service discovery failed.
174      */
reportServiceDiscoveryFailed(boolean isLegacy, int transactionId, long durationMs)175     public void reportServiceDiscoveryFailed(boolean isLegacy, int transactionId,
176             long durationMs) {
177         final Builder builder = makeReportedBuilder(isLegacy, transactionId);
178         builder.setType(NsdEventType.NET_DISCOVER);
179         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_DISCOVERY_FAILED);
180         builder.setEventDurationMillisec(durationMs);
181         mDependencies.statsWrite(builder.build());
182     }
183 
184     /**
185      * Report service discovery stop metric data.
186      *
187      * @param isLegacy Whether this call is using legacy backend.
188      * @param transactionId The transaction id of service discovery.
189      * @param durationMs The duration of discovering services.
190      * @param foundCallbackCount The count of found service callbacks before stop discovery.
191      * @param lostCallbackCount The count of lost service callbacks before stop discovery.
192      * @param servicesCount The count of found services.
193      * @param sentQueryCount The count of sent queries before stop discovery.
194      */
reportServiceDiscoveryStop(boolean isLegacy, int transactionId, long durationMs, int foundCallbackCount, int lostCallbackCount, int servicesCount, int sentQueryCount, boolean isServiceFromCache)195     public void reportServiceDiscoveryStop(boolean isLegacy, int transactionId, long durationMs,
196             int foundCallbackCount, int lostCallbackCount, int servicesCount, int sentQueryCount,
197             boolean isServiceFromCache) {
198         final Builder builder = makeReportedBuilder(isLegacy, transactionId);
199         builder.setType(NsdEventType.NET_DISCOVER);
200         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_DISCOVERY_STOP);
201         builder.setEventDurationMillisec(durationMs);
202         builder.setFoundCallbackCount(foundCallbackCount);
203         builder.setLostCallbackCount(lostCallbackCount);
204         builder.setFoundServiceCount(servicesCount);
205         builder.setSentQueryCount(sentQueryCount);
206         builder.setIsKnownService(isServiceFromCache);
207         mDependencies.statsWrite(builder.build());
208     }
209 
210     /**
211      * Report service resolution success metric data.
212      *
213      * @param isLegacy Whether this call is using legacy backend.
214      * @param transactionId The transaction id of service resolution.
215      * @param durationMs The duration of resolving services.
216      * @param isServiceFromCache Whether the resolved service is from cache.
217      * @param sentQueryCount The count of sent queries during resolving.
218      */
reportServiceResolved(boolean isLegacy, int transactionId, long durationMs, boolean isServiceFromCache, int sentQueryCount)219     public void reportServiceResolved(boolean isLegacy, int transactionId, long durationMs,
220             boolean isServiceFromCache, int sentQueryCount) {
221         final Builder builder = makeReportedBuilder(isLegacy, transactionId);
222         builder.setType(NsdEventType.NET_RESOLVE);
223         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_RESOLVED);
224         builder.setEventDurationMillisec(durationMs);
225         builder.setIsKnownService(isServiceFromCache);
226         builder.setSentQueryCount(sentQueryCount);
227         mDependencies.statsWrite(builder.build());
228     }
229 
230     /**
231      * Report service resolution failed metric data.
232      *
233      * @param isLegacy Whether this call is using legacy backend.
234      * @param transactionId The transaction id of service resolution.
235      * @param durationMs The duration of service resolution failed.
236      */
reportServiceResolutionFailed(boolean isLegacy, int transactionId, long durationMs)237     public void reportServiceResolutionFailed(boolean isLegacy, int transactionId,
238             long durationMs) {
239         final Builder builder = makeReportedBuilder(isLegacy, transactionId);
240         builder.setType(NsdEventType.NET_RESOLVE);
241         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_RESOLUTION_FAILED);
242         builder.setEventDurationMillisec(durationMs);
243         mDependencies.statsWrite(builder.build());
244     }
245 
246     /**
247      * Report service resolution stop metric data.
248      *
249      * @param isLegacy Whether this call is using legacy backend.
250      * @param transactionId The transaction id of service resolution.
251      * @param durationMs The duration before stop resolving the service.
252      * @param sentQueryCount The count of sent queries during resolving.
253      */
reportServiceResolutionStop(boolean isLegacy, int transactionId, long durationMs, int sentQueryCount)254     public void reportServiceResolutionStop(boolean isLegacy, int transactionId, long durationMs,
255             int sentQueryCount) {
256         final Builder builder = makeReportedBuilder(isLegacy, transactionId);
257         builder.setType(NsdEventType.NET_RESOLVE);
258         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_RESOLUTION_STOP);
259         builder.setEventDurationMillisec(durationMs);
260         builder.setSentQueryCount(sentQueryCount);
261         mDependencies.statsWrite(builder.build());
262     }
263 
264     /**
265      * Report service info callback registered metric data.
266      *
267      * @param transactionId The transaction id of service info callback registration.
268      */
reportServiceInfoCallbackRegistered(int transactionId)269     public void reportServiceInfoCallbackRegistered(int transactionId) {
270         // service info callback is always using new backend.
271         final Builder builder = makeReportedBuilder(false /* isLegacy */, transactionId);
272         builder.setType(NsdEventType.NET_SERVICE_INFO_CALLBACK);
273         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_INFO_CALLBACK_REGISTERED);
274         mDependencies.statsWrite(builder.build());
275     }
276 
277     /**
278      * Report service info callback registration failed metric data.
279      *
280      * @param transactionId The transaction id of service callback registration.
281      */
reportServiceInfoCallbackRegistrationFailed(int transactionId)282     public void reportServiceInfoCallbackRegistrationFailed(int transactionId) {
283         // service info callback is always using new backend.
284         final Builder builder = makeReportedBuilder(false /* isLegacy */, transactionId);
285         builder.setType(NsdEventType.NET_SERVICE_INFO_CALLBACK);
286         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_INFO_CALLBACK_REGISTRATION_FAILED);
287         mDependencies.statsWrite(builder.build());
288     }
289 
290     /**
291      * Report service callback unregistered metric data.
292      *
293      * @param transactionId The transaction id of service callback registration.
294      * @param durationMs The duration of service callback stayed registered.
295      * @param updateCallbackCount The count of service update callbacks during this registration.
296      * @param lostCallbackCount The count of service lost callbacks during this registration.
297      * @param isServiceFromCache Whether the resolved service is from cache.
298      * @param sentQueryCount The count of sent queries during this registration.
299      */
reportServiceInfoCallbackUnregistered(int transactionId, long durationMs, int updateCallbackCount, int lostCallbackCount, boolean isServiceFromCache, int sentQueryCount)300     public void reportServiceInfoCallbackUnregistered(int transactionId, long durationMs,
301             int updateCallbackCount, int lostCallbackCount, boolean isServiceFromCache,
302             int sentQueryCount) {
303         // service info callback is always using new backend.
304         final Builder builder = makeReportedBuilder(false /* isLegacy */, transactionId);
305         builder.setType(NsdEventType.NET_SERVICE_INFO_CALLBACK);
306         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_INFO_CALLBACK_UNREGISTERED);
307         builder.setEventDurationMillisec(durationMs);
308         builder.setFoundCallbackCount(updateCallbackCount);
309         builder.setLostCallbackCount(lostCallbackCount);
310         builder.setIsKnownService(isServiceFromCache);
311         builder.setSentQueryCount(sentQueryCount);
312         mDependencies.statsWrite(builder.build());
313     }
314 }
315