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
This commit is contained in:
parent
d681a590c7
commit
a375ea0bf7
@ -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
|
||||
|
@ -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 PowerStatsServiceProto incident_report = 3054;
|
||||
}
|
||||
|
||||
message PowerStatsServiceProto {
|
||||
repeated RailInfoProto rail_info = 1;
|
||||
repeated EnergyDataProto energy_data = 2;
|
||||
optional PowerStatsServiceMeterProto incident_report = 3054;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rail information:
|
||||
* Reports information related to the rails being monitored.
|
||||
* 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 RailInfoProto {
|
||||
/** Index corresponding to the rail */
|
||||
optional int32 index = 1;
|
||||
|
||||
/** Name of the rail (opaque to the framework) */
|
||||
optional string rail_name = 2;
|
||||
|
||||
/** Name of the subsystem to which this rail belongs (opaque to the framework) */
|
||||
optional string subsys_name = 3;
|
||||
|
||||
/** Hardware sampling rate */
|
||||
optional int32 sampling_rate = 4;
|
||||
message IncidentReportModelProto {
|
||||
/** Section number matches that in incident.proto */
|
||||
optional PowerStatsServiceModelProto incident_report = 3055;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rail level energy measurements:
|
||||
* Reports accumulated energy since boot on each rail.
|
||||
* 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 EnergyDataProto {
|
||||
/**
|
||||
* Index corresponding to the rail. This index matches
|
||||
* the index returned in RailInfo
|
||||
*/
|
||||
optional int32 index = 1;
|
||||
message PowerStatsServiceModelProto {
|
||||
repeated EnergyConsumerIdProto energy_consumer_id = 1;
|
||||
repeated EnergyConsumerResultProto energy_consumer_result = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 PowerStatsServiceMeterProto {
|
||||
repeated ChannelInfoProto channel_info = 1;
|
||||
repeated EnergyMeasurementProto energy_measurement = 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/** Name of the energy meter channel */
|
||||
optional string channel_name = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Energy measurements:
|
||||
* Reports accumulated energy since boot for each energy meter.
|
||||
*/
|
||||
message EnergyMeasurementProto {
|
||||
/**
|
||||
* Index corresponding to the energy meter channel. This index matches
|
||||
* the index returned in ChannelInfo.
|
||||
*/
|
||||
optional int32 channel_id = 1;
|
||||
|
||||
/** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
|
||||
optional int64 timestamp_ms = 2;
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -198,19 +198,21 @@ public class PowerStatsDataStorage {
|
||||
* array and written to on-device storage.
|
||||
*/
|
||||
public void write(byte[] data) {
|
||||
mLock.lock();
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 array of RailInfo objects containing rail info for all
|
||||
* available rails.
|
||||
* @return List of EnergyConsumerIds all available energy consumers.
|
||||
*/
|
||||
PowerStatsData.RailInfo[] readRailInfo();
|
||||
int[] getEnergyConsumerInfo();
|
||||
|
||||
/**
|
||||
* 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 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 array of EnergyData objects containing energy data for all
|
||||
* available rails.
|
||||
* @return List of EnergyConsumerResult objects containing energy consumer results for all
|
||||
* available energy consumers (power models).
|
||||
*/
|
||||
PowerStatsData.EnergyData[] readEnergyData();
|
||||
android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed();
|
||||
|
||||
/**
|
||||
* Returns channel info for all available energy meters.
|
||||
*
|
||||
* @return List of ChannelInfo objects containing channel info for all available energy
|
||||
* meters.
|
||||
*/
|
||||
android.hardware.power.stats.ChannelInfo[] getEnergyMeterInfo();
|
||||
|
||||
/**
|
||||
* 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 List of EnergyMeasurement objects containing energy measurements for all
|
||||
* available energy meters.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,40 @@ 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 data stored in PowerStatsDataStorage is
|
||||
* written. Data is written in protobuf format as defined by
|
||||
* powerstatsservice.proto.
|
||||
* @param fd FileDescriptor where meter 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 writeMeterDataToFile(FileDescriptor fd) {
|
||||
if (DEBUG) Log.d(TAG, "Writing meter data to file");
|
||||
|
||||
final ProtoOutputStream pos = new ProtoOutputStream(fd);
|
||||
|
||||
try {
|
||||
PowerStatsData railInfo = new PowerStatsData(mPowerStatsHALWrapper.readRailInfo());
|
||||
railInfo.toProto(pos);
|
||||
if (DEBUG) railInfo.print();
|
||||
ChannelInfo[] channelInfo = mPowerStatsHALWrapper.getEnergyMeterInfo();
|
||||
ChannelInfoUtils.packProtoMessage(channelInfo, pos);
|
||||
if (DEBUG) ChannelInfoUtils.print(channelInfo);
|
||||
|
||||
mPowerStatsDataStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
|
||||
mPowerStatsMeterStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
|
||||
@Override
|
||||
public void onReadDataElement(byte[] data) {
|
||||
try {
|
||||
@ -84,26 +101,70 @@ 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();
|
||||
EnergyMeasurement[] energyMeasurement =
|
||||
EnergyMeasurementUtils.unpackProtoMessage(data);
|
||||
EnergyMeasurementUtils.packProtoMessage(energyMeasurement, pos);
|
||||
if (DEBUG) EnergyMeasurementUtils.print(energyMeasurement);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to write energy data to incident report.");
|
||||
Log.e(TAG, "Failed to write energy meter 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 meter info to incident report.");
|
||||
}
|
||||
|
||||
pos.flush();
|
||||
}
|
||||
|
||||
public PowerStatsLogger(Context context, File dataStoragePath, String dataStorageFilename,
|
||||
IPowerStatsHALWrapper powerStatsHALWrapper) {
|
||||
/**
|
||||
* Writes model data stored in PowerStatsDataStorage to a file descriptor.
|
||||
*
|
||||
* @param fd FileDescriptor where model data stored in PowerStatsDataStorage is written. Data
|
||||
* is written in protobuf format as defined by powerstatsservice.proto.
|
||||
*/
|
||||
public void writeModelDataToFile(FileDescriptor fd) {
|
||||
if (DEBUG) Log.d(TAG, "Writing model data to file");
|
||||
|
||||
final ProtoOutputStream pos = new ProtoOutputStream(fd);
|
||||
|
||||
try {
|
||||
int[] energyConsumerId = mPowerStatsHALWrapper.getEnergyConsumerInfo();
|
||||
EnergyConsumerIdUtils.packProtoMessage(energyConsumerId, pos);
|
||||
if (DEBUG) EnergyConsumerIdUtils.print(energyConsumerId);
|
||||
|
||||
mPowerStatsModelStorage.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.
|
||||
EnergyConsumerResult[] energyConsumerResult =
|
||||
EnergyConsumerResultUtils.unpackProtoMessage(data);
|
||||
EnergyConsumerResultUtils.packProtoMessage(energyConsumerResult, pos);
|
||||
if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResult);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to write energy model data to incident report.");
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to write energy model info to incident report.");
|
||||
}
|
||||
|
||||
pos.flush();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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",
|
||||
|
@ -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
|
@ -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);
|
||||
|
@ -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 railInfoArray;
|
||||
return energyConsumerInfoList;
|
||||
}
|
||||
|
||||
@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 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 energyDataArray;
|
||||
return energyConsumedList;
|
||||
}
|
||||
|
||||
@Override
|
||||
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 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,74 @@ 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 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 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 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);
|
||||
// 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 testCorruptOnDeviceStorage() throws IOException {
|
||||
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.
|
||||
@ -191,7 +265,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(bytes);
|
||||
onDeviceStorageFos.close();
|
||||
@ -199,33 +273,72 @@ 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);
|
||||
// Parse the incident data into a PowerStatsServiceMeterProto object.
|
||||
PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.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);
|
||||
// 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 energyData should be written to the incident report since it
|
||||
// No energyMeasurements should be written to the incident report since it
|
||||
// is all corrupt (random bytes generated above).
|
||||
assertTrue(pssProto.energyData.length == 0);
|
||||
assertTrue(pssProto.energyMeasurement.length == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEnoughBytesAfterLengthField() throws IOException {
|
||||
public void testCorruptOnDeviceModelStorage() 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, MODEL_FILENAME + ".1234-2234");
|
||||
FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
|
||||
onDeviceStorageFos.write(bytes);
|
||||
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 energyConsumerResults should be written to the incident report since it
|
||||
// is all corrupt (random bytes generated above).
|
||||
assertTrue(pssProto.energyConsumerResult.length == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
// Parse the incident data into a PowerStatsServiceMeterProto object.
|
||||
PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.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);
|
||||
// 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 energyData should be written to the incident report since the
|
||||
// No energyMeasurements should be written to the incident report since the
|
||||
// input buffer had only length and no data.
|
||||
assertTrue(pssProto.energyData.length == 0);
|
||||
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 energyConsumerResults should be written to the incident report since the
|
||||
// input buffer had only length and no data.
|
||||
assertTrue(pssProto.energyConsumerResult.length == 0);
|
||||
}
|
||||
}
|
||||
|
@ -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 (irProto.hasIncidentReport()) {
|
||||
PowerStatsServiceProto pssProto = irProto.getIncidentReport();
|
||||
printRailInfo(pssProto);
|
||||
printEnergyData(pssProto);
|
||||
if (irMeterProto.hasIncidentReport()) {
|
||||
PowerStatsServiceMeterProto pssMeterProto = irMeterProto.getIncidentReport();
|
||||
printEnergyMeterInfo(pssMeterProto);
|
||||
printEnergyMeasurements(pssMeterProto);
|
||||
} else {
|
||||
System.out.println("Incident report not found. Exiting.");
|
||||
System.out.println("Meter incident report not found. Exiting.");
|
||||
}
|
||||
|
||||
// Print power model data.
|
||||
IncidentReportModelProto irModelProto =
|
||||
IncidentReportModelProto.parseFrom(new FileInputStream(pathToIncidentReport));
|
||||
|
||||
if (irModelProto.hasIncidentReport()) {
|
||||
PowerStatsServiceModelProto pssModelProto = irModelProto.getIncidentReport();
|
||||
printEnergyConsumerId(pssModelProto);
|
||||
printEnergyConsumerResults(pssModelProto);
|
||||
} else {
|
||||
System.out.println("Model incident report not found. Exiting.");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println("Unable to open incident report file: " + pathToIncidentReport);
|
||||
|
Loading…
x
Reference in New Issue
Block a user