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 package android.cts.statsd.metric;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 
20 import android.cts.statsdatom.lib.AtomTestUtils;
21 import android.cts.statsdatom.lib.ConfigUtils;
22 import android.cts.statsdatom.lib.DeviceUtils;
23 import android.cts.statsdatom.lib.ReportUtils;
24 
25 import com.android.internal.os.StatsdConfigProto;
26 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
27 import com.android.internal.os.StatsdConfigProto.FieldMatcher;
28 import com.android.internal.os.StatsdConfigProto.Predicate;
29 import com.android.internal.os.StatsdConfigProto.SimplePredicate;
30 import com.android.os.AtomsProto.AppBreadcrumbReported;
31 import com.android.os.AtomsProto.Atom;
32 import com.android.os.StatsLog.DurationBucketInfo;
33 import com.android.os.StatsLog.StatsLogReport;
34 import com.android.tradefed.log.LogUtil;
35 import com.android.tradefed.testtype.DeviceTestCase;
36 import com.android.tradefed.util.RunUtil;
37 
38 import com.google.common.collect.Range;
39 import com.google.protobuf.ExtensionRegistry;
40 
41 
42 public class DurationMetricsTests extends DeviceTestCase {
43 
44     private static final int APP_BREADCRUMB_REPORTED_A_MATCH_START_ID = 0;
45     private static final int APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID = 1;
46     private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2;
47     private static final int APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID = 3;
48 
49     @Override
setUp()50     protected void setUp() throws Exception {
51         super.setUp();
52         ConfigUtils.removeConfig(getDevice());
53         ReportUtils.clearReports(getDevice());
54         RunUtil.getDefault().sleep(1000);
55     }
56 
57     @Override
tearDown()58     protected void tearDown() throws Exception {
59         ConfigUtils.removeConfig(getDevice());
60         ReportUtils.clearReports(getDevice());
61         super.tearDown();
62     }
63 
testDurationMetric()64     public void testDurationMetric() throws Exception {
65         final int label = 1;
66         // Add AtomMatchers.
67         AtomMatcher startAtomMatcher =
68                 MetricsUtils.startAtomMatcherWithLabel(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID,
69                         label);
70         AtomMatcher stopAtomMatcher =
71                 MetricsUtils.stopAtomMatcherWithLabel(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID,
72                         label);
73 
74         StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
75                 MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
76         builder.addAtomMatcher(startAtomMatcher);
77         builder.addAtomMatcher(stopAtomMatcher);
78 
79         // Add Predicates.
80         SimplePredicate simplePredicate = SimplePredicate.newBuilder()
81                 .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
82                 .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
83                 .build();
84         Predicate predicate = Predicate.newBuilder()
85                 .setId(MetricsUtils.StringToId("Predicate"))
86                 .setSimplePredicate(simplePredicate)
87                 .build();
88         builder.addPredicate(predicate);
89 
90         // Add DurationMetric.
91         builder.addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
92                 .setId(MetricsUtils.DURATION_METRIC_ID)
93                 .setWhat(predicate.getId())
94                 .setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
95                 .setBucket(StatsdConfigProto.TimeUnit.CTS));
96 
97         // Upload config.
98         ConfigUtils.uploadConfig(getDevice(), builder);
99 
100         // Create AppBreadcrumbReported Start/Stop events.
101         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
102                 AppBreadcrumbReported.State.START.getNumber(), label);
103         RunUtil.getDefault().sleep(2000);
104         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
105                 AppBreadcrumbReported.State.STOP.getNumber(), label);
106 
107         // Wait for the metrics to propagate to statsd.
108         RunUtil.getDefault().sleep(2000);
109 
110         StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
111                 ExtensionRegistry.getEmptyRegistry());
112         assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
113         LogUtil.CLog.d("Received the following data: " + metricReport.toString());
114         assertThat(metricReport.hasDurationMetrics()).isTrue();
115         StatsLogReport.DurationMetricDataWrapper durationData
116                 = metricReport.getDurationMetrics();
117         assertThat(durationData.getDataCount()).isEqualTo(1);
118         assertThat(durationData.getData(0).getBucketInfo(0).getDurationNanos())
119                 .isIn(Range.open(0L, (long) 1e9));
120     }
121 
testDurationMetricWithCondition()122     public void testDurationMetricWithCondition() throws Exception {
123         final int durationLabel = 1;
124         final int conditionLabel = 2;
125 
126         // Add AtomMatchers.
127         AtomMatcher startAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
128                 APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, durationLabel);
129         AtomMatcher stopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
130                 APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, durationLabel);
131         AtomMatcher conditionStartAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
132                 APP_BREADCRUMB_REPORTED_B_MATCH_START_ID, conditionLabel);
133         AtomMatcher conditionStopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
134                 APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID, conditionLabel);
135 
136         StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
137                         DeviceUtils.STATSD_ATOM_TEST_PKG)
138                 .addAtomMatcher(startAtomMatcher)
139                 .addAtomMatcher(stopAtomMatcher)
140                 .addAtomMatcher(conditionStartAtomMatcher)
141                 .addAtomMatcher(conditionStopAtomMatcher);
142 
143         // Add Predicates.
144         SimplePredicate simplePredicate = SimplePredicate.newBuilder()
145                 .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
146                 .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
147                 .build();
148         Predicate predicate = Predicate.newBuilder()
149                 .setId(MetricsUtils.StringToId("Predicate"))
150                 .setSimplePredicate(simplePredicate)
151                 .build();
152 
153         SimplePredicate conditionSimplePredicate = SimplePredicate.newBuilder()
154                 .setStart(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
155                 .setStop(APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID)
156                 .build();
157         Predicate conditionPredicate = Predicate.newBuilder()
158                 .setId(MetricsUtils.StringToId("ConditionPredicate"))
159                 .setSimplePredicate(conditionSimplePredicate)
160                 .build();
161 
162         builder.addPredicate(predicate).addPredicate(conditionPredicate);
163 
164         // Add DurationMetric.
165         builder.addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
166                 .setId(MetricsUtils.DURATION_METRIC_ID)
167                 .setWhat(predicate.getId())
168                 .setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
169                 .setBucket(StatsdConfigProto.TimeUnit.CTS)
170                 .setCondition(conditionPredicate.getId())
171         );
172 
173         // Upload config.
174         ConfigUtils.uploadConfig(getDevice(), builder);
175 
176         // Start uncounted duration.
177         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
178                 AppBreadcrumbReported.State.START.getNumber(), durationLabel);
179         RunUtil.getDefault().sleep(10);
180 
181         RunUtil.getDefault().sleep(2_000);
182 
183         // Stop uncounted duration.
184         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
185                 AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
186         RunUtil.getDefault().sleep(10);
187 
188         // Set the condition to true.
189         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
190                 AppBreadcrumbReported.State.START.getNumber(), conditionLabel);
191         RunUtil.getDefault().sleep(10);
192 
193         // Start counted duration.
194         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
195                 AppBreadcrumbReported.State.START.getNumber(), durationLabel);
196         RunUtil.getDefault().sleep(10);
197 
198         RunUtil.getDefault().sleep(2_000);
199 
200         // Stop counted duration.
201         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
202                 AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
203         RunUtil.getDefault().sleep(10);
204 
205         // Set the condition to false.
206         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
207                 AppBreadcrumbReported.State.STOP.getNumber(), conditionLabel);
208         RunUtil.getDefault().sleep(10);
209 
210         // Start uncounted duration.
211         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
212                 AppBreadcrumbReported.State.START.getNumber(), durationLabel);
213         RunUtil.getDefault().sleep(10);
214 
215         RunUtil.getDefault().sleep(2_000);
216 
217         // Stop uncounted duration.
218         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
219                 AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
220         RunUtil.getDefault().sleep(10);
221 
222         RunUtil.getDefault().sleep(2_000);
223         StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
224                 ExtensionRegistry.getEmptyRegistry());
225         assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
226         LogUtil.CLog.d("Received the following data: " + metricReport.toString());
227         assertThat(metricReport.hasDurationMetrics()).isTrue();
228         StatsLogReport.DurationMetricDataWrapper durationData
229                 = metricReport.getDurationMetrics();
230         assertThat(durationData.getDataCount()).isEqualTo(1);
231         long totalDuration = durationData.getData(0).getBucketInfoList().stream()
232                 .mapToLong(bucketInfo -> bucketInfo.getDurationNanos())
233                 .peek(durationNs -> assertThat(durationNs).isIn(Range.openClosed(0L, (long) 1e9)))
234                 .sum();
235         assertThat(totalDuration).isIn(Range.open((long) 2e9, (long) 3e9));
236     }
237 
testDurationMetricWithActivation()238     public void testDurationMetricWithActivation() throws Exception {
239         final int activationMatcherId = 5;
240         final int activationMatcherLabel = 5;
241         final int ttlSec = 5;
242         final int durationLabel = 1;
243 
244         // Add AtomMatchers.
245         AtomMatcher startAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
246                 APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, durationLabel);
247         AtomMatcher stopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
248                 APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, durationLabel);
249         StatsdConfigProto.AtomMatcher activationMatcher =
250                 MetricsUtils.appBreadcrumbMatcherWithLabel(activationMatcherId,
251                         activationMatcherLabel);
252 
253         StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
254                         MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
255                 .addAtomMatcher(startAtomMatcher)
256                 .addAtomMatcher(stopAtomMatcher)
257                 .addAtomMatcher(activationMatcher);
258 
259         // Add Predicates.
260         SimplePredicate simplePredicate = SimplePredicate.newBuilder()
261                 .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
262                 .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
263                 .build();
264         Predicate predicate = Predicate.newBuilder()
265                 .setId(MetricsUtils.StringToId("Predicate"))
266                 .setSimplePredicate(simplePredicate)
267                 .build();
268         builder.addPredicate(predicate);
269 
270         // Add DurationMetric.
271         builder.addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
272                         .setId(MetricsUtils.DURATION_METRIC_ID)
273                         .setWhat(predicate.getId())
274                         .setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
275                         .setBucket(StatsdConfigProto.TimeUnit.CTS))
276                 .addMetricActivation(StatsdConfigProto.MetricActivation.newBuilder()
277                         .setMetricId(MetricsUtils.DURATION_METRIC_ID)
278                         .addEventActivation(StatsdConfigProto.EventActivation.newBuilder()
279                                 .setAtomMatcherId(activationMatcherId)
280                                 .setActivationType(
281                                         StatsdConfigProto.ActivationType.ACTIVATE_IMMEDIATELY)
282                                 .setTtlSeconds(ttlSec)));
283 
284         // Upload config.
285         ConfigUtils.uploadConfig(getDevice(), builder);
286 
287         // Start uncounted duration.
288         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
289                 AppBreadcrumbReported.State.START.getNumber(), durationLabel);
290         RunUtil.getDefault().sleep(10);
291 
292         RunUtil.getDefault().sleep(2_000);
293 
294         // Stop uncounted duration.
295         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
296                 AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
297         RunUtil.getDefault().sleep(10);
298 
299         // Activate the metric.
300         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
301                 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), activationMatcherLabel);
302         RunUtil.getDefault().sleep(10);
303 
304         // Start counted duration.
305         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
306                 AppBreadcrumbReported.State.START.getNumber(), durationLabel);
307         RunUtil.getDefault().sleep(10);
308 
309         RunUtil.getDefault().sleep(2_000);
310 
311         // Stop counted duration.
312         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
313                 AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
314         RunUtil.getDefault().sleep(10);
315 
316         RunUtil.getDefault().sleep(2_000);
317         StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
318                 ExtensionRegistry.getEmptyRegistry());
319         assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
320         LogUtil.CLog.d("Received the following data: " + metricReport.toString());
321         assertThat(metricReport.hasDurationMetrics()).isTrue();
322         StatsLogReport.DurationMetricDataWrapper durationData
323                 = metricReport.getDurationMetrics();
324         assertThat(durationData.getDataCount()).isEqualTo(1);
325         long totalDuration = durationData.getData(0).getBucketInfoList().stream()
326                 .mapToLong(bucketInfo -> bucketInfo.getDurationNanos())
327                 .peek(durationNs -> assertThat(durationNs).isIn(Range.openClosed(0L, (long) 1e9)))
328                 .sum();
329         assertThat(totalDuration).isIn(Range.open((long) 2e9, (long) 3e9));
330     }
331 
testDurationMetricWithConditionAndActivation()332     public void testDurationMetricWithConditionAndActivation() throws Exception {
333         final int durationLabel = 1;
334         final int conditionLabel = 2;
335         final int activationMatcherId = 5;
336         final int activationMatcherLabel = 5;
337         final int ttlSec = 5;
338 
339         // Add AtomMatchers.
340         AtomMatcher startAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
341                 APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, durationLabel);
342         AtomMatcher stopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
343                 APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, durationLabel);
344         AtomMatcher conditionStartAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
345                 APP_BREADCRUMB_REPORTED_B_MATCH_START_ID, conditionLabel);
346         AtomMatcher conditionStopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
347                 APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID, conditionLabel);
348         StatsdConfigProto.AtomMatcher activationMatcher =
349                 MetricsUtils.appBreadcrumbMatcherWithLabel(activationMatcherId,
350                         activationMatcherLabel);
351 
352         StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
353                         MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
354                 .addAtomMatcher(startAtomMatcher)
355                 .addAtomMatcher(stopAtomMatcher)
356                 .addAtomMatcher(conditionStartAtomMatcher)
357                 .addAtomMatcher(conditionStopAtomMatcher)
358                 .addAtomMatcher(activationMatcher);
359 
360         // Add Predicates.
361         SimplePredicate simplePredicate = SimplePredicate.newBuilder()
362                 .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
363                 .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
364                 .build();
365         Predicate predicate = Predicate.newBuilder()
366                 .setId(MetricsUtils.StringToId("Predicate"))
367                 .setSimplePredicate(simplePredicate)
368                 .build();
369         builder.addPredicate(predicate);
370 
371         SimplePredicate conditionSimplePredicate = SimplePredicate.newBuilder()
372                 .setStart(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
373                 .setStop(APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID)
374                 .build();
375         Predicate conditionPredicate = Predicate.newBuilder()
376                 .setId(MetricsUtils.StringToId("ConditionPredicate"))
377                 .setSimplePredicate(conditionSimplePredicate)
378                 .build();
379         builder.addPredicate(conditionPredicate);
380 
381         // Add DurationMetric.
382         builder.addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
383                         .setId(MetricsUtils.DURATION_METRIC_ID)
384                         .setWhat(predicate.getId())
385                         .setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
386                         .setBucket(StatsdConfigProto.TimeUnit.CTS)
387                         .setCondition(conditionPredicate.getId()))
388                 .addMetricActivation(StatsdConfigProto.MetricActivation.newBuilder()
389                         .setMetricId(MetricsUtils.DURATION_METRIC_ID)
390                         .addEventActivation(StatsdConfigProto.EventActivation.newBuilder()
391                                 .setAtomMatcherId(activationMatcherId)
392                                 .setActivationType(
393                                         StatsdConfigProto.ActivationType.ACTIVATE_IMMEDIATELY)
394                                 .setTtlSeconds(ttlSec)));
395 
396         // Upload config.
397         ConfigUtils.uploadConfig(getDevice(), builder);
398 
399         // Activate the metric.
400         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
401                 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), activationMatcherLabel);
402         RunUtil.getDefault().sleep(10);
403 
404         // Set the condition to true.
405         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
406                 AppBreadcrumbReported.State.START.getNumber(), conditionLabel);
407         RunUtil.getDefault().sleep(10);
408 
409         // Start counted duration.
410         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
411                 AppBreadcrumbReported.State.START.getNumber(), durationLabel);
412         RunUtil.getDefault().sleep(10);
413 
414         RunUtil.getDefault().sleep(2_000);
415 
416         // Stop counted duration.
417         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
418                 AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
419         RunUtil.getDefault().sleep(10);
420 
421         // Set the condition to false.
422         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
423                 AppBreadcrumbReported.State.STOP.getNumber(), conditionLabel);
424         RunUtil.getDefault().sleep(10);
425 
426         // Start uncounted duration.
427         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
428                 AppBreadcrumbReported.State.START.getNumber(), durationLabel);
429         RunUtil.getDefault().sleep(10);
430 
431         RunUtil.getDefault().sleep(2_000);
432 
433         // Stop uncounted duration.
434         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
435                 AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
436         RunUtil.getDefault().sleep(10);
437 
438         // Let the metric deactivate.
439         RunUtil.getDefault().sleep(ttlSec * 1000);
440         //doAppBreadcrumbReported(99); // TODO: maybe remove?
441         //RunUtil.getDefault().sleep(10);
442 
443         // Start uncounted duration.
444         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
445                 AppBreadcrumbReported.State.START.getNumber(), durationLabel);
446         RunUtil.getDefault().sleep(10);
447 
448         RunUtil.getDefault().sleep(2_000);
449 
450         // Stop uncounted duration.
451         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
452                 AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
453         RunUtil.getDefault().sleep(10);
454 
455         // Set condition to true again.
456         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
457                 AppBreadcrumbReported.State.START.getNumber(), conditionLabel);
458         RunUtil.getDefault().sleep(10);
459 
460         // Start uncounted duration
461         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
462                 AppBreadcrumbReported.State.START.getNumber(), durationLabel);
463         RunUtil.getDefault().sleep(10);
464 
465         RunUtil.getDefault().sleep(2_000);
466 
467         // Stop uncounted duration.
468         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
469                 AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
470         RunUtil.getDefault().sleep(10);
471 
472         // Activate the metric.
473         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
474                 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), activationMatcherLabel);
475         RunUtil.getDefault().sleep(10);
476 
477         // Start counted duration.
478         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
479                 AppBreadcrumbReported.State.START.getNumber(), durationLabel);
480         RunUtil.getDefault().sleep(10);
481 
482         RunUtil.getDefault().sleep(2_000);
483 
484         // Stop counted duration.
485         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
486                 AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
487         RunUtil.getDefault().sleep(10);
488 
489         // Let the metric deactivate.
490         RunUtil.getDefault().sleep(ttlSec * 1000);
491 
492         // Start uncounted duration.
493         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
494                 AppBreadcrumbReported.State.START.getNumber(), durationLabel);
495         RunUtil.getDefault().sleep(10);
496 
497         RunUtil.getDefault().sleep(2_000);
498 
499         // Stop uncounted duration.
500         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
501                 AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
502         RunUtil.getDefault().sleep(10);
503 
504         // Wait for the metrics to propagate to statsd.
505         RunUtil.getDefault().sleep(2000);
506 
507         StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
508                 ExtensionRegistry.getEmptyRegistry());
509         LogUtil.CLog.d("Received the following data: " + metricReport.toString());
510         assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
511         assertThat(metricReport.hasDurationMetrics()).isTrue();
512         StatsLogReport.DurationMetricDataWrapper durationData
513                 = metricReport.getDurationMetrics();
514         assertThat(durationData.getDataCount()).isEqualTo(1);
515         long totalDuration = durationData.getData(0).getBucketInfoList().stream()
516                 .mapToLong(bucketInfo -> bucketInfo.getDurationNanos())
517                 .peek(durationNs -> assertThat(durationNs).isIn(Range.openClosed(0L, (long) 1e9)))
518                 .sum();
519         assertThat(totalDuration).isIn(Range.open((long) 4e9, (long) 5e9));
520     }
521 
testDurationMetricWithDimension()522     public void testDurationMetricWithDimension() throws Exception {
523         // Add AtomMatchers.
524         AtomMatcher startAtomMatcherA =
525                 MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
526         AtomMatcher stopAtomMatcherA =
527                 MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID);
528         AtomMatcher startAtomMatcherB =
529                 MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID);
530         AtomMatcher stopAtomMatcherB =
531                 MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID);
532 
533         StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
534                 MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
535         builder.addAtomMatcher(startAtomMatcherA);
536         builder.addAtomMatcher(stopAtomMatcherA);
537         builder.addAtomMatcher(startAtomMatcherB);
538         builder.addAtomMatcher(stopAtomMatcherB);
539 
540         // Add Predicates.
541         SimplePredicate simplePredicateA = SimplePredicate.newBuilder()
542                 .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
543                 .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
544                 .build();
545         Predicate predicateA = Predicate.newBuilder()
546                 .setId(MetricsUtils.StringToId("Predicate_A"))
547                 .setSimplePredicate(simplePredicateA)
548                 .build();
549         builder.addPredicate(predicateA);
550 
551         FieldMatcher.Builder dimensionsBuilder = FieldMatcher.newBuilder()
552                 .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER);
553         dimensionsBuilder
554                 .addChild(FieldMatcher.newBuilder().setField(
555                         AppBreadcrumbReported.LABEL_FIELD_NUMBER));
556         Predicate predicateB = Predicate.newBuilder()
557                 .setId(MetricsUtils.StringToId("Predicate_B"))
558                 .setSimplePredicate(SimplePredicate.newBuilder()
559                         .setStart(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
560                         .setStop(APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID)
561                         .setDimensions(dimensionsBuilder.build())
562                         .build())
563                 .build();
564         builder.addPredicate(predicateB);
565 
566         // Add DurationMetric.
567         builder.addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
568                 .setId(MetricsUtils.DURATION_METRIC_ID)
569                 .setWhat(predicateB.getId())
570                 .setCondition(predicateA.getId())
571                 .setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
572                 .setBucket(StatsdConfigProto.TimeUnit.CTS)
573                 .setDimensionsInWhat(
574                         FieldMatcher.newBuilder()
575                                 .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
576                                 .addChild(FieldMatcher.newBuilder().setField(
577                                         AppBreadcrumbReported.LABEL_FIELD_NUMBER))));
578 
579         // Upload config.
580         ConfigUtils.uploadConfig(getDevice(), builder);
581 
582         // Trigger events.
583         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
584                 AppBreadcrumbReported.State.START.getNumber(), 1);
585         RunUtil.getDefault().sleep(2000);
586         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
587                 AppBreadcrumbReported.State.START.getNumber(), 2);
588         RunUtil.getDefault().sleep(2000);
589         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
590                 AppBreadcrumbReported.State.STOP.getNumber(), 1);
591         RunUtil.getDefault().sleep(2000);
592         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
593                 AppBreadcrumbReported.State.STOP.getNumber(), 2);
594 
595         // Wait for the metrics to propagate to statsd.
596         RunUtil.getDefault().sleep(2000);
597 
598         StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
599                 ExtensionRegistry.getEmptyRegistry());
600         assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
601         assertThat(metricReport.hasDurationMetrics()).isTrue();
602         StatsLogReport.DurationMetricDataWrapper durationData
603                 = metricReport.getDurationMetrics();
604         assertThat(durationData.getDataCount()).isEqualTo(2);
605         assertThat(durationData.getData(0).getBucketInfoCount()).isGreaterThan(3);
606         assertThat(durationData.getData(1).getBucketInfoCount()).isGreaterThan(3);
607         long totalDuration = 0;
608         for (DurationBucketInfo bucketInfo : durationData.getData(0).getBucketInfoList()) {
609             assertThat(bucketInfo.getDurationNanos()).isIn(Range.openClosed(0L, (long) 1e9));
610             totalDuration += bucketInfo.getDurationNanos();
611         }
612         // Duration for both labels is expected to be 4s.
613         assertThat(totalDuration).isIn(Range.open((long) 3e9, (long) 8e9));
614         totalDuration = 0;
615         for (DurationBucketInfo bucketInfo : durationData.getData(1).getBucketInfoList()) {
616             assertThat(bucketInfo.getDurationNanos()).isIn(Range.openClosed(0L, (long) 1e9));
617             totalDuration += bucketInfo.getDurationNanos();
618         }
619         assertThat(totalDuration).isIn(Range.open((long) 3e9, (long) 8e9));
620     }
621 }
622