Implement EnergyConsumer type to ids map for targeted querying
The EnergyConsumer type to id map is built at boot, based on the EnergyConsumer info query. Using the map, BatteryExernalStatsWorker can selectively choose which EnergyConsumers to query based on EnergyConsumer type. Also, collect the EnergyConsumerResults asynchronously with a CompletableFuture. Fixes: 180029015 Bug: 180079165 Test: atest FrameworksServicesTests:BatteryExternalStatsWorkerTest Change-Id: I8d87c1da9170897065f866e7e1e14f72e0c73543
This commit is contained in:
@@ -50,8 +50,6 @@ import com.android.server.LocalServices;
|
||||
|
||||
import libcore.util.EmptyArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -149,7 +147,6 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
|
||||
* Maps an {@link EnergyConsumerType} to it's corresponding {@link EnergyConsumer#id}s,
|
||||
* unless it is of {@link EnergyConsumer#type}=={@link EnergyConsumerType#OTHER}
|
||||
*/
|
||||
// TODO(b/180029015): Hook this up (it isn't used yet)
|
||||
@GuardedBy("mWorkerLock")
|
||||
private @Nullable SparseArray<int[]> mEnergyConsumerTypeToIdMap = null;
|
||||
|
||||
@@ -209,11 +206,22 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
|
||||
= populateEnergyConsumerSubsystemMapsLocked();
|
||||
if (idToConsumer != null) {
|
||||
mMeasuredEnergySnapshot = new MeasuredEnergySnapshot(idToConsumer);
|
||||
final EnergyConsumerResult[] initialEcrs = getEnergyConsumptionData();
|
||||
// According to spec, initialEcrs will include 0s for consumers that haven't
|
||||
// used any energy yet, as long as they are supported; however, attributed uid
|
||||
// energies will be absent if their energy is 0.
|
||||
mMeasuredEnergySnapshot.updateAndGetDelta(initialEcrs);
|
||||
try {
|
||||
final EnergyConsumerResult[] initialEcrs = getEnergyConsumptionData().get(
|
||||
EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
|
||||
// According to spec, initialEcrs will include 0s for consumers that haven't
|
||||
// used any energy yet, as long as they are supported; however,
|
||||
// attributed uid energies will be absent if their energy is 0.
|
||||
mMeasuredEnergySnapshot.updateAndGetDelta(initialEcrs);
|
||||
} catch (TimeoutException | InterruptedException e) {
|
||||
Slog.w(TAG, "timeout or interrupt reading initial getEnergyConsumedAsync: "
|
||||
+ e);
|
||||
// Continue running, later attempts to query may be successful.
|
||||
} catch (ExecutionException e) {
|
||||
Slog.wtf(TAG, "exception reading initial getEnergyConsumedAsync: "
|
||||
+ e.getCause());
|
||||
// Continue running, later attempts to query may be successful.
|
||||
}
|
||||
numCustomBuckets = mMeasuredEnergySnapshot.getNumOtherOrdinals();
|
||||
supportedStdBuckets = getSupportedEnergyBuckets(idToConsumer);
|
||||
}
|
||||
@@ -498,6 +506,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
|
||||
CompletableFuture<ModemActivityInfo> modemFuture = CompletableFuture.completedFuture(null);
|
||||
boolean railUpdated = false;
|
||||
|
||||
CompletableFuture<EnergyConsumerResult[]> futureECRs = getMeasuredEnergyLocked(updateFlags);
|
||||
|
||||
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
|
||||
// We were asked to fetch WiFi data.
|
||||
// Only fetch WiFi power data if it is supported.
|
||||
@@ -574,9 +584,23 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
|
||||
Slog.w(TAG, "exception reading modem stats: " + e.getCause());
|
||||
}
|
||||
|
||||
final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDeltas =
|
||||
mMeasuredEnergySnapshot == null ? null :
|
||||
mMeasuredEnergySnapshot.updateAndGetDelta(getMeasuredEnergyLocked(updateFlags));
|
||||
final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDeltas;
|
||||
if (mMeasuredEnergySnapshot == null || futureECRs == null) {
|
||||
measuredEnergyDeltas = null;
|
||||
} else {
|
||||
EnergyConsumerResult[] ecrs;
|
||||
try {
|
||||
ecrs = futureECRs.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException | InterruptedException e) {
|
||||
// TODO (b/180519623): Invalidate the MeasuredEnergy derived data until next reset.
|
||||
Slog.w(TAG, "timeout or interrupt reading getEnergyConsumedAsync: " + e);
|
||||
ecrs = null;
|
||||
} catch (ExecutionException e) {
|
||||
Slog.wtf(TAG, "exception reading getEnergyConsumedAsync: " + e.getCause());
|
||||
ecrs = null;
|
||||
}
|
||||
measuredEnergyDeltas = mMeasuredEnergySnapshot.updateAndGetDelta(ecrs);
|
||||
}
|
||||
|
||||
final long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||
final long uptime = SystemClock.uptimeMillis();
|
||||
@@ -786,22 +810,29 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
|
||||
return buckets;
|
||||
}
|
||||
|
||||
/** Get {@link EnergyConsumerResult}s with the latest energy usage since boot. */
|
||||
/** Get all {@link EnergyConsumerResult}s with the latest energy usage since boot. */
|
||||
@GuardedBy("mWorkerLock")
|
||||
private @Nullable EnergyConsumerResult[] getEnergyConsumptionData() {
|
||||
try {
|
||||
return mPowerStatsInternal.getEnergyConsumedAsync(new int[0])
|
||||
.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Failed to getEnergyConsumedAsync", e);
|
||||
return null;
|
||||
}
|
||||
@Nullable
|
||||
private CompletableFuture<EnergyConsumerResult[]> getEnergyConsumptionData() {
|
||||
return getEnergyConsumptionData(new int[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get {@link EnergyConsumerResult}s of the specified {@link EnergyConsumer} ids with the latest
|
||||
* energy usage since boot.
|
||||
*/
|
||||
@GuardedBy("mWorkerLock")
|
||||
@Nullable
|
||||
private CompletableFuture<EnergyConsumerResult[]> getEnergyConsumptionData(int[] consumerIds) {
|
||||
return mPowerStatsInternal.getEnergyConsumedAsync(consumerIds);
|
||||
}
|
||||
|
||||
/** Fetch EnergyConsumerResult[] for supported subsystems based on the given updateFlags. */
|
||||
@VisibleForTesting
|
||||
@GuardedBy("mWorkerLock")
|
||||
private @Nullable EnergyConsumerResult[] getMeasuredEnergyLocked(@ExternalUpdateFlag int flags)
|
||||
{
|
||||
@Nullable
|
||||
public CompletableFuture<EnergyConsumerResult[]> getMeasuredEnergyLocked(
|
||||
@ExternalUpdateFlag int flags) {
|
||||
if (mMeasuredEnergySnapshot == null || mPowerStatsInternal == null) return null;
|
||||
|
||||
if (flags == UPDATE_ALL) {
|
||||
@@ -809,24 +840,27 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
|
||||
return getEnergyConsumptionData();
|
||||
}
|
||||
|
||||
final List<Integer> energyConsumerIds = new ArrayList<>();
|
||||
final IntArray energyConsumerIds = new IntArray();
|
||||
if ((flags & UPDATE_CPU) != 0) {
|
||||
addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.CPU_CLUSTER);
|
||||
}
|
||||
if ((flags & UPDATE_DISPLAY) != 0) {
|
||||
addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.DISPLAY);
|
||||
}
|
||||
// TODO: Wifi, Bluetooth, etc., go here
|
||||
|
||||
if (energyConsumerIds.isEmpty()) {
|
||||
if (energyConsumerIds.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
// TODO(b/180029015): Query specific subsystems from HAL based on energyConsumerIds.toArray
|
||||
return getEnergyConsumptionData();
|
||||
return getEnergyConsumptionData(energyConsumerIds.toArray());
|
||||
}
|
||||
|
||||
@GuardedBy("mWorkerLock")
|
||||
private void addEnergyConsumerIdLocked(
|
||||
List<Integer> energyConsumerIds, @EnergyConsumerType int type) {
|
||||
final int consumerId = 0; // TODO(b/180029015): Use mEnergyConsumerTypeToIdMap to get this
|
||||
energyConsumerIds.add(consumerId);
|
||||
IntArray energyConsumerIds, @EnergyConsumerType int type) {
|
||||
final int[] consumerIds = mEnergyConsumerTypeToIdMap.get(type);
|
||||
if (consumerIds == null) return;
|
||||
energyConsumerIds.addAll(consumerIds);
|
||||
}
|
||||
|
||||
/** Populates the cached type->ids map, and returns the (inverse) id->EnergyConsumer map. */
|
||||
@@ -840,12 +874,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO(b/180029015): Initialize typeToIds
|
||||
// Maps type -> {ids} (1:n map, since multiple ids might have the same type)
|
||||
// final SparseArray<SparseIntArray> typeToIds = new SparseArray<>();
|
||||
|
||||
// Maps id -> EnergyConsumer (1:1 map)
|
||||
final SparseArray<EnergyConsumer> idToConsumer = new SparseArray<>(energyConsumers.length);
|
||||
// Maps type -> {ids} (1:n map, since multiple ids might have the same type)
|
||||
final SparseArray<IntArray> tempTypeToId = new SparseArray<>();
|
||||
|
||||
// Add all expected EnergyConsumers to the maps
|
||||
for (final EnergyConsumer consumer : energyConsumers) {
|
||||
@@ -862,9 +894,23 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
|
||||
}
|
||||
}
|
||||
idToConsumer.put(consumer.id, consumer);
|
||||
// TODO(b/180029015): Also populate typeToIds map
|
||||
|
||||
IntArray ids = tempTypeToId.get(consumer.type);
|
||||
if (ids == null) {
|
||||
ids = new IntArray();
|
||||
tempTypeToId.put(consumer.type, ids);
|
||||
}
|
||||
ids.add(consumer.id);
|
||||
}
|
||||
|
||||
mEnergyConsumerTypeToIdMap = new SparseArray<>(tempTypeToId.size());
|
||||
// Populate mEnergyConsumerTypeToIdMap with EnergyConsumer type to ids mappings
|
||||
final int size = tempTypeToId.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
final int consumerType = tempTypeToId.keyAt(i);
|
||||
final int[] consumerIds = tempTypeToId.valueAt(i).toArray();
|
||||
mEnergyConsumerTypeToIdMap.put(consumerType, consumerIds);
|
||||
}
|
||||
// TODO(b/180029015): Store typeToIds in mEnergyConsumerTypeToIdMap.
|
||||
return idToConsumer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
|
||||
package com.android.server.am;
|
||||
|
||||
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL;
|
||||
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU;
|
||||
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -27,6 +33,7 @@ import android.hardware.power.stats.EnergyMeasurement;
|
||||
import android.hardware.power.stats.PowerEntity;
|
||||
import android.hardware.power.stats.StateResidencyResult;
|
||||
import android.power.PowerStatsInternal;
|
||||
import android.util.IntArray;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
@@ -34,7 +41,9 @@ import androidx.test.InstrumentationRegistry;
|
||||
import com.android.internal.os.BatteryStatsImpl;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
@@ -58,6 +67,69 @@ public class BatteryExternalStatsWorkerTest {
|
||||
mBatteryStatsImpl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTargetedEnergyConsumerQuerying() {
|
||||
final int numCpuClusters = 4;
|
||||
final int numOther = 3;
|
||||
|
||||
final IntArray tempAllIds = new IntArray();
|
||||
// Add some energy consumers used by BatteryExternalStatsWorker.
|
||||
final int displayId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.DISPLAY, 0,
|
||||
"display");
|
||||
tempAllIds.add(displayId);
|
||||
mPowerStatsInternal.incrementEnergyConsumption(displayId, 12345);
|
||||
|
||||
final int[] cpuClusterIds = new int[numCpuClusters];
|
||||
for (int i = 0; i < numCpuClusters; i++) {
|
||||
cpuClusterIds[i] = mPowerStatsInternal.addEnergyConsumer(
|
||||
EnergyConsumerType.CPU_CLUSTER, i, "cpu_cluster" + i);
|
||||
tempAllIds.add(cpuClusterIds[i]);
|
||||
mPowerStatsInternal.incrementEnergyConsumption(cpuClusterIds[i], 1111 + i);
|
||||
}
|
||||
Arrays.sort(cpuClusterIds);
|
||||
|
||||
final int[] otherIds = new int[numOther];
|
||||
for (int i = 0; i < numOther; i++) {
|
||||
otherIds[i] = mPowerStatsInternal.addEnergyConsumer(
|
||||
EnergyConsumerType.OTHER, i, "other" + i);
|
||||
tempAllIds.add(otherIds[i]);
|
||||
mPowerStatsInternal.incrementEnergyConsumption(otherIds[i], 3000 + i);
|
||||
}
|
||||
Arrays.sort(otherIds);
|
||||
|
||||
final int[] allIds = tempAllIds.toArray();
|
||||
Arrays.sort(allIds);
|
||||
|
||||
// Inform BESW that PowerStatsInternal is ready to query
|
||||
mBatteryExternalStatsWorker.systemServicesReady();
|
||||
|
||||
final EnergyConsumerResult[] displayResults =
|
||||
mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_DISPLAY).getNow(null);
|
||||
// Results should only have the display energy consumer
|
||||
assertEquals(1, displayResults.length);
|
||||
assertEquals(displayId, displayResults[0].id);
|
||||
|
||||
final EnergyConsumerResult[] cpuResults =
|
||||
mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_CPU).getNow(null);
|
||||
// Results should only have the cpu cluster energy consumers
|
||||
final int[] receivedCpuIds = new int[cpuResults.length];
|
||||
for (int i = 0; i < cpuResults.length; i++) {
|
||||
receivedCpuIds[i] = cpuResults[i].id;
|
||||
}
|
||||
Arrays.sort(receivedCpuIds);
|
||||
assertArrayEquals(cpuClusterIds, receivedCpuIds);
|
||||
|
||||
final EnergyConsumerResult[] allResults =
|
||||
mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_ALL).getNow(null);
|
||||
// All energy consumer results should be available
|
||||
final int[] receivedAllIds = new int[allResults.length];
|
||||
for (int i = 0; i < allResults.length; i++) {
|
||||
receivedAllIds[i] = allResults[i].id;
|
||||
}
|
||||
Arrays.sort(receivedAllIds);
|
||||
assertArrayEquals(allIds, receivedAllIds);
|
||||
}
|
||||
|
||||
public class TestInjector extends BatteryExternalStatsWorker.Injector {
|
||||
public TestInjector(Context context) {
|
||||
super(context);
|
||||
@@ -79,9 +151,8 @@ public class BatteryExternalStatsWorkerTest {
|
||||
}
|
||||
|
||||
public class TestPowerStatsInternal extends PowerStatsInternal {
|
||||
private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray<>();
|
||||
private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults =
|
||||
new SparseArray<>();
|
||||
private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray();
|
||||
private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults = new SparseArray();
|
||||
private final int mTimeSinceBoot = 0;
|
||||
|
||||
@Override
|
||||
@@ -98,10 +169,19 @@ public class BatteryExternalStatsWorkerTest {
|
||||
public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
|
||||
int[] energyConsumerIds) {
|
||||
final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture();
|
||||
final int size = mEnergyConsumerResults.size();
|
||||
final EnergyConsumerResult[] results = new EnergyConsumerResult[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
results[i] = mEnergyConsumerResults.valueAt(i);
|
||||
final EnergyConsumerResult[] results;
|
||||
final int length = energyConsumerIds.length;
|
||||
if (length == 0) {
|
||||
final int size = mEnergyConsumerResults.size();
|
||||
results = new EnergyConsumerResult[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
results[i] = mEnergyConsumerResults.valueAt(i);
|
||||
}
|
||||
} else {
|
||||
results = new EnergyConsumerResult[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
results[i] = mEnergyConsumerResults.get(energyConsumerIds[i]);
|
||||
}
|
||||
}
|
||||
future.complete(results);
|
||||
return future;
|
||||
|
||||
Reference in New Issue
Block a user