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 24 import com.android.modules.utils.TypedXmlPullParser; 25 import com.android.modules.utils.TypedXmlSerializer; 26 27 import org.xmlpull.v1.XmlPullParser; 28 import org.xmlpull.v1.XmlPullParserException; 29 30 import java.io.IOException; 31 import java.io.PrintWriter; 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 35 /** 36 * Contains power consumption data attributed to a specific UID. 37 * 38 * @hide 39 */ 40 @android.ravenwood.annotation.RavenwoodKeepWholeClass 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_INDEX_TIME_IN_FOREGROUND_SERVICE = COLUMN_INDEX_UID + 4; 76 static final int COLUMN_COUNT = BatteryConsumer.COLUMN_COUNT + 5; 77 UidBatteryConsumer(BatteryConsumerData data)78 UidBatteryConsumer(BatteryConsumerData data) { 79 super(data); 80 } 81 UidBatteryConsumer(@onNull Builder builder)82 private UidBatteryConsumer(@NonNull Builder builder) { 83 super(builder.mData, builder.mPowerComponentsBuilder.build()); 84 } 85 getUid()86 public int getUid() { 87 return mData.getInt(COLUMN_INDEX_UID); 88 } 89 90 @Nullable getPackageWithHighestDrain()91 public String getPackageWithHighestDrain() { 92 return mData.getString(COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN); 93 } 94 95 /** 96 * Returns the amount of time in milliseconds this UID spent in the specified state. 97 * @deprecated use {@link #getTimeInProcessStateMs} instead. 98 */ 99 @Deprecated getTimeInStateMs(@tate int state)100 public long getTimeInStateMs(@State int state) { 101 switch (state) { 102 case STATE_BACKGROUND: 103 return mData.getInt(COLUMN_INDEX_TIME_IN_BACKGROUND) 104 + mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE); 105 case STATE_FOREGROUND: 106 return mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND); 107 } 108 return 0; 109 } 110 111 /** 112 * Returns the amount of time in milliseconds this UID spent in the specified process state. 113 */ getTimeInProcessStateMs(@rocessState int state)114 public long getTimeInProcessStateMs(@ProcessState int state) { 115 switch (state) { 116 case PROCESS_STATE_BACKGROUND: 117 return mData.getInt(COLUMN_INDEX_TIME_IN_BACKGROUND); 118 case PROCESS_STATE_FOREGROUND: 119 return mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND); 120 case PROCESS_STATE_FOREGROUND_SERVICE: 121 return mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE); 122 } 123 return 0; 124 } 125 126 @Override dump(PrintWriter pw, boolean skipEmptyComponents)127 public void dump(PrintWriter pw, boolean skipEmptyComponents) { 128 pw.print("UID "); 129 UserHandle.formatUid(pw, getUid()); 130 pw.print(": "); 131 pw.print(BatteryStats.formatCharge(getConsumedPower())); 132 133 if (mData.layout.processStateDataIncluded) { 134 StringBuilder sb = new StringBuilder(); 135 appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_FOREGROUND, 136 skipEmptyComponents); 137 appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_BACKGROUND, 138 skipEmptyComponents); 139 appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 140 skipEmptyComponents); 141 appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_CACHED, 142 skipEmptyComponents); 143 pw.print(sb); 144 } 145 146 pw.print(" ( "); 147 mPowerComponents.dump(pw, skipEmptyComponents /* skipTotalPowerComponent */); 148 pw.print(" ) "); 149 } 150 appendProcessStateData(StringBuilder sb, @ProcessState int processState, boolean skipEmptyComponents)151 private void appendProcessStateData(StringBuilder sb, @ProcessState int processState, 152 boolean skipEmptyComponents) { 153 Dimensions dimensions = new Dimensions(POWER_COMPONENT_ANY, processState); 154 final double power = mPowerComponents.getConsumedPower(dimensions); 155 if (power == 0 && skipEmptyComponents) { 156 return; 157 } 158 159 sb.append(" ").append(processStateToString(processState)).append(": ") 160 .append(BatteryStats.formatCharge(power)); 161 } 162 create(BatteryConsumerData data)163 static UidBatteryConsumer create(BatteryConsumerData data) { 164 return new UidBatteryConsumer(data); 165 } 166 167 /** Serializes this object to XML */ writeToXml(TypedXmlSerializer serializer)168 void writeToXml(TypedXmlSerializer serializer) throws IOException { 169 if (getConsumedPower() == 0) { 170 return; 171 } 172 173 serializer.startTag(null, BatteryUsageStats.XML_TAG_UID); 174 serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_UID, getUid()); 175 final String packageWithHighestDrain = getPackageWithHighestDrain(); 176 if (!TextUtils.isEmpty(packageWithHighestDrain)) { 177 serializer.attribute(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE, 178 packageWithHighestDrain); 179 } 180 serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND, 181 getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND)); 182 serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND, 183 getTimeInProcessStateMs(PROCESS_STATE_BACKGROUND)); 184 serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND_SERVICE, 185 getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE)); 186 mPowerComponents.writeToXml(serializer); 187 serializer.endTag(null, BatteryUsageStats.XML_TAG_UID); 188 } 189 190 /** Parses an XML representation and populates the BatteryUsageStats builder */ createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)191 static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder) 192 throws XmlPullParserException, IOException { 193 final int uid = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_UID); 194 final UidBatteryConsumer.Builder consumerBuilder = 195 builder.getOrCreateUidBatteryConsumerBuilder(uid); 196 197 int eventType = parser.getEventType(); 198 if (eventType != XmlPullParser.START_TAG 199 || !parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) { 200 throw new XmlPullParserException("Invalid XML parser state"); 201 } 202 203 consumerBuilder.setPackageWithHighestDrain( 204 parser.getAttributeValue(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE)); 205 consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND, 206 parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND)); 207 consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND, 208 parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND)); 209 consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE, 210 parser.getAttributeLong(null, 211 BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND_SERVICE)); 212 while (!(eventType == XmlPullParser.END_TAG 213 && parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) 214 && eventType != XmlPullParser.END_DOCUMENT) { 215 if (eventType == XmlPullParser.START_TAG) { 216 if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { 217 PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder); 218 } 219 } 220 eventType = parser.next(); 221 } 222 } 223 224 /** 225 * Builder for UidBatteryConsumer. 226 */ 227 @android.ravenwood.annotation.RavenwoodKeepWholeClass 228 public static final class Builder extends BaseBuilder<Builder> { 229 private static final String PACKAGE_NAME_UNINITIALIZED = ""; 230 private final BatteryStats.Uid mBatteryStatsUid; 231 private final int mUid; 232 private final boolean mIsVirtualUid; 233 private String mPackageWithHighestDrain = PACKAGE_NAME_UNINITIALIZED; 234 private boolean mExcludeFromBatteryUsageStats; 235 Builder(BatteryConsumerData data, @NonNull BatteryStats.Uid batteryStatsUid, double minConsumedPowerThreshold)236 public Builder(BatteryConsumerData data, @NonNull BatteryStats.Uid batteryStatsUid, 237 double minConsumedPowerThreshold) { 238 this(data, batteryStatsUid, batteryStatsUid.getUid(), minConsumedPowerThreshold); 239 } 240 Builder(BatteryConsumerData data, int uid, double minConsumedPowerThreshold)241 public Builder(BatteryConsumerData data, int uid, double minConsumedPowerThreshold) { 242 this(data, null, uid, minConsumedPowerThreshold); 243 } 244 Builder(BatteryConsumerData data, @Nullable BatteryStats.Uid batteryStatsUid, int uid, double minConsumedPowerThreshold)245 private Builder(BatteryConsumerData data, @Nullable BatteryStats.Uid batteryStatsUid, 246 int uid, double minConsumedPowerThreshold) { 247 super(data, CONSUMER_TYPE_UID, minConsumedPowerThreshold); 248 mBatteryStatsUid = batteryStatsUid; 249 mUid = uid; 250 mIsVirtualUid = mUid == Process.SDK_SANDBOX_VIRTUAL_UID; 251 data.putLong(COLUMN_INDEX_UID, mUid); 252 } 253 254 @NonNull getBatteryStatsUid()255 public BatteryStats.Uid getBatteryStatsUid() { 256 if (mBatteryStatsUid == null) { 257 throw new IllegalStateException( 258 "UidBatteryConsumer.Builder was initialized without a BatteryStats.Uid"); 259 } 260 return mBatteryStatsUid; 261 } 262 getUid()263 public int getUid() { 264 return mUid; 265 } 266 isVirtualUid()267 public boolean isVirtualUid() { 268 return mIsVirtualUid; 269 } 270 271 /** 272 * Sets the name of the package owned by this UID that consumed the highest amount 273 * of power since BatteryStats reset. 274 */ 275 @NonNull setPackageWithHighestDrain(@ullable String packageName)276 public Builder setPackageWithHighestDrain(@Nullable String packageName) { 277 mPackageWithHighestDrain = TextUtils.nullIfEmpty(packageName); 278 return this; 279 } 280 281 /** 282 * Sets the duration, in milliseconds, that this UID was active in a particular state, 283 * such as foreground or background. 284 * @deprecated use {@link #setTimeInProcessStateMs} instead. 285 */ 286 @Deprecated 287 @NonNull setTimeInStateMs(@tate int state, long timeInStateMs)288 public Builder setTimeInStateMs(@State int state, long timeInStateMs) { 289 switch (state) { 290 case STATE_FOREGROUND: 291 mData.putLong(COLUMN_INDEX_TIME_IN_FOREGROUND, timeInStateMs); 292 break; 293 case STATE_BACKGROUND: 294 mData.putLong(COLUMN_INDEX_TIME_IN_BACKGROUND, timeInStateMs); 295 break; 296 default: 297 throw new IllegalArgumentException("Unsupported state: " + state); 298 } 299 return this; 300 } 301 302 /** 303 * Sets the duration, in milliseconds, that this UID was active in a particular process 304 * state, such as foreground service. 305 */ 306 @NonNull setTimeInProcessStateMs(@rocessState int state, long timeInProcessStateMs)307 public Builder setTimeInProcessStateMs(@ProcessState int state, long timeInProcessStateMs) { 308 switch (state) { 309 case PROCESS_STATE_FOREGROUND: 310 mData.putLong(COLUMN_INDEX_TIME_IN_FOREGROUND, timeInProcessStateMs); 311 break; 312 case PROCESS_STATE_BACKGROUND: 313 mData.putLong(COLUMN_INDEX_TIME_IN_BACKGROUND, timeInProcessStateMs); 314 break; 315 case PROCESS_STATE_FOREGROUND_SERVICE: 316 mData.putLong(COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE, timeInProcessStateMs); 317 break; 318 default: 319 throw new IllegalArgumentException("Unsupported process state: " + state); 320 } 321 return this; 322 } 323 324 /** 325 * Marks the UidBatteryConsumer for exclusion from the result set. 326 */ excludeFromBatteryUsageStats()327 public Builder excludeFromBatteryUsageStats() { 328 mExcludeFromBatteryUsageStats = true; 329 return this; 330 } 331 332 /** 333 * Adds power and usage duration from the supplied UidBatteryConsumer. 334 */ add(UidBatteryConsumer consumer)335 public Builder add(UidBatteryConsumer consumer) { 336 mPowerComponentsBuilder.addPowerAndDuration(consumer.mPowerComponents); 337 338 setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND, 339 mData.getLong(COLUMN_INDEX_TIME_IN_FOREGROUND) 340 + consumer.getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND)); 341 setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND, 342 mData.getLong(COLUMN_INDEX_TIME_IN_BACKGROUND) 343 + consumer.getTimeInProcessStateMs(PROCESS_STATE_BACKGROUND)); 344 setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE, 345 mData.getLong(COLUMN_INDEX_TIME_IN_FOREGROUND_SERVICE) 346 + consumer.getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE)); 347 348 if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) { 349 mPackageWithHighestDrain = consumer.getPackageWithHighestDrain(); 350 } else if (!TextUtils.equals(mPackageWithHighestDrain, 351 consumer.getPackageWithHighestDrain())) { 352 // Consider combining two UidBatteryConsumers with this distribution 353 // of power drain between packages: 354 // (package1=100, package2=10) and (package1=100, package2=101). 355 // Since we don't know the actual power distribution between packages at this 356 // point, we have no way to correctly declare package1 as the winner. 357 // The naive logic of picking the consumer with the higher total consumed 358 // power would produce an incorrect result. 359 mPackageWithHighestDrain = null; 360 } 361 return this; 362 } 363 364 /** 365 * Returns true if this UidBatteryConsumer must be excluded from the 366 * BatteryUsageStats. 367 */ isExcludedFromBatteryUsageStats()368 public boolean isExcludedFromBatteryUsageStats() { 369 return mExcludeFromBatteryUsageStats; 370 } 371 372 /** 373 * Creates a read-only object out of the Builder values. 374 */ 375 @NonNull build()376 public UidBatteryConsumer build() { 377 if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) { 378 mPackageWithHighestDrain = null; 379 } 380 if (mPackageWithHighestDrain != null) { 381 mData.putString(COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN, mPackageWithHighestDrain); 382 } 383 return new UidBatteryConsumer(this); 384 } 385 } 386 } 387