From 932ececa1674c59a8da9f3e32d2651e781b86fc4 Mon Sep 17 00:00:00 2001 From: Yangster-mac Date: Thu, 1 Feb 2018 10:23:52 -0800 Subject: [PATCH] Alarm: wakes up statsd and notifies the subscribers. Test: manually tested it. Change-Id: Id796a68976aeb1611183023ba4e9c6a8b8c44bb8 --- cmds/statsd/Android.mk | 7 +- cmds/statsd/src/HashableDimensionKey.h | 2 +- cmds/statsd/src/StatsLogProcessor.cpp | 24 +++- cmds/statsd/src/StatsLogProcessor.h | 16 ++- cmds/statsd/src/StatsService.cpp | 110 +++++++++++++----- cmds/statsd/src/StatsService.h | 13 ++- .../{AnomalyMonitor.cpp => AlarmMonitor.cpp} | 45 ++++--- .../{AnomalyMonitor.h => AlarmMonitor.h} | 34 +++--- cmds/statsd/src/anomaly/AlarmTracker.cpp | 79 +++++++++++++ cmds/statsd/src/anomaly/AlarmTracker.h | 76 ++++++++++++ cmds/statsd/src/anomaly/AnomalyTracker.cpp | 36 +----- cmds/statsd/src/anomaly/AnomalyTracker.h | 17 ++- .../src/anomaly/DurationAnomalyTracker.cpp | 25 ++-- .../src/anomaly/DurationAnomalyTracker.h | 19 ++- cmds/statsd/src/anomaly/subscriber_util.cpp | 75 ++++++++++++ cmds/statsd/src/anomaly/subscriber_util.h | 34 ++++++ cmds/statsd/src/external/Perfetto.h | 2 + cmds/statsd/src/guardrail/StatsdStats.cpp | 20 ++++ cmds/statsd/src/guardrail/StatsdStats.h | 8 ++ .../src/metrics/DurationMetricProducer.cpp | 6 +- .../src/metrics/DurationMetricProducer.h | 3 +- cmds/statsd/src/metrics/MetricProducer.h | 3 +- cmds/statsd/src/metrics/MetricsManager.cpp | 29 +++-- cmds/statsd/src/metrics/MetricsManager.h | 15 ++- .../src/metrics/metrics_manager_util.cpp | 76 ++++++++++-- .../statsd/src/metrics/metrics_manager_util.h | 14 ++- cmds/statsd/src/stats_log.proto | 2 +- cmds/statsd/src/storage/StorageManager.cpp | 14 +++ cmds/statsd/src/storage/StorageManager.h | 5 + .../src/subscriber/IncidentdReporter.cpp | 6 +- .../statsd/src/subscriber/IncidentdReporter.h | 2 +- ...Monitor_test.cpp => AlarmMonitor_test.cpp} | 21 ++-- cmds/statsd/tests/ConfigManager_test.cpp | 8 +- cmds/statsd/tests/MetricsManager_test.cpp | 56 +++++++-- cmds/statsd/tests/StatsLogProcessor_test.cpp | 27 +++-- cmds/statsd/tests/UidMap_test.cpp | 6 +- .../tests/anomaly/AlarmTracker_test.cpp | 68 +++++++++++ .../metrics/CountMetricProducer_test.cpp | 6 +- .../metrics/DurationMetricProducer_test.cpp | 3 +- .../metrics/GaugeMetricProducer_test.cpp | 7 +- .../tests/metrics/MaxDurationTracker_test.cpp | 14 ++- .../metrics/OringDurationTracker_test.cpp | 16 ++- .../metrics/ValueMetricProducer_test.cpp | 3 +- cmds/statsd/tests/statsd_test_util.cpp | 5 +- .../android/os/IStatsCompanionService.aidl | 14 ++- core/java/android/os/IStatsManager.aidl | 7 ++ .../server/stats/StatsCompanionService.java | 55 ++++++++- 47 files changed, 896 insertions(+), 237 deletions(-) rename cmds/statsd/src/anomaly/{AnomalyMonitor.cpp => AlarmMonitor.cpp} (73%) rename cmds/statsd/src/anomaly/{AnomalyMonitor.h => AlarmMonitor.h} (77%) create mode 100644 cmds/statsd/src/anomaly/AlarmTracker.cpp create mode 100644 cmds/statsd/src/anomaly/AlarmTracker.h create mode 100644 cmds/statsd/src/anomaly/subscriber_util.cpp create mode 100644 cmds/statsd/src/anomaly/subscriber_util.h rename cmds/statsd/tests/{AnomalyMonitor_test.cpp => AlarmMonitor_test.cpp} (70%) create mode 100644 cmds/statsd/tests/anomaly/AlarmTracker_test.cpp diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 87825f1619702..21d5bcf96110b 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -21,9 +21,11 @@ statsd_common_src := \ src/statsd_config.proto \ src/FieldValue.cpp \ src/stats_log_util.cpp \ - src/anomaly/AnomalyMonitor.cpp \ + src/anomaly/AlarmMonitor.cpp \ + src/anomaly/AlarmTracker.cpp \ src/anomaly/AnomalyTracker.cpp \ src/anomaly/DurationAnomalyTracker.cpp \ + src/anomaly/subscriber_util.cpp \ src/condition/CombinationConditionTracker.cpp \ src/condition/condition_util.cpp \ src/condition/SimpleConditionTracker.cpp \ @@ -170,7 +172,8 @@ LOCAL_SRC_FILES := \ src/atom_field_options.proto \ src/atoms.proto \ src/stats_log.proto \ - tests/AnomalyMonitor_test.cpp \ + tests/AlarmMonitor_test.cpp \ + tests/anomaly/AlarmTracker_test.cpp \ tests/anomaly/AnomalyTracker_test.cpp \ tests/ConfigManager_test.cpp \ tests/external/puller_util_test.cpp \ diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h index 89fe317834d81..5d016e9f28049 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -40,7 +40,7 @@ public: mValues = values; } - HashableDimensionKey(){}; + HashableDimensionKey() {}; HashableDimensionKey(const HashableDimensionKey& that) : mValues(that.getValues()){}; diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 87dec5d1656d2..a4c9d1845461d 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -66,11 +66,13 @@ const int FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS = 4; #define STATS_DATA_DIR "/data/misc/stats-data" StatsLogProcessor::StatsLogProcessor(const sp& uidMap, - const sp& anomalyMonitor, + const sp& anomalyAlarmMonitor, + const sp& periodicAlarmMonitor, const long timeBaseSec, const std::function& sendBroadcast) : mUidMap(uidMap), - mAnomalyMonitor(anomalyMonitor), + mAnomalyAlarmMonitor(anomalyAlarmMonitor), + mPeriodicAlarmMonitor(periodicAlarmMonitor), mSendBroadcast(sendBroadcast), mTimeBaseSec(timeBaseSec) { StatsPullerManager statsPullerManager; @@ -82,10 +84,19 @@ StatsLogProcessor::~StatsLogProcessor() { void StatsLogProcessor::onAnomalyAlarmFired( const uint64_t timestampNs, - unordered_set, SpHash> anomalySet) { + unordered_set, SpHash> alarmSet) { std::lock_guard lock(mMetricsMutex); for (const auto& itr : mMetricsManagers) { - itr.second->onAnomalyAlarmFired(timestampNs, anomalySet); + itr.second->onAnomalyAlarmFired(timestampNs, alarmSet); + } +} +void StatsLogProcessor::onPeriodicAlarmFired( + const uint64_t timestampNs, + unordered_set, SpHash> alarmSet) { + + std::lock_guard lock(mMetricsMutex); + for (const auto& itr : mMetricsManagers) { + itr.second->onPeriodicAlarmFired(timestampNs, alarmSet); } } @@ -170,7 +181,9 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) { std::lock_guard lock(mMetricsMutex); VLOG("Updated configuration for key %s", key.ToString().c_str()); - sp newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap); + sp newMetricsManager = + new MetricsManager(key, config, mTimeBaseSec, mUidMap, + mAnomalyAlarmMonitor, mPeriodicAlarmMonitor); auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) { ALOGE("Can't accept more configs!"); @@ -179,7 +192,6 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig if (newMetricsManager->isConfigValid()) { mUidMap->OnConfigUpdated(key); - newMetricsManager->setAnomalyMonitor(mAnomalyMonitor); if (newMetricsManager->shouldAddUidMapListener()) { // We have to add listener after the MetricsManager is constructed because it's // not safe to create wp or sp from this pointer inside its constructor. diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 144430639d9f4..4d9f18509ddba 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -34,7 +34,8 @@ namespace statsd { class StatsLogProcessor : public ConfigListener { public: - StatsLogProcessor(const sp& uidMap, const sp& anomalyMonitor, + StatsLogProcessor(const sp& uidMap, const sp& anomalyAlarmMonitor, + const sp& subscriberTriggerAlarmMonitor, const long timeBaseSec, const std::function& sendBroadcast); virtual ~StatsLogProcessor(); @@ -48,10 +49,15 @@ public: void onDumpReport(const ConfigKey& key, const uint64_t dumpTimeNs, vector* outData); - /* Tells MetricsManager that the alarms in anomalySet have fired. Modifies anomalySet. */ + /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */ void onAnomalyAlarmFired( const uint64_t timestampNs, - unordered_set, SpHash> anomalySet); + unordered_set, SpHash> alarmSet); + + /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies periodic alarmSet. */ + void onPeriodicAlarmFired( + const uint64_t timestampNs, + unordered_set, SpHash> alarmSet); /* Flushes data to disk. Data on memory will be gone after written to disk. */ void WriteDataToDisk(); @@ -76,7 +82,9 @@ private: StatsPullerManager mStatsPullerManager; - sp mAnomalyMonitor; + sp mAnomalyAlarmMonitor; + + sp mPeriodicAlarmMonitor; void onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeNs, vector* outData); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 26db3af7d41a6..573b7c9e8eb2d 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -50,49 +50,73 @@ namespace statsd { constexpr const char* kPermissionDump = "android.permission.DUMP"; #define STATS_SERVICE_DIR "/data/misc/stats-service" -// ====================================================================== /** * Watches for the death of the stats companion (system process). */ class CompanionDeathRecipient : public IBinder::DeathRecipient { public: - CompanionDeathRecipient(const sp& anomalyMonitor); + CompanionDeathRecipient(const sp& anomalyAlarmMonitor, + const sp& periodicAlarmMonitor) : + mAnomalyAlarmMonitor(anomalyAlarmMonitor), + mPeriodicAlarmMonitor(periodicAlarmMonitor) {} virtual void binderDied(const wp& who); private: - const sp mAnomalyMonitor; + sp mAnomalyAlarmMonitor; + sp mPeriodicAlarmMonitor; }; -CompanionDeathRecipient::CompanionDeathRecipient(const sp& anomalyMonitor) - : mAnomalyMonitor(anomalyMonitor) { -} - void CompanionDeathRecipient::binderDied(const wp& who) { ALOGW("statscompanion service died"); - mAnomalyMonitor->setStatsCompanionService(nullptr); + mAnomalyAlarmMonitor->setStatsCompanionService(nullptr); + mPeriodicAlarmMonitor->setStatsCompanionService(nullptr); SubscriberReporter::getInstance().setStatsCompanionService(nullptr); } -// ====================================================================== StatsService::StatsService(const sp& handlerLooper) - : mAnomalyMonitor(new AnomalyMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS)) -{ + : mAnomalyAlarmMonitor(new AlarmMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, + [](const sp& sc, int64_t timeMillis) { + if (sc != nullptr) { + sc->setAnomalyAlarm(timeMillis); + StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); + } + }, + [](const sp& sc) { + if (sc != nullptr) { + sc->cancelAnomalyAlarm(); + StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); + } + })), + mPeriodicAlarmMonitor(new AlarmMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, + [](const sp& sc, int64_t timeMillis) { + if (sc != nullptr) { + sc->setAlarmForSubscriberTriggering(timeMillis); + StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged(); + } + }, + [](const sp& sc) { + if (sc != nullptr) { + sc->cancelAlarmForSubscriberTriggering(); + StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged(); + } + + })) { mUidMap = new UidMap(); StatsPuller::SetUidMap(mUidMap); mConfigManager = new ConfigManager(); - mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, getElapsedRealtimeSec(), - [this](const ConfigKey& key) { - sp sc = getStatsCompanionService(); - auto receiver = mConfigManager->GetConfigReceiver(key); - if (sc == nullptr) { - VLOG("Could not find StatsCompanionService"); - } else if (receiver == nullptr) { - VLOG("Statscompanion could not find a broadcast receiver for %s", - key.ToString().c_str()); - } else { - sc->sendDataBroadcast(receiver); - } + mProcessor = new StatsLogProcessor(mUidMap, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor, + getElapsedRealtimeSec(), [this](const ConfigKey& key) { + sp sc = getStatsCompanionService(); + auto receiver = mConfigManager->GetConfigReceiver(key); + if (sc == nullptr) { + VLOG("Could not find StatsCompanionService"); + } else if (receiver == nullptr) { + VLOG("Statscompanion could not find a broadcast receiver for %s", + key.ToString().c_str()); + } else { + sc->sendDataBroadcast(receiver); } + } ); mConfigManager->AddListener(mProcessor); @@ -615,7 +639,8 @@ status_t StatsService::cmd_dump_memory_info(FILE* out) { status_t StatsService::cmd_clear_puller_cache(FILE* out) { IPCThreadState* ipc = IPCThreadState::self(); - VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid()); + VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i", + ipc->getCallingPid(), ipc->getCallingUid()); if (checkCallingPermission(String16(kPermissionDump))) { int cleared = mStatsPullerManager.ForceClearPullerCache(); fprintf(out, "Puller removed %d cached data!\n", cleared); @@ -670,18 +695,40 @@ Status StatsService::informAnomalyAlarmFired() { return Status::fromExceptionCode(Status::EX_SECURITY, "Only system uid can call informAnomalyAlarmFired"); } + uint64_t currentTimeSec = getElapsedRealtimeSec(); - std::unordered_set, SpHash> anomalySet = - mAnomalyMonitor->popSoonerThan(static_cast(currentTimeSec)); - if (anomalySet.size() > 0) { + 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, anomalySet); + 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() { + VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called"); + + if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { + return Status::fromExceptionCode( + Status::EX_SECURITY, + "Only system uid can call informAlarmForSubscriberTriggeringFired"); + } + + uint64_t currentTimeSec = time(nullptr); + std::unordered_set, SpHash> alarmSet = + mPeriodicAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); + if (alarmSet.size() > 0) { + VLOG("Found periodic alarm fired."); + mProcessor->onPeriodicAlarmFired(currentTimeSec * NS_PER_SEC, alarmSet); + } else { + ALOGW("Cannot find an periodic alarm that fired. Perhaps it was recently cancelled."); + } + return Status::ok(); +} + Status StatsService::informPollAlarmFired() { VLOG("StatsService::informPollAlarmFired was called"); @@ -766,10 +813,11 @@ Status StatsService::statsCompanionReady() { "statscompanion unavailable despite it contacting statsd!"); } VLOG("StatsService::statsCompanionReady linking to statsCompanion."); - IInterface::asBinder(statsCompanion)->linkToDeath(new CompanionDeathRecipient(mAnomalyMonitor)); - mAnomalyMonitor->setStatsCompanionService(statsCompanion); + IInterface::asBinder(statsCompanion)->linkToDeath( + new CompanionDeathRecipient(mAnomalyAlarmMonitor, mPeriodicAlarmMonitor)); + mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion); + mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion); SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion); - return Status::ok(); } diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 9690de702c248..e0a1299a9c384 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -18,7 +18,7 @@ #define STATS_SERVICE_H #include "StatsLogProcessor.h" -#include "anomaly/AnomalyMonitor.h" +#include "anomaly/AlarmMonitor.h" #include "config/ConfigManager.h" #include "external/StatsPullerManager.h" #include "packages/UidMap.h" @@ -58,6 +58,8 @@ public: virtual Status statsCompanionReady(); virtual Status informAnomalyAlarmFired(); virtual Status informPollAlarmFired(); + virtual Status informAlarmForSubscriberTriggeringFired(); + virtual Status informAllUidData(const vector& uid, const vector& version, const vector& app); virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version); @@ -244,9 +246,14 @@ private: sp mProcessor; /** - * The anomaly detector. + * The alarm monitor for anomaly detection. */ - const sp mAnomalyMonitor; + const sp mAnomalyAlarmMonitor; + + /** + * The alarm monitor for alarms to directly trigger subscriber. + */ + const sp mPeriodicAlarmMonitor; /** * Whether this is an eng build. diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp similarity index 73% rename from cmds/statsd/src/anomaly/AnomalyMonitor.cpp rename to cmds/statsd/src/anomaly/AlarmMonitor.cpp index ca34dc6d87fd3..78f0c2b095377 100644 --- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp +++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp @@ -17,21 +17,24 @@ #define DEBUG false #include "Log.h" -#include "anomaly/AnomalyMonitor.h" +#include "anomaly/AlarmMonitor.h" #include "guardrail/StatsdStats.h" namespace android { namespace os { namespace statsd { -AnomalyMonitor::AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec) - : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec) { -} +AlarmMonitor::AlarmMonitor( + uint32_t minDiffToUpdateRegisteredAlarmTimeSec, + const std::function&, int64_t)>& updateAlarm, + const std::function&)>& cancelAlarm) + : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec), + mUpdateAlarm(updateAlarm), + mCancelAlarm(cancelAlarm) {} -AnomalyMonitor::~AnomalyMonitor() { -} +AlarmMonitor::~AlarmMonitor() {} -void AnomalyMonitor::setStatsCompanionService(sp statsCompanionService) { +void AlarmMonitor::setStatsCompanionService(sp statsCompanionService) { std::lock_guard lock(mLock); sp tmpForLock = mStatsCompanionService; mStatsCompanionService = statsCompanionService; @@ -40,13 +43,13 @@ void AnomalyMonitor::setStatsCompanionService(sp statsCo return; } VLOG("Creating link to statsCompanionService"); - const sp top = mPq.top(); + const sp top = mPq.top(); if (top != nullptr) { updateRegisteredAlarmTime_l(top->timestampSec); } } -void AnomalyMonitor::add(sp alarm) { +void AlarmMonitor::add(sp alarm) { std::lock_guard lock(mLock); if (alarm == nullptr) { ALOGW("Asked to add a null alarm."); @@ -66,7 +69,7 @@ void AnomalyMonitor::add(sp alarm) { } } -void AnomalyMonitor::remove(sp alarm) { +void AlarmMonitor::remove(sp alarm) { std::lock_guard lock(mLock); if (alarm == nullptr) { ALOGW("Asked to remove a null alarm."); @@ -89,13 +92,13 @@ void AnomalyMonitor::remove(sp alarm) { // More efficient than repeatedly calling remove(mPq.top()) since it batches the // updates to the registered alarm. -unordered_set, SpHash> AnomalyMonitor::popSoonerThan( +unordered_set, SpHash> AlarmMonitor::popSoonerThan( uint32_t timestampSec) { VLOG("Removing alarms with time <= %u", timestampSec); - unordered_set, SpHash> oldAlarms; + unordered_set, SpHash> oldAlarms; std::lock_guard lock(mLock); - for (sp t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec; + for (sp t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec; t = mPq.top()) { oldAlarms.insert(t); mPq.pop(); // remove t @@ -113,25 +116,19 @@ unordered_set, SpHash> AnomalyMonitor::popS return oldAlarms; } -void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) { +void AlarmMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) { VLOG("Updating reg alarm time to %u", timestampSec); mRegisteredAlarmTimeSec = timestampSec; - if (mStatsCompanionService != nullptr) { - mStatsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec)); - StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); - } + mUpdateAlarm(mStatsCompanionService, secToMs(mRegisteredAlarmTimeSec)); } -void AnomalyMonitor::cancelRegisteredAlarmTime_l() { +void AlarmMonitor::cancelRegisteredAlarmTime_l() { VLOG("Cancelling reg alarm."); mRegisteredAlarmTimeSec = 0; - if (mStatsCompanionService != nullptr) { - mStatsCompanionService->cancelAnomalyAlarm(); - StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); - } + mCancelAlarm(mStatsCompanionService); } -int64_t AnomalyMonitor::secToMs(uint32_t timeSec) { +int64_t AlarmMonitor::secToMs(uint32_t timeSec) { return ((int64_t)timeSec) * 1000; } diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h similarity index 77% rename from cmds/statsd/src/anomaly/AnomalyMonitor.h rename to cmds/statsd/src/anomaly/AlarmMonitor.h index 7acc7904bb570..3badb1faece62 100644 --- a/cmds/statsd/src/anomaly/AnomalyMonitor.h +++ b/cmds/statsd/src/anomaly/AlarmMonitor.h @@ -41,33 +41,34 @@ namespace statsd { * threshold. * Timestamps are in seconds since epoch in a uint32, so will fail in year 2106. */ -struct AnomalyAlarm : public RefBase { - AnomalyAlarm(uint32_t timestampSec) : timestampSec(timestampSec) { +struct InternalAlarm : public RefBase { + InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) { } const uint32_t timestampSec; - /** AnomalyAlarm a is smaller (higher priority) than b if its timestamp is sooner. */ + /** InternalAlarm a is smaller (higher priority) than b if its timestamp is sooner. */ struct SmallerTimestamp { - bool operator()(sp a, sp b) const { + bool operator()(sp a, sp b) const { return (a->timestampSec < b->timestampSec); } }; }; -// TODO: Rename this file to AnomalyAlarmMonitor. /** - * Manages alarms for Anomaly Detection. + * Manages internal alarms that may get registered with the AlarmManager. */ -class AnomalyMonitor : public RefBase { +class AlarmMonitor : public RefBase { public: /** * @param minDiffToUpdateRegisteredAlarmTimeSec If the soonest alarm differs * from the registered alarm by more than this amount, update the registered * alarm. */ - AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec); - ~AnomalyMonitor(); + AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec, + const std::function&, int64_t)>& updateAlarm, + const std::function&)>& cancelAlarm); + ~AlarmMonitor(); /** * Tells AnomalyMonitor what IStatsCompanionService to use and, if @@ -80,20 +81,20 @@ public: /** * Adds the given alarm (reference) to the queue. */ - void add(sp alarm); + void add(sp alarm); /** * Removes the given alarm (reference) from the queue. * Note that alarm comparison is reference-based; if another alarm exists * with the same timestampSec, that alarm will still remain in the queue. */ - void remove(sp alarm); + void remove(sp alarm); /** * Returns and removes all alarms whose timestamp <= the given timestampSec. * Always updates the registered alarm if return is non-empty. */ - unordered_set, SpHash> popSoonerThan( + unordered_set, SpHash> popSoonerThan( uint32_t timestampSec); /** @@ -119,7 +120,7 @@ private: /** * Priority queue of alarms, prioritized by soonest alarm.timestampSec. */ - indexed_priority_queue mPq; + indexed_priority_queue mPq; /** * Binder interface for communicating with StatsCompanionService. @@ -146,6 +147,13 @@ private: /** Converts uint32 timestamp in seconds to a Java long in msec. */ int64_t secToMs(uint32_t timeSec); + + // Callback function to update the alarm via StatsCompanionService. + std::function, int64_t)> mUpdateAlarm; + + // Callback function to cancel the alarm via StatsCompanionService. + std::function)> mCancelAlarm; + }; } // namespace statsd diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp new file mode 100644 index 0000000000000..eb283838afd76 --- /dev/null +++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG true // STOPSHIP if true +#include "Log.h" + +#include "anomaly/AlarmTracker.h" +#include "anomaly/subscriber_util.h" +#include "HashableDimensionKey.h" +#include "stats_util.h" +#include "storage/StorageManager.h" + +#include +#include + +namespace android { +namespace os { +namespace statsd { + +AlarmTracker::AlarmTracker(uint64_t startMillis, + const Alarm& alarm, const ConfigKey& configKey, + const sp& alarmMonitor) + : mAlarmConfig(alarm), + mConfigKey(configKey), + mAlarmMonitor(alarmMonitor) { + VLOG("AlarmTracker() called"); + mAlarmSec = (startMillis + mAlarmConfig.offset_millis()) / MS_PER_SEC; + mInternalAlarm = new InternalAlarm{static_cast(mAlarmSec)}; + mAlarmMonitor->add(mInternalAlarm); +} + +AlarmTracker::~AlarmTracker() { + VLOG("~AlarmTracker() called"); + if (mInternalAlarm != nullptr) { + mAlarmMonitor->remove(mInternalAlarm); + } +} + +void AlarmTracker::addSubscription(const Subscription& subscription) { + mSubscriptions.push_back(subscription); +} + +uint64_t AlarmTracker::findNextAlarmSec(uint64_t currentTimeSec) { + int periodsForward = (currentTimeSec - mAlarmSec) * MS_PER_SEC / mAlarmConfig.period_millis(); + return mAlarmSec + (periodsForward + 1) * mAlarmConfig.period_millis() / MS_PER_SEC; +} + +void AlarmTracker::informAlarmsFired( + const uint64_t& timestampNs, + unordered_set, SpHash>& firedAlarms) { + if (firedAlarms.empty() || firedAlarms.find(mInternalAlarm) == firedAlarms.end()) { + return; + } + if (!mSubscriptions.empty()) { + triggerSubscribers(mAlarmConfig.id(), DEFAULT_METRIC_DIMENSION_KEY, mConfigKey, + mSubscriptions); + } + firedAlarms.erase(mInternalAlarm); + mAlarmSec = findNextAlarmSec(timestampNs / NS_PER_SEC); + mInternalAlarm = new InternalAlarm{static_cast(mAlarmSec)}; + mAlarmMonitor->add(mInternalAlarm); +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h new file mode 100644 index 0000000000000..d59dacaa1b698 --- /dev/null +++ b/cmds/statsd/src/anomaly/AlarmTracker.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "AlarmMonitor.h" +#include "config/ConfigKey.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alarm + +#include +#include +#include + +using android::os::IStatsCompanionService; + +namespace android { +namespace os { +namespace statsd { + +class AlarmTracker : public virtual RefBase { +public: + AlarmTracker(uint64_t startMillis, + const Alarm& alarm, const ConfigKey& configKey, + const sp& subscriberAlarmMonitor); + + virtual ~AlarmTracker(); + + void onAlarmFired(); + + void addSubscription(const Subscription& subscription); + + void informAlarmsFired(const uint64_t& timestampNs, + unordered_set, SpHash>& firedAlarms); + +protected: + uint64_t findNextAlarmSec(uint64_t currentTimeMillis); + + // statsd_config.proto Alarm message that defines this tracker. + const Alarm mAlarmConfig; + + // A reference to the Alarm's config key. + const ConfigKey& mConfigKey; + + // The subscriptions that depend on this alarm. + std::vector mSubscriptions; + + // Alarm monitor. + sp mAlarmMonitor; + + // The current expected alarm time in seconds. + uint64_t mAlarmSec; + + // The current alarm. + sp mInternalAlarm; + + FRIEND_TEST(AlarmTrackerTest, TestTriggerTimestamp); +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index c40eb812f949e..642604e17bf5f 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "AnomalyTracker.h" +#include "subscriber_util.h" #include "external/Perfetto.h" #include "guardrail/StatsdStats.h" #include "subscriber/IncidentdReporter.h" @@ -231,40 +232,7 @@ bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs, } void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) { - VLOG("informSubscribers called."); - if (mSubscriptions.empty()) { - // The config just wanted to log the anomaly. That's fine. - VLOG("No Subscriptions were associated with the alert."); - return; - } - - for (const Subscription& subscription : mSubscriptions) { - if (subscription.probability_of_informing() < 1 - && ((float)rand() / RAND_MAX) >= subscription.probability_of_informing()) { - // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always. - // The config writer was advised to use -0.1 and 1.1 for never/always. - ALOGI("Fate decided that a subscriber would not be informed."); - continue; - } - switch (subscription.subscriber_information_case()) { - case Subscription::SubscriberInformationCase::kIncidentdDetails: - if (!GenerateIncidentReport(subscription.incidentd_details(), mAlert, mConfigKey)) { - ALOGW("Failed to generate incident report."); - } - break; - case Subscription::SubscriberInformationCase::kPerfettoDetails: - if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details())) { - ALOGW("Failed to generate prefetto traces."); - } - break; - case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails: - SubscriberReporter::getInstance().alertBroadcastSubscriber(mConfigKey, subscription, - key); - break; - default: - break; - } - } + triggerSubscribers(mAlert.id(), key, mConfigKey, mSubscriptions); } } // namespace statsd diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h index 3be959d14109e..e3f493cfe7cf0 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ b/cmds/statsd/src/anomaly/AnomalyTracker.h @@ -23,7 +23,7 @@ #include #include -#include "AnomalyMonitor.h" +#include "AlarmMonitor.h" #include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert #include "stats_util.h" // HashableDimensionKey and DimToValMap @@ -64,9 +64,9 @@ public: void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, const MetricDimensionKey& key, const int64_t& currentBucketValue); - // Init the AnomalyMonitor which is shared across anomaly trackers. - virtual void setAnomalyMonitor(const sp& anomalyMonitor) { - return; // Base AnomalyTracker class has no need for the AnomalyMonitor. + // Init the AlarmMonitor which is shared across anomaly trackers. + virtual void setAlarmMonitor(const sp& alarmMonitor) { + return; // Base AnomalyTracker class has no need for the AlarmMonitor. } // Helper function to return the sum value of past buckets at given dimension. @@ -92,11 +92,10 @@ public: } // Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker, - // and removes it from firedAlarms. Does NOT remove the alarm from the AnomalyMonitor. - virtual void informAlarmsFired( - const uint64_t& timestampNs, - unordered_set, SpHash>& firedAlarms) { - return; // The base AnomalyTracker class doesn't have alarms. + // and removes it from firedAlarms. Does NOT remove the alarm from the AlarmMonitor. + virtual void informAlarmsFired(const uint64_t& timestampNs, + unordered_set, SpHash>& firedAlarms) { + return; // The base AnomalyTracker class doesn't have alarms. } protected: diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp index 3ba943c310bbd..31d50be7ec262 100644 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp @@ -24,8 +24,9 @@ namespace android { namespace os { namespace statsd { -DurationAnomalyTracker::DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey) - : AnomalyTracker(alert, configKey) { +DurationAnomalyTracker::DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey, + const sp& alarmMonitor) + : AnomalyTracker(alert, configKey), mAlarmMonitor(alarmMonitor) { } DurationAnomalyTracker::~DurationAnomalyTracker() { @@ -59,10 +60,10 @@ void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey, VLOG("Setting a delayed anomaly alarm lest it fall in the refractory period"); timestampSec = getRefractoryPeriodEndsSec(dimensionKey) + 1; } - sp alarm = new AnomalyAlarm{timestampSec}; + sp alarm = new InternalAlarm{timestampSec}; mAlarms.insert({dimensionKey, alarm}); - if (mAnomalyMonitor != nullptr) { - mAnomalyMonitor->add(alarm); + if (mAlarmMonitor != nullptr) { + mAlarmMonitor->add(alarm); } } @@ -70,8 +71,8 @@ void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey) { auto itr = mAlarms.find(dimensionKey); if (itr != mAlarms.end()) { mAlarms.erase(dimensionKey); - if (mAnomalyMonitor != nullptr) { - mAnomalyMonitor->remove(itr->second); + if (mAlarmMonitor != nullptr) { + mAlarmMonitor->remove(itr->second); } } } @@ -86,16 +87,16 @@ void DurationAnomalyTracker::stopAllAlarms() { } } -void DurationAnomalyTracker::informAlarmsFired( - const uint64_t& timestampNs, - unordered_set, SpHash>& firedAlarms) { +void DurationAnomalyTracker::informAlarmsFired(const uint64_t& timestampNs, + unordered_set, SpHash>& firedAlarms) { + if (firedAlarms.empty() || mAlarms.empty()) return; // Find the intersection of firedAlarms and mAlarms. // The for loop is inefficient, since it loops over all keys, but that's okay since it is very - // seldomly called. The alternative would be having AnomalyAlarms store information about the + // seldomly called. The alternative would be having InternalAlarms store information about the // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that // is rarely ever called. - unordered_map> matchedAlarms; + unordered_map> matchedAlarms; for (const auto& kv : mAlarms) { if (firedAlarms.count(kv.second) > 0) { matchedAlarms.insert({kv.first, kv.second}); diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h index 15aef29bc644c..51186df3e93d9 100644 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h +++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h @@ -16,7 +16,7 @@ #pragma once -#include "AnomalyMonitor.h" +#include "AlarmMonitor.h" #include "AnomalyTracker.h" namespace android { @@ -27,7 +27,8 @@ using std::unordered_map; class DurationAnomalyTracker : public virtual AnomalyTracker { public: - DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey); + DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey, + const sp& alarmMonitor); virtual ~DurationAnomalyTracker(); @@ -40,11 +41,6 @@ public: // Stop all the alarms owned by this tracker. void stopAllAlarms(); - // Init the AnomalyMonitor which is shared across anomaly trackers. - void setAnomalyMonitor(const sp& anomalyMonitor) override { - mAnomalyMonitor = anomalyMonitor; - } - // Declares the anomaly when the alarm expired given the current timestamp. void declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey, const uint64_t& timestampNs); @@ -53,17 +49,16 @@ public: // and removes it from firedAlarms. // Note that this will generally be called from a different thread from the other functions; // the caller is responsible for thread safety. - void informAlarmsFired( - const uint64_t& timestampNs, - unordered_set, SpHash>& firedAlarms) override; + void informAlarmsFired(const uint64_t& timestampNs, + unordered_set, SpHash>& firedAlarms) override; protected: // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they // are still active. - std::unordered_map> mAlarms; + std::unordered_map> mAlarms; // Anomaly alarm monitor. - sp mAnomalyMonitor; + sp mAlarmMonitor; // Resets all bucket data. For use when all the data gets stale. void resetStorage() override; diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp new file mode 100644 index 0000000000000..e796d19efd935 --- /dev/null +++ b/cmds/statsd/src/anomaly/subscriber_util.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG true // STOPSHIP if true +#include "Log.h" + +#include +#include +#include + +#include "external/Perfetto.h" +#include "frameworks/base/libs/incident/proto/android/os/header.pb.h" +#include "subscriber/IncidentdReporter.h" +#include "subscriber/SubscriberReporter.h" + +namespace android { +namespace os { +namespace statsd { + +void triggerSubscribers(const int64_t rule_id, + const MetricDimensionKey& dimensionKey, + const ConfigKey& configKey, + const std::vector& subscriptions) { + VLOG("informSubscribers called."); + if (subscriptions.empty()) { + VLOG("No Subscriptions were associated."); + return; + } + + for (const Subscription& subscription : subscriptions) { + if (subscription.probability_of_informing() < 1 + && ((float)rand() / RAND_MAX) >= subscription.probability_of_informing()) { + // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always. + // The config writer was advised to use -0.1 and 1.1 for never/always. + ALOGI("Fate decided that a subscriber would not be informed."); + continue; + } + switch (subscription.subscriber_information_case()) { + case Subscription::SubscriberInformationCase::kIncidentdDetails: + if (!GenerateIncidentReport(subscription.incidentd_details(), rule_id, configKey)) { + ALOGW("Failed to generate incident report."); + } + break; + case Subscription::SubscriberInformationCase::kPerfettoDetails: + if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details())) { + ALOGW("Failed to generate prefetto traces."); + } + break; + case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails: + SubscriberReporter::getInstance().alertBroadcastSubscriber(configKey, subscription, + dimensionKey); + break; + default: + break; + } + } +} + + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/anomaly/subscriber_util.h b/cmds/statsd/src/anomaly/subscriber_util.h new file mode 100644 index 0000000000000..dba8981a72aad --- /dev/null +++ b/cmds/statsd/src/anomaly/subscriber_util.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "config/ConfigKey.h" +#include "HashableDimensionKey.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +namespace android { +namespace os { +namespace statsd { + +void triggerSubscribers(const int64_t rule_id, + const MetricDimensionKey& dimensionKey, + const ConfigKey& configKey, + const std::vector& subscriptions); + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h index e2e02533f17fd..2a5679cc79fd3 100644 --- a/cmds/statsd/src/external/Perfetto.h +++ b/cmds/statsd/src/external/Perfetto.h @@ -16,6 +16,8 @@ #pragma once +#include + using android::os::StatsLogEventWrapper; namespace android { diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 66cb1d04a4e18..b0d23397d7067 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -47,11 +47,13 @@ const int FIELD_ID_UIDMAP_STATS = 8; const int FIELD_ID_ANOMALY_ALARM_STATS = 9; // const int FIELD_ID_PULLED_ATOM_STATS = 10; // The proto is written in stats_log_util.cpp const int FIELD_ID_LOGGER_ERROR_STATS = 11; +const int FIELD_ID_SUBSCRIBER_ALARM_STATS = 12; const int FIELD_ID_ATOM_STATS_TAG = 1; const int FIELD_ID_ATOM_STATS_COUNT = 2; const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1; +const int FIELD_ID_SUBSCRIBER_ALARMS_REGISTERED = 1; const int FIELD_ID_LOGGER_STATS_TIME = 1; const int FIELD_ID_LOGGER_STATS_ERROR_CODE = 2; @@ -248,6 +250,11 @@ void StatsdStats::noteRegisteredAnomalyAlarmChanged() { mAnomalyAlarmRegisteredStats++; } +void StatsdStats::noteRegisteredPeriodicAlarmChanged() { + lock_guard lock(mLock); + mPeriodicAlarmRegisteredStats++; +} + void StatsdStats::updateMinPullIntervalSec(int pullAtomId, long intervalSec) { lock_guard lock(mLock); mPulledAtomStats[pullAtomId].minPullIntervalSec = intervalSec; @@ -297,6 +304,7 @@ void StatsdStats::resetInternalLocked() { std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0); mAlertStats.clear(); mAnomalyAlarmRegisteredStats = 0; + mPeriodicAlarmRegisteredStats = 0; mMatcherStats.clear(); mLoggerErrors.clear(); for (auto& config : mConfigStats) { @@ -462,6 +470,11 @@ void StatsdStats::dumpStats(FILE* out) const { fprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats); } + if (mPeriodicAlarmRegisteredStats > 0) { + fprintf(out, "********SubscriberAlarmStats stats***********\n"); + fprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats); + } + fprintf(out, "UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes " "lost=%d\n", @@ -531,6 +544,13 @@ void StatsdStats::dumpStats(std::vector* output, bool reset) { proto.end(token); } + if (mPeriodicAlarmRegisteredStats > 0) { + long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SUBSCRIBER_ALARM_STATS); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_SUBSCRIBER_ALARMS_REGISTERED, + mPeriodicAlarmRegisteredStats); + proto.end(token); + } + const int numBytes = mUidMapStats.ByteSize(); vector buffer(numBytes); mUidMapStats.SerializeToArray(&buffer[0], numBytes); diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 8c16e4e9c2eb6..24ac6883f4443 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -169,6 +169,11 @@ public: */ void noteRegisteredAnomalyAlarmChanged(); + /** + * Report that statsd modified the periodic alarm registered with StatsCompanionService. + */ + void noteRegisteredPeriodicAlarmChanged(); + /** * Records the number of snapshot and delta entries that are being dropped from the uid map. */ @@ -264,6 +269,9 @@ private: // StatsCompanionService. int mAnomalyAlarmRegisteredStats = 0; + // Stores the number of times statsd registers the periodic alarm changes + int mPeriodicAlarmRegisteredStats = 0; + // Stores the number of times an anomaly detection alert has been declared // (per config, per alert name). The map size is capped by kMaxConfigCount. std::map> mAlertStats; diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 9c65371ba958b..16cac99f1fb98 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -109,9 +109,11 @@ DurationMetricProducer::~DurationMetricProducer() { VLOG("~DurationMetric() called"); } -sp DurationMetricProducer::addAnomalyTracker(const Alert &alert) { +sp DurationMetricProducer::addAnomalyTracker( + const Alert &alert, const sp& anomalyAlarmMonitor) { std::lock_guard lock(mMutex); - sp anomalyTracker = new DurationAnomalyTracker(alert, mConfigKey); + sp anomalyTracker = + new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor); if (anomalyTracker != nullptr) { mAnomalyTrackers.push_back(anomalyTracker); } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 5f29281a8a3c1..f41c278c59153 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -46,7 +46,8 @@ public: virtual ~DurationMetricProducer(); - sp addAnomalyTracker(const Alert &alert) override; + sp addAnomalyTracker(const Alert &alert, + const sp& anomalyAlarmMonitor) override; protected: void onMatchedLogEventInternalLocked( diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 8663e5eed7a71..83e1740c1fad5 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -124,7 +124,8 @@ public: } /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */ - virtual sp addAnomalyTracker(const Alert &alert) { + virtual sp addAnomalyTracker(const Alert &alert, + const sp& anomalyAlarmMonitor) { std::lock_guard lock(mMutex); sp anomalyTracker = new AnomalyTracker(alert, mConfigKey); if (anomalyTracker != nullptr) { diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index e75b710cc9db9..1c0521473358a 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -49,13 +49,17 @@ namespace statsd { const int FIELD_ID_METRICS = 1; MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, - const long timeBaseSec, sp uidMap) + const long timeBaseSec, + const sp &uidMap, + const sp& anomalyAlarmMonitor, + const sp& periodicAlarmMonitor) : mConfigKey(key), mUidMap(uidMap), mLastReportTimeNs(timeBaseSec * NS_PER_SEC) { mConfigValid = - initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, - mAllConditionTrackers, - mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap, - mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds); + initStatsdConfig(key, config, *uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, mTagIds, mAllAtomMatchers, + mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers, + mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap, + mTrackerToConditionMap, mNoReportMetricIds); if (config.allowed_log_source_size() == 0) { // TODO(b/70794411): uncomment the following line and remove the hard coded log source @@ -312,16 +316,19 @@ void MetricsManager::onLogEvent(const LogEvent& event) { } } -void MetricsManager::onAnomalyAlarmFired(const uint64_t timestampNs, - unordered_set, SpHash>& anomalySet) { +void MetricsManager::onAnomalyAlarmFired( + const uint64_t timestampNs, + unordered_set, SpHash>& alarmSet) { for (const auto& itr : mAllAnomalyTrackers) { - itr->informAlarmsFired(timestampNs, anomalySet); + itr->informAlarmsFired(timestampNs, alarmSet); } } -void MetricsManager::setAnomalyMonitor(const sp& anomalyMonitor) { - for (auto& itr : mAllAnomalyTrackers) { - itr->setAnomalyMonitor(anomalyMonitor); +void MetricsManager::onPeriodicAlarmFired( + const uint64_t timestampNs, + unordered_set, SpHash>& alarmSet) { + for (const auto& itr : mAllPeriodicAlarmTrackers) { + itr->informAlarmsFired(timestampNs, alarmSet); } } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index d4f844fe386c2..b50ef4a0c5a20 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -16,7 +16,8 @@ #pragma once -#include "anomaly/AnomalyMonitor.h" +#include "anomaly/AlarmMonitor.h" +#include "anomaly/AlarmTracker.h" #include "anomaly/AnomalyTracker.h" #include "condition/ConditionTracker.h" #include "config/ConfigKey.h" @@ -36,7 +37,8 @@ namespace statsd { class MetricsManager : public PackageInfoListener { public: MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const long timeBaseSec, - sp uidMap); + const sp& uidMap, const sp& anomalyAlarmMonitor, + const sp& periodicAlarmMonitor); virtual ~MetricsManager(); @@ -47,9 +49,11 @@ public: void onAnomalyAlarmFired( const uint64_t timestampNs, - unordered_set, SpHash>& anomalySet); + unordered_set, SpHash>& alarmSet); - void setAnomalyMonitor(const sp& anomalyMonitor); + void onPeriodicAlarmFired( + const uint64_t timestampNs, + unordered_set, SpHash>& alarmSet); void notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid, const int64_t version) override; @@ -120,6 +124,9 @@ private: // Hold all alert trackers. std::vector> mAllAnomalyTrackers; + // Hold all periodic alarm trackers. + std::vector> mAllPeriodicAlarmTrackers; + // To make the log processing more efficient, we want to do as much filtering as possible // before we go into individual trackers and conditions to match. diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 71e5c33b1b886..9912afa01c3dc 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -17,16 +17,19 @@ #define DEBUG false // STOPSHIP if true #include "Log.h" +#include "metrics_manager_util.h" + #include "../condition/CombinationConditionTracker.h" #include "../condition/SimpleConditionTracker.h" #include "../external/StatsPullerManager.h" #include "../matchers/CombinationLogMatchingTracker.h" #include "../matchers/SimpleLogMatchingTracker.h" -#include "CountMetricProducer.h" -#include "DurationMetricProducer.h" -#include "EventMetricProducer.h" -#include "GaugeMetricProducer.h" -#include "ValueMetricProducer.h" +#include "../metrics/CountMetricProducer.h" +#include "../metrics/DurationMetricProducer.h" +#include "../metrics/EventMetricProducer.h" +#include "../metrics/GaugeMetricProducer.h" +#include "../metrics/ValueMetricProducer.h" + #include "stats_util.h" using std::set; @@ -494,6 +497,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti bool initAlerts(const StatsdConfig& config, const unordered_map& metricProducerMap, + const sp& anomalyAlarmMonitor, vector>& allMetricProducers, vector>& allAnomalyTrackers) { unordered_map anomalyTrackerMap; @@ -512,7 +516,7 @@ bool initAlerts(const StatsdConfig& config, } const int metricIndex = itr->second; sp metric = allMetricProducers[metricIndex]; - sp anomalyTracker = metric->addAnomalyTracker(alert); + sp anomalyTracker = metric->addAnomalyTracker(alert, anomalyAlarmMonitor); if (anomalyTracker == nullptr) { // The ALOGW for this invalid alert was already displayed in addAnomalyTracker(). return false; @@ -522,6 +526,9 @@ bool initAlerts(const StatsdConfig& config, } for (int i = 0; i < config.subscription_size(); ++i) { const Subscription& subscription = config.subscription(i); + if (subscription.rule_type() != Subscription::ALERT) { + continue; + } if (subscription.subscriber_information_case() == Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) { ALOGW("subscription \"%lld\" has no subscriber info.\"", @@ -540,13 +547,60 @@ bool initAlerts(const StatsdConfig& config, return true; } +bool initAlarms(const StatsdConfig& config, const ConfigKey& key, + const sp& periodicAlarmMonitor, + const long timeBaseSec, + vector>& allAlarmTrackers) { + unordered_map alarmTrackerMap; + uint64_t startMillis = (uint64_t)timeBaseSec * MS_PER_SEC; + for (int i = 0; i < config.alarm_size(); i++) { + const Alarm& alarm = config.alarm(i); + if (alarm.offset_millis() <= 0) { + ALOGW("Alarm offset_millis should be larger than 0."); + return false; + } + if (alarm.period_millis() <= 0) { + ALOGW("Alarm period_millis should be larger than 0."); + return false; + } + alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size())); + allAlarmTrackers.push_back( + new AlarmTracker(startMillis, alarm, key, periodicAlarmMonitor)); + } + for (int i = 0; i < config.subscription_size(); ++i) { + const Subscription& subscription = config.subscription(i); + if (subscription.rule_type() != Subscription::ALARM) { + continue; + } + if (subscription.subscriber_information_case() == + Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) { + ALOGW("subscription \"%lld\" has no subscriber info.\"", + (long long)subscription.id()); + return false; + } + const auto& itr = alarmTrackerMap.find(subscription.rule_id()); + if (itr == alarmTrackerMap.end()) { + ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"", + (long long)subscription.id(), (long long)subscription.rule_id()); + return false; + } + const int trackerIndex = itr->second; + allAlarmTrackers[trackerIndex]->addSubscription(subscription); + } + return true; +} + bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const UidMap& uidMap, - const long timeBaseSec, set& allTagIds, + const sp& anomalyAlarmMonitor, + const sp& periodicAlarmMonitor, + const long timeBaseSec, + set& allTagIds, vector>& allAtomMatchers, vector>& allConditionTrackers, vector>& allMetricProducers, vector>& allAnomalyTrackers, + vector>& allPeriodicAlarmTrackers, unordered_map>& conditionToMetricMap, unordered_map>& trackerToMetricMap, unordered_map>& trackerToConditionMap, @@ -573,10 +627,16 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, ALOGE("initMetricProducers failed"); return false; } - if (!initAlerts(config, metricProducerMap, allMetricProducers, allAnomalyTrackers)) { + if (!initAlerts(config, metricProducerMap, anomalyAlarmMonitor, allMetricProducers, + allAnomalyTrackers)) { ALOGE("initAlerts failed"); return false; } + if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseSec, allPeriodicAlarmTrackers)) { + ALOGE("initAlarms failed"); + return false; + } + return true; } diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index 4f19ada5b0225..edda53d5e0cf2 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -13,16 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef METRIC_UTIL_H -#define METRIC_UTIL_H + +#pragma once + #include #include #include #include +#include "../anomaly/AlarmTracker.h" #include "../condition/ConditionTracker.h" #include "../external/StatsPullerManagerImpl.h" #include "../matchers/LogMatchingTracker.h" +#include "../metrics/MetricProducer.h" namespace android { namespace os { @@ -93,11 +96,15 @@ bool initMetrics( // Parameters are the members of MetricsManager. See MetricsManager for declaration. bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const UidMap& uidMap, - const long timeBaseSec, std::set& allTagIds, + const sp& anomalyAlarmMonitor, + const sp& periodicAlarmMonitor, + const long timeBaseSec, + std::set& allTagIds, std::vector>& allAtomMatchers, std::vector>& allConditionTrackers, std::vector>& allMetricProducers, vector>& allAnomalyTrackers, + vector>& allPeriodicAlarmTrackers, std::unordered_map>& conditionToMetricMap, std::unordered_map>& trackerToMetricMap, std::unordered_map>& trackerToConditionMap, @@ -106,4 +113,3 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, } // namespace statsd } // namespace os } // namespace android -#endif // METRIC_UTIL_H diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 272e90be662da..269f25b30c84c 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -164,4 +164,4 @@ message ConfigMetricsReportList { optional ConfigKey config_key = 1; repeated ConfigMetricsReport reports = 2; -} \ No newline at end of file +} diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 6a1db72b3911b..781ecede1700e 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -195,6 +195,20 @@ void StorageManager::appendConfigMetricsReport(ProtoOutputStream& proto) { } } +bool StorageManager::readFileToString(const char* file, string* content) { + int fd = open(file, O_RDONLY | O_CLOEXEC); + bool res = false; + if (fd != -1) { + if (android::base::ReadFdToString(fd, content)) { + res = true; + } else { + VLOG("Failed to read file %s\n", file); + } + close(fd); + } + return res; +} + void StorageManager::readConfigFromDisk(map& configsMap) { unique_ptr dir(opendir(STATS_SERVICE_DIR), closedir); if (dir == NULL) { diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h index d319674b89876..6c8ed0a967046 100644 --- a/cmds/statsd/src/storage/StorageManager.h +++ b/cmds/statsd/src/storage/StorageManager.h @@ -36,6 +36,11 @@ public: */ static void writeFile(const char* file, const void* buffer, int numBytes); + /** + * Reads the file content to the buffer. + */ + static bool readFileToString(const char* file, string* content); + /** * Deletes a single file given a file name. */ diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp index d9a8fc894804e..1c18f673aeb96 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp +++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp @@ -28,10 +28,10 @@ namespace android { namespace os { namespace statsd { -bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert, +bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id, const ConfigKey& configKey) { if (config.section_size() == 0) { - VLOG("The alert %lld contains zero section in config(%d,%lld)", alert.id(), + VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id, configKey.GetUid(), (long long) configKey.GetId()); return false; } @@ -39,7 +39,7 @@ bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert, IncidentReportArgs incidentReport; android::os::IncidentHeaderProto header; - header.set_alert_id(alert.id()); + header.set_alert_id(rule_id); header.mutable_config_key()->set_uid(configKey.GetUid()); header.mutable_config_key()->set_id(configKey.GetId()); incidentReport.addHeader(header); diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h index 229ed778af3ab..1b83fe23de8f5 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.h +++ b/cmds/statsd/src/subscriber/IncidentdReporter.h @@ -26,7 +26,7 @@ namespace statsd { /** * Calls incidentd to trigger an incident report and put in dropbox for uploading. */ -bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert, +bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id, const ConfigKey& configKey); } // namespace statsd diff --git a/cmds/statsd/tests/AnomalyMonitor_test.cpp b/cmds/statsd/tests/AlarmMonitor_test.cpp similarity index 70% rename from cmds/statsd/tests/AnomalyMonitor_test.cpp rename to cmds/statsd/tests/AlarmMonitor_test.cpp index 920ca08ef9558..1fccb35eccb57 100644 --- a/cmds/statsd/tests/AnomalyMonitor_test.cpp +++ b/cmds/statsd/tests/AlarmMonitor_test.cpp @@ -12,28 +12,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "anomaly/AnomalyMonitor.h" +#include "anomaly/AlarmMonitor.h" #include using namespace android::os::statsd; #ifdef __ANDROID__ -TEST(AnomalyMonitor, popSoonerThan) { +TEST(AlarmMonitor, popSoonerThan) { std::string emptyMetricId; std::string emptyDimensionId; - unordered_set, SpHash> set; - AnomalyMonitor am(2); + unordered_set, SpHash> set; + AlarmMonitor am(2, [](const sp&, int64_t){}, + [](const sp&){}); set = am.popSoonerThan(5); EXPECT_TRUE(set.empty()); - sp a = new AnomalyAlarm{10}; - sp b = new AnomalyAlarm{20}; - sp c = new AnomalyAlarm{20}; - sp d = new AnomalyAlarm{30}; - sp e = new AnomalyAlarm{40}; - sp f = new AnomalyAlarm{50}; + sp a = new InternalAlarm{10}; + sp b = new InternalAlarm{20}; + sp c = new InternalAlarm{20}; + sp d = new InternalAlarm{30}; + sp e = new InternalAlarm{40}; + sp f = new InternalAlarm{50}; am.add(a); am.add(b); diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp index 62bdba406de2e..90c3a2f1a539b 100644 --- a/cmds/statsd/tests/ConfigManager_test.cpp +++ b/cmds/statsd/tests/ConfigManager_test.cpp @@ -65,8 +65,12 @@ MATCHER_P(StatsdConfigEq, id, 0) { const int64_t testConfigId = 12345; TEST(ConfigManagerTest, TestFakeConfig) { - auto metricsManager = std::make_unique(ConfigKey(0, testConfigId), - build_fake_config(), 1000, new UidMap()); + auto metricsManager = std::make_unique( + ConfigKey(0, testConfigId), build_fake_config(), 1000, new UidMap(), + new AlarmMonitor(10, [](const sp&, int64_t){}, + [](const sp&){}), + new AlarmMonitor(10, [](const sp&, int64_t){}, + [](const sp&){})); EXPECT_TRUE(metricsManager->isConfigValid()); } diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index f90ca409e84cf..5d8c3f72551e0 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -271,19 +271,25 @@ StatsdConfig buildCirclePredicates() { TEST(MetricsManagerTest, TestGoodConfig) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildGoodConfig(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); EXPECT_EQ(1u, allMetricProducers.size()); @@ -293,112 +299,148 @@ TEST(MetricsManagerTest, TestGoodConfig) { TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildDimensionMetricsWithMultiTags(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); } TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildCircleMatchers(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingMatchers) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildMissingMatchers(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingPredicate) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildMissingPredicate(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); } TEST(MetricsManagerTest, TestCirclePredicateDependency) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildCirclePredicates(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); } TEST(MetricsManagerTest, testAlertWithUnknownMetric) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildAlertWithUnknownMetric(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); } diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index cb72697941e0c..3238b74fb635c 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -41,7 +41,13 @@ using android::util::ProtoOutputStream; */ class MockMetricsManager : public MetricsManager { public: - MockMetricsManager() : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, new UidMap()) { + MockMetricsManager() : MetricsManager( + ConfigKey(1, 12345), StatsdConfig(), 1000, + new UidMap(), + new AlarmMonitor(10, [](const sp&, int64_t){}, + [](const sp&){}), + new AlarmMonitor(10, [](const sp&, int64_t){}, + [](const sp&){})) { } MOCK_METHOD0(byteSize, size_t()); @@ -50,9 +56,11 @@ public: TEST(StatsLogProcessorTest, TestRateLimitByteSize) { sp m = new UidMap(); - sp anomalyMonitor; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; // Construct the processor with a dummy sendBroadcast function that does nothing. - StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {}); + StatsLogProcessor p(m, anomalyAlarmMonitor, periodicAlarmMonitor, 0, + [](const ConfigKey& key) {}); MockMetricsManager mockMetricsManager; @@ -67,11 +75,11 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) { TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { sp m = new UidMap(); - sp anomalyMonitor; + sp anomalyAlarmMonitor; + sp subscriberAlarmMonitor; int broadcastCount = 0; - StatsLogProcessor p(m, anomalyMonitor, 0, [&broadcastCount](const ConfigKey& key) { - broadcastCount++; - }); + StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, + [&broadcastCount](const ConfigKey& key) { broadcastCount++; }); MockMetricsManager mockMetricsManager; @@ -93,9 +101,10 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { sp m = new UidMap(); - sp anomalyMonitor; + sp anomalyAlarmMonitor; + sp subscriberAlarmMonitor; int broadcastCount = 0; - StatsLogProcessor p(m, anomalyMonitor, 0, + StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, [&broadcastCount](const ConfigKey& key) { broadcastCount++; }); MockMetricsManager mockMetricsManager; diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index f26c10d33e673..ca656ed9ab354 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -36,9 +36,11 @@ const string kApp2 = "app2.sharing.1"; TEST(UidMapTest, TestIsolatedUID) { sp m = new UidMap(); - sp anomalyMonitor; + sp anomalyAlarmMonitor; + sp subscriberAlarmMonitor; // Construct the processor with a dummy sendBroadcast function that does nothing. - StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {}); + StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, + [](const ConfigKey& key) {}); LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1); addEvent.write(100); // parent UID addEvent.write(101); // isolated UID diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp new file mode 100644 index 0000000000000..3330ee93eddf6 --- /dev/null +++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp @@ -0,0 +1,68 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/anomaly/AlarmTracker.h" + +#include +#include +#include + +using namespace testing; +using android::sp; +using std::set; +using std::unordered_map; +using std::vector; + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +const ConfigKey kConfigKey(0, 12345); + +TEST(AlarmTrackerTest, TestTriggerTimestamp) { + sp subscriberAlarmMonitor = + new AlarmMonitor(100, [](const sp&, int64_t){}, + [](const sp&){}); + Alarm alarm; + alarm.set_offset_millis(15 * MS_PER_SEC); + alarm.set_period_millis(60 * 60 * MS_PER_SEC); // 1hr + uint64_t startMillis = 100000000 * MS_PER_SEC; + AlarmTracker tracker(startMillis, alarm, kConfigKey, + subscriberAlarmMonitor); + + EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15); + + uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10; + std::unordered_set, SpHash> firedAlarmSet = + subscriberAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); + EXPECT_TRUE(firedAlarmSet.empty()); + tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); + EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15); + + currentTimeSec = startMillis / MS_PER_SEC + 7000; + firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); + EXPECT_EQ(firedAlarmSet.size(), 1u); + tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); + EXPECT_TRUE(firedAlarmSet.empty()); + EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15 + 2 * 60 * 60); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 20ddbe9f0e38e..9a0de0d818025 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -201,6 +201,7 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { } TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { + sp alarmMonitor; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; @@ -222,7 +223,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { bucketStartTimeNs); countProducer.setBucketSize(60 * NS_PER_SEC); - sp anomalyTracker = countProducer.addAnomalyTracker(alert); + sp anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); // Bucket is flushed yet. @@ -315,6 +316,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) { } TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { + sp alarmMonitor; Alert alert; alert.set_id(11); alert.set_metric_id(1); @@ -337,7 +339,7 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { bucketStartTimeNs); countProducer.setBucketSize(60 * NS_PER_SEC); - sp anomalyTracker = countProducer.addAnomalyTracker(alert); + sp anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); int tagId = 1; LogEvent event1(tagId, bucketStartTimeNs + 1); diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index 79695967a6ddc..1b22d75da7b44 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -239,6 +239,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { } TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { + sp alarmMonitor; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; @@ -263,7 +264,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); durationProducer.setBucketSize(60 * NS_PER_SEC); - sp anomalyTracker = durationProducer.addAnomalyTracker(alert); + sp anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); LogEvent start_event(tagId, startTimeNs); diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 0eb8ce2603bdc..77b3ace90aff9 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -129,6 +129,7 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { } TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { + sp alarmMonitor; GaugeMetric metric; metric.set_id(metricId); metric.set_bucket(ONE_MINUTE); @@ -145,8 +146,9 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -1 /* -1 means no pulling */, bucketStartTimeNs, pullerManager); + gaugeProducer.setBucketSize(60 * NS_PER_SEC); - sp anomalyTracker = gaugeProducer.addAnomalyTracker(alert); + sp anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); shared_ptr event1 = make_shared(tagId, bucketStartTimeNs + 10); @@ -339,6 +341,7 @@ TEST(GaugeMetricProducerTest, TestWithCondition) { } TEST(GaugeMetricProducerTest, TestAnomalyDetection) { + sp alarmMonitor; sp wizard = new NaggyMock(); shared_ptr pullerManager = @@ -363,7 +366,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { alert.set_num_buckets(2); const int32_t refPeriodSec = 60; alert.set_refractory_period_secs(refPeriodSec); - sp anomalyTracker = gaugeProducer.addAnomalyTracker(alert); + sp anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); int tagId = 1; std::shared_ptr event1 = std::make_shared(tagId, bucketStartTimeNs + 1); diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index a164c12134b59..83b1cbfb6fb3d 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -276,13 +276,15 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { alert.set_num_buckets(2); const int32_t refPeriodSec = 45; alert.set_refractory_period_secs(refPeriodSec); - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + sp alarmMonitor; + sp anomalyTracker = + new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, true, {anomalyTracker}); tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1); - sp alarm = anomalyTracker->mAlarms.begin()->second; + sp alarm = anomalyTracker->mAlarms.begin()->second; EXPECT_EQ((long long)(53ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); // Remove the anomaly alarm when the duration is no longer fully met. @@ -336,7 +338,9 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) { alert.set_num_buckets(2); const int32_t refPeriodSec = 45; alert.set_refractory_period_secs(refPeriodSec); - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + sp alarmMonitor; + sp anomalyTracker = + new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, true, {anomalyTracker}); @@ -390,7 +394,9 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) { alert.set_num_buckets(2); const int32_t refPeriodSec = 45; alert.set_refractory_period_secs(refPeriodSec); - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + sp alarmMonitor; + sp anomalyTracker = + new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, true, {anomalyTracker}); diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index cb731c555e908..aa41038b9acb6 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -334,7 +334,9 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { uint64_t bucketNum = 0; uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + sp alarmMonitor; + sp anomalyTracker = + new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, true, {anomalyTracker}); @@ -403,7 +405,9 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { uint64_t bucketNum = 0; uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + sp alarmMonitor; + sp anomalyTracker = + new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, true /*nesting*/, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker}); @@ -453,14 +457,16 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; uint64_t bucketSizeNs = 30 * NS_PER_SEC; - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + sp alarmMonitor; + sp anomalyTracker = + new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, true /*nesting*/, bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker}); tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1 EXPECT_EQ(1u, anomalyTracker->mAlarms.size()); - sp alarm = anomalyTracker->mAlarms.begin()->second; + sp alarm = anomalyTracker->mAlarms.begin()->second; EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); @@ -487,7 +493,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); // Now, at 60s, which is 38s after key1 started again, we have reached 40s of 'on' time. - std::unordered_set, SpHash> firedAlarms({alarm}); + std::unordered_set, SpHash> firedAlarms({alarm}); anomalyTracker->informAlarmsFired(62 * NS_PER_SEC, firedAlarms); EXPECT_EQ(0u, anomalyTracker->mAlarms.size()); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index ce4fa32784931..a0addcccb7a1d 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -346,6 +346,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { } TEST(ValueMetricProducerTest, TestAnomalyDetection) { + sp alarmMonitor; Alert alert; alert.set_id(101); alert.set_metric_id(metricId); @@ -365,7 +366,7 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { -1 /*not pulled*/, bucketStartTimeNs); valueProducer.setBucketSize(60 * NS_PER_SEC); - sp anomalyTracker = valueProducer.addAnomalyTracker(alert); + sp anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor); shared_ptr event1 diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 7568348108c40..242b6ebe5ec1b 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -391,9 +391,10 @@ std::unique_ptr CreateIsolatedUidChangedEvent( sp CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config, const ConfigKey& key) { sp uidMap = new UidMap(); - sp anomalyMonitor = new AnomalyMonitor(10); // 10 seconds + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; sp processor = new StatsLogProcessor( - uidMap, anomalyMonitor, timeBaseSec, [](const ConfigKey&){}); + uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){}); processor->OnConfigUpdated(key, config); return processor; } diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl index eae52171ee486..29c298e31ae98 100644 --- a/core/java/android/os/IStatsCompanionService.aidl +++ b/core/java/android/os/IStatsCompanionService.aidl @@ -41,7 +41,7 @@ interface IStatsCompanionService { oneway void cancelAnomalyAlarm(); /** - * Register a repeating alarm for polling to fire at the given timestamp and every + * Register a repeating alarm for pulling to fire at the given timestamp and every * intervalMs thereafter (in ms since epoch). * If polling alarm had already been registered, it will be replaced by new one. * Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately, @@ -49,9 +49,19 @@ interface IStatsCompanionService { */ oneway void setPullingAlarms(long timestampMs, long intervalMs); - /** Cancel any repeating polling alarm. */ + /** Cancel any repeating pulling alarm. */ oneway void cancelPullingAlarms(); + /** + * Register an alarm when we want to trigger subscribers at the given + * timestamp (in ms since epoch). + * If an alarm had already been registered, it will be replaced by new one. + */ + oneway void setAlarmForSubscriberTriggering(long timestampMs); + + /** Cancel any alarm for the purpose of subscriber triggering. */ + oneway void cancelAlarmForSubscriberTriggering(); + /** Pull the specified data. Results will be sent to statsd when complete. */ StatsLogEventWrapper[] pullData(int pullCode); diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index 682a24f17648b..2a68714431d40 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -46,6 +46,13 @@ interface IStatsManager { */ void informPollAlarmFired(); + /** + * Tells statsd that it is time to handle periodic alarms. Statsd will be responsible for + * determing what alarm subscriber to trigger. + * Two-way binder call so that caller's method (and corresponding wakelocks) will linger. + */ + void informAlarmForSubscriberTriggeringFired(); + /** * Tells statsd to store data to disk. */ diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 95c30d10341f2..6e017cd0a3d17 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -100,6 +100,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private final PendingIntent mAnomalyAlarmIntent; private final PendingIntent mPullingAlarmIntent; + private final PendingIntent mPeriodicAlarmIntent; private final BroadcastReceiver mAppUpdateReceiver; private final BroadcastReceiver mUserUpdateReceiver; private final ShutdownEventReceiver mShutdownEventReceiver; @@ -123,6 +124,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { new Intent(mContext, AnomalyAlarmReceiver.class), 0); mPullingAlarmIntent = PendingIntent.getBroadcast( mContext, 0, new Intent(mContext, PullingAlarmReceiver.class), 0); + mPeriodicAlarmIntent = PendingIntent.getBroadcast( + mContext, 0, new Intent(mContext, PeriodicAlarmReceiver.class), 0); mAppUpdateReceiver = new AppUpdateReceiver(); mUserUpdateReceiver = new BroadcastReceiver() { @Override @@ -329,7 +332,28 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - private final static class ShutdownEventReceiver extends BroadcastReceiver { + public final static class PeriodicAlarmReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) + Slog.d(TAG, "Time to poll something."); + synchronized (sStatsdLock) { + if (sStatsd == null) { + Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing."); + return; + } + try { + // Two-way call to statsd to retain AlarmManager wakelock + sStatsd.informAlarmForSubscriberTriggeringFired(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to inform statsd of periodic alarm firing.", e); + } + } + // AlarmManager releases its own wakelock here. + } + } + + public final static class ShutdownEventReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { /** @@ -384,6 +408,35 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } + @Override // Binder call + public void setAlarmForSubscriberTriggering(long timestampMs) { + enforceCallingPermission(); + if (DEBUG) + Slog.d(TAG, "Setting periodic alarm at " + 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. + // This alarm is inexact, leaving its exactness completely up to the OS optimizations. + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, timestampMs, mPeriodicAlarmIntent); + } finally { + Binder.restoreCallingIdentity(callingToken); + } + } + + @Override // Binder call + public void cancelAlarmForSubscriberTriggering() { + enforceCallingPermission(); + if (DEBUG) + Slog.d(TAG, "Cancelling periodic alarm"); + final long callingToken = Binder.clearCallingIdentity(); + try { + mAlarmManager.cancel(mPeriodicAlarmIntent); + } finally { + Binder.restoreCallingIdentity(callingToken); + } + } + @Override // Binder call public void setPullingAlarms(long timestampMs, long intervalMs) { enforceCallingPermission();