1 /*
2  * Copyright (C) 2018 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.server.wifi.rtt;
18 
19 import static org.hamcrest.core.IsEqual.equalTo;
20 import static org.mockito.Mockito.when;
21 
22 import android.net.MacAddress;
23 import android.net.wifi.rtt.RangingRequest;
24 import android.net.wifi.rtt.RangingResult;
25 import android.net.wifi.rtt.ResponderConfig;
26 import android.os.WorkSource;
27 import android.util.Log;
28 
29 import androidx.test.filters.SmallTest;
30 
31 import com.android.server.wifi.Clock;
32 import com.android.server.wifi.WifiBaseTest;
33 import com.android.server.wifi.hal.WifiRttController;
34 import com.android.server.wifi.proto.nano.WifiMetricsProto;
35 
36 import org.junit.Before;
37 import org.junit.Rule;
38 import org.junit.Test;
39 import org.junit.rules.ErrorCollector;
40 import org.mockito.Mock;
41 import org.mockito.MockitoAnnotations;
42 
43 import java.io.PrintWriter;
44 import java.io.StringWriter;
45 import java.util.ArrayList;
46 import java.util.List;
47 
48 /**
49  * Unit test harness for RttMetrics
50  */
51 @SmallTest
52 public class RttMetricsTest extends WifiBaseTest {
53     private RttMetrics mDut;
54 
55     @Mock
56     Clock mClock;
57 
58     @Rule
59     public ErrorCollector collector = new ErrorCollector();
60 
61     /**
62      * Pre-test configuration. Initialize and install mocks.
63      */
64     @Before
setUp()65     public void setUp() throws Exception {
66         MockitoAnnotations.initMocks(this);
67 
68         setTime(1);
69         mDut = new RttMetrics(mClock);
70     }
71 
72     /**
73      * Verify that recordRequest() records valid metrics.
74      */
75     @Test
testRecordRequest()76     public void testRecordRequest() {
77         WifiMetricsProto.WifiRttLog log;
78 
79         // no requests
80         log = mDut.consolidateProto();
81         checkMainStats("No requests", log, 0, 0, 0, 0);
82         checkPeerStats("No requests: AP", log.rttToAp, 0, 0, 0, 0, 0, 0, 0, 0);
83         checkPeerStats("No requests: Aware", log.rttToAware, 0, 0, 0, 0, 0, 0, 0, 0);
84 
85         // multiple AP requests from multiple sources
86         WorkSource ws1 = new WorkSource(10);
87         WorkSource ws2 = new WorkSource(20);
88         ws2.add(10);
89 
90         RangingRequest requestAp1 = getDummyRangingRequest(1, 0);
91         RangingRequest requestAp2 = getDummyRangingRequest(2, 0);
92         RangingRequest requestAp5 = getDummyRangingRequest(5, 0);
93         RangingRequest requestAp6 = getDummyRangingRequest(6, 0);
94 
95         mDut.clear();
96         mDut.recordRequest(ws1, requestAp1);
97         setTime(10); // delta = 9
98         mDut.recordRequest(ws1, requestAp2);
99         setTime(20); // delta = 10
100         mDut.recordRequest(ws1, requestAp5);
101         setTime(21); // delta = 1
102         mDut.recordRequest(ws1, requestAp6);
103         setTime(1000); // delta = 979
104         mDut.recordRequest(ws1, requestAp5);
105         setTime(5000); // delta = 4,000
106         mDut.recordRequest(ws1, requestAp5);
107         setTime(1000000); // delta = 995,000
108         mDut.recordRequest(ws1, requestAp2);
109         mDut.recordRequest(ws2, requestAp5);
110         mDut.recordRequest(ws2, requestAp5);
111         mDut.recordRequest(ws2, requestAp5);
112 
113         log = mDut.consolidateProto();
114         checkMainStats("Sequence AP-only", log, 10, 0, 0, 0);
115 
116         checkPeerStats("Sequence AP-only: AP", log.rttToAp, 10, 41, 2, 2, 4, 0, 0, 5);
117 
118         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramNumRequestsPerApp[0]",
119                 log.rttToAp.histogramNumRequestsPerApp[0], 1, 10, 1);
120         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramNumRequestsPerApp[1]",
121                 log.rttToAp.histogramNumRequestsPerApp[1], 10, 100, 1);
122 
123         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramNumPeersPerRequest[0]",
124                 log.rttToAp.histogramNumPeersPerRequest[0], 1, 1, 1);
125         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramNumPeersPerRequest[1]",
126                 log.rttToAp.histogramNumPeersPerRequest[1], 2, 2, 2);
127         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramNumPeersPerRequest[2]",
128                 log.rttToAp.histogramNumPeersPerRequest[2], 5, 5, 6);
129         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramNumPeersPerRequest[3]",
130                 log.rttToAp.histogramNumPeersPerRequest[3], 6, 6, 1);
131 
132         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramRequestIntervalMs[0]",
133                 log.rttToAp.histogramRequestIntervalMs[0], 1, 10, 5);
134         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramRequestIntervalMs[1]",
135                 log.rttToAp.histogramRequestIntervalMs[1], 10, 100, 1);
136         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramRequestIntervalMs[2]",
137                 log.rttToAp.histogramRequestIntervalMs[2], 100, 1000, 1);
138         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramRequestIntervalMs[3]",
139                 log.rttToAp.histogramRequestIntervalMs[3], 1000, 10000, 1);
140         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramRequestIntervalMs[4]",
141                 log.rttToAp.histogramRequestIntervalMs[4], 100000, 1000000, 1);
142 
143         checkPeerStats("Sequence AP-only: Aware", log.rttToAware, 0, 0, 0, 0, 0, 0, 0, 0);
144 
145         // mix of AP and Aware requests
146         WorkSource ws3 = new WorkSource(30);
147         ws3.add(20);
148         ws3.add(40);
149 
150         RangingRequest requestMixed03 = getDummyRangingRequest(0, 3);
151         RangingRequest requestMixed25 = getDummyRangingRequest(2, 5);
152         RangingRequest requestMixed50 = getDummyRangingRequest(5, 0);
153         RangingRequest requestMixed08 = getDummyRangingRequest(0, 8);
154 
155         mDut.clear();
156         setTime(100);
157         mDut.recordRequest(ws3, requestMixed03);
158         setTime(101);
159         mDut.recordRequest(ws3, requestMixed25);
160         setTime(102);
161         mDut.recordRequest(ws3, requestMixed50);
162         setTime(103);
163         mDut.recordRequest(ws3, requestMixed08);
164 
165         log = mDut.consolidateProto();
166         checkMainStats("Sequence Mixed AP/Aware", log, 4, 0, 0, 0);
167 
168         checkPeerStats("Sequence Mixed AP/Aware: AP", log.rttToAp, 2, 7, 3, 1, 2, 0, 0, 1);
169 
170         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAp.histogramNumRequestsPerApp[0]",
171                 log.rttToAp.histogramNumRequestsPerApp[0], 1, 10, 3);
172 
173         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAp.histogramNumPeersPerRequest[0]",
174                 log.rttToAp.histogramNumPeersPerRequest[0], 2, 2, 1);
175         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAp.histogramNumPeersPerRequest[1]",
176                 log.rttToAp.histogramNumPeersPerRequest[1], 5, 5, 1);
177 
178         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAp.histogramRequestIntervalMs[0]",
179                 log.rttToAp.histogramRequestIntervalMs[0], 1, 10, 1);
180 
181         checkPeerStats("Sequence Mixed AP/Aware: Aware", log.rttToAware, 3, 16, 3, 1, 3, 0, 0, 1);
182 
183         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAware.histogramNumRequestsPerApp[0]",
184                 log.rttToAware.histogramNumRequestsPerApp[0], 1, 10, 3);
185 
186         validateProtoHistBucket(
187                 "Sequence Mixed AP/Aware: rttToAware.histogramNumPeersPerRequest[0]",
188                 log.rttToAware.histogramNumPeersPerRequest[0], 3, 3, 1);
189         validateProtoHistBucket(
190                 "Sequence Mixed AP/Aware: rttToAware.histogramNumPeersPerRequest[1]",
191                 log.rttToAware.histogramNumPeersPerRequest[1], 5, 5, 1);
192         validateProtoHistBucket(
193                 "Sequence Mixed AP/Aware: rttToAware.histogramNumPeersPerRequest[2]",
194                 log.rttToAware.histogramNumPeersPerRequest[2], 8, 8, 1);
195 
196         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAware.histogramRequestIntervalMs[0]",
197                 log.rttToAware.histogramRequestIntervalMs[0], 1, 10, 2);
198     }
199 
200     /**
201      * Verify that recordResult() records valid metrics.
202      */
203     @Test
testRecordResult()204     public void testRecordResult() {
205         WifiMetricsProto.WifiRttLog log;
206 
207         // no requests
208         log = mDut.consolidateProto();
209         checkMainStats("No requests", log, 0, 0, 0, 0);
210         checkPeerStats("No requests: AP", log.rttToAp, 0, 0, 0, 0, 0, 0, 0, 0);
211         checkPeerStats("No requests: Aware", log.rttToAware, 0, 0, 0, 0, 0, 0, 0, 0);
212 
213         // multiple AP results
214         RangingRequest requestAp1 = getDummyRangingRequest(1, 0);
215         RangingRequest requestAp2 = getDummyRangingRequest(2, 0);
216         RangingRequest requestAp5 = getDummyRangingRequest(5, 0);
217         RangingRequest requestAp6 = getDummyRangingRequest(6, 0);
218 
219         mDut.clear();
220         mDut.recordResult(requestAp1, getDummyRangingResults(
221                 WifiRttController.FRAMEWORK_RTT_STATUS_SUCCESS,
222                 requestAp1, 5, 0), 500);
223         mDut.recordResult(requestAp2, getDummyRangingResults(
224                 WifiRttController.FRAMEWORK_RTT_STATUS_SUCCESS,
225                 requestAp2, 10, 30), 1500);
226         mDut.recordResult(requestAp5, getDummyRangingResults(
227                 WifiRttController.FRAMEWORK_RTT_STATUS_SUCCESS,
228                 requestAp5, 0.3, -0.2), 700);
229         mDut.recordResult(requestAp6, getDummyRangingResults(
230                 WifiRttController.FRAMEWORK_RTT_STATUS_SUCCESS,
231                 requestAp6, 40, 30), 1800);
232         log = mDut.consolidateProto();
233 
234         checkMainStats("Sequence AP-only", log, 0, 0, 2, 0);
235         checkPeerStats("Sequence AP-only: AP", log.rttToAp, 0, 0, 0, 0, 0, 1, 6, 0);
236 
237         validateProtoHistBucket("Sequence AP-only: histogramMeasurementDurationApOnly[0]",
238                 log.histogramMeasurementDurationApOnly[0], Integer.MIN_VALUE, 1 * 1000, 2);
239         validateProtoHistBucket("Sequence AP-only: histogramMeasurementDurationApOnly[1]",
240                 log.histogramMeasurementDurationApOnly[1], 1 * 1000, 2 * 1000, 2);
241 
242         validateProtoIndividualStatusHistBucket(
243                 "Sequence AP-only: rttToAp.histogramIndividualStatus[0]",
244                 log.rttToAp.histogramIndividualStatus[0], WifiMetricsProto.WifiRttLog.SUCCESS, 14);
245 
246         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramDistance[0]",
247                 log.rttToAp.histogramDistance[0], Integer.MIN_VALUE, 0, 3);
248         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramDistance[1]",
249                 log.rttToAp.histogramDistance[1], 0, 5 * 1000, 2);
250         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramDistance[2]",
251                 log.rttToAp.histogramDistance[2], 5 * 1000, 15 * 1000, 2);
252         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramDistance[3]",
253                 log.rttToAp.histogramDistance[3], 30 * 1000, 60 * 1000, 2);
254         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramDistance[4]",
255                 log.rttToAp.histogramDistance[4], 60 * 1000, 100 * 1000, 1);
256         validateProtoHistBucket("Sequence AP-only: rttToAp.histogramDistance[5]",
257                 log.rttToAp.histogramDistance[5], 100 * 1000, Integer.MAX_VALUE, 4);
258 
259         checkPeerStats("Sequence AP-only: Aware", log.rttToAware, 0, 0, 0, 0, 0, 0, 0, 0);
260 
261         // mix of AP and Aware requests
262         RangingRequest requestMixed03 = getDummyRangingRequest(0, 3);
263         RangingRequest requestMixed25 = getDummyRangingRequest(2, 5);
264         RangingRequest requestMixed50 = getDummyRangingRequest(5, 0);
265         RangingRequest requestMixed08 = getDummyRangingRequest(0, 8);
266 
267         mDut.clear();
268         mDut.recordResult(requestMixed03, getDummyRangingResults(
269                 WifiRttController.FRAMEWORK_RTT_STATUS_SUCCESS,
270                 requestMixed03, 5, 0), 6400);
271         mDut.recordResult(requestMixed25, getDummyRangingResults(
272                 WifiRttController.FRAMEWORK_RTT_STATUS_SUCCESS,
273                 requestMixed25, 10, 30), 7800);
274         mDut.recordResult(requestMixed50, getDummyRangingResults(
275                 WifiRttController.FRAMEWORK_RTT_STATUS_SUCCESS,
276                         requestMixed50, 0.3, -0.2), 3100);
277         mDut.recordResult(requestMixed08, getDummyRangingResults(
278                 WifiRttController.FRAMEWORK_RTT_STATUS_SUCCESS,
279                 requestMixed08, 40, 30), 9500);
280         log = mDut.consolidateProto();
281 
282         checkMainStats("Sequence Mixed AP/Aware", log, 0, 0, 1, 2);
283 
284         checkPeerStats("Sequence Mixed AP/Aware: AP", log.rttToAp, 0, 0, 0, 0, 0, 1, 4, 0);
285 
286         validateProtoHistBucket("Sequence Mixed AP/Aware: histogramMeasurementDurationApOnly[0]",
287                 log.histogramMeasurementDurationApOnly[0], 3 * 1000, 4 * 1000, 1);
288         validateProtoHistBucket("Sequence Mixed AP/Aware: histogramMeasurementDurationWithAware[0]",
289                 log.histogramMeasurementDurationWithAware[0], 6 * 1000, 8 * 1000, 2);
290         validateProtoHistBucket("Sequence Mixed AP/Aware: histogramMeasurementDurationWithAware[1]",
291                 log.histogramMeasurementDurationWithAware[1], 8 * 1000, Integer.MAX_VALUE, 1);
292 
293         validateProtoIndividualStatusHistBucket(
294                 "Sequence Mixed AP/Aware: rttToAp.histogramIndividualStatus[0]",
295                 log.rttToAp.histogramIndividualStatus[0], WifiMetricsProto.WifiRttLog.SUCCESS, 7);
296 
297         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAp.histogramDistance[0]",
298                 log.rttToAp.histogramDistance[0], Integer.MIN_VALUE, 0, 3);
299         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAp.histogramDistance[1]",
300                 log.rttToAp.histogramDistance[1], 0, 5 * 1000, 2);
301         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAp.histogramDistance[2]",
302                 log.rttToAp.histogramDistance[2], 5 * 1000, 15 * 1000, 1);
303         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAp.histogramDistance[3]",
304                 log.rttToAp.histogramDistance[3], 30 * 1000, 60 * 1000, 1);
305 
306         checkPeerStats("Sequence Mixed AP/Aware: Aware", log.rttToAware, 0, 0, 0, 0, 0, 1, 4, 0);
307 
308         validateProtoIndividualStatusHistBucket(
309                 "Sequence Mixed AP/Aware: rttToAware.histogramIndividualStatus[0]",
310                 log.rttToAware.histogramIndividualStatus[0], WifiMetricsProto.WifiRttLog.SUCCESS,
311                 16);
312 
313         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAware.histogramDistance[0]",
314                 log.rttToAware.histogramDistance[0], 5 * 1000, 15 * 1000, 3);
315         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAware.histogramDistance[1]",
316                 log.rttToAware.histogramDistance[1], 30 * 1000, 60 * 1000, 1);
317         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAware.histogramDistance[2]",
318                 log.rttToAware.histogramDistance[2], 60 * 1000, 100 * 1000, 2);
319         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAware.histogramDistance[3]",
320                 log.rttToAware.histogramDistance[3], 100 * 1000, Integer.MAX_VALUE, 10);
321     }
322 
323     /**
324      * Verify the behavior when the HAL returns with missing results or some results set to null.
325      */
326     @Test
testRecordMissingResults()327     public void testRecordMissingResults() {
328         WifiMetricsProto.WifiRttLog log;
329 
330         mDut.clear();
331         RangingRequest requestMixed25 = getDummyRangingRequest(2, 5);
332         List<RangingResult> resultMixed25 = getDummyRangingResults(
333                 WifiRttController.FRAMEWORK_RTT_STATUS_SUCCESS, requestMixed25, 10, 30);
334         // remove some results
335         resultMixed25.remove(3); // Second Aware result: distance = 100
336         resultMixed25.remove(0); // First AP result: distance = 10
337         resultMixed25.add(null);
338         mDut.recordResult(requestMixed25, resultMixed25, 0);
339 
340         log = mDut.consolidateProto();
341 
342         checkMainStats("Sequence Mixed AP/Aware", log, 0, 0, 0, 1);
343 
344         checkPeerStats("Sequence Mixed AP/Aware: AP", log.rttToAp, 0, 0, 0, 0, 0, 2, 1, 0);
345 
346         validateProtoIndividualStatusHistBucket(
347                 "Sequence Mixed AP/Aware: rttToAp.histogramIndividualStatus[0]",
348                 log.rttToAp.histogramIndividualStatus[0], WifiMetricsProto.WifiRttLog.SUCCESS, 1);
349         validateProtoIndividualStatusHistBucket(
350                 "Sequence Mixed AP/Aware: rttToAp.histogramIndividualStatus[1]",
351                 log.rttToAp.histogramIndividualStatus[1],
352                 WifiMetricsProto.WifiRttLog.MISSING_RESULT, 1);
353 
354         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAp.histogramDistance[0]",
355                 log.rttToAp.histogramDistance[0], 30 * 1000, 60 * 1000, 1);
356 
357         checkPeerStats("Sequence Mixed AP/Aware: Aware", log.rttToAware, 0, 0, 0, 0, 0, 2, 2, 0);
358 
359         validateProtoIndividualStatusHistBucket(
360                 "Sequence Mixed AP/Aware: rttToAware.histogramIndividualStatus[0]",
361                 log.rttToAware.histogramIndividualStatus[0], WifiMetricsProto.WifiRttLog.SUCCESS,
362                 4);
363         validateProtoIndividualStatusHistBucket(
364                 "Sequence Mixed AP/Aware: rttToAware.histogramIndividualStatus[1]",
365                 log.rttToAware.histogramIndividualStatus[1],
366                 WifiMetricsProto.WifiRttLog.MISSING_RESULT, 1);
367 
368         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAware.histogramDistance[0]",
369                 log.rttToAware.histogramDistance[0], 60 * 1000, 100 * 1000, 1);
370         validateProtoHistBucket("Sequence Mixed AP/Aware: rttToAware.histogramDistance[1]",
371                 log.rttToAware.histogramDistance[1], 100 * 1000, Integer.MAX_VALUE, 3);
372     }
373 
374     /**
375      * Verify the behavior when the HAL returns with NULL array.
376      */
377     @Test
testRecordNullArrayResults()378     public void testRecordNullArrayResults() {
379         WifiMetricsProto.WifiRttLog log;
380 
381         mDut.clear();
382         RangingRequest requestMixed25 = getDummyRangingRequest(2, 5);
383         mDut.recordResult(requestMixed25, null, 0);
384 
385         log = mDut.consolidateProto();
386 
387         checkMainStats("Sequence Mixed AP/Aware", log, 0, 0, 0, 0);
388 
389         checkPeerStats("Sequence Mixed AP/Aware: AP", log.rttToAp, 0, 0, 0, 0, 0, 1, 0, 0);
390 
391         validateProtoIndividualStatusHistBucket(
392                 "Sequence Mixed AP/Aware: rttToAp.histogramIndividualStatus[1]",
393                 log.rttToAp.histogramIndividualStatus[0],
394                 WifiMetricsProto.WifiRttLog.MISSING_RESULT, 2);
395 
396         checkPeerStats("Sequence Mixed AP/Aware: Aware", log.rttToAware, 0, 0, 0, 0, 0, 1, 0, 0);
397 
398         validateProtoIndividualStatusHistBucket(
399                 "Sequence Mixed AP/Aware: rttToAware.histogramIndividualStatus[0]",
400                 log.rttToAware.histogramIndividualStatus[0],
401                 WifiMetricsProto.WifiRttLog.MISSING_RESULT, 5);
402     }
403 
404     /**
405      * Verify that all individual status codes are translated correctly.
406      */
407     @Test
testRecordResultsStatuses()408     public void testRecordResultsStatuses() {
409         WifiMetricsProto.WifiRttLog log;
410 
411         mDut.clear();
412 
413         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_SUCCESS, 5);
414         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_FAILURE, 6);
415         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_NO_RSP, 7);
416         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_REJECTED, 8);
417         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_NOT_SCHEDULED_YET, 9);
418         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_TM_TIMEOUT, 10);
419         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL, 11);
420         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_NO_CAPABILITY, 12);
421         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_ABORTED, 13);
422         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_INVALID_TS, 14);
423         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_PROTOCOL, 15);
424         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_SCHEDULE, 16);
425         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_BUSY_TRY_LATER, 17);
426         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_INVALID_REQ, 18);
427         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_NO_WIFI, 19);
428         recordResultNTimes(WifiRttController.FRAMEWORK_RTT_STATUS_FAIL_FTM_PARAM_OVERRIDE, 20);
429 
430         log = mDut.consolidateProto();
431 
432         collector.checkThat("AP histogramIndividualStatus.length",
433                 log.rttToAp.histogramIndividualStatus.length, equalTo(16));
434 
435         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[0]",
436                 log.rttToAp.histogramIndividualStatus[0], WifiMetricsProto.WifiRttLog.SUCCESS, 5);
437         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[1]",
438                 log.rttToAp.histogramIndividualStatus[1], WifiMetricsProto.WifiRttLog.FAILURE, 6);
439         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[2]",
440                 log.rttToAp.histogramIndividualStatus[2], WifiMetricsProto.WifiRttLog.FAIL_NO_RSP,
441                 7);
442         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[3]",
443                 log.rttToAp.histogramIndividualStatus[3], WifiMetricsProto.WifiRttLog.FAIL_REJECTED,
444                 8);
445         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[4]",
446                 log.rttToAp.histogramIndividualStatus[4],
447                 WifiMetricsProto.WifiRttLog.FAIL_NOT_SCHEDULED_YET, 9);
448         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[5]",
449                 log.rttToAp.histogramIndividualStatus[5],
450                 WifiMetricsProto.WifiRttLog.FAIL_TM_TIMEOUT, 10);
451         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[6]",
452                 log.rttToAp.histogramIndividualStatus[6],
453                 WifiMetricsProto.WifiRttLog.FAIL_AP_ON_DIFF_CHANNEL, 11);
454         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[7]",
455                 log.rttToAp.histogramIndividualStatus[7],
456                 WifiMetricsProto.WifiRttLog.FAIL_NO_CAPABILITY, 12);
457         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[8]",
458                 log.rttToAp.histogramIndividualStatus[8], WifiMetricsProto.WifiRttLog.ABORTED, 13);
459         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[9]",
460                 log.rttToAp.histogramIndividualStatus[9],
461                 WifiMetricsProto.WifiRttLog.FAIL_INVALID_TS, 14);
462         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[10]",
463                 log.rttToAp.histogramIndividualStatus[10],
464                 WifiMetricsProto.WifiRttLog.FAIL_PROTOCOL, 15);
465         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[11]",
466                 log.rttToAp.histogramIndividualStatus[11],
467                 WifiMetricsProto.WifiRttLog.FAIL_SCHEDULE, 16);
468         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[12]",
469                 log.rttToAp.histogramIndividualStatus[12],
470                 WifiMetricsProto.WifiRttLog.FAIL_BUSY_TRY_LATER, 17);
471         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[13]",
472                 log.rttToAp.histogramIndividualStatus[13], WifiMetricsProto.WifiRttLog.INVALID_REQ,
473                 18);
474         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[14]",
475                 log.rttToAp.histogramIndividualStatus[14], WifiMetricsProto.WifiRttLog.NO_WIFI, 19);
476         validateProtoIndividualStatusHistBucket("rttToAp.histogramIndividualStatus[15]",
477                 log.rttToAp.histogramIndividualStatus[15],
478                 WifiMetricsProto.WifiRttLog.FAIL_FTM_PARAM_OVERRIDE, 20);
479 
480         collector.checkThat("Aware histogramIndividualStatus.length",
481                 log.rttToAware.histogramIndividualStatus.length, equalTo(0));
482     }
483 
484     /**
485      * Verify that all overall status codes are recorded correctly.
486      */
487     @Test
testRecordOverallStatus()488     public void testRecordOverallStatus() {
489         WifiMetricsProto.WifiRttLog log;
490 
491         mDut.clear();
492 
493         recordOverallStatusNTimes(WifiMetricsProto.WifiRttLog.OVERALL_SUCCESS, 5);
494         recordOverallStatusNTimes(WifiMetricsProto.WifiRttLog.OVERALL_FAIL, 6);
495         recordOverallStatusNTimes(WifiMetricsProto.WifiRttLog.OVERALL_RTT_NOT_AVAILABLE, 7);
496         recordOverallStatusNTimes(WifiMetricsProto.WifiRttLog.OVERALL_TIMEOUT, 8);
497         recordOverallStatusNTimes(WifiMetricsProto.WifiRttLog.OVERALL_THROTTLE, 9);
498         recordOverallStatusNTimes(WifiMetricsProto.WifiRttLog.OVERALL_HAL_FAILURE, 10);
499         recordOverallStatusNTimes(WifiMetricsProto.WifiRttLog.OVERALL_AWARE_TRANSLATION_FAILURE,
500                 11);
501         recordOverallStatusNTimes(WifiMetricsProto.WifiRttLog.OVERALL_LOCATION_PERMISSION_MISSING,
502                 12);
503 
504         log = mDut.consolidateProto();
505 
506         collector.checkThat("histogramOverallStatus.length", log.histogramOverallStatus.length,
507                 equalTo(8));
508 
509         validateProtoOverallStatusHistBucket("histogramOverallStatus[0]",
510                 log.histogramOverallStatus[0], WifiMetricsProto.WifiRttLog.OVERALL_SUCCESS, 5);
511         validateProtoOverallStatusHistBucket("histogramOverallStatus[1]",
512                 log.histogramOverallStatus[1], WifiMetricsProto.WifiRttLog.OVERALL_FAIL, 6);
513         validateProtoOverallStatusHistBucket("histogramOverallStatus[2]",
514                 log.histogramOverallStatus[2],
515                 WifiMetricsProto.WifiRttLog.OVERALL_RTT_NOT_AVAILABLE, 7);
516         validateProtoOverallStatusHistBucket("histogramOverallStatus[3]",
517                 log.histogramOverallStatus[3], WifiMetricsProto.WifiRttLog.OVERALL_TIMEOUT, 8);
518         validateProtoOverallStatusHistBucket("histogramOverallStatus[4]",
519                 log.histogramOverallStatus[4], WifiMetricsProto.WifiRttLog.OVERALL_THROTTLE, 9);
520         validateProtoOverallStatusHistBucket("histogramOverallStatus[5]",
521                 log.histogramOverallStatus[5], WifiMetricsProto.WifiRttLog.OVERALL_HAL_FAILURE, 10);
522         validateProtoOverallStatusHistBucket("histogramOverallStatus[6]",
523                 log.histogramOverallStatus[6],
524                 WifiMetricsProto.WifiRttLog.OVERALL_AWARE_TRANSLATION_FAILURE, 11);
525         validateProtoOverallStatusHistBucket("histogramOverallStatus[7]",
526                 log.histogramOverallStatus[7],
527                 WifiMetricsProto.WifiRttLog.OVERALL_LOCATION_PERMISSION_MISSING, 12);
528     }
529 
530     // Utilities
531 
532     /**
533      * Mock the elapsed time since boot to the input argument.
534      */
setTime(long timeMs)535     private void setTime(long timeMs) {
536         when(mClock.getElapsedSinceBootMillis()).thenReturn(timeMs);
537     }
538 
validateProtoHistBucket(String logPrefix, WifiMetricsProto.WifiRttLog.HistogramBucket bucket, long start, long end, int count)539     private void validateProtoHistBucket(String logPrefix,
540             WifiMetricsProto.WifiRttLog.HistogramBucket bucket, long start, long end, int count) {
541         collector.checkThat(logPrefix + ": start", bucket.start, equalTo(start));
542         collector.checkThat(logPrefix + ": end", bucket.end, equalTo(end));
543         collector.checkThat(logPrefix + ": count", bucket.count, equalTo(count));
544     }
545 
validateProtoOverallStatusHistBucket(String logPrefix, WifiMetricsProto.WifiRttLog.RttOverallStatusHistogramBucket bucket, int status, int count)546     private void validateProtoOverallStatusHistBucket(String logPrefix,
547             WifiMetricsProto.WifiRttLog.RttOverallStatusHistogramBucket bucket, int status,
548             int count) {
549         collector.checkThat(logPrefix + ": statusType", bucket.statusType, equalTo(status));
550         collector.checkThat(logPrefix + ": count", bucket.count, equalTo(count));
551     }
552 
validateProtoIndividualStatusHistBucket(String logPrefix, WifiMetricsProto.WifiRttLog.RttIndividualStatusHistogramBucket bucket, int status, int count)553     private void validateProtoIndividualStatusHistBucket(String logPrefix,
554             WifiMetricsProto.WifiRttLog.RttIndividualStatusHistogramBucket bucket, int status,
555             int count) {
556         collector.checkThat(logPrefix + ": statusType", bucket.statusType, equalTo(status));
557         collector.checkThat(logPrefix + ": count", bucket.count, equalTo(count));
558     }
559 
checkMainStats(String msgPrefix, WifiMetricsProto.WifiRttLog log, int numRequests, int histogramOverallStatusLength, int histogramMeasurementDurationApOnlyLength, int histogramMeasurementDurationWithAwareLength)560     private void checkMainStats(String msgPrefix, WifiMetricsProto.WifiRttLog log, int numRequests,
561             int histogramOverallStatusLength, int histogramMeasurementDurationApOnlyLength,
562             int histogramMeasurementDurationWithAwareLength) {
563         collector.checkThat(msgPrefix + ": numRequests", log.numRequests, equalTo(numRequests));
564         collector.checkThat(msgPrefix + ": histogramOverallStatus.length",
565                 log.histogramOverallStatus.length,
566                 equalTo(histogramOverallStatusLength));
567         collector.checkThat(msgPrefix + ": histogramMeasurementDurationApOnly.length",
568                 log.histogramMeasurementDurationApOnly.length,
569                 equalTo(histogramMeasurementDurationApOnlyLength));
570         collector.checkThat(msgPrefix + ": histogramMeasurementDurationWithAware.length",
571                 log.histogramMeasurementDurationWithAware.length,
572                 equalTo(histogramMeasurementDurationWithAwareLength));
573     }
574 
checkPeerStats(String msgPrefix, WifiMetricsProto.WifiRttLog.RttToPeerLog log, int numRequests, int numIndividualRequests, int numApps, int histogramNumRequestsPerAppLength, int histogramNumPeersPerRequestLength, int histogramIndividualStatusLength, int histogramDistanceLength, int histogramRequestIntervalMsLength)575     private void checkPeerStats(String msgPrefix, WifiMetricsProto.WifiRttLog.RttToPeerLog log,
576             int numRequests, int numIndividualRequests,
577             int numApps, int histogramNumRequestsPerAppLength,
578             int histogramNumPeersPerRequestLength, int histogramIndividualStatusLength,
579             int histogramDistanceLength, int histogramRequestIntervalMsLength) {
580         collector.checkThat(msgPrefix + ": numRequests", log.numRequests, equalTo(numRequests));
581         collector.checkThat(msgPrefix + ": numIndividualRequests", log.numIndividualRequests,
582                 equalTo(numIndividualRequests));
583         collector.checkThat(msgPrefix + ": numApps", log.numApps, equalTo(numApps));
584         collector.checkThat(msgPrefix + ": histogramNumRequestsPerApp.length",
585                 log.histogramNumRequestsPerApp.length, equalTo(histogramNumRequestsPerAppLength));
586         collector.checkThat(msgPrefix + ": histogramNumPeersPerRequest.length",
587                 log.histogramNumPeersPerRequest.length, equalTo(histogramNumPeersPerRequestLength));
588         collector.checkThat(msgPrefix + ": histogramIndividualStatus.length",
589                 log.histogramIndividualStatus.length, equalTo(histogramIndividualStatusLength));
590         collector.checkThat(msgPrefix + ": histogramDistance.length",
591                 log.histogramDistance.length, equalTo(histogramDistanceLength));
592         collector.checkThat(msgPrefix + ": histogramRequestIntervalMs.length",
593                 log.histogramRequestIntervalMs.length, equalTo(histogramRequestIntervalMsLength));
594     }
595 
getDummyRangingRequest(int countAp, int countAware)596     private RangingRequest getDummyRangingRequest(int countAp, int countAware) {
597         RangingRequest.Builder builder = new RangingRequest.Builder();
598         byte[] placeholderMacBase = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5};
599 
600         for (int i = 0; i < countAp; ++i) {
601             placeholderMacBase[0]++;
602             builder.addResponder(new ResponderConfig.Builder()
603                     .setMacAddress(MacAddress.fromBytes(placeholderMacBase))
604                     .setResponderType(ResponderConfig.RESPONDER_AP)
605                     .set80211mcSupported(true)
606                     .build());
607         }
608         for (int i = 0; i < countAware; ++i) {
609             placeholderMacBase[0]++;
610             builder.addResponder(new ResponderConfig.Builder()
611                     .setMacAddress(MacAddress.fromBytes(placeholderMacBase))
612                     .setResponderType(ResponderConfig.RESPONDER_AWARE)
613                     .set80211mcSupported(true)
614                     .build());
615         }
616 
617         return builder.build();
618     }
619 
getDummyRangingResults(int status, RangingRequest request, double baseDistanceM, double incrDistanceM)620     private List<RangingResult> getDummyRangingResults(int status, RangingRequest request,
621             double baseDistanceM, double incrDistanceM) {
622         List<RangingResult> rangingResults = new ArrayList<>();
623         double distance = baseDistanceM;
624 
625         for (ResponderConfig peer : request.mRttPeers) {
626 
627             RangingResult rttResult = new RangingResult.Builder()
628                     .setStatus(status)
629                     .setMacAddress(peer.getMacAddress())
630                     .setDistanceMm((int) (distance * 1000))
631                     .setNumAttemptedMeasurements(8)
632                     .setNumSuccessfulMeasurements(8)
633                     .set80211mcMeasurement(true)
634                     .build();
635             distance += incrDistanceM;
636             rangingResults.add(rttResult);
637         }
638 
639         return rangingResults;
640     }
641 
recordResultNTimes(int status, int n)642     private void recordResultNTimes(int status, int n) {
643         RangingRequest request = getDummyRangingRequest(1, 0);
644         List<RangingResult> results = getDummyRangingResults(status, request, 0, 0);
645 
646         for (int i = 0; i < n; ++i) {
647             mDut.recordResult(request, results, 0);
648         }
649     }
650 
recordOverallStatusNTimes(int status, int n)651     private void recordOverallStatusNTimes(int status, int n) {
652         for (int i = 0; i < n; ++i) {
653             mDut.recordOverallStatus(status);
654         }
655     }
656 
dumpDut(String prefix)657     private void dumpDut(String prefix) {
658         StringWriter sw = new StringWriter();
659         mDut.dump(null, new PrintWriter(sw), null);
660         Log.e("RttMetrics", prefix + sw.toString());
661     }
662 }
663