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

Commit 072a8ab0 authored by Adam Bookatz's avatar Adam Bookatz Committed by Automerger Merge Worker
Browse files

Merge "BatteryStats receives custom measured energies" into sc-dev am: 420a8ca3

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13526643

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Id6475ab872f1ba59c8e361c4668a7a1c56112070
parents 6c091873 420a8ca3
Loading
Loading
Loading
Loading
+73 −0
Original line number Diff line number Diff line
@@ -7173,6 +7173,20 @@ public class BatteryStatsImpl extends BatteryStats {
                .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE);
    }
    /**
     * Returns the energy in microjoules that the given custom energy bucket consumed.
     * Will return {@link #ENERGY_DATA_UNAVAILABLE} if data is unavailable
     *
     * @param customEnergyBucket custom energy bucket of interest
     * @return energy (in microjoules) used by this uid for this energy bucket
     */
    public long getCustomMeasuredEnergyMicroJoules(int customEnergyBucket) {
        if (mGlobalMeasuredEnergyStats == null) {
            return ENERGY_DATA_UNAVAILABLE;
        }
        return mGlobalMeasuredEnergyStats.getAccumulatedCustomBucketEnergy(customEnergyBucket);
    }
    @Override public long getStartClockTime() {
        final long currentTimeMs = System.currentTimeMillis();
        if ((currentTimeMs > MILLISECONDS_IN_YEAR
@@ -7941,6 +7955,13 @@ public class BatteryStatsImpl extends BatteryStats {
                    .updateStandardBucket(energyBucket, energyDeltaUJ, accumulate);
        }
        /** Adds the given energy to the given custom energy bucket for this uid. */
        private void addEnergyToCustomBucketLocked(long energyDeltaUJ, int energyBucket,
                boolean accumulate) {
            getOrCreateMeasuredEnergyStatsLocked()
                    .updateCustomBucket(energyBucket, energyDeltaUJ, accumulate);
        }
        /**
         * Returns the energy used by this uid for a standard energy bucket of interest.
         * @param bucket standard energy bucket of interest
@@ -7957,6 +7978,22 @@ public class BatteryStatsImpl extends BatteryStats {
            return mUidMeasuredEnergyStats.getAccumulatedStandardBucketEnergy(bucket);
        }
        /**
         * Returns the energy used by this uid for a custom energy bucket of interest.
         * @param customEnergyBucket custom energy bucket of interest
         * @return energy (in microjoules) used by this uid for this energy bucket
         */
        public long getCustomMeasuredEnergyMicroJoules(int customEnergyBucket) {
            if (mBsi.mGlobalMeasuredEnergyStats == null
                    || !mBsi.mGlobalMeasuredEnergyStats.isValidCustomBucket(customEnergyBucket)) {
                return ENERGY_DATA_UNAVAILABLE;
            }
            if (mUidMeasuredEnergyStats == null) {
                return 0L; // It is supported, but was never filled, so it must be 0
            }
            return mUidMeasuredEnergyStats.getAccumulatedCustomBucketEnergy(customEnergyBucket);
        }
        /**
         * Gets the minimum of the uid's foreground activity time and its PROCESS_STATE_TOP time
         * since last marked. Also sets the mark time for both these timers.
@@ -12464,6 +12501,42 @@ public class BatteryStatsImpl extends BatteryStats {
        }
    }
    /**
     * Accumulate Custom energy bucket energy, globally and for each app.
     *
     * @param totalEnergyUJ energy (microjoules) used for this bucket since this was last called.
     * @param uidEnergies map of uid->energy (microjoules) for this bucket since last called.
     *                    Data inside uidEnergies will not be modified (treated immutable).
     */
    public void updateCustomMeasuredEnergyDataLocked(int customEnergyBucket,
            long totalEnergyUJ, @Nullable SparseLongArray uidEnergies) {
        if (DEBUG_ENERGY) {
            Slog.d(TAG, "Updating attributed measured energy stats for custom bucket "
                    + customEnergyBucket
                    + " with total energy " + totalEnergyUJ
                    + " and uid energies " + String.valueOf(uidEnergies));
        }
        if (mGlobalMeasuredEnergyStats == null) return;
        if (!mOnBatteryInternal || mIgnoreNextExternalStats || totalEnergyUJ <= 0) return;
        mGlobalMeasuredEnergyStats.updateCustomBucket(customEnergyBucket, totalEnergyUJ, true);
        if (uidEnergies == null) return;
        final int numUids = uidEnergies.size();
        for (int i = 0; i < numUids; i++) {
            final int uidInt = mapUid(uidEnergies.keyAt(i));
            final long uidEnergyUJ = uidEnergies.valueAt(i);
            if (uidEnergyUJ == 0) continue;
            // TODO: Worry about uids not in BSI currently, including uninstalled uids 'coming back'
            //  Specifically: What if the uid had been removed? We'll re-create it now.
            //  And if we instead use getAvailableUidStatsLocked() and chec for null, then we might
            //  not create a Uid even when we should be (say, the app's first event, somehow, was to
            //  use GPU). I guess that CPU/kernel data might already have this problem?
            final Uid uidObj = getUidStatsLocked(uidInt);
            uidObj.addEnergyToCustomBucketLocked(uidEnergyUJ, customEnergyBucket, true);
        }
    }
    /**
     * Read and record Rail Energy data.
     */
+0 −66
Original line number Diff line number Diff line
/*
 * 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.internal.power;


import android.annotation.IntDef;

import com.android.internal.os.RailStats;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Interface to provide subsystem energy data.
 * TODO: replace this and {@link RailStats} once b/173077356 is done
 */
public interface MeasuredEnergyArray {
    int SUBSYSTEM_UNKNOWN = -1;
    int SUBSYSTEM_DISPLAY = 0;
    int NUMBER_SUBSYSTEMS = 1;
    String[] SUBSYSTEM_NAMES = {"display"};


    @IntDef(prefix = { "SUBSYSTEM_" }, value = {
            SUBSYSTEM_UNKNOWN,
            SUBSYSTEM_DISPLAY,
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface MeasuredEnergySubsystem {}

    /**
     * Get the subsystem at an index in array.
     *
     * @param index into the array.
     * @return subsystem.
     */
    @MeasuredEnergySubsystem
    int getSubsystem(int index);

    /**
     * Get the energy (in microjoules) consumed since boot of the subsystem at an index.
     *
     * @param index into the array.
     * @return energy (in microjoules) consumed since boot.
     */
    long getEnergy(int index);

    /**
     * Return number of subsystems in the array.
     */
    int size();
}
+3 −3
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import android.util.Slog;
import android.view.Display;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.power.MeasuredEnergyArray.MeasuredEnergySubsystem;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -247,7 +246,7 @@ public class MeasuredEnergyStats {
    }

    /**
     * Map {@link MeasuredEnergySubsystem} and device state to Display {@link StandardEnergyBucket}.
     * Map {@link android.view.Display} STATE_ to corresponding {@link StandardEnergyBucket}.
     */
    public static @StandardEnergyBucket int getDisplayEnergyBucket(int screenState) {
        if (Display.isOnState(screenState)) {
@@ -450,7 +449,8 @@ public class MeasuredEnergyStats {
        return bucket >= 0 && bucket < NUMBER_STANDARD_ENERGY_BUCKETS;
    }

    private boolean isValidCustomBucket(int customBucket) {
    /** Returns whether the given custom bucket is valid (exists) on this device. */
    public boolean isValidCustomBucket(int customBucket) {
        return customBucket >= 0
                && customBucketToIndex(customBucket) < mAccumulatedEnergiesMicroJoules.length;
    }
+115 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.os.BatteryStats;
import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.Uid.Sensor;
import android.os.WorkSource;
import android.util.SparseLongArray;
import android.view.Display;

import androidx.test.filters.SmallTest;
@@ -583,6 +584,95 @@ public class BatteryStatsNoteTest extends TestCase {
        checkMeasuredEnergy("H", uid1, blame1, uid2, blame2, globalDoze, bi);
    }

    @SmallTest
    public void testUpdateCustomMeasuredEnergyDataLocked_neverCalled() {
        final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
        final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
        bi.setOnBatteryInternal(true);

        final int uid1 = 11500;
        final int uid2 = 11501;

        // Initially, all custom buckets report energy of 0.
        checkCustomMeasuredEnergy("0", 0, 0, uid1, 0, 0, uid2, 0, 0, bi);
    }

    @SmallTest
    public void testUpdateCustomMeasuredEnergyDataLocked() {
        final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
        final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);

        final int bucketA = 0; // Custom bucket 0
        final int bucketB = 1; // Custom bucket 1

        long totalBlameA = 0; // Total energy consumption for bucketA (may exceed sum of uids)
        long totalBlameB = 0; // Total energy consumption for bucketB (may exceed sum of uids)

        final int uid1 = 10500;
        long blame1A = 0; // Blame for uid1 in bucketA
        long blame1B = 0; // Blame for uid1 in bucketB

        final int uid2 = 10501;
        long blame2A = 0; // Blame for uid2 in bucketA
        long blame2B = 0; // Blame for uid2 in bucketB

        final SparseLongArray newEnergiesA = new SparseLongArray(2);
        final SparseLongArray newEnergiesB = new SparseLongArray(2);


        // ----- Case A: battery off (so blame does not increase)
        bi.setOnBatteryInternal(false);

        newEnergiesA.put(uid1, 20_000);
        // Implicit newEnergiesA.put(uid2, 0);
        bi.updateCustomMeasuredEnergyDataLocked(bucketA, 500_000, newEnergiesA);

        newEnergiesB.put(uid1, 60_000);
        // Implicit newEnergiesB.put(uid2, 0);
        bi.updateCustomMeasuredEnergyDataLocked(bucketB, 700_000, newEnergiesB);

        checkCustomMeasuredEnergy(
                "A", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);


        // ----- Case B: battery on
        bi.setOnBatteryInternal(true);

        newEnergiesA.put(uid1, 7_000); blame1A += 7_000;
        // Implicit newEnergiesA.put(uid2, 0); blame2A += 0;
        bi.updateCustomMeasuredEnergyDataLocked(bucketA, 310_000, newEnergiesA);
        totalBlameA += 310_000;

        newEnergiesB.put(uid1, 63_000); blame1B += 63_000;
        newEnergiesB.put(uid2, 15_000); blame2B += 15_000;
        bi.updateCustomMeasuredEnergyDataLocked(bucketB, 790_000, newEnergiesB);
        totalBlameB += 790_000;

        checkCustomMeasuredEnergy(
                "B", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);


        // ----- Case C: battery still on
        newEnergiesA.delete(uid1); blame1A += 0;
        newEnergiesA.put(uid2, 16_000); blame2A += 16_000;
        bi.updateCustomMeasuredEnergyDataLocked(bucketA, 560_000, newEnergiesA);
        totalBlameA += 560_000;

        bi.updateCustomMeasuredEnergyDataLocked(bucketB, 10_000, null);
        totalBlameB += 10_000;

        checkCustomMeasuredEnergy(
                "C", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);


        // ----- Case D: battery still on
        bi.updateCustomMeasuredEnergyDataLocked(bucketA, 0, newEnergiesA);
        bi.updateCustomMeasuredEnergyDataLocked(bucketB, 15_000, new SparseLongArray(1));
        totalBlameB += 15_000;
        checkCustomMeasuredEnergy(
                "D", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
    }

    private void setFgState(int uid, boolean fgOn, MockBatteryStatsImpl bi) {
        // Note that noteUidProcessStateLocked uses ActivityManager process states.
        if (fgOn) {
@@ -610,4 +700,29 @@ public class BatteryStatsNoteTest extends TestCase {
        assertEquals("Wrong doze for Case " + caseName, globalDoze,
                bi.getScreenDozeEnergy());
    }

    private void checkCustomMeasuredEnergy(String caseName,
            long totalBlameA, long totalBlameB,
            int uid1, long blame1A, long blame1B,
            int uid2, long blame2A, long blame2B,
            MockBatteryStatsImpl bi) {

        assertEquals("Wrong total blame in bucket 0 for Case " + caseName, totalBlameA,
                bi.getCustomMeasuredEnergyMicroJoules(0));

        assertEquals("Wrong total blame in bucket 1 for Case " + caseName, totalBlameB,
                bi.getCustomMeasuredEnergyMicroJoules(1));

        assertEquals("Wrong uid1 blame in bucket 0 for Case " + caseName, blame1A,
                bi.getUidStatsLocked(uid1).getCustomMeasuredEnergyMicroJoules(0));

        assertEquals("Wrong uid1 blame in bucket 1 for Case " + caseName, blame1B,
                bi.getUidStatsLocked(uid1).getCustomMeasuredEnergyMicroJoules(1));

        assertEquals("Wrong uid2 blame in bucket 0 for Case " + caseName, blame2A,
                bi.getUidStatsLocked(uid2).getCustomMeasuredEnergyMicroJoules(0));

        assertEquals("Wrong uid2 blame in bucket 1 for Case " + caseName, blame2B,
                bi.getUidStatsLocked(uid2).getCustomMeasuredEnergyMicroJoules(1));
    }
}
+18 −0
Original line number Diff line number Diff line
@@ -386,6 +386,24 @@ public class MeasuredEnergyStatsTest {
        assertEquals(60, stats.getAccumulatedCustomBucketEnergy(1));
    }

    @Test
    public void testIsValidCustomBucket() {
        final MeasuredEnergyStats stats
                = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 3);
        assertFalse(stats.isValidCustomBucket(-1));
        assertTrue(stats.isValidCustomBucket(0));
        assertTrue(stats.isValidCustomBucket(1));
        assertTrue(stats.isValidCustomBucket(2));
        assertFalse(stats.isValidCustomBucket(3));
        assertFalse(stats.isValidCustomBucket(4));

        final MeasuredEnergyStats boringStats
                = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 0);
        assertFalse(boringStats.isValidCustomBucket(-1));
        assertFalse(boringStats.isValidCustomBucket(0));
        assertFalse(boringStats.isValidCustomBucket(1));
    }

    @Test
    public void testReset() {
        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
Loading