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