387 lines
16 KiB
Java
387 lines
16 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
}
|
|
}
|