• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Handler;
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.DebugUtils;
37 import android.util.Log;
38 
39 import com.android.settings.R;
40 import com.android.settings.fuelgauge.BatteryUtils;
41 import com.android.settingslib.Utils;
42 
43 import java.util.ArrayList;
44 import java.util.Comparator;
45 import java.util.HashMap;
46 import java.util.Locale;
47 
48 /**
49  * Wraps the power usage data of a BatterySipper with information about package name
50  * and icon image.
51  */
52 public class BatteryEntry {
53 
54     /** The app name and icon in app list. */
55     public static final class NameAndIcon {
56         public final String mName;
57         public final String mPackageName;
58         public final Drawable mIcon;
59         public final int mIconId;
60 
NameAndIcon(String name, Drawable icon, int iconId)61         public NameAndIcon(String name, Drawable icon, int iconId) {
62             this(name, /*packageName=*/ null, icon, iconId);
63         }
64 
NameAndIcon( String name, String packageName, Drawable icon, int iconId)65         public NameAndIcon(
66                 String name, String packageName, Drawable icon, int iconId) {
67             this.mName = name;
68             this.mIcon = icon;
69             this.mIconId = iconId;
70             this.mPackageName = packageName;
71         }
72     }
73 
74     public static final int MSG_UPDATE_NAME_ICON = 1;
75     public static final int MSG_REPORT_FULLY_DRAWN = 2;
76 
77     private static final String TAG = "BatteryEntry";
78     private static final String PACKAGE_SYSTEM = "android";
79 
80     static final HashMap<String, UidToDetail> sUidCache = new HashMap<>();
81 
82     static final ArrayList<BatteryEntry> sRequestQueue = new ArrayList<BatteryEntry>();
83     static Handler sHandler;
84 
85     static Locale sCurrentLocale = null;
86 
87     private static class NameAndIconLoader extends Thread {
88         private boolean mAbort = false;
89 
NameAndIconLoader()90         NameAndIconLoader() {
91             super("BatteryUsage Icon Loader");
92         }
93 
abort()94         public void abort() {
95             mAbort = true;
96         }
97 
98         @Override
run()99         public void run() {
100             while (true) {
101                 BatteryEntry be;
102                 synchronized (sRequestQueue) {
103                     if (sRequestQueue.isEmpty() || mAbort) {
104                         if (sHandler != null) {
105                             sHandler.sendEmptyMessage(MSG_REPORT_FULLY_DRAWN);
106                         }
107                         return;
108                     }
109                     be = sRequestQueue.remove(0);
110                 }
111                 final NameAndIcon nameAndIcon =
112                         BatteryEntry.loadNameAndIcon(
113                                 be.mContext, be.getUid(), sHandler, be,
114                                 be.mDefaultPackageName, be.mName, be.mIcon);
115                 if (nameAndIcon != null) {
116                     be.mIcon = nameAndIcon.mIcon;
117                     be.mName = nameAndIcon.mName;
118                     be.mDefaultPackageName = nameAndIcon.mPackageName;
119                 }
120             }
121         }
122     }
123 
124     private static NameAndIconLoader sRequestThread;
125 
126     /** Starts the request queue. */
startRequestQueue()127     public static void startRequestQueue() {
128         if (sHandler != null) {
129             synchronized (sRequestQueue) {
130                 if (!sRequestQueue.isEmpty()) {
131                     if (sRequestThread != null) {
132                         sRequestThread.abort();
133                     }
134                     sRequestThread = new NameAndIconLoader();
135                     sRequestThread.setPriority(Thread.MIN_PRIORITY);
136                     sRequestThread.start();
137                     sRequestQueue.notify();
138                 }
139             }
140         }
141     }
142 
143     /** Stops the request queue. */
stopRequestQueue()144     public static void stopRequestQueue() {
145         synchronized (sRequestQueue) {
146             if (sRequestThread != null) {
147                 sRequestThread.abort();
148                 sRequestThread = null;
149                 sRequestQueue.clear();
150                 sHandler = null;
151             }
152         }
153     }
154 
155     /** Clears the UID cache. */
clearUidCache()156     public static void clearUidCache() {
157         sUidCache.clear();
158     }
159 
160     public static final Comparator<BatteryEntry> COMPARATOR =
161             (a, b) -> Double.compare(b.getConsumedPower(), a.getConsumedPower());
162 
163     private final Context mContext;
164     private final BatteryConsumer mBatteryConsumer;
165     private final int mUid;
166     private final boolean mIsHidden;
167     @ConvertUtils.ConsumerType
168     private final int mConsumerType;
169     @BatteryConsumer.PowerComponent
170     private final int mPowerComponentId;
171     private long mUsageDurationMs;
172     private long mTimeInForegroundMs;
173     private long mTimeInBackgroundMs;
174 
175     public String mName;
176     public Drawable mIcon;
177     public int mIconId;
178     public double mPercent;
179     private String mDefaultPackageName;
180     private double mConsumedPower;
181 
182     static class UidToDetail {
183         String mName;
184         String mPackageName;
185         Drawable mIcon;
186     }
187 
BatteryEntry(Context context, Handler handler, UserManager um, BatteryConsumer batteryConsumer, boolean isHidden, int uid, String[] packages, String packageName)188     public BatteryEntry(Context context, Handler handler, UserManager um,
189             BatteryConsumer batteryConsumer, boolean isHidden, int uid, String[] packages,
190             String packageName) {
191         this(context, handler, um, batteryConsumer, isHidden, uid, packages, packageName, true);
192     }
193 
BatteryEntry(Context context, Handler handler, UserManager um, BatteryConsumer batteryConsumer, boolean isHidden, int uid, String[] packages, String packageName, boolean loadDataInBackground)194     public BatteryEntry(Context context, Handler handler, UserManager um,
195             BatteryConsumer batteryConsumer, boolean isHidden, int uid, String[] packages,
196             String packageName, boolean loadDataInBackground) {
197         sHandler = handler;
198         mContext = context;
199         mBatteryConsumer = batteryConsumer;
200         mIsHidden = isHidden;
201         mDefaultPackageName = packageName;
202         mPowerComponentId = -1;
203 
204         if (batteryConsumer instanceof UidBatteryConsumer) {
205             mUid = uid;
206             mConsumerType = ConvertUtils.CONSUMER_TYPE_UID_BATTERY;
207             mConsumedPower = batteryConsumer.getConsumedPower();
208 
209             UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer;
210             if (mDefaultPackageName == null) {
211                 // Apps should only have one package
212                 if (packages != null && packages.length == 1) {
213                     mDefaultPackageName = packages[0];
214                 } else {
215                     mDefaultPackageName = isSystemUid(uid)
216                             ? PACKAGE_SYSTEM : uidBatteryConsumer.getPackageWithHighestDrain();
217                 }
218             }
219             if (mDefaultPackageName != null) {
220                 PackageManager pm = context.getPackageManager();
221                 try {
222                     ApplicationInfo appInfo =
223                             pm.getApplicationInfo(mDefaultPackageName, 0 /* no flags */);
224                     mName = pm.getApplicationLabel(appInfo).toString();
225                 } catch (NameNotFoundException e) {
226                     Log.d(TAG, "PackageManager failed to retrieve ApplicationInfo for: "
227                             + mDefaultPackageName);
228                     mName = mDefaultPackageName;
229                 }
230             }
231             getQuickNameIconForUid(uid, packages, loadDataInBackground);
232             mTimeInForegroundMs =
233                     uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND);
234             mTimeInBackgroundMs =
235                     uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND);
236         } else if (batteryConsumer instanceof UserBatteryConsumer) {
237             mUid = Process.INVALID_UID;
238             mConsumerType = ConvertUtils.CONSUMER_TYPE_USER_BATTERY;
239             mConsumedPower = batteryConsumer.getConsumedPower();
240             final NameAndIcon nameAndIcon = getNameAndIconFromUserId(
241                     context, ((UserBatteryConsumer) batteryConsumer).getUserId());
242             mIcon = nameAndIcon.mIcon;
243             mName = nameAndIcon.mName;
244         } else {
245             throw new IllegalArgumentException("Unsupported: " + batteryConsumer);
246         }
247     }
248 
249     /** Battery entry for a power component of AggregateBatteryConsumer */
BatteryEntry(Context context, int powerComponentId, double devicePowerMah, double appsPowerMah, long usageDurationMs)250     public BatteryEntry(Context context, int powerComponentId, double devicePowerMah,
251             double appsPowerMah, long usageDurationMs) {
252         mContext = context;
253         mBatteryConsumer = null;
254         mUid = Process.INVALID_UID;
255         mIsHidden = false;
256         mPowerComponentId = powerComponentId;
257         mConsumedPower =
258                 powerComponentId == BatteryConsumer.POWER_COMPONENT_SCREEN
259                         ? devicePowerMah
260                         : devicePowerMah - appsPowerMah;
261         mUsageDurationMs = usageDurationMs;
262         mConsumerType = ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY;
263 
264         final NameAndIcon nameAndIcon =
265                 getNameAndIconFromPowerComponent(context, powerComponentId);
266         mIconId = nameAndIcon.mIconId;
267         mName = nameAndIcon.mName;
268         if (mIconId != 0) {
269             mIcon = context.getDrawable(mIconId);
270         }
271     }
272 
273     /** Battery entry for a custom power component of AggregateBatteryConsumer */
BatteryEntry(Context context, int powerComponentId, String powerComponentName, double devicePowerMah, double appsPowerMah)274     public BatteryEntry(Context context, int powerComponentId, String powerComponentName,
275             double devicePowerMah, double appsPowerMah) {
276         mContext = context;
277         mBatteryConsumer = null;
278         mUid = Process.INVALID_UID;
279         mIsHidden = false;
280         mPowerComponentId = powerComponentId;
281 
282         mIconId = R.drawable.ic_power_system;
283         mIcon = context.getDrawable(mIconId);
284         mName = powerComponentName;
285         mConsumedPower =
286                 powerComponentId == BatteryConsumer.POWER_COMPONENT_SCREEN
287                         ? devicePowerMah
288                         : devicePowerMah - appsPowerMah;
289         mConsumerType = ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY;
290     }
291 
getIcon()292     public Drawable getIcon() {
293         return mIcon;
294     }
295 
getLabel()296     public String getLabel() {
297         return mName;
298     }
299 
300     @ConvertUtils.ConsumerType
getConsumerType()301     public int getConsumerType() {
302         return mConsumerType;
303     }
304 
305     @BatteryConsumer.PowerComponent
getPowerComponentId()306     public int getPowerComponentId() {
307         return mPowerComponentId;
308     }
309 
getQuickNameIconForUid( final int uid, final String[] packages, final boolean loadDataInBackground)310     void getQuickNameIconForUid(
311             final int uid, final String[] packages, final boolean loadDataInBackground) {
312         // Locale sync to system config in Settings
313         final Locale locale = Locale.getDefault();
314         if (sCurrentLocale != locale) {
315             clearUidCache();
316             sCurrentLocale = locale;
317         }
318 
319         final String uidString = Integer.toString(uid);
320         if (sUidCache.containsKey(uidString)) {
321             UidToDetail utd = sUidCache.get(uidString);
322             mDefaultPackageName = utd.mPackageName;
323             mName = utd.mName;
324             mIcon = utd.mIcon;
325             return;
326         }
327 
328         if (packages == null || packages.length == 0) {
329             final NameAndIcon nameAndIcon = getNameAndIconFromUid(mContext, mName, uid);
330             mIcon = nameAndIcon.mIcon;
331             mName = nameAndIcon.mName;
332         } else {
333             mIcon = mContext.getPackageManager().getDefaultActivityIcon();
334         }
335 
336         // Avoids post the loading icon and label in the background request.
337         if (sHandler != null && loadDataInBackground) {
338             synchronized (sRequestQueue) {
339                 sRequestQueue.add(this);
340             }
341         }
342     }
343 
344     /** Loads the app label and icon image and stores into the cache. */
loadNameAndIcon( Context context, int uid, Handler handler, BatteryEntry batteryEntry, String defaultPackageName, String name, Drawable icon)345     public static NameAndIcon loadNameAndIcon(
346             Context context,
347             int uid,
348             Handler handler,
349             BatteryEntry batteryEntry,
350             String defaultPackageName,
351             String name,
352             Drawable icon) {
353         // Bail out if the current sipper is not an App sipper.
354         if (uid == 0 || uid == Process.INVALID_UID) {
355             return null;
356         }
357 
358         final PackageManager pm = context.getPackageManager();
359         final String[] packages = isSystemUid(uid)
360                 ? new String[]{PACKAGE_SYSTEM} : pm.getPackagesForUid(uid);
361         if (packages != null) {
362             final String[] packageLabels = new String[packages.length];
363             System.arraycopy(packages, 0, packageLabels, 0, packages.length);
364 
365             // Convert package names to user-facing labels where possible
366             final IPackageManager ipm = AppGlobals.getPackageManager();
367             final int userId = UserHandle.getUserId(uid);
368             for (int i = 0; i < packageLabels.length; i++) {
369                 try {
370                     final ApplicationInfo ai = ipm.getApplicationInfo(packageLabels[i],
371                             0 /* no flags */, userId);
372                     if (ai == null) {
373                         Log.d(TAG, "Retrieving null app info for package "
374                                 + packageLabels[i] + ", user " + userId);
375                         continue;
376                     }
377                     final CharSequence label = ai.loadLabel(pm);
378                     if (label != null) {
379                         packageLabels[i] = label.toString();
380                     }
381                     if (ai.icon != 0) {
382                         defaultPackageName = packages[i];
383                         icon = ai.loadIcon(pm);
384                         break;
385                     }
386                 } catch (RemoteException e) {
387                     Log.d(TAG, "Error while retrieving app info for package "
388                             + packageLabels[i] + ", user " + userId, e);
389                 }
390             }
391 
392             if (packageLabels.length == 1) {
393                 name = packageLabels[0];
394             } else {
395                 // Look for an official name for this UID.
396                 for (String pkgName : packages) {
397                     try {
398                         final PackageInfo pi = ipm.getPackageInfo(pkgName, 0, userId);
399                         if (pi == null) {
400                             Log.d(TAG, "Retrieving null package info for package "
401                                     + pkgName + ", user " + userId);
402                             continue;
403                         }
404                         if (pi.sharedUserLabel != 0) {
405                             final CharSequence nm = pm.getText(pkgName,
406                                     pi.sharedUserLabel, pi.applicationInfo);
407                             if (nm != null) {
408                                 name = nm.toString();
409                                 if (pi.applicationInfo.icon != 0) {
410                                     defaultPackageName = pkgName;
411                                     icon = pi.applicationInfo.loadIcon(pm);
412                                 }
413                                 break;
414                             }
415                         }
416                     } catch (RemoteException e) {
417                         Log.d(TAG, "Error while retrieving package info for package "
418                                 + pkgName + ", user " + userId, e);
419                     }
420                 }
421             }
422         }
423 
424         final String uidString = Integer.toString(uid);
425         if (icon == null) {
426             icon = pm.getDefaultActivityIcon();
427         }
428 
429         UidToDetail utd = new UidToDetail();
430         utd.mName = name;
431         utd.mIcon = icon;
432         utd.mPackageName = defaultPackageName;
433 
434         sUidCache.put(uidString, utd);
435         if (handler != null) {
436             handler.sendMessage(handler.obtainMessage(MSG_UPDATE_NAME_ICON, batteryEntry));
437         }
438         return new NameAndIcon(name, defaultPackageName, icon, /*iconId=*/ 0);
439     }
440 
441     /** Returns a string that uniquely identifies this battery consumer. */
getKey()442     public String getKey() {
443         if (mBatteryConsumer instanceof UidBatteryConsumer) {
444             return Integer.toString(mUid);
445         } else if (mBatteryConsumer instanceof UserBatteryConsumer) {
446             return "U|" + ((UserBatteryConsumer) mBatteryConsumer).getUserId();
447         } else {
448             return "S|" + mPowerComponentId;
449         }
450     }
451 
452     /** Returns true if the entry is hidden from the battery usage summary list. */
isHidden()453     public boolean isHidden() {
454         return mIsHidden;
455     }
456 
457     /** Returns true if this entry describes an app (UID). */
isAppEntry()458     public boolean isAppEntry() {
459         return mBatteryConsumer instanceof UidBatteryConsumer;
460     }
461 
462     /** Returns true if this entry describes a User. */
isUserEntry()463     public boolean isUserEntry() {
464         if (mBatteryConsumer instanceof UserBatteryConsumer) {
465             return true;
466         }
467         return false;
468     }
469 
470     /**
471      * Returns the package name that should be used to represent the UID described
472      * by this entry.
473      */
getDefaultPackageName()474     public String getDefaultPackageName() {
475         return mDefaultPackageName;
476     }
477 
478     /**
479      * Returns the UID of the app described by this entry.
480      */
getUid()481     public int getUid() {
482         return mUid;
483     }
484 
485     /** Returns foreground foreground time/ms that is attributed to this entry. */
getTimeInForegroundMs()486     public long getTimeInForegroundMs() {
487         if (mBatteryConsumer instanceof UidBatteryConsumer) {
488             return mTimeInForegroundMs;
489         } else {
490             return mUsageDurationMs;
491         }
492     }
493 
494     /** Returns background activity time/ms that is attributed to this entry. */
getTimeInBackgroundMs()495     public long getTimeInBackgroundMs() {
496         if (mBatteryConsumer instanceof UidBatteryConsumer) {
497             return mTimeInBackgroundMs;
498         } else {
499             return 0;
500         }
501     }
502 
503     /**
504      * Returns total amount of power (in milli-amp-hours) that is attributed to this entry.
505      */
getConsumedPower()506     public double getConsumedPower() {
507         return mConsumedPower;
508     }
509 
510     /**
511      * Adds the consumed power of the supplied BatteryConsumer to this entry. Also
512      * uses its package 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 += uidBatteryConsumer.getTimeInStateMs(
519                     UidBatteryConsumer.STATE_FOREGROUND);
520             mTimeInBackgroundMs += uidBatteryConsumer.getTimeInStateMs(
521                     UidBatteryConsumer.STATE_BACKGROUND);
522             if (mDefaultPackageName == null) {
523                 mDefaultPackageName = uidBatteryConsumer.getPackageWithHighestDrain();
524             }
525         }
526     }
527 
528     /** Gets name and icon resource from UserBatteryConsumer userId. */
getNameAndIconFromUserId( Context context, final int userId)529     public static NameAndIcon getNameAndIconFromUserId(
530             Context context, final int userId) {
531         UserManager um = context.getSystemService(UserManager.class);
532         UserInfo info = um.getUserInfo(userId);
533 
534         Drawable icon = null;
535         String name = null;
536         if (info != null) {
537             icon = Utils.getUserIcon(context, um, info);
538             name = Utils.getUserLabel(context, info);
539         } else {
540             name = context.getResources().getString(
541                     R.string.running_process_item_removed_user_label);
542         }
543         return new NameAndIcon(name, icon, 0 /* iconId */);
544     }
545 
546     /** Gets name and icon resource from UidBatteryConsumer uid. */
getNameAndIconFromUid( Context context, String name, final int uid)547     public static NameAndIcon getNameAndIconFromUid(
548             Context context, String name, final int uid) {
549         Drawable icon = context.getDrawable(R.drawable.ic_power_system);
550         if (uid == 0) {
551             name = context.getResources().getString(R.string.process_kernel_label);
552         } else if (uid == BatteryUtils.UID_REMOVED_APPS) {
553             name = context.getResources().getString(R.string.process_removed_apps);
554         } else if (uid == BatteryUtils.UID_TETHERING) {
555             name = context.getResources().getString(R.string.process_network_tethering);
556         } else if ("mediaserver".equals(name)) {
557             name = context.getResources().getString(R.string.process_mediaserver_label);
558         } else if ("dex2oat".equals(name) || "dex2oat32".equals(name)
559                 || "dex2oat64".equals(name)) {
560             name = context.getResources().getString(R.string.process_dex2oat_label);
561         }
562         return new NameAndIcon(name, icon, 0 /* iconId */);
563     }
564 
565     /** Gets name and icon resource from BatteryConsumer power component ID. */
getNameAndIconFromPowerComponent( Context context, @BatteryConsumer.PowerComponent int powerComponentId)566     public static NameAndIcon getNameAndIconFromPowerComponent(
567             Context context, @BatteryConsumer.PowerComponent int powerComponentId) {
568         String name;
569         int iconId;
570         switch (powerComponentId) {
571             case BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY:
572                 name = context.getResources().getString(R.string.ambient_display_screen_title);
573                 iconId = R.drawable.ic_settings_aod;
574                 break;
575             case BatteryConsumer.POWER_COMPONENT_BLUETOOTH:
576                 name = context.getResources().getString(R.string.power_bluetooth);
577                 iconId = R.drawable.ic_settings_bluetooth;
578                 break;
579             case BatteryConsumer.POWER_COMPONENT_CAMERA:
580                 name = context.getResources().getString(R.string.power_camera);
581                 iconId = R.drawable.ic_settings_camera;
582                 break;
583             case BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO:
584                 name = context.getResources().getString(R.string.power_cell);
585                 iconId = R.drawable.ic_cellular_1_bar;
586                 break;
587             case BatteryConsumer.POWER_COMPONENT_FLASHLIGHT:
588                 name = context.getResources().getString(R.string.power_flashlight);
589                 iconId = R.drawable.ic_settings_display;
590                 break;
591             case BatteryConsumer.POWER_COMPONENT_PHONE:
592                 name = context.getResources().getString(R.string.power_phone);
593                 iconId = R.drawable.ic_settings_voice_calls;
594                 break;
595             case BatteryConsumer.POWER_COMPONENT_SCREEN:
596                 name = context.getResources().getString(R.string.power_screen);
597                 iconId = R.drawable.ic_settings_display;
598                 break;
599             case BatteryConsumer.POWER_COMPONENT_WIFI:
600                 name = context.getResources().getString(R.string.power_wifi);
601                 iconId = R.drawable.ic_settings_wireless_no_theme;
602                 break;
603             case BatteryConsumer.POWER_COMPONENT_IDLE:
604             case BatteryConsumer.POWER_COMPONENT_MEMORY:
605                 name = context.getResources().getString(R.string.power_idle);
606                 iconId = R.drawable.ic_settings_phone_idle;
607                 break;
608             default:
609                 Log.w(TAG, "unknown attribute:" + DebugUtils.constantToString(
610                         BatteryConsumer.class, "POWER_COMPONENT_", powerComponentId));
611                 name = null;
612                 iconId = R.drawable.ic_power_system;
613                 break;
614         }
615         return new NameAndIcon(name, null /* icon */, iconId);
616     }
617 
618     /** Whether the uid is system uid. */
isSystemUid(int uid)619     public static boolean isSystemUid(int uid) {
620         return uid == Process.SYSTEM_UID;
621     }
622 }
623