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/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);
 }