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