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

Commit 3838323d authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Refactor BatteryStatsImpl.updateCpuTimeLocked.

Bug: 62240247
Test: bit FrameworksCoreTests:com.android.internal.os.BatteryStatsTests
Change-Id: I666c44ef9cb4e0720564b161da0ff1c94f15d5b9
parent 07fa4544
Loading
Loading
Loading
Loading
+232 −209
Original line number Diff line number Diff line
@@ -157,6 +157,15 @@ public class BatteryStatsImpl extends BatteryStats {
    // Number of transmit power states the Bluetooth controller can be in.
    private static final int NUM_BT_TX_LEVELS = 1;

    /**
     * Holding a wakelock costs more than just using the cpu.
     * Currently, we assign only half the cpu time to an app that is running but
     * not holding a wakelock. The apps holding wakelocks get the rest of the blame.
     * If no app is holding a wakelock, then the distribution is normal.
     */
    @VisibleForTesting
    public static final int WAKE_LOCK_WEIGHT = 50;

    protected Clocks mClocks;

    private final JournaledFile mFile;
@@ -171,9 +180,12 @@ public class BatteryStatsImpl extends BatteryStats {
    private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
    private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();

    private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
    private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
    private final KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader =
    @VisibleForTesting
    protected KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
    @VisibleForTesting
    protected KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
    @VisibleForTesting
    protected KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader =
            new KernelUidCpuFreqTimeReader();

    private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
@@ -197,10 +209,12 @@ public class BatteryStatsImpl extends BatteryStats {
    public static abstract class UserInfoProvider {
        private int[] userIds;
        protected abstract @Nullable int[] getUserIds();
        private final void refreshUserIds() {
        @VisibleForTesting
        public final void refreshUserIds() {
            userIds = getUserIds();
        }
        private final boolean exists(int userId) {
        @VisibleForTesting
        public boolean exists(int userId) {
            return userIds != null ? ArrayUtils.contains(userIds, userId) : true;
        }
    }
@@ -273,7 +287,8 @@ public class BatteryStatsImpl extends BatteryStats {

    public final MyHandler mHandler;
    private ExternalStatsSync mExternalSync = null;
    private UserInfoProvider mUserInfoProvider = null;
    @VisibleForTesting
    protected UserInfoProvider mUserInfoProvider = null;

    private BatteryCallback mCallback;

@@ -291,7 +306,8 @@ public class BatteryStatsImpl extends BatteryStats {
    // elapsed time by the number of active timers to arrive at that timer's share of the time.
    // In order to do this, we must refresh each timer whenever the number of active timers
    // changes.
    final ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<>();
    @VisibleForTesting
    protected ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<>();
    final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<>();
    final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<>();
    final ArrayList<StopwatchTimer> mDrawTimers = new ArrayList<>();
@@ -308,7 +324,8 @@ public class BatteryStatsImpl extends BatteryStats {
    final ArrayList<StopwatchTimer> mBluetoothScanOnTimers = new ArrayList<>();

    // Last partial timers we use for distributing CPU usage.
    final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<>();
    @VisibleForTesting
    protected ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<>();

    // These are the objects that will want to do something when the device
    // is unplugged from power.
@@ -536,7 +553,8 @@ public class BatteryStatsImpl extends BatteryStats {
     * in to power.
     */
    boolean mOnBattery;
    boolean mOnBatteryInternal;
    @VisibleForTesting
    protected boolean mOnBatteryInternal;

    /**
     * External reporting of whether the device is actually charging.
@@ -606,7 +624,8 @@ public class BatteryStatsImpl extends BatteryStats {

    private long[] mCpuFreqs;

    private PowerProfile mPowerProfile;
    @VisibleForTesting
    protected PowerProfile mPowerProfile;

    /*
     * Holds a SamplingTimer associated with each kernel wakelock name being tracked.
@@ -2020,7 +2039,8 @@ public class BatteryStatsImpl extends BatteryStats {
         * For partial wake locks, keep track of whether we are in the list
         * to consume CPU cycles.
         */
        boolean mInList;
        @VisibleForTesting
        public boolean mInList;

        public StopwatchTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
                TimeBase timeBase, Parcel in) {
@@ -10262,53 +10282,153 @@ public class BatteryStatsImpl extends BatteryStats {
            Slog.d(TAG, "!Cpu updating!");
        }

        // Holding a wakelock costs more than just using the cpu.
        // Currently, we assign only half the cpu time to an app that is running but
        // not holding a wakelock. The apps holding wakelocks get the rest of the blame.
        // If no app is holding a wakelock, then the distribution is normal.
        final int wakelockWeight = 50;

        int numWakelocks = 0;

        // Calculate how many wakelocks we have to distribute amongst. The system is excluded.
        // Only distribute cpu power to wakelocks if the screen is off and we're on battery.
        final int numPartialTimers = mPartialTimers.size();
        // Calculate the wakelocks we have to distribute amongst. The system is excluded as it is
        // usually holding the wakelock on behalf of an app.
        // And Only distribute cpu power to wakelocks if the screen is off and we're on battery.
        ArrayList<StopwatchTimer> partialTimersToConsider = null;
        if (mOnBatteryScreenOffTimeBase.isRunning()) {
            for (int i = 0; i < numPartialTimers; i++) {
            partialTimersToConsider = new ArrayList<>();
            for (int i = mPartialTimers.size() - 1; i >= 0; --i) {
                final StopwatchTimer timer = mPartialTimers.get(i);
                if (timer.mInList && timer.mUid != null && timer.mUid.mUid != Process.SYSTEM_UID) {
                // Since the collection and blaming of wakelocks can be scheduled to run after
                // some delay, the mPartialTimers list may have new entries. We can't blame
                // the newly added timer for past cpu time, so we only consider timers that
                // were present for one round of collection. Once a timer has gone through
                // a round of collection, its mInList field is set to true.
                    numWakelocks++;
                if (timer.mInList && timer.mUid != null && timer.mUid.mUid != Process.SYSTEM_UID) {
                    partialTimersToConsider.add(timer);
                }
            }
        }
        markPartialTimersAsEligible();

        final int numWakelocksF = numWakelocks;
        mTempTotalCpuUserTimeUs = 0;
        mTempTotalCpuSystemTimeUs = 0;
        // 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 (!mOnBatteryInternal) {
            mKernelUidCpuTimeReader.readDelta(null);
            mKernelUidCpuFreqTimeReader.readDelta(null);
            for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
                mKernelCpuSpeedReaders[cluster].readDelta();
            }
            return;
        }

        mUserInfoProvider.refreshUserIds();
        final SparseLongArray updatedUids = new SparseLongArray();
        readKernelUidCpuTimesLocked(partialTimersToConsider, updatedUids);
        updateClusterSpeedTimes(updatedUids);
        if (updateCpuFreqData) {
            readKernelUidCpuFreqTimesLocked();
        }
    }

    /**
     * Mark the current partial timers as gone through a collection so that they will be
     * considered in the next cpu times distribution to wakelock holders.
     */
    @VisibleForTesting
    public void markPartialTimersAsEligible() {
        if (ArrayUtils.referenceEquals(mPartialTimers, mLastPartialTimers)) {
            // No difference, so each timer is now considered for the next collection.
            for (int i = mPartialTimers.size() - 1; i >= 0; --i) {
                mPartialTimers.get(i).mInList = true;
            }
        } else {
            // The lists are different, meaning we added (or removed a timer) since the last
            // collection.
            for (int i = mLastPartialTimers.size() - 1; i >= 0; --i) {
                mLastPartialTimers.get(i).mInList = false;
            }
            mLastPartialTimers.clear();

            // Mark the current timers as gone through a collection.
            final int numPartialTimers = mPartialTimers.size();
            for (int i = 0; i < numPartialTimers; ++i) {
                final StopwatchTimer timer = mPartialTimers.get(i);
                timer.mInList = true;
                mLastPartialTimers.add(timer);
            }
        }
    }

    /**
     * Take snapshot of cpu times (aggregated over all uids) at different frequencies and
     * calculate cpu times spent by each uid at different frequencies.
     *
     * @param updatedUids The uids for which times spent at different frequencies are calculated.
     */
    @VisibleForTesting
    public void updateClusterSpeedTimes(@NonNull SparseLongArray updatedUids) {
        long totalCpuClustersTimeMs = 0;
        // Read the time spent for each cluster at various cpu frequencies.
        final long[][] clusterSpeedTimesMs = new long[mKernelCpuSpeedReaders.length][];
        for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
            clusterSpeedTimesMs[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
            if (clusterSpeedTimesMs[cluster] != null) {
                for (int speed = clusterSpeedTimesMs[cluster].length - 1; speed >= 0; --speed) {
                    totalCpuClustersTimeMs += clusterSpeedTimesMs[cluster][speed];
                }
            }
        }
        if (totalCpuClustersTimeMs != 0) {
            // We have cpu times per freq aggregated over all uids but we need the times per uid.
            // So, we distribute total time spent by an uid to different cpu freqs based on the
            // amount of time cpu was running at that freq.
            final int updatedUidsCount = updatedUids.size();
            for (int i = 0; i < updatedUidsCount; ++i) {
                final Uid u = getUidStatsLocked(updatedUids.keyAt(i));
                final long appCpuTimeUs = updatedUids.valueAt(i);
                // Add the cpu speeds to this UID.
                final int numClusters = mPowerProfile.getNumCpuClusters();
                if (u.mCpuClusterSpeedTimesUs == null ||
                        u.mCpuClusterSpeedTimesUs.length != numClusters) {
                    u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
                }

                for (int cluster = 0; cluster < clusterSpeedTimesMs.length; cluster++) {
                    final int speedsInCluster = clusterSpeedTimesMs[cluster].length;
                    if (u.mCpuClusterSpeedTimesUs[cluster] == null || speedsInCluster !=
                            u.mCpuClusterSpeedTimesUs[cluster].length) {
                        u.mCpuClusterSpeedTimesUs[cluster]
                                = new LongSamplingCounter[speedsInCluster];
                    }

                    final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeedTimesUs[cluster];
                    for (int speed = 0; speed < speedsInCluster; speed++) {
                        if (cpuSpeeds[speed] == null) {
                            cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
                        }
                        cpuSpeeds[speed].addCountLocked(appCpuTimeUs
                                * clusterSpeedTimesMs[cluster][speed]
                                / totalCpuClustersTimeMs);
                    }
                }
            }
        }
    }

        // Read the CPU data for each UID. This will internally generate a snapshot so next time
        // we read, we get a delta. If we are to distribute the cpu time, then do so. Otherwise
        // we just ignore the data.
    /**
     * Take a snapshot of the cpu times spent by each uid and update the corresponding counters.
     * If {@param partialTimers} is not null and empty, then we assign a portion of cpu times to
     * wakelock holders.
     *
     * @param partialTimers The wakelock holders among which the cpu times will be distributed.
     * @param updatedUids If not null, then the uids found in the snapshot will be added to this.
     */
    @VisibleForTesting
    public void readKernelUidCpuTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers,
            @Nullable SparseLongArray updatedUids) {
        mTempTotalCpuUserTimeUs = mTempTotalCpuSystemTimeUs = 0;
        final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
        final long startTimeMs = mClocks.uptimeMillis();
        mUserInfoProvider.refreshUserIds();
        mKernelUidCpuTimeReader.readDelta(!mOnBatteryInternal ? null :
                new KernelUidCpuTimeReader.Callback() {
                    @Override
                    public void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs) {

        mKernelUidCpuTimeReader.readDelta((uid, userTimeUs, systemTimeUs) -> {
            uid = mapUid(uid);
            if (Process.isIsolated(uid)) {
                            // This could happen if the isolated uid mapping was removed before
                            // that process was actually killed.
                // This could happen if the isolated uid mapping was removed before that process
                // was actually killed.
                mKernelUidCpuTimeReader.removeUid(uid);
                            Slog.d(TAG, "Got readings for an isolated uid with"
                                    + " no mapping to owning uid: " + uid);
                Slog.d(TAG, "Got readings for an isolated uid with no mapping: " + uid);
                return;
            }
            if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
@@ -10332,12 +10452,12 @@ public class BatteryStatsImpl extends BatteryStats {
                sb.append("\n");
            }

                        if (numWakelocksF > 0) {
            if (numWakelocks > 0) {
                // We have wakelocks being held, so only give a portion of the
                // time to the process. The rest will be distributed among wakelock
                // holders.
                            userTimeUs = (userTimeUs * wakelockWeight) / 100;
                            systemTimeUs = (systemTimeUs * wakelockWeight) / 100;
                userTimeUs = (userTimeUs * WAKE_LOCK_WEIGHT) / 100;
                systemTimeUs = (systemTimeUs * WAKE_LOCK_WEIGHT) / 100;
            }

            if (sb != null) {
@@ -10350,36 +10470,29 @@ public class BatteryStatsImpl extends BatteryStats {

            u.mUserCpuTime.addCountLocked(userTimeUs);
            u.mSystemCpuTime.addCountLocked(systemTimeUs);
            if (updatedUids != null) {
                updatedUids.put(u.getUid(), userTimeUs + systemTimeUs);
            }
        });

        if (updateCpuFreqData) {
            readKernelUidCpuFreqTimesLocked();
        final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
        if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
            Slog.d(TAG, "Reading cpu stats took " + elapsedTimeMs + "ms");
        }

        final long elapse = (mClocks.uptimeMillis() - startTimeMs);
        if (DEBUG_ENERGY_CPU || (elapse >= 100)) {
            Slog.d(TAG, "Reading cpu stats took " + elapse + " ms");
        }

        if (mOnBatteryInternal && numWakelocks > 0) {
        if (numWakelocks > 0) {
            // Distribute a portion of the total cpu time to wakelock holders.
            mTempTotalCpuUserTimeUs = (mTempTotalCpuUserTimeUs * (100 - wakelockWeight)) / 100;
            mTempTotalCpuUserTimeUs = (mTempTotalCpuUserTimeUs * (100 - WAKE_LOCK_WEIGHT)) / 100;
            mTempTotalCpuSystemTimeUs =
                    (mTempTotalCpuSystemTimeUs * (100 - wakelockWeight)) / 100;

            for (int i = 0; i < numPartialTimers; i++) {
                final StopwatchTimer timer = mPartialTimers.get(i);
                    (mTempTotalCpuSystemTimeUs * (100 - WAKE_LOCK_WEIGHT)) / 100;

                // The system does not share any blame, as it is usually holding the wakelock
                // on behalf of an app.
                if (timer.mInList && timer.mUid != null && timer.mUid.mUid != Process.SYSTEM_UID) {
                    int userTimeUs = (int) (mTempTotalCpuUserTimeUs / numWakelocks);
                    int systemTimeUs = (int) (mTempTotalCpuSystemTimeUs / numWakelocks);
            for (int i = 0; i < numWakelocks; ++i) {
                final StopwatchTimer timer = partialTimers.get(i);
                final int userTimeUs = (int) (mTempTotalCpuUserTimeUs / (numWakelocks - i));
                final int systemTimeUs = (int) (mTempTotalCpuSystemTimeUs / (numWakelocks - i));

                if (DEBUG_ENERGY_CPU) {
                        StringBuilder sb = new StringBuilder();
                    final StringBuilder sb = new StringBuilder();
                    sb.append("  Distributing wakelock uid=").append(timer.mUid.mUid)
                            .append(": u=");
                    TimeUtils.formatDuration(userTimeUs / 1000, sb);
@@ -10390,116 +10503,27 @@ public class BatteryStatsImpl extends BatteryStats {

                timer.mUid.mUserCpuTime.addCountLocked(userTimeUs);
                timer.mUid.mSystemCpuTime.addCountLocked(systemTimeUs);
                if (updatedUids != null) {
                    final int uid = timer.mUid.getUid();
                    updatedUids.put(uid, updatedUids.get(uid, 0) + userTimeUs + systemTimeUs);
                }

                final Uid.Proc proc = timer.mUid.getProcessStatsLocked("*wakelock*");
                proc.addCpuTimeLocked(userTimeUs / 1000, systemTimeUs / 1000);

                mTempTotalCpuUserTimeUs -= userTimeUs;
                mTempTotalCpuSystemTimeUs -= systemTimeUs;
                    numWakelocks--;
                }
            }

            if (mTempTotalCpuUserTimeUs > 0 || mTempTotalCpuSystemTimeUs > 0) {
                // Anything left over is given to the system.
                if (DEBUG_ENERGY_CPU) {
                    StringBuilder sb = new StringBuilder();
                    sb.append("  Distributing lost time to system: u=");
                    TimeUtils.formatDuration(mTempTotalCpuUserTimeUs / 1000, sb);
                    sb.append(" s=");
                    TimeUtils.formatDuration(mTempTotalCpuSystemTimeUs / 1000, sb);
                    Slog.d(TAG, sb.toString());
                }

                final Uid u = getUidStatsLocked(Process.SYSTEM_UID);
                u.mUserCpuTime.addCountLocked(mTempTotalCpuUserTimeUs);
                u.mSystemCpuTime.addCountLocked(mTempTotalCpuSystemTimeUs);
                updatedUids.put(Process.SYSTEM_UID, updatedUids.get(Process.SYSTEM_UID, 0)
                        + mTempTotalCpuUserTimeUs + mTempTotalCpuSystemTimeUs);

                final Uid.Proc proc = u.getProcessStatsLocked("*lost*");
                proc.addCpuTimeLocked((int) mTempTotalCpuUserTimeUs / 1000,
                        (int) mTempTotalCpuSystemTimeUs / 1000);
            }
        }

        long totalCpuClustersTimeMs = 0;
        // Read the time spent for each cluster at various cpu frequencies.
        final long[][] clusterSpeedTimesMs = new long[mKernelCpuSpeedReaders.length][];
        for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
            clusterSpeedTimesMs[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
            if (clusterSpeedTimesMs[cluster] != null) {
                for (int speed = clusterSpeedTimesMs[cluster].length - 1; speed >= 0; --speed) {
                    totalCpuClustersTimeMs += clusterSpeedTimesMs[cluster][speed];
                }
            }
        }
        if (totalCpuClustersTimeMs != 0) {
            // We have cpu times per freq aggregated over all uids but we need the times per uid.
            // So, we distribute total time spent by an uid to different cpu freqs based on the
            // amount of time cpu was running at that freq.
            final int updatedUidsCount = updatedUids.size();
            for (int i = 0; i < updatedUidsCount; ++i) {
                final Uid u = getUidStatsLocked(updatedUids.keyAt(i));
                final long appCpuTimeUs = updatedUids.valueAt(i);
                // Add the cpu speeds to this UID.
                final int numClusters = mPowerProfile.getNumCpuClusters();
                if (u.mCpuClusterSpeedTimesUs == null || u.mCpuClusterSpeedTimesUs.length !=
                        numClusters) {
                    u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
                }

                for (int cluster = 0; cluster < clusterSpeedTimesMs.length; cluster++) {
                    final int speedsInCluster = clusterSpeedTimesMs[cluster].length;
                    if (u.mCpuClusterSpeedTimesUs[cluster] == null || speedsInCluster !=
                            u.mCpuClusterSpeedTimesUs[cluster].length) {
                        u.mCpuClusterSpeedTimesUs[cluster]
                                = new LongSamplingCounter[speedsInCluster];
                    }

                    final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeedTimesUs[cluster];
                    for (int speed = 0; speed < speedsInCluster; speed++) {
                        if (cpuSpeeds[speed] == null) {
                            cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
                        }
                        cpuSpeeds[speed].addCountLocked(appCpuTimeUs
                                * clusterSpeedTimesMs[cluster][speed]
                                / totalCpuClustersTimeMs);
                    }
            }
        }
    }

        // See if there is a difference in wakelocks between this collection and the last
        // collection.
        if (ArrayUtils.referenceEquals(mPartialTimers, mLastPartialTimers)) {
            // No difference, so each timer is now considered for the next collection.
            for (int i = 0; i < numPartialTimers; i++) {
                mPartialTimers.get(i).mInList = true;
            }
        } else {
            // The lists are different, meaning we added (or removed a timer) since the last
            // collection.
            final int numLastPartialTimers = mLastPartialTimers.size();
            for (int i = 0; i < numLastPartialTimers; i++) {
                mLastPartialTimers.get(i).mInList = false;
            }
            mLastPartialTimers.clear();

            // Mark the current timers as gone through a collection.
            for (int i = 0; i < numPartialTimers; i++) {
                final StopwatchTimer timer = mPartialTimers.get(i);
                timer.mInList = true;
                mLastPartialTimers.add(timer);
            }
        }
    }

    void readKernelUidCpuFreqTimesLocked() {
        mKernelUidCpuFreqTimeReader.readDelta(!mOnBatteryInternal ? null :
                new KernelUidCpuFreqTimeReader.Callback() {
    /**
     * Take a snapshot of the cpu times spent by each uid in each freq and update the
     * corresponding counters.
     */
    @VisibleForTesting
    public void readKernelUidCpuFreqTimesLocked() {
        mKernelUidCpuFreqTimeReader.readDelta(new KernelUidCpuFreqTimeReader.Callback() {
            @Override
            public void onCpuFreqs(long[] cpuFreqs) {
                mCpuFreqs = cpuFreqs;
@@ -10510,12 +10534,11 @@ public class BatteryStatsImpl extends BatteryStats {
                uid = mapUid(uid);
                if (Process.isIsolated(uid)) {
                    mKernelUidCpuFreqTimeReader.removeUid(uid);
                            Slog.d(TAG, "Got freq readings for an isolated uid with"
                                    + " no mapping to owning uid: " + uid);
                    Slog.d(TAG, "Got freq readings for an isolated uid with no mapping: " + uid);
                    return;
                }
                if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
                            Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
                    Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
                    mKernelUidCpuFreqTimeReader.removeUid(uid);
                    return;
                }
+774 −0

File added.

Preview size limit exceeded, changes collapsed.

+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import org.junit.runners.Suite;

@RunWith(Suite.class)
@Suite.SuiteClasses({
        BatteryStatsCpuTimesTest.class,
        BatteryStatsBackgroundStatsTest.class,
        BatteryStatsCounterTest.class,
        BatteryStatsDualTimerTest.class,
+42 −0

File changed.

Preview size limit exceeded, changes collapsed.