power hal: Add power HAL API  1.1 impl for Marlin

Add a full binderized implementation for Power hal

Many subsystems (e.g.wifi) could be living on an
independent power island (sourced from VBatt directly)
and might even have their own dedicated XTAL to source
their clocks. Since these SOCs are capable of
autonomously operating (while the platform is in one
of the sleep states), they are still drawing power
from the VBatt. Hence it is critical to understand
the  (SOC) level low power statistics as well when
the battery level changes and be able to find any
correlation in event of unexpected battery drain.

This commit adds the support of the Power Hal 1.1
to marlin devices. Which includes the new api for
wlan specific power stats

Bug: 29339696
Test: Manual
Change-Id: Ia53c99fe60e76d32c2f36708839990c241cbbdf2
Signed-off-by: Ahmed ElArabawy <arabawy@google.com>
diff --git a/common/base.mk b/common/base.mk
index 12f6c8f..45b89cc 100644
--- a/common/base.mk
+++ b/common/base.mk
@@ -476,10 +476,6 @@
 #LIBQDMETADATA
 LIBQDMETADATA := libqdMetaData
 
-#LIBPOWER
-LIBPOWER := power.marlin
-LIBPOWER += android.hardware.power@1.0-impl
-
 #LLVM for RenderScript
 #use qcom LLVM
 $(call inherit-product-if-exists, external/llvm/llvm-select.mk)
diff --git a/device-common.mk b/device-common.mk
index fd58d6c..9744511 100644
--- a/device-common.mk
+++ b/device-common.mk
@@ -120,7 +120,7 @@
     android.hardware.light@2.0-service \
     android.hardware.memtrack@1.0-service \
     android.hardware.nfc@1.0-service \
-    android.hardware.power@1.0-service \
+    android.hardware.power@1.1-service.marlin \
     android.hardware.sensors@1.0-service \
     android.hardware.thermal@1.0-service \
     android.hardware.vr@1.0-service \
diff --git a/manifest.xml b/manifest.xml
index c74b22d..86308fc 100644
--- a/manifest.xml
+++ b/manifest.xml
@@ -192,7 +192,7 @@
     <hal format="hidl">
         <name>android.hardware.power</name>
         <transport>hwbinder</transport>
-        <version>1.0</version>
+        <version>1.1</version>
         <interface>
             <name>IPower</name>
             <instance>default</instance>
diff --git a/power/Android.mk b/power/Android.mk
index 057992e..08cea92 100644
--- a/power/Android.mk
+++ b/power/Android.mk
@@ -1,14 +1,30 @@
+# Copyright (C) 2017 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.
+
 LOCAL_PATH := $(call my-dir)
 
 ifeq ($(call is-vendor-board-platform,QCOM),true)
-
-# HAL module implemenation stored in
-# hw/<POWERS_HARDWARE_MODULE_ID>.<ro.hardware>.so
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SHARED_LIBRARIES := liblog libcutils libdl
-LOCAL_SRC_FILES := power.c metadata-parser.c utils.c list.c hint-data.c
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_OWNER := qcom
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := android.hardware.power@1.1-service.marlin
+LOCAL_INIT_RC := android.hardware.power@1.1-service.marlin.rc
+LOCAL_SRC_FILES := service.cpp Power.cpp power-helper.c metadata-parser.c utils.c list.c hint-data.c
 
 # Include target-specific files.
 ifeq ($(call is-board-platform-in-list, msm8996), true)
@@ -23,12 +39,13 @@
     LOCAL_CFLAGS += -DEXTRA_POWERHAL_HINTS
 endif
 
-LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libhidlbase \
+    libhidltransport \
+    liblog \
+    libutils \
+    android.hardware.power@1.1 \
 
-LOCAL_MODULE := power.marlin
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_OWNER := qcom
-LOCAL_PROPRIETARY_MODULE := true
-include $(BUILD_SHARED_LIBRARY)
-
+include $(BUILD_EXECUTABLE)
 endif
diff --git a/power/Power.cpp b/power/Power.cpp
new file mode 100644
index 0000000..7cca7bd
--- /dev/null
+++ b/power/Power.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2016 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 LOG_TAG "android.hardware.power@1.1-service.marlin"
+
+#include <android/log.h>
+#include <utils/Log.h>
+#include "Power.h"
+#include "power-helper.h"
+
+/* RPM runs at 19.2Mhz. Divide by 19200 for msec */
+#define RPM_CLK 19200
+
+namespace android {
+namespace hardware {
+namespace power {
+namespace V1_1 {
+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;
+using ::android::hardware::power::V1_1::PowerStateSubsystem;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+Power::Power() {
+    power_init();
+}
+
+// Methods from ::android::hardware::power::V1_0::IPower follow.
+Return<void> Power::setInteractive(bool interactive)  {
+    power_set_interactive(interactive ? 1 : 0);
+    return Void();
+}
+
+Return<void> Power::powerHint(PowerHint hint, int32_t data) {
+    int32_t param = data;
+    power_hint(static_cast<power_hint_t>(hint), &param);
+    return Void();
+}
+
+Return<void> Power::setFeature(Feature /*feature*/, bool /*activate*/)  {
+    return Void();
+}
+
+Return<void> Power::getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) {
+
+    hidl_vec<PowerStatePlatformSleepState> states;
+    uint64_t stats[platform_param_id::PLATFORM_PARAM_COUNT] = {0};
+    struct PowerStatePlatformSleepState *state;
+    int ret;
+
+    ret = extract_platform_stats(stats);
+    if (ret != 0) {
+        states.resize(0);
+        goto done;
+    }
+
+    states.resize(platform_mode_id::RPM_MODE_COUNT);
+
+    /* Update statistics for XO_shutdown */
+    state = &states[platform_mode_id::RPM_MODE_XO];
+    state->name = "XO_shutdown";
+
+    state->residencyInMsecSinceBoot = stats[platform_param_id::ACCUMULATED_VLOW_TIME];
+    state->totalTransitions = stats[platform_param_id::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[platform_param_id::XO_ACCUMULATED_DURATION_APSS] / RPM_CLK;
+    state->voters[0].totalNumberOfTimesVotedSinceBoot = stats[platform_param_id::XO_COUNT_APSS];
+
+    /* Update statistics for MPSS voter */
+    state->voters[1].name = "MPSS";
+    state->voters[1].totalTimeInMsecVotedForSinceBoot =
+        stats[platform_param_id::XO_ACCUMULATED_DURATION_MPSS] / RPM_CLK;
+    state->voters[1].totalNumberOfTimesVotedSinceBoot = stats[platform_param_id::XO_COUNT_MPSS];
+
+    /* Update statistics for ADSP voter */
+    state->voters[2].name = "ADSP";
+    state->voters[2].totalTimeInMsecVotedForSinceBoot =
+        stats[platform_param_id::XO_ACCUMULATED_DURATION_ADSP] / RPM_CLK;
+    state->voters[2].totalNumberOfTimesVotedSinceBoot = stats[platform_param_id::XO_COUNT_ADSP];
+
+    /* Update statistics for SLPI voter */
+    state->voters[3].name = "SLPI";
+    state->voters[3].totalTimeInMsecVotedForSinceBoot =
+        stats[platform_param_id::XO_ACCUMULATED_DURATION_SLPI] / RPM_CLK;
+    state->voters[3].totalNumberOfTimesVotedSinceBoot = stats[platform_param_id::XO_COUNT_SLPI];
+
+
+    /* Update statistics for VMIN state */
+    state = &states[platform_mode_id::RPM_MODE_VMIN];
+    state->name = "VMIN";
+
+    state->residencyInMsecSinceBoot = stats[platform_param_id::ACCUMULATED_VMIN_TIME];
+    state->totalTransitions = stats[platform_param_id::VMIN_COUNT];
+    state->supportedOnlyInSuspend = false;
+    state->voters.resize(VMIN_VOTERS);
+    //Note: No filling of state voters since VMIN_VOTERS = 0
+
+done:
+    _hidl_cb(states, Status::SUCCESS);
+    return Void();
+}
+
+
+// Methods from ::android::hardware::power::V1_1::IPower follow.
+
+static int get_wlan_low_power_stats(struct PowerStateSubsystem &subsystem) {
+
+    uint64_t stats[WLAN_PARAM_COUNT] = {0};
+    struct PowerStateSubsystemSleepState *state;
+    int ret;
+
+    ret = extract_wlan_stats(stats);
+    if (ret)
+        return ret;
+
+    subsystem.name = "wlan";
+    subsystem.states.resize(WLAN_STATE_COUNT);
+
+    /* Update statistics for Active State */
+    state = &subsystem.states[WLAN_STATE_ACTIVE];
+    state->name = "Active";
+    state->residencyInMsecSinceBoot = stats[wlan_param_id::CUMULATIVE_TOTAL_ON_TIME_MS];
+    state->totalTransitions = stats[wlan_param_id::DEEP_SLEEP_ENTER_COUNTER];
+    state->lastEntryTimestampMs = 0; //FIXME need a new value from Qcom
+    state->supportedOnlyInSuspend = false;
+
+    /* Update statistics for Deep-Sleep state */
+    state = &subsystem.states[WLAN_STATE_DEEP_SLEEP];
+    state->name = "Deep-Sleep";
+    state->residencyInMsecSinceBoot = stats[wlan_param_id::CUMULATIVE_SLEEP_TIME_MS];
+    state->totalTransitions = stats[wlan_param_id::DEEP_SLEEP_ENTER_COUNTER];
+    state->lastEntryTimestampMs = stats[wlan_param_id::LAST_DEEP_SLEEP_ENTER_TSTAMP_MS];
+    state->supportedOnlyInSuspend = false;
+
+    return 0;
+}
+
+Return<void> Power::getSubsystemLowPowerStats(getSubsystemLowPowerStats_cb _hidl_cb) {
+
+    hidl_vec<PowerStateSubsystem> subsystems;
+    int ret;
+
+    subsystems.resize(subsystem_type::SUBSYSTEM_COUNT);
+
+    //We currently have only one Subsystem for WLAN
+    ret = get_wlan_low_power_stats(subsystems[subsystem_type::SUBSYSTEM_WLAN]);
+    if (ret != 0) {
+        goto done;
+    }
+
+    //Add query for other subsystems here
+
+done:
+    _hidl_cb(subsystems, Status::SUCCESS);
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
diff --git a/power/Power.h b/power/Power.h
new file mode 100644
index 0000000..5907095
--- /dev/null
+++ b/power/Power.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ANDROID_HARDWARE_POWER_V1_1_POWER_H
+#define ANDROID_HARDWARE_POWER_V1_1_POWER_H
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <hardware/power.h>
+
+namespace android {
+namespace hardware {
+namespace power {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::power::V1_0::Feature;
+using ::android::hardware::power::V1_0::PowerHint;
+using ::android::hardware::power::V1_1::IPower;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+struct Power : public IPower {
+    // Methods from ::android::hardware::power::V1_0::IPower follow.
+
+    Power();
+
+    Return<void> setInteractive(bool interactive) override;
+    Return<void> powerHint(PowerHint hint, int32_t data) override;
+    Return<void> setFeature(Feature feature, bool activate) override;
+    Return<void> getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) override;
+
+    // Methods from ::android::hardware::power::V1_1::IPower follow.
+    Return<void> getSubsystemLowPowerStats(getSubsystemLowPowerStats_cb _hidl_cb) override;
+
+    // Methods from ::android::hidl::base::V1_0::IBase follow.
+
+};
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_POWER_V1_1_POWER_H
diff --git a/power/android.hardware.power@1.1-service.marlin.rc b/power/android.hardware.power@1.1-service.marlin.rc
new file mode 100644
index 0000000..69407de
--- /dev/null
+++ b/power/android.hardware.power@1.1-service.marlin.rc
@@ -0,0 +1,4 @@
+service power-hal-1-1 /vendor/bin/hw/android.hardware.power@1.1-service.marlin
+    class hal
+    user system
+    group system
diff --git a/power/power-8996.c b/power/power-8996.c
index e68d82f..c0e4397 100644
--- a/power/power-8996.c
+++ b/power/power-8996.c
@@ -281,7 +281,7 @@
     return HINT_NONE;
 }
 
-int power_hint_override(struct power_module *module, power_hint_t hint, void *data)
+int power_hint_override(power_hint_t hint, void *data)
 {
     int ret_val = HINT_NONE;
     switch(hint) {
@@ -302,7 +302,7 @@
     return ret_val;
 }
 
-int set_interactive_override(struct power_module *module, int on)
+int set_interactive_override(int on)
 {
     return HINT_HANDLED; /* Don't excecute this code path, not in use */
     char governor[80];
diff --git a/power/power-common.h b/power/power-common.h
index aefce30..0b2680c 100644
--- a/power/power-common.h
+++ b/power/power-common.h
@@ -48,3 +48,6 @@
     CPU2 = 2,
     CPU3 = 3
 };
+
+#define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
+
diff --git a/power/power.c b/power/power-helper.c
similarity index 81%
rename from power/power.c
rename to power/power-helper.c
index 29f169e..e9daaeb 100644
--- a/power/power.c
+++ b/power/power-helper.c
@@ -4,7 +4,7 @@
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
  * met:
- * *    * Redistributions of source code must retain the above copyright
+ *     * Redistributions of source code must retain the above copyright
  *       notice, this list of conditions and the following disclaimer.
  *     * Redistributions in binary form must reproduce the above
  *       copyright notice, this list of conditions and the following
@@ -40,7 +40,6 @@
 
 #define LOG_TAG "QCOM PowerHAL"
 #include <utils/Log.h>
-#include <hardware/hardware.h>
 #include <hardware/power.h>
 #include <cutils/properties.h>
 
@@ -49,17 +48,11 @@
 #include "hint-data.h"
 #include "performance.h"
 #include "power-common.h"
+#include "power-helper.h"
 
 #define USINSEC 1000000L
 #define NSINUS 1000L
 
-#define PLATFORM_SLEEP_MODES 2
-#define XO_VOTERS 4
-#define VMIN_VOTERS 0
-
-#define RPM_PARAMETERS 4
-#define NUM_PARAMETERS 12
-
 #ifndef RPM_STAT
 #define RPM_STAT "/d/rpm_stats"
 #endif
@@ -68,14 +61,18 @@
 #define RPM_MASTER_STAT "/d/rpm_master_stats"
 #endif
 
-/* RPM runs at 19.2Mhz. Divide by 19200 for msec */
-#define RPM_CLK 19200
+#ifndef WLAN_POWER_STAT
+#define WLAN_POWER_STAT "/d/wlan_wcnss/power_stats"
+#endif
 
-const char *parameter_names[] = {
+static const char *rpm_param_names[] = {
     "vlow_count",
     "accumulated_vlow_time",
     "vmin_count",
-    "accumulated_vmin_time",
+    "accumulated_vmin_time"
+};
+
+static const char *rpm_master_param_names[] = {
     "xo_accumulated_duration",
     "xo_count",
     "xo_accumulated_duration",
@@ -86,6 +83,14 @@
     "xo_count"
 };
 
+static const char *wlan_param_names[] = {
+    "cumulative_sleep_time_ms",
+    "cumulative_total_on_time_ms",
+    "deep_sleep_enter_counter",
+    "last_deep_sleep_enter_tstamp_ms"
+};
+
+
 static int saved_dcvs_cpu0_slack_max = -1;
 static int saved_dcvs_cpu0_slack_min = -1;
 static int saved_mpdecision_slack_max = -1;
@@ -100,11 +105,10 @@
 int vr_mode = 0;
 
 //interaction boost global variables
-static pthread_mutex_t s_interaction_lock = PTHREAD_MUTEX_INITIALIZER;
 static struct timespec s_previous_boost_timespec;
 static int s_previous_duration;
 
-static void power_init(struct power_module *module)
+void power_init(void)
 {
     ALOGV("QCOM power HAL initing.");
 
@@ -231,8 +235,8 @@
     }
 }
 
-int __attribute__ ((weak)) power_hint_override(struct power_module *module, power_hint_t hint,
-        void *data)
+int __attribute__ ((weak)) power_hint_override(power_hint_t UNUSED(hint),
+        void * UNUSED(data))
 {
     return HINT_NONE;
 }
@@ -248,11 +252,10 @@
     return diff_in_us;
 }
 
-static void power_hint(struct power_module *module, power_hint_t hint,
-        void *data)
+void power_hint(power_hint_t hint, void *data)
 {
     /* Check if this hint has been overridden. */
-    if (power_hint_override(module, hint, data) == HINT_HANDLED) {
+    if (power_hint_override(hint, data) == HINT_HANDLED) {
         /* The power_hint has been handled. We can skip the rest. */
         return;
     }
@@ -272,7 +275,6 @@
         case POWER_HINT_SUSTAINED_PERFORMANCE:
         {
             int duration = 0;
-            pthread_mutex_lock(&s_interaction_lock);
             if (data && sustained_performance_mode == 0) {
                 int* resources;
                 if (vr_mode == 0) { // Sustained mode only.
@@ -330,7 +332,6 @@
                 }
                 sustained_performance_mode = 0;
             }
-            pthread_mutex_unlock(&s_interaction_lock);
         }
         break;
         /* VR mode:
@@ -341,7 +342,6 @@
         case POWER_HINT_VR_MODE:
         {
             int duration = 0;
-            pthread_mutex_lock(&s_interaction_lock);
             if (data && vr_mode == 0) {
                 if (sustained_performance_mode == 0) { // VR mode only.
                     // Ensure that POWER_HINT_LAUNCH is not in progress.
@@ -402,7 +402,6 @@
                 }
                 vr_mode = 0;
             }
-            pthread_mutex_unlock(&s_interaction_lock);
         }
         break;
         case POWER_HINT_INTERACTION:
@@ -414,9 +413,7 @@
                 return;
             }
 
-            pthread_mutex_lock(&s_interaction_lock);
             if (sustained_performance_mode || vr_mode) {
-                pthread_mutex_unlock(&s_interaction_lock);
                 return;
             }
 
@@ -434,7 +431,6 @@
             long long elapsed_time = calc_timespan_us(s_previous_boost_timespec, cur_boost_timespec);
             // don't hint if previous hint's duration covers this hint's duration
             if ((s_previous_duration * 1000) > (elapsed_time + duration * 1000)) {
-                pthread_mutex_unlock(&s_interaction_lock);
                 return;
             }
             s_previous_boost_timespec = cur_boost_timespec;
@@ -450,7 +446,6 @@
                 int resources[] = {0x41800000, 0x33, 0x40800000, 1000, 0x40800100, 1000, 0x40C00000, 0x1};
                 interaction(duration, sizeof(resources)/sizeof(resources[0]), resources);
             }
-            pthread_mutex_unlock(&s_interaction_lock);
         }
         break;
         case POWER_HINT_VIDEO_ENCODE:
@@ -459,24 +454,24 @@
         case POWER_HINT_VIDEO_DECODE:
             process_video_decode_hint(data);
         break;
-	default:
-	break;
+        default:
+        break;
     }
 }
 
-int __attribute__ ((weak)) set_interactive_override(struct power_module *module, int on)
+int __attribute__ ((weak)) set_interactive_override(int UNUSED(on))
 {
     return HINT_NONE;
 }
 
-void set_interactive(struct power_module *module, int on)
+void power_set_interactive(int on)
 {
     char governor[80];
     char tmp_str[NODE_MAX];
     struct video_encode_metadata_t video_encode_metadata;
     int rc = 0;
 
-    if (set_interactive_override(module, on) == HINT_HANDLED) {
+    if (set_interactive_override(on) == HINT_HANDLED) {
         return;
     }
 
@@ -674,29 +669,20 @@
     saved_interactive_mode = !!on;
 }
 
-static ssize_t get_number_of_platform_modes(struct power_module *module) {
-   return PLATFORM_SLEEP_MODES;
-}
 
-static int get_voter_list(struct power_module *module, size_t *voter) {
-   voter[0] = XO_VOTERS;
-   voter[1] = VMIN_VOTERS;
-
-   return 0;
-}
-
-static int extract_stats(uint64_t *list, char *file,
-    unsigned int num_parameters, unsigned int index) {
+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", __func__, strerror(errno));
+        ALOGE("%s: failed to open: %s Error = %s", __func__, file, strerror(errno));
         return ret;
     }
 
@@ -707,7 +693,7 @@
         char* offset;
 
         size_t begin = strspn(line, " \t");
-        if (strncmp(line + begin, parameter_names[index], strlen(parameter_names[index]))) {
+        if (strncmp(line + begin, param_names[index], strlen(param_names[index]))) {
             continue;
         }
 
@@ -716,137 +702,53 @@
             continue;
         }
 
-        if (!strcmp(file, RPM_MASTER_STAT)) {
-            /* RPM_MASTER_STAT is reported in hex */
+        if (isHex) {
             sscanf(offset, ":%" SCNx64, &value);
-            /* Duration is reported in rpm SLEEP TICKS */
-            if (!strcmp(parameter_names[index], "xo_accumulated_duration")) {
-                value /= RPM_CLK;
-            }
         } else {
-            /* RPM_STAT is reported in decimal */
             sscanf(offset, ":%" SCNu64, &value);
         }
         list[index] = value;
         index++;
     }
-    free(line);
 
+    free(line);
     fclose(fp);
+
     return 0;
 }
 
-static int get_platform_low_power_stats(struct power_module *module,
-    power_state_platform_sleep_state_t *list) {
-    uint64_t stats[sizeof(parameter_names)] = {0};
+
+int extract_platform_stats(uint64_t *list) {
+
     int ret;
 
-    if (!list) {
-        return -EINVAL;
-    }
+    //Data is located in two files
 
-    ret = extract_stats(stats, RPM_STAT, RPM_PARAMETERS, 0);
-
+    ret = extract_stats(list, RPM_STAT, rpm_param_names, RPM_PARAM_COUNT, false);
     if (ret) {
-        return ret;
+        for (size_t i=0; i < RPM_PARAM_COUNT; i++)
+            list[i] = 0;
     }
 
-    ret = extract_stats(stats, RPM_MASTER_STAT, NUM_PARAMETERS, RPM_PARAMETERS);
-
+    ret = extract_stats(list + RPM_PARAM_COUNT, RPM_MASTER_STAT,
+                        rpm_master_param_names, PLATFORM_PARAM_COUNT - RPM_PARAM_COUNT, true);
     if (ret) {
-        return ret;
+        for (size_t i=RPM_PARAM_COUNT; i < PLATFORM_PARAM_COUNT; i++)
+        list[i] = 0;
     }
 
-    /* Update statistics for XO_shutdown */
-    strcpy(list[0].name, "XO_shutdown");
-    list[0].total_transitions = stats[0];
-    list[0].residency_in_msec_since_boot = stats[1];
-    list[0].supported_only_in_suspend = false;
-    list[0].number_of_voters = XO_VOTERS;
-
-    /* Update statistics for APSS voter */
-    strcpy(list[0].voters[0].name, "APSS");
-    list[0].voters[0].total_time_in_msec_voted_for_since_boot = stats[4];
-    list[0].voters[0].total_number_of_times_voted_since_boot = stats[5];
-
-    /* Update statistics for MPSS voter */
-    strcpy(list[0].voters[1].name, "MPSS");
-    list[0].voters[1].total_time_in_msec_voted_for_since_boot = stats[6];
-    list[0].voters[1].total_number_of_times_voted_since_boot = stats[7];
-
-    /* Update statistics for ADSP voter */
-    strcpy(list[0].voters[2].name, "ADSP");
-    list[0].voters[2].total_time_in_msec_voted_for_since_boot = stats[8];
-    list[0].voters[2].total_number_of_times_voted_since_boot = stats[9];
-
-    /* Update statistics for SLPI voter */
-    strcpy(list[0].voters[3].name, "SLPI");
-    list[0].voters[3].total_time_in_msec_voted_for_since_boot = stats[10];
-    list[0].voters[3].total_number_of_times_voted_since_boot = stats[11];
-
-    /* Update statistics for VMIN state */
-    strcpy(list[1].name, "VMIN");
-    list[1].total_transitions = stats[2];
-    list[1].residency_in_msec_since_boot = stats[3];
-    list[1].supported_only_in_suspend = false;
-    list[1].number_of_voters = VMIN_VOTERS;
-
     return 0;
 }
 
-static int power_open(const hw_module_t* module, const char* name,
-                    hw_device_t** device)
-{
-    ALOGV("%s: enter; name=%s", __FUNCTION__, name);
-    int retval = 0; /* 0 is ok; -1 is error */
+int extract_wlan_stats(uint64_t *list) {
 
-    if (strcmp(name, POWER_HARDWARE_MODULE_ID) == 0) {
-        power_module_t *dev = (power_module_t *)calloc(1,
-                sizeof(power_module_t));
+    int ret;
 
-        if (dev) {
-            /* Common hw_device_t fields */
-            dev->common.tag = HARDWARE_MODULE_TAG;
-            dev->common.module_api_version = POWER_MODULE_API_VERSION_0_5;
-            dev->common.module_api_version = HARDWARE_HAL_API_VERSION;
-
-            dev->init = power_init;
-            dev->powerHint = power_hint;
-            dev->setInteractive = set_interactive;
-            dev->get_number_of_platform_modes = get_number_of_platform_modes;
-            dev->get_platform_low_power_stats = get_platform_low_power_stats;
-            dev->get_voter_list = get_voter_list;
-
-            *device = (hw_device_t*)dev;
-        } else
-            retval = -ENOMEM;
-    } else {
-        retval = -EINVAL;
+    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;
     }
 
-    ALOGV("%s: exit %d", __FUNCTION__, retval);
-    return retval;
+    return 0;
 }
-
-static struct hw_module_methods_t power_module_methods = {
-    .open = power_open,
-};
-
-struct power_module HAL_MODULE_INFO_SYM = {
-    .common = {
-        .tag = HARDWARE_MODULE_TAG,
-        .module_api_version = POWER_MODULE_API_VERSION_0_5,
-        .hal_api_version = HARDWARE_HAL_API_VERSION,
-        .id = POWER_HARDWARE_MODULE_ID,
-        .name = "QCOM Power HAL",
-        .author = "Qualcomm",
-        .methods = &power_module_methods,
-    },
-
-    .init = power_init,
-    .powerHint = power_hint,
-    .setInteractive = set_interactive,
-    .get_number_of_platform_modes = get_number_of_platform_modes,
-    .get_platform_low_power_stats = get_platform_low_power_stats,
-    .get_voter_list = get_voter_list
-};
diff --git a/power/power-helper.h b/power/power-helper.h
new file mode 100644
index 0000000..95fb8f7
--- /dev/null
+++ b/power/power-helper.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * *    * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __POWER_HELPER_H__
+#define __POWER_HELPER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <hardware/power.h>
+
+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
+};
+
+enum platform_mode_id {
+    RPM_MODE_XO = 0,
+    RPM_MODE_VMIN,
+
+    //Don't add any lines after that line
+    RPM_MODE_COUNT
+};
+
+#define XO_VOTERS 4
+#define VMIN_VOTERS 0
+
+enum voter_id {
+    APSS,
+    MPSS,
+    ADSP,
+    SLPI,
+
+    //Don't add any lines after that line
+    VOTER_COUNT
+};
+
+enum subsystem_type {
+    SUBSYSTEM_WLAN,
+
+    //Don't add any lines after that line
+    SUBSYSTEM_COUNT
+};
+
+enum wlan_param_id {
+    CUMULATIVE_SLEEP_TIME_MS,
+    CUMULATIVE_TOTAL_ON_TIME_MS,
+    DEEP_SLEEP_ENTER_COUNTER,
+    LAST_DEEP_SLEEP_ENTER_TSTAMP_MS,
+
+    //Don't add any lines after that line
+    WLAN_PARAM_COUNT
+};
+
+enum wlan_state_id {
+    WLAN_STATE_ACTIVE = 0,
+    WLAN_STATE_DEEP_SLEEP,
+
+    //Don't add any lines after that line
+    WLAN_STATE_COUNT
+};
+
+
+void power_init(void);
+void power_hint(power_hint_t hint, void *data);
+void power_set_interactive(int on);
+int extract_platform_stats(uint64_t *list);
+int extract_wlan_stats(uint64_t *list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //__POWER_HELPER_H__
diff --git a/power/service.cpp b/power/service.cpp
new file mode 100644
index 0000000..e138fcc
--- /dev/null
+++ b/power/service.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 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 LOG_TAG "android.hardware.power@1.1-service.marlin"
+
+#include <android/log.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hardware/power.h>
+#include "Power.h"
+
+using android::sp;
+using android::status_t;
+using android::OK;
+
+// libhwbinder:
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+// Generated HIDL files
+using android::hardware::power::V1_1::IPower;
+using android::hardware::power::V1_1::implementation::Power;
+
+int main() {
+
+    status_t status;
+    android::sp<IPower> service = nullptr;
+
+    ALOGI("Power HAL Service 1.1 for Marlin is starting.");
+
+    service = new Power();
+    if (service == nullptr) {
+        ALOGE("Can not create an instance of Power HAL Iface, exiting.");
+
+        goto shutdown;
+    }
+
+    configureRpcThreadpool(1, true /*callerWillJoin*/);
+
+    status = service->registerAsService();
+    if (status != OK) {
+        ALOGE("Could not register service for Power HAL Iface (%d).", status);
+        goto shutdown;
+    }
+
+    ALOGI("Power Service is ready");
+    joinRpcThreadpool();
+    //Should not pass this line
+
+shutdown:
+    // In normal operation, we don't expect the thread pool to exit
+
+    ALOGE("Power Service is shutting down");
+    return 1;
+}
diff --git a/sepolicy/file.te b/sepolicy/file.te
index 5081d8e..1ad44a2 100644
--- a/sepolicy/file.te
+++ b/sepolicy/file.te
@@ -42,6 +42,7 @@
 type debugfs_rmt_storage, debugfs_type, fs_type;
 type debugfs_sps, debugfs_type, fs_type;
 type debugfs_rpm, debugfs_type, fs_type;
+type debugfs_wlan, debugfs_type, fs_type;
 type debugfs_kgsl, debugfs_type, fs_type;
 type debugfs_ipc, debugfs_type, fs_type;
 type debugfs_bufinfo, debugfs_type, fs_type;
diff --git a/sepolicy/file_contexts b/sepolicy/file_contexts
index e1bbb3c..ae152a5 100644
--- a/sepolicy/file_contexts
+++ b/sepolicy/file_contexts
@@ -117,6 +117,7 @@
 /vendor/bin/hw/android\.hardware\.drm@1\.0-service.widevine          u:object_r:hal_drm_widevine_exec:s0
 /vendor/bin/hw/android\.hardware\.dumpstate@1\.0-service.marlin      u:object_r:hal_dumpstate_impl_exec:s0
 /vendor/bin/hw/android\.hardware\.usb@1\.0-service.marlin            u:object_r:hal_usb_default_exec:s0
+/vendor/bin/hw/android\.hardware\.power@1\.1-service.marlin          u:object_r:hal_power_default_exec:s0
 /vendor/bin/hw/android\.hardware\.vibrator@1\.0-service.marlin       u:object_r:hal_vibrator_default_exec:s0
 /vendor/bin/msm_irqbalance  u:object_r:irqbalance_exec:s0
 /vendor/bin/nanohub_slpi    u:object_r:nanohub_slpi_exec:s0
diff --git a/sepolicy/genfs_contexts b/sepolicy/genfs_contexts
index 6b896df..7f45087 100644
--- a/sepolicy/genfs_contexts
+++ b/sepolicy/genfs_contexts
@@ -13,3 +13,4 @@
 genfscon proc /debugdriver/driverdump                 u:object_r:proc_wifi_dbg:s0
 
 genfscon debugfs /kgsl/proc                           u:object_r:debugfs_kgsl:s0
+genfscon debugfs /wlan_wcnss                          u:object_r:debugfs_wlan:s0
diff --git a/sepolicy/hal_power.te b/sepolicy/hal_power.te
index 8d24f17..e37b033 100644
--- a/sepolicy/hal_power.te
+++ b/sepolicy/hal_power.te
@@ -2,7 +2,14 @@
 allow hal_power perfd_data_file:dir search;
 allow hal_power perfd_data_file:sock_file write;
 
-allow hal_power debugfs_rpm:file { open read getattr };
-
 allow hal_power sysfs_soc:dir search;
 allow hal_power sysfs_soc:file { open read };
+
+userdebug_or_eng(`
+# debugfs entries are only needed in user-debug or eng builds
+allow hal_power debugfs_rpm:file { open read getattr };
+
+allow hal_power debugfs_wlan:dir search;
+allow hal_power debugfs_wlan:file { open read getattr };
+')
+
diff --git a/sepolicy/hal_wifi.te b/sepolicy/hal_wifi.te
index bd7ebc1..896685d 100644
--- a/sepolicy/hal_wifi.te
+++ b/sepolicy/hal_wifi.te
@@ -8,3 +8,11 @@
 
 # Allow wifi hal to read debug info from the driver.
 r_dir_file(hal_wifi, proc_wifi_dbg)
+
+userdebug_or_eng(`
+# debugfs entries are only needed in user-debug or eng builds
+
+# Allow wifi hal to access wlan debugfs files and directories
+allow hal_wifi debugfs_wlan:dir search;
+')
+