Statsd Puller Callback Registration

Creates a hidden api to register puller callbacks for vendor atoms.

Test: manual local test
Test: statsd unit tests
Bug: 119898637
Change-Id: Id28817b8fc718e128adc4e1c6b2e997db84517f9
diff --git a/Android.bp b/Android.bp
index 7bdedc7..0848d2b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -804,6 +804,7 @@
     srcs: [
         "core/java/android/os/IStatsCompanionService.aidl",
         "core/java/android/os/IStatsManager.aidl",
+        "core/java/android/os/IStatsPullerCallback.aidl",
     ],
 }
 
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index d6f045e..faf2053 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -71,6 +71,7 @@
         "src/external/Perfetto.cpp",
         "src/external/Perfprofd.cpp",
         "src/external/StatsPuller.cpp",
+        "src/external/StatsCallbackPuller.cpp",
         "src/external/StatsCompanionServicePuller.cpp",
         "src/external/SubsystemSleepStatePuller.cpp",
         "src/external/PowerStatsPuller.cpp",
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 86bf3ec..bd21a95 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1057,6 +1057,24 @@
     return Status::ok();
 }
 
+Status StatsService::registerPullerCallback(int32_t atomTag,
+        const sp<android::os::IStatsPullerCallback>& pullerCallback,
+        const String16& packageName) {
+    ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+
+    VLOG("StatsService::registerPullerCallback called.");
+    mPullerManager->RegisterPullerCallback(atomTag, pullerCallback);
+    return Status::ok();
+}
+
+Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& packageName) {
+    ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+
+    VLOG("StatsService::unregisterPullerCallback called.");
+    mPullerManager->UnregisterPullerCallback(atomTag);
+    return Status::ok();
+}
+
 hardware::Return<void> StatsService::reportSpeakerImpedance(
         const SpeakerImpedance& speakerImpedance) {
     LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), speakerImpedance);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index cdff50f..941ed46 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -173,6 +173,19 @@
     virtual Status sendAppBreadcrumbAtom(int32_t label, int32_t state) override;
 
     /**
+     * Binder call to register a callback function for a vendor pulled atom.
+     * Note: this atom must NOT have uid as a field.
+     */
+    virtual Status registerPullerCallback(int32_t atomTag,
+        const sp<android::os::IStatsPullerCallback>& pullerCallback,
+        const String16& packageName) override;
+
+    /**
+     * Binder call to unregister any existing callback function for a vendor pulled atom.
+     */
+    virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override;
+
+    /**
      * Binder call to get SpeakerImpedance atom.
      */
     virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override;
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
new file mode 100644
index 0000000..d718273
--- /dev/null
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define DEBUG false  // STOPSHIP if true
+#include "Log.h"
+
+#include <android/os/IStatsPullerCallback.h>
+
+#include "StatsCallbackPuller.h"
+#include "logd/LogEvent.h"
+#include "stats_log_util.h"
+
+using namespace android::binder;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+StatsCallbackPuller::StatsCallbackPuller(int tagId, const sp<IStatsPullerCallback>& callback) :
+        StatsPuller(tagId), mCallback(callback) {
+        VLOG("StatsCallbackPuller created for tag %d", tagId);
+}
+
+bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+    VLOG("StatsCallbackPuller called for tag %d", mTagId)
+    if(mCallback == nullptr) {
+        ALOGW("No callback registered");
+        return false;
+    }
+    int64_t wallClockTimeNs = getWallClockNs();
+    int64_t elapsedTimeNs = getElapsedRealtimeNs();
+    vector<StatsLogEventWrapper> returned_value;
+    Status status = mCallback->pullData(mTagId, elapsedTimeNs, wallClockTimeNs, &returned_value);
+    if (!status.isOk()) {
+        ALOGW("StatsCallbackPuller::pull failed for %d", mTagId);
+        return false;
+    }
+    data->clear();
+    for (const StatsLogEventWrapper& it: returned_value) {
+        LogEvent::createLogEvents(it, *data);
+    }
+    VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId);
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.h b/cmds/statsd/src/external/StatsCallbackPuller.h
new file mode 100644
index 0000000..c4bfa89
--- /dev/null
+++ b/cmds/statsd/src/external/StatsCallbackPuller.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <android/os/IStatsPullerCallback.h>
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StatsCallbackPuller : public StatsPuller {
+public:
+    explicit StatsCallbackPuller(int tagId, const sp<IStatsPullerCallback>& callback);
+
+private:
+    bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
+    const sp<IStatsPullerCallback> mCallback;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index c7c22ee..9552c0a 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -33,7 +33,7 @@
 void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
 
 StatsPuller::StatsPuller(const int tagId)
-    : mTagId(tagId) {
+    : mTagId(tagId), mLastPullTimeNs(0) {
 }
 
 bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index ecdcd21..ed72b29 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include <android/os/IStatsCompanionService.h>
+#include <android/os/IStatsPullerCallback.h>
 #include <cutils/log.h>
 #include <math.h>
 #include <stdint.h>
@@ -29,6 +30,7 @@
 #include "PowerStatsPuller.h"
 #include "ResourceHealthManagerPuller.h"
 #include "StatsCompanionServicePuller.h"
+#include "StatsCallbackPuller.h"
 #include "StatsPullerManager.h"
 #include "SubsystemSleepStatePuller.h"
 #include "statslog.h"
@@ -49,7 +51,7 @@
 // Values smaller than this may require to update the alarm.
 const int64_t NO_ALARM_UPDATE = INT64_MAX;
 
-const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
+std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
         // wifi_bytes_transfer
         {android::util::WIFI_BYTES_TRANSFER,
          {.additiveFields = {2, 3, 4, 5},
@@ -420,6 +422,30 @@
     return totalCleared;
 }
 
+void StatsPullerManager::RegisterPullerCallback(int32_t atomTag,
+        const sp<IStatsPullerCallback>& callback) {
+    AutoMutex _l(mLock);
+    // Platform pullers cannot be changed.
+    if (atomTag < StatsdStats::kMaxPlatformAtomTag) {
+        VLOG("RegisterPullerCallback: atom tag %d is less than min tag %d",
+                atomTag, StatsdStats::kMaxPlatformAtomTag);
+        return;
+    }
+    VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
+    StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
+    kAllPullAtomInfo[atomTag] = {.puller = new StatsCallbackPuller(atomTag, callback)};
+}
+
+void StatsPullerManager::UnregisterPullerCallback(int32_t atomTag) {
+    AutoMutex _l(mLock);
+    // Platform pullers cannot be changed.
+    if (atomTag < StatsdStats::kMaxPlatformAtomTag) {
+        return;
+    }
+    StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false);
+    kAllPullAtomInfo.erase(atomTag);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 807e4af..45f6b35 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android/os/IStatsCompanionService.h>
+#include <android/os/IStatsPullerCallback.h>
 #include <binder/IServiceManager.h>
 #include <utils/RefBase.h>
 #include <utils/threads.h>
@@ -91,7 +92,12 @@
 
     void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
 
-    const static std::map<int, PullAtomInfo> kAllPullAtomInfo;
+    void RegisterPullerCallback(int32_t atomTag,
+                                const sp<IStatsPullerCallback>& callback);
+
+    void UnregisterPullerCallback(int32_t atomTag);
+
+    static std::map<int, PullAtomInfo> kAllPullAtomInfo;
 
 private:
     sp<IStatsCompanionService> mStatsCompanionService = nullptr;
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index c4034ff..40329b7 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -423,6 +423,15 @@
     mPulledAtomStats[atomId].emptyData++;
 }
 
+void StatsdStats::notePullerCallbackRegistrationChanged(int atomId, bool registered) {
+    lock_guard<std::mutex> lock(mLock);
+    if (registered) {
+        mPulledAtomStats[atomId].registeredCount++;
+    } else {
+        mPulledAtomStats[atomId].unregisteredCount++;
+    }
+}
+
 void StatsdStats::noteHardDimensionLimitReached(int64_t metricId) {
     lock_guard<std::mutex> lock(mLock);
     getAtomMetricStats(metricId).hardDimensionLimitReached++;
@@ -514,6 +523,8 @@
         pullStats.second.dataError = 0;
         pullStats.second.pullTimeout = 0;
         pullStats.second.pullExceedMaxDelay = 0;
+        pullStats.second.registeredCount = 0;
+        pullStats.second.unregisteredCount = 0;
     }
     mAtomMetricStats.clear();
 }
@@ -625,12 +636,14 @@
                 "  (average pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay "
                 "nanos)%lld, "
                 "  (max pull delay nanos)%lld, (data error)%ld\n"
-                "  (pull timeout)%ld, (pull exceed max delay)%ld\n",
+                "  (pull timeout)%ld, (pull exceed max delay)%ld\n"
+                "  (registered count) %ld, (unregistered count) %ld\n",
                 (int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache,
                 (long)pair.second.minPullIntervalSec, (long long)pair.second.avgPullTimeNs,
                 (long long)pair.second.maxPullTimeNs, (long long)pair.second.avgPullDelayNs,
                 (long long)pair.second.maxPullDelayNs, pair.second.dataError,
-                pair.second.pullTimeout, pair.second.pullExceedMaxDelay);
+                pair.second.pullTimeout, pair.second.pullExceedMaxDelay,
+                pair.second.registeredCount, pair.second.unregisteredCount);
     }
 
     if (mAnomalyAlarmRegisteredStats > 0) {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index ea3f3b3..65e8a32 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -146,6 +146,10 @@
 
     // Max time to do a pull.
     static const int64_t kPullMaxDelayNs = 10 * NS_PER_SEC;
+
+    // Max platform atom tag number.
+    static const int32_t kMaxPlatformAtomTag = 100000;
+
     /**
      * Report a new config has been received and report the static stats about the config.
      *
@@ -340,6 +344,13 @@
     void noteEmptyData(int atomId);
 
     /**
+     * Records that a puller callback for the given atomId was registered or unregistered.
+     *
+     * @param registered True if the callback was registered, false if was unregistered.
+     */
+    void notePullerCallbackRegistrationChanged(int atomId, bool registered);
+
+    /**
      * Hard limit was reached in the cardinality of an atom
      */
     void noteHardDimensionLimitReached(int64_t metricId);
@@ -416,6 +427,8 @@
         long statsCompanionPullFailed = 0;
         long statsCompanionPullBinderTransactionFailed = 0;
         long emptyData = 0;
+        long registeredCount = 0;
+        long unregisteredCount = 0;
     } PulledAtomStats;
 
     typedef struct {
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 6a07a3f..863261a 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -407,6 +407,8 @@
         optional int64 stats_companion_pull_failed = 13;
         optional int64 stats_companion_pull_binder_transaction_failed = 14;
         optional int64 empty_data = 15;
+        optional int64 registered_count = 16;
+        optional int64 unregistered_count = 17;
     }
     repeated PulledAtomStats pulled_atom_stats = 10;
 
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index aa8cfc5..0ebf2ca 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -70,6 +70,8 @@
 const int FIELD_ID_STATS_COMPANION_FAILED = 13;
 const int FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED = 14;
 const int FIELD_ID_EMPTY_DATA = 15;
+const int FIELD_ID_PULL_REGISTERED_COUNT = 16;
+const int FIELD_ID_PULL_UNREGISTERED_COUNT = 17;
 // for AtomMetricStats proto
 const int FIELD_ID_ATOM_METRIC_STATS = 17;
 const int FIELD_ID_METRIC_ID = 1;
@@ -480,6 +482,10 @@
                        (long long)pair.second.statsCompanionPullBinderTransactionFailed);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA,
                        (long long)pair.second.emptyData);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_REGISTERED_COUNT,
+                       (long long) pair.second.registeredCount);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT,
+                       (long long) pair.second.unregisteredCount);
     protoOutput->end(token);
 }
 
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 92aa998..44a88f0 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -259,6 +259,10 @@
     stats.notePullDelay(android::util::DISK_SPACE, 3335L);
     stats.notePull(android::util::DISK_SPACE);
     stats.notePullFromCache(android::util::DISK_SPACE);
+    stats.notePullerCallbackRegistrationChanged(android::util::DISK_SPACE, true);
+    stats.notePullerCallbackRegistrationChanged(android::util::DISK_SPACE, false);
+    stats.notePullerCallbackRegistrationChanged(android::util::DISK_SPACE, true);
+
 
     vector<uint8_t> output;
     stats.dumpStats(&output, false);
@@ -276,6 +280,8 @@
     EXPECT_EQ(3333L, report.pulled_atom_stats(0).max_pull_time_nanos());
     EXPECT_EQ(2223L, report.pulled_atom_stats(0).average_pull_delay_nanos());
     EXPECT_EQ(3335L, report.pulled_atom_stats(0).max_pull_delay_nanos());
+    EXPECT_EQ(2L, report.pulled_atom_stats(0).registered_count());
+    EXPECT_EQ(1L, report.pulled_atom_stats(0).unregistered_count());
 }
 
 TEST(StatsdStatsTest, TestAtomMetricsStats) {
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 3119b37..7746148 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.os.IBinder;
 import android.os.IStatsManager;
+import android.os.IStatsPullerCallback;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.AndroidException;
@@ -408,6 +409,39 @@
         }
     }
 
+    /**
+     * Registers a callback for an atom when that atom is to be pulled. The stats service will
+     * invoke pullData in the callback when the stats service determines that this atom needs to be
+     * pulled. Currently, this only works for atoms with tags above 100,000 that do not have a uid.
+     *
+     * @param atomTag   The tag of the atom for this puller callback. Must be at least 100000.
+     * @param callback  The callback to be invoked when the stats service pulls the atom.
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+     *
+     * @hide
+     */
+    @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
+    public void setPullerCallback(int atomTag, IStatsPullerCallback callback)
+            throws StatsUnavailableException {
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (callback == null) {
+                    service.unregisterPullerCallback(atomTag, mContext.getOpPackageName());
+                } else {
+                    service.registerPullerCallback(atomTag, callback,
+                            mContext.getOpPackageName());
+                }
+
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to connect to statsd when registering data listener.");
+                throw new StatsUnavailableException("could not connect", e);
+            } catch (SecurityException e) {
+                throw new StatsUnavailableException(e.getMessage(), e);
+            }
+        }
+    }
+
     private class StatsdDeathRecipient implements IBinder.DeathRecipient {
         @Override
         public void binderDied() {
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 93d6f4c..f1bba1a 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.IStatsPullerCallback;
+
 /**
   * Binder interface to communicate with the statistics management service.
   * {@hide}
@@ -178,4 +180,20 @@
      * this label. This allows building custom metrics and predicates.
      */
     void sendAppBreadcrumbAtom(int label, int state);
+
+    /**
+     * Registers a puller callback function that, when invoked, pulls the data
+     * for the specified vendor atom tag.
+     *
+     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS
+     */
+    oneway void registerPullerCallback(int atomTag, IStatsPullerCallback pullerCallback,
+                                       String packageName);
+
+   /**
+    * Unregisters a puller callback function for the given vendor atom.
+    *
+    * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS
+    */
+   oneway void unregisterPullerCallback(int atomTag, String packageName);
 }
diff --git a/core/java/android/os/IStatsPullerCallback.aidl b/core/java/android/os/IStatsPullerCallback.aidl
new file mode 100644
index 0000000..1684aeb
--- /dev/null
+++ b/core/java/android/os/IStatsPullerCallback.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 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 android.os.StatsLogEventWrapper;
+
+/**
+  * Binder interface to pull atoms for the stats service.
+  * {@hide}
+  */
+interface IStatsPullerCallback {
+    /**
+     * Pull data for the specified atom tag. Returns an array of StatsLogEventWrapper containing
+     * the data.
+     *
+     * Note: These pulled atoms should not have uid/attribution chain. Additionally, the event
+     * timestamps will be truncated to the nearest 5 minutes.
+     */
+    StatsLogEventWrapper[] pullData(int atomTag, long elapsedNanos, long wallClocknanos);
+
+}
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index acb9eac..2334242 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -57,20 +57,18 @@
     public static final Parcelable.Creator<StatsLogEventWrapper> CREATOR = new
             Parcelable.Creator<StatsLogEventWrapper>() {
                 public StatsLogEventWrapper createFromParcel(Parcel in) {
-                    android.util.EventLog.writeEvent(0x534e4554, "112550251",
-                            android.os.Binder.getCallingUid(), "");
-                    // Purposefully leaving this method not implemented.
-                    throw new RuntimeException("Not implemented");
+                    return new StatsLogEventWrapper(in);
                 }
 
                 public StatsLogEventWrapper[] newArray(int size) {
-                    android.util.EventLog.writeEvent(0x534e4554, "112550251",
-                            android.os.Binder.getCallingUid(), "");
-                    // Purposefully leaving this method not implemented.
-                    throw new RuntimeException("Not implemented");
+                    return new StatsLogEventWrapper[size];
                 }
             };
 
+    private StatsLogEventWrapper(Parcel in) {
+        readFromParcel(in);
+    }
+
     /**
      * Set work source if any.
      */
@@ -197,6 +195,70 @@
     }
 
     /**
+     * Reads from parcel and appropriately fills member fields.
+     */
+    public void readFromParcel(Parcel in) {
+        mTypes = new ArrayList<>();
+        mValues = new ArrayList<>();
+        mWorkSource = null;
+
+        mTag = in.readInt();
+        mElapsedTimeNs = in.readLong();
+        mWallClockTimeNs = in.readLong();
+
+        // Clear any data.
+        if (DEBUG) {
+            Slog.d(TAG, "Reading " + mTag + " " + mElapsedTimeNs + " " + mWallClockTimeNs);
+        }
+        // Set up worksource if present.
+        int numWorkChains = in.readInt();
+        if (numWorkChains > 0) {
+            mWorkSource = new WorkSource();
+            for (int i = 0; i < numWorkChains; i++) {
+                android.os.WorkSource.WorkChain workChain = mWorkSource.createWorkChain();
+                int workChainSize = in.readInt();
+                for (int j = 0; j < workChainSize; j++) {
+                    int uid = in.readInt();
+                    String tag = in.readString();
+                    workChain.addNode(uid, tag);
+                }
+            }
+        }
+
+        // Do the rest of the types.
+        int numTypes = in.readInt();
+        if (DEBUG) {
+            Slog.d(TAG, "Reading " + numTypes + " elements");
+        }
+        for (int i = 0; i < numTypes; i++) {
+            int type = in.readInt();
+            mTypes.add(type);
+            switch (type) {
+                case EVENT_TYPE_INT:
+                    mValues.add(in.readInt());
+                    break;
+                case EVENT_TYPE_LONG:
+                    mValues.add(in.readLong());
+                    break;
+                case EVENT_TYPE_FLOAT:
+                    mValues.add(in.readFloat());
+                    break;
+                case EVENT_TYPE_DOUBLE:
+                    mValues.add(in.readDouble());
+                    break;
+                case EVENT_TYPE_STRING:
+                    mValues.add(in.readString());
+                    break;
+                case EVENT_TYPE_STORAGE:
+                    mValues.add(in.createByteArray());
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    /**
      * Boilerplate for Parcel.
      */
     public int describeContents() {