Changes pulled data to use Parcel objects.

Previously, pulled data was returned as a string. We instead
return the data as an array of StatsLogEventWrapper, which encodes
using the binary-encoded format liblog uses. StatsD uses the same
parsing as for pushed events to convert these. This CL also fixes
the parsing of log_msg since the strings were previously emptied
before we had a chance to read the values.

Note that the cpp-aidl can't support List of Parcelable, so we
have to return the results as an array.

Test: Manual using the new command in StatsService to print results.
Also created a new unit-test by creating a dummy pull code of -1,
but this test is deleted since it required creating a fake output in
StatsCompanionService.

Change-Id: I1cfb9ea081a59292a60e934e8527adc40982ed80
diff --git a/Android.mk b/Android.mk
index 6470e34..3bc6a86 100644
--- a/Android.mk
+++ b/Android.mk
@@ -579,6 +579,8 @@
 
 LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings
 
+LOCAL_AIDL_INCLUDES += core/java/android/os/StatsLogEventWrapper.aidl
+
 LOCAL_AIDL_INCLUDES += frameworks/base/lowpan/java
 LOCAL_SRC_FILES += \
 	lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl \
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 3c2f2d5..8505d4c 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -49,7 +49,8 @@
     src/stats_util.cpp
 
 statsd_common_c_includes := \
-    $(LOCAL_PATH)/src
+    $(LOCAL_PATH)/src \
+    $(LOCAL_PATH)/../../libs/services/include
 
 statsd_common_aidl_includes := \
     $(LOCAL_PATH)/../../core/java
@@ -95,7 +96,7 @@
 endif
 LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
 
-LOCAL_AIDL_INCLUDES := $(statsd_common_c_includes)
+LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
 LOCAL_C_INCLUDES += $(statsd_common_c_includes)
 
 LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries)
@@ -117,7 +118,7 @@
 LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_AIDL_INCLUDES := $(statsd_common_c_includes)
+LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
 LOCAL_C_INCLUDES += $(statsd_common_c_includes)
 
 LOCAL_CFLAGS += \
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 87616d3..1faeee0 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -63,9 +63,9 @@
 
 // ======================================================================
 StatsService::StatsService(const sp<Looper>& handlerLooper)
-    : mStatsPullerManager(),
-      mAnomalyMonitor(new AnomalyMonitor(2))  // TODO: Put this comment somewhere better
+    : mAnomalyMonitor(new AnomalyMonitor(2))  // TODO: Put this comment somewhere better
 {
+    mStatsPullerManager = new StatsPullerManager();
     mUidMap = new UidMap();
     mConfigManager = new ConfigManager();
     mProcessor = new StatsLogProcessor(mUidMap);
@@ -193,6 +193,10 @@
         if (!args[0].compare(String8("dump-report"))) {
             return cmd_dump_report(out, err, args);
         }
+
+        if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
+            return cmd_print_pulled_metrics(out, args);
+        }
     }
 
     print_cmd_help(out);
@@ -210,6 +214,11 @@
     fprintf(out, "  Prints the UID, app name, version mapping.\n");
     fprintf(out, "\n");
     fprintf(out, "\n");
+    fprintf(out, "usage: adb shell cmds stats pull-source [int] \n");
+    fprintf(out, "\n");
+    fprintf(out, "  Prints the output of a pulled metrics source (int indicates source)\n");
+    fprintf(out, "\n");
+    fprintf(out, "\n");
     fprintf(out, "usage: adb shell cmd stats config remove [UID] NAME\n");
     fprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
     fprintf(out, "\n");
@@ -353,6 +362,16 @@
     return NO_ERROR;
 }
 
+status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
+    int s = atoi(args[1].c_str());
+    auto stats = mStatsPullerManager->Pull(s);
+    for (const auto& it : stats) {
+        fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
+    }
+    fprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size());
+    return NO_ERROR;
+}
+
 Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
                                       const vector<String16>& app) {
     if (DEBUG) ALOGD("StatsService::informAllUidData was called");
@@ -414,10 +433,6 @@
 
     if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded");
     // TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them.
-    String16 output = mStatsPullerManager.pull(StatsPullerManager::KERNEL_WAKELOCKS);
-    // TODO: do something useful with the output instead of writing a string to screen.
-    ALOGD("%s", String8(output).string());
-    ALOGD("%d", int(output.size()));
 
     return Status::ok();
 }
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 294aec8..449a2b8 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -121,6 +121,11 @@
     status_t cmd_print_uid_map(FILE* out);
 
     /**
+     * Print contents of a pulled metrics source.
+     */
+    status_t cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args);
+
+    /**
      * Update a configuration.
      */
     void set_config(int uid, const string& name, const StatsdConfig& config);
@@ -132,9 +137,8 @@
 
     /**
      * Fetches external metrics.
-     * TODO: This should be an sp<>
      */
-    StatsPullerManager mStatsPullerManager;
+    sp<StatsPullerManager> mStatsPullerManager;
 
     /**
      * Tracks the configurations that have been passed to statsd.
diff --git a/cmds/statsd/src/external/KernelWakelockPuller.cpp b/cmds/statsd/src/external/KernelWakelockPuller.cpp
index b9abee0..ee072f8 100644
--- a/cmds/statsd/src/external/KernelWakelockPuller.cpp
+++ b/cmds/statsd/src/external/KernelWakelockPuller.cpp
@@ -37,9 +37,9 @@
 
 // The reading and parsing are implemented in Java. It is not difficult to port over. But for now
 // let StatsCompanionService handle that and send the data back.
-String16 KernelWakelockPuller::pull() {
+vector<StatsLogEventWrapper> KernelWakelockPuller::pull() {
     sp<IStatsCompanionService> statsCompanion = StatsService::getStatsCompanionService();
-    String16 returned_value("");
+    vector<StatsLogEventWrapper> returned_value;
     if (statsCompanion != NULL) {
         Status status = statsCompanion->pullData(KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS,
                                                  &returned_value);
@@ -47,12 +47,10 @@
             ALOGW("error pulling kernel wakelock");
         }
         ALOGD("KernelWakelockPuller::pull succeeded!");
-        // TODO: remove this when we integrate into aggregation chain.
-        ALOGD("%s", String8(returned_value).string());
         return returned_value;
     } else {
         ALOGW("statsCompanion not found!");
-        return String16();
+        return returned_value;
     }
 }
 
diff --git a/cmds/statsd/src/external/KernelWakelockPuller.h b/cmds/statsd/src/external/KernelWakelockPuller.h
index 1ec3376..c12806c 100644
--- a/cmds/statsd/src/external/KernelWakelockPuller.h
+++ b/cmds/statsd/src/external/KernelWakelockPuller.h
@@ -29,7 +29,7 @@
     // a number of stats need to be pulled from StatsCompanionService
     //
     const static int PULL_CODE_KERNEL_WAKELOCKS;
-    String16 pull() override;
+    vector<StatsLogEventWrapper> pull() override;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 5e556b8..6655629 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -17,7 +17,12 @@
 #ifndef STATSD_STATSPULLER_H
 #define STATSD_STATSPULLER_H
 
+#include <android/os/StatsLogEventWrapper.h>
 #include <utils/String16.h>
+#include <vector>
+
+using android::os::StatsLogEventWrapper;
+using std::vector;
 
 namespace android {
 namespace os {
@@ -26,8 +31,8 @@
 class StatsPuller {
 public:
     virtual ~StatsPuller(){};
-    // use string for now, until we figure out how to integrate into the aggregation path
-    virtual String16 pull() = 0;
+
+    virtual vector<StatsLogEventWrapper> pull() = 0;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 6e8d58bc..7f554d3 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -21,6 +21,11 @@
 #include "KernelWakelockPuller.h"
 #include "StatsService.h"
 #include "external/StatsPullerManager.h"
+#include "logd/LogEvent.h"
+#include <cutils/log.h>
+#include <algorithm>
+
+#include <iostream>
 
 using namespace android;
 
@@ -35,13 +40,27 @@
             {static_cast<int>(KERNEL_WAKELOCKS), std::make_unique<KernelWakelockPuller>()});
 }
 
-String16 StatsPullerManager::pull(int pullCode) {
+vector<std::shared_ptr<LogEvent>> StatsPullerManager::Pull(int pullCode) {
     if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
+
+    vector<std::shared_ptr<LogEvent>> ret;
     if (mStatsPullers.find(pullCode) != mStatsPullers.end()) {
-        return (mStatsPullers.find(pullCode)->second)->pull();
+        vector<StatsLogEventWrapper> outputs = (mStatsPullers.find(pullCode)->second)->pull();
+        for (const StatsLogEventWrapper& it : outputs) {
+            log_msg tmp;
+            tmp.entry_v1.len = it.bytes.size();
+            // Manually set the header size to 28 bytes to match the pushed log events.
+            tmp.entry.hdr_size = 28;
+            // And set the received bytes starting after the 28 bytes reserved for header.
+            std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + 28);
+            std::shared_ptr<LogEvent> evt = std::make_shared<LogEvent>(tmp);
+            ret.push_back(evt);
+            // ret.emplace_back(tmp);
+        }
+        return ret;
     } else {
         ALOGD("Unknown pull code %d", pullCode);
-        return String16();
+        return ret;  // Return early since we don't know what to pull.
     }
 }
 
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index f143424..e46aec1 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -20,6 +20,8 @@
 #include <utils/String16.h>
 #include <unordered_map>
 #include "external/StatsPuller.h"
+#include "logd/LogEvent.h"
+#include "matchers/matcher_util.h"
 
 namespace android {
 namespace os {
@@ -27,7 +29,7 @@
 
 const static int KERNEL_WAKELOCKS = 1;
 
-class StatsPullerManager {
+class StatsPullerManager : public virtual RefBase {
 public:
     // Enums of pulled data types (pullCodes)
     // These values must be kept in sync with com/android/server/stats/StatsCompanionService.java.
@@ -35,7 +37,8 @@
     const static int KERNEL_WAKELOCKS;
     StatsPullerManager();
 
-    String16 pull(const int pullCode);
+    // We return a vector of shared_ptr since LogEvent's copy constructor is not available.
+    vector<std::shared_ptr<LogEvent>> Pull(const int pullCode);
 
 private:
     std::unordered_map<int, std::unique_ptr<StatsPuller>> mStatsPullers;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 032b4b8..fb992c1 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -23,29 +23,30 @@
 namespace statsd {
 
 using std::ostringstream;
+using std::string;
 
-LogEvent::LogEvent(const log_msg& msg) {
-    init(msg);
+// We need to keep a copy of the android_log_event_list owned by this instance so that the char*
+// for strings is not cleared before we can read them.
+LogEvent::LogEvent(log_msg msg) : mList(msg) {
+    init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &mList);
 }
 
-LogEvent::LogEvent(int64_t timestampNs, android_log_event_list* reader) {
-    init(timestampNs, reader);
+LogEvent::LogEvent(int tag) : mList(tag) {
 }
 
 LogEvent::~LogEvent() {
 }
 
+void LogEvent::init() {
+    mList.convert_to_reader();
+    init(mTimestampNs, &mList);
+}
+
 /**
  * The elements of each log event are stored as a vector of android_log_list_elements.
  * The goal is to do as little preprocessing as possible, because we read a tiny fraction
  * of the elements that are written to the log.
  */
-void LogEvent::init(const log_msg& msg) {
-
-    android_log_event_list list(const_cast<log_msg&>(msg));
-    init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &list);
-}
-
 void LogEvent::init(int64_t timestampNs, android_log_event_list* reader) {
     mTimestampNs = timestampNs;
     mTagId = reader->tag();
@@ -79,6 +80,10 @@
     } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
 }
 
+android_log_event_list* LogEvent::GetAndroidLogEventList() {
+    return &mList;
+}
+
 int64_t LogEvent::GetLong(size_t key, status_t* err) const {
     if (key < 1 || (key - 1)  >= mElements.size()) {
         *err = BAD_INDEX;
@@ -109,7 +114,8 @@
         *err = BAD_TYPE;
         return NULL;
     }
-    return elem.data.string;
+    // Need to add the '/0' at the end by specifying the length of the string.
+    return string(elem.data.string, elem.len).c_str();
 }
 
 bool LogEvent::GetBool(size_t key, status_t* err) const {
@@ -189,7 +195,8 @@
         } else if (elem.type == EVENT_TYPE_FLOAT) {
             result << elem.data.float32;
         } else if (elem.type == EVENT_TYPE_STRING) {
-            result << elem.data.string;
+            // Need to add the '/0' at the end by specifying the length of the string.
+            result << string(elem.data.string, elem.len).c_str();
         }
     }
     result << " }";
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 464afca..4102675 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -22,6 +22,7 @@
 #include <log/log_event_list.h>
 #include <log/log_read.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -40,12 +41,16 @@
     /**
      * Read a LogEvent from a log_msg.
      */
-    explicit LogEvent(const log_msg& msg);
+    explicit LogEvent(log_msg msg);
 
     /**
-     * Read a LogEvent from an android_log_context.
+     * Constructs a LogEvent with the specified tag and creates an android_log_event_list in write
+     * mode. Obtain this list with the getter. Make sure to call init() before attempting to read
+     * any of the values. This constructor is useful for unit-testing since we can't pass in an
+     * android_log_event_list since there is no copy constructor or assignment operator available.
      */
-    explicit LogEvent(int64_t timestampNs, android_log_event_list* reader);
+    explicit LogEvent(int tag);
+
     ~LogEvent();
 
     /**
@@ -85,6 +90,19 @@
      */
     KeyValuePair GetKeyValueProto(size_t key) const;
 
+    /**
+     * A pointer to the contained log_event_list.
+     *
+     * @return The android_log_event_list contained within.
+     */
+    android_log_event_list* GetAndroidLogEventList();
+
+    /**
+     * Used with the constructor where tag is passed in. Converts the log_event_list to read mode
+     * and prepares the list for reading.
+     */
+    void init();
+
 private:
     /**
      * Don't copy, it's slower. If we really need this we can add it but let's try to
@@ -103,6 +121,8 @@
     void init(int64_t timestampNs, android_log_event_list* reader);
 
     vector<android_log_list_element> mElements;
+    // Need a copy of the android_log_event_list so the strings are not cleared.
+    android_log_event_list mList;
     long mTimestampNs;
     int mTagId;
 };
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 19403c0..fdfe8ef 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -42,12 +42,10 @@
     auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
     simpleMatcher->set_tag(TAG_ID);
 
-    // Set up the event
-    android_log_event_list list(TAG_ID);
+    LogEvent event(TAG_ID);
 
     // Convert to a LogEvent
-    list.convert_to_reader();
-    LogEvent event(999, &list);
+    event.init();
 
     // Test
     EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
@@ -64,13 +62,13 @@
     keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2);
 
     // Set up the event
-    android_log_event_list list(TAG_ID);
-    list << true;
-    list << false;
+    LogEvent event(TAG_ID);
+    auto list = event.GetAndroidLogEventList();
+    *list << true;
+    *list << false;
 
     // Convert to a LogEvent
-    list.convert_to_reader();
-    LogEvent event(999, &list);
+    event.init();
 
     // Test
     keyValue1->set_eq_bool(true);
@@ -100,12 +98,12 @@
     keyValue->set_eq_string("some value");
 
     // Set up the event
-    android_log_event_list list(TAG_ID);
-    list << "some value";
+    LogEvent event(TAG_ID);
+    auto list = event.GetAndroidLogEventList();
+    *list << "some value";
 
     // Convert to a LogEvent
-    list.convert_to_reader();
-    LogEvent event(999, &list);
+    event.init();
 
     // Test
     EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
@@ -121,12 +119,11 @@
     keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
 
     // Set up the event
-    android_log_event_list list(TAG_ID);
-    list << 11;
+    LogEvent event(TAG_ID);
+    auto list = event.GetAndroidLogEventList();
+    *list << 11;
 
-    // Convert to a LogEvent
-    list.convert_to_reader();
-    LogEvent event(999, &list);
+    event.init();
 
     // Test
 
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index d8f9567..20f6c8e 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import android.os.StatsLogEventWrapper;
+
 /**
   * Binder interface to communicate with the Java-based statistics service helper.
   * {@hide}
@@ -50,5 +52,5 @@
     oneway void cancelPollingAlarms();
 
     /** Pull the specified data. Results will be sent to statsd when complete. */
-    String pullData(int pullCode);
+    StatsLogEventWrapper[] pullData(int pullCode);
 }
diff --git a/core/java/android/os/StatsLogEventWrapper.aidl b/core/java/android/os/StatsLogEventWrapper.aidl
new file mode 100644
index 0000000..766343e
--- /dev/null
+++ b/core/java/android/os/StatsLogEventWrapper.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 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;
+
+/** @hide */
+parcelable StatsLogEventWrapper cpp_header "android/os/StatsLogEventWrapper.h";
\ No newline at end of file
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
new file mode 100644
index 0000000..9491bec
--- /dev/null
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 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;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Wrapper class for sending data from Android OS to StatsD.
+ *
+ * @hide
+ */
+public final class StatsLogEventWrapper implements Parcelable {
+    private ByteArrayOutputStream mStorage = new ByteArrayOutputStream();
+
+    // Below are constants copied from log/log.h
+    private static final int EVENT_TYPE_INT = 0;  /* int32_t */
+    private static final int EVENT_TYPE_LONG = 1; /* int64_t */
+    private static final int EVENT_TYPE_STRING = 2;
+    private static final int EVENT_TYPE_LIST = 3;
+    private static final int EVENT_TYPE_FLOAT = 4;
+
+    /**
+     * Creates a log_event that is binary-encoded as implemented in
+     * system/core/liblog/log_event_list.c; this allows us to use the same parsing logic in statsd
+     * for pushed and pulled data. The write* methods must be called in the same order as their
+     * field number. There is no checking that the correct number of write* methods is called.
+     * We also write an END_LIST character before beginning to write to parcel, but this END_LIST
+     * may be unnecessary.
+     *
+     * @param tag    The integer representing the tag for this event.
+     * @param fields The number of fields specified in this event.
+     */
+    public StatsLogEventWrapper(int tag, int fields) {
+        // Write four bytes from tag, starting with least-significant bit.
+        write4Bytes(tag);
+        mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
+        mStorage.write(fields); // Indicate number of elements in this list.
+    }
+
+    /**
+     * Boilerplate for Parcel.
+     */
+    public static final Parcelable.Creator<StatsLogEventWrapper> CREATOR = new
+            Parcelable.Creator<StatsLogEventWrapper>() {
+                public StatsLogEventWrapper createFromParcel(Parcel in) {
+                    return new StatsLogEventWrapper(in);
+                }
+
+                public StatsLogEventWrapper[] newArray(int size) {
+                    return new StatsLogEventWrapper[size];
+                }
+            };
+
+    private void write4Bytes(int val) {
+        mStorage.write(val);
+        mStorage.write(val >>> 8);
+        mStorage.write(val >>> 16);
+        mStorage.write(val >>> 24);
+    }
+
+    private void write8Bytes(long val) {
+        write4Bytes((int) (val & 0xFFFFFFFF)); // keep the lowe 32-bits
+        write4Bytes((int) (val >>> 32)); // Write the high 32-bits.
+    }
+
+    /**
+     * Adds 32-bit integer to output.
+     */
+    public void writeInt(int val) {
+        mStorage.write(EVENT_TYPE_INT);
+        write4Bytes(val);
+    }
+
+    /**
+     * Adds 64-bit long to output.
+     */
+    public void writeLong(long val) {
+        mStorage.write(EVENT_TYPE_LONG);
+        write8Bytes(val);
+    }
+
+    /**
+     * Adds a 4-byte floating point value to output.
+     */
+    public void writeFloat(float val) {
+        int v = Float.floatToIntBits(val);
+        mStorage.write(EVENT_TYPE_FLOAT);
+        write4Bytes(v);
+    }
+
+    /**
+     * Adds a string to the output.
+     */
+    public void writeString(String val) {
+        mStorage.write(EVENT_TYPE_STRING);
+        write4Bytes(val.length());
+        byte[] bytes = val.getBytes(StandardCharsets.UTF_8);
+        mStorage.write(bytes, 0, bytes.length);
+    }
+
+    private StatsLogEventWrapper(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * Writes the stored fields to a byte array. Will first write a new-line character to denote
+     * END_LIST before writing contents to byte array.
+     */
+    public void writeToParcel(Parcel out, int flags) {
+        mStorage.write(10); // new-line character is same as END_LIST
+        out.writeByteArray(mStorage.toByteArray());
+    }
+
+    /**
+     * Not implemented.
+     */
+    public void readFromParcel(Parcel in) {
+        // Not needed since this java class is for sending to statsd only.
+    }
+
+    /**
+     * Boilerplate for Parcel.
+     */
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/libs/services/Android.mk b/libs/services/Android.mk
index cbfd4b3..d72059a 100644
--- a/libs/services/Android.mk
+++ b/libs/services/Android.mk
@@ -21,7 +21,8 @@
 LOCAL_MODULE := libservices
 LOCAL_SRC_FILES := \
     ../../core/java/com/android/internal/os/IDropBoxManagerService.aidl \
-    src/os/DropBoxManager.cpp
+    src/os/DropBoxManager.cpp \
+    src/os/StatsLogEventWrapper.cpp
 
 LOCAL_AIDL_INCLUDES := \
     $(LOCAL_PATH)/../../core/java
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
new file mode 100644
index 0000000..255619c
--- /dev/null
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#ifndef STATS_LOG_EVENT_WRAPPER_H
+#define STATS_LOG_EVENT_WRAPPER_H
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/Status.h>
+#include <utils/RefBase.h>
+#include <vector>
+
+namespace android {
+namespace os {
+
+// Represents a parcelable object. Only used to send data from Android OS to statsd.
+class StatsLogEventWrapper : public android::Parcelable {
+ public:
+  StatsLogEventWrapper();
+
+  StatsLogEventWrapper(StatsLogEventWrapper&& in) = default;
+
+  android::status_t writeToParcel(android::Parcel* out) const;
+
+  android::status_t readFromParcel(const android::Parcel* in);
+
+  // These are public for ease of conversion.
+  std::vector<uint8_t> bytes;
+};
+} // Namespace os
+} // Namespace android
+
+
+#endif  // STATS_LOG_EVENT_WRAPPER_H
+
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
new file mode 100644
index 0000000..8b3aa9a
--- /dev/null
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#include <android/os/StatsLogEventWrapper.h>
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/Status.h>
+#include <utils/RefBase.h>
+#include <vector>
+
+using android::Parcel;
+using android::Parcelable;
+using android::status_t;
+using std::vector;
+
+namespace android {
+namespace os {
+
+StatsLogEventWrapper::StatsLogEventWrapper(){};
+
+status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const {
+    out->writeByteVector(bytes);
+    return ::android::NO_ERROR;
+};
+
+status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) {
+    in->readByteVector(&bytes);
+    return ::android::NO_ERROR;
+};
+
+} // Namespace os
+} // Namespace android
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index ca3dd05..22d2bcf 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -33,12 +33,14 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.StatsLogEventWrapper;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
 
 import java.util.ArrayList;
 import java.util.List;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.KernelWakelockReader;
 import com.android.internal.os.KernelWakelockStats;
@@ -49,6 +51,7 @@
 /**
  * Helper service for statsd (the native stats management service in cmds/statsd/).
  * Used for registering and receiving alarms on behalf of statsd.
+ *
  * @hide
  */
 public class StatsCompanionService extends IStatsCompanionService.Stub {
@@ -90,7 +93,7 @@
                         // Needed since the new user basically has a version of every app.
                         informAllUidsLocked(context);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
+                        Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
                         forgetEverything();
                     }
                 }
@@ -99,9 +102,9 @@
         Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED.");
     }
 
-    private final static int[] toIntArray(List<Integer> list){
+    private final static int[] toIntArray(List<Integer> list) {
         int[] ret = new int[list.size()];
-        for(int i = 0;i < ret.length;i++) {
+        for (int i = 0; i < ret.length; i++) {
             ret[i] = list.get(i);
         }
         return ret;
@@ -113,7 +116,7 @@
         PackageManager pm = context.getPackageManager();
         final List<UserInfo> users = um.getUsers(true);
         if (DEBUG) {
-            Slog.w(TAG, "Iterating over "+users.size() + " profiles.");
+            Slog.w(TAG, "Iterating over " + users.size() + " profiles.");
         }
 
         List<Integer> uids = new ArrayList();
@@ -122,23 +125,23 @@
 
         // Add in all the apps for every user/profile.
         for (UserInfo profile : users) {
-          List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
-          for (int j = 0; j < pi.size(); j++) {
-              if (pi.get(j).applicationInfo != null) {
-                  uids.add(pi.get(j).applicationInfo.uid);
-                  versions.add(pi.get(j).versionCode);
-                  apps.add(pi.get(j).packageName);
-              }
-          }
+            List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
+            for (int j = 0; j < pi.size(); j++) {
+                if (pi.get(j).applicationInfo != null) {
+                    uids.add(pi.get(j).applicationInfo.uid);
+                    versions.add(pi.get(j).versionCode);
+                    apps.add(pi.get(j).packageName);
+                }
+            }
         }
         sStatsd.informAllUidData(toIntArray(uids), toIntArray(versions), apps.toArray(new
-            String[apps.size()]));
+                String[apps.size()]));
         if (DEBUG) {
-            Slog.w(TAG, "Sent data for "+uids.size() +" apps");
+            Slog.w(TAG, "Sent data for " + uids.size() + " apps");
         }
     }
 
-    public final static class AppUpdateReceiver extends BroadcastReceiver  {
+    public final static class AppUpdateReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
@@ -147,7 +150,7 @@
              * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
              */
             if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED) &&
-                intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                 return; // Keep only replacing or normal add and remove.
             }
             synchronized (sStatsdLock) {
@@ -180,9 +183,9 @@
                 }
             }
         }
-    };
+    }
 
-    public final static class AnomalyAlarmReceiver extends BroadcastReceiver  {
+    public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
@@ -200,7 +203,7 @@
             }
             // AlarmManager releases its own wakelock here.
         }
-    };
+    }
 
     public final static class PollingAlarmReceiver extends BroadcastReceiver {
         @Override
@@ -220,7 +223,7 @@
             }
             // AlarmManager releases its own wakelock here.
         }
-    };
+    }
 
     @Override // Binder call
     public void setAnomalyAlarm(long timestampMs) {
@@ -286,33 +289,34 @@
     private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
 
     @Override // Binder call
-    public String pullData(int pullCode) {
+    public StatsLogEventWrapper[] pullData(int pullCode) {
         enforceCallingPermission();
-        if (DEBUG) Slog.d(TAG, "Fetching " + pullCode);
+        if (DEBUG) {
+            Slog.d(TAG, "Pulling " + pullCode);
+        }
 
-        StringBuilder s = new StringBuilder(); // TODO: use and return a Parcel instead of a string
+        List<StatsLogEventWrapper> ret = new ArrayList<>();
         switch (pullCode) {
-            case PULL_CODE_KERNEL_WAKELOCKS:
+            case PULL_CODE_KERNEL_WAKELOCKS: {
                 final KernelWakelockStats wakelockStats =
                         mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
-
                 for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
                     String name = ent.getKey();
                     KernelWakelockStats.Entry kws = ent.getValue();
-                    s.append("Wakelock ")
-                            .append(name)
-                            .append(", time=")
-                            .append(kws.mTotalTime)
-                            .append(", count=")
-                            .append(kws.mCount)
-                            .append('\n');
+                    StatsLogEventWrapper e = new StatsLogEventWrapper(101, 4);
+                    e.writeInt(kws.mCount);
+                    e.writeInt(kws.mVersion);
+                    e.writeLong(kws.mTotalTime);
+                    e.writeString(name);
+                    ret.add(e);
                 }
                 break;
+            }
             default:
                 Slog.w(TAG, "No such pollable data as " + pullCode);
                 return null;
         }
-        return s.toString();
+        return ret.toArray(new StatsLogEventWrapper[ret.size()]);
     }
 
     @Override // Binder call
@@ -331,7 +335,9 @@
 
     // Lifecycle and related code
 
-    /** Fetches the statsd IBinder service */
+    /**
+     * Fetches the statsd IBinder service
+     */
     private static IStatsManager fetchStatsdService() {
         return IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
     }
@@ -363,13 +369,17 @@
         }
     }
 
-    /** Now that the android system is ready, StatsCompanion is ready too, so inform statsd. */
+    /**
+     * 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. */
+    /**
+     * Tells statsd that statscompanion is ready. If the binder call returns, link to statsd.
+     */
     private void sayHiToStatsd() {
         synchronized (sStatsdLock) {
             if (sStatsd != null) {
@@ -398,14 +408,14 @@
                 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
                 filter.addDataScheme("package");
                 mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null,
-                    null);
+                        null);
 
                 // Setup receiver for user initialize (which happens once for a new user) and
                 // if a user is removed.
                 filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
                 filter.addAction(Intent.ACTION_USER_REMOVED);
                 mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL,
-                    filter, null, null);
+                        filter, null, null);
 
                 // Pull the latest state of UID->app name, version mapping when statsd starts.
                 informAllUidsLocked(mContext);