• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.annotation.UserIdInt;
20 import android.app.Activity;
21 import android.app.ActivityManager;
22 import android.app.LoaderManager;
23 import android.app.admin.DevicePolicyManager;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.Loader;
27 import android.content.pm.PackageManager;
28 import android.os.BatteryStats;
29 import android.os.Bundle;
30 import android.os.UserHandle;
31 import android.os.UserManager;
32 import android.support.annotation.VisibleForTesting;
33 import android.support.v7.preference.Preference;
34 import android.text.TextUtils;
35 import android.util.Log;
36 import android.view.View;
37 
38 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
39 import com.android.internal.os.BatterySipper;
40 import com.android.internal.os.BatteryStatsHelper;
41 import com.android.internal.util.ArrayUtils;
42 import com.android.settings.R;
43 import com.android.settings.SettingsActivity;
44 import com.android.settings.Utils;
45 import com.android.settings.applications.LayoutPreference;
46 import com.android.settings.core.InstrumentedPreferenceFragment;
47 import com.android.settings.core.SubSettingLauncher;
48 import com.android.settings.dashboard.DashboardFragment;
49 import com.android.settings.fuelgauge.anomaly.Anomaly;
50 import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment;
51 import com.android.settings.fuelgauge.anomaly.AnomalyLoader;
52 import com.android.settings.fuelgauge.anomaly.AnomalySummaryPreferenceController;
53 import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
54 import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
55 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
56 import com.android.settings.widget.EntityHeaderController;
57 import com.android.settingslib.applications.AppUtils;
58 import com.android.settingslib.applications.ApplicationsState;
59 import com.android.settingslib.core.AbstractPreferenceController;
60 import com.android.settingslib.utils.StringUtil;
61 
62 import java.util.ArrayList;
63 import java.util.List;
64 
65 /**
66  * Power usage detail fragment for each app, this fragment contains
67  *
68  * 1. Detail battery usage information for app(i.e. usage time, usage amount)
69  * 2. Battery related controls for app(i.e uninstall, force stop)
70  */
71 public class AdvancedPowerUsageDetail extends DashboardFragment implements
72         ButtonActionDialogFragment.AppButtonsDialogListener,
73         AnomalyDialogFragment.AnomalyDialogListener,
74         LoaderManager.LoaderCallbacks<List<Anomaly>>,
75         BatteryTipPreferenceController.BatteryTipListener {
76 
77     public static final String TAG = "AdvancedPowerDetail";
78     public static final String EXTRA_UID = "extra_uid";
79     public static final String EXTRA_PACKAGE_NAME = "extra_package_name";
80     public static final String EXTRA_FOREGROUND_TIME = "extra_foreground_time";
81     public static final String EXTRA_BACKGROUND_TIME = "extra_background_time";
82     public static final String EXTRA_LABEL = "extra_label";
83     public static final String EXTRA_ICON_ID = "extra_icon_id";
84     public static final String EXTRA_POWER_USAGE_PERCENT = "extra_power_usage_percent";
85     public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount";
86     public static final String EXTRA_ANOMALY_LIST = "extra_anomaly_list";
87 
88     private static final String KEY_PREF_FOREGROUND = "app_usage_foreground";
89     private static final String KEY_PREF_BACKGROUND = "app_usage_background";
90     private static final String KEY_PREF_HEADER = "header_view";
91 
92     private static final int REQUEST_UNINSTALL = 0;
93     private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
94 
95     private static final int ANOMALY_LOADER = 0;
96 
97     @VisibleForTesting
98     LayoutPreference mHeaderPreference;
99     @VisibleForTesting
100     ApplicationsState mState;
101     @VisibleForTesting
102     ApplicationsState.AppEntry mAppEntry;
103     @VisibleForTesting
104     BatteryUtils mBatteryUtils;
105 
106     @VisibleForTesting
107     Preference mForegroundPreference;
108     @VisibleForTesting
109     Preference mBackgroundPreference;
110     @VisibleForTesting
111     AnomalySummaryPreferenceController mAnomalySummaryPreferenceController;
112     private AppButtonsPreferenceController mAppButtonsPreferenceController;
113     private BackgroundActivityPreferenceController mBackgroundActivityPreferenceController;
114 
115     private DevicePolicyManager mDpm;
116     private UserManager mUserManager;
117     private PackageManager mPackageManager;
118     private List<Anomaly> mAnomalies;
119     private String mPackageName;
120 
121     @VisibleForTesting
startBatteryDetailPage(Activity caller, BatteryUtils batteryUtils, InstrumentedPreferenceFragment fragment, BatteryStatsHelper helper, int which, BatteryEntry entry, String usagePercent, List<Anomaly> anomalies)122     static void startBatteryDetailPage(Activity caller, BatteryUtils batteryUtils,
123             InstrumentedPreferenceFragment fragment, BatteryStatsHelper helper, int which,
124             BatteryEntry entry, String usagePercent, List<Anomaly> anomalies) {
125         // Initialize mStats if necessary.
126         helper.getStats();
127 
128         final Bundle args = new Bundle();
129         final BatterySipper sipper = entry.sipper;
130         final BatteryStats.Uid uid = sipper.uidObj;
131         final boolean isTypeApp = sipper.drainType == BatterySipper.DrainType.APP;
132 
133         final long foregroundTimeMs = isTypeApp ? batteryUtils.getProcessTimeMs(
134                 BatteryUtils.StatusType.FOREGROUND, uid, which) : sipper.usageTimeMs;
135         final long backgroundTimeMs = isTypeApp ? batteryUtils.getProcessTimeMs(
136                 BatteryUtils.StatusType.BACKGROUND, uid, which) : 0;
137 
138         if (ArrayUtils.isEmpty(sipper.mPackages)) {
139             // populate data for system app
140             args.putString(EXTRA_LABEL, entry.getLabel());
141             args.putInt(EXTRA_ICON_ID, entry.iconId);
142             args.putString(EXTRA_PACKAGE_NAME, null);
143         } else {
144             // populate data for normal app
145             args.putString(EXTRA_PACKAGE_NAME, entry.defaultPackageName != null
146                     ? entry.defaultPackageName
147                     : sipper.mPackages[0]);
148         }
149 
150         args.putInt(EXTRA_UID, sipper.getUid());
151         args.putLong(EXTRA_BACKGROUND_TIME, backgroundTimeMs);
152         args.putLong(EXTRA_FOREGROUND_TIME, foregroundTimeMs);
153         args.putString(EXTRA_POWER_USAGE_PERCENT, usagePercent);
154         args.putInt(EXTRA_POWER_USAGE_AMOUNT, (int) sipper.totalPowerMah);
155         args.putParcelableList(EXTRA_ANOMALY_LIST, anomalies);
156 
157         new SubSettingLauncher(caller)
158                 .setDestination(AdvancedPowerUsageDetail.class.getName())
159                 .setTitle(R.string.battery_details_title)
160                 .setArguments(args)
161                 .setSourceMetricsCategory(fragment.getMetricsCategory())
162                 .setUserHandle(new UserHandle(getUserIdToLaunchAdvancePowerUsageDetail(sipper)))
163                 .launch();
164     }
165 
166     private static @UserIdInt
getUserIdToLaunchAdvancePowerUsageDetail(BatterySipper bs)167     int getUserIdToLaunchAdvancePowerUsageDetail(BatterySipper bs) {
168         if (bs.drainType == BatterySipper.DrainType.USER) {
169             return ActivityManager.getCurrentUser();
170         }
171         return UserHandle.getUserId(bs.getUid());
172     }
173 
startBatteryDetailPage(Activity caller, InstrumentedPreferenceFragment fragment, BatteryStatsHelper helper, int which, BatteryEntry entry, String usagePercent, List<Anomaly> anomalies)174     public static void startBatteryDetailPage(Activity caller,
175             InstrumentedPreferenceFragment fragment, BatteryStatsHelper helper, int which,
176             BatteryEntry entry, String usagePercent, List<Anomaly> anomalies) {
177         startBatteryDetailPage(caller, BatteryUtils.getInstance(caller), fragment, helper, which,
178                 entry, usagePercent, anomalies);
179     }
180 
startBatteryDetailPage(Activity caller, InstrumentedPreferenceFragment fragment, String packageName)181     public static void startBatteryDetailPage(Activity caller,
182             InstrumentedPreferenceFragment fragment, String packageName) {
183         final Bundle args = new Bundle(3);
184         final PackageManager packageManager = caller.getPackageManager();
185         args.putString(EXTRA_PACKAGE_NAME, packageName);
186         args.putString(EXTRA_POWER_USAGE_PERCENT, Utils.formatPercentage(0));
187         try {
188             args.putInt(EXTRA_UID, packageManager.getPackageUid(packageName, 0 /* no flag */));
189         } catch (PackageManager.NameNotFoundException e) {
190             Log.e(TAG, "Cannot find package: " + packageName, e);
191         }
192 
193         new SubSettingLauncher(caller)
194                 .setDestination(AdvancedPowerUsageDetail.class.getName())
195                 .setTitle(R.string.battery_details_title)
196                 .setArguments(args)
197                 .setSourceMetricsCategory(fragment.getMetricsCategory())
198                 .launch();
199     }
200 
201     @Override
onAttach(Activity activity)202     public void onAttach(Activity activity) {
203         super.onAttach(activity);
204 
205         mState = ApplicationsState.getInstance(getActivity().getApplication());
206         mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
207         mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
208         mPackageManager = activity.getPackageManager();
209         mBatteryUtils = BatteryUtils.getInstance(getContext());
210     }
211 
212     @Override
onCreate(Bundle icicle)213     public void onCreate(Bundle icicle) {
214         super.onCreate(icicle);
215 
216         mPackageName = getArguments().getString(EXTRA_PACKAGE_NAME);
217         mAnomalySummaryPreferenceController = new AnomalySummaryPreferenceController(
218                 (SettingsActivity) getActivity(), this);
219         mForegroundPreference = findPreference(KEY_PREF_FOREGROUND);
220         mBackgroundPreference = findPreference(KEY_PREF_BACKGROUND);
221         mHeaderPreference = (LayoutPreference) findPreference(KEY_PREF_HEADER);
222 
223         if (mPackageName != null) {
224             mAppEntry = mState.getEntry(mPackageName, UserHandle.myUserId());
225             initAnomalyInfo();
226         }
227     }
228 
229     @Override
onResume()230     public void onResume() {
231         super.onResume();
232 
233         initHeader();
234         initPreference();
235     }
236 
237     @VisibleForTesting
initAnomalyInfo()238     void initAnomalyInfo() {
239         mAnomalies = getArguments().getParcelableArrayList(EXTRA_ANOMALY_LIST);
240         if (mAnomalies == null) {
241             getLoaderManager().initLoader(ANOMALY_LOADER, Bundle.EMPTY, this);
242         } else if (mAnomalies != null) {
243             mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(mAnomalies);
244         }
245     }
246 
247     @VisibleForTesting
initHeader()248     void initHeader() {
249         final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header);
250         final Activity context = getActivity();
251         final Bundle bundle = getArguments();
252         EntityHeaderController controller = EntityHeaderController
253                 .newInstance(context, this, appSnippet)
254                 .setRecyclerView(getListView(), getLifecycle())
255                 .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
256                         EntityHeaderController.ActionType.ACTION_NONE);
257 
258         if (mAppEntry == null) {
259             controller.setLabel(bundle.getString(EXTRA_LABEL));
260 
261             final int iconId = bundle.getInt(EXTRA_ICON_ID, 0);
262             if (iconId == 0) {
263                 controller.setIcon(context.getPackageManager().getDefaultActivityIcon());
264             } else {
265                 controller.setIcon(context.getDrawable(bundle.getInt(EXTRA_ICON_ID)));
266             }
267         } else {
268             mState.ensureIcon(mAppEntry);
269             controller.setLabel(mAppEntry);
270             controller.setIcon(mAppEntry);
271             boolean isInstantApp = AppUtils.isInstant(mAppEntry.info);
272             CharSequence summary = isInstantApp
273                     ? null : getString(Utils.getInstallationStatus(mAppEntry.info));
274             controller.setIsInstantApp(AppUtils.isInstant(mAppEntry.info));
275             controller.setSummary(summary);
276         }
277 
278         controller.done(context, true /* rebindActions */);
279     }
280 
281     @VisibleForTesting
initPreference()282     void initPreference() {
283         final Bundle bundle = getArguments();
284         final Context context = getContext();
285 
286         final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME);
287         final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
288         final String usagePercent = bundle.getString(EXTRA_POWER_USAGE_PERCENT);
289         final int powerMah = bundle.getInt(EXTRA_POWER_USAGE_AMOUNT);
290         mForegroundPreference.setSummary(
291                 TextUtils.expandTemplate(getText(R.string.battery_used_for),
292                         StringUtil.formatElapsedTime(context, foregroundTimeMs, false)));
293         mBackgroundPreference.setSummary(
294                 TextUtils.expandTemplate(getText(R.string.battery_active_for),
295                         StringUtil.formatElapsedTime(context, backgroundTimeMs, false)));
296     }
297 
298     @Override
onPreferenceTreeClick(Preference preference)299     public boolean onPreferenceTreeClick(Preference preference) {
300         if (TextUtils.equals(preference.getKey(), AnomalySummaryPreferenceController.ANOMALY_KEY)) {
301             mAnomalySummaryPreferenceController.onPreferenceTreeClick(preference);
302             return true;
303         }
304         return super.onPreferenceTreeClick(preference);
305     }
306 
307     @Override
getMetricsCategory()308     public int getMetricsCategory() {
309         return MetricsEvent.FUELGAUGE_POWER_USAGE_DETAIL;
310     }
311 
312     @Override
getLogTag()313     protected String getLogTag() {
314         return TAG;
315     }
316 
317     @Override
getPreferenceScreenResId()318     protected int getPreferenceScreenResId() {
319         return R.xml.power_usage_detail;
320     }
321 
322     @Override
createPreferenceControllers(Context context)323     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
324         final List<AbstractPreferenceController> controllers = new ArrayList<>();
325         final Bundle bundle = getArguments();
326         final int uid = bundle.getInt(EXTRA_UID, 0);
327         final String packageName = bundle.getString(EXTRA_PACKAGE_NAME);
328 
329         mBackgroundActivityPreferenceController = new BackgroundActivityPreferenceController(
330                 context, this, uid, packageName);
331         controllers.add(mBackgroundActivityPreferenceController);
332         controllers.add(new BatteryOptimizationPreferenceController(
333                 (SettingsActivity) getActivity(), this, packageName));
334         mAppButtonsPreferenceController = new AppButtonsPreferenceController(
335                 (SettingsActivity) getActivity(), this, getLifecycle(), packageName, mState, mDpm,
336                 mUserManager, mPackageManager, REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN);
337         controllers.add(mAppButtonsPreferenceController);
338 
339         return controllers;
340     }
341 
342     @Override
onActivityResult(int requestCode, int resultCode, Intent data)343     public void onActivityResult(int requestCode, int resultCode, Intent data) {
344         super.onActivityResult(requestCode, resultCode, data);
345         if (mAppButtonsPreferenceController != null) {
346             mAppButtonsPreferenceController.handleActivityResult(requestCode, resultCode, data);
347         }
348     }
349 
350     @Override
handleDialogClick(int id)351     public void handleDialogClick(int id) {
352         if (mAppButtonsPreferenceController != null) {
353             mAppButtonsPreferenceController.handleDialogClick(id);
354         }
355     }
356 
357     @Override
onAnomalyHandled(Anomaly anomaly)358     public void onAnomalyHandled(Anomaly anomaly) {
359         mAnomalySummaryPreferenceController.hideHighUsagePreference();
360     }
361 
362     @Override
onCreateLoader(int id, Bundle args)363     public Loader<List<Anomaly>> onCreateLoader(int id, Bundle args) {
364         return new AnomalyLoader(getContext(), mPackageName);
365     }
366 
367     @Override
onLoadFinished(Loader<List<Anomaly>> loader, List<Anomaly> data)368     public void onLoadFinished(Loader<List<Anomaly>> loader, List<Anomaly> data) {
369         final AnomalyUtils anomalyUtils = AnomalyUtils.getInstance(getContext());
370         anomalyUtils.logAnomalies(mMetricsFeatureProvider, data,
371                 MetricsEvent.FUELGAUGE_POWER_USAGE_DETAIL);
372         mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(data);
373     }
374 
375     @Override
onLoaderReset(Loader<List<Anomaly>> loader)376     public void onLoaderReset(Loader<List<Anomaly>> loader) {
377 
378     }
379 
380     @Override
onBatteryTipHandled(BatteryTip batteryTip)381     public void onBatteryTipHandled(BatteryTip batteryTip) {
382         mBackgroundActivityPreferenceController.updateSummary(
383                 findPreference(mBackgroundActivityPreferenceController.getPreferenceKey()));
384     }
385 }
386