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