Merge changes from topic "power.stats HAL"

* changes:
  power/stats: Add VTS test cases for power.stats HAL
  power/stats: Add VTS testcases
  power/stats: Add default implementation
  power/stats: Update HIDL to include power stats API
  power/stats: Update HIDL to use synchronous FMQ
  power.stats: Add power.stats HIDL interface
This commit is contained in:
Benjamin Schwartz
2019-01-08 17:58:59 +00:00
committed by Gerrit Code Review
11 changed files with 1450 additions and 0 deletions

View File

@@ -304,6 +304,14 @@
<instance>default</instance>
</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>

View File

@@ -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,
}

View File

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

View File

@@ -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,
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

128
power/stats/1.0/types.hal Normal file
View File

@@ -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;
};

View File

@@ -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",
],
}

View File

@@ -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