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 <android/binder_ibinder.h>
16 #include <android/binder_interface_utils.h>
17 #include <gtest/gtest.h>
18
19 #include <vector>
20
21 #include "src/StatsLogProcessor.h"
22 #include "src/StatsService.h"
23 #include "src/anomaly/DurationAnomalyTracker.h"
24 #include "src/packages/UidMap.h"
25 #include "src/stats_log_util.h"
26 #include "tests/statsd_test_util.h"
27
28 using ::ndk::SharedRefBase;
29
30 namespace android {
31 namespace os {
32 namespace statsd {
33
34 #ifdef __ANDROID__
35
36 namespace {
37
CreateStatsdConfig(int num_buckets,uint64_t threshold_ns,DurationMetric::AggregationType aggregationType,bool nesting)38 StatsdConfig CreateStatsdConfig(int num_buckets,
39 uint64_t threshold_ns,
40 DurationMetric::AggregationType aggregationType,
41 bool nesting) {
42 StatsdConfig config;
43 *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
44 *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
45 *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
46 *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
47
48 auto screenIsOffPredicate = CreateScreenIsOffPredicate();
49 *config.add_predicate() = screenIsOffPredicate;
50
51 auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
52 FieldMatcher dimensions = CreateAttributionUidDimensions(
53 util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
54 dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock.
55 *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
56 holdingWakelockPredicate.mutable_simple_predicate()->set_count_nesting(nesting);
57 *config.add_predicate() = holdingWakelockPredicate;
58
59 auto durationMetric = config.add_duration_metric();
60 durationMetric->set_id(StringToId("WakelockDuration"));
61 durationMetric->set_what(holdingWakelockPredicate.id());
62 durationMetric->set_condition(screenIsOffPredicate.id());
63 durationMetric->set_aggregation_type(aggregationType);
64 *durationMetric->mutable_dimensions_in_what() =
65 CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
66 durationMetric->set_bucket(FIVE_MINUTES);
67
68 auto alert = config.add_alert();
69 alert->set_id(StringToId("alert"));
70 alert->set_metric_id(StringToId("WakelockDuration"));
71 alert->set_num_buckets(num_buckets);
72 alert->set_refractory_period_secs(2);
73 alert->set_trigger_if_sum_gt(threshold_ns);
74 return config;
75 }
76
77 std::vector<int> attributionUids1 = {111, 222};
78 std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1"};
79
80 std::vector<int> attributionUids2 = {111, 222};
81 std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1"};
82
83 std::vector<int> attributionUids3 = {222};
84 std::vector<string> attributionTags3 = {"GMSCoreModule1"};
85
86 MetricDimensionKey dimensionKey1(
87 HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED,
88 (int32_t)0x02010101),
89 Value((int32_t)111))}),
90 DEFAULT_DIMENSION_KEY);
91
92 MetricDimensionKey dimensionKey2(
93 HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED,
94 (int32_t)0x02010101), Value((int32_t)222))}),
95 DEFAULT_DIMENSION_KEY);
96
97 } // namespace
98
99 // Setup for test fixture.
100 class AnomalyDurationDetectionE2eTest : public StatsServiceConfigTest {};
101
TEST_F(AnomalyDurationDetectionE2eTest,TestDurationMetric_SUM_single_bucket)102 TEST_F(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
103 const int num_buckets = 1;
104 const uint64_t threshold_ns = NS_PER_SEC;
105 auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
106 const uint64_t alert_id = config.alert(0).id();
107 const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
108
109 sendConfig(config);
110
111 auto processor = service->mProcessor;
112 ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
113 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
114 ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
115
116 int64_t bucketStartTimeNs = processor->mTimeBaseNs;
117 int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC;
118 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6;
119
120 sp<AnomalyTracker> anomalyTracker =
121 processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
122
123 auto screen_on_event = CreateScreenStateChangedEvent(
124 bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
125 auto screen_off_event = CreateScreenStateChangedEvent(
126 bucketStartTimeNs + 10, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
127 processor->OnLogEvent(screen_on_event.get());
128 processor->OnLogEvent(screen_off_event.get());
129
130 // Acquire wakelock wl1.
131 auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 11, attributionUids1,
132 attributionTags1, "wl1");
133 processor->OnLogEvent(acquire_event.get());
134 EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1,
135 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
136 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
137
138 // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event.
139 auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 101, attributionUids1,
140 attributionTags1, "wl1");
141 processor->OnLogEvent(release_event.get());
142 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
143 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
144
145 // Acquire wakelock wl1 within bucket #0.
146 acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 110, attributionUids2,
147 attributionTags2, "wl1");
148 processor->OnLogEvent(acquire_event.get());
149 EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1,
150 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
151 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
152
153 // Release wakelock wl1. One anomaly detected.
154 release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 109,
155 attributionUids2, attributionTags2, "wl1");
156 processor->OnLogEvent(release_event.get());
157 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
158 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
159 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
160
161 // Acquire wakelock wl1.
162 acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 112,
163 attributionUids1, attributionTags1, "wl1");
164 processor->OnLogEvent(acquire_event.get());
165 // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the
166 // end of the refractory period.
167 const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey1);
168 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
169 (uint32_t)alarmFiredTimestampSec0);
170 EXPECT_EQ(alarmFiredTimestampSec0,
171 processor->getAnomalyAlarmMonitor()->getRegisteredAlarmTimeSec());
172
173 // Anomaly alarm fired.
174 auto alarmTriggerEvent = CreateBatterySaverOnEvent(alarmFiredTimestampSec0 * NS_PER_SEC);
175 processor->OnLogEvent(alarmTriggerEvent.get(), alarmFiredTimestampSec0 * NS_PER_SEC);
176
177 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
178 EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
179 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
180
181 // Release wakelock wl1.
182 release_event =
183 CreateReleaseWakelockEvent(alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1,
184 attributionUids1, attributionTags1, "wl1");
185 processor->OnLogEvent(release_event.get());
186 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
187 // Within refractory period. No more anomaly detected.
188 EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
189 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
190
191 // Acquire wakelock wl1.
192 acquire_event = CreateAcquireWakelockEvent(
193 roundedBucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11, attributionUids2,
194 attributionTags2, "wl1");
195 processor->OnLogEvent(acquire_event.get());
196 const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey1);
197 EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC,
198 (uint64_t)alarmFiredTimestampSec1);
199
200 // Release wakelock wl1.
201 int64_t release_event_time = roundedBucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10;
202 release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2,
203 attributionTags2, "wl1");
204 processor->OnLogEvent(release_event.get(), release_event_time);
205 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
206 EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC + 1,
207 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
208
209 auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
210 static_cast<uint32_t>(alarmFiredTimestampSec1));
211 ASSERT_EQ(0u, alarmSet.size());
212
213 // Acquire wakelock wl1 near the end of bucket #0.
214 acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs - 2,
215 attributionUids1, attributionTags1, "wl1");
216 processor->OnLogEvent(acquire_event.get());
217 EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC,
218 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
219
220 // Release the event at early bucket #1.
221 release_event_time = roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1;
222 release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1,
223 attributionTags1, "wl1");
224 processor->OnLogEvent(release_event.get(), release_event_time);
225 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
226 // Anomaly detected when stopping the alarm. The refractory period does not change.
227 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
228 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
229
230 // Condition changes to false.
231 screen_on_event =
232 CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 20,
233 android::view::DisplayStateEnum::DISPLAY_STATE_ON);
234 processor->OnLogEvent(screen_on_event.get());
235 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
236 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
237 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
238
239 acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 30,
240 attributionUids2, attributionTags2, "wl1");
241 processor->OnLogEvent(acquire_event.get());
242 // The condition is false. Do not start the alarm.
243 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
244 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
245 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
246
247 // Condition turns true.
248 screen_off_event =
249 CreateScreenStateChangedEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC,
250 android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
251 processor->OnLogEvent(screen_off_event.get());
252 EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC,
253 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
254
255 // Condition turns to false.
256 int64_t condition_false_time = bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1;
257 screen_on_event = CreateScreenStateChangedEvent(
258 condition_false_time, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
259 processor->OnLogEvent(screen_on_event.get(), condition_false_time);
260 // Condition turns to false. Cancelled the alarm.
261 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
262 // Detected one anomaly.
263 EXPECT_EQ(refractory_period_sec +
264 (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1,
265 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
266
267 // Condition turns to true again.
268 screen_off_event =
269 CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2,
270 android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
271 processor->OnLogEvent(screen_off_event.get());
272 EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1,
273 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
274
275 release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC;
276 release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2,
277 attributionTags2, "wl1");
278 processor->OnLogEvent(release_event.get());
279 EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC,
280 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
281 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
282 }
283
TEST_F(AnomalyDurationDetectionE2eTest,TestDurationMetric_SUM_multiple_buckets)284 TEST_F(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) {
285 const int num_buckets = 3;
286 const uint64_t threshold_ns = NS_PER_SEC;
287 auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
288 const uint64_t alert_id = config.alert(0).id();
289 const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
290
291 sendConfig(config);
292
293 auto processor = service->mProcessor;
294 ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
295 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
296 ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
297
298 int64_t bucketStartTimeNs = processor->mTimeBaseNs;
299 int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC;
300 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6;
301
302 sp<AnomalyTracker> anomalyTracker =
303 processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
304
305 auto screen_off_event = CreateScreenStateChangedEvent(
306 bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
307 processor->OnLogEvent(screen_off_event.get());
308
309 // Acquire wakelock "wc1" in bucket #0.
310 auto acquire_event =
311 CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1,
312 attributionUids1, attributionTags1, "wl1");
313 processor->OnLogEvent(acquire_event.get());
314 EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
315 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
316 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
317
318 // Release wakelock "wc1" in bucket #0.
319 int64_t release_event_time = roundedBucketStartTimeNs + bucketSizeNs - 1;
320 auto release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1,
321 attributionTags1, "wl1");
322 processor->OnLogEvent(release_event.get(), release_event_time);
323 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
324 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
325
326 // Acquire wakelock "wc1" in bucket #1.
327 acquire_event =
328 CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC + 1,
329 attributionUids2, attributionTags2, "wl1");
330 processor->OnLogEvent(acquire_event.get());
331 EXPECT_EQ((bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC + 1,
332 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
333 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
334
335 release_event_time = roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC + 100;
336 release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2,
337 attributionTags2, "wl1");
338 processor->OnLogEvent(release_event.get(), release_event_time);
339 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
340 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
341
342 // Acquire wakelock "wc2" in bucket #2.
343 acquire_event =
344 CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + 1,
345 attributionUids3, attributionTags3, "wl2");
346 processor->OnLogEvent(acquire_event.get());
347 EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 3,
348 anomalyTracker->getAlarmTimestampSec(dimensionKey2));
349 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
350
351 // Release wakelock "wc2" in bucket #2.
352 release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC;
353 release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids3,
354 attributionTags3, "wl2");
355 processor->OnLogEvent(release_event.get(), release_event_time);
356 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
357 EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC,
358 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
359
360 // Acquire wakelock "wc1" in bucket #2.
361 acquire_event =
362 CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC,
363 attributionUids2, attributionTags2, "wl1");
364 processor->OnLogEvent(acquire_event.get());
365 EXPECT_EQ((roundedBucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 3 + 1,
366 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
367 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
368
369 // Release wakelock "wc1" in bucket #2.
370 release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 3.5 * NS_PER_SEC;
371 release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2,
372 attributionTags2, "wl1");
373 processor->OnLogEvent(release_event.get(), release_event_time);
374 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
375 EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC + 1,
376 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
377
378 acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 6 * bucketSizeNs + 4,
379 attributionUids3, attributionTags3, "wl2");
380 processor->OnLogEvent(acquire_event.get());
381 acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 6 * bucketSizeNs + 5,
382 attributionUids1, attributionTags1, "wl1");
383 processor->OnLogEvent(acquire_event.get());
384 EXPECT_EQ((roundedBucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 2,
385 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
386 EXPECT_EQ((roundedBucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 2,
387 anomalyTracker->getAlarmTimestampSec(dimensionKey2));
388
389 release_event_time = roundedBucketStartTimeNs + 6 * bucketSizeNs + NS_PER_SEC + 2;
390 release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids3,
391 attributionTags3, "wl2");
392 processor->OnLogEvent(release_event.get(), release_event_time);
393 release_event = CreateReleaseWakelockEvent(release_event_time + 4, attributionUids1,
394 attributionTags1, "wl1");
395 processor->OnLogEvent(release_event.get(), release_event_time + 4);
396 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
397 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
398 // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered.
399 EXPECT_EQ(refractory_period_sec +
400 (int64_t)(roundedBucketStartTimeNs + 6 * bucketSizeNs + NS_PER_SEC) /
401 NS_PER_SEC +
402 1,
403 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
404 }
405
TEST_F(AnomalyDurationDetectionE2eTest,TestDurationMetric_SUM_partial_bucket)406 TEST_F(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_partial_bucket) {
407 const int num_buckets = 1;
408 const uint64_t threshold_ns = NS_PER_SEC;
409 auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
410 const uint64_t alert_id = config.alert(0).id();
411 const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
412
413 sendConfig(config);
414
415 auto processor = service->mProcessor;
416 ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
417 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
418 ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
419
420 int64_t bucketStartTimeNs = processor->mTimeBaseNs;
421 int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC;
422 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6;
423
424 UidData uidData;
425 *uidData.add_app_info() = createApplicationInfo(/*uid*/ 1, /*version*/ 1, "v1", "randomApp");
426 processor->getUidMap()->updateMap(bucketStartTimeNs, uidData);
427
428 sp<AnomalyTracker> anomalyTracker =
429 processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
430
431 auto screen_off_event = CreateScreenStateChangedEvent(
432 bucketStartTimeNs + 10, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
433 processor->OnLogEvent(screen_off_event.get());
434
435 // Acquire wakelock wl1.
436 auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 11, attributionUids1,
437 attributionTags1, "wl1");
438 processor->OnLogEvent(acquire_event.get());
439 EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1,
440 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
441 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
442
443 // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event.
444 // 90 ns accumulated.
445 auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 101, attributionUids1,
446 attributionTags1, "wl1");
447 processor->OnLogEvent(release_event.get());
448 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
449 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
450
451 // Acquire wakelock wl2.
452 acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 110, attributionUids3,
453 attributionTags3, "wl2");
454 processor->OnLogEvent(acquire_event.get());
455 int64_t wl2AlarmTimeNs = bucketStartTimeNs + 110 + threshold_ns;
456 EXPECT_EQ(wl2AlarmTimeNs / NS_PER_SEC + 1, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
457 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
458
459 // Partial bucket split.
460 int64_t appUpgradeTimeNs = bucketStartTimeNs + 500;
461 service->mUidMap->updateApp(appUpgradeTimeNs, "randomApp", 1, 2, "v2", "",
462 /* certificateHash */ {});
463 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
464 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
465 EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns) / NS_PER_SEC + 1,
466 anomalyTracker->getAlarmTimestampSec(dimensionKey2));
467 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
468
469 // Acquire wakelock wl1. Subtract 100 ns since that accumulated before the bucket split.
470 acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 510, attributionUids1,
471 attributionTags1, "wl1");
472 processor->OnLogEvent(acquire_event.get());
473 int64_t wl1AlarmTimeNs = bucketStartTimeNs + 510 + threshold_ns - 90;
474 EXPECT_EQ(wl1AlarmTimeNs / NS_PER_SEC + 1, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
475 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
476
477 // Release wakelock wl1. One anomaly detected.
478 release_event = CreateReleaseWakelockEvent(wl1AlarmTimeNs + 1, attributionUids2,
479 attributionTags2, "wl1");
480 processor->OnLogEvent(release_event.get());
481 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
482 EXPECT_EQ(refractory_period_sec + (wl1AlarmTimeNs + 1) / NS_PER_SEC + 1,
483 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
484
485 // Anomaly alarm fired.
486 auto alarmTriggerEvent = CreateBatterySaverOnEvent(wl2AlarmTimeNs);
487 processor->OnLogEvent(alarmTriggerEvent.get(), wl2AlarmTimeNs);
488
489 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
490 EXPECT_EQ(refractory_period_sec + wl2AlarmTimeNs / NS_PER_SEC + 1,
491 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
492 }
493
TEST_F(AnomalyDurationDetectionE2eTest,TestDurationMetric_SUM_long_refractory_period)494 TEST_F(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) {
495 const int num_buckets = 2;
496 const uint64_t threshold_ns = 3 * NS_PER_SEC;
497 auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false);
498 const uint64_t alert_id = config.alert(0).id();
499 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6;
500 const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC;
501 config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec);
502
503 sendConfig(config);
504
505 auto processor = service->mProcessor;
506 ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
507 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
508 ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
509
510 int64_t bucketStartTimeNs = processor->mTimeBaseNs;
511 int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC;
512
513 sp<AnomalyTracker> anomalyTracker =
514 processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
515
516 auto screen_off_event = CreateScreenStateChangedEvent(
517 bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
518 processor->OnLogEvent(screen_off_event.get());
519
520 // Acquire wakelock "wc1" in bucket #0.
521 auto acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs - 100,
522 attributionUids1, attributionTags1, "wl1");
523 processor->OnLogEvent(acquire_event.get());
524 EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
525 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
526 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
527
528 // Acquire the wakelock "wc1" again.
529 acquire_event =
530 CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1,
531 attributionUids1, attributionTags1, "wl1");
532 processor->OnLogEvent(acquire_event.get());
533 // The alarm does not change.
534 EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
535 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
536 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
537
538 // Anomaly alarm fired late.
539 const int64_t firedAlarmTimestampNs = roundedBucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC;
540 auto alarmTriggerEvent = CreateBatterySaverOnEvent(firedAlarmTimestampNs);
541 processor->OnLogEvent(alarmTriggerEvent.get(), firedAlarmTimestampNs);
542 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
543 EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
544 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
545
546 acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs - 100,
547 attributionUids1, attributionTags1, "wl1");
548 processor->OnLogEvent(acquire_event.get());
549 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
550 EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
551 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
552
553 int64_t release_event_time = bucketStartTimeNs + 2 * bucketSizeNs + 1;
554 auto release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1,
555 attributionTags1, "wl1");
556 processor->OnLogEvent(release_event.get(), release_event_time);
557 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
558 // Within the refractory period. No anomaly.
559 EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
560 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
561
562 // A new wakelock, but still within refractory period.
563 acquire_event = CreateAcquireWakelockEvent(
564 roundedBucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC, attributionUids1,
565 attributionTags1, "wl1");
566 processor->OnLogEvent(acquire_event.get());
567 EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
568 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
569
570 release_event =
571 CreateReleaseWakelockEvent(roundedBucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC,
572 attributionUids1, attributionTags1, "wl1");
573 // Still in the refractory period. No anomaly.
574 processor->OnLogEvent(release_event.get());
575 EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
576 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
577
578 acquire_event = CreateAcquireWakelockEvent(
579 roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 5, attributionUids1,
580 attributionTags1, "wl1");
581 processor->OnLogEvent(acquire_event.get());
582 EXPECT_EQ((roundedBucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC + 1,
583 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
584
585 release_event_time = roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 4;
586 release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1,
587 attributionTags1, "wl1");
588 processor->OnLogEvent(release_event.get(), release_event_time);
589 EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
590
591 acquire_event = CreateAcquireWakelockEvent(
592 roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 3, attributionUids1,
593 attributionTags1, "wl1");
594 processor->OnLogEvent(acquire_event.get());
595 EXPECT_EQ((roundedBucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC + 1,
596 anomalyTracker->getAlarmTimestampSec(dimensionKey1));
597 }
598
599 #else
600 GTEST_LOG_(INFO) << "This test does nothing.\n";
601 #endif
602
603 } // namespace statsd
604 } // namespace os
605 } // namespace android
606