1 // Copyright (C) 2018 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/statsd_metadata.pb.h"
18 #include "src/StatsLogProcessor.h"
19 #include "src/stats_log_util.h"
20 #include "tests/statsd_test_util.h"
21 
22 #include <vector>
23 
24 namespace android {
25 namespace os {
26 namespace statsd {
27 
28 #ifdef __ANDROID__
29 
30 namespace {
31 
CreateStatsdConfig(int num_buckets,int threshold,int refractory_period_sec)32 StatsdConfig CreateStatsdConfig(int num_buckets, int threshold, int refractory_period_sec) {
33     StatsdConfig config;
34     auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
35 
36     *config.add_atom_matcher() = wakelockAcquireMatcher;
37 
38     auto countMetric = config.add_count_metric();
39     countMetric->set_id(123456);
40     countMetric->set_what(wakelockAcquireMatcher.id());
41     *countMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
42             util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
43     countMetric->set_bucket(FIVE_MINUTES);
44 
45     auto alert = config.add_alert();
46     alert->set_id(StringToId("alert"));
47     alert->set_metric_id(123456);
48     alert->set_num_buckets(num_buckets);
49     alert->set_refractory_period_secs(refractory_period_sec);
50     alert->set_trigger_if_sum_gt(threshold);
51     return config;
52 }
53 
54 }  // namespace
55 
TEST(AnomalyCountDetectionE2eTest,TestSlicedCountMetric_single_bucket)56 TEST(AnomalyCountDetectionE2eTest, TestSlicedCountMetric_single_bucket) {
57     const int num_buckets = 1;
58     const int threshold = 3;
59     const int refractory_period_sec = 10;
60     auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
61     const uint64_t alert_id = config.alert(0).id();
62 
63     int64_t bucketStartTimeNs = 10000000000;
64     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
65 
66     ConfigKey cfgKey;
67     auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
68     ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
69     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
70     ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
71 
72     sp<AnomalyTracker> anomalyTracker =
73             processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
74 
75     std::vector<int> attributionUids1 = {111};
76     std::vector<string> attributionTags1 = {"App1"};
77     std::vector<int> attributionUids2 = {111, 222};
78     std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
79     std::vector<int> attributionUids3 = {111, 333};
80     std::vector<string> attributionTags3 = {"App1", "App3"};
81     std::vector<int> attributionUids4 = {222, 333};
82     std::vector<string> attributionTags4 = {"GMSCoreModule1", "App3"};
83     std::vector<int> attributionUids5 = {222};
84     std::vector<string> attributionTags5 = {"GMSCoreModule1"};
85 
86     FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
87                            Value((int32_t)111));
88     HashableDimensionKey whatKey1({fieldValue1});
89     MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
90 
91     FieldValue fieldValue2(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
92                            Value((int32_t)222));
93     HashableDimensionKey whatKey2({fieldValue2});
94     MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
95 
96     auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
97                                             attributionTags1, "wl1");
98     processor->OnLogEvent(event.get());
99     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
100 
101     event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids4, attributionTags4,
102                                        "wl2");
103     processor->OnLogEvent(event.get());
104     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
105 
106     event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2,
107                                        "wl1");
108     processor->OnLogEvent(event.get());
109     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
110 
111     event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids5, attributionTags5,
112                                        "wl2");
113     processor->OnLogEvent(event.get());
114     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
115 
116     event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids3, attributionTags3,
117                                        "wl1");
118     processor->OnLogEvent(event.get());
119     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
120 
121     event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids5, attributionTags5,
122                                        "wl2");
123     processor->OnLogEvent(event.get());
124     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
125 
126     // Fired alarm and refractory period end timestamp updated.
127     event = CreateAcquireWakelockEvent(bucketStartTimeNs + 5, attributionUids1, attributionTags1,
128                                        "wl1");
129     processor->OnLogEvent(event.get());
130     EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
131               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
132 
133     event = CreateAcquireWakelockEvent(bucketStartTimeNs + 100, attributionUids1, attributionTags1,
134                                        "wl1");
135     processor->OnLogEvent(event.get());
136     EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
137               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
138 
139     event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids1,
140                                        attributionTags1, "wl1");
141     processor->OnLogEvent(event.get());
142     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
143               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
144 
145     event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1,
146                                        attributionTags1, "wl1");
147     processor->OnLogEvent(event.get());
148     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
149               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
150 
151     event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids4,
152                                        attributionTags4, "wl2");
153     processor->OnLogEvent(event.get());
154     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
155 
156     event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids5,
157                                        attributionTags5, "wl2");
158     processor->OnLogEvent(event.get());
159     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
160 
161     event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 3, attributionUids5,
162                                        attributionTags5, "wl2");
163     processor->OnLogEvent(event.get());
164     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
165 
166     event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 4, attributionUids5,
167                                        attributionTags5, "wl2");
168     processor->OnLogEvent(event.get());
169     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1,
170               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
171 }
172 
TEST(AnomalyCountDetectionE2eTest,TestSlicedCountMetric_multiple_buckets)173 TEST(AnomalyCountDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) {
174     const int num_buckets = 3;
175     const int threshold = 3;
176     const int refractory_period_sec = 10;
177     auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
178     const uint64_t alert_id = config.alert(0).id();
179 
180     int64_t bucketStartTimeNs = 10000000000;
181     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
182 
183     ConfigKey cfgKey;
184     auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
185     ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
186     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
187     ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
188 
189     sp<AnomalyTracker> anomalyTracker =
190             processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
191 
192     std::vector<int> attributionUids1 = {111};
193     std::vector<string> attributionTags1 = {"App1"};
194     std::vector<int> attributionUids2 = {111, 222};
195     std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
196 
197     FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
198                            Value((int32_t)111));
199     HashableDimensionKey whatKey1({fieldValue1});
200     MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
201 
202     auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
203                                             attributionTags1, "wl1");
204     processor->OnLogEvent(event.get());
205     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
206 
207     event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2,
208                                        "wl1");
209     processor->OnLogEvent(event.get());
210     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
211 
212     // Fired alarm and refractory period end timestamp updated.
213     event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids1, attributionTags1,
214                                        "wl1");
215     processor->OnLogEvent(event.get());
216     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
217 
218     event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1,
219                                        attributionTags1, "wl1");
220     processor->OnLogEvent(event.get());
221     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
222               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
223 
224     event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids2,
225                                        attributionTags2, "wl1");
226     processor->OnLogEvent(event.get());
227     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
228               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
229 
230     event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, attributionUids2,
231                                        attributionTags2, "wl1");
232     processor->OnLogEvent(event.get());
233     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
234               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
235 
236     event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 2, attributionUids2,
237                                        attributionTags2, "wl1");
238     processor->OnLogEvent(event.get());
239     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1,
240               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
241 }
242 
TEST(AnomalyCountDetectionE2eTest,TestCountMetric_save_refractory_to_disk_no_data_written)243 TEST(AnomalyCountDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written) {
244     const int num_buckets = 1;
245     const int threshold = 0;
246     const int refractory_period_sec = 86400 * 365; // 1 year
247     auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
248     const int64_t alert_id = config.alert(0).id();
249 
250     int64_t bucketStartTimeNs = 10000000000;
251 
252     int configUid = 2000;
253     int64_t configId = 1000;
254     ConfigKey cfgKey(configUid, configId);
255     auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
256     ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
257     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
258     ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
259 
260     metadata::StatsMetadataList result;
261     int64_t mockWallClockNs = 1584991200 * NS_PER_SEC;
262     int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC;
263     processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result);
264 
265     ASSERT_EQ(result.stats_metadata_size(), 0);
266 }
267 
TEST(AnomalyCountDetectionE2eTest,TestCountMetric_save_refractory_to_disk)268 TEST(AnomalyCountDetectionE2eTest, TestCountMetric_save_refractory_to_disk) {
269     const int num_buckets = 1;
270     const int threshold = 0;
271     const int refractory_period_sec = 86400 * 365; // 1 year
272     auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
273     const int64_t alert_id = config.alert(0).id();
274 
275     int64_t bucketStartTimeNs = 10000000000;
276 
277     int configUid = 2000;
278     int64_t configId = 1000;
279     ConfigKey cfgKey(configUid, configId);
280     auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
281     ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
282     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
283     ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
284 
285     sp<AnomalyTracker> anomalyTracker =
286             processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
287 
288     std::vector<int> attributionUids1 = {111};
289     std::vector<string> attributionTags1 = {"App1"};
290     std::vector<int> attributionUids2 = {111, 222};
291     std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
292 
293     FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
294                            Value((int32_t)111));
295     HashableDimensionKey whatKey1({fieldValue1});
296     MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
297 
298     auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
299                                             attributionTags1, "wl1");
300     processor->OnLogEvent(event.get());
301     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1,
302               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
303 
304     metadata::StatsMetadataList result;
305     int64_t mockWallClockNs = 1584991200 * NS_PER_SEC;
306     int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC;
307     processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result);
308 
309     metadata::StatsMetadata statsMetadata = result.stats_metadata(0);
310     ASSERT_EQ(result.stats_metadata_size(), 1);
311     EXPECT_EQ(statsMetadata.config_key().config_id(), configId);
312     EXPECT_EQ(statsMetadata.config_key().uid(), configUid);
313 
314     metadata::AlertMetadata alertMetadata = statsMetadata.alert_metadata(0);
315     ASSERT_EQ(statsMetadata.alert_metadata_size(), 1);
316     EXPECT_EQ(alertMetadata.alert_id(), alert_id);
317     metadata::AlertDimensionKeyedData keyedData = alertMetadata.alert_dim_keyed_data(0);
318     ASSERT_EQ(alertMetadata.alert_dim_keyed_data_size(), 1);
319     EXPECT_EQ(keyedData.last_refractory_ends_sec(),
320               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) -
321               mockElapsedTimeNs / NS_PER_SEC +
322               mockWallClockNs / NS_PER_SEC);
323 
324     metadata::MetricDimensionKey metadataDimKey = keyedData.dimension_key();
325     metadata::FieldValue dimKeyInWhat = metadataDimKey.dimension_key_in_what(0);
326     EXPECT_EQ(dimKeyInWhat.field().tag(), fieldValue1.mField.getTag());
327     EXPECT_EQ(dimKeyInWhat.field().field(), fieldValue1.mField.getField());
328     EXPECT_EQ(dimKeyInWhat.value_int(), fieldValue1.mValue.int_value);
329 }
330 
TEST(AnomalyCountDetectionE2eTest,TestCountMetric_load_refractory_from_disk)331 TEST(AnomalyCountDetectionE2eTest, TestCountMetric_load_refractory_from_disk) {
332     const int num_buckets = 1;
333     const int threshold = 0;
334     const int refractory_period_sec = 86400 * 365; // 1 year
335     auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
336     const int64_t alert_id = config.alert(0).id();
337 
338     int64_t bucketStartTimeNs = 10000000000;
339 
340     int configUid = 2000;
341     int64_t configId = 1000;
342     ConfigKey cfgKey(configUid, configId);
343     auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
344     ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
345     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
346     ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
347 
348     sp<AnomalyTracker> anomalyTracker =
349             processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
350 
351     std::vector<int> attributionUids1 = {111};
352     std::vector<string> attributionTags1 = {"App1"};
353     std::vector<int> attributionUids2 = {111, 222};
354     std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
355 
356     FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
357                            Value((int32_t)111));
358     HashableDimensionKey whatKey1({fieldValue1});
359     MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
360 
361     auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
362                                             attributionTags1, "wl1");
363     processor->OnLogEvent(event.get());
364     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1,
365               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
366 
367     int64_t mockWallClockNs = 1584991200 * NS_PER_SEC;
368     int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC;
369     processor->SaveMetadataToDisk(mockWallClockNs, mockElapsedTimeNs);
370 
371     auto processor2 = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
372     int64_t mockElapsedTimeSinceBoot = 10 * NS_PER_SEC;
373     processor2->LoadMetadataFromDisk(mockWallClockNs, mockElapsedTimeSinceBoot);
374 
375     sp<AnomalyTracker> anomalyTracker2 =
376                 processor2->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
377     EXPECT_EQ(anomalyTracker2->getRefractoryPeriodEndsSec(dimensionKey1) -
378               mockElapsedTimeSinceBoot / NS_PER_SEC,
379               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) -
380               mockElapsedTimeNs / NS_PER_SEC);
381 }
382 
383 #else
384 GTEST_LOG_(INFO) << "This test does nothing.\n";
385 #endif
386 
387 }  // namespace statsd
388 }  // namespace os
389 }  // namespace android
390