blob: 2e3e9562d258382a48ea12bb9f278abd1ff226ac [file] [log] [blame]
/*
* Copyright (C) 2021 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 "src/android_internal/power_stats.h"
#include "perfetto/ext/base/utils.h"
#include <string.h>
#include <algorithm>
#include <memory>
#include <vector>
// Legacy HAL interfacte for devices shipped before Android S.
#include <android/hardware/power/stats/1.0/IPowerStats.h>
// AIDL interface for Android S+.
#include <android/hardware/power/stats/IPowerStats.h>
#include <binder/IServiceManager.h>
namespace perfetto {
namespace android_internal {
namespace hal = android::hardware::power::stats::V1_0;
namespace aidl = android::hardware::power::stats;
namespace {
// Common interface for data from power stats service. Devices prior to
// Android S, uses the HAL interface while device from Android S or later
// uses the AIDL interfact.
class PowerStatsDataProvider {
public:
virtual bool GetAvailableRails(RailDescriptor*, size_t* size_of_arr) = 0;
virtual bool GetRailEnergyData(RailEnergyData*, size_t* size_of_arr) = 0;
// Available from Android S+.
virtual bool GetEnergyConsumerInfo(EnergyConsumerInfo* consumers,
size_t* size_of_arr) = 0;
virtual bool GetEnergyConsumed(EnergyEstimationBreakdown* breakdown,
size_t* size_of_arr) = 0;
virtual bool GetPowerEntityStates(PowerEntityState* state,
size_t* size_of_arr) = 0;
virtual bool GetPowerEntityStateResidency(PowerEntityStateResidency* state,
size_t* size_of_arr) = 0;
virtual ~PowerStatsDataProvider() = default;
};
class PowerStatsHalDataProvider : public PowerStatsDataProvider {
public:
bool GetAvailableRails(RailDescriptor*, size_t* size_of_arr) override;
bool GetRailEnergyData(RailEnergyData*, size_t* size_of_arr) override;
bool GetEnergyConsumerInfo(EnergyConsumerInfo* consumers,
size_t* size_of_arr) override;
bool GetEnergyConsumed(EnergyEstimationBreakdown* breakdown,
size_t* size_of_arr) override;
bool GetPowerEntityStates(PowerEntityState* state,
size_t* size_of_arr) override;
bool GetPowerEntityStateResidency(PowerEntityStateResidency* state,
size_t* size_of_arr) override;
PowerStatsHalDataProvider() = default;
~PowerStatsHalDataProvider() override = default;
private:
android::sp<hal::IPowerStats> svc_;
hal::IPowerStats* MaybeGetService();
};
class PowerStatsAidlDataProvider : public PowerStatsDataProvider {
public:
static constexpr char INSTANCE[] =
"android.hardware.power.stats.IPowerStats/default";
bool GetAvailableRails(RailDescriptor*, size_t* size_of_arr) override;
bool GetRailEnergyData(RailEnergyData*, size_t* size_of_arr) override;
bool GetEnergyConsumerInfo(EnergyConsumerInfo* consumers,
size_t* size_of_arr) override;
bool GetEnergyConsumed(EnergyEstimationBreakdown* breakdown,
size_t* size_of_arr) override;
bool GetPowerEntityStates(PowerEntityState* state,
size_t* size_of_arr) override;
bool GetPowerEntityStateResidency(PowerEntityStateResidency* state,
size_t* size_of_arr) override;
PowerStatsAidlDataProvider() = default;
~PowerStatsAidlDataProvider() override = default;
private:
android::sp<aidl::IPowerStats> svc_;
aidl::IPowerStats* MaybeGetService();
void ResetService();
};
PowerStatsDataProvider* GetDataProvider() {
static std::unique_ptr<PowerStatsDataProvider> data_provider;
if (data_provider == nullptr) {
const android::sp<android::IServiceManager> sm =
android::defaultServiceManager();
if (sm->isDeclared(
android::String16(PowerStatsAidlDataProvider::INSTANCE))) {
data_provider = std::make_unique<PowerStatsAidlDataProvider>();
} else {
data_provider = std::make_unique<PowerStatsHalDataProvider>();
}
}
return data_provider.get();
}
} // anonymous namespace
bool GetAvailableRails(RailDescriptor* descriptor, size_t* size_of_arr) {
return GetDataProvider()->GetAvailableRails(descriptor, size_of_arr);
}
bool GetRailEnergyData(RailEnergyData* data, size_t* size_of_arr) {
return GetDataProvider()->GetRailEnergyData(data, size_of_arr);
}
bool GetEnergyConsumerInfo(EnergyConsumerInfo* consumers, size_t* size_of_arr) {
return GetDataProvider()->GetEnergyConsumerInfo(consumers, size_of_arr);
}
bool GetEnergyConsumed(EnergyEstimationBreakdown* breakdown,
size_t* size_of_arr) {
return GetDataProvider()->GetEnergyConsumed(breakdown, size_of_arr);
}
bool GetPowerEntityStates(PowerEntityState* state, size_t* size_of_arr) {
return GetDataProvider()->GetPowerEntityStates(state, size_of_arr);
}
bool GetPowerEntityStateResidency(PowerEntityStateResidency* residency,
size_t* size_of_arr) {
return GetDataProvider()->GetPowerEntityStateResidency(residency,
size_of_arr);
}
/*** Power Stats HAL Implemenation *******************************************/
using android::hardware::hidl_vec;
using android::hardware::Return;
hal::IPowerStats* PowerStatsHalDataProvider::MaybeGetService() {
if (svc_ == nullptr) {
svc_ = hal::IPowerStats::tryGetService();
}
return svc_.get();
}
bool PowerStatsHalDataProvider::GetAvailableRails(
RailDescriptor* rail_descriptors,
size_t* size_of_arr) {
const size_t in_array_size = *size_of_arr;
*size_of_arr = 0;
hal::IPowerStats* svc = MaybeGetService();
if (svc == nullptr) {
return false;
}
hal::Status status;
auto rails_cb = [rail_descriptors, size_of_arr, &in_array_size, &status](
hidl_vec<hal::RailInfo> r, hal::Status s) {
status = s;
if (status == hal::Status::SUCCESS) {
*size_of_arr = std::min(in_array_size, r.size());
for (int i = 0; i < *size_of_arr; ++i) {
const hal::RailInfo& rail_info = r[i];
RailDescriptor& descriptor = rail_descriptors[i];
descriptor.index = rail_info.index;
descriptor.sampling_rate = rail_info.samplingRate;
strlcpy(descriptor.rail_name, rail_info.railName.c_str(),
sizeof(descriptor.rail_name));
strlcpy(descriptor.subsys_name, rail_info.subsysName.c_str(),
sizeof(descriptor.subsys_name));
}
}
};
Return<void> ret = svc->getRailInfo(rails_cb);
return status == hal::Status::SUCCESS;
}
bool PowerStatsHalDataProvider::GetRailEnergyData(
RailEnergyData* rail_energy_array,
size_t* size_of_arr) {
const size_t in_array_size = *size_of_arr;
*size_of_arr = 0;
hal::IPowerStats* svc = MaybeGetService();
if (svc == nullptr) {
return false;
}
hal::Status status;
auto energy_cb = [rail_energy_array, size_of_arr, &in_array_size, &status](
hidl_vec<hal::EnergyData> m, hal::Status s) {
status = s;
if (status == hal::Status::SUCCESS) {
*size_of_arr = std::min(in_array_size, m.size());
for (int i = 0; i < *size_of_arr; ++i) {
const hal::EnergyData& measurement = m[i];
RailEnergyData& element = rail_energy_array[i];
element.index = measurement.index;
element.timestamp = measurement.timestamp;
element.energy = measurement.energy;
}
}
};
Return<void> ret = svc_->getEnergyData(hidl_vec<uint32_t>(), energy_cb);
return status == hal::Status::SUCCESS;
}
bool PowerStatsHalDataProvider::GetEnergyConsumerInfo(EnergyConsumerInfo*,
size_t*) {
return false;
}
bool PowerStatsHalDataProvider::GetEnergyConsumed(EnergyEstimationBreakdown*,
size_t*) {
return false;
}
bool PowerStatsHalDataProvider::GetPowerEntityStates(PowerEntityState*,
size_t*) {
return false;
}
bool PowerStatsHalDataProvider::GetPowerEntityStateResidency(
PowerEntityStateResidency*,
size_t*) {
return false;
}
/*** End of Power Stats HAL Implemenation *************************************/
/*** Power Stats AIDL Implemenation *******************************************/
aidl::IPowerStats* PowerStatsAidlDataProvider::MaybeGetService() {
if (svc_ == nullptr) {
svc_ = android::checkDeclaredService<aidl::IPowerStats>(
android::String16(INSTANCE));
}
return svc_.get();
}
void PowerStatsAidlDataProvider::ResetService() {
svc_.clear();
}
bool PowerStatsAidlDataProvider::GetAvailableRails(RailDescriptor* descriptor,
size_t* size_of_arr) {
const size_t in_array_size = *size_of_arr;
*size_of_arr = 0;
aidl::IPowerStats* svc = MaybeGetService();
if (svc_ == nullptr) {
return false;
}
std::vector<aidl::Channel> results;
android::binder::Status status = svc->getEnergyMeterInfo(&results);
if (!status.isOk()) {
if (status.transactionError() == android::DEAD_OBJECT) {
// Service has died. Reset it to attempt to acquire a new one next time.
ResetService();
}
return false;
}
size_t max_size = std::min(in_array_size, results.size());
for (const auto& result : results) {
if (*size_of_arr >= max_size) {
break;
}
auto& cur = descriptor[(*size_of_arr)++];
cur.index = result.id;
cur.sampling_rate = 0;
strlcpy(cur.rail_name, result.name.c_str(), sizeof(cur.rail_name));
strlcpy(cur.subsys_name, result.subsystem.c_str(), sizeof(cur.subsys_name));
}
return true;
}
bool PowerStatsAidlDataProvider::GetRailEnergyData(RailEnergyData* data,
size_t* size_of_arr) {
const size_t in_array_size = *size_of_arr;
*size_of_arr = 0;
aidl::IPowerStats* svc = MaybeGetService();
if (svc == nullptr) {
return false;
}
std::vector<int> ids;
std::vector<aidl::EnergyMeasurement> results;
android::binder::Status status = svc->readEnergyMeter(ids, &results);
if (!status.isOk()) {
if (status.transactionError() == android::DEAD_OBJECT) {
// Service has died. Reset it to attempt to acquire a new one next time.
ResetService();
}
return false;
}
size_t max_size = std::min(in_array_size, results.size());
for (const auto& result : results) {
if (*size_of_arr >= max_size) {
break;
}
auto& cur = data[(*size_of_arr)++];
cur.index = result.id;
cur.timestamp = result.timestampMs;
cur.energy = result.energyUWs;
}
return true;
}
bool PowerStatsAidlDataProvider::GetEnergyConsumerInfo(
EnergyConsumerInfo* consumers,
size_t* size_of_arr) {
const size_t in_array_size = *size_of_arr;
*size_of_arr = 0;
aidl::IPowerStats* svc = MaybeGetService();
if (svc == nullptr) {
return false;
}
std::vector<aidl::EnergyConsumer> results;
android::binder::Status status = svc->getEnergyConsumerInfo(&results);
if (!status.isOk()) {
if (status.transactionError() == android::DEAD_OBJECT) {
// Service has died. Reset it to attempt to acquire a new one next time.
ResetService();
}
return false;
}
size_t max_size = std::min(in_array_size, results.size());
for (const auto& result : results) {
if (*size_of_arr >= max_size) {
break;
}
auto& cur = consumers[(*size_of_arr)++];
cur.energy_consumer_id = result.id;
cur.ordinal = result.ordinal;
strlcpy(cur.type, aidl::toString(result.type).c_str(), sizeof(cur.type));
strlcpy(cur.name, result.name.c_str(), sizeof(cur.name));
}
return true;
}
bool PowerStatsAidlDataProvider::GetEnergyConsumed(
EnergyEstimationBreakdown* breakdown,
size_t* size_of_arr) {
const size_t in_array_size = *size_of_arr;
*size_of_arr = 0;
aidl::IPowerStats* svc = MaybeGetService();
if (svc == nullptr) {
return false;
}
std::vector<int> ids;
std::vector<aidl::EnergyConsumerResult> results;
android::binder::Status status = svc->getEnergyConsumed(ids, &results);
if (!status.isOk()) {
if (status.transactionError() == android::DEAD_OBJECT) {
// Service has died. Reset it to attempt to acquire a new one next time.
ResetService();
}
return false;
}
size_t max_size = std::min(in_array_size, results.size());
// Iterate through all consumer ID.
for (const auto& result : results) {
if (*size_of_arr >= max_size) {
break;
}
auto& cur = breakdown[(*size_of_arr)++];
cur.energy_consumer_id = result.id;
cur.uid = ALL_UIDS_FOR_CONSUMER;
cur.energy_uws = result.energyUWs;
// Iterate through all UIDs for this consumer.
for (const auto& attribution : result.attribution) {
if (*size_of_arr >= max_size) {
break;
}
auto& cur = breakdown[(*size_of_arr)++];
cur.energy_consumer_id = result.id;
cur.uid = attribution.uid;
cur.energy_uws = attribution.energyUWs;
}
}
return true;
}
bool PowerStatsAidlDataProvider::GetPowerEntityStates(
PowerEntityState* entity_state,
size_t* size_of_arr) {
const size_t in_array_size = *size_of_arr;
*size_of_arr = 0;
aidl::IPowerStats* svc = MaybeGetService();
if (svc == nullptr) {
return false;
}
std::vector<aidl::PowerEntity> entities;
android::binder::Status status = svc->getPowerEntityInfo(&entities);
if (!status.isOk()) {
if (status.transactionError() == android::DEAD_OBJECT) {
// Service has died. Reset it to attempt to acquire a new one next time.
ResetService();
}
return false;
}
// Iterate through all entities.
for (const auto& entity : entities) {
if (*size_of_arr >= in_array_size) {
break;
}
// Iterate through all states for this entity.
for (const auto& state : entity.states) {
if (*size_of_arr >= in_array_size) {
break;
}
auto& cur = entity_state[(*size_of_arr)++];
cur.entity_id = entity.id;
strlcpy(cur.entity_name, entity.name.c_str(), sizeof(cur.entity_name));
cur.state_id = state.id;
strlcpy(cur.state_name, state.name.c_str(), sizeof(cur.state_name));
}
}
return true;
}
bool PowerStatsAidlDataProvider::GetPowerEntityStateResidency(
PowerEntityStateResidency* residency,
size_t* size_of_arr) {
const size_t in_array_size = *size_of_arr;
*size_of_arr = 0;
aidl::IPowerStats* svc = MaybeGetService();
if (svc == nullptr) {
return false;
}
std::vector<int> ids;
std::vector<aidl::StateResidencyResult> entities;
android::binder::Status status = svc->getStateResidency(ids, &entities);
if (!status.isOk()) {
if (status.transactionError() == android::DEAD_OBJECT) {
// Service has died. Reset it to attempt to acquire a new one next time.
ResetService();
}
return false;
}
// Iterate through all entities.
for (const auto& entity : entities) {
if (*size_of_arr >= in_array_size) {
break;
}
// Iterate through all states for this entity.
for (const auto& stateResidencyData : entity.stateResidencyData) {
if (*size_of_arr >= in_array_size) {
break;
}
auto& cur = residency[(*size_of_arr)++];
cur.entity_id = entity.id;
cur.state_id = stateResidencyData.id;
cur.total_time_in_state_ms = stateResidencyData.totalTimeInStateMs;
cur.total_state_entry_count = stateResidencyData.totalStateEntryCount;
cur.last_entry_timestamp_ms = stateResidencyData.lastEntryTimestampMs;
}
}
return true;
}
/*** End of Power Stats AIDL Implemenation ************************************/
} // namespace android_internal
} // namespace perfetto