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

Commit e6e723d5 authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

Stop tracking user specific uid cpu time on user stop

Telling kernel module uid_cputime to stop accounting time for uids
belonging to a stopped user. Upon user remove, removing uid states for
those uids.

Test: adb shell am instrument -e class \
com.android.internal.os.BatteryStatsUserLifecycleTests -w \
com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner

adb shell am instrument -e class android.util.SparseLongArrayTest -w \
com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner

Bug: 62390461
Change-Id: Ibe07778465e3d2c13679e4d88d32f7b92fa959d9
parent f43346c1
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -118,6 +118,23 @@ public class SparseLongArray implements Cloneable {
        }
    }

    /**
     * @hide
     * Remove a range of mappings as a batch.
     *
     * @param index Index to begin at
     * @param size Number of mappings to remove
     *
     * <p>For indices outside of the range <code>0...size()-1</code>,
     * the behavior is undefined.</p>
     */
    public void removeAtRange(int index, int size) {
        size = Math.min(size, mSize - index);
        System.arraycopy(mKeys, index + size, mKeys, index, mSize - (index + size));
        System.arraycopy(mValues, index + size, mValues, index, mSize - (index + size));
        mSize -= size;
    }

    /**
     * Removes the mapping at the given index.
     */
+50 −10
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.WorkSource;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
@@ -75,7 +75,7 @@ import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
import com.android.server.NetworkManagementSocketTagger;

import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -194,6 +194,17 @@ public class BatteryStatsImpl extends BatteryStats {
        public String getSubsystemLowPowerStats();
    }

    public static abstract class UserInfoProvider {
        private int[] userIds;
        protected abstract @Nullable int[] getUserIds();
        private final void refreshUserIds() {
            userIds = getUserIds();
        }
        private final boolean exists(int userId) {
            return userIds != null ? ArrayUtils.contains(userIds, userId) : true;
        }
    }

    private final PlatformIdleStateCallback mPlatformIdleStateCallback;

    final class MyHandler extends Handler {
@@ -262,6 +273,7 @@ public class BatteryStatsImpl extends BatteryStats {

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

    private BatteryCallback mCallback;

@@ -649,6 +661,7 @@ public class BatteryStatsImpl extends BatteryStats {
        mDailyFile = null;
        mHandler = null;
        mPlatformIdleStateCallback = null;
        mUserInfoProvider = null;
        clearHistoryLocked();
    }

@@ -8588,16 +8601,14 @@ public class BatteryStatsImpl extends BatteryStats {
        return mCpuFreqs;
    }

    public BatteryStatsImpl(File systemDir, Handler handler) {
        this(new SystemClocks(), systemDir, handler, null);
    public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb,
            UserInfoProvider userInfoProvider) {
        this(new SystemClocks(), systemDir, handler, cb, userInfoProvider);
    }

    public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb) {
        this(new SystemClocks(), systemDir, handler, cb);
    }

    public BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler,
            PlatformIdleStateCallback cb) {
    private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler,
            PlatformIdleStateCallback cb,
            UserInfoProvider userInfoProvider) {
        init(clocks);

        if (systemDir != null) {
@@ -8684,6 +8695,7 @@ public class BatteryStatsImpl extends BatteryStats {
        clearHistoryLocked();
        updateDailyDeadlineLocked();
        mPlatformIdleStateCallback = cb;
        mUserInfoProvider = userInfoProvider;
    }

    public BatteryStatsImpl(Parcel p) {
@@ -10172,6 +10184,7 @@ public class BatteryStatsImpl extends BatteryStats {
        // we read, we get a delta. If we are to distribute the cpu time, then do so. Otherwise
        // we just ignore the data.
        final long startTimeMs = mClocks.uptimeMillis();
        mUserInfoProvider.refreshUserIds();
        mKernelUidCpuTimeReader.readDelta(!mOnBatteryInternal ? null :
                new KernelUidCpuTimeReader.Callback() {
                    @Override
@@ -10185,6 +10198,11 @@ public class BatteryStatsImpl extends BatteryStats {
                                    + " no mapping to owning uid: " + uid);
                            return;
                        }
                        if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
                            Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
                            mKernelUidCpuTimeReader.removeUid(uid);
                            return;
                        }
                        final Uid u = getUidStatsLocked(uid);

                        // Accumulate the total system and user time.
@@ -10357,6 +10375,11 @@ public class BatteryStatsImpl extends BatteryStats {
                                    + " no mapping to owning uid: " + uid);
                            return;
                        }
                        if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
                            Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
                            mKernelUidCpuFreqTimeReader.removeUid(uid);
                            return;
                        }
                        final Uid u = getUidStatsLocked(uid);
                        if (u.mCpuFreqTimeMs == null) {
                            u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase);
@@ -11018,6 +11041,23 @@ public class BatteryStatsImpl extends BatteryStats {
        return u;
    }

    public void onCleanupUserLocked(int userId) {
        final int firstUidForUser = UserHandle.getUid(userId, 0);
        final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1);
        mKernelUidCpuFreqTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser);
        mKernelUidCpuTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser);
    }

    public void onUserRemovedLocked(int userId) {
        final int firstUidForUser = UserHandle.getUid(userId, 0);
        final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1);
        mUidStats.put(firstUidForUser, null);
        mUidStats.put(lastUidForUser, null);
        final int firstIndex = mUidStats.indexOfKey(firstUidForUser);
        final int lastIndex = mUidStats.indexOfKey(lastUidForUser);
        mUidStats.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
    }

    /**
     * Remove the statistics object for a particular uid.
     */
+11 −0
Original line number Diff line number Diff line
@@ -76,6 +76,17 @@ public class KernelUidCpuFreqTimeReader {
        mLastUidCpuFreqTimeMs.delete(uid);
    }

    public void removeUidsInRange(int startUid, int endUid) {
        if (endUid < startUid) {
            return;
        }
        mLastUidCpuFreqTimeMs.put(startUid, null);
        mLastUidCpuFreqTimeMs.put(endUid, null);
        final int firstIndex = mLastUidCpuFreqTimeMs.indexOfKey(startUid);
        final int lastIndex = mLastUidCpuFreqTimeMs.indexOfKey(endUid);
        mLastUidCpuFreqTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
    }

    @VisibleForTesting
    public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException {
        String line = reader.readLine();
+29 −4
Original line number Diff line number Diff line
@@ -130,17 +130,42 @@ public class KernelUidCpuTimeReader {
     * @param uid The UID to remove.
     */
    public void removeUid(int uid) {
        int index = mLastUserTimeUs.indexOfKey(uid);
        final int index = mLastSystemTimeUs.indexOfKey(uid);
        if (index >= 0) {
            mLastUserTimeUs.removeAt(index);
            mLastSystemTimeUs.removeAt(index);
            mLastUserTimeUs.removeAt(index);
        }
        removeUidsFromKernelModule(uid, uid);
    }

    /**
     * Removes UIDs in a given range from the kernel module and internal accounting data.
     * @param startUid the first uid to remove
     * @param endUid the last uid to remove
     */
    public void removeUidsInRange(int startUid, int endUid) {
        if (endUid < startUid) {
            return;
        }
        mLastSystemTimeUs.put(startUid, 0);
        mLastUserTimeUs.put(startUid, 0);
        mLastSystemTimeUs.put(endUid, 0);
        mLastUserTimeUs.put(endUid, 0);
        final int startIndex = mLastSystemTimeUs.indexOfKey(startUid);
        final int endIndex = mLastSystemTimeUs.indexOfKey(endUid);
        mLastSystemTimeUs.removeAtRange(startIndex, endIndex - startIndex + 1);
        mLastUserTimeUs.removeAtRange(startIndex, endIndex - startIndex + 1);
        removeUidsFromKernelModule(startUid, endUid);
    }

    private void removeUidsFromKernelModule(int startUid, int endUid) {
        Slog.d(TAG, "Removing uids " + startUid + "-" + endUid);
        try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) {
            writer.write(Integer.toString(uid) + "-" + Integer.toString(uid));
            writer.write(startUid + "-" + endUid);
            writer.flush();
        } catch (IOException e) {
            Slog.e(TAG, "failed to remove uid from uid_cputime module", e);
            Slog.e(TAG, "failed to remove uids " + startUid + " - " + endUid
                    + " from uid_cputime module", e);
        }
    }
}
+156 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 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 android.util;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import android.support.annotation.NonNull;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.Random;

/**
 * Internal tests for {@link SparseLongArray}.
 */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SparseLongArrayTest {

    private static final int TEST_SIZE = 1000;

    private SparseLongArray mSparseLongArray;
    private int[] mKeys;
    private long[] mValues;
    private Random mRandom;

    private static boolean isSame(@NonNull SparseLongArray array1,
            @NonNull SparseLongArray array2) {
        if (array1.size() != array2.size()) {
            return false;
        }
        for (int i = 0; i < array1.size(); i++) {
            if (array1.keyAt(i) != array2.keyAt(i) || array1.valueAt(i) != array2.valueAt(i)) {
                return false;
            }
        }
        return true;
    }

    private void assertRemoved(int startIndex, int endIndex) {
        for (int i = 0; i < TEST_SIZE; i++) {
            if (i >= startIndex && i <= endIndex) {
                assertEquals("Entry not removed", Long.MIN_VALUE,
                        mSparseLongArray.get(mKeys[i], Long.MIN_VALUE));
            } else {
                assertEquals("Untouched entry corrupted", mValues[i],
                        mSparseLongArray.get(mKeys[i]));
            }
        }
    }

    /**
     * Generates a sorted array of distinct and random keys
     *
     * @param size the number of keys to return in the array. Should be < (2^31)/1000.
     * @return the array of keys
     */
    private int[] generateRandomKeys(int size) {
        final int[] keys = new int[size];
        keys[0] = -1 * mRandom.nextInt(size * 500);
        for (int i = 1; i < size; i++) {
            keys[i] = keys[i - 1] + 1 + mRandom.nextInt(1000);
            assertTrue(keys[i] > keys[i - 1]);
        }
        return keys;
    }

    @Before
    public void setUp() {
        mSparseLongArray = new SparseLongArray();
        mRandom = new Random(12345);
        mKeys = generateRandomKeys(TEST_SIZE);
        mValues = new long[TEST_SIZE];
        for (int i = 0; i < TEST_SIZE; i++) {
            mValues[i] = i + 1;
            mSparseLongArray.put(mKeys[i], mValues[i]);
        }
    }

    @Test
    public void testRemoveAtRange_removeHead() {
        mSparseLongArray.removeAtRange(0, 100);
        assertEquals(TEST_SIZE - 100, mSparseLongArray.size());
        assertRemoved(0, 99);
    }

    @Test
    public void testRemoveAtRange_removeTail() {
        mSparseLongArray.removeAtRange(TEST_SIZE - 200, 200);
        assertEquals(TEST_SIZE - 200, mSparseLongArray.size());
        assertRemoved(TEST_SIZE - 200, TEST_SIZE - 1);
    }

    @Test
    public void testRemoveAtRange_removeOverflow() {
        mSparseLongArray.removeAtRange(TEST_SIZE - 100, 200);
        assertEquals(TEST_SIZE - 100, mSparseLongArray.size());
        assertRemoved(TEST_SIZE - 100, TEST_SIZE - 1);
    }

    @Test
    public void testRemoveAtRange_removeEverything() {
        mSparseLongArray.removeAtRange(0, TEST_SIZE);
        assertEquals(0, mSparseLongArray.size());
        assertRemoved(0, TEST_SIZE - 1);
    }

    @Test
    public void testRemoveAtRange_removeMiddle() {
        mSparseLongArray.removeAtRange(200, 200);
        assertEquals(TEST_SIZE - 200, mSparseLongArray.size());
        assertRemoved(200, 399);
    }

    @Test
    public void testRemoveAtRange_removeSingle() {
        mSparseLongArray.removeAtRange(300, 1);
        assertEquals(TEST_SIZE - 1, mSparseLongArray.size());
        assertRemoved(300, 300);
    }

    @Test
    public void testRemoveAtRange_compareRemoveAt() {
        final SparseLongArray sparseLongArray2 = mSparseLongArray.clone();
        assertTrue(isSame(mSparseLongArray, sparseLongArray2));

        final int startIndex = 101;
        final int endIndex = 200;
        mSparseLongArray.removeAtRange(startIndex, endIndex - startIndex + 1);
        for (int i = endIndex; i >= startIndex; i--) {
            sparseLongArray2.removeAt(i);
        }
        assertEquals(TEST_SIZE - (endIndex - startIndex + 1), mSparseLongArray.size());
        assertRemoved(startIndex, endIndex);
        assertTrue(isSame(sparseLongArray2, mSparseLongArray));
    }
}
Loading