From 4b8a26bcaad9049a455b45ca17b51cef555c9cea Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Mon, 17 May 2021 17:45:50 -0700 Subject: [PATCH] Convert BatteryUsageStats to and from XML Bug: 187223764 Test: atest FrameworksCoreTests:BatteryUsageStatsTest#testXml Change-Id: I6aec09bfb87c1003ede1b24cce8cf474675d2448 --- .../android/os/AggregateBatteryConsumer.java | 43 ++++++ core/java/android/os/BatteryUsageStats.java | 137 ++++++++++++++++++ core/java/android/os/PowerComponents.java | 122 ++++++++++++++++ core/java/android/os/UidBatteryConsumer.java | 57 ++++++++ core/java/android/os/UserBatteryConsumer.java | 42 ++++++ .../internal/os/BatteryUsageStatsTest.java | 24 +++ 6 files changed, 425 insertions(+) diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java index 7e167d9dca37d..802387c675b5b 100644 --- a/core/java/android/os/AggregateBatteryConsumer.java +++ b/core/java/android/os/AggregateBatteryConsumer.java @@ -17,7 +17,13 @@ package android.os; import android.annotation.NonNull; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; import java.io.PrintWriter; /** @@ -72,6 +78,43 @@ public final class AggregateBatteryConsumer extends BatteryConsumer implements P return mConsumedPowerMah; } + /** Serializes this object to XML */ + void writeToXml(TypedXmlSerializer serializer, + @BatteryUsageStats.AggregateBatteryConsumerScope int scope) throws IOException { + serializer.startTag(null, BatteryUsageStats.XML_TAG_AGGREGATE); + serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_SCOPE, scope); + serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, mConsumedPowerMah); + mPowerComponents.writeToXml(serializer); + serializer.endTag(null, BatteryUsageStats.XML_TAG_AGGREGATE); + } + + /** Parses an XML representation and populates the BatteryUsageStats builder */ + static void parseXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder) + throws XmlPullParserException, IOException { + final int scope = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_SCOPE); + final Builder consumerBuilder = builder.getAggregateBatteryConsumerBuilder(scope); + + int eventType = parser.getEventType(); + if (eventType != XmlPullParser.START_TAG || !parser.getName().equals( + BatteryUsageStats.XML_TAG_AGGREGATE)) { + throw new XmlPullParserException("Invalid XML parser state"); + } + + consumerBuilder.setConsumedPower( + parser.getAttributeDouble(null, BatteryUsageStats.XML_ATTR_POWER)); + + while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals( + BatteryUsageStats.XML_TAG_AGGREGATE)) + && eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { + PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder); + } + } + eventType = parser.next(); + } + } + /** * Builder for DeviceBatteryConsumer. */ diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 59f1fefe00a5f..64645d9ba7c57 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -20,12 +20,18 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.util.Range; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.os.BatteryStatsHistory; import com.android.internal.os.BatteryStatsHistoryIterator; import com.android.internal.os.PowerCalculator; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -76,6 +82,34 @@ public final class BatteryUsageStats implements Parcelable { public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT = 2; + // XML tags and attributes for BatteryUsageStats persistence + static final String XML_TAG_BATTERY_USAGE_STATS = "battery_usage_stats"; + static final String XML_TAG_AGGREGATE = "aggregate"; + static final String XML_TAG_UID = "uid"; + static final String XML_TAG_USER = "user"; + static final String XML_TAG_POWER_COMPONENTS = "power_components"; + static final String XML_TAG_COMPONENT = "component"; + static final String XML_TAG_CUSTOM_COMPONENT = "custom_component"; + static final String XML_ATTR_ID = "id"; + static final String XML_ATTR_UID = "uid"; + static final String XML_ATTR_USER_ID = "user_id"; + static final String XML_ATTR_SCOPE = "scope"; + static final String XML_ATTR_PREFIX_CUSTOM_COMPONENT = "custom_component_"; + static final String XML_ATTR_START_TIMESTAMP = "start_timestamp"; + static final String XML_ATTR_END_TIMESTAMP = "end_timestamp"; + static final String XML_ATTR_POWER = "power"; + static final String XML_ATTR_DURATION = "duration"; + static final String XML_ATTR_MODEL = "model"; + static final String XML_ATTR_BATTERY_CAPACITY = "battery_capacity"; + static final String XML_ATTR_DISCHARGE_PERCENT = "discharge_pct"; + static final String XML_ATTR_DISCHARGE_LOWER = "discharge_lower"; + static final String XML_ATTR_DISCHARGE_UPPER = "discharge_upper"; + static final String XML_ATTR_BATTERY_REMAINING = "battery_remaining"; + static final String XML_ATTR_CHARGE_REMAINING = "charge_remaining"; + static final String XML_ATTR_HIGHEST_DRAIN_PACKAGE = "highest_drain_package"; + static final String XML_ATTR_TIME_IN_FOREGROUND = "time_in_foreground"; + static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background"; + private final int mDischargePercentage; private final double mBatteryCapacityMah; private final long mStatsStartTimestampMs; @@ -526,6 +560,109 @@ public final class BatteryUsageStats implements Parcelable { } } + /** Serializes this object to XML */ + public void writeXml(TypedXmlSerializer serializer) throws IOException { + serializer.startTag(null, XML_TAG_BATTERY_USAGE_STATS); + + for (int i = 0; i < mCustomPowerComponentNames.length; i++) { + serializer.attribute(null, XML_ATTR_PREFIX_CUSTOM_COMPONENT + i, + mCustomPowerComponentNames[i]); + } + + serializer.attributeLong(null, XML_ATTR_START_TIMESTAMP, mStatsStartTimestampMs); + serializer.attributeLong(null, XML_ATTR_END_TIMESTAMP, mStatsEndTimestampMs); + serializer.attributeLong(null, XML_ATTR_DURATION, mStatsDurationMs); + serializer.attributeDouble(null, XML_ATTR_BATTERY_CAPACITY, mBatteryCapacityMah); + serializer.attributeInt(null, XML_ATTR_DISCHARGE_PERCENT, mDischargePercentage); + serializer.attributeDouble(null, XML_ATTR_DISCHARGE_LOWER, mDischargedPowerLowerBound); + serializer.attributeDouble(null, XML_ATTR_DISCHARGE_UPPER, mDischargedPowerUpperBound); + serializer.attributeLong(null, XML_ATTR_BATTERY_REMAINING, mBatteryTimeRemainingMs); + serializer.attributeLong(null, XML_ATTR_CHARGE_REMAINING, mChargeTimeRemainingMs); + + for (int scope = 0; scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; + scope++) { + mAggregateBatteryConsumers[scope].writeToXml(serializer, scope); + } + for (UidBatteryConsumer consumer : mUidBatteryConsumers) { + consumer.writeToXml(serializer); + } + for (UserBatteryConsumer consumer : mUserBatteryConsumers) { + consumer.writeToXml(serializer); + } + serializer.endTag(null, XML_TAG_BATTERY_USAGE_STATS); + } + + /** Parses an XML representation of BatteryUsageStats */ + public static BatteryUsageStats createFromXml(TypedXmlPullParser parser) + throws XmlPullParserException, IOException { + Builder builder = null; + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG + && parser.getName().equals(XML_TAG_BATTERY_USAGE_STATS)) { + List customComponentNames = new ArrayList<>(); + int i = 0; + while (true) { + int index = parser.getAttributeIndex(null, + XML_ATTR_PREFIX_CUSTOM_COMPONENT + i); + if (index == -1) { + break; + } + customComponentNames.add(parser.getAttributeValue(index)); + i++; + } + + builder = new Builder( + customComponentNames.toArray(new String[0]), true); + + builder.setStatsStartTimestamp( + parser.getAttributeLong(null, XML_ATTR_START_TIMESTAMP)); + builder.setStatsEndTimestamp( + parser.getAttributeLong(null, XML_ATTR_END_TIMESTAMP)); + builder.setStatsDuration( + parser.getAttributeLong(null, XML_ATTR_DURATION)); + builder.setBatteryCapacity( + parser.getAttributeDouble(null, XML_ATTR_BATTERY_CAPACITY)); + builder.setDischargePercentage( + parser.getAttributeInt(null, XML_ATTR_DISCHARGE_PERCENT)); + builder.setDischargedPowerRange( + parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_LOWER), + parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_UPPER)); + builder.setBatteryTimeRemainingMs( + parser.getAttributeLong(null, XML_ATTR_BATTERY_REMAINING)); + builder.setChargeTimeRemainingMs( + parser.getAttributeLong(null, XML_ATTR_CHARGE_REMAINING)); + + eventType = parser.next(); + break; + } + eventType = parser.next(); + } + + if (builder == null) { + throw new XmlPullParserException("No root element"); + } + + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + switch (parser.getName()) { + case XML_TAG_AGGREGATE: + AggregateBatteryConsumer.parseXml(parser, builder); + break; + case XML_TAG_UID: + UidBatteryConsumer.createFromXml(parser, builder); + break; + case XML_TAG_USER: + UserBatteryConsumer.createFromXml(parser, builder); + break; + } + } + eventType = parser.next(); + } + + return builder.build(); + } + /** * Builder for BatteryUsageStats. */ diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index 39a8501b3342f..db3d13bdb07bd 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -19,10 +19,16 @@ import static android.os.BatteryConsumer.convertMahToDeciCoulombs; import android.annotation.NonNull; import android.annotation.Nullable; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.os.PowerCalculator; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; @@ -302,6 +308,122 @@ class PowerComponents { return interestingData; } + void writeToXml(TypedXmlSerializer serializer) throws IOException { + serializer.startTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS); + for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT; + componentId++) { + final double powerMah = getConsumedPower(componentId); + final long durationMs = getUsageDurationMillis(componentId); + if (powerMah == 0 && durationMs == 0) { + continue; + } + + serializer.startTag(null, BatteryUsageStats.XML_TAG_COMPONENT); + serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId); + if (powerMah != 0) { + serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah); + } + if (durationMs != 0) { + serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs); + } + if (mPowerModels != null) { + serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_MODEL, + mPowerModels[componentId]); + } + serializer.endTag(null, BatteryUsageStats.XML_TAG_COMPONENT); + } + + final int customComponentEnd = + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + mCustomPowerComponentCount; + for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID; + componentId < customComponentEnd; + componentId++) { + final double powerMah = getConsumedPowerForCustomComponent(componentId); + final long durationMs = getUsageDurationForCustomComponentMillis(componentId); + if (powerMah == 0 && durationMs == 0) { + continue; + } + + serializer.startTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT); + serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId); + if (powerMah != 0) { + serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah); + } + if (durationMs != 0) { + serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs); + } + serializer.endTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT); + } + + serializer.endTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS); + } + + + static void parseXml(TypedXmlPullParser parser, PowerComponents.Builder builder) + throws XmlPullParserException, IOException { + int eventType = parser.getEventType(); + if (eventType != XmlPullParser.START_TAG || !parser.getName().equals( + BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { + throw new XmlPullParserException("Invalid XML parser state"); + } + + while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals( + BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) + && eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + switch (parser.getName()) { + case BatteryUsageStats.XML_TAG_COMPONENT: { + int componentId = -1; + double powerMah = 0; + long durationMs = 0; + int model = BatteryConsumer.POWER_MODEL_UNDEFINED; + for (int i = 0; i < parser.getAttributeCount(); i++) { + switch (parser.getAttributeName(i)) { + case BatteryUsageStats.XML_ATTR_ID: + componentId = parser.getAttributeInt(i); + break; + case BatteryUsageStats.XML_ATTR_POWER: + powerMah = parser.getAttributeDouble(i); + break; + case BatteryUsageStats.XML_ATTR_DURATION: + durationMs = parser.getAttributeLong(i); + break; + case BatteryUsageStats.XML_ATTR_MODEL: + model = parser.getAttributeInt(i); + break; + } + } + builder.setConsumedPower(componentId, powerMah, model); + builder.setUsageDurationMillis(componentId, durationMs); + break; + } + case BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT: { + int componentId = -1; + double powerMah = 0; + long durationMs = 0; + for (int i = 0; i < parser.getAttributeCount(); i++) { + switch (parser.getAttributeName(i)) { + case BatteryUsageStats.XML_ATTR_ID: + componentId = parser.getAttributeInt(i); + break; + case BatteryUsageStats.XML_ATTR_POWER: + powerMah = parser.getAttributeDouble(i); + break; + case BatteryUsageStats.XML_ATTR_DURATION: + durationMs = parser.getAttributeLong(i); + break; + } + } + builder.setConsumedPowerForCustomComponent(componentId, powerMah); + builder.setUsageDurationForCustomComponentMillis(componentId, durationMs); + break; + } + } + } + eventType = parser.next(); + } + } + /** * Builder for PowerComponents. */ diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index 0268ca8713835..bfc4f73835d9b 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -20,9 +20,15 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.text.TextUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.os.PowerCalculator; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -144,6 +150,57 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela return 0; } + /** Serializes this object to XML */ + void writeToXml(TypedXmlSerializer serializer) throws IOException { + if (getConsumedPower() == 0) { + return; + } + + serializer.startTag(null, BatteryUsageStats.XML_TAG_UID); + serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_UID, getUid()); + if (!TextUtils.isEmpty(mPackageWithHighestDrain)) { + serializer.attribute(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE, + mPackageWithHighestDrain); + } + serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND, + mTimeInForegroundMs); + serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND, + mTimeInBackgroundMs); + mPowerComponents.writeToXml(serializer); + serializer.endTag(null, BatteryUsageStats.XML_TAG_UID); + } + + /** Parses an XML representation and populates the BatteryUsageStats builder */ + static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder) + throws XmlPullParserException, IOException { + final int uid = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_UID); + final UidBatteryConsumer.Builder consumerBuilder = + builder.getOrCreateUidBatteryConsumerBuilder(uid); + + int eventType = parser.getEventType(); + if (eventType != XmlPullParser.START_TAG + || !parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) { + throw new XmlPullParserException("Invalid XML parser state"); + } + + consumerBuilder.setPackageWithHighestDrain( + parser.getAttributeValue(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE)); + consumerBuilder.setTimeInStateMs(STATE_FOREGROUND, + parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND)); + consumerBuilder.setTimeInStateMs(STATE_BACKGROUND, + parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND)); + while (!(eventType == XmlPullParser.END_TAG + && parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) + && eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { + PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder); + } + } + eventType = parser.next(); + } + } + /** * Builder for UidBatteryConsumer. */ diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java index 429d2c53a836b..b508a8cd98aec 100644 --- a/core/java/android/os/UserBatteryConsumer.java +++ b/core/java/android/os/UserBatteryConsumer.java @@ -17,9 +17,15 @@ package android.os; import android.annotation.NonNull; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.os.PowerCalculator; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -85,6 +91,42 @@ public class UserBatteryConsumer extends BatteryConsumer implements Parcelable { return 0; } + /** Serializes this object to XML */ + void writeToXml(TypedXmlSerializer serializer) throws IOException { + if (getConsumedPower() == 0) { + return; + } + + serializer.startTag(null, BatteryUsageStats.XML_TAG_USER); + serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_USER_ID, getUserId()); + mPowerComponents.writeToXml(serializer); + serializer.endTag(null, BatteryUsageStats.XML_TAG_USER); + } + + /** Parses an XML representation and populates the BatteryUsageStats builder */ + static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder) + throws XmlPullParserException, IOException { + final int userId = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_USER_ID); + final UserBatteryConsumer.Builder consumerBuilder = + builder.getOrCreateUserBatteryConsumerBuilder(userId); + + int eventType = parser.getEventType(); + if (eventType != XmlPullParser.START_TAG + || !parser.getName().equals(BatteryUsageStats.XML_TAG_USER)) { + throw new XmlPullParserException("Invalid XML parser state"); + } + while (!(eventType == XmlPullParser.END_TAG + && parser.getName().equals(BatteryUsageStats.XML_TAG_USER)) + && eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { + PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder); + } + } + eventType = parser.next(); + } + } + /** * Builder for UserBatteryConsumer. */ diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java index fcc970e1833a9..3e620c2bbec64 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java @@ -30,6 +30,9 @@ import android.os.BatteryUsageStats; import android.os.Parcel; import android.os.UidBatteryConsumer; import android.os.UserBatteryConsumer; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; +import android.util.Xml; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -37,8 +40,11 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -157,6 +163,24 @@ public class BatteryUsageStatsTest { assertThrows(IllegalArgumentException.class, () -> builder.add(stats)); } + @Test + public void testXml() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + TypedXmlSerializer serializer = Xml.newBinarySerializer(); + serializer.setOutput(out, StandardCharsets.UTF_8.name()); + serializer.startDocument(null, true); + final BatteryUsageStats stats = buildBatteryUsageStats1(true).build(); + stats.writeXml(serializer); + serializer.endDocument(); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + TypedXmlPullParser parser = Xml.newBinaryPullParser(); + parser.setInput(in, StandardCharsets.UTF_8.name()); + final BatteryUsageStats fromXml = BatteryUsageStats.createFromXml(parser); + + assertBatteryUsageStats1(fromXml, true); + } + private BatteryUsageStats.Builder buildBatteryUsageStats1(boolean includeUserBatteryConsumer) { final MockClocks clocks = new MockClocks(); final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);