Support StatsD sending broadcasts.

StatsD will send a broadcast when we're 90% of the way to our
allocated memory limit for the configuration. If the memory usage
goes over the limit, we just lose all the data for this config.

Also modifies the adb shell commands to facilitate debugging of the
broadcasts.

Test: Manually tested on marlin-eng with custom gmscore code.

Change-Id: I517a15bd4c959aa221802f84a51f13141a725102
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 8bc776f..779f60f 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -73,8 +73,18 @@
 {
     mUidMap = new UidMap();
     mConfigManager = new ConfigManager();
-    mProcessor = new StatsLogProcessor(mUidMap, [](const vector<uint8_t>& log) {
-        // TODO: Update how we send data out of StatsD.
+    mProcessor = new StatsLogProcessor(mUidMap, [this](const ConfigKey& key) {
+        auto sc = getStatsCompanionService();
+        auto receiver = mConfigManager->GetConfigReceiver(key);
+        if (sc == nullptr) {
+            ALOGD("Could not find StatsCompanionService");
+        } else if (receiver.first.size() == 0) {
+            ALOGD("Statscompanion could not find a broadcast receiver for %s",
+                  key.ToString().c_str());
+        } else {
+            sc->sendBroadcast(String16(receiver.first.c_str()),
+                              String16(receiver.second.c_str()));
+        }
     });
 
     mConfigManager->AddListener(mProcessor);
@@ -206,7 +216,11 @@
         }
 
         if (!args[0].compare(String8("send-broadcast"))) {
-            return cmd_trigger_broadcast(args);
+            return cmd_trigger_broadcast(out, args);
+        }
+
+        if (!args[0].compare(String8("print-stats"))) {
+            return cmd_print_stats(out);
         }
 
         if (!args[0].compare(String8("clear-config"))) {
@@ -259,16 +273,56 @@
     fprintf(out, "  NAME          The name of the configuration\n");
     fprintf(out, "\n");
     fprintf(out, "\n");
-    fprintf(out, "usage: adb shell cmd stats send-broadcast PACKAGE CLASS\n");
-    fprintf(out, "  Send a broadcast that triggers one subscriber to fetch metrics.\n");
-    fprintf(out, "  PACKAGE        The name of the package to receive the broadcast.\n");
-    fprintf(out, "  CLASS          The name of the class to receive the broadcast.\n");
+    fprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n");
+    fprintf(out, "  Send a broadcast that triggers the subscriber to fetch metrics.\n");
+    fprintf(out, "  UID           The uid of the configuration. It is only possible to pass\n");
+    fprintf(out, "                the UID parameter on eng builds. If UID is omitted the\n");
+    fprintf(out, "                calling uid is used.\n");
+    fprintf(out, "  NAME          The name of the configuration\n");
+    fprintf(out, "\n");
+    fprintf(out, "\n");
+    fprintf(out, "usage: adb shell cmd stats print-stats\n");
+    fprintf(out, "  Prints some basic stats.\n");
 }
 
-status_t StatsService::cmd_trigger_broadcast(Vector<String8>& args) {
+status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) {
+    string name;
+    bool good = false;
+    int uid;
+    const int argCount = args.size();
+    if (argCount == 2) {
+        // Automatically pick the UID
+        uid = IPCThreadState::self()->getCallingUid();
+        // TODO: What if this isn't a binder call? Should we fail?
+        name.assign(args[1].c_str(), args[1].size());
+        good = true;
+    } else if (argCount == 3) {
+        // If it's a userdebug or eng build, then the shell user can
+        // impersonate other uids.
+        if (mEngBuild) {
+            const char* s = args[1].c_str();
+            if (*s != '\0') {
+                char* end = NULL;
+                uid = strtol(s, &end, 0);
+                if (*end == '\0') {
+                    name.assign(args[2].c_str(), args[2].size());
+                    good = true;
+                }
+            }
+        } else {
+            fprintf(out,
+                    "The metrics can only be dumped for other UIDs on eng or userdebug "
+                            "builds.\n");
+        }
+    }
+    if (!good) {
+        print_cmd_help(out);
+        return UNKNOWN_ERROR;
+    }
+    auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, name));
     auto sc = getStatsCompanionService();
-    sc->sendBroadcast(String16(args[1]), String16(args[2]));
-    ALOGD("StatsService::trigger broadcast succeeded");
+    sc->sendBroadcast(String16(receiver.first.c_str()), String16(receiver.second.c_str()));
+    ALOGD("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(), args[2].c_str());
     return NO_ERROR;
 }
 
@@ -373,7 +427,8 @@
             }
         }
         if (good) {
-            mProcessor->onDumpReport(ConfigKey(uid, name));
+            vector<uint8_t> data;
+            mProcessor->onDumpReport(ConfigKey(uid, name), &data);
             // TODO: print the returned StatsLogReport to file instead of printing to logcat.
             fprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str());
             fprintf(out, "See the StatsLogReport in logcat...\n");
@@ -389,6 +444,15 @@
     }
 }
 
+status_t StatsService::cmd_print_stats(FILE* out) {
+    vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys();
+    for (const ConfigKey& key : configs) {
+        fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
+                mProcessor->GetMetricsSize(key));
+    }
+    return NO_ERROR;
+}
+
 status_t StatsService::cmd_print_stats_log(FILE* out, const Vector<String8>& args) {
     long msec = 0;
 
@@ -573,10 +637,14 @@
     mProcessor->OnLogEvent(event);
 }
 
-Status StatsService::getData(const String16& key, vector<uint8_t>* output) {
+Status StatsService::getData(const String16& key, vector <uint8_t>* output) {
     IPCThreadState* ipc = IPCThreadState::self();
+    ALOGD("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(),
+          ipc->getCallingUid());
     if (checkCallingPermission(String16(kPermissionDump))) {
-        // TODO: Implement this.
+        string keyStr = string(String8(key).string());
+        ConfigKey configKey(ipc->getCallingUid(), keyStr);
+        mProcessor->onDumpReport(configKey, output);
         return Status::ok();
     } else {
         return Status::fromExceptionCode(binder::Status::EX_SECURITY);
@@ -588,10 +656,9 @@
                                       const String16& package, const String16& cls,
                                       bool* success) {
     IPCThreadState* ipc = IPCThreadState::self();
-    int32_t* uid = reinterpret_cast<int32_t*>(ipc->getCallingUid());
     if (checkCallingPermission(String16(kPermissionDump))) {
         string keyString = string(String8(key).string());
-        ConfigKey configKey(*uid, keyString);
+        ConfigKey configKey(ipc->getCallingUid(), keyString);
         StatsdConfig cfg;
         cfg.ParseFromArray(&config[0], config.size());
         mConfigManager->UpdateConfig(configKey, cfg);
@@ -607,7 +674,8 @@
 Status StatsService::removeConfiguration(const String16& key, bool* success) {
     IPCThreadState* ipc = IPCThreadState::self();
     if (checkCallingPermission(String16(kPermissionDump))) {
-        // TODO: Implement this.
+        string keyStr = string(String8(key).string());
+        mConfigManager->RemoveConfig(ConfigKey(ipc->getCallingUid(), keyStr));
         return Status::ok();
     } else {
         *success = false;
@@ -615,7 +683,7 @@
     }
 }
 
-void StatsService::binderDied(const wp<IBinder>& who) {
+void StatsService::binderDied(const wp <IBinder>& who) {
     for (size_t i = 0; i < mCallbacks.size(); i++) {
         if (IInterface::asBinder(mCallbacks[i]) == who) {
             mCallbacks.removeAt(i);