• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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