Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2cc5ce22 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I474cd019,I8d87c1da,I90ebad48 into sc-dev

* changes:
  Make CpuPowerCalculator's Estimators available to com.android.internal
  Implement EnergyConsumer type to ids map for targeted querying
  Add cpu proc file read throttling enabling/disabling
parents e982bd25 af23d81f
Loading
Loading
Loading
Loading
+8 −8
Original line number Diff line number Diff line
@@ -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);
+40 −5
Original line number Diff line number Diff line
@@ -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);
    }
}
+11 −2
Original line number Diff line number Diff line
@@ -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");
            }
+38 −11
Original line number Diff line number Diff line
@@ -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]));
        }
+81 −35
Original line number Diff line number Diff line
@@ -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();
                    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.
                        // 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;
    }
}
Loading