Add EnergyConsumer data to battery history
Bug: 243199649
Test: atest FrameworksServicesTests:BatteryStatsTests
Change-Id: I9ed353dc313a01995d0de96415336dd5298e4aa0
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index da20626..0ad596b 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1767,6 +1767,49 @@
}
/**
+ * 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.
*/
public static final class HistoryItem {
@@ -1886,6 +1929,7 @@
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 @@
// 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 @@
eventCode = EVENT_NONE;
eventTag = null;
tagsFirstOccurrence = false;
+ measuredEnergyDetails = null;
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -2162,6 +2210,7 @@
}
tagsFirstOccurrence = o.tagsFirstOccurrence;
currentTime = o.currentTime;
+ measuredEnergyDetails = o.measuredEnergyDetails;
}
public boolean sameNonEvent(HistoryItem o) {
@@ -6951,6 +7000,14 @@
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 @@
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
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 6909965..146af6a 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -21,6 +21,7 @@
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 @@
// 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 @@
private long mTrackRunningHistoryElapsedRealtimeMs = 0;
private long mTrackRunningHistoryUptimeMs = 0;
private long mHistoryBaseTimeMs;
+ private boolean mMeasuredEnergyHeaderWritten = false;
private byte mLastHistoryStepLevel = 0;
@@ -293,6 +298,7 @@
mLastHistoryElapsedRealtimeMs = 0;
mTrackRunningHistoryElapsedRealtimeMs = 0;
mTrackRunningHistoryUptimeMs = 0;
+ mMeasuredEnergyHeaderWritten = false;
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
@@ -933,6 +939,16 @@
}
/**
+ * 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 @@
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 @@
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 @@
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 @@
}
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) {
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index 1bf878cb..ee3d15b 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -33,6 +33,7 @@
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 @@
}
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) {
diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
index df902c2..49ac559 100644
--- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
@@ -663,6 +663,11 @@
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();
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 968f916..5fdd006 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -7404,6 +7404,16 @@
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();
diff --git a/services/core/java/com/android/server/power/stats/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/power/stats/MeasuredEnergySnapshot.java
index b55c799..c8b4e36 100644
--- a/services/core/java/com/android/server/power/stats/MeasuredEnergySnapshot.java
+++ b/services/core/java/com/android/server/power/stats/MeasuredEnergySnapshot.java
@@ -23,19 +23,17 @@
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 @@
*/
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 @@
/** 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 @@
// 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;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index 5c934852..047fcd6 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -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.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 @@
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 @@
}
}
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 @@
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();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MeasuredEnergySnapshotTest.java b/services/tests/servicestests/src/com/android/server/power/stats/MeasuredEnergySnapshotTest.java
index 8a0f924..122f7eb 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/MeasuredEnergySnapshotTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/MeasuredEnergySnapshotTest.java
@@ -26,6 +26,7 @@
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 @@
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;