summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitri Plotnikov <dplotnikov@google.com>2020-11-13 09:46:45 -0800
committerDmitri Plotnikov <dplotnikov@google.com>2020-12-03 10:42:41 -0800
commitbfe822007b3f70b58eaf0f755d32ce6b59684a31 (patch)
tree6aa13d4220d2aa70dce187c0238fb7465324daf4
parent7c976c677409827bf26864e7a0d5a0d02e03d64a (diff)
Introduce BatteryUsageStats API
Bug: 158137862 Test: Start Settings app, navigate to Apps, pick an app, navigate to Battery Change-Id: I8da8cc9501e01282b855f1bee6032911d91aaf77
-rw-r--r--core/java/android/os/BatteryConsumer.java89
-rw-r--r--core/java/android/os/BatteryStatsManager.java16
-rw-r--r--core/java/android/os/BatteryUsageStats.aidl19
-rw-r--r--core/java/android/os/BatteryUsageStats.java138
-rw-r--r--core/java/android/os/PowerComponents.java170
-rw-r--r--core/java/android/os/UidBatteryConsumer.java145
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl4
-rw-r--r--core/java/com/android/internal/os/BatteryUsageStatsProvider.java76
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java14
-rw-r--r--tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java74
10 files changed, 745 insertions, 0 deletions
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
new file mode 100644
index 000000000000..d00c3c361722
--- /dev/null
+++ b/core/java/android/os/BatteryConsumer.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Interface for objects containing battery attribution data.
+ *
+ * @hide
+ */
+public abstract class BatteryConsumer {
+
+ /**
+ * Power usage component, describing the particular part of the system
+ * responsible for power drain.
+ *
+ * @hide
+ */
+ @IntDef(prefix = {"POWER_COMPONENT_"}, value = {
+ POWER_COMPONENT_CPU,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public static @interface PowerComponent {
+ }
+
+ public static final int POWER_COMPONENT_CPU = 0;
+
+ public static final int POWER_COMPONENT_COUNT = 1;
+
+ public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000;
+ public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999;
+
+ private final PowerComponents mPowerComponents;
+
+ protected BatteryConsumer(@NonNull PowerComponents powerComponents) {
+ mPowerComponents = powerComponents;
+ }
+
+ /**
+ * Total power consumed by this consumer, in mAh.
+ */
+ public double getConsumedPower() {
+ return mPowerComponents.getTotalPowerConsumed();
+ }
+
+ /**
+ * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+ *
+ * @param componentId The ID of the power component, e.g.
+ * {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+ * @return Amount of consumed power in mAh.
+ */
+ public double getConsumedPower(@PowerComponent int componentId) {
+ return mPowerComponents.getConsumedPower(componentId);
+ }
+
+ /**
+ * Returns the amount of drain attributed to the specified custom drain type.
+ *
+ * @param componentId The ID of the custom power component.
+ * @return Amount of consumed power in mAh.
+ */
+ public double getConsumedPowerForCustomComponent(int componentId) {
+ return mPowerComponents.getConsumedPowerForCustomComponent(componentId);
+ }
+
+ protected void writeToParcel(Parcel dest, int flags) {
+ mPowerComponents.writeToParcel(dest, flags);
+ }
+}
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index a9585c62866b..d9e01cd4fc7d 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -161,6 +161,22 @@ public final class BatteryStatsManager {
}
/**
+ * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
+ * and per-UID basis.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+ @NonNull
+ public BatteryUsageStats getBatteryUsageStats() {
+ try {
+ return mBatteryStats.getBatteryUsageStats();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Indicates that the wifi connection RSSI has changed.
*
* @param newRssi The new RSSI value.
diff --git a/core/java/android/os/BatteryUsageStats.aidl b/core/java/android/os/BatteryUsageStats.aidl
new file mode 100644
index 000000000000..0400f19974f1
--- /dev/null
+++ b/core/java/android/os/BatteryUsageStats.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.os;
+
+parcelable BatteryUsageStats;
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
new file mode 100644
index 000000000000..3f036cdcfa72
--- /dev/null
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains a snapshot of battery attribution data, on a per-subsystem and per-UID basis.
+ *
+ * @hide
+ */
+public final class BatteryUsageStats implements Parcelable {
+ private final double mConsumedPower;
+ private final int mDischargePercentage;
+ private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers;
+
+ private BatteryUsageStats(@NonNull Builder builder) {
+ mConsumedPower = builder.mConsumedPower;
+ mDischargePercentage = builder.mDischargePercentage;
+ mUidBatteryConsumers = builder.mUidBatteryConsumers;
+ }
+
+ /**
+ * Portion of battery charge drained since BatteryStats reset (e.g. due to being fully
+ * charged), as percentage of the full charge in the range [0:100]
+ */
+ public int getDischargePercentage() {
+ return mDischargePercentage;
+ }
+
+ /**
+ * Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully
+ * charged), in mAh
+ */
+ public double getConsumedPower() {
+ return mConsumedPower;
+ }
+
+ @NonNull
+ public List<UidBatteryConsumer> getUidBatteryConsumers() {
+ return mUidBatteryConsumers;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private BatteryUsageStats(@NonNull Parcel source) {
+ mUidBatteryConsumers = new ArrayList<>();
+ source.readParcelableList(mUidBatteryConsumers, getClass().getClassLoader());
+ mConsumedPower = source.readDouble();
+ mDischargePercentage = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelableList(mUidBatteryConsumers, flags);
+ dest.writeDouble(mConsumedPower);
+ dest.writeInt(mDischargePercentage);
+ }
+
+ @NonNull
+ public static final Creator<BatteryUsageStats> CREATOR = new Creator<BatteryUsageStats>() {
+ public BatteryUsageStats createFromParcel(@NonNull Parcel source) {
+ return new BatteryUsageStats(source);
+ }
+
+ public BatteryUsageStats[] newArray(int size) {
+ return new BatteryUsageStats[size];
+ }
+ };
+
+ /**
+ * Builder for BatteryUsageStats.
+ */
+ public static final class Builder {
+ private double mConsumedPower;
+ private int mDischargePercentage;
+ private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers = new ArrayList<>();
+
+ /**
+ * Constructs a read-only object using the Builder values.
+ */
+ @NonNull
+ public BatteryUsageStats build() {
+ return new BatteryUsageStats(this);
+ }
+
+ /**
+ * Sets the battery discharge amount since BatteryStats reset as percentage of the full
+ * charge.
+ */
+ @SuppressLint("PercentageInt") // See b/174188159
+ @NonNull
+ public Builder setDischargePercentage(int dischargePercentage) {
+ mDischargePercentage = dischargePercentage;
+ return this;
+ }
+
+ /**
+ * Sets the battery discharge amount since BatteryStats reset, in mAh.
+ */
+ @NonNull
+ public Builder setConsumedPower(double consumedPower) {
+ mConsumedPower = consumedPower;
+ return this;
+ }
+
+ /**
+ * Adds a UidBatteryConsumer, which represents battery attribution data for an
+ * individual UID.
+ */
+ @NonNull
+ public Builder addUidBatteryConsumer(@NonNull UidBatteryConsumer uidBatteryConsumer) {
+ mUidBatteryConsumers.add(uidBatteryConsumer);
+ return this;
+ }
+ }
+}
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
new file mode 100644
index 000000000000..42ba1ff60e5a
--- /dev/null
+++ b/core/java/android/os/PowerComponents.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.os;
+
+
+import android.annotation.NonNull;
+
+/**
+ * Contains details of battery attribution data broken down to individual power drain types
+ * such as CPU, RAM, GPU etc.
+ *
+ * @hide
+ */
+class PowerComponents {
+
+ private final double mTotalPowerConsumed;
+ private final double[] mPowerComponents;
+
+ PowerComponents(@NonNull Builder builder) {
+ mTotalPowerConsumed = builder.mTotalPowerConsumed;
+ mPowerComponents = builder.mPowerComponents;
+ }
+
+ PowerComponents(@NonNull Parcel source) {
+ mTotalPowerConsumed = source.readDouble();
+ mPowerComponents = source.createDoubleArray();
+ }
+
+ /** Writes contents to Parcel */
+ void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mTotalPowerConsumed);
+ dest.writeDoubleArray(mPowerComponents);
+ }
+
+ /**
+ * Total power consumed by this consumer, in mAh.
+ */
+ public double getTotalPowerConsumed() {
+ return mTotalPowerConsumed;
+ }
+
+ /**
+ * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+ *
+ * @param componentId The ID of the power component, e.g.
+ * {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+ * @return Amount of consumed power in mAh.
+ */
+ public double getConsumedPower(@UidBatteryConsumer.PowerComponent int componentId) {
+ if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) {
+ throw new IllegalArgumentException(
+ "Unsupported power component ID: " + componentId);
+ }
+ try {
+ return mPowerComponents[componentId];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("Unsupported power component ID: " + componentId);
+ }
+ }
+
+ /**
+ * Returns the amount of drain attributed to the specified custom drain type.
+ *
+ * @param componentId The ID of the custom power component.
+ * @return Amount of consumed power in mAh.
+ */
+ public double getConsumedPowerForCustomComponent(int componentId) {
+ if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
+ throw new IllegalArgumentException(
+ "Unsupported custom power component ID: " + componentId);
+ }
+ try {
+ return mPowerComponents[
+ BatteryConsumer.POWER_COMPONENT_COUNT + componentId
+ - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException(
+ "Unsupported custom power component ID: " + componentId);
+ }
+ }
+
+ /**
+ * Builder for PowerComponents.
+ */
+ static final class Builder {
+ private double mTotalPowerConsumed;
+ private final double[] mPowerComponents;
+
+ Builder(int customPowerComponentCount) {
+ mPowerComponents = new double[BatteryConsumer.POWER_COMPONENT_COUNT
+ + customPowerComponentCount];
+ }
+
+ /**
+ * Sets the sum amount of power consumed since BatteryStats reset.
+ */
+ @NonNull
+ public Builder setTotalPowerConsumed(double totalPowerConsumed) {
+ mTotalPowerConsumed = totalPowerConsumed;
+ return this;
+ }
+
+ /**
+ * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+ *
+ * @param componentId The ID of the power component, e.g.
+ * {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+ * @param componentPower Amount of consumed power in mAh.
+ */
+ @NonNull
+ public Builder setConsumedPower(@UidBatteryConsumer.PowerComponent int componentId,
+ double componentPower) {
+ if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) {
+ throw new IllegalArgumentException(
+ "Unsupported power component ID: " + componentId);
+ }
+ try {
+ mPowerComponents[componentId] = componentPower;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException(
+ "Unsupported power component ID: " + componentId);
+ }
+ return this;
+ }
+
+ /**
+ * Sets the amount of drain attributed to the specified custom drain type.
+ *
+ * @param componentId The ID of the custom power component.
+ * @param componentPower Amount of consumed power in mAh.
+ */
+ @NonNull
+ public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) {
+ if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
+ throw new IllegalArgumentException(
+ "Unsupported custom power component ID: " + componentId);
+ }
+ try {
+ mPowerComponents[BatteryConsumer.POWER_COMPONENT_COUNT + componentId
+ - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID] = componentPower;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException(
+ "Unsupported custom power component ID: " + componentId);
+ }
+ return this;
+ }
+
+ /**
+ * Creates a read-only object out of the Builder values.
+ */
+ @NonNull
+ public PowerComponents build() {
+ return new PowerComponents(this);
+ }
+ }
+}
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
new file mode 100644
index 000000000000..7dcbf7d4cef3
--- /dev/null
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Contains power consumption data attributed to a specific UID.
+ *
+ * @hide
+ */
+public final class UidBatteryConsumer extends BatteryConsumer implements Parcelable {
+
+ private final int mUid;
+ @Nullable
+ private final String mPackageWithHighestDrain;
+
+ public int getUid() {
+ return mUid;
+ }
+
+ @Nullable
+ public String getPackageWithHighestDrain() {
+ return mPackageWithHighestDrain;
+ }
+
+ private UidBatteryConsumer(@NonNull Builder builder) {
+ super(builder.mPowerComponentsBuilder.build());
+ mUid = builder.mUid;
+ mPackageWithHighestDrain = builder.mPackageWithHighestDrain;
+ }
+
+ private UidBatteryConsumer(@NonNull Parcel source) {
+ super(new PowerComponents(source));
+ mUid = source.readInt();
+ mPackageWithHighestDrain = source.readString();
+ }
+
+ /**
+ * Writes the contents into a Parcel.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mUid);
+ dest.writeString(mPackageWithHighestDrain);
+ }
+
+ @NonNull
+ public static final Creator<UidBatteryConsumer> CREATOR = new Creator<UidBatteryConsumer>() {
+ public UidBatteryConsumer createFromParcel(@NonNull Parcel source) {
+ return new UidBatteryConsumer(source);
+ }
+
+ public UidBatteryConsumer[] newArray(int size) {
+ return new UidBatteryConsumer[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Builder for UidBatteryConsumer.
+ */
+ public static final class Builder {
+ private final PowerComponents.Builder mPowerComponentsBuilder;
+ private final int mUid;
+ private String mPackageWithHighestDrain;
+
+ public Builder(int customPowerComponentCount, int uid) {
+ mPowerComponentsBuilder = new PowerComponents.Builder(customPowerComponentCount);
+ mUid = uid;
+ }
+
+ /**
+ * Creates a read-only object out of the Builder values.
+ */
+ @NonNull
+ public UidBatteryConsumer build() {
+ return new UidBatteryConsumer(this);
+ }
+
+ /**
+ * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+ *
+ * @param componentId The ID of the power component, e.g.
+ * {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+ * @param componentPower Amount of consumed power in mAh.
+ */
+ @NonNull
+ public Builder setConsumedPower(@PowerComponent int componentId, double componentPower) {
+ mPowerComponentsBuilder.setConsumedPower(componentId, componentPower);
+ return this;
+ }
+
+ /**
+ * Sets the amount of drain attributed to the specified custom drain type.
+ *
+ * @param componentId The ID of the custom power component.
+ * @param componentPower Amount of consumed power in mAh.
+ */
+ @NonNull
+ public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) {
+ mPowerComponentsBuilder.setConsumedPowerForCustomComponent(componentId, componentPower);
+ return this;
+ }
+
+ /**
+ * Sets the amount of power consumed since BatteryStats reset, mAh.
+ */
+ @NonNull
+ public Builder setConsumedPower(double consumedPower) {
+ mPowerComponentsBuilder.setTotalPowerConsumed(consumedPower);
+ return this;
+ }
+
+ /**
+ * Sets the name of the package owned by this UID that consumed the highest amount
+ * of power since BatteryStats reset.
+ */
+ @NonNull
+ public Builder setPackageWithHighestDrain(@Nullable String packageName) {
+ mPackageWithHighestDrain = packageName;
+ return this;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 6d98a5982c4f..0b047a2a3d47 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -19,6 +19,7 @@ package com.android.internal.app;
import com.android.internal.os.BatteryStatsImpl;
import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.os.BatteryUsageStats;
import android.os.ParcelFileDescriptor;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
@@ -49,6 +50,9 @@ interface IBatteryStats {
void noteResetFlashlight();
// Remaining methods are only used in Java.
+
+ BatteryUsageStats getBatteryUsageStats();
+
@UnsupportedAppUsage
byte[] getStatistics();
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
new file mode 100644
index 000000000000..62e9f98181ab
--- /dev/null
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package com.android.internal.os;
+
+import android.content.Context;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.Bundle;
+import android.os.UidBatteryConsumer;
+import android.os.UserManager;
+
+import java.util.List;
+
+/**
+ * Uses accumulated battery stats data and PowerCalculators to produce power
+ * usage data attributed to subsystems and UIDs.
+ */
+public class BatteryUsageStatsProvider {
+ private final Context mContext;
+ private final BatteryStatsImpl mStats;
+
+ public BatteryUsageStatsProvider(Context context, BatteryStatsImpl stats) {
+ mContext = context;
+ mStats = stats;
+ }
+
+ /**
+ * Returns a snapshot of battery attribution data.
+ */
+ public BatteryUsageStats getBatteryUsageStats() {
+
+ // TODO(b/174186345): instead of BatteryStatsHelper, use PowerCalculators directly.
+ final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(mContext,
+ false /* collectBatteryBroadcast */);
+ batteryStatsHelper.create((Bundle) null);
+ final UserManager userManager = mContext.getSystemService(UserManager.class);
+ batteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
+ userManager.getUserProfiles());
+
+ // TODO(b/174186358): read extra power component number from configuration
+ final int customPowerComponentCount = 0;
+ final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder()
+ .setDischargePercentage(batteryStatsHelper.getStats().getDischargeAmount(0))
+ .setConsumedPower(batteryStatsHelper.getTotalPower());
+
+ final List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
+ for (int i = 0; i < usageList.size(); i++) {
+ final BatterySipper sipper = usageList.get(i);
+ if (sipper.drainType == BatterySipper.DrainType.APP) {
+ batteryUsageStatsBuilder.addUidBatteryConsumer(
+ new UidBatteryConsumer.Builder(customPowerComponentCount, sipper.getUid())
+ .setPackageWithHighestDrain(sipper.packageWithHighestDrain)
+ .setConsumedPower(sipper.sumPower())
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU,
+ sipper.cpuPowerMah)
+ .build());
+ }
+ }
+ return batteryUsageStatsBuilder.build();
+ }
+}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 46e16bcbb1b6..3b6f0ac42ed2 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -25,6 +25,7 @@ import android.hardware.power.stats.EnergyConsumerId;
import android.hardware.power.stats.EnergyConsumerResult;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
+import android.os.BatteryUsageStats;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -57,6 +58,7 @@ import android.util.Slog;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.BatteryUsageStatsProvider;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.RailStats;
@@ -105,6 +107,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider;
private final Context mContext;
private final BatteryExternalStatsWorker mWorker;
+ private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
private native void getLowPowerStats(RpmStats rpmStats);
private native int getPlatformLowPowerStats(ByteBuffer outBuffer);
@@ -258,6 +261,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
mStats.setPowerProfileLocked(new PowerProfile(context));
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats);
}
public void publish() {
@@ -550,6 +554,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub
// Public interface...
+ /**
+ * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
+ * and per-UID basis.
+ */
+ public BatteryUsageStats getBatteryUsageStats() {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ return mBatteryUsageStatsProvider.getBatteryUsageStats();
+ }
+
public byte[] getStatistics() {
mContext.enforceCallingPermission(
android.Manifest.permission.BATTERY_STATS, null);
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
new file mode 100644
index 000000000000..54d70478f762
--- /dev/null
+++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.BatteryStatsManager;
+import android.os.BatteryUsageStats;
+import android.os.UidBatteryConsumer;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BatteryUsageStatsPerfTest {
+
+ @Rule
+ public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ /**
+ * Measures the performance of {@link BatteryStatsManager#getBatteryUsageStats()},
+ * which triggers a battery stats sync on every iteration.
+ */
+ @Test
+ public void testGetBatteryUsageStats() {
+ final Context context = InstrumentationRegistry.getContext();
+ final BatteryStatsManager batteryStatsManager =
+ context.getSystemService(BatteryStatsManager.class);
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats();
+
+ state.pauseTiming();
+
+ List<UidBatteryConsumer> uidBatteryConsumers =
+ batteryUsageStats.getUidBatteryConsumers();
+ double power = 0;
+ for (int i = 0; i < uidBatteryConsumers.size(); i++) {
+ UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(i);
+ power += uidBatteryConsumer.getConsumedPower();
+ }
+
+ assertThat(power).isGreaterThan(0.0);
+
+ state.resumeTiming();
+ }
+ }
+}