summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMat Bevilacqua <matbev@google.com>2020-09-21 18:59:06 -0700
committerMat Bevilacqua <matbev@google.com>2020-10-21 00:04:35 +0000
commita375ea0bf7e1ed7ff40673ebffdf2214ddca6f30 (patch)
treee7d0b35e95eeee886da0a44a97b7cead64e53f7a
parentd681a590c7de9138d314d6c595e0e7a8866ee00f (diff)
Migrate to PowerStats HAL 2.0
Bug: 169622556 Test: Tested and verified ODPM data is correctly captured in incident reports. Tests pass. Service gracefully fails on devices without PowerStats HAL 2.0 implementation. Change-Id: I521e0a8fd26122476601d8c8450f0508549eb311
-rw-r--r--core/proto/android/os/incident.proto9
-rw-r--r--core/proto/android/server/powerstatsservice.proto99
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsData.java286
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java26
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java180
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsLogger.java117
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsService.java36
-rw-r--r--services/core/java/com/android/server/powerstats/ProtoStreamUtils.java273
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_powerstats_PowerStatsService.cpp222
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java295
-rw-r--r--tools/powerstats/PowerStatsServiceProtoParser.java95
14 files changed, 915 insertions, 727 deletions
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 9fed1b95f6c3..fe65bda365af 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -511,9 +511,14 @@ message IncidentProto {
(section).args = "sensorservice --proto"
];
- optional com.android.server.powerstats.PowerStatsServiceProto powerstats = 3054 [
+ optional com.android.server.powerstats.PowerStatsServiceMeterProto powerstats_meter = 3054 [
(section).type = SECTION_DUMPSYS,
- (section).args = "power_stats --proto"
+ (section).args = "power_stats --proto meter"
+ ];
+
+ optional com.android.server.powerstats.PowerStatsServiceModelProto powerstats_model = 3055 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "power_stats --proto model"
];
// Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto
index c80524402335..9a7ed7cbe98f 100644
--- a/core/proto/android/server/powerstatsservice.proto
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -20,44 +20,99 @@ package com.android.server.powerstats;
option java_multiple_files = true;
-message IncidentReportProto {
+/**
+ * IncidentReportMeterProto is used only in the parsing tool located
+ * in frameworks/base/tools which is used to parse this data out of
+ * incident reports.
+ */
+message IncidentReportMeterProto {
+ /** Section number matches that in incident.proto */
+ optional PowerStatsServiceMeterProto incident_report = 3054;
+}
+
+/**
+ * IncidentReportModelProto is used only in the parsing tool located
+ * in frameworks/base/tools which is used to parse this data out of
+ * incident reports.
+ */
+message IncidentReportModelProto {
/** Section number matches that in incident.proto */
- optional PowerStatsServiceProto incident_report = 3054;
+ optional PowerStatsServiceModelProto incident_report = 3055;
}
-message PowerStatsServiceProto {
- repeated RailInfoProto rail_info = 1;
- repeated EnergyDataProto energy_data = 2;
+/**
+ * EnergyConsumer (model) data is exposed by the PowerStats HAL. This data
+ * represents modeled energy consumption estimates and is provided per
+ * subsystem. The default subsystems are defined in EnergyConsumerId.aidl.
+ * Energy model estimates will be logged to incident reports in addition to
+ * the raw energy meter data.
+ */
+message PowerStatsServiceModelProto {
+ repeated EnergyConsumerIdProto energy_consumer_id = 1;
+ repeated EnergyConsumerResultProto energy_consumer_result = 2;
}
/**
- * Rail information:
- * Reports information related to the rails being monitored.
+ * EnergyMeasurement (meter) data is exposed by the PowerStats HAL. This data
+ * represents measurements taken directly from on-device energy meters.
+ * This raw energy meter data will be logged to incident reports.
*/
-message RailInfoProto {
- /** Index corresponding to the rail */
- optional int32 index = 1;
+message PowerStatsServiceMeterProto {
+ repeated ChannelInfoProto channel_info = 1;
+ repeated EnergyMeasurementProto energy_measurement = 2;
+}
- /** Name of the rail (opaque to the framework) */
- optional string rail_name = 2;
+/**
+ * Energy consumer ID:
+ * A list of default subsystems for which energy consumption estimates
+ * may be provided (hardware dependent).
+ */
+message EnergyConsumerIdProto {
+ /** Unique index identifying the energy consumer. */
+ optional int32 energy_consumer_id = 1;
+}
- /** Name of the subsystem to which this rail belongs (opaque to the framework) */
- optional string subsys_name = 3;
+/**
+ * Energy consumer result:
+ * An estimate of energy consumption since boot for the subsystem identified
+ * by the unique energy_consumer_id.
+ */
+message EnergyConsumerResultProto {
+ /** Unique index identifying the energy consumer. */
+ optional int32 energy_consumer_id = 1;
+
+ /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
+ optional int64 timestamp_ms = 2;
+
+ /** Accumulated energy since device boot in microwatt-seconds (uWs) */
+ optional int64 energy_uws = 3;
+}
+
+/**
+ * Channel information:
+ * Reports information related to the energy meter channels being monitored.
+ */
+message ChannelInfoProto {
+ /**
+ * Index corresponding to the energy meter channel. This index matches
+ * the index returned in ChannelInfo.
+ */
+ optional int32 channel_id = 1;
- /** Hardware sampling rate */
- optional int32 sampling_rate = 4;
+ /** Name of the energy meter channel */
+ optional string channel_name = 2;
}
/**
- * Rail level energy measurements:
- * Reports accumulated energy since boot on each rail.
+ * Energy measurements:
+ * Reports accumulated energy since boot for each energy meter.
*/
-message EnergyDataProto {
+message EnergyMeasurementProto {
/**
- * Index corresponding to the rail. This index matches
- * the index returned in RailInfo
+ * Index corresponding to the energy meter channel. This index matches
+ * the index returned in ChannelInfo.
*/
- optional int32 index = 1;
+ optional int32 channel_id = 1;
/** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
optional int64 timestamp_ms = 2;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 1a7f0d1ce37e..320100072066 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -116,6 +116,7 @@ java_library_static {
"android.hardware.contexthub-V1.0-java",
"android.hardware.rebootescrow-java",
"android.hardware.soundtrigger-V2.3-java",
+ "android.hardware.power.stats-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
"dnsresolver_aidl_interface-java",
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsData.java b/services/core/java/com/android/server/powerstats/PowerStatsData.java
deleted file mode 100644
index 755bd5fce45e..000000000000
--- a/services/core/java/com/android/server/powerstats/PowerStatsData.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.powerstats;
-
-import android.util.Log;
-import android.util.proto.ProtoInputStream;
-import android.util.proto.ProtoOutputStream;
-import android.util.proto.ProtoUtils;
-import android.util.proto.WireTypeMismatchException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * PowerStatsData is a class that performs two operations:
- * 1) Unpacks serialized protobuf byte arrays, as defined in powerstatsservice.proto,
- * into RailInfo or EnergyData object arrays.
- *
- * 2) Packs RailInfo or EnergyData object arrays in protobuf byte arrays as
- * defined in powerstatsservice.proto.
- *
- * Inside frameworks, proto source is generated with the genstream option
- * and therefore the getter/setter helper functions are not available.
- * The protos need to be packed/unpacked in a more manual way using
- * ProtoOutputStream/ProtoInputStream.
- */
-public class PowerStatsData {
- private static final String TAG = PowerStatsData.class.getSimpleName();
-
- private List<Data> mDataList;
-
- public PowerStatsData(ProtoInputStream pis) throws IOException {
- mDataList = new ArrayList<Data>();
- unpackProto(pis);
- }
-
- public PowerStatsData(Data[] data) {
- mDataList = new ArrayList<Data>(Arrays.asList(data));
- }
-
- private void unpackProto(ProtoInputStream pis) throws IOException {
- long token;
-
- while (true) {
- try {
- switch (pis.nextField()) {
- case (int) PowerStatsServiceProto.RAIL_INFO:
- token = pis.start(PowerStatsServiceProto.RAIL_INFO);
- mDataList.add(new RailInfo(pis));
- pis.end(token);
- break;
-
- case (int) PowerStatsServiceProto.ENERGY_DATA:
- token = pis.start(PowerStatsServiceProto.ENERGY_DATA);
- mDataList.add(new EnergyData(pis));
- pis.end(token);
- break;
-
- case ProtoInputStream.NO_MORE_FIELDS:
- return;
-
- default:
- Log.e(TAG, "Unhandled field in proto: "
- + ProtoUtils.currentFieldToString(pis));
- break;
- }
- } catch (WireTypeMismatchException wtme) {
- Log.e(TAG, "Wire Type mismatch in proto: " + ProtoUtils.currentFieldToString(pis));
- }
- }
- }
-
- /**
- * Write this object to an output stream in protobuf format.
- *
- * @param pos ProtoOutputStream of file where data is to be written. Data is
- * written in protobuf format as defined by powerstatsservice.proto.
- */
- public void toProto(ProtoOutputStream pos) {
- long token;
-
- for (Data data : mDataList) {
- if (data instanceof RailInfo) {
- token = pos.start(PowerStatsServiceProto.RAIL_INFO);
- } else {
- token = pos.start(PowerStatsServiceProto.ENERGY_DATA);
- }
- data.toProto(pos);
- pos.end(token);
- }
- }
-
- /**
- * Convert mDataList to proto format and return the serialized byte array.
- *
- * @return byte array containing a serialized protobuf of mDataList.
- */
- public byte[] getProtoBytes() {
- ProtoOutputStream pos = new ProtoOutputStream();
- long token;
-
- for (Data data : mDataList) {
- if (data instanceof RailInfo) {
- token = pos.start(PowerStatsServiceProto.RAIL_INFO);
- } else {
- token = pos.start(PowerStatsServiceProto.ENERGY_DATA);
- }
- data.toProto(pos);
- pos.end(token);
- }
- return pos.getBytes();
- }
-
- /**
- * Print this object to logcat.
- */
- public void print() {
- for (Data data : mDataList) {
- Log.d(TAG, data.toString());
- }
- }
-
- /**
- * RailInfo is a class that stores a description for an individual ODPM
- * rail. It provides functionality to unpack a RailInfo object from a
- * serialized protobuf byte array, and to pack a RailInfo object into
- * a ProtoOutputStream.
- */
- public static class RailInfo extends Data {
- public String mRailName;
- public String mSubSysName;
- public long mSamplingRate;
-
- public RailInfo(ProtoInputStream pis) throws IOException {
- unpackProto(pis);
- }
-
- public RailInfo(long index, String railName, String subSysName, long samplingRate) {
- mIndex = index;
- mRailName = railName;
- mSubSysName = subSysName;
- mSamplingRate = samplingRate;
- }
-
- @Override
- protected void unpackProto(ProtoInputStream pis) throws IOException {
- while (true) {
- try {
- switch (pis.nextField()) {
- case (int) RailInfoProto.INDEX:
- mIndex = pis.readInt(RailInfoProto.INDEX);
- break;
-
- case (int) RailInfoProto.RAIL_NAME:
- mRailName = pis.readString(RailInfoProto.RAIL_NAME);
- break;
-
- case (int) RailInfoProto.SUBSYS_NAME:
- mSubSysName = pis.readString(RailInfoProto.SUBSYS_NAME);
- break;
-
- case (int) RailInfoProto.SAMPLING_RATE:
- mSamplingRate = pis.readInt(RailInfoProto.SAMPLING_RATE);
- break;
-
- case ProtoInputStream.NO_MORE_FIELDS:
- return;
-
- default:
- Log.e(TAG, "Unhandled field in RailInfoProto: "
- + ProtoUtils.currentFieldToString(pis));
- break;
- }
- } catch (WireTypeMismatchException wtme) {
- Log.e(TAG, "Wire Type mismatch in RailInfoProto: "
- + ProtoUtils.currentFieldToString(pis));
- }
- }
- }
-
- @Override
- public void toProto(ProtoOutputStream pos) {
- pos.write(RailInfoProto.INDEX, mIndex);
- pos.write(RailInfoProto.RAIL_NAME, mRailName);
- pos.write(RailInfoProto.SUBSYS_NAME, mSubSysName);
- pos.write(RailInfoProto.SAMPLING_RATE, mSamplingRate);
- }
-
- @Override
- public String toString() {
- return String.format("Index = " + mIndex
- + ", RailName = " + mRailName
- + ", SubSysName = " + mSubSysName
- + ", SamplingRate = " + mSamplingRate);
- }
- }
-
- /**
- * EnergyData is a class that stores an energy (uWs) data reading for an
- * individual ODPM rail. It provides functionality to unpack an EnergyData
- * object from a serialized protobuf byte array, and to pack an EnergyData
- * object into a ProtoOutputStream.
- */
- public static class EnergyData extends Data {
- public long mTimestampMs;
- public long mEnergyUWs;
-
- public EnergyData(ProtoInputStream pis) throws IOException {
- unpackProto(pis);
- }
-
- public EnergyData(long index, long timestampMs, long energyUWs) {
- mIndex = index;
- mTimestampMs = timestampMs;
- mEnergyUWs = energyUWs;
- }
-
- @Override
- protected void unpackProto(ProtoInputStream pis) throws IOException {
- while (true) {
- try {
- switch (pis.nextField()) {
- case (int) EnergyDataProto.INDEX:
- mIndex = pis.readInt(EnergyDataProto.INDEX);
- break;
-
- case (int) EnergyDataProto.TIMESTAMP_MS:
- mTimestampMs = pis.readLong(EnergyDataProto.TIMESTAMP_MS);
- break;
-
- case (int) EnergyDataProto.ENERGY_UWS:
- mEnergyUWs = pis.readLong(EnergyDataProto.ENERGY_UWS);
- break;
-
- case ProtoInputStream.NO_MORE_FIELDS:
- return;
-
- default:
- Log.e(TAG, "Unhandled field in EnergyDataProto: "
- + ProtoUtils.currentFieldToString(pis));
- break;
- }
- } catch (WireTypeMismatchException wtme) {
- Log.e(TAG, "Wire Type mismatch in EnergyDataProto: "
- + ProtoUtils.currentFieldToString(pis));
- }
- }
- }
-
- @Override
- protected void toProto(ProtoOutputStream pos) {
- pos.write(EnergyDataProto.INDEX, mIndex);
- pos.write(EnergyDataProto.TIMESTAMP_MS, mTimestampMs);
- pos.write(EnergyDataProto.ENERGY_UWS, mEnergyUWs);
- }
-
- @Override
- public String toString() {
- return String.format("Index = " + mIndex
- + ", Timestamp (ms) = " + mTimestampMs
- + ", Energy (uWs) = " + mEnergyUWs);
- }
- }
-
- private abstract static class Data {
- public long mIndex;
- protected abstract void unpackProto(ProtoInputStream pis) throws IOException;
- protected abstract void toProto(ProtoOutputStream pos);
- }
-}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
index 84a6fc94598e..5d8afd43a5f7 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -198,19 +198,21 @@ public class PowerStatsDataStorage {
* array and written to on-device storage.
*/
public void write(byte[] data) {
- mLock.lock();
-
- long currentTimeMillis = System.currentTimeMillis();
- try {
- DataElement dataElement = new DataElement(data);
- mFileRotator.rewriteActive(new DataRewriter(dataElement.toByteArray()),
- currentTimeMillis);
- mFileRotator.maybeRotate(currentTimeMillis);
- } catch (IOException e) {
- Log.e(TAG, "Failed to write to on-device storage: " + e);
- }
+ if (data.length > 0) {
+ mLock.lock();
+
+ long currentTimeMillis = System.currentTimeMillis();
+ try {
+ DataElement dataElement = new DataElement(data);
+ mFileRotator.rewriteActive(new DataRewriter(dataElement.toByteArray()),
+ currentTimeMillis);
+ mFileRotator.maybeRotate(currentTimeMillis);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write to on-device storage: " + e);
+ }
- mLock.unlock();
+ mLock.unlock();
+ }
}
/**
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
index dc996a3e2d2e..18646b9cc06c 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
@@ -16,6 +16,17 @@
package com.android.server.powerstats;
+import android.hardware.power.stats.IPowerStats;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.function.Supplier;
+
/**
* PowerStatsHALWrapper is a wrapper class for the PowerStats HAL API calls.
*/
@@ -27,30 +38,50 @@ public final class PowerStatsHALWrapper {
*/
public interface IPowerStatsHALWrapper {
/**
- * Returns rail info for all available ODPM rails.
+ * Returns the energy consumer IDs for all available energy consumers (power models) on the
+ * device. Examples of subsystems for which energy consumer results (power models)
+ * may be available are GPS, display, wifi, etc. The default list of energy
+ * consumers can be found in the PowerStats HAL definition (EnergyConsumerId.aidl).
+ * The availability of energy consumer IDs is hardware dependent.
+ *
+ * @return List of EnergyConsumerIds all available energy consumers.
+ */
+ int[] getEnergyConsumerInfo();
+
+ /**
+ * Returns the energy consumer result for all available energy consumers (power models).
+ * Available consumers can be retrieved by calling getEnergyConsumerInfo(). The
+ * subsystem corresponding to the energy consumer result is defined by the energy
+ * consumer ID.
+ *
+ * @return List of EnergyConsumerResult objects containing energy consumer results for all
+ * available energy consumers (power models).
+ */
+ android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed();
+
+ /**
+ * Returns channel info for all available energy meters.
*
- * @return array of RailInfo objects containing rail info for all
- * available rails.
+ * @return List of ChannelInfo objects containing channel info for all available energy
+ * meters.
*/
- PowerStatsData.RailInfo[] readRailInfo();
+ android.hardware.power.stats.ChannelInfo[] getEnergyMeterInfo();
/**
- * Returns energy data for all available ODPM rails. Available rails can
- * be retrieved by calling nativeGetRailInfo. Energy data and
- * rail info can be linked through the index field.
+ * Returns energy measurements for all available energy meters. Available channels can be
+ * retrieved by calling getEnergyMeterInfo(). Energy measurements and channel info
+ * can be linked through the channelId field.
*
- * @return array of EnergyData objects containing energy data for all
- * available rails.
+ * @return List of EnergyMeasurement objects containing energy measurements for all
+ * available energy meters.
*/
- PowerStatsData.EnergyData[] readEnergyData();
+ android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters();
/**
* Returns boolean indicating if connection to power stats HAL was
* established.
*
- * @return true if connection to power stats HAL was correctly established
- * and that energy data and rail info can be read from the interface.
- * false otherwise
+ * @return true if connection to power stats HAL was correctly established.
*/
boolean initialize();
}
@@ -61,45 +92,108 @@ public final class PowerStatsHALWrapper {
* framework and will be passed into the PowerStatsService through an injector.
*/
public static final class PowerStatsHALWrapperImpl implements IPowerStatsHALWrapper {
- private static native boolean nativeInit();
- private static native PowerStatsData.RailInfo[] nativeGetRailInfo();
- private static native PowerStatsData.EnergyData[] nativeGetEnergyData();
+ private static Supplier<IPowerStats> sVintfPowerStats;
- /**
- * Returns rail info for all available ODPM rails.
- *
- * @return array of RailInfo objects containing rail info for all
- * available rails.
- */
@Override
- public PowerStatsData.RailInfo[] readRailInfo() {
- return nativeGetRailInfo();
+ public int[] getEnergyConsumerInfo() {
+ int[] energyConsumerInfoHAL = null;
+
+ if (sVintfPowerStats != null) {
+ try {
+ energyConsumerInfoHAL = sVintfPowerStats.get().getEnergyConsumerInfo();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get energy consumer info from PowerStats HAL");
+ }
+ }
+
+ return energyConsumerInfoHAL;
}
- /**
- * Returns energy data for all available ODPM rails. Available rails can
- * be retrieved by calling nativeGetRailInfo. Energy data and
- * rail info can be linked through the index field.
- *
- * @return array of EnergyData objects containing energy data for all
- * available rails.
- */
@Override
- public PowerStatsData.EnergyData[] readEnergyData() {
- return nativeGetEnergyData();
+ public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed() {
+ android.hardware.power.stats.EnergyConsumerResult[] energyConsumedHAL = null;
+
+ if (sVintfPowerStats != null) {
+ try {
+ energyConsumedHAL =
+ sVintfPowerStats.get().getEnergyConsumed(new int[0]);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get energy consumer results from PowerStats HAL");
+ }
+ }
+
+ return energyConsumedHAL;
+ }
+
+ @Override
+ public android.hardware.power.stats.ChannelInfo[] getEnergyMeterInfo() {
+ android.hardware.power.stats.ChannelInfo[] energyMeterInfoHAL = null;
+
+ if (sVintfPowerStats != null) {
+ try {
+ energyMeterInfoHAL = sVintfPowerStats.get().getEnergyMeterInfo();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get energy meter info from PowerStats HAL");
+ }
+ }
+
+ return energyMeterInfoHAL;
+ }
+
+ @Override
+ public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters() {
+ android.hardware.power.stats.EnergyMeasurement[] energyMeasurementHAL = null;
+
+ if (sVintfPowerStats != null) {
+ try {
+ energyMeasurementHAL =
+ sVintfPowerStats.get().readEnergyMeters(new int[0]);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get energy measurements from PowerStats HAL");
+ }
+ }
+
+ return energyMeasurementHAL;
}
- /**
- * Returns boolean indicating if connection to power stats HAL was
- * established.
- *
- * @return true if connection to power stats HAL was correctly established
- * and that energy data and rail info can be read from the interface.
- * false otherwise
- */
@Override
public boolean initialize() {
- return nativeInit();
+ Supplier<IPowerStats> service = new VintfHalCache();
+
+ if (service.get() == null) {
+ sVintfPowerStats = null;
+ return false;
+ } else {
+ sVintfPowerStats = service;
+ return true;
+ }
+ }
+ }
+
+ private static class VintfHalCache implements Supplier<IPowerStats>, IBinder.DeathRecipient {
+ @GuardedBy("this")
+ private IPowerStats mInstance = null;
+
+ @Override
+ public synchronized IPowerStats get() {
+ if (mInstance == null) {
+ IBinder binder = Binder.allowBlocking(ServiceManager.waitForDeclaredService(
+ "android.hardware.power.stats.IPowerStats/default"));
+ if (binder != null) {
+ mInstance = IPowerStats.Stub.asInterface(binder);
+ try {
+ binder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to register DeathRecipient for " + mInstance);
+ }
+ }
+ }
+ return mInstance;
+ }
+
+ @Override
+ public synchronized void binderDied() {
+ mInstance = null;
}
}
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index 71a34a4174e5..bec99bca4d46 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -17,6 +17,9 @@
package com.android.server.powerstats;
import android.content.Context;
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -25,6 +28,10 @@ import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
+import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyMeasurementUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -32,18 +39,19 @@ import java.io.FileDescriptor;
import java.io.IOException;
/**
- * PowerStatsLogger is responsible for logging energy data to on-device
- * storage. Messages are sent to its message handler to request that energy
- * data be logged, at which time it queries the PowerStats HAL and logs the
- * data to on-device storage. The on-device storage is dumped to file by
- * calling writeToFile with a file descriptor that points to the output file.
+ * PowerStatsLogger is responsible for logging model and meter energy data to on-device storage.
+ * Messages are sent to its message handler to request that energy data be logged, at which time it
+ * queries the PowerStats HAL and logs the data to on-device storage. The on-device storage is
+ * dumped to file by calling writeModelDataToFile or writeMeterDataToFile with a file descriptor
+ * that points to the output file.
*/
public final class PowerStatsLogger extends Handler {
private static final String TAG = PowerStatsLogger.class.getSimpleName();
private static final boolean DEBUG = false;
protected static final int MSG_LOG_TO_DATA_STORAGE = 0;
- private final PowerStatsDataStorage mPowerStatsDataStorage;
+ private final PowerStatsDataStorage mPowerStatsMeterStorage;
+ private final PowerStatsDataStorage mPowerStatsModelStorage;
private final IPowerStatsHALWrapper mPowerStatsHALWrapper;
@Override
@@ -51,31 +59,81 @@ public final class PowerStatsLogger extends Handler {
switch (msg.what) {
case MSG_LOG_TO_DATA_STORAGE:
if (DEBUG) Log.d(TAG, "Logging to data storage");
- PowerStatsData energyData =
- new PowerStatsData(mPowerStatsHALWrapper.readEnergyData());
- mPowerStatsDataStorage.write(energyData.getProtoBytes());
+
+ // Log power meter data.
+ EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeters();
+ mPowerStatsMeterStorage.write(
+ EnergyMeasurementUtils.getProtoBytes(energyMeasurements));
+ if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements);
+
+ // Log power model data.
+ EnergyConsumerResult[] energyConsumerResults =
+ mPowerStatsHALWrapper.getEnergyConsumed();
+ mPowerStatsModelStorage.write(
+ EnergyConsumerResultUtils.getProtoBytes(energyConsumerResults));
+ if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResults);
break;
}
}
/**
- * Writes data stored in PowerStatsDataStorage to a file descriptor.
+ * Writes meter data stored in PowerStatsDataStorage to a file descriptor.
+ *
+ * @param fd FileDescriptor where meter data stored in PowerStatsDataStorage is written. Data
+ * is written in protobuf format as defined by powerstatsservice.proto.
+ */
+ public void writeMeterDataToFile(FileDescriptor fd) {
+ if (DEBUG) Log.d(TAG, "Writing meter data to file");
+
+ final ProtoOutputStream pos = new ProtoOutputStream(fd);
+
+ try {
+ ChannelInfo[] channelInfo = mPowerStatsHALWrapper.getEnergyMeterInfo();
+ ChannelInfoUtils.packProtoMessage(channelInfo, pos);
+ if (DEBUG) ChannelInfoUtils.print(channelInfo);
+
+ mPowerStatsMeterStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
+ @Override
+ public void onReadDataElement(byte[] data) {
+ try {
+ final ProtoInputStream pis =
+ new ProtoInputStream(new ByteArrayInputStream(data));
+ // TODO(b/166535853): ProtoOutputStream doesn't provide a method to write
+ // a byte array that already contains a serialized proto, so I have to
+ // deserialize, then re-serialize. This is computationally inefficient.
+ EnergyMeasurement[] energyMeasurement =
+ EnergyMeasurementUtils.unpackProtoMessage(data);
+ EnergyMeasurementUtils.packProtoMessage(energyMeasurement, pos);
+ if (DEBUG) EnergyMeasurementUtils.print(energyMeasurement);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write energy meter data to incident report.");
+ }
+ }
+ });
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write energy meter info to incident report.");
+ }
+
+ pos.flush();
+ }
+
+ /**
+ * Writes model data stored in PowerStatsDataStorage to a file descriptor.
*
- * @param fd FileDescriptor where data stored in PowerStatsDataStorage is
- * written. Data is written in protobuf format as defined by
- * powerstatsservice.proto.
+ * @param fd FileDescriptor where model data stored in PowerStatsDataStorage is written. Data
+ * is written in protobuf format as defined by powerstatsservice.proto.
*/
- public void writeToFile(FileDescriptor fd) {
- if (DEBUG) Log.d(TAG, "Writing to file");
+ public void writeModelDataToFile(FileDescriptor fd) {
+ if (DEBUG) Log.d(TAG, "Writing model data to file");
final ProtoOutputStream pos = new ProtoOutputStream(fd);
try {
- PowerStatsData railInfo = new PowerStatsData(mPowerStatsHALWrapper.readRailInfo());
- railInfo.toProto(pos);
- if (DEBUG) railInfo.print();
+ int[] energyConsumerId = mPowerStatsHALWrapper.getEnergyConsumerInfo();
+ EnergyConsumerIdUtils.packProtoMessage(energyConsumerId, pos);
+ if (DEBUG) EnergyConsumerIdUtils.print(energyConsumerId);
- mPowerStatsDataStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
+ mPowerStatsModelStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
@Override
public void onReadDataElement(byte[] data) {
try {
@@ -84,26 +142,29 @@ public final class PowerStatsLogger extends Handler {
// TODO(b/166535853): ProtoOutputStream doesn't provide a method to write
// a byte array that already contains a serialized proto, so I have to
// deserialize, then re-serialize. This is computationally inefficient.
- final PowerStatsData energyData = new PowerStatsData(pis);
- energyData.toProto(pos);
- if (DEBUG) energyData.print();
+ EnergyConsumerResult[] energyConsumerResult =
+ EnergyConsumerResultUtils.unpackProtoMessage(data);
+ EnergyConsumerResultUtils.packProtoMessage(energyConsumerResult, pos);
+ if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResult);
} catch (IOException e) {
- Log.e(TAG, "Failed to write energy data to incident report.");
+ Log.e(TAG, "Failed to write energy model data to incident report.");
}
}
});
} catch (IOException e) {
- Log.e(TAG, "Failed to write rail info to incident report.");
+ Log.e(TAG, "Failed to write energy model info to incident report.");
}
pos.flush();
}
- public PowerStatsLogger(Context context, File dataStoragePath, String dataStorageFilename,
- IPowerStatsHALWrapper powerStatsHALWrapper) {
+ public PowerStatsLogger(Context context, File dataStoragePath, String meterFilename,
+ String modelFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
super(Looper.getMainLooper());
mPowerStatsHALWrapper = powerStatsHALWrapper;
- mPowerStatsDataStorage = new PowerStatsDataStorage(context, dataStoragePath,
- dataStorageFilename);
+ mPowerStatsMeterStorage = new PowerStatsDataStorage(context, dataStoragePath,
+ meterFilename);
+ mPowerStatsModelStorage = new PowerStatsDataStorage(context, dataStoragePath,
+ modelFilename);
}
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 81883f3012e9..b89464fe4656 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -43,7 +43,8 @@ public class PowerStatsService extends SystemService {
private static final boolean DEBUG = false;
private static final String DATA_STORAGE_SUBDIR = "powerstats";
private static final int DATA_STORAGE_VERSION = 0;
- private static final String DATA_STORAGE_FILENAME = "log.powerstats." + DATA_STORAGE_VERSION;
+ private static final String METER_FILENAME = "log.powerstats.meter." + DATA_STORAGE_VERSION;
+ private static final String MODEL_FILENAME = "log.powerstats.model." + DATA_STORAGE_VERSION;
private final Injector mInjector;
@@ -63,8 +64,12 @@ public class PowerStatsService extends SystemService {
DATA_STORAGE_SUBDIR);
}
- String createDataStorageFilename() {
- return DATA_STORAGE_FILENAME;
+ String createMeterFilename() {
+ return METER_FILENAME;
+ }
+
+ String createModelFilename() {
+ return MODEL_FILENAME;
}
IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
@@ -72,9 +77,10 @@ public class PowerStatsService extends SystemService {
}
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String dataStorageFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
- return new PowerStatsLogger(context, dataStoragePath, dataStorageFilename,
- powerStatsHALWrapper);
+ String meterFilename, String modelFilename,
+ IPowerStatsHALWrapper powerStatsHALWrapper) {
+ return new PowerStatsLogger(context, dataStoragePath, meterFilename,
+ modelFilename, powerStatsHALWrapper);
}
BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
@@ -91,11 +97,15 @@ public class PowerStatsService extends SystemService {
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- if (args.length > 0 && "--proto".equals(args[0])) {
- if (mPowerStatsLogger == null) {
- Log.e(TAG, "PowerStats HAL is not initialized. No data available.");
- } else {
- mPowerStatsLogger.writeToFile(fd);
+ if (mPowerStatsLogger == null) {
+ Log.e(TAG, "PowerStats HAL is not initialized. No data available.");
+ } else {
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ if ("model".equals(args[1])) {
+ mPowerStatsLogger.writeModelDataToFile(fd);
+ } else if ("meter".equals(args[1])) {
+ mPowerStatsLogger.writeMeterDataToFile(fd);
+ }
}
}
}
@@ -121,8 +131,8 @@ public class PowerStatsService extends SystemService {
// Only start logger and triggers if initialization is successful.
mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext,
- mInjector.createDataStoragePath(), mInjector.createDataStorageFilename(),
- mPowerStatsHALWrapper);
+ mInjector.createDataStoragePath(), mInjector.createMeterFilename(),
+ mInjector.createModelFilename(), mPowerStatsHALWrapper);
mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
} else {
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
new file mode 100644
index 000000000000..c29c5daf34a5
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ */
+
+package com.android.server.powerstats;
+
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
+import android.util.Log;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
+import android.util.proto.WireTypeMismatchException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * ProtoStreamUtils provides helper functions for the PowerStats HAL objects returned from calls
+ * to the PowerStats HAL APIs. It provides functions to pack/unpack object arrays to/from protobuf
+ * format. These helper functions are required since frameworks code uses the genstream option
+ * when generating source code and therefore, getter/setter helper functions are not available. The
+ * protobufs need to be packed/unpacked in a more manual way using
+ * ProtoOutputStream/ProtoInputStream. It also provides print() functions for debugging purposes.
+ */
+public class ProtoStreamUtils {
+ private static final String TAG = ProtoStreamUtils.class.getSimpleName();
+
+ static class ChannelInfoUtils {
+ public static void packProtoMessage(ChannelInfo[] channelInfo, ProtoOutputStream pos) {
+ long token;
+
+ for (int i = 0; i < channelInfo.length; i++) {
+ token = pos.start(PowerStatsServiceMeterProto.CHANNEL_INFO);
+ pos.write(ChannelInfoProto.CHANNEL_ID, channelInfo[i].channelId);
+ pos.write(ChannelInfoProto.CHANNEL_NAME, channelInfo[i].channelName);
+ pos.end(token);
+ }
+
+ }
+
+ public static void print(ChannelInfo[] channelInfo) {
+ for (int i = 0; i < channelInfo.length; i++) {
+ Log.d(TAG, "ChannelId = " + channelInfo[i].channelId
+ + ", ChannelName = " + channelInfo[i].channelName);
+ }
+ }
+ }
+
+ static class EnergyMeasurementUtils {
+ public static byte[] getProtoBytes(EnergyMeasurement[] energyMeasurement) {
+ ProtoOutputStream pos = new ProtoOutputStream();
+ packProtoMessage(energyMeasurement, pos);
+ return pos.getBytes();
+ }
+
+ public static void packProtoMessage(EnergyMeasurement[] energyMeasurement,
+ ProtoOutputStream pos) {
+ long token;
+
+ for (int i = 0; i < energyMeasurement.length; i++) {
+ token = pos.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
+ pos.write(EnergyMeasurementProto.CHANNEL_ID, energyMeasurement[i].channelId);
+ pos.write(EnergyMeasurementProto.TIMESTAMP_MS, energyMeasurement[i].timestampMs);
+ pos.write(EnergyMeasurementProto.ENERGY_UWS, energyMeasurement[i].energyUWs);
+ pos.end(token);
+ }
+ }
+
+ public static EnergyMeasurement[] unpackProtoMessage(byte[] data) throws IOException {
+ final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
+ List<EnergyMeasurement> energyMeasurementList = new ArrayList<EnergyMeasurement>();
+ long token;
+
+ while (true) {
+ try {
+ int nextField = pis.nextField();
+ EnergyMeasurement energyMeasurement = new EnergyMeasurement();
+
+ if (nextField == (int) PowerStatsServiceMeterProto.ENERGY_MEASUREMENT) {
+ token = pis.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
+ energyMeasurementList.add(unpackProtoMessage(pis));
+ pis.end(token);
+ } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
+ return energyMeasurementList.toArray(
+ new EnergyMeasurement[energyMeasurementList.size()]);
+ } else {
+ Log.e(TAG, "Unhandled field in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Log.e(TAG, "Wire Type mismatch in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ private static EnergyMeasurement unpackProtoMessage(ProtoInputStream pis)
+ throws IOException {
+ EnergyMeasurement energyMeasurement = new EnergyMeasurement();
+
+ while (true) {
+ try {
+ switch (pis.nextField()) {
+ case (int) EnergyMeasurementProto.CHANNEL_ID:
+ energyMeasurement.channelId =
+ pis.readInt(EnergyMeasurementProto.CHANNEL_ID);
+ break;
+
+ case (int) EnergyMeasurementProto.TIMESTAMP_MS:
+ energyMeasurement.timestampMs =
+ pis.readLong(EnergyMeasurementProto.TIMESTAMP_MS);
+ break;
+
+ case (int) EnergyMeasurementProto.ENERGY_UWS:
+ energyMeasurement.energyUWs =
+ pis.readLong(EnergyMeasurementProto.ENERGY_UWS);
+ break;
+
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return energyMeasurement;
+
+ default:
+ Log.e(TAG, "Unhandled field in EnergyMeasurementProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ break;
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Log.e(TAG, "Wire Type mismatch in EnergyMeasurementProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ public static void print(EnergyMeasurement[] energyMeasurement) {
+ for (int i = 0; i < energyMeasurement.length; i++) {
+ Log.d(TAG, "ChannelId = " + energyMeasurement[i].channelId
+ + ", Timestamp (ms) = " + energyMeasurement[i].timestampMs
+ + ", Energy (uWs) = " + energyMeasurement[i].energyUWs);
+ }
+ }
+ }
+
+ static class EnergyConsumerIdUtils {
+ public static void packProtoMessage(int[] energyConsumerId, ProtoOutputStream pos) {
+ long token;
+
+ for (int i = 0; i < energyConsumerId.length; i++) {
+ token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_ID);
+ pos.write(EnergyConsumerIdProto.ENERGY_CONSUMER_ID, energyConsumerId[i]);
+ pos.end(token);
+ }
+ }
+
+ public static void print(int[] energyConsumerId) {
+ for (int i = 0; i < energyConsumerId.length; i++) {
+ Log.d(TAG, "EnergyConsumerId = " + energyConsumerId[i]);
+ }
+ }
+ }
+
+ static class EnergyConsumerResultUtils {
+ public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult) {
+ ProtoOutputStream pos = new ProtoOutputStream();
+ packProtoMessage(energyConsumerResult, pos);
+ return pos.getBytes();
+ }
+
+ public static void packProtoMessage(EnergyConsumerResult[] energyConsumerResult,
+ ProtoOutputStream pos) {
+ long token;
+
+ for (int i = 0; i < energyConsumerResult.length; i++) {
+ token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
+ pos.write(EnergyConsumerResultProto.ENERGY_CONSUMER_ID,
+ energyConsumerResult[i].energyConsumerId);
+ pos.write(EnergyConsumerResultProto.TIMESTAMP_MS,
+ energyConsumerResult[i].timestampMs);
+ pos.write(EnergyConsumerResultProto.ENERGY_UWS, energyConsumerResult[i].energyUWs);
+ pos.end(token);
+ }
+ }
+
+ public static EnergyConsumerResult[] unpackProtoMessage(byte[] data) throws IOException {
+ final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
+ List<EnergyConsumerResult> energyConsumerResultList =
+ new ArrayList<EnergyConsumerResult>();
+ long token;
+
+ while (true) {
+ try {
+ int nextField = pis.nextField();
+ EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult();
+
+ if (nextField == (int) PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT) {
+ token = pis.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
+ energyConsumerResultList.add(unpackProtoMessage(pis));
+ pis.end(token);
+ } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
+ return energyConsumerResultList.toArray(
+ new EnergyConsumerResult[energyConsumerResultList.size()]);
+ } else {
+ Log.e(TAG, "Unhandled field in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Log.e(TAG, "Wire Type mismatch in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ private static EnergyConsumerResult unpackProtoMessage(ProtoInputStream pis)
+ throws IOException {
+ EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult();
+
+ while (true) {
+ try {
+ switch (pis.nextField()) {
+ case (int) EnergyConsumerResultProto.ENERGY_CONSUMER_ID:
+ energyConsumerResult.energyConsumerId =
+ pis.readInt(EnergyConsumerResultProto.ENERGY_CONSUMER_ID);
+ break;
+
+ case (int) EnergyConsumerResultProto.TIMESTAMP_MS:
+ energyConsumerResult.timestampMs =
+ pis.readLong(EnergyConsumerResultProto.TIMESTAMP_MS);
+ break;
+
+ case (int) EnergyConsumerResultProto.ENERGY_UWS:
+ energyConsumerResult.energyUWs =
+ pis.readLong(EnergyConsumerResultProto.ENERGY_UWS);
+ break;
+
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return energyConsumerResult;
+
+ default:
+ Log.e(TAG, "Unhandled field in EnergyConsumerResultProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ break;
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Log.e(TAG, "Wire Type mismatch in EnergyConsumerResultProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ public static void print(EnergyConsumerResult[] energyConsumerResult) {
+ for (int i = 0; i < energyConsumerResult.length; i++) {
+ Log.d(TAG, "EnergyConsumerId = " + energyConsumerResult[i].energyConsumerId
+ + ", Timestamp (ms) = " + energyConsumerResult[i].timestampMs
+ + ", Energy (uWs) = " + energyConsumerResult[i].energyUWs);
+ }
+ }
+ }
+}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index d6a56ba531d0..9f83bafbed75 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -38,7 +38,6 @@ 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/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
deleted file mode 100644
index 5eb6b7343945..000000000000
--- a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * 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;
-
-static jclass class_railInfo;
-static jmethodID method_railInfoInit;
-static jclass class_energyData;
-static jmethodID method_energyDataInit;
-
-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 nativeGetRailInfo(JNIEnv *env, jclass clazz) {
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!connectToPowerStatsHal()) {
- ALOGE("nativeGetRailInfo failed to connect to power.stats HAL");
- return nullptr;
- }
-
- hidl_vec<RailInfo> list;
- Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo([&list](auto rails, auto status) {
- if (status != Status::SUCCESS) {
- ALOGW("Rail information is not available");
- } else {
- list = std::move(rails);
- }
- });
-
- if (!checkResult(ret, __func__)) {
- ALOGE("getRailInfo failed");
- return nullptr;
- } else {
- jobjectArray railInfoArray = env->NewObjectArray(list.size(), class_railInfo, nullptr);
- for (int i = 0; i < list.size(); i++) {
- jstring railName = env->NewStringUTF(list[i].railName.c_str());
- jstring subsysName = env->NewStringUTF(list[i].subsysName.c_str());
- jobject railInfo = env->NewObject(class_railInfo, method_railInfoInit, list[i].index,
- railName, subsysName, list[i].samplingRate);
- env->SetObjectArrayElement(railInfoArray, i, railInfo);
- env->DeleteLocalRef(railName);
- env->DeleteLocalRef(subsysName);
- env->DeleteLocalRef(railInfo);
- }
- return railInfoArray;
- }
-}
-
-static jobjectArray nativeGetEnergyData(JNIEnv *env, jclass clazz) {
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!connectToPowerStatsHal()) {
- ALOGE("nativeGetEnergy failed to connect to power.stats HAL");
- }
-
- hidl_vec<EnergyData> list;
- Return<void> ret =
- gPowerStatsHalV1_0_ptr->getEnergyData({}, [&list](auto energyData, auto status) {
- if (status != Status::SUCCESS) {
- ALOGW("getEnergyData is not supported");
- } else {
- list = std::move(energyData);
- }
- });
-
- if (!checkResult(ret, __func__)) {
- ALOGE("getEnergyData failed");
- return nullptr;
- } else {
- jobjectArray energyDataArray = env->NewObjectArray(list.size(), class_energyData, nullptr);
- for (int i = 0; i < list.size(); i++) {
- jobject energyData = env->NewObject(class_energyData, method_energyDataInit,
- list[i].index, list[i].timestamp, list[i].energy);
- env->SetObjectArrayElement(energyDataArray, i, energyData);
- env->DeleteLocalRef(energyData);
- }
- return energyDataArray;
- }
-}
-
-static jboolean nativeInit(JNIEnv *env, jclass clazz) {
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- jclass temp = env->FindClass("com/android/server/powerstats/PowerStatsData$RailInfo");
- if (temp == nullptr) return false;
-
- class_railInfo = (jclass)env->NewGlobalRef(temp);
- if (class_railInfo == nullptr) return false;
-
- method_railInfoInit =
- env->GetMethodID(class_railInfo, "<init>", "(JLjava/lang/String;Ljava/lang/String;J)V");
- if (method_railInfoInit == nullptr) return false;
-
- temp = env->FindClass("com/android/server/powerstats/PowerStatsData$EnergyData");
- if (temp == nullptr) return false;
-
- class_energyData = (jclass)env->NewGlobalRef(temp);
- if (class_energyData == nullptr) return false;
-
- method_energyDataInit = env->GetMethodID(class_energyData, "<init>", "(JJJ)V");
- if (method_energyDataInit == nullptr) return false;
-
- bool rv = true;
-
- if (!connectToPowerStatsHal()) {
- ALOGE("nativeInit failed to connect to power.stats HAL");
- rv = false;
- } else {
- Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo([&rv](auto rails, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("nativeInit RailInfo is unavailable");
- rv = false;
- }
- });
-
- ret = gPowerStatsHalV1_0_ptr->getEnergyData({}, [&rv](auto energyData, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("nativeInit EnergyData is unavailable");
- rv = false;
- }
- });
- }
-
- return rv;
-}
-
-static const JNINativeMethod method_table[] = {
- {"nativeInit", "()Z", (void *)nativeInit},
- {"nativeGetRailInfo", "()[Lcom/android/server/powerstats/PowerStatsData$RailInfo;",
- (void *)nativeGetRailInfo},
- {"nativeGetEnergyData", "()[Lcom/android/server/powerstats/PowerStatsData$EnergyData;",
- (void *)nativeGetEnergyData},
-};
-
-int register_android_server_PowerStatsService(JNIEnv *env) {
- return jniRegisterNativeMethods(env,
- "com/android/server/powerstats/"
- "PowerStatsHALWrapper$PowerStatsHALWrapperImpl",
- method_table, NELEM(method_table));
-}
-
-}; // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 48d524433741..0ffa5c39bacc 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -29,7 +29,6 @@ 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);
@@ -84,7 +83,6 @@ 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 3221a4d4f12c..59aff8d43755 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -20,12 +20,16 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.Context;
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
import androidx.test.InstrumentationRegistry;
import com.android.server.SystemService;
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
-import com.android.server.powerstats.nano.PowerStatsServiceProto;
+import com.android.server.powerstats.nano.PowerStatsServiceMeterProto;
+import com.android.server.powerstats.nano.PowerStatsServiceModelProto;
import org.junit.Before;
import org.junit.Test;
@@ -48,11 +52,12 @@ import java.util.Random;
public class PowerStatsServiceTest {
private static final String TAG = PowerStatsServiceTest.class.getSimpleName();
private static final String DATA_STORAGE_SUBDIR = "powerstatstest";
- private static final String DATA_STORAGE_FILENAME = "test";
+ private static final String METER_FILENAME = "metertest";
+ private static final String MODEL_FILENAME = "modeltest";
private static final String PROTO_OUTPUT_FILENAME = "powerstats.proto";
- private static final String RAIL_NAME = "railname";
- private static final String SUBSYS_NAME = "subsysname";
- private static final int POWER_RAIL_COUNT = 8;
+ private static final String CHANNEL_NAME = "channelname";
+ private static final int ENERGY_METER_COUNT = 8;
+ private static final int ENERGY_CONSUMER_COUNT = 2;
private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
private PowerStatsService mService;
@@ -75,8 +80,13 @@ public class PowerStatsServiceTest {
}
@Override
- String createDataStorageFilename() {
- return DATA_STORAGE_FILENAME;
+ String createMeterFilename() {
+ return METER_FILENAME;
+ }
+
+ @Override
+ String createModelFilename() {
+ return MODEL_FILENAME;
}
@Override
@@ -86,9 +96,10 @@ public class PowerStatsServiceTest {
@Override
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String dataStorageFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
- mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, dataStorageFilename,
- powerStatsHALWrapper);
+ String meterFilename, String modelFilename,
+ IPowerStatsHALWrapper powerStatsHALWrapper) {
+ mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, meterFilename,
+ modelFilename, powerStatsHALWrapper);
return mPowerStatsLogger;
}
@@ -107,23 +118,48 @@ public class PowerStatsServiceTest {
public static final class TestPowerStatsHALWrapper implements IPowerStatsHALWrapper {
@Override
- public PowerStatsData.RailInfo[] readRailInfo() {
- PowerStatsData.RailInfo[] railInfoArray = new PowerStatsData.RailInfo[POWER_RAIL_COUNT];
- for (int i = 0; i < POWER_RAIL_COUNT; i++) {
- railInfoArray[i] = new PowerStatsData.RailInfo(i, RAIL_NAME + i, SUBSYS_NAME + i,
- i);
+ public int[] getEnergyConsumerInfo() {
+ int[] energyConsumerInfoList = new int[ENERGY_CONSUMER_COUNT];
+ for (int i = 0; i < energyConsumerInfoList.length; i++) {
+ energyConsumerInfoList[i] = i;
+ }
+ return energyConsumerInfoList;
+ }
+
+ @Override
+ public EnergyConsumerResult[] getEnergyConsumed() {
+ EnergyConsumerResult[] energyConsumedList =
+ new EnergyConsumerResult[ENERGY_CONSUMER_COUNT];
+ for (int i = 0; i < energyConsumedList.length; i++) {
+ energyConsumedList[i] = new EnergyConsumerResult();
+ energyConsumedList[i].energyConsumerId = i;
+ energyConsumedList[i].timestampMs = i;
+ energyConsumedList[i].energyUWs = i;
}
- return railInfoArray;
+ return energyConsumedList;
}
@Override
- public PowerStatsData.EnergyData[] readEnergyData() {
- PowerStatsData.EnergyData[] energyDataArray =
- new PowerStatsData.EnergyData[POWER_RAIL_COUNT];
- for (int i = 0; i < POWER_RAIL_COUNT; i++) {
- energyDataArray[i] = new PowerStatsData.EnergyData(i, i, i);
+ public ChannelInfo[] getEnergyMeterInfo() {
+ ChannelInfo[] energyMeterInfoList = new ChannelInfo[ENERGY_METER_COUNT];
+ for (int i = 0; i < energyMeterInfoList.length; i++) {
+ energyMeterInfoList[i] = new ChannelInfo();
+ energyMeterInfoList[i].channelId = i;
+ energyMeterInfoList[i].channelName = new String(CHANNEL_NAME + i);
}
- return energyDataArray;
+ return energyMeterInfoList;
+ }
+
+ @Override
+ public EnergyMeasurement[] readEnergyMeters() {
+ EnergyMeasurement[] energyMeasurementList = new EnergyMeasurement[ENERGY_METER_COUNT];
+ for (int i = 0; i < energyMeasurementList.length; i++) {
+ energyMeasurementList[i] = new EnergyMeasurement();
+ energyMeasurementList[i].channelId = i;
+ energyMeasurementList[i].timestampMs = i;
+ energyMeasurementList[i].energyUWs = i;
+ }
+ return energyMeasurementList;
}
@Override
@@ -138,7 +174,7 @@ public class PowerStatsServiceTest {
}
@Test
- public void testWrittenPowerStatsHALDataMatchesReadIncidentReportData()
+ public void testWrittenMeterDataMatchesReadIncidentReportData()
throws InterruptedException, IOException {
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -152,36 +188,116 @@ public class PowerStatsServiceTest {
// Write on-device storage to an incident report.
File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
FileOutputStream fos = new FileOutputStream(incidentReport);
- mPowerStatsLogger.writeToFile(fos.getFD());
+ mPowerStatsLogger.writeMeterDataToFile(fos.getFD());
// Read the incident report in to a byte array.
FileInputStream fis = new FileInputStream(incidentReport);
byte[] fileContent = new byte[(int) incidentReport.length()];
fis.read(fileContent);
- // Parse the incident data into a PowerStatsServiceProto object.
- PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent);
+ // Parse the incident data into a PowerStatsServiceMeterProto object.
+ PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.parseFrom(fileContent);
+
+ // Validate the channelInfo array matches what was written to on-device storage.
+ assertTrue(pssProto.channelInfo.length == ENERGY_METER_COUNT);
+ for (int i = 0; i < pssProto.channelInfo.length; i++) {
+ assertTrue(pssProto.channelInfo[i].channelId == i);
+ assertTrue(pssProto.channelInfo[i].channelName.equals(CHANNEL_NAME + i));
+ }
- // Validate the railInfo array matches what was written to on-device storage.
- assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT);
- for (int i = 0; i < pssProto.railInfo.length; i++) {
- assertTrue(pssProto.railInfo[i].index == i);
- assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i));
- assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i));
- assertTrue(pssProto.railInfo[i].samplingRate == i);
+ // Validate the energyMeasurement array matches what was written to on-device storage.
+ assertTrue(pssProto.energyMeasurement.length == ENERGY_METER_COUNT);
+ for (int i = 0; i < pssProto.energyMeasurement.length; i++) {
+ assertTrue(pssProto.energyMeasurement[i].channelId == i);
+ assertTrue(pssProto.energyMeasurement[i].timestampMs == i);
+ assertTrue(pssProto.energyMeasurement[i].energyUws == i);
}
+ }
+
+ @Test
+ public void testWrittenModelDataMatchesReadIncidentReportData()
+ throws InterruptedException, IOException {
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Write data to on-device storage.
+ mTimerTrigger.logPowerStatsData();
+
+ // The above call puts a message on a handler. Wait for
+ // it to be processed.
+ Thread.sleep(100);
+
+ // Write on-device storage to an incident report.
+ File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+ FileOutputStream fos = new FileOutputStream(incidentReport);
+ mPowerStatsLogger.writeModelDataToFile(fos.getFD());
+
+ // Read the incident report in to a byte array.
+ FileInputStream fis = new FileInputStream(incidentReport);
+ byte[] fileContent = new byte[(int) incidentReport.length()];
+ fis.read(fileContent);
+
+ // Parse the incident data into a PowerStatsServiceModelProto object.
+ PowerStatsServiceModelProto pssProto = PowerStatsServiceModelProto.parseFrom(fileContent);
+
+ // Validate the energyConsumerId array matches what was written to on-device storage.
+ assertTrue(pssProto.energyConsumerId.length == ENERGY_CONSUMER_COUNT);
+ for (int i = 0; i < pssProto.energyConsumerId.length; i++) {
+ assertTrue(pssProto.energyConsumerId[i].energyConsumerId == i);
+ }
+
+ // Validate the energyConsumerResult array matches what was written to on-device storage.
+ assertTrue(pssProto.energyConsumerResult.length == ENERGY_CONSUMER_COUNT);
+ for (int i = 0; i < pssProto.energyConsumerResult.length; i++) {
+ assertTrue(pssProto.energyConsumerResult[i].energyConsumerId == i);
+ assertTrue(pssProto.energyConsumerResult[i].timestampMs == i);
+ assertTrue(pssProto.energyConsumerResult[i].energyUws == i);
+ }
+ }
+
+ @Test
+ public void testCorruptOnDeviceMeterStorage() throws IOException {
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Generate random array of bytes to emulate corrupt data.
+ Random rd = new Random();
+ byte[] bytes = new byte[100];
+ rd.nextBytes(bytes);
+
+ // Store corrupt data in on-device storage. Add fake timestamp to filename
+ // to match format expected by FileRotator.
+ File onDeviceStorageFile = new File(mDataStorageDir, METER_FILENAME + ".1234-2234");
+ FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
- // Validate the energyData array matches what was written to on-device storage.
- assertTrue(pssProto.energyData.length == POWER_RAIL_COUNT);
- for (int i = 0; i < pssProto.energyData.length; i++) {
- assertTrue(pssProto.energyData[i].index == i);
- assertTrue(pssProto.energyData[i].timestampMs == i);
- assertTrue(pssProto.energyData[i].energyUws == i);
+ // Write on-device storage to an incident report.
+ File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+ FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
+ mPowerStatsLogger.writeMeterDataToFile(incidentReportFos.getFD());
+
+ // Read the incident report in to a byte array.
+ FileInputStream fis = new FileInputStream(incidentReport);
+ byte[] fileContent = new byte[(int) incidentReport.length()];
+ fis.read(fileContent);
+
+ // Parse the incident data into a PowerStatsServiceMeterProto object.
+ PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.parseFrom(fileContent);
+
+ // Valid channelInfo data is written to the incident report in the call to
+ // mPowerStatsLogger.writeMeterDataToFile().
+ assertTrue(pssProto.channelInfo.length == ENERGY_METER_COUNT);
+ for (int i = 0; i < pssProto.channelInfo.length; i++) {
+ assertTrue(pssProto.channelInfo[i].channelId == i);
+ assertTrue(pssProto.channelInfo[i].channelName.equals(CHANNEL_NAME + i));
}
+
+ // No energyMeasurements should be written to the incident report since it
+ // is all corrupt (random bytes generated above).
+ assertTrue(pssProto.energyMeasurement.length == 0);
}
@Test
- public void testCorruptOnDeviceStorage() throws IOException {
+ public void testCorruptOnDeviceModelStorage() throws IOException {
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
// Generate random array of bytes to emulate corrupt data.
@@ -191,7 +307,7 @@ public class PowerStatsServiceTest {
// Store corrupt data in on-device storage. Add fake timestamp to filename
// to match format expected by FileRotator.
- File onDeviceStorageFile = new File(mDataStorageDir, DATA_STORAGE_FILENAME + ".1234-2234");
+ File onDeviceStorageFile = new File(mDataStorageDir, MODEL_FILENAME + ".1234-2234");
FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
onDeviceStorageFos.write(bytes);
onDeviceStorageFos.close();
@@ -199,33 +315,30 @@ public class PowerStatsServiceTest {
// Write on-device storage to an incident report.
File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
- mPowerStatsLogger.writeToFile(incidentReportFos.getFD());
+ mPowerStatsLogger.writeModelDataToFile(incidentReportFos.getFD());
// Read the incident report in to a byte array.
FileInputStream fis = new FileInputStream(incidentReport);
byte[] fileContent = new byte[(int) incidentReport.length()];
fis.read(fileContent);
- // Parse the incident data into a PowerStatsServiceProto object.
- PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent);
-
- // Valid railInfo data is written to the incident report in the call to
- // mPowerStatsLogger.writeToFile().
- assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT);
- for (int i = 0; i < pssProto.railInfo.length; i++) {
- assertTrue(pssProto.railInfo[i].index == i);
- assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i));
- assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i));
- assertTrue(pssProto.railInfo[i].samplingRate == i);
+ // Parse the incident data into a PowerStatsServiceModelProto object.
+ PowerStatsServiceModelProto pssProto = PowerStatsServiceModelProto.parseFrom(fileContent);
+
+ // Valid energyConsumerId data is written to the incident report in the call to
+ // mPowerStatsLogger.writeModelDataToFile().
+ assertTrue(pssProto.energyConsumerId.length == ENERGY_CONSUMER_COUNT);
+ for (int i = 0; i < pssProto.energyConsumerId.length; i++) {
+ assertTrue(pssProto.energyConsumerId[i].energyConsumerId == i);
}
- // No energyData should be written to the incident report since it
+ // No energyConsumerResults should be written to the incident report since it
// is all corrupt (random bytes generated above).
- assertTrue(pssProto.energyData.length == 0);
+ assertTrue(pssProto.energyConsumerResult.length == 0);
}
@Test
- public void testNotEnoughBytesAfterLengthField() throws IOException {
+ public void testNotEnoughBytesAfterMeterLengthField() throws IOException {
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
// Create corrupt data.
@@ -236,7 +349,7 @@ public class PowerStatsServiceTest {
// Store corrupt data in on-device storage. Add fake timestamp to filename
// to match format expected by FileRotator.
- File onDeviceStorageFile = new File(mDataStorageDir, DATA_STORAGE_FILENAME + ".1234-2234");
+ File onDeviceStorageFile = new File(mDataStorageDir, METER_FILENAME + ".1234-2234");
FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
onDeviceStorageFos.write(data.toByteArray());
onDeviceStorageFos.close();
@@ -244,28 +357,68 @@ public class PowerStatsServiceTest {
// Write on-device storage to an incident report.
File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
- mPowerStatsLogger.writeToFile(incidentReportFos.getFD());
+ mPowerStatsLogger.writeMeterDataToFile(incidentReportFos.getFD());
// Read the incident report in to a byte array.
FileInputStream fis = new FileInputStream(incidentReport);
byte[] fileContent = new byte[(int) incidentReport.length()];
fis.read(fileContent);
- // Parse the incident data into a PowerStatsServiceProto object.
- PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent);
-
- // Valid railInfo data is written to the incident report in the call to
- // mPowerStatsLogger.writeToFile().
- assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT);
- for (int i = 0; i < pssProto.railInfo.length; i++) {
- assertTrue(pssProto.railInfo[i].index == i);
- assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i));
- assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i));
- assertTrue(pssProto.railInfo[i].samplingRate == i);
+ // Parse the incident data into a PowerStatsServiceMeterProto object.
+ PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.parseFrom(fileContent);
+
+ // Valid channelInfo data is written to the incident report in the call to
+ // mPowerStatsLogger.writeMeterDataToFile().
+ assertTrue(pssProto.channelInfo.length == ENERGY_METER_COUNT);
+ for (int i = 0; i < pssProto.channelInfo.length; i++) {
+ assertTrue(pssProto.channelInfo[i].channelId == i);
+ assertTrue(pssProto.channelInfo[i].channelName.equals(CHANNEL_NAME + i));
+ }
+
+ // No energyMeasurements should be written to the incident report since the
+ // input buffer had only length and no data.
+ assertTrue(pssProto.energyMeasurement.length == 0);
+ }
+
+ @Test
+ public void testNotEnoughBytesAfterModelLengthField() throws IOException {
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Create corrupt data.
+ // Length field is correct, but there is no data following the length.
+ ByteArrayOutputStream data = new ByteArrayOutputStream();
+ data.write(ByteBuffer.allocate(4).putInt(50).array());
+ byte[] test = data.toByteArray();
+
+ // Store corrupt data in on-device storage. Add fake timestamp to filename
+ // to match format expected by FileRotator.
+ File onDeviceStorageFile = new File(mDataStorageDir, MODEL_FILENAME + ".1234-2234");
+ FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(data.toByteArray());
+ onDeviceStorageFos.close();
+
+ // Write on-device storage to an incident report.
+ File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+ FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
+ mPowerStatsLogger.writeModelDataToFile(incidentReportFos.getFD());
+
+ // Read the incident report in to a byte array.
+ FileInputStream fis = new FileInputStream(incidentReport);
+ byte[] fileContent = new byte[(int) incidentReport.length()];
+ fis.read(fileContent);
+
+ // Parse the incident data into a PowerStatsServiceModelProto object.
+ PowerStatsServiceModelProto pssProto = PowerStatsServiceModelProto.parseFrom(fileContent);
+
+ // Valid energyConsumerId data is written to the incident report in the call to
+ // mPowerStatsLogger.writeModelDataToFile().
+ assertTrue(pssProto.energyConsumerId.length == ENERGY_CONSUMER_COUNT);
+ for (int i = 0; i < pssProto.energyConsumerId.length; i++) {
+ assertTrue(pssProto.energyConsumerId[i].energyConsumerId == i);
}
- // No energyData should be written to the incident report since the
+ // No energyConsumerResults should be written to the incident report since the
// input buffer had only length and no data.
- assertTrue(pssProto.energyData.length == 0);
+ assertTrue(pssProto.energyConsumerResult.length == 0);
}
}
diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java
index 8ab302a50662..76edd6341b55 100644
--- a/tools/powerstats/PowerStatsServiceProtoParser.java
+++ b/tools/powerstats/PowerStatsServiceProtoParser.java
@@ -25,50 +25,95 @@ import java.io.IOException;
* is output to STDOUT in csv format.
*/
public class PowerStatsServiceProtoParser {
- private static void printRailInfo(PowerStatsServiceProto proto) {
+ private static void printEnergyMeterInfo(PowerStatsServiceMeterProto proto) {
String csvHeader = new String();
- for (int i = 0; i < proto.getRailInfoCount(); i++) {
- RailInfoProto railInfo = proto.getRailInfo(i);
- csvHeader += "Index" + ","
- + "Timestamp" + ","
- + railInfo.getRailName() + "/" + railInfo.getSubsysName() + ",";
+ for (int i = 0; i < proto.getChannelInfoCount(); i++) {
+ ChannelInfoProto energyMeterInfo = proto.getChannelInfo(i);
+ csvHeader += "Index,Timestamp," + energyMeterInfo.getChannelId()
+ + "/" + energyMeterInfo.getChannelName() + ",";
}
System.out.println(csvHeader);
}
- private static void printEnergyData(PowerStatsServiceProto proto) {
- int railInfoCount = proto.getRailInfoCount();
+ private static void printEnergyMeasurements(PowerStatsServiceMeterProto proto) {
+ int energyMeterInfoCount = proto.getChannelInfoCount();
- if (railInfoCount > 0) {
- int energyDataCount = proto.getEnergyDataCount();
- int energyDataSetCount = energyDataCount / railInfoCount;
+ if (energyMeterInfoCount > 0) {
+ int energyMeasurementCount = proto.getEnergyMeasurementCount();
+ int energyMeasurementSetCount = energyMeasurementCount / energyMeterInfoCount;
- for (int i = 0; i < energyDataSetCount; i++) {
+ for (int i = 0; i < energyMeasurementSetCount; i++) {
String csvRow = new String();
- for (int j = 0; j < railInfoCount; j++) {
- EnergyDataProto energyData = proto.getEnergyData(i * railInfoCount + j);
- csvRow += energyData.getIndex() + ","
- + energyData.getTimestampMs() + ","
- + energyData.getEnergyUws() + ",";
+ for (int j = 0; j < energyMeterInfoCount; j++) {
+ EnergyMeasurementProto energyMeasurement =
+ proto.getEnergyMeasurement(i * energyMeterInfoCount + j);
+ csvRow += energyMeasurement.getChannelId() + ","
+ + energyMeasurement.getTimestampMs() + ","
+ + energyMeasurement.getEnergyUws() + ",";
}
System.out.println(csvRow);
}
} else {
- System.out.println("Error: railInfoCount is zero");
+ System.out.println("Error: energyMeterInfoCount is zero");
+ }
+ }
+
+ private static void printEnergyConsumerId(PowerStatsServiceModelProto proto) {
+ String csvHeader = new String();
+ for (int i = 0; i < proto.getEnergyConsumerIdCount(); i++) {
+ EnergyConsumerIdProto energyConsumerId = proto.getEnergyConsumerId(i);
+ csvHeader += "Index,Timestamp," + energyConsumerId.getEnergyConsumerId() + ",";
+ }
+ System.out.println(csvHeader);
+ }
+
+ private static void printEnergyConsumerResults(PowerStatsServiceModelProto proto) {
+ int energyConsumerIdCount = proto.getEnergyConsumerIdCount();
+
+ if (energyConsumerIdCount > 0) {
+ int energyConsumerResultCount = proto.getEnergyConsumerResultCount();
+ int energyConsumerResultSetCount = energyConsumerResultCount / energyConsumerIdCount;
+
+ for (int i = 0; i < energyConsumerResultSetCount; i++) {
+ String csvRow = new String();
+ for (int j = 0; j < energyConsumerIdCount; j++) {
+ EnergyConsumerResultProto energyConsumerResult =
+ proto.getEnergyConsumerResult(i * energyConsumerIdCount + j);
+ csvRow += energyConsumerResult.getEnergyConsumerId() + ","
+ + energyConsumerResult.getTimestampMs() + ","
+ + energyConsumerResult.getEnergyUws() + ",";
+ }
+ System.out.println(csvRow);
+ }
+ } else {
+ System.out.println("Error: energyConsumerIdCount is zero");
}
}
private static void generateCsvFile(String pathToIncidentReport) {
try {
- IncidentReportProto irProto =
- IncidentReportProto.parseFrom(new FileInputStream(pathToIncidentReport));
+ // Print power meter data.
+ IncidentReportMeterProto irMeterProto =
+ IncidentReportMeterProto.parseFrom(new FileInputStream(pathToIncidentReport));
+
+ if (irMeterProto.hasIncidentReport()) {
+ PowerStatsServiceMeterProto pssMeterProto = irMeterProto.getIncidentReport();
+ printEnergyMeterInfo(pssMeterProto);
+ printEnergyMeasurements(pssMeterProto);
+ } else {
+ System.out.println("Meter incident report not found. Exiting.");
+ }
+
+ // Print power model data.
+ IncidentReportModelProto irModelProto =
+ IncidentReportModelProto.parseFrom(new FileInputStream(pathToIncidentReport));
- if (irProto.hasIncidentReport()) {
- PowerStatsServiceProto pssProto = irProto.getIncidentReport();
- printRailInfo(pssProto);
- printEnergyData(pssProto);
+ if (irModelProto.hasIncidentReport()) {
+ PowerStatsServiceModelProto pssModelProto = irModelProto.getIncidentReport();
+ printEnergyConsumerId(pssModelProto);
+ printEnergyConsumerResults(pssModelProto);
} else {
- System.out.println("Incident report not found. Exiting.");
+ System.out.println("Model incident report not found. Exiting.");
}
} catch (IOException e) {
System.out.println("Unable to open incident report file: " + pathToIncidentReport);