PowerUsageDetail.java revision 04ac693da73d350788bc1e0f83ab8808061cfc11
1/*
2 * Copyright (C) 2009 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.Activity;
20import android.app.ActivityManager;
21import android.app.ApplicationErrorReport;
22import android.app.admin.DevicePolicyManager;
23import android.content.BroadcastReceiver;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.pm.ApplicationInfo;
28import android.content.pm.PackageInfo;
29import android.content.pm.PackageManager;
30import android.content.pm.PackageManager.NameNotFoundException;
31import android.graphics.drawable.Drawable;
32import android.net.Uri;
33import android.os.BatteryStats;
34import android.os.Bundle;
35import android.os.Process;
36import android.os.UserHandle;
37import android.preference.Preference;
38import android.preference.Preference.OnPreferenceClickListener;
39import android.preference.PreferenceCategory;
40import android.text.TextUtils;
41import android.util.Log;
42import android.view.View;
43import android.widget.Button;
44
45import com.android.internal.logging.MetricsLogger;
46import com.android.internal.os.BatterySipper;
47import com.android.internal.os.BatterySipper.DrainType;
48import com.android.internal.os.BatteryStatsHelper;
49import com.android.internal.util.FastPrintWriter;
50import com.android.settings.AppHeader;
51import com.android.settings.DisplaySettings;
52import com.android.settings.R;
53import com.android.settings.SettingsActivity;
54import com.android.settings.Utils;
55import com.android.settings.WirelessSettings;
56import com.android.settings.applications.AppInfoBase;
57import com.android.settings.applications.AppInfoWithHeader;
58import com.android.settings.applications.InstalledAppDetails;
59import com.android.settings.applications.LayoutPreference;
60import com.android.settings.bluetooth.BluetoothSettings;
61import com.android.settings.location.LocationSettings;
62import com.android.settings.wifi.WifiSettings;
63
64import java.io.PrintWriter;
65import java.io.StringWriter;
66import java.io.Writer;
67
68public class PowerUsageDetail extends PowerUsageBase implements Button.OnClickListener {
69
70    // Note: Must match the sequence of the DrainType
71    private static int[] sDrainTypeDesciptions = new int[] {
72        R.string.battery_desc_standby,
73        R.string.battery_desc_radio,
74        R.string.battery_desc_voice,
75        R.string.battery_desc_wifi,
76        R.string.battery_desc_bluetooth,
77        R.string.battery_desc_flashlight,
78        R.string.battery_desc_display,
79        R.string.battery_desc_apps,
80        R.string.battery_desc_users,
81        R.string.battery_desc_unaccounted,
82        R.string.battery_desc_overcounted,
83        R.string.battery_desc_camera,
84    };
85
86    public static void startBatteryDetailPage(
87            SettingsActivity caller, BatteryStatsHelper helper, int statsType, BatteryEntry entry,
88            boolean showLocationButton) {
89        // Initialize mStats if necessary.
90        helper.getStats();
91
92        final int dischargeAmount = helper.getStats().getDischargeAmount(statsType);
93        Bundle args = new Bundle();
94        args.putString(PowerUsageDetail.EXTRA_TITLE, entry.name);
95        args.putInt(PowerUsageDetail.EXTRA_PERCENT, (int)
96                ((entry.sipper.totalPowerMah * dischargeAmount / helper.getTotalPower()) + .5));
97        args.putInt(PowerUsageDetail.EXTRA_GAUGE, (int)
98                Math.ceil(entry.sipper.totalPowerMah * 100 / helper.getMaxPower()));
99        args.putLong(PowerUsageDetail.EXTRA_USAGE_DURATION, helper.getStatsPeriod());
100        args.putString(PowerUsageDetail.EXTRA_ICON_PACKAGE, entry.defaultPackageName);
101        args.putInt(PowerUsageDetail.EXTRA_ICON_ID, entry.iconId);
102        args.putDouble(PowerUsageDetail.EXTRA_NO_COVERAGE, entry.sipper.noCoveragePercent);
103        if (entry.sipper.uidObj != null) {
104            args.putInt(PowerUsageDetail.EXTRA_UID, entry.sipper.uidObj.getUid());
105        }
106        args.putSerializable(PowerUsageDetail.EXTRA_DRAIN_TYPE, entry.sipper.drainType);
107        args.putBoolean(PowerUsageDetail.EXTRA_SHOW_LOCATION_BUTTON, showLocationButton);
108
109        int userId = UserHandle.myUserId();
110        int[] types;
111        double[] values;
112        switch (entry.sipper.drainType) {
113            case APP:
114            case USER:
115            {
116                BatteryStats.Uid uid = entry.sipper.uidObj;
117                types = new int[] {
118                    R.string.usage_type_cpu,
119                    R.string.usage_type_cpu_foreground,
120                    R.string.usage_type_wake_lock,
121                    R.string.usage_type_gps,
122                    R.string.usage_type_wifi_running,
123                    R.string.usage_type_data_recv,
124                    R.string.usage_type_data_send,
125                    R.string.usage_type_radio_active,
126                    R.string.usage_type_data_wifi_recv,
127                    R.string.usage_type_data_wifi_send,
128                    R.string.usage_type_audio,
129                    R.string.usage_type_video,
130                    R.string.usage_type_camera,
131                    R.string.usage_type_flashlight,
132                };
133                values = new double[] {
134                    entry.sipper.cpuTimeMs,
135                    entry.sipper.cpuFgTimeMs,
136                    entry.sipper.wakeLockTimeMs,
137                    entry.sipper.gpsTimeMs,
138                    entry.sipper.wifiRunningTimeMs,
139                    entry.sipper.mobileRxPackets,
140                    entry.sipper.mobileTxPackets,
141                    entry.sipper.mobileActive,
142                    entry.sipper.wifiRxPackets,
143                    entry.sipper.wifiTxPackets,
144                    0,
145                    0,
146                    entry.sipper.cameraTimeMs,
147                    entry.sipper.flashlightTimeMs,
148                };
149
150                if (entry.sipper.drainType == BatterySipper.DrainType.APP) {
151                    Writer result = new StringWriter();
152                    PrintWriter printWriter = new FastPrintWriter(result, false, 1024);
153                    helper.getStats().dumpLocked(caller, printWriter, "", helper.getStatsType(),
154                            uid.getUid());
155                    printWriter.flush();
156                    args.putString(PowerUsageDetail.EXTRA_REPORT_DETAILS, result.toString());
157
158                    result = new StringWriter();
159                    printWriter = new FastPrintWriter(result, false, 1024);
160                    helper.getStats().dumpCheckinLocked(caller, printWriter, helper.getStatsType(),
161                            uid.getUid());
162                    printWriter.flush();
163                    args.putString(PowerUsageDetail.EXTRA_REPORT_CHECKIN_DETAILS,
164                            result.toString());
165                    userId = UserHandle.getUserId(uid.getUid());
166                }
167            }
168            break;
169            case CELL:
170            {
171                types = new int[] {
172                    R.string.usage_type_on_time,
173                    R.string.usage_type_no_coverage,
174                    R.string.usage_type_radio_active,
175                };
176                values = new double[] {
177                    entry.sipper.usageTimeMs,
178                    entry.sipper.noCoveragePercent,
179                    entry.sipper.mobileActive
180                };
181            }
182            break;
183            case WIFI:
184            {
185                types = new int[] {
186                    R.string.usage_type_wifi_running,
187                    R.string.usage_type_cpu,
188                    R.string.usage_type_cpu_foreground,
189                    R.string.usage_type_wake_lock,
190                    R.string.usage_type_data_recv,
191                    R.string.usage_type_data_send,
192                    R.string.usage_type_data_wifi_recv,
193                    R.string.usage_type_data_wifi_send,
194                };
195                values = new double[] {
196                    entry.sipper.usageTimeMs,
197                    entry.sipper.cpuTimeMs,
198                    entry.sipper.cpuFgTimeMs,
199                    entry.sipper.wakeLockTimeMs,
200                    entry.sipper.mobileRxPackets,
201                    entry.sipper.mobileTxPackets,
202                    entry.sipper.wifiRxPackets,
203                    entry.sipper.wifiTxPackets,
204                };
205            } break;
206            case BLUETOOTH:
207            {
208                types = new int[] {
209                    R.string.usage_type_on_time,
210                    R.string.usage_type_cpu,
211                    R.string.usage_type_cpu_foreground,
212                    R.string.usage_type_wake_lock,
213                    R.string.usage_type_data_recv,
214                    R.string.usage_type_data_send,
215                    R.string.usage_type_data_wifi_recv,
216                    R.string.usage_type_data_wifi_send,
217                };
218                values = new double[] {
219                    entry.sipper.usageTimeMs,
220                    entry.sipper.cpuTimeMs,
221                    entry.sipper.cpuFgTimeMs,
222                    entry.sipper.wakeLockTimeMs,
223                    entry.sipper.mobileRxPackets,
224                    entry.sipper.mobileTxPackets,
225                    entry.sipper.wifiRxPackets,
226                    entry.sipper.wifiTxPackets,
227                };
228            } break;
229            case UNACCOUNTED:
230            {
231                types = new int[] {
232                    R.string.usage_type_total_battery_capacity,
233                    R.string.usage_type_computed_power,
234                    R.string.usage_type_actual_power,
235                };
236                values = new double[] {
237                    helper.getPowerProfile().getBatteryCapacity(),
238                    helper.getComputedPower(),
239                    helper.getMinDrainedPower(),
240                };
241            } break;
242            case OVERCOUNTED:
243            {
244                types = new int[] {
245                    R.string.usage_type_total_battery_capacity,
246                    R.string.usage_type_computed_power,
247                    R.string.usage_type_actual_power,
248                };
249                values = new double[] {
250                    helper.getPowerProfile().getBatteryCapacity(),
251                    helper.getComputedPower(),
252                    helper.getMaxDrainedPower(),
253                };
254            } break;
255            default:
256            {
257                types = new int[] {
258                    R.string.usage_type_on_time
259                };
260                values = new double[] {
261                    entry.sipper.usageTimeMs
262                };
263            }
264        }
265        args.putIntArray(PowerUsageDetail.EXTRA_DETAIL_TYPES, types);
266        args.putDoubleArray(PowerUsageDetail.EXTRA_DETAIL_VALUES, values);
267
268        caller.startPreferencePanelAsUser(PowerUsageDetail.class.getName(), args,
269                R.string.details_title, null, new UserHandle(userId));
270    }
271
272    public static final int ACTION_DISPLAY_SETTINGS = 1;
273    public static final int ACTION_WIFI_SETTINGS = 2;
274    public static final int ACTION_BLUETOOTH_SETTINGS = 3;
275    public static final int ACTION_WIRELESS_SETTINGS = 4;
276    public static final int ACTION_APP_DETAILS = 5;
277    public static final int ACTION_LOCATION_SETTINGS = 6;
278    public static final int ACTION_FORCE_STOP = 7;
279    public static final int ACTION_REPORT = 8;
280
281    public static final int USAGE_SINCE_UNPLUGGED = 1;
282    public static final int USAGE_SINCE_RESET = 2;
283
284    public static final String EXTRA_TITLE = "title";
285    public static final String EXTRA_PERCENT = "percent";
286    public static final String EXTRA_GAUGE = "gauge";
287    public static final String EXTRA_UID = "uid";
288    public static final String EXTRA_USAGE_SINCE = "since";
289    public static final String EXTRA_USAGE_DURATION = "duration";
290    public static final String EXTRA_REPORT_DETAILS = "report_details";
291    public static final String EXTRA_REPORT_CHECKIN_DETAILS = "report_checkin_details";
292    public static final String EXTRA_DETAIL_TYPES = "types"; // Array of usage types (cpu, gps, etc)
293    public static final String EXTRA_DETAIL_VALUES = "values"; // Array of doubles
294    public static final String EXTRA_DRAIN_TYPE = "drainType"; // DrainType
295    public static final String EXTRA_ICON_PACKAGE = "iconPackage"; // String
296    public static final String EXTRA_NO_COVERAGE = "noCoverage";
297    public static final String EXTRA_ICON_ID = "iconId"; // Int
298    public static final String EXTRA_SHOW_LOCATION_BUTTON = "showLocationButton";  // Boolean
299
300    private static final String TAG = "PowerUsageDetail";
301
302    private static final String KEY_DETAILS_PARENT = "details_parent";
303    private static final String KEY_CONTROLS_PARENT = "controls_parent";
304    private static final String KEY_MESSAGES_PARENT = "messages_parent";
305    private static final String KEY_PACKAGES_PARENT = "packages_parent";
306    private static final String KEY_TWO_BUTTONS = "two_buttons";
307    private static final String KEY_HIGH_POWER = "high_power";
308
309    private PackageManager mPm;
310    private DevicePolicyManager mDpm;
311    private int mUsageSince;
312    private int[] mTypes;
313    private int mUid;
314    private double[] mValues;
315    private Button mForceStopButton;
316    private Button mReportButton;
317    private long mStartTime;
318    private BatterySipper.DrainType mDrainType;
319    private double mNoCoverage; // Percentage of time that there was no coverage
320
321    private PreferenceCategory mDetailsParent;
322    private PreferenceCategory mControlsParent;
323    private PreferenceCategory mMessagesParent;
324    private PreferenceCategory mPackagesParent;
325
326    private boolean mUsesGps;
327    private boolean mShowLocationButton;
328
329    private String[] mPackages;
330
331    ApplicationInfo mApp;
332    ComponentName mInstaller;
333    private Preference mHighPower;
334
335    @Override
336    public void onCreate(Bundle icicle) {
337        super.onCreate(icicle);
338        mPm = getActivity().getPackageManager();
339        mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
340
341        addPreferencesFromResource(R.xml.power_usage_details);
342        mDetailsParent = (PreferenceCategory) findPreference(KEY_DETAILS_PARENT);
343        mControlsParent = (PreferenceCategory) findPreference(KEY_CONTROLS_PARENT);
344        mMessagesParent = (PreferenceCategory) findPreference(KEY_MESSAGES_PARENT);
345        mPackagesParent = (PreferenceCategory) findPreference(KEY_PACKAGES_PARENT);
346
347        createDetails();
348    }
349
350    @Override
351    protected int getMetricsCategory() {
352        return MetricsLogger.FUELGAUGE_POWER_USAGE_DETAIL;
353    }
354
355    @Override
356    public void onResume() {
357        super.onResume();
358        mStartTime = android.os.Process.getElapsedCpuTime();
359        checkForceStop();
360        if (mHighPower != null) {
361            mHighPower.setSummary(HighPowerDetail.getSummary(getActivity(), mApp.packageName));
362        }
363
364        setupHeader();
365    }
366
367    private void createDetails() {
368        final Bundle args = getArguments();
369        Context context = getActivity();
370        mUsageSince = args.getInt(EXTRA_USAGE_SINCE, USAGE_SINCE_UNPLUGGED);
371        mUid = args.getInt(EXTRA_UID, 0);
372        mPackages = context.getPackageManager().getPackagesForUid(mUid);
373        mDrainType = (BatterySipper.DrainType) args.getSerializable(EXTRA_DRAIN_TYPE);
374        mNoCoverage = args.getDouble(EXTRA_NO_COVERAGE, 0);
375        mShowLocationButton = args.getBoolean(EXTRA_SHOW_LOCATION_BUTTON);
376
377        mTypes = args.getIntArray(EXTRA_DETAIL_TYPES);
378        mValues = args.getDoubleArray(EXTRA_DETAIL_VALUES);
379
380        LayoutPreference twoButtons = (LayoutPreference) findPreference(KEY_TWO_BUTTONS);
381        mForceStopButton = (Button) twoButtons.findViewById(R.id.left_button);
382        mReportButton = (Button) twoButtons.findViewById(R.id.right_button);
383        mForceStopButton.setEnabled(false);
384
385        if (mUid >= Process.FIRST_APPLICATION_UID) {
386            mForceStopButton.setText(R.string.force_stop);
387            mForceStopButton.setTag(ACTION_FORCE_STOP);
388            mForceStopButton.setOnClickListener(this);
389            mReportButton.setText(com.android.internal.R.string.report);
390            mReportButton.setTag(ACTION_REPORT);
391            mReportButton.setOnClickListener(this);
392
393            if (mPackages != null && mPackages.length > 0) {
394                try {
395                    mApp = context.getPackageManager().getApplicationInfo(
396                            mPackages[0], 0);
397                } catch (NameNotFoundException e) {
398                }
399            } else {
400                Log.d(TAG, "No packages!!");
401            }
402            // check if error reporting is enabled in secure settings
403            int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
404                    android.provider.Settings.Global.SEND_ACTION_APP_ERROR, 0);
405            if (enabled != 0) {
406                if (mApp != null) {
407                    mInstaller = ApplicationErrorReport.getErrorReportReceiver(
408                            context, mPackages[0], mApp.flags);
409                }
410                mReportButton.setEnabled(mInstaller != null);
411            } else {
412                removePreference(KEY_TWO_BUTTONS);
413            }
414            if (mApp != null) {
415                mHighPower = findPreference(KEY_HIGH_POWER);
416                mHighPower.setOnPreferenceClickListener(new OnPreferenceClickListener() {
417                    @Override
418                    public boolean onPreferenceClick(Preference preference) {
419                        AppInfoBase.startAppInfoFragment(HighPowerDetail.class,
420                                R.string.high_power_apps, mApp.packageName, mApp.uid,
421                                PowerUsageDetail.this, 0);
422                        return true;
423                    }
424                });
425            } else {
426                removePreference(KEY_HIGH_POWER);
427            }
428        } else {
429            removePreference(KEY_TWO_BUTTONS);
430            removePreference(KEY_HIGH_POWER);
431        }
432
433        refreshStats();
434
435        fillDetailsSection();
436        fillPackagesSection(mUid);
437        fillControlsSection(mUid);
438        fillMessagesSection(mUid);
439    }
440
441    private void setupHeader() {
442        final Bundle args = getArguments();
443        String title = args.getString(EXTRA_TITLE);
444        String pkg = args.getString(EXTRA_ICON_PACKAGE);
445        int iconId = args.getInt(EXTRA_ICON_ID, 0);
446        Drawable appIcon = null;
447
448        if (!TextUtils.isEmpty(pkg)) {
449            try {
450                final PackageManager pm = getActivity().getPackageManager();
451                ApplicationInfo ai = pm.getPackageInfo(pkg, 0).applicationInfo;
452                if (ai != null) {
453                    appIcon = ai.loadIcon(pm);
454                }
455            } catch (NameNotFoundException nnfe) {
456                // Use default icon
457            }
458        } else if (iconId != 0) {
459            appIcon = getActivity().getDrawable(iconId);
460        }
461        if (appIcon == null) {
462            appIcon = getActivity().getPackageManager().getDefaultActivityIcon();
463        }
464
465        if (pkg == null && mPackages != null) {
466            pkg = mPackages[0];
467        }
468        AppHeader.createAppHeader(this, appIcon, title,
469                pkg != null ? AppInfoWithHeader.getInfoIntent(this, pkg) : null,
470                mDrainType != DrainType.APP ? android.R.color.white : 0);
471    }
472
473    public void onClick(View v) {
474        doAction((Integer) v.getTag());
475    }
476
477    // utility method used to start sub activity
478    private void startApplicationDetailsActivity() {
479        // start new fragment to display extended information
480        Bundle args = new Bundle();
481        args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mPackages[0]);
482
483        SettingsActivity sa = (SettingsActivity) getActivity();
484        sa.startPreferencePanel(InstalledAppDetails.class.getName(), args,
485                R.string.application_info_label, null, null, 0);
486    }
487
488    private void doAction(int action) {
489        SettingsActivity sa = (SettingsActivity)getActivity();
490        switch (action) {
491            case ACTION_DISPLAY_SETTINGS:
492                sa.startPreferencePanel(DisplaySettings.class.getName(), null,
493                        R.string.display_settings_title, null, null, 0);
494                break;
495            case ACTION_WIFI_SETTINGS:
496                sa.startPreferencePanel(WifiSettings.class.getName(), null,
497                        R.string.wifi_settings, null, null, 0);
498                break;
499            case ACTION_BLUETOOTH_SETTINGS:
500                sa.startPreferencePanel(BluetoothSettings.class.getName(), null,
501                        R.string.bluetooth_settings, null, null, 0);
502                break;
503            case ACTION_WIRELESS_SETTINGS:
504                sa.startPreferencePanel(WirelessSettings.class.getName(), null,
505                        R.string.radio_controls_title, null, null, 0);
506                break;
507            case ACTION_APP_DETAILS:
508                startApplicationDetailsActivity();
509                break;
510            case ACTION_LOCATION_SETTINGS:
511                sa.startPreferencePanel(LocationSettings.class.getName(), null,
512                        R.string.location_settings_title, null, null, 0);
513                break;
514            case ACTION_FORCE_STOP:
515                killProcesses();
516                break;
517            case ACTION_REPORT:
518                reportBatteryUse();
519                break;
520        }
521    }
522
523    private void fillDetailsSection() {
524        if (mTypes != null && mValues != null) {
525            for (int i = 0; i < mTypes.length; i++) {
526                // Only add an item if the time is greater than zero
527                if (mValues[i] <= 0) continue;
528                final String label = getString(mTypes[i]);
529                String value = null;
530                switch (mTypes[i]) {
531                    case R.string.usage_type_data_recv:
532                    case R.string.usage_type_data_send:
533                    case R.string.usage_type_data_wifi_recv:
534                    case R.string.usage_type_data_wifi_send:
535                        final long packets = (long) (mValues[i]);
536                        value = Long.toString(packets);
537                        break;
538                    case R.string.usage_type_no_coverage:
539                        final int percentage = (int) Math.floor(mValues[i]);
540                        value = Utils.formatPercentage(percentage);
541                        break;
542                    case R.string.usage_type_total_battery_capacity:
543                    case R.string.usage_type_computed_power:
544                    case R.string.usage_type_actual_power:
545                        value = getActivity().getString(R.string.mah, (long)(mValues[i]));
546                        break;
547                    case R.string.usage_type_gps:
548                        mUsesGps = true;
549                        // Fall through
550                    default:
551                        value = Utils.formatElapsedTime(getActivity(), mValues[i], true);
552                }
553                addHorizontalPreference(mDetailsParent, label, value);
554            }
555        }
556    }
557
558    private void addHorizontalPreference(PreferenceCategory parent, CharSequence title,
559            CharSequence summary) {
560        Preference pref = new Preference(getActivity());
561        pref.setLayoutResource(R.layout.horizontal_preference);
562        pref.setTitle(title);
563        pref.setSummary(summary);
564        pref.setSelectable(false);
565        parent.addPreference(pref);
566    }
567
568    private void fillControlsSection(int uid) {
569        PackageManager pm = getActivity().getPackageManager();
570        String[] packages = pm.getPackagesForUid(uid);
571        PackageInfo pi = null;
572        try {
573            pi = packages != null ? pm.getPackageInfo(packages[0], 0) : null;
574        } catch (NameNotFoundException nnfe) { /* Nothing */ }
575        ApplicationInfo ai = pi != null? pi.applicationInfo : null;
576
577        boolean removeHeader = true;
578        switch (mDrainType) {
579            case APP:
580                // If it is a Java application and only one package is associated with the Uid
581                if (packages != null && packages.length == 1) {
582                    addControl(R.string.battery_action_app_details,
583                            R.string.battery_sugg_apps_info, ACTION_APP_DETAILS);
584                    removeHeader = false;
585                    // If the application has a settings screen, jump to  that
586                    // TODO:
587                }
588                // If power usage detail page is launched from location page, suppress "Location"
589                // button to prevent circular loops.
590                if (mUsesGps && mShowLocationButton) {
591                    addControl(R.string.location_settings_title,
592                            R.string.battery_sugg_apps_gps, ACTION_LOCATION_SETTINGS);
593                    removeHeader = false;
594                }
595                break;
596            case SCREEN:
597                addControl(R.string.display_settings,
598                        R.string.battery_sugg_display,
599                        ACTION_DISPLAY_SETTINGS);
600                removeHeader = false;
601                break;
602            case WIFI:
603                addControl(R.string.wifi_settings,
604                        R.string.battery_sugg_wifi,
605                        ACTION_WIFI_SETTINGS);
606                removeHeader = false;
607                break;
608            case BLUETOOTH:
609                addControl(R.string.bluetooth_settings,
610                        R.string.battery_sugg_bluetooth_basic,
611                        ACTION_BLUETOOTH_SETTINGS);
612                removeHeader = false;
613                break;
614            case CELL:
615                if (mNoCoverage > 10) {
616                    addControl(R.string.radio_controls_title,
617                            R.string.battery_sugg_radio,
618                            ACTION_WIRELESS_SETTINGS);
619                    removeHeader = false;
620                }
621                break;
622        }
623        if (removeHeader) {
624            mControlsParent.setTitle(null);
625        }
626    }
627
628    private void addControl(int pageSummary, int actionTitle, final int action) {
629        Preference pref = new Preference(getActivity());
630        pref.setTitle(actionTitle);
631        pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
632            @Override
633            public boolean onPreferenceClick(Preference preference) {
634                doAction(action);
635                return true;
636            }
637        });
638        mControlsParent.addPreference(pref);
639    }
640
641    private void fillMessagesSection(int uid) {
642        boolean removeHeader = true;
643        switch (mDrainType) {
644            case UNACCOUNTED:
645                addMessage(R.string.battery_msg_unaccounted);
646                removeHeader = false;
647                break;
648        }
649        if (removeHeader) {
650            mMessagesParent.setTitle(null);
651        }
652    }
653
654    private void addMessage(int message) {
655        addHorizontalPreference(mMessagesParent, getString(message), null);
656    }
657
658    private void removePackagesSection() {
659        getPreferenceScreen().removePreference(mPackagesParent);
660    }
661
662    private void killProcesses() {
663        if (mPackages == null) return;
664        ActivityManager am = (ActivityManager)getActivity().getSystemService(
665                Context.ACTIVITY_SERVICE);
666        final int userId = UserHandle.getUserId(mUid);
667        for (int i = 0; i < mPackages.length; i++) {
668            am.forceStopPackageAsUser(mPackages[i], userId);
669        }
670        checkForceStop();
671    }
672
673    private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
674        @Override
675        public void onReceive(Context context, Intent intent) {
676            mForceStopButton.setEnabled(getResultCode() != Activity.RESULT_CANCELED);
677        }
678    };
679
680    private void checkForceStop() {
681        if (mPackages == null || mUid < Process.FIRST_APPLICATION_UID) {
682            mForceStopButton.setEnabled(false);
683            return;
684        }
685        for (int i = 0; i < mPackages.length; i++) {
686            if (mDpm.packageHasActiveAdmins(mPackages[i])) {
687                mForceStopButton.setEnabled(false);
688                return;
689            }
690        }
691        for (int i = 0; i < mPackages.length; i++) {
692            try {
693                ApplicationInfo info = mPm.getApplicationInfo(mPackages[i], 0);
694                if ((info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
695                    mForceStopButton.setEnabled(true);
696                    break;
697                }
698            } catch (PackageManager.NameNotFoundException e) {
699            }
700        }
701        Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
702                Uri.fromParts("package", mPackages[0], null));
703        intent.putExtra(Intent.EXTRA_PACKAGES, mPackages);
704        intent.putExtra(Intent.EXTRA_UID, mUid);
705        intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mUid));
706        getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
707                Activity.RESULT_CANCELED, null, null);
708    }
709
710    private void reportBatteryUse() {
711        if (mPackages == null) return;
712
713        ApplicationErrorReport report = new ApplicationErrorReport();
714        report.type = ApplicationErrorReport.TYPE_BATTERY;
715        report.packageName = mPackages[0];
716        report.installerPackageName = mInstaller.getPackageName();
717        report.processName = mPackages[0];
718        report.time = System.currentTimeMillis();
719        report.systemApp = (mApp.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
720
721        final Bundle args = getArguments();
722        ApplicationErrorReport.BatteryInfo batteryInfo = new ApplicationErrorReport.BatteryInfo();
723        batteryInfo.usagePercent = args.getInt(EXTRA_PERCENT, 1);
724        batteryInfo.durationMicros = args.getLong(EXTRA_USAGE_DURATION, 0);
725        batteryInfo.usageDetails = args.getString(EXTRA_REPORT_DETAILS);
726        batteryInfo.checkinDetails = args.getString(EXTRA_REPORT_CHECKIN_DETAILS);
727        report.batteryInfo = batteryInfo;
728
729        Intent result = new Intent(Intent.ACTION_APP_ERROR);
730        result.setComponent(mInstaller);
731        result.putExtra(Intent.EXTRA_BUG_REPORT, report);
732        result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
733        startActivity(result);
734    }
735
736    private void fillPackagesSection(int uid) {
737        if (uid < 1) {
738            removePackagesSection();
739            return;
740        }
741        if (mPackages == null || mPackages.length < 2) {
742            removePackagesSection();
743            return;
744        }
745
746        PackageManager pm = getPackageManager();
747        // Convert package names to user-facing labels where possible
748        for (int i = 0; i < mPackages.length; i++) {
749            try {
750                ApplicationInfo ai = pm.getApplicationInfo(mPackages[i], 0);
751                CharSequence label = ai.loadLabel(pm);
752                if (label != null) {
753                    mPackages[i] = label.toString();
754                }
755                addHorizontalPreference(mPackagesParent, mPackages[i], null);
756            } catch (NameNotFoundException e) {
757            }
758        }
759    }
760}
761