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 public final class UidBatteryConsumer extends BatteryConsumer { 41 42 static final int CONSUMER_TYPE_UID = 1; 43 44 @Retention(RetentionPolicy.SOURCE) 45 @IntDef({ 46 STATE_FOREGROUND, 47 STATE_BACKGROUND 48 }) 49 public @interface State { 50 } 51 52 /** 53 * The state of an application when it is either running a foreground (top) activity. 54 */ 55 public static final int STATE_FOREGROUND = 0; 56 57 /** 58 * The state of an application when it is running in the background, including the following 59 * states: 60 * 61 * {@link android.app.ActivityManager#PROCESS_STATE_IMPORTANT_BACKGROUND}, 62 * {@link android.app.ActivityManager#PROCESS_STATE_TRANSIENT_BACKGROUND}, 63 * {@link android.app.ActivityManager#PROCESS_STATE_BACKUP}, 64 * {@link android.app.ActivityManager#PROCESS_STATE_SERVICE}, 65 * {@link android.app.ActivityManager#PROCESS_STATE_RECEIVER}, 66 * {@link android.app.ActivityManager#PROCESS_STATE_FOREGROUND_SERVICE}. 67 */ 68 public static final int STATE_BACKGROUND = 1; 69 70 static final int COLUMN_INDEX_UID = BatteryConsumer.COLUMN_COUNT; 71 static final int COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN = COLUMN_INDEX_UID + 1; 72 static final int COLUMN_INDEX_TIME_IN_FOREGROUND = COLUMN_INDEX_UID + 2; 73 static final int COLUMN_INDEX_TIME_IN_BACKGROUND = COLUMN_INDEX_UID + 3; 74 static final int COLUMN_COUNT = BatteryConsumer.COLUMN_COUNT + 4; 75 UidBatteryConsumer(BatteryConsumerData data)76 UidBatteryConsumer(BatteryConsumerData data) { 77 super(data); 78 } 79 UidBatteryConsumer(@onNull Builder builder)80 private UidBatteryConsumer(@NonNull Builder builder) { 81 super(builder.mData, builder.mPowerComponentsBuilder.build()); 82 } 83 getUid()84 public int getUid() { 85 return mData.getInt(COLUMN_INDEX_UID); 86 } 87 88 @Nullable getPackageWithHighestDrain()89 public String getPackageWithHighestDrain() { 90 return mData.getString(COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN); 91 } 92 93 /** 94 * Returns the amount of time in milliseconds this UID spent in the specified state. 95 */ getTimeInStateMs(@tate int state)96 public long getTimeInStateMs(@State int state) { 97 switch (state) { 98 case STATE_BACKGROUND: 99 return mData.getInt(COLUMN_INDEX_TIME_IN_BACKGROUND); 100 case STATE_FOREGROUND: 101 return mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND); 102 } 103 return 0; 104 } 105 106 @Override dump(PrintWriter pw, boolean skipEmptyComponents)107 public void dump(PrintWriter pw, boolean skipEmptyComponents) { 108 pw.print("UID "); 109 UserHandle.formatUid(pw, getUid()); 110 pw.print(": "); 111 pw.print(BatteryStats.formatCharge(getConsumedPower())); 112 113 if (mData.layout.processStateDataIncluded) { 114 StringBuilder sb = new StringBuilder(); 115 appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_FOREGROUND, 116 skipEmptyComponents); 117 appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_BACKGROUND, 118 skipEmptyComponents); 119 appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 120 skipEmptyComponents); 121 appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_CACHED, 122 skipEmptyComponents); 123 pw.print(sb); 124 } 125 126 pw.print(" ( "); 127 mPowerComponents.dump(pw, skipEmptyComponents /* skipTotalPowerComponent */); 128 pw.print(" ) "); 129 } 130 appendProcessStateData(StringBuilder sb, @ProcessState int processState, boolean skipEmptyComponents)131 private void appendProcessStateData(StringBuilder sb, @ProcessState int processState, 132 boolean skipEmptyComponents) { 133 Dimensions dimensions = new Dimensions(POWER_COMPONENT_ANY, processState); 134 final double power = mPowerComponents.getConsumedPower(dimensions); 135 if (power == 0 && skipEmptyComponents) { 136 return; 137 } 138 139 sb.append(" ").append(processStateToString(processState)).append(": ") 140 .append(BatteryStats.formatCharge(power)); 141 } 142 create(BatteryConsumerData data)143 static UidBatteryConsumer create(BatteryConsumerData data) { 144 return new UidBatteryConsumer(data); 145 } 146 147 /** Serializes this object to XML */ writeToXml(TypedXmlSerializer serializer)148 void writeToXml(TypedXmlSerializer serializer) throws IOException { 149 if (getConsumedPower() == 0) { 150 return; 151 } 152 153 serializer.startTag(null, BatteryUsageStats.XML_TAG_UID); 154 serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_UID, getUid()); 155 final String packageWithHighestDrain = getPackageWithHighestDrain(); 156 if (!TextUtils.isEmpty(packageWithHighestDrain)) { 157 serializer.attribute(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE, 158 packageWithHighestDrain); 159 } 160 serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND, 161 getTimeInStateMs(STATE_FOREGROUND)); 162 serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND, 163 getTimeInStateMs(STATE_BACKGROUND)); 164 mPowerComponents.writeToXml(serializer); 165 serializer.endTag(null, BatteryUsageStats.XML_TAG_UID); 166 } 167 168 /** Parses an XML representation and populates the BatteryUsageStats builder */ createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)169 static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder) 170 throws XmlPullParserException, IOException { 171 final int uid = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_UID); 172 final UidBatteryConsumer.Builder consumerBuilder = 173 builder.getOrCreateUidBatteryConsumerBuilder(uid); 174 175 int eventType = parser.getEventType(); 176 if (eventType != XmlPullParser.START_TAG 177 || !parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) { 178 throw new XmlPullParserException("Invalid XML parser state"); 179 } 180 181 consumerBuilder.setPackageWithHighestDrain( 182 parser.getAttributeValue(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE)); 183 consumerBuilder.setTimeInStateMs(STATE_FOREGROUND, 184 parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND)); 185 consumerBuilder.setTimeInStateMs(STATE_BACKGROUND, 186 parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND)); 187 while (!(eventType == XmlPullParser.END_TAG 188 && parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) 189 && eventType != XmlPullParser.END_DOCUMENT) { 190 if (eventType == XmlPullParser.START_TAG) { 191 if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) { 192 PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder); 193 } 194 } 195 eventType = parser.next(); 196 } 197 } 198 199 /** 200 * Builder for UidBatteryConsumer. 201 */ 202 public static final class Builder extends BaseBuilder<Builder> { 203 private static final String PACKAGE_NAME_UNINITIALIZED = ""; 204 private final BatteryStats.Uid mBatteryStatsUid; 205 private final int mUid; 206 private final boolean mIsVirtualUid; 207 private String mPackageWithHighestDrain = PACKAGE_NAME_UNINITIALIZED; 208 private boolean mExcludeFromBatteryUsageStats; 209 Builder(BatteryConsumerData data, @NonNull BatteryStats.Uid batteryStatsUid, double minConsumedPowerThreshold)210 public Builder(BatteryConsumerData data, @NonNull BatteryStats.Uid batteryStatsUid, 211 double minConsumedPowerThreshold) { 212 this(data, batteryStatsUid, batteryStatsUid.getUid(), minConsumedPowerThreshold); 213 } 214 Builder(BatteryConsumerData data, int uid, double minConsumedPowerThreshold)215 public Builder(BatteryConsumerData data, int uid, double minConsumedPowerThreshold) { 216 this(data, null, uid, minConsumedPowerThreshold); 217 } 218 Builder(BatteryConsumerData data, @Nullable BatteryStats.Uid batteryStatsUid, int uid, double minConsumedPowerThreshold)219 private Builder(BatteryConsumerData data, @Nullable BatteryStats.Uid batteryStatsUid, 220 int uid, double minConsumedPowerThreshold) { 221 super(data, CONSUMER_TYPE_UID, minConsumedPowerThreshold); 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