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

Commit df2fa88b authored by Satoshi Niwa's avatar Satoshi Niwa Committed by Android (Google) Code Review
Browse files

Merge "Refactor CoreSettingsObserver to improve locking and clarity" into main

parents 87823ed2 d41d9c9b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -4822,7 +4822,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                        preBindInfo.configuration,
                        app.getCompat(),
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        mCoreSettingsObserver.getCoreSettings(),
                        buildSerial,
                        autofillOptions,
                        contentCaptureOptions,
+115 −55
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.am;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.companion.virtual.VirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
@@ -27,11 +28,13 @@ import android.net.Uri;
import android.os.Bundle;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.Slog;
import android.util.IntArray;
import android.widget.WidgetFlags;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.utils.Slogf;

import java.util.ArrayList;
import java.util.HashMap;
@@ -49,6 +52,9 @@ import java.util.Objects;
final class CoreSettingsObserver extends ContentObserver {

    private static final String TAG = "CoreSettingsObserver";
    private static final boolean DEBUG = false;

    private final Object mLock = new Object();

    private static class DeviceConfigEntry<T> {
        String namespace;
@@ -170,9 +176,12 @@ final class CoreSettingsObserver extends ContentObserver {
    }
    private static volatile boolean sDeviceConfigContextEntriesLoaded = false;

    @GuardedBy("mLock")
    private final Bundle mCoreSettings = new Bundle();

    private final ActivityManagerService mActivityManagerService;

    @Nullable
    private VirtualDeviceManager mVirtualDeviceManager;

    public CoreSettingsObserver(ActivityManagerService activityManagerService) {
@@ -200,87 +209,127 @@ final class CoreSettingsObserver extends ContentObserver {
                        .getInteger(R.integer.config_defaultAnalogClockSecondsHandFps)));
    }

    public Bundle getCoreSettingsLocked() {
    /**
     * Gets a deep copy of the core settings.
     */
    public Bundle getCoreSettings() {
        synchronized (mLock) {
            return mCoreSettings.deepCopy();
        }
    }

    @Override
    public void onChange(boolean selfChange) {
        synchronized (mActivityManagerService) {
            sendCoreSettings();
        if (DEBUG) {
            Slogf.d(TAG, "Core settings changed, selfChange: %b", selfChange);
        }
        sendCoreSettings();
    }

    private void sendCoreSettings() {
        final Context context = mActivityManagerService.mContext;
        if (android.companion.virtualdevice.flags.Flags.deviceAwareSettingsOverride()) {
            final List<Integer> deviceIds = new ArrayList<>();
    private IntArray getVirtualDeviceIds() {
        if (mVirtualDeviceManager == null) {
            mVirtualDeviceManager = mActivityManagerService.mContext.getSystemService(
                    VirtualDeviceManager.class);
            if (mVirtualDeviceManager == null) {
                return new IntArray(0);
            }
        }
            if (mVirtualDeviceManager != null) {
                final List<VirtualDevice> virtualDevices =
                        mVirtualDeviceManager.getVirtualDevices();
                for (VirtualDevice virtualDevice : virtualDevices) {
                    deviceIds.add(virtualDevice.getDeviceId());

        List<VirtualDevice> virtualDevices = mVirtualDeviceManager.getVirtualDevices();
        IntArray deviceIds = new IntArray(virtualDevices.size());
        for (int i = 0; i < virtualDevices.size(); i++) {
            deviceIds.add(virtualDevices.get(i).getDeviceId());
        }
        return deviceIds;
    }

            Bundle globalSettingsBundle = new Bundle();
    /**
     * Populates the core settings bundle with the latest values and sends them to app processes
     * via {@link ActivityThread}.
     */
    private void sendCoreSettings() {
        Context context = mActivityManagerService.mContext;

        // Create a temporary bundle to store the settings that will be sent.
        Bundle settingsToSend;

        if (android.companion.virtualdevice.flags.Flags.deviceAwareSettingsOverride()) {
            IntArray deviceIds = getVirtualDeviceIds();
            deviceIds.add(Context.DEVICE_ID_DEFAULT);
            settingsToSend = new Bundle(deviceIds.size());

            // Global settings and device config values do not vary across devices, so we can
            // populate them once.
            Bundle globalSettingsBundle = new Bundle(sGlobalSettingToTypeMap.size());
            populateSettings(context, globalSettingsBundle, sGlobalSettingToTypeMap);
            Bundle deviceConfigBundle = new Bundle();
            Bundle deviceConfigBundle = new Bundle(sDeviceConfigEntries.size());
            populateSettingsFromDeviceConfig(deviceConfigBundle);

            deviceIds.add(Context.DEVICE_ID_DEFAULT);
            for (int deviceId : deviceIds) {
                final Bundle deviceBundle = new Bundle();
            for (int i = 0; i < deviceIds.size(); i++) {
                int deviceId = deviceIds.get(i);
                Context deviceContext = null;
                if (deviceId == Context.DEVICE_ID_DEFAULT) {
                    deviceContext = context;
                } else {
                    try {
                        deviceContext = context.createDeviceContext(deviceId);
                    } catch (IllegalArgumentException e) {
                    Slog.w(TAG, "Exception during Context#createDeviceContext", e);
                        Slogf.e(TAG, e, "Exception during Context#createDeviceContext "
                                + "for deviceId: %d", deviceId);
                        continue;
                    }
                }

                if (deviceContext != null) {
                if (DEBUG) {
                    Slogf.d(TAG, "Populating settings for deviceId: %d", deviceId);
                }
                Bundle deviceBundle = new Bundle();
                populateSettings(deviceContext, deviceBundle, sSecureSettingToTypeMap);
                populateSettings(deviceContext, deviceBundle, sSystemSettingToTypeMap);

                    // Copy global settings and device config values, as they don't vary across
                    // devices.
                // Copy global settings and device config values.
                deviceBundle.putAll(globalSettingsBundle);
                deviceBundle.putAll(deviceConfigBundle);

                    mCoreSettings.putBundle(String.valueOf(deviceId), deviceBundle);
                }
                settingsToSend.putBundle(String.valueOf(deviceId), deviceBundle);
            }
        } else {
            populateSettings(context, mCoreSettings, sSecureSettingToTypeMap);
            populateSettings(context, mCoreSettings, sSystemSettingToTypeMap);
            populateSettings(context, mCoreSettings, sGlobalSettingToTypeMap);
            populateSettingsFromDeviceConfig(mCoreSettings);
            if (DEBUG) {
                Slogf.d(TAG, "Populating settings for default device");
            }

            // For non-device-aware case, populate all settings into the single bundle.
            settingsToSend = new Bundle();
            populateSettings(context, settingsToSend, sSecureSettingToTypeMap);
            populateSettings(context, settingsToSend, sSystemSettingToTypeMap);
            populateSettings(context, settingsToSend, sGlobalSettingToTypeMap);
            populateSettingsFromDeviceConfig(settingsToSend);
        }

        synchronized (mLock) {
            mCoreSettings.clear();
            mCoreSettings.putAll(settingsToSend);
        }

        mActivityManagerService.onCoreSettingsChange(mCoreSettings);
        mActivityManagerService.onCoreSettingsChange(settingsToSend);
    }

    private void beginObserveCoreSettings() {
        ContentResolver cr = mActivityManagerService.mContext.getContentResolver();

        for (String setting : sSecureSettingToTypeMap.keySet()) {
            Uri uri = Settings.Secure.getUriFor(setting);
            mActivityManagerService.mContext.getContentResolver().registerContentObserver(
                    uri, false, this);
            cr.registerContentObserver(uri, false, this);
        }

        for (String setting : sSystemSettingToTypeMap.keySet()) {
            Uri uri = Settings.System.getUriFor(setting);
            mActivityManagerService.mContext.getContentResolver().registerContentObserver(
                    uri, false, this);
            cr.registerContentObserver(uri, false, this);
        }

        for (String setting : sGlobalSettingToTypeMap.keySet()) {
            Uri uri = Settings.Global.getUriFor(setting);
            mActivityManagerService.mContext.getContentResolver().registerContentObserver(
                    uri, false, this);
            cr.registerContentObserver(uri, false, this);
        }

        HashSet<String> deviceConfigNamespaces = new HashSet<>();
@@ -294,6 +343,13 @@ final class CoreSettingsObserver extends ContentObserver {
        }
    }

    /**
     * Populates the given bundle with settings from the given map.
     *
     * @param context The context to use for retrieving the settings.
     * @param snapshot The bundle to populate.
     * @param map The map of settings to retrieve.
     */
    @VisibleForTesting
    void populateSettings(Context context, Bundle snapshot, Map<String, Class<?>> map) {
        final ContentResolver cr = context.getContentResolver();
@@ -312,6 +368,7 @@ final class CoreSettingsObserver extends ContentObserver {
                continue;
            }
            Class<?> type = entry.getValue();
            try {
                if (type == String.class) {
                    snapshot.putString(setting, value);
                } else if (type == int.class) {
@@ -321,6 +378,9 @@ final class CoreSettingsObserver extends ContentObserver {
                } else if (type == long.class) {
                    snapshot.putLong(setting, Long.parseLong(value));
                }
            } catch (NumberFormatException e) {
                Slogf.w(TAG, e, "Couldn't parse %s for %s", value, setting);
            }
        }
    }

+27 −22
Original line number Diff line number Diff line
@@ -20,9 +20,9 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat

import static com.android.server.am.ActivityManagerService.Injector;

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

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -67,6 +67,7 @@ public class CoreSettingsObserverTest {
    private static final int TEST_INT = 111;
    private static final float TEST_FLOAT = 3.14f;
    private static final String TEST_STRING = "testString";
    private static final float TOLERANCE = 0.001f;

    @Rule
    public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
@@ -99,6 +100,7 @@ public class CoreSettingsObserverTest {
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        // Mock context and content resolver.
        final Context originalContext = getInstrumentation().getTargetContext();
        when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo());
        mContentResolver = new MockContentResolver(mContext);
@@ -114,6 +116,7 @@ public class CoreSettingsObserverTest {
        when(mockTypedArray.length()).thenReturn(1);
        when(mResources.obtainTypedArray(anyInt())).thenReturn(mockTypedArray);

        // Initialize ActivityManagerService and CoreSettingsObserver.
        mAms = new ActivityManagerService(new TestInjector(mContext),
                mServiceThreadRule.getThread());
        mCoreSettingsObserver = new CoreSettingsObserver(mAms);
@@ -127,23 +130,23 @@ public class CoreSettingsObserverTest {

        final Bundle settingsBundle = getPopulatedBundle();

        assertEquals("Unexpected value of " + TEST_SETTING_SECURE_INT,
                TEST_INT, settingsBundle.getInt(TEST_SETTING_SECURE_INT));
        assertEquals("Unexpected value of " + TEST_SETTING_GLOBAL_FLOAT,
                TEST_FLOAT, settingsBundle.getFloat(TEST_SETTING_GLOBAL_FLOAT), 0);
        assertEquals("Unexpected value of " + TEST_SETTING_SYSTEM_STRING,
                TEST_STRING, settingsBundle.getString(TEST_SETTING_SYSTEM_STRING));
        // Assert that the bundle contains the correct values.
        assertThat(settingsBundle.getInt(TEST_SETTING_SECURE_INT)).isEqualTo(TEST_INT);
        assertThat(settingsBundle.getFloat(TEST_SETTING_GLOBAL_FLOAT)).isWithin(TOLERANCE).of(
                TEST_FLOAT);
        assertThat(settingsBundle.getString(TEST_SETTING_SYSTEM_STRING)).isEqualTo(TEST_STRING);
    }

    @Test
    public void testPopulateSettings_settingNotSet() {
        final Bundle settingsBundle = getPopulatedBundle();

        assertWithMessage("Bundle should not contain " + TEST_SETTING_SECURE_INT)
        // Assert that the bundle does not contain any of the test settings.
        assertWithMessage("Bundle should not contain %s", TEST_SETTING_SECURE_INT)
                .that(settingsBundle.keySet()).doesNotContain(TEST_SETTING_SECURE_INT);
        assertWithMessage("Bundle should not contain " + TEST_SETTING_GLOBAL_FLOAT)
        assertWithMessage("Bundle should not contain %s", TEST_SETTING_GLOBAL_FLOAT)
                .that(settingsBundle.keySet()).doesNotContain(TEST_SETTING_GLOBAL_FLOAT);
        assertWithMessage("Bundle should not contain " + TEST_SETTING_SYSTEM_STRING)
        assertWithMessage("Bundle should not contain %s", TEST_SETTING_SYSTEM_STRING)
                .that(settingsBundle.keySet()).doesNotContain(TEST_SETTING_SYSTEM_STRING);
    }

@@ -153,24 +156,26 @@ public class CoreSettingsObserverTest {
        Settings.Global.putFloat(mContentResolver, TEST_SETTING_GLOBAL_FLOAT, TEST_FLOAT);
        Settings.System.putString(mContentResolver, TEST_SETTING_SYSTEM_STRING, TEST_STRING);

        // Trigger the observer and get the populated bundle.
        Bundle settingsBundle = getPopulatedBundle();

        assertEquals("Unexpected value of " + TEST_SETTING_SECURE_INT,
                TEST_INT, settingsBundle.getInt(TEST_SETTING_SECURE_INT));
        assertEquals("Unexpected value of " + TEST_SETTING_GLOBAL_FLOAT,
                TEST_FLOAT, settingsBundle.getFloat(TEST_SETTING_GLOBAL_FLOAT), 0);
        assertEquals("Unexpected value of " + TEST_SETTING_SYSTEM_STRING,
                TEST_STRING, settingsBundle.getString(TEST_SETTING_SYSTEM_STRING));
        // Assert that the bundle contains the correct values initially.
        assertThat(settingsBundle.getInt(TEST_SETTING_SECURE_INT)).isEqualTo(TEST_INT);
        assertThat(settingsBundle.getFloat(TEST_SETTING_GLOBAL_FLOAT)).isWithin(TOLERANCE).of(
                TEST_FLOAT);
        assertThat(settingsBundle.getString(TEST_SETTING_SYSTEM_STRING)).isEqualTo(TEST_STRING);

        // Delete one of the settings.
        Settings.Global.putString(mContentResolver, TEST_SETTING_GLOBAL_FLOAT, null);
        // Trigger the observer again.
        settingsBundle = getPopulatedBundle();

        assertWithMessage("Bundle should not contain " + TEST_SETTING_GLOBAL_FLOAT)
        // Assert that the deleted setting is no longer in the bundle.
        assertWithMessage("Bundle should not contain %s", TEST_SETTING_GLOBAL_FLOAT)
                .that(settingsBundle.keySet()).doesNotContain(TEST_SETTING_GLOBAL_FLOAT);
        assertEquals("Unexpected value of " + TEST_SETTING_SECURE_INT,
                TEST_INT, settingsBundle.getInt(TEST_SETTING_SECURE_INT));
        assertEquals("Unexpected value of " + TEST_SETTING_SYSTEM_STRING,
                TEST_STRING, settingsBundle.getString(TEST_SETTING_SYSTEM_STRING));
        // Assert that the other settings remain.
        assertThat(settingsBundle.getInt(TEST_SETTING_SECURE_INT)).isEqualTo(TEST_INT);
        assertThat(settingsBundle.getString(TEST_SETTING_SYSTEM_STRING)).isEqualTo(TEST_STRING);
    }

    @Test
@@ -182,7 +187,7 @@ public class CoreSettingsObserverTest {

    private Bundle getPopulatedBundle() {
        mCoreSettingsObserver.onChange(false);
        return mCoreSettingsObserver.getCoreSettingsLocked().getBundle(
        return mCoreSettingsObserver.getCoreSettings().getBundle(
                String.valueOf(Context.DEVICE_ID_DEFAULT));
    }