diff --git a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl index 5cdb3249501be..d56a4bd0a8e54 100644 --- a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl +++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl @@ -26,17 +26,6 @@ interface IStatsCompanionService { */ oneway void statsdReady(); - /** - * Register an alarm for anomaly detection to fire at the given timestamp (ms since epoch). - * If anomaly alarm had already been registered, it will be replaced with the new timestamp. - * Uses AlarmManager.set API, so if the timestamp is in the past, alarm fires immediately, and - * alarm is inexact. - */ - oneway void setAnomalyAlarm(long timestampMs); - - /** Cancel any anomaly detection alarm. */ - oneway void cancelAnomalyAlarm(); - /** * Register a repeating alarm for pulling to fire at the given timestamp and every * intervalMs thereafter (in ms since epoch). diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl index 0d3f4208a2ab1..066412a9f157d 100644 --- a/apex/statsd/aidl/android/os/IStatsd.aidl +++ b/apex/statsd/aidl/android/os/IStatsd.aidl @@ -41,13 +41,6 @@ interface IStatsd { */ void statsCompanionReady(); - /** - * Tells statsd that an anomaly may have occurred, so statsd can check whether this is so and - * act accordingly. - * Two-way binder call so that caller's method (and corresponding wakelocks) will linger. - */ - void informAnomalyAlarmFired(); - /** * Tells statsd that it is time to poll some stats. Statsd will be responsible for determing * what stats to poll and initiating the polling. diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index cbc8ed636ff2a..b5e72247a4a35 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -100,7 +100,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private static IStatsd sStatsd; private static final Object sStatsdLock = new Object(); - private final OnAlarmListener mAnomalyAlarmListener; private final OnAlarmListener mPullingAlarmListener; private final OnAlarmListener mPeriodicAlarmListener; @@ -124,7 +123,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { handlerThread.start(); mHandler = new CompanionHandler(handlerThread.getLooper()); - mAnomalyAlarmListener = new AnomalyAlarmListener(context); mPullingAlarmListener = new PullingAlarmListener(context); mPeriodicAlarmListener = new PeriodicAlarmListener(context); } @@ -336,41 +334,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - public static final class AnomalyAlarmListener implements OnAlarmListener { - private final Context mContext; - - AnomalyAlarmListener(Context context) { - mContext = context; - } - - @Override - public void onAlarm() { - if (DEBUG) { - Log.i(TAG, "StatsCompanionService believes an anomaly has occurred at time " - + System.currentTimeMillis() + "ms."); - } - IStatsd statsd = getStatsdNonblocking(); - if (statsd == null) { - Log.w(TAG, "Could not access statsd to inform it of anomaly alarm firing"); - return; - } - - // Wakelock needs to be retained while calling statsd. - Thread thread = new WakelockThread(mContext, - AnomalyAlarmListener.class.getCanonicalName(), new Runnable() { - @Override - public void run() { - try { - statsd.informAnomalyAlarmFired(); - } catch (RemoteException e) { - Log.w(TAG, "Failed to inform statsd of anomaly alarm firing", e); - } - } - }); - thread.start(); - } - } - public final static class PullingAlarmListener implements OnAlarmListener { private final Context mContext; @@ -468,34 +431,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - @Override // Binder call - public void setAnomalyAlarm(long timestampMs) { - StatsCompanion.enforceStatsdCallingUid(); - if (DEBUG) Log.d(TAG, "Setting anomaly alarm for " + timestampMs); - final long callingToken = Binder.clearCallingIdentity(); - try { - // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will - // only fire when it awakens. - // AlarmManager will automatically cancel any previous mAnomalyAlarmListener alarm. - mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".anomaly", - mAnomalyAlarmListener, mHandler); - } finally { - Binder.restoreCallingIdentity(callingToken); - } - } - - @Override // Binder call - public void cancelAnomalyAlarm() { - StatsCompanion.enforceStatsdCallingUid(); - if (DEBUG) Log.d(TAG, "Cancelling anomaly alarm"); - final long callingToken = Binder.clearCallingIdentity(); - try { - mAlarmManager.cancel(mAnomalyAlarmListener); - } finally { - Binder.restoreCallingIdentity(callingToken); - } - } - @Override // Binder call public void setAlarmForSubscriberTriggering(long timestampMs) { StatsCompanion.enforceStatsdCallingUid(); @@ -666,7 +601,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { // instead of in binder death because statsd can come back and set different alarms, or not // want to set an alarm when it had been set. This guarantees that when we get a new statsd, // we cancel any alarms before it is able to set them. - cancelAnomalyAlarm(); cancelPullingAlarm(); cancelAlarmForSubscriberTriggering(); diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index e7b32c56551a0..c10f248f47023 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -120,10 +120,9 @@ static void flushProtoToBuffer(ProtoOutputStream& proto, vector* outDat } } -void StatsLogProcessor::onAnomalyAlarmFired( +void StatsLogProcessor::processFiredAnomalyAlarmsLocked( const int64_t& timestampNs, unordered_set, SpHash> alarmSet) { - std::lock_guard 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 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 lock(mAnomalyAlarmMutex); + mNextAnomalyAlarmTime = elapsedTimeMillis; +} + +void StatsLogProcessor::cancelAnomalyAlarm() { + std::lock_guard lock(mAnomalyAlarmMutex); + mNextAnomalyAlarmTime = 0; +} + +void StatsLogProcessor::informAnomalyAlarmFiredLocked(const int64_t elapsedTimeMillis) { + VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called"); + std::unordered_set, SpHash> alarmSet = + mAnomalyAlarmMonitor->popSoonerThan(static_cast(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 diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 23f2584655b0d..08f46688bbbaa 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -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, SpHash> 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 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> mMetricsManagers; std::unordered_map mLastBroadcastTimes; @@ -250,6 +254,15 @@ private: // Reset the specified configs. void resetConfigsLocked(const int64_t timestampNs, const std::vector& 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, SpHash> alarmSet); + // Function used to send a broadcast so that receiver for the config key can call getData // to retrieve the stored data. std::function 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 diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 322648229d0ec..368a6c40c7461 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -91,17 +91,13 @@ Status checkUid(uid_t expectedUid) { StatsService::StatsService(const sp& handlerLooper, shared_ptr queue) : mAnomalyAlarmMonitor(new AlarmMonitor( MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, - [](const shared_ptr& sc, int64_t timeMillis) { - if (sc != nullptr) { - sc->setAnomalyAlarm(timeMillis); - StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); - } + [this](const shared_ptr& /*sc*/, int64_t timeMillis) { + mProcessor->setAnomalyAlarm(timeMillis); + StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); }, - [](const shared_ptr& sc) { - if (sc != nullptr) { - sc->cancelAnomalyAlarm(); - StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); - } + [this](const shared_ptr& /*sc*/) { + mProcessor->cancelAnomalyAlarm(); + StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); })), mPeriodicAlarmMonitor(new AlarmMonitor( MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, @@ -975,22 +971,6 @@ Status StatsService::informOnePackageRemoved(const string& app, int32_t uid) { return Status::ok(); } -Status StatsService::informAnomalyAlarmFired() { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::informAnomalyAlarmFired was called"); - int64_t currentTimeSec = getElapsedRealtimeSec(); - std::unordered_set, SpHash> alarmSet = - mAnomalyAlarmMonitor->popSoonerThan(static_cast(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(); -} - Status StatsService::informAlarmForSubscriberTriggeringFired() { ENFORCE_UID(AID_SYSTEM); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 324ffbd65e511..479f4e87ec961 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -66,7 +66,6 @@ public: virtual Status systemRunning(); virtual Status statsCompanionReady(); virtual Status bootCompleted(); - virtual Status informAnomalyAlarmFired(); virtual Status informPollAlarmFired(); virtual Status informAlarmForSubscriberTriggeringFired(); @@ -404,6 +403,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 diff --git a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp index 95e301002a1b8..70e7365ec2389 100644 --- a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp @@ -12,14 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include +#include #include -#include "src/anomaly/DurationAnomalyTracker.h" +#include + #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 +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& service, const StatsdConfig& config) { + string str; + config.SerializeToString(&str); + std::vector 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 service = SharedRefBase::make(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 = 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(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(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 service = SharedRefBase::make(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 = 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 service = SharedRefBase::make(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 = 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(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)); }