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();
+ }
+ }
+
}