1 /*
2  * Copyright (C) 2022 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 com.android.settings.fuelgauge.batteryusage;
18 
19 import android.app.AppGlobals;
20 import android.content.Context;
21 import android.content.pm.ApplicationInfo;
22 import android.content.pm.IPackageManager;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.PackageManager.NameNotFoundException;
26 import android.content.pm.UserInfo;
27 import android.graphics.drawable.Drawable;
28 import android.os.BatteryConsumer;
29 import android.os.BatteryConsumer.Dimensions;
30 import android.os.Process;
31 import android.os.RemoteException;
32 import android.os.UidBatteryConsumer;
33 import android.os.UserBatteryConsumer;
34 import android.os.UserHandle;
35 import android.os.UserManager;
36 import android.util.ArrayMap;
37 import android.util.DebugUtils;
38 import android.util.Log;
39 
40 import com.android.settings.R;
41 import com.android.settings.fuelgauge.BatteryUtils;
42 import com.android.settingslib.Utils;
43 import com.android.settingslib.utils.StringUtil;
44 
45 import java.util.Comparator;
46 import java.util.Locale;
47 
48 /**
49  * Wraps the power usage data of a BatterySipper with information about package name and icon image.
50  */
51 public class BatteryEntry {
52 
53     /** The app name and icon in app list. */
54     public static final class NameAndIcon {
55         public final String mName;
56         public final String mPackageName;
57         public final Drawable mIcon;
58         public final int mIconId;
59 
NameAndIcon(String name, Drawable icon, int iconId)60         public NameAndIcon(String name, Drawable icon, int iconId) {
61             this(name, /* packageName= */ null, icon, iconId);
62         }
63 
NameAndIcon(String name, String packageName, Drawable icon, int iconId)64         public NameAndIcon(String name, String packageName, Drawable icon, int iconId) {
65             this.mName = name;
66             this.mIcon = icon;
67             this.mIconId = iconId;
68             this.mPackageName = packageName;
69         }
70     }
71 
72     private static final String TAG = "BatteryEntry";
73     private static final String PACKAGE_SYSTEM = "android";
74 
75     static final int BATTERY_USAGE_INDEX_FOREGROUND = 0;
76     static final int BATTERY_USAGE_INDEX_FOREGROUND_SERVICE = 1;
77     static final int BATTERY_USAGE_INDEX_BACKGROUND = 2;
78     static final int BATTERY_USAGE_INDEX_CACHED = 3;
79 
80     static final Dimensions[] BATTERY_DIMENSIONS =
81             new Dimensions[] {
82                 new Dimensions(
83                         BatteryConsumer.POWER_COMPONENT_ANY,
84                         BatteryConsumer.PROCESS_STATE_FOREGROUND),
85                 new Dimensions(
86                         BatteryConsumer.POWER_COMPONENT_ANY,
87                         BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE),
88                 new Dimensions(
89                         BatteryConsumer.POWER_COMPONENT_ANY,
90                         BatteryConsumer.PROCESS_STATE_BACKGROUND),
91                 new Dimensions(
92                         BatteryConsumer.POWER_COMPONENT_ANY, BatteryConsumer.PROCESS_STATE_CACHED),
93             };
94 
95     static final ArrayMap<String, UidToDetail> sUidCache = new ArrayMap<>();
96 
97     static Locale sCurrentLocale = null;
98 
99     /** Clears the UID cache. */
clearUidCache()100     public static void clearUidCache() {
101         sUidCache.clear();
102     }
103 
104     public static final Comparator<BatteryEntry> COMPARATOR =
105             (a, b) -> Double.compare(b.getConsumedPower(), a.getConsumedPower());
106 
107     private final Context mContext;
108     private final BatteryConsumer mBatteryConsumer;
109     private final int mUid;
110     private final boolean mIsHidden;
111     @ConvertUtils.ConsumerType private final int mConsumerType;
112     @BatteryConsumer.PowerComponent private final int mPowerComponentId;
113     private long mUsageDurationMs;
114     private long mTimeInForegroundMs;
115     private long mTimeInForegroundServiceMs;
116     private long mTimeInBackgroundMs;
117 
118     public String mName;
119     public Drawable mIcon;
120     public int mIconId;
121     public double mPercent;
122     private String mDefaultPackageName;
123     private double mConsumedPower;
124     private double mConsumedPowerInForeground;
125     private double mConsumedPowerInForegroundService;
126     private double mConsumedPowerInBackground;
127     private double mConsumedPowerInCached;
128 
129     static class UidToDetail {
130         String mName;
131         String mPackageName;
132         Drawable mIcon;
133     }
134 
BatteryEntry( Context context, UserManager um, BatteryConsumer batteryConsumer, boolean isHidden, int uid, String[] packages, String packageName)135     public BatteryEntry(
136             Context context,
137             UserManager um,
138             BatteryConsumer batteryConsumer,
139             boolean isHidden,
140             int uid,
141             String[] packages,
142             String packageName) {
143         this(context, um, batteryConsumer, isHidden, uid, packages, packageName, true);
144     }
145 
BatteryEntry( Context context, UserManager um, BatteryConsumer batteryConsumer, boolean isHidden, int uid, String[] packages, String packageName, boolean loadDataInBackground)146     public BatteryEntry(
147             Context context,
148             UserManager um,
149             BatteryConsumer batteryConsumer,
150             boolean isHidden,
151             int uid,
152             String[] packages,
153             String packageName,
154             boolean loadDataInBackground) {
155         mContext = context;
156         mBatteryConsumer = batteryConsumer;
157         mIsHidden = isHidden;
158         mDefaultPackageName = packageName;
159         mPowerComponentId = -1;
160 
161         if (batteryConsumer instanceof UidBatteryConsumer) {
162             mUid = uid;
163             mConsumerType = ConvertUtils.CONSUMER_TYPE_UID_BATTERY;
164             mConsumedPower = batteryConsumer.getConsumedPower();
165 
166             UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer;
167             if (mDefaultPackageName == null) {
168                 // Apps should only have one package
169                 if (packages != null && packages.length == 1) {
170                     mDefaultPackageName = packages[0];
171                 } else {
172                     mDefaultPackageName =
173                             isSystemUid(uid)
174                                     ? PACKAGE_SYSTEM
175                                     : uidBatteryConsumer.getPackageWithHighestDrain();
176                 }
177             }
178             if (mDefaultPackageName != null) {
179                 PackageManager pm = context.getPackageManager();
180                 try {
181                     ApplicationInfo appInfo =
182                             pm.getApplicationInfo(mDefaultPackageName, 0 /* no flags */);
183                     mName = pm.getApplicationLabel(appInfo).toString();
184                 } catch (NameNotFoundException e) {
185                     Log.d(
186                             TAG,
187                             "PackageManager failed to retrieve ApplicationInfo for: "
188                                     + mDefaultPackageName);
189                     mName = mDefaultPackageName;
190                 }
191             }
192             mTimeInForegroundMs =
193                     uidBatteryConsumer.getTimeInProcessStateMs(
194                             UidBatteryConsumer.PROCESS_STATE_FOREGROUND);
195             mTimeInForegroundServiceMs =
196                     uidBatteryConsumer.getTimeInProcessStateMs(
197                             UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
198             mTimeInBackgroundMs =
199                     uidBatteryConsumer.getTimeInProcessStateMs(
200                             UidBatteryConsumer.PROCESS_STATE_BACKGROUND);
201             mConsumedPowerInForeground =
202                     safeGetConsumedPower(
203                             uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND]);
204             mConsumedPowerInForegroundService =
205                     safeGetConsumedPower(
206                             uidBatteryConsumer,
207                             BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND_SERVICE]);
208             mConsumedPowerInBackground =
209                     safeGetConsumedPower(
210                             uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_BACKGROUND]);
211             mConsumedPowerInCached =
212                     safeGetConsumedPower(
213                             uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_CACHED]);
214         } else if (batteryConsumer instanceof UserBatteryConsumer) {
215             mUid = Process.INVALID_UID;
216             mConsumerType = ConvertUtils.CONSUMER_TYPE_USER_BATTERY;
217             mConsumedPower = batteryConsumer.getConsumedPower();
218             final NameAndIcon nameAndIcon =
219                     getNameAndIconFromUserId(
220                             context, ((UserBatteryConsumer) batteryConsumer).getUserId());
221             mIcon = nameAndIcon.mIcon;
222             mName = nameAndIcon.mName;
223         } else {
224             throw new IllegalArgumentException("Unsupported: " + batteryConsumer);
225         }
226     }
227 
228     /** Battery entry for a power component of AggregateBatteryConsumer */
BatteryEntry( Context context, int powerComponentId, double devicePowerMah, long usageDurationMs, boolean isHidden)229     public BatteryEntry(
230             Context context,
231             int powerComponentId,
232             double devicePowerMah,
233             long usageDurationMs,
234             boolean isHidden) {
235         mContext = context;
236         mBatteryConsumer = null;
237         mUid = Process.INVALID_UID;
238         mIsHidden = isHidden;
239         mPowerComponentId = powerComponentId;
240         mConsumedPower = devicePowerMah;
241         mUsageDurationMs = usageDurationMs;
242         mConsumerType = ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY;
243 
244         final NameAndIcon nameAndIcon = getNameAndIconFromPowerComponent(context, powerComponentId);
245         mIconId = nameAndIcon.mIconId;
246         mName = nameAndIcon.mName;
247         if (mIconId != 0) {
248             mIcon = context.getDrawable(mIconId);
249         }
250     }
251 
252     /** Battery entry for a custom power component of AggregateBatteryConsumer */
BatteryEntry( Context context, int powerComponentId, String powerComponentName, double devicePowerMah)253     public BatteryEntry(
254             Context context,
255             int powerComponentId,
256             String powerComponentName,
257             double devicePowerMah) {
258         mContext = context;
259         mBatteryConsumer = null;
260         mUid = Process.INVALID_UID;
261         mIsHidden = false;
262         mPowerComponentId = powerComponentId;
263 
264         mIconId = R.drawable.ic_power_system;
265         mIcon = context.getDrawable(mIconId);
266         mName = powerComponentName;
267         mConsumedPower = devicePowerMah;
268         mConsumerType = ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY;
269     }
270 
getIcon()271     public Drawable getIcon() {
272         return mIcon;
273     }
274 
getLabel()275     public String getLabel() {
276         return mName;
277     }
278 
279     @ConvertUtils.ConsumerType
getConsumerType()280     public int getConsumerType() {
281         return mConsumerType;
282     }
283 
284     @BatteryConsumer.PowerComponent
getPowerComponentId()285     public int getPowerComponentId() {
286         return mPowerComponentId;
287     }
288 
289     /** Loads the app label and icon image and stores into the cache. */
loadNameAndIcon( Context context, int uid, BatteryEntry batteryEntry, String defaultPackageName, String name, Drawable icon)290     public static NameAndIcon loadNameAndIcon(
291             Context context,
292             int uid,
293             BatteryEntry batteryEntry,
294             String defaultPackageName,
295             String name,
296             Drawable icon) {
297         // Bail out if the current sipper is not an App sipper.
298         if (uid == 0 || uid == Process.INVALID_UID) {
299             return null;
300         }
301 
302         final PackageManager pm = context.getPackageManager();
303         final String[] packages =
304                 isSystemUid(uid) ? new String[] {PACKAGE_SYSTEM} : pm.getPackagesForUid(uid);
305         if (packages != null) {
306             final String[] packageLabels = new String[packages.length];
307             System.arraycopy(packages, 0, packageLabels, 0, packages.length);
308 
309             // Convert package names to user-facing labels where possible
310             final IPackageManager ipm = AppGlobals.getPackageManager();
311             final int userId = UserHandle.getUserId(uid);
312             for (int i = 0; i < packageLabels.length; i++) {
313                 try {
314                     final ApplicationInfo ai =
315                             ipm.getApplicationInfo(packageLabels[i], 0 /* no flags */, userId);
316                     if (ai == null) {
317                         Log.d(
318                                 TAG,
319                                 "Retrieving null app info for package "
320                                         + packageLabels[i]
321                                         + ", user "
322                                         + userId);
323                         continue;
324                     }
325                     final CharSequence label = ai.loadLabel(pm);
326                     if (label != null) {
327                         packageLabels[i] = label.toString();
328                     }
329                     if (ai.icon != 0) {
330                         defaultPackageName = packages[i];
331                         icon = ai.loadIcon(pm);
332                         break;
333                     }
334                 } catch (RemoteException e) {
335                     Log.d(
336                             TAG,
337                             "Error while retrieving app info for package "
338                                     + packageLabels[i]
339                                     + ", user "
340                                     + userId,
341                             e);
342                 }
343             }
344 
345             if (packageLabels.length == 1) {
346                 name = packageLabels[0];
347             } else {
348                 // Look for an official name for this UID.
349                 for (String pkgName : packages) {
350                     try {
351                         final PackageInfo pi = ipm.getPackageInfo(pkgName, 0, userId);
352                         if (pi == null) {
353                             Log.d(
354                                     TAG,
355                                     "Retrieving null package info for package "
356                                             + pkgName
357                                             + ", user "
358                                             + userId);
359                             continue;
360                         }
361                         if (pi.sharedUserLabel != 0) {
362                             final CharSequence nm =
363                                     pm.getText(pkgName, pi.sharedUserLabel, pi.applicationInfo);
364                             if (nm != null) {
365                                 name = nm.toString();
366                                 if (pi.applicationInfo.icon != 0) {
367                                     defaultPackageName = pkgName;
368                                     icon = pi.applicationInfo.loadIcon(pm);
369                                 }
370                                 break;
371                             }
372                         }
373                     } catch (RemoteException e) {
374                         Log.d(
375                                 TAG,
376                                 "Error while retrieving package info for package "
377                                         + pkgName
378                                         + ", user "
379                                         + userId,
380                                 e);
381                     }
382                 }
383             }
384         }
385 
386         final String uidString = Integer.toString(uid);
387         if (icon == null) {
388             icon = pm.getDefaultActivityIcon();
389         }
390 
391         UidToDetail utd = new UidToDetail();
392         utd.mName = name;
393         utd.mIcon = icon;
394         utd.mPackageName = defaultPackageName;
395 
396         sUidCache.put(uidString, utd);
397         return new NameAndIcon(name, defaultPackageName, icon, /* iconId= */ 0);
398     }
399 
400     /** Returns a string that uniquely identifies this battery consumer. */
getKey()401     public String getKey() {
402         if (mBatteryConsumer instanceof UidBatteryConsumer) {
403             return Integer.toString(mUid);
404         } else if (mBatteryConsumer instanceof UserBatteryConsumer) {
405             return "U|" + ((UserBatteryConsumer) mBatteryConsumer).getUserId();
406         } else {
407             return "S|" + mPowerComponentId;
408         }
409     }
410 
411     /** Returns true if the entry is hidden from the battery usage summary list. */
isHidden()412     public boolean isHidden() {
413         return mIsHidden;
414     }
415 
416     /** Returns true if this entry describes an app (UID). */
isAppEntry()417     public boolean isAppEntry() {
418         return mBatteryConsumer instanceof UidBatteryConsumer;
419     }
420 
421     /** Returns true if this entry describes a User. */
isUserEntry()422     public boolean isUserEntry() {
423         if (mBatteryConsumer instanceof UserBatteryConsumer) {
424             return true;
425         }
426         return false;
427     }
428 
429     /**
430      * Returns the package name that should be used to represent the UID described by this entry.
431      */
getDefaultPackageName()432     public String getDefaultPackageName() {
433         return mDefaultPackageName;
434     }
435 
436     /** Returns the UID of the app described by this entry. */
getUid()437     public int getUid() {
438         return mUid;
439     }
440 
441     /** Returns foreground time/ms that is attributed to this entry. */
getTimeInForegroundMs()442     public long getTimeInForegroundMs() {
443         return (mBatteryConsumer instanceof UidBatteryConsumer)
444                 ? mTimeInForegroundMs
445                 : mUsageDurationMs;
446     }
447 
448     /** Returns foreground service time/ms that is attributed to this entry. */
getTimeInForegroundServiceMs()449     public long getTimeInForegroundServiceMs() {
450         return (mBatteryConsumer instanceof UidBatteryConsumer) ? mTimeInForegroundServiceMs : 0;
451     }
452 
453     /** Returns background activity time/ms that is attributed to this entry. */
getTimeInBackgroundMs()454     public long getTimeInBackgroundMs() {
455         return (mBatteryConsumer instanceof UidBatteryConsumer) ? mTimeInBackgroundMs : 0;
456     }
457 
458     /** Returns total amount of power (in milli-amp-hours) that is attributed to this entry. */
getConsumedPower()459     public double getConsumedPower() {
460         return mConsumedPower;
461     }
462 
463     /**
464      * Returns amount of power (in milli-amp-hours) used in foreground that is attributed to this
465      * entry.
466      */
getConsumedPowerInForeground()467     public double getConsumedPowerInForeground() {
468         if (mBatteryConsumer instanceof UidBatteryConsumer) {
469             return mConsumedPowerInForeground;
470         } else {
471             return 0;
472         }
473     }
474 
475     /**
476      * Returns amount of power (in milli-amp-hours) used in foreground service that is attributed to
477      * this entry.
478      */
getConsumedPowerInForegroundService()479     public double getConsumedPowerInForegroundService() {
480         if (mBatteryConsumer instanceof UidBatteryConsumer) {
481             return mConsumedPowerInForegroundService;
482         } else {
483             return 0;
484         }
485     }
486 
487     /**
488      * Returns amount of power (in milli-amp-hours) used in background that is attributed to this
489      * entry.
490      */
getConsumedPowerInBackground()491     public double getConsumedPowerInBackground() {
492         if (mBatteryConsumer instanceof UidBatteryConsumer) {
493             return mConsumedPowerInBackground;
494         } else {
495             return 0;
496         }
497     }
498 
499     /**
500      * Returns amount of power (in milli-amp-hours) used in cached that is attributed to this entry.
501      */
getConsumedPowerInCached()502     public double getConsumedPowerInCached() {
503         if (mBatteryConsumer instanceof UidBatteryConsumer) {
504             return mConsumedPowerInCached;
505         } else {
506             return 0;
507         }
508     }
509 
510     /**
511      * Adds the consumed power of the supplied BatteryConsumer to this entry. Also uses its package
512      * with highest drain, if necessary.
513      */
add(BatteryConsumer batteryConsumer)514     public void add(BatteryConsumer batteryConsumer) {
515         mConsumedPower += batteryConsumer.getConsumedPower();
516         if (batteryConsumer instanceof UidBatteryConsumer) {
517             UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer;
518             mTimeInForegroundMs +=
519                     uidBatteryConsumer.getTimeInProcessStateMs(
520                             UidBatteryConsumer.PROCESS_STATE_FOREGROUND);
521             mTimeInForegroundServiceMs +=
522                     uidBatteryConsumer.getTimeInProcessStateMs(
523                             UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
524             mTimeInBackgroundMs +=
525                     uidBatteryConsumer.getTimeInProcessStateMs(
526                             UidBatteryConsumer.PROCESS_STATE_BACKGROUND);
527             mConsumedPowerInForeground +=
528                     safeGetConsumedPower(
529                             uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND]);
530             mConsumedPowerInForegroundService +=
531                     safeGetConsumedPower(
532                             uidBatteryConsumer,
533                             BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND_SERVICE]);
534             mConsumedPowerInBackground +=
535                     safeGetConsumedPower(
536                             uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_BACKGROUND]);
537             mConsumedPowerInCached +=
538                     safeGetConsumedPower(
539                             uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_CACHED]);
540             if (mDefaultPackageName == null) {
541                 mDefaultPackageName = uidBatteryConsumer.getPackageWithHighestDrain();
542             }
543         }
544     }
545 
546     /** Gets name and icon resource from UserBatteryConsumer userId. */
getNameAndIconFromUserId(Context context, final int userId)547     public static NameAndIcon getNameAndIconFromUserId(Context context, final int userId) {
548         UserManager um = context.getSystemService(UserManager.class);
549         UserInfo info = um.getUserInfo(userId);
550 
551         Drawable icon = null;
552         String name = null;
553         if (info != null) {
554             icon = Utils.getUserIcon(context, um, info);
555             name = Utils.getUserLabel(context, info);
556         } else {
557             name =
558                     context.getResources()
559                             .getString(R.string.running_process_item_removed_user_label);
560         }
561         return new NameAndIcon(name, icon, 0 /* iconId */);
562     }
563 
564     /** Gets name and icon resource from UidBatteryConsumer uid. */
getNameAndIconFromUid(Context context, String name, final int uid)565     public static NameAndIcon getNameAndIconFromUid(Context context, String name, final int uid) {
566         Drawable icon = context.getDrawable(R.drawable.ic_power_system);
567         if (uid == 0) {
568             name =
569                     context.getResources()
570                             .getString(com.android.settingslib.R.string.process_kernel_label);
571         } else if (uid == BatteryUtils.UID_REMOVED_APPS) {
572             name = context.getResources().getString(R.string.process_removed_apps);
573         } else if (uid == BatteryUtils.UID_TETHERING) {
574             name = context.getResources().getString(R.string.process_network_tethering);
575         } else if ("mediaserver".equals(name)) {
576             name = context.getResources().getString(R.string.process_mediaserver_label);
577         } else if ("dex2oat".equals(name) || "dex2oat32".equals(name) || "dex2oat64".equals(name)) {
578             name = context.getResources().getString(R.string.process_dex2oat_label);
579         }
580         return new NameAndIcon(name, icon, 0 /* iconId */);
581     }
582 
583     /** Gets name and icon resource from BatteryConsumer power component ID. */
getNameAndIconFromPowerComponent( Context context, @BatteryConsumer.PowerComponent int powerComponentId)584     public static NameAndIcon getNameAndIconFromPowerComponent(
585             Context context, @BatteryConsumer.PowerComponent int powerComponentId) {
586         String name;
587         int iconId;
588         switch (powerComponentId) {
589                 // Please see go/battery-usage-system-component-map
590             case BatteryConsumer.POWER_COMPONENT_SCREEN: // id: 0
591                 name = context.getResources().getString(R.string.power_screen);
592                 iconId = R.drawable.ic_settings_display;
593                 break;
594             case BatteryConsumer.POWER_COMPONENT_CPU: // id: 1
595                 name = context.getResources().getString(R.string.power_cpu);
596                 iconId = R.drawable.ic_settings_cpu;
597                 break;
598             case BatteryConsumer.POWER_COMPONENT_BLUETOOTH: // id: 2
599                 name = context.getResources().getString(R.string.power_bluetooth);
600                 iconId = R.drawable.ic_settings_bluetooth;
601                 break;
602             case BatteryConsumer.POWER_COMPONENT_CAMERA: // id: 3
603                 name = context.getResources().getString(R.string.power_camera);
604                 iconId = R.drawable.ic_settings_camera;
605                 break;
606             case BatteryConsumer.POWER_COMPONENT_FLASHLIGHT: // id: 6
607                 name = context.getResources().getString(R.string.power_flashlight);
608                 iconId = R.drawable.ic_settings_flashlight;
609                 break;
610             case BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO: // id: 8
611                 name = context.getResources().getString(R.string.power_cell);
612                 iconId = R.drawable.ic_settings_cellular;
613                 break;
614             case BatteryConsumer.POWER_COMPONENT_GNSS: // id: 10
615                 name = context.getResources().getString(R.string.power_gps);
616                 iconId = R.drawable.ic_settings_gps;
617                 break;
618             case BatteryConsumer.POWER_COMPONENT_WIFI: // id: 11
619                 name = context.getResources().getString(R.string.power_wifi);
620                 iconId = R.drawable.ic_settings_wireless_no_theme;
621                 break;
622             case BatteryConsumer.POWER_COMPONENT_PHONE: // id: 14
623                 name = context.getResources().getString(R.string.power_phone);
624                 iconId = R.drawable.ic_settings_voice_calls;
625                 break;
626             case BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY: // id :15
627                 name = context.getResources().getString(R.string.ambient_display_screen_title);
628                 iconId = R.drawable.ic_settings_aod;
629                 break;
630             default:
631                 Log.w(
632                         TAG,
633                         "unknown attribute:"
634                                 + DebugUtils.constantToString(
635                                         BatteryConsumer.class,
636                                         "POWER_COMPONENT_",
637                                         powerComponentId));
638                 name = null;
639                 iconId = R.drawable.ic_power_system;
640                 break;
641         }
642         return new NameAndIcon(name, null /* icon */, iconId);
643     }
644 
645     /** Whether the uid is system uid. */
isSystemUid(int uid)646     public static boolean isSystemUid(int uid) {
647         return uid == Process.SYSTEM_UID;
648     }
649 
safeGetConsumedPower( final UidBatteryConsumer uidBatteryConsumer, final Dimensions dimension)650     private static double safeGetConsumedPower(
651             final UidBatteryConsumer uidBatteryConsumer, final Dimensions dimension) {
652         try {
653             return uidBatteryConsumer.getConsumedPower(dimension);
654         } catch (IllegalArgumentException e) {
655             Log.e(TAG, "safeGetConsumedPower failed:" + e);
656             return 0.0d;
657         }
658     }
659 
660     @Override
toString()661     public String toString() {
662         return new StringBuilder()
663                 .append("BatteryEntry{")
664                 .append(String.format("\n\tname=%s isHidden=%b", mName, mIsHidden))
665                 .append(String.format("\n\tconsume=%.2f%% | %f", mPercent, mConsumedPower))
666                 .append(
667                         String.format(
668                                 "\n\tconsume power= foreground:%f foregroundService:%f",
669                                 mConsumedPowerInForeground, mConsumedPowerInForegroundService))
670                 .append(
671                         String.format(
672                                 "\n\tconsume power= background:%f cached:%f",
673                                 mConsumedPowerInBackground, mConsumedPowerInCached))
674                 .append(
675                         String.format(
676                                 "\n\ttime= foreground:%s foregroundService:%s "
677                                         + "background:%s usageDuration:%s",
678                                 StringUtil.formatElapsedTime(
679                                         mContext,
680                                         (double) mTimeInForegroundMs,
681                                         /* withSeconds= */ true,
682                                         /* collapseTimeUnit= */ false),
683                                 StringUtil.formatElapsedTime(
684                                         mContext,
685                                         (double) mTimeInForegroundServiceMs,
686                                         /* withSeconds= */ true,
687                                         /* collapseTimeUnit= */ false),
688                                 StringUtil.formatElapsedTime(
689                                         mContext,
690                                         (double) mTimeInBackgroundMs,
691                                         /* withSeconds= */ true,
692                                         /* collapseTimeUnit= */ false),
693                                 StringUtil.formatElapsedTime(
694                                         mContext,
695                                         (double) mUsageDurationMs,
696                                         /* withSeconds= */ true,
697                                         /* collapseTimeUnit= */ false)))
698                 .append(String.format("\n\tpackage:%s uid:%d", mDefaultPackageName, mUid))
699                 .toString();
700     }
701 }
702