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