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 <binder/IPCThreadState.h>
18 #include "src/StatsLogProcessor.h"
19 #include "src/StatsService.h"
20 #include "src/stats_log_util.h"
21 #include "tests/statsd_test_util.h"
22 
23 #include <vector>
24 
25 namespace android {
26 namespace os {
27 namespace statsd {
28 
29 #ifdef __ANDROID__
30 
31 const string kAndroid = "android";
32 const string kApp1 = "app1.sharing.1";
33 const int kConfigKey = 789130123;  // Randomly chosen to avoid collisions with existing configs.
34 
SendConfig(StatsService & service,const StatsdConfig & config)35 void SendConfig(StatsService& service, const StatsdConfig& config) {
36     string str;
37     config.SerializeToString(&str);
38     std::vector<uint8_t> configAsVec(str.begin(), str.end());
39     bool success;
40     service.addConfiguration(kConfigKey, configAsVec, String16(kAndroid.c_str()));
41 }
42 
GetReports(sp<StatsLogProcessor> processor,int64_t timestamp,bool include_current=false)43 ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp,
44                                bool include_current = false) {
45     vector<uint8_t> output;
46     IPCThreadState* ipc = IPCThreadState::self();
47     ConfigKey configKey(ipc->getCallingUid(), kConfigKey);
48     processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/,
49                             true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &output);
50     ConfigMetricsReportList reports;
51     reports.ParseFromArray(output.data(), output.size());
52     EXPECT_EQ(1, reports.reports_size());
53     return reports.reports(0);
54 }
55 
MakeConfig()56 StatsdConfig MakeConfig() {
57     StatsdConfig config;
58     config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
59 
60     auto appCrashMatcher = CreateProcessCrashAtomMatcher();
61     *config.add_atom_matcher() = appCrashMatcher;
62     auto countMetric = config.add_count_metric();
63     countMetric->set_id(StringToId("AppCrashes"));
64     countMetric->set_what(appCrashMatcher.id());
65     countMetric->set_bucket(FIVE_MINUTES);
66     return config;
67 }
68 
MakeValueMetricConfig(int64_t minTime)69 StatsdConfig MakeValueMetricConfig(int64_t minTime) {
70     StatsdConfig config;
71     config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
72 
73     auto pulledAtomMatcher =
74             CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
75     *config.add_atom_matcher() = pulledAtomMatcher;
76     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
77     *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
78 
79     auto valueMetric = config.add_value_metric();
80     valueMetric->set_id(123456);
81     valueMetric->set_what(pulledAtomMatcher.id());
82     *valueMetric->mutable_value_field() =
83             CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
84     *valueMetric->mutable_dimensions_in_what() =
85             CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
86     valueMetric->set_bucket(FIVE_MINUTES);
87     valueMetric->set_min_bucket_size_nanos(minTime);
88     valueMetric->set_use_absolute_value_on_reset(true);
89     return config;
90 }
91 
MakeGaugeMetricConfig(int64_t minTime)92 StatsdConfig MakeGaugeMetricConfig(int64_t minTime) {
93     StatsdConfig config;
94     config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
95 
96     auto pulledAtomMatcher =
97                 CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
98     *config.add_atom_matcher() = pulledAtomMatcher;
99     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
100     *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
101 
102     auto gaugeMetric = config.add_gauge_metric();
103     gaugeMetric->set_id(123456);
104     gaugeMetric->set_what(pulledAtomMatcher.id());
105     gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
106     *gaugeMetric->mutable_dimensions_in_what() =
107             CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
108     gaugeMetric->set_bucket(FIVE_MINUTES);
109     gaugeMetric->set_min_bucket_size_nanos(minTime);
110     return config;
111 }
112 
TEST(PartialBucketE2eTest,TestCountMetricWithoutSplit)113 TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
114     StatsService service(nullptr, nullptr);
115     SendConfig(service, MakeConfig());
116     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
117                                              // initialized with.
118 
119     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
120     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
121 
122     ConfigMetricsReport report = GetReports(service.mProcessor, start + 3);
123     // Expect no metrics since the bucket has not finished yet.
124     EXPECT_EQ(1, report.metrics_size());
125     EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
126 }
127 
TEST(PartialBucketE2eTest,TestCountMetricNoSplitOnNewApp)128 TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
129     StatsService service(nullptr, nullptr);
130     SendConfig(service, MakeConfig());
131     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
132                                              // initialized with.
133 
134     // Force the uidmap to update at timestamp 2.
135     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
136     // This is a new installation, so there shouldn't be a split (should be same as the without
137     // split case).
138     service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
139                                String16(""));
140     // Goes into the second bucket.
141     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
142 
143     ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
144     EXPECT_EQ(1, report.metrics_size());
145     EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
146 }
147 
TEST(PartialBucketE2eTest,TestCountMetricSplitOnUpgrade)148 TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
149     StatsService service(nullptr, nullptr);
150     SendConfig(service, MakeConfig());
151     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
152                                              // initialized with.
153     service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
154                                {String16("")});
155 
156     // Force the uidmap to update at timestamp 2.
157     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
158     service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
159                                String16(""));
160     // Goes into the second bucket.
161     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
162 
163     ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
164     backfillStartEndTimestamp(&report);
165 
166     ASSERT_EQ(1, report.metrics_size());
167     ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
168     ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
169     EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
170                     has_start_bucket_elapsed_nanos());
171     EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
172                     has_end_bucket_elapsed_nanos());
173     EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
174 }
175 
TEST(PartialBucketE2eTest,TestCountMetricSplitOnRemoval)176 TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
177     StatsService service(nullptr, nullptr);
178     SendConfig(service, MakeConfig());
179     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
180                                              // initialized with.
181     service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
182                                {String16("")});
183 
184     // Force the uidmap to update at timestamp 2.
185     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
186     service.mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
187     // Goes into the second bucket.
188     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
189 
190     ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
191     backfillStartEndTimestamp(&report);
192 
193     ASSERT_EQ(1, report.metrics_size());
194     ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
195     ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
196     EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
197                     has_start_bucket_elapsed_nanos());
198     EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
199                     has_end_bucket_elapsed_nanos());
200     EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
201 }
202 
TEST(PartialBucketE2eTest,TestValueMetricWithoutMinPartialBucket)203 TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
204     StatsService service(nullptr, nullptr);
205     // Partial buckets don't occur when app is first installed.
206     service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
207     SendConfig(service, MakeValueMetricConfig(0));
208     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
209                                              // initialized with.
210 
211     service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
212     service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
213                                String16("v2"), String16(""));
214 
215     ConfigMetricsReport report =
216             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
217     EXPECT_EQ(1, report.metrics_size());
218     EXPECT_EQ(0, report.metrics(0).value_metrics().skipped_size());
219 }
220 
TEST(PartialBucketE2eTest,TestValueMetricWithMinPartialBucket)221 TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
222     StatsService service(nullptr, nullptr);
223     // Partial buckets don't occur when app is first installed.
224     service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
225     SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */));
226     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
227                                              // initialized with.
228 
229     const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
230     service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
231     service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
232                                String16(""));
233 
234     ConfigMetricsReport report =
235             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
236     backfillStartEndTimestamp(&report);
237 
238     ASSERT_EQ(1, report.metrics_size());
239     ASSERT_EQ(1, report.metrics(0).value_metrics().skipped_size());
240     EXPECT_TRUE(report.metrics(0).value_metrics().skipped(0).has_start_bucket_elapsed_nanos());
241     // Can't test the start time since it will be based on the actual time when the pulling occurs.
242     EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
243               report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos());
244 }
245 
TEST(PartialBucketE2eTest,TestGaugeMetricWithoutMinPartialBucket)246 TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
247     StatsService service(nullptr, nullptr);
248     // Partial buckets don't occur when app is first installed.
249     service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
250     SendConfig(service, MakeGaugeMetricConfig(0));
251     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
252                                              // initialized with.
253 
254     service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
255     service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
256                                String16("v2"), String16(""));
257 
258     ConfigMetricsReport report =
259             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
260     EXPECT_EQ(1, report.metrics_size());
261     EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
262 }
263 
TEST(PartialBucketE2eTest,TestGaugeMetricWithMinPartialBucket)264 TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
265     StatsService service(nullptr, nullptr);
266     // Partial buckets don't occur when app is first installed.
267     service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
268     SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */));
269     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
270                                              // initialized with.
271 
272     const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
273     service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
274     service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
275                                String16(""));
276 
277     ConfigMetricsReport report =
278             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
279     backfillStartEndTimestamp(&report);
280     ASSERT_EQ(1, report.metrics_size());
281     ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
282     // Can't test the start time since it will be based on the actual time when the pulling occurs.
283     EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos());
284     EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
285               report.metrics(0).gauge_metrics().skipped(0).end_bucket_elapsed_nanos());
286 }
287 
288 #else
289 GTEST_LOG_(INFO) << "This test does nothing.\n";
290 #endif
291 
292 }  // namespace statsd
293 }  // namespace os
294 }  // namespace android
295