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

Commit 2384df75 authored by Ben Murdoch's avatar Ben Murdoch
Browse files

Add sample rate for app compaction metrics.

Introduce a parameter for simple sampling of app compaction
metrics as they go into statsd. Rate is confirgurable with
DeviceConfig.

Test: atest FrameworksServicesTests:AppCompactorTest
Bug: 123574961

Change-Id: Iebfd815dc4f7c6db7b849259d8b0b031e7de8621
parent aaf9b888
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -5832,6 +5832,7 @@ package android.provider {
  public static interface DeviceConfig.ActivityManager {
    field public static final String KEY_COMPACT_ACTION_1 = "compact_action_1";
    field public static final String KEY_COMPACT_ACTION_2 = "compact_action_2";
    field public static final String KEY_COMPACT_STATSD_SAMPLE_RATE = "compact_statsd_sample_rate";
    field public static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
    field public static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
    field public static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
+1 −0
Original line number Diff line number Diff line
@@ -250,6 +250,7 @@ public final class DeviceConfig {
        String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
        String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
        String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
        String KEY_COMPACT_STATSD_SAMPLE_RATE = "compact_statsd_sample_rate";

        /**
         * Maximum number of cached processes. See
+42 −12
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.am;
import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_1;
import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_2;
import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_STATSD_SAMPLE_RATE;
import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_1;
import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_2;
import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_3;
@@ -45,6 +46,7 @@ import com.android.server.ServiceThread;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Random;

public final class AppCompactor {

@@ -65,6 +67,8 @@ public final class AppCompactor {
    @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
    @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
    @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000;
    // The sampling rate to push app compaction events into statsd for upload.
    @VisibleForTesting static final float DEFAULT_STATSD_SAMPLE_RATE = 0.1f;

    @VisibleForTesting
    interface PropertyChangedCallbackForTest {
@@ -104,6 +108,8 @@ public final class AppCompactor {
                                || KEY_COMPACT_THROTTLE_3.equals(name)
                                || KEY_COMPACT_THROTTLE_4.equals(name)) {
                            updateCompactionThrottles();
                        } else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) {
                            updateStatsdSampleRate();
                        }
                    }
                    if (mTestCallback != null) {
@@ -116,21 +122,25 @@ public final class AppCompactor {

    // Configured by phenotype. Updates from the server take effect immediately.
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting String mCompactActionSome =
    @VisibleForTesting volatile String mCompactActionSome =
            compactActionIntToString(DEFAULT_COMPACT_ACTION_1);
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting String mCompactActionFull =
    @VisibleForTesting volatile String mCompactActionFull =
            compactActionIntToString(DEFAULT_COMPACT_ACTION_2);
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
    @VisibleForTesting volatile long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting long mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
    @VisibleForTesting volatile long mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting long mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
    @VisibleForTesting volatile long mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
    @VisibleForTesting volatile long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
    @GuardedBy("mPhenotypeFlagLock")
    private boolean mUseCompaction = DEFAULT_USE_COMPACTION;
    private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION;

    private final Random mRandom = new Random();
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting volatile float mStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;

    // Handler on which compaction runs.
    private Handler mCompactionHandler;
@@ -158,6 +168,7 @@ public final class AppCompactor {
            updateUseCompaction();
            updateCompactionActions();
            updateCompactionThrottles();
            updateStatsdSampleRate();
        }
    }

@@ -181,6 +192,7 @@ public final class AppCompactor {
            pw.println("  " + KEY_COMPACT_THROTTLE_2 + "=" + mCompactThrottleSomeFull);
            pw.println("  " + KEY_COMPACT_THROTTLE_3 + "=" + mCompactThrottleFullSome);
            pw.println("  " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull);
            pw.println("  " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mStatsdSampleRate);
        }
    }

@@ -289,6 +301,19 @@ public final class AppCompactor {
        }
    }

    @GuardedBy("mPhenotypeFlagLock")
    private void updateStatsdSampleRate() {
        String sampleRateFlag = DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE,
                KEY_COMPACT_STATSD_SAMPLE_RATE);
        try {
            mStatsdSampleRate = TextUtils.isEmpty(sampleRateFlag)
                    ? DEFAULT_STATSD_SAMPLE_RATE : Float.parseFloat(sampleRateFlag);
        } catch (NumberFormatException e) {
            mStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
        }
        mStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mStatsdSampleRate));
    }

    @VisibleForTesting
    static String compactActionIntToString(int action) {
        switch(action) {
@@ -385,11 +410,16 @@ public final class AppCompactor {
                                rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
                                rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
                                lastCompactAction, lastCompactTime, msg.arg1, msg.arg2);
                        // Note that as above not taking mPhenoTypeFlagLock here to avoid locking
                        // on every single compaction for a flag that will seldom change and the
                        // impact of reading the wrong value here is low.
                        if (mRandom.nextFloat() < mStatsdSampleRate) {
                            StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
                                    rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
                                    rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
                                    lastCompactAction, lastCompactTime, msg.arg1,
                                    ActivityManager.processStateAmToProto(msg.arg2));
                        }
                        synchronized (mAm) {
                            proc.lastCompactTime = end;
                            proc.lastCompactAction = pendingAction;
+77 −7
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.am;

import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_1;
import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_2;
import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_STATSD_SAMPLE_RATE;
import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_1;
import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_2;
import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_3;
@@ -61,6 +62,9 @@ import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
public final class AppCompactorTest {

    private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
            "device_config delete activity_manager";

    @Mock private AppOpsService mAppOpsService;
    private AppCompactor mCompactorUnderTest;
    private HandlerThread mHandlerThread;
@@ -70,19 +74,21 @@ public final class AppCompactorTest {
    private static void clearDeviceConfig() throws IOException  {
        UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
        uiDevice.executeShellCommand(
                "device_config delete activity_manager " + KEY_USE_COMPACTION);
                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_USE_COMPACTION);
        uiDevice.executeShellCommand(
                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_ACTION_1);
        uiDevice.executeShellCommand(
                "device_config delete activity_manager " + KEY_COMPACT_ACTION_1);
                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_ACTION_2);
        uiDevice.executeShellCommand(
                "device_config delete activity_manager " + KEY_COMPACT_ACTION_2);
                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_1);
        uiDevice.executeShellCommand(
                "device_config delete activity_manager " + KEY_COMPACT_THROTTLE_1);
                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_2);
        uiDevice.executeShellCommand(
                "device_config delete activity_manager " + KEY_COMPACT_THROTTLE_2);
                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_3);
        uiDevice.executeShellCommand(
                "device_config delete activity_manager " + KEY_COMPACT_THROTTLE_3);
                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_4);
        uiDevice.executeShellCommand(
                "device_config delete activity_manager " + KEY_COMPACT_THROTTLE_4);
                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_STATSD_SAMPLE_RATE);
    }

    @Before
@@ -128,6 +134,8 @@ public final class AppCompactorTest {
                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
        assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
        assertThat(mCompactorUnderTest.mStatsdSampleRate,
                is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE));
    }

    @Test
@@ -155,6 +163,9 @@ public final class AppCompactorTest {
        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
                KEY_COMPACT_THROTTLE_4,
                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
                KEY_COMPACT_STATSD_SAMPLE_RATE,
                Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);

        // Then calling init will read and set that flag.
        mCompactorUnderTest.init();
@@ -173,6 +184,8 @@ public final class AppCompactorTest {
                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1));
        assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1));
        assertThat(mCompactorUnderTest.mStatsdSampleRate,
                is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f));
    }

    @Test
@@ -365,6 +378,63 @@ public final class AppCompactorTest {
                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
    }

    @Test
    public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException {
        mCompactorUnderTest.init();

        // When we override mStatsdSampleRate with a reasonable values ...
        mCountDown = new CountDownLatch(1);
        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
                KEY_COMPACT_STATSD_SAMPLE_RATE,
                Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));

        // Then that override is reflected in the compactor.
        assertThat(mCompactorUnderTest.mStatsdSampleRate,
                is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f));
    }

    @Test
    public void statsdSanokeRate_listensToDeviceConfigChangesBadValues()
            throws InterruptedException {
        mCompactorUnderTest.init();

        // When we override mStatsdSampleRate with a reasonable values ...
        mCountDown = new CountDownLatch(1);
        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
                KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false);
        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));

        // Then that override is reflected in the compactor.
        assertThat(mCompactorUnderTest.mStatsdSampleRate,
                is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE));
    }

    @Test
    public void statsdSanokeRate_listensToDeviceConfigChangesOutOfRangeValues()
            throws InterruptedException {
        mCompactorUnderTest.init();

        // When we override mStatsdSampleRate with an value outside of [0..1]...
        mCountDown = new CountDownLatch(1);
        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
                KEY_COMPACT_STATSD_SAMPLE_RATE,
                Float.toString(-1.0f), false);
        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));

        // Then the values is capped in the range.
        assertThat(mCompactorUnderTest.mStatsdSampleRate, is(0.0f));

        mCountDown = new CountDownLatch(1);
        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
                KEY_COMPACT_STATSD_SAMPLE_RATE,
                Float.toString(1.01f), false);
        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));

        // Then the values is capped in the range.
        assertThat(mCompactorUnderTest.mStatsdSampleRate, is(1.0f));
    }

    private class TestInjector extends Injector {
        @Override
        public AppOpsService getAppOpsService(File file, Handler handler) {