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