Merge "Add EnergyConsumer data to battery history"

This commit is contained in:
Dmitri Plotnikov
2022-08-31 21:42:42 +00:00
committed by Android (Google) Code Review
8 changed files with 432 additions and 24 deletions

View File

@@ -1766,6 +1766,49 @@ public abstract class BatteryStats {
}
}
/**
* Measured energy delta from the previous reading.
*/
public static final class MeasuredEnergyDetails {
/**
* Description of the energy consumer, such as CPU, DISPLAY etc
*/
public static final class EnergyConsumer {
/**
* See android.hardware.power.stats.EnergyConsumerType
*/
public int type;
/**
* Used when there are multipe energy consumers of the same type, such
* as CPU clusters, multiple displays on foldable devices etc.
*/
public int ordinal;
/**
* Human-readable name of the energy consumer, e.g. "CPU"
*/
public String name;
}
public EnergyConsumer[] consumers;
public long[] chargeUC;
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < consumers.length; i++) {
if (chargeUC[i] == POWER_DATA_UNAVAILABLE) {
continue;
}
if (sb.length() != 0) {
sb.append(' ');
}
sb.append(consumers[i].name);
sb.append('=');
sb.append(chargeUC[i]);
}
return sb.toString();
}
}
/**
* Battery history record.
*/
@@ -1886,6 +1929,7 @@ public abstract class BatteryStats {
public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20;
public static final int STATE2_CELLULAR_HIGH_TX_POWER_FLAG = 1 << 19;
public static final int STATE2_USB_DATA_LINK_FLAG = 1 << 18;
public static final int STATE2_EXTENSIONS_FLAG = 1 << 17;
public static final int MOST_INTERESTING_STATES2 =
STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
@@ -1905,6 +1949,9 @@ public abstract class BatteryStats {
// Non-null when there is more detailed information at this step.
public HistoryStepDetails stepDetails;
// Non-null when there is measured energy information
public MeasuredEnergyDetails measuredEnergyDetails;
public static final int EVENT_FLAG_START = 0x8000;
public static final int EVENT_FLAG_FINISH = 0x4000;
@@ -2113,6 +2160,7 @@ public abstract class BatteryStats {
eventCode = EVENT_NONE;
eventTag = null;
tagsFirstOccurrence = false;
measuredEnergyDetails = null;
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -2162,6 +2210,7 @@ public abstract class BatteryStats {
}
tagsFirstOccurrence = o.tagsFirstOccurrence;
currentTime = o.currentTime;
measuredEnergyDetails = o.measuredEnergyDetails;
}
public boolean sameNonEvent(HistoryItem o) {
@@ -6951,6 +7000,14 @@ public abstract class BatteryStats {
item.append("\"");
}
}
if ((rec.states2 & HistoryItem.STATE2_EXTENSIONS_FLAG) != 0) {
if (!checkin) {
item.append(" ext=");
if (rec.measuredEnergyDetails != null) {
item.append("E");
}
}
}
if (rec.eventCode != HistoryItem.EVENT_NONE) {
item.append(checkin ? "," : " ");
if ((rec.eventCode&HistoryItem.EVENT_FLAG_START) != 0) {
@@ -7075,6 +7132,25 @@ public abstract class BatteryStats {
item.append("\n");
}
}
if (rec.measuredEnergyDetails != null) {
if (!checkin) {
item.append(" Energy: ");
item.append(rec.measuredEnergyDetails);
item.append("\n");
} else {
item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(',');
item.append(HISTORY_DATA); item.append(",0,XE");
for (int i = 0; i < rec.measuredEnergyDetails.consumers.length; i++) {
if (rec.measuredEnergyDetails.chargeUC[i] != POWER_DATA_UNAVAILABLE) {
item.append(',');
item.append(rec.measuredEnergyDetails.consumers[i].name);
item.append('=');
item.append(rec.measuredEnergyDetails.chargeUC[i]);
}
}
item.append("\n");
}
}
oldState = rec.states;
oldState2 = rec.states2;
// Clear High Tx Power Flag for volta positioning

View File

@@ -21,6 +21,7 @@ import android.os.BatteryManager;
import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.HistoryStepDetails;
import android.os.BatteryStats.HistoryTag;
import android.os.BatteryStats.MeasuredEnergyDetails;
import android.os.Parcel;
import android.os.ParcelFormatException;
import android.os.Process;
@@ -113,6 +114,9 @@ public class BatteryStatsHistory {
// therefore the tag value is written in the parcel
static final int TAG_FIRST_OCCURRENCE_FLAG = 0x8000;
static final int EXTENSION_MEASURED_ENERGY_HEADER_FLAG = 0x00000001;
static final int EXTENSION_MEASURED_ENERGY_FLAG = 0x00000002;
private final Parcel mHistoryBuffer;
private final File mSystemDir;
private final HistoryStepDetailsCalculator mStepDetailsCalculator;
@@ -183,6 +187,7 @@ public class BatteryStatsHistory {
private long mTrackRunningHistoryElapsedRealtimeMs = 0;
private long mTrackRunningHistoryUptimeMs = 0;
private long mHistoryBaseTimeMs;
private boolean mMeasuredEnergyHeaderWritten = false;
private byte mLastHistoryStepLevel = 0;
@@ -293,6 +298,7 @@ public class BatteryStatsHistory {
mLastHistoryElapsedRealtimeMs = 0;
mTrackRunningHistoryElapsedRealtimeMs = 0;
mTrackRunningHistoryUptimeMs = 0;
mMeasuredEnergyHeaderWritten = false;
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
@@ -932,6 +938,16 @@ public class BatteryStatsHistory {
writeHistoryItem(elapsedRealtimeMs, uptimeMs);
}
/**
* Records measured energy data.
*/
public void recordMeasuredEnergyDetails(long elapsedRealtimeMs, long uptimeMs,
MeasuredEnergyDetails measuredEnergyDetails) {
mHistoryCur.measuredEnergyDetails = measuredEnergyDetails;
mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG;
writeHistoryItem(elapsedRealtimeMs, uptimeMs);
}
/**
* Records a history item with the amount of charge consumed by WiFi. Used on certain devices
* equipped with on-device power metering.
@@ -1256,6 +1272,7 @@ public class BatteryStatsHistory {
cur.eventCode = HistoryItem.EVENT_NONE;
cur.eventTag = null;
cur.tagsFirstOccurrence = false;
cur.measuredEnergyDetails = null;
if (DEBUG) {
Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
+ " now " + mHistoryBuffer.dataPosition()
@@ -1348,6 +1365,7 @@ public class BatteryStatsHistory {
return;
}
int extensionFlags = 0;
final long deltaTime = cur.time - last.time;
final int lastBatteryLevelInt = buildBatteryLevelInt(last);
final int lastStateInt = buildStateInt(last);
@@ -1374,6 +1392,15 @@ public class BatteryStatsHistory {
if (stateIntChanged) {
firstToken |= BatteryStatsHistory.DELTA_STATE_FLAG;
}
if (cur.measuredEnergyDetails != null) {
extensionFlags |= BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_FLAG;
if (!mMeasuredEnergyHeaderWritten) {
extensionFlags |= BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_HEADER_FLAG;
}
}
if (extensionFlags != 0) {
cur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG;
}
final boolean state2IntChanged = cur.states2 != last.states2;
if (state2IntChanged) {
firstToken |= BatteryStatsHistory.DELTA_STATE2_FLAG;
@@ -1491,6 +1518,28 @@ public class BatteryStatsHistory {
}
dest.writeDouble(cur.modemRailChargeMah);
dest.writeDouble(cur.wifiRailChargeMah);
if (extensionFlags != 0) {
dest.writeInt(extensionFlags);
if (cur.measuredEnergyDetails != null) {
if (DEBUG) {
Slog.i(TAG, "WRITE DELTA: measuredEnergyDetails=" + cur.measuredEnergyDetails);
}
if (!mMeasuredEnergyHeaderWritten) {
MeasuredEnergyDetails.EnergyConsumer[] consumers =
cur.measuredEnergyDetails.consumers;
dest.writeInt(consumers.length);
for (MeasuredEnergyDetails.EnergyConsumer consumer : consumers) {
dest.writeInt(consumer.type);
dest.writeInt(consumer.ordinal);
dest.writeString(consumer.name);
}
mMeasuredEnergyHeaderWritten = true;
}
for (long chargeUC : cur.measuredEnergyDetails.chargeUC) {
dest.writeLong(chargeUC);
}
}
}
}
private int buildBatteryLevelInt(HistoryItem h) {

View File

@@ -33,6 +33,7 @@ public class BatteryStatsHistoryIterator {
private final BatteryStats.HistoryStepDetails mReadHistoryStepDetails =
new BatteryStats.HistoryStepDetails();
private final SparseArray<BatteryStats.HistoryTag> mHistoryTags = new SparseArray<>();
private BatteryStats.MeasuredEnergyDetails mMeasuredEnergyDetails;
public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history) {
mBatteryStatsHistory = history;
@@ -198,6 +199,40 @@ public class BatteryStatsHistoryIterator {
}
cur.modemRailChargeMah = src.readDouble();
cur.wifiRailChargeMah = src.readDouble();
if ((cur.states2 & BatteryStats.HistoryItem.STATE2_EXTENSIONS_FLAG) != 0) {
final int extensionFlags = src.readInt();
if ((extensionFlags & BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_HEADER_FLAG) != 0) {
if (mMeasuredEnergyDetails == null) {
mMeasuredEnergyDetails = new BatteryStats.MeasuredEnergyDetails();
}
final int consumerCount = src.readInt();
mMeasuredEnergyDetails.consumers =
new BatteryStats.MeasuredEnergyDetails.EnergyConsumer[consumerCount];
mMeasuredEnergyDetails.chargeUC = new long[consumerCount];
for (int i = 0; i < consumerCount; i++) {
BatteryStats.MeasuredEnergyDetails.EnergyConsumer consumer =
new BatteryStats.MeasuredEnergyDetails.EnergyConsumer();
consumer.type = src.readInt();
consumer.ordinal = src.readInt();
consumer.name = src.readString();
mMeasuredEnergyDetails.consumers[i] = consumer;
}
}
if ((extensionFlags & BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_FLAG) != 0) {
if (mMeasuredEnergyDetails == null) {
throw new IllegalStateException("MeasuredEnergyDetails without a header");
}
for (int i = 0; i < mMeasuredEnergyDetails.chargeUC.length; i++) {
mMeasuredEnergyDetails.chargeUC[i] = src.readLong();
}
cur.measuredEnergyDetails = mMeasuredEnergyDetails;
}
} else {
cur.measuredEnergyDetails = null;
}
}
private boolean readHistoryTag(Parcel src, int index, BatteryStats.HistoryTag outTag) {

View File

@@ -663,6 +663,11 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat
BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS,
reason, 0);
if (measuredEnergyDeltas != null && !measuredEnergyDeltas.isEmpty()) {
mStats.recordMeasuredEnergyDetailsLocked(elapsedRealtime, uptime,
mMeasuredEnergySnapshot.getMeasuredEnergyDetails(measuredEnergyDeltas));
}
if ((updateFlags & UPDATE_CPU) != 0) {
if (useLatestStates) {
onBattery = mStats.isOnBatteryLocked();

View File

@@ -7404,6 +7404,16 @@ public class BatteryStatsImpl extends BatteryStats {
return names;
}
/**
* Adds measured energy delta to battery history.
*/
@GuardedBy("this")
public void recordMeasuredEnergyDetailsLocked(long elapsedRealtimeMs,
long uptimeMs, MeasuredEnergyDetails measuredEnergyDetails) {
mHistory.recordMeasuredEnergyDetails(elapsedRealtimeMs, uptimeMs,
measuredEnergyDetails);
}
@GuardedBy("this")
@Override public long getStartClockTime() {
final long currentTimeMs = mClock.currentTimeMillis();

View File

@@ -23,19 +23,17 @@ import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerAttribution;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryStats.MeasuredEnergyDetails;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
/**
* Keeps snapshots of data from previously pulled EnergyConsumerResults.
*/
@VisibleForTesting
public class MeasuredEnergySnapshot {
private static final String TAG = "MeasuredEnergySnapshot";
@@ -87,6 +85,8 @@ public class MeasuredEnergySnapshot {
*/
private final SparseArray<SparseLongArray> mAttributionSnapshots;
private MeasuredEnergyDetails mMeasuredEnergyDetails;
/**
* Constructor that initializes to the given id->EnergyConsumer map, indicating which consumers
* exist and what their details are.
@@ -128,6 +128,28 @@ public class MeasuredEnergySnapshot {
/** Map of {@link EnergyConsumerType#OTHER} ordinals to their {uid->chargeUC} maps. */
public @Nullable SparseLongArray[] otherUidChargesUC = null;
boolean isEmpty() {
return bluetoothChargeUC <= 0
&& isEmpty(cpuClusterChargeUC)
&& isEmpty(displayChargeUC)
&& gnssChargeUC <= 0
&& mobileRadioChargeUC <= 0
&& wifiChargeUC <= 0
&& isEmpty(otherTotalChargeUC);
}
private boolean isEmpty(long[] values) {
if (values == null) {
return true;
}
for (long value: values) {
if (value > 0) {
return false;
}
}
return true;
}
}
/**
@@ -394,4 +416,119 @@ public class MeasuredEnergySnapshot {
// since the last snapshot. Round off to the nearest whole long.
return (deltaEnergyUJ * MILLIVOLTS_PER_VOLT + (avgVoltageMV / 2)) / avgVoltageMV;
}
/**
* Converts the MeasuredEnergyDeltaData object to MeasuredEnergyDetails, which can
* be saved in battery history.
*/
MeasuredEnergyDetails getMeasuredEnergyDetails(
MeasuredEnergySnapshot.MeasuredEnergyDeltaData delta) {
if (mMeasuredEnergyDetails == null) {
mMeasuredEnergyDetails = createMeasuredEnergyDetails();
}
final long[] chargeUC = mMeasuredEnergyDetails.chargeUC;
for (int i = 0; i < mMeasuredEnergyDetails.consumers.length; i++) {
MeasuredEnergyDetails.EnergyConsumer energyConsumer =
mMeasuredEnergyDetails.consumers[i];
switch (energyConsumer.type) {
case EnergyConsumerType.BLUETOOTH:
chargeUC[i] = delta.bluetoothChargeUC;
break;
case EnergyConsumerType.CPU_CLUSTER:
if (delta.cpuClusterChargeUC != null) {
chargeUC[i] = delta.cpuClusterChargeUC[energyConsumer.ordinal];
} else {
chargeUC[i] = UNAVAILABLE;
}
break;
case EnergyConsumerType.DISPLAY:
if (delta.displayChargeUC != null) {
chargeUC[i] = delta.displayChargeUC[energyConsumer.ordinal];
} else {
chargeUC[i] = UNAVAILABLE;
}
break;
case EnergyConsumerType.GNSS:
chargeUC[i] = delta.gnssChargeUC;
break;
case EnergyConsumerType.MOBILE_RADIO:
chargeUC[i] = delta.mobileRadioChargeUC;
break;
case EnergyConsumerType.WIFI:
chargeUC[i] = delta.wifiChargeUC;
break;
case EnergyConsumerType.OTHER:
if (delta.otherTotalChargeUC != null) {
chargeUC[i] = delta.otherTotalChargeUC[energyConsumer.ordinal];
} else {
chargeUC[i] = UNAVAILABLE;
}
break;
default:
chargeUC[i] = UNAVAILABLE;
break;
}
}
return mMeasuredEnergyDetails;
}
private MeasuredEnergyDetails createMeasuredEnergyDetails() {
MeasuredEnergyDetails details = new MeasuredEnergyDetails();
details.consumers =
new MeasuredEnergyDetails.EnergyConsumer[mEnergyConsumers.size()];
for (int i = 0; i < mEnergyConsumers.size(); i++) {
EnergyConsumer energyConsumer = mEnergyConsumers.valueAt(i);
MeasuredEnergyDetails.EnergyConsumer consumer =
new MeasuredEnergyDetails.EnergyConsumer();
consumer.type = energyConsumer.type;
consumer.ordinal = energyConsumer.ordinal;
switch (consumer.type) {
case EnergyConsumerType.BLUETOOTH:
consumer.name = "BLUETOOTH";
break;
case EnergyConsumerType.CPU_CLUSTER:
consumer.name = "CPU";
break;
case EnergyConsumerType.DISPLAY:
consumer.name = "DISPLAY";
break;
case EnergyConsumerType.GNSS:
consumer.name = "GNSS";
break;
case EnergyConsumerType.MOBILE_RADIO:
consumer.name = "MOBILE_RADIO";
break;
case EnergyConsumerType.WIFI:
consumer.name = "WIFI";
break;
case EnergyConsumerType.OTHER:
consumer.name = sanitizeCustomBucketName(energyConsumer.name);
break;
default:
consumer.name = "UNKNOWN";
break;
}
if (consumer.type != EnergyConsumerType.OTHER) {
boolean hasOrdinal = consumer.ordinal != 0;
if (!hasOrdinal) {
// See if any other EnergyConsumer of the same type has an ordinal
for (int j = 0; j < mEnergyConsumers.size(); j++) {
EnergyConsumer aConsumer = mEnergyConsumers.valueAt(j);
if (aConsumer.type == consumer.type && aConsumer.ordinal != 0) {
hasOrdinal = true;
break;
}
}
}
if (hasOrdinal) {
consumer.name = consumer.name + "/" + energyConsumer.ordinal;
}
}
details.consumers[i] = consumer;
}
details.chargeUC = new long[details.consumers.length];
return details;
}
}

View File

@@ -16,11 +16,17 @@
package com.android.server.power.stats;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.BatteryStats.MeasuredEnergyDetails;
import android.os.Parcel;
import android.util.Log;
@@ -28,15 +34,19 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
import com.android.internal.os.Clock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -51,6 +61,9 @@ public class BatteryStatsHistoryTest {
private File mSystemDir;
private File mHistoryDir;
private final Clock mClock = new MockClock();
private BatteryStatsHistory mHistory;
@Mock
private BatteryStatsHistory.HistoryStepDetailsCalculator mStepDetailsCalculator;
@Before
public void setUp() {
@@ -65,55 +78,56 @@ public class BatteryStatsHistoryTest {
}
}
mHistoryDir.delete();
mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
mStepDetailsCalculator, mClock);
when(mStepDetailsCalculator.getHistoryStepDetails())
.thenReturn(new BatteryStats.HistoryStepDetails());
}
@Test
public void testConstruct() {
BatteryStatsHistory history = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
null, mClock);
createActiveFile(history);
verifyFileNumbers(history, Arrays.asList(0));
verifyActiveFile(history, "0.bin");
createActiveFile(mHistory);
verifyFileNumbers(mHistory, Arrays.asList(0));
verifyActiveFile(mHistory, "0.bin");
}
@Test
public void testStartNextFile() {
BatteryStatsHistory history = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
null, mClock);
List<Integer> fileList = new ArrayList<>();
fileList.add(0);
createActiveFile(history);
createActiveFile(mHistory);
// create file 1 to 31.
for (int i = 1; i < 32; i++) {
fileList.add(i);
history.startNextFile();
createActiveFile(history);
verifyFileNumbers(history, fileList);
verifyActiveFile(history, i + ".bin");
mHistory.startNextFile();
createActiveFile(mHistory);
verifyFileNumbers(mHistory, fileList);
verifyActiveFile(mHistory, i + ".bin");
}
// create file 32
history.startNextFile();
createActiveFile(history);
mHistory.startNextFile();
createActiveFile(mHistory);
fileList.add(32);
fileList.remove(0);
// verify file 0 is deleted.
verifyFileDeleted("0.bin");
verifyFileNumbers(history, fileList);
verifyActiveFile(history, "32.bin");
verifyFileNumbers(mHistory, fileList);
verifyActiveFile(mHistory, "32.bin");
// create file 33
history.startNextFile();
createActiveFile(history);
mHistory.startNextFile();
createActiveFile(mHistory);
// verify file 1 is deleted
fileList.add(33);
fileList.remove(0);
verifyFileDeleted("1.bin");
verifyFileNumbers(history, fileList);
verifyActiveFile(history, "33.bin");
verifyFileNumbers(mHistory, fileList);
verifyActiveFile(mHistory, "33.bin");
assertEquals(0, history.getHistoryUsedSize());
assertEquals(0, mHistory.getHistoryUsedSize());
// create a new BatteryStatsHistory object, it will pick up existing history files.
BatteryStatsHistory history2 = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
@@ -168,4 +182,74 @@ public class BatteryStatsHistoryTest {
Log.e(TAG, "Error creating history file " + file.getPath(), e);
}
}
@Test
public void testRecordMeasuredEnergyDetails() {
mHistory.forceRecordAllHistory();
mHistory.startRecordingHistory(0, 0, /* reset */ true);
mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80,
1234);
MeasuredEnergyDetails details = new MeasuredEnergyDetails();
MeasuredEnergyDetails.EnergyConsumer consumer1 =
new MeasuredEnergyDetails.EnergyConsumer();
consumer1.type = 42;
consumer1.ordinal = 0;
consumer1.name = "A";
MeasuredEnergyDetails.EnergyConsumer consumer2 =
new MeasuredEnergyDetails.EnergyConsumer();
consumer2.type = 777;
consumer2.ordinal = 0;
consumer2.name = "B/0";
MeasuredEnergyDetails.EnergyConsumer consumer3 =
new MeasuredEnergyDetails.EnergyConsumer();
consumer3.type = 777;
consumer3.ordinal = 1;
consumer3.name = "B/1";
MeasuredEnergyDetails.EnergyConsumer consumer4 =
new MeasuredEnergyDetails.EnergyConsumer();
consumer4.type = 314;
consumer4.ordinal = 1;
consumer4.name = "C";
details.consumers =
new MeasuredEnergyDetails.EnergyConsumer[]{consumer1, consumer2, consumer3,
consumer4};
details.chargeUC = new long[details.consumers.length];
for (int i = 0; i < details.chargeUC.length; i++) {
details.chargeUC[i] = 100L * i;
}
details.chargeUC[3] = BatteryStats.POWER_DATA_UNAVAILABLE;
mHistory.recordMeasuredEnergyDetails(200, 200, details);
BatteryStatsHistoryIterator iterator = mHistory.iterate();
BatteryStats.HistoryItem item = new BatteryStats.HistoryItem();
assertThat(iterator.next(item)).isTrue(); // First item contains current time only
assertThat(iterator.next(item)).isTrue();
String dump = toString(item, /* checkin */ false);
assertThat(dump).contains("+200ms");
assertThat(dump).contains("ext=E");
assertThat(dump).contains("Energy: A=0 B/0=100 B/1=200");
assertThat(dump).doesNotContain("C=");
String checkin = toString(item, /* checkin */ true);
assertThat(checkin).contains("XE");
assertThat(checkin).contains("A=0,B/0=100,B/1=200");
assertThat(checkin).doesNotContain("C=");
}
private String toString(BatteryStats.HistoryItem item, boolean checkin) {
BatteryStats.HistoryPrinter printer = new BatteryStats.HistoryPrinter();
StringWriter writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);
printer.printNextItem(pw, item, 0, checkin, /* verbose */ true);
pw.flush();
return writer.toString();
}
}

View File

@@ -26,6 +26,7 @@ import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerAttribution;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryStats;
import android.util.SparseArray;
import android.util.SparseLongArray;
@@ -236,6 +237,17 @@ public final class MeasuredEnergySnapshotTest {
assertEquals(0, snapshot.getOtherOrdinalNames().length);
}
@Test
public void getMeasuredEnergyDetails() {
final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP);
snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_1, VOLTAGE_1);
BatteryStats.MeasuredEnergyDetails details = snapshot.getMeasuredEnergyDetails(delta);
assertThat(details.consumers).hasLength(4);
assertThat(details.chargeUC).isEqualTo(new long[]{2667, 3200000, 0, 0});
assertThat(details.toString()).isEqualTo("DISPLAY=2667 HPU=3200000 GPU=0 IPU &_=0");
}
private static EnergyConsumer createEnergyConsumer(int id, int ord, byte type, String name) {
final EnergyConsumer ec = new EnergyConsumer();
ec.id = id;