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                             ADB_DUMP, &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 temperatureAtomMatcher = CreateTemperatureAtomMatcher();
74     *config.add_atom_matcher() = temperatureAtomMatcher;
75     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
76     *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
77 
78     auto valueMetric = config.add_value_metric();
79     valueMetric->set_id(123456);
80     valueMetric->set_what(temperatureAtomMatcher.id());
81     *valueMetric->mutable_value_field() =
82             CreateDimensions(android::util::TEMPERATURE, {3 /* temperature degree field */});
83     *valueMetric->mutable_dimensions_in_what() =
84             CreateDimensions(android::util::TEMPERATURE, {2 /* sensor name field */});
85     valueMetric->set_bucket(FIVE_MINUTES);
86     valueMetric->set_min_bucket_size_nanos(minTime);
87     valueMetric->set_use_absolute_value_on_reset(true);
88     return config;
89 }
90 
MakeGaugeMetricConfig(int64_t minTime)91 StatsdConfig MakeGaugeMetricConfig(int64_t minTime) {
92     StatsdConfig config;
93     config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
94 
95     auto temperatureAtomMatcher = CreateTemperatureAtomMatcher();
96     *config.add_atom_matcher() = temperatureAtomMatcher;
97     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
98     *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
99 
100     auto gaugeMetric = config.add_gauge_metric();
101     gaugeMetric->set_id(123456);
102     gaugeMetric->set_what(temperatureAtomMatcher.id());
103     gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
104     *gaugeMetric->mutable_dimensions_in_what() =
105             CreateDimensions(android::util::TEMPERATURE, {2 /* sensor name field */});
106     gaugeMetric->set_bucket(FIVE_MINUTES);
107     gaugeMetric->set_min_bucket_size_nanos(minTime);
108     return config;
109 }
110 
TEST(PartialBucketE2eTest,TestCountMetricWithoutSplit)111 TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
112     StatsService service(nullptr);
113     SendConfig(service, MakeConfig());
114     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
115                                              // initialized with.
116 
117     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
118     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
119 
120     ConfigMetricsReport report = GetReports(service.mProcessor, start + 3);
121     // Expect no metrics since the bucket has not finished yet.
122     EXPECT_EQ(0, report.metrics_size());
123 }
124 
TEST(PartialBucketE2eTest,TestCountMetricNoSplitOnNewApp)125 TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
126     StatsService service(nullptr);
127     SendConfig(service, MakeConfig());
128     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
129                                              // initialized with.
130 
131     // Force the uidmap to update at timestamp 2.
132     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
133     // This is a new installation, so there shouldn't be a split (should be same as the without
134     // split case).
135     service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2);
136     // Goes into the second bucket.
137     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
138 
139     ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
140     EXPECT_EQ(0, report.metrics_size());
141 }
142 
TEST(PartialBucketE2eTest,TestCountMetricSplitOnUpgrade)143 TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
144     StatsService service(nullptr);
145     SendConfig(service, MakeConfig());
146     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
147                                              // initialized with.
148     service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())});
149 
150     // Force the uidmap to update at timestamp 2.
151     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
152     service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2);
153     // Goes into the second bucket.
154     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
155 
156     ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
157     backfillStartEndTimestamp(&report);
158     EXPECT_EQ(1, report.metrics_size());
159     EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
160                     has_start_bucket_elapsed_nanos());
161     EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
162                     has_end_bucket_elapsed_nanos());
163     EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
164 }
165 
TEST(PartialBucketE2eTest,TestCountMetricSplitOnRemoval)166 TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
167     StatsService service(nullptr);
168     SendConfig(service, MakeConfig());
169     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
170                                              // initialized with.
171     service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())});
172 
173     // Force the uidmap to update at timestamp 2.
174     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
175     service.mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
176     // Goes into the second bucket.
177     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
178 
179     ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
180     backfillStartEndTimestamp(&report);
181     EXPECT_EQ(1, report.metrics_size());
182     EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
183                     has_start_bucket_elapsed_nanos());
184     EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
185                     has_end_bucket_elapsed_nanos());
186     EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
187 }
188 
TEST(PartialBucketE2eTest,TestValueMetricWithoutMinPartialBucket)189 TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
190     StatsService service(nullptr);
191     // Partial buckets don't occur when app is first installed.
192     service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
193     SendConfig(service, MakeValueMetricConfig(0));
194     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
195                                              // initialized with.
196 
197     service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
198     service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2);
199 
200     ConfigMetricsReport report =
201             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
202     EXPECT_EQ(1, report.metrics_size());
203     EXPECT_EQ(0, report.metrics(0).value_metrics().skipped_size());
204 }
205 
TEST(PartialBucketE2eTest,TestValueMetricWithMinPartialBucket)206 TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
207     StatsService service(nullptr);
208     // Partial buckets don't occur when app is first installed.
209     service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
210     SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */));
211     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
212                                              // initialized with.
213 
214     const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
215     service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
216     service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2);
217 
218     ConfigMetricsReport report =
219             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
220     backfillStartEndTimestamp(&report);
221     EXPECT_EQ(1, report.metrics_size());
222     EXPECT_EQ(1, report.metrics(0).value_metrics().skipped_size());
223     EXPECT_TRUE(report.metrics(0).value_metrics().skipped(0).has_start_bucket_elapsed_nanos());
224     // Can't test the start time since it will be based on the actual time when the pulling occurs.
225     EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
226               report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos());
227 }
228 
TEST(PartialBucketE2eTest,TestGaugeMetricWithoutMinPartialBucket)229 TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
230     StatsService service(nullptr);
231     // Partial buckets don't occur when app is first installed.
232     service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
233     SendConfig(service, MakeGaugeMetricConfig(0));
234     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
235                                              // initialized with.
236 
237     service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
238     service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2);
239 
240     ConfigMetricsReport report =
241             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
242     EXPECT_EQ(1, report.metrics_size());
243     EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
244 }
245 
TEST(PartialBucketE2eTest,TestGaugeMetricWithMinPartialBucket)246 TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
247     StatsService service(nullptr);
248     // Partial buckets don't occur when app is first installed.
249     service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
250     SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */));
251     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
252                                              // initialized with.
253 
254     const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
255     service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
256     service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2);
257 
258     ConfigMetricsReport report =
259             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
260     backfillStartEndTimestamp(&report);
261     EXPECT_EQ(1, report.metrics_size());
262     EXPECT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
263     // Can't test the start time since it will be based on the actual time when the pulling occurs.
264     EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos());
265     EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
266               report.metrics(0).gauge_metrics().skipped(0).end_bucket_elapsed_nanos());
267 }
268 
269 #else
270 GTEST_LOG_(INFO) << "This test does nothing.\n";
271 #endif
272 
273 }  // namespace statsd
274 }  // namespace os
275 }  // namespace android
276