BatteryEntry.java revision e35df2ff3b8188bdf710f84390f0fc60ec329ab5
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 17package com.android.settings.fuelgauge; 18 19import android.app.AppGlobals; 20import android.content.Context; 21import android.content.pm.ApplicationInfo; 22import android.content.pm.IPackageManager; 23import android.content.pm.PackageInfo; 24import android.content.pm.PackageManager; 25import android.content.pm.UserInfo; 26import android.graphics.drawable.Drawable; 27import android.os.BatteryStats; 28import android.os.Handler; 29import android.os.RemoteException; 30import android.os.UserHandle; 31import android.os.UserManager; 32import android.util.Log; 33 34import com.android.internal.os.BatterySipper; 35import com.android.settings.R; 36import com.android.settings.Utils; 37 38import java.util.ArrayList; 39import java.util.HashMap; 40 41/** 42 * Wraps the power usage data of a BatterySipper with information about package name 43 * and icon image. 44 */ 45public 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 57 public NameAndIconLoader() { 58 super("BatteryUsage Icon Loader"); 59 } 60 61 public void abort() { 62 mAbort = true; 63 } 64 65 @Override 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 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 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 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 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_wireless; 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 case CAMERA: 186 name = context.getResources().getString(R.string.power_camera); 187 iconId = R.drawable.ic_settings_camera; 188 break; 189 } 190 if (iconId > 0) { 191 icon = context.getDrawable(iconId); 192 } 193 if ((name == null || iconId == 0) && this.sipper.uidObj != null) { 194 getQuickNameIconForUid(this.sipper.uidObj); 195 } 196 } 197 198 public Drawable getIcon() { 199 return icon; 200 } 201 202 /** 203 * Gets the application name 204 */ 205 public String getLabel() { 206 return name; 207 } 208 209 void getQuickNameIconForUid(BatteryStats.Uid uidObj) { 210 final int uid = uidObj.getUid(); 211 final String uidString = Integer.toString(uid); 212 if (sUidCache.containsKey(uidString)) { 213 UidToDetail utd = sUidCache.get(uidString); 214 defaultPackageName = utd.packageName; 215 name = utd.name; 216 icon = utd.icon; 217 return; 218 } 219 PackageManager pm = context.getPackageManager(); 220 String[] packages = pm.getPackagesForUid(uid); 221 icon = pm.getDefaultActivityIcon(); 222 if (packages == null) { 223 //name = Integer.toString(uid); 224 if (uid == 0) { 225 name = context.getResources().getString(R.string.process_kernel_label); 226 } else if ("mediaserver".equals(name)) { 227 name = context.getResources().getString(R.string.process_mediaserver_label); 228 } 229 iconId = R.drawable.ic_power_system; 230 icon = context.getDrawable(iconId); 231 return; 232 } else { 233 //name = packages[0]; 234 } 235 if (sHandler != null) { 236 synchronized (mRequestQueue) { 237 mRequestQueue.add(this); 238 } 239 } 240 } 241 242 /** 243 * Loads the app label and icon image and stores into the cache. 244 */ 245 public void loadNameAndIcon() { 246 // Bail out if the current sipper is not an App sipper. 247 if (sipper.uidObj == null) { 248 return; 249 } 250 PackageManager pm = context.getPackageManager(); 251 final int uid = sipper.uidObj.getUid(); 252 final Drawable defaultActivityIcon = pm.getDefaultActivityIcon(); 253 sipper.mPackages = pm.getPackagesForUid(uid); 254 if (sipper.mPackages == null) { 255 name = Integer.toString(uid); 256 return; 257 } 258 259 String[] packageLabels = new String[sipper.mPackages.length]; 260 System.arraycopy(sipper.mPackages, 0, packageLabels, 0, sipper.mPackages.length); 261 262 // Convert package names to user-facing labels where possible 263 IPackageManager ipm = AppGlobals.getPackageManager(); 264 final int userId = UserHandle.getUserId(uid); 265 for (int i = 0; i < packageLabels.length; i++) { 266 try { 267 final ApplicationInfo ai = ipm.getApplicationInfo(packageLabels[i], 268 0 /* no flags */, userId); 269 if (ai == null) { 270 Log.d(PowerUsageSummary.TAG, "Retrieving null app info for package " 271 + packageLabels[i] + ", user " + userId); 272 continue; 273 } 274 CharSequence label = ai.loadLabel(pm); 275 if (label != null) { 276 packageLabels[i] = label.toString(); 277 } 278 if (ai.icon != 0) { 279 defaultPackageName = sipper.mPackages[i]; 280 icon = ai.loadIcon(pm); 281 break; 282 } 283 } catch (RemoteException e) { 284 Log.d(PowerUsageSummary.TAG, "Error while retrieving app info for package " 285 + packageLabels[i] + ", user " + userId, e); 286 } 287 } 288 if (icon == null) { 289 icon = defaultActivityIcon; 290 } 291 292 if (packageLabels.length == 1) { 293 name = packageLabels[0]; 294 } else { 295 // Look for an official name for this UID. 296 for (String pkgName : sipper.mPackages) { 297 try { 298 final PackageInfo pi = ipm.getPackageInfo(pkgName, 0 /* no flags */, userId); 299 if (pi == null) { 300 Log.d(PowerUsageSummary.TAG, "Retrieving null package info for package " 301 + pkgName + ", user " + userId); 302 continue; 303 } 304 if (pi.sharedUserLabel != 0) { 305 final CharSequence nm = pm.getText(pkgName, 306 pi.sharedUserLabel, pi.applicationInfo); 307 if (nm != null) { 308 name = nm.toString(); 309 if (pi.applicationInfo.icon != 0) { 310 defaultPackageName = pkgName; 311 icon = pi.applicationInfo.loadIcon(pm); 312 } 313 break; 314 } 315 } 316 } catch (RemoteException e) { 317 Log.d(PowerUsageSummary.TAG, "Error while retrieving package info for package " 318 + pkgName + ", user " + userId, e); 319 } 320 } 321 } 322 final String uidString = Integer.toString(sipper.uidObj.getUid()); 323 UidToDetail utd = new UidToDetail(); 324 utd.name = name; 325 utd.icon = icon; 326 utd.packageName = defaultPackageName; 327 sUidCache.put(uidString, utd); 328 if (sHandler != null) { 329 sHandler.sendMessage(sHandler.obtainMessage(MSG_UPDATE_NAME_ICON, this)); 330 } 331 } 332} 333