Merge "Native puller API: libstatspulled"
diff --git a/libstats/pull/Android.bp b/libstats/pull/Android.bp
new file mode 100644
index 0000000..9772da1
--- /dev/null
+++ b/libstats/pull/Android.bp
@@ -0,0 +1,41 @@
+//
+// 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.
+//
+
+// ==========================================================
+// Native library to register a pull atom callback with statsd
+// ==========================================================
+cc_library_shared {
+    name: "libstatspull",
+    srcs: [
+        ":statsd_aidl",
+        "stats_pull_atom_callback.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        //TODO: use libbinder_ndk.
+        "libbinder",
+        "libstatssocket",
+        "libservices",
+    ],
+    static_libs: [
+        "liblog",
+        "libutils",
+    ]
+}
diff --git a/libstats/pull/OWNERS b/libstats/pull/OWNERS
new file mode 100644
index 0000000..7855774
--- /dev/null
+++ b/libstats/pull/OWNERS
@@ -0,0 +1,7 @@
+joeo@google.com
+muhammadq@google.com
+ruchirr@google.com
+singhtejinder@google.com
+tsaichristine@google.com
+yaochen@google.com
+yro@google.com
diff --git a/libstats/pull/include/stats_pull_atom_callback.h b/libstats/pull/include/stats_pull_atom_callback.h
new file mode 100644
index 0000000..ee69ea7
--- /dev/null
+++ b/libstats/pull/include/stats_pull_atom_callback.h
@@ -0,0 +1,46 @@
+/*
+ * 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 <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Metadata for registering a stats_pull_atom_callback.
+ * All fields are optional, and defaults will be used for unspecified fields.
+ */
+typedef struct pull_atom_metadata {
+    int64_t cool_down_ns;
+    int64_t timeout_ns;
+    int32_t* additive_fields;
+    int32_t additive_fields_size;
+} pull_atom_metadata;
+
+typedef struct pulled_stats_event_list pulled_stats_event_list;
+
+typedef bool (*stats_pull_atom_callback_t)(int32_t atom_tag, pulled_stats_event_list* data,
+                                           const void* cookie);
+
+struct stats_event* add_stats_event_to_pull_data(pulled_stats_event_list* pull_data);
+void register_stats_pull_atom_callback(int32_t atom_tag, stats_pull_atom_callback_t* callback,
+                                       pull_atom_metadata* metadata, const void* cookie);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libstats/pull/stats_pull_atom_callback.cpp b/libstats/pull/stats_pull_atom_callback.cpp
new file mode 100644
index 0000000..f61d646
--- /dev/null
+++ b/libstats/pull/stats_pull_atom_callback.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+#include <map>
+#include <vector>
+
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+
+#include <android/os/BnPullAtomCallback.h>
+#include <android/os/IPullAtomResultReceiver.h>
+#include <android/os/IStatsManager.h>
+#include <android/util/StatsEvent.h>
+#include <binder/IServiceManager.h>
+#include "include/stats_pull_atom_callback.h"
+
+struct pulled_stats_event_list {
+    std::vector<stats_event*> data;
+};
+
+struct stats_event* add_stats_event_to_pull_data(pulled_stats_event_list* pull_data) {
+    struct stats_event* event = stats_event_obtain();
+    pull_data->data.push_back(event);
+    return event;
+}
+
+static const int64_t DEFAULT_COOL_DOWN_NS = 1000000000LL;  // 1 second.
+static const int64_t DEFAULT_TIMEOUT_NS = 10000000000LL;   // 10 seconds.
+
+class StatsPullAtomCallbackInternal : public android::os::BnPullAtomCallback {
+  public:
+    StatsPullAtomCallbackInternal(const stats_pull_atom_callback_t* callback, const void* cookie,
+                                  const int64_t coolDownNs, const int64_t timeoutNs,
+                                  const std::vector<int32_t> additiveFields)
+        : mCallback(callback),
+          mCookie(cookie),
+          mCoolDownNs(coolDownNs),
+          mTimeoutNs(timeoutNs),
+          mAdditiveFields(additiveFields) {}
+
+    ::android::binder::Status onPullAtom(
+            int32_t atomTag,
+            const ::android::sp<::android::os::IPullAtomResultReceiver>& resultReceiver) override {
+        pulled_stats_event_list statsEventList;
+        bool success = (*mCallback)(atomTag, &statsEventList, mCookie);
+        std::vector<android::util::StatsEvent> output;
+        // TODO convert stats_event into parcelable stats_event.
+        resultReceiver->pullFinished(atomTag, success, output);
+        for (int i = 0; i < statsEventList.data.size(); i++) {
+            stats_event_release(statsEventList.data[i]);
+        }
+        return android::binder::Status::ok();
+    }
+
+    const int64_t& getCoolDownNs() const { return mCoolDownNs; }
+    const int64_t& getTimeoutNs() const { return mTimeoutNs; }
+    const std::vector<int32_t>& getAdditiveFields() const { return mAdditiveFields; }
+
+  private:
+    const stats_pull_atom_callback_t* mCallback;
+    const void* mCookie;
+    const int64_t mCoolDownNs;
+    const int64_t mTimeoutNs;
+    const std::vector<int32_t> mAdditiveFields;
+};
+
+static std::mutex pullAtomMutex;
+static android::sp<android::os::IStatsManager> sStatsd = nullptr;
+
+static std::map<int32_t, android::sp<StatsPullAtomCallbackInternal>> mPullers;
+static android::sp<android::os::IStatsManager> getStatsServiceLocked();
+
+class StatsDeathRecipient : public android::IBinder::DeathRecipient {
+  public:
+    StatsDeathRecipient() = default;
+    ~StatsDeathRecipient() override = default;
+
+    // android::IBinder::DeathRecipient override:
+    void binderDied(const android::wp<android::IBinder>& /* who */) override {
+        std::lock_guard<std::mutex> lock(pullAtomMutex);
+        if (sStatsd) {
+            sStatsd = nullptr;
+        }
+        android::sp<android::os::IStatsManager> statsService = getStatsServiceLocked();
+        if (statsService == nullptr) {
+            return;
+        }
+        for (auto it : mPullers) {
+            statsService->registerNativePullAtomCallback(it.first, it.second->getCoolDownNs(),
+                                                         it.second->getTimeoutNs(),
+                                                         it.second->getAdditiveFields(), it.second);
+        }
+    }
+};
+
+static android::sp<StatsDeathRecipient> statsDeathRecipient = new StatsDeathRecipient();
+
+static android::sp<android::os::IStatsManager> getStatsServiceLocked() {
+    if (!sStatsd) {
+        // Fetch statsd.
+        const android::sp<android::IBinder> binder =
+                android::defaultServiceManager()->checkService(android::String16("stats"));
+        if (!binder) {
+            return nullptr;
+        }
+        binder->linkToDeath(statsDeathRecipient);
+        sStatsd = android::interface_cast<android::os::IStatsManager>(binder);
+    }
+    return sStatsd;
+}
+
+void register_stats_pull_atom_callback(int32_t atom_tag, stats_pull_atom_callback_t* callback,
+                                       pull_atom_metadata* metadata, void* cookie) {
+    int64_t coolDownNs = metadata == nullptr ? DEFAULT_COOL_DOWN_NS : metadata->cool_down_ns;
+    int64_t timeoutNs = metadata == nullptr ? DEFAULT_TIMEOUT_NS : metadata->timeout_ns;
+
+    std::vector<int32_t> additiveFields;
+    if (metadata != nullptr && metadata->additive_fields != nullptr) {
+        additiveFields.assign(metadata->additive_fields,
+                              metadata->additive_fields + metadata->additive_fields_size);
+    }
+
+    std::lock_guard<std::mutex> lg(pullAtomMutex);
+    const android::sp<android::os::IStatsManager> statsService = getStatsServiceLocked();
+    if (statsService == nullptr) {
+        // Error - statsd not available
+        return;
+    }
+
+    android::sp<StatsPullAtomCallbackInternal> callbackBinder = new StatsPullAtomCallbackInternal(
+            callback, cookie, coolDownNs, timeoutNs, additiveFields);
+    mPullers[atom_tag] = callbackBinder;
+    statsService->registerNativePullAtomCallback(atom_tag, coolDownNs, timeoutNs, additiveFields,
+                                                 callbackBinder);
+}