Merge "Add REDUCE_* operations"
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 6858819..0bd297b 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -305,6 +305,14 @@
         </interface>
     </hal>
     <hal format="hidl" optional="true">
+        <name>android.hardware.power.stats</name>
+        <version>1.0</version>
+        <interface>
+            <name>IPowerStats</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
         <name>android.hardware.radio</name>
         <version>1.0-2</version>
         <interface>
diff --git a/keymaster/3.0/vts/functional/keymaster_tags.h b/keymaster/3.0/vts/functional/keymaster_tags.h
index f241ef1..8544bf7 100644
--- a/keymaster/3.0/vts/functional/keymaster_tags.h
+++ b/keymaster/3.0/vts/functional/keymaster_tags.h
@@ -274,7 +274,10 @@
  */
 template <typename ValueT> class NullOr {
     template <typename T> struct reference_initializer {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnull-dereference"
         static T&& init() { return *static_cast<std::remove_reference_t<T>*>(nullptr); }
+#pragma GCC diagnostic pop
     };
     template <typename T> struct pointer_initializer {
         static T init() { return nullptr; }
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
index 61c444c..97dab68 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
@@ -282,7 +282,10 @@
 class NullOr {
     template <typename T>
     struct reference_initializer {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnull-dereference"
         static T&& init() { return *static_cast<std::remove_reference_t<T>*>(nullptr); }
+#pragma GCC diagnostic pop
     };
     template <typename T>
     struct pointer_initializer {
diff --git a/power/stats/1.0/Android.bp b/power/stats/1.0/Android.bp
new file mode 100644
index 0000000..9a956e4
--- /dev/null
+++ b/power/stats/1.0/Android.bp
@@ -0,0 +1,29 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.power.stats@1.0",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "types.hal",
+        "IPowerStats.hal",
+    ],
+    interfaces: [
+        "android.hidl.base@1.0",
+    ],
+    types: [
+        "EnergyData",
+        "PowerEntityInfo",
+        "PowerEntityStateInfo",
+        "PowerEntityStateResidencyData",
+        "PowerEntityStateResidencyResult",
+        "PowerEntityStateSpace",
+        "PowerEntityType",
+        "RailInfo",
+        "Status",
+    ],
+    gen_java: false,
+}
+
diff --git a/power/stats/1.0/IPowerStats.hal b/power/stats/1.0/IPowerStats.hal
new file mode 100644
index 0000000..74ceb8f
--- /dev/null
+++ b/power/stats/1.0/IPowerStats.hal
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+package android.hardware.power.stats@1.0;
+
+interface IPowerStats {
+
+    /**
+     * Rail information:
+     * Reports information related to the rails being monitored.
+     *
+     * @return rails Information about monitored rails.
+     * @return status SUCCESS on success or NOT_SUPPORTED if
+     *     feature is not enabled or FILESYSTEM_ERROR on filesystem nodes
+     *     access error.
+     */
+    getRailInfo()
+        generates(vec<RailInfo> rails, Status status);
+
+    /**
+     * Rail level energy measurements for low frequency clients:
+     * Reports accumulated energy since boot on each rail.
+     *
+     * @param railIndices Indices of rails for which data is required.
+     *     To get data for all rails pass an empty vector. Rail name to
+     *     index mapping can be queried from getRailInfo() API.
+     * @return data Energy values since boot for all requested rails.
+     * @return status SUCCESS on success or NOT_SUPPORTED if
+     *     feature is not enabled or FILESYSTEM_ERROR on filesystem nodes
+     *     access error.
+     */
+    getEnergyData(vec<uint32_t> railIndices)
+        generates(vec<EnergyData> data, Status status);
+
+    /**
+     * Stream rail level power measurements for high frequency clients:
+     * Streams accumulated energy since boot on each rail. This API is
+     * asynchronous.
+     *
+     * @param timeMs Time(in ms) for which energyData should be streamed
+     * @param samplingRate Frequency(in Hz) at which samples should be
+     *     captured. If the requested sampling rate is not supported then
+     *     SUCCESS is returned and numSamples are reported back according
+     *     to the supported sampling rate.
+     * @return mqDesc Blocking Synchronous Fast Message Queue descriptor - One
+     *     writer(power.stats HAL) and one reader are supported. Data is
+     *     present in the following format in the queue:
+     *     +-----------------------+       <--
+     *     | EnergyData for rail 1 |         |
+     *     +-----------------------+         |
+     *     | EnergyData for rail 2 |         |
+     *     +-----------------------+         |
+     *     |          .            |         |-- 1st Sample
+     *     |          .            |         |
+     *     |          .            |         |
+     *     +-----------------------+         |
+     *     | EnergyData for rail n |         |
+     *     +-----------------------+       <--
+     *     |          .            |
+     *     |          .            |
+     *     |          .            |
+     *     +-----------------------+       <--
+     *     | EnergyData for rail 1 |         |
+     *     +-----------------------+         |
+     *     | EnergyData for rail 2 |         |
+     *     +-----------------------+         |
+     *     |          .            |         |-- kth Sample
+     *     |          .            |         |
+     *     |          .            |         |
+     *     +-----------------------+         |
+     *     | EnergyData for rail n |         |
+     *     +-----------------------+       <--
+     *
+     *     where,
+     *     n = railsPerSample
+     *     k = numSamples
+     *
+     * @return numSamples Number of samples which will be generated in timeMs.
+     * @return railsPerSample Number of rails measured per sample.
+     * @return status SUCCESS on success or FILESYSTEM_ERROR on filesystem
+     *     nodes access or NOT_SUPPORTED if feature is not enabled or
+     *     INSUFFICIENT_RESOURCES if there are not enough resources.
+     */
+    streamEnergyData(uint32_t timeMs, uint32_t samplingRate)
+        generates(fmq_sync<EnergyData> mqDesc, uint32_t numSamples,
+                uint32_t railsPerSample, Status status);
+
+    /**
+     * PowerEntity information:
+     * Reports information related to all supported PowerEntity(s) for which
+     * data is available. A PowerEntity is defined as a platform subsystem,
+     * peripheral, or power domain that impacts the total device power
+     * consumption.
+     *
+     * @return powerEntityInfos List of information on each PowerEntity
+     * @return status SUCCESS on success, NOT_SUPPORTED if feature is not
+     *     enabled, FILESYSTEM_ERROR if there was an error accessing the
+     *     filesystem.
+     */
+    getPowerEntityInfo()
+        generates(vec<PowerEntityInfo> powerEntityInfos, Status status);
+
+    /**
+     * PowerEntity state information:
+     * Reports the set of power states for which the specified
+     * PowerEntity(s) provide residency data.
+     *
+     * @param powerEntityIds collection of IDs of PowerEntity(s) for which
+     *     state information is requested. PowerEntity name to ID mapping may
+     *     be queried from getPowerEntityInfo(). To get state space
+     *     information for all PowerEntity(s) pass an empty vector.
+     *
+     * @return powerEntityStateSpaces PowerEntity state space information for
+     *     each specified PowerEntity that provides state space information.
+     * @return status SUCCESS on success, NOT_SUPPORTED if feature is not
+     *     enabled, FILESYSTEM_ERROR if there was an error accessing the
+     *     filesystem, INVALID_INPUT if any requested PowerEntity(s) do not
+     *     provide state space information and there was not a filesystem error.
+     */
+    getPowerEntityStateInfo(vec<uint32_t> powerEntityIds)
+        generates(vec<PowerEntityStateSpace> powerEntityStateSpaces,
+                  Status status);
+
+    /**
+     * PowerEntity residencies for low frequency clients:
+     * Reports accumulated residency data for each specified PowerEntity.
+     * Each PowerEntity may reside in one of multiple states. It may also
+     * transition to another state. Residency data is an accumulation of time
+     * that a specified PowerEntity resided in each of its possible states,
+     * the number of times that each state was entered, and a timestamp
+     * corresponding to the last time that state was entered. Data is
+     * accumulated starting from the last time the PowerEntity was reset.
+     *
+     * @param powerEntityId collection of IDs of PowerEntity(s) for which
+     *     residency data is requested. PowerEntity name to ID mapping may
+     *     be queried from getPowerEntityInfo(). To get state residency
+     *     data for all PowerEntity(s) pass an empty vector.
+     * @return stateResidencyResults state residency data for each specified
+     *     PowerEntity that provides state residency data.
+     * @return status SUCCESS on success, NOT_SUPPORTED if feature is not
+     *     enabled, FILESYSTEM_ERROR if there was an error accessing the
+     *     filesystem, INVALID_INPUT if any requested PowerEntity(s) do not
+     *     provide state residency data and there was not a filesystem error.
+     */
+    getPowerEntityStateResidencyData(vec<uint32_t> powerEntityIds)
+        generates(vec<PowerEntityStateResidencyResult> stateResidencyResults,
+                  Status status);
+};
diff --git a/power/stats/1.0/default/Android.bp b/power/stats/1.0/default/Android.bp
new file mode 100644
index 0000000..04270c1
--- /dev/null
+++ b/power/stats/1.0/default/Android.bp
@@ -0,0 +1,34 @@
+// 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
+
+cc_library_shared {
+    name: "android.hardware.power.stats@1.0-service",
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.power.stats@1.0-service.rc"],
+    srcs: ["service.cpp", "PowerStats.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+        "android.hardware.power.stats@1.0",
+    ],
+    vendor: true,
+}
diff --git a/power/stats/1.0/default/PowerStats.cpp b/power/stats/1.0/default/PowerStats.cpp
new file mode 100644
index 0000000..810c575
--- /dev/null
+++ b/power/stats/1.0/default/PowerStats.cpp
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+
+#include "PowerStats.h"
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <algorithm>
+#include <exception>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+namespace V1_0 {
+namespace implementation {
+
+#define MAX_FILE_PATH_LEN 128
+#define MAX_DEVICE_NAME_LEN 64
+#define MAX_QUEUE_SIZE 8192
+
+constexpr char kIioDirRoot[] = "/sys/bus/iio/devices/";
+constexpr char kDeviceName[] = "pm_device_name";
+constexpr char kDeviceType[] = "iio:device";
+constexpr uint32_t MAX_SAMPLING_RATE = 10;
+constexpr uint64_t WRITE_TIMEOUT_NS = 1000000000;
+
+void PowerStats::findIioPowerMonitorNodes() {
+    struct dirent* ent;
+    int fd;
+    char devName[MAX_DEVICE_NAME_LEN];
+    char filePath[MAX_FILE_PATH_LEN];
+    DIR* iioDir = opendir(kIioDirRoot);
+    if (!iioDir) {
+        ALOGE("Error opening directory: %s", kIioDirRoot);
+        return;
+    }
+    while (ent = readdir(iioDir), ent) {
+        if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0 &&
+            strlen(ent->d_name) > strlen(kDeviceType) &&
+            strncmp(ent->d_name, kDeviceType, strlen(kDeviceType)) == 0) {
+            snprintf(filePath, MAX_FILE_PATH_LEN, "%s/%s", ent->d_name, "name");
+            fd = openat(dirfd(iioDir), filePath, O_RDONLY);
+            if (fd < 0) {
+                ALOGW("Failed to open directory: %s", filePath);
+                continue;
+            }
+            if (read(fd, devName, MAX_DEVICE_NAME_LEN) < 0) {
+                ALOGW("Failed to read device name from file: %s(%d)", filePath, fd);
+                close(fd);
+                continue;
+            }
+
+            if (strncmp(devName, kDeviceName, strlen(kDeviceName)) == 0) {
+                snprintf(filePath, MAX_FILE_PATH_LEN, "%s/%s", kIioDirRoot, ent->d_name);
+                mPm.devicePaths.push_back(filePath);
+            }
+            close(fd);
+        }
+    }
+    closedir(iioDir);
+    return;
+}
+
+size_t PowerStats::parsePowerRails() {
+    std::string data;
+    std::string railFileName;
+    std::string spsFileName;
+    uint32_t index = 0;
+    uint32_t samplingRate;
+    for (const auto& path : mPm.devicePaths) {
+        railFileName = path + "/enabled_rails";
+        spsFileName = path + "/sampling_rate";
+        if (!android::base::ReadFileToString(spsFileName, &data)) {
+            ALOGW("Error reading file: %s", spsFileName.c_str());
+            continue;
+        }
+        samplingRate = strtoul(data.c_str(), NULL, 10);
+        if (!samplingRate || samplingRate == ULONG_MAX) {
+            ALOGE("Error parsing: %s", spsFileName.c_str());
+            break;
+        }
+        if (!android::base::ReadFileToString(railFileName, &data)) {
+            ALOGW("Error reading file: %s", railFileName.c_str());
+            continue;
+        }
+        std::istringstream railNames(data);
+        std::string line;
+        while (std::getline(railNames, line)) {
+            std::vector<std::string> words = android::base::Split(line, ":");
+            if (words.size() == 2) {
+                mPm.railsInfo.emplace(words[0], RailData{.devicePath = path,
+                                                         .index = index,
+                                                         .subsysName = words[1],
+                                                         .samplingRate = samplingRate});
+                index++;
+            } else {
+                ALOGW("Unexpected format in file: %s", railFileName.c_str());
+            }
+        }
+    }
+    return index;
+}
+
+int PowerStats::parseIioEnergyNode(std::string devName) {
+    int ret = 0;
+    std::string data;
+    std::string fileName = devName + "/energy_value";
+    if (!android::base::ReadFileToString(fileName, &data)) {
+        ALOGE("Error reading file: %s", fileName.c_str());
+        return -1;
+    }
+
+    std::istringstream energyData(data);
+    std::string line;
+    uint64_t timestamp = 0;
+    bool timestampRead = false;
+    while (std::getline(energyData, line)) {
+        std::vector<std::string> words = android::base::Split(line, ",");
+        if (timestampRead == false) {
+            if (words.size() == 1) {
+                timestamp = strtoull(words[0].c_str(), NULL, 10);
+                if (timestamp == 0 || timestamp == ULLONG_MAX) {
+                    ALOGW("Potentially wrong timestamp: %" PRIu64, timestamp);
+                }
+                timestampRead = true;
+            }
+        } else if (words.size() == 2) {
+            std::string railName = words[0];
+            if (mPm.railsInfo.count(railName) != 0) {
+                size_t index = mPm.railsInfo[railName].index;
+                mPm.reading[index].index = index;
+                mPm.reading[index].timestamp = timestamp;
+                mPm.reading[index].energy = strtoull(words[1].c_str(), NULL, 10);
+                if (mPm.reading[index].energy == ULLONG_MAX) {
+                    ALOGW("Potentially wrong energy value: %" PRIu64, mPm.reading[index].energy);
+                }
+            }
+        } else {
+            ALOGW("Unexpected format in file: %s", fileName.c_str());
+            ret = -1;
+            break;
+        }
+    }
+    return ret;
+}
+
+Status PowerStats::parseIioEnergyNodes() {
+    Status ret = Status::SUCCESS;
+    if (mPm.hwEnabled == false) {
+        return Status::NOT_SUPPORTED;
+    }
+
+    for (const auto& devicePath : mPm.devicePaths) {
+        if (parseIioEnergyNode(devicePath) < 0) {
+            ALOGE("Error in parsing power stats");
+            ret = Status::FILESYSTEM_ERROR;
+            break;
+        }
+    }
+    return ret;
+}
+
+PowerStats::PowerStats() {
+    findIioPowerMonitorNodes();
+    size_t numRails = parsePowerRails();
+    if (mPm.devicePaths.empty() || numRails == 0) {
+        mPm.hwEnabled = false;
+    } else {
+        mPm.hwEnabled = true;
+        mPm.reading.resize(numRails);
+    }
+}
+
+Return<void> PowerStats::getRailInfo(getRailInfo_cb _hidl_cb) {
+    hidl_vec<RailInfo> rInfo;
+    Status ret = Status::SUCCESS;
+    size_t index;
+    std::lock_guard<std::mutex> _lock(mPm.mLock);
+    if (mPm.hwEnabled == false) {
+        _hidl_cb(rInfo, Status::NOT_SUPPORTED);
+        return Void();
+    }
+    rInfo.resize(mPm.railsInfo.size());
+    for (const auto& railData : mPm.railsInfo) {
+        index = railData.second.index;
+        rInfo[index].railName = railData.first;
+        rInfo[index].subsysName = railData.second.subsysName;
+        rInfo[index].index = index;
+        rInfo[index].samplingRate = railData.second.samplingRate;
+    }
+    _hidl_cb(rInfo, ret);
+    return Void();
+}
+
+Return<void> PowerStats::getEnergyData(const hidl_vec<uint32_t>& railIndices,
+                                       getEnergyData_cb _hidl_cb) {
+    hidl_vec<EnergyData> eVal;
+    std::lock_guard<std::mutex> _lock(mPm.mLock);
+    Status ret = parseIioEnergyNodes();
+
+    if (ret != Status::SUCCESS) {
+        ALOGE("Failed to getEnergyData");
+        _hidl_cb(eVal, ret);
+        return Void();
+    }
+
+    if (railIndices.size() == 0) {
+        eVal.resize(mPm.railsInfo.size());
+        memcpy(&eVal[0], &mPm.reading[0], mPm.reading.size() * sizeof(EnergyData));
+    } else {
+        eVal.resize(railIndices.size());
+        int i = 0;
+        for (const auto& railIndex : railIndices) {
+            if (railIndex >= mPm.reading.size()) {
+                ret = Status::INVALID_INPUT;
+                eVal.resize(0);
+                break;
+            }
+            memcpy(&eVal[i], &mPm.reading[railIndex], sizeof(EnergyData));
+            i++;
+        }
+    }
+    _hidl_cb(eVal, ret);
+    return Void();
+}
+
+Return<void> PowerStats::streamEnergyData(uint32_t timeMs, uint32_t samplingRate,
+                                          streamEnergyData_cb _hidl_cb) {
+    std::lock_guard<std::mutex> _lock(mPm.mLock);
+    if (mPm.fmqSynchronized != nullptr) {
+        _hidl_cb(MessageQueueSync::Descriptor(), 0, 0, Status::INSUFFICIENT_RESOURCES);
+        return Void();
+    }
+    uint32_t sps = std::min(samplingRate, MAX_SAMPLING_RATE);
+    uint32_t numSamples = timeMs * sps / 1000;
+    mPm.fmqSynchronized.reset(new (std::nothrow) MessageQueueSync(MAX_QUEUE_SIZE, true));
+    if (mPm.fmqSynchronized == nullptr || mPm.fmqSynchronized->isValid() == false) {
+        mPm.fmqSynchronized = nullptr;
+        _hidl_cb(MessageQueueSync::Descriptor(), 0, 0, Status::INSUFFICIENT_RESOURCES);
+        return Void();
+    }
+    std::thread pollThread = std::thread([this, sps, numSamples]() {
+        uint64_t sleepTimeUs = 1000000 / sps;
+        uint32_t currSamples = 0;
+        while (currSamples < numSamples) {
+            mPm.mLock.lock();
+            if (parseIioEnergyNodes() == Status::SUCCESS) {
+                mPm.fmqSynchronized->writeBlocking(&mPm.reading[0], mPm.reading.size(),
+                                                   WRITE_TIMEOUT_NS);
+                mPm.mLock.unlock();
+                currSamples++;
+                if (usleep(sleepTimeUs) < 0) {
+                    ALOGW("Sleep interrupted");
+                    break;
+                }
+            } else {
+                mPm.mLock.unlock();
+                break;
+            }
+        }
+        mPm.mLock.lock();
+        mPm.fmqSynchronized = nullptr;
+        mPm.mLock.unlock();
+        return;
+    });
+    pollThread.detach();
+    _hidl_cb(*(mPm.fmqSynchronized)->getDesc(), numSamples, mPm.reading.size(), Status::SUCCESS);
+    return Void();
+}
+
+Return<void> PowerStats::getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb) {
+    hidl_vec<PowerEntityInfo> eInfo;
+    _hidl_cb(eInfo, Status::NOT_SUPPORTED);
+    return Void();
+}
+
+Return<void> PowerStats::getPowerEntityStateInfo(const hidl_vec<uint32_t>& powerEntityIds,
+                                                 getPowerEntityStateInfo_cb _hidl_cb) {
+    (void)powerEntityIds;
+    hidl_vec<PowerEntityStateSpace> powerEntityStateSpaces;
+    _hidl_cb(powerEntityStateSpaces, Status::NOT_SUPPORTED);
+    return Void();
+}
+
+Return<void> PowerStats::getPowerEntityStateResidencyData(
+    const hidl_vec<uint32_t>& powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) {
+    (void)powerEntityIds;
+    hidl_vec<PowerEntityStateResidencyResult> results;
+    _hidl_cb(results, Status::NOT_SUPPORTED);
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
diff --git a/power/stats/1.0/default/PowerStats.h b/power/stats/1.0/default/PowerStats.h
new file mode 100644
index 0000000..fb2c6a8
--- /dev/null
+++ b/power/stats/1.0/default/PowerStats.h
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_POWERSTATS_V1_0_POWERSTATS_H
+#define ANDROID_HARDWARE_POWERSTATS_V1_0_POWERSTATS_H
+
+#include <android/hardware/power/stats/1.0/IPowerStats.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::power::stats::V1_0::EnergyData;
+using ::android::hardware::power::stats::V1_0::PowerEntityInfo;
+using ::android::hardware::power::stats::V1_0::PowerEntityStateInfo;
+using ::android::hardware::power::stats::V1_0::PowerEntityStateResidencyData;
+using ::android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
+using ::android::hardware::power::stats::V1_0::PowerEntityStateSpace;
+using ::android::hardware::power::stats::V1_0::PowerEntityType;
+using ::android::hardware::power::stats::V1_0::RailInfo;
+using ::android::hardware::power::stats::V1_0::Status;
+
+typedef MessageQueue<EnergyData, kSynchronizedReadWrite> MessageQueueSync;
+struct RailData {
+    std::string devicePath;
+    uint32_t index;
+    std::string subsysName;
+    uint32_t samplingRate;
+};
+
+struct OnDeviceMmt {
+    std::mutex mLock;
+    bool hwEnabled;
+    std::vector<std::string> devicePaths;
+    std::map<std::string, RailData> railsInfo;
+    std::vector<EnergyData> reading;
+    std::unique_ptr<MessageQueueSync> fmqSynchronized;
+};
+
+struct PowerStats : public IPowerStats {
+    PowerStats();
+    // Methods from ::android::hardware::power::stats::V1_0::IPowerStats follow.
+    Return<void> getRailInfo(getRailInfo_cb _hidl_cb) override;
+    Return<void> getEnergyData(const hidl_vec<uint32_t>& railIndices,
+                               getEnergyData_cb _hidl_cb) override;
+    Return<void> streamEnergyData(uint32_t timeMs, uint32_t samplingRate,
+                                  streamEnergyData_cb _hidl_cb) override;
+    Return<void> getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb) override;
+    Return<void> getPowerEntityStateInfo(const hidl_vec<uint32_t>& powerEntityIds,
+                                         getPowerEntityStateInfo_cb _hidl_cb) override;
+    Return<void> getPowerEntityStateResidencyData(
+        const hidl_vec<uint32_t>& powerEntityIds,
+        getPowerEntityStateResidencyData_cb _hidl_cb) override;
+
+   private:
+    OnDeviceMmt mPm;
+    void findIioPowerMonitorNodes();
+    size_t parsePowerRails();
+    int parseIioEnergyNode(std::string devName);
+    Status parseIioEnergyNodes();
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_POWERSTATS_V1_0_POWERSTATS_H
diff --git a/power/stats/1.0/default/android.hardware.power.stats@1.0-service.rc b/power/stats/1.0/default/android.hardware.power.stats@1.0-service.rc
new file mode 100644
index 0000000..d7e546b
--- /dev/null
+++ b/power/stats/1.0/default/android.hardware.power.stats@1.0-service.rc
@@ -0,0 +1,4 @@
+service vendor.power.stats-hal-1-0 /vendor/bin/hw/android.hardware.power.stats@1.0-service
+    class hal
+    user system
+    group system
diff --git a/power/stats/1.0/default/service.cpp b/power/stats/1.0/default/service.cpp
new file mode 100644
index 0000000..80649f5
--- /dev/null
+++ b/power/stats/1.0/default/service.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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 LOG_TAG "android.hardware.power.stats@1.0-service"
+
+#include <android/log.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "PowerStats.h"
+
+using android::OK;
+using android::sp;
+using android::status_t;
+
+// libhwbinder:
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+// Generated HIDL files
+using android::hardware::power::stats::V1_0::IPowerStats;
+using android::hardware::power::stats::V1_0::implementation::PowerStats;
+
+int main(int /* argc */, char** /* argv */) {
+    ALOGI("power.stats service 1.0 is starting.");
+
+    android::sp<IPowerStats> service = new PowerStats();
+    if (service == nullptr) {
+        ALOGE("Can not create an instance of power.stats HAL Iface, exiting.");
+        return 1;
+    }
+
+    configureRpcThreadpool(1, true /*callerWillJoin*/);
+
+    status_t status = service->registerAsService();
+    if (status != OK) {
+        ALOGE("Could not register service for power.stats HAL Iface (%d), exiting.", status);
+        return 1;
+    }
+
+    ALOGI("power.stats service is ready");
+    joinRpcThreadpool();
+
+    // In normal operation, we don't expect the thread pool to exit
+    ALOGE("power.stats service is shutting down");
+    return 1;
+}
diff --git a/power/stats/1.0/types.hal b/power/stats/1.0/types.hal
new file mode 100644
index 0000000..644224b
--- /dev/null
+++ b/power/stats/1.0/types.hal
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+package android.hardware.power.stats@1.0;
+
+enum Status : uint32_t {
+    SUCCESS = 0,
+    NOT_SUPPORTED = 1,
+    INVALID_INPUT = 2,
+    FILESYSTEM_ERROR = 3,
+    INSUFFICIENT_RESOURCES = 4,
+};
+
+struct RailInfo {
+    /** Index corresponding to the rail */
+    uint32_t index;
+    /** Name of the rail */
+    string railName;
+    /** Name of the subsystem to which this rail belongs */
+    string subsysName;
+    /** Hardware sampling rate */
+    uint32_t samplingRate;
+};
+
+struct EnergyData {
+    /**
+     * Index corresponding to the rail. This index matches
+     * the index returned in RailInfo
+     */
+    uint32_t index;
+    /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
+    uint64_t timestamp;
+    /** Accumulated energy since device boot in microwatt-seconds (uWs) */
+    uint64_t energy;
+};
+
+enum PowerEntityType : uint32_t {
+    /**
+     * A subsystem is a self-contained compute unit. Some examples include
+     * application processor, DSP, GPU.
+     */
+    SUBSYSTEM = 0,
+    /**
+     * A peripheral is an auxiliary device that connects to and works with a
+     * compute unit. Some examples include simple sensors, camera, display.
+     */
+    PERIPHERAL = 1,
+    /**
+     * A power domain is a single subsystem or a collection of subsystems
+     * that is controlled by a single voltage rail.
+     */
+    POWER_DOMAIN = 2,
+};
+
+/**
+ * PowerEntityInfo contains information, such as the ID, name, and type of a
+ * given PowerEntity.
+ */
+struct PowerEntityInfo {
+    /** Unique ID corresponding to the PowerEntity */
+    uint32_t powerEntityId;
+    /** Name of the PowerEntity */
+    string powerEntityName;
+    /** Type of the PowerEntity */
+    PowerEntityType type;
+};
+
+struct PowerEntityStateInfo {
+    /**
+     * ID corresponding to the state. Unique for a given PowerEntityStateSpace
+     */
+    uint32_t powerEntityStateId;
+    /** Name of the state */
+    string powerEntityStateName;
+};
+
+/**
+ * PowerEntityStateSpace contains the state space information of a given
+ * PowerEntity. The state space, is the set of possible states that a given
+ * PowerEntity provides residency data for.
+ */
+struct PowerEntityStateSpace {
+    /** Unique ID of the corresponding PowerEntity */
+    uint32_t powerEntityId;
+
+    /** List of states that the PowerEntity may reside in */
+    vec<PowerEntityStateInfo> states;
+};
+
+/** Contains residency data for a single state */
+struct PowerEntityStateResidencyData {
+    /** Unique ID of the corresponding PowerEntityStateInfo */
+    uint32_t powerEntityStateId;
+    /**
+     * Total time in milliseconds that the corresponding PowerEntity resided
+     * in this state since the PowerEntity was reset
+     */
+    uint64_t totalTimeInStateMs;
+    /**
+     * Total number of times that the state was entered since the corresponding
+     * PowerEntity was reset
+     */
+    uint64_t totalStateEntryCount;
+    /**
+     * Last time this state was entered. Time in milliseconds since the
+     * corresponding PowerEntity was reset
+     */
+    uint64_t lastEntryTimestampMs;
+};
+
+struct PowerEntityStateResidencyResult {
+    /** Unique ID of the corresponding PowerEntity */
+    uint32_t powerEntityId;
+    /** Residency data for each state the PowerEntity's state space */
+    vec<PowerEntityStateResidencyData> stateResidencyData;
+};
diff --git a/power/stats/1.0/vts/functional/Android.bp b/power/stats/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..4f0b325
--- /dev/null
+++ b/power/stats/1.0/vts/functional/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "VtsHalPowerStatsV1_0TargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults"
+    ],
+    srcs: [
+        "VtsHalPowerStatsV1_0TargetTest.cpp"
+    ],
+    static_libs: [
+        "android.hardware.power.stats@1.0"
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libhidlbase",
+        "libfmq",
+        "libhidltransport",
+        "libhwbinder",
+        "libutils",
+    ],
+}
diff --git a/power/stats/1.0/vts/functional/VtsHalPowerStatsV1_0TargetTest.cpp b/power/stats/1.0/vts/functional/VtsHalPowerStatsV1_0TargetTest.cpp
new file mode 100644
index 0000000..9f007e4
--- /dev/null
+++ b/power/stats/1.0/vts/functional/VtsHalPowerStatsV1_0TargetTest.cpp
@@ -0,0 +1,580 @@
+/*
+ * 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 LOG_TAG "android.power.stats.vts"
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
+#include <android-base/logging.h>
+#include <android/hardware/power/stats/1.0/IPowerStats.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <inttypes.h>
+#include <algorithm>
+#include <random>
+#include <thread>
+
+namespace android {
+namespace power {
+namespace stats {
+namespace vts {
+namespace {
+
+using android::sp;
+using android::hardware::hidl_vec;
+using android::hardware::kSynchronizedReadWrite;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::power::stats::V1_0::EnergyData;
+using android::hardware::power::stats::V1_0::IPowerStats;
+using android::hardware::power::stats::V1_0::PowerEntityInfo;
+using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
+using android::hardware::power::stats::V1_0::PowerEntityStateSpace;
+using android::hardware::power::stats::V1_0::RailInfo;
+using android::hardware::power::stats::V1_0::Status;
+
+}  // namespace
+
+typedef hardware::MessageQueue<EnergyData, kSynchronizedReadWrite> MessageQueueSync;
+// Test environment for Power HIDL HAL.
+class PowerStatsHidlEnv : public ::testing::VtsHalHidlTargetTestEnvBase {
+   public:
+    // get the test environment singleton
+    static PowerStatsHidlEnv* Instance() {
+        static PowerStatsHidlEnv* instance = new PowerStatsHidlEnv;
+        return instance;
+    }
+
+    virtual void registerTestServices() override { registerTestService<IPowerStats>(); }
+};
+
+class PowerStatsHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+   public:
+    virtual void SetUp() override {
+        service_ = ::testing::VtsHalHidlTargetTestBase::getService<IPowerStats>(
+            PowerStatsHidlEnv::Instance()->getServiceName<IPowerStats>());
+        ASSERT_NE(service_, nullptr);
+    }
+
+    virtual void TearDown() override {}
+
+    void getInfos(hidl_vec<PowerEntityInfo>& infos);
+    void getStateSpaces(hidl_vec<PowerEntityStateSpace>& stateSpaces,
+                        const std::vector<uint32_t>& ids);
+    void getResidencyResults(hidl_vec<PowerEntityStateResidencyResult>& results,
+                             const std::vector<uint32_t>& ids);
+    void getRandomIds(std::vector<uint32_t>& ids);
+
+    sp<IPowerStats> service_;
+};
+
+void PowerStatsHidlTest::getInfos(hidl_vec<PowerEntityInfo>& infos) {
+    Status status;
+    Return<void> ret = service_->getPowerEntityInfo([&status, &infos](auto rInfos, auto rStatus) {
+        status = rStatus;
+        infos = rInfos;
+    });
+    ASSERT_TRUE(ret.isOk());
+
+    if (status == Status::SUCCESS) {
+        ASSERT_NE(infos.size(), 0) << "powerEntityInfos must have entries if supported";
+    } else {
+        ASSERT_EQ(status, Status::NOT_SUPPORTED);
+        ASSERT_EQ(infos.size(), 0);
+        LOG(INFO) << "getPowerEntityInfo not supported";
+    }
+}
+
+void PowerStatsHidlTest::getStateSpaces(hidl_vec<PowerEntityStateSpace>& stateSpaces,
+                                        const std::vector<uint32_t>& ids = {}) {
+    Status status;
+    Return<void> ret = service_->getPowerEntityStateInfo(
+        ids, [&status, &stateSpaces](auto rStateSpaces, auto rStatus) {
+            status = rStatus;
+            stateSpaces = rStateSpaces;
+        });
+    ASSERT_TRUE(ret.isOk());
+
+    if (status == Status::SUCCESS) {
+        ASSERT_NE(stateSpaces.size(), 0) << "powerEntityStateSpaces must have entries if supported";
+    } else {
+        ASSERT_EQ(status, Status::NOT_SUPPORTED);
+        ASSERT_EQ(stateSpaces.size(), 0);
+        LOG(INFO) << "getPowerEntityStateInfo not supported";
+    }
+}
+
+void PowerStatsHidlTest::getResidencyResults(hidl_vec<PowerEntityStateResidencyResult>& results,
+                                             const std::vector<uint32_t>& ids = {}) {
+    Status status;
+    Return<void> ret = service_->getPowerEntityStateResidencyData(
+        ids, [&status, &results](auto rResults, auto rStatus) {
+            status = rStatus;
+            results = rResults;
+        });
+    ASSERT_TRUE(ret.isOk());
+
+    if (status == Status::SUCCESS) {
+        ASSERT_NE(results.size(), 0);
+    } else {
+        ASSERT_EQ(status, Status::NOT_SUPPORTED);
+        ASSERT_EQ(results.size(), 0);
+        LOG(INFO) << "getPowerEntityStateResidencyData not supported";
+    }
+}
+
+void PowerStatsHidlTest::getRandomIds(std::vector<uint32_t>& ids) {
+    hidl_vec<PowerEntityStateSpace> stateSpaces;
+    getStateSpaces(stateSpaces);
+
+    if (stateSpaces.size() == 0) {
+        return;
+    }
+
+    for (auto stateSpace : stateSpaces) {
+        ids.push_back(stateSpace.powerEntityId);
+    }
+
+    unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
+    auto gen = std::default_random_engine(seed);
+    std::uniform_int_distribution<uint32_t> dist(1, stateSpaces.size());
+
+    std::shuffle(ids.begin(), ids.end(), gen);
+    ids.resize(dist(gen));
+}
+
+// Each PowerEntity must have a valid name
+TEST_F(PowerStatsHidlTest, ValidatePowerEntityNames) {
+    hidl_vec<PowerEntityInfo> infos;
+    getInfos(infos);
+    for (auto info : infos) {
+        ASSERT_NE(info.powerEntityName, "");
+    }
+}
+
+// Each PowerEntity must have a unique ID
+TEST_F(PowerStatsHidlTest, ValidatePowerEntityIds) {
+    hidl_vec<PowerEntityInfo> infos;
+    getInfos(infos);
+
+    set<uint32_t> ids;
+    for (auto info : infos) {
+        ASSERT_TRUE(ids.insert(info.powerEntityId).second);
+    }
+}
+
+// Each PowerEntityStateSpace must have an associated PowerEntityInfo
+TEST_F(PowerStatsHidlTest, ValidateStateInfoAssociation) {
+    hidl_vec<PowerEntityInfo> infos;
+    getInfos(infos);
+
+    hidl_vec<PowerEntityStateSpace> stateSpaces;
+    getStateSpaces(stateSpaces);
+
+    std::set<uint32_t> ids;
+    for (auto info : infos) {
+        ids.insert(info.powerEntityId);
+    }
+
+    for (auto stateSpace : stateSpaces) {
+        ASSERT_NE(ids.count(stateSpace.powerEntityId), 0);
+    }
+}
+
+// Each state must have a valid name
+TEST_F(PowerStatsHidlTest, ValidateStateNames) {
+    hidl_vec<PowerEntityStateSpace> stateSpaces;
+    getStateSpaces(stateSpaces);
+
+    for (auto stateSpace : stateSpaces) {
+        for (auto state : stateSpace.states) {
+            ASSERT_NE(state.powerEntityStateName, "");
+        }
+    }
+}
+
+// Each state must have an ID that is unique to the PowerEntityStateSpace
+TEST_F(PowerStatsHidlTest, ValidateStateUniqueIds) {
+    hidl_vec<PowerEntityStateSpace> stateSpaces;
+    getStateSpaces(stateSpaces);
+
+    for (auto stateSpace : stateSpaces) {
+        set<uint32_t> stateIds;
+        for (auto state : stateSpace.states) {
+            ASSERT_TRUE(stateIds.insert(state.powerEntityStateId).second);
+        }
+    }
+}
+
+// getPowerEntityStateInfo must support passing in requested IDs
+// Results must contain state space information for all requested IDs
+TEST_F(PowerStatsHidlTest, ValidateStateInfoAssociationSelect) {
+    std::vector<uint32_t> randomIds;
+    getRandomIds(randomIds);
+
+    if (randomIds.empty()) {
+        return;
+    }
+
+    hidl_vec<PowerEntityStateSpace> stateSpaces;
+    getStateSpaces(stateSpaces, randomIds);
+
+    ASSERT_EQ(stateSpaces.size(), randomIds.size());
+
+    std::set<uint32_t> ids;
+    for (auto id : randomIds) {
+        ids.insert(id);
+    }
+    for (auto stateSpace : stateSpaces) {
+        ASSERT_NE(ids.count(stateSpace.powerEntityId), 0);
+    }
+}
+
+// Requested state space info must match initially obtained stateinfos
+TEST_F(PowerStatsHidlTest, ValidateStateInfoSelect) {
+    hidl_vec<PowerEntityStateSpace> stateSpaces;
+    getStateSpaces(stateSpaces);
+    if (stateSpaces.size() == 0) {
+        return;
+    }
+
+    std::vector<uint32_t> randomIds;
+    getRandomIds(randomIds);
+    ASSERT_FALSE(randomIds.empty());
+
+    hidl_vec<PowerEntityStateSpace> selectedStateSpaces;
+    getStateSpaces(selectedStateSpaces, randomIds);
+
+    std::map<uint32_t, PowerEntityStateSpace> stateSpaceMap;
+    for (auto stateSpace : stateSpaces) {
+        stateSpaceMap[stateSpace.powerEntityId] = stateSpace;
+    }
+
+    for (auto stateSpace : selectedStateSpaces) {
+        auto it = stateSpaceMap.find(stateSpace.powerEntityId);
+        ASSERT_NE(it, stateSpaceMap.end());
+
+        ASSERT_EQ(stateSpace.states.size(), it->second.states.size());
+        for (auto i = 0; i < stateSpace.states.size(); i++) {
+            ASSERT_EQ(stateSpace.states[i].powerEntityStateId,
+                      it->second.states[i].powerEntityStateId);
+            ASSERT_EQ(stateSpace.states[i].powerEntityStateName,
+                      it->second.states[i].powerEntityStateName);
+        }
+    }
+}
+
+// stateResidencyResults must contain results for every PowerEntityStateSpace
+// returned by getPowerEntityStateInfo
+TEST_F(PowerStatsHidlTest, ValidateResidencyResultsAssociation) {
+    hidl_vec<PowerEntityStateSpace> stateSpaces;
+    getStateSpaces(stateSpaces);
+
+    hidl_vec<PowerEntityStateResidencyResult> results;
+    getResidencyResults(results);
+
+    std::map<uint32_t, PowerEntityStateResidencyResult> resultsMap;
+    for (auto result : results) {
+        resultsMap[result.powerEntityId] = result;
+    }
+
+    for (auto stateSpace : stateSpaces) {
+        auto it = resultsMap.find(stateSpace.powerEntityId);
+        ASSERT_NE(it, resultsMap.end());
+
+        ASSERT_EQ(stateSpace.states.size(), it->second.stateResidencyData.size());
+
+        std::set<uint32_t> stateIds;
+        for (auto residency : it->second.stateResidencyData) {
+            stateIds.insert(residency.powerEntityStateId);
+        }
+
+        for (auto state : stateSpace.states) {
+            ASSERT_NE(stateIds.count(state.powerEntityStateId), 0);
+        }
+    }
+}
+
+// getPowerEntityStateResidencyData must support passing in requested IDs
+// stateResidencyResults must contain results for each PowerEntityStateSpace
+// returned by getPowerEntityStateInfo
+TEST_F(PowerStatsHidlTest, ValidateResidencyResultsAssociationSelect) {
+    std::vector<uint32_t> randomIds;
+    getRandomIds(randomIds);
+    if (randomIds.empty()) {
+        return;
+    }
+
+    hidl_vec<PowerEntityStateSpace> stateSpaces;
+    getStateSpaces(stateSpaces, randomIds);
+
+    hidl_vec<PowerEntityStateResidencyResult> results;
+    getResidencyResults(results, randomIds);
+
+    std::map<uint32_t, PowerEntityStateResidencyResult> resultsMap;
+    for (auto result : results) {
+        resultsMap[result.powerEntityId] = result;
+    }
+
+    for (auto stateSpace : stateSpaces) {
+        auto it = resultsMap.find(stateSpace.powerEntityId);
+        ASSERT_NE(it, resultsMap.end());
+
+        ASSERT_EQ(stateSpace.states.size(), it->second.stateResidencyData.size());
+
+        std::set<uint32_t> stateIds;
+        for (auto residency : it->second.stateResidencyData) {
+            stateIds.insert(residency.powerEntityStateId);
+        }
+
+        for (auto state : stateSpace.states) {
+            ASSERT_NE(stateIds.count(state.powerEntityStateId), 0);
+        }
+    }
+}
+
+TEST_F(PowerStatsHidlTest, ValidateRailInfo) {
+    hidl_vec<RailInfo> rails[2];
+    Status s;
+    auto cb = [&rails, &s](hidl_vec<RailInfo> rail_subsys, Status status) {
+        rails[0] = rail_subsys;
+        s = status;
+    };
+    Return<void> ret = service_->getRailInfo(cb);
+    EXPECT_TRUE(ret.isOk());
+    if (s == Status::SUCCESS) {
+        /* Rails size should be non-zero on SUCCESS*/
+        ASSERT_NE(rails[0].size(), 0);
+        /* check if indices returned are unique*/
+        set<uint32_t> ids;
+        for (auto rail : rails[0]) {
+            ASSERT_TRUE(ids.insert(rail.index).second);
+        }
+        auto cb = [&rails, &s](hidl_vec<RailInfo> rail_subsys, Status status) {
+            rails[1] = rail_subsys;
+            s = status;
+        };
+        Return<void> ret = service_->getRailInfo(cb);
+        EXPECT_TRUE(ret.isOk());
+        ASSERT_EQ(s, Status::SUCCESS);
+        ASSERT_EQ(rails[0].size(), rails[1].size());
+        /* check if data returned by two calls to getRailInfo is same*/
+        for (int i = 0; i < rails[0].size(); i++) {
+            ASSERT_NE(rails[0][i].railName, "");
+            ASSERT_NE(rails[0][i].subsysName, "");
+            int j = 0;
+            bool match = false;
+            for (j = 0; j < rails[1].size(); j++) {
+                if (rails[0][i].index == rails[1][j].index) {
+                    ASSERT_EQ(rails[0][i].railName, rails[1][i].railName);
+                    ASSERT_EQ(rails[0][i].subsysName, rails[1][i].subsysName);
+                    match = true;
+                    break;
+                }
+            }
+            ASSERT_TRUE(match);
+        }
+    } else if (s == Status::FILESYSTEM_ERROR) {
+        ALOGI("ValidateRailInfo returned FILESYSTEM_ERROR");
+        ASSERT_EQ(rails[0].size(), 0);
+    } else if (s == Status::NOT_SUPPORTED) {
+        ALOGI("ValidateRailInfo returned NOT_SUPPORTED");
+        ASSERT_EQ(rails[0].size(), 0);
+    } else if (s == Status::INVALID_INPUT) {
+        ALOGI("ValidateRailInfo returned INVALID_INPUT");
+        ASSERT_EQ(rails[0].size(), 0);
+    } else if (s == Status::INSUFFICIENT_RESOURCES) {
+        ALOGI("ValidateRailInfo returned INSUFFICIENT_RESOURCES");
+        ASSERT_EQ(rails[0].size(), 0);
+    }
+}
+
+TEST_F(PowerStatsHidlTest, ValidateAllPowerData) {
+    hidl_vec<EnergyData> measurements[2];
+    Status s;
+    auto cb = [&measurements, &s](hidl_vec<EnergyData> measure, Status status) {
+        measurements[0] = measure;
+        s = status;
+    };
+    Return<void> ret = service_->getEnergyData(hidl_vec<uint32_t>(), cb);
+    EXPECT_TRUE(ret.isOk());
+    if (s == Status::SUCCESS) {
+        /*measurements size should be non-zero on SUCCESS*/
+        ASSERT_NE(measurements[0].size(), 0);
+        auto cb = [&measurements, &s](hidl_vec<EnergyData> measure, Status status) {
+            measurements[1] = measure;
+            s = status;
+        };
+        Return<void> ret = service_->getEnergyData(hidl_vec<uint32_t>(), cb);
+        EXPECT_TRUE(ret.isOk());
+        ASSERT_EQ(s, Status::SUCCESS);
+        /*Both calls should returns same amount of data*/
+        ASSERT_EQ(measurements[0].size(), measurements[1].size());
+        /*Check is energy and timestamp are monotonically increasing*/
+        for (int i = 0; i < measurements[0].size(); i++) {
+            int j;
+            for (j = 0; j < measurements[1].size(); j++) {
+                if (measurements[0][i].index == measurements[1][j].index) {
+                    EXPECT_GE(measurements[1][j].timestamp, measurements[0][i].timestamp);
+                    EXPECT_GE(measurements[1][j].energy, measurements[0][i].energy);
+                    break;
+                }
+            }
+            /*Check is indices for two call match*/
+            ASSERT_NE(j, measurements[1].size());
+        }
+    } else if (s == Status::FILESYSTEM_ERROR) {
+        ALOGI("ValidateAllPowerData returned FILESYSTEM_ERROR");
+        ASSERT_EQ(measurements[0].size(), 0);
+    } else if (s == Status::NOT_SUPPORTED) {
+        ALOGI("ValidateAllPowerData returned NOT_SUPPORTED");
+        ASSERT_EQ(measurements[0].size(), 0);
+    } else if (s == Status::INVALID_INPUT) {
+        ALOGI("ValidateAllPowerData returned INVALID_INPUT");
+        ASSERT_EQ(measurements[0].size(), 0);
+    } else if (s == Status::INSUFFICIENT_RESOURCES) {
+        ALOGI("ValidateAllPowerData returned INSUFFICIENT_RESOURCES");
+        ASSERT_EQ(measurements[0].size(), 0);
+    }
+}
+
+TEST_F(PowerStatsHidlTest, ValidateFilteredPowerData) {
+    hidl_vec<RailInfo> rails;
+    hidl_vec<EnergyData> measurements;
+    hidl_vec<uint32_t> indices;
+    std::string debugString;
+    Status s;
+    auto cb = [&rails, &s](hidl_vec<RailInfo> rail_subsys, Status status) {
+        rails = rail_subsys;
+        s = status;
+    };
+    Return<void> ret = service_->getRailInfo(cb);
+    EXPECT_TRUE(ret.isOk());
+    std::time_t seed = std::time(nullptr);
+    std::srand(seed);
+    if (s == Status::SUCCESS) {
+        size_t sz = std::max(1, (int)(std::rand() % rails.size()));
+        indices.resize(sz);
+        for (int i = 0; i < sz; i++) {
+            int j = std::rand() % rails.size();
+            indices[i] = rails[j].index;
+            debugString += std::to_string(indices[i]) + ", ";
+        }
+        debugString += "\n";
+        ALOGI("ValidateFilteredPowerData for indices: %s", debugString.c_str());
+        auto cb = [&measurements, &s](hidl_vec<EnergyData> measure, Status status) {
+            measurements = measure;
+            s = status;
+        };
+        Return<void> ret = service_->getEnergyData(indices, cb);
+        EXPECT_TRUE(ret.isOk());
+        if (s == Status::SUCCESS) {
+            /* Make sure that all the measurements are returned */
+            ASSERT_EQ(sz, measurements.size());
+            for (int i = 0; i < measurements.size(); i++) {
+                int j;
+                bool match = false;
+                /* Check that the measurement belongs to the requested index */
+                for (j = 0; j < indices.size(); j++) {
+                    if (indices[j] == measurements[i].index) {
+                        match = true;
+                        break;
+                    }
+                }
+                ASSERT_TRUE(match);
+            }
+        }
+    } else {
+        /* size should be zero is stats is NOT SUCCESS */
+        ASSERT_EQ(rails.size(), 0);
+    }
+}
+
+void readEnergy(sp<IPowerStats> service_, uint32_t timeMs) {
+    std::unique_ptr<MessageQueueSync> mQueue;
+    Status s;
+    uint32_t railsInSample;
+    uint32_t totalSamples;
+    auto cb = [&s, &mQueue, &totalSamples, &railsInSample](
+                  const hardware::MQDescriptorSync<EnergyData>& in, uint32_t numSamples,
+                  uint32_t railsPerSample, Status status) {
+        mQueue.reset(new (std::nothrow) MessageQueueSync(in));
+        s = status;
+        totalSamples = numSamples;
+        railsInSample = railsPerSample;
+    };
+    service_->streamEnergyData(timeMs, 10, cb);
+    if (s == Status::SUCCESS) {
+        ASSERT_NE(nullptr, mQueue);
+        ASSERT_TRUE(mQueue->isValid());
+        bool rc;
+        int sampleCount = 0;
+        uint32_t totalQuants = railsInSample * totalSamples;
+        uint64_t timeout_ns = 10000000000;
+        if (totalSamples > 0) {
+            uint32_t batch = std::max(1, (int)((std::rand() % totalSamples) * railsInSample));
+            ALOGI("Read energy, timsMs: %u, batch: %u", timeMs, batch);
+            std::vector<EnergyData> data(batch);
+            while (sampleCount < totalQuants) {
+                rc = mQueue->readBlocking(&data[0], batch, timeout_ns);
+                if (rc == false) {
+                    break;
+                }
+                sampleCount = sampleCount + batch;
+                if (batch > totalQuants - sampleCount) {
+                    batch = 1;
+                }
+            }
+            ASSERT_EQ(totalQuants, sampleCount);
+        }
+    } else if (s == Status::FILESYSTEM_ERROR) {
+        ASSERT_FALSE(mQueue->isValid());
+        ASSERT_EQ(totalSamples, 0);
+        ASSERT_EQ(railsInSample, 0);
+    } else if (s == Status::NOT_SUPPORTED) {
+        ASSERT_FALSE(mQueue->isValid());
+        ASSERT_EQ(totalSamples, 0);
+        ASSERT_EQ(railsInSample, 0);
+    } else if (s == Status::INVALID_INPUT) {
+        ASSERT_FALSE(mQueue->isValid());
+        ASSERT_EQ(totalSamples, 0);
+        ASSERT_EQ(railsInSample, 0);
+    } else if (s == Status::INSUFFICIENT_RESOURCES) {
+        ASSERT_FALSE(mQueue->isValid());
+        ASSERT_EQ(totalSamples, 0);
+        ASSERT_EQ(railsInSample, 0);
+    }
+}
+
+TEST_F(PowerStatsHidlTest, StreamEnergyData) {
+    std::time_t seed = std::time(nullptr);
+    std::srand(seed);
+    std::thread thread1 = std::thread(readEnergy, service_, std::rand() % 5000);
+    thread1.join();
+}
+
+int main(int argc, char** argv) {
+    ::testing::AddGlobalTestEnvironment(PowerStatsHidlEnv::Instance());
+    ::testing::InitGoogleTest(&argc, argv);
+    PowerStatsHidlEnv::Instance()->init(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    LOG(INFO) << "Test result = " << status;
+    return status;
+}
+}  // namespace vts
+}  // namespace stats
+}  // namespace power
+}  // namespace android