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