Add PowerStats HAL 1.0 support
Bug: 173123066 Test: atest FrameworksServicesTests:PowerStatsServiceTest Test: Tested across a range of existing devices with varying degrees of PowerStats HAL 1.0/2.0 support and no support. Change-Id: I0e93d5e718ebc1f4e8fa9422d480a3717593ea32
This commit is contained in:
@@ -250,9 +250,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
|
||||
mHandler = new Handler(mHandlerThread.getLooper());
|
||||
|
||||
// TODO(b/173077356): Replace directly calling the HAL with PowerStatsService queries
|
||||
// Make sure to init Hal Wrapper before creating BatteryStatsImpl.
|
||||
mPowerStatsHALWrapper = new PowerStatsHALWrapper.PowerStatsHALWrapperImpl();
|
||||
mPowerStatsHALWrapper.initialize();
|
||||
mPowerStatsHALWrapper = PowerStatsHALWrapper.getPowerStatsHalImpl();
|
||||
|
||||
mStats = new BatteryStatsImpl(systemDir, handler, this,
|
||||
this, mUserManagerUserInfoProvider);
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package com.android.server.powerstats;
|
||||
|
||||
import android.hardware.power.stats.ChannelInfo;
|
||||
import android.hardware.power.stats.EnergyMeasurement;
|
||||
import android.hardware.power.stats.IPowerStats;
|
||||
import android.hardware.power.stats.PowerEntityInfo;
|
||||
import android.hardware.power.stats.StateResidencyResult;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
@@ -32,6 +36,7 @@ import java.util.function.Supplier;
|
||||
*/
|
||||
public final class PowerStatsHALWrapper {
|
||||
private static final String TAG = PowerStatsHALWrapper.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/**
|
||||
* IPowerStatsHALWrapper defines the interface to the PowerStatsHAL.
|
||||
@@ -122,17 +127,30 @@ public final class PowerStatsHALWrapper {
|
||||
*
|
||||
* @return true if connection to power stats HAL was correctly established.
|
||||
*/
|
||||
boolean initialize();
|
||||
boolean isInitialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* PowerStatsHALWrapperImpl is the implementation of the IPowerStatsHALWrapper
|
||||
* used by the PowerStatsService. Other implementations will be used by the testing
|
||||
* framework and will be passed into the PowerStatsService through an injector.
|
||||
* PowerStatsHALWrapper20Impl is the implementation of the IPowerStatsHALWrapper
|
||||
* used by the PowerStatsService on devices that support only PowerStats HAL 2.0.
|
||||
* Other implementations will be used by the testing framework and will be passed
|
||||
* into the PowerStatsService through an injector.
|
||||
*/
|
||||
public static final class PowerStatsHALWrapperImpl implements IPowerStatsHALWrapper {
|
||||
public static final class PowerStatsHAL20WrapperImpl implements IPowerStatsHALWrapper {
|
||||
private static Supplier<IPowerStats> sVintfPowerStats;
|
||||
|
||||
public PowerStatsHAL20WrapperImpl() {
|
||||
Supplier<IPowerStats> service = new VintfHalCache();
|
||||
sVintfPowerStats = null;
|
||||
|
||||
if (service.get() == null) {
|
||||
if (DEBUG) Slog.d(TAG, "PowerStats HAL 2.0 not available on this device.");
|
||||
sVintfPowerStats = null;
|
||||
} else {
|
||||
sVintfPowerStats = service;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.hardware.power.stats.PowerEntityInfo[] getPowerEntityInfo() {
|
||||
android.hardware.power.stats.PowerEntityInfo[] powerEntityInfoHAL = null;
|
||||
@@ -141,7 +159,7 @@ public final class PowerStatsHALWrapper {
|
||||
try {
|
||||
powerEntityInfoHAL = sVintfPowerStats.get().getPowerEntityInfo();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to get power entity info from PowerStats HAL");
|
||||
if (DEBUG) Slog.d(TAG, "Failed to get power entity info from PowerStats HAL");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +176,7 @@ public final class PowerStatsHALWrapper {
|
||||
stateResidencyResultHAL =
|
||||
sVintfPowerStats.get().getStateResidency(powerEntityIds);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to get state residency from PowerStats HAL");
|
||||
if (DEBUG) Slog.d(TAG, "Failed to get state residency from PowerStats HAL");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +191,9 @@ public final class PowerStatsHALWrapper {
|
||||
try {
|
||||
energyConsumerInfoHAL = sVintfPowerStats.get().getEnergyConsumerInfo();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to get energy consumer info from PowerStats HAL");
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Failed to get energy consumer info from PowerStats HAL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +210,9 @@ public final class PowerStatsHALWrapper {
|
||||
energyConsumedHAL =
|
||||
sVintfPowerStats.get().getEnergyConsumed(energyConsumerIds);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to get energy consumer results from PowerStats HAL");
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Failed to get energy consumer results from PowerStats HAL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +227,7 @@ public final class PowerStatsHALWrapper {
|
||||
try {
|
||||
energyMeterInfoHAL = sVintfPowerStats.get().getEnergyMeterInfo();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to get energy meter info from PowerStats HAL");
|
||||
if (DEBUG) Slog.d(TAG, "Failed to get energy meter info from PowerStats HAL");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +243,7 @@ public final class PowerStatsHALWrapper {
|
||||
energyMeasurementHAL =
|
||||
sVintfPowerStats.get().readEnergyMeters(channelIds);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to get energy measurements from PowerStats HAL");
|
||||
if (DEBUG) Slog.d(TAG, "Failed to get energy measurements from PowerStats HAL");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,17 +251,90 @@ public final class PowerStatsHALWrapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initialize() {
|
||||
Supplier<IPowerStats> service = new VintfHalCache();
|
||||
public boolean isInitialized() {
|
||||
return (sVintfPowerStats != null);
|
||||
}
|
||||
}
|
||||
|
||||
if (service.get() == null) {
|
||||
sVintfPowerStats = null;
|
||||
return false;
|
||||
/**
|
||||
* PowerStatsHALWrapper10Impl is the implementation of the IPowerStatsHALWrapper
|
||||
* used by the PowerStatsService on devices that support only PowerStats HAL 1.0.
|
||||
* Other implementations will be used by the testing framework and will be passed
|
||||
* into the PowerStatsService through an injector.
|
||||
*/
|
||||
public static final class PowerStatsHAL10WrapperImpl implements IPowerStatsHALWrapper {
|
||||
private boolean mIsInitialized;
|
||||
|
||||
// PowerStatsHAL 1.0 native functions exposed by JNI layer.
|
||||
private static native boolean nativeInit();
|
||||
private static native PowerEntityInfo[] nativeGetPowerEntityInfo();
|
||||
private static native StateResidencyResult[] nativeGetStateResidency(int[] powerEntityIds);
|
||||
private static native ChannelInfo[] nativeGetEnergyMeterInfo();
|
||||
private static native EnergyMeasurement[] nativeReadEnergyMeters(int[] channelIds);
|
||||
|
||||
public PowerStatsHAL10WrapperImpl() {
|
||||
if (nativeInit()) {
|
||||
mIsInitialized = true;
|
||||
} else {
|
||||
sVintfPowerStats = service;
|
||||
return true;
|
||||
if (DEBUG) Slog.d(TAG, "PowerStats HAL 1.0 not available on this device.");
|
||||
mIsInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.hardware.power.stats.PowerEntityInfo[] getPowerEntityInfo() {
|
||||
return nativeGetPowerEntityInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.hardware.power.stats.StateResidencyResult[] getStateResidency(
|
||||
int[] powerEntityIds) {
|
||||
return nativeGetStateResidency(powerEntityIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getEnergyConsumerInfo() {
|
||||
if (DEBUG) Slog.d(TAG, "Energy consumer info is not supported");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed(
|
||||
int[] energyConsumerIds) {
|
||||
if (DEBUG) Slog.d(TAG, "Energy consumer results are not supported");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.hardware.power.stats.ChannelInfo[] getEnergyMeterInfo() {
|
||||
return nativeGetEnergyMeterInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds) {
|
||||
return nativeReadEnergyMeters(channelIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return mIsInitialized;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of an IPowerStatsHALWrapper. If PowerStats HAL 2.0 is supported on the
|
||||
* device, return a PowerStatsHAL20WrapperImpl, else return a PowerStatsHAL10WrapperImpl.
|
||||
*
|
||||
* @return an instance of an IPowerStatsHALWrapper where preference is given to PowerStats HAL
|
||||
* 2.0.
|
||||
*/
|
||||
public static IPowerStatsHALWrapper getPowerStatsHalImpl() {
|
||||
PowerStatsHAL20WrapperImpl powerStatsHAL20WrapperImpl = new PowerStatsHAL20WrapperImpl();
|
||||
if (powerStatsHAL20WrapperImpl.isInitialized()) {
|
||||
return powerStatsHAL20WrapperImpl;
|
||||
} else {
|
||||
return new PowerStatsHAL10WrapperImpl();
|
||||
}
|
||||
}
|
||||
|
||||
private static class VintfHalCache implements Supplier<IPowerStats>, IBinder.DeathRecipient {
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
|
||||
import com.android.server.powerstats.PowerStatsHALWrapper.PowerStatsHALWrapperImpl;
|
||||
import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils;
|
||||
import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils;
|
||||
import com.android.server.powerstats.ProtoStreamUtils.PowerEntityInfoUtils;
|
||||
@@ -78,7 +77,7 @@ public class PowerStatsService extends SystemService {
|
||||
}
|
||||
|
||||
IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
|
||||
return new PowerStatsHALWrapperImpl();
|
||||
return PowerStatsHALWrapper.getPowerStatsHalImpl();
|
||||
}
|
||||
|
||||
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
|
||||
@@ -143,7 +142,7 @@ public class PowerStatsService extends SystemService {
|
||||
private void onSystemServiceReady() {
|
||||
mPowerStatsHALWrapper = mInjector.createPowerStatsHALWrapperImpl();
|
||||
|
||||
if (mPowerStatsHALWrapper.initialize()) {
|
||||
if (mPowerStatsHALWrapper.isInitialized()) {
|
||||
if (DEBUG) Slog.d(TAG, "Starting PowerStatsService");
|
||||
|
||||
// Only start logger and triggers if initialization is successful.
|
||||
|
||||
@@ -46,23 +46,31 @@ public class ProtoStreamUtils {
|
||||
|
||||
static class PowerEntityInfoUtils {
|
||||
public static void print(PowerEntityInfo[] powerEntityInfo) {
|
||||
if (powerEntityInfo == null) return;
|
||||
|
||||
for (int i = 0; i < powerEntityInfo.length; i++) {
|
||||
Slog.d(TAG, "PowerEntityId: " + powerEntityInfo[i].powerEntityId
|
||||
+ ", PowerEntityName: " + powerEntityInfo[i].powerEntityName);
|
||||
for (int j = 0; j < powerEntityInfo[i].states.length; j++) {
|
||||
Slog.d(TAG, " StateId: " + powerEntityInfo[i].states[j].stateId
|
||||
+ ", StateName: " + powerEntityInfo[i].states[j].stateName);
|
||||
if (powerEntityInfo[i].states != null) {
|
||||
for (int j = 0; j < powerEntityInfo[i].states.length; j++) {
|
||||
Slog.d(TAG, " StateId: " + powerEntityInfo[i].states[j].stateId
|
||||
+ ", StateName: " + powerEntityInfo[i].states[j].stateName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void dumpsys(PowerEntityInfo[] powerEntityInfo, PrintWriter pw) {
|
||||
if (powerEntityInfo == null) return;
|
||||
|
||||
for (int i = 0; i < powerEntityInfo.length; i++) {
|
||||
pw.println("PowerEntityId: " + powerEntityInfo[i].powerEntityId
|
||||
+ ", PowerEntityName: " + powerEntityInfo[i].powerEntityName);
|
||||
for (int j = 0; j < powerEntityInfo[i].states.length; j++) {
|
||||
pw.println(" StateId: " + powerEntityInfo[i].states[j].stateId
|
||||
+ ", StateName: " + powerEntityInfo[i].states[j].stateName);
|
||||
if (powerEntityInfo[i].states != null) {
|
||||
for (int j = 0; j < powerEntityInfo[i].states.length; j++) {
|
||||
pw.println(" StateId: " + powerEntityInfo[i].states[j].stateId
|
||||
+ ", StateName: " + powerEntityInfo[i].states[j].stateName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,6 +78,8 @@ public class ProtoStreamUtils {
|
||||
|
||||
static class StateResidencyResultUtils {
|
||||
public static void print(StateResidencyResult[] stateResidencyResult) {
|
||||
if (stateResidencyResult == null) return;
|
||||
|
||||
for (int i = 0; i < stateResidencyResult.length; i++) {
|
||||
Slog.d(TAG, "PowerEntityId: " + stateResidencyResult[i].powerEntityId);
|
||||
for (int j = 0; j < stateResidencyResult[i].stateResidencyData.length; j++) {
|
||||
@@ -90,6 +100,8 @@ public class ProtoStreamUtils {
|
||||
public static void packProtoMessage(ChannelInfo[] channelInfo, ProtoOutputStream pos) {
|
||||
long token;
|
||||
|
||||
if (channelInfo == null) return;
|
||||
|
||||
for (int i = 0; i < channelInfo.length; i++) {
|
||||
token = pos.start(PowerStatsServiceMeterProto.CHANNEL_INFO);
|
||||
pos.write(ChannelInfoProto.CHANNEL_ID, channelInfo[i].channelId);
|
||||
@@ -100,6 +112,8 @@ public class ProtoStreamUtils {
|
||||
}
|
||||
|
||||
public static void print(ChannelInfo[] channelInfo) {
|
||||
if (channelInfo == null) return;
|
||||
|
||||
for (int i = 0; i < channelInfo.length; i++) {
|
||||
Slog.d(TAG, "ChannelId: " + channelInfo[i].channelId
|
||||
+ ", ChannelName: " + channelInfo[i].channelName);
|
||||
@@ -107,6 +121,8 @@ public class ProtoStreamUtils {
|
||||
}
|
||||
|
||||
public static void dumpsys(ChannelInfo[] channelInfo, PrintWriter pw) {
|
||||
if (channelInfo == null) return;
|
||||
|
||||
for (int i = 0; i < channelInfo.length; i++) {
|
||||
pw.println("ChannelId: " + channelInfo[i].channelId
|
||||
+ ", ChannelName: " + channelInfo[i].channelName);
|
||||
@@ -125,6 +141,8 @@ public class ProtoStreamUtils {
|
||||
ProtoOutputStream pos) {
|
||||
long token;
|
||||
|
||||
if (energyMeasurement == null) return;
|
||||
|
||||
for (int i = 0; i < energyMeasurement.length; i++) {
|
||||
token = pos.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
|
||||
pos.write(EnergyMeasurementProto.CHANNEL_ID, energyMeasurement[i].channelId);
|
||||
@@ -200,6 +218,8 @@ public class ProtoStreamUtils {
|
||||
}
|
||||
|
||||
public static void print(EnergyMeasurement[] energyMeasurement) {
|
||||
if (energyMeasurement == null) return;
|
||||
|
||||
for (int i = 0; i < energyMeasurement.length; i++) {
|
||||
Slog.d(TAG, "ChannelId: " + energyMeasurement[i].channelId
|
||||
+ ", Timestamp (ms): " + energyMeasurement[i].timestampMs
|
||||
@@ -212,6 +232,8 @@ public class ProtoStreamUtils {
|
||||
public static void packProtoMessage(int[] energyConsumerId, ProtoOutputStream pos) {
|
||||
long token;
|
||||
|
||||
if (energyConsumerId == null) return;
|
||||
|
||||
for (int i = 0; i < energyConsumerId.length; i++) {
|
||||
token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_ID);
|
||||
pos.write(EnergyConsumerIdProto.ENERGY_CONSUMER_ID, energyConsumerId[i]);
|
||||
@@ -220,12 +242,16 @@ public class ProtoStreamUtils {
|
||||
}
|
||||
|
||||
public static void print(int[] energyConsumerId) {
|
||||
if (energyConsumerId == null) return;
|
||||
|
||||
for (int i = 0; i < energyConsumerId.length; i++) {
|
||||
Slog.d(TAG, "EnergyConsumerId: " + energyConsumerId[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void dumpsys(int[] energyConsumerId, PrintWriter pw) {
|
||||
if (energyConsumerId == null) return;
|
||||
|
||||
for (int i = 0; i < energyConsumerId.length; i++) {
|
||||
pw.println("EnergyConsumerId: " + energyConsumerId[i]);
|
||||
}
|
||||
@@ -243,6 +269,8 @@ public class ProtoStreamUtils {
|
||||
ProtoOutputStream pos) {
|
||||
long token;
|
||||
|
||||
if (energyConsumerResult == null) return;
|
||||
|
||||
for (int i = 0; i < energyConsumerResult.length; i++) {
|
||||
token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
|
||||
pos.write(EnergyConsumerResultProto.ENERGY_CONSUMER_ID,
|
||||
@@ -321,6 +349,8 @@ public class ProtoStreamUtils {
|
||||
}
|
||||
|
||||
public static void print(EnergyConsumerResult[] energyConsumerResult) {
|
||||
if (energyConsumerResult == null) return;
|
||||
|
||||
for (int i = 0; i < energyConsumerResult.length; i++) {
|
||||
Slog.d(TAG, "EnergyConsumerId: " + energyConsumerResult[i].energyConsumerId
|
||||
+ ", Timestamp (ms): " + energyConsumerResult[i].timestampMs
|
||||
|
||||
@@ -37,6 +37,7 @@ cc_library_static {
|
||||
"com_android_server_locksettings_SyntheticPasswordManager.cpp",
|
||||
"com_android_server_net_NetworkStatsService.cpp",
|
||||
"com_android_server_power_PowerManagerService.cpp",
|
||||
"com_android_server_powerstats_PowerStatsService.cpp",
|
||||
"com_android_server_security_VerityUtils.cpp",
|
||||
"com_android_server_SerialService.cpp",
|
||||
"com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
|
||||
|
||||
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 "PowerStatsService"
|
||||
|
||||
#include <android/hardware/power/stats/1.0/IPowerStats.h>
|
||||
#include <jni.h>
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
using android::hardware::hidl_vec;
|
||||
using android::hardware::Return;
|
||||
using android::hardware::power::stats::V1_0::EnergyData;
|
||||
using android::hardware::power::stats::V1_0::RailInfo;
|
||||
using android::hardware::power::stats::V1_0::Status;
|
||||
|
||||
// ChannelInfo
|
||||
static jclass class_CI;
|
||||
static jmethodID method_CI_init;
|
||||
static jfieldID field_CI_channelId;
|
||||
static jfieldID field_CI_channelName;
|
||||
|
||||
// EnergyMeasurement
|
||||
static jclass class_EM;
|
||||
static jmethodID method_EM_init;
|
||||
static jfieldID field_EM_channelId;
|
||||
static jfieldID field_EM_timestampMs;
|
||||
static jfieldID field_EM_durationMs;
|
||||
static jfieldID field_EM_energyUWs;
|
||||
|
||||
// StateInfo
|
||||
static jclass class_SI;
|
||||
static jmethodID method_SI_init;
|
||||
static jfieldID field_SI_stateId;
|
||||
static jfieldID field_SI_stateName;
|
||||
|
||||
// PowerEntityInfo
|
||||
static jclass class_PEI;
|
||||
static jmethodID method_PEI_init;
|
||||
static jfieldID field_PEI_powerEntityId;
|
||||
static jfieldID field_PEI_powerEntityName;
|
||||
static jfieldID field_PEI_states;
|
||||
|
||||
// StateResidency
|
||||
static jclass class_SR;
|
||||
static jmethodID method_SR_init;
|
||||
static jfieldID field_SR_stateId;
|
||||
static jfieldID field_SR_totalTimeInStateMs;
|
||||
static jfieldID field_SR_totalStateEntryCount;
|
||||
static jfieldID field_SR_lastEntryTimestampMs;
|
||||
|
||||
// StateResidencyResult
|
||||
static jclass class_SRR;
|
||||
static jmethodID method_SRR_init;
|
||||
static jfieldID field_SRR_powerEntityId;
|
||||
static jfieldID field_SRR_stateResidencyData;
|
||||
|
||||
namespace android {
|
||||
|
||||
static std::mutex gPowerStatsHalMutex;
|
||||
static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0_ptr = nullptr;
|
||||
|
||||
static void deinitPowerStats() {
|
||||
gPowerStatsHalV1_0_ptr = nullptr;
|
||||
}
|
||||
|
||||
struct PowerStatsHalDeathRecipient : virtual public hardware::hidl_death_recipient {
|
||||
virtual void serviceDied(uint64_t cookie,
|
||||
const wp<android::hidl::base::V1_0::IBase> &who) override {
|
||||
// The HAL just died. Reset all handles to HAL services.
|
||||
std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
|
||||
deinitPowerStats();
|
||||
}
|
||||
};
|
||||
|
||||
sp<PowerStatsHalDeathRecipient> gPowerStatsHalDeathRecipient = new PowerStatsHalDeathRecipient();
|
||||
|
||||
static bool connectToPowerStatsHal() {
|
||||
if (gPowerStatsHalV1_0_ptr == nullptr) {
|
||||
gPowerStatsHalV1_0_ptr = android::hardware::power::stats::V1_0::IPowerStats::getService();
|
||||
|
||||
if (gPowerStatsHalV1_0_ptr == nullptr) {
|
||||
ALOGE("Unable to get power.stats HAL service.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Link death recipient to power.stats service handle
|
||||
hardware::Return<bool> linked =
|
||||
gPowerStatsHalV1_0_ptr->linkToDeath(gPowerStatsHalDeathRecipient, 0);
|
||||
if (!linked.isOk()) {
|
||||
ALOGE("Transaction error in linking to power.stats HAL death: %s",
|
||||
linked.description().c_str());
|
||||
deinitPowerStats();
|
||||
return false;
|
||||
} else if (!linked) {
|
||||
ALOGW("Unable to link to power.stats HAL death notifications");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool checkResult(const Return<void> &ret, const char *function) {
|
||||
if (!ret.isOk()) {
|
||||
ALOGE("%s failed: requested HAL service not available. Description: %s", function,
|
||||
ret.description().c_str());
|
||||
if (ret.isDeadObject()) {
|
||||
deinitPowerStats();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static jobjectArray nativeGetPowerEntityInfo(JNIEnv *env, jclass clazz) {
|
||||
std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
|
||||
|
||||
if (!connectToPowerStatsHal()) {
|
||||
ALOGE("nativeGetPowerEntityInfo failed to connect to power.stats HAL");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jobjectArray powerEntityInfoArray = nullptr;
|
||||
Return<void> ret = gPowerStatsHalV1_0_ptr->getPowerEntityInfo(
|
||||
[&env, &powerEntityInfoArray](auto infos, auto status) {
|
||||
if (status != Status::SUCCESS) {
|
||||
ALOGE("Error getting power entity info");
|
||||
} else {
|
||||
powerEntityInfoArray = env->NewObjectArray(infos.size(), class_PEI, nullptr);
|
||||
for (int i = 0; i < infos.size(); i++) {
|
||||
jstring powerEntityName =
|
||||
env->NewStringUTF(infos[i].powerEntityName.c_str());
|
||||
jobject powerEntityInfo = env->NewObject(class_PEI, method_PEI_init);
|
||||
env->SetIntField(powerEntityInfo, field_PEI_powerEntityId,
|
||||
infos[i].powerEntityId);
|
||||
env->SetObjectField(powerEntityInfo, field_PEI_powerEntityName,
|
||||
powerEntityName);
|
||||
env->SetObjectArrayElement(powerEntityInfoArray, i, powerEntityInfo);
|
||||
env->DeleteLocalRef(powerEntityName);
|
||||
env->DeleteLocalRef(powerEntityInfo);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!checkResult(ret, __func__)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret = gPowerStatsHalV1_0_ptr->getPowerEntityStateInfo(
|
||||
{}, [&env, &powerEntityInfoArray](auto infos, auto status) {
|
||||
if (status != Status::SUCCESS) {
|
||||
ALOGE("Error getting power entity state info");
|
||||
} else {
|
||||
for (int i = 0; i < infos.size(); i++) {
|
||||
jobjectArray stateInfoArray =
|
||||
env->NewObjectArray(infos[i].states.size(), class_SI, nullptr);
|
||||
for (int j = 0; j < infos[i].states.size(); j++) {
|
||||
jstring powerEntityStateName = env->NewStringUTF(
|
||||
infos[i].states[j].powerEntityStateName.c_str());
|
||||
jobject stateInfo = env->NewObject(class_SI, method_SI_init);
|
||||
env->SetIntField(stateInfo, field_SI_stateId,
|
||||
infos[i].states[j].powerEntityStateId);
|
||||
env->SetObjectField(stateInfo, field_SI_stateName,
|
||||
powerEntityStateName);
|
||||
env->SetObjectArrayElement(stateInfoArray, j, stateInfo);
|
||||
env->DeleteLocalRef(powerEntityStateName);
|
||||
env->DeleteLocalRef(stateInfo);
|
||||
}
|
||||
|
||||
for (int j = 0; j < env->GetArrayLength(powerEntityInfoArray); j++) {
|
||||
jobject powerEntityInfo =
|
||||
env->GetObjectArrayElement(powerEntityInfoArray, j);
|
||||
if (env->GetIntField(powerEntityInfo, field_PEI_powerEntityId) ==
|
||||
infos[i].powerEntityId) {
|
||||
env->SetObjectField(powerEntityInfo, field_PEI_states,
|
||||
stateInfoArray);
|
||||
env->SetObjectArrayElement(powerEntityInfoArray, j,
|
||||
powerEntityInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!checkResult(ret, __func__)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return powerEntityInfoArray;
|
||||
}
|
||||
|
||||
static jobjectArray nativeGetStateResidency(JNIEnv *env, jclass clazz, jintArray powerEntityIds) {
|
||||
std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
|
||||
|
||||
if (!connectToPowerStatsHal()) {
|
||||
ALOGE("nativeGetStateResidency failed to connect to power.stats HAL");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t powerEntityIdCount = env->GetArrayLength(powerEntityIds);
|
||||
hidl_vec<uint32_t> powerEntityIdVector(powerEntityIdCount);
|
||||
|
||||
jint *powerEntityIdElements = env->GetIntArrayElements(powerEntityIds, 0);
|
||||
for (int i = 0; i < powerEntityIdCount; i++) {
|
||||
powerEntityIdVector[i] = powerEntityIdElements[i];
|
||||
}
|
||||
env->ReleaseIntArrayElements(powerEntityIds, powerEntityIdElements, 0);
|
||||
|
||||
jobjectArray stateResidencyResultArray = nullptr;
|
||||
Return<void> ret = gPowerStatsHalV1_0_ptr->getPowerEntityStateResidencyData(
|
||||
powerEntityIdVector, [&env, &stateResidencyResultArray](auto results, auto status) {
|
||||
if (status != Status::SUCCESS) {
|
||||
ALOGE("Error getting power entity state residency data");
|
||||
} else {
|
||||
stateResidencyResultArray =
|
||||
env->NewObjectArray(results.size(), class_SRR, nullptr);
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
jobjectArray stateResidencyArray =
|
||||
env->NewObjectArray(results[i].stateResidencyData.size(), class_SR,
|
||||
nullptr);
|
||||
for (int j = 0; j < results[i].stateResidencyData.size(); j++) {
|
||||
jobject stateResidency = env->NewObject(class_SR, method_SR_init);
|
||||
env->SetIntField(stateResidency, field_SR_stateId,
|
||||
results[i].stateResidencyData[j].powerEntityStateId);
|
||||
env->SetLongField(stateResidency, field_SR_totalTimeInStateMs,
|
||||
results[i].stateResidencyData[j].totalTimeInStateMs);
|
||||
env->SetLongField(stateResidency, field_SR_totalStateEntryCount,
|
||||
results[i]
|
||||
.stateResidencyData[j]
|
||||
.totalStateEntryCount);
|
||||
env->SetLongField(stateResidency, field_SR_lastEntryTimestampMs,
|
||||
results[i]
|
||||
.stateResidencyData[j]
|
||||
.lastEntryTimestampMs);
|
||||
env->SetObjectArrayElement(stateResidencyArray, j, stateResidency);
|
||||
env->DeleteLocalRef(stateResidency);
|
||||
}
|
||||
jobject stateResidencyResult = env->NewObject(class_SRR, method_SRR_init);
|
||||
env->SetIntField(stateResidencyResult, field_SRR_powerEntityId,
|
||||
results[i].powerEntityId);
|
||||
env->SetObjectField(stateResidencyResult, field_SRR_stateResidencyData,
|
||||
stateResidencyArray);
|
||||
env->SetObjectArrayElement(stateResidencyResultArray, i,
|
||||
stateResidencyResult);
|
||||
env->DeleteLocalRef(stateResidencyResult);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!checkResult(ret, __func__)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return stateResidencyResultArray;
|
||||
}
|
||||
|
||||
static jobjectArray nativeGetEnergyMeterInfo(JNIEnv *env, jclass clazz) {
|
||||
std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
|
||||
|
||||
if (!connectToPowerStatsHal()) {
|
||||
ALOGE("nativeGetEnergyMeterInfo failed to connect to power.stats HAL");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jobjectArray channelInfoArray = nullptr;
|
||||
Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo(
|
||||
[&env, &channelInfoArray](auto railInfo, auto status) {
|
||||
if (status != Status::SUCCESS) {
|
||||
ALOGW("Error getting rail info");
|
||||
} else {
|
||||
channelInfoArray = env->NewObjectArray(railInfo.size(), class_CI, nullptr);
|
||||
for (int i = 0; i < railInfo.size(); i++) {
|
||||
jstring channelName = env->NewStringUTF(railInfo[i].railName.c_str());
|
||||
jobject channelInfo = env->NewObject(class_CI, method_CI_init);
|
||||
env->SetIntField(channelInfo, field_CI_channelId, railInfo[i].index);
|
||||
env->SetObjectField(channelInfo, field_CI_channelName, channelName);
|
||||
env->SetObjectArrayElement(channelInfoArray, i, channelInfo);
|
||||
env->DeleteLocalRef(channelName);
|
||||
env->DeleteLocalRef(channelInfo);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!checkResult(ret, __func__)) {
|
||||
ALOGE("getRailInfo failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return channelInfoArray;
|
||||
}
|
||||
|
||||
static jobjectArray nativeReadEnergyMeters(JNIEnv *env, jclass clazz, jintArray channelIds) {
|
||||
std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
|
||||
|
||||
if (!connectToPowerStatsHal()) {
|
||||
ALOGE("nativeGetEnergy failed to connect to power.stats HAL");
|
||||
}
|
||||
|
||||
size_t channelIdCount = env->GetArrayLength(channelIds);
|
||||
hidl_vec<uint32_t> channelIdVector(channelIdCount);
|
||||
|
||||
jint *channelIdElements = env->GetIntArrayElements(channelIds, 0);
|
||||
for (int i = 0; i < channelIdCount; i++) {
|
||||
channelIdVector[i] = channelIdElements[i];
|
||||
}
|
||||
env->ReleaseIntArrayElements(channelIds, channelIdElements, 0);
|
||||
|
||||
jobjectArray energyMeasurementArray = nullptr;
|
||||
Return<void> ret =
|
||||
gPowerStatsHalV1_0_ptr
|
||||
->getEnergyData(channelIdVector,
|
||||
[&env, &energyMeasurementArray](auto energyData, auto status) {
|
||||
if (status != Status::SUCCESS) {
|
||||
ALOGW("Error getting energy data");
|
||||
} else {
|
||||
energyMeasurementArray =
|
||||
env->NewObjectArray(energyData.size(), class_EM,
|
||||
nullptr);
|
||||
for (int i = 0; i < energyData.size(); i++) {
|
||||
jobject energyMeasurement =
|
||||
env->NewObject(class_EM, method_EM_init);
|
||||
env->SetIntField(energyMeasurement,
|
||||
field_EM_channelId,
|
||||
energyData[i].index);
|
||||
env->SetLongField(energyMeasurement,
|
||||
field_EM_timestampMs,
|
||||
energyData[i].timestamp);
|
||||
env->SetLongField(energyMeasurement,
|
||||
field_EM_durationMs, -1);
|
||||
env->SetLongField(energyMeasurement,
|
||||
field_EM_energyUWs,
|
||||
energyData[i].energy);
|
||||
env->SetObjectArrayElement(energyMeasurementArray,
|
||||
i, energyMeasurement);
|
||||
env->DeleteLocalRef(energyMeasurement);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!checkResult(ret, __func__)) {
|
||||
ALOGE("getEnergyData failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return energyMeasurementArray;
|
||||
}
|
||||
|
||||
static jboolean nativeInit(JNIEnv *env, jclass clazz) {
|
||||
std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
|
||||
|
||||
// ChannelInfo
|
||||
jclass temp = env->FindClass("android/hardware/power/stats/ChannelInfo");
|
||||
class_CI = (jclass)env->NewGlobalRef(temp);
|
||||
method_CI_init = env->GetMethodID(class_CI, "<init>", "()V");
|
||||
field_CI_channelId = env->GetFieldID(class_CI, "channelId", "I");
|
||||
field_CI_channelName = env->GetFieldID(class_CI, "channelName", "Ljava/lang/String;");
|
||||
|
||||
// EnergyMeasurement
|
||||
temp = env->FindClass("android/hardware/power/stats/EnergyMeasurement");
|
||||
class_EM = (jclass)env->NewGlobalRef(temp);
|
||||
method_EM_init = env->GetMethodID(class_EM, "<init>", "()V");
|
||||
field_EM_channelId = env->GetFieldID(class_EM, "channelId", "I");
|
||||
field_EM_timestampMs = env->GetFieldID(class_EM, "timestampMs", "J");
|
||||
field_EM_durationMs = env->GetFieldID(class_EM, "durationMs", "J");
|
||||
field_EM_energyUWs = env->GetFieldID(class_EM, "energyUWs", "J");
|
||||
|
||||
// StateInfo
|
||||
temp = env->FindClass("android/hardware/power/stats/StateInfo");
|
||||
class_SI = (jclass)env->NewGlobalRef(temp);
|
||||
method_SI_init = env->GetMethodID(class_SI, "<init>", "()V");
|
||||
field_SI_stateId = env->GetFieldID(class_SI, "stateId", "I");
|
||||
field_SI_stateName = env->GetFieldID(class_SI, "stateName", "Ljava/lang/String;");
|
||||
|
||||
// PowerEntityInfo
|
||||
temp = env->FindClass("android/hardware/power/stats/PowerEntityInfo");
|
||||
class_PEI = (jclass)env->NewGlobalRef(temp);
|
||||
method_PEI_init = env->GetMethodID(class_PEI, "<init>", "()V");
|
||||
field_PEI_powerEntityId = env->GetFieldID(class_PEI, "powerEntityId", "I");
|
||||
field_PEI_powerEntityName = env->GetFieldID(class_PEI, "powerEntityName", "Ljava/lang/String;");
|
||||
field_PEI_states =
|
||||
env->GetFieldID(class_PEI, "states", "[Landroid/hardware/power/stats/StateInfo;");
|
||||
|
||||
// StateResidency
|
||||
temp = env->FindClass("android/hardware/power/stats/StateResidency");
|
||||
class_SR = (jclass)env->NewGlobalRef(temp);
|
||||
method_SR_init = env->GetMethodID(class_SR, "<init>", "()V");
|
||||
field_SR_stateId = env->GetFieldID(class_SR, "stateId", "I");
|
||||
field_SR_totalTimeInStateMs = env->GetFieldID(class_SR, "totalTimeInStateMs", "J");
|
||||
field_SR_totalStateEntryCount = env->GetFieldID(class_SR, "totalStateEntryCount", "J");
|
||||
field_SR_lastEntryTimestampMs = env->GetFieldID(class_SR, "lastEntryTimestampMs", "J");
|
||||
|
||||
// StateResidencyResult
|
||||
temp = env->FindClass("android/hardware/power/stats/StateResidencyResult");
|
||||
class_SRR = (jclass)env->NewGlobalRef(temp);
|
||||
method_SRR_init = env->GetMethodID(class_SRR, "<init>", "()V");
|
||||
field_SRR_powerEntityId = env->GetFieldID(class_SRR, "powerEntityId", "I");
|
||||
field_SRR_stateResidencyData =
|
||||
env->GetFieldID(class_SRR, "stateResidencyData",
|
||||
"[Landroid/hardware/power/stats/StateResidency;");
|
||||
|
||||
if (!connectToPowerStatsHal()) {
|
||||
ALOGE("nativeInit failed to connect to power.stats HAL");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const JNINativeMethod method_table[] = {
|
||||
{"nativeInit", "()Z", (void *)nativeInit},
|
||||
{"nativeGetPowerEntityInfo", "()[Landroid/hardware/power/stats/PowerEntityInfo;",
|
||||
(void *)nativeGetPowerEntityInfo},
|
||||
{"nativeGetStateResidency", "([I)[Landroid/hardware/power/stats/StateResidencyResult;",
|
||||
(void *)nativeGetStateResidency},
|
||||
{"nativeGetEnergyMeterInfo", "()[Landroid/hardware/power/stats/ChannelInfo;",
|
||||
(void *)nativeGetEnergyMeterInfo},
|
||||
{"nativeReadEnergyMeters", "([I)[Landroid/hardware/power/stats/EnergyMeasurement;",
|
||||
(void *)nativeReadEnergyMeters},
|
||||
};
|
||||
|
||||
int register_android_server_PowerStatsService(JNIEnv *env) {
|
||||
return jniRegisterNativeMethods(env,
|
||||
"com/android/server/powerstats/"
|
||||
"PowerStatsHALWrapper$PowerStatsHAL10WrapperImpl",
|
||||
method_table, NELEM(method_table));
|
||||
}
|
||||
|
||||
}; // namespace android
|
||||
@@ -29,6 +29,7 @@ int register_android_server_ConsumerIrService(JNIEnv *env);
|
||||
int register_android_server_InputManager(JNIEnv* env);
|
||||
int register_android_server_LightsService(JNIEnv* env);
|
||||
int register_android_server_PowerManagerService(JNIEnv* env);
|
||||
int register_android_server_PowerStatsService(JNIEnv* env);
|
||||
int register_android_server_storage_AppFuse(JNIEnv* env);
|
||||
int register_android_server_SerialService(JNIEnv* env);
|
||||
int register_android_server_SystemServer(JNIEnv* env);
|
||||
@@ -82,6 +83,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
|
||||
register_android_server_broadcastradio_BroadcastRadioService(env);
|
||||
register_android_server_broadcastradio_Tuner(vm, env);
|
||||
register_android_server_PowerManagerService(env);
|
||||
register_android_server_PowerStatsService(env);
|
||||
register_android_server_SerialService(env);
|
||||
register_android_server_InputManager(env);
|
||||
register_android_server_LightsService(env);
|
||||
|
||||
@@ -209,7 +209,7 @@ public class PowerStatsServiceTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initialize() {
|
||||
public boolean isInitialized() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user