blob: 6faa7e7c89e75f4e6079aeaf869284907ea002c9 [file] [log] [blame]
/*
* 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.server.am;
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL;
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_BT;
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU;
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY;
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO;
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import android.content.Context;
import android.hardware.power.stats.Channel;
import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
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;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.PowerProfile;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
/**
* Tests for {@link BatteryExternalStatsWorker}.
*
* Build/Install/Run:
* atest FrameworksServicesTests:BatteryExternalStatsWorkerTest
*/
public class BatteryExternalStatsWorkerTest {
private BatteryExternalStatsWorker mBatteryExternalStatsWorker;
private TestBatteryStatsImpl mBatteryStatsImpl;
private TestPowerStatsInternal mPowerStatsInternal;
@Before
public void setUp() {
final Context context = InstrumentationRegistry.getContext();
mBatteryStatsImpl = new TestBatteryStatsImpl(context);
mPowerStatsInternal = new TestPowerStatsInternal();
mBatteryExternalStatsWorker = new BatteryExternalStatsWorker(new TestInjector(context),
mBatteryStatsImpl);
}
@Test
public void testTargetedEnergyConsumerQuerying() {
final int numCpuClusters = 4;
final int numDisplays = 5;
final int numOther = 3;
// Add some energy consumers used by BatteryExternalStatsWorker.
final IntArray tempAllIds = new IntArray();
final int[] displayIds = new int[numDisplays];
for (int i = 0; i < numDisplays; i++) {
displayIds[i] = mPowerStatsInternal.addEnergyConsumer(
EnergyConsumerType.DISPLAY, i, "display" + i);
tempAllIds.add(displayIds[i]);
mPowerStatsInternal.incrementEnergyConsumption(displayIds[i], 12345 + i);
}
Arrays.sort(displayIds);
final int wifiId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.WIFI, 0,
"wifi");
tempAllIds.add(wifiId);
mPowerStatsInternal.incrementEnergyConsumption(wifiId, 23456);
final int btId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.BLUETOOTH, 0,
"bt");
tempAllIds.add(btId);
mPowerStatsInternal.incrementEnergyConsumption(btId, 34567);
final int gnssId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.GNSS, 0,
"gnss");
tempAllIds.add(gnssId);
mPowerStatsInternal.incrementEnergyConsumption(gnssId, 787878);
final int mobileRadioId = mPowerStatsInternal.addEnergyConsumer(
EnergyConsumerType.MOBILE_RADIO, 0, "mobile_radio");
tempAllIds.add(mobileRadioId);
mPowerStatsInternal.incrementEnergyConsumption(mobileRadioId, 62626);
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 cpu cluster energy consumers
final int[] receivedDisplayIds = new int[displayResults.length];
for (int i = 0; i < displayResults.length; i++) {
receivedDisplayIds[i] = displayResults[i].id;
}
Arrays.sort(receivedDisplayIds);
assertArrayEquals(displayIds, receivedDisplayIds);
final EnergyConsumerResult[] wifiResults =
mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_WIFI).getNow(null);
// Results should only have the wifi energy consumer
assertEquals(1, wifiResults.length);
assertEquals(wifiId, wifiResults[0].id);
final EnergyConsumerResult[] bluetoothResults =
mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_BT).getNow(null);
// Results should only have the bluetooth energy consumer
assertEquals(1, bluetoothResults.length);
assertEquals(btId, bluetoothResults[0].id);
final EnergyConsumerResult[] mobileRadioResults =
mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_RADIO).getNow(null);
// Results should only have the mobile radio energy consumer
assertEquals(1, mobileRadioResults.length);
assertEquals(mobileRadioId, mobileRadioResults[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);
}
public <T> T getSystemService(Class<T> serviceClass) {
return null;
}
public <T> T getLocalService(Class<T> serviceClass) {
if (serviceClass == PowerStatsInternal.class) {
return (T) mPowerStatsInternal;
}
return null;
}
}
public class TestBatteryStatsImpl extends BatteryStatsImpl {
public TestBatteryStatsImpl(Context context) {
mPowerProfile = new PowerProfile(context, true /* forTest */);
initTimersAndCounters();
}
}
public class TestPowerStatsInternal extends PowerStatsInternal {
private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray();
private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults = new SparseArray();
private final int mTimeSinceBoot = 0;
@Override
public EnergyConsumer[] getEnergyConsumerInfo() {
final int size = mEnergyConsumers.size();
final EnergyConsumer[] consumers = new EnergyConsumer[size];
for (int i = 0; i < size; i++) {
consumers[i] = mEnergyConsumers.valueAt(i);
}
return consumers;
}
@Override
public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
int[] energyConsumerIds) {
final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture();
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;
}
@Override
public PowerEntity[] getPowerEntityInfo() {
return new PowerEntity[0];
}
@Override
public CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
int[] powerEntityIds) {
return new CompletableFuture<>();
}
@Override
public Channel[] getEnergyMeterInfo() {
return new Channel[0];
}
@Override
public CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
int[] channelIds) {
return new CompletableFuture<>();
}
/**
* Util method to add a new EnergyConsumer for testing
*
* @return the EnergyConsumer id of the new EnergyConsumer
*/
public int addEnergyConsumer(@EnergyConsumerType byte type, int ordinal, String name) {
final EnergyConsumer consumer = new EnergyConsumer();
final int id = getNextAvailableId();
consumer.id = id;
consumer.type = type;
consumer.ordinal = ordinal;
consumer.name = name;
mEnergyConsumers.put(id, consumer);
final EnergyConsumerResult result = new EnergyConsumerResult();
result.id = id;
result.timestampMs = mTimeSinceBoot;
result.energyUWs = 0;
mEnergyConsumerResults.put(id, result);
return id;
}
public void incrementEnergyConsumption(int id, long energyUWs) {
EnergyConsumerResult result = mEnergyConsumerResults.get(id, null);
assertNotNull(result);
result.energyUWs += energyUWs;
}
private int getNextAvailableId() {
final int size = mEnergyConsumers.size();
// Just return the first index that does not match the key (aka the EnergyConsumer id)
for (int i = size - 1; i >= 0; i--) {
if (mEnergyConsumers.keyAt(i) == i) return i + 1;
}
// Otherwise return the lowest id
return 0;
}
}
}