Adds aidl definitions and their implementations for binder transfer of
statsd entries to clients. This change only includes changes on statds
side and does not include java library for clients to import. Java
library will be a separate change as it requires system api review.

Test: statsd, statsd_test
Change-Id: I306c6e9687801668cc0145b12d38406bfe634775
diff --git a/Android.mk b/Android.mk
index 648544a..9890bb4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -270,6 +270,7 @@
 	core/java/android/os/IRecoverySystemProgressListener.aidl \
 	core/java/android/os/IRemoteCallback.aidl \
 	core/java/android/os/ISchedulingPolicyService.aidl \
+	core/java/android/os/IStatsCallbacks.aidl \
 	core/java/android/os/IStatsCompanionService.aidl \
 	core/java/android/os/IStatsManager.aidl \
 	core/java/android/os/IThermalEventListener.aidl \
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 8946aed..50f6311 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -15,6 +15,7 @@
 LOCAL_PATH:= $(call my-dir)
 
 statsd_common_src := \
+    ../../core/java/android/os/IStatsCallbacks.aidl \
     ../../core/java/android/os/IStatsCompanionService.aidl \
     ../../core/java/android/os/IStatsManager.aidl \
     src/stats_log.proto \
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index e7825cf..68f48a4 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -33,8 +33,9 @@
 namespace os {
 namespace statsd {
 
-StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap)
-    : m_dropbox_writer("all-logs"), mUidMap(uidMap) {
+StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
+                                     const std::function<void(const vector<uint8_t>&)>& pushLog)
+    : m_dropbox_writer("all-logs"), mUidMap(uidMap), mPushLog(pushLog) {
 }
 
 StatsLogProcessor::~StatsLogProcessor() {
@@ -91,6 +92,41 @@
     }
 }
 
+void StatsLogProcessor::addEventMetricData(const EventMetricData& eventMetricData) {
+    // TODO: Replace this code when MetricsManager.onDumpReport() is ready to
+    // get a list of byte arrays.
+    flushIfNecessary(eventMetricData);
+    const int numBytes = eventMetricData.ByteSize();
+    char buffer[numBytes];
+    eventMetricData.SerializeToArray(&buffer[0], numBytes);
+    string bufferString(buffer, numBytes);
+    mEvents.push_back(bufferString);
+    mBufferSize += eventMetricData.ByteSize();
+}
+
+void StatsLogProcessor::flushIfNecessary(const EventMetricData& eventMetricData) {
+    if (eventMetricData.ByteSize() + mBufferSize > kMaxSerializedBytes) {
+      flush();
+    }
+}
+
+void StatsLogProcessor::flush() {
+    StatsLogReport logReport;
+    for (string eventBuffer : mEvents) {
+        EventMetricData eventFromBuffer;
+        eventFromBuffer.ParseFromString(eventBuffer);
+        EventMetricData* newEntry = logReport.mutable_event_metrics()->add_data();
+        newEntry->CopyFrom(eventFromBuffer);
+    }
+
+    const int numBytes = logReport.ByteSize();
+    vector<uint8_t> logReportBuffer(numBytes);
+    logReport.SerializeToArray(&logReportBuffer[0], numBytes);
+    mPushLog(logReportBuffer);
+    mEvents.clear();
+    mBufferSize = 0;
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 3cefd29..08090c1 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -33,7 +33,8 @@
 
 class StatsLogProcessor : public ConfigListener {
 public:
-    StatsLogProcessor(const sp<UidMap>& uidMap);
+    StatsLogProcessor(const sp<UidMap>& uidMap,
+                      const std::function<void(const vector<uint8_t>&)>& pushLog);
     virtual ~StatsLogProcessor();
 
     virtual void OnLogEvent(const LogEvent& event);
@@ -44,6 +45,9 @@
     // TODO: Once we have the ProtoOutputStream in c++, we can just return byte array.
     std::vector<StatsLogReport> onDumpReport(const ConfigKey& key);
 
+    /* Request a flush through a binder call. */
+    void flush();
+
 private:
     // TODO: use EventMetrics to log the events.
     DropboxWriter m_dropbox_writer;
@@ -51,6 +55,33 @@
     std::unordered_map<ConfigKey, std::unique_ptr<MetricsManager>> mMetricsManagers;
 
     sp<UidMap> mUidMap;  // Reference to the UidMap to lookup app name and version for each uid.
+
+    /* Max *serialized* size of the logs kept in memory before flushing through binder call.
+       Proto lite does not implement the SpaceUsed() function which gives the in memory byte size.
+       So we cap memory usage by limiting the serialized size. Note that protobuf's in memory size
+       is higher than its serialized size.
+     */
+    static const size_t kMaxSerializedBytes = 16 * 1024;
+
+    /* List of data that was captured for a single metric over a given interval of time. */
+    vector<string> mEvents;
+
+    /* Current *serialized* size of the logs kept in memory.
+       To save computation, we will not calculate the size of the StatsLogReport every time when a
+       new entry is added, which would recursively call ByteSize() on every log entry. Instead, we
+       keep the sum of all individual stats log entry sizes. The size of a proto is approximately
+       the sum of the size of all member protos.
+     */
+    size_t mBufferSize = 0;
+
+    /* Check if the buffer size exceeds the max buffer size when the new entry is added, and flush
+       the logs to dropbox if true. */
+    void flushIfNecessary(const EventMetricData& eventMetricData);
+
+    /* Append event metric data to StatsLogReport. */
+    void addEventMetricData(const EventMetricData& eventMetricData);
+
+    std::function<void(const vector<uint8_t>&)> mPushLog;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 1faeee0..604753e 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -68,7 +68,9 @@
     mStatsPullerManager = new StatsPullerManager();
     mUidMap = new UidMap();
     mConfigManager = new ConfigManager();
-    mProcessor = new StatsLogProcessor(mUidMap);
+    mProcessor = new StatsLogProcessor(mUidMap, [this](const vector<uint8_t>& log) {
+      pushLog(log);
+    });
 
     mConfigManager->AddListener(mProcessor);
 
@@ -507,6 +509,40 @@
     mProcessor->OnLogEvent(event);
 }
 
+Status StatsService::requestPush() {
+    mProcessor->flush();
+    return Status::ok();
+}
+
+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);
+    }
+    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);
+        }
+    }
+    mCallbacks.add(callback);
+    IInterface::asBinder(callback)->linkToDeath(this);
+    return Status::ok();
+}
+
+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);
+            break;
+        }
+    }
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 449a2b8..7f04658 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -24,6 +24,7 @@
 #include "packages/UidMap.h"
 
 #include <android/os/BnStatsManager.h>
+#include <android/os/IStatsCallbacks.h>
 #include <android/os/IStatsCompanionService.h>
 #include <binder/IResultReceiver.h>
 #include <binder/IShellCallback.h>
@@ -42,7 +43,7 @@
 namespace os {
 namespace statsd {
 
-class StatsService : public BnStatsManager, public LogListener {
+class StatsService : public BnStatsManager, public LogListener, public IBinder::DeathRecipient {
 public:
     StatsService(const sp<Looper>& handlerLooper);
     virtual ~StatsService();
@@ -70,6 +71,22 @@
      */
     virtual void OnLogEvent(const LogEvent& event);
 
+    /**
+     * Binder call to force trigger pushLog. This would be called by callback
+     * clients.
+     */
+    virtual Status requestPush() override;
+
+    /**
+     * Pushes stats log entries from statsd to callback clients.
+     */
+    Status pushLog(const vector<uint8_t>& log);
+
+    /**
+     * Binder call to listen to statsd to send stats log entries.
+     */
+    virtual Status subscribeStatsLog(const sp<IStatsCallbacks>& callbacks) override;
+
     // TODO: public for testing since statsd doesn't run when system starts. Change to private
     // later.
     /** Inform statsCompanion that statsd is ready. */
@@ -78,6 +95,9 @@
     /** Fetches and returns the StatsCompanionService. */
     static sp<IStatsCompanionService> getStatsCompanionService();
 
+    /** IBinder::DeathRecipient */
+    virtual void binderDied(const wp<IBinder>& who) override;
+
 private:
     /**
      * Load system properties at init.
@@ -159,6 +179,16 @@
      * Whether this is an eng build.
      */
     bool mEngBuild;
+
+    /**
+     * Lock for callback handling.
+     */
+    std::mutex mLock;
+
+    /**
+     * Vector maintaining the list of callbacks for clients.
+     */
+    Vector< sp<IStatsCallbacks> > mCallbacks;
 };
 
 }  // namespace statsd
diff --git a/core/java/android/os/IStatsCallbacks.aidl b/core/java/android/os/IStatsCallbacks.aidl
new file mode 100644
index 0000000..02e7cd3
--- /dev/null
+++ b/core/java/android/os/IStatsCallbacks.aidl
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+  * Callback for Statsd to allow binder calls to clients.
+  * {@hide}
+  */
+interface IStatsCallbacks {
+    void onReceiveLogs(out byte[] log);
+}
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index daacc4e..480296c 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import android.os.IStatsCallbacks;
+
 /**
   * Binder interface to communicate with the statistics management service.
   * {@hide}
@@ -61,4 +63,15 @@
      * Inform stats that an app was removed.
      */
     oneway void informOnePackageRemoved(in String app, in int uid);
+
+    /**
+     * Trigger pushLog to force push stats log entries from statsd on client side.
+     */
+    void requestPush();
+
+    /**
+     * Listen to statsd to send stats log entries.
+     * TODO: Limit callbacks with specific configurations.
+     */
+    void subscribeStatsLog(IStatsCallbacks callbacks);
 }