| /* |
| * 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 com.android.modules.utils.TypedXmlPullParser; |
| import com.android.modules.utils.TypedXmlSerializer; |
| |
| 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 |
| */ |
| @android.ravenwood.annotation.RavenwoodKeepWholeClass |
| public final class UidBatteryConsumer extends BatteryConsumer { |
| |
| static final int CONSUMER_TYPE_UID = 1; |
| |
| @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. |
| */ |
| 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}, |
| * {@link android.app.ActivityManager#PROCESS_STATE_FOREGROUND_SERVICE}. |
| */ |
| public static final int STATE_BACKGROUND = 1; |
| |
| static final int COLUMN_INDEX_UID = BatteryConsumer.COLUMN_COUNT; |
| static final int COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN = COLUMN_INDEX_UID + 1; |
| static final int COLUMN_INDEX_TIME_IN_FOREGROUND = COLUMN_INDEX_UID + 2; |
| static final int COLUMN_INDEX_TIME_IN_BACKGROUND = COLUMN_INDEX_UID + 3; |
| static final int COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE = COLUMN_INDEX_UID + 4; |
| static final int COLUMN_COUNT = BatteryConsumer.COLUMN_COUNT + 5; |
| |
| UidBatteryConsumer(BatteryConsumerData data) { |
| super(data); |
| } |
| |
| private UidBatteryConsumer(@NonNull Builder builder) { |
| super(builder.mData, builder.mPowerComponentsBuilder.build()); |
| } |
| |
| public int getUid() { |
| return mData.getInt(COLUMN_INDEX_UID); |
| } |
| |
| @Nullable |
| public String getPackageWithHighestDrain() { |
| return mData.getString(COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN); |
| } |
| |
| /** |
| * Returns the amount of time in milliseconds this UID spent in the specified state. |
| * @deprecated use {@link #getTimeInProcessStateMs} instead. |
| */ |
| @Deprecated |
| public long getTimeInStateMs(@State int state) { |
| switch (state) { |
| case STATE_BACKGROUND: |
| return mData.getInt(COLUMN_INDEX_TIME_IN_BACKGROUND) |
| + mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE); |
| case STATE_FOREGROUND: |
| return mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND); |
| } |
| return 0; |
| } |
| |
| /** |
| * Returns the amount of time in milliseconds this UID spent in the specified process state. |
| */ |
| public long getTimeInProcessStateMs(@ProcessState int state) { |
| switch (state) { |
| case PROCESS_STATE_BACKGROUND: |
| return mData.getInt(COLUMN_INDEX_TIME_IN_BACKGROUND); |
| case PROCESS_STATE_FOREGROUND: |
| return mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND); |
| case PROCESS_STATE_FOREGROUND_SERVICE: |
| return mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE); |
| } |
| return 0; |
| } |
| |
| @Override |
| public void dump(PrintWriter pw, boolean skipEmptyComponents) { |
| pw.print("UID "); |
| UserHandle.formatUid(pw, getUid()); |
| pw.print(": "); |
| pw.print(BatteryStats.formatCharge(getConsumedPower())); |
| |
| if (mData.layout.processStateDataIncluded) { |
| StringBuilder sb = new StringBuilder(); |
| appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_FOREGROUND, |
| skipEmptyComponents); |
| appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_BACKGROUND, |
| skipEmptyComponents); |
| appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, |
| skipEmptyComponents); |
| appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_CACHED, |
| skipEmptyComponents); |
| pw.print(sb); |
| } |
| |
| pw.print(" ( "); |
| mPowerComponents.dump(pw, skipEmptyComponents /* skipTotalPowerComponent */); |
| pw.print(" ) "); |
| } |
| |
| private void appendProcessStateData(StringBuilder sb, @ProcessState int processState, |
| boolean skipEmptyComponents) { |
| Dimensions dimensions = new Dimensions(POWER_COMPONENT_ANY, processState); |
| final double power = mPowerComponents.getConsumedPower(dimensions); |
| if (power == 0 && skipEmptyComponents) { |
| return; |
| } |
| |
| sb.append(" ").append(processStateToString(processState)).append(": ") |
| .append(BatteryStats.formatCharge(power)); |
| } |
| |
| static UidBatteryConsumer create(BatteryConsumerData data) { |
| return new UidBatteryConsumer(data); |
| } |
| |
| /** 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()); |
| final String packageWithHighestDrain = getPackageWithHighestDrain(); |
| if (!TextUtils.isEmpty(packageWithHighestDrain)) { |
| serializer.attribute(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE, |
| packageWithHighestDrain); |
| } |
| serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND, |
| getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND)); |
| serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND, |
| getTimeInProcessStateMs(PROCESS_STATE_BACKGROUND)); |
| serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND_SERVICE, |
| getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE)); |
| 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.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND, |
| parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND)); |
| consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND, |
| parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND)); |
| consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE, |
| parser.getAttributeLong(null, |
| BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND_SERVICE)); |
| 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. |
| */ |
| @android.ravenwood.annotation.RavenwoodKeepWholeClass |
| public static final class Builder extends BaseBuilder<Builder> { |
| private static final String PACKAGE_NAME_UNINITIALIZED = ""; |
| private final BatteryStats.Uid mBatteryStatsUid; |
| private final int mUid; |
| private final boolean mIsVirtualUid; |
| private String mPackageWithHighestDrain = PACKAGE_NAME_UNINITIALIZED; |
| private boolean mExcludeFromBatteryUsageStats; |
| |
| public Builder(BatteryConsumerData data, @NonNull BatteryStats.Uid batteryStatsUid, |
| double minConsumedPowerThreshold) { |
| this(data, batteryStatsUid, batteryStatsUid.getUid(), minConsumedPowerThreshold); |
| } |
| |
| public Builder(BatteryConsumerData data, int uid, double minConsumedPowerThreshold) { |
| this(data, null, uid, minConsumedPowerThreshold); |
| } |
| |
| private Builder(BatteryConsumerData data, @Nullable BatteryStats.Uid batteryStatsUid, |
| int uid, double minConsumedPowerThreshold) { |
| super(data, CONSUMER_TYPE_UID, minConsumedPowerThreshold); |
| mBatteryStatsUid = batteryStatsUid; |
| mUid = uid; |
| mIsVirtualUid = mUid == Process.SDK_SANDBOX_VIRTUAL_UID; |
| data.putLong(COLUMN_INDEX_UID, mUid); |
| } |
| |
| @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; |
| } |
| |
| public boolean isVirtualUid() { |
| return mIsVirtualUid; |
| } |
| |
| /** |
| * 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. |
| * @deprecated use {@link #setTimeInProcessStateMs} instead. |
| */ |
| @Deprecated |
| @NonNull |
| public Builder setTimeInStateMs(@State int state, long timeInStateMs) { |
| switch (state) { |
| case STATE_FOREGROUND: |
| mData.putLong(COLUMN_INDEX_TIME_IN_FOREGROUND, timeInStateMs); |
| break; |
| case STATE_BACKGROUND: |
| mData.putLong(COLUMN_INDEX_TIME_IN_BACKGROUND, timeInStateMs); |
| break; |
| default: |
| throw new IllegalArgumentException("Unsupported state: " + state); |
| } |
| return this; |
| } |
| |
| /** |
| * Sets the duration, in milliseconds, that this UID was active in a particular process |
| * state, such as foreground service. |
| */ |
| @NonNull |
| public Builder setTimeInProcessStateMs(@ProcessState int state, long timeInProcessStateMs) { |
| switch (state) { |
| case PROCESS_STATE_FOREGROUND: |
| mData.putLong(COLUMN_INDEX_TIME_IN_FOREGROUND, timeInProcessStateMs); |
| break; |
| case PROCESS_STATE_BACKGROUND: |
| mData.putLong(COLUMN_INDEX_TIME_IN_BACKGROUND, timeInProcessStateMs); |
| break; |
| case PROCESS_STATE_FOREGROUND_SERVICE: |
| mData.putLong(COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE, timeInProcessStateMs); |
| break; |
| default: |
| throw new IllegalArgumentException("Unsupported process 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); |
| |
| setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND, |
| mData.getLong(COLUMN_INDEX_TIME_IN_FOREGROUND) |
| + consumer.getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND)); |
| setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND, |
| mData.getLong(COLUMN_INDEX_TIME_IN_BACKGROUND) |
| + consumer.getTimeInProcessStateMs(PROCESS_STATE_BACKGROUND)); |
| setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE, |
| mData.getLong(COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE) |
| + consumer.getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE)); |
| |
| if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) { |
| mPackageWithHighestDrain = consumer.getPackageWithHighestDrain(); |
| } else if (!TextUtils.equals(mPackageWithHighestDrain, |
| consumer.getPackageWithHighestDrain())) { |
| // 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; |
| } |
| if (mPackageWithHighestDrain != null) { |
| mData.putString(COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN, mPackageWithHighestDrain); |
| } |
| return new UidBatteryConsumer(this); |
| } |
| } |
| } |