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

Commit 54cf10a8 authored by Parth Sane's avatar Parth Sane
Browse files

Store system properties when native binder stat config is set

Test: Manually test out change in sysprop
Bug: 299356196
Flag: build.RELEASE_LIBBINDER_BINDER_OBSERVER

Change-Id: Ib0d99e37758cd8e53d5393f32b51d29aa3274873
parent a3ea108e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -398,6 +398,7 @@ java_defaults {
        "bouncycastle-repackaged-unbundled",
        "com.android.sysprop.foldlockbehavior",
        "com.android.sysprop.view",
        "com.android.sysprop.nativebinderstats",
        "framework-internal-utils",
        "dropboxmanager_aidl-java",
        "dynamic_instrumentation_manager_aidl-java",
+63 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.provider.Settings;
import android.sysprop.NativeBinderStatsProperties;
import android.util.KeyValueListParser;
import android.util.Slog;

@@ -74,10 +75,18 @@ public class NativeBinderStats {

    private final Context mContext;

    private final PropertiesWrapper mPropertiesWrapper;

    private final SettingsObserver mSettingsObserver;

    public NativeBinderStats(Context context) {
        this(context, new PropertiesWrapper());
    }

    @VisibleForTesting
    public NativeBinderStats(Context context, PropertiesWrapper propertiesWrapper) {
        mContext = context;
        mPropertiesWrapper = propertiesWrapper;
        mSettingsObserver = new SettingsObserver(context);
    }

@@ -86,6 +95,51 @@ public class NativeBinderStats {
        mSettingsObserver.register();
    }

    /**
     * A wrapper around System properties for testability.
     */
    @VisibleForTesting
    public static class PropertiesWrapper {
        /** Sets the enabled property. */
        public void setEnabled(boolean value) {
            NativeBinderStatsProperties.enabled(value);
        }

        /** Sets the process_sharding property. */
        public void setProcessSharding(int value) {
            NativeBinderStatsProperties.process_sharding(value);
        }

        /**
         * Sets the spam_sharding property.
         */
        public void setSpamSharding(int value) {
            NativeBinderStatsProperties.spam_sharding(value);
        }

        /**
         * Sets the call_sharding property.
         */
        public void setCallSharding(int value) {
            NativeBinderStatsProperties.call_sharding(value);
        }

        /** Like {@link #setProcessSharding} but for system_server. */
        public void setSystemProcessSharding(int value) {
            NativeBinderStatsProperties.system_process_sharding(value);
        }

        /** Like {@link #setSpamSharding} but for system_server. */
        public void setSystemSpamSharding(int value) {
            NativeBinderStatsProperties.system_spam_sharding(value);
        }

        /** Like {@link #setCallSharding} but for system_server. */
        public void setSystemCallSharding(int value) {
            NativeBinderStatsProperties.system_call_sharding(value);
        }
    }

    @VisibleForTesting
    public class SettingsObserver extends ContentObserver {
        private static final String KEY_ENABLED = "enabled";
@@ -138,7 +192,15 @@ public class NativeBinderStats {
                    mParser.getInt(KEY_SYSTEM_SPAM_SHARDING, DEFAULT_SYSTEM_SPAM_SHARDING);
            mSystemCallSharding =
                    mParser.getInt(KEY_SYSTEM_CALL_SHARDING, DEFAULT_SYSTEM_CALL_SHARDING);
            // TODO(b/407694522): If settings change, propagate to other processes.

            mPropertiesWrapper.setEnabled(mEnabled);
            mPropertiesWrapper.setProcessSharding(mProcessSharding);
            mPropertiesWrapper.setSpamSharding(mSpamSharding);
            mPropertiesWrapper.setCallSharding(mCallSharding);
            mPropertiesWrapper.setSystemProcessSharding(mSystemProcessSharding);
            mPropertiesWrapper.setSystemSpamSharding(mSystemSpamSharding);
            mPropertiesWrapper.setSystemCallSharding(mSystemCallSharding);

            Slog.i(
                    TAG,
                    String.format(
+7 −0
Original line number Diff line number Diff line
@@ -50,3 +50,10 @@ sysprop_library {
    property_owner: "Platform",
    api_packages: ["android.sysprop"],
}

sysprop_library {
    name: "com.android.sysprop.nativebinderstats",
    srcs: ["NativeBinderStatsProperties.sysprop"],
    property_owner: "Platform",
    api_packages: ["android.sysprop"],
}
+80 −0
Original line number Diff line number Diff line
# This file is used to define system properties for native binder stats.
# The properties are used to control the collection of binder stats.
# The properties are set by the NativeBinderStats class.

# The module that owns this property.
module: "android.sysprop.NativeBinderStatsProperties"

# Whether native binder stats are enabled.
prop {
    api_name: "enabled"
    type: Boolean
    prop_name: "persist.sys.native_binder_stats.enabled"
    scope: Internal
    access: ReadWrite
}

# The inverse probability that a given process will track binder stats. E.g. 100 means that
# 1% of the processes will report stats. 0 is a special value that means no process will
# report stats.
prop {
    api_name: "process_sharding"
    type: Integer
    prop_name: "persist.sys.native_binder_stats.process_sharding"
    scope: Internal
    access: ReadWrite
}

# The inverse probability that a given AIDL method will be selected for spam detection and
# reporting (provided the containing process is selected for stats). 0 means no spam
# tracking.
prop {
    api_name: "spam_sharding"
    type: Integer
    prop_name: "persist.sys.native_binder_stats.spam_sharding"
    scope: Internal
    access: ReadWrite
}

# The inverse probability that a given AIDL method will be selected for call stats
# (provided the containing process is selected for stats). 0 means no call stats.
prop {
    api_name: "call_sharding"
    type: Integer
    prop_name: "persist.sys.native_binder_stats.call_sharding"
    scope: Internal
    access: ReadWrite
}

# The inverse probability that a given process will track binder stats. E.g. 100 means that
# 1% of the processes will report stats. 0 is a special value that means no process will
# report stats. This property is for system_server.
prop {
    api_name: "system_process_sharding"
    type: Integer
    prop_name: "persist.sys.native_binder_stats.system_process_sharding"
    scope: Internal
    access: ReadWrite
}

# The inverse probability that a given AIDL method will be selected for spam detection and
# reporting (provided the containing process is selected for stats). 0 means no spam
# tracking. This property is for system_server.
prop {
    api_name: "system_spam_sharding"
    type: Integer
    prop_name: "persist.sys.native_binder_stats.system_spam_sharding"
    scope: Internal
    access: ReadWrite
}

# The inverse probability that a given AIDL method will be selected for call stats
# (provided the containing process is selected for stats). 0 means no call stats.
# This property is for system_server.
prop {
    api_name: "system_call_sharding"
    type: Integer
    prop_name: "persist.sys.native_binder_stats.system_call_sharding"
    scope: Internal
    access: ReadWrite
}
+135 −5
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.internal.os;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

@@ -38,6 +39,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;

@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -46,6 +48,7 @@ import org.junit.runner.RunWith;
public class NativeBinderStatsTest {
    private Context mContext;
    private MockContentResolver mResolver;
    private NativeBinderStats.PropertiesWrapper mMockPropertiesWrapper;

    @Before
    public void setup() {
@@ -53,6 +56,7 @@ public class NativeBinderStatsTest {
        mContext = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
        mResolver = new MockContentResolver(mContext);
        mResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
        mMockPropertiesWrapper = mock(NativeBinderStats.PropertiesWrapper.class);
        when(mContext.getContentResolver()).thenReturn(mResolver);
    }

@@ -63,16 +67,36 @@ public class NativeBinderStatsTest {

    @Test
    public void testSettingsObserver_disabledByDefault() {
        NativeBinderStats nativeBinderStats = new NativeBinderStats(mContext);
        NativeBinderStats nativeBinderStats =
                new NativeBinderStats(mContext, mMockPropertiesWrapper);
        nativeBinderStats.systemReady();

        // Verify that the injector was called with default values
        Mockito.verify(mMockPropertiesWrapper).setEnabled(NativeBinderStats.DEFAULT_ENABLED);
        Mockito.verify(mMockPropertiesWrapper)
                .setProcessSharding(NativeBinderStats.DEFAULT_PROCESS_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSpamSharding(NativeBinderStats.DEFAULT_SPAM_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setCallSharding(NativeBinderStats.DEFAULT_CALL_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemProcessSharding(NativeBinderStats.DEFAULT_SYSTEM_PROCESS_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemSpamSharding(NativeBinderStats.DEFAULT_SYSTEM_SPAM_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemCallSharding(NativeBinderStats.DEFAULT_SYSTEM_CALL_SHARDING);

        assertThat(nativeBinderStats.mEnabled).isFalse();
    }

    @Test
    public void testSettingsObserver_enabled() {
        Settings.Global.putString(mResolver, Settings.Global.NATIVE_BINDER_STATS, "enabled=true");
        NativeBinderStats nativeBinderStats = new NativeBinderStats(mContext);
        // Reset mock to clear previous interactions from setup (if any)
        Mockito.reset(mMockPropertiesWrapper);

        NativeBinderStats nativeBinderStats =
                new NativeBinderStats(mContext, mMockPropertiesWrapper);
        nativeBinderStats.systemReady();

        assertThat(nativeBinderStats.mEnabled).isTrue();
@@ -88,24 +112,58 @@ public class NativeBinderStatsTest {
                .isEqualTo(NativeBinderStats.DEFAULT_SYSTEM_SPAM_SHARDING);
        assertThat(nativeBinderStats.mSystemCallSharding)
                .isEqualTo(NativeBinderStats.DEFAULT_SYSTEM_CALL_SHARDING);
        // Verify that the injector was called correctly
        Mockito.verify(mMockPropertiesWrapper).setEnabled(true);
        Mockito.verify(mMockPropertiesWrapper)
                .setProcessSharding(NativeBinderStats.DEFAULT_PROCESS_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSpamSharding(NativeBinderStats.DEFAULT_SPAM_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setCallSharding(NativeBinderStats.DEFAULT_CALL_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemProcessSharding(NativeBinderStats.DEFAULT_SYSTEM_PROCESS_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemSpamSharding(NativeBinderStats.DEFAULT_SYSTEM_SPAM_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemCallSharding(NativeBinderStats.DEFAULT_SYSTEM_CALL_SHARDING);
    }

    @Test
    public void testSettingsObserver_disabled() {
        Settings.Global.putString(mResolver, Settings.Global.NATIVE_BINDER_STATS, "enabled=false");
        NativeBinderStats nativeBinderStats = new NativeBinderStats(mContext);
        NativeBinderStats nativeBinderStats =
                new NativeBinderStats(mContext, mMockPropertiesWrapper);
        nativeBinderStats.systemReady();

        assertThat(nativeBinderStats.mEnabled).isFalse();
        // Verify that the injector was called correctly
        Mockito.verify(mMockPropertiesWrapper).setEnabled(false);
    }

    @Test
    public void testSettingsObserver_changed() {
        Settings.Global.putString(mResolver, Settings.Global.NATIVE_BINDER_STATS, "enabled=true");
        NativeBinderStats nativeBinderStats = new NativeBinderStats(mContext);
        NativeBinderStats nativeBinderStats =
                new NativeBinderStats(mContext, mMockPropertiesWrapper);
        nativeBinderStats.systemReady();

        assertThat(nativeBinderStats.mEnabled).isTrue();
        Mockito.verify(mMockPropertiesWrapper).setEnabled(true);
        Mockito.verify(mMockPropertiesWrapper)
                .setProcessSharding(NativeBinderStats.DEFAULT_PROCESS_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSpamSharding(NativeBinderStats.DEFAULT_SPAM_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setCallSharding(NativeBinderStats.DEFAULT_CALL_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemProcessSharding(NativeBinderStats.DEFAULT_SYSTEM_PROCESS_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemSpamSharding(NativeBinderStats.DEFAULT_SYSTEM_SPAM_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemCallSharding(NativeBinderStats.DEFAULT_SYSTEM_CALL_SHARDING);

        // Reset mock to clear previous interactions
        Mockito.reset(mMockPropertiesWrapper);

        Settings.Global.putString(mResolver, Settings.Global.NATIVE_BINDER_STATS, "enabled=false");
        // FakeSettingsProvider doesn't support notifications, so we need to notify manually.
@@ -115,6 +173,19 @@ public class NativeBinderStatsTest {
                false, Settings.Global.getUriFor(Settings.Global.NATIVE_BINDER_STATS), 0);

        assertThat(nativeBinderStats.mEnabled).isFalse();
        Mockito.verify(mMockPropertiesWrapper).setEnabled(false);
        Mockito.verify(mMockPropertiesWrapper)
                .setProcessSharding(NativeBinderStats.DEFAULT_PROCESS_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSpamSharding(NativeBinderStats.DEFAULT_SPAM_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setCallSharding(NativeBinderStats.DEFAULT_CALL_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemProcessSharding(NativeBinderStats.DEFAULT_SYSTEM_PROCESS_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemSpamSharding(NativeBinderStats.DEFAULT_SYSTEM_SPAM_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemCallSharding(NativeBinderStats.DEFAULT_SYSTEM_CALL_SHARDING);
    }

    @Test
@@ -124,7 +195,8 @@ public class NativeBinderStatsTest {
                Settings.Global.NATIVE_BINDER_STATS,
                "enabled=true,process_sharding=5,spam_sharding=1,call_sharding=2,"
                    + "system_process_sharding=1,system_spam_sharding=5,system_call_sharding=10");
        NativeBinderStats nativeBinderStats = new NativeBinderStats(mContext);
        NativeBinderStats nativeBinderStats =
                new NativeBinderStats(mContext, mMockPropertiesWrapper);
        nativeBinderStats.systemReady();

        assertThat(nativeBinderStats.mEnabled).isTrue();
@@ -134,5 +206,63 @@ public class NativeBinderStatsTest {
        assertThat(nativeBinderStats.mSystemProcessSharding).isEqualTo(1);
        assertThat(nativeBinderStats.mSystemSpamSharding).isEqualTo(5);
        assertThat(nativeBinderStats.mSystemCallSharding).isEqualTo(10);
        // Verify that the injector was called with required values
        Mockito.verify(mMockPropertiesWrapper).setEnabled(true);
        Mockito.verify(mMockPropertiesWrapper).setProcessSharding(5);
        Mockito.verify(mMockPropertiesWrapper).setSpamSharding(1);
        Mockito.verify(mMockPropertiesWrapper).setCallSharding(2);
        Mockito.verify(mMockPropertiesWrapper).setSystemProcessSharding(1);
        Mockito.verify(mMockPropertiesWrapper).setSystemSpamSharding(5);
        Mockito.verify(mMockPropertiesWrapper).setSystemCallSharding(10);
    }

    @Test
    public void testSettingsObserver_badInput() {
        Settings.Global.putString(mResolver, Settings.Global.NATIVE_BINDER_STATS,
                "enabled=truepxrocess_sharding=5,spam_sharding=1,call_sharding=2,"
                        + "system_process_sharding=1,system_spam_sharding=5,system_call_sharding="
                        + "10");

        NativeBinderStats nativeBinderStats =
                new NativeBinderStats(mContext, mMockPropertiesWrapper);
        nativeBinderStats.systemReady();

        // Verify that the injector was called with default values
        Mockito.verify(mMockPropertiesWrapper).setEnabled(NativeBinderStats.DEFAULT_ENABLED);
        Mockito.verify(mMockPropertiesWrapper)
                .setProcessSharding(NativeBinderStats.DEFAULT_PROCESS_SHARDING);
        // Verify that correctly got values were set
        Mockito.verify(mMockPropertiesWrapper).setSpamSharding(1);
        Mockito.verify(mMockPropertiesWrapper).setCallSharding(2);
        Mockito.verify(mMockPropertiesWrapper).setSystemProcessSharding(1);
        Mockito.verify(mMockPropertiesWrapper).setSystemSpamSharding(5);
        Mockito.verify(mMockPropertiesWrapper).setSystemCallSharding(10);

        assertThat(nativeBinderStats.mEnabled).isFalse();
    }

    @Test
    public void testSettingsObserver_triggerInputException() {
        Settings.Global.putString(
                mResolver, Settings.Global.NATIVE_BINDER_STATS, "Lorem,ipsum,enable123");

        NativeBinderStats nativeBinderStats =
                new NativeBinderStats(mContext, mMockPropertiesWrapper);
        nativeBinderStats.systemReady();

        // Verify that the injector was called with default values
        Mockito.verify(mMockPropertiesWrapper).setEnabled(NativeBinderStats.DEFAULT_ENABLED);
        Mockito.verify(mMockPropertiesWrapper)
                .setProcessSharding(NativeBinderStats.DEFAULT_PROCESS_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSpamSharding(NativeBinderStats.DEFAULT_SPAM_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setCallSharding(NativeBinderStats.DEFAULT_CALL_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemProcessSharding(NativeBinderStats.DEFAULT_SYSTEM_PROCESS_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemSpamSharding(NativeBinderStats.DEFAULT_SYSTEM_SPAM_SHARDING);
        Mockito.verify(mMockPropertiesWrapper)
                .setSystemCallSharding(NativeBinderStats.DEFAULT_SYSTEM_CALL_SHARDING);
    }
}