Merge "Show all BatteryUsageStats in BatteryStatsViewer" into sc-dev

This commit is contained in:
Dmitri Plotnikov
2021-04-14 21:22:36 +00:00
committed by Android (Google) Code Review
11 changed files with 396 additions and 449 deletions

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#d14d2c">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11.17,19.5h-0.83l0.82,-5.83 -4.18,0.01 5.85,-9.17h0.83l-0.84,5.84h4.17l-5.82,9.15z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#269e5c">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM19,19H5V5h14V19z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M6.25,7.72h5v1.5h-5z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M13,15.75h5v1.5h-5z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M13,13.25h5v1.5h-5z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M8,18l1.5,0l0,-2l2,0l0,-1.5l-2,0l0,-2l-1.5,0l0,2l-2,0l0,1.5l2,0z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M14.09,10.95l1.41,-1.41l1.41,1.41l1.06,-1.06l-1.41,-1.42l1.41,-1.41l-1.06,-1.06l-1.41,1.41l-1.41,-1.41l-1.06,1.06l1.41,1.41l-1.41,1.42z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#d14d2c">
<path
android:fillColor="@android:color/white"
android:pathData="M22,9L22,7h-2L20,5c0,-1.1 -0.9,-2 -2,-2L4,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-2h2v-2h-2v-2h2v-2h-2L20,9h2zM18,19L4,19L4,5h14v14zM6,13h5v4L6,17v-4zM12,7h4v3h-4L12,7zM6,7h5v5L6,12L6,7zM12,11h4v6h-4v-6z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M15,3L9,3L9,1h6v2zM11,14h2L13,8h-2v6zM21,13.01c0,4.97 -4.02,9 -9,9s-9,-4.03 -9,-9 4.03,-9 9,-9c2.12,0 4.07,0.74 5.62,1.98l1.42,-1.42c0.51,0.42 0.98,0.9 1.41,1.41L19.03,7.4C20.26,8.93 21,10.89 21,13.01zM19,13.01c0,-3.87 -3.13,-7 -7,-7s-7,3.13 -7,7 3.13,7 7,7 7,-3.13 7,-7z"/>
</vector>

View File

@@ -25,6 +25,13 @@
android:paddingTop="8dp"
android:paddingBottom="8dp">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="8dp"/>
<TextView
android:id="@+id/title"
android:layout_width="0dp"
@@ -34,16 +41,18 @@
<TextView
android:id="@+id/amount"
android:layout_width="0dp"
android:layout_weight="0.7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:gravity="right"
android:maxLines="1"
android:textAppearance="@style/TextAppearanceBody"/>
<TextView
android:id="@+id/percent"
android:layout_width="64dp"
android:layout_width="76dp"
android:layout_height="wrap_content"
android:gravity="right"
android:maxLines="1"
android:textAppearance="@style/TextAppearanceBody"/>
</LinearLayout>

View File

@@ -43,10 +43,40 @@
android:paddingEnd="10dp">
<include layout="@layout/battery_consumer_info_layout"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<LinearLayout
android:id="@+id/headings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="2dp"
android:paddingBottom="4dp">
<FrameLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingEnd="10dp"
android:text="Total"/>
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingEnd="30dp"
android:text="Apps"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/darker_gray"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/battery_consumer_data_view"
android:layout_width="match_parent"

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<color name="battery_consumer_bg_power_profile">#ffffff</color>
<color name="battery_consumer_bg_measured_energy">#fff5eb</color>
</resources>

View File

@@ -18,32 +18,21 @@ package com.android.frameworks.core.batterystatsviewer;
import android.content.Context;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.Process;
import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import android.util.DebugUtils;
import java.util.ArrayList;
import java.util.List;
public class BatteryConsumerData {
private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar";
private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media";
private static final String PACKAGE_SYSTEMUI = "com.android.systemui";
private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER,
PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI};
// Unit conversion:
// mAh = uC * (1/1000)(milli/micro) * (1/3600)(hours/second)
private static final double UC_2_MAH = (1.0 / 1000) * (1.0 / 3600);
enum EntryType {
POWER,
POWER_MODELED,
POWER_MEASURED,
POWER_CUSTOM,
DURATION,
}
@@ -52,260 +41,155 @@ public class BatteryConsumerData {
public EntryType entryType;
public double value;
public double total;
public boolean isSystemBatteryConsumer;
}
private final BatteryConsumerInfoHelper.BatteryConsumerInfo mBatteryConsumerInfo;
private final List<Entry> mEntries = new ArrayList<>();
public BatteryConsumerData(Context context, BatteryStatsHelper batteryStatsHelper,
public BatteryConsumerData(Context context,
List<BatteryUsageStats> batteryUsageStatsList, String batteryConsumerId) {
BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0);
BatteryUsageStats powerProfileModeledUsageStats = batteryUsageStatsList.get(1);
List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
BatteryStats batteryStats = batteryStatsHelper.getStats();
BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1);
double totalPowerMah = 0;
double totalSmearedPowerMah = 0;
double totalPowerExcludeSystemMah = 0;
double totalScreenPower = 0;
double totalProportionalSmearMah = 0;
double totalCpuPowerMah = 0;
double totalSystemServiceCpuPowerMah = 0;
double totalUsagePowerMah = 0;
double totalWakeLockPowerMah = 0;
double totalMobileRadioPowerMah = 0;
double totalWifiPowerMah = 0;
double totalBluetoothPowerMah = 0;
double totalGpsPowerMah = 0;
double totalCameraPowerMah = 0;
double totalFlashlightPowerMah = 0;
double totalSensorPowerMah = 0;
double totalAudioPowerMah = 0;
double totalVideoPowerMah = 0;
BatteryConsumer requestedBatteryConsumer = getRequestedBatteryConsumer(batteryUsageStats,
batteryConsumerId);
BatteryConsumer requestedModeledBatteryConsumer = getRequestedBatteryConsumer(
modeledBatteryUsageStats, batteryConsumerId);
long totalCpuTimeMs = 0;
long totalCpuFgTimeMs = 0;
long totalWakeLockTimeMs = 0;
long totalWifiRunningTimeMs = 0;
long totalBluetoothRunningTimeMs = 0;
long totalGpsTimeMs = 0;
long totalCameraTimeMs = 0;
long totalFlashlightTimeMs = 0;
long totalAudioTimeMs = 0;
long totalVideoTimeMs = 0;
BatterySipper requestedBatterySipper = null;
for (BatterySipper sipper : usageList) {
if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
totalScreenPower = sipper.sumPower();
}
if (batteryConsumerId(sipper).equals(batteryConsumerId)) {
requestedBatterySipper = sipper;
}
totalPowerMah += sipper.sumPower();
totalSmearedPowerMah += sipper.totalSmearedPowerMah;
totalProportionalSmearMah += sipper.proportionalSmearMah;
if (!isSystemSipper(sipper)) {
totalPowerExcludeSystemMah += sipper.totalSmearedPowerMah;
}
totalCpuPowerMah += sipper.cpuPowerMah;
totalSystemServiceCpuPowerMah += sipper.systemServiceCpuPowerMah;
totalUsagePowerMah += sipper.usagePowerMah;
totalWakeLockPowerMah += sipper.wakeLockPowerMah;
totalMobileRadioPowerMah += sipper.mobileRadioPowerMah;
totalWifiPowerMah += sipper.wifiPowerMah;
totalBluetoothPowerMah += sipper.bluetoothPowerMah;
totalGpsPowerMah += sipper.gpsPowerMah;
totalCameraPowerMah += sipper.cameraPowerMah;
totalFlashlightPowerMah += sipper.flashlightPowerMah;
totalSensorPowerMah += sipper.sensorPowerMah;
totalAudioPowerMah += sipper.audioPowerMah;
totalVideoPowerMah += sipper.videoPowerMah;
totalCpuTimeMs += sipper.cpuTimeMs;
totalCpuFgTimeMs += sipper.cpuFgTimeMs;
totalWakeLockTimeMs += sipper.wakeLockTimeMs;
totalWifiRunningTimeMs += sipper.wifiRunningTimeMs;
totalBluetoothRunningTimeMs += sipper.bluetoothRunningTimeMs;
totalGpsTimeMs += sipper.gpsTimeMs;
totalCameraTimeMs += sipper.cameraTimeMs;
totalFlashlightTimeMs += sipper.flashlightTimeMs;
totalAudioTimeMs += sipper.audioTimeMs;
totalVideoTimeMs += sipper.videoTimeMs;
}
BatteryConsumer requestedBatteryConsumer = null;
for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
requestedBatteryConsumer = consumer;
}
}
double totalModeledCpuPowerMah = 0;
BatteryConsumer requestedBatteryConsumerPowerProfileModeled = null;
for (BatteryConsumer consumer : powerProfileModeledUsageStats.getUidBatteryConsumers()) {
if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
requestedBatteryConsumerPowerProfileModeled = consumer;
}
totalModeledCpuPowerMah += consumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU);
}
if (requestedBatterySipper == null) {
if (requestedBatteryConsumer == null || requestedModeledBatteryConsumer == null) {
mBatteryConsumerInfo = null;
return;
}
if (requestedBatteryConsumer == null) {
for (BatteryConsumer consumer : batteryUsageStats.getSystemBatteryConsumers()) {
if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
requestedBatteryConsumer = consumer;
break;
}
}
}
mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(
context.getPackageManager(), requestedBatterySipper);
long totalScreenMeasuredChargeUC =
batteryStats.getScreenOnMeasuredBatteryConsumptionUC();
long uidScreenMeasuredChargeUC =
requestedBatterySipper.uidObj.getScreenOnMeasuredBatteryConsumptionUC();
context.getPackageManager(), requestedBatteryConsumer);
addEntry("Total power", EntryType.POWER,
requestedBatterySipper.totalSmearedPowerMah, totalSmearedPowerMah);
maybeAddMeasuredEnergyEntry(requestedBatterySipper.drainType, batteryStats);
double[] totalPowerByComponentMah = new double[BatteryConsumer.POWER_COMPONENT_COUNT];
double[] totalModeledPowerByComponentMah =
new double[BatteryConsumer.POWER_COMPONENT_COUNT];
long[] totalDurationByComponentMs = new long[BatteryConsumer.TIME_COMPONENT_COUNT];
final int customComponentCount =
requestedBatteryConsumer.getCustomPowerComponentCount();
double[] totalCustomPowerByComponentMah = new double[customComponentCount];
addEntry("... excluding system", EntryType.POWER,
requestedBatterySipper.totalSmearedPowerMah, totalPowerExcludeSystemMah);
addEntry("Screen, smeared", EntryType.POWER,
requestedBatterySipper.screenPowerMah, totalScreenPower);
if (uidScreenMeasuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE
&& totalScreenMeasuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE) {
final double measuredCharge = UC_2_MAH * uidScreenMeasuredChargeUC;
final double totalMeasuredCharge = UC_2_MAH * totalScreenMeasuredChargeUC;
addEntry("Screen, measured", EntryType.POWER,
measuredCharge, totalMeasuredCharge);
}
addEntry("Other, smeared", EntryType.POWER,
requestedBatterySipper.proportionalSmearMah, totalProportionalSmearMah);
addEntry("Excluding smeared", EntryType.POWER,
requestedBatterySipper.totalPowerMah, totalPowerMah);
if (requestedBatteryConsumer != null) {
addEntry("CPU", EntryType.POWER,
requestedBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU),
totalCpuPowerMah);
if (requestedBatteryConsumerPowerProfileModeled != null) {
addEntry("CPU (modeled)", EntryType.POWER,
requestedBatteryConsumerPowerProfileModeled.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU),
totalModeledCpuPowerMah);
}
} else {
addEntry("CPU (sipper)", EntryType.POWER,
requestedBatterySipper.cpuPowerMah, totalCpuPowerMah);
}
addEntry("System services", EntryType.POWER,
requestedBatterySipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah);
addEntry("Wake lock", EntryType.POWER,
requestedBatterySipper.wakeLockPowerMah, totalWakeLockPowerMah);
addEntry("Mobile radio", EntryType.POWER,
requestedBatterySipper.mobileRadioPowerMah, totalMobileRadioPowerMah);
addEntry("WiFi", EntryType.POWER,
requestedBatterySipper.wifiPowerMah, totalWifiPowerMah);
addEntry("Bluetooth", EntryType.POWER,
requestedBatterySipper.bluetoothPowerMah, totalBluetoothPowerMah);
addEntry("GPS", EntryType.POWER,
requestedBatterySipper.gpsPowerMah, totalGpsPowerMah);
addEntry("Camera", EntryType.POWER,
requestedBatterySipper.cameraPowerMah, totalCameraPowerMah);
addEntry("Flashlight", EntryType.POWER,
requestedBatterySipper.flashlightPowerMah, totalFlashlightPowerMah);
addEntry("Sensors", EntryType.POWER,
requestedBatterySipper.sensorPowerMah, totalSensorPowerMah);
addEntry("Audio", EntryType.POWER,
requestedBatterySipper.audioPowerMah, totalAudioPowerMah);
addEntry("Video", EntryType.POWER,
requestedBatterySipper.videoPowerMah, totalVideoPowerMah);
computeTotalPower(batteryUsageStats, totalPowerByComponentMah);
computeTotalPower(modeledBatteryUsageStats, totalModeledPowerByComponentMah);
computeTotalPowerForCustomComponent(batteryUsageStats, totalCustomPowerByComponentMah);
computeTotalDuration(batteryUsageStats, totalDurationByComponentMs);
addEntry("CPU time", EntryType.DURATION,
requestedBatterySipper.cpuTimeMs, totalCpuTimeMs);
addEntry("CPU foreground time", EntryType.DURATION,
requestedBatterySipper.cpuFgTimeMs, totalCpuFgTimeMs);
addEntry("Wake lock time", EntryType.DURATION,
requestedBatterySipper.wakeLockTimeMs, totalWakeLockTimeMs);
addEntry("WiFi running time", EntryType.DURATION,
requestedBatterySipper.wifiRunningTimeMs, totalWifiRunningTimeMs);
addEntry("Bluetooth time", EntryType.DURATION,
requestedBatterySipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs);
addEntry("GPS time", EntryType.DURATION,
requestedBatterySipper.gpsTimeMs, totalGpsTimeMs);
addEntry("Camera time", EntryType.DURATION,
requestedBatterySipper.cameraTimeMs, totalCameraTimeMs);
addEntry("Flashlight time", EntryType.DURATION,
requestedBatterySipper.flashlightTimeMs, totalFlashlightTimeMs);
addEntry("Audio time", EntryType.DURATION,
requestedBatterySipper.audioTimeMs, totalAudioTimeMs);
addEntry("Video time", EntryType.DURATION,
requestedBatterySipper.videoTimeMs, totalVideoTimeMs);
}
private boolean isSystemSipper(BatterySipper sipper) {
final int uid = sipper.uidObj == null ? -1 : sipper.getUid();
if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) {
return true;
} else if (sipper.mPackages != null) {
for (final String packageName : sipper.mPackages) {
for (final String systemPackage : PACKAGES_SYSTEM) {
if (systemPackage.equals(packageName)) {
return true;
}
}
for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
final String metricTitle = getPowerMetricTitle(component);
final int powerModel = requestedBatteryConsumer.getPowerModel(component);
if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
addEntry(metricTitle, EntryType.POWER_MODELED,
requestedBatteryConsumer.getConsumedPower(component),
totalPowerByComponentMah[component],
mBatteryConsumerInfo.isSystemBatteryConsumer);
} else {
addEntry(metricTitle + " (measured)", EntryType.POWER_MEASURED,
requestedBatteryConsumer.getConsumedPower(component),
totalPowerByComponentMah[component],
mBatteryConsumerInfo.isSystemBatteryConsumer);
addEntry(metricTitle + " (modeled)", EntryType.POWER_MODELED,
requestedModeledBatteryConsumer.getConsumedPower(component),
totalModeledPowerByComponentMah[component],
mBatteryConsumerInfo.isSystemBatteryConsumer);
}
}
return false;
for (int component = 0; component < customComponentCount; component++) {
final String name = requestedBatteryConsumer.getCustomPowerComponentName(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
addEntry(name + " (custom)", EntryType.POWER_CUSTOM,
requestedBatteryConsumer.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component),
totalCustomPowerByComponentMah[component],
mBatteryConsumerInfo.isSystemBatteryConsumer);
}
for (int component = 0; component < BatteryConsumer.TIME_COMPONENT_COUNT; component++) {
final String metricTitle = getTimeMetricTitle(component);
addEntry(metricTitle, EntryType.DURATION,
requestedBatteryConsumer.getUsageDurationMillis(component),
totalDurationByComponentMs[component],
mBatteryConsumerInfo.isSystemBatteryConsumer);
}
}
private void addEntry(String title, EntryType entryType, double amount, double totalAmount) {
private BatteryConsumer getRequestedBatteryConsumer(BatteryUsageStats batteryUsageStats,
String batteryConsumerId) {
for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
return consumer;
}
}
for (BatteryConsumer consumer : batteryUsageStats.getSystemBatteryConsumers()) {
if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
return consumer;
}
}
return null;
}
static String getPowerMetricTitle(int componentId) {
final String componentName = DebugUtils.constantToString(BatteryConsumer.class,
"POWER_COMPONENT_", componentId);
return componentName.charAt(0) + componentName.substring(1).toLowerCase().replace('_', ' ')
+ " power";
}
static String getTimeMetricTitle(int componentId) {
final String componentName = DebugUtils.constantToString(BatteryConsumer.class,
"TIME_COMPONENT_", componentId);
return componentName.charAt(0) + componentName.substring(1).toLowerCase().replace('_', ' ')
+ " time";
}
private void computeTotalPower(BatteryUsageStats batteryUsageStats,
double[] powerByComponentMah) {
for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT;
component++) {
powerByComponentMah[component] += consumer.getConsumedPower(component);
}
}
}
private void computeTotalDuration(BatteryUsageStats batteryUsageStats,
long[] durationByComponentMs) {
for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
for (int component = 0; component < BatteryConsumer.TIME_COMPONENT_COUNT;
component++) {
durationByComponentMs[component] += consumer.getUsageDurationMillis(component);
}
}
}
private void computeTotalPowerForCustomComponent(
BatteryUsageStats batteryUsageStats, double[] powerByComponentMah) {
for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
final int customComponentCount = consumer.getCustomPowerComponentCount();
for (int component = 0;
component < Math.min(customComponentCount, powerByComponentMah.length);
component++) {
powerByComponentMah[component] += consumer.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
}
}
}
private void addEntry(String title, EntryType entryType, double amount, double totalAmount,
boolean isSystemBatteryConsumer) {
Entry entry = new Entry();
entry.title = title;
entry.entryType = entryType;
entry.value = amount;
entry.total = totalAmount;
entry.isSystemBatteryConsumer = isSystemBatteryConsumer;
mEntries.add(entry);
}
private void maybeAddMeasuredEnergyEntry(BatterySipper.DrainType drainType,
BatteryStats batteryStats) {
switch (drainType) {
case AMBIENT_DISPLAY:
final long totalDozeMeasuredChargeUC =
batteryStats.getScreenDozeMeasuredBatteryConsumptionUC();
if (totalDozeMeasuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE) {
final double measuredCharge = UC_2_MAH * totalDozeMeasuredChargeUC;
addEntry("Measured ambient display power", EntryType.POWER, measuredCharge,
measuredCharge);
}
break;
case SCREEN:
final long totalScreenMeasuredChargeUC =
batteryStats.getScreenOnMeasuredBatteryConsumptionUC();
if (totalScreenMeasuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE) {
final double measuredCharge = UC_2_MAH * totalScreenMeasuredChargeUC;
addEntry("Measured screen power", EntryType.POWER, measuredCharge,
measuredCharge);
}
break;
}
}
public BatteryConsumerInfoHelper.BatteryConsumerInfo getBatteryConsumerInfo() {
return mBatteryConsumerInfo;
}
@@ -314,13 +198,9 @@ public class BatteryConsumerData {
return mEntries;
}
public static String batteryConsumerId(BatterySipper sipper) {
return sipper.drainType + "|" + sipper.userId + "|" + sipper.getUid();
}
public static String batteryConsumerId(BatteryConsumer consumer) {
if (consumer instanceof UidBatteryConsumer) {
return BatterySipper.DrainType.APP + "|"
return "APP|"
+ UserHandle.getUserId(((UidBatteryConsumer) consumer).getUid()) + "|"
+ ((UidBatteryConsumer) consumer).getUid();
} else if (consumer instanceof SystemBatteryConsumer) {

View File

@@ -18,14 +18,14 @@ package com.android.frameworks.core.batterystatsviewer;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.BatteryConsumer;
import android.os.Process;
import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.util.DebugUtils;
import androidx.annotation.NonNull;
import com.android.internal.os.BatterySipper;
import java.util.Locale;
class BatteryConsumerInfoHelper {
private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
@@ -37,111 +37,79 @@ class BatteryConsumerInfoHelper {
public ApplicationInfo iconInfo;
public CharSequence packages;
public CharSequence details;
public boolean isSystemBatteryConsumer;
}
@NonNull
public static BatteryConsumerInfo makeBatteryConsumerInfo(PackageManager packageManager,
@NonNull BatterySipper sipper) {
@NonNull BatteryConsumer batteryConsumer) {
BatteryConsumerInfo info = new BatteryConsumerInfo();
info.id = BatteryConsumerData.batteryConsumerId(sipper);
sipper.sumPower();
info.powerMah = sipper.totalSmearedPowerMah;
switch (sipper.drainType) {
case APP: {
int uid = sipper.getUid();
info.details = String.format("UID: %d", uid);
String packageWithHighestDrain = sipper.packageWithHighestDrain;
if (uid == Process.ROOT_UID) {
info.label = "<root>";
} else {
String[] packages = packageManager.getPackagesForUid(uid);
String primaryPackageName = null;
if (uid == Process.SYSTEM_UID) {
primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME;
} else if (packages != null) {
for (String name : packages) {
primaryPackageName = name;
if (name.equals(packageWithHighestDrain)) {
break;
}
}
}
info.id = BatteryConsumerData.batteryConsumerId(batteryConsumer);
info.powerMah = batteryConsumer.getConsumedPower();
if (primaryPackageName != null) {
try {
ApplicationInfo applicationInfo =
packageManager.getApplicationInfo(primaryPackageName, 0);
info.label = applicationInfo.loadLabel(packageManager);
info.iconInfo = applicationInfo;
} catch (PackageManager.NameNotFoundException e) {
info.label = primaryPackageName;
if (batteryConsumer instanceof UidBatteryConsumer) {
final UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer;
int uid = uidBatteryConsumer.getUid();
info.details = String.format("UID: %d", uid);
String packageWithHighestDrain = uidBatteryConsumer.getPackageWithHighestDrain();
if (uid == Process.ROOT_UID) {
info.label = "<root>";
} else {
String[] packages = packageManager.getPackagesForUid(uid);
String primaryPackageName = null;
if (uid == Process.SYSTEM_UID) {
primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME;
} else if (packages != null) {
for (String name : packages) {
primaryPackageName = name;
if (name.equals(packageWithHighestDrain)) {
break;
}
} else if (packageWithHighestDrain != null) {
info.label = packageWithHighestDrain;
}
if (packages != null && packages.length > 0) {
StringBuilder sb = new StringBuilder();
if (primaryPackageName != null) {
sb.append(primaryPackageName);
}
for (String packageName : packages) {
if (packageName.equals(primaryPackageName)) {
continue;
}
if (sb.length() != 0) {
sb.append(", ");
}
sb.append(packageName);
}
info.packages = sb;
}
}
break;
if (primaryPackageName != null) {
try {
ApplicationInfo applicationInfo =
packageManager.getApplicationInfo(primaryPackageName, 0);
info.label = applicationInfo.loadLabel(packageManager);
info.iconInfo = applicationInfo;
} catch (PackageManager.NameNotFoundException e) {
info.label = primaryPackageName;
}
} else if (packageWithHighestDrain != null) {
info.label = packageWithHighestDrain;
}
if (packages != null && packages.length > 0) {
StringBuilder sb = new StringBuilder();
if (primaryPackageName != null) {
sb.append(primaryPackageName);
}
for (String packageName : packages) {
if (packageName.equals(primaryPackageName)) {
continue;
}
if (sb.length() != 0) {
sb.append(", ");
}
sb.append(packageName);
}
info.packages = sb;
}
}
case USER:
info.label = "User";
info.details = String.format(Locale.getDefault(), "User ID: %d", sipper.userId);
break;
case AMBIENT_DISPLAY:
info.label = "Ambient display";
break;
case BLUETOOTH:
info.label = "Bluetooth";
break;
case CAMERA:
info.label = "Camera";
break;
case CELL:
info.label = "Cell";
break;
case FLASHLIGHT:
info.label = "Flashlight";
break;
case IDLE:
info.label = "Idle";
break;
case MEMORY:
info.label = "Memory";
break;
case OVERCOUNTED:
info.label = "Overcounted";
break;
case PHONE:
info.label = "Phone";
break;
case SCREEN:
info.label = "Screen";
break;
case UNACCOUNTED:
info.label = "Unaccounted";
break;
case WIFI:
info.label = "WiFi";
break;
} else if (batteryConsumer instanceof SystemBatteryConsumer) {
final SystemBatteryConsumer systemBatteryConsumer =
(SystemBatteryConsumer) batteryConsumer;
final int drainType = systemBatteryConsumer.getDrainType();
String name = DebugUtils.constantToString(SystemBatteryConsumer.class, "DRAIN_TYPE_",
drainType);
info.label = name.charAt(0) + name.substring(1).toLowerCase().replace('_', ' ');
info.isSystemBatteryConsumer = true;
}
// Default the app icon to System Server. This includes root, dex2oat and other UIDs.
if (info.iconInfo == null) {
try {

View File

@@ -18,10 +18,11 @@ package com.android.frameworks.core.batterystatsviewer;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.BatteryStats;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -37,8 +38,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.frameworks.core.batterystatsviewer.BatteryConsumerInfoHelper.BatteryConsumerInfo;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settingslib.utils.AsyncLoaderCompat;
import java.util.ArrayList;
@@ -99,44 +98,39 @@ public class BatteryConsumerPickerFragment extends Fragment {
private static class BatteryConsumerListLoader extends
AsyncLoaderCompat<List<BatteryConsumerInfo>> {
private final BatteryStatsHelper mStatsHelper;
private final int mPickerType;
private final UserManager mUserManager;
private final BatteryStatsManager mBatteryStatsManager;
private final PackageManager mPackageManager;
BatteryConsumerListLoader(Context context, int pickerType) {
super(context);
mUserManager = context.getSystemService(UserManager.class);
mStatsHelper = new BatteryStatsHelper(context, false /* collectBatteryBroadcast */);
mBatteryStatsManager = context.getSystemService(BatteryStatsManager.class);
mPickerType = pickerType;
mStatsHelper.create((Bundle) null);
mStatsHelper.clearStats();
mPackageManager = context.getPackageManager();
}
@Override
public List<BatteryConsumerInfo> loadInBackground() {
final BatteryUsageStats batteryUsageStats = mBatteryStatsManager.getBatteryUsageStats();
List<BatteryConsumerInfo> batteryConsumerList = new ArrayList<>();
mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
final List<BatterySipper> usageList = mStatsHelper.getUsageList();
for (BatterySipper sipper : usageList) {
switch (mPickerType) {
case PICKER_TYPE_APP:
if (sipper.drainType != BatterySipper.DrainType.APP) {
continue;
}
break;
case PICKER_TYPE_DRAIN:
default:
if (sipper.drainType == BatterySipper.DrainType.APP) {
continue;
}
}
batteryConsumerList.add(
BatteryConsumerInfoHelper.makeBatteryConsumerInfo(mPackageManager, sipper));
switch (mPickerType) {
case PICKER_TYPE_APP:
for (UidBatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
batteryConsumerList.add(
BatteryConsumerInfoHelper.makeBatteryConsumerInfo(mPackageManager,
consumer));
}
break;
case PICKER_TYPE_DRAIN:
default:
for (SystemBatteryConsumer consumer :
batteryUsageStats.getSystemBatteryConsumers()) {
batteryConsumerList.add(
BatteryConsumerInfoHelper.makeBatteryConsumerInfo(mPackageManager,
consumer));
}
break;
}
batteryConsumerList.sort(

View File

@@ -18,13 +18,10 @@ package com.android.frameworks.core.batterystatsviewer;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.BatteryStats;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -41,7 +38,6 @@ import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settingslib.utils.AsyncLoaderCompat;
import java.util.Collections;
@@ -50,24 +46,24 @@ import java.util.Locale;
public class BatteryStatsViewerActivity extends ComponentActivity {
private static final int BATTERY_STATS_REFRESH_RATE_MILLIS = 60 * 1000;
public static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId";
public static final int LOADER_BATTERY_STATS_HELPER = 0;
public static final int LOADER_BATTERY_USAGE_STATS = 1;
private static final int MILLIS_IN_MINUTE = 60000;
private static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId";
private static final int LOADER_BATTERY_USAGE_STATS = 1;
private BatteryStatsDataAdapter mBatteryStatsDataAdapter;
private Runnable mBatteryStatsRefresh = this::periodicBatteryStatsRefresh;
private final Runnable mBatteryStatsRefresh = this::periodicBatteryStatsRefresh;
private SharedPreferences mSharedPref;
private String mBatteryConsumerId;
private TextView mTitleView;
private TextView mDetailsView;
private ImageView mIconView;
private TextView mPackagesView;
private View mHeadingsView;
private RecyclerView mBatteryConsumerDataView;
private View mLoadingView;
private View mEmptyView;
private ActivityResultLauncher<Void> mStartAppPicker = registerForActivityResult(
private final ActivityResultLauncher<Void> mStartAppPicker = registerForActivityResult(
BatteryConsumerPickerActivity.CONTRACT, this::onApplicationSelected);
private BatteryStatsHelper mBatteryStatsHelper;
private List<BatteryUsageStats> mBatteryUsageStats;
@Override
@@ -85,6 +81,7 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
mDetailsView = findViewById(R.id.details);
mIconView = findViewById(android.R.id.icon);
mPackagesView = findViewById(R.id.packages);
mHeadingsView = findViewById(R.id.headings);
mBatteryConsumerDataView = findViewById(R.id.battery_consumer_data_view);
mBatteryConsumerDataView.setLayoutManager(new LinearLayoutManager(this));
@@ -139,55 +136,10 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
private void loadBatteryStats() {
LoaderManager loaderManager = LoaderManager.getInstance(this);
loaderManager.restartLoader(LOADER_BATTERY_STATS_HELPER, null,
new BatteryStatsHelperLoaderCallbacks());
loaderManager.restartLoader(LOADER_BATTERY_USAGE_STATS, null,
new BatteryUsageStatsLoaderCallbacks());
}
private static class BatteryStatsHelperLoader extends AsyncLoaderCompat<BatteryStatsHelper> {
private final BatteryStatsHelper mBatteryStatsHelper;
private final UserManager mUserManager;
BatteryStatsHelperLoader(Context context) {
super(context);
mUserManager = context.getSystemService(UserManager.class);
mBatteryStatsHelper = new BatteryStatsHelper(context,
false /* collectBatteryBroadcast */);
mBatteryStatsHelper.create((Bundle) null);
mBatteryStatsHelper.clearStats();
}
@Override
public BatteryStatsHelper loadInBackground() {
mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
UserHandle.myUserId());
return mBatteryStatsHelper;
}
@Override
protected void onDiscardResult(BatteryStatsHelper result) {
}
}
private class BatteryStatsHelperLoaderCallbacks implements LoaderCallbacks<BatteryStatsHelper> {
@NonNull
@Override
public Loader<BatteryStatsHelper> onCreateLoader(int id, Bundle args) {
return new BatteryStatsHelperLoader(BatteryStatsViewerActivity.this);
}
@Override
public void onLoadFinished(@NonNull Loader<BatteryStatsHelper> loader,
BatteryStatsHelper batteryStatsHelper) {
onBatteryStatsHelperLoaded(batteryStatsHelper);
}
@Override
public void onLoaderReset(@NonNull Loader<BatteryStatsHelper> loader) {
}
}
private static class BatteryUsageStatsLoader extends
AsyncLoaderCompat<List<BatteryUsageStats>> {
private final BatteryStatsManager mBatteryStatsManager;
@@ -200,10 +152,13 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
@Override
public List<BatteryUsageStats> loadInBackground() {
final BatteryUsageStatsQuery queryDefault =
new BatteryUsageStatsQuery.Builder().build();
new BatteryUsageStatsQuery.Builder()
.includePowerModels()
.build();
final BatteryUsageStatsQuery queryPowerProfileModeledOnly =
new BatteryUsageStatsQuery.Builder()
.powerProfileModeledOnly()
.includePowerModels()
.build();
return mBatteryStatsManager.getBatteryUsageStats(
List.of(queryDefault, queryPowerProfileModeledOnly));
@@ -233,22 +188,13 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
}
}
public void onBatteryStatsHelperLoaded(BatteryStatsHelper batteryStatsHelper) {
mBatteryStatsHelper = batteryStatsHelper;
onBatteryStatsDataLoaded();
}
private void onBatteryUsageStatsLoaded(List<BatteryUsageStats> batteryUsageStats) {
mBatteryUsageStats = batteryUsageStats;
onBatteryStatsDataLoaded();
}
public void onBatteryStatsDataLoaded() {
if (mBatteryStatsHelper == null || mBatteryUsageStats == null) {
return;
}
BatteryConsumerData batteryConsumerData = new BatteryConsumerData(this, mBatteryStatsHelper,
BatteryConsumerData batteryConsumerData = new BatteryConsumerData(this,
mBatteryUsageStats, mBatteryConsumerId);
BatteryConsumerInfoHelper.BatteryConsumerInfo
@@ -256,6 +202,7 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
if (batteryConsumerInfo == null) {
mTitleView.setText("Battery consumer not found");
mPackagesView.setVisibility(View.GONE);
mHeadingsView.setVisibility(View.GONE);
} else {
mTitleView.setText(batteryConsumerInfo.label);
if (batteryConsumerInfo.details != null) {
@@ -273,6 +220,12 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
} else {
mPackagesView.setVisibility(View.GONE);
}
if (batteryConsumerInfo.isSystemBatteryConsumer) {
mHeadingsView.setVisibility(View.VISIBLE);
} else {
mHeadingsView.setVisibility(View.GONE);
}
}
mBatteryStatsDataAdapter.setEntries(batteryConsumerData.getEntries());
@@ -290,6 +243,7 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
private static class BatteryStatsDataAdapter extends
RecyclerView.Adapter<BatteryStatsDataAdapter.ViewHolder> {
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView iconImageView;
public TextView titleTextView;
public TextView amountTextView;
public TextView percentTextView;
@@ -297,6 +251,7 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
ViewHolder(View itemView) {
super(itemView);
iconImageView = itemView.findViewById(R.id.icon);
titleTextView = itemView.findViewById(R.id.title);
amountTextView = itemView.findViewById(R.id.amount);
percentTextView = itemView.findViewById(R.id.percent);
@@ -328,21 +283,56 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
BatteryConsumerData.Entry entry = mEntries.get(position);
switch (entry.entryType) {
case POWER:
case POWER_MODELED:
viewHolder.titleTextView.setText(entry.title);
viewHolder.amountTextView.setText(
String.format(Locale.getDefault(), "%.1f mAh", entry.value));
viewHolder.iconImageView.setImageResource(R.drawable.gm_calculate_24);
viewHolder.itemView.setBackgroundResource(
R.color.battery_consumer_bg_power_profile);
break;
case POWER_MEASURED:
viewHolder.titleTextView.setText(entry.title);
viewHolder.amountTextView.setText(
String.format(Locale.getDefault(), "%.1f mAh", entry.value));
viewHolder.iconImageView.setImageResource(R.drawable.gm_amp_24);
viewHolder.itemView.setBackgroundResource(
R.color.battery_consumer_bg_measured_energy);
break;
case POWER_CUSTOM:
viewHolder.titleTextView.setText(entry.title);
viewHolder.amountTextView.setText(
String.format(Locale.getDefault(), "%.1f mAh", entry.value));
viewHolder.iconImageView.setImageResource(R.drawable.gm_custom_24);
viewHolder.itemView.setBackgroundResource(
R.color.battery_consumer_bg_measured_energy);
break;
case DURATION:
viewHolder.titleTextView.setText(entry.title);
viewHolder.amountTextView.setText(
String.format(Locale.getDefault(), "%,d ms", (long) entry.value));
final long durationMs = (long) entry.value;
CharSequence text;
if (durationMs < MILLIS_IN_MINUTE) {
text = String.format(Locale.getDefault(), "%,d ms", durationMs);
} else {
text = String.format(Locale.getDefault(), "%,d m %d s",
durationMs / MILLIS_IN_MINUTE,
(durationMs % MILLIS_IN_MINUTE) / 1000);
}
viewHolder.amountTextView.setText(text);
viewHolder.iconImageView.setImageResource(R.drawable.gm_timer_24);
viewHolder.itemView.setBackground(null);
break;
}
double proportion = entry.total != 0 ? entry.value * 100 / entry.total : 0;
viewHolder.percentTextView.setText(String.format(Locale.getDefault(), "%.1f%%",
proportion));
double proportion;
if (entry.isSystemBatteryConsumer) {
proportion = entry.value != 0 ? entry.total * 100 / entry.value : 0;
} else {
proportion = entry.total != 0 ? entry.value * 100 / entry.total : 0;
}
viewHolder.percentTextView.setText(
String.format(Locale.getDefault(), "%.1f%%", proportion));
}
}
}