Loading packages/SettingsProvider/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ android_library { "unsupportedappusage", ], static_libs: [ "aconfig_new_storage_flags_lib", "aconfigd_java_utils", "aconfig_demo_flags_java_lib", "device_config_service_flags_java", "libaconfig_java_proto_lite", Loading packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +152 −0 Original line number Diff line number Diff line Loading @@ -82,6 +82,18 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; // FOR ACONFIGD TEST MISSION AND ROLLOUT import java.io.DataInputStream; import java.io.DataOutputStream; import android.net.LocalSocketAddress; import android.net.LocalSocket; import android.util.proto.ProtoInputStream; import android.aconfigd.Aconfigd.StorageRequestMessage; import android.aconfigd.Aconfigd.StorageRequestMessages; import android.aconfigd.Aconfigd.StorageReturnMessage; import android.aconfigd.Aconfigd.StorageReturnMessages; import android.aconfigd.AconfigdJavaUtils; import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon; /** * This class contains the state for one type of settings. It is responsible * for saving the state asynchronously to an XML file after a mutation and Loading Loading @@ -346,6 +358,7 @@ final class SettingsState { mNamespaceDefaults = new HashMap<>(); ProtoOutputStream requests = null; synchronized (mLock) { readStateSyncLocked(); Loading @@ -361,7 +374,146 @@ final class SettingsState { loadAconfigDefaultValuesLocked(apexProtoPaths); } } if (isConfigSettingsKey(mKey)) { requests = handleBulkSyncToNewStorage(); } } if (requests != null) { LocalSocket client = new LocalSocket(); try{ client.connect(new LocalSocketAddress( "aconfigd", LocalSocketAddress.Namespace.RESERVED)); Slog.d(LOG_TAG, "connected to aconfigd socket"); } catch (IOException ioe) { Slog.e(LOG_TAG, "failed to connect to aconfigd socket", ioe); return; } AconfigdJavaUtils.sendAconfigdRequests(client, requests); } } // TODO(b/341764371): migrate aconfig flag push to GMS core public static class FlagOverrideToSync { public String packageName; public String flagName; public String flagValue; public boolean isLocal; } // TODO(b/341764371): migrate aconfig flag push to GMS core @VisibleForTesting @GuardedBy("mLock") public FlagOverrideToSync getFlagOverrideToSync(String name, String value) { int slashIdx = name.indexOf("/"); if (slashIdx <= 0 || slashIdx >= name.length()-1) { Slog.e(LOG_TAG, "invalid flag name " + name); return null; } String namespace = name.substring(0, slashIdx); String fullFlagName = name.substring(slashIdx + 1); boolean isLocal = false; // get actual fully qualified flag name <package>.<flag>, note this is done // after staged flag is applied, so no need to check staged flags if (namespace.equals("device_config_overrides")) { int colonIdx = fullFlagName.indexOf(":"); if (colonIdx == -1) { Slog.e(LOG_TAG, "invalid local override flag name " + name); return null; } namespace = fullFlagName.substring(0, colonIdx); fullFlagName = fullFlagName.substring(colonIdx + 1); isLocal = true; } String aconfigName = namespace + "/" + fullFlagName; boolean isAconfig = mNamespaceDefaults.containsKey(namespace) && mNamespaceDefaults.get(namespace).containsKey(aconfigName); if (!isAconfig) { return null; } // get package name and flag name int dotIdx = fullFlagName.lastIndexOf("."); if (dotIdx == -1) { Slog.e(LOG_TAG, "invalid override flag name " + name); return null; } FlagOverrideToSync flag = new FlagOverrideToSync(); flag.packageName = fullFlagName.substring(0, dotIdx); flag.flagName = fullFlagName.substring(dotIdx + 1); flag.isLocal = isLocal; flag.flagValue = value; return flag; } // TODO(b/341764371): migrate aconfig flag push to GMS core @VisibleForTesting @GuardedBy("mLock") public ProtoOutputStream handleBulkSyncToNewStorage() { // get marker or add marker if it does not exist final String bulkSyncMarkerName = new String("aconfigd_marker/bulk_synced"); Setting markerSetting = mSettings.get(bulkSyncMarkerName); if (markerSetting == null) { markerSetting = new Setting( bulkSyncMarkerName, "false", false, "aconfig", "aconfig"); mSettings.put(bulkSyncMarkerName, markerSetting); } if (enableAconfigStorageDaemon()) { if (markerSetting.value.equals("true")) { // CASE 1, flag is on, bulk sync marker true, nothing to do return null; } else { // CASE 2, flag is on, bulk sync marker false. Do following two tasks // (1) Do bulk sync here. // (2) After bulk sync, set marker to true. // first add storage reset request ProtoOutputStream requests = new ProtoOutputStream(); AconfigdJavaUtils.writeResetStorageRequest(requests); // loop over all settings and add flag override requests final int numSettings = mSettings.size(); int num_requests = 0; for (int i = 0; i < numSettings; i++) { String name = mSettings.keyAt(i); Setting setting = mSettings.valueAt(i); FlagOverrideToSync flag = getFlagOverrideToSync(name, setting.getValue()); if (flag == null) { continue; } ++num_requests; AconfigdJavaUtils.writeFlagOverrideRequest( requests, flag.packageName, flag.flagName, flag.flagValue, flag.isLocal); } Slog.i(LOG_TAG, num_requests + " flag override requests created"); // mark sync has been done markerSetting.value = "true"; scheduleWriteIfNeededLocked(); return requests; } } else { if (markerSetting.value.equals("true")) { // CASE 3, flag is off, bulk sync marker true, clear the marker markerSetting.value = "false"; scheduleWriteIfNeededLocked(); return null; } else { // CASE 4, flag is off, bulk sync marker false, nothing to do return null; } } } @GuardedBy("mLock") Loading packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +117 −0 Original line number Diff line number Diff line Loading @@ -29,12 +29,18 @@ import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.Xml; import android.util.proto.ProtoOutputStream; import com.android.providers.settings.SettingsState.FlagOverrideToSync; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.modules.utils.TypedXmlSerializer; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.DisableFlags; import android.platform.test.flag.junit.SetFlagsRule; import com.google.common.base.Strings; import java.io.ByteArrayOutputStream; Loading Loading @@ -947,4 +953,115 @@ public class SettingsStateTest { + testValue1.length() /* size for default */) * Character.BYTES; assertEquals(expectedMemUsageForPackage2, settingsState.getMemoryUsage(package2)); } @Test public void testGetFlagOverrideToSync() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); parsed_flags flags = parsed_flags .newBuilder() .addParsedFlag(parsed_flag .newBuilder() .setPackage("com.android.flags") .setName("flag1") .setNamespace("test_namespace") .setDescription("test flag") .addBug("12345678") .setState(Aconfig.flag_state.DISABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .build(); synchronized (lock) { Map<String, Map<String, String>> defaults = new HashMap<>(); settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults); Map<String, String> namespaceDefaults = defaults.get("test_namespace"); assertEquals(1, namespaceDefaults.keySet().size()); settingsState.addAconfigDefaultValuesFromMap(defaults); } // invalid flag name assertTrue(settingsState.getFlagOverrideToSync( "invalid_flag", "false") == null); // non aconfig flag assertTrue(settingsState.getFlagOverrideToSync( "some_namespace/some_flag", "false") == null); // server override FlagOverrideToSync flag = settingsState.getFlagOverrideToSync( "test_namespace/com.android.flags.flag1", "false"); assertTrue(flag != null); assertEquals(flag.packageName, "com.android.flags"); assertEquals(flag.flagName, "flag1"); assertEquals(flag.flagValue, "false"); assertEquals(flag.isLocal, false); // local override flag = settingsState.getFlagOverrideToSync( "device_config_overrides/test_namespace:com.android.flags.flag1", "false"); assertTrue(flag != null); assertEquals(flag.packageName, "com.android.flags"); assertEquals(flag.flagName, "flag1"); assertEquals(flag.flagValue, "false"); assertEquals(flag.isLocal, true); } @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Test @EnableFlags(com.android.aconfig_new_storage.Flags.FLAG_ENABLE_ACONFIG_STORAGE_DAEMON) public void testHandleBulkSyncWithAconfigdEnabled() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { settingsState.insertSettingLocked("aconfigd_marker/bulk_synced", "false", null, false, "aconfig"); // first bulk sync ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(); assertTrue(requests != null); String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); assertEquals("true", value); // send time should no longer bulk sync requests = settingsState.handleBulkSyncToNewStorage(); assertTrue(requests == null); value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); assertEquals("true", value); } } @Test @DisableFlags(com.android.aconfig_new_storage.Flags.FLAG_ENABLE_ACONFIG_STORAGE_DAEMON) public void testHandleBulkSyncWithAconfigdDisabled() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { settingsState.insertSettingLocked("aconfigd_marker/bulk_synced", "true", null, false, "aconfig"); // when aconfigd is off, should change the marker to false ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(); assertTrue(requests == null); String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); assertEquals("false", value); // marker started with false value, after call, it should remain false requests = settingsState.handleBulkSyncToNewStorage(); assertTrue(requests == null); value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); assertEquals("false", value); } } } Loading
packages/SettingsProvider/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ android_library { "unsupportedappusage", ], static_libs: [ "aconfig_new_storage_flags_lib", "aconfigd_java_utils", "aconfig_demo_flags_java_lib", "device_config_service_flags_java", "libaconfig_java_proto_lite", Loading
packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +152 −0 Original line number Diff line number Diff line Loading @@ -82,6 +82,18 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; // FOR ACONFIGD TEST MISSION AND ROLLOUT import java.io.DataInputStream; import java.io.DataOutputStream; import android.net.LocalSocketAddress; import android.net.LocalSocket; import android.util.proto.ProtoInputStream; import android.aconfigd.Aconfigd.StorageRequestMessage; import android.aconfigd.Aconfigd.StorageRequestMessages; import android.aconfigd.Aconfigd.StorageReturnMessage; import android.aconfigd.Aconfigd.StorageReturnMessages; import android.aconfigd.AconfigdJavaUtils; import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon; /** * This class contains the state for one type of settings. It is responsible * for saving the state asynchronously to an XML file after a mutation and Loading Loading @@ -346,6 +358,7 @@ final class SettingsState { mNamespaceDefaults = new HashMap<>(); ProtoOutputStream requests = null; synchronized (mLock) { readStateSyncLocked(); Loading @@ -361,7 +374,146 @@ final class SettingsState { loadAconfigDefaultValuesLocked(apexProtoPaths); } } if (isConfigSettingsKey(mKey)) { requests = handleBulkSyncToNewStorage(); } } if (requests != null) { LocalSocket client = new LocalSocket(); try{ client.connect(new LocalSocketAddress( "aconfigd", LocalSocketAddress.Namespace.RESERVED)); Slog.d(LOG_TAG, "connected to aconfigd socket"); } catch (IOException ioe) { Slog.e(LOG_TAG, "failed to connect to aconfigd socket", ioe); return; } AconfigdJavaUtils.sendAconfigdRequests(client, requests); } } // TODO(b/341764371): migrate aconfig flag push to GMS core public static class FlagOverrideToSync { public String packageName; public String flagName; public String flagValue; public boolean isLocal; } // TODO(b/341764371): migrate aconfig flag push to GMS core @VisibleForTesting @GuardedBy("mLock") public FlagOverrideToSync getFlagOverrideToSync(String name, String value) { int slashIdx = name.indexOf("/"); if (slashIdx <= 0 || slashIdx >= name.length()-1) { Slog.e(LOG_TAG, "invalid flag name " + name); return null; } String namespace = name.substring(0, slashIdx); String fullFlagName = name.substring(slashIdx + 1); boolean isLocal = false; // get actual fully qualified flag name <package>.<flag>, note this is done // after staged flag is applied, so no need to check staged flags if (namespace.equals("device_config_overrides")) { int colonIdx = fullFlagName.indexOf(":"); if (colonIdx == -1) { Slog.e(LOG_TAG, "invalid local override flag name " + name); return null; } namespace = fullFlagName.substring(0, colonIdx); fullFlagName = fullFlagName.substring(colonIdx + 1); isLocal = true; } String aconfigName = namespace + "/" + fullFlagName; boolean isAconfig = mNamespaceDefaults.containsKey(namespace) && mNamespaceDefaults.get(namespace).containsKey(aconfigName); if (!isAconfig) { return null; } // get package name and flag name int dotIdx = fullFlagName.lastIndexOf("."); if (dotIdx == -1) { Slog.e(LOG_TAG, "invalid override flag name " + name); return null; } FlagOverrideToSync flag = new FlagOverrideToSync(); flag.packageName = fullFlagName.substring(0, dotIdx); flag.flagName = fullFlagName.substring(dotIdx + 1); flag.isLocal = isLocal; flag.flagValue = value; return flag; } // TODO(b/341764371): migrate aconfig flag push to GMS core @VisibleForTesting @GuardedBy("mLock") public ProtoOutputStream handleBulkSyncToNewStorage() { // get marker or add marker if it does not exist final String bulkSyncMarkerName = new String("aconfigd_marker/bulk_synced"); Setting markerSetting = mSettings.get(bulkSyncMarkerName); if (markerSetting == null) { markerSetting = new Setting( bulkSyncMarkerName, "false", false, "aconfig", "aconfig"); mSettings.put(bulkSyncMarkerName, markerSetting); } if (enableAconfigStorageDaemon()) { if (markerSetting.value.equals("true")) { // CASE 1, flag is on, bulk sync marker true, nothing to do return null; } else { // CASE 2, flag is on, bulk sync marker false. Do following two tasks // (1) Do bulk sync here. // (2) After bulk sync, set marker to true. // first add storage reset request ProtoOutputStream requests = new ProtoOutputStream(); AconfigdJavaUtils.writeResetStorageRequest(requests); // loop over all settings and add flag override requests final int numSettings = mSettings.size(); int num_requests = 0; for (int i = 0; i < numSettings; i++) { String name = mSettings.keyAt(i); Setting setting = mSettings.valueAt(i); FlagOverrideToSync flag = getFlagOverrideToSync(name, setting.getValue()); if (flag == null) { continue; } ++num_requests; AconfigdJavaUtils.writeFlagOverrideRequest( requests, flag.packageName, flag.flagName, flag.flagValue, flag.isLocal); } Slog.i(LOG_TAG, num_requests + " flag override requests created"); // mark sync has been done markerSetting.value = "true"; scheduleWriteIfNeededLocked(); return requests; } } else { if (markerSetting.value.equals("true")) { // CASE 3, flag is off, bulk sync marker true, clear the marker markerSetting.value = "false"; scheduleWriteIfNeededLocked(); return null; } else { // CASE 4, flag is off, bulk sync marker false, nothing to do return null; } } } @GuardedBy("mLock") Loading
packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +117 −0 Original line number Diff line number Diff line Loading @@ -29,12 +29,18 @@ import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.Xml; import android.util.proto.ProtoOutputStream; import com.android.providers.settings.SettingsState.FlagOverrideToSync; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.modules.utils.TypedXmlSerializer; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.DisableFlags; import android.platform.test.flag.junit.SetFlagsRule; import com.google.common.base.Strings; import java.io.ByteArrayOutputStream; Loading Loading @@ -947,4 +953,115 @@ public class SettingsStateTest { + testValue1.length() /* size for default */) * Character.BYTES; assertEquals(expectedMemUsageForPackage2, settingsState.getMemoryUsage(package2)); } @Test public void testGetFlagOverrideToSync() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); parsed_flags flags = parsed_flags .newBuilder() .addParsedFlag(parsed_flag .newBuilder() .setPackage("com.android.flags") .setName("flag1") .setNamespace("test_namespace") .setDescription("test flag") .addBug("12345678") .setState(Aconfig.flag_state.DISABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .build(); synchronized (lock) { Map<String, Map<String, String>> defaults = new HashMap<>(); settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults); Map<String, String> namespaceDefaults = defaults.get("test_namespace"); assertEquals(1, namespaceDefaults.keySet().size()); settingsState.addAconfigDefaultValuesFromMap(defaults); } // invalid flag name assertTrue(settingsState.getFlagOverrideToSync( "invalid_flag", "false") == null); // non aconfig flag assertTrue(settingsState.getFlagOverrideToSync( "some_namespace/some_flag", "false") == null); // server override FlagOverrideToSync flag = settingsState.getFlagOverrideToSync( "test_namespace/com.android.flags.flag1", "false"); assertTrue(flag != null); assertEquals(flag.packageName, "com.android.flags"); assertEquals(flag.flagName, "flag1"); assertEquals(flag.flagValue, "false"); assertEquals(flag.isLocal, false); // local override flag = settingsState.getFlagOverrideToSync( "device_config_overrides/test_namespace:com.android.flags.flag1", "false"); assertTrue(flag != null); assertEquals(flag.packageName, "com.android.flags"); assertEquals(flag.flagName, "flag1"); assertEquals(flag.flagValue, "false"); assertEquals(flag.isLocal, true); } @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Test @EnableFlags(com.android.aconfig_new_storage.Flags.FLAG_ENABLE_ACONFIG_STORAGE_DAEMON) public void testHandleBulkSyncWithAconfigdEnabled() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { settingsState.insertSettingLocked("aconfigd_marker/bulk_synced", "false", null, false, "aconfig"); // first bulk sync ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(); assertTrue(requests != null); String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); assertEquals("true", value); // send time should no longer bulk sync requests = settingsState.handleBulkSyncToNewStorage(); assertTrue(requests == null); value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); assertEquals("true", value); } } @Test @DisableFlags(com.android.aconfig_new_storage.Flags.FLAG_ENABLE_ACONFIG_STORAGE_DAEMON) public void testHandleBulkSyncWithAconfigdDisabled() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { settingsState.insertSettingLocked("aconfigd_marker/bulk_synced", "true", null, false, "aconfig"); // when aconfigd is off, should change the marker to false ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(); assertTrue(requests == null); String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); assertEquals("false", value); // marker started with false value, after call, it should remain false requests = settingsState.handleBulkSyncToNewStorage(); assertTrue(requests == null); value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); assertEquals("false", value); } } }