/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os; 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; /** * Contains power consumption data attributed to a specific UID. * * @hide */ public final class UidBatteryConsumer extends BatteryConsumer implements Parcelable { @Retention(RetentionPolicy.SOURCE) @IntDef({ STATE_FOREGROUND, STATE_BACKGROUND }) public @interface State { } /** * The state of an application when it is either running a foreground (top) activity * or a foreground service. */ public static final int STATE_FOREGROUND = 0; /** * The state of an application when it is running in the background, including the following * states: * * {@link android.app.ActivityManager#PROCESS_STATE_IMPORTANT_BACKGROUND}, * {@link android.app.ActivityManager#PROCESS_STATE_TRANSIENT_BACKGROUND}, * {@link android.app.ActivityManager#PROCESS_STATE_BACKUP}, * {@link android.app.ActivityManager#PROCESS_STATE_SERVICE}, * {@link android.app.ActivityManager#PROCESS_STATE_RECEIVER}. */ public static final int STATE_BACKGROUND = 1; private final int mUid; @Nullable private final String mPackageWithHighestDrain; private final long mTimeInForegroundMs; private final long mTimeInBackgroundMs; public int getUid() { return mUid; } @Nullable public String getPackageWithHighestDrain() { return mPackageWithHighestDrain; } /** * Returns the amount of time in milliseconds this UID spent in the specified state. */ public long getTimeInStateMs(@State int state) { switch (state) { case STATE_BACKGROUND: return mTimeInBackgroundMs; case STATE_FOREGROUND: return mTimeInForegroundMs; } return 0; } private UidBatteryConsumer(@NonNull Builder builder) { super(builder.mPowerComponentsBuilder.build()); mUid = builder.mUid; mPackageWithHighestDrain = builder.mPackageWithHighestDrain; mTimeInForegroundMs = builder.mTimeInForegroundMs; mTimeInBackgroundMs = builder.mTimeInBackgroundMs; } private UidBatteryConsumer(@NonNull Parcel source) { super(new PowerComponents(source)); mUid = source.readInt(); mPackageWithHighestDrain = source.readString(); mTimeInForegroundMs = source.readLong(); mTimeInBackgroundMs = source.readLong(); } /** * Writes the contents into a Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(mUid); dest.writeString(mPackageWithHighestDrain); dest.writeLong(mTimeInForegroundMs); dest.writeLong(mTimeInBackgroundMs); } @Override public void dump(PrintWriter pw, boolean skipEmptyComponents) { final double consumedPower = getConsumedPower(); pw.print("UID "); UserHandle.formatUid(pw, getUid()); pw.print(": "); PowerCalculator.printPowerMah(pw, consumedPower); pw.print(" ( "); mPowerComponents.dump(pw, skipEmptyComponents /* skipTotalPowerComponent */); pw.print(" ) "); } @NonNull public static final Creator CREATOR = new Creator() { public UidBatteryConsumer createFromParcel(@NonNull Parcel source) { return new UidBatteryConsumer(source); } public UidBatteryConsumer[] newArray(int size) { return new UidBatteryConsumer[size]; } }; @Override public int describeContents() { 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. */ public static final class Builder extends BaseBuilder { private static final String PACKAGE_NAME_UNINITIALIZED = ""; private final BatteryStats.Uid mBatteryStatsUid; private final int mUid; private String mPackageWithHighestDrain = PACKAGE_NAME_UNINITIALIZED; public long mTimeInForegroundMs; public long mTimeInBackgroundMs; private boolean mExcludeFromBatteryUsageStats; public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels, @NonNull BatteryStats.Uid batteryStatsUid) { super(customPowerComponentNames, includePowerModels); mBatteryStatsUid = batteryStatsUid; mUid = batteryStatsUid.getUid(); } public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels, int uid) { super(customPowerComponentNames, includePowerModels); mBatteryStatsUid = null; mUid = uid; } @NonNull public BatteryStats.Uid getBatteryStatsUid() { if (mBatteryStatsUid == null) { throw new IllegalStateException( "UidBatteryConsumer.Builder was initialized without a BatteryStats.Uid"); } return mBatteryStatsUid; } public int getUid() { return mUid; } /** * Sets the name of the package owned by this UID that consumed the highest amount * of power since BatteryStats reset. */ @NonNull public Builder setPackageWithHighestDrain(@Nullable String packageName) { mPackageWithHighestDrain = TextUtils.nullIfEmpty(packageName); return this; } /** * Sets the duration, in milliseconds, that this UID was active in a particular state, * such as foreground or background. */ @NonNull public Builder setTimeInStateMs(@State int state, long timeInStateMs) { switch (state) { case STATE_FOREGROUND: mTimeInForegroundMs = timeInStateMs; break; case STATE_BACKGROUND: mTimeInBackgroundMs = timeInStateMs; break; default: throw new IllegalArgumentException("Unsupported state: " + state); } return this; } /** * Marks the UidBatteryConsumer for exclusion from the result set. */ public Builder excludeFromBatteryUsageStats() { mExcludeFromBatteryUsageStats = true; return this; } /** * Adds power and usage duration from the supplied UidBatteryConsumer. */ public Builder add(UidBatteryConsumer consumer) { mPowerComponentsBuilder.addPowerAndDuration(consumer.mPowerComponents); mTimeInBackgroundMs += consumer.mTimeInBackgroundMs; mTimeInForegroundMs += consumer.mTimeInForegroundMs; if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) { mPackageWithHighestDrain = consumer.mPackageWithHighestDrain; } else if (!TextUtils.equals(mPackageWithHighestDrain, consumer.mPackageWithHighestDrain)) { // Consider combining two UidBatteryConsumers with this distribution // of power drain between packages: // (package1=100, package2=10) and (package1=100, package2=101). // Since we don't know the actual power distribution between packages at this // point, we have no way to correctly declare package1 as the winner. // The naive logic of picking the consumer with the higher total consumed // power would produce an incorrect result. mPackageWithHighestDrain = null; } return this; } /** * Returns true if this UidBatteryConsumer must be excluded from the * BatteryUsageStats. */ public boolean isExcludedFromBatteryUsageStats() { return mExcludeFromBatteryUsageStats; } /** * Creates a read-only object out of the Builder values. */ @NonNull public UidBatteryConsumer build() { if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) { mPackageWithHighestDrain = null; } return new UidBatteryConsumer(this); } } }