power: Add 1.0 HAL for devices that don't have WiFi stats

Some devices (cough Broadcom cough) don't have nodes that return WiFi
stats, so add a 1.0 HAL that devices can build. Also actually enable
the system power stats code and use it. Also add a legacy stats option
for devices that use an old RPM driver

Change-Id: If946b81b589ce11b0bde46437396bb4be74a731a
diff --git a/Android.mk b/Android.mk
index 7c457d1..cfc28fe 100644
--- a/Android.mk
+++ b/Android.mk
@@ -30,8 +30,7 @@
     libhidlbase \
     libhidltransport \
     libhardware \
-    libutils \
-    android.hardware.power@1.1
+    libutils
 
 LOCAL_SRC_FILES := \
     service.cpp \
@@ -115,8 +114,24 @@
     LOCAL_CFLAGS += -DTAP_TO_WAKE_NODE=\"$(TARGET_TAP_TO_WAKE_NODE)\"
 endif
 
+ifeq ($(TARGET_HAS_LEGACY_POWER_STATS),true)
+    LOCAL_CFLAGS += -DLEGACY_STATS
+endif
+
+ifneq ($(TARGET_WLAN_POWER_STAT),)
+    LOCAL_CFLAGS += -DWLAN_POWER_STAT=\"$(TARGET_WLAN_POWER_STAT)\"
+endif
+
+ifeq ($(TARGET_HAS_NO_WIFI_STATS),true)
+LOCAL_MODULE := android.hardware.power@1.0-service-qti
+LOCAL_INIT_RC := android.hardware.power@1.0-service-qti.rc
+LOCAL_SHARED_LIBRARIES += android.hardware.power@1.0
+LOCAL_CFLAGS += -DV1_0_HAL
+else
 LOCAL_MODULE := android.hardware.power@1.1-service-qti
 LOCAL_INIT_RC := android.hardware.power@1.1-service-qti.rc
+LOCAL_SHARED_LIBRARIES += android.hardware.power@1.1
+endif
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_OWNER := qcom
 LOCAL_VENDOR_MODULE := true
diff --git a/Power.cpp b/Power.cpp
index 5b838ff..bb4bb50 100644
--- a/Power.cpp
+++ b/Power.cpp
@@ -15,7 +15,11 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "android.hardware.power@1.1-service.wahoo"
+#ifdef V1_0_HAL
+#define LOG_TAG "android.hardware.power@1.0-service-qti"
+#else
+#define LOG_TAG "android.hardware.power@1.1-service-qti"
+#endif
 
 #include <android/log.h>
 #include <utils/Log.h>
@@ -31,14 +35,20 @@
 namespace android {
 namespace hardware {
 namespace power {
+#ifdef V1_0_HAL
+namespace V1_0 {
+#else
 namespace V1_1 {
+#endif
 namespace implementation {
 
 using ::android::hardware::power::V1_0::Feature;
 using ::android::hardware::power::V1_0::PowerHint;
 using ::android::hardware::power::V1_0::PowerStatePlatformSleepState;
 using ::android::hardware::power::V1_0::Status;
+#ifndef V1_0_HAL
 using ::android::hardware::power::V1_1::PowerStateSubsystem;
+#endif
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
@@ -64,21 +74,66 @@
 }
 
 Return<void> Power::getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) {
-#if 0 // we don't do anything with this
     hidl_vec<PowerStatePlatformSleepState> states;
     uint64_t stats[MAX_PLATFORM_STATS * MAX_RPM_PARAMS] = {0};
     uint64_t *values;
     struct PowerStatePlatformSleepState *state;
     int ret;
 
-    states.resize(PLATFORM_SLEEP_MODES_COUNT);
-
     ret = extract_platform_stats(stats);
     if (ret != 0) {
         states.resize(0);
         goto done;
     }
 
+#ifdef LEGACY_STATS
+    states.resize(RPM_MODE_MAX);
+
+    /* Update statistics for XO_shutdown */
+    state = &states[RPM_MODE_XO];
+    state->name = "XO_shutdown";
+
+    state->residencyInMsecSinceBoot = stats[ACCUMULATED_VLOW_TIME];
+    state->totalTransitions = stats[VLOW_COUNT];
+    state->supportedOnlyInSuspend = false;
+    state->voters.resize(XO_VOTERS);
+
+    /* Update statistics for APSS voter */
+    state->voters[0].name = "APSS";
+    state->voters[0].totalTimeInMsecVotedForSinceBoot =
+        stats[XO_ACCUMULATED_DURATION_APSS] / RPM_CLK;
+    state->voters[0].totalNumberOfTimesVotedSinceBoot = stats[XO_COUNT_APSS];
+
+    /* Update statistics for MPSS voter */
+    state->voters[1].name = "MPSS";
+    state->voters[1].totalTimeInMsecVotedForSinceBoot =
+        stats[XO_ACCUMULATED_DURATION_MPSS] / RPM_CLK;
+    state->voters[1].totalNumberOfTimesVotedSinceBoot = stats[XO_COUNT_MPSS];
+
+    /* Update statistics for ADSP voter */
+    state->voters[2].name = "ADSP";
+    state->voters[2].totalTimeInMsecVotedForSinceBoot =
+        stats[XO_ACCUMULATED_DURATION_ADSP] / RPM_CLK;
+    state->voters[2].totalNumberOfTimesVotedSinceBoot = stats[XO_COUNT_ADSP];
+
+    /* Update statistics for SLPI voter */
+    state->voters[3].name = "SLPI";
+    state->voters[3].totalTimeInMsecVotedForSinceBoot =
+        stats[XO_ACCUMULATED_DURATION_SLPI] / RPM_CLK;
+    state->voters[3].totalNumberOfTimesVotedSinceBoot = stats[XO_COUNT_SLPI];
+
+    /* Update statistics for VMIN state */
+    state = &states[RPM_MODE_VMIN];
+
+    state->name = "VMIN";
+    state->residencyInMsecSinceBoot = stats[ACCUMULATED_VMIN_TIME];
+    state->totalTransitions = stats[VMIN_COUNT];
+    state->supportedOnlyInSuspend = false;
+    state->voters.resize(VMIN_VOTERS);
+    //Note: No filling of state voters since VMIN_VOTERS = 0
+#else
+    states.resize(PLATFORM_SLEEP_MODES_COUNT);
+
     /* Update statistics for XO_shutdown */
     state = &states[RPM_MODE_XO];
     state->name = "XO_shutdown";
@@ -106,13 +161,15 @@
     state->supportedOnlyInSuspend = false;
     state->voters.resize(VMIN_VOTERS);
     //Note: No filling of state voters since VMIN_VOTERS = 0
-
+#endif
 done:
     _hidl_cb(states, Status::SUCCESS);
-#endif
     return Void();
 }
 
+#ifndef V1_0_HAL
+// Methods from ::android::hardware::power::V1_1::IPower follow.
+
 static int get_wlan_low_power_stats(struct PowerStateSubsystem &subsystem) {
 
     uint64_t stats[WLAN_POWER_PARAMS_COUNT] = {0};
@@ -145,16 +202,15 @@
     return 0;
 }
 
-// Methods from ::android::hardware::power::V1_1::IPower follow.
 Return<void> Power::getSubsystemLowPowerStats(getSubsystemLowPowerStats_cb _hidl_cb) {
 
     hidl_vec<PowerStateSubsystem> subsystems;
     int ret;
 
-    subsystems.resize(SUBSYSTEM_COUNT);
+    subsystems.resize(subsystem_type::SUBSYSTEM_COUNT);
 
     //We currently have only one Subsystem for WLAN
-    ret = get_wlan_low_power_stats(subsystems[SUBSYSTEM_WLAN]);
+    ret = get_wlan_low_power_stats(subsystems[subsystem_type::SUBSYSTEM_WLAN]);
     if (ret != 0)
         goto done;
 
@@ -169,9 +225,10 @@
     // just call the normal power hint in this oneway function
     return powerHint(hint, data);
 }
+#endif
 
 }  // namespace implementation
-}  // namespace V1_1
+}  // namespace V1_0/1
 }  // namespace power
 }  // namespace hardware
 }  // namespace android
diff --git a/Power.h b/Power.h
index 130d2f2..1c85a6d 100644
--- a/Power.h
+++ b/Power.h
@@ -18,7 +18,11 @@
 #ifndef ANDROID_HARDWARE_POWER_V1_1_POWER_H
 #define ANDROID_HARDWARE_POWER_V1_1_POWER_H
 
+#ifdef V1_0_HAL
+#include <android/hardware/power/1.0/IPower.h>
+#else
 #include <android/hardware/power/1.1/IPower.h>
+#endif
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
 #include <hardware/power.h>
@@ -26,12 +30,20 @@
 namespace android {
 namespace hardware {
 namespace power {
+#ifdef V1_0_HAL
+namespace V1_0 {
+#else
 namespace V1_1 {
+#endif
 namespace implementation {
 
 using ::android::hardware::power::V1_0::Feature;
 using ::android::hardware::power::V1_0::PowerHint;
+#ifdef V1_0_HAL
+using ::android::hardware::power::V1_0::IPower;
+#else
 using ::android::hardware::power::V1_1::IPower;
+#endif
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 
@@ -45,16 +57,18 @@
     Return<void> setFeature(Feature feature, bool activate) override;
     Return<void> getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) override;
 
+#ifndef V1_0_HAL
     // Methods from ::android::hardware::power::V1_1::IPower follow.
     Return<void> getSubsystemLowPowerStats(getSubsystemLowPowerStats_cb _hidl_cb) override;
     Return<void> powerHintAsync(PowerHint hint, int32_t data) override;
+#endif
 
     // Methods from ::android::hidl::base::V1_0::IBase follow.
 
 };
 
 }  // namespace implementation
-}  // namespace V1_1
+}  // namespace V1_0/1
 }  // namespace power
 }  // namespace hardware
 }  // namespace android
diff --git a/android.hardware.power@1.0-service-qti.rc b/android.hardware.power@1.0-service-qti.rc
new file mode 100644
index 0000000..01739a0
--- /dev/null
+++ b/android.hardware.power@1.0-service-qti.rc
@@ -0,0 +1,4 @@
+service power-hal-1-0 /vendor/bin/hw/android.hardware.power@1.0-service-qti
+    class hal
+    user system
+    group system
diff --git a/power-helper.c b/power-helper.c
index 1e19c7d..db864a9 100644
--- a/power-helper.c
+++ b/power-helper.c
@@ -1,5 +1,7 @@
 /*
  * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2017 The LineageOS Project
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -35,6 +37,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <dlfcn.h>
+#include <inttypes.h>
 #include <stdlib.h>
 #include <unistd.h>
 
@@ -50,13 +53,79 @@
 #include "power-feature.h"
 #include "power-helper.h"
 
+#define USINSEC 1000000L
+#define NSINUS 1000L
+
+#ifndef RPM_STAT
+#define RPM_STAT "/d/rpm_stats"
+#endif
+
+#ifndef RPM_MASTER_STAT
+#define RPM_MASTER_STAT "/d/rpm_master_stats"
+#endif
+
+#ifndef RPM_SYSTEM_STAT
+#define RPM_SYSTEM_STAT "/d/system_stats"
+#endif
+
+/*
+   Set with TARGET_WLAN_POWER_STAT in BoardConfig.mk
+   Defaults to QCACLD3 path
+   Path for QCACLD3: /d/wlan0/power_stats
+   Path for QCACLD2 and Prima: /d/wlan_wcnss/power_stats
+ */
+
+#ifndef V1_0_HAL
 #ifndef WLAN_POWER_STAT
 #define WLAN_POWER_STAT "/d/wlan0/power_stats"
 #endif
+#endif
 
 #define ARRAY_SIZE(x) (sizeof((x))/sizeof((x)[0]))
 #define LINE_SIZE 128
 
+#ifdef LEGACY_STATS
+/* Use these stats on pre-nougat qualcomm kernels */
+static const char *rpm_param_names[] = {
+    "vlow_count",
+    "accumulated_vlow_time",
+    "vmin_count",
+    "accumulated_vmin_time"
+};
+
+static const char *rpm_master_param_names[] = {
+    "xo_accumulated_duration",
+    "xo_count",
+    "xo_accumulated_duration",
+    "xo_count",
+    "xo_accumulated_duration",
+    "xo_count",
+    "xo_accumulated_duration",
+    "xo_count"
+};
+#else
+/* Use these stats on nougat and forward */
+const char *rpm_stat_params[MAX_RPM_PARAMS] = {
+    "count",
+    "actual last sleep(msec)",
+};
+
+const char *master_stat_params[MAX_RPM_PARAMS] = {
+    "Accumulated XO duration",
+    "XO Count",
+};
+
+struct stat_pair rpm_stat_map[] = {
+    { RPM_MODE_XO,   "RPM Mode:vlow", rpm_stat_params, ARRAY_SIZE(rpm_stat_params) },
+    { RPM_MODE_VMIN, "RPM Mode:vmin", rpm_stat_params, ARRAY_SIZE(rpm_stat_params) },
+    { VOTER_APSS,    "APSS",    master_stat_params, ARRAY_SIZE(master_stat_params) },
+    { VOTER_MPSS,    "MPSS",    master_stat_params, ARRAY_SIZE(master_stat_params) },
+    { VOTER_ADSP,    "ADSP",    master_stat_params, ARRAY_SIZE(master_stat_params) },
+    { VOTER_SLPI,    "SLPI",    master_stat_params, ARRAY_SIZE(master_stat_params) },
+};
+#endif
+
+#ifndef V1_0_HAL
 const char *wlan_power_stat_params[] = {
     "cumulative_sleep_time_ms",
     "cumulative_total_on_time_ms",
@@ -67,6 +136,7 @@
 struct stat_pair wlan_stat_map[] = {
     { WLAN_POWER_DEBUG_STATS, "POWER DEBUG STATS", wlan_power_stat_params, ARRAY_SIZE(wlan_power_stat_params) },
 };
+#endif
 
 static int saved_dcvs_cpu0_slack_max = -1;
 static int saved_dcvs_cpu0_slack_min = -1;
@@ -405,7 +475,7 @@
                 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
             undo_hint_action(DISPLAY_STATE_HINT_ID);
             display_hint_sent = 0;
-        } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) && 
+        } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) &&
                 (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) {
             if (saved_interactive_mode == -1 || saved_interactive_mode == 0) {
                 /* Display turned on. Restore if possible. */
@@ -514,6 +584,84 @@
     return 0;
 }
 
+#ifdef LEGACY_STATS
+static int extract_stats(uint64_t *list, char *file, const char**param_names,
+                         unsigned int num_parameters, int isHex) {
+    FILE *fp;
+    ssize_t read;
+    size_t len;
+    size_t index = 0;
+    char *line;
+    int ret;
+
+    fp = fopen(file, "r");
+    if (fp == NULL) {
+        ret = -errno;
+        ALOGE("%s: failed to open: %s Error = %s", __func__, file, strerror(errno));
+        return ret;
+    }
+
+    for (line = NULL, len = 0;
+         ((read = getline(&line, &len, fp) != -1) && (index < num_parameters));
+         free(line), line = NULL, len = 0) {
+        uint64_t value;
+        char* offset;
+
+        size_t begin = strspn(line, " \t");
+        if (strncmp(line + begin, param_names[index], strlen(param_names[index]))) {
+            continue;
+        }
+
+        offset = memchr(line, ':', len);
+        if (!offset) {
+            continue;
+        }
+
+        if (isHex) {
+            sscanf(offset, ":%" SCNx64, &value);
+        } else {
+            sscanf(offset, ":%" SCNu64, &value);
+        }
+        list[index] = value;
+        index++;
+    }
+
+    free(line);
+    fclose(fp);
+
+    return 0;
+}
+
+int extract_platform_stats(uint64_t *list) {
+    int ret;
+    //Data is located in two files
+    ret = extract_stats(list, RPM_STAT, rpm_param_names, RPM_PARAM_COUNT, false);
+    if (ret) {
+        for (size_t i=0; i < RPM_PARAM_COUNT; i++)
+            list[i] = 0;
+    }
+    ret = extract_stats(list + RPM_PARAM_COUNT, RPM_MASTER_STAT,
+                        rpm_master_param_names, PLATFORM_PARAM_COUNT - RPM_PARAM_COUNT, true);
+    if (ret) {
+        for (size_t i=RPM_PARAM_COUNT; i < PLATFORM_PARAM_COUNT; i++)
+        list[i] = 0;
+    }
+    return 0;
+}
+
+#ifndef V1_0_HAL
+int extract_wlan_stats(uint64_t *list) {
+    int ret;
+    ret = extract_stats(list, WLAN_POWER_STAT, wlan_param_names, WLAN_PARAM_COUNT, false);
+    if (ret) {
+        for (size_t i=0; i < WLAN_PARAM_COUNT; i++)
+            list[i] = 0;
+    }
+    return 0;
+}
+#endif
+#else
+
 static int extract_stats(uint64_t *list, char *file,
                          struct stat_pair *map, size_t map_size) {
     FILE *fp;
@@ -522,27 +670,33 @@
     char *line;
     size_t i, stats_read = 0;
     int ret = 0;
+
     fp = fopen(file, "re");
     if (fp == NULL) {
         ALOGE("%s: failed to open: %s Error = %s", __func__, file, strerror(errno));
         return -errno;
     }
+
     line = malloc(len);
     if (!line) {
         ALOGE("%s: no memory to hold line", __func__);
         fclose(fp);
         return -ENOMEM;
     }
+
     while ((stats_read < map_size) && (read = getline(&line, &len, fp) != -1)) {
         size_t begin = strspn(line, " \t");
+
         for (i = 0; i < map_size; i++) {
             if (!strncmp(line + begin, map[i].label, strlen(map[i].label))) {
                 stats_read++;
                 break;
             }
         }
+
         if (i == map_size)
             continue;
+
         ret = parse_stats(map[i].parameters, map[i].num_parameters,
                           &list[map[i].stat * MAX_RPM_PARAMS], fp);
         if (ret < 0)
@@ -550,9 +704,17 @@
     }
     free(line);
     fclose(fp);
+
     return ret;
 }
 
+int extract_platform_stats(uint64_t *list) {
+    return extract_stats(list, RPM_SYSTEM_STAT, rpm_stat_map, ARRAY_SIZE(rpm_stat_map));
+}
+
+#ifndef V1_0_HAL
 int extract_wlan_stats(uint64_t *list) {
     return extract_stats(list, WLAN_POWER_STAT, wlan_stat_map, ARRAY_SIZE(wlan_stat_map));
 }
+#endif
+#endif
diff --git a/power-helper.h b/power-helper.h
index d7862c7..3d22353 100644
--- a/power-helper.h
+++ b/power-helper.h
@@ -36,20 +36,57 @@
 
 #include "hardware/power.h"
 
+#ifdef LEGACY_STATS
+enum platform_param_id {
+    VLOW_COUNT = 0,
+    ACCUMULATED_VLOW_TIME,
+    VMIN_COUNT,
+    ACCUMULATED_VMIN_TIME,
+    RPM_PARAM_COUNT,
+
+    XO_ACCUMULATED_DURATION_APSS = RPM_PARAM_COUNT,
+    XO_COUNT_APSS,
+    XO_ACCUMULATED_DURATION_MPSS,
+    XO_COUNT_MPSS,
+    XO_ACCUMULATED_DURATION_ADSP,
+    XO_COUNT_ADSP,
+    XO_ACCUMULATED_DURATION_SLPI,
+    XO_COUNT_SLPI,
+
+    //Don't add any lines after that line
+    PLATFORM_PARAM_COUNT
+};
+#endif
 
 enum stats_type {
+    //Platform Stats
+    RPM_MODE_XO = 0,
+    RPM_MODE_VMIN,
+    RPM_MODE_MAX,
+    XO_VOTERS_START = RPM_MODE_MAX,
+    VOTER_APSS = XO_VOTERS_START,
+    VOTER_MPSS,
+    VOTER_ADSP,
+    VOTER_SLPI,
+    MAX_PLATFORM_STATS,
+
+#ifndef V1_0_HAL
     //WLAN Stats
     WLAN_POWER_DEBUG_STATS = 0,
     MAX_WLAN_STATS,
+#endif
 };
 
 enum subsystem_type {
+#ifndef V1_0_HAL
     SUBSYSTEM_WLAN = 0,
+#endif
 
     //Don't add any lines after this line
     SUBSYSTEM_COUNT
 };
 
+#ifndef V1_0_HAL
 enum wlan_sleep_states {
     WLAN_STATE_ACTIVE = 0,
     WLAN_STATE_DEEP_SLEEP,
@@ -67,10 +104,16 @@
     //Don't add any lines after this line
     WLAN_POWER_PARAMS_COUNT
 };
+#endif
 
+#define PLATFORM_SLEEP_MODES_COUNT RPM_MODE_MAX
 
 #define MAX_RPM_PARAMS 2
+#ifdef LEGACY_STATS
+#define XO_VOTERS 4
+#else
 #define XO_VOTERS (MAX_PLATFORM_STATS - XO_VOTERS_START)
+#endif
 #define VMIN_VOTERS 0
 
 struct stat_pair {
@@ -85,8 +128,10 @@
 void power_hint(power_hint_t hint, void *data);
 void power_set_interactive(int on);
 void set_feature(feature_t feature, int state);
+int extract_platform_stats(uint64_t *list);
+#ifndef V1_0_HAL
 int extract_wlan_stats(uint64_t *list);
-
+#endif
 
 #ifdef __cplusplus
 }
diff --git a/service.cpp b/service.cpp
index d119052..ffb1f2a 100644
--- a/service.cpp
+++ b/service.cpp
@@ -15,7 +15,11 @@
  * limitations under the License.
  */
 
+#ifdef V1_0_HAL
+#define LOG_TAG "android.hardware.power@1.0-service-qti"
+#else
 #define LOG_TAG "android.hardware.power@1.1-service-qti"
+#endif
 
 #include <android/log.h>
 #include <hidl/HidlTransportSupport.h>
@@ -31,15 +35,24 @@
 using android::hardware::joinRpcThreadpool;
 
 // Generated HIDL files
+#ifdef V1_0_HAL
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::implementation::Power;
+#else
 using android::hardware::power::V1_1::IPower;
 using android::hardware::power::V1_1::implementation::Power;
+#endif
 
 int main() {
 
     status_t status;
     android::sp<IPower> service = nullptr;
 
+#ifdef V1_0_HAL
+    ALOGI("Power HAL Service 1.0 for QCOM is starting.");
+#else
     ALOGI("Power HAL Service 1.1 for QCOM is starting.");
+#endif
 
     service = new Power();
     if (service == nullptr) {