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