mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 16:50:18 +00:00
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:
@@ -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>
|
||||
|
||||
29
power/stats/1.0/Android.bp
Normal file
29
power/stats/1.0/Android.bp
Normal 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,
|
||||
}
|
||||
|
||||
160
power/stats/1.0/IPowerStats.hal
Normal file
160
power/stats/1.0/IPowerStats.hal
Normal 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);
|
||||
};
|
||||
34
power/stats/1.0/default/Android.bp
Normal file
34
power/stats/1.0/default/Android.bp
Normal 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,
|
||||
}
|
||||
317
power/stats/1.0/default/PowerStats.cpp
Normal file
317
power/stats/1.0/default/PowerStats.cpp
Normal 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
|
||||
93
power/stats/1.0/default/PowerStats.h
Normal file
93
power/stats/1.0/default/PowerStats.h
Normal 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
|
||||
@@ -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
|
||||
59
power/stats/1.0/default/service.cpp
Normal file
59
power/stats/1.0/default/service.cpp
Normal 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
128
power/stats/1.0/types.hal
Normal 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;
|
||||
};
|
||||
38
power/stats/1.0/vts/functional/Android.bp
Normal file
38
power/stats/1.0/vts/functional/Android.bp
Normal 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",
|
||||
],
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user