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