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.ReportUtils;
23 
24 import com.android.internal.os.StatsdConfigProto;
25 import com.android.internal.os.StatsdConfigProto.ActivationType;
26 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
27 import com.android.internal.os.StatsdConfigProto.EventActivation;
28 import com.android.internal.os.StatsdConfigProto.FieldFilter;
29 import com.android.internal.os.StatsdConfigProto.FieldMatcher;
30 import com.android.internal.os.StatsdConfigProto.GaugeMetric;
31 import com.android.internal.os.StatsdConfigProto.MetricActivation;
32 import com.android.internal.os.StatsdConfigProto.Predicate;
33 import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
34 import com.android.internal.os.StatsdConfigProto.SimplePredicate;
35 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
36 import com.android.internal.os.StatsdConfigProto.TimeUnit;
37 import com.android.os.AtomsProto.AppBreadcrumbReported;
38 import com.android.os.AtomsProto.Atom;
39 import com.android.os.StatsLog;
40 import com.android.os.StatsLog.GaugeBucketInfo;
41 import com.android.os.StatsLog.GaugeMetricData;
42 import com.android.os.StatsLog.StatsLogReport;
43 import com.android.tradefed.log.LogUtil;
44 import com.android.tradefed.testtype.DeviceTestCase;
45 import com.android.tradefed.util.Pair;
46 import com.android.tradefed.util.RunUtil;
47 
48 import com.google.protobuf.ExtensionRegistry;
49 
50 import java.util.ArrayList;
51 import java.util.Comparator;
52 import java.util.List;
53 
54 public class GaugeMetricsTests extends DeviceTestCase {
55 
56     private static final int APP_BREADCRUMB_REPORTED_A_MATCH_START_ID = 0;
57     private static final int APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID = 1;
58     private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2;
59 
60     @Override
setUp()61     protected void setUp() throws Exception {
62         super.setUp();
63         ConfigUtils.removeConfig(getDevice());
64         ReportUtils.clearReports(getDevice());
65         RunUtil.getDefault().sleep(1000);
66     }
67 
68     @Override
tearDown()69     protected void tearDown() throws Exception {
70         ConfigUtils.removeConfig(getDevice());
71         ReportUtils.clearReports(getDevice());
72         super.tearDown();
73     }
74 
testGaugeMetric()75     public void testGaugeMetric() throws Exception {
76         // Add AtomMatcher's.
77         AtomMatcher startAtomMatcher =
78                 MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
79         AtomMatcher stopAtomMatcher =
80                 MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID);
81         AtomMatcher atomMatcher =
82                 MetricsUtils.simpleAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID);
83 
84         StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
85                 MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
86         builder.addAtomMatcher(startAtomMatcher);
87         builder.addAtomMatcher(stopAtomMatcher);
88         builder.addAtomMatcher(atomMatcher);
89 
90         // Add Predicate's.
91         SimplePredicate simplePredicate = SimplePredicate.newBuilder()
92                 .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
93                 .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
94                 .build();
95         Predicate predicate = Predicate.newBuilder()
96                 .setId(MetricsUtils.StringToId("Predicate"))
97                 .setSimplePredicate(simplePredicate)
98                 .build();
99         builder.addPredicate(predicate);
100 
101         // Add GaugeMetric.
102         FieldMatcher fieldMatcher = FieldMatcher.newBuilder()
103                 .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
104                 .build();
105         builder.addGaugeMetric(StatsdConfigProto.GaugeMetric.newBuilder()
106                 .setId(MetricsUtils.GAUGE_METRIC_ID)
107                 .setWhat(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
108                 .setCondition(predicate.getId())
109                 .setGaugeFieldsFilter(FieldFilter.newBuilder()
110                         .setIncludeAll(false)
111                         .setFields(fieldMatcher)
112                         .build())
113                 .setDimensionsInWhat(FieldMatcher.newBuilder()
114                         .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
115                         .addChild(FieldMatcher.newBuilder()
116                                 .setField(AppBreadcrumbReported.STATE_FIELD_NUMBER)
117                                 .build())
118                         .build())
119                 .setBucket(StatsdConfigProto.TimeUnit.CTS)
120                 .build());
121 
122         // Upload config.
123         ConfigUtils.uploadConfig(getDevice(), builder);
124 
125         // Create AppBreadcrumbReported Start/Stop events.
126         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
127                 AppBreadcrumbReported.State.START.getNumber(), 0);
128         RunUtil.getDefault().sleep(10);
129         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
130                 AppBreadcrumbReported.State.START.getNumber(), 1);
131         RunUtil.getDefault().sleep(10);
132         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
133                 AppBreadcrumbReported.State.START.getNumber(), 2);
134         RunUtil.getDefault().sleep(2000);
135         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
136                 AppBreadcrumbReported.State.STOP.getNumber(), 2);
137         RunUtil.getDefault().sleep(10);
138         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
139                 AppBreadcrumbReported.State.STOP.getNumber(), 0);
140         RunUtil.getDefault().sleep(10);
141         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
142                 AppBreadcrumbReported.State.STOP.getNumber(), 1);
143         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
144                 AppBreadcrumbReported.State.START.getNumber(), 2);
145         RunUtil.getDefault().sleep(10);
146         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
147                 AppBreadcrumbReported.State.START.getNumber(), 1);
148         RunUtil.getDefault().sleep(2000);
149         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
150                 AppBreadcrumbReported.State.STOP.getNumber(), 2);
151         RunUtil.getDefault().sleep(10);
152         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
153                 AppBreadcrumbReported.State.STOP.getNumber(), 1);
154 
155         // Wait for the metrics to propagate to statsd.
156         RunUtil.getDefault().sleep(2000);
157 
158         StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
159                 ExtensionRegistry.getEmptyRegistry());
160         LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
161         assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
162         assertThat(metricReport.hasGaugeMetrics()).isTrue();
163         StatsLogReport.GaugeMetricDataWrapper gaugeData = metricReport.getGaugeMetrics();
164         gaugeData = backfillGaugeMetricData(gaugeData);
165         assertThat(gaugeData.getDataCount()).isEqualTo(1);
166 
167         int bucketCount = gaugeData.getData(0).getBucketInfoCount();
168         GaugeMetricData data = gaugeData.getData(0);
169         assertThat(bucketCount).isGreaterThan(2);
170         MetricsUtils.assertBucketTimePresent(data.getBucketInfo(0));
171         assertThat(data.getBucketInfo(0).getAtomCount()).isEqualTo(1);
172         assertThat(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getLabel())
173                 .isEqualTo(0);
174         assertThat(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getState())
175                 .isEqualTo(AppBreadcrumbReported.State.START);
176 
177         MetricsUtils.assertBucketTimePresent(data.getBucketInfo(1));
178         assertThat(data.getBucketInfo(1).getAtomCount()).isEqualTo(1);
179 
180         MetricsUtils.assertBucketTimePresent(data.getBucketInfo(bucketCount - 1));
181         assertThat(data.getBucketInfo(bucketCount - 1).getAtomCount()).isEqualTo(1);
182         assertThat(data.getBucketInfo(bucketCount - 1).getAtom(
183                 0).getAppBreadcrumbReported().getLabel())
184                 .isEqualTo(2);
185         assertThat(data.getBucketInfo(bucketCount - 1).getAtom(
186                 0).getAppBreadcrumbReported().getState())
187                 .isEqualTo(AppBreadcrumbReported.State.STOP);
188     }
189 
testPulledGaugeMetricWithActivation()190     public void testPulledGaugeMetricWithActivation() throws Exception {
191         // Add AtomMatcher's.
192         int activationAtomMatcherId = 1;
193         int activationAtomMatcherLabel = 1;
194 
195         int systemUptimeMatcherId = 2;
196         AtomMatcher activationAtomMatcher = MetricsUtils.appBreadcrumbMatcherWithLabel(
197                 activationAtomMatcherId, activationAtomMatcherLabel);
198         AtomMatcher systemUptimeMatcher = AtomMatcher.newBuilder()
199                 .setId(systemUptimeMatcherId)
200                 .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
201                         .setAtomId(Atom.SYSTEM_UPTIME_FIELD_NUMBER))
202                 .build();
203 
204         StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
205                 MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
206         builder.addAtomMatcher(activationAtomMatcher);
207         builder.addAtomMatcher(systemUptimeMatcher);
208 
209         // Add GaugeMetric.
210         builder.addGaugeMetric(StatsdConfigProto.GaugeMetric.newBuilder()
211                 .setId(MetricsUtils.GAUGE_METRIC_ID)
212                 .setWhat(systemUptimeMatcherId)
213                 .setGaugeFieldsFilter(
214                         FieldFilter.newBuilder().setIncludeAll(true).build())
215                 .setBucket(StatsdConfigProto.TimeUnit.CTS)
216                 .build());
217 
218         // Add activation.
219         builder.addMetricActivation(MetricActivation.newBuilder()
220                 .setMetricId(MetricsUtils.GAUGE_METRIC_ID)
221                 .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
222                 .addEventActivation(EventActivation.newBuilder()
223                         .setAtomMatcherId(activationAtomMatcherId)
224                         .setTtlSeconds(5)));
225 
226         // Upload config.
227         ConfigUtils.uploadConfig(getDevice(), builder);
228 
229         // Plenty of time to pull, but we should not keep the data since we are not active.
230         RunUtil.getDefault().sleep(20_000);
231 
232         StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
233                 ExtensionRegistry.getEmptyRegistry());
234         LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
235         assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
236         assertThat(metricReport.hasGaugeMetrics()).isFalse();
237     }
238 
testPulledGaugeMetricWithConditionAndActivation()239     public void testPulledGaugeMetricWithConditionAndActivation() throws Exception {
240         final int conditionLabel = 2;
241         final int activationMatcherId = 5;
242         final int activationMatcherLabel = 5;
243         final int whatMatcherId = 8;
244         final int ttlSec = 5;
245 
246         // Add AtomMatchers.
247         AtomMatcher conditionStartAtomMatcher = MetricsUtils.startAtomMatcherWithLabel(
248                 APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, conditionLabel);
249         AtomMatcher conditionStopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
250                 APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, conditionLabel);
251         AtomMatcher activationMatcher = MetricsUtils.startAtomMatcherWithLabel(
252                 activationMatcherId, activationMatcherLabel);
253         AtomMatcher whatMatcher = MetricsUtils.unspecifiedAtomMatcher(whatMatcherId);
254 
255         StatsdConfig.Builder builder =
256                 ConfigUtils.createConfigBuilder(MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
257                         .addAtomMatcher(conditionStartAtomMatcher)
258                         .addAtomMatcher(conditionStopAtomMatcher)
259                         .addAtomMatcher(whatMatcher)
260                         .addAtomMatcher(activationMatcher);
261 
262         // Add Predicates.
263         SimplePredicate simplePredicate = SimplePredicate.newBuilder()
264                 .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
265                 .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
266                 .build();
267         Predicate predicate = Predicate.newBuilder()
268                 .setId(MetricsUtils.StringToId("Predicate"))
269                 .setSimplePredicate(simplePredicate)
270                 .build();
271         builder.addPredicate(predicate);
272 
273         // Add GaugeMetric.
274         builder.addGaugeMetric(GaugeMetric.newBuilder()
275                         .setId(MetricsUtils.GAUGE_METRIC_ID)
276                         .setWhat(whatMatcher.getId())
277                         .setBucket(TimeUnit.ONE_MINUTE)
278                         .setCondition(predicate.getId())
279                         .setGaugeFieldsFilter(FieldFilter.newBuilder()
280                                 .setIncludeAll(false)
281                                 .setFields(FieldMatcher.newBuilder()
282                                         .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)))
283                         .setDimensionsInWhat(FieldMatcher.newBuilder().setField(whatMatcherId)))
284                 .addMetricActivation(MetricActivation.newBuilder()
285                         .setMetricId(MetricsUtils.GAUGE_METRIC_ID)
286                         .addEventActivation(EventActivation.newBuilder()
287                                 .setAtomMatcherId(activationMatcherId)
288                                 .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
289                                 .setTtlSeconds(ttlSec)));
290 
291         ConfigUtils.uploadConfig(getDevice(), builder);
292 
293         // Activate the metric.
294         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
295                 AppBreadcrumbReported.State.START.getNumber(), activationMatcherLabel);
296         RunUtil.getDefault().sleep(10);
297 
298         // Set the condition to true.
299         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
300                 AppBreadcrumbReported.State.START.getNumber(), conditionLabel);
301         RunUtil.getDefault().sleep(10);
302 
303         // This value is collected.
304         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
305                 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 10);
306         RunUtil.getDefault().sleep(10);
307 
308         // Ignored; value already collected.
309         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
310                 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 20);
311         RunUtil.getDefault().sleep(10);
312 
313         // Set the condition to false.
314         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
315                 AppBreadcrumbReported.State.STOP.getNumber(), conditionLabel);
316         RunUtil.getDefault().sleep(10);
317 
318         // Value not updated because condition is false.
319         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
320                 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 30);
321         RunUtil.getDefault().sleep(10);
322 
323         // Let the metric deactivate.
324         RunUtil.getDefault().sleep(ttlSec * 1000);
325 
326         // Value not collected.
327         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
328                 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 40);
329         RunUtil.getDefault().sleep(10);
330 
331         // Condition to true again.
332         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
333                 AppBreadcrumbReported.State.START.getNumber(), conditionLabel);
334         RunUtil.getDefault().sleep(10);
335 
336         // Value not collected.
337         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
338                 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 50);
339         RunUtil.getDefault().sleep(10);
340 
341         // Activate the metric.
342         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
343                 AppBreadcrumbReported.State.START.getNumber(), activationMatcherLabel);
344         RunUtil.getDefault().sleep(10);
345 
346         // Value collected.
347         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
348                 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 60);
349         RunUtil.getDefault().sleep(10);
350 
351         // Let the metric deactivate.
352         RunUtil.getDefault().sleep(ttlSec * 1000);
353 
354         // Value not collected.
355         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
356                 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 70);
357         RunUtil.getDefault().sleep(10);
358 
359         // Wait for the metrics to propagate to statsd.
360         RunUtil.getDefault().sleep(2000);
361 
362         StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
363                 ExtensionRegistry.getEmptyRegistry());
364         LogUtil.CLog.d("Received the following data: " + metricReport.toString());
365         assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
366         assertThat(metricReport.hasGaugeMetrics()).isTrue();
367         assertThat(metricReport.getIsActive()).isFalse();
368 
369         StatsLogReport.GaugeMetricDataWrapper gaugeData = metricReport.getGaugeMetrics();
370         gaugeData = backfillGaugeMetricData(gaugeData);
371         assertThat(gaugeData.getDataCount()).isEqualTo(1);
372         assertThat(gaugeData.getData(0).getBucketInfoCount()).isEqualTo(2);
373 
374         GaugeBucketInfo bucketInfo = gaugeData.getData(0).getBucketInfo(0);
375         MetricsUtils.assertBucketTimePresent(bucketInfo);
376         assertThat(bucketInfo.getAtomCount()).isEqualTo(1);
377         assertThat(bucketInfo.getAtom(0).getAppBreadcrumbReported().getLabel()).isEqualTo(10);
378 
379         bucketInfo = gaugeData.getData(0).getBucketInfo(1);
380         MetricsUtils.assertBucketTimePresent(bucketInfo);
381         assertThat(bucketInfo.getAtomCount()).isEqualTo(1);
382         assertThat(bucketInfo.getAtom(0).getAppBreadcrumbReported().getLabel()).isEqualTo(60);
383     }
384 
backfillGaugeMetricData( StatsLogReport.GaugeMetricDataWrapper dataWrapper)385     private StatsLogReport.GaugeMetricDataWrapper backfillGaugeMetricData(
386             StatsLogReport.GaugeMetricDataWrapper dataWrapper) {
387         StatsLogReport.GaugeMetricDataWrapper.Builder dataWrapperBuilder = dataWrapper.toBuilder();
388         List<GaugeMetricData> backfilledMetricData = new ArrayList<>();
389         for (GaugeMetricData gaugeMetricData : dataWrapperBuilder.getDataList()) {
390             GaugeMetricData.Builder gaugeMetricDataBuilder = gaugeMetricData.toBuilder();
391             List<GaugeBucketInfo> backfilledBuckets = new ArrayList<>();
392             for (GaugeBucketInfo bucketInfo : gaugeMetricData.getBucketInfoList()) {
393                 backfilledBuckets.add(backfillGaugeBucket(bucketInfo.toBuilder()));
394             }
395             gaugeMetricDataBuilder.clearBucketInfo();
396             gaugeMetricDataBuilder.addAllBucketInfo(backfilledBuckets);
397             backfilledMetricData.add(gaugeMetricDataBuilder.build());
398         }
399         dataWrapperBuilder.clearData();
400         dataWrapperBuilder.addAllData(backfilledMetricData);
401         return dataWrapperBuilder.build();
402     }
403 
backfillGaugeBucket(GaugeBucketInfo.Builder bucketInfoBuilder)404     private GaugeBucketInfo backfillGaugeBucket(GaugeBucketInfo.Builder bucketInfoBuilder) {
405         if (bucketInfoBuilder.getAtomCount() != 0) {
406             return bucketInfoBuilder.build();
407         }
408         List<Pair<Atom, Long>> atomTimestampData = new ArrayList<>();
409         for (StatsLog.AggregatedAtomInfo atomInfo : bucketInfoBuilder.getAggregatedAtomInfoList()) {
410             for (long timestampNs : atomInfo.getElapsedTimestampNanosList()) {
411                 atomTimestampData.add(Pair.create(atomInfo.getAtom(), timestampNs));
412             }
413         }
414         atomTimestampData.sort(Comparator.comparing(o -> o.second));
415         bucketInfoBuilder.clearAggregatedAtomInfo();
416         for (Pair<Atom, Long> atomTimestamp : atomTimestampData) {
417             bucketInfoBuilder.addAtom(atomTimestamp.first);
418             bucketInfoBuilder.addElapsedTimestampNanos(atomTimestamp.second);
419         }
420         return bucketInfoBuilder.build();
421     }
422 }
423