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
24namespace android {
25namespace os {
26namespace statsd {
27
28#ifdef __ANDROID__
29
30namespace {
31
32StatsdConfig 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
74std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
75                                                      CreateAttribution(222, "GMSCoreModule1")};
76
77std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App2"),
78                                                      CreateAttribution(222, "GMSCoreModule1")};
79
80std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1")};
81
82MetricDimensionKey dimensionKey(
83    HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED,
84                                           (int32_t)0x02010101), Value((int32_t)111))}),
85    DEFAULT_DIMENSION_KEY);
86
87MetricDimensionKey dimensionKey2(
88    HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED,
89                                           (int32_t)0x02010101), Value((int32_t)222))}),
90    DEFAULT_DIMENSION_KEY);
91
92TEST(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
269TEST(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
381TEST(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
481GTEST_LOG_(INFO) << "This test does nothing.\n";
482#endif
483
484}  // namespace statsd
485}  // namespace os
486}  // namespace android
487