BatteryEntry.java revision 6fd48e224f89e4d4e154dcf2e494c201293cc167
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
17package com.android.settings.fuelgauge;
18
19import android.content.Context;
20import android.content.pm.ApplicationInfo;
21import android.content.pm.PackageInfo;
22import android.content.pm.PackageManager;
23import android.content.pm.UserInfo;
24import android.graphics.drawable.Drawable;
25import android.os.BatteryStats;
26import android.os.Handler;
27import android.os.UserManager;
28import com.android.internal.os.BatterySipper;
29import com.android.settings.R;
30import com.android.settings.users.UserUtils;
31
32import java.util.ArrayList;
33import java.util.HashMap;
34
35/**
36 * Wraps the power usage data of a BatterySipper with information about package name
37 * and icon image.
38 */
39public class BatteryEntry {
40    public static final int MSG_UPDATE_NAME_ICON = 1;
41    public static final int MSG_REPORT_FULLY_DRAWN = 2;
42
43    static final HashMap<String,UidToDetail> sUidCache = new HashMap<String,UidToDetail>();
44
45    static final ArrayList<BatteryEntry> mRequestQueue = new ArrayList<BatteryEntry>();
46    static Handler sHandler;
47
48    static private class NameAndIconLoader extends Thread {
49        private boolean mAbort = false;
50
51        public NameAndIconLoader() {
52            super("BatteryUsage Icon Loader");
53        }
54
55        public void abort() {
56            mAbort = true;
57        }
58
59        @Override
60        public void run() {
61            while (true) {
62                BatteryEntry be;
63                synchronized (mRequestQueue) {
64                    if (mRequestQueue.isEmpty() || mAbort) {
65                        if (sHandler != null) {
66                            sHandler.sendEmptyMessage(MSG_REPORT_FULLY_DRAWN);
67                        }
68                        mRequestQueue.clear();
69                        return;
70                    }
71                    be = mRequestQueue.remove(0);
72                }
73                be.loadNameAndIcon();
74            }
75        }
76    }
77
78    private static NameAndIconLoader mRequestThread;
79
80    public static void startRequestQueue() {
81        if (sHandler != null) {
82            synchronized (mRequestQueue) {
83                if (!mRequestQueue.isEmpty()) {
84                    if (mRequestThread != null) {
85                        mRequestThread.abort();
86                    }
87                    mRequestThread = new NameAndIconLoader();
88                    mRequestThread.setPriority(Thread.MIN_PRIORITY);
89                    mRequestThread.start();
90                    mRequestQueue.notify();
91                }
92            }
93        }
94    }
95
96    public static void stopRequestQueue() {
97        synchronized (mRequestQueue) {
98            if (mRequestThread != null) {
99                mRequestThread.abort();
100                mRequestThread = null;
101                sHandler = null;
102            }
103        }
104    }
105
106    public static void clearUidCache() {
107        sUidCache.clear();
108    }
109
110    public final Context context;
111    public final BatterySipper sipper;
112
113    public String name;
114    public Drawable icon;
115    public int iconId; // For passing to the detail screen.
116    public String defaultPackageName;
117
118    static class UidToDetail {
119        String name;
120        String packageName;
121        Drawable icon;
122    }
123
124    public BatteryEntry(Context context, Handler handler, UserManager um, BatterySipper sipper) {
125        sHandler = handler;
126        this.context = context;
127        this.sipper = sipper;
128        switch (sipper.drainType) {
129            case IDLE:
130                name = context.getResources().getString(R.string.power_idle);
131                iconId = R.drawable.ic_settings_phone_idle;
132                break;
133            case CELL:
134                name = context.getResources().getString(R.string.power_cell);
135                iconId = R.drawable.ic_settings_cell_standby;
136                break;
137            case PHONE:
138                name = context.getResources().getString(R.string.power_phone);
139                iconId = R.drawable.ic_settings_voice_calls;
140                break;
141            case WIFI:
142                name = context.getResources().getString(R.string.power_wifi);
143                iconId = R.drawable.ic_settings_wifi;
144                break;
145            case BLUETOOTH:
146                name = context.getResources().getString(R.string.power_bluetooth);
147                iconId = R.drawable.ic_settings_bluetooth;
148                break;
149            case SCREEN:
150                name = context.getResources().getString(R.string.power_screen);
151                iconId = R.drawable.ic_settings_display;
152                break;
153            case FLASHLIGHT:
154                name = context.getResources().getString(R.string.power_flashlight);
155                iconId = R.drawable.ic_settings_display;
156                break;
157            case APP:
158                name = sipper.packageWithHighestDrain;
159                break;
160            case USER: {
161                UserInfo info = um.getUserInfo(sipper.userId);
162                if (info != null) {
163                    icon = UserUtils.getUserIcon(context, um, info, context.getResources());
164                    name = info != null ? info.name : null;
165                    if (name == null) {
166                        name = Integer.toString(info.id);
167                    }
168                    name = context.getResources().getString(
169                            R.string.running_process_item_user_label, name);
170                } else {
171                    icon = null;
172                    name = context.getResources().getString(
173                            R.string.running_process_item_removed_user_label);
174                }
175            } break;
176            case UNACCOUNTED:
177                name = context.getResources().getString(R.string.power_unaccounted);
178                iconId = R.drawable.ic_power_system;
179                break;
180            case OVERCOUNTED:
181                name = context.getResources().getString(R.string.power_overcounted);
182                iconId = R.drawable.ic_power_system;
183                break;
184        }
185        if (iconId > 0) {
186            icon = context.getResources().getDrawable(iconId);
187        }
188        if ((name == null || iconId == 0) && this.sipper.uidObj != null) {
189            getQuickNameIconForUid(this.sipper.uidObj);
190        }
191    }
192
193    public Drawable getIcon() {
194        return icon;
195    }
196
197    /**
198     * Gets the application name
199     */
200    public String getLabel() {
201        return name;
202    }
203
204    void getQuickNameIconForUid(BatteryStats.Uid uidObj) {
205        final int uid = uidObj.getUid();
206        final String uidString = Integer.toString(uid);
207        if (sUidCache.containsKey(uidString)) {
208            UidToDetail utd = sUidCache.get(uidString);
209            defaultPackageName = utd.packageName;
210            name = utd.name;
211            icon = utd.icon;
212            return;
213        }
214        PackageManager pm = context.getPackageManager();
215        String[] packages = pm.getPackagesForUid(uid);
216        icon = pm.getDefaultActivityIcon();
217        if (packages == null) {
218            //name = Integer.toString(uid);
219            if (uid == 0) {
220                name = context.getResources().getString(R.string.process_kernel_label);
221            } else if ("mediaserver".equals(name)) {
222                name = context.getResources().getString(R.string.process_mediaserver_label);
223            }
224            iconId = R.drawable.ic_power_system;
225            icon = context.getResources().getDrawable(iconId);
226            return;
227        } else {
228            //name = packages[0];
229        }
230        if (sHandler != null) {
231            synchronized (mRequestQueue) {
232                mRequestQueue.add(this);
233            }
234        }
235    }
236
237    /**
238     * Loads the app label and icon image and stores into the cache.
239     */
240    public void loadNameAndIcon() {
241        // Bail out if the current sipper is not an App sipper.
242        if (sipper.uidObj == null) {
243            return;
244        }
245        PackageManager pm = context.getPackageManager();
246        final int uid = sipper.uidObj.getUid();
247        final Drawable defaultActivityIcon = pm.getDefaultActivityIcon();
248        sipper.mPackages = pm.getPackagesForUid(uid);
249        if (sipper.mPackages == null) {
250            name = Integer.toString(uid);
251            return;
252        }
253
254        String[] packageLabels = new String[sipper.mPackages.length];
255        System.arraycopy(sipper.mPackages, 0, packageLabels, 0, sipper.mPackages.length);
256
257        // Convert package names to user-facing labels where possible
258        for (int i = 0; i < packageLabels.length; i++) {
259            try {
260                ApplicationInfo ai = pm.getApplicationInfo(packageLabels[i], 0);
261                CharSequence label = ai.loadLabel(pm);
262                if (label != null) {
263                    packageLabels[i] = label.toString();
264                }
265                if (ai.icon != 0) {
266                    defaultPackageName = sipper.mPackages[i];
267                    icon = ai.loadIcon(pm);
268                    break;
269                }
270            } catch (PackageManager.NameNotFoundException e) {
271            }
272        }
273        if (icon == null) icon = defaultActivityIcon;
274
275        if (packageLabels.length == 1) {
276            name = packageLabels[0];
277        } else {
278            // Look for an official name for this UID.
279            for (String pkgName : sipper.mPackages) {
280                try {
281                    final PackageInfo pi = pm.getPackageInfo(pkgName, 0);
282                    if (pi.sharedUserLabel != 0) {
283                        final CharSequence nm = pm.getText(pkgName,
284                                pi.sharedUserLabel, pi.applicationInfo);
285                        if (nm != null) {
286                            name = nm.toString();
287                            if (pi.applicationInfo.icon != 0) {
288                                defaultPackageName = pkgName;
289                                icon = pi.applicationInfo.loadIcon(pm);
290                            }
291                            break;
292                        }
293                    }
294                } catch (PackageManager.NameNotFoundException e) {
295                }
296            }
297        }
298        final String uidString = Integer.toString(sipper.uidObj.getUid());
299        UidToDetail utd = new UidToDetail();
300        utd.name = name;
301        utd.icon = icon;
302        utd.packageName = defaultPackageName;
303        sUidCache.put(uidString, utd);
304        if (sHandler != null) {
305            sHandler.sendMessage(sHandler.obtainMessage(MSG_UPDATE_NAME_ICON, this));
306        }
307    }
308}
309