Statsd Anomaly Detection fixes

Minor fixes and notes regarding the setting of refractory periods and
anomaly alarms.
Also changes the anomaly alarms from inexact to exact.

Test: run cts-dev -m CtsStatsdHostTestCases -t android.cts.statsd.alert.AnomalyDetectionTests
Change-Id: Ia4f4c84dd647ebbbad4bab66164cd7709f8628e8
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index e111f58..ee38667 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -669,11 +669,18 @@
                                          "Only system uid can call informAnomalyAlarmFired");
     }
 
-    VLOG("StatsService::informAnomalyAlarmFired succeeded");
+    // TODO: This may be a bug. time(nullptr) can be off (wrt AlarmManager's time) and cause us to
+    //       miss the alarm! Eventually we will switch to using elapsedRealTime everywhere,
+    //       which may hopefully fix the problem, so we'll leave this alone for now.
     uint64_t currentTimeSec = time(nullptr);
     std::unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet =
             mAnomalyMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
-    mProcessor->onAnomalyAlarmFired(currentTimeSec * NS_PER_SEC, anomalySet);
+    if (anomalySet.size() > 0) {
+        VLOG("Found an anomaly alarm that fired.");
+        mProcessor->onAnomalyAlarmFired(currentTimeSec * NS_PER_SEC, anomalySet);
+    } else {
+        VLOG("Cannot find an anomaly alarm that fired. Perhaps it was recently cancelled.");
+    }
     return Status::ok();
 }
 
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
index 72d29d0..ca34dc6 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
@@ -96,7 +96,7 @@
     std::lock_guard<std::mutex> lock(mLock);
 
     for (sp<const AnomalyAlarm> t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec;
-         t = mPq.top()) {
+        t = mPq.top()) {
         oldAlarms.insert(t);
         mPq.pop();  // remove t
     }
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index fa0bc0c..3ba943c 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -53,10 +53,11 @@
 
 void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
                                         const uint64_t& timestampNs) {
-    uint32_t timestampSec = static_cast<uint32_t>(timestampNs / NS_PER_SEC);
+    // Alarms are stored in secs. Must round up, since if it fires early, it is ignored completely.
+    uint32_t timestampSec = static_cast<uint32_t>((timestampNs -1)/ NS_PER_SEC) + 1; // round up
     if (isInRefractoryPeriod(timestampNs, dimensionKey)) {
-        VLOG("Skipping setting anomaly alarm since it'd fall in the refractory period");
-        return;
+        VLOG("Setting a delayed anomaly alarm lest it fall in the refractory period");
+        timestampSec = getRefractoryPeriodEndsSec(dimensionKey) + 1;
     }
     sp<const AnomalyAlarm> alarm = new AnomalyAlarm{timestampSec};
     mAlarms.insert({dimensionKey, alarm});
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index ef6067a..c22ec4b 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -290,7 +290,8 @@
     public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
+            Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred at time "
+                    + System.currentTimeMillis() + "ms.");
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
                     Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
@@ -361,9 +362,8 @@
         final long callingToken = Binder.clearCallingIdentity();
         try {
             // using RTC, not RTC_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.
             // AlarmManager will automatically cancel any previous mAnomalyAlarmIntent alarm.
-            mAlarmManager.set(AlarmManager.RTC, timestampMs, mAnomalyAlarmIntent);
+            mAlarmManager.setExact(AlarmManager.RTC, timestampMs, mAnomalyAlarmIntent);
         } finally {
             Binder.restoreCallingIdentity(callingToken);
         }