• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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;
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.Handler;
29 import android.os.Process;
30 import android.os.RemoteException;
31 import android.os.UserHandle;
32 import android.os.UserManager;
33 import android.util.Log;
34 
35 import com.android.internal.os.BatterySipper;
36 import com.android.settings.R;
37 import com.android.settingslib.Utils;
38 
39 import java.util.ArrayList;
40 import java.util.HashMap;
41 import java.util.Locale;
42 
43 /**
44  * Wraps the power usage data of a BatterySipper with information about package name
45  * and icon image.
46  */
47 public class BatteryEntry {
48     public static final int MSG_UPDATE_NAME_ICON = 1;
49     public static final int MSG_REPORT_FULLY_DRAWN = 2;
50 
51     private static final String TAG = "BatteryEntry";
52     private static final String PACKAGE_SYSTEM = "android";
53 
54     static final HashMap<String,UidToDetail> sUidCache = new HashMap<String,UidToDetail>();
55 
56     static final ArrayList<BatteryEntry> mRequestQueue = new ArrayList<BatteryEntry>();
57     static Handler sHandler;
58 
59     static Locale sCurrentLocale = null;
60 
61     static private class NameAndIconLoader extends Thread {
62         private boolean mAbort = false;
63 
NameAndIconLoader()64         public NameAndIconLoader() {
65             super("BatteryUsage Icon Loader");
66         }
67 
abort()68         public void abort() {
69             mAbort = true;
70         }
71 
72         @Override
run()73         public void run() {
74             while (true) {
75                 BatteryEntry be;
76                 synchronized (mRequestQueue) {
77                     if (mRequestQueue.isEmpty() || mAbort) {
78                         if (sHandler != null) {
79                             sHandler.sendEmptyMessage(MSG_REPORT_FULLY_DRAWN);
80                         }
81                         mRequestQueue.clear();
82                         return;
83                     }
84                     be = mRequestQueue.remove(0);
85                 }
86                 be.loadNameAndIcon();
87             }
88         }
89     }
90 
91     private static NameAndIconLoader mRequestThread;
92 
startRequestQueue()93     public static void startRequestQueue() {
94         if (sHandler != null) {
95             synchronized (mRequestQueue) {
96                 if (!mRequestQueue.isEmpty()) {
97                     if (mRequestThread != null) {
98                         mRequestThread.abort();
99                     }
100                     mRequestThread = new NameAndIconLoader();
101                     mRequestThread.setPriority(Thread.MIN_PRIORITY);
102                     mRequestThread.start();
103                     mRequestQueue.notify();
104                 }
105             }
106         }
107     }
108 
stopRequestQueue()109     public static void stopRequestQueue() {
110         synchronized (mRequestQueue) {
111             if (mRequestThread != null) {
112                 mRequestThread.abort();
113                 mRequestThread = null;
114                 sHandler = null;
115             }
116         }
117     }
118 
clearUidCache()119     public static void clearUidCache() {
120         sUidCache.clear();
121     }
122 
123     public final Context context;
124     public final BatterySipper sipper;
125 
126     public String name;
127     public Drawable icon;
128     public int iconId; // For passing to the detail screen.
129     public String defaultPackageName;
130 
131     static class UidToDetail {
132         String name;
133         String packageName;
134         Drawable icon;
135     }
136 
BatteryEntry(Context context, Handler handler, UserManager um, BatterySipper sipper)137     public BatteryEntry(Context context, Handler handler, UserManager um, BatterySipper sipper) {
138         sHandler = handler;
139         this.context = context;
140         this.sipper = sipper;
141         switch (sipper.drainType) {
142             case IDLE:
143                 name = context.getResources().getString(R.string.power_idle);
144                 iconId = R.drawable.ic_settings_phone_idle;
145                 break;
146             case CELL:
147                 name = context.getResources().getString(R.string.power_cell);
148                 iconId = R.drawable.ic_cellular_1_bar;
149                 break;
150             case PHONE:
151                 name = context.getResources().getString(R.string.power_phone);
152                 iconId = R.drawable.ic_settings_voice_calls;
153                 break;
154             case WIFI:
155                 name = context.getResources().getString(R.string.power_wifi);
156                 iconId = R.drawable.ic_settings_wireless;
157                 break;
158             case BLUETOOTH:
159                 name = context.getResources().getString(R.string.power_bluetooth);
160                 iconId = com.android.internal.R.drawable.ic_settings_bluetooth;
161                 break;
162             case SCREEN:
163                 name = context.getResources().getString(R.string.power_screen);
164                 iconId = R.drawable.ic_settings_display;
165                 break;
166             case FLASHLIGHT:
167                 name = context.getResources().getString(R.string.power_flashlight);
168                 iconId = R.drawable.ic_settings_display;
169                 break;
170             case APP:
171                 PackageManager pm = context.getPackageManager();
172                 sipper.mPackages = pm.getPackagesForUid(sipper.uidObj.getUid());
173                 // Apps should only have one package
174                 if (sipper.mPackages == null || sipper.mPackages.length != 1) {
175                     name = sipper.packageWithHighestDrain;
176                 } else {
177                     defaultPackageName = pm.getPackagesForUid(sipper.uidObj.getUid())[0];
178                     try {
179                         ApplicationInfo appInfo =
180                             pm.getApplicationInfo(defaultPackageName, 0 /* no flags */);
181                         name = pm.getApplicationLabel(appInfo).toString();
182                     } catch (NameNotFoundException e) {
183                         Log.d(TAG, "PackageManager failed to retrieve ApplicationInfo for: "
184                             + defaultPackageName);
185                         name = defaultPackageName;
186                     }
187                 }
188                 break;
189             case USER: {
190                 UserInfo info = um.getUserInfo(sipper.userId);
191                 if (info != null) {
192                     icon = Utils.getUserIcon(context, um, info);
193                     name = Utils.getUserLabel(context, info);
194                 } else {
195                     icon = null;
196                     name = context.getResources().getString(
197                             R.string.running_process_item_removed_user_label);
198                 }
199             } break;
200             case UNACCOUNTED:
201                 name = context.getResources().getString(R.string.power_unaccounted);
202                 iconId = R.drawable.ic_android;
203                 break;
204             case OVERCOUNTED:
205                 name = context.getResources().getString(R.string.power_overcounted);
206                 iconId = R.drawable.ic_android;
207                 break;
208             case CAMERA:
209                 name = context.getResources().getString(R.string.power_camera);
210                 iconId = R.drawable.ic_settings_camera;
211                 break;
212             case AMBIENT_DISPLAY:
213                 name = context.getResources().getString(R.string.ambient_display_screen_title);
214                 iconId = R.drawable.ic_settings_aod;
215                 break;
216         }
217         if (iconId > 0) {
218             icon = context.getDrawable(iconId);
219         }
220         if ((name == null || iconId == 0) && this.sipper.uidObj != null) {
221             getQuickNameIconForUid(this.sipper.uidObj.getUid());
222         }
223     }
224 
getIcon()225     public Drawable getIcon() {
226         return icon;
227     }
228 
229     /**
230      * Gets the application name
231      */
getLabel()232     public String getLabel() {
233         return name;
234     }
235 
getQuickNameIconForUid(final int uid)236     void getQuickNameIconForUid(final int uid) {
237         // Locale sync to system config in Settings
238         final Locale locale = Locale.getDefault();
239         if (sCurrentLocale != locale) {
240             clearUidCache();
241             sCurrentLocale = locale;
242         }
243 
244         final String uidString = Integer.toString(uid);
245         if (sUidCache.containsKey(uidString)) {
246             UidToDetail utd = sUidCache.get(uidString);
247             defaultPackageName = utd.packageName;
248             name = utd.name;
249             icon = utd.icon;
250             return;
251         }
252         PackageManager pm = context.getPackageManager();
253         icon = pm.getDefaultActivityIcon();
254         if (pm.getPackagesForUid(uid) == null) {
255             if (uid == 0) {
256                 name = context.getResources().getString(R.string.process_kernel_label);
257             } else if ("mediaserver".equals(name)) {
258                 name = context.getResources().getString(R.string.process_mediaserver_label);
259             } else if ("dex2oat".equals(name)) {
260                 name = context.getResources().getString(R.string.process_dex2oat_label);
261             }
262             iconId = R.drawable.ic_power_system;
263             icon = context.getDrawable(iconId);
264         }
265 
266         if (sHandler != null) {
267             synchronized (mRequestQueue) {
268                 mRequestQueue.add(this);
269             }
270         }
271     }
272 
273     /**
274      * Loads the app label and icon image and stores into the cache.
275      */
loadNameAndIcon()276     public void loadNameAndIcon() {
277         // Bail out if the current sipper is not an App sipper.
278         if (sipper.uidObj == null) {
279             return;
280         }
281 
282         PackageManager pm = context.getPackageManager();
283         final int uid = sipper.uidObj.getUid();
284         if (sipper.mPackages == null) {
285             sipper.mPackages = pm.getPackagesForUid(uid);
286         }
287 
288         final String[] packages = extractPackagesFromSipper(sipper);
289         if (packages != null) {
290             String[] packageLabels = new String[packages.length];
291             System.arraycopy(packages, 0, packageLabels, 0, packages.length);
292 
293             // Convert package names to user-facing labels where possible
294             IPackageManager ipm = AppGlobals.getPackageManager();
295             final int userId = UserHandle.getUserId(uid);
296             for (int i = 0; i < packageLabels.length; i++) {
297                 try {
298                     final ApplicationInfo ai = ipm.getApplicationInfo(packageLabels[i],
299                             0 /* no flags */, userId);
300                     if (ai == null) {
301                         Log.d(TAG, "Retrieving null app info for package "
302                                 + packageLabels[i] + ", user " + userId);
303                         continue;
304                     }
305                     CharSequence label = ai.loadLabel(pm);
306                     if (label != null) {
307                         packageLabels[i] = label.toString();
308                     }
309                     if (ai.icon != 0) {
310                         defaultPackageName = packages[i];
311                         icon = ai.loadIcon(pm);
312                         break;
313                     }
314                 } catch (RemoteException e) {
315                     Log.d(TAG, "Error while retrieving app info for package "
316                             + packageLabels[i] + ", user " + userId, e);
317                 }
318             }
319 
320             if (packageLabels.length == 1) {
321                 name = packageLabels[0];
322             } else {
323                 // Look for an official name for this UID.
324                 for (String pkgName : packages) {
325                     try {
326                         final PackageInfo pi = ipm.getPackageInfo(pkgName, 0 /* no flags */, userId);
327                         if (pi == null) {
328                             Log.d(TAG, "Retrieving null package info for package "
329                                     + pkgName + ", user " + userId);
330                             continue;
331                         }
332                         if (pi.sharedUserLabel != 0) {
333                             final CharSequence nm = pm.getText(pkgName,
334                                     pi.sharedUserLabel, pi.applicationInfo);
335                             if (nm != null) {
336                                 name = nm.toString();
337                                 if (pi.applicationInfo.icon != 0) {
338                                     defaultPackageName = pkgName;
339                                     icon = pi.applicationInfo.loadIcon(pm);
340                                 }
341                                 break;
342                             }
343                         }
344                     } catch (RemoteException e) {
345                         Log.d(TAG, "Error while retrieving package info for package "
346                                 + pkgName + ", user " + userId, e);
347                     }
348                 }
349             }
350         }
351 
352         final String uidString = Integer.toString(uid);
353         if (name == null) {
354             name = uidString;
355         }
356 
357         if (icon == null) {
358             icon = pm.getDefaultActivityIcon();
359         }
360 
361         UidToDetail utd = new UidToDetail();
362         utd.name = name;
363         utd.icon = icon;
364         utd.packageName = defaultPackageName;
365         sUidCache.put(uidString, utd);
366         if (sHandler != null) {
367             sHandler.sendMessage(sHandler.obtainMessage(MSG_UPDATE_NAME_ICON, this));
368         }
369     }
370 
extractPackagesFromSipper(BatterySipper sipper)371     String[] extractPackagesFromSipper(BatterySipper sipper) {
372         // Only use system package if uid is system uid, so it could find a consistent name and icon
373         return sipper.getUid() == Process.SYSTEM_UID
374                 ? new String[]{PACKAGE_SYSTEM}
375                 : sipper.mPackages;
376     }
377 }
378