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 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_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 194 public Drawable getIcon() { 195 return icon; 196 } 197 198 /** 199 * Gets the application name 200 */ 201 public String getLabel() { 202 return name; 203 } 204 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 */ 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