summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java4
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java131
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsService.java5
-rw-r--r--services/core/java/com/android/server/powerstats/ProtoStreamUtils.java42
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/OWNERS1
-rw-r--r--services/core/jni/com_android_server_powerstats_PowerStatsService.cpp440
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java2
9 files changed, 597 insertions, 31 deletions
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 083e970cc434..2f7c5234d982 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -254,9 +254,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();
final MeasuredEnergyArray initialEnergies = getEnergyConsumptionData();
final boolean[] supportedBuckets = getSupportedEnergyBuckets(initialEnergies);
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
index 88e5f69e02c4..79c039291f57 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
@@ -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 {
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 1150d4bbe770..ce50e5833c45 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -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.
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
index 5a4256ac0264..5e23b86e0adb 100644
--- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -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
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 2c4eb1b92086..77ca4e9eddab 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -40,6 +40,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",
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 7fc55656fc80..995cfe9fc2de 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -23,6 +23,7 @@ per-file com_android_server_locksettings_* = file:/services/core/java/com/androi
per-file com_android_server_net_* = file:/services/core/java/com/android/server/net/OWNERS
per-file com_android_server_pm_* = file:/services/core/java/com/android/server/pm/OWNERS
per-file com_android_server_power_* = file:/services/core/java/com/android/server/power/OWNERS
+per-file com_android_server_powerstats_* = file:/services/core/java/com/android/server/powerstats/OWNERS
per-file com_android_server_se_* = file:/core/java/android/se/OWNERS
per-file com_android_server_security_* = file:/core/java/android/security/OWNERS
per-file com_android_server_tv_* = file:/media/java/android/media/tv/OWNERS
diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
new file mode 100644
index 000000000000..f5b851f7aea5
--- /dev/null
+++ b/services/core/jni/com_android_server_powerstats_PowerStatsService.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
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 5a0d08aeb6b3..85ef39470cb3 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -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);
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index b26d1efef2a8..08d4caacd777 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -209,7 +209,7 @@ public class PowerStatsServiceTest {
}
@Override
- public boolean initialize() {
+ public boolean isInitialized() {
return true;
}
}