statsd & statscompanion communication more robust

If statsd or statsdcompanion crashes, or if one loads
before the other, the other will be able to accomodate.

When statsd loads, it will attempt to tell statscompanion that it's
alive, and then get on to its business, while assuming that
statscompanion is not alive. Only when statscompanion tells statsd
that it is alive, statsd will then start to use it.

When statscompanion loads, it will attempt to tell statsd that it's
alive and then do nothing (since it has nothing to do). When statsd
tells statscompanion that statsd is alive, statscompanion will respond,
telling statsd that it is alive and, if that binder call returns, will
get to work.

This way, if statsd loads first, it can work unobstructed until
statscompanion informs statsd that it is alive, at which point they
shake hands and work. Conversely, if statscompanion loads first, it will
do nothing until statsd contacts it, at which point they will shake
hands and work.

Test: manual
Change-Id: I969ad47fb8060e27814d05ad37433a02711cfa6a
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 6de5303..b9ee7ff 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -40,8 +40,8 @@
 LOCAL_MODULE := statsd
 
 LOCAL_SRC_FILES := \
-    ../../core/java/android/os/IStatsManager.aidl \
     ../../core/java/android/os/IStatsCompanionService.aidl \
+    ../../core/java/android/os/IStatsManager.aidl \
     src/StatsService.cpp \
     src/AnomalyMonitor.cpp \
     src/LogEntryPrinter.cpp \
@@ -119,6 +119,7 @@
 	STATSD_PROTO_INCLUDES
 
 LOCAL_SRC_FILES := \
+    ../../core/java/android/os/IStatsCompanionService.aidl \
     ../../core/java/android/os/IStatsManager.aidl \
     src/StatsService.cpp \
     tests/indexed_priority_queue_test.cpp \
diff --git a/cmds/statsd/src/AnomalyMonitor.cpp b/cmds/statsd/src/AnomalyMonitor.cpp
index d73de95..8fd5249 100644
--- a/cmds/statsd/src/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/AnomalyMonitor.cpp
@@ -19,10 +19,9 @@
 
 #include <AnomalyMonitor.h>
 
-#include <binder/IServiceManager.h>
 #include <cutils/log.h>
 
-namespace statsd {
+using namespace android::os::statsd;
 
 AnomalyMonitor::AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec)
         : mRegisteredAlarmTimeSec(0),
@@ -32,7 +31,23 @@
 AnomalyMonitor::~AnomalyMonitor() {
 }
 
+void AnomalyMonitor::setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
+    std::lock_guard<std::mutex> lock(mLock);
+    sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+    mStatsCompanionService = statsCompanionService;
+    if (statsCompanionService == nullptr) {
+        if (DEBUG) ALOGD("Erasing link to statsCompanionService");
+        return;
+    }
+    if (DEBUG) ALOGD("Creating link to statsCompanionService");
+    const sp<const AnomalyAlarm> top = mPq.top();
+    if (top != nullptr) {
+        updateRegisteredAlarmTime_l(top->timestampSec);
+    }
+}
+
 void AnomalyMonitor::add(sp<const AnomalyAlarm> alarm) {
+    std::lock_guard<std::mutex> lock(mLock);
     if (alarm == nullptr) {
         ALOGW("Asked to add a null alarm.");
         return;
@@ -42,70 +57,46 @@
         ALOGW("Asked to add a 0-time alarm.");
         return;
     }
-    std::lock_guard<std::mutex> 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);
+        updateRegisteredAlarmTime_l(alarm->timestampSec);
     }
 }
 
 void AnomalyMonitor::remove(sp<const AnomalyAlarm> alarm) {
+    std::lock_guard<std::mutex> lock(mLock);
     if (alarm == nullptr) {
         ALOGW("Asked to remove a null alarm.");
         return;
     }
-    std::lock_guard<std::mutex> lock(mLock);
     if (DEBUG) ALOGD("Removing alarm with time %u", alarm->timestampSec);
     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<IStatsCompanionService> statsCompanionService = getStatsCompanion_l();
-        if (statsCompanionService != nullptr) {
-            statsCompanionService->cancelAnomalyAlarm();
+        if (mStatsCompanionService != nullptr) {
+            mStatsCompanionService->cancelAnomalyAlarm();
         }
         return;
     }
     uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec;
     if (DEBUG) ALOGD("Soonest alarm is %u", soonestAlarmTimeSec);
     if (soonestAlarmTimeSec > mRegisteredAlarmTimeSec + mMinUpdateTimeSec) {
-        updateRegisteredAlarmTime(soonestAlarmTimeSec);
+        updateRegisteredAlarmTime_l(soonestAlarmTimeSec);
     }
 }
 
-void AnomalyMonitor::updateRegisteredAlarmTime(uint32_t timestampSec) {
+void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) {
     if (DEBUG) ALOGD("Updating reg alarm time to %u", timestampSec);
     mRegisteredAlarmTimeSec = timestampSec;
-    sp<IStatsCompanionService> statsCompanionService = getStatsCompanion_l();
-    if (statsCompanionService != nullptr) {
-        statsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec));
+    if (mStatsCompanionService != nullptr) {
+        mStatsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec));
     }
 }
 
-sp<IStatsCompanionService> AnomalyMonitor::getStatsCompanion_l() {
-    if (mStatsCompanion != nullptr) {
-        return mStatsCompanion;
-    }
-    // Get statscompanion service from service manager
-    const sp<IServiceManager> sm(defaultServiceManager());
-    if (sm != nullptr) {
-        const String16 name("statscompanion");
-        mStatsCompanion =
-                interface_cast<IStatsCompanionService>(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
index 5418cf0..3ee5354 100644
--- a/cmds/statsd/src/AnomalyMonitor.h
+++ b/cmds/statsd/src/AnomalyMonitor.h
@@ -24,9 +24,11 @@
 #include <queue>
 #include <vector>
 
-using namespace android::os;
 using namespace android;
+using namespace android::os;
 
+namespace android {
+namespace os {
 namespace statsd {
 
 /**
@@ -52,7 +54,7 @@
 /**
  * Manages alarms for Anomaly Detection.
  */
-class AnomalyMonitor {
+class AnomalyMonitor : public RefBase {
  public:
     /**
      * @param minDiffToUpdateRegisteredAlarmTimeSec If the soonest alarm differs
@@ -63,6 +65,14 @@
     ~AnomalyMonitor();
 
     /**
+     * Tells AnomalyMonitor what IStatsCompanionService to use and, if
+     * applicable, immediately registers an existing alarm with it.
+     * If nullptr, AnomalyMonitor will continue to add/remove alarms, but won't
+     * update IStatsCompanionService (until such time as it is set non-null).
+     */
+    void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
+
+    /**
      * Adds the given alarm (reference) to the queue.
      */
     void add(sp<const AnomalyAlarm> alarm);
@@ -84,7 +94,6 @@
     }
 
  private:
-    /** Lock for accessing/writing to mPq. */
     std::mutex mLock;
 
     /**
@@ -103,11 +112,11 @@
     /**
      * Binder interface for communicating with StatsCompanionService.
      */
-    sp<IStatsCompanionService> mStatsCompanion;
+    sp<IStatsCompanionService> mStatsCompanionService = nullptr;
 
     /**
      * Amount by which the soonest projected alarm must differ from
-     * mRegisteredAlarmTimeSec before updateRegisteredAlarmTime is called.
+     * mRegisteredAlarmTimeSec before updateRegisteredAlarmTime_l is called.
      */
     uint32_t mMinUpdateTimeSec;
 
@@ -115,15 +124,14 @@
      * Updates the alarm registered with StatsCompanionService to the given time.
      * Also correspondingly updates mRegisteredAlarmTimeSec.
      */
-    void updateRegisteredAlarmTime(uint32_t timestampSec);
-
-    /** Returns the StatsCompanionService. */
-    sp<IStatsCompanionService> getStatsCompanion_l();
+    void updateRegisteredAlarmTime_l(uint32_t timestampSec);
 
     /** Converts uint32 timestamp in seconds to a Java long in msec. */
     int64_t secToMs(uint32_t timeSec);
 };
 
 } // namespace statsd
+} // namespace os
+} // namespace android
 
 #endif // ANOMALY_MONITOR_H
\ No newline at end of file
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 67f6782..976fc26 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -15,6 +15,7 @@
  */
 
 #define LOG_TAG "statsd"
+#define DEBUG true
 
 #include "StatsService.h"
 #include "DropboxReader.h"
@@ -37,6 +38,7 @@
 
 // ================================================================================
 StatsService::StatsService(const sp<Looper>& handlerLooper)
+        : mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Change this based on the config
 {
     ALOGD("stats service constructed");
 }
@@ -164,14 +166,14 @@
 Status
 StatsService::informAnomalyAlarmFired()
 {
-    ALOGD("StatsService::informAnomalyAlarmFired was called");
+    if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired was called");
 
     if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
         return Status::fromExceptionCode(Status::EX_SECURITY,
                 "Only system uid can call informAnomalyAlarmFired");
     }
 
-    ALOGD("StatsService::informAnomalyAlarmFired succeeded");
+    if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired succeeded");
     // TODO: check through all counters/timers and see if an anomaly has indeed occurred.
 
     return Status::ok();
@@ -180,14 +182,14 @@
 Status
 StatsService::informPollAlarmFired()
 {
-    ALOGD("StatsService::informPollAlarmFired was called");
+    if (DEBUG) ALOGD("StatsService::informPollAlarmFired was called");
 
     if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
         return Status::fromExceptionCode(Status::EX_SECURITY,
                 "Only system uid can call informPollAlarmFired");
     }
 
-    ALOGD("StatsService::informPollAlarmFired succeeded");
+    if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded");
     // TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them.
 
     return Status::ok();
@@ -204,6 +206,8 @@
     // When system_server is up and running, schedule the dropbox task to run.
     ALOGD("StatsService::systemRunning");
 
+    sayHiToStatsCompanion();
+
     return Status::ok();
 }
 
@@ -223,3 +227,60 @@
     fprintf(out, "\t print-stats-log [tag_required] [timestamp_nsec_optional]\n");
     fprintf(out, "\t config\t Loads a new config from command-line (must be proto in wire-encoded format).\n");
 }
+
+void
+StatsService::sayHiToStatsCompanion()
+{
+    // TODO: This method needs to be private. It is temporarily public and unsecured for testing purposes.
+    sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
+    if (statsCompanion != nullptr) {
+        if (DEBUG) ALOGD("Telling statsCompanion that statsd is ready");
+        statsCompanion->statsdReady();
+    } else {
+        if (DEBUG) ALOGD("Could not access statsCompanion");
+    }
+}
+
+Status
+StatsService::statsCompanionReady()
+{
+    if (DEBUG) ALOGD("StatsService::statsCompanionReady was called");
+
+    if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
+        return Status::fromExceptionCode(Status::EX_SECURITY,
+                                         "Only system uid can call statsCompanionReady");
+    }
+
+    sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
+    if (statsCompanion == nullptr) {
+        return Status::fromExceptionCode(Status::EX_NULL_POINTER,
+                                         "statscompanion unavailable despite it contacting statsd!");
+    }
+    if (DEBUG) ALOGD("StatsService::statsCompanionReady linking to statsCompanion.");
+    IInterface::asBinder(statsCompanion)->linkToDeath(new StatsdDeathRecipient(mAnomalyMonitor));
+    mAnomalyMonitor->setStatsCompanionService(statsCompanion);
+
+    return Status::ok();
+}
+
+sp<IStatsCompanionService>
+StatsService::getStatsCompanionService() {
+    sp<IStatsCompanionService> statsCompanion = nullptr;
+    // Get statscompanion service from service manager
+    const sp<IServiceManager> sm(defaultServiceManager());
+    if (sm != nullptr) {
+        const String16 name("statscompanion");
+        statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name));
+        if (statsCompanion == nullptr) {
+            ALOGW("statscompanion service unavailable!");
+            return nullptr;
+        }
+    }
+    return statsCompanion;
+}
+
+void
+StatsdDeathRecipient::binderDied(const wp<IBinder>& who) {
+    ALOGW("statscompanion service died");
+    mAnmlyMntr->setStatsCompanionService(nullptr);
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 2a8c3f6..467c2bd 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -17,9 +17,11 @@
 #ifndef STATS_SERVICE_H
 #define STATS_SERVICE_H
 
+#include "AnomalyMonitor.h"
 #include "StatsLogProcessor.h"
 
 #include <android/os/BnStatsManager.h>
+#include <android/os/IStatsCompanionService.h>
 #include <binder/IResultReceiver.h>
 #include <binder/IShellCallback.h>
 #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
@@ -32,7 +34,9 @@
 using namespace android::base;
 using namespace android::binder;
 using namespace android::os;
+using namespace android::os::statsd;
 using namespace std;
+
 using android::os::statsd::StatsdConfig;
 
 // ================================================================================
@@ -49,17 +53,46 @@
 
     virtual Status systemRunning();
 
+    // Inform statsd that statsCompanion is ready.
+    virtual Status statsCompanionReady();
+
     virtual Status informAnomalyAlarmFired();
 
     virtual Status informPollAlarmFired();
 
     virtual status_t setProcessor(const sp<StatsLogProcessor>& main_processor);
 
+    // TODO: public for testing since statsd doesn't run when system starts. Change to private later.
+    /** Inform statsCompanion that statsd is ready. */
+    virtual void sayHiToStatsCompanion();
+
 private:
     sp<StatsLogProcessor> m_processor; // Reference to the processor for updating configs.
+
     status_t doPrintStatsLog(FILE* out, const Vector<String8>& args);
+
     void printCmdHelp(FILE* out);
+
     status_t doLoadConfig(FILE* in);
+
+    const sp<AnomalyMonitor> mAnomalyMonitor;  // TODO: Move this to a more logical file/class
+
+ private:
+    /** Fetches the StatsCompanionService. */
+    sp<IStatsCompanionService> getStatsCompanionService();
+};
+
+// --- StatsdDeathRecipient ---
+class StatsdDeathRecipient : public IBinder::DeathRecipient {
+public:
+    StatsdDeathRecipient(sp<AnomalyMonitor> anomalyMonitor)
+            : mAnmlyMntr(anomalyMonitor) {
+    }
+
+    virtual void binderDied(const wp<IBinder>& who);
+
+private:
+    const sp<AnomalyMonitor> mAnmlyMntr;
 };
 
 #endif // STATS_SERVICE_H
diff --git a/cmds/statsd/src/indexed_priority_queue.h b/cmds/statsd/src/indexed_priority_queue.h
index d302f85..76409c07 100644
--- a/cmds/statsd/src/indexed_priority_queue.h
+++ b/cmds/statsd/src/indexed_priority_queue.h
@@ -29,6 +29,8 @@
 
 using namespace android;
 
+namespace android {
+namespace os {
 namespace statsd {
 
 /** Defines a hash function for sp<AA>, returning the hash of the underlying pointer. */
@@ -191,5 +193,7 @@
 }
 
 } // namespace statsd
+} // namespace os
+} // namespace android
 
 #endif //STATSD_INDEXED_PRIORITY_QUEUE_H
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index f9265c6..161b630 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -129,6 +129,10 @@
         return -1;
     }
 
+    // TODO: This line is temporary, since statsd doesn't start up automatically (and therefore
+    // the call in StatsService::SystemRunning() won't ever be called right now).
+    service->sayHiToStatsCompanion();
+
     // Start the log reader thread
     err = start_log_reader_thread(service);
     if (err != NO_ERROR) {
diff --git a/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp
index a679128..1aad089 100644
--- a/cmds/statsd/tests/indexed_priority_queue_test.cpp
+++ b/cmds/statsd/tests/indexed_priority_queue_test.cpp
@@ -18,7 +18,7 @@
 
 #include <gtest/gtest.h>
 
-using namespace statsd;
+using namespace android::os::statsd;
 
 /** struct for template in indexed_priority_queue */
 struct AATest : public RefBase {
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index b17d2f1b..a83d313 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -22,6 +22,11 @@
   */
 oneway interface IStatsCompanionService {
     /**
+     * Tell statscompanion that stastd is up and running.
+     */
+    void statsdReady();
+
+    /**
     * Register an alarm for anomaly detection to fire at the given timestamp (ms since epoch).
     * If anomaly alarm had already been registered, it will be replaced with the new timestamp.
     * Uses AlarmManager.set API, so  if the timestamp is in the past, alarm fires immediately, and
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index b73ca20..f8f2813 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -20,20 +20,29 @@
   * Binder interface to communicate with the statistics management service.
   * {@hide}
   */
-oneway interface IStatsManager {
+interface IStatsManager {
     /**
      * Tell the stats daemon that the android system server is up and running.
      */
-    void systemRunning();
+    oneway void systemRunning();
+
+    /**
+     * Tell the stats daemon that the StatsCompanionService is up and running.
+     * Two-way binder call so that caller knows message received.
+     */
+    void statsCompanionReady();
 
     /**
      * Tells statsd that an anomaly may have occurred, so statsd can check whether this is so and
      * act accordingly.
+     * Two-way binder call so that caller's method (and corresponding wakelocks) will linger.
      */
     void informAnomalyAlarmFired();
 
-    /** Tells statsd that it is time to poll some stats. Statsd will be responsible for determing
+    /**
+     * Tells statsd that it is time to poll some stats. Statsd will be responsible for determing
      * what stats to poll and initiating the polling.
+     * Two-way binder call so that caller's method (and corresponding wakelocks) will linger.
      */
     void informPollAlarmFired();
 }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index cf81e5b..11ae212 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
+import android.os.IBinder;
 import android.os.IStatsCompanionService;
 import android.os.IStatsManager;
 import android.os.Process;
@@ -28,6 +29,7 @@
 import android.os.ServiceManager;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.SystemService;
 
 /**
@@ -40,44 +42,13 @@
 
     private final Context mContext;
     private final AlarmManager mAlarmManager;
+    @GuardedBy("sStatsdLock")
     private static IStatsManager sStatsd;
+    private static final Object sStatsdLock = new Object();
 
     private final PendingIntent mAnomalyAlarmIntent;
     private final PendingIntent mPollingAlarmIntent;
 
-    public final static class AnomalyAlarmReceiver extends BroadcastReceiver  {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
-            try {
-                // TODO: should be twoway so device won't sleep before acting?
-                getStatsdService().informAnomalyAlarmFired();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "failed to inform statsd of anomaly alarm firing", e);
-            } catch (NullPointerException e) {
-                Slog.e(TAG, "could not access statsd to inform it of anomaly alarm firing", e);
-            }
-            // AlarmManager releases its own wakelock here.
-        }
-    };
-
-    public final static class PollingAlarmReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (DEBUG) Slog.d(TAG, "Time to poll something.");
-            if (DEBUG) Slog.d(TAG, "Time to poll something.");
-            try {
-                // TODO: should be twoway so device won't sleep before acting?
-                getStatsdService().informPollAlarmFired();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "failed to inform statsd of polling alarm firing",e);
-            } catch (NullPointerException e) {
-                Slog.e(TAG, "could not access statsd to inform it of polling alarm firing", e);
-            }
-            // AlarmManager releases its own wakelock here.
-        }
-    };
-
     public StatsCompanionService(Context context) {
         super();
         mContext = context;
@@ -89,33 +60,45 @@
                 new Intent(mContext, PollingAlarmReceiver.class), 0);
     }
 
-    /** Returns the statsd IBinder service */
-    public static IStatsManager getStatsdService() {
-        if (sStatsd != null) {
-            return sStatsd;
-        }
-        sStatsd = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
-        return sStatsd;
-    }
-
-    public static final class Lifecycle extends SystemService {
-        private StatsCompanionService mStatsCompanionService;
-
-        public Lifecycle(Context context) {
-            super(context);
-        }
-
+    public final static class AnomalyAlarmReceiver extends BroadcastReceiver  {
         @Override
-        public void onStart() {
-            mStatsCompanionService = new StatsCompanionService(getContext());
-            try {
-                publishBinderService(Context.STATS_COMPANION_SERVICE, mStatsCompanionService);
-                if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
-            } catch (Exception e) {
-                Slog.e(TAG, "Failed to publishBinderService", e);
+        public void onReceive(Context context, Intent intent) {
+            Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
+            synchronized (sStatsdLock) {
+                if (sStatsd == null) {
+                    Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
+                    return;
+                }
+                try {
+                    // Two-way call to statsd to retain AlarmManager wakelock
+                    sStatsd.informAnomalyAlarmFired();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
+                }
             }
+            // AlarmManager releases its own wakelock here.
         }
-    }
+    };
+
+    public final static class PollingAlarmReceiver 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 polling alarm firing");
+                    return;
+                }
+                try {
+                    // Two-way call to statsd to retain AlarmManager wakelock
+                    sStatsd.informPollAlarmFired();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to inform statsd of polling alarm firing", e);
+                }
+            }
+            // AlarmManager releases its own wakelock here.
+        }
+    };
 
     @Override // Binder call
     public void setAnomalyAlarm(long timestampMs) {
@@ -173,6 +156,13 @@
         }
     }
 
+    @Override
+    public void statsdReady() {
+        enforceCallingPermission();
+        if (DEBUG) Slog.d(TAG, "learned that statsdReady");
+        sayHiToStatsd(); // tell statsd that we're ready too and link to it
+    }
+
     private void enforceCallingPermission() {
         if (Binder.getCallingPid() == Process.myPid()) {
             return;
@@ -180,4 +170,90 @@
         mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
     }
 
+    // Lifecycle and related code
+
+    /** Fetches the statsd IBinder service */
+    private static IStatsManager fetchStatsdService() {
+        return IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
+    }
+
+    public static final class Lifecycle extends SystemService {
+        private StatsCompanionService mStatsCompanionService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mStatsCompanionService = new StatsCompanionService(getContext());
+            try {
+                publishBinderService(Context.STATS_COMPANION_SERVICE, mStatsCompanionService);
+                if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
+            } catch (Exception e) {
+                Slog.e(TAG, "Failed to publishBinderService", e);
+            }
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            super.onBootPhase(phase);
+            if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+                mStatsCompanionService.systemReady();
+            }
+        }
+    }
+
+    /** Now that the android system is ready, StatsCompanion is ready too, so inform statsd. */
+    private void systemReady() {
+        if (DEBUG) Slog.d(TAG, "Learned that systemReady");
+        sayHiToStatsd();
+    }
+
+    /** Tells statsd that statscompanion is ready. If the binder call returns, link to statsd. */
+    private void sayHiToStatsd() {
+        synchronized (sStatsdLock) {
+            if (sStatsd != null) {
+                Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
+                        new IllegalStateException("sStatsd is not null when being fetched"));
+                return;
+            }
+            sStatsd = fetchStatsdService();
+            if (sStatsd == null) {
+                Slog.w(TAG, "Could not access statsd");
+                return;
+            }
+            if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
+            try {
+                sStatsd.statsCompanionReady();
+                // If the statsCompanionReady two-way binder call returns, link to statsd.
+                try {
+                    sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
+                    forgetEverything();
+                }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
+                forgetEverything();
+            }
+        }
+    }
+
+    private class StatsdDeathRecipient implements IBinder.DeathRecipient {
+        @Override
+        public void binderDied() {
+            Slog.i(TAG, "Statsd is dead - erase all my knowledge.");
+            forgetEverything();
+        }
+    }
+
+    private void forgetEverything() {
+        synchronized (sStatsdLock) {
+            sStatsd = null;
+            cancelAnomalyAlarm();
+            cancelPollingAlarms();
+        }
+    }
+
 }