BatteryEntry.java revision e35df2ff3b8188bdf710f84390f0fc60ec329ab5
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.app.AppGlobals;
20import android.content.Context;
21import android.content.pm.ApplicationInfo;
22import android.content.pm.IPackageManager;
23import android.content.pm.PackageInfo;
24import android.content.pm.PackageManager;
25import android.content.pm.UserInfo;
26import android.graphics.drawable.Drawable;
27import android.os.BatteryStats;
28import android.os.Handler;
29import android.os.RemoteException;
30import android.os.UserHandle;
31import android.os.UserManager;
32import android.util.Log;
33
34import com.android.internal.os.BatterySipper;
35import com.android.settings.R;
36import com.android.settings.Utils;
37
38import java.util.ArrayList;
39import java.util.HashMap;
40
41/**
42 * Wraps the power usage data of a BatterySipper with information about package name
43 * and icon image.
44 */
45public 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_wireless;
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            case CAMERA:
186                name = context.getResources().getString(R.string.power_camera);
187                iconId = R.drawable.ic_settings_camera;
188                break;
189        }
190        if (iconId > 0) {
191            icon = context.getDrawable(iconId);
192        }
193        if ((name == null || iconId == 0) && this.sipper.uidObj != null) {
194            getQuickNameIconForUid(this.sipper.uidObj);
195        }
196    }
197
198    public Drawable getIcon() {
199        return icon;
200    }
201
202    /**
203     * Gets the application name
204     */
205    public String getLabel() {
206        return name;
207    }
208
209    void getQuickNameIconForUid(BatteryStats.Uid uidObj) {
210        final int uid = uidObj.getUid();
211        final String uidString = Integer.toString(uid);
212        if (sUidCache.containsKey(uidString)) {
213            UidToDetail utd = sUidCache.get(uidString);
214            defaultPackageName = utd.packageName;
215            name = utd.name;
216            icon = utd.icon;
217            return;
218        }
219        PackageManager pm = context.getPackageManager();
220        String[] packages = pm.getPackagesForUid(uid);
221        icon = pm.getDefaultActivityIcon();
222        if (packages == null) {
223            //name = Integer.toString(uid);
224            if (uid == 0) {
225                name = context.getResources().getString(R.string.process_kernel_label);
226            } else if ("mediaserver".equals(name)) {
227                name = context.getResources().getString(R.string.process_mediaserver_label);
228            }
229            iconId = R.drawable.ic_power_system;
230            icon = context.getDrawable(iconId);
231            return;
232        } else {
233            //name = packages[0];
234        }
235        if (sHandler != null) {
236            synchronized (mRequestQueue) {
237                mRequestQueue.add(this);
238            }
239        }
240    }
241
242    /**
243     * Loads the app label and icon image and stores into the cache.
244     */
245    public void loadNameAndIcon() {
246        // Bail out if the current sipper is not an App sipper.
247        if (sipper.uidObj == null) {
248            return;
249        }
250        PackageManager pm = context.getPackageManager();
251        final int uid = sipper.uidObj.getUid();
252        final Drawable defaultActivityIcon = pm.getDefaultActivityIcon();
253        sipper.mPackages = pm.getPackagesForUid(uid);
254        if (sipper.mPackages == null) {
255            name = Integer.toString(uid);
256            return;
257        }
258
259        String[] packageLabels = new String[sipper.mPackages.length];
260        System.arraycopy(sipper.mPackages, 0, packageLabels, 0, sipper.mPackages.length);
261
262        // Convert package names to user-facing labels where possible
263        IPackageManager ipm = AppGlobals.getPackageManager();
264        final int userId = UserHandle.getUserId(uid);
265        for (int i = 0; i < packageLabels.length; i++) {
266            try {
267                final ApplicationInfo ai = ipm.getApplicationInfo(packageLabels[i],
268                        0 /* no flags */, userId);
269                if (ai == null) {
270                    Log.d(PowerUsageSummary.TAG, "Retrieving null app info for package "
271                            + packageLabels[i] + ", user " + userId);
272                    continue;
273                }
274                CharSequence label = ai.loadLabel(pm);
275                if (label != null) {
276                    packageLabels[i] = label.toString();
277                }
278                if (ai.icon != 0) {
279                    defaultPackageName = sipper.mPackages[i];
280                    icon = ai.loadIcon(pm);
281                    break;
282                }
283            } catch (RemoteException e) {
284                Log.d(PowerUsageSummary.TAG, "Error while retrieving app info for package "
285                        + packageLabels[i] + ", user " + userId, e);
286            }
287        }
288        if (icon == null) {
289            icon = defaultActivityIcon;
290        }
291
292        if (packageLabels.length == 1) {
293            name = packageLabels[0];
294        } else {
295            // Look for an official name for this UID.
296            for (String pkgName : sipper.mPackages) {
297                try {
298                    final PackageInfo pi = ipm.getPackageInfo(pkgName, 0 /* no flags */, userId);
299                    if (pi == null) {
300                        Log.d(PowerUsageSummary.TAG, "Retrieving null package info for package "
301                                + pkgName + ", user " + userId);
302                        continue;
303                    }
304                    if (pi.sharedUserLabel != 0) {
305                        final CharSequence nm = pm.getText(pkgName,
306                                pi.sharedUserLabel, pi.applicationInfo);
307                        if (nm != null) {
308                            name = nm.toString();
309                            if (pi.applicationInfo.icon != 0) {
310                                defaultPackageName = pkgName;
311                                icon = pi.applicationInfo.loadIcon(pm);
312                            }
313                            break;
314                        }
315                    }
316                } catch (RemoteException e) {
317                    Log.d(PowerUsageSummary.TAG, "Error while retrieving package info for package "
318                            + pkgName + ", user " + userId, e);
319                }
320            }
321        }
322        final String uidString = Integer.toString(sipper.uidObj.getUid());
323        UidToDetail utd = new UidToDetail();
324        utd.name = name;
325        utd.icon = icon;
326        utd.packageName = defaultPackageName;
327        sUidCache.put(uidString, utd);
328        if (sHandler != null) {
329            sHandler.sendMessage(sHandler.obtainMessage(MSG_UPDATE_NAME_ICON, this));
330        }
331    }
332}
333