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