diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index db8c89dafaba3..6c6e037ea38ac 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -24,7 +24,9 @@ LOCAL_MODULE := statsd LOCAL_SRC_FILES := \ ../../core/java/android/os/IStatsManager.aidl \ + ../../core/java/android/os/IStatsCompanionService.aidl \ src/StatsService.cpp \ + src/AnomalyMonitor.cpp \ src/LogEntryPrinter.cpp \ src/LogReader.cpp \ src/main.cpp diff --git a/cmds/statsd/src/AnomalyMonitor.cpp b/cmds/statsd/src/AnomalyMonitor.cpp new file mode 100644 index 0000000000000..8338d5026b460 --- /dev/null +++ b/cmds/statsd/src/AnomalyMonitor.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 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 LOG_TAG "AnomalyMonitor" +#define DEBUG true + +#include + +#include +#include + +namespace statsd { + +AnomalyMonitor::AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec) + : mRegisteredAlarmTimeSec(0), + mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec) { +} + +AnomalyMonitor::~AnomalyMonitor() { +} + +void AnomalyMonitor::add(sp alarm) { + if (alarm == nullptr) { + ALOGW("Asked to add a null alarm."); + return; + } + if (alarm->timestampSec < 1) { + // forbidden since a timestamp 0 is used to indicate no alarm registered + ALOGW("Asked to add a 0-time alarm."); + return; + } + std::lock_guard lock(mLock); + // TODO: Ensure that refractory period is respected. + if (DEBUG) ALOGD("Adding alarm with time %u", alarm->timestampSec); + mPq.push(alarm); + if (mRegisteredAlarmTimeSec < 1 || + alarm->timestampSec + mMinUpdateTimeSec < mRegisteredAlarmTimeSec) { + updateRegisteredAlarmTime(alarm->timestampSec); + } +} + +void AnomalyMonitor::remove(sp alarm) { + if (alarm == nullptr) { + ALOGW("Asked to remove a null alarm."); + return; + } + std::lock_guard lock(mLock); + if (DEBUG) ALOGD("Removing alarm with time %u", alarm->timestampSec); + // TODO: make priority queue able to have items removed from it !!! + // mPq.remove(alarm); + if (mPq.empty()) { + if (DEBUG) ALOGD("Queue is empty. Cancel any alarm."); + mRegisteredAlarmTimeSec = 0; + // TODO: Make this resistant to doing work when companion is not ready yet + sp statsCompanionService = getStatsCompanion_l(); + if (statsCompanionService != nullptr) { + statsCompanionService->cancelAnomalyAlarm(); + } + return; + } + uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec; + if (DEBUG) ALOGD("Soonest alarm is %u", soonestAlarmTimeSec); + if (soonestAlarmTimeSec > mRegisteredAlarmTimeSec + mMinUpdateTimeSec) { + updateRegisteredAlarmTime(soonestAlarmTimeSec); + } +} + +void AnomalyMonitor::updateRegisteredAlarmTime(uint32_t timestampSec) { + if (DEBUG) ALOGD("Updating reg alarm time to %u", timestampSec); + mRegisteredAlarmTimeSec = timestampSec; + sp statsCompanionService = getStatsCompanion_l(); + if (statsCompanionService != nullptr) { + statsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec)); + } +} + +sp AnomalyMonitor::getStatsCompanion_l() { + if (mStatsCompanion != nullptr) { + return mStatsCompanion; + } + // Get statscompanion service from service manager + const sp sm(defaultServiceManager()); + if (sm != nullptr) { + const String16 name("statscompanion"); + mStatsCompanion = + interface_cast(sm->checkService(name)); + if (mStatsCompanion == nullptr) { + ALOGW("statscompanion service unavailable!"); + return nullptr; + } + } + return mStatsCompanion; +} + +int64_t AnomalyMonitor::secToMs(uint32_t timeSec) { + return ((int64_t) timeSec) * 1000; +} + +} // namespace statsd \ No newline at end of file diff --git a/cmds/statsd/src/AnomalyMonitor.h b/cmds/statsd/src/AnomalyMonitor.h new file mode 100644 index 0000000000000..63b806416c7e7 --- /dev/null +++ b/cmds/statsd/src/AnomalyMonitor.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef ANOMALY_MONITOR_H +#define ANOMALY_MONITOR_H + +#include +#include + +#include +#include + +using namespace android::os; +using namespace android; + +namespace statsd { + +/** + * Represents an alarm, associated with some aggregate metric, holding a + * projected time at which the metric is expected to exceed its anomaly + * 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) { + } + + const uint32_t timestampSec; + + /** AnomalyAlarm a is higher priority than b if its timestamp is sooner. */ + struct Comparator { + bool operator()(sp a, sp b) const + { + return (a->timestampSec > b->timestampSec); + } + }; +}; + +/** + * Manages alarms for Anomaly Detection. + */ +class AnomalyMonitor { + 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(); + + /** + * Adds the given alarm (reference) to the queue. + */ + 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); + + /** + * Returns the projected alarm timestamp that is registered with + * StatsCompanionService. This may not be equal to the soonest alarm, + * but should be within minDiffToUpdateRegisteredAlarmTimeSec of it. + */ + uint32_t getRegisteredAlarmTimeSec() const { + return mRegisteredAlarmTimeSec; + } + + private: + /** Lock for accessing/writing to mPq. */ + std::mutex mLock; + + /** + * Timestamp (seconds since epoch) of the alarm registered with + * StatsCompanionService. This, in general, may not be equal to the soonest + * alarm stored in mPq, but should be within minUpdateTimeSec of it. + * A value of 0 indicates that no alarm is currently registered. + */ + uint32_t mRegisteredAlarmTimeSec; + + /** + * Priority queue of alarms, prioritized by soonest alarm.timestampSec. + */ + // TODO: use a priority queue from which elements can be removed + // Can I use the one in toolchain/gcc/gcc-4.9/include/fibheap.h? + std::priority_queue, std::vector>, + AnomalyAlarm::Comparator> mPq; + + /** + * Binder interface for communicating with StatsCompanionService. + */ + sp mStatsCompanion; + + /** + * Amount by which the soonest projected alarm must differ from + * mRegisteredAlarmTimeSec before updateRegisteredAlarmTime is called. + */ + uint32_t mMinUpdateTimeSec; + + /** + * Updates the alarm registered with StatsCompanionService to the given time. + * Also correspondingly updates mRegisteredAlarmTimeSec. + */ + void updateRegisteredAlarmTime(uint32_t timestampSec); + + /** Returns the StatsCompanionService. */ + sp getStatsCompanion_l(); + + /** Converts uint32 timestamp in seconds to a Java long in msec. */ + int64_t secToMs(uint32_t timeSec); +}; + +} // namespace statsd + +#endif // ANOMALY_MONITOR_H \ No newline at end of file