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

Commit c907b9bd authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add config for Native Binder Stats" into main

parents 90aa83bb dd838c25
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -19878,6 +19878,26 @@ public final class Settings {
        @Readable
        public static final String BINDER_CALLS_STATS = "binder_calls_stats";
        /**
         * Native binder stats settings.
         *
         * These parameters are represented by a comma-delimited key-value list.
         *
         * The following strings are supported as keys:
         * <pre>
         *     enabled                      (boolean)
         *     process_sharding             (int)
         *     spam_sharding                (int)
         *     call_sharding                (int)
         *     system_process_sharding      (int)
         *     system_spam_sharding         (int)
         *     system_call_sharding         (int)
         * </pre>
         *
         * @hide
         */
        public static final String NATIVE_BINDER_STATS = "native_binder_stats";
        /**
         * Looper stats settings.
         *
+160 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.os;

import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.provider.Settings;
import android.util.KeyValueListParser;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

/**
 * Coordinates native binder stats collection. Propagates settings to the processes that use
 * libbinder for tracking stats.
 */
public class NativeBinderStats {
    private static final String TAG = "NativeBinderStats";

    static final boolean DEFAULT_ENABLED = false;
    static final int DEFAULT_PROCESS_SHARDING = 50;
    static final int DEFAULT_SPAM_SHARDING = 10;
    static final int DEFAULT_CALL_SHARDING = 20;
    static final int DEFAULT_SYSTEM_PROCESS_SHARDING = 10;
    static final int DEFAULT_SYSTEM_SPAM_SHARDING = 50;
    static final int DEFAULT_SYSTEM_CALL_SHARDING = 100;

    /** Whether native binder stats are enabled. */
    @VisibleForTesting public boolean mEnabled = DEFAULT_ENABLED;

    /**
     * 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.
     */
    @VisibleForTesting public int mProcessSharding = DEFAULT_PROCESS_SHARDING;

    /**
     * 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.
     */
    @VisibleForTesting public int mSpamSharding = DEFAULT_SPAM_SHARDING;

    /**
     * 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.
     */
    @VisibleForTesting public int mCallSharding = DEFAULT_CALL_SHARDING;

    /** Like {@link #mProcessSharding} but for system_server. */
    @VisibleForTesting public int mSystemProcessSharding = DEFAULT_SYSTEM_PROCESS_SHARDING;

    /** Like {@link #mSpamSharding} but for system_server. */
    @VisibleForTesting public int mSystemSpamSharding = DEFAULT_SYSTEM_SPAM_SHARDING;

    /** Like {@link #mCallSharding} but for system_server. */
    @VisibleForTesting public int mSystemCallSharding = DEFAULT_SYSTEM_CALL_SHARDING;

    private final Context mContext;

    private final SettingsObserver mSettingsObserver;

    public NativeBinderStats(Context context) {
        mContext = context;
        mSettingsObserver = new SettingsObserver(context);
    }

    public void systemReady() {
        // Start observing settings.
        mSettingsObserver.register();
    }

    @VisibleForTesting
    public class SettingsObserver extends ContentObserver {
        private static final String KEY_ENABLED = "enabled";
        private static final String KEY_PROCESS_SHARDING = "process_sharding";
        private static final String KEY_SPAM_SHARDING = "spam_sharding";
        private static final String KEY_CALL_SHARDING = "call_sharding";
        private static final String KEY_SYSTEM_PROCESS_SHARDING = "system_process_sharding";
        private static final String KEY_SYSTEM_SPAM_SHARDING = "system_spam_sharding";
        private static final String KEY_SYSTEM_CALL_SHARDING = "system_call_sharding";

        private final Uri mUri = Settings.Global.getUriFor(Settings.Global.NATIVE_BINDER_STATS);
        private final KeyValueListParser mParser = new KeyValueListParser(',');
        private final Context mContext;

        SettingsObserver(Context context) {
            super(BackgroundThread.getHandler());
            mContext = context;
        }

        void register() {
            mContext.getContentResolver().registerContentObserver(mUri, false, this);
            // Trigger update so we get the initial state.
            onChange();
        }

        @Override
        public void onChange(boolean selfChange, Uri uri, int userId) {
            if (mUri.equals(uri)) {
                onChange();
            }
        }

        void onChange() {
            try {
                mParser.setString(
                        Settings.Global.getString(
                                mContext.getContentResolver(),
                                Settings.Global.NATIVE_BINDER_STATS));
            } catch (IllegalArgumentException e) {
                Slog.e(TAG, "Bad native binder stats settings", e);
            }

            mEnabled = mParser.getBoolean(KEY_ENABLED, DEFAULT_ENABLED);
            mProcessSharding = mParser.getInt(KEY_PROCESS_SHARDING, DEFAULT_PROCESS_SHARDING);
            mSpamSharding = mParser.getInt(KEY_SPAM_SHARDING, DEFAULT_SPAM_SHARDING);
            mCallSharding = mParser.getInt(KEY_CALL_SHARDING, DEFAULT_CALL_SHARDING);
            mSystemProcessSharding =
                    mParser.getInt(KEY_SYSTEM_PROCESS_SHARDING, DEFAULT_SYSTEM_PROCESS_SHARDING);
            mSystemSpamSharding =
                    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.
            Slog.i(
                    TAG,
                    String.format(
                            "Native binder stats settings changed: %b, %d, %d, %d, %d,  %d, %d",
                            mEnabled,
                            mProcessSharding,
                            mSpamSharding,
                            mCallSharding,
                            mSystemProcessSharding,
                            mSystemSpamSharding,
                            mSystemCallSharding));
        }
    }

    @VisibleForTesting
    public SettingsObserver getSettingsObserverForTesting() {
        return mSettingsObserver;
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -257,6 +257,7 @@ android_ravenwood_test {
        "android.view.flags-aconfig-java",
        "ext",
        "framework",
        "frameworks-base-testutils",
        "framework-res",
        "libprotobuf-java-lite",
        "org.apache.http.legacy.stubs",
+137 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.os;

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

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

import android.content.Context;
import android.content.ContextWrapper;
import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.test.mock.MockContentResolver;

import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.internal.util.test.FakeSettingsProvider;

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

@SmallTest
@RunWith(AndroidJUnit4.class)
@Presubmit
@DisabledOnRavenwood(blockedBy = MockContentResolver.class)
public class NativeBinderStatsTest {
    private Context mContext;
    private MockContentResolver mResolver;

    @Before
    public void setup() {
        mContext = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
        mResolver = new MockContentResolver(mContext);
        mResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
        when(mContext.getContentResolver()).thenReturn(mResolver);
    }

    @After
    public void tearDown() {
        FakeSettingsProvider.clearSettingsProvider();
    }

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

        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);
        nativeBinderStats.systemReady();

        assertThat(nativeBinderStats.mEnabled).isTrue();
        assertThat(nativeBinderStats.mProcessSharding)
                .isEqualTo(NativeBinderStats.DEFAULT_PROCESS_SHARDING);
        assertThat(nativeBinderStats.mSpamSharding)
                .isEqualTo(NativeBinderStats.DEFAULT_SPAM_SHARDING);
        assertThat(nativeBinderStats.mCallSharding)
                .isEqualTo(NativeBinderStats.DEFAULT_CALL_SHARDING);
        assertThat(nativeBinderStats.mSystemProcessSharding)
                .isEqualTo(NativeBinderStats.DEFAULT_SYSTEM_PROCESS_SHARDING);
        assertThat(nativeBinderStats.mSystemSpamSharding)
                .isEqualTo(NativeBinderStats.DEFAULT_SYSTEM_SPAM_SHARDING);
        assertThat(nativeBinderStats.mSystemCallSharding)
                .isEqualTo(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.systemReady();

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

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

        assertThat(nativeBinderStats.mEnabled).isTrue();

        Settings.Global.putString(mResolver, Settings.Global.NATIVE_BINDER_STATS, "enabled=false");
        // FakeSettingsProvider doesn't support notifications, so we need to notify manually.
        // This assumes that SettingsObserver is registered, which we cannot check because
        // registerContentObserver is final.
        nativeBinderStats.getSettingsObserverForTesting().onChange(
                false, Settings.Global.getUriFor(Settings.Global.NATIVE_BINDER_STATS), 0);

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

    @Test
    public void testSettingsObserver_allValues() {
        Settings.Global.putString(
                mResolver,
                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.systemReady();

        assertThat(nativeBinderStats.mEnabled).isTrue();
        assertThat(nativeBinderStats.mProcessSharding).isEqualTo(5);
        assertThat(nativeBinderStats.mSpamSharding).isEqualTo(1);
        assertThat(nativeBinderStats.mCallSharding).isEqualTo(2);
        assertThat(nativeBinderStats.mSystemProcessSharding).isEqualTo(1);
        assertThat(nativeBinderStats.mSystemSpamSharding).isEqualTo(5);
        assertThat(nativeBinderStats.mSystemCallSharding).isEqualTo(10);
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -104,6 +104,7 @@ public class SettingsBackupTest {
                    Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
                    Settings.Global.BATTERY_STATS_CONSTANTS,
                    Settings.Global.BINDER_CALLS_STATS,
                    Settings.Global.NATIVE_BINDER_STATS,
                    Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
                    Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS,
                    Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS,
Loading