| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.am; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| |
| import android.hardware.power.stats.EnergyConsumer; |
| import android.hardware.power.stats.EnergyConsumerAttribution; |
| import android.hardware.power.stats.EnergyConsumerResult; |
| import android.hardware.power.stats.EnergyConsumerType; |
| import android.util.SparseArray; |
| import android.util.SparseLongArray; |
| |
| import androidx.test.filters.SmallTest; |
| |
| import com.android.server.am.MeasuredEnergySnapshot.MeasuredEnergyDeltaData; |
| |
| import org.junit.Test; |
| |
| /** |
| * Test class for {@link MeasuredEnergySnapshot}. |
| * |
| * To run the tests, use |
| * atest FrameworksServicesTests:com.android.server.am.MeasuredEnergySnapshotTest |
| */ |
| @SmallTest |
| public final class MeasuredEnergySnapshotTest { |
| private static final EnergyConsumer CONSUMER_DISPLAY = createEnergyConsumer( |
| 0, 0, EnergyConsumerType.DISPLAY, "Display"); |
| private static final EnergyConsumer CONSUMER_OTHER_0 = createEnergyConsumer( |
| 47, 0, EnergyConsumerType.OTHER, "GPU"); |
| private static final EnergyConsumer CONSUMER_OTHER_1 = createEnergyConsumer( |
| 1, 1, EnergyConsumerType.OTHER, "HPU"); |
| private static final EnergyConsumer CONSUMER_OTHER_2 = createEnergyConsumer( |
| 436, 2, EnergyConsumerType.OTHER, "IPU\n&\005"); |
| |
| private static final SparseArray<EnergyConsumer> ALL_ID_CONSUMER_MAP = createIdToConsumerMap( |
| CONSUMER_DISPLAY, CONSUMER_OTHER_0, CONSUMER_OTHER_1, CONSUMER_OTHER_2); |
| private static final SparseArray<EnergyConsumer> SOME_ID_CONSUMER_MAP = createIdToConsumerMap( |
| CONSUMER_DISPLAY); |
| |
| private static final int VOLTAGE_0 = 4_000; |
| private static final int VOLTAGE_1 = 3_500; |
| private static final int VOLTAGE_2 = 3_100; |
| private static final int VOLTAGE_3 = 3_000; |
| private static final int VOLTAGE_4 = 2_800; |
| |
| // Elements in each results are purposefully out of order. |
| private static final EnergyConsumerResult[] RESULTS_0 = new EnergyConsumerResult[]{ |
| createEnergyConsumerResult(CONSUMER_OTHER_0.id, 90_000, new int[]{47, 3}, |
| new long[]{14_000, 13_000}), |
| createEnergyConsumerResult(CONSUMER_DISPLAY.id, 14_000, null, null), |
| createEnergyConsumerResult(CONSUMER_OTHER_1.id, 0, null, null), |
| // No CONSUMER_OTHER_2 |
| }; |
| private static final EnergyConsumerResult[] RESULTS_1 = new EnergyConsumerResult[]{ |
| createEnergyConsumerResult(CONSUMER_DISPLAY.id, 24_000, null, null), |
| createEnergyConsumerResult(CONSUMER_OTHER_0.id, 90_000, new int[]{47, 3}, |
| new long[]{14_000, 13_000}), |
| createEnergyConsumerResult(CONSUMER_OTHER_2.id, 12_000, new int[]{6}, |
| new long[]{10_000}), |
| createEnergyConsumerResult(CONSUMER_OTHER_1.id, 12_000_000, null, null), |
| }; |
| private static final EnergyConsumerResult[] RESULTS_2 = new EnergyConsumerResult[]{ |
| createEnergyConsumerResult(CONSUMER_DISPLAY.id, 36_000, null, null), |
| // No CONSUMER_OTHER_0 |
| // No CONSUMER_OTHER_1 |
| // No CONSUMER_OTHER_2 |
| }; |
| private static final EnergyConsumerResult[] RESULTS_3 = new EnergyConsumerResult[]{ |
| // No CONSUMER_DISPLAY |
| createEnergyConsumerResult(CONSUMER_OTHER_2.id, 13_000, new int[]{6}, |
| new long[]{10_000}), |
| createEnergyConsumerResult( |
| CONSUMER_OTHER_0.id, 190_000, new int[]{2, 3, 47, 7}, |
| new long[]{9_000, 18_000, 14_000, 6_000}), |
| createEnergyConsumerResult(CONSUMER_OTHER_1.id, 12_000_000, null, null), |
| }; |
| private static final EnergyConsumerResult[] RESULTS_4 = new EnergyConsumerResult[]{ |
| createEnergyConsumerResult(CONSUMER_DISPLAY.id, 43_000, null, null), |
| createEnergyConsumerResult( |
| CONSUMER_OTHER_0.id, 290_000, new int[]{7, 47, 3, 2}, |
| new long[]{6_000, 14_000, 18_000, 11_000}), |
| // No CONSUMER_OTHER_1 |
| createEnergyConsumerResult(CONSUMER_OTHER_2.id, 165_000, new int[]{6, 47}, |
| new long[]{10_000, 8_000}), |
| }; |
| |
| @Test |
| public void testUpdateAndGetDelta_empty() { |
| final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP); |
| assertNull(snapshot.updateAndGetDelta(null, VOLTAGE_0)); |
| assertNull(snapshot.updateAndGetDelta(new EnergyConsumerResult[0], VOLTAGE_0)); |
| } |
| |
| @Test |
| public void testUpdateAndGetDelta() { |
| final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP); |
| |
| // results0 |
| MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0); |
| if (delta != null) { // null is fine here. If non-null, it better be uninteresting though. |
| assertNull(delta.displayChargeUC); |
| assertNull(delta.otherTotalChargeUC); |
| assertNull(delta.otherUidChargesUC); |
| } |
| |
| // results1 |
| delta = snapshot.updateAndGetDelta(RESULTS_1, VOLTAGE_1); |
| assertNotNull(delta); |
| long expectedChargeUC; |
| expectedChargeUC = calculateChargeConsumedUC(14_000, VOLTAGE_0, 24_000, VOLTAGE_1); |
| assertEquals(expectedChargeUC, delta.displayChargeUC[0]); |
| |
| assertNotNull(delta.otherTotalChargeUC); |
| |
| expectedChargeUC = calculateChargeConsumedUC(90_000, VOLTAGE_0, 90_000, VOLTAGE_1); |
| assertEquals(expectedChargeUC, delta.otherTotalChargeUC[0]); |
| expectedChargeUC = calculateChargeConsumedUC(0, VOLTAGE_0, 12_000_000, VOLTAGE_1); |
| assertEquals(expectedChargeUC, delta.otherTotalChargeUC[1]); |
| assertEquals(0, delta.otherTotalChargeUC[2]); // First good pull. Treat delta as 0. |
| |
| assertNotNull(delta.otherUidChargesUC); |
| assertNullOrEmpty(delta.otherUidChargesUC[0]); // No change in uid energies |
| assertNullOrEmpty(delta.otherUidChargesUC[1]); |
| assertNullOrEmpty(delta.otherUidChargesUC[2]); |
| |
| // results2 |
| delta = snapshot.updateAndGetDelta(RESULTS_2, VOLTAGE_2); |
| assertNotNull(delta); |
| expectedChargeUC = calculateChargeConsumedUC(24_000, VOLTAGE_1, 36_000, VOLTAGE_2); |
| assertEquals(expectedChargeUC, delta.displayChargeUC[0]); |
| assertNull(delta.otherUidChargesUC); |
| assertNull(delta.otherTotalChargeUC); |
| |
| // results3 |
| delta = snapshot.updateAndGetDelta(RESULTS_3, VOLTAGE_3); |
| assertNotNull(delta); |
| assertNull(delta.displayChargeUC); |
| |
| assertNotNull(delta.otherTotalChargeUC); |
| |
| expectedChargeUC = calculateChargeConsumedUC(90_000, VOLTAGE_1, 190_000, VOLTAGE_3); |
| assertEquals(expectedChargeUC, delta.otherTotalChargeUC[0]); |
| expectedChargeUC = calculateChargeConsumedUC(12_000_000, VOLTAGE_1, 12_000_000, VOLTAGE_3); |
| assertEquals(expectedChargeUC, delta.otherTotalChargeUC[1]); |
| expectedChargeUC = calculateChargeConsumedUC(12_000, VOLTAGE_1, 13_000, VOLTAGE_3); |
| assertEquals(expectedChargeUC, delta.otherTotalChargeUC[2]); |
| |
| assertNotNull(delta.otherUidChargesUC); |
| |
| assertEquals(3, delta.otherUidChargesUC[0].size()); |
| expectedChargeUC = calculateChargeConsumedUC(0, VOLTAGE_1, 9_000, VOLTAGE_3); |
| assertEquals(expectedChargeUC, delta.otherUidChargesUC[0].get(2)); |
| expectedChargeUC = calculateChargeConsumedUC(13_000, VOLTAGE_1, 18_000, VOLTAGE_3); |
| assertEquals(expectedChargeUC, delta.otherUidChargesUC[0].get(3)); |
| expectedChargeUC = calculateChargeConsumedUC(0, VOLTAGE_1, 6_000, VOLTAGE_3); |
| assertEquals(expectedChargeUC, delta.otherUidChargesUC[0].get(7)); |
| assertNullOrEmpty(delta.otherUidChargesUC[1]); |
| assertNullOrEmpty(delta.otherUidChargesUC[2]); |
| |
| // results4 |
| delta = snapshot.updateAndGetDelta(RESULTS_4, VOLTAGE_4); |
| assertNotNull(delta); |
| expectedChargeUC = calculateChargeConsumedUC(36_000, VOLTAGE_2, 43_000, VOLTAGE_4); |
| assertEquals(expectedChargeUC, delta.displayChargeUC[0]); |
| |
| assertNotNull(delta.otherTotalChargeUC); |
| expectedChargeUC = calculateChargeConsumedUC(190_000, VOLTAGE_3, 290_000, VOLTAGE_4); |
| assertEquals(expectedChargeUC, delta.otherTotalChargeUC[0]); |
| assertEquals(0, delta.otherTotalChargeUC[1]); // Not present (e.g. missing data) |
| expectedChargeUC = calculateChargeConsumedUC(13_000, VOLTAGE_3, 165_000, VOLTAGE_4); |
| assertEquals(expectedChargeUC, delta.otherTotalChargeUC[2]); |
| |
| assertNotNull(delta.otherUidChargesUC); |
| assertEquals(1, delta.otherUidChargesUC[0].size()); |
| expectedChargeUC = calculateChargeConsumedUC(9_000, VOLTAGE_3, 11_000, VOLTAGE_4); |
| assertEquals(expectedChargeUC, delta.otherUidChargesUC[0].get(2)); |
| assertNullOrEmpty(delta.otherUidChargesUC[1]); // Not present |
| assertEquals(1, delta.otherUidChargesUC[2].size()); |
| expectedChargeUC = calculateChargeConsumedUC(0, VOLTAGE_3, 8_000, VOLTAGE_4); |
| assertEquals(expectedChargeUC, delta.otherUidChargesUC[2].get(47)); |
| } |
| |
| /** Test updateAndGetDelta() when the results have consumers absent from idToConsumerMap. */ |
| @Test |
| public void testUpdateAndGetDelta_some() { |
| final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(SOME_ID_CONSUMER_MAP); |
| |
| // results0 |
| MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0); |
| if (delta != null) { // null is fine here. If non-null, it better be uninteresting though. |
| assertNull(delta.displayChargeUC); |
| assertNull(delta.otherTotalChargeUC); |
| assertNull(delta.otherUidChargesUC); |
| } |
| |
| // results1 |
| delta = snapshot.updateAndGetDelta(RESULTS_1, VOLTAGE_1); |
| assertNotNull(delta); |
| final long expectedChargeUC = |
| calculateChargeConsumedUC(14_000, VOLTAGE_0, 24_000, VOLTAGE_1); |
| assertEquals(expectedChargeUC, delta.displayChargeUC[0]); |
| assertNull(delta.otherTotalChargeUC); // Although in the results, they're not in the idMap |
| assertNull(delta.otherUidChargesUC); |
| } |
| |
| @Test |
| public void testGetOtherOrdinalNames() { |
| final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP); |
| assertThat(snapshot.getOtherOrdinalNames()).asList() |
| .containsExactly("GPU", "HPU", "IPU &_"); |
| } |
| |
| @Test |
| public void testGetOtherOrdinalNames_none() { |
| final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(SOME_ID_CONSUMER_MAP); |
| assertEquals(0, snapshot.getOtherOrdinalNames().length); |
| } |
| |
| private static EnergyConsumer createEnergyConsumer(int id, int ord, byte type, String name) { |
| final EnergyConsumer ec = new EnergyConsumer(); |
| ec.id = id; |
| ec.ordinal = ord; |
| ec.type = type; |
| ec.name = name; |
| return ec; |
| } |
| |
| private static SparseArray<EnergyConsumer> createIdToConsumerMap(EnergyConsumer ... ecs) { |
| final SparseArray<EnergyConsumer> map = new SparseArray<>(); |
| for (EnergyConsumer ec : ecs) { |
| map.put(ec.id, ec); |
| } |
| return map; |
| } |
| |
| private static EnergyConsumerResult createEnergyConsumerResult( |
| int id, long energyUWs, int[] uids, long[] uidEnergies) { |
| final EnergyConsumerResult ecr = new EnergyConsumerResult(); |
| ecr.id = id; |
| ecr.energyUWs = energyUWs; |
| if (uids != null) { |
| ecr.attribution = new EnergyConsumerAttribution[uids.length]; |
| for (int i = 0; i < uids.length; i++) { |
| ecr.attribution[i] = new EnergyConsumerAttribution(); |
| ecr.attribution[i].uid = uids[i]; |
| ecr.attribution[i].energyUWs = uidEnergies[i]; |
| } |
| } |
| return ecr; |
| } |
| |
| private static long calculateChargeConsumedUC(long energyUWs0, long voltageMv0, long energyUWs1, |
| long voltageMv1) { |
| final long deltaEnergyUWs = energyUWs1 - energyUWs0; |
| final long avgVoltageMv = (voltageMv1 + voltageMv0 + 1) / 2; |
| |
| // Charge uC = Energy uWs * (1000 mV/V) / (voltage mV) + 0.5 (for rounding) |
| return (deltaEnergyUWs * 1000 + (avgVoltageMv / 2)) / avgVoltageMv; |
| } |
| |
| private void assertNullOrEmpty(SparseLongArray a) { |
| if (a != null) assertEquals("Array should be null or empty", 0, a.size()); |
| } |
| } |