Statsd pulls on-device power measurements

Pulls power rail measurements into statslog.

Bug: 119034725
Test: cts-tradefed run cts-dev -m CtsStatsdHostTestCases -t android.cts.statsd.atom.HostAtomTests
(after enabling OPTINAL_TESTS_ENABLED)

Change-Id: If083ae50160d508a9d180f748f9515557d35d8a1
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 6547b3a..7f38372 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -72,6 +72,7 @@
         "src/external/StatsPuller.cpp",
         "src/external/StatsCompanionServicePuller.cpp",
         "src/external/SubsystemSleepStatePuller.cpp",
+        "src/external/PowerStatsPuller.cpp",
         "src/external/ResourceHealthManagerPuller.cpp",
         "src/external/ResourceThermalManagerPuller.cpp",
         "src/external/StatsPullerManager.cpp",
@@ -134,6 +135,7 @@
         "android.hardware.health@2.0",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
+        "android.hardware.power.stats@1.0",
         "android.hardware.thermal@1.0",
         "libpackagelistparser",
         "libsysutils",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5620184..21e7203 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -190,6 +190,7 @@
         ProcessCpuTime process_cpu_time = 10035;
         NativeProcessMemoryState native_process_memory_state = 10036;
         CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037;
+        OnDevicePowerMeasurement on_device_power_measurement = 10038;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -2262,6 +2263,27 @@
 }
 
 /**
+ * Pulls on-device power measurement information.
+ * Data defined by hardware/interfaces/power/stats/1.0/types.hal.
+ * Pulled from:
+ *   frameworks/base/cmds/statsd/src/external/PowerStatsPuller.cpp
+ */
+message OnDevicePowerMeasurement {
+    // Name of the subsystem (to which the rail belongs).
+    optional string subsystem_name = 1;
+
+    // Rail name. The rail lies within the subsystem.
+    optional string rail_name = 2;
+
+    // Time (in ms since boot) at which the rail energy value was measured.
+    // This may differ slightly from the time that statsd logs this information.
+    optional uint64 measurement_timestamp_millis = 3;
+
+    // Accumulated energy used via the rail since device boot in uWs.
+    optional uint64 energy_microwatt_secs = 4;
+}
+
+/**
  * Pulls Cpu time per frequency.
  * Pulls the time the cpu spend on the frequency index. Frequency index
  * starts from highest to lowest. The value should be monotonically
diff --git a/cmds/statsd/src/external/PowerStatsPuller.cpp b/cmds/statsd/src/external/PowerStatsPuller.cpp
new file mode 100644
index 0000000..71e5fa0
--- /dev/null
+++ b/cmds/statsd/src/external/PowerStatsPuller.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 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/hardware/power/stats/1.0/IPowerStats.h>
+
+#include <vector>
+
+#include "PowerStatsPuller.h"
+#include "stats_log_util.h"
+
+using android::hardware::hidl_vec;
+using android::hardware::power::stats::V1_0::IPowerStats;
+using android::hardware::power::stats::V1_0::EnergyData;
+using android::hardware::power::stats::V1_0::RailInfo;
+using android::hardware::power::stats::V1_0::Status;
+using android::hardware::Return;
+using android::hardware::Void;
+
+using std::make_shared;
+using std::shared_ptr;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr;
+std::mutex gPowerStatsHalMutex;
+bool gPowerStatsExist = true; // Initialized to ensure making a first attempt.
+std::vector<RailInfo> gRailInfo;
+
+bool getPowerStatsHal() {
+    if (gPowerStatsHal == nullptr && gPowerStatsExist) {
+        gPowerStatsHal = android::hardware::power::stats::V1_0::IPowerStats::getService();
+        if (gPowerStatsHal == nullptr) {
+            ALOGW("Couldn't load power.stats HAL service");
+            gPowerStatsExist = false;
+        }
+    }
+    return gPowerStatsHal != nullptr;
+}
+
+PowerStatsPuller::PowerStatsPuller() : StatsPuller(android::util::ON_DEVICE_POWER_MEASUREMENT) {
+}
+
+bool PowerStatsPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+    std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+
+    if (!getPowerStatsHal()) {
+        ALOGE("power.stats Hal not loaded");
+        return false;
+    }
+
+    int64_t wallClockTimestampNs = getWallClockNs();
+    int64_t elapsedTimestampNs = getElapsedRealtimeNs();
+
+    data->clear();
+
+    // Pull getRailInfo if necessary
+    if (gRailInfo.empty()) {
+        bool resultSuccess = true;
+        Return<void> ret = gPowerStatsHal->getRailInfo(
+                [&resultSuccess](const hidl_vec<RailInfo> &list, Status status) {
+                    resultSuccess = (status == Status::SUCCESS || status == Status::NOT_SUPPORTED);
+                    if (status != Status::SUCCESS) return;
+
+                    gRailInfo.reserve(list.size());
+                    for (size_t i = 0; i < list.size(); ++i) {
+                        gRailInfo.push_back(list[i]);
+                    }
+                });
+        if (!resultSuccess || !ret.isOk()) {
+            ALOGE("power.stats getRailInfo() failed. Description: %s", ret.description().c_str());
+            gPowerStatsHal = nullptr;
+            return false;
+        }
+        // If SUCCESS but empty, or if NOT_SUPPORTED, then never try again.
+        if (gRailInfo.empty()) {
+            ALOGE("power.stats has no rail information");
+            gPowerStatsExist = false; // No rail info, so never try again.
+            return false;
+        }
+    }
+
+    // Pull getEnergyData and write the data out
+    const hidl_vec<uint32_t> desiredRailIndices; // Empty vector indicates we want all.
+    bool resultSuccess = true;
+    Return<void> ret = gPowerStatsHal->getEnergyData(desiredRailIndices,
+                [&data, wallClockTimestampNs, elapsedTimestampNs, &resultSuccess]
+                (hidl_vec<EnergyData> energyDataList, Status status) {
+                    resultSuccess = (status == Status::SUCCESS);
+                    if (!resultSuccess) return;
+
+                    for (size_t i = 0; i < energyDataList.size(); i++) {
+                        const EnergyData& energyData = energyDataList[i];
+
+                        if (energyData.index >= gRailInfo.size()) {
+                            ALOGE("power.stats getEnergyData() returned an invalid rail index %u.",
+                                    energyData.index);
+                            resultSuccess = false;
+                            return;
+                        }
+                        const RailInfo& rail = gRailInfo[energyData.index];
+
+                        auto ptr = make_shared<LogEvent>(android::util::ON_DEVICE_POWER_MEASUREMENT,
+                              wallClockTimestampNs, elapsedTimestampNs);
+                        ptr->write(rail.subsysName);
+                        ptr->write(rail.railName);
+                        ptr->write(energyData.timestamp);
+                        ptr->write(energyData.energy);
+                        ptr->init();
+                        data->push_back(ptr);
+
+                        VLOG("power.stat: %s.%s: %llu, %llu",
+                             rail.subsysName.c_str(),
+                             rail.railName.c_str(),
+                             (unsigned long long)energyData.timestamp,
+                             (unsigned long long)energyData.energy);
+                    }
+                });
+    if (!resultSuccess || !ret.isOk()) {
+        ALOGE("power.stats getEnergyData() failed. Description: %s", ret.description().c_str());
+        gPowerStatsHal = nullptr;
+        return false;
+    }
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/PowerStatsPuller.h b/cmds/statsd/src/external/PowerStatsPuller.h
new file mode 100644
index 0000000..dd5ff8f
--- /dev/null
+++ b/cmds/statsd/src/external/PowerStatsPuller.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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 "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads hal for power.stats
+ */
+class PowerStatsPuller : public StatsPuller {
+public:
+    PowerStatsPuller();
+    bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 9633980..8378ae1 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -26,6 +26,7 @@
 #include "../logd/LogEvent.h"
 #include "../stats_log_util.h"
 #include "../statscompanion_util.h"
+#include "PowerStatsPuller.h"
 #include "ResourceHealthManagerPuller.h"
 #include "ResourceThermalManagerPuller.h"
 #include "StatsCompanionServicePuller.h"
@@ -86,6 +87,9 @@
         // subsystem_sleep_state
         {android::util::SUBSYSTEM_SLEEP_STATE,
          {{}, {}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
+        // on_device_power_measurement
+        {android::util::ON_DEVICE_POWER_MEASUREMENT,
+         {{}, {}, 1 * NS_PER_SEC, new PowerStatsPuller()}},
         // cpu_time_per_freq
         {android::util::CPU_TIME_PER_FREQ,
          {{3},