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