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