Remove alarm manager dependency from anomalyalarms am: 8b914c0f00 am: 9483816bad am: 903198768a

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1455630

Change-Id: I6fe6e1e98ff3dbcd53c34ca1048e94e1e4d3c72e
This commit is contained in:
Tej Singh
2020-10-13 01:58:57 +00:00
committed by Automerger Merge Worker
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,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
for (const auto& itr : mMetricsManagers) {
itr.second->onAnomalyAlarmFired(timestampNs, alarmSet);
}
@@ -431,6 +430,20 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
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();
if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) {
mPullerManager->ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC);
@@ -1092,6 +1105,28 @@ void StatsLogProcessor::noteOnDiskData(const ConfigKey& 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 os
} // namespace android

View File

@@ -66,11 +66,6 @@ public:
const DumpLatency dumpLatency,
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. */
void onPeriodicAlarmFired(
const int64_t& timestampNs,
@@ -148,6 +143,10 @@ public:
// Add a specific config key to the possible configs to dump ASAP.
void noteOnDiskData(const ConfigKey& key);
void setAnomalyAlarm(const int64_t timeMillis);
void cancelAnomalyAlarm();
private:
// For testing only.
inline sp<AlarmMonitor> getAnomalyAlarmMonitor() const {
@@ -160,6 +159,11 @@ private:
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, int64_t> mLastBroadcastTimes;
@@ -250,6 +254,15 @@ private:
// Reset the specified 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
// to retrieve the stored data.
std::function<bool(const ConfigKey& key)> mSendBroadcast;
@@ -276,6 +289,9 @@ private:
//Last time we wrote metadata to disk.
int64_t mLastMetadataWriteNs = 0;
// The time for the next anomaly alarm for alerts.
int64_t mNextAnomalyAlarmTime = 0;
#ifdef VERY_VERBOSE_PRINTING
bool mPrintAllLogs = false;
#endif

View File

@@ -91,17 +91,13 @@ Status checkUid(uid_t expectedUid) {
StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQueue> queue)
: mAnomalyAlarmMonitor(new AlarmMonitor(
MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
[](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) {
if (sc != nullptr) {
sc->setAnomalyAlarm(timeMillis);
StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
}
[this](const shared_ptr<IStatsCompanionService>& /*sc*/, int64_t timeMillis) {
mProcessor->setAnomalyAlarm(timeMillis);
StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
},
[](const shared_ptr<IStatsCompanionService>& sc) {
if (sc != nullptr) {
sc->cancelAnomalyAlarm();
StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
}
[this](const shared_ptr<IStatsCompanionService>& /*sc*/) {
mProcessor->cancelAnomalyAlarm();
StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
})),
mPeriodicAlarmMonitor(new AlarmMonitor(
MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
@@ -977,17 +973,7 @@ Status StatsService::informOnePackageRemoved(const string& app, int32_t uid) {
Status StatsService::informAnomalyAlarmFired() {
ENFORCE_UID(AID_SYSTEM);
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.");
}
// Anomaly alarms are handled internally now. This code should be fully deleted.
return Status::ok();
}

View File

@@ -404,6 +404,10 @@ private:
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket);
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

View File

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