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