1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.power.stats; 18 19 import static com.android.server.power.stats.BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL; 20 import static com.android.server.power.stats.BatteryStatsImpl.ExternalStatsSync.UPDATE_BT; 21 import static com.android.server.power.stats.BatteryStatsImpl.ExternalStatsSync.UPDATE_CAMERA; 22 import static com.android.server.power.stats.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU; 23 import static com.android.server.power.stats.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY; 24 import static com.android.server.power.stats.BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO; 25 import static com.android.server.power.stats.BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI; 26 27 import static org.junit.Assert.assertArrayEquals; 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.assertNotNull; 30 31 import android.content.Context; 32 import android.hardware.power.stats.Channel; 33 import android.hardware.power.stats.EnergyConsumer; 34 import android.hardware.power.stats.EnergyConsumerResult; 35 import android.hardware.power.stats.EnergyConsumerType; 36 import android.hardware.power.stats.EnergyMeasurement; 37 import android.hardware.power.stats.PowerEntity; 38 import android.hardware.power.stats.StateResidencyResult; 39 import android.os.Handler; 40 import android.os.Looper; 41 import android.platform.test.ravenwood.RavenwoodRule; 42 import android.power.PowerStatsInternal; 43 import android.util.IntArray; 44 import android.util.SparseArray; 45 46 import androidx.test.InstrumentationRegistry; 47 48 import com.android.internal.os.Clock; 49 import com.android.internal.os.CpuScalingPolicies; 50 import com.android.internal.os.MonotonicClock; 51 import com.android.internal.os.PowerProfile; 52 53 import org.junit.Before; 54 import org.junit.Rule; 55 import org.junit.Test; 56 57 import java.util.Arrays; 58 import java.util.concurrent.CompletableFuture; 59 60 /** 61 * Tests for {@link BatteryExternalStatsWorker}. 62 * 63 * Build/Install/Run: 64 * atest FrameworksServicesTests:BatteryExternalStatsWorkerTest 65 */ 66 @SuppressWarnings("GuardedBy") 67 @android.platform.test.annotations.DisabledOnRavenwood 68 public class BatteryExternalStatsWorkerTest { 69 @Rule 70 public final RavenwoodRule mRavenwood = new RavenwoodRule(); 71 72 private BatteryExternalStatsWorker mBatteryExternalStatsWorker; 73 private TestPowerStatsInternal mPowerStatsInternal; 74 75 @Before setUp()76 public void setUp() { 77 final Context context = InstrumentationRegistry.getContext(); 78 79 BatteryStatsImpl batteryStats = new BatteryStatsImpl( 80 new BatteryStatsImpl.BatteryStatsConfig.Builder().build(), Clock.SYSTEM_CLOCK, 81 new MonotonicClock(0, Clock.SYSTEM_CLOCK), null, 82 new Handler(Looper.getMainLooper()), null, null, null, 83 new PowerProfile(context, true /* forTest */), buildScalingPolicies(), 84 new PowerStatsUidResolver()); 85 mPowerStatsInternal = new TestPowerStatsInternal(); 86 mBatteryExternalStatsWorker = 87 new BatteryExternalStatsWorker(new TestInjector(context), batteryStats); 88 } 89 90 @Test testTargetedEnergyConsumerQuerying()91 public void testTargetedEnergyConsumerQuerying() { 92 final int numCpuClusters = 4; 93 final int numDisplays = 5; 94 final int numOther = 3; 95 96 // Add some energy consumers used by BatteryExternalStatsWorker. 97 final IntArray tempAllIds = new IntArray(); 98 99 final int[] displayIds = new int[numDisplays]; 100 for (int i = 0; i < numDisplays; i++) { 101 displayIds[i] = mPowerStatsInternal.addEnergyConsumer( 102 EnergyConsumerType.DISPLAY, i, "display" + i); 103 tempAllIds.add(displayIds[i]); 104 mPowerStatsInternal.incrementEnergyConsumption(displayIds[i], 12345 + i); 105 } 106 Arrays.sort(displayIds); 107 108 final int wifiId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.WIFI, 0, 109 "wifi"); 110 tempAllIds.add(wifiId); 111 mPowerStatsInternal.incrementEnergyConsumption(wifiId, 23456); 112 113 final int btId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.BLUETOOTH, 0, 114 "bt"); 115 tempAllIds.add(btId); 116 mPowerStatsInternal.incrementEnergyConsumption(btId, 34567); 117 118 final int gnssId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.GNSS, 0, 119 "gnss"); 120 tempAllIds.add(gnssId); 121 mPowerStatsInternal.incrementEnergyConsumption(gnssId, 787878); 122 123 final int cameraId = 124 mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.CAMERA, 0, "camera"); 125 tempAllIds.add(cameraId); 126 mPowerStatsInternal.incrementEnergyConsumption(cameraId, 901234); 127 128 final int mobileRadioId = mPowerStatsInternal.addEnergyConsumer( 129 EnergyConsumerType.MOBILE_RADIO, 0, "mobile_radio"); 130 tempAllIds.add(mobileRadioId); 131 mPowerStatsInternal.incrementEnergyConsumption(mobileRadioId, 62626); 132 133 final int[] cpuClusterIds = new int[numCpuClusters]; 134 for (int i = 0; i < numCpuClusters; i++) { 135 cpuClusterIds[i] = mPowerStatsInternal.addEnergyConsumer( 136 EnergyConsumerType.CPU_CLUSTER, i, "cpu_cluster" + i); 137 tempAllIds.add(cpuClusterIds[i]); 138 mPowerStatsInternal.incrementEnergyConsumption(cpuClusterIds[i], 1111 + i); 139 } 140 Arrays.sort(cpuClusterIds); 141 142 final int[] otherIds = new int[numOther]; 143 for (int i = 0; i < numOther; i++) { 144 otherIds[i] = mPowerStatsInternal.addEnergyConsumer( 145 EnergyConsumerType.OTHER, i, "other" + i); 146 tempAllIds.add(otherIds[i]); 147 mPowerStatsInternal.incrementEnergyConsumption(otherIds[i], 3000 + i); 148 } 149 Arrays.sort(otherIds); 150 151 final int[] allIds = tempAllIds.toArray(); 152 Arrays.sort(allIds); 153 154 // Inform BESW that PowerStatsInternal is ready to query 155 mBatteryExternalStatsWorker.systemServicesReady(); 156 157 final EnergyConsumerResult[] displayResults = 158 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_DISPLAY).getNow(null); 159 // Results should only have the cpu cluster energy consumers 160 final int[] receivedDisplayIds = new int[displayResults.length]; 161 for (int i = 0; i < displayResults.length; i++) { 162 receivedDisplayIds[i] = displayResults[i].id; 163 } 164 Arrays.sort(receivedDisplayIds); 165 assertArrayEquals(displayIds, receivedDisplayIds); 166 167 final EnergyConsumerResult[] wifiResults = 168 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_WIFI).getNow(null); 169 // Results should only have the wifi energy consumer 170 assertEquals(1, wifiResults.length); 171 assertEquals(wifiId, wifiResults[0].id); 172 173 final EnergyConsumerResult[] bluetoothResults = 174 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_BT).getNow(null); 175 // Results should only have the bluetooth energy consumer 176 assertEquals(1, bluetoothResults.length); 177 assertEquals(btId, bluetoothResults[0].id); 178 179 final EnergyConsumerResult[] mobileRadioResults = 180 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_RADIO).getNow(null); 181 // Results should only have the mobile radio energy consumer 182 assertEquals(1, mobileRadioResults.length); 183 assertEquals(mobileRadioId, mobileRadioResults[0].id); 184 185 final EnergyConsumerResult[] cpuResults = 186 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_CPU).getNow(null); 187 // Results should only have the cpu cluster energy consumers 188 final int[] receivedCpuIds = new int[cpuResults.length]; 189 for (int i = 0; i < cpuResults.length; i++) { 190 receivedCpuIds[i] = cpuResults[i].id; 191 } 192 Arrays.sort(receivedCpuIds); 193 assertArrayEquals(cpuClusterIds, receivedCpuIds); 194 195 final EnergyConsumerResult[] cameraResults = 196 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_CAMERA).getNow(null); 197 // Results should only have the camera energy consumer 198 assertEquals(1, cameraResults.length); 199 assertEquals(cameraId, cameraResults[0].id); 200 201 final EnergyConsumerResult[] allResults = 202 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_ALL).getNow(null); 203 // All energy consumer results should be available 204 final int[] receivedAllIds = new int[allResults.length]; 205 for (int i = 0; i < allResults.length; i++) { 206 receivedAllIds[i] = allResults[i].id; 207 } 208 Arrays.sort(receivedAllIds); 209 assertArrayEquals(allIds, receivedAllIds); 210 } 211 212 public class TestInjector extends BatteryExternalStatsWorker.Injector { TestInjector(Context context)213 public TestInjector(Context context) { 214 super(context); 215 } 216 getSystemService(Class<T> serviceClass)217 public <T> T getSystemService(Class<T> serviceClass) { 218 return null; 219 } 220 getLocalService(Class<T> serviceClass)221 public <T> T getLocalService(Class<T> serviceClass) { 222 if (serviceClass == PowerStatsInternal.class) { 223 return (T) mPowerStatsInternal; 224 } 225 return null; 226 } 227 } 228 buildScalingPolicies()229 private static CpuScalingPolicies buildScalingPolicies() { 230 SparseArray<int[]> cpusByPolicy = new SparseArray<>(); 231 cpusByPolicy.put(0, new int[]{0, 1, 2, 3}); 232 cpusByPolicy.put(4, new int[]{4, 5, 6, 7}); 233 SparseArray<int[]> freqsByPolicy = new SparseArray<>(); 234 freqsByPolicy.put(0, new int[]{300000, 1000000, 2000000}); 235 freqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000}); 236 return new CpuScalingPolicies(freqsByPolicy, freqsByPolicy); 237 } 238 239 private static class TestPowerStatsInternal extends PowerStatsInternal { 240 private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray(); 241 private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults = new SparseArray(); 242 private final int mTimeSinceBoot = 0; 243 244 @Override getEnergyConsumerInfo()245 public EnergyConsumer[] getEnergyConsumerInfo() { 246 final int size = mEnergyConsumers.size(); 247 final EnergyConsumer[] consumers = new EnergyConsumer[size]; 248 for (int i = 0; i < size; i++) { 249 consumers[i] = mEnergyConsumers.valueAt(i); 250 } 251 return consumers; 252 } 253 254 @Override getEnergyConsumedAsync( int[] energyConsumerIds)255 public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync( 256 int[] energyConsumerIds) { 257 final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture(); 258 final EnergyConsumerResult[] results; 259 final int length = energyConsumerIds.length; 260 if (length == 0) { 261 final int size = mEnergyConsumerResults.size(); 262 results = new EnergyConsumerResult[size]; 263 for (int i = 0; i < size; i++) { 264 results[i] = mEnergyConsumerResults.valueAt(i); 265 } 266 } else { 267 results = new EnergyConsumerResult[length]; 268 for (int i = 0; i < length; i++) { 269 results[i] = mEnergyConsumerResults.get(energyConsumerIds[i]); 270 } 271 } 272 future.complete(results); 273 return future; 274 } 275 276 @Override getPowerEntityInfo()277 public PowerEntity[] getPowerEntityInfo() { 278 return new PowerEntity[0]; 279 } 280 281 @Override getStateResidencyAsync( int[] powerEntityIds)282 public CompletableFuture<StateResidencyResult[]> getStateResidencyAsync( 283 int[] powerEntityIds) { 284 return new CompletableFuture<>(); 285 } 286 287 @Override getEnergyMeterInfo()288 public Channel[] getEnergyMeterInfo() { 289 return new Channel[0]; 290 } 291 292 @Override readEnergyMeterAsync( int[] channelIds)293 public CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync( 294 int[] channelIds) { 295 return new CompletableFuture<>(); 296 } 297 298 /** 299 * Util method to add a new EnergyConsumer for testing 300 * 301 * @return the EnergyConsumer id of the new EnergyConsumer 302 */ addEnergyConsumer(@nergyConsumerType byte type, int ordinal, String name)303 public int addEnergyConsumer(@EnergyConsumerType byte type, int ordinal, String name) { 304 final EnergyConsumer consumer = new EnergyConsumer(); 305 final int id = getNextAvailableId(); 306 consumer.id = id; 307 consumer.type = type; 308 consumer.ordinal = ordinal; 309 consumer.name = name; 310 mEnergyConsumers.put(id, consumer); 311 312 final EnergyConsumerResult result = new EnergyConsumerResult(); 313 result.id = id; 314 result.timestampMs = mTimeSinceBoot; 315 result.energyUWs = 0; 316 mEnergyConsumerResults.put(id, result); 317 return id; 318 } 319 incrementEnergyConsumption(int id, long energyUWs)320 public void incrementEnergyConsumption(int id, long energyUWs) { 321 EnergyConsumerResult result = mEnergyConsumerResults.get(id, null); 322 assertNotNull(result); 323 result.energyUWs += energyUWs; 324 } 325 getNextAvailableId()326 private int getNextAvailableId() { 327 final int size = mEnergyConsumers.size(); 328 // Just return the first index that does not match the key (aka the EnergyConsumer id) 329 for (int i = size - 1; i >= 0; i--) { 330 if (mEnergyConsumers.keyAt(i) == i) return i + 1; 331 } 332 // Otherwise return the lowest id 333 return 0; 334 } 335 } 336 } 337