1// Copyright (C) 2017 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include <gtest/gtest.h>
16
17#include "src/StatsLogProcessor.h"
18#include "src/stats_log_util.h"
19#include "tests/statsd_test_util.h"
20
21#include <vector>
22
23namespace android {
24namespace os {
25namespace statsd {
26
27#ifdef __ANDROID__
28
29namespace {
30
31StatsdConfig CreateDurationMetricConfig_NoLink_SimpleCondition(
32        DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
33    StatsdConfig config;
34    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
35    *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
36    *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
37    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
38    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
39
40    auto scheduledJobPredicate = CreateScheduledJobPredicate();
41    auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
42    dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
43    dimensions->add_child()->set_field(2);  // job name field.
44
45    auto isSyncingPredicate = CreateIsSyncingPredicate();
46    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
47    *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
48                                                          {Position::FIRST});
49    if (addExtraDimensionInCondition) {
50        syncDimension->add_child()->set_field(2 /* name field*/);
51    }
52
53    *config.add_predicate() = scheduledJobPredicate;
54    *config.add_predicate() = isSyncingPredicate;
55
56    auto metric = config.add_duration_metric();
57    metric->set_bucket(FIVE_MINUTES);
58    metric->set_id(StringToId("scheduledJob"));
59    metric->set_what(scheduledJobPredicate.id());
60    metric->set_condition(isSyncingPredicate.id());
61    metric->set_aggregation_type(aggregationType);
62    auto dimensionWhat = metric->mutable_dimensions_in_what();
63    dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
64    dimensionWhat->add_child()->set_field(2);  // job name field.
65    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
66            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
67    return config;
68}
69
70}  // namespace
71
72TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition) {
73    for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : {true, false}) {
74        for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
75            ConfigKey cfgKey;
76            auto config = CreateDurationMetricConfig_NoLink_SimpleCondition(
77                    aggregationType, isDimensionInConditionSubSetOfConditionTrackerDimension);
78            int64_t bucketStartTimeNs = 10000000000;
79            int64_t bucketSizeNs =
80                    TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
81
82            auto processor = CreateStatsLogProcessor(
83                    bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
84            EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
85            EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
86
87            std::vector<AttributionNodeInternal> attributions1 = {
88                    CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
89                    CreateAttribution(222, "GMSCoreModule2")};
90
91            std::vector<AttributionNodeInternal> attributions2 = {
92                    CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
93                    CreateAttribution(555, "GMSCoreModule2")};
94
95            std::vector<std::unique_ptr<LogEvent>> events;
96
97            events.push_back(CreateStartScheduledJobEvent(
98                    {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 1));
99            events.push_back(CreateFinishScheduledJobEvent(
100                    {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
101
102            events.push_back(CreateStartScheduledJobEvent(
103                    {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
104            events.push_back(CreateFinishScheduledJobEvent(
105                    {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
106
107            events.push_back(CreateStartScheduledJobEvent(
108                    {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
109            events.push_back(CreateFinishScheduledJobEvent(
110                    {CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850));
111
112            events.push_back(CreateStartScheduledJobEvent(
113                    {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600));
114            events.push_back(CreateFinishScheduledJobEvent(
115                    {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900));
116
117            events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
118                                                  bucketStartTimeNs + 10));
119            events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
120                                                bucketStartTimeNs + 50));
121
122            events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
123                                                  bucketStartTimeNs + 200));
124            events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
125                                                bucketStartTimeNs + bucketSizeNs + 300));
126
127            events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
128                                                  bucketStartTimeNs + 400));
129            events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
130                                                bucketStartTimeNs + bucketSizeNs - 1));
131
132            events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
133                                                  bucketStartTimeNs + 401));
134            events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
135                                                bucketStartTimeNs + bucketSizeNs + 700));
136
137            sortLogEventsByTimestamp(&events);
138
139            for (const auto& event : events) {
140                processor->OnLogEvent(event.get());
141            }
142
143            ConfigMetricsReportList reports;
144            vector<uint8_t> buffer;
145            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
146                                    ADB_DUMP, &buffer);
147            EXPECT_TRUE(buffer.size() > 0);
148            EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
149            backfillDimensionPath(&reports);
150            backfillStringInReport(&reports);
151            backfillStartEndTimestamp(&reports);
152
153            EXPECT_EQ(reports.reports_size(), 1);
154            EXPECT_EQ(reports.reports(0).metrics_size(), 1);
155            StatsLogReport::DurationMetricDataWrapper metrics;
156            sortMetricDataByDimensionsValue(
157                    reports.reports(0).metrics(0).duration_metrics(), &metrics);
158            if (aggregationType == DurationMetric::SUM) {
159                EXPECT_EQ(metrics.data_size(), 4);
160                auto data = metrics.data(0);
161                EXPECT_EQ(data.dimensions_in_what().field(),
162                          android::util::SCHEDULED_JOB_STATE_CHANGED);
163                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
164                          2);  // job name field
165                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
166                          "job0");  // job name
167                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
168                                                      android::util::SYNC_STATE_CHANGED, 111, "App1");
169                EXPECT_EQ(data.bucket_info_size(), 1);
170                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40);
171                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
172                          bucketStartTimeNs);
173                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
174                    bucketStartTimeNs + bucketSizeNs);
175
176                data = metrics.data(1);
177                EXPECT_EQ(data.dimensions_in_what().field(),
178                          android::util::SCHEDULED_JOB_STATE_CHANGED);
179                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
180                          2);  // job name field
181                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
182                          "job1");  // job name
183                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
184                                                      android::util::SYNC_STATE_CHANGED, 333, "App2");
185                EXPECT_EQ(data.bucket_info_size(), 1);
186                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
187                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
188                          bucketStartTimeNs + bucketSizeNs);
189                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
190                    bucketStartTimeNs + 2 * bucketSizeNs);
191
192                data = metrics.data(2);
193                EXPECT_EQ(data.dimensions_in_what().field(),
194                          android::util::SCHEDULED_JOB_STATE_CHANGED);
195                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
196                          2);  // job name field
197                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
198                          "job2");  // job name
199                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
200                                                      android::util::SYNC_STATE_CHANGED, 111, "App1");
201                EXPECT_EQ(data.bucket_info_size(), 2);
202                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
203                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
204                          bucketStartTimeNs + bucketSizeNs);
205                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
206                EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
207                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
208                          bucketStartTimeNs + bucketSizeNs);
209                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
210                          bucketStartTimeNs + 2 * bucketSizeNs);
211
212                data = metrics.data(3);
213                EXPECT_EQ(data.dimensions_in_what().field(),
214                          android::util::SCHEDULED_JOB_STATE_CHANGED);
215                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
216                          2);  // job name field
217                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
218                          "job2");  // job name
219                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
220                                                      android::util::SYNC_STATE_CHANGED, 333, "App2");
221                EXPECT_EQ(data.bucket_info_size(), 2);
222                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
223                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
224                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
225                          bucketStartTimeNs + bucketSizeNs);
226                EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
227                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
228                          bucketStartTimeNs + bucketSizeNs);
229                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
230                          bucketStartTimeNs + 2 * bucketSizeNs);
231            } else {
232                EXPECT_EQ(metrics.data_size(), 4);
233                auto data = metrics.data(0);
234                EXPECT_EQ(data.dimensions_in_what().field(),
235                          android::util::SCHEDULED_JOB_STATE_CHANGED);
236                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
237                          2);  // job name field
238                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
239                          "job0");  // job name
240                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
241                                                      android::util::SYNC_STATE_CHANGED, 111, "App1");
242                EXPECT_EQ(data.bucket_info_size(), 1);
243                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40);
244                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
245                          bucketStartTimeNs);
246                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
247                    bucketStartTimeNs + bucketSizeNs);
248
249                data = metrics.data(1);
250                EXPECT_EQ(data.dimensions_in_what().field(),
251                          android::util::SCHEDULED_JOB_STATE_CHANGED);
252                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
253                          2);  // job name field
254                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
255                          "job1");  // job name
256                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
257                                                      android::util::SYNC_STATE_CHANGED, 333, "App2");
258                EXPECT_EQ(data.bucket_info_size(), 1);
259                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
260                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
261                          bucketStartTimeNs + bucketSizeNs);
262                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
263                    bucketStartTimeNs + 2 * bucketSizeNs);
264
265                data = metrics.data(2);
266                EXPECT_EQ(data.dimensions_in_what().field(),
267                          android::util::SCHEDULED_JOB_STATE_CHANGED);
268                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
269                          2);  // job name field
270                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
271                          "job2");  // job name
272                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
273                                                      android::util::SYNC_STATE_CHANGED, 111, "App1");
274                EXPECT_EQ(data.bucket_info_size(), 2);
275                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
276                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
277                          bucketStartTimeNs + bucketSizeNs);
278                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201);
279                EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 300);
280                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
281                          bucketStartTimeNs + bucketSizeNs);
282                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
283                          bucketStartTimeNs + 2 * bucketSizeNs);
284
285                data = metrics.data(3);
286                EXPECT_EQ(data.dimensions_in_what().field(),
287                          android::util::SCHEDULED_JOB_STATE_CHANGED);
288                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
289                          2);  // job name field
290                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
291                          "job2");  // job name
292                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
293                                                      android::util::SYNC_STATE_CHANGED, 333, "App2");
294                EXPECT_EQ(data.bucket_info_size(), 2);
295                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 );
296                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
297                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
298                          bucketStartTimeNs + bucketSizeNs);
299                EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
300                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
301                          bucketStartTimeNs + bucketSizeNs);
302                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
303                          bucketStartTimeNs + 2 * bucketSizeNs);
304            }
305        }
306    }
307}
308
309namespace {
310
311StatsdConfig createDurationMetric_Link_SimpleConditionConfig(
312        DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
313    StatsdConfig config;
314    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
315    *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
316    *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
317    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
318    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
319
320    auto scheduledJobPredicate = CreateScheduledJobPredicate();
321    auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
322    *dimensions = CreateAttributionUidDimensions(
323                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
324    dimensions->add_child()->set_field(2);  // job name field.
325
326    auto isSyncingPredicate = CreateIsSyncingPredicate();
327    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
328    *syncDimension = CreateAttributionUidDimensions(
329            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
330    if (addExtraDimensionInCondition) {
331        syncDimension->add_child()->set_field(2 /* name field*/);
332    }
333
334    *config.add_predicate() = scheduledJobPredicate;
335    *config.add_predicate() = isSyncingPredicate;
336
337    auto metric = config.add_duration_metric();
338    metric->set_bucket(FIVE_MINUTES);
339    metric->set_id(StringToId("scheduledJob"));
340    metric->set_what(scheduledJobPredicate.id());
341    metric->set_condition(isSyncingPredicate.id());
342    metric->set_aggregation_type(aggregationType);
343    *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
344            android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
345
346    auto links = metric->add_links();
347    links->set_condition(isSyncingPredicate.id());
348    *links->mutable_fields_in_what() =
349            CreateAttributionUidDimensions(
350                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
351    *links->mutable_fields_in_condition() =
352            CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
353    return config;
354}
355
356}  // namespace
357
358TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition) {
359    for (bool isFullLink : {true, false}) {
360        for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
361            ConfigKey cfgKey;
362            auto config = createDurationMetric_Link_SimpleConditionConfig(
363                    aggregationType, !isFullLink);
364            int64_t bucketStartTimeNs = 10000000000;
365            int64_t bucketSizeNs =
366                    TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
367
368            auto processor = CreateStatsLogProcessor(
369                    bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
370            EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
371            EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
372
373            std::vector<AttributionNodeInternal> attributions1 = {
374                    CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
375                    CreateAttribution(222, "GMSCoreModule2")};
376
377            std::vector<AttributionNodeInternal> attributions2 = {
378                    CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
379                    CreateAttribution(555, "GMSCoreModule2")};
380
381            std::vector<AttributionNodeInternal> attributions3 = {
382                    CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
383                    CreateAttribution(555, "GMSCoreModule2")};
384
385            std::vector<std::unique_ptr<LogEvent>> events;
386
387            events.push_back(CreateStartScheduledJobEvent(
388                    {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
389            events.push_back(CreateFinishScheduledJobEvent(
390                    {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
391
392            events.push_back(CreateStartScheduledJobEvent(
393                    {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
394            events.push_back(CreateFinishScheduledJobEvent(
395                    {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
396            events.push_back(CreateStartScheduledJobEvent(
397                    {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
398            events.push_back(
399                CreateFinishScheduledJobEvent({CreateAttribution(333, "App2")}, "job2",
400                                               bucketStartTimeNs + bucketSizeNs + 850));
401
402            events.push_back(
403                CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
404                                             bucketStartTimeNs + bucketSizeNs - 2));
405            events.push_back(
406                CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
407                                              bucketStartTimeNs + bucketSizeNs + 900));
408
409            events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
410                                                  bucketStartTimeNs + 50));
411            events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
412                                                bucketStartTimeNs + 110));
413
414            events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
415                                                  bucketStartTimeNs + 300));
416            events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
417                                                bucketStartTimeNs + bucketSizeNs + 700));
418            events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
419                                                  bucketStartTimeNs + 400));
420            events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
421                                                bucketStartTimeNs + bucketSizeNs - 1));
422
423            events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
424                                                  bucketStartTimeNs + 550));
425            events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
426                                                bucketStartTimeNs + 800));
427            events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
428                                                  bucketStartTimeNs + bucketSizeNs - 1));
429            events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
430                                                bucketStartTimeNs + bucketSizeNs + 700));
431
432            sortLogEventsByTimestamp(&events);
433
434            for (const auto& event : events) {
435                processor->OnLogEvent(event.get());
436            }
437
438            ConfigMetricsReportList reports;
439            vector<uint8_t> buffer;
440            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
441                                    ADB_DUMP, &buffer);
442            EXPECT_TRUE(buffer.size() > 0);
443            EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
444            backfillDimensionPath(&reports);
445            backfillStringInReport(&reports);
446            backfillStartEndTimestamp(&reports);
447
448            EXPECT_EQ(reports.reports_size(), 1);
449            EXPECT_EQ(reports.reports(0).metrics_size(), 1);
450            StatsLogReport::DurationMetricDataWrapper metrics;
451            sortMetricDataByDimensionsValue(
452                    reports.reports(0).metrics(0).duration_metrics(), &metrics);
453
454            if (aggregationType == DurationMetric::SUM) {
455                EXPECT_EQ(metrics.data_size(), 3);
456                auto data = metrics.data(0);
457                ValidateAttributionUidDimension(
458                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
459                EXPECT_EQ(data.bucket_info_size(), 1);
460                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
461                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
462                          bucketStartTimeNs);
463                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
464                    bucketStartTimeNs + bucketSizeNs);
465
466                data = metrics.data(1);
467                ValidateAttributionUidDimension(
468                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
469                EXPECT_EQ(data.bucket_info_size(), 2);
470                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
471                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
472                          bucketStartTimeNs + bucketSizeNs);
473                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300 + bucketSizeNs - 600);
474                EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
475                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
476                          bucketStartTimeNs + bucketSizeNs);
477                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
478                          bucketStartTimeNs + 2 * bucketSizeNs);
479
480                data = metrics.data(2);
481                ValidateAttributionUidDimension(
482                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
483                EXPECT_EQ(data.bucket_info_size(), 2);
484                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
485                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
486                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
487                          bucketStartTimeNs + bucketSizeNs);
488                EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
489                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
490                          bucketStartTimeNs + bucketSizeNs);
491                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
492                          bucketStartTimeNs + 2 * bucketSizeNs);
493            } else {
494                EXPECT_EQ(metrics.data_size(), 3);
495                auto data = metrics.data(0);
496                ValidateAttributionUidDimension(
497                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
498                EXPECT_EQ(data.bucket_info_size(), 1);
499                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
500                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
501                          bucketStartTimeNs);
502                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
503                    bucketStartTimeNs + bucketSizeNs);
504
505                data = metrics.data(1);
506                ValidateAttributionUidDimension(
507                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
508                EXPECT_EQ(data.bucket_info_size(), 2);
509                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
510                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
511                          bucketStartTimeNs + bucketSizeNs);
512                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300);
513                EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
514                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
515                          bucketStartTimeNs + bucketSizeNs);
516                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
517                          bucketStartTimeNs + 2 * bucketSizeNs);
518
519                data = metrics.data(2);
520                ValidateAttributionUidDimension(
521                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
522                EXPECT_EQ(data.bucket_info_size(), 1);
523                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 701);
524                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
525                          bucketStartTimeNs + bucketSizeNs);
526                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
527                          bucketStartTimeNs + 2 * bucketSizeNs);
528            }
529        }
530    }
531}
532
533namespace {
534
535StatsdConfig createDurationMetric_PartialLink_SimpleConditionConfig(
536        DurationMetric::AggregationType aggregationType) {
537    StatsdConfig config;
538    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
539    *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
540    *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
541    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
542    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
543
544    auto scheduledJobPredicate = CreateScheduledJobPredicate();
545    auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
546    *dimensions = CreateAttributionUidDimensions(
547                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
548    dimensions->add_child()->set_field(2);  // job name field.
549
550    auto isSyncingPredicate = CreateIsSyncingPredicate();
551    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
552    *syncDimension = CreateAttributionUidDimensions(
553            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
554    syncDimension->add_child()->set_field(2 /* name field*/);
555
556    *config.add_predicate() = scheduledJobPredicate;
557    *config.add_predicate() = isSyncingPredicate;
558
559    auto metric = config.add_duration_metric();
560    metric->set_bucket(FIVE_MINUTES);
561    metric->set_id(StringToId("scheduledJob"));
562    metric->set_what(scheduledJobPredicate.id());
563    metric->set_condition(isSyncingPredicate.id());
564    metric->set_aggregation_type(aggregationType);
565    *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
566            android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
567    *metric->mutable_dimensions_in_condition() = *syncDimension;
568
569    auto links = metric->add_links();
570    links->set_condition(isSyncingPredicate.id());
571    *links->mutable_fields_in_what() =
572            CreateAttributionUidDimensions(
573                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
574    *links->mutable_fields_in_condition() =
575            CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
576    return config;
577}
578
579}  // namespace
580
581TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition) {
582    for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
583        ConfigKey cfgKey;
584        auto config = createDurationMetric_PartialLink_SimpleConditionConfig(
585                aggregationType);
586        int64_t bucketStartTimeNs = 10000000000;
587        int64_t bucketSizeNs =
588                TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
589
590        auto processor = CreateStatsLogProcessor(
591                bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
592        EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
593        EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
594
595        std::vector<AttributionNodeInternal> attributions1 = {
596                CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
597                CreateAttribution(222, "GMSCoreModule2")};
598
599        std::vector<AttributionNodeInternal> attributions2 = {
600                CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
601                CreateAttribution(555, "GMSCoreModule2")};
602
603        std::vector<AttributionNodeInternal> attributions3 = {
604                CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
605                CreateAttribution(555, "GMSCoreModule2")};
606
607        std::vector<std::unique_ptr<LogEvent>> events;
608
609        events.push_back(CreateStartScheduledJobEvent(
610                {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
611        events.push_back(CreateFinishScheduledJobEvent(
612                {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
613
614        events.push_back(CreateStartScheduledJobEvent(
615                {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
616        events.push_back(CreateFinishScheduledJobEvent(
617                {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
618        events.push_back(CreateStartScheduledJobEvent(
619                {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
620        events.push_back(CreateFinishScheduledJobEvent(
621                {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + bucketSizeNs + 850));
622
623        events.push_back(
624            CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
625                                         bucketStartTimeNs + bucketSizeNs - 2));
626        events.push_back(
627            CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
628                                          bucketStartTimeNs + bucketSizeNs + 900));
629
630        events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
631                                              bucketStartTimeNs + 50));
632        events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
633                                            bucketStartTimeNs + 110));
634
635        events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
636                                              bucketStartTimeNs + 300));
637        events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
638                                            bucketStartTimeNs + bucketSizeNs + 700));
639        events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
640                                              bucketStartTimeNs + 400));
641        events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
642                                            bucketStartTimeNs + bucketSizeNs - 1));
643
644        events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
645                                              bucketStartTimeNs + 550));
646        events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
647                                            bucketStartTimeNs + 800));
648        events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
649                                              bucketStartTimeNs + bucketSizeNs - 1));
650        events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
651                                            bucketStartTimeNs + bucketSizeNs + 700));
652
653        sortLogEventsByTimestamp(&events);
654
655        for (const auto& event : events) {
656            processor->OnLogEvent(event.get());
657        }
658
659        ConfigMetricsReportList reports;
660        vector<uint8_t> buffer;
661        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
662                                &buffer);
663        EXPECT_TRUE(buffer.size() > 0);
664        EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
665        backfillDimensionPath(&reports);
666        backfillStringInReport(&reports);
667        backfillStartEndTimestamp(&reports);
668
669        EXPECT_EQ(reports.reports_size(), 1);
670        EXPECT_EQ(reports.reports(0).metrics_size(), 1);
671        StatsLogReport::DurationMetricDataWrapper metrics;
672        sortMetricDataByDimensionsValue(
673                reports.reports(0).metrics(0).duration_metrics(), &metrics);
674
675        if (aggregationType == DurationMetric::SUM) {
676            EXPECT_EQ(4, metrics.data_size());
677            auto data = metrics.data(0);
678            ValidateAttributionUidDimension(
679                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
680            ValidateAttributionUidDimension(
681                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
682            EXPECT_EQ("ReadEmail",
683                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
684            EXPECT_EQ(data.bucket_info_size(), 1);
685            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
686            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
687                      bucketStartTimeNs);
688            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
689                bucketStartTimeNs + bucketSizeNs);
690
691            data = metrics.data(1);
692            ValidateAttributionUidDimension(
693                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
694            ValidateAttributionUidDimension(
695                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
696            EXPECT_EQ("ReadDoc",
697                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
698            EXPECT_EQ(data.bucket_info_size(), 1);
699            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
700            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
701                      bucketStartTimeNs + bucketSizeNs);
702            EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 1 - 400 - 100);
703
704            data = metrics.data(2);
705            ValidateAttributionUidDimension(
706                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
707            ValidateAttributionUidDimension(
708                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
709            EXPECT_EQ("ReadEmail",
710                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
711            EXPECT_EQ(data.bucket_info_size(), 2);
712            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
713            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
714                      bucketStartTimeNs + bucketSizeNs);
715            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300 + bucketSizeNs - 600);
716            EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
717            EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
718                      bucketStartTimeNs + bucketSizeNs);
719            EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
720                      bucketStartTimeNs + 2 * bucketSizeNs);
721
722            data = metrics.data(3);
723            ValidateAttributionUidDimension(
724                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
725            ValidateAttributionUidDimension(
726                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
727            EXPECT_EQ("ReadDoc",
728                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
729            EXPECT_EQ(data.bucket_info_size(), 2);
730            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
731            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
732            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
733                      bucketStartTimeNs + bucketSizeNs);
734            EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
735            EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
736                      bucketStartTimeNs + bucketSizeNs);
737            EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
738                      bucketStartTimeNs + 2 * bucketSizeNs);
739        } else {
740            EXPECT_EQ(metrics.data_size(), 4);
741            auto data = metrics.data(0);
742            ValidateAttributionUidDimension(
743                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
744            ValidateAttributionUidDimension(
745                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
746            EXPECT_EQ("ReadEmail",
747                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
748            EXPECT_EQ(data.bucket_info_size(), 1);
749            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
750            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
751                      bucketStartTimeNs);
752            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
753                bucketStartTimeNs + bucketSizeNs);
754
755            data = metrics.data(1);
756            ValidateAttributionUidDimension(
757                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
758            ValidateAttributionUidDimension(
759                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
760            EXPECT_EQ("ReadDoc",
761                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
762            EXPECT_EQ(data.bucket_info_size(), 2);
763            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
764            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
765                      bucketStartTimeNs + bucketSizeNs);
766            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
767            EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
768                      bucketStartTimeNs + bucketSizeNs);
769            EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
770                      bucketStartTimeNs + 2 * bucketSizeNs);
771            EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 1 - 600);
772
773            data = metrics.data(2);
774            ValidateAttributionUidDimension(
775                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
776            ValidateAttributionUidDimension(
777                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
778            EXPECT_EQ("ReadEmail",
779                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
780            EXPECT_EQ(data.bucket_info_size(), 2);
781            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
782            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
783                      bucketStartTimeNs + bucketSizeNs);
784            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300);
785            EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
786            EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
787                      bucketStartTimeNs + bucketSizeNs);
788            EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
789                      bucketStartTimeNs + 2 * bucketSizeNs);
790
791            data = metrics.data(3);
792            ValidateAttributionUidDimension(
793                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
794            ValidateAttributionUidDimension(
795                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
796            EXPECT_EQ("ReadDoc",
797                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
798            EXPECT_EQ(data.bucket_info_size(), 1);
799            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 701);
800            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
801                      bucketStartTimeNs + bucketSizeNs);
802            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
803                      bucketStartTimeNs + 2 * bucketSizeNs);
804        }
805    }
806}
807
808#else
809GTEST_LOG_(INFO) << "This test does nothing.\n";
810#endif
811
812}  // namespace statsd
813}  // namespace os
814}  // namespace android
815