Home | History | Annotate | Download | only in fuelgauge
      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