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