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