*/
package com.android.settings.fuelgauge;
+import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.BatteryStats;
import android.os.Bundle;
-import android.os.Process;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserManager;
import android.provider.SearchIndexableResource;
import android.support.annotation.ColorInt;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
+
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.core.PreferenceController;
import com.android.settings.fuelgauge.PowerUsageAdvanced.PowerUsageData.UsageType;
import com.android.settings.overlay.FeatureFactory;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
public class PowerUsageAdvanced extends PowerUsageBase {
private static final String TAG = "AdvancedBatteryUsage";
private static final String KEY_BATTERY_GRAPH = "battery_graph";
private static final String KEY_BATTERY_USAGE_LIST = "battery_usage_list";
+ private static final int STATUS_TYPE = BatteryStats.STATS_SINCE_CHARGED;
@VisibleForTesting
final int[] mUsageTypes = {
UsageType.BLUETOOTH,
UsageType.USER,
UsageType.IDLE,
- UsageType.APP};
+ UsageType.APP,
+ UsageType.UNACCOUNTED};
private BatteryHistoryPreference mHistPref;
private PreferenceGroup mUsageListGroup;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
private PackageManager mPackageManager;
+ private UserManager mUserManager;
+ private Map<Integer, PowerUsageData> mBatteryDataMap;
+ private BatteryUtils mBatteryUtils;
+
+ Handler mHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case BatteryEntry.MSG_UPDATE_NAME_ICON:
+ final int dischargeAmount = mStatsHelper.getStats().getDischargeAmount(
+ STATUS_TYPE);
+ final double totalPower = mStatsHelper.getTotalPower();
+ final BatteryEntry entry = (BatteryEntry) msg.obj;
+ final int usageType = extractUsageType(entry.sipper);
+
+ PowerUsageData usageData = mBatteryDataMap.get(usageType);
+ Preference pref = findPreference(String.valueOf(usageType));
+ if (pref != null && usageData != null) {
+ updateUsageDataSummary(usageData, totalPower, dischargeAmount);
+ pref.setSummary(usageData.summary);
+ }
+ break;
+ case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
+ Activity activity = getActivity();
+ if (activity != null) {
+ activity.reportFullyDrawn();
+ }
+ break;
+ }
+ super.handleMessage(msg);
+ }
+ };
@Override
public void onCreate(Bundle icicle) {
mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context);
mPackageManager = context.getPackageManager();
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mBatteryUtils = BatteryUtils.getInstance(context);
}
@Override
}
@Override
+ public void onPause() {
+ BatteryEntry.stopRequestQueue();
+ mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
+ super.onPause();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (getActivity().isChangingConfigurations()) {
+ BatteryEntry.clearUidCache();
+ }
+ }
+
+ @Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.FUELGAUGE_BATTERY_HISTORY_DETAIL;
}
mUsageListGroup.removeAll();
for (int i = 0, size = dataList.size(); i < size; i++) {
final PowerUsageData batteryData = dataList.get(i);
- final PowerGaugePreference pref = new PowerGaugePreference(getContext());
+ final PowerGaugePreference pref = new PowerGaugePreference(getPrefContext());
+ pref.setKey(String.valueOf(batteryData.usageType));
pref.setTitle(batteryData.titleResId);
pref.setSummary(batteryData.summary);
pref.setPercent(batteryData.percentage);
mUsageListGroup.addPreference(pref);
}
+
+ BatteryEntry.startRequestQueue();
}
@VisibleForTesting
- @UsageType int extractUsageType(BatterySipper sipper) {
+ @UsageType
+ int extractUsageType(BatterySipper sipper) {
final DrainType drainType = sipper.drainType;
final int uid = sipper.getUid();
- // TODO(b/34385770): add logic to extract type service
if (drainType == DrainType.WIFI) {
return UsageType.WIFI;
} else if (drainType == DrainType.BLUETOOTH) {
return UsageType.USER;
} else if (drainType == DrainType.CELL) {
return UsageType.CELL;
+ } else if (drainType == DrainType.UNACCOUNTED) {
+ return UsageType.UNACCOUNTED;
} else if (mPowerUsageFeatureProvider.isTypeSystem(sipper)) {
return UsageType.SYSTEM;
} else if (mPowerUsageFeatureProvider.isTypeService(sipper)) {
sipper.mPackages = mPackageManager.getPackagesForUid(sipper.getUid());
final PowerUsageData usageData = batteryDataMap.get(extractUsageType(sipper));
usageData.totalPowerMah += sipper.totalPowerMah;
+ if (sipper.drainType == DrainType.APP && sipper.usageTimeMs != 0) {
+ sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
+ BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, STATUS_TYPE);
+ }
+ usageData.totalUsageTimeMs += sipper.usageTimeMs;
+ usageData.usageList.add(sipper);
}
- // TODO(b/34385770): add logic to extract the summary
final List<PowerUsageData> batteryDataList = new ArrayList<>(batteryDataMap.values());
+ final int dischargeAmount = statusHelper.getStats().getDischargeAmount(STATUS_TYPE);
final double totalPower = statusHelper.getTotalPower();
for (final PowerUsageData usageData : batteryDataList) {
- usageData.percentage = (usageData.totalPowerMah / totalPower) * 100;
+ usageData.percentage = (usageData.totalPowerMah / totalPower) * dischargeAmount;
+ updateUsageDataSummary(usageData, totalPower, dischargeAmount);
}
Collections.sort(batteryDataList);
+ mBatteryDataMap = batteryDataMap;
return batteryDataList;
}
@VisibleForTesting
+ void updateUsageDataSummary(PowerUsageData usageData, double totalPower, int dischargeAmount) {
+ if (usageData.usageList.size() <= 1) {
+ usageData.summary = getString(R.string.battery_used_for,
+ Utils.formatElapsedTime(getContext(), usageData.totalUsageTimeMs, false));
+ } else {
+ BatterySipper sipper = findBatterySipperWithMaxBatteryUsage(usageData.usageList);
+ BatteryEntry batteryEntry = new BatteryEntry(getContext(), mHandler, mUserManager,
+ sipper);
+ final double percentage = (sipper.totalPowerMah / totalPower) * dischargeAmount;
+ usageData.summary = getString(R.string.battery_used_by,
+ Utils.formatPercentage(percentage, true), batteryEntry.name);
+ }
+ }
+
+ @VisibleForTesting
+ BatterySipper findBatterySipperWithMaxBatteryUsage(List<BatterySipper> usageList) {
+ BatterySipper sipper = usageList.get(0);
+ for (int i = 1, size = usageList.size(); i < size; i++) {
+ final BatterySipper comparedSipper = usageList.get(i);
+ if (comparedSipper.totalPowerMah > sipper.totalPowerMah) {
+ sipper = comparedSipper;
+ }
+ }
+
+ return sipper;
+ }
+
+ @VisibleForTesting
void setPackageManager(PackageManager packageManager) {
mPackageManager = packageManager;
}
UsageType.SYSTEM,
UsageType.BLUETOOTH,
UsageType.USER,
- UsageType.IDLE})
+ UsageType.IDLE,
+ UsageType.UNACCOUNTED})
public @interface UsageType {
int APP = 0;
int WIFI = 1;
int BLUETOOTH = 5;
int USER = 6;
int IDLE = 7;
+ int UNACCOUNTED = 8;
}
@StringRes
public String summary;
public double percentage;
public double totalPowerMah;
+ public long totalUsageTimeMs;
@ColorInt
public int iconColor;
@UsageType
public int usageType;
+ public List<BatterySipper> usageList;
public PowerUsageData(@UsageType int usageType) {
this(usageType, 0);
public PowerUsageData(@UsageType int usageType, double totalPower) {
this.usageType = usageType;
totalPowerMah = 0;
+ totalUsageTimeMs = 0;
titleResId = getTitleResId(usageType);
totalPowerMah = totalPower;
+ usageList = new ArrayList<>();
}
private int getTitleResId(@UsageType int usageType) {
return R.string.power_user;
case UsageType.IDLE:
return R.string.power_idle;
+ case UsageType.UNACCOUNTED:
+ return R.string.power_unaccounted;
case UsageType.APP:
default:
return R.string.power_apps;
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
- if (!FeatureFactory.getFactory(context).getDashboardFeatureProvider(context)
- .isEnabled()) {
- return null;
- }
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = R.xml.power_usage_advanced;
return Arrays.asList(sir);