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/anomaly/DurationAnomalyTracker.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,uint64_t threshold_ns,DurationMetric::AggregationType aggregationType,bool nesting)32 StatsdConfig CreateStatsdConfig(int num_buckets,
33                                 uint64_t threshold_ns,
34                                 DurationMetric::AggregationType aggregationType,
35                                 bool nesting) {
36     StatsdConfig config;
37     config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
38     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
39     *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
40     *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
41     *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
42 
43     auto screenIsOffPredicate = CreateScreenIsOffPredicate();
44     *config.add_predicate() = screenIsOffPredicate;
45 
46     auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
47     FieldMatcher dimensions = CreateAttributionUidDimensions(
48             util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
49     dimensions.add_child()->set_field(3);  // The wakelock tag is set in field 3 of the wakelock.
50     *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
51     holdingWakelockPredicate.mutable_simple_predicate()->set_count_nesting(nesting);
52     *config.add_predicate() = holdingWakelockPredicate;
53 
54     auto durationMetric = config.add_duration_metric();
55     durationMetric->set_id(StringToId("WakelockDuration"));
56     durationMetric->set_what(holdingWakelockPredicate.id());
57     durationMetric->set_condition(screenIsOffPredicate.id());
58     durationMetric->set_aggregation_type(aggregationType);
59     *durationMetric->mutable_dimensions_in_what() =
60         CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
61     durationMetric->set_bucket(FIVE_MINUTES);
62 
63     auto alert = config.add_alert();
64     alert->set_id(StringToId("alert"));
65     alert->set_metric_id(StringToId("WakelockDuration"));
66     alert->set_num_buckets(num_buckets);
67     alert->set_refractory_period_secs(2);
68     alert->set_trigger_if_sum_gt(threshold_ns);
69     return config;
70 }
71 
72 std::vector<int> attributionUids1 = {111, 222};
73 std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1"};
74 
75 std::vector<int> attributionUids2 = {111, 222};
76 std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1"};
77 
78 std::vector<int> attributionUids3 = {222};
79 std::vector<string> attributionTags3 = {"GMSCoreModule1"};
80 
81 MetricDimensionKey dimensionKey1(
82         HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED,
83                                                (int32_t)0x02010101),
84                                          Value((int32_t)111))}),
85         DEFAULT_DIMENSION_KEY);
86 
87 MetricDimensionKey dimensionKey2(
88     HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED,
89                                            (int32_t)0x02010101), Value((int32_t)222))}),
90     DEFAULT_DIMENSION_KEY);
91 
92 }  // namespace
93 
TEST(AnomalyDetectionE2eTest,TestDurationMetric_SUM_single_bucket)94 TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
95     const int num_buckets = 1;
96     const uint64_t threshold_ns = NS_PER_SEC;
97     auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
98     const uint64_t alert_id = config.alert(0).id();
99     const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
100 
101     int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
102     int64_t bucketSizeNs =
103             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
104 
105     ConfigKey cfgKey;
106     auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
107     ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
108     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
109     ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
110 
111     sp<AnomalyTracker> anomalyTracker =
112             processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
113 
114     auto screen_on_event = CreateScreenStateChangedEvent(
115             bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
116     auto screen_off_event = CreateScreenStateChangedEvent(
117             bucketStartTimeNs + 10, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
118     processor->OnLogEvent(screen_on_event.get());
119     processor->OnLogEvent(screen_off_event.get());
120 
121     // Acquire wakelock wl1.
122     auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 11, attributionUids1,
123                                                     attributionTags1, "wl1");
124     processor->OnLogEvent(acquire_event.get());
125     EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1,
126               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
127     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
128 
129     // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event.
130     auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 101, attributionUids1,
131                                                     attributionTags1, "wl1");
132     processor->OnLogEvent(release_event.get());
133     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
134     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
135 
136     // Acquire wakelock wl1 within bucket #0.
137     acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 110, attributionUids2,
138                                                attributionTags2, "wl1");
139     processor->OnLogEvent(acquire_event.get());
140     EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1,
141               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
142     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
143 
144     // Release wakelock wl1. One anomaly detected.
145     release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 109,
146                                                attributionUids2, attributionTags2, "wl1");
147     processor->OnLogEvent(release_event.get());
148     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
149     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
150               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
151 
152     // Acquire wakelock wl1.
153     acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 112,
154                                                attributionUids1, attributionTags1, "wl1");
155     processor->OnLogEvent(acquire_event.get());
156     // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the
157     // end of the refractory period.
158     const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey1);
159     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
160               (uint32_t)alarmFiredTimestampSec0);
161 
162     // Anomaly alarm fired.
163     auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
164             static_cast<uint32_t>(alarmFiredTimestampSec0));
165     ASSERT_EQ(1u, alarmSet.size());
166     processor->onAnomalyAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet);
167     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
168     EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
169               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
170 
171     // Release wakelock wl1.
172     release_event =
173             CreateReleaseWakelockEvent(alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1,
174                                        attributionUids1, attributionTags1, "wl1");
175     processor->OnLogEvent(release_event.get());
176     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
177     // Within refractory period. No more anomaly detected.
178     EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
179               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
180 
181     // Acquire wakelock wl1.
182     acquire_event =
183             CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11,
184                                        attributionUids2, attributionTags2, "wl1");
185     processor->OnLogEvent(acquire_event.get());
186     const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey1);
187     EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC,
188               (uint64_t)alarmFiredTimestampSec1);
189 
190     // Release wakelock wl1.
191     release_event =
192             CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10,
193                                        attributionUids2, attributionTags2, "wl1");
194     processor->OnLogEvent(release_event.get());
195     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
196     EXPECT_EQ(refractory_period_sec +
197                       (bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1,
198               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
199 
200     alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
201             static_cast<uint32_t>(alarmFiredTimestampSec1));
202     ASSERT_EQ(0u, alarmSet.size());
203 
204     // Acquire wakelock wl1 near the end of bucket #0.
205     acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 2,
206                                                attributionUids1, attributionTags1, "wl1");
207     processor->OnLogEvent(acquire_event.get());
208     EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC,
209               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
210 
211     // Release the event at early bucket #1.
212     release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1,
213                                                attributionUids1, attributionTags1, "wl1");
214     processor->OnLogEvent(release_event.get());
215     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
216     // Anomaly detected when stopping the alarm. The refractory period does not change.
217     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
218               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
219 
220     // Condition changes to false.
221     screen_on_event =
222             CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 20,
223                                           android::view::DisplayStateEnum::DISPLAY_STATE_ON);
224     processor->OnLogEvent(screen_on_event.get());
225     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
226               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
227     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
228 
229     acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 30,
230                                                attributionUids2, attributionTags2, "wl1");
231     processor->OnLogEvent(acquire_event.get());
232     // The condition is false. Do not start the alarm.
233     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
234     EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
235               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
236 
237     // Condition turns true.
238     screen_off_event =
239             CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC,
240                                           android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
241     processor->OnLogEvent(screen_off_event.get());
242     EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC,
243               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
244 
245     // Condition turns to false.
246     screen_on_event =
247             CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1,
248                                           android::view::DisplayStateEnum::DISPLAY_STATE_ON);
249     processor->OnLogEvent(screen_on_event.get());
250     // Condition turns to false. Cancelled the alarm.
251     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
252     //  Detected one anomaly.
253     EXPECT_EQ(refractory_period_sec +
254                       (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1,
255               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
256 
257     // Condition turns to true again.
258     screen_off_event =
259             CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2,
260                                           android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
261     processor->OnLogEvent(screen_off_event.get());
262     EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1,
263               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
264 
265     release_event =
266             CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC,
267                                        attributionUids2, attributionTags2, "wl1");
268     processor->OnLogEvent(release_event.get());
269     EXPECT_EQ(refractory_period_sec +
270                       (bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC,
271               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
272     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
273 }
274 
TEST(AnomalyDetectionE2eTest,TestDurationMetric_SUM_multiple_buckets)275 TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) {
276     const int num_buckets = 3;
277     const uint64_t threshold_ns = NS_PER_SEC;
278     auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
279     const uint64_t alert_id = config.alert(0).id();
280     const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
281 
282     int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
283     int64_t bucketSizeNs =
284             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
285 
286     ConfigKey cfgKey;
287     auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
288     ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
289     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
290     ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
291 
292     sp<AnomalyTracker> anomalyTracker =
293             processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
294 
295     auto screen_off_event = CreateScreenStateChangedEvent(
296             bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
297     processor->OnLogEvent(screen_off_event.get());
298 
299     // Acquire wakelock "wc1" in bucket #0.
300     auto acquire_event =
301             CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1,
302                                        attributionUids1, attributionTags1, "wl1");
303     processor->OnLogEvent(acquire_event.get());
304     EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
305               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
306     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
307 
308     // Release wakelock "wc1" in bucket #0.
309     auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1,
310                                                     attributionUids1, attributionTags1, "wl1");
311     processor->OnLogEvent(release_event.get());
312     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
313     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
314 
315     // Acquire wakelock "wc1" in bucket #1.
316     acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1,
317                                                attributionUids2, attributionTags2, "wl1");
318     processor->OnLogEvent(acquire_event.get());
319     EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
320               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
321     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
322 
323     release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 100,
324                                                attributionUids2, attributionTags2, "wl1");
325     processor->OnLogEvent(release_event.get());
326     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
327     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
328 
329     // Acquire wakelock "wc2" in bucket #2.
330     acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1,
331                                                attributionUids3, attributionTags3, "wl2");
332     processor->OnLogEvent(acquire_event.get());
333     EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2,
334               anomalyTracker->getAlarmTimestampSec(dimensionKey2));
335     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
336 
337     // Release wakelock "wc2" in bucket #2.
338     release_event =
339             CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC,
340                                        attributionUids3, attributionTags3, "wl2");
341     processor->OnLogEvent(release_event.get());
342     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
343     EXPECT_EQ(refractory_period_sec +
344                       (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC,
345               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
346 
347     // Acquire wakelock "wc1" in bucket #2.
348     acquire_event =
349             CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC,
350                                        attributionUids2, attributionTags2, "wl1");
351     processor->OnLogEvent(acquire_event.get());
352     EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 1,
353               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
354     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
355 
356     // Release wakelock "wc1" in bucket #2.
357     release_event =
358             CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC,
359                                        attributionUids2, attributionTags2, "wl1");
360     processor->OnLogEvent(release_event.get());
361     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
362     EXPECT_EQ(refractory_period_sec +
363                       (int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) /
364                               NS_PER_SEC +
365                       1,
366               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
367 
368     acquire_event =
369             CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4,
370                                        attributionUids3, attributionTags3, "wl2");
371     processor->OnLogEvent(acquire_event.get());
372     acquire_event =
373             CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5,
374                                        attributionUids1, attributionTags1, "wl1");
375     processor->OnLogEvent(acquire_event.get());
376     EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
377               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
378     EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
379               anomalyTracker->getAlarmTimestampSec(dimensionKey2));
380 
381     release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 2,
382                                                attributionUids3, attributionTags3, "wl2");
383     processor->OnLogEvent(release_event.get());
384     release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 6,
385                                                attributionUids1, attributionTags1, "wl1");
386     processor->OnLogEvent(release_event.get());
387     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
388     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
389     // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered.
390     EXPECT_EQ(refractory_period_sec + (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC +
391                       1,
392               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
393 }
394 
TEST(AnomalyDetectionE2eTest,TestDurationMetric_SUM_long_refractory_period)395 TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) {
396     const int num_buckets = 2;
397     const uint64_t threshold_ns = 3 * NS_PER_SEC;
398     auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false);
399     int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
400     int64_t bucketSizeNs =
401             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
402 
403     const uint64_t alert_id = config.alert(0).id();
404     const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC;
405     config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec);
406 
407     ConfigKey cfgKey;
408     auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
409     ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
410     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
411     ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
412 
413     sp<AnomalyTracker> anomalyTracker =
414             processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
415 
416     auto screen_off_event = CreateScreenStateChangedEvent(
417             bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
418     processor->OnLogEvent(screen_off_event.get());
419 
420     // Acquire wakelock "wc1" in bucket #0.
421     auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 100,
422                                                     attributionUids1, attributionTags1, "wl1");
423     processor->OnLogEvent(acquire_event.get());
424     EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
425               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
426     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
427 
428     // Acquire the wakelock "wc1" again.
429     acquire_event =
430             CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1,
431                                        attributionUids1, attributionTags1, "wl1");
432     processor->OnLogEvent(acquire_event.get());
433     // The alarm does not change.
434     EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
435               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
436     EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
437 
438     // Anomaly alarm fired late.
439     const int64_t firedAlarmTimestampNs = bucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC;
440     auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
441             static_cast<uint32_t>(firedAlarmTimestampNs / NS_PER_SEC));
442     ASSERT_EQ(1u, alarmSet.size());
443     processor->onAnomalyAlarmFired(firedAlarmTimestampNs, alarmSet);
444     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
445     EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
446               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
447 
448     acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100,
449                                                attributionUids1, attributionTags1, "wl1");
450     processor->OnLogEvent(acquire_event.get());
451     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
452     EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
453               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
454 
455     auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1,
456                                                     attributionUids1, attributionTags1, "wl1");
457     processor->OnLogEvent(release_event.get());
458     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
459     // Within the refractory period. No anomaly.
460     EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
461               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
462 
463     // A new wakelock, but still within refractory period.
464     acquire_event =
465             CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC,
466                                        attributionUids1, attributionTags1, "wl1");
467     processor->OnLogEvent(acquire_event.get());
468     EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
469               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
470 
471     release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC,
472                                                attributionUids1, attributionTags1, "wl1");
473     // Still in the refractory period. No anomaly.
474     processor->OnLogEvent(release_event.get());
475     EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
476               anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
477 
478     acquire_event =
479             CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5,
480                                        attributionUids1, attributionTags1, "wl1");
481     processor->OnLogEvent(acquire_event.get());
482     EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC,
483               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
484 
485     release_event =
486             CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4,
487                                        attributionUids1, attributionTags1, "wl1");
488     processor->OnLogEvent(release_event.get());
489     EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
490 
491     acquire_event =
492             CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3,
493                                        attributionUids1, attributionTags1, "wl1");
494     processor->OnLogEvent(acquire_event.get());
495     EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC,
496               anomalyTracker->getAlarmTimestampSec(dimensionKey1));
497 }
498 
499 #else
500 GTEST_LOG_(INFO) << "This test does nothing.\n";
501 #endif
502 
503 }  // namespace statsd
504 }  // namespace os
505 }  // namespace android
506