OSDN Git Service

Merge "Make anomaly item refresh in battery" into oc-dr1-dev
[android-x86/packages-apps-Settings.git] / src / com / android / settings / fuelgauge / PowerUsageSummary.java
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.LoaderManager;
21 import android.app.LoaderManager.LoaderCallbacks;
22 import android.content.Context;
23 import android.content.Loader;
24 import android.content.res.TypedArray;
25 import android.graphics.drawable.Drawable;
26 import android.os.BatteryStats;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.os.Process;
31 import android.os.UserHandle;
32 import android.provider.SearchIndexableResource;
33 import android.support.annotation.VisibleForTesting;
34 import android.support.v7.preference.Preference;
35 import android.support.v7.preference.PreferenceGroup;
36 import android.text.TextUtils;
37 import android.text.format.DateUtils;
38 import android.text.format.Formatter;
39 import android.util.Log;
40 import android.util.SparseArray;
41 import android.view.Menu;
42 import android.view.MenuInflater;
43 import android.view.MenuItem;
44 import android.view.View;
45 import android.view.View.OnClickListener;
46 import android.view.View.OnLongClickListener;
47 import android.widget.TextView;
48
49 import com.android.internal.hardware.AmbientDisplayConfiguration;
50 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
51 import com.android.internal.os.BatterySipper;
52 import com.android.internal.os.BatterySipper.DrainType;
53 import com.android.internal.os.PowerProfile;
54 import com.android.settings.R;
55 import com.android.settings.Settings.HighPowerApplicationsActivity;
56 import com.android.settings.SettingsActivity;
57 import com.android.settings.Utils;
58 import com.android.settings.applications.LayoutPreference;
59 import com.android.settings.applications.ManageApplications;
60 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
61 import com.android.settings.dashboard.SummaryLoader;
62 import com.android.settings.display.AmbientDisplayPreferenceController;
63 import com.android.settings.display.AutoBrightnessPreferenceController;
64 import com.android.settings.display.BatteryPercentagePreferenceController;
65 import com.android.settings.display.TimeoutPreferenceController;
66 import com.android.settings.fuelgauge.anomaly.Anomaly;
67 import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
68 import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment.AnomalyDialogListener;
69 import com.android.settings.fuelgauge.anomaly.AnomalyLoader;
70 import com.android.settings.fuelgauge.anomaly.AnomalySummaryPreferenceController;
71 import com.android.settings.overlay.FeatureFactory;
72 import com.android.settings.search.BaseSearchIndexProvider;
73 import com.android.settingslib.core.AbstractPreferenceController;
74
75 import java.util.ArrayList;
76 import java.util.Arrays;
77 import java.util.List;
78
79 /**
80  * Displays a list of apps and subsystems that consume power, ordered by how much power was
81  * consumed since the last time it was unplugged.
82  */
83 public class PowerUsageSummary extends PowerUsageBase implements
84         AnomalyDialogListener, OnLongClickListener, OnClickListener {
85
86     static final String TAG = "PowerUsageSummary";
87
88     private static final boolean DEBUG = false;
89     private static final boolean USE_FAKE_DATA = false;
90     private static final String KEY_APP_LIST = "app_list";
91     private static final String KEY_BATTERY_HEADER = "battery_header";
92     private static final String KEY_SHOW_ALL_APPS = "show_all_apps";
93     private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10;
94     private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
95
96     private static final String KEY_SCREEN_USAGE = "screen_usage";
97     private static final String KEY_TIME_SINCE_LAST_FULL_CHARGE = "last_full_charge";
98
99     private static final String KEY_AUTO_BRIGHTNESS = "auto_brightness_battery";
100     private static final String KEY_SCREEN_TIMEOUT = "screen_timeout_battery";
101     private static final String KEY_AMBIENT_DISPLAY = "ambient_display_battery";
102     private static final String KEY_BATTERY_SAVER_SUMMARY = "battery_saver_summary";
103     private static final String KEY_HIGH_USAGE = "high_usage";
104
105     @VisibleForTesting
106     static final int ANOMALY_LOADER = 1;
107     @VisibleForTesting
108     static final int BATTERY_INFO_LOADER = 2;
109     private static final int MENU_STATS_TYPE = Menu.FIRST;
110     @VisibleForTesting
111     static final int MENU_HIGH_POWER_APPS = Menu.FIRST + 3;
112     @VisibleForTesting
113     static final int MENU_ADDITIONAL_BATTERY_INFO = Menu.FIRST + 4;
114     @VisibleForTesting
115     static final int MENU_TOGGLE_APPS = Menu.FIRST + 5;
116     private static final int MENU_HELP = Menu.FIRST + 6;
117     public static final int DEBUG_INFO_LOADER = 3;
118
119     @VisibleForTesting
120     boolean mShowAllApps = false;
121     @VisibleForTesting
122     PowerGaugePreference mScreenUsagePref;
123     @VisibleForTesting
124     PowerGaugePreference mLastFullChargePref;
125     @VisibleForTesting
126     PowerUsageFeatureProvider mPowerFeatureProvider;
127     @VisibleForTesting
128     BatteryUtils mBatteryUtils;
129     @VisibleForTesting
130     LayoutPreference mBatteryLayoutPref;
131
132     /**
133      * SparseArray that maps uid to {@link Anomaly}, so we could find {@link Anomaly} by uid
134      */
135     @VisibleForTesting
136     SparseArray<List<Anomaly>> mAnomalySparseArray;
137     @VisibleForTesting
138     PreferenceGroup mAppListGroup;
139     private BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
140     private AnomalySummaryPreferenceController mAnomalySummaryPreferenceController;
141     private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
142
143     private LoaderManager.LoaderCallbacks<List<Anomaly>> mAnomalyLoaderCallbacks =
144             new LoaderManager.LoaderCallbacks<List<Anomaly>>() {
145
146                 @Override
147                 public Loader<List<Anomaly>> onCreateLoader(int id, Bundle args) {
148                     return new AnomalyLoader(getContext(), mStatsHelper);
149                 }
150
151                 @Override
152                 public void onLoadFinished(Loader<List<Anomaly>> loader, List<Anomaly> data) {
153                     // show high usage preference if possible
154                     mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(data);
155
156                     updateAnomalySparseArray(data);
157                     refreshAnomalyIcon();
158                 }
159
160                 @Override
161                 public void onLoaderReset(Loader<List<Anomaly>> loader) {
162
163                 }
164             };
165
166     @VisibleForTesting
167     LoaderManager.LoaderCallbacks<BatteryInfo> mBatteryInfoLoaderCallbacks =
168             new LoaderManager.LoaderCallbacks<BatteryInfo>() {
169
170                 @Override
171                 public Loader<BatteryInfo> onCreateLoader(int i, Bundle bundle) {
172                     return new BatteryInfoLoader(getContext(), mStatsHelper);
173                 }
174
175                 @Override
176                 public void onLoadFinished(Loader<BatteryInfo> loader, BatteryInfo batteryInfo) {
177                     mBatteryHeaderPreferenceController.updateHeaderPreference(batteryInfo);
178                 }
179
180                 @Override
181                 public void onLoaderReset(Loader<BatteryInfo> loader) {
182                     // do nothing
183                 }
184             };
185
186     LoaderManager.LoaderCallbacks<List<BatteryInfo>> mBatteryInfoDebugLoaderCallbacks =
187             new LoaderCallbacks<List<BatteryInfo>>() {
188                 @Override
189                 public Loader<List<BatteryInfo>> onCreateLoader(int i, Bundle bundle) {
190                     return new DebugEstimatesLoader(getContext(), mStatsHelper);
191                 }
192
193                 @Override
194                 public void onLoadFinished(Loader<List<BatteryInfo>> loader,
195                         List<BatteryInfo> batteryInfos) {
196                     final BatteryMeterView batteryView = (BatteryMeterView) mBatteryLayoutPref
197                             .findViewById(R.id.battery_header_icon);
198                     final TextView percentRemaining =
199                             mBatteryLayoutPref.findViewById(R.id.battery_percent);
200                     final TextView summary1 = mBatteryLayoutPref.findViewById(R.id.summary1);
201                     final TextView summary2 = mBatteryLayoutPref.findViewById(R.id.summary2);
202                     BatteryInfo oldInfo = batteryInfos.get(0);
203                     BatteryInfo newInfo = batteryInfos.get(1);
204                     percentRemaining.setText(Utils.formatPercentage(oldInfo.batteryLevel));
205
206                     // set the text to the old estimate (copied from battery info). Note that this
207                     // can sometimes say 0 time remaining because battery stats requires the phone
208                     // be unplugged for a period of time before being willing ot make an estimate.
209                     summary1.setText(mPowerFeatureProvider.getOldEstimateDebugString(
210                             Formatter.formatShortElapsedTime(getContext(),
211                                     BatteryUtils.convertUsToMs(oldInfo.remainingTimeUs))));
212
213                     // for this one we can just set the string directly
214                     summary2.setText(mPowerFeatureProvider.getEnhancedEstimateDebugString(
215                             Formatter.formatShortElapsedTime(getContext(),
216                                     BatteryUtils.convertUsToMs(newInfo.remainingTimeUs))));
217
218                     batteryView.setBatteryLevel(oldInfo.batteryLevel);
219                     batteryView.setCharging(!oldInfo.discharging);
220                 }
221
222                 @Override
223                 public void onLoaderReset(Loader<List<BatteryInfo>> loader) {
224                 }
225             };
226
227     @Override
228     public void onCreate(Bundle icicle) {
229         super.onCreate(icicle);
230         setAnimationAllowed(true);
231
232         initFeatureProvider();
233         mBatteryLayoutPref = (LayoutPreference) findPreference(KEY_BATTERY_HEADER);
234
235         mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST);
236         mScreenUsagePref = (PowerGaugePreference) findPreference(KEY_SCREEN_USAGE);
237         mLastFullChargePref = (PowerGaugePreference) findPreference(
238                 KEY_TIME_SINCE_LAST_FULL_CHARGE);
239         mFooterPreferenceMixin.createFooterPreference().setTitle(R.string.battery_footer_summary);
240         mAnomalySummaryPreferenceController = new AnomalySummaryPreferenceController(
241                 (SettingsActivity) getActivity(), this, MetricsEvent.FUELGAUGE_POWER_USAGE_SUMMARY);
242         mBatteryUtils = BatteryUtils.getInstance(getContext());
243         mAnomalySparseArray = new SparseArray<>();
244
245         restartBatteryInfoLoader();
246         restoreSavedInstance(icicle);
247     }
248
249     @Override
250     public int getMetricsCategory() {
251         return MetricsEvent.FUELGAUGE_POWER_USAGE_SUMMARY;
252     }
253
254     @Override
255     public void onPause() {
256         BatteryEntry.stopRequestQueue();
257         mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
258         super.onPause();
259     }
260
261     @Override
262     public void onDestroy() {
263         super.onDestroy();
264         if (getActivity().isChangingConfigurations()) {
265             BatteryEntry.clearUidCache();
266         }
267     }
268
269     @Override
270     public void onSaveInstanceState(Bundle outState) {
271         super.onSaveInstanceState(outState);
272         outState.putBoolean(KEY_SHOW_ALL_APPS, mShowAllApps);
273     }
274
275     @Override
276     public boolean onPreferenceTreeClick(Preference preference) {
277         if (mAnomalySummaryPreferenceController.onPreferenceTreeClick(preference)) {
278             return true;
279         }
280         if (KEY_BATTERY_HEADER.equals(preference.getKey())) {
281             performBatteryHeaderClick();
282             return true;
283         } else if (!(preference instanceof PowerGaugePreference)) {
284             return super.onPreferenceTreeClick(preference);
285         }
286         PowerGaugePreference pgp = (PowerGaugePreference) preference;
287         BatteryEntry entry = pgp.getInfo();
288         AdvancedPowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
289                 this, mStatsHelper, mStatsType, entry, pgp.getPercent(),
290                 mAnomalySparseArray.get(entry.sipper.getUid()));
291         return super.onPreferenceTreeClick(preference);
292     }
293
294     @Override
295     protected String getLogTag() {
296         return TAG;
297     }
298
299     @Override
300     protected int getPreferenceScreenResId() {
301         return R.xml.power_usage_summary;
302     }
303
304     @Override
305     protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
306         final List<AbstractPreferenceController> controllers = new ArrayList<>();
307         mBatteryHeaderPreferenceController = new BatteryHeaderPreferenceController(
308                 context, getActivity(), this /* host */, getLifecycle());
309         controllers.add(mBatteryHeaderPreferenceController);
310         controllers.add(new AutoBrightnessPreferenceController(context, KEY_AUTO_BRIGHTNESS));
311         controllers.add(new TimeoutPreferenceController(context, KEY_SCREEN_TIMEOUT));
312         controllers.add(new BatterySaverController(context, getLifecycle()));
313         controllers.add(new BatteryPercentagePreferenceController(context));
314         controllers.add(new AmbientDisplayPreferenceController(
315                 context,
316                 new AmbientDisplayConfiguration(context),
317                 KEY_AMBIENT_DISPLAY));
318         return controllers;
319     }
320
321     @Override
322     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
323         if (DEBUG) {
324             menu.add(Menu.NONE, MENU_STATS_TYPE, Menu.NONE, R.string.menu_stats_total)
325                     .setIcon(com.android.internal.R.drawable.ic_menu_info_details)
326                     .setAlphabeticShortcut('t');
327         }
328
329         menu.add(Menu.NONE, MENU_HIGH_POWER_APPS, Menu.NONE, R.string.high_power_apps);
330
331         if (mPowerFeatureProvider.isAdditionalBatteryInfoEnabled()) {
332             menu.add(Menu.NONE, MENU_ADDITIONAL_BATTERY_INFO,
333                     Menu.NONE, R.string.additional_battery_info);
334         }
335         if (mPowerFeatureProvider.isPowerAccountingToggleEnabled()) {
336             menu.add(Menu.NONE, MENU_TOGGLE_APPS, Menu.NONE,
337                     mShowAllApps ? R.string.hide_extra_apps : R.string.show_all_apps);
338         }
339
340         super.onCreateOptionsMenu(menu, inflater);
341     }
342
343     @Override
344     protected int getHelpResource() {
345         return R.string.help_url_battery;
346     }
347
348     @Override
349     public boolean onOptionsItemSelected(MenuItem item) {
350         final SettingsActivity sa = (SettingsActivity) getActivity();
351         final Context context = getContext();
352         final MetricsFeatureProvider metricsFeatureProvider =
353                 FeatureFactory.getFactory(context).getMetricsFeatureProvider();
354
355         switch (item.getItemId()) {
356             case MENU_STATS_TYPE:
357                 if (mStatsType == BatteryStats.STATS_SINCE_CHARGED) {
358                     mStatsType = BatteryStats.STATS_SINCE_UNPLUGGED;
359                 } else {
360                     mStatsType = BatteryStats.STATS_SINCE_CHARGED;
361                 }
362                 refreshUi();
363                 return true;
364             case MENU_HIGH_POWER_APPS:
365                 Bundle args = new Bundle();
366                 args.putString(ManageApplications.EXTRA_CLASSNAME,
367                         HighPowerApplicationsActivity.class.getName());
368                 sa.startPreferencePanel(this, ManageApplications.class.getName(), args,
369                         R.string.high_power_apps, null, null, 0);
370                 metricsFeatureProvider.action(context,
371                         MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_OPTIMIZATION);
372                 return true;
373             case MENU_ADDITIONAL_BATTERY_INFO:
374                 startActivity(mPowerFeatureProvider
375                         .getAdditionalBatteryInfoIntent());
376                 metricsFeatureProvider.action(context,
377                         MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_USAGE_ALERTS);
378                 return true;
379             case MENU_TOGGLE_APPS:
380                 mShowAllApps = !mShowAllApps;
381                 item.setTitle(mShowAllApps ? R.string.hide_extra_apps : R.string.show_all_apps);
382                 metricsFeatureProvider.action(context,
383                         MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE, mShowAllApps);
384                 restartBatteryStatsLoader();
385                 return true;
386             default:
387                 return super.onOptionsItemSelected(item);
388         }
389     }
390
391     @VisibleForTesting
392     void restoreSavedInstance(Bundle savedInstance) {
393         if (savedInstance != null) {
394             mShowAllApps = savedInstance.getBoolean(KEY_SHOW_ALL_APPS, false);
395         }
396     }
397
398     private void addNotAvailableMessage() {
399         final String NOT_AVAILABLE = "not_available";
400         Preference notAvailable = getCachedPreference(NOT_AVAILABLE);
401         if (notAvailable == null) {
402             notAvailable = new Preference(getPrefContext());
403             notAvailable.setKey(NOT_AVAILABLE);
404             notAvailable.setTitle(R.string.power_usage_not_available);
405             mAppListGroup.addPreference(notAvailable);
406         }
407     }
408
409     private void performBatteryHeaderClick() {
410         if (mPowerFeatureProvider.isAdvancedUiEnabled()) {
411             Utils.startWithFragment(getContext(), PowerUsageAdvanced.class.getName(), null,
412                     null, 0, R.string.advanced_battery_title, null, getMetricsCategory());
413         } else {
414             mStatsHelper.storeStatsHistoryInFile(BatteryHistoryDetail.BATTERY_HISTORY_FILE);
415             Bundle args = new Bundle(2);
416             args.putString(BatteryHistoryDetail.EXTRA_STATS,
417                     BatteryHistoryDetail.BATTERY_HISTORY_FILE);
418             args.putParcelable(BatteryHistoryDetail.EXTRA_BROADCAST,
419                     mStatsHelper.getBatteryBroadcast());
420             Utils.startWithFragment(getContext(), BatteryHistoryDetail.class.getName(), args,
421                     null, 0, R.string.history_details_title, null, getMetricsCategory());
422         }
423     }
424
425     private static boolean isSharedGid(int uid) {
426         return UserHandle.getAppIdFromSharedAppGid(uid) > 0;
427     }
428
429     private static boolean isSystemUid(int uid) {
430         return uid >= Process.SYSTEM_UID && uid < Process.FIRST_APPLICATION_UID;
431     }
432
433     /**
434      * We want to coalesce some UIDs. For example, dex2oat runs under a shared gid that
435      * exists for all users of the same app. We detect this case and merge the power use
436      * for dex2oat to the device OWNER's use of the app.
437      *
438      * @return A sorted list of apps using power.
439      */
440     private List<BatterySipper> getCoalescedUsageList(final List<BatterySipper> sippers) {
441         final SparseArray<BatterySipper> uidList = new SparseArray<>();
442
443         final ArrayList<BatterySipper> results = new ArrayList<>();
444         final int numSippers = sippers.size();
445         for (int i = 0; i < numSippers; i++) {
446             BatterySipper sipper = sippers.get(i);
447             if (sipper.getUid() > 0) {
448                 int realUid = sipper.getUid();
449
450                 // Check if this UID is a shared GID. If so, we combine it with the OWNER's
451                 // actual app UID.
452                 if (isSharedGid(sipper.getUid())) {
453                     realUid = UserHandle.getUid(UserHandle.USER_SYSTEM,
454                             UserHandle.getAppIdFromSharedAppGid(sipper.getUid()));
455                 }
456
457                 // Check if this UID is a system UID (mediaserver, logd, nfc, drm, etc).
458                 if (isSystemUid(realUid)
459                         && !"mediaserver".equals(sipper.packageWithHighestDrain)) {
460                     // Use the system UID for all UIDs running in their own sandbox that
461                     // are not apps. We exclude mediaserver because we already are expected to
462                     // report that as a separate item.
463                     realUid = Process.SYSTEM_UID;
464                 }
465
466                 if (realUid != sipper.getUid()) {
467                     // Replace the BatterySipper with a new one with the real UID set.
468                     BatterySipper newSipper = new BatterySipper(sipper.drainType,
469                             new FakeUid(realUid), 0.0);
470                     newSipper.add(sipper);
471                     newSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
472                     newSipper.mPackages = sipper.mPackages;
473                     sipper = newSipper;
474                 }
475
476                 int index = uidList.indexOfKey(realUid);
477                 if (index < 0) {
478                     // New entry.
479                     uidList.put(realUid, sipper);
480                 } else {
481                     // Combine BatterySippers if we already have one with this UID.
482                     final BatterySipper existingSipper = uidList.valueAt(index);
483                     existingSipper.add(sipper);
484                     if (existingSipper.packageWithHighestDrain == null
485                             && sipper.packageWithHighestDrain != null) {
486                         existingSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
487                     }
488
489                     final int existingPackageLen = existingSipper.mPackages != null ?
490                             existingSipper.mPackages.length : 0;
491                     final int newPackageLen = sipper.mPackages != null ?
492                             sipper.mPackages.length : 0;
493                     if (newPackageLen > 0) {
494                         String[] newPackages = new String[existingPackageLen + newPackageLen];
495                         if (existingPackageLen > 0) {
496                             System.arraycopy(existingSipper.mPackages, 0, newPackages, 0,
497                                     existingPackageLen);
498                         }
499                         System.arraycopy(sipper.mPackages, 0, newPackages, existingPackageLen,
500                                 newPackageLen);
501                         existingSipper.mPackages = newPackages;
502                     }
503                 }
504             } else {
505                 results.add(sipper);
506             }
507         }
508
509         final int numUidSippers = uidList.size();
510         for (int i = 0; i < numUidSippers; i++) {
511             results.add(uidList.valueAt(i));
512         }
513
514         // The sort order must have changed, so re-sort based on total power use.
515         mBatteryUtils.sortUsageList(results);
516         return results;
517     }
518
519     protected void refreshUi() {
520         final Context context = getContext();
521         if (context == null) {
522             return;
523         }
524
525         restartAnomalyDetectionIfPossible();
526
527         // reload BatteryInfo and updateUI
528         restartBatteryInfoLoader();
529         final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(mStatsHelper,
530                 System.currentTimeMillis());
531         updateScreenPreference();
532         updateLastFullChargePreference(lastFullChargeTime);
533
534         final CharSequence timeSequence = Utils.formatElapsedTime(context, lastFullChargeTime,
535                 false);
536         final int resId = mShowAllApps ? R.string.power_usage_list_summary_device
537                 : R.string.power_usage_list_summary;
538         mAppListGroup.setTitle(TextUtils.expandTemplate(getText(resId), timeSequence));
539
540         refreshAppListGroup();
541     }
542
543     private void refreshAppListGroup() {
544         final Context context = getContext();
545         final PowerProfile powerProfile = mStatsHelper.getPowerProfile();
546         final BatteryStats stats = mStatsHelper.getStats();
547         final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
548         boolean addedSome = false;
549
550         TypedArray array = context.obtainStyledAttributes(
551                 new int[]{android.R.attr.colorControlNormal});
552         final int colorControl = array.getColor(0, 0);
553         array.recycle();
554
555         final int dischargeAmount = USE_FAKE_DATA ? 5000
556                 : stats != null ? stats.getDischargeAmount(mStatsType) : 0;
557
558         cacheRemoveAllPrefs(mAppListGroup);
559         mAppListGroup.setOrderingAsAdded(false);
560
561         if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP || USE_FAKE_DATA) {
562             final List<BatterySipper> usageList = getCoalescedUsageList(
563                     USE_FAKE_DATA ? getFakeStats() : mStatsHelper.getUsageList());
564             double hiddenPowerMah = mShowAllApps ? 0 :
565                     mBatteryUtils.removeHiddenBatterySippers(usageList);
566             mBatteryUtils.sortUsageList(usageList);
567
568             final int numSippers = usageList.size();
569             for (int i = 0; i < numSippers; i++) {
570                 final BatterySipper sipper = usageList.get(i);
571                 double totalPower = USE_FAKE_DATA ? 4000 : mStatsHelper.getTotalPower();
572
573                 final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
574                         sipper.totalPowerMah, totalPower, hiddenPowerMah, dischargeAmount);
575
576                 if (((int) (percentOfTotal + .5)) < 1) {
577                     continue;
578                 }
579                 if (shouldHideSipper(sipper)) {
580                     continue;
581                 }
582                 final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid()));
583                 final BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper);
584                 final Drawable badgedIcon = mUm.getBadgedIconForUser(entry.getIcon(),
585                         userHandle);
586                 final CharSequence contentDescription = mUm.getBadgedLabelForUser(entry.getLabel(),
587                         userHandle);
588
589                 final String key = extractKeyFromSipper(sipper);
590                 PowerGaugePreference pref = (PowerGaugePreference) getCachedPreference(key);
591                 if (pref == null) {
592                     pref = new PowerGaugePreference(getPrefContext(), badgedIcon,
593                             contentDescription, entry);
594                     pref.setKey(key);
595                 }
596
597                 final double percentOfMax = (sipper.totalPowerMah * 100)
598                         / mStatsHelper.getMaxPower();
599                 sipper.percent = percentOfTotal;
600                 pref.setTitle(entry.getLabel());
601                 pref.setOrder(i + 1);
602                 pref.setPercent(percentOfTotal);
603                 pref.shouldShowAnomalyIcon(false);
604                 if (sipper.usageTimeMs == 0 && sipper.drainType == DrainType.APP) {
605                     sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
606                             BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, mStatsType);
607                 }
608                 setUsageSummary(pref, sipper);
609                 if ((sipper.drainType != DrainType.APP
610                         || sipper.uidObj.getUid() == Process.ROOT_UID)
611                         && sipper.drainType != DrainType.USER) {
612                     pref.setTint(colorControl);
613                 }
614                 addedSome = true;
615                 mAppListGroup.addPreference(pref);
616                 if (mAppListGroup.getPreferenceCount() - getCachedCount()
617                         > (MAX_ITEMS_TO_LIST + 1)) {
618                     break;
619                 }
620             }
621         }
622         if (!addedSome) {
623             addNotAvailableMessage();
624         }
625         removeCachedPrefs(mAppListGroup);
626
627         BatteryEntry.startRequestQueue();
628     }
629
630     @VisibleForTesting
631     boolean shouldHideSipper(BatterySipper sipper) {
632         // Don't show over-counted and unaccounted in any condition
633         return sipper.drainType == BatterySipper.DrainType.OVERCOUNTED
634                 || sipper.drainType == BatterySipper.DrainType.UNACCOUNTED;
635     }
636
637     @VisibleForTesting
638     void refreshAnomalyIcon() {
639         for (int i = 0, size = mAnomalySparseArray.size(); i < size; i++) {
640             final String key = extractKeyFromUid(mAnomalySparseArray.keyAt(i));
641             final PowerGaugePreference pref = (PowerGaugePreference) mAppListGroup.findPreference(
642                     key);
643             if (pref != null) {
644                 pref.shouldShowAnomalyIcon(true);
645             }
646         }
647     }
648
649     @VisibleForTesting
650     void restartAnomalyDetectionIfPossible() {
651         if (getAnomalyDetectionPolicy().isAnomalyDetectionEnabled()) {
652             getLoaderManager().restartLoader(ANOMALY_LOADER, Bundle.EMPTY, mAnomalyLoaderCallbacks);
653         }
654     }
655
656     @VisibleForTesting
657     AnomalyDetectionPolicy getAnomalyDetectionPolicy() {
658         return new AnomalyDetectionPolicy(getContext());
659     }
660
661     @VisibleForTesting
662     BatterySipper findBatterySipperByType(List<BatterySipper> usageList, DrainType type) {
663         for (int i = 0, size = usageList.size(); i < size; i++) {
664             final BatterySipper sipper = usageList.get(i);
665             if (sipper.drainType == type) {
666                 return sipper;
667             }
668         }
669         return null;
670     }
671
672     @VisibleForTesting
673     void updateScreenPreference() {
674         final BatterySipper sipper = findBatterySipperByType(
675                 mStatsHelper.getUsageList(), DrainType.SCREEN);
676         final long usageTimeMs = sipper != null ? sipper.usageTimeMs : 0;
677
678         mScreenUsagePref.setSubtitle(Utils.formatElapsedTime(getContext(), usageTimeMs, false));
679     }
680
681     @VisibleForTesting
682     void updateLastFullChargePreference(long timeMs) {
683         final CharSequence timeSequence = Utils.formatElapsedTime(getContext(), timeMs, false);
684         mLastFullChargePref.setSubtitle(
685                 TextUtils.expandTemplate(getText(R.string.power_last_full_charge_summary),
686                         timeSequence));
687     }
688
689     @VisibleForTesting
690     void showBothEstimates() {
691         final Context context = getContext();
692         if (context == null
693                 || !mPowerFeatureProvider.isEnhancedBatteryPredictionEnabled(context)) {
694             return;
695         }
696         getLoaderManager().restartLoader(DEBUG_INFO_LOADER, Bundle.EMPTY,
697                 mBatteryInfoDebugLoaderCallbacks);
698     }
699
700     @VisibleForTesting
701     double calculatePercentage(double powerUsage, double dischargeAmount) {
702         final double totalPower = mStatsHelper.getTotalPower();
703         return totalPower == 0 ? 0 :
704                 ((powerUsage / totalPower) * dischargeAmount);
705     }
706
707     @VisibleForTesting
708     void setUsageSummary(Preference preference, BatterySipper sipper) {
709         // Only show summary when usage time is longer than one minute
710         final long usageTimeMs = sipper.usageTimeMs;
711         if (usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
712             final CharSequence timeSequence = Utils.formatElapsedTime(getContext(), usageTimeMs,
713                     false);
714             preference.setSummary(
715                     (sipper.drainType != DrainType.APP || mBatteryUtils.shouldHideSipper(sipper))
716                             ? timeSequence
717                             : TextUtils.expandTemplate(getText(R.string.battery_screen_usage),
718                                     timeSequence));
719         }
720     }
721
722     @VisibleForTesting
723     String extractKeyFromSipper(BatterySipper sipper) {
724         if (sipper.uidObj != null) {
725             return extractKeyFromUid(sipper.getUid());
726         } else if (sipper.drainType != DrainType.APP) {
727             return sipper.drainType.toString();
728         } else if (sipper.getPackages() != null) {
729             return TextUtils.concat(sipper.getPackages()).toString();
730         } else {
731             Log.w(TAG, "Inappropriate BatterySipper without uid and package names: " + sipper);
732             return "-1";
733         }
734     }
735
736     @VisibleForTesting
737     String extractKeyFromUid(int uid) {
738         return Integer.toString(uid);
739     }
740
741     @VisibleForTesting
742     void setBatteryLayoutPreference(LayoutPreference layoutPreference) {
743         mBatteryLayoutPref = layoutPreference;
744     }
745
746     @VisibleForTesting
747     void initFeatureProvider() {
748         final Context context = getContext();
749         mPowerFeatureProvider = FeatureFactory.getFactory(context)
750                 .getPowerUsageFeatureProvider(context);
751     }
752
753     @VisibleForTesting
754     void updateAnomalySparseArray(List<Anomaly> anomalies) {
755         mAnomalySparseArray.clear();
756         for (int i = 0, size = anomalies.size(); i < size; i++) {
757             final Anomaly anomaly = anomalies.get(i);
758             if (mAnomalySparseArray.get(anomaly.uid) == null) {
759                 mAnomalySparseArray.append(anomaly.uid, new ArrayList<>());
760             }
761             mAnomalySparseArray.get(anomaly.uid).add(anomaly);
762         }
763     }
764
765     @VisibleForTesting
766     void restartBatteryInfoLoader() {
767         getLoaderManager().restartLoader(BATTERY_INFO_LOADER, Bundle.EMPTY,
768                 mBatteryInfoLoaderCallbacks);
769         if (mPowerFeatureProvider.isEstimateDebugEnabled()) {
770             // Unfortunately setting a long click listener on a view means it will no
771             // longer pass the regular click event to the parent, so we have to register
772             // a regular click listener as well.
773             View header = mBatteryLayoutPref.findViewById(R.id.summary1);
774             header.setOnLongClickListener(this);
775             header.setOnClickListener(this);
776         }
777     }
778
779     private static List<BatterySipper> getFakeStats() {
780         ArrayList<BatterySipper> stats = new ArrayList<>();
781         float use = 5;
782         for (DrainType type : DrainType.values()) {
783             if (type == DrainType.APP) {
784                 continue;
785             }
786             stats.add(new BatterySipper(type, null, use));
787             use += 5;
788         }
789         for (int i = 0; i < 100; i++) {
790             stats.add(new BatterySipper(DrainType.APP,
791                     new FakeUid(Process.FIRST_APPLICATION_UID + i), use));
792         }
793         stats.add(new BatterySipper(DrainType.APP,
794                 new FakeUid(0), use));
795
796         // Simulate dex2oat process.
797         BatterySipper sipper = new BatterySipper(DrainType.APP,
798                 new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID)), 10.0f);
799         sipper.packageWithHighestDrain = "dex2oat";
800         stats.add(sipper);
801
802         sipper = new BatterySipper(DrainType.APP,
803                 new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID + 1)), 10.0f);
804         sipper.packageWithHighestDrain = "dex2oat";
805         stats.add(sipper);
806
807         sipper = new BatterySipper(DrainType.APP,
808                 new FakeUid(UserHandle.getSharedAppGid(Process.LOG_UID)), 9.0f);
809         stats.add(sipper);
810
811         return stats;
812     }
813
814     Handler mHandler = new Handler() {
815
816         @Override
817         public void handleMessage(Message msg) {
818             switch (msg.what) {
819                 case BatteryEntry.MSG_UPDATE_NAME_ICON:
820                     BatteryEntry entry = (BatteryEntry) msg.obj;
821                     PowerGaugePreference pgp =
822                             (PowerGaugePreference) findPreference(
823                                     Integer.toString(entry.sipper.uidObj.getUid()));
824                     if (pgp != null) {
825                         final int userId = UserHandle.getUserId(entry.sipper.getUid());
826                         final UserHandle userHandle = new UserHandle(userId);
827                         pgp.setIcon(mUm.getBadgedIconForUser(entry.getIcon(), userHandle));
828                         pgp.setTitle(entry.name);
829                         if (entry.sipper.drainType == DrainType.APP) {
830                             pgp.setContentDescription(entry.name);
831                         }
832                     }
833                     break;
834                 case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
835                     Activity activity = getActivity();
836                     if (activity != null) {
837                         activity.reportFullyDrawn();
838                     }
839                     break;
840             }
841             super.handleMessage(msg);
842         }
843     };
844
845     @Override
846     public void onAnomalyHandled(Anomaly anomaly) {
847         mAnomalySummaryPreferenceController.hideHighUsagePreference();
848     }
849
850     @Override
851     public boolean onLongClick(View view) {
852         showBothEstimates();
853         view.setOnLongClickListener(null);
854         return true;
855     }
856
857     @Override
858     public void onClick(View view) {
859         performBatteryHeaderClick();
860     }
861
862     @Override
863     protected void restartBatteryStatsLoader() {
864         super.restartBatteryStatsLoader();
865         mBatteryHeaderPreferenceController.quickUpdateHeaderPreference();
866     }
867
868     private static class SummaryProvider implements SummaryLoader.SummaryProvider {
869         private final Context mContext;
870         private final SummaryLoader mLoader;
871         private final BatteryBroadcastReceiver mBatteryBroadcastReceiver;
872
873         private SummaryProvider(Context context, SummaryLoader loader) {
874             mContext = context;
875             mLoader = loader;
876             mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(mContext);
877             mBatteryBroadcastReceiver.setBatteryChangedListener(() -> {
878                 BatteryInfo.getBatteryInfo(mContext, new BatteryInfo.Callback() {
879                     @Override
880                     public void onBatteryInfoLoaded(BatteryInfo info) {
881                         mLoader.setSummary(SummaryProvider.this, info.chargeLabel);
882                     }
883                 });
884             });
885         }
886
887         @Override
888         public void setListening(boolean listening) {
889             if (listening) {
890                 mBatteryBroadcastReceiver.register();
891             } else {
892                 mBatteryBroadcastReceiver.unRegister();
893             }
894         }
895     }
896
897     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
898             new BaseSearchIndexProvider() {
899                 @Override
900                 public List<SearchIndexableResource> getXmlResourcesToIndex(
901                         Context context, boolean enabled) {
902                     final SearchIndexableResource sir = new SearchIndexableResource(context);
903                     sir.xmlResId = R.xml.power_usage_summary;
904                     return Arrays.asList(sir);
905                 }
906
907                 @Override
908                 public List<String> getNonIndexableKeys(Context context) {
909                     List<String> niks = super.getNonIndexableKeys(context);
910                     niks.add(KEY_HIGH_USAGE);
911                     niks.add(KEY_BATTERY_SAVER_SUMMARY);
912                     // Duplicates in display
913                     niks.add(KEY_AUTO_BRIGHTNESS);
914                     niks.add(KEY_SCREEN_TIMEOUT);
915                     niks.add(KEY_AMBIENT_DISPLAY);
916                     return niks;
917                 }
918             };
919
920     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
921             = new SummaryLoader.SummaryProviderFactory() {
922         @Override
923         public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
924                 SummaryLoader summaryLoader) {
925             return new SummaryProvider(activity, summaryLoader);
926         }
927     };
928 }