diff options
8 files changed, 432 insertions, 24 deletions
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index da206268917e..0ad596b682ce 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1767,6 +1767,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. */ public static final class HistoryItem { @@ -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 diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index 6909965edcd8..146af6a34b31 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.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); @@ -933,6 +939,16 @@ public class BatteryStatsHistory { } /** + * 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) { diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java index 1bf878cb9119..ee3d15b1ad7e 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java +++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java @@ -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) { 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 df902c2916ba..49ac559bf93f 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 @@ 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(); 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 968f9161b3c1..5fdd006933ee 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 @@ 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(); 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 b55c79928e62..c8b4e3671eb3 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.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; + } } 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 5c9348525861..047fcd6ed108 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.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(); + } } 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 8a0f9247dffb..122f7eb86ac8 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.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; |