1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.os; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.text.TextUtils; 23 import android.util.TypedXmlPullParser; 24 import android.util.TypedXmlSerializer; 25 26 import com.android.internal.os.PowerCalculator; 27 28 import org.xmlpull.v1.XmlPullParser; 29 import org.xmlpull.v1.XmlPullParserException; 30 31 import java.io.IOException; 32 import java.io.PrintWriter; 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 36 /** 37 * Contains power consumption data attributed to a specific UID. 38 * 39 * @hide 40 */ 41 public final class UidBatteryConsumer extends BatteryConsumer { 42 43 static final int CONSUMER_TYPE_UID = 1; 44 45 @Retention(RetentionPolicy.SOURCE) 46 @IntDef({ 47 STATE_FOREGROUND, 48 STATE_BACKGROUND 49 }) 50 public @interface State { 51 } 52 53 /** 54 * The state of an application when it is either running a foreground (top) activity. 55 */ 56 public static final int STATE_FOREGROUND = 0; 57 58 /** 59 * The state of an application when it is running in the background, including the following 60 * states: 61 * 62 * {@link android.app.ActivityManager#PROCESS_STATE_IMPORTANT_BACKGROUND}, 63 * {@link android.app.ActivityManager#PROCESS_STATE_TRANSIENT_BACKGROUND}, 64 * {@link android.app.ActivityManager#PROCESS_STATE_BACKUP}, 65 * {@link android.app.ActivityManager#PROCESS_STATE_SERVICE}, 66 * {@link android.app.ActivityManager#PROCESS_STATE_RECEIVER}, 67 * {@link android.app.ActivityManager#PROCESS_STATE_FOREGROUND_SERVICE}. 68 */ 69 public static final int STATE_BACKGROUND = 1; 70 71 static final int COLUMN_INDEX_UID = BatteryConsumer.COLUMN_COUNT; 72 static final int COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN = COLUMN_INDEX_UID + 1; 73 static final int COLUMN_INDEX_TIME_IN_FOREGROUND = COLUMN_INDEX_UID + 2; 74 static final int COLUMN_INDEX_TIME_IN_BACKGROUND = COLUMN_INDEX_UID + 3; 75 static final int COLUMN_COUNT = BatteryConsumer.COLUMN_COUNT + 4; 76 UidBatteryConsumer(BatteryConsumerData data)77 UidBatteryConsumer(BatteryConsumerData data) { 78 super(data); 79 } 80 UidBatteryConsumer(@onNull Builder builder)81 private UidBatteryConsumer(@NonNull Builder builder) { 82 super(builder.mData, builder.mPowerComponentsBuilder.build()); 83 } 84 getUid()85 public int getUid() { 86 return mData.getInt(COLUMN_INDEX_UID); 87 } 88 89 @Nullable getPackageWithHighestDrain()90 public String getPackageWithHighestDrain() { 91 return mData.getString(COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN); 92 } 93 94 /** 95 * Returns the amount of time in milliseconds this UID spent in the specified state. 96 */ getTimeInStateMs(@tate int state)97 public long getTimeInStateMs(@State int state) { 98 switch (state) { 99 case STATE_BACKGROUND: 100 return mData.getInt(COLUMN_INDEX_TIME_IN_BACKGROUND); 101 case STATE_FOREGROUND: 102 return mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND); 103 } 104 return 0; 105 } 106 107 @Override dump(PrintWriter pw, boolean skipEmptyComponents)108 public void dump(PrintWriter pw, boolean skipEmptyComponents) { 109 pw.print("UID "); 110 UserHandle.formatUid(pw, getUid()); 111 pw.print(": "); 112 PowerCalculator.printPowerMah(pw, getConsumedPower()); 113 114 if (mData.layout.processStateDataIncluded) { 115 StringBuilder sb = new StringBuilder(); 116 appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_FOREGROUND, 117 skipEmptyComponents); 118 appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_BACKGROUND, 119 skipEmptyComponents); 120 appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 121 skipEmptyComponents); 122 appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_CACHED, 123 skipEmptyComponents); 124 pw.print(sb); 125 } 126 127 pw.print(" ( "); 128 mPowerComponents.dump(pw, skipEmptyComponents /* skipTotalPowerComponent */); 129 pw.print(" ) "); 130 } 131 appendProcessStateData(StringBuilder sb, @ProcessState int processState, boolean skipEmptyComponents)132 private void appendProcessStateData(StringBuilder sb, @ProcessState int processState, 133 boolean skipEmptyComponents) { 134 Dimensions dimensions = new Dimensions(POWER_COMPONENT_ANY, processState); 135 final double power = mPowerComponents.getConsumedPower(dimensions); 136 if (power == 0 && skipEmptyComponents) { 137 return; 138 } 139 140 sb.append(" ").append(processStateToString(processState)).append(": ") 141 .append(BatteryStats.formatCharge(power)); 142 } 143 create(BatteryConsumerData data)144 static UidBatteryConsumer create(BatteryConsumerData data) { 145 return new UidBatteryConsumer(data); 146 } 147 148 /** Serializes this object to XML */ writeToXml(TypedXmlSerializer serializer)149 void writeToXml(TypedXmlSerializer serializer) throws IOException { 150 if (getConsumedPower() == 0) { 151 return; 152 } 153 154 serializer.startTag(null, BatteryUsageStats.XML_TAG_UID); 155 serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_UID, getUid()); 156 final String packageWithHighestDrain = getPackageWithHighestDrain(); 157 if (!TextUtils.isEmpty(packageWithHighestDrain)) { 158 serializer.attribute(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE, 159 packageWithHighestDrain); 160 } 161 serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND, 162 getTimeInStateMs(STATE_FOREGROUND)); 163 serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND, 164 getTimeInStateMs(STATE_BACKGROUND)); 165 mPowerComponents.writeToXml(serializer); 166 serializer.endTag(null, BatteryUsageStats.XML_TAG_UID); 167 } 168 169 /** Parses an XML representation and populates the BatteryUsageStats builder */ createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)170 static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder) 171 throws XmlPullParserException, IOException { 172 final int uid = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_UID); 173 final UidBatteryConsumer.Builder consumerBuilder = 174 builder.getOrCreateUidBatteryConsumerBuilder(uid); 175 176 int eventType = parser.getEventType(); 177 if (eventType != XmlPullParser.START_TAG 178 || !parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) { 179 throw new XmlPullParserException("Invalid XML parser state"); 180 } 181 182 consumerBuilder.setPackageWithHighestDrain( 183 parser.getAttributeValue(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE)); 184 consumerBuilder.setTimeInStateMs(STATE_FOREGROUND, 185 parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND)); 186 consumerBuilder.setTimeInStateMs(STATE_BACKGROUND, 187 parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND)); 188 while (!(eventType == XmlPullParser.END_TAG 189 && parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) 190 && eventType != XmlPullParser.END_DOCUMENT) { 191 if (eventType == XmlPullParser.START_TAG) { 192 if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { 193 PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder); 194 } 195 } 196 eventType = parser.next(); 197 } 198 } 199 200 /** 201 * Builder for UidBatteryConsumer. 202 */ 203 public static final class Builder extends BaseBuilder<Builder> { 204 private static final String PACKAGE_NAME_UNINITIALIZED = ""; 205 private final BatteryStats.Uid mBatteryStatsUid; 206 private final int mUid; 207 private final boolean mIsVirtualUid; 208 private String mPackageWithHighestDrain = PACKAGE_NAME_UNINITIALIZED; 209 private boolean mExcludeFromBatteryUsageStats; 210 Builder(BatteryConsumerData data, @NonNull BatteryStats.Uid batteryStatsUid)211 public Builder(BatteryConsumerData data, @NonNull BatteryStats.Uid batteryStatsUid) { 212 this(data, batteryStatsUid, batteryStatsUid.getUid()); 213 } 214 Builder(BatteryConsumerData data, int uid)215 public Builder(BatteryConsumerData data, int uid) { 216 this(data, null, uid); 217 } 218 Builder(BatteryConsumerData data, @Nullable BatteryStats.Uid batteryStatsUid, int uid)219 private Builder(BatteryConsumerData data, @Nullable BatteryStats.Uid batteryStatsUid, 220 int uid) { 221 super(data, CONSUMER_TYPE_UID); 222 mBatteryStatsUid = batteryStatsUid; 223 mUid = uid; 224 mIsVirtualUid = mUid == Process.SDK_SANDBOX_VIRTUAL_UID; 225 data.putLong(COLUMN_INDEX_UID, mUid); 226 } 227 228 @NonNull getBatteryStatsUid()229 public BatteryStats.Uid getBatteryStatsUid() { 230 if (mBatteryStatsUid == null) { 231 throw new IllegalStateException( 232 "UidBatteryConsumer.Builder was initialized without a BatteryStats.Uid"); 233 } 234 return mBatteryStatsUid; 235 } 236 getUid()237 public int getUid() { 238 return mUid; 239 } 240 isVirtualUid()241 public boolean isVirtualUid() { 242 return mIsVirtualUid; 243 } 244 245 /** 246 * Sets the name of the package owned by this UID that consumed the highest amount 247 * of power since BatteryStats reset. 248 */ 249 @NonNull setPackageWithHighestDrain(@ullable String packageName)250 public Builder setPackageWithHighestDrain(@Nullable String packageName) { 251 mPackageWithHighestDrain = TextUtils.nullIfEmpty(packageName); 252 return this; 253 } 254 255 /** 256 * Sets the duration, in milliseconds, that this UID was active in a particular state, 257 * such as foreground or background. 258 */ 259 @NonNull setTimeInStateMs(@tate int state, long timeInStateMs)260 public Builder setTimeInStateMs(@State int state, long timeInStateMs) { 261 switch (state) { 262 case STATE_FOREGROUND: 263 mData.putLong(COLUMN_INDEX_TIME_IN_FOREGROUND, timeInStateMs); 264 break; 265 case STATE_BACKGROUND: 266 mData.putLong(COLUMN_INDEX_TIME_IN_BACKGROUND, timeInStateMs); 267 break; 268 default: 269 throw new IllegalArgumentException("Unsupported state: " + state); 270 } 271 return this; 272 } 273 274 /** 275 * Marks the UidBatteryConsumer for exclusion from the result set. 276 */ excludeFromBatteryUsageStats()277 public Builder excludeFromBatteryUsageStats() { 278 mExcludeFromBatteryUsageStats = true; 279 return this; 280 } 281 282 /** 283 * Adds power and usage duration from the supplied UidBatteryConsumer. 284 */ add(UidBatteryConsumer consumer)285 public Builder add(UidBatteryConsumer consumer) { 286 mPowerComponentsBuilder.addPowerAndDuration(consumer.mPowerComponents); 287 288 setTimeInStateMs(STATE_FOREGROUND, 289 mData.getLong(COLUMN_INDEX_TIME_IN_FOREGROUND) 290 + consumer.getTimeInStateMs(STATE_FOREGROUND)); 291 setTimeInStateMs(STATE_BACKGROUND, 292 mData.getLong(COLUMN_INDEX_TIME_IN_BACKGROUND) 293 + consumer.getTimeInStateMs(STATE_BACKGROUND)); 294 295 if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) { 296 mPackageWithHighestDrain = consumer.getPackageWithHighestDrain(); 297 } else if (!TextUtils.equals(mPackageWithHighestDrain, 298 consumer.getPackageWithHighestDrain())) { 299 // Consider combining two UidBatteryConsumers with this distribution 300 // of power drain between packages: 301 // (package1=100, package2=10) and (package1=100, package2=101). 302 // Since we don't know the actual power distribution between packages at this 303 // point, we have no way to correctly declare package1 as the winner. 304 // The naive logic of picking the consumer with the higher total consumed 305 // power would produce an incorrect result. 306 mPackageWithHighestDrain = null; 307 } 308 return this; 309 } 310 311 /** 312 * Returns true if this UidBatteryConsumer must be excluded from the 313 * BatteryUsageStats. 314 */ isExcludedFromBatteryUsageStats()315 public boolean isExcludedFromBatteryUsageStats() { 316 return mExcludeFromBatteryUsageStats; 317 } 318 319 /** 320 * Creates a read-only object out of the Builder values. 321 */ 322 @NonNull build()323 public UidBatteryConsumer build() { 324 if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) { 325 mPackageWithHighestDrain = null; 326 } 327 if (mPackageWithHighestDrain != null) { 328 mData.putString(COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN, mPackageWithHighestDrain); 329 } 330 return new UidBatteryConsumer(this); 331 } 332 } 333 } 334