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 import static com.google.common.truth.Truth.assertWithMessage;
20 
21 import android.cts.statsd.atom.DeviceAtomTestCase;
22 
23 import com.android.internal.os.StatsdConfigProto;
24 import com.android.internal.os.StatsdConfigProto.FieldMatcher;
25 import com.android.internal.os.StatsdConfigProto.Position;
26 import com.android.os.AtomsProto.Atom;
27 import com.android.os.AtomsProto.AppBreadcrumbReported;
28 import com.android.os.AtomsProto.AttributionNode;
29 import com.android.os.AtomsProto.BleScanStateChanged;
30 import com.android.os.AtomsProto.WakelockStateChanged;
31 import com.android.os.StatsLog;
32 import com.android.os.StatsLog.ConfigMetricsReport;
33 import com.android.os.StatsLog.ConfigMetricsReportList;
34 import com.android.os.StatsLog.CountBucketInfo;
35 import com.android.os.StatsLog.CountMetricData;
36 import com.android.os.StatsLog.StatsLogReport;
37 import com.android.tradefed.device.DeviceNotAvailableException;
38 import com.android.tradefed.log.LogUtil;
39 
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.stream.Collectors;
43 import java.util.stream.IntStream;
44 
45 public class CountMetricsTests extends DeviceAtomTestCase {
46 
testSimpleEventCountMetric()47     public void testSimpleEventCountMetric() throws Exception {
48         int matcherId = 1;
49         StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
50         builder.addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
51                 .setId(MetricsUtils.COUNT_METRIC_ID)
52                 .setBucket(StatsdConfigProto.TimeUnit.CTS)
53                 .setWhat(matcherId))
54                 .addAtomMatcher(MetricsUtils.simpleAtomMatcher(matcherId));
55         uploadConfig(builder);
56 
57         doAppBreadcrumbReportedStart(0);
58         Thread.sleep(100);
59         doAppBreadcrumbReportedStop(0);
60         Thread.sleep(2000);  // Wait for the metrics to propagate to statsd.
61 
62         StatsLogReport metricReport = getStatsLogReport();
63         LogUtil.CLog.d("Got the following stats log report: \n" + metricReport.toString());
64         assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
65         assertThat(metricReport.hasCountMetrics()).isTrue();
66 
67         StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics();
68 
69         assertThat(countData.getDataCount()).isGreaterThan(0);
70         assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(2);
71     }
testEventCountWithCondition()72     public void testEventCountWithCondition() throws Exception {
73         int startMatcherId = 1;
74         int endMatcherId = 2;
75         int whatMatcherId = 3;
76         int conditionId = 4;
77 
78         StatsdConfigProto.AtomMatcher whatMatcher =
79                MetricsUtils.unspecifiedAtomMatcher(whatMatcherId);
80 
81         StatsdConfigProto.AtomMatcher predicateStartMatcher =
82                 MetricsUtils.startAtomMatcher(startMatcherId);
83 
84         StatsdConfigProto.AtomMatcher predicateEndMatcher =
85                 MetricsUtils.stopAtomMatcher(endMatcherId);
86 
87         StatsdConfigProto.Predicate p = StatsdConfigProto.Predicate.newBuilder()
88                 .setSimplePredicate(StatsdConfigProto.SimplePredicate.newBuilder()
89                         .setStart(startMatcherId)
90                         .setStop(endMatcherId)
91                         .setCountNesting(false))
92                 .setId(conditionId)
93                 .build();
94 
95         StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
96                 .addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
97                         .setId(MetricsUtils.COUNT_METRIC_ID)
98                         .setBucket(StatsdConfigProto.TimeUnit.CTS)
99                         .setWhat(whatMatcherId)
100                         .setCondition(conditionId))
101                 .addAtomMatcher(whatMatcher)
102                 .addAtomMatcher(predicateStartMatcher)
103                 .addAtomMatcher(predicateEndMatcher)
104                 .addPredicate(p);
105 
106         uploadConfig(builder);
107 
108         doAppBreadcrumbReported(0, AppBreadcrumbReported.State.UNSPECIFIED.ordinal());
109         Thread.sleep(10);
110         doAppBreadcrumbReportedStart(0);
111         Thread.sleep(10);
112         doAppBreadcrumbReported(0, AppBreadcrumbReported.State.UNSPECIFIED.ordinal());
113         Thread.sleep(10);
114         doAppBreadcrumbReportedStop(0);
115         Thread.sleep(10);
116         doAppBreadcrumbReported(0, AppBreadcrumbReported.State.UNSPECIFIED.ordinal());
117         Thread.sleep(2000);  // Wait for the metrics to propagate to statsd.
118 
119         StatsLogReport metricReport = getStatsLogReport();
120         assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
121         assertThat(metricReport.hasCountMetrics()).isTrue();
122 
123         StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics();
124 
125         assertThat(countData.getDataCount()).isGreaterThan(0);
126         assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(1);
127     }
128 
testEventCountWithConditionAndActivation()129     public void testEventCountWithConditionAndActivation() throws Exception {
130         int startMatcherId = 1;
131         int startMatcherLabel = 1;
132         int endMatcherId = 2;
133         int endMatcherLabel = 2;
134         int whatMatcherId = 3;
135         int whatMatcherLabel = 3;
136         int conditionId = 4;
137         int activationMatcherId = 5;
138         int activationMatcherLabel = 5;
139         int ttlSec = 5;
140 
141         StatsdConfigProto.AtomMatcher whatMatcher =
142                 MetricsUtils.appBreadcrumbMatcherWithLabel(whatMatcherId, whatMatcherLabel);
143 
144         StatsdConfigProto.AtomMatcher predicateStartMatcher =
145                 MetricsUtils.startAtomMatcherWithLabel(startMatcherId, startMatcherLabel);
146 
147         StatsdConfigProto.AtomMatcher predicateEndMatcher =
148                 MetricsUtils.stopAtomMatcherWithLabel(endMatcherId, endMatcherLabel);
149 
150         StatsdConfigProto.AtomMatcher activationMatcher =
151                 MetricsUtils.appBreadcrumbMatcherWithLabel(activationMatcherId,
152                                                            activationMatcherLabel);
153 
154         StatsdConfigProto.Predicate p = StatsdConfigProto.Predicate.newBuilder()
155                 .setSimplePredicate(StatsdConfigProto.SimplePredicate.newBuilder()
156                         .setStart(startMatcherId)
157                         .setStop(endMatcherId)
158                         .setCountNesting(false))
159                 .setId(conditionId)
160                 .build();
161 
162         StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
163                 .addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
164                         .setId(MetricsUtils.COUNT_METRIC_ID)
165                         .setBucket(StatsdConfigProto.TimeUnit.CTS)
166                         .setWhat(whatMatcherId)
167                         .setCondition(conditionId)
168                 )
169                 .addAtomMatcher(whatMatcher)
170                 .addAtomMatcher(predicateStartMatcher)
171                 .addAtomMatcher(predicateEndMatcher)
172                 .addAtomMatcher(activationMatcher)
173                 .addPredicate(p)
174                 .addMetricActivation(StatsdConfigProto.MetricActivation.newBuilder()
175                         .setMetricId(MetricsUtils.COUNT_METRIC_ID)
176                         .setActivationType(StatsdConfigProto.ActivationType.ACTIVATE_IMMEDIATELY)
177                         .addEventActivation(StatsdConfigProto.EventActivation.newBuilder()
178                                 .setAtomMatcherId(activationMatcherId)
179                                 .setTtlSeconds(ttlSec)));
180 
181         uploadConfig(builder);
182 
183         // Activate the metric.
184         doAppBreadcrumbReported(activationMatcherLabel);
185         Thread.sleep(10);
186 
187         // Set the condition to true.
188         doAppBreadcrumbReportedStart(startMatcherLabel);
189         Thread.sleep(10);
190 
191         // Log an event that should be counted. Bucket 1 Count 1.
192         doAppBreadcrumbReported(whatMatcherLabel);
193         Thread.sleep(10);
194 
195         // Log an event that should be counted. Bucket 1 Count 2.
196         doAppBreadcrumbReported(whatMatcherLabel);
197         Thread.sleep(10);
198 
199         // Set the condition to false.
200         doAppBreadcrumbReportedStop(endMatcherLabel);
201         Thread.sleep(10);
202 
203         // Log an event that should not be counted because condition is false.
204         doAppBreadcrumbReported(whatMatcherLabel);
205         Thread.sleep(10);
206 
207         // Let the metric deactivate.
208         Thread.sleep(ttlSec * 1000);
209 
210         // Log an event that should not be counted.
211         doAppBreadcrumbReported(whatMatcherLabel);
212         Thread.sleep(10);
213 
214         // Condition to true again.
215         doAppBreadcrumbReportedStart(startMatcherLabel);
216         Thread.sleep(10);
217 
218         // Event should not be counted, metric is still not active.
219         doAppBreadcrumbReported(whatMatcherLabel);
220         Thread.sleep(10);
221 
222         // Activate the metric.
223         doAppBreadcrumbReported(activationMatcherLabel);
224         Thread.sleep(10);
225 
226         //  Log an event that should be counted.
227         doAppBreadcrumbReported(whatMatcherLabel);
228         Thread.sleep(10);
229 
230         // Let the metric deactivate.
231         Thread.sleep(ttlSec * 1000);
232 
233         // Log an event that should not be counted.
234         doAppBreadcrumbReported(whatMatcherLabel);
235         Thread.sleep(10);
236 
237         // Wait for the metrics to propagate to statsd.
238         Thread.sleep(2000);
239 
240         StatsLogReport metricReport = getStatsLogReport();
241         assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
242         LogUtil.CLog.d("Received the following data: " + metricReport.toString());
243         assertThat(metricReport.hasCountMetrics()).isTrue();
244         assertThat(metricReport.getIsActive()).isFalse();
245 
246         StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics();
247         assertThat(countData.getDataCount()).isEqualTo(1);
248         assertThat(countData.getData(0).getBucketInfoCount()).isEqualTo(2);
249         assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(2);
250         assertThat(countData.getData(0).getBucketInfo(1).getCount()).isEqualTo(1);
251     }
252 
testPartialBucketCountMetric()253     public void testPartialBucketCountMetric() throws Exception {
254         int matcherId = 1;
255         StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
256         builder
257             .addCountMetric(
258                 StatsdConfigProto.CountMetric.newBuilder()
259                     .setId(MetricsUtils.COUNT_METRIC_ID)
260                     .setBucket(StatsdConfigProto.TimeUnit.ONE_DAY) // Ensures partial bucket.
261                     .setWhat(matcherId)
262                     .setSplitBucketForAppUpgrade(true))
263             .addAtomMatcher(MetricsUtils.simpleAtomMatcher(matcherId));
264         uploadConfig(builder);
265 
266         doAppBreadcrumbReportedStart(0);
267 
268         builder.getCountMetricBuilder(0).setBucket(StatsdConfigProto.TimeUnit.CTS);
269         uploadConfig(builder);  // The count metric had a partial bucket.
270         doAppBreadcrumbReportedStart(0);
271         Thread.sleep(10);
272         doAppBreadcrumbReportedStart(0);
273         Thread.sleep(WAIT_TIME_LONG); // Finish the current bucket.
274 
275         ConfigMetricsReportList reports = getReportList();
276         LogUtil.CLog.d("Got following report list: " + reports.toString());
277 
278         assertThat(reports.getReportsCount()).isEqualTo(2);
279         boolean inOrder = reports.getReports(0).getCurrentReportWallClockNanos() <
280                 reports.getReports(1).getCurrentReportWallClockNanos();
281 
282         // Only 1 metric, so there should only be 1 StatsLogReport.
283         for (ConfigMetricsReport report : reports.getReportsList()) {
284             assertThat(report.getMetricsCount()).isEqualTo(1);
285             assertThat(report.getMetrics(0).getCountMetrics().getDataCount()).isEqualTo(1);
286         }
287         CountMetricData data1 =
288                 reports.getReports(inOrder? 0 : 1).getMetrics(0).getCountMetrics().getData(0);
289         CountMetricData data2 =
290                 reports.getReports(inOrder? 1 : 0).getMetrics(0).getCountMetrics().getData(0);
291         // Data1 should have only 1 bucket, and it should be a partial bucket.
292         // The count should be 1.
293         assertThat(data1.getBucketInfoCount()).isEqualTo(1);
294         CountBucketInfo bucketInfo = data1.getBucketInfo(0);
295         assertThat(bucketInfo.getCount()).isEqualTo(1);
296         assertWithMessage("First report's bucket should be less than 1 day")
297                 .that(bucketInfo.getEndBucketElapsedNanos())
298                 .isLessThan(bucketInfo.getStartBucketElapsedNanos() +
299                         1_000_000_000L * 60L * 60L * 24L);
300 
301         //Second report should have a count of 2.
302         assertThat(data2.getBucketInfoCount()).isAtMost(2);
303         int totalCount = 0;
304         for (CountBucketInfo bucket : data2.getBucketInfoList()) {
305             totalCount += bucket.getCount();
306         }
307         assertThat(totalCount).isEqualTo(2);
308     }
309 
310     public void testSlicedStateCountMetricNoReset() throws Exception {
311         int whatMatcherId = 3;
312         int stateId = 4;
313         int onStateGroupId = 5;
314         int offStateGroupId = 6;
315 
316         // Atom 9998 {
317         //     repeated AttributionNode attribution_node = 1;
318         //     optional WakeLockLevelEnum type = 2;
319         //     optional string tag = 3;
320         // }
321         int whatAtomId = 9_998;
322 
323         StatsdConfigProto.AtomMatcher whatMatcher =
324                 MetricsUtils.getAtomMatcher(whatAtomId)
325                         .setId(whatMatcherId)
326                         .build();
327 
328         StatsdConfigProto.State state = StatsdConfigProto.State.newBuilder()
329             .setId(stateId)
330             .setAtomId(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER)
331             .setMap(StatsdConfigProto.StateMap.newBuilder()
332                     .addGroup(StatsdConfigProto.StateMap.StateGroup.newBuilder()
333                             .setGroupId(onStateGroupId)
334                             .addValue(WakelockStateChanged.State.ACQUIRE_VALUE)
335                             .addValue(WakelockStateChanged.State.CHANGE_ACQUIRE_VALUE)
336                     )
337                     .addGroup(StatsdConfigProto.StateMap.StateGroup.newBuilder()
338                             .setGroupId(offStateGroupId)
339                             .addValue(WakelockStateChanged.State.RELEASE_VALUE)
340                             .addValue(WakelockStateChanged.State.CHANGE_RELEASE_VALUE)
341                     )
342             )
343             .build();
344 
345         StatsdConfigProto.MetricStateLink stateLink = StatsdConfigProto.MetricStateLink.newBuilder()
346             .setStateAtomId(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER)
347             .setFieldsInWhat(FieldMatcher.newBuilder()
348                     .setField(whatAtomId)
349                     .addChild(FieldMatcher.newBuilder()
350                             .setField(1)
351                             .setPosition(Position.FIRST)
352                             .addChild(FieldMatcher.newBuilder()
353                                     .setField(AttributionNode.UID_FIELD_NUMBER)
354                             )
355                     )
356                     .addChild(FieldMatcher.newBuilder()
357                             .setField(2)
358                     )
359                     .addChild(FieldMatcher.newBuilder()
360                             .setField(3)
361                     )
362             )
363             .setFieldsInState(FieldMatcher.newBuilder()
364                     .setField(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER)
365                     .addChild(FieldMatcher.newBuilder()
366                             .setField(WakelockStateChanged.ATTRIBUTION_NODE_FIELD_NUMBER)
367                             .setPosition(Position.FIRST)
368                             .addChild(FieldMatcher.newBuilder()
369                                     .setField(AttributionNode.UID_FIELD_NUMBER)
370                             )
371                     )
372                     .addChild(FieldMatcher.newBuilder()
373                             .setField(WakelockStateChanged.TYPE_FIELD_NUMBER)
374                     )
375                     .addChild(FieldMatcher.newBuilder()
376                             .setField(WakelockStateChanged.TAG_FIELD_NUMBER)
377                     )
378             )
379             .build();
380 
381         StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
382                 .addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
383                     .setId(MetricsUtils.COUNT_METRIC_ID)
384                     .setBucket(StatsdConfigProto.TimeUnit.CTS)
385                     .setWhat(whatMatcherId)
386                     .addSliceByState(stateId)
387                     .addStateLink(stateLink)
388                 )
389                 .addAtomMatcher(whatMatcher)
390                 .addState(state);
391         uploadConfig(builder);
392 
393         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSliceByWakelockState");
394 
395         StatsLogReport metricReport = getStatsLogReport();
396         LogUtil.CLog.d("Got the following stats log report: \n" + metricReport.toString());
397         assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
398         assertThat(metricReport.hasCountMetrics()).isTrue();
399 
400         StatsLogReport.CountMetricDataWrapper dataWrapper = metricReport.getCountMetrics();
401         assertThat(dataWrapper.getDataCount()).isEqualTo(2);
402 
403 
404         List<CountMetricData> sortedDataList = IntStream.range(0, dataWrapper.getDataCount())
405                 .mapToObj(i -> {
406                         CountMetricData data = dataWrapper.getData(i);
407                         assertWithMessage("Unexpected SliceByState count for data[%s]", "" + i)
408                                 .that(data.getSliceByStateCount()).isEqualTo(1);
409                         return data;
410                 })
411                 .sorted((data1, data2) ->
412                         Long.compare(data1.getSliceByState(0).getGroupId(),
413                                 data2.getSliceByState(0).getGroupId())
414                 )
415                 .collect(Collectors.toList());
416 
417         CountMetricData data = sortedDataList.get(0);
418         assertThat(data.getSliceByState(0).getAtomId())
419                 .isEqualTo(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER);
420         assertThat(data.getSliceByState(0).getGroupId())
421                 .isEqualTo(onStateGroupId);
422         long totalCount = data.getBucketInfoList().stream()
423                 .mapToLong(CountBucketInfo::getCount)
424                 .sum();
425         assertThat(totalCount).isEqualTo(6);
426 
427         data = sortedDataList.get(1);
428         assertThat(data.getSliceByState(0).getAtomId())
429                 .isEqualTo(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER);
430         assertThat(data.getSliceByState(0).getGroupId())
431                 .isEqualTo(offStateGroupId);
432         totalCount = data.getBucketInfoList().stream()
433                 .mapToLong(CountBucketInfo::getCount)
434                 .sum();
435         assertThat(totalCount).isEqualTo(3);
436     }
437 }
438