diff options
6 files changed, 265 insertions, 68 deletions
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 87820a89280c..e599888c3bee 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -12706,12 +12706,12 @@ public class BatteryStatsImpl extends BatteryStats { // When the battery is not on, we don't attribute the cpu times to any timers but we still // need to take the snapshots. if (!onBattery) { - mCpuUidUserSysTimeReader.readDelta(null); - mCpuUidFreqTimeReader.readDelta(null); + mCpuUidUserSysTimeReader.readDelta(false, null); + mCpuUidFreqTimeReader.readDelta(false, null); mNumAllUidCpuTimeReads += 2; if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) { - mCpuUidActiveTimeReader.readDelta(null); - mCpuUidClusterTimeReader.readDelta(null); + mCpuUidActiveTimeReader.readDelta(false, null); + mCpuUidClusterTimeReader.readDelta(false, null); mNumAllUidCpuTimeReads += 2; } for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) { @@ -12897,7 +12897,7 @@ public class BatteryStatsImpl extends BatteryStats { final long startTimeMs = mClocks.uptimeMillis(); final long elapsedRealtimeMs = mClocks.elapsedRealtime(); - mCpuUidUserSysTimeReader.readDelta((uid, timesUs) -> { + mCpuUidUserSysTimeReader.readDelta(false, (uid, timesUs) -> { long userTimeUs = timesUs[0], systemTimeUs = timesUs[1]; uid = mapUid(uid); @@ -13011,7 +13011,7 @@ public class BatteryStatsImpl extends BatteryStats { final long startTimeMs = mClocks.uptimeMillis(); final long elapsedRealtimeMs = mClocks.elapsedRealtime(); final List<Integer> uidsToRemove = new ArrayList<>(); - mCpuUidFreqTimeReader.readDelta((uid, cpuFreqTimeMs) -> { + mCpuUidFreqTimeReader.readDelta(false, (uid, cpuFreqTimeMs) -> { uid = mapUid(uid); if (Process.isIsolated(uid)) { uidsToRemove.add(uid); @@ -13129,7 +13129,7 @@ public class BatteryStatsImpl extends BatteryStats { final long startTimeMs = mClocks.uptimeMillis(); final long elapsedRealtimeMs = mClocks.elapsedRealtime(); final List<Integer> uidsToRemove = new ArrayList<>(); - mCpuUidActiveTimeReader.readDelta((uid, cpuActiveTimesMs) -> { + mCpuUidActiveTimeReader.readDelta(false, (uid, cpuActiveTimesMs) -> { uid = mapUid(uid); if (Process.isIsolated(uid)) { uidsToRemove.add(uid); @@ -13163,7 +13163,7 @@ public class BatteryStatsImpl extends BatteryStats { final long startTimeMs = mClocks.uptimeMillis(); final long elapsedRealtimeMs = mClocks.elapsedRealtime(); final List<Integer> uidsToRemove = new ArrayList<>(); - mCpuUidClusterTimeReader.readDelta((uid, cpuClusterTimesMs) -> { + mCpuUidClusterTimeReader.readDelta(false, (uid, cpuClusterTimesMs) -> { uid = mapUid(uid); if (Process.isIsolated(uid)) { uidsToRemove.add(uid); diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index 45d81280af4a..97f727ba72c5 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -124,15 +124,15 @@ public class CpuPowerCalculator extends PowerCalculator { long durationMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; // Constant battery drain when CPU is active - double powerMah = mCpuActivePowerEstimator.calculatePower(u.getCpuActiveTime()); + double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime()); // Additional per-cluster battery drain long[] cpuClusterTimes = u.getCpuClusterTimes(); if (cpuClusterTimes != null) { if (cpuClusterTimes.length == mNumCpuClusters) { for (int cluster = 0; cluster < mNumCpuClusters; cluster++) { - double power = mPerClusterPowerEstimators[cluster] - .calculatePower(cpuClusterTimes[cluster]); + double power = calculatePerCpuClusterPowerMah(cluster, + cpuClusterTimes[cluster]); powerMah += power; if (DEBUG) { Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster @@ -151,8 +151,8 @@ public class CpuPowerCalculator extends PowerCalculator { final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length; for (int speed = 0; speed < speedsForCluster; speed++) { final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType); - final double power = - mPerCpuFreqPowerEstimators[cluster][speed].calculatePower(timeUs / 1000); + final double power = calculatePerCpuFreqPowerMah(cluster, speed, + timeUs / 1000); if (DEBUG) { Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #" + speed + " timeUs=" + timeUs + " power=" @@ -207,4 +207,39 @@ public class CpuPowerCalculator extends PowerCalculator { result.powerMah = powerMah; result.packageWithHighestDrain = packageWithHighestDrain; } + + /** + * Calculates active CPU power consumption. + * + * @param durationsMs duration of CPU usage. + * @return a double in milliamp-hours of estimated active CPU power consumption. + */ + public double calculateActiveCpuPowerMah(long durationsMs) { + return mCpuActivePowerEstimator.calculatePower(durationsMs); + } + + /** + * Calculates CPU cluster power consumption. + * + * @param cluster CPU cluster used. + * @param clusterDurationMs duration of CPU cluster usage. + * @return a double in milliamp-hours of estimated CPU cluster power consumption. + */ + public double calculatePerCpuClusterPowerMah(int cluster, long clusterDurationMs) { + return mPerClusterPowerEstimators[cluster].calculatePower(clusterDurationMs); + } + + /** + * Calculates CPU cluster power consumption at a specific speedstep. + * + * @param cluster CPU cluster used. + * @param speedStep which speedstep used. + * @param clusterSpeedDurationsMs duration of CPU cluster usage at the specified speed step. + * @return a double in milliamp-hours of estimated CPU cluster-speed power consumption. + */ + public double calculatePerCpuFreqPowerMah(int cluster, int speedStep, + long clusterSpeedDurationsMs) { + return mPerCpuFreqPowerEstimators[cluster][speedStep].calculatePower( + clusterSpeedDurationsMs); + } } diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java index f7fad2c5bbaa..4299f0936dae 100644 --- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java +++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java @@ -91,15 +91,24 @@ public abstract class KernelCpuUidTimeReader<T> { * Reads the proc file, calling into the callback with a delta of time for each UID. * * @param cb The callback to invoke for each line of the proc file. If null,the data is - * consumed and subsequent calls to readDelta will provide a fresh delta. */ public void readDelta(@Nullable Callback<T> cb) { + readDelta(false, cb); + } + + /** + * Reads the proc file, calling into the callback with a delta of time for each UID. + * + * @param force Ignore the throttling and force read the delta. + * @param cb The callback to invoke for each line of the proc file. If null,the data is + */ + public void readDelta(boolean force, @Nullable Callback<T> cb) { if (!mThrottle) { readDeltaImpl(cb); return; } final long currTimeMs = SystemClock.elapsedRealtime(); - if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) { + if (!force && currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) { if (DEBUG) { Slog.d(mTag, "Throttle readDelta"); } diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java index 8f81ea237a15..7dca0cb92f9d 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java @@ -93,35 +93,62 @@ public class KernelCpuUidUserSysTimeReaderTest { mReader.setThrottle(500); writeToFile(uidLines(mUids, mInitialTimes)); - mReader.readDelta(mCallback); + mReader.readDelta(false, mCallback); assertEquals(6, mCallback.mData.size()); long[][] times1 = increaseTime(mInitialTimes); writeToFile(uidLines(mUids, times1)); mCallback.clear(); - mReader.readDelta(mCallback); + mReader.readDelta(false, mCallback); assertEquals(0, mCallback.mData.size()); + // TODO(b/180473895): Replace sleeps with injected simulated time. SystemClock.sleep(600); long[][] times2 = increaseTime(times1); writeToFile(uidLines(mUids, times2)); mCallback.clear(); - mReader.readDelta(mCallback); + mReader.readDelta(false, mCallback); assertEquals(6, mCallback.mData.size()); long[][] times3 = increaseTime(times2); writeToFile(uidLines(mUids, times3)); mCallback.clear(); - mReader.readDelta(mCallback); + mReader.readDelta(false, mCallback); assertEquals(0, mCallback.mData.size()); + + // Force the delta read, previously skipped increments should now be read + mCallback.clear(); + mReader.readDelta(true, mCallback); + assertEquals(6, mCallback.mData.size()); + + SystemClock.sleep(600); + + long[][] times4 = increaseTime(times3); + writeToFile(uidLines(mUids, times4)); + mCallback.clear(); + mReader.readDelta(true, mCallback); + assertEquals(6, mCallback.mData.size()); + + // Don't force the delta read, throttle should be set from last read. + long[][] times5 = increaseTime(times4); + writeToFile(uidLines(mUids, times5)); + mCallback.clear(); + mReader.readDelta(false, mCallback); + assertEquals(0, mCallback.mData.size()); + + SystemClock.sleep(600); + + mCallback.clear(); + mReader.readDelta(false, mCallback); + assertEquals(6, mCallback.mData.size()); } @Test public void testReadDelta() throws Exception { final long[][] times1 = mInitialTimes; writeToFile(uidLines(mUids, times1)); - mReader.readDelta(mCallback); + mReader.readDelta(false, mCallback); for (int i = 0; i < mUids.length; i++) { mCallback.verify(mUids[i], times1[i]); } @@ -131,7 +158,7 @@ public class KernelCpuUidUserSysTimeReaderTest { // Verify that a second call will only return deltas. final long[][] times2 = increaseTime(times1); writeToFile(uidLines(mUids, times2)); - mReader.readDelta(mCallback); + mReader.readDelta(false, mCallback); for (int i = 0; i < mUids.length; i++) { mCallback.verify(mUids[i], subtract(times2[i], times1[i])); } @@ -139,20 +166,20 @@ public class KernelCpuUidUserSysTimeReaderTest { mCallback.clear(); // Verify that there won't be a callback if the proc file values didn't change. - mReader.readDelta(mCallback); + mReader.readDelta(false, mCallback); mCallback.verifyNoMoreInteractions(); mCallback.clear(); // Verify that calling with a null callback doesn't result in any crashes final long[][] times3 = increaseTime(times2); writeToFile(uidLines(mUids, times3)); - mReader.readDelta(null); + mReader.readDelta(false, null); // Verify that the readDelta call will only return deltas when // the previous call had null callback. final long[][] times4 = increaseTime(times3); writeToFile(uidLines(mUids, times4)); - mReader.readDelta(mCallback); + mReader.readDelta(false, mCallback); for (int i = 0; i < mUids.length; i++) { mCallback.verify(mUids[i], subtract(times4[i], times3[i])); } @@ -165,7 +192,7 @@ public class KernelCpuUidUserSysTimeReaderTest { public void testReadDeltaWrongData() throws Exception { final long[][] times1 = mInitialTimes; writeToFile(uidLines(mUids, times1)); - mReader.readDelta(mCallback); + mReader.readDelta(false, mCallback); for (int i = 0; i < mUids.length; i++) { mCallback.verify(mUids[i], times1[i]); } @@ -176,7 +203,7 @@ public class KernelCpuUidUserSysTimeReaderTest { final long[][] times2 = increaseTime(times1); times2[0][0] = 1000; writeToFile(uidLines(mUids, times2)); - mReader.readDelta(mCallback); + mReader.readDelta(false, mCallback); for (int i = 1; i < mUids.length; i++) { mCallback.verify(mUids[i], subtract(times2[i], times1[i])); } diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 52bb55f12d79..fc28bfbea710 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -50,8 +50,6 @@ import com.android.server.LocalServices; import libcore.util.EmptyArray; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -149,7 +147,6 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { * Maps an {@link EnergyConsumerType} to it's corresponding {@link EnergyConsumer#id}s, * unless it is of {@link EnergyConsumer#type}=={@link EnergyConsumerType#OTHER} */ - // TODO(b/180029015): Hook this up (it isn't used yet) @GuardedBy("mWorkerLock") private @Nullable SparseArray<int[]> mEnergyConsumerTypeToIdMap = null; @@ -209,11 +206,22 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { = populateEnergyConsumerSubsystemMapsLocked(); if (idToConsumer != null) { mMeasuredEnergySnapshot = new MeasuredEnergySnapshot(idToConsumer); - final EnergyConsumerResult[] initialEcrs = getEnergyConsumptionData(); - // According to spec, initialEcrs will include 0s for consumers that haven't - // used any energy yet, as long as they are supported; however, attributed uid - // energies will be absent if their energy is 0. - mMeasuredEnergySnapshot.updateAndGetDelta(initialEcrs); + try { + final EnergyConsumerResult[] initialEcrs = getEnergyConsumptionData().get( + EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + // According to spec, initialEcrs will include 0s for consumers that haven't + // used any energy yet, as long as they are supported; however, + // attributed uid energies will be absent if their energy is 0. + mMeasuredEnergySnapshot.updateAndGetDelta(initialEcrs); + } catch (TimeoutException | InterruptedException e) { + Slog.w(TAG, "timeout or interrupt reading initial getEnergyConsumedAsync: " + + e); + // Continue running, later attempts to query may be successful. + } catch (ExecutionException e) { + Slog.wtf(TAG, "exception reading initial getEnergyConsumedAsync: " + + e.getCause()); + // Continue running, later attempts to query may be successful. + } numCustomBuckets = mMeasuredEnergySnapshot.getNumOtherOrdinals(); supportedStdBuckets = getSupportedEnergyBuckets(idToConsumer); } @@ -498,6 +506,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { CompletableFuture<ModemActivityInfo> modemFuture = CompletableFuture.completedFuture(null); boolean railUpdated = false; + CompletableFuture<EnergyConsumerResult[]> futureECRs = getMeasuredEnergyLocked(updateFlags); + if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) { // We were asked to fetch WiFi data. // Only fetch WiFi power data if it is supported. @@ -574,9 +584,23 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { Slog.w(TAG, "exception reading modem stats: " + e.getCause()); } - final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDeltas = - mMeasuredEnergySnapshot == null ? null : - mMeasuredEnergySnapshot.updateAndGetDelta(getMeasuredEnergyLocked(updateFlags)); + final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDeltas; + if (mMeasuredEnergySnapshot == null || futureECRs == null) { + measuredEnergyDeltas = null; + } else { + EnergyConsumerResult[] ecrs; + try { + ecrs = futureECRs.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (TimeoutException | InterruptedException e) { + // TODO (b/180519623): Invalidate the MeasuredEnergy derived data until next reset. + Slog.w(TAG, "timeout or interrupt reading getEnergyConsumedAsync: " + e); + ecrs = null; + } catch (ExecutionException e) { + Slog.wtf(TAG, "exception reading getEnergyConsumedAsync: " + e.getCause()); + ecrs = null; + } + measuredEnergyDeltas = mMeasuredEnergySnapshot.updateAndGetDelta(ecrs); + } final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); @@ -786,22 +810,29 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { return buckets; } - /** Get {@link EnergyConsumerResult}s with the latest energy usage since boot. */ + /** Get all {@link EnergyConsumerResult}s with the latest energy usage since boot. */ @GuardedBy("mWorkerLock") - private @Nullable EnergyConsumerResult[] getEnergyConsumptionData() { - try { - return mPowerStatsInternal.getEnergyConsumedAsync(new int[0]) - .get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); - } catch (Exception e) { - Slog.e(TAG, "Failed to getEnergyConsumedAsync", e); - return null; - } + @Nullable + private CompletableFuture<EnergyConsumerResult[]> getEnergyConsumptionData() { + return getEnergyConsumptionData(new int[0]); + } + + /** + * Get {@link EnergyConsumerResult}s of the specified {@link EnergyConsumer} ids with the latest + * energy usage since boot. + */ + @GuardedBy("mWorkerLock") + @Nullable + private CompletableFuture<EnergyConsumerResult[]> getEnergyConsumptionData(int[] consumerIds) { + return mPowerStatsInternal.getEnergyConsumedAsync(consumerIds); } /** Fetch EnergyConsumerResult[] for supported subsystems based on the given updateFlags. */ + @VisibleForTesting @GuardedBy("mWorkerLock") - private @Nullable EnergyConsumerResult[] getMeasuredEnergyLocked(@ExternalUpdateFlag int flags) - { + @Nullable + public CompletableFuture<EnergyConsumerResult[]> getMeasuredEnergyLocked( + @ExternalUpdateFlag int flags) { if (mMeasuredEnergySnapshot == null || mPowerStatsInternal == null) return null; if (flags == UPDATE_ALL) { @@ -809,24 +840,27 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { return getEnergyConsumptionData(); } - final List<Integer> energyConsumerIds = new ArrayList<>(); + final IntArray energyConsumerIds = new IntArray(); + if ((flags & UPDATE_CPU) != 0) { + addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.CPU_CLUSTER); + } if ((flags & UPDATE_DISPLAY) != 0) { addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.DISPLAY); } // TODO: Wifi, Bluetooth, etc., go here - if (energyConsumerIds.isEmpty()) { + if (energyConsumerIds.size() == 0) { return null; } - // TODO(b/180029015): Query specific subsystems from HAL based on energyConsumerIds.toArray - return getEnergyConsumptionData(); + return getEnergyConsumptionData(energyConsumerIds.toArray()); } @GuardedBy("mWorkerLock") private void addEnergyConsumerIdLocked( - List<Integer> energyConsumerIds, @EnergyConsumerType int type) { - final int consumerId = 0; // TODO(b/180029015): Use mEnergyConsumerTypeToIdMap to get this - energyConsumerIds.add(consumerId); + IntArray energyConsumerIds, @EnergyConsumerType int type) { + final int[] consumerIds = mEnergyConsumerTypeToIdMap.get(type); + if (consumerIds == null) return; + energyConsumerIds.addAll(consumerIds); } /** Populates the cached type->ids map, and returns the (inverse) id->EnergyConsumer map. */ @@ -840,12 +874,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { return null; } - // TODO(b/180029015): Initialize typeToIds - // Maps type -> {ids} (1:n map, since multiple ids might have the same type) - // final SparseArray<SparseIntArray> typeToIds = new SparseArray<>(); - // Maps id -> EnergyConsumer (1:1 map) final SparseArray<EnergyConsumer> idToConsumer = new SparseArray<>(energyConsumers.length); + // Maps type -> {ids} (1:n map, since multiple ids might have the same type) + final SparseArray<IntArray> tempTypeToId = new SparseArray<>(); // Add all expected EnergyConsumers to the maps for (final EnergyConsumer consumer : energyConsumers) { @@ -862,9 +894,23 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } } idToConsumer.put(consumer.id, consumer); - // TODO(b/180029015): Also populate typeToIds map + + IntArray ids = tempTypeToId.get(consumer.type); + if (ids == null) { + ids = new IntArray(); + tempTypeToId.put(consumer.type, ids); + } + ids.add(consumer.id); + } + + mEnergyConsumerTypeToIdMap = new SparseArray<>(tempTypeToId.size()); + // Populate mEnergyConsumerTypeToIdMap with EnergyConsumer type to ids mappings + final int size = tempTypeToId.size(); + for (int i = 0; i < size; i++) { + final int consumerType = tempTypeToId.keyAt(i); + final int[] consumerIds = tempTypeToId.valueAt(i).toArray(); + mEnergyConsumerTypeToIdMap.put(consumerType, consumerIds); } - // TODO(b/180029015): Store typeToIds in mEnergyConsumerTypeToIdMap. return idToConsumer; } } diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java index a946534f4ecd..8d54ead75761 100644 --- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java @@ -16,6 +16,12 @@ package com.android.server.am; +import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL; +import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU; +import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import android.content.Context; @@ -27,6 +33,7 @@ import android.hardware.power.stats.EnergyMeasurement; import android.hardware.power.stats.PowerEntity; import android.hardware.power.stats.StateResidencyResult; import android.power.PowerStatsInternal; +import android.util.IntArray; import android.util.SparseArray; import androidx.test.InstrumentationRegistry; @@ -34,7 +41,9 @@ import androidx.test.InstrumentationRegistry; import com.android.internal.os.BatteryStatsImpl; import org.junit.Before; +import org.junit.Test; +import java.util.Arrays; import java.util.concurrent.CompletableFuture; /** @@ -58,6 +67,69 @@ public class BatteryExternalStatsWorkerTest { mBatteryStatsImpl); } + @Test + public void testTargetedEnergyConsumerQuerying() { + final int numCpuClusters = 4; + final int numOther = 3; + + final IntArray tempAllIds = new IntArray(); + // Add some energy consumers used by BatteryExternalStatsWorker. + final int displayId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.DISPLAY, 0, + "display"); + tempAllIds.add(displayId); + mPowerStatsInternal.incrementEnergyConsumption(displayId, 12345); + + final int[] cpuClusterIds = new int[numCpuClusters]; + for (int i = 0; i < numCpuClusters; i++) { + cpuClusterIds[i] = mPowerStatsInternal.addEnergyConsumer( + EnergyConsumerType.CPU_CLUSTER, i, "cpu_cluster" + i); + tempAllIds.add(cpuClusterIds[i]); + mPowerStatsInternal.incrementEnergyConsumption(cpuClusterIds[i], 1111 + i); + } + Arrays.sort(cpuClusterIds); + + final int[] otherIds = new int[numOther]; + for (int i = 0; i < numOther; i++) { + otherIds[i] = mPowerStatsInternal.addEnergyConsumer( + EnergyConsumerType.OTHER, i, "other" + i); + tempAllIds.add(otherIds[i]); + mPowerStatsInternal.incrementEnergyConsumption(otherIds[i], 3000 + i); + } + Arrays.sort(otherIds); + + final int[] allIds = tempAllIds.toArray(); + Arrays.sort(allIds); + + // Inform BESW that PowerStatsInternal is ready to query + mBatteryExternalStatsWorker.systemServicesReady(); + + final EnergyConsumerResult[] displayResults = + mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_DISPLAY).getNow(null); + // Results should only have the display energy consumer + assertEquals(1, displayResults.length); + assertEquals(displayId, displayResults[0].id); + + final EnergyConsumerResult[] cpuResults = + mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_CPU).getNow(null); + // Results should only have the cpu cluster energy consumers + final int[] receivedCpuIds = new int[cpuResults.length]; + for (int i = 0; i < cpuResults.length; i++) { + receivedCpuIds[i] = cpuResults[i].id; + } + Arrays.sort(receivedCpuIds); + assertArrayEquals(cpuClusterIds, receivedCpuIds); + + final EnergyConsumerResult[] allResults = + mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_ALL).getNow(null); + // All energy consumer results should be available + final int[] receivedAllIds = new int[allResults.length]; + for (int i = 0; i < allResults.length; i++) { + receivedAllIds[i] = allResults[i].id; + } + Arrays.sort(receivedAllIds); + assertArrayEquals(allIds, receivedAllIds); + } + public class TestInjector extends BatteryExternalStatsWorker.Injector { public TestInjector(Context context) { super(context); @@ -79,9 +151,8 @@ public class BatteryExternalStatsWorkerTest { } public class TestPowerStatsInternal extends PowerStatsInternal { - private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray<>(); - private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults = - new SparseArray<>(); + private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray(); + private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults = new SparseArray(); private final int mTimeSinceBoot = 0; @Override @@ -98,10 +169,19 @@ public class BatteryExternalStatsWorkerTest { public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync( int[] energyConsumerIds) { final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture(); - final int size = mEnergyConsumerResults.size(); - final EnergyConsumerResult[] results = new EnergyConsumerResult[size]; - for (int i = 0; i < size; i++) { - results[i] = mEnergyConsumerResults.valueAt(i); + final EnergyConsumerResult[] results; + final int length = energyConsumerIds.length; + if (length == 0) { + final int size = mEnergyConsumerResults.size(); + results = new EnergyConsumerResult[size]; + for (int i = 0; i < size; i++) { + results[i] = mEnergyConsumerResults.valueAt(i); + } + } else { + results = new EnergyConsumerResult[length]; + for (int i = 0; i < length; i++) { + results[i] = mEnergyConsumerResults.get(energyConsumerIds[i]); + } } future.complete(results); return future; |