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