Adds client API for interacting with statsd.

This API will primarily be used by GmsCore to send updated configs.
Also, sending a config will implicitly notify the StatsD that this
client wants to know when it should request data for this config.

We send a broadcast so that all interested subscribers can know if
data needs to be pulled.

Test: Manually tested that sending broadcast works via new adb
command added in StatsService.

Change-Id: I23cdd1df706036e14b32c3d01af30c3d4af819fa
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 11c5de1..07b1c5c 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -18,6 +18,8 @@
 #include "Log.h"
 
 #include "StatsService.h"
+#include "config/ConfigKey.h"
+#include "config/ConfigManager.h"
 #include "storage/DropboxReader.h"
 
 #include <android-base/file.h>
@@ -39,6 +41,8 @@
 namespace os {
 namespace statsd {
 
+constexpr const char* kPermissionDump = "android.permission.DUMP";
+
 // ======================================================================
 /**
  * Watches for the death of the stats companion (system process).
@@ -67,8 +71,8 @@
 {
     mUidMap = new UidMap();
     mConfigManager = new ConfigManager();
-    mProcessor = new StatsLogProcessor(mUidMap, [this](const vector<uint8_t>& log) {
-      pushLog(log);
+    mProcessor = new StatsLogProcessor(mUidMap, [](const vector<uint8_t>& log) {
+        // TODO: Update how we send data out of StatsD.
     });
 
     mConfigManager->AddListener(mProcessor);
@@ -198,6 +202,10 @@
         if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
             return cmd_print_pulled_metrics(out, args);
         }
+
+        if (!args[0].compare(String8("send-broadcast"))) {
+            return cmd_trigger_broadcast(args);
+        }
     }
 
     print_cmd_help(out);
@@ -238,6 +246,19 @@
     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 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");
+}
+
+status_t StatsService::cmd_trigger_broadcast(Vector<String8>& args) {
+    auto sc = getStatsCompanionService();
+    sc->sendBroadcast(String16(args[1]), String16(args[2]));
+    ALOGD("StatsService::trigger broadcast succeeded");
+    return NO_ERROR;
 }
 
 status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
@@ -520,29 +541,51 @@
     mProcessor->OnLogEvent(event);
 }
 
-Status StatsService::requestPush() {
-    mProcessor->flush();
-    return Status::ok();
+Status StatsService::getData(const String16& key, vector<uint8_t>* output) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    if (checkCallingPermission(String16(kPermissionDump),
+                               reinterpret_cast<int32_t*>(ipc->getCallingPid()),
+                               reinterpret_cast<int32_t*>(ipc->getCallingUid()))) {
+        // TODO: Implement this.
+        return Status::ok();
+    } else {
+        return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+    }
 }
 
-Status StatsService::pushLog(const vector<uint8_t>& log) {
-    std::lock_guard<std::mutex> lock(mLock);
-    for (size_t i = 0; i < mCallbacks.size(); i++) {
-        mCallbacks[i]->onReceiveLogs((vector<uint8_t>*)&log);
+Status StatsService::addConfiguration(const String16& key,
+                                      const vector <uint8_t>& config,
+                                      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),
+                               reinterpret_cast<int32_t*>(ipc->getCallingPid()), uid)) {
+        string keyString = string(String8(key).string());
+        ConfigKey configKey(*uid, keyString);
+        StatsdConfig cfg;
+        cfg.ParseFromArray(&config[0], config.size());
+        mConfigManager->UpdateConfig(configKey, cfg);
+        mConfigManager->SetConfigReceiver(configKey, string(String8(package).string()),
+                                          string(String8(cls).string()));
+        *success = true;
+        return Status::ok();
+    } else {
+        return Status::fromExceptionCode(binder::Status::EX_SECURITY);
     }
-    return Status::ok();
 }
 
-Status StatsService::subscribeStatsLog(const sp<IStatsCallbacks>& callback) {
-    std::lock_guard<std::mutex> lock(mLock);
-    for (size_t i = 0; i < mCallbacks.size(); i++) {
-        if (mCallbacks[i] == callback) {
-           return Status::fromStatusT(-errno);
-        }
+Status StatsService::removeConfiguration(const String16& key, bool* success) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    if (checkCallingPermission(String16(kPermissionDump),
+                               reinterpret_cast<int32_t*>(ipc->getCallingPid()),
+                               reinterpret_cast<int32_t*>(ipc->getCallingUid()))) {
+        // TODO: Implement this.
+        return Status::ok();
+    } else {
+        *success = false;
+        return Status::fromExceptionCode(binder::Status::EX_SECURITY);
     }
-    mCallbacks.add(callback);
-    IInterface::asBinder(callback)->linkToDeath(this);
-    return Status::ok();
 }
 
 void StatsService::binderDied(const wp<IBinder>& who) {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 1d7e5a61..7991d82 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -71,20 +71,22 @@
     virtual void OnLogEvent(const LogEvent& event);
 
     /**
-     * Binder call to force trigger pushLog. This would be called by callback
-     * clients.
+     * Binder call for clients to request data for this configuration key.
      */
-    virtual Status requestPush() override;
+    virtual Status getData(const String16& key, vector<uint8_t>* output) override;
 
     /**
-     * Pushes stats log entries from statsd to callback clients.
+     * Binder call to let clients send a configuration and indicate they're interested when they
+     * should requestData for this configuration.
      */
-    Status pushLog(const vector<uint8_t>& log);
+    virtual Status addConfiguration(const String16& key, const vector <uint8_t>& config,
+                                   const String16& package, const String16& cls, bool* success)
+    override;
 
     /**
-     * Binder call to listen to statsd to send stats log entries.
+     * Binder call to allow clients to remove the specified configuration.
      */
-    virtual Status subscribeStatsLog(const sp<IStatsCallbacks>& callbacks) override;
+    virtual Status removeConfiguration(const String16& key, bool* success) override;
 
     // TODO: public for testing since statsd doesn't run when system starts. Change to private
     // later.
@@ -120,6 +122,11 @@
     void print_cmd_help(FILE* out);
 
     /**
+     * Trigger a broadcast.
+     */
+    status_t cmd_trigger_broadcast(Vector<String8>& args);
+
+    /**
      * Handle the config sub-command.
      */
     status_t cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index d86ab57..2a8a741 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -60,6 +60,14 @@
     }
 }
 
+void ConfigManager::SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls) {
+    mConfigReceivers[key] = pair<string, string>(pkg, cls);
+}
+
+void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
+    mConfigReceivers.erase(key);
+}
+
 void ConfigManager::RemoveConfig(const ConfigKey& key) {
     unordered_map<ConfigKey, StatsdConfig>::iterator it = mConfigs.find(key);
     if (it != mConfigs.end()) {
@@ -85,6 +93,7 @@
         if (it->first.GetUid() == uid) {
             removed.push_back(it->first);
             it = mConfigs.erase(it);
+            mConfigReceivers.erase(it->first);
         } else {
             it++;
         }
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index 5d73eaf..c21247f9 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -32,6 +32,7 @@
 using std::string;
 using std::unordered_map;
 using std::vector;
+using std::pair;
 
 /**
  * Keeps track of which configurations have been set from various sources.
@@ -64,6 +65,16 @@
     void UpdateConfig(const ConfigKey& key, const StatsdConfig& data);
 
     /**
+     * Sets the broadcast receiver for a configuration key.
+     */
+    void SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls);
+
+    /**
+     * Erase any broadcast receiver associated with this config key.
+     */
+    void RemoveConfigReceiver(const ConfigKey& key);
+
+    /**
      * A configuration was removed.
      *
      * Reports this to listeners.
@@ -87,11 +98,17 @@
     void update_saved_configs();
 
     /**
-     * The Configs that have been set
+     * The Configs that have been set. Each config should
      */
     unordered_map<ConfigKey, StatsdConfig> mConfigs;
 
     /**
+     * Each config key can be subscribed by up to one receiver, specified as the package name and
+     * class name.
+     */
+    unordered_map<ConfigKey, pair<string, string>> mConfigReceivers;
+
+    /**
      * The ConfigListeners that will be told about changes.
      */
     vector<sp<ConfigListener>> mListeners;