• 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 
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