Home | History | Annotate | Download | only in fuelgauge
      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 
     17 package com.android.settings.fuelgauge;
     18 
     19 import android.app.Activity;
     20 import android.app.ActivityManager;
     21 import android.app.ApplicationErrorReport;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ComponentName;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.pm.ApplicationInfo;
     27 import android.content.pm.PackageInfo;
     28 import android.content.pm.PackageManager;
     29 import android.content.pm.PackageManager.NameNotFoundException;
     30 import android.content.res.Resources;
     31 import android.graphics.drawable.Drawable;
     32 import android.net.Uri;
     33 import android.os.Bundle;
     34 import android.os.Process;
     35 import android.provider.Settings;
     36 import android.text.TextUtils;
     37 import android.view.LayoutInflater;
     38 import android.view.View;
     39 import android.view.ViewGroup;
     40 import android.widget.Button;
     41 import android.widget.ImageView;
     42 import android.widget.TextView;
     43 import com.android.settings.R;
     44 import com.android.settings.applications.InstalledAppDetails;
     45 import com.android.settings.applications.ManageApplications;
     46 
     47 public class PowerUsageDetail extends Activity implements Button.OnClickListener {
     48 
     49     enum DrainType {
     50         IDLE,
     51         CELL,
     52         PHONE,
     53         WIFI,
     54         BLUETOOTH,
     55         SCREEN,
     56         APP
     57     }
     58 
     59     // Note: Must match the sequence of the DrainType
     60     private static int[] sDrainTypeDesciptions = new int[] {
     61         R.string.battery_desc_standby,
     62         R.string.battery_desc_radio,
     63         R.string.battery_desc_voice,
     64         R.string.battery_desc_wifi,
     65         R.string.battery_desc_bluetooth,
     66         R.string.battery_desc_display,
     67         R.string.battery_desc_apps
     68     };
     69 
     70     public static final int ACTION_DISPLAY_SETTINGS = 1;
     71     public static final int ACTION_WIFI_SETTINGS = 2;
     72     public static final int ACTION_BLUETOOTH_SETTINGS = 3;
     73     public static final int ACTION_WIRELESS_SETTINGS = 4;
     74     public static final int ACTION_APP_DETAILS = 5;
     75     public static final int ACTION_SECURITY_SETTINGS = 6;
     76     public static final int ACTION_FORCE_STOP = 7;
     77     public static final int ACTION_REPORT = 8;
     78 
     79     public static final int USAGE_SINCE_UNPLUGGED = 1;
     80     public static final int USAGE_SINCE_RESET = 2;
     81 
     82     public static final String EXTRA_TITLE = "title";
     83     public static final String EXTRA_PERCENT = "percent";
     84     public static final String EXTRA_GAUGE = "gauge";
     85     public static final String EXTRA_UID = "uid";
     86     public static final String EXTRA_USAGE_SINCE = "since";
     87     public static final String EXTRA_USAGE_DURATION = "duration";
     88     public static final String EXTRA_REPORT_DETAILS = "report_details";
     89     public static final String EXTRA_REPORT_CHECKIN_DETAILS = "report_checkin_details";
     90     public static final String EXTRA_DETAIL_TYPES = "types"; // Array of usage types (cpu, gps, etc)
     91     public static final String EXTRA_DETAIL_VALUES = "values"; // Array of doubles
     92     public static final String EXTRA_DRAIN_TYPE = "drainType"; // DrainType
     93     public static final String EXTRA_ICON_PACKAGE = "iconPackage"; // String
     94     public static final String EXTRA_NO_COVERAGE = "noCoverage";
     95     public static final String EXTRA_ICON_ID = "iconId"; // Int
     96 
     97     private static final boolean DEBUG = true;
     98     private String mTitle;
     99     private int mUsageSince;
    100     private int[] mTypes;
    101     private int mUid;
    102     private double[] mValues;
    103     private TextView mTitleView;
    104     private ViewGroup mTwoButtonsPanel;
    105     private Button mForceStopButton;
    106     private Button mReportButton;
    107     private ViewGroup mDetailsParent;
    108     private ViewGroup mControlsParent;
    109     private long mStartTime;
    110     private DrainType mDrainType;
    111     private PercentageBar mGauge;
    112     private Drawable mAppIcon;
    113     private double mNoCoverage; // Percentage of time that there was no coverage
    114 
    115     private boolean mUsesGps;
    116 
    117     private static final String TAG = "PowerUsageDetail";
    118     private String[] mPackages;
    119 
    120     ApplicationInfo mApp;
    121     ComponentName mInstaller;
    122 
    123     @Override
    124     protected void onCreate(Bundle icicle) {
    125         super.onCreate(icicle);
    126         setContentView(R.layout.power_usage_details);
    127         createDetails();
    128     }
    129 
    130     @Override
    131     protected void onResume() {
    132         super.onResume();
    133         mStartTime = android.os.Process.getElapsedCpuTime();
    134         checkForceStop();
    135     }
    136 
    137     @Override
    138     protected void onPause() {
    139         super.onPause();
    140     }
    141 
    142     private void createDetails() {
    143         final Intent intent = getIntent();
    144         mTitle = intent.getStringExtra(EXTRA_TITLE);
    145         final int percentage = intent.getIntExtra(EXTRA_PERCENT, 1);
    146         final int gaugeValue = intent.getIntExtra(EXTRA_GAUGE, 1);
    147         mUsageSince = intent.getIntExtra(EXTRA_USAGE_SINCE, USAGE_SINCE_UNPLUGGED);
    148         mUid = intent.getIntExtra(EXTRA_UID, 0);
    149         mDrainType = (DrainType) intent.getSerializableExtra(EXTRA_DRAIN_TYPE);
    150         mNoCoverage = intent.getDoubleExtra(EXTRA_NO_COVERAGE, 0);
    151         String iconPackage = intent.getStringExtra(EXTRA_ICON_PACKAGE);
    152         int iconId = intent.getIntExtra(EXTRA_ICON_ID, 0);
    153         if (!TextUtils.isEmpty(iconPackage)) {
    154             try {
    155                 final PackageManager pm = getPackageManager();
    156                 ApplicationInfo ai = pm.getPackageInfo(iconPackage, 0).applicationInfo;
    157                 if (ai != null) {
    158                     mAppIcon = ai.loadIcon(pm);
    159                 }
    160             } catch (NameNotFoundException nnfe) {
    161                 // Use default icon
    162             }
    163         } else if (iconId != 0) {
    164             mAppIcon = getResources().getDrawable(iconId);
    165         }
    166         if (mAppIcon == null) {
    167             mAppIcon = getPackageManager().getDefaultActivityIcon();
    168         }
    169 
    170         // Set the description
    171         String summary = getDescriptionForDrainType();
    172         ((TextView)findViewById(R.id.summary)).setText(summary);
    173 
    174         mTypes = intent.getIntArrayExtra(EXTRA_DETAIL_TYPES);
    175         mValues = intent.getDoubleArrayExtra(EXTRA_DETAIL_VALUES);
    176 
    177         mTitleView = (TextView) findViewById(R.id.name);
    178         mTitleView.setText(mTitle);
    179         ((TextView)findViewById(R.id.battery_percentage))
    180             .setText(String.format("%d%%", percentage));
    181 
    182         mTwoButtonsPanel = (ViewGroup) findViewById(R.id.two_buttons_panel);
    183         mForceStopButton = (Button) findViewById(R.id.left_button);
    184         mReportButton = (Button) findViewById(R.id.right_button);
    185         mForceStopButton.setEnabled(false);
    186 
    187         ImageView gaugeImage = (ImageView) findViewById(R.id.gauge);
    188         mGauge = new PercentageBar();
    189         mGauge.percent = gaugeValue;
    190         mGauge.bar = getResources().getDrawable(R.drawable.app_gauge);
    191         gaugeImage.setImageDrawable(mGauge);
    192 
    193         ImageView iconImage = (ImageView) findViewById(R.id.icon);
    194         iconImage.setImageDrawable(mAppIcon);
    195 
    196         mDetailsParent = (ViewGroup) findViewById(R.id.details);
    197         mControlsParent = (ViewGroup) findViewById(R.id.controls);
    198 
    199         fillDetailsSection();
    200         fillPackagesSection(mUid);
    201         fillControlsSection(mUid);
    202 
    203         if (mUid >= Process.FIRST_APPLICATION_UID) {
    204             mForceStopButton.setText(R.string.force_stop);
    205             mForceStopButton.setTag(ACTION_FORCE_STOP);
    206             mForceStopButton.setOnClickListener(this);
    207             mReportButton.setText(com.android.internal.R.string.report);
    208             mReportButton.setTag(ACTION_REPORT);
    209             mReportButton.setOnClickListener(this);
    210 
    211             // check if error reporting is enabled in secure settings
    212             int enabled = Settings.Secure.getInt(getContentResolver(),
    213                     Settings.Secure.SEND_ACTION_APP_ERROR, 0);
    214             if (enabled != 0) {
    215                 if (mPackages != null && mPackages.length > 0) {
    216                     try {
    217                         mApp = getPackageManager().getApplicationInfo(mPackages[0], 0);
    218                         mInstaller = ApplicationErrorReport.getErrorReportReceiver(
    219                                 this, mPackages[0], mApp.flags);
    220                     } catch (NameNotFoundException e) {
    221                     }
    222                 }
    223                 mReportButton.setEnabled(mInstaller != null);
    224             } else {
    225                 mTwoButtonsPanel.setVisibility(View.GONE);
    226             }
    227         } else {
    228             mTwoButtonsPanel.setVisibility(View.GONE);
    229         }
    230     }
    231 
    232     public void onClick(View v) {
    233         doAction((Integer) v.getTag());
    234     }
    235 
    236     private void doAction(int action) {
    237         switch (action) {
    238             case ACTION_DISPLAY_SETTINGS:
    239                 startActivity(new Intent(Settings.ACTION_DISPLAY_SETTINGS));
    240                 break;
    241             case ACTION_WIFI_SETTINGS:
    242                 startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
    243                 break;
    244             case ACTION_BLUETOOTH_SETTINGS:
    245                 startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
    246                 break;
    247             case ACTION_WIRELESS_SETTINGS:
    248                 startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
    249                 break;
    250             case ACTION_APP_DETAILS:
    251                 Intent intent = new Intent(Intent.ACTION_VIEW,
    252                         Uri.fromParts("package", mPackages[0], null));
    253                 intent.setClass(this, InstalledAppDetails.class);
    254                 startActivity(intent);
    255                 break;
    256             case ACTION_SECURITY_SETTINGS:
    257                 startActivity(new Intent(Settings.ACTION_SECURITY_SETTINGS));
    258                 break;
    259             case ACTION_FORCE_STOP:
    260                 killProcesses();
    261                 break;
    262             case ACTION_REPORT:
    263                 reportBatteryUse();
    264                 break;
    265         }
    266     }
    267 
    268     private void fillDetailsSection() {
    269         LayoutInflater inflater = getLayoutInflater();
    270         if (mTypes != null && mValues != null) {
    271             for (int i = 0; i < mTypes.length; i++) {
    272                 // Only add an item if the time is greater than zero
    273                 if (mValues[i] <= 0) continue;
    274                 final String label = getString(mTypes[i]);
    275                 String value = null;
    276                 switch (mTypes[i]) {
    277                     case R.string.usage_type_data_recv:
    278                     case R.string.usage_type_data_send:
    279                         value = Utils.formatBytes(this, mValues[i]);
    280                         break;
    281                     case R.string.usage_type_no_coverage:
    282                         value = String.format("%d%%", (int) Math.floor(mValues[i]));
    283                         break;
    284                     case R.string.usage_type_gps:
    285                         mUsesGps = true;
    286                         // Fall through
    287                     default:
    288                         value = Utils.formatElapsedTime(this, mValues[i]);
    289                 }
    290                 ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_detail_item_text,
    291                         null);
    292                 mDetailsParent.addView(item);
    293                 TextView labelView = (TextView) item.findViewById(R.id.label);
    294                 TextView valueView = (TextView) item.findViewById(R.id.value);
    295                 labelView.setText(label);
    296                 valueView.setText(value);
    297             }
    298         }
    299     }
    300 
    301     private void fillControlsSection(int uid) {
    302         PackageManager pm = getPackageManager();
    303         String[] packages = pm.getPackagesForUid(uid);
    304         PackageInfo pi = null;
    305         try {
    306             pi = packages != null ? pm.getPackageInfo(packages[0], 0) : null;
    307         } catch (NameNotFoundException nnfe) { /* Nothing */ }
    308         ApplicationInfo ai = pi != null? pi.applicationInfo : null;
    309         boolean isSystem = ai != null? (ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0 : false;
    310 
    311         boolean removeHeader = true;
    312         switch (mDrainType) {
    313             case APP:
    314                 // If it is a Java application and it's not a system application
    315                 if (packages != null && !isSystem) {
    316                     addControl(R.string.battery_action_app_details,
    317                             R.string.battery_sugg_apps_info, ACTION_APP_DETAILS);
    318                     removeHeader = false;
    319                     // If the application has a settings screen, jump to  that
    320                     // TODO:
    321                 }
    322                 if (mUsesGps) {
    323                     addControl(R.string.security_settings_title,
    324                             R.string.battery_sugg_apps_gps, ACTION_SECURITY_SETTINGS);
    325                     removeHeader = false;
    326                 }
    327                 break;
    328             case SCREEN:
    329                 addControl(R.string.display_settings,
    330                         R.string.battery_sugg_display,
    331                         ACTION_DISPLAY_SETTINGS);
    332                 removeHeader = false;
    333                 break;
    334             case WIFI:
    335                 addControl(R.string.wifi_settings,
    336                         R.string.battery_sugg_wifi,
    337                         ACTION_WIFI_SETTINGS);
    338                 removeHeader = false;
    339                 break;
    340             case BLUETOOTH:
    341                 addControl(R.string.bluetooth_settings,
    342                         R.string.battery_sugg_bluetooth_basic,
    343                         ACTION_BLUETOOTH_SETTINGS);
    344                 removeHeader = false;
    345                 break;
    346             case CELL:
    347                 if (mNoCoverage > 10) {
    348                     addControl(R.string.radio_controls_title,
    349                             R.string.battery_sugg_radio,
    350                             ACTION_WIRELESS_SETTINGS);
    351                     removeHeader = false;
    352                 }
    353                 break;
    354         }
    355         if (removeHeader) {
    356             mControlsParent.setVisibility(View.GONE);
    357         }
    358     }
    359 
    360     private void addControl(int title, int summary, int action) {
    361         final Resources res = getResources();
    362         LayoutInflater inflater = getLayoutInflater();
    363         ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_action_item,null);
    364         mControlsParent.addView(item);
    365         Button actionButton = (Button) item.findViewById(R.id.action_button);
    366         TextView summaryView = (TextView) item.findViewById(R.id.summary);
    367         actionButton.setText(res.getString(title));
    368         summaryView.setText(res.getString(summary));
    369         actionButton.setOnClickListener(this);
    370         actionButton.setTag(new Integer(action));
    371     }
    372 
    373     private void removePackagesSection() {
    374         View view;
    375         if ((view = findViewById(R.id.packages_section_title)) != null) {
    376             view.setVisibility(View.GONE);
    377         }
    378         if ((view = findViewById(R.id.packages_section)) != null) {
    379             view.setVisibility(View.GONE);
    380         }
    381     }
    382 
    383     private void killProcesses() {
    384         if (mPackages == null) return;
    385         ActivityManager am = (ActivityManager)getSystemService(
    386                 Context.ACTIVITY_SERVICE);
    387         for (int i = 0; i < mPackages.length; i++) {
    388             am.forceStopPackage(mPackages[i]);
    389         }
    390         checkForceStop();
    391     }
    392 
    393     private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
    394         @Override
    395         public void onReceive(Context context, Intent intent) {
    396             mForceStopButton.setEnabled(getResultCode() != RESULT_CANCELED);
    397         }
    398     };
    399 
    400     private void checkForceStop() {
    401         if (mPackages == null || mUid < Process.FIRST_APPLICATION_UID) {
    402             mForceStopButton.setEnabled(false);
    403             return;
    404         }
    405         Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
    406                 Uri.fromParts("package", mPackages[0], null));
    407         intent.putExtra(Intent.EXTRA_PACKAGES, mPackages);
    408         intent.putExtra(Intent.EXTRA_UID, mUid);
    409         sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
    410                 Activity.RESULT_CANCELED, null, null);
    411     }
    412 
    413     private void reportBatteryUse() {
    414         if (mPackages == null) return;
    415 
    416         ApplicationErrorReport report = new ApplicationErrorReport();
    417         report.type = ApplicationErrorReport.TYPE_BATTERY;
    418         report.packageName = mPackages[0];
    419         report.installerPackageName = mInstaller.getPackageName();
    420         report.processName = mPackages[0];
    421         report.time = System.currentTimeMillis();
    422         report.systemApp = (mApp.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
    423 
    424         final Intent intent = getIntent();
    425         ApplicationErrorReport.BatteryInfo batteryInfo = new ApplicationErrorReport.BatteryInfo();
    426         batteryInfo.usagePercent = intent.getIntExtra(EXTRA_PERCENT, 1);
    427         batteryInfo.durationMicros = intent.getLongExtra(EXTRA_USAGE_DURATION, 0);
    428         batteryInfo.usageDetails = intent.getStringExtra(EXTRA_REPORT_DETAILS);
    429         batteryInfo.checkinDetails = intent.getStringExtra(EXTRA_REPORT_CHECKIN_DETAILS);
    430         report.batteryInfo = batteryInfo;
    431 
    432         Intent result = new Intent(Intent.ACTION_APP_ERROR);
    433         result.setComponent(mInstaller);
    434         result.putExtra(Intent.EXTRA_BUG_REPORT, report);
    435         result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    436         startActivity(result);
    437     }
    438 
    439     private void fillPackagesSection(int uid) {
    440         if (uid < 1) {
    441             removePackagesSection();
    442             return;
    443         }
    444         ViewGroup packagesParent = (ViewGroup) findViewById(R.id.packages_section);
    445         if (packagesParent == null) return;
    446         LayoutInflater inflater = getLayoutInflater();
    447 
    448         PackageManager pm = getPackageManager();
    449         //final Drawable defaultActivityIcon = pm.getDefaultActivityIcon();
    450         mPackages = pm.getPackagesForUid(uid);
    451         if (mPackages == null || mPackages.length < 2) {
    452             removePackagesSection();
    453             return;
    454         }
    455 
    456         // Convert package names to user-facing labels where possible
    457         for (int i = 0; i < mPackages.length; i++) {
    458             try {
    459                 ApplicationInfo ai = pm.getApplicationInfo(mPackages[i], 0);
    460                 CharSequence label = ai.loadLabel(pm);
    461                 //Drawable icon = defaultActivityIcon;
    462                 if (label != null) {
    463                     mPackages[i] = label.toString();
    464                 }
    465                 //if (ai.icon != 0) {
    466                 //    icon = ai.loadIcon(pm);
    467                 //}
    468                 ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_package_item,
    469                         null);
    470                 packagesParent.addView(item);
    471                 TextView labelView = (TextView) item.findViewById(R.id.label);
    472                 labelView.setText(mPackages[i]);
    473             } catch (NameNotFoundException e) {
    474             }
    475         }
    476     }
    477 
    478     private String getDescriptionForDrainType() {
    479         return getResources().getString(sDrainTypeDesciptions[mDrainType.ordinal()]);
    480     }
    481 }
    482