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 = info != null ? info.name : null;
    171                     if (name == null) {
    172                         name = Integer.toString(info.id);
    173                     }
    174                     name = context.getResources().getString(
    175                             R.string.running_process_item_user_label, name);
    176                 } else {
    177                     icon = null;
    178                     name = context.getResources().getString(
    179                             R.string.running_process_item_removed_user_label);
    180                 }
    181             } break;
    182             case UNACCOUNTED:
    183                 name = context.getResources().getString(R.string.power_unaccounted);
    184                 iconId = R.drawable.ic_power_system;
    185                 break;
    186             case OVERCOUNTED:
    187                 name = context.getResources().getString(R.string.power_overcounted);
    188                 iconId = R.drawable.ic_power_system;
    189                 break;
    190         }
    191         if (iconId > 0) {
    192             icon = context.getResources().getDrawable(iconId);
    193         }
    194         if ((name == null || iconId == 0) && this.sipper.uidObj != null) {
    195             getQuickNameIconForUid(this.sipper.uidObj);
    196         }
    197     }
    198 
    199     public Drawable getIcon() {
    200         return icon;
    201     }
    202 
    203     /**
    204      * Gets the application name
    205      */
    206     public String getLabel() {
    207         return name;
    208     }
    209 
    210     void getQuickNameIconForUid(BatteryStats.Uid uidObj) {
    211         final int uid = uidObj.getUid();
    212         final String uidString = Integer.toString(uid);
    213         if (sUidCache.containsKey(uidString)) {
    214             UidToDetail utd = sUidCache.get(uidString);
    215             defaultPackageName = utd.packageName;
    216             name = utd.name;
    217             icon = utd.icon;
    218             return;
    219         }
    220         PackageManager pm = context.getPackageManager();
    221         String[] packages = pm.getPackagesForUid(uid);
    222         icon = pm.getDefaultActivityIcon();
    223         if (packages == null) {
    224             //name = Integer.toString(uid);
    225             if (uid == 0) {
    226                 name = context.getResources().getString(R.string.process_kernel_label);
    227             } else if ("mediaserver".equals(name)) {
    228                 name = context.getResources().getString(R.string.process_mediaserver_label);
    229             }
    230             iconId = R.drawable.ic_power_system;
    231             icon = context.getResources().getDrawable(iconId);
    232             return;
    233         } else {
    234             //name = packages[0];
    235         }
    236         if (sHandler != null) {
    237             synchronized (mRequestQueue) {
    238                 mRequestQueue.add(this);
    239             }
    240         }
    241     }
    242 
    243     /**
    244      * Loads the app label and icon image and stores into the cache.
    245      */
    246     public void loadNameAndIcon() {
    247         // Bail out if the current sipper is not an App sipper.
    248         if (sipper.uidObj == null) {
    249             return;
    250         }
    251         PackageManager pm = context.getPackageManager();
    252         final int uid = sipper.uidObj.getUid();
    253         final Drawable defaultActivityIcon = pm.getDefaultActivityIcon();
    254         sipper.mPackages = pm.getPackagesForUid(uid);
    255         if (sipper.mPackages == null) {
    256             name = Integer.toString(uid);
    257             return;
    258         }
    259 
    260         String[] packageLabels = new String[sipper.mPackages.length];
    261         System.arraycopy(sipper.mPackages, 0, packageLabels, 0, sipper.mPackages.length);
    262 
    263         // Convert package names to user-facing labels where possible
    264         IPackageManager ipm = AppGlobals.getPackageManager();
    265         final int userId = UserHandle.getUserId(uid);
    266         for (int i = 0; i < packageLabels.length; i++) {
    267             try {
    268                 final ApplicationInfo ai = ipm.getApplicationInfo(packageLabels[i],
    269                         0 /* no flags */, userId);
    270                 if (ai == null) {
    271                     Log.d(PowerUsageSummary.TAG, "Retrieving null app info for package "
    272                             + packageLabels[i] + ", user " + userId);
    273                     continue;
    274                 }
    275                 CharSequence label = ai.loadLabel(pm);
    276                 if (label != null) {
    277                     packageLabels[i] = label.toString();
    278                 }
    279                 if (ai.icon != 0) {
    280                     defaultPackageName = sipper.mPackages[i];
    281                     icon = ai.loadIcon(pm);
    282                     break;
    283                 }
    284             } catch (RemoteException e) {
    285                 Log.d(PowerUsageSummary.TAG, "Error while retrieving app info for package "
    286                         + packageLabels[i] + ", user " + userId, e);
    287             }
    288         }
    289         if (icon == null) {
    290             icon = defaultActivityIcon;
    291         }
    292 
    293         if (packageLabels.length == 1) {
    294             name = packageLabels[0];
    295         } else {
    296             // Look for an official name for this UID.
    297             for (String pkgName : sipper.mPackages) {
    298                 try {
    299                     final PackageInfo pi = ipm.getPackageInfo(pkgName, 0 /* no flags */, userId);
    300                     if (pi == null) {
    301                         Log.d(PowerUsageSummary.TAG, "Retrieving null package info for package "
    302                                 + pkgName + ", user " + userId);
    303                         continue;
    304                     }
    305                     if (pi.sharedUserLabel != 0) {
    306                         final CharSequence nm = pm.getText(pkgName,
    307                                 pi.sharedUserLabel, pi.applicationInfo);
    308                         if (nm != null) {
    309                             name = nm.toString();
    310                             if (pi.applicationInfo.icon != 0) {
    311                                 defaultPackageName = pkgName;
    312                                 icon = pi.applicationInfo.loadIcon(pm);
    313                             }
    314                             break;
    315                         }
    316                     }
    317                 } catch (RemoteException e) {
    318                     Log.d(PowerUsageSummary.TAG, "Error while retrieving package info for package "
    319                             + pkgName + ", user " + userId, e);
    320                 }
    321             }
    322         }
    323         final String uidString = Integer.toString(sipper.uidObj.getUid());
    324         UidToDetail utd = new UidToDetail();
    325         utd.name = name;
    326         utd.icon = icon;
    327         utd.packageName = defaultPackageName;
    328         sUidCache.put(uidString, utd);
    329         if (sHandler != null) {
    330             sHandler.sendMessage(sHandler.obtainMessage(MSG_UPDATE_NAME_ICON, this));
    331         }
    332     }
    333 }
    334