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