Merge "statsd & statscompanion communication more robust"
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();
+        }
+    }
+
 }