Remove alarm manager dependency from anomalyalarms

Anomaly alarms, which are used for prediction-base duration metric
anomaly detection, currently use alarm manager for setting alarms. This
can result in excessive binder calls to SCS to set/cancel the alarms if
the duration metric that the alarm is based on gets stopped/started in
quick succession.

To resolve this without losing precision, we can instead rely on the
flow of events into the statsd socket for this prediction. The socket
receives events very frequently, so there should be minimal (if any)
loss of precision, and we can remove the binder call entirely.

One concern is that we used to hold a wakelock when the alarm fired
(since alarm manager would hold a wakelock), and we now lose this
ability.

Test: atest statsd_test
Test: atest AnomalyDetectionTests
Bug: 161326200
Bug: 152642091
Change-Id: If37e5c6ccd8efa4e822c70a0444fa6b5a31bebca
Merged-In: If37e5c6ccd8efa4e822c70a0444fa6b5a31bebca
This commit is contained in:
Tej Singh
2020-07-14 23:16:24 -07:00
committed by Jeffrey Huang
parent 85e07ab8bb
commit 8b914c0f00
5 changed files with 212 additions and 149 deletions

View File

@@ -120,10 +120,9 @@ static void flushProtoToBuffer(ProtoOutputStream& proto, vector<uint8_t>* outDat
} }
} }
void StatsLogProcessor::onAnomalyAlarmFired( void StatsLogProcessor::processFiredAnomalyAlarmsLocked(
const int64_t& timestampNs, const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) { unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
for (const auto& itr : mMetricsManagers) { for (const auto& itr : mMetricsManagers) {
itr.second->onAnomalyAlarmFired(timestampNs, alarmSet); itr.second->onAnomalyAlarmFired(timestampNs, alarmSet);
} }
@@ -431,6 +430,20 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
return; return;
} }
bool fireAlarm = false;
{
std::lock_guard<std::mutex> anomalyLock(mAnomalyAlarmMutex);
if (mNextAnomalyAlarmTime != 0 &&
MillisToNano(mNextAnomalyAlarmTime) <= elapsedRealtimeNs) {
mNextAnomalyAlarmTime = 0;
VLOG("informing anomaly alarm at time %lld", (long long)elapsedRealtimeNs);
fireAlarm = true;
}
}
if (fireAlarm) {
informAnomalyAlarmFiredLocked(NanoToMillis(elapsedRealtimeNs));
}
int64_t curTimeSec = getElapsedRealtimeSec(); int64_t curTimeSec = getElapsedRealtimeSec();
if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) { if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) {
mPullerManager->ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC); mPullerManager->ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC);
@@ -1092,6 +1105,28 @@ void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) {
mOnDiskDataConfigs.insert(key); mOnDiskDataConfigs.insert(key);
} }
void StatsLogProcessor::setAnomalyAlarm(const int64_t elapsedTimeMillis) {
std::lock_guard<std::mutex> lock(mAnomalyAlarmMutex);
mNextAnomalyAlarmTime = elapsedTimeMillis;
}
void StatsLogProcessor::cancelAnomalyAlarm() {
std::lock_guard<std::mutex> lock(mAnomalyAlarmMutex);
mNextAnomalyAlarmTime = 0;
}
void StatsLogProcessor::informAnomalyAlarmFiredLocked(const int64_t elapsedTimeMillis) {
VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called");
std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet =
mAnomalyAlarmMonitor->popSoonerThan(static_cast<uint32_t>(elapsedTimeMillis / 1000));
if (alarmSet.size() > 0) {
VLOG("Found periodic alarm fired.");
processFiredAnomalyAlarmsLocked(MillisToNano(elapsedTimeMillis), alarmSet);
} else {
ALOGW("Cannot find an periodic alarm that fired. Perhaps it was recently cancelled.");
}
}
} // namespace statsd } // namespace statsd
} // namespace os } // namespace os
} // namespace android } // namespace android

View File

@@ -66,11 +66,6 @@ public:
const DumpLatency dumpLatency, const DumpLatency dumpLatency,
ProtoOutputStream* proto); ProtoOutputStream* proto);
/* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
void onAnomalyAlarmFired(
const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet);
/* Tells MetricsManager that the alarms in alarmSet have fired. Modifies periodic alarmSet. */ /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies periodic alarmSet. */
void onPeriodicAlarmFired( void onPeriodicAlarmFired(
const int64_t& timestampNs, const int64_t& timestampNs,
@@ -148,6 +143,10 @@ public:
// Add a specific config key to the possible configs to dump ASAP. // Add a specific config key to the possible configs to dump ASAP.
void noteOnDiskData(const ConfigKey& key); void noteOnDiskData(const ConfigKey& key);
void setAnomalyAlarm(const int64_t timeMillis);
void cancelAnomalyAlarm();
private: private:
// For testing only. // For testing only.
inline sp<AlarmMonitor> getAnomalyAlarmMonitor() const { inline sp<AlarmMonitor> getAnomalyAlarmMonitor() const {
@@ -160,6 +159,11 @@ private:
mutable mutex mMetricsMutex; mutable mutex mMetricsMutex;
// Guards mNextAnomalyAlarmTime. A separate mutex is needed because alarms are set/cancelled
// in the onLogEvent code path, which is locked by mMetricsMutex.
// DO NOT acquire mMetricsMutex while holding mAnomalyAlarmMutex. This can lead to a deadlock.
mutable mutex mAnomalyAlarmMutex;
std::unordered_map<ConfigKey, sp<MetricsManager>> mMetricsManagers; std::unordered_map<ConfigKey, sp<MetricsManager>> mMetricsManagers;
std::unordered_map<ConfigKey, int64_t> mLastBroadcastTimes; std::unordered_map<ConfigKey, int64_t> mLastBroadcastTimes;
@@ -250,6 +254,15 @@ private:
// Reset the specified configs. // Reset the specified configs.
void resetConfigsLocked(const int64_t timestampNs, const std::vector<ConfigKey>& configs); void resetConfigsLocked(const int64_t timestampNs, const std::vector<ConfigKey>& configs);
// An anomaly alarm should have fired.
// Check with anomaly alarm manager to find the alarms and process the result.
void informAnomalyAlarmFiredLocked(const int64_t elapsedTimeMillis);
/* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
void processFiredAnomalyAlarmsLocked(
const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet);
// Function used to send a broadcast so that receiver for the config key can call getData // Function used to send a broadcast so that receiver for the config key can call getData
// to retrieve the stored data. // to retrieve the stored data.
std::function<bool(const ConfigKey& key)> mSendBroadcast; std::function<bool(const ConfigKey& key)> mSendBroadcast;
@@ -276,6 +289,9 @@ private:
//Last time we wrote metadata to disk. //Last time we wrote metadata to disk.
int64_t mLastMetadataWriteNs = 0; int64_t mLastMetadataWriteNs = 0;
// The time for the next anomaly alarm for alerts.
int64_t mNextAnomalyAlarmTime = 0;
#ifdef VERY_VERBOSE_PRINTING #ifdef VERY_VERBOSE_PRINTING
bool mPrintAllLogs = false; bool mPrintAllLogs = false;
#endif #endif

View File

@@ -91,17 +91,13 @@ Status checkUid(uid_t expectedUid) {
StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQueue> queue) StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQueue> queue)
: mAnomalyAlarmMonitor(new AlarmMonitor( : mAnomalyAlarmMonitor(new AlarmMonitor(
MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
[](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) { [this](const shared_ptr<IStatsCompanionService>& /*sc*/, int64_t timeMillis) {
if (sc != nullptr) { mProcessor->setAnomalyAlarm(timeMillis);
sc->setAnomalyAlarm(timeMillis); StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
}
}, },
[](const shared_ptr<IStatsCompanionService>& sc) { [this](const shared_ptr<IStatsCompanionService>& /*sc*/) {
if (sc != nullptr) { mProcessor->cancelAnomalyAlarm();
sc->cancelAnomalyAlarm(); StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
}
})), })),
mPeriodicAlarmMonitor(new AlarmMonitor( mPeriodicAlarmMonitor(new AlarmMonitor(
MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
@@ -977,17 +973,7 @@ Status StatsService::informOnePackageRemoved(const string& app, int32_t uid) {
Status StatsService::informAnomalyAlarmFired() { Status StatsService::informAnomalyAlarmFired() {
ENFORCE_UID(AID_SYSTEM); ENFORCE_UID(AID_SYSTEM);
// Anomaly alarms are handled internally now. This code should be fully deleted.
VLOG("StatsService::informAnomalyAlarmFired was called");
int64_t currentTimeSec = getElapsedRealtimeSec();
std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet =
mAnomalyAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
if (alarmSet.size() > 0) {
VLOG("Found an anomaly alarm that fired.");
mProcessor->onAnomalyAlarmFired(currentTimeSec * NS_PER_SEC, alarmSet);
} else {
VLOG("Cannot find an anomaly alarm that fired. Perhaps it was recently cancelled.");
}
return Status::ok(); return Status::ok();
} }

View File

@@ -404,6 +404,10 @@ private:
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket); FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket); FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket); FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
}; };
} // namespace statsd } // namespace statsd

View File

@@ -12,14 +12,19 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include <android/binder_ibinder.h>
#include <android/binder_interface_utils.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "src/anomaly/DurationAnomalyTracker.h" #include <vector>
#include "src/StatsLogProcessor.h" #include "src/StatsLogProcessor.h"
#include "src/StatsService.h"
#include "src/anomaly/DurationAnomalyTracker.h"
#include "src/stats_log_util.h" #include "src/stats_log_util.h"
#include "tests/statsd_test_util.h" #include "tests/statsd_test_util.h"
#include <vector> using ::ndk::SharedRefBase;
namespace android { namespace android {
namespace os { namespace os {
@@ -29,6 +34,9 @@ namespace statsd {
namespace { namespace {
const int kConfigKey = 789130124;
const int kCallingUid = 0;
StatsdConfig CreateStatsdConfig(int num_buckets, StatsdConfig CreateStatsdConfig(int num_buckets,
uint64_t threshold_ns, uint64_t threshold_ns,
DurationMetric::AggregationType aggregationType, DurationMetric::AggregationType aggregationType,
@@ -89,6 +97,13 @@ MetricDimensionKey dimensionKey2(
(int32_t)0x02010101), Value((int32_t)222))}), (int32_t)0x02010101), Value((int32_t)222))}),
DEFAULT_DIMENSION_KEY); DEFAULT_DIMENSION_KEY);
void sendConfig(shared_ptr<StatsService>& service, const StatsdConfig& config) {
string str;
config.SerializeToString(&str);
std::vector<uint8_t> configAsVec(str.begin(), str.end());
service->addConfiguration(kConfigKey, configAsVec, kCallingUid);
}
} // namespace } // namespace
TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) { TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
@@ -98,16 +113,18 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
const uint64_t alert_id = config.alert(0).id(); const uint64_t alert_id = config.alert(0).id();
const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
int64_t bucketStartTimeNs = 10 * NS_PER_SEC; shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
int64_t bucketSizeNs = sendConfig(service, config);
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
ConfigKey cfgKey; auto processor = service->mProcessor;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
ASSERT_EQ(processor->mMetricsManagers.size(), 1u); ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
int64_t bucketStartTimeNs = processor->mTimeBaseNs;
int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6;
sp<AnomalyTracker> anomalyTracker = sp<AnomalyTracker> anomalyTracker =
processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
@@ -158,12 +175,13 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey1); const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey1);
EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
(uint32_t)alarmFiredTimestampSec0); (uint32_t)alarmFiredTimestampSec0);
EXPECT_EQ(alarmFiredTimestampSec0,
processor->getAnomalyAlarmMonitor()->getRegisteredAlarmTimeSec());
// Anomaly alarm fired. // Anomaly alarm fired.
auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( auto alarmTriggerEvent = CreateBatterySaverOnEvent(alarmFiredTimestampSec0 * NS_PER_SEC);
static_cast<uint32_t>(alarmFiredTimestampSec0)); processor->OnLogEvent(alarmTriggerEvent.get(), alarmFiredTimestampSec0 * NS_PER_SEC);
ASSERT_EQ(1u, alarmSet.size());
processor->onAnomalyAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet);
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
@@ -179,39 +197,39 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Acquire wakelock wl1. // Acquire wakelock wl1.
acquire_event = acquire_event = CreateAcquireWakelockEvent(
CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11, roundedBucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11, attributionUids2,
attributionUids2, attributionTags2, "wl1"); attributionTags2, "wl1");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey1); const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey1);
EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC, EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC,
(uint64_t)alarmFiredTimestampSec1); (uint64_t)alarmFiredTimestampSec1);
// Release wakelock wl1. // Release wakelock wl1.
release_event = int64_t release_event_time = roundedBucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10;
CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10, release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2,
attributionUids2, attributionTags2, "wl1"); attributionTags2, "wl1");
processor->OnLogEvent(release_event.get()); processor->OnLogEvent(release_event.get(), release_event_time);
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(refractory_period_sec + EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC + 1,
(bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
static_cast<uint32_t>(alarmFiredTimestampSec1)); static_cast<uint32_t>(alarmFiredTimestampSec1));
ASSERT_EQ(0u, alarmSet.size()); ASSERT_EQ(0u, alarmSet.size());
// Acquire wakelock wl1 near the end of bucket #0. // Acquire wakelock wl1 near the end of bucket #0.
acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 2, acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs - 2,
attributionUids1, attributionTags1, "wl1"); attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC, EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC,
anomalyTracker->getAlarmTimestampSec(dimensionKey1)); anomalyTracker->getAlarmTimestampSec(dimensionKey1));
// Release the event at early bucket #1. // Release the event at early bucket #1.
release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1, release_event_time = roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1;
attributionUids1, attributionTags1, "wl1"); release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1,
processor->OnLogEvent(release_event.get()); attributionTags1, "wl1");
processor->OnLogEvent(release_event.get(), release_event_time);
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
// Anomaly detected when stopping the alarm. The refractory period does not change. // Anomaly detected when stopping the alarm. The refractory period does not change.
EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
@@ -236,17 +254,17 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
// Condition turns true. // Condition turns true.
screen_off_event = screen_off_event =
CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC, CreateScreenStateChangedEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_OFF); android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
processor->OnLogEvent(screen_off_event.get()); processor->OnLogEvent(screen_off_event.get());
EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC, EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC,
anomalyTracker->getAlarmTimestampSec(dimensionKey1)); anomalyTracker->getAlarmTimestampSec(dimensionKey1));
// Condition turns to false. // Condition turns to false.
screen_on_event = int64_t condition_false_time = bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1;
CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1, screen_on_event = CreateScreenStateChangedEvent(
android::view::DisplayStateEnum::DISPLAY_STATE_ON); condition_false_time, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
processor->OnLogEvent(screen_on_event.get()); processor->OnLogEvent(screen_on_event.get(), condition_false_time);
// Condition turns to false. Cancelled the alarm. // Condition turns to false. Cancelled the alarm.
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
// Detected one anomaly. // Detected one anomaly.
@@ -262,12 +280,11 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1, EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1,
anomalyTracker->getAlarmTimestampSec(dimensionKey1)); anomalyTracker->getAlarmTimestampSec(dimensionKey1));
release_event = release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC;
CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC, release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2,
attributionUids2, attributionTags2, "wl1"); attributionTags2, "wl1");
processor->OnLogEvent(release_event.get()); processor->OnLogEvent(release_event.get());
EXPECT_EQ(refractory_period_sec + EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC,
(bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
} }
@@ -279,16 +296,18 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) {
const uint64_t alert_id = config.alert(0).id(); const uint64_t alert_id = config.alert(0).id();
const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
int64_t bucketStartTimeNs = 10 * NS_PER_SEC; shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
int64_t bucketSizeNs = sendConfig(service, config);
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
ConfigKey cfgKey; auto processor = service->mProcessor;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
ASSERT_EQ(processor->mMetricsManagers.size(), 1u); ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
int64_t bucketStartTimeNs = processor->mTimeBaseNs;
int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6;
sp<AnomalyTracker> anomalyTracker = sp<AnomalyTracker> anomalyTracker =
processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
@@ -298,96 +317,97 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) {
// Acquire wakelock "wc1" in bucket #0. // Acquire wakelock "wc1" in bucket #0.
auto acquire_event = auto acquire_event =
CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1, CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1,
attributionUids1, attributionTags1, "wl1"); attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
anomalyTracker->getAlarmTimestampSec(dimensionKey1)); anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Release wakelock "wc1" in bucket #0. // Release wakelock "wc1" in bucket #0.
auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, int64_t release_event_time = roundedBucketStartTimeNs + bucketSizeNs - 1;
attributionUids1, attributionTags1, "wl1"); auto release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1,
processor->OnLogEvent(release_event.get()); attributionTags1, "wl1");
processor->OnLogEvent(release_event.get(), release_event_time);
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Acquire wakelock "wc1" in bucket #1. // Acquire wakelock "wc1" in bucket #1.
acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, acquire_event =
attributionUids2, attributionTags2, "wl1"); CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC + 1,
attributionUids2, attributionTags2, "wl1");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, EXPECT_EQ((bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC + 1,
anomalyTracker->getAlarmTimestampSec(dimensionKey1)); anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 100, release_event_time = roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC + 100;
attributionUids2, attributionTags2, "wl1"); release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2,
processor->OnLogEvent(release_event.get()); attributionTags2, "wl1");
processor->OnLogEvent(release_event.get(), release_event_time);
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Acquire wakelock "wc2" in bucket #2. // Acquire wakelock "wc2" in bucket #2.
acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, acquire_event =
attributionUids3, attributionTags3, "wl2"); CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + 1,
attributionUids3, attributionTags3, "wl2");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2, EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 3,
anomalyTracker->getAlarmTimestampSec(dimensionKey2)); anomalyTracker->getAlarmTimestampSec(dimensionKey2));
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
// Release wakelock "wc2" in bucket #2. // Release wakelock "wc2" in bucket #2.
release_event = release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC;
CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC, release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids3,
attributionUids3, attributionTags3, "wl2"); attributionTags3, "wl2");
processor->OnLogEvent(release_event.get()); processor->OnLogEvent(release_event.get(), release_event_time);
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
EXPECT_EQ(refractory_period_sec + EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC,
(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
// Acquire wakelock "wc1" in bucket #2. // Acquire wakelock "wc1" in bucket #2.
acquire_event = acquire_event =
CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC, CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC,
attributionUids2, attributionTags2, "wl1"); attributionUids2, attributionTags2, "wl1");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 1, EXPECT_EQ((roundedBucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 3 + 1,
anomalyTracker->getAlarmTimestampSec(dimensionKey1)); anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Release wakelock "wc1" in bucket #2. // Release wakelock "wc1" in bucket #2.
release_event = release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 3.5 * NS_PER_SEC;
CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC, release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2,
attributionUids2, attributionTags2, "wl1"); attributionTags2, "wl1");
processor->OnLogEvent(release_event.get()); processor->OnLogEvent(release_event.get(), release_event_time);
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(refractory_period_sec + EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC + 1,
(int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) /
NS_PER_SEC +
1,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
acquire_event = acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 6 * bucketSizeNs + 4,
CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4, attributionUids3, attributionTags3, "wl2");
attributionUids3, attributionTags3, "wl2");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
acquire_event = acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 6 * bucketSizeNs + 5,
CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5, attributionUids1, attributionTags1, "wl1");
attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, EXPECT_EQ((roundedBucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 2,
anomalyTracker->getAlarmTimestampSec(dimensionKey1)); anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, EXPECT_EQ((roundedBucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 2,
anomalyTracker->getAlarmTimestampSec(dimensionKey2)); anomalyTracker->getAlarmTimestampSec(dimensionKey2));
release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 2, release_event_time = roundedBucketStartTimeNs + 6 * bucketSizeNs + NS_PER_SEC + 2;
attributionUids3, attributionTags3, "wl2"); release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids3,
processor->OnLogEvent(release_event.get()); attributionTags3, "wl2");
release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 6, processor->OnLogEvent(release_event.get(), release_event_time);
attributionUids1, attributionTags1, "wl1"); release_event = CreateReleaseWakelockEvent(release_event_time + 4, attributionUids1,
processor->OnLogEvent(release_event.get()); attributionTags1, "wl1");
processor->OnLogEvent(release_event.get(), release_event_time + 4);
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
// The buckets are not messed up across dimensions. Only one dimension has anomaly triggered. // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered.
EXPECT_EQ(refractory_period_sec + (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + EXPECT_EQ(refractory_period_sec +
(int64_t)(roundedBucketStartTimeNs + 6 * bucketSizeNs + NS_PER_SEC) /
NS_PER_SEC +
1, 1,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
} }
@@ -396,20 +416,22 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) {
const int num_buckets = 2; const int num_buckets = 2;
const uint64_t threshold_ns = 3 * NS_PER_SEC; const uint64_t threshold_ns = 3 * NS_PER_SEC;
auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false); auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false);
int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
const uint64_t alert_id = config.alert(0).id(); const uint64_t alert_id = config.alert(0).id();
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6;
const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC; const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC;
config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec); config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec);
ConfigKey cfgKey; shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); sendConfig(service, config);
auto processor = service->mProcessor;
ASSERT_EQ(processor->mMetricsManagers.size(), 1u); ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
int64_t bucketStartTimeNs = processor->mTimeBaseNs;
int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC;
sp<AnomalyTracker> anomalyTracker = sp<AnomalyTracker> anomalyTracker =
processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
@@ -418,81 +440,81 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) {
processor->OnLogEvent(screen_off_event.get()); processor->OnLogEvent(screen_off_event.get());
// Acquire wakelock "wc1" in bucket #0. // Acquire wakelock "wc1" in bucket #0.
auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 100, auto acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs - 100,
attributionUids1, attributionTags1, "wl1"); attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
anomalyTracker->getAlarmTimestampSec(dimensionKey1)); anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Acquire the wakelock "wc1" again. // Acquire the wakelock "wc1" again.
acquire_event = acquire_event =
CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1, CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1,
attributionUids1, attributionTags1, "wl1"); attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
// The alarm does not change. // The alarm does not change.
EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
anomalyTracker->getAlarmTimestampSec(dimensionKey1)); anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Anomaly alarm fired late. // Anomaly alarm fired late.
const int64_t firedAlarmTimestampNs = bucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC; const int64_t firedAlarmTimestampNs = roundedBucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC;
auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( auto alarmTriggerEvent = CreateBatterySaverOnEvent(firedAlarmTimestampNs);
static_cast<uint32_t>(firedAlarmTimestampNs / NS_PER_SEC)); processor->OnLogEvent(alarmTriggerEvent.get(), firedAlarmTimestampNs);
ASSERT_EQ(1u, alarmSet.size());
processor->onAnomalyAlarmFired(firedAlarmTimestampNs, alarmSet);
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100, acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs - 100,
attributionUids1, attributionTags1, "wl1"); attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, int64_t release_event_time = bucketStartTimeNs + 2 * bucketSizeNs + 1;
attributionUids1, attributionTags1, "wl1"); auto release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1,
processor->OnLogEvent(release_event.get()); attributionTags1, "wl1");
processor->OnLogEvent(release_event.get(), release_event_time);
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
// Within the refractory period. No anomaly. // Within the refractory period. No anomaly.
EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// A new wakelock, but still within refractory period. // A new wakelock, but still within refractory period.
acquire_event = acquire_event = CreateAcquireWakelockEvent(
CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC, roundedBucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC, attributionUids1,
attributionUids1, attributionTags1, "wl1"); attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
anomalyTracker->getAlarmTimestampSec(dimensionKey1)); anomalyTracker->getAlarmTimestampSec(dimensionKey1));
release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC, release_event =
attributionUids1, attributionTags1, "wl1"); CreateReleaseWakelockEvent(roundedBucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC,
attributionUids1, attributionTags1, "wl1");
// Still in the refractory period. No anomaly. // Still in the refractory period. No anomaly.
processor->OnLogEvent(release_event.get()); processor->OnLogEvent(release_event.get());
EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
acquire_event = acquire_event = CreateAcquireWakelockEvent(
CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5, roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 5, attributionUids1,
attributionUids1, attributionTags1, "wl1"); attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC, EXPECT_EQ((roundedBucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC + 1,
anomalyTracker->getAlarmTimestampSec(dimensionKey1)); anomalyTracker->getAlarmTimestampSec(dimensionKey1));
release_event = release_event_time = roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 4;
CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4, release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1,
attributionUids1, attributionTags1, "wl1"); attributionTags1, "wl1");
processor->OnLogEvent(release_event.get()); processor->OnLogEvent(release_event.get(), release_event_time);
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
acquire_event = acquire_event = CreateAcquireWakelockEvent(
CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3, roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 3, attributionUids1,
attributionUids1, attributionTags1, "wl1"); attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get()); processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC, EXPECT_EQ((roundedBucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC + 1,
anomalyTracker->getAlarmTimestampSec(dimensionKey1)); anomalyTracker->getAlarmTimestampSec(dimensionKey1));
} }