BatteryUsageStats atom - frameworks/base

Writes the BatteryUsageStats atoms.proto atoms
based on the current BatteryUsageStats data in BatteryStats.

Does NOT write the past pre-reset snapshot atoms; that is
an adventure for a future cl.

Bug: 184095105
Test: atest BatteryUsageStatsProtoTests
Test: statsd_testdrive <atomId>
Change-Id: I2fc5a983deb58d7d393c0696db2165b124c94dc2
This commit is contained in:
Adam Bookatz
2021-05-05 12:39:16 -07:00
parent c73c098665
commit 2f3abb4441
8 changed files with 611 additions and 20 deletions

View File

@@ -18,6 +18,8 @@ package android.os;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -59,27 +61,32 @@ public abstract class BatteryConsumer {
public static @interface PowerComponent {
}
public static final int POWER_COMPONENT_SCREEN = 0;
public static final int POWER_COMPONENT_CPU = 1;
public static final int POWER_COMPONENT_BLUETOOTH = 2;
public static final int POWER_COMPONENT_CAMERA = 3;
public static final int POWER_COMPONENT_AUDIO = 4;
public static final int POWER_COMPONENT_VIDEO = 5;
public static final int POWER_COMPONENT_FLASHLIGHT = 6;
public static final int POWER_COMPONENT_SYSTEM_SERVICES = 7;
public static final int POWER_COMPONENT_MOBILE_RADIO = 8;
public static final int POWER_COMPONENT_SENSORS = 9;
public static final int POWER_COMPONENT_GNSS = 10;
public static final int POWER_COMPONENT_WIFI = 11;
public static final int POWER_COMPONENT_WAKELOCK = 12;
public static final int POWER_COMPONENT_MEMORY = 13;
public static final int POWER_COMPONENT_PHONE = 14;
public static final int POWER_COMPONENT_AMBIENT_DISPLAY = 15;
public static final int POWER_COMPONENT_IDLE = 16;
public static final int POWER_COMPONENT_SCREEN = OsProtoEnums.POWER_COMPONENT_SCREEN; // 0
public static final int POWER_COMPONENT_CPU = OsProtoEnums.POWER_COMPONENT_CPU; // 1
public static final int POWER_COMPONENT_BLUETOOTH = OsProtoEnums.POWER_COMPONENT_BLUETOOTH; // 2
public static final int POWER_COMPONENT_CAMERA = OsProtoEnums.POWER_COMPONENT_CAMERA; // 3
public static final int POWER_COMPONENT_AUDIO = OsProtoEnums.POWER_COMPONENT_AUDIO; // 4
public static final int POWER_COMPONENT_VIDEO = OsProtoEnums.POWER_COMPONENT_VIDEO; // 5
public static final int POWER_COMPONENT_FLASHLIGHT =
OsProtoEnums.POWER_COMPONENT_FLASHLIGHT; // 6
public static final int POWER_COMPONENT_SYSTEM_SERVICES =
OsProtoEnums.POWER_COMPONENT_SYSTEM_SERVICES; // 7
public static final int POWER_COMPONENT_MOBILE_RADIO =
OsProtoEnums.POWER_COMPONENT_MOBILE_RADIO; // 8
public static final int POWER_COMPONENT_SENSORS = OsProtoEnums.POWER_COMPONENT_SENSORS; // 9
public static final int POWER_COMPONENT_GNSS = OsProtoEnums.POWER_COMPONENT_GNSS; // 10
public static final int POWER_COMPONENT_WIFI = OsProtoEnums.POWER_COMPONENT_WIFI; // 11
public static final int POWER_COMPONENT_WAKELOCK = OsProtoEnums.POWER_COMPONENT_WAKELOCK; // 12
public static final int POWER_COMPONENT_MEMORY = OsProtoEnums.POWER_COMPONENT_MEMORY; // 13
public static final int POWER_COMPONENT_PHONE = OsProtoEnums.POWER_COMPONENT_PHONE; // 14
public static final int POWER_COMPONENT_AMBIENT_DISPLAY =
OsProtoEnums.POWER_COMPONENT_AMBIENT_DISPLAY; // 15
public static final int POWER_COMPONENT_IDLE = OsProtoEnums.POWER_COMPONENT_IDLE; // 16
// Power that is re-attributed to other battery consumers. For example, for System Server
// this represents the power attributed to apps requesting system services.
// The value should be negative or zero.
public static final int POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS = 17;
public static final int POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS =
OsProtoEnums.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS; // 17
public static final int POWER_COMPONENT_COUNT = 18;
@@ -263,6 +270,54 @@ public abstract class BatteryConsumer {
*/
public abstract void dump(PrintWriter pw, boolean skipEmptyComponents);
/** Returns whether there are any atoms.proto BATTERY_CONSUMER_DATA data to write to a proto. */
boolean hasStatsProtoData() {
return writeStatsProtoImpl(null, /* Irrelevant fieldId: */ 0);
}
/** Writes the atoms.proto BATTERY_CONSUMER_DATA for this BatteryConsumer to the given proto. */
void writeStatsProto(@NonNull ProtoOutputStream proto, long fieldId) {
writeStatsProtoImpl(proto, fieldId);
}
/**
* Returns whether there are any atoms.proto BATTERY_CONSUMER_DATA data to write to a proto,
* and writes it to the given proto if it is non-null.
*/
private boolean writeStatsProtoImpl(@Nullable ProtoOutputStream proto, long fieldId) {
final long totalConsumedPowerDeciCoulombs = convertMahToDeciCoulombs(getConsumedPower());
if (totalConsumedPowerDeciCoulombs == 0) {
// NOTE: Strictly speaking we should also check !mPowerComponents.hasStatsProtoData().
// However, that call is a bit expensive (a for loop). And the only way that
// totalConsumedPower can be 0 while mPowerComponents.hasStatsProtoData() is true is
// if POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS (which is the only negative
// allowed) happens to exactly equal the sum of all other components, which
// can't really happen in practice.
// So we'll just adopt the rule "if total==0, don't write any details".
// If negative values are used for other things in the future, this can be revisited.
return false;
}
if (proto == null) {
// We're just asked whether there is data, not to actually write it. And there is.
return true;
}
final long token = proto.start(fieldId);
proto.write(
BatteryUsageStatsAtomsProto.BatteryConsumerData.TOTAL_CONSUMED_POWER_DECI_COULOMBS,
totalConsumedPowerDeciCoulombs);
mPowerComponents.writeStatsProto(proto);
proto.end(token);
return true;
}
/** Converts charge from milliamp hours (mAh) to decicoulombs (dC). */
static long convertMahToDeciCoulombs(double powerMah) {
return (long) (powerMah * (10 * 3600 / 1000) + 0.5);
}
protected abstract static class BaseBuilder<T extends BaseBuilder<?>> {
final PowerComponents.Builder mPowerComponentsBuilder;

View File

@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.util.Range;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
@@ -158,7 +159,8 @@ public final class BatteryUsageStats implements Parcelable {
/**
* 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]
* charged), as percentage of the full charge in the range [0:100]. May exceed 100 if
* the device repeatedly charged and discharged prior to the reset.
*/
public int getDischargePercentage() {
return mDischargePercentage;
@@ -335,6 +337,70 @@ public final class BatteryUsageStats implements Parcelable {
}
};
/** Returns a proto (as used for atoms.proto) corresponding to this BatteryUsageStats. */
public byte[] getStatsProto(long sessionEndTimestampMs) {
final long sessionStartMillis = getStatsStartTimestamp();
// TODO(b/187223764): Use the getStatsEndTimestamp() instead, once that is added.
final long sessionEndMillis = sessionEndTimestampMs;
final long sessionDurationMillis = sessionEndTimestampMs - getStatsStartTimestamp();
final BatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer(
AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
final int sessionDischargePercentage = getDischargePercentage();
final ProtoOutputStream proto = new ProtoOutputStream();
proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, sessionStartMillis);
proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, sessionEndMillis);
proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, sessionDurationMillis);
deviceBatteryConsumer.writeStatsProto(proto,
BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER);
writeUidBatteryConsumersProto(proto);
proto.write(BatteryUsageStatsAtomsProto.SESSION_DISCHARGE_PERCENTAGE,
sessionDischargePercentage);
return proto.getBytes();
}
/**
* Writes the UidBatteryConsumers data, held by this BatteryUsageStats, to the proto (as used
* for atoms.proto).
*/
private void writeUidBatteryConsumersProto(ProtoOutputStream proto) {
final List<UidBatteryConsumer> consumers = getUidBatteryConsumers();
// TODO: Sort the list by power consumption. If during the for, proto.getRawSize() > 45kb,
// truncate the remainder of the list.
final int size = consumers.size();
for (int i = 0; i < size; i++) {
final UidBatteryConsumer consumer = consumers.get(i);
final long fgMs = consumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND);
final long bgMs = consumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND);
final boolean hasBaseData = consumer.hasStatsProtoData();
if (fgMs == 0 && bgMs == 0 && !hasBaseData) {
continue;
}
final long token = proto.start(BatteryUsageStatsAtomsProto.UID_BATTERY_CONSUMERS);
proto.write(
BatteryUsageStatsAtomsProto.UidBatteryConsumer.UID,
consumer.getUid());
if (hasBaseData) {
consumer.writeStatsProto(proto,
BatteryUsageStatsAtomsProto.UidBatteryConsumer.BATTERY_CONSUMER_DATA);
}
proto.write(
BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_FOREGROUND_MILLIS,
fgMs);
proto.write(
BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_BACKGROUND_MILLIS,
bgMs);
proto.end(token);
}
}
/**
* Prints the stats in a human-readable format.
*/

View File

@@ -15,7 +15,11 @@
*/
package android.os;
import static android.os.BatteryConsumer.convertMahToDeciCoulombs;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.PowerCalculator;
@@ -237,6 +241,59 @@ class PowerComponents {
}
}
/** Returns whether there are any atoms.proto POWER_COMPONENTS data to write to a proto. */
boolean hasStatsProtoData() {
return writeStatsProtoImpl(null);
}
/** Writes all atoms.proto POWER_COMPONENTS for this PowerComponents to the given proto. */
void writeStatsProto(@NonNull ProtoOutputStream proto) {
writeStatsProtoImpl(proto);
}
/**
* Returns whether there are any atoms.proto POWER_COMPONENTS data to write to a proto,
* and writes it to the given proto if it is non-null.
*/
private boolean writeStatsProtoImpl(@Nullable ProtoOutputStream proto) {
boolean interestingData = false;
for (int idx = 0; idx < mPowerComponentsMah.length; idx++) {
final int componentId = idx < BatteryConsumer.POWER_COMPONENT_COUNT ?
idx : idx - CUSTOM_POWER_COMPONENT_OFFSET;
final long powerDeciCoulombs = convertMahToDeciCoulombs(mPowerComponentsMah[idx]);
final long durationMs = mUsageDurationsMs[idx];
if (powerDeciCoulombs == 0 && durationMs == 0) {
// No interesting data. Make sure not to even write the COMPONENT int.
continue;
}
interestingData = true;
if (proto == null) {
// We're just asked whether there is data, not to actually write it. And there is.
return true;
}
final long token =
proto.start(BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS);
proto.write(
BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage
.COMPONENT,
componentId);
proto.write(
BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage
.POWER_DECI_COULOMBS,
powerDeciCoulombs);
proto.write(
BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage
.DURATION_MILLIS,
durationMs);
proto.end(token);
}
return interestingData;
}
/**
* Builder for PowerComponents.
*/

View File

@@ -0,0 +1,81 @@
/*
* 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.
*/
syntax = "proto2";
package android.os;
option java_multiple_files = true;
import "frameworks/proto_logging/stats/enums/os/enums.proto";
// This message is used for statsd logging and should be kept in sync with
// frameworks/proto_logging/stats/atoms.proto
/**
* Represents a device's BatteryUsageStats, with power usage information about the device
* and each app.
*/
message BatteryUsageStatsAtomsProto {
// The session start timestamp in UTC milliseconds since January 1, 1970, per Date#getTime().
// All data is no older than this time.
optional int64 session_start_millis = 1;
// The session end timestamp in UTC milliseconds since January 1, 1970, per Date#getTime().
// All data is no more recent than this time.
optional int64 session_end_millis = 2;
// Length that the reported data covered. This usually will be equal to the entire session,
// session_end_millis - session_start_millis, but may not be if some data during this time frame
// is missing.
optional int64 session_duration_millis = 3;
// Represents usage of a consumer, storing all of its power component usage.
message BatteryConsumerData {
// Total power consumed by this BatteryConsumer (including all of its PowerComponents).
// May not equal the sum of the PowerComponentUsage due to under- or over-estimations.
// Multiply by 1/36 to obtain mAh.
optional int64 total_consumed_power_deci_coulombs = 1;
// Represents power and time usage of a particular power component.
message PowerComponentUsage {
// Holds android.os.PowerComponentEnum, or custom component value between 1000 and 9999.
// Evidently, if one attempts to write an int to an enum field that is out of range, it
// is treated as 0, so we must make this an int32.
optional int32 component = 1;
// Power consumed by this component. Multiply by 1/36 to obtain mAh.
optional int64 power_deci_coulombs = 2;
optional int64 duration_millis = 3;
}
repeated PowerComponentUsage power_components = 2;
}
// Total power usage for the device during this session.
optional BatteryConsumerData device_battery_consumer = 4;
// Power usage by a uid during this session.
message UidBatteryConsumer {
optional int32 uid = 1;
optional BatteryConsumerData battery_consumer_data = 2;
optional int64 time_in_foreground_millis = 3;
optional int64 time_in_background_millis = 4;
}
repeated UidBatteryConsumer uid_battery_consumers = 5;
// Sum of all discharge percentage point drops during the reported session.
optional int32 session_discharge_percentage = 6;
}

View File

@@ -0,0 +1,28 @@
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
}
android_test {
name: "BatteryUsageStatsProtoTests",
srcs: ["src/**/*.java"],
static_libs: [
"androidx.test.rules",
"junit",
"platform-test-annotations",
"platformprotosnano",
"statsdprotolite",
],
libs: ["android.test.runner"],
platform_apis: true,
certificate: "platform",
test_suites: ["device-tests"],
}

View File

@@ -0,0 +1,28 @@
<?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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.core.batteryusagestatsprototests">
<uses-permission android:name="android.permission.BATTERY_STATS"/>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.core.batteryusagestatsprototests"
android:label="BatteryUsageStats Proto Tests" />
</manifest>

View File

@@ -0,0 +1,229 @@
/*
* 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.
*/
package com.android.internal.os;
import static android.os.BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import android.os.BatteryConsumer;
import android.os.BatteryUsageStats;
import android.os.nano.BatteryUsageStatsAtomsProto;
import android.os.nano.BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage;
import androidx.test.filters.SmallTest;
import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
@SmallTest
public class BatteryUsageStatsPulledTest {
private static final int UID_0 = 1000;
private static final int UID_1 = 2000;
private static final int UID_2 = 3000;
private static final int UID_3 = 4000;
@Test
public void testGetStatsProto() {
final long sessionEndTimestampMs = 1050;
final BatteryUsageStats bus = buildBatteryUsageStats();
final byte[] bytes = bus.getStatsProto(sessionEndTimestampMs);
BatteryUsageStatsAtomsProto proto;
try {
proto = BatteryUsageStatsAtomsProto.parseFrom(bytes);
} catch (InvalidProtocolBufferNanoException e) {
fail("Invalid proto: " + e);
return;
}
assertEquals(bus.getStatsStartTimestamp(), proto.sessionStartMillis);
assertEquals(sessionEndTimestampMs, proto.sessionEndMillis);
assertEquals(
sessionEndTimestampMs - bus.getStatsStartTimestamp(),
proto.sessionDurationMillis);
assertEquals(bus.getDischargePercentage(), proto.sessionDischargePercentage);
assertEquals(3, proto.deviceBatteryConsumer.powerComponents.length); // Only 3 are non-empty
assertSameBatteryConsumer("For deviceBatteryConsumer",
bus.getAggregateBatteryConsumer(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE),
proto.deviceBatteryConsumer);
// Now for the UidBatteryConsumers.
final List<android.os.UidBatteryConsumer> uidConsumers = bus.getUidBatteryConsumers();
uidConsumers.sort((a, b) -> a.getUid() - b.getUid());
final BatteryUsageStatsAtomsProto.UidBatteryConsumer[] uidConsumersProto
= proto.uidBatteryConsumers;
Arrays.sort(uidConsumersProto, (a, b) -> a.uid - b.uid);
// UID_0 - After sorting, UID_0 should be in position 0 for both data structures
assertEquals(UID_0, bus.getUidBatteryConsumers().get(0).getUid());
assertEquals(UID_0, proto.uidBatteryConsumers[0].uid);
assertSameUidBatteryConsumer(
bus.getUidBatteryConsumers().get(0),
proto.uidBatteryConsumers[0],
false);
// UID_1 - After sorting, UID_1 should be in position 1 for both data structures
assertEquals(UID_1, bus.getUidBatteryConsumers().get(1).getUid());
assertEquals(UID_1, proto.uidBatteryConsumers[1].uid);
assertSameUidBatteryConsumer(
bus.getUidBatteryConsumers().get(1),
proto.uidBatteryConsumers[1],
true);
// UID_2 - After sorting, UID_2 should be in position 2 for both data structures
assertEquals(UID_2, bus.getUidBatteryConsumers().get(2).getUid());
assertEquals(UID_2, proto.uidBatteryConsumers[2].uid);
assertSameUidBatteryConsumer(
bus.getUidBatteryConsumers().get(2),
proto.uidBatteryConsumers[2],
false);
// UID_3 - Should be none, since no interesting data (done last for debugging convenience).
assertEquals(3, proto.uidBatteryConsumers.length);
}
private void assertSameBatteryConsumer(String message, BatteryConsumer consumer,
android.os.nano.BatteryUsageStatsAtomsProto.BatteryConsumerData consumerProto) {
assertNotNull(message, consumerProto);
assertEquals(
convertMahToDc(consumer.getConsumedPower()),
consumerProto.totalConsumedPowerDeciCoulombs);
for (PowerComponentUsage componentProto : consumerProto.powerComponents) {
final int componentId = componentProto.component;
if (componentId < BatteryConsumer.POWER_COMPONENT_COUNT) {
assertEquals(message + " for component " + componentId,
convertMahToDc(consumer.getConsumedPower(componentId)),
componentProto.powerDeciCoulombs);
assertEquals(message + " for component " + componentId,
consumer.getUsageDurationMillis(componentId),
componentProto.durationMillis);
} else {
assertEquals(message + " for custom component " + componentId,
convertMahToDc(consumer.getConsumedPowerForCustomComponent(componentId)),
componentProto.powerDeciCoulombs);
assertEquals(message + " for custom component " + componentId,
consumer.getUsageDurationForCustomComponentMillis(componentId),
componentProto.durationMillis);
}
}
}
private void assertSameUidBatteryConsumer(
android.os.UidBatteryConsumer uidConsumer,
BatteryUsageStatsAtomsProto.UidBatteryConsumer uidConsumerProto,
boolean expectNullBatteryConsumerData) {
final int uid = uidConsumerProto.uid;
assertEquals("Uid consumers had mismatched uids", uid, uidConsumer.getUid());
assertEquals("For uid " + uid,
uidConsumer.getTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND),
uidConsumerProto.timeInForegroundMillis);
assertEquals("For uid " + uid,
uidConsumer.getTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND),
uidConsumerProto.timeInBackgroundMillis);
if (expectNullBatteryConsumerData) {
assertNull("For uid " + uid, uidConsumerProto.batteryConsumerData);
} else {
assertSameBatteryConsumer("For uid " + uid,
uidConsumer,
uidConsumerProto.batteryConsumerData);
}
}
/** Converts charge from milliamp hours (mAh) to decicoulombs (dC). */
private long convertMahToDc(double powerMah) {
return (long) (powerMah * 36 + 0.5);
}
private BatteryUsageStats buildBatteryUsageStats() {
final BatteryStatsImpl batteryStats = new BatteryStatsImpl();
final BatteryStatsImpl.Uid batteryStatsUid0 = batteryStats.getUidStatsLocked(UID_0);
final BatteryStatsImpl.Uid batteryStatsUid1 = batteryStats.getUidStatsLocked(UID_1);
final BatteryStatsImpl.Uid batteryStatsUid2 = batteryStats.getUidStatsLocked(UID_2);
final BatteryStatsImpl.Uid batteryStatsUid3 = batteryStats.getUidStatsLocked(UID_3);
final BatteryUsageStats.Builder builder =
new BatteryUsageStats.Builder(new String[]{"CustomConsumer1", "CustomConsumer2"})
.setDischargePercentage(20)
.setDischargedPowerRange(1000, 2000)
.setStatsStartTimestamp(1000);
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid0)
.setPackageWithHighestDrain("myPackage0")
.setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 1000)
.setTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND, 2000)
.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, 400)
.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 450)
.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1, 500)
.setUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, 600)
.setUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1, 800);
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid1)
.setPackageWithHighestDrain("myPackage1")
.setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 1234);
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid2)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
766);
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid3);
builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.setConsumedPower(30000)
.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, 20100)
.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_AUDIO, 0) // Empty
.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_CAMERA, 20150)
.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200)
.setUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, 20300)
.setUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20400);
// Not used; just to make sure extraneous data doesn't mess things up.
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, 10100)
.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
return builder.build();
}
}

View File

@@ -20,6 +20,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
import android.annotation.NonNull;
import android.app.StatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.content.ContentResolver;
import android.content.Context;
@@ -69,9 +70,11 @@ import android.telephony.ModemActivityInfo;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.util.Slog;
import android.util.StatsEvent;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.BatteryUsageStatsProvider;
@@ -380,6 +383,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
final DataConnectionStats dataConnectionStats = new DataConnectionStats(mContext, mHandler);
dataConnectionStats.startMonitoring();
registerStatsCallbacks();
}
private final class LocalService extends BatteryStatsInternal {
@@ -672,7 +677,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
* and per-UID basis.
*/
public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
mContext.enforceCallingPermission(
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BATTERY_STATS, null);
awaitCompletion();
@@ -734,6 +739,48 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
}
/** Register callbacks for statsd pulled atoms. */
private void registerStatsCallbacks() {
final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
final StatsPullAtomCallbackImpl pullAtomCallback = new StatsPullAtomCallbackImpl();
statsManager.setPullAtomCallback(
FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET,
null, // use default PullAtomMetadata values
BackgroundThread.getExecutor(), pullAtomCallback);
statsManager.setPullAtomCallback(
FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL,
null, // use default PullAtomMetadata values
BackgroundThread.getExecutor(), pullAtomCallback);
}
/** StatsPullAtomCallback for pulling BatteryUsageStats data. */
private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
@Override
public int onPullAtom(int atomTag, List<StatsEvent> data) {
final BatteryUsageStats bus;
switch (atomTag) {
case FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET:
bus = getBatteryUsageStats(List.of(BatteryUsageStatsQuery.DEFAULT)).get(0);
break;
case FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL:
final BatteryUsageStatsQuery powerProfileQuery =
new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build();
bus = getBatteryUsageStats(List.of(powerProfileQuery)).get(0);
break;
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
// TODO(b/187223764): busTime won't be needed once end_session is a field in BUS.
final long busTime = System.currentTimeMillis();
final byte[] statsProto = bus.getStatsProto(busTime);
data.add(FrameworkStatsLog.buildStatsEvent(atomTag, statsProto));
return StatsManager.PULL_SUCCESS;
}
}
public boolean isCharging() {
synchronized (mStats) {
return mStats.isCharging();