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.power.PowerStatsInternal;
40 import android.util.IntArray;
41 import android.util.SparseArray;
42 
43 import androidx.test.InstrumentationRegistry;
44 
45 import com.android.internal.os.PowerProfile;
46 
47 import org.junit.Before;
48 import org.junit.Test;
49 
50 import java.util.Arrays;
51 import java.util.concurrent.CompletableFuture;
52 
53 /**
54  * Tests for {@link BatteryExternalStatsWorker}.
55  *
56  * Build/Install/Run:
57  * atest FrameworksServicesTests:BatteryExternalStatsWorkerTest
58  */
59 @SuppressWarnings("GuardedBy")
60 public class BatteryExternalStatsWorkerTest {
61     private BatteryExternalStatsWorker mBatteryExternalStatsWorker;
62     private TestBatteryStatsImpl mBatteryStatsImpl;
63     private TestPowerStatsInternal mPowerStatsInternal;
64 
65     @Before
setUp()66     public void setUp() {
67         final Context context = InstrumentationRegistry.getContext();
68 
69         mBatteryStatsImpl = new TestBatteryStatsImpl(context);
70         mPowerStatsInternal = new TestPowerStatsInternal();
71         mBatteryExternalStatsWorker = new BatteryExternalStatsWorker(new TestInjector(context),
72                 mBatteryStatsImpl);
73     }
74 
75     @Test
testTargetedEnergyConsumerQuerying()76     public void testTargetedEnergyConsumerQuerying() {
77         final int numCpuClusters = 4;
78         final int numDisplays = 5;
79         final int numOther = 3;
80 
81         // Add some energy consumers used by BatteryExternalStatsWorker.
82         final IntArray tempAllIds = new IntArray();
83 
84         final int[] displayIds = new int[numDisplays];
85         for (int i = 0; i < numDisplays; i++) {
86             displayIds[i] = mPowerStatsInternal.addEnergyConsumer(
87                     EnergyConsumerType.DISPLAY, i, "display" + i);
88             tempAllIds.add(displayIds[i]);
89             mPowerStatsInternal.incrementEnergyConsumption(displayIds[i], 12345 + i);
90         }
91         Arrays.sort(displayIds);
92 
93         final int wifiId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.WIFI, 0,
94                 "wifi");
95         tempAllIds.add(wifiId);
96         mPowerStatsInternal.incrementEnergyConsumption(wifiId, 23456);
97 
98         final int btId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.BLUETOOTH, 0,
99                 "bt");
100         tempAllIds.add(btId);
101         mPowerStatsInternal.incrementEnergyConsumption(btId, 34567);
102 
103         final int gnssId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.GNSS, 0,
104                 "gnss");
105         tempAllIds.add(gnssId);
106         mPowerStatsInternal.incrementEnergyConsumption(gnssId, 787878);
107 
108         final int cameraId =
109                 mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.CAMERA, 0, "camera");
110         tempAllIds.add(cameraId);
111         mPowerStatsInternal.incrementEnergyConsumption(cameraId, 901234);
112 
113         final int mobileRadioId = mPowerStatsInternal.addEnergyConsumer(
114                 EnergyConsumerType.MOBILE_RADIO, 0, "mobile_radio");
115         tempAllIds.add(mobileRadioId);
116         mPowerStatsInternal.incrementEnergyConsumption(mobileRadioId, 62626);
117 
118         final int[] cpuClusterIds = new int[numCpuClusters];
119         for (int i = 0; i < numCpuClusters; i++) {
120             cpuClusterIds[i] = mPowerStatsInternal.addEnergyConsumer(
121                     EnergyConsumerType.CPU_CLUSTER, i, "cpu_cluster" + i);
122             tempAllIds.add(cpuClusterIds[i]);
123             mPowerStatsInternal.incrementEnergyConsumption(cpuClusterIds[i], 1111 + i);
124         }
125         Arrays.sort(cpuClusterIds);
126 
127         final int[] otherIds = new int[numOther];
128         for (int i = 0; i < numOther; i++) {
129             otherIds[i] = mPowerStatsInternal.addEnergyConsumer(
130                     EnergyConsumerType.OTHER, i, "other" + i);
131             tempAllIds.add(otherIds[i]);
132             mPowerStatsInternal.incrementEnergyConsumption(otherIds[i], 3000 + i);
133         }
134         Arrays.sort(otherIds);
135 
136         final int[] allIds = tempAllIds.toArray();
137         Arrays.sort(allIds);
138 
139         // Inform BESW that PowerStatsInternal is ready to query
140         mBatteryExternalStatsWorker.systemServicesReady();
141 
142         final EnergyConsumerResult[] displayResults =
143                 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_DISPLAY).getNow(null);
144         // Results should only have the cpu cluster energy consumers
145         final int[] receivedDisplayIds = new int[displayResults.length];
146         for (int i = 0; i < displayResults.length; i++) {
147             receivedDisplayIds[i] = displayResults[i].id;
148         }
149         Arrays.sort(receivedDisplayIds);
150         assertArrayEquals(displayIds, receivedDisplayIds);
151 
152         final EnergyConsumerResult[] wifiResults =
153                 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_WIFI).getNow(null);
154         // Results should only have the wifi energy consumer
155         assertEquals(1, wifiResults.length);
156         assertEquals(wifiId, wifiResults[0].id);
157 
158         final EnergyConsumerResult[] bluetoothResults =
159                 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_BT).getNow(null);
160         // Results should only have the bluetooth energy consumer
161         assertEquals(1, bluetoothResults.length);
162         assertEquals(btId, bluetoothResults[0].id);
163 
164         final EnergyConsumerResult[] mobileRadioResults =
165                 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_RADIO).getNow(null);
166         // Results should only have the mobile radio energy consumer
167         assertEquals(1, mobileRadioResults.length);
168         assertEquals(mobileRadioId, mobileRadioResults[0].id);
169 
170         final EnergyConsumerResult[] cpuResults =
171                 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_CPU).getNow(null);
172         // Results should only have the cpu cluster energy consumers
173         final int[] receivedCpuIds = new int[cpuResults.length];
174         for (int i = 0; i < cpuResults.length; i++) {
175             receivedCpuIds[i] = cpuResults[i].id;
176         }
177         Arrays.sort(receivedCpuIds);
178         assertArrayEquals(cpuClusterIds, receivedCpuIds);
179 
180         final EnergyConsumerResult[] cameraResults =
181                 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_CAMERA).getNow(null);
182         // Results should only have the camera energy consumer
183         assertEquals(1, cameraResults.length);
184         assertEquals(cameraId, cameraResults[0].id);
185 
186         final EnergyConsumerResult[] allResults =
187                 mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_ALL).getNow(null);
188         // All energy consumer results should be available
189         final int[] receivedAllIds = new int[allResults.length];
190         for (int i = 0; i < allResults.length; i++) {
191             receivedAllIds[i] = allResults[i].id;
192         }
193         Arrays.sort(receivedAllIds);
194         assertArrayEquals(allIds, receivedAllIds);
195     }
196 
197     public class TestInjector extends BatteryExternalStatsWorker.Injector {
TestInjector(Context context)198         public TestInjector(Context context) {
199             super(context);
200         }
201 
getSystemService(Class<T> serviceClass)202         public <T> T getSystemService(Class<T> serviceClass) {
203             return null;
204         }
205 
getLocalService(Class<T> serviceClass)206         public <T> T getLocalService(Class<T> serviceClass) {
207             if (serviceClass == PowerStatsInternal.class) {
208                 return (T) mPowerStatsInternal;
209             }
210             return null;
211         }
212     }
213 
214     public class TestBatteryStatsImpl extends BatteryStatsImpl {
TestBatteryStatsImpl(Context context)215         public TestBatteryStatsImpl(Context context) {
216             mPowerProfile = new PowerProfile(context, true /* forTest */);
217             initTimersAndCounters();
218         }
219     }
220 
221     public class TestPowerStatsInternal extends PowerStatsInternal {
222         private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray();
223         private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults = new SparseArray();
224         private final int mTimeSinceBoot = 0;
225 
226         @Override
getEnergyConsumerInfo()227         public EnergyConsumer[] getEnergyConsumerInfo() {
228             final int size = mEnergyConsumers.size();
229             final EnergyConsumer[] consumers = new EnergyConsumer[size];
230             for (int i = 0; i < size; i++) {
231                 consumers[i] = mEnergyConsumers.valueAt(i);
232             }
233             return consumers;
234         }
235 
236         @Override
getEnergyConsumedAsync( int[] energyConsumerIds)237         public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
238                 int[] energyConsumerIds) {
239             final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture();
240             final EnergyConsumerResult[] results;
241             final int length = energyConsumerIds.length;
242             if (length == 0) {
243                 final int size = mEnergyConsumerResults.size();
244                 results = new EnergyConsumerResult[size];
245                 for (int i = 0; i < size; i++) {
246                     results[i] = mEnergyConsumerResults.valueAt(i);
247                 }
248             } else {
249                 results = new EnergyConsumerResult[length];
250                 for (int i = 0; i < length; i++) {
251                     results[i] = mEnergyConsumerResults.get(energyConsumerIds[i]);
252                 }
253             }
254             future.complete(results);
255             return future;
256         }
257 
258         @Override
getPowerEntityInfo()259         public PowerEntity[] getPowerEntityInfo() {
260             return new PowerEntity[0];
261         }
262 
263         @Override
getStateResidencyAsync( int[] powerEntityIds)264         public CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
265                 int[] powerEntityIds) {
266             return new CompletableFuture<>();
267         }
268 
269         @Override
getEnergyMeterInfo()270         public Channel[] getEnergyMeterInfo() {
271             return new Channel[0];
272         }
273 
274         @Override
readEnergyMeterAsync( int[] channelIds)275         public CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
276                 int[] channelIds) {
277             return new CompletableFuture<>();
278         }
279 
280         /**
281          * Util method to add a new EnergyConsumer for testing
282          *
283          * @return the EnergyConsumer id of the new EnergyConsumer
284          */
addEnergyConsumer(@nergyConsumerType byte type, int ordinal, String name)285         public int addEnergyConsumer(@EnergyConsumerType byte type, int ordinal, String name) {
286             final EnergyConsumer consumer = new EnergyConsumer();
287             final int id = getNextAvailableId();
288             consumer.id = id;
289             consumer.type = type;
290             consumer.ordinal = ordinal;
291             consumer.name = name;
292             mEnergyConsumers.put(id, consumer);
293 
294             final EnergyConsumerResult result = new EnergyConsumerResult();
295             result.id = id;
296             result.timestampMs = mTimeSinceBoot;
297             result.energyUWs = 0;
298             mEnergyConsumerResults.put(id, result);
299             return id;
300         }
301 
incrementEnergyConsumption(int id, long energyUWs)302         public void incrementEnergyConsumption(int id, long energyUWs) {
303             EnergyConsumerResult result = mEnergyConsumerResults.get(id, null);
304             assertNotNull(result);
305             result.energyUWs += energyUWs;
306         }
307 
getNextAvailableId()308         private int getNextAvailableId() {
309             final int size = mEnergyConsumers.size();
310             // Just return the first index that does not match the key (aka the EnergyConsumer id)
311             for (int i = size - 1; i >= 0; i--) {
312                 if (mEnergyConsumers.keyAt(i) == i) return i + 1;
313             }
314             // Otherwise return the lowest id
315             return 0;
316         }
317     }
318 }
319