1 /*
2  * Copyright (C) 2020 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.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertNull;
24 
25 import android.hardware.power.stats.EnergyConsumer;
26 import android.hardware.power.stats.EnergyConsumerAttribution;
27 import android.hardware.power.stats.EnergyConsumerResult;
28 import android.hardware.power.stats.EnergyConsumerType;
29 import android.os.BatteryStats;
30 import android.util.SparseArray;
31 import android.util.SparseLongArray;
32 
33 import androidx.test.filters.SmallTest;
34 
35 import com.android.server.power.stats.EnergyConsumerSnapshot.EnergyConsumerDeltaData;
36 
37 import org.junit.Test;
38 
39 /**
40  * Test class for {@link EnergyConsumerSnapshot}.
41  *
42  * To run the tests, use
43  * atest FrameworksServicesTests:com.android.server.power.stats.MeasuredEnergySnapshotTest
44  */
45 @SmallTest
46 public final class EnergyConsumerSnapshotTest {
47     private static final EnergyConsumer CONSUMER_DISPLAY = createEnergyConsumer(
48             0, 0, EnergyConsumerType.DISPLAY, "Display");
49     private static final  EnergyConsumer CONSUMER_OTHER_0 = createEnergyConsumer(
50             47, 0, EnergyConsumerType.OTHER, "GPU");
51     private static final  EnergyConsumer CONSUMER_OTHER_1 = createEnergyConsumer(
52             1, 1, EnergyConsumerType.OTHER, "HPU");
53     private static final  EnergyConsumer CONSUMER_OTHER_2 = createEnergyConsumer(
54             436, 2, EnergyConsumerType.OTHER, "IPU\n&\005");
55 
56     private static final SparseArray<EnergyConsumer> ALL_ID_CONSUMER_MAP = createIdToConsumerMap(
57             CONSUMER_DISPLAY, CONSUMER_OTHER_0, CONSUMER_OTHER_1, CONSUMER_OTHER_2);
58     private static final SparseArray<EnergyConsumer> SOME_ID_CONSUMER_MAP = createIdToConsumerMap(
59             CONSUMER_DISPLAY);
60 
61     private static final int VOLTAGE_0 = 4_000;
62     private static final int VOLTAGE_1 = 3_500;
63     private static final int VOLTAGE_2 = 3_100;
64     private static final int VOLTAGE_3 = 3_000;
65     private static final int VOLTAGE_4 = 2_800;
66 
67     // Elements in each results are purposefully out of order.
68     private static final EnergyConsumerResult[] RESULTS_0 = new EnergyConsumerResult[]{
69             createEnergyConsumerResult(CONSUMER_OTHER_0.id, 90_000, new int[]{47, 3},
70                     new long[]{14_000, 13_000}),
71             createEnergyConsumerResult(CONSUMER_DISPLAY.id, 14_000, null, null),
72             createEnergyConsumerResult(CONSUMER_OTHER_1.id, 0, null, null),
73             // No CONSUMER_OTHER_2
74     };
75     private static final EnergyConsumerResult[] RESULTS_1 = new EnergyConsumerResult[]{
76             createEnergyConsumerResult(CONSUMER_DISPLAY.id, 24_000, null, null),
77             createEnergyConsumerResult(CONSUMER_OTHER_0.id, 90_000, new int[]{47, 3},
78                     new long[]{14_000, 13_000}),
79             createEnergyConsumerResult(CONSUMER_OTHER_2.id, 12_000, new int[]{6},
80                     new long[]{10_000}),
81             createEnergyConsumerResult(CONSUMER_OTHER_1.id, 12_000_000, null, null),
82     };
83     private static final EnergyConsumerResult[] RESULTS_2 = new EnergyConsumerResult[]{
84             createEnergyConsumerResult(CONSUMER_DISPLAY.id, 36_000, null, null),
85             // No CONSUMER_OTHER_0
86             // No CONSUMER_OTHER_1
87             // No CONSUMER_OTHER_2
88     };
89     private static final EnergyConsumerResult[] RESULTS_3 = new EnergyConsumerResult[]{
90             // No CONSUMER_DISPLAY
91             createEnergyConsumerResult(CONSUMER_OTHER_2.id, 13_000, new int[]{6},
92                     new long[]{10_000}),
93             createEnergyConsumerResult(
94                     CONSUMER_OTHER_0.id, 190_000, new int[]{2, 3, 47, 7},
95                     new long[]{9_000, 18_000, 14_000, 6_000}),
96             createEnergyConsumerResult(CONSUMER_OTHER_1.id, 12_000_000, null, null),
97     };
98     private static final EnergyConsumerResult[] RESULTS_4 = new EnergyConsumerResult[]{
99             createEnergyConsumerResult(CONSUMER_DISPLAY.id, 43_000, null, null),
100             createEnergyConsumerResult(
101                     CONSUMER_OTHER_0.id, 290_000, new int[]{7, 47, 3, 2},
102                     new long[]{6_000, 14_000, 18_000, 11_000}),
103             // No CONSUMER_OTHER_1
104             createEnergyConsumerResult(CONSUMER_OTHER_2.id, 165_000, new int[]{6, 47},
105                     new long[]{10_000, 8_000}),
106     };
107 
108     @Test
testUpdateAndGetDelta_empty()109     public void testUpdateAndGetDelta_empty() {
110         final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(ALL_ID_CONSUMER_MAP);
111         assertNull(snapshot.updateAndGetDelta(null, VOLTAGE_0));
112         assertNull(snapshot.updateAndGetDelta(new EnergyConsumerResult[0], VOLTAGE_0));
113     }
114 
115     @Test
testUpdateAndGetDelta()116     public void testUpdateAndGetDelta() {
117         final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(ALL_ID_CONSUMER_MAP);
118 
119         // results0
120         EnergyConsumerDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
121         if (delta != null) { // null is fine here. If non-null, it better be uninteresting though.
122             assertNull(delta.displayChargeUC);
123             assertNull(delta.otherTotalChargeUC);
124             assertNull(delta.otherUidChargesUC);
125         }
126 
127         // results1
128         delta = snapshot.updateAndGetDelta(RESULTS_1, VOLTAGE_1);
129         assertNotNull(delta);
130         long expectedChargeUC;
131         expectedChargeUC = calculateChargeConsumedUC(14_000, VOLTAGE_0, 24_000, VOLTAGE_1);
132         assertEquals(expectedChargeUC, delta.displayChargeUC[0]);
133 
134         assertNotNull(delta.otherTotalChargeUC);
135 
136         expectedChargeUC = calculateChargeConsumedUC(90_000, VOLTAGE_0, 90_000, VOLTAGE_1);
137         assertEquals(expectedChargeUC, delta.otherTotalChargeUC[0]);
138         expectedChargeUC = calculateChargeConsumedUC(0, VOLTAGE_0, 12_000_000, VOLTAGE_1);
139         assertEquals(expectedChargeUC, delta.otherTotalChargeUC[1]);
140         assertEquals(0, delta.otherTotalChargeUC[2]); // First good pull. Treat delta as 0.
141 
142         assertNotNull(delta.otherUidChargesUC);
143         assertNullOrEmpty(delta.otherUidChargesUC[0]); // No change in uid energies
144         assertNullOrEmpty(delta.otherUidChargesUC[1]);
145         assertNullOrEmpty(delta.otherUidChargesUC[2]);
146 
147         // results2
148         delta = snapshot.updateAndGetDelta(RESULTS_2, VOLTAGE_2);
149         assertNotNull(delta);
150         expectedChargeUC = calculateChargeConsumedUC(24_000, VOLTAGE_1, 36_000, VOLTAGE_2);
151         assertEquals(expectedChargeUC, delta.displayChargeUC[0]);
152         assertNull(delta.otherUidChargesUC);
153         assertNull(delta.otherTotalChargeUC);
154 
155         // results3
156         delta = snapshot.updateAndGetDelta(RESULTS_3, VOLTAGE_3);
157         assertNotNull(delta);
158         assertNull(delta.displayChargeUC);
159 
160         assertNotNull(delta.otherTotalChargeUC);
161 
162         expectedChargeUC = calculateChargeConsumedUC(90_000, VOLTAGE_1, 190_000, VOLTAGE_3);
163         assertEquals(expectedChargeUC, delta.otherTotalChargeUC[0]);
164         expectedChargeUC = calculateChargeConsumedUC(12_000_000, VOLTAGE_1, 12_000_000, VOLTAGE_3);
165         assertEquals(expectedChargeUC, delta.otherTotalChargeUC[1]);
166         expectedChargeUC = calculateChargeConsumedUC(12_000, VOLTAGE_1, 13_000, VOLTAGE_3);
167         assertEquals(expectedChargeUC, delta.otherTotalChargeUC[2]);
168 
169         assertNotNull(delta.otherUidChargesUC);
170 
171         assertEquals(3, delta.otherUidChargesUC[0].size());
172         expectedChargeUC = calculateChargeConsumedUC(0, VOLTAGE_1, 9_000, VOLTAGE_3);
173         assertEquals(expectedChargeUC, delta.otherUidChargesUC[0].get(2));
174         expectedChargeUC = calculateChargeConsumedUC(13_000, VOLTAGE_1, 18_000, VOLTAGE_3);
175         assertEquals(expectedChargeUC, delta.otherUidChargesUC[0].get(3));
176         expectedChargeUC = calculateChargeConsumedUC(0, VOLTAGE_1, 6_000, VOLTAGE_3);
177         assertEquals(expectedChargeUC, delta.otherUidChargesUC[0].get(7));
178         assertNullOrEmpty(delta.otherUidChargesUC[1]);
179         assertNullOrEmpty(delta.otherUidChargesUC[2]);
180 
181         // results4
182         delta = snapshot.updateAndGetDelta(RESULTS_4, VOLTAGE_4);
183         assertNotNull(delta);
184         expectedChargeUC = calculateChargeConsumedUC(36_000, VOLTAGE_2, 43_000, VOLTAGE_4);
185         assertEquals(expectedChargeUC, delta.displayChargeUC[0]);
186 
187         assertNotNull(delta.otherTotalChargeUC);
188         expectedChargeUC = calculateChargeConsumedUC(190_000, VOLTAGE_3, 290_000, VOLTAGE_4);
189         assertEquals(expectedChargeUC, delta.otherTotalChargeUC[0]);
190         assertEquals(0, delta.otherTotalChargeUC[1]); // Not present (e.g. missing data)
191         expectedChargeUC = calculateChargeConsumedUC(13_000, VOLTAGE_3, 165_000, VOLTAGE_4);
192         assertEquals(expectedChargeUC, delta.otherTotalChargeUC[2]);
193 
194         assertNotNull(delta.otherUidChargesUC);
195         assertEquals(1, delta.otherUidChargesUC[0].size());
196         expectedChargeUC = calculateChargeConsumedUC(9_000, VOLTAGE_3, 11_000, VOLTAGE_4);
197         assertEquals(expectedChargeUC, delta.otherUidChargesUC[0].get(2));
198         assertNullOrEmpty(delta.otherUidChargesUC[1]); // Not present
199         assertEquals(1, delta.otherUidChargesUC[2].size());
200         expectedChargeUC = calculateChargeConsumedUC(0, VOLTAGE_3, 8_000, VOLTAGE_4);
201         assertEquals(expectedChargeUC, delta.otherUidChargesUC[2].get(47));
202     }
203 
204     /** Test updateAndGetDelta() when the results have consumers absent from idToConsumerMap. */
205     @Test
testUpdateAndGetDelta_some()206     public void testUpdateAndGetDelta_some() {
207         final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(SOME_ID_CONSUMER_MAP);
208 
209         // results0
210         EnergyConsumerDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
211         if (delta != null) { // null is fine here. If non-null, it better be uninteresting though.
212             assertNull(delta.displayChargeUC);
213             assertNull(delta.otherTotalChargeUC);
214             assertNull(delta.otherUidChargesUC);
215         }
216 
217         // results1
218         delta = snapshot.updateAndGetDelta(RESULTS_1, VOLTAGE_1);
219         assertNotNull(delta);
220         final long expectedChargeUC =
221                 calculateChargeConsumedUC(14_000, VOLTAGE_0, 24_000, VOLTAGE_1);
222         assertEquals(expectedChargeUC, delta.displayChargeUC[0]);
223         assertNull(delta.otherTotalChargeUC); // Although in the results, they're not in the idMap
224         assertNull(delta.otherUidChargesUC);
225     }
226 
227     @Test
testGetOtherOrdinalNames()228     public void testGetOtherOrdinalNames() {
229         final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(ALL_ID_CONSUMER_MAP);
230         assertThat(snapshot.getOtherOrdinalNames()).asList()
231                 .containsExactly("GPU", "HPU", "IPU &_");
232     }
233 
234     @Test
testGetOtherOrdinalNames_none()235     public void testGetOtherOrdinalNames_none() {
236         final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(SOME_ID_CONSUMER_MAP);
237         assertEquals(0, snapshot.getOtherOrdinalNames().length);
238     }
239 
240     @Test
getMeasuredEnergyDetails()241     public void getMeasuredEnergyDetails() {
242         final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(ALL_ID_CONSUMER_MAP);
243         snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
244         EnergyConsumerDeltaData delta = snapshot.updateAndGetDelta(RESULTS_1, VOLTAGE_1);
245         BatteryStats.EnergyConsumerDetails details = snapshot.getEnergyConsumerDetails(delta);
246         assertThat(details.consumers).hasLength(4);
247         assertThat(details.chargeUC).isEqualTo(new long[]{2667, 3200000, 0, 0});
248         assertThat(details.toString()).isEqualTo("DISPLAY=2667 HPU=3200000 GPU=0 IPU &_=0");
249     }
250 
251     @Test
testUpdateAndGetDelta_updatesCameraCharge()252     public void testUpdateAndGetDelta_updatesCameraCharge() {
253         EnergyConsumer cameraConsumer =
254                 createEnergyConsumer(7, 0, EnergyConsumerType.CAMERA, "CAMERA");
255         final EnergyConsumerSnapshot snapshot =
256                 new EnergyConsumerSnapshot(createIdToConsumerMap(cameraConsumer));
257 
258         // An initial result with only one energy consumer
259         EnergyConsumerResult[] result0 = new EnergyConsumerResult[]{
260                 createEnergyConsumerResult(cameraConsumer.id, 60_000, null, null),
261         };
262         snapshot.updateAndGetDelta(result0, VOLTAGE_1);
263 
264         // A subsequent result
265         EnergyConsumerResult[] result1 = new EnergyConsumerResult[]{
266                 createEnergyConsumerResult(cameraConsumer.id, 90_000, null, null),
267         };
268         EnergyConsumerDeltaData delta = snapshot.updateAndGetDelta(result1, VOLTAGE_1);
269 
270         // Verify that the delta between the two results is reported.
271         BatteryStats.EnergyConsumerDetails details = snapshot.getEnergyConsumerDetails(delta);
272         assertThat(details.consumers).hasLength(1);
273         long expectedDeltaUC = calculateChargeConsumedUC(60_000, VOLTAGE_1, 90_000, VOLTAGE_1);
274         assertThat(details.chargeUC[0]).isEqualTo(expectedDeltaUC);
275     }
276 
createEnergyConsumer(int id, int ord, byte type, String name)277     private static EnergyConsumer createEnergyConsumer(int id, int ord, byte type, String name) {
278         final EnergyConsumer ec = new EnergyConsumer();
279         ec.id = id;
280         ec.ordinal = ord;
281         ec.type = type;
282         ec.name = name;
283         return ec;
284     }
285 
createIdToConsumerMap(EnergyConsumer .... ecs)286     private static SparseArray<EnergyConsumer> createIdToConsumerMap(EnergyConsumer ... ecs) {
287         final SparseArray<EnergyConsumer> map = new SparseArray<>();
288         for (EnergyConsumer ec : ecs) {
289             map.put(ec.id, ec);
290         }
291         return map;
292     }
293 
createEnergyConsumerResult( int id, long energyUWs, int[] uids, long[] uidEnergies)294     private static EnergyConsumerResult createEnergyConsumerResult(
295             int id, long energyUWs, int[] uids, long[] uidEnergies) {
296         final EnergyConsumerResult ecr = new EnergyConsumerResult();
297         ecr.id = id;
298         ecr.energyUWs = energyUWs;
299         if (uids != null) {
300             ecr.attribution = new EnergyConsumerAttribution[uids.length];
301             for (int i = 0; i < uids.length; i++) {
302                 ecr.attribution[i] = new EnergyConsumerAttribution();
303                 ecr.attribution[i].uid = uids[i];
304                 ecr.attribution[i].energyUWs = uidEnergies[i];
305             }
306         }
307         return ecr;
308     }
309 
calculateChargeConsumedUC(long energyUWs0, long voltageMv0, long energyUWs1, long voltageMv1)310     private static long calculateChargeConsumedUC(long energyUWs0, long voltageMv0, long energyUWs1,
311             long voltageMv1) {
312         final long deltaEnergyUWs = energyUWs1 - energyUWs0;
313         final long avgVoltageMv = (voltageMv1 + voltageMv0 + 1) / 2;
314 
315         // Charge uC = Energy uWs * (1000 mV/V) / (voltage mV) + 0.5 (for rounding)
316         return (deltaEnergyUWs * 1000 + (avgVoltageMv / 2)) / avgVoltageMv;
317     }
318 
assertNullOrEmpty(SparseLongArray a)319     private void assertNullOrEmpty(SparseLongArray a) {
320         if (a != null) assertEquals("Array should be null or empty", 0, a.size());
321     }
322 }
323