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