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