Loading services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +39 −92 Original line number Diff line number Diff line Loading @@ -342,33 +342,29 @@ public class SettingsToPropertiesMapper { AsyncTask.THREAD_POOL_EXECUTOR, (DeviceConfig.Properties properties) -> { HashMap<String, HashMap<String, String>> propsToStage = getStagedFlagsWithValueChange(properties); // send prop stage request to sys prop for (HashMap.Entry<String, HashMap<String, String>> entry : propsToStage.entrySet()) { String actualNamespace = entry.getKey(); HashMap<String, String> flagValuesToStage = entry.getValue(); for (String flagName : flagValuesToStage.keySet()) { String stagedValue = flagValuesToStage.get(flagName); String propertyName = "next_boot." + makeAconfigFlagPropertyName( actualNamespace, flagName); if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX) || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) { logErr("unable to construct system property for " + actualNamespace + "/" + flagName); for (String flagName : properties.getKeyset()) { String flagValue = properties.getString(flagName, null); if (flagName == null || flagValue == null) { continue; } setProperty(propertyName, stagedValue); int idx = flagName.indexOf(NAMESPACE_REBOOT_STAGING_DELIMITER); if (idx == -1 || idx == flagName.length() - 1 || idx == 0) { logErr("invalid staged flag: " + flagName); continue; } String actualNamespace = flagName.substring(0, idx); String actualFlagName = flagName.substring(idx+1); String propertyName = "next_boot." + makeAconfigFlagPropertyName( actualNamespace, actualFlagName); setProperty(propertyName, flagValue); } // send prop stage request to new storage if (enableAconfigStorageDaemon()) { stageFlagsInNewStorage(propsToStage); stageFlagsInNewStorage(properties); } }); Loading Loading @@ -607,26 +603,34 @@ public class SettingsToPropertiesMapper { * @param propsToStage */ @VisibleForTesting static void stageFlagsInNewStorage(HashMap<String, HashMap<String, String>> propsToStage) { static void stageFlagsInNewStorage(DeviceConfig.Properties props) { // write aconfigd requests proto to proto output stream int num_requests = 0; ProtoOutputStream requests = new ProtoOutputStream(); for (HashMap.Entry<String, HashMap<String, String>> entry : propsToStage.entrySet()) { String actualNamespace = entry.getKey(); HashMap<String, String> flagValuesToStage = entry.getValue(); for (String fullFlagName : flagValuesToStage.keySet()) { String stagedValue = flagValuesToStage.get(fullFlagName); int idx = fullFlagName.lastIndexOf("."); for (String flagName : props.getKeyset()) { String flagValue = props.getString(flagName, null); if (flagName == null || flagValue == null) { continue; } int idx = flagName.indexOf("*"); if (idx == -1 || idx == flagName.length() - 1 || idx == 0) { logErr("invalid local flag override: " + flagName); continue; } String actualNamespace = flagName.substring(0, idx); String fullFlagName = flagName.substring(idx+1); idx = fullFlagName.lastIndexOf("."); if (idx == -1) { logErr("invalid flag name: " + fullFlagName); continue; } String packageName = fullFlagName.substring(0, idx); String flagName = fullFlagName.substring(idx+1); writeFlagOverrideRequest(requests, packageName, flagName, stagedValue, false); String realFlagName = fullFlagName.substring(idx+1); writeFlagOverrideRequest(requests, packageName, realFlagName, flagValue, false); ++num_requests; } } if (num_requests == 0) { return; Loading Loading @@ -665,63 +669,6 @@ public class SettingsToPropertiesMapper { return propertyName; } /** * Get the flags that need to be staged in sys prop, only these with a real value * change needs to be staged in sys prop. Otherwise, the flag stage is useless and * create performance problem at sys prop side. * @param properties * @return a hash map of namespace name to actual flags to stage */ @VisibleForTesting static HashMap<String, HashMap<String, String>> getStagedFlagsWithValueChange( DeviceConfig.Properties properties) { // sort flags by actual namespace of the flag HashMap<String, HashMap<String, String>> stagedProps = new HashMap<>(); for (String flagName : properties.getKeyset()) { int idx = flagName.indexOf(NAMESPACE_REBOOT_STAGING_DELIMITER); if (idx == -1 || idx == flagName.length() - 1 || idx == 0) { logErr("invalid staged flag: " + flagName); continue; } String actualNamespace = flagName.substring(0, idx); String actualFlagName = flagName.substring(idx+1); HashMap<String, String> flagStagedValues = stagedProps.get(actualNamespace); if (flagStagedValues == null) { flagStagedValues = new HashMap<String, String>(); stagedProps.put(actualNamespace, flagStagedValues); } flagStagedValues.put(actualFlagName, properties.getString(flagName, null)); } // for each namespace, find flags with real flag value change HashMap<String, HashMap<String, String>> propsToStage = new HashMap<>(); for (HashMap.Entry<String, HashMap<String, String>> entry : stagedProps.entrySet()) { String actualNamespace = entry.getKey(); HashMap<String, String> flagStagedValues = entry.getValue(); Map<String, String> flagCurrentValues = Settings.Config.getStrings( actualNamespace, new ArrayList<String>(flagStagedValues.keySet())); HashMap<String, String> flagsToStage = new HashMap<>(); for (String flagName : flagStagedValues.keySet()) { String stagedValue = flagStagedValues.get(flagName); String currentValue = flagCurrentValues.get(flagName); if (stagedValue == null) { continue; } if (currentValue == null || !stagedValue.equalsIgnoreCase(currentValue)) { flagsToStage.put(flagName, stagedValue); } } if (!flagsToStage.isEmpty()) { propsToStage.put(actualNamespace, flagsToStage); } } return propsToStage; } private void setProperty(String key, String value) { // Check if need to clear the property if (value == null) { Loading services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java +0 −55 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import android.content.ContentResolver; import android.os.SystemProperties; import android.provider.Settings; import android.provider.DeviceConfig.Properties; import android.text.TextUtils; import com.android.dx.mockito.inline.extended.ExtendedMockito; Loading @@ -43,7 +42,6 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; /** * Test SettingsToPropertiesMapper. Loading @@ -63,7 +61,6 @@ public class SettingsToPropertiesMapperTest { private HashMap<String, String> mSystemSettingsMap; private HashMap<String, String> mGlobalSettingsMap; private HashMap<String, String> mConfigSettingsMap; @Before public void setUp() throws Exception { Loading @@ -74,11 +71,9 @@ public class SettingsToPropertiesMapperTest { .spyStatic(SystemProperties.class) .spyStatic(Settings.Global.class) .spyStatic(SettingsToPropertiesMapper.class) .spyStatic(Settings.Config.class) .startMocking(); mSystemSettingsMap = new HashMap<>(); mGlobalSettingsMap = new HashMap<>(); mConfigSettingsMap = new HashMap<>(); // Mock SystemProperties setter and various getters doAnswer((Answer<Void>) invocationOnMock -> { Loading Loading @@ -106,21 +101,6 @@ public class SettingsToPropertiesMapperTest { } ).when(() -> Settings.Global.getString(any(), anyString())); // Mock Settings.Config getstrings method doAnswer((Answer<Map<String, String>>) invocationOnMock -> { String namespace = invocationOnMock.getArgument(0); List<String> flags = invocationOnMock.getArgument(1); HashMap<String, String> values = new HashMap<>(); for (String flag : flags) { String value = mConfigSettingsMap.get(namespace + "/" + flag); if (value != null) { values.put(flag, value); } } return values; } ).when(() -> Settings.Config.getStrings(anyString(), any())); mTestMapper = new SettingsToPropertiesMapper( mMockContentResolver, TEST_MAPPING, new String[] {}, new String[] {}); } Loading Loading @@ -259,39 +239,4 @@ public class SettingsToPropertiesMapperTest { Assert.assertTrue(categories.contains("category2")); Assert.assertTrue(categories.contains("category3")); } @Test public void testGetStagedFlagsWithValueChange() { // mock up what is in the setting already mConfigSettingsMap.put("namespace_1/flag_1", "true"); mConfigSettingsMap.put("namespace_1/flag_2", "true"); // mock up input String namespace = "staged"; Map<String, String> keyValueMap = new HashMap<>(); // case 1: existing prop, stage the same value keyValueMap.put("namespace_1*flag_1", "true"); // case 2: existing prop, stage a different value keyValueMap.put("namespace_1*flag_2", "false"); // case 3: new prop keyValueMap.put("namespace_2*flag_1", "true"); Properties props = new Properties(namespace, keyValueMap); HashMap<String, HashMap<String, String>> toStageProps = SettingsToPropertiesMapper.getStagedFlagsWithValueChange(props); HashMap<String, String> namespace_1_to_stage = toStageProps.get("namespace_1"); HashMap<String, String> namespace_2_to_stage = toStageProps.get("namespace_2"); Assert.assertTrue(namespace_1_to_stage != null); Assert.assertTrue(namespace_2_to_stage != null); String namespace_1_flag_1 = namespace_1_to_stage.get("flag_1"); String namespace_1_flag_2 = namespace_1_to_stage.get("flag_2"); String namespace_2_flag_1 = namespace_2_to_stage.get("flag_1"); Assert.assertTrue(namespace_1_flag_1 == null); Assert.assertTrue(namespace_1_flag_2 != null); Assert.assertTrue(namespace_2_flag_1 != null); Assert.assertTrue(namespace_1_flag_2.equals("false")); Assert.assertTrue(namespace_2_flag_1.equals("true")); } } Loading
services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +39 −92 Original line number Diff line number Diff line Loading @@ -342,33 +342,29 @@ public class SettingsToPropertiesMapper { AsyncTask.THREAD_POOL_EXECUTOR, (DeviceConfig.Properties properties) -> { HashMap<String, HashMap<String, String>> propsToStage = getStagedFlagsWithValueChange(properties); // send prop stage request to sys prop for (HashMap.Entry<String, HashMap<String, String>> entry : propsToStage.entrySet()) { String actualNamespace = entry.getKey(); HashMap<String, String> flagValuesToStage = entry.getValue(); for (String flagName : flagValuesToStage.keySet()) { String stagedValue = flagValuesToStage.get(flagName); String propertyName = "next_boot." + makeAconfigFlagPropertyName( actualNamespace, flagName); if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX) || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) { logErr("unable to construct system property for " + actualNamespace + "/" + flagName); for (String flagName : properties.getKeyset()) { String flagValue = properties.getString(flagName, null); if (flagName == null || flagValue == null) { continue; } setProperty(propertyName, stagedValue); int idx = flagName.indexOf(NAMESPACE_REBOOT_STAGING_DELIMITER); if (idx == -1 || idx == flagName.length() - 1 || idx == 0) { logErr("invalid staged flag: " + flagName); continue; } String actualNamespace = flagName.substring(0, idx); String actualFlagName = flagName.substring(idx+1); String propertyName = "next_boot." + makeAconfigFlagPropertyName( actualNamespace, actualFlagName); setProperty(propertyName, flagValue); } // send prop stage request to new storage if (enableAconfigStorageDaemon()) { stageFlagsInNewStorage(propsToStage); stageFlagsInNewStorage(properties); } }); Loading Loading @@ -607,26 +603,34 @@ public class SettingsToPropertiesMapper { * @param propsToStage */ @VisibleForTesting static void stageFlagsInNewStorage(HashMap<String, HashMap<String, String>> propsToStage) { static void stageFlagsInNewStorage(DeviceConfig.Properties props) { // write aconfigd requests proto to proto output stream int num_requests = 0; ProtoOutputStream requests = new ProtoOutputStream(); for (HashMap.Entry<String, HashMap<String, String>> entry : propsToStage.entrySet()) { String actualNamespace = entry.getKey(); HashMap<String, String> flagValuesToStage = entry.getValue(); for (String fullFlagName : flagValuesToStage.keySet()) { String stagedValue = flagValuesToStage.get(fullFlagName); int idx = fullFlagName.lastIndexOf("."); for (String flagName : props.getKeyset()) { String flagValue = props.getString(flagName, null); if (flagName == null || flagValue == null) { continue; } int idx = flagName.indexOf("*"); if (idx == -1 || idx == flagName.length() - 1 || idx == 0) { logErr("invalid local flag override: " + flagName); continue; } String actualNamespace = flagName.substring(0, idx); String fullFlagName = flagName.substring(idx+1); idx = fullFlagName.lastIndexOf("."); if (idx == -1) { logErr("invalid flag name: " + fullFlagName); continue; } String packageName = fullFlagName.substring(0, idx); String flagName = fullFlagName.substring(idx+1); writeFlagOverrideRequest(requests, packageName, flagName, stagedValue, false); String realFlagName = fullFlagName.substring(idx+1); writeFlagOverrideRequest(requests, packageName, realFlagName, flagValue, false); ++num_requests; } } if (num_requests == 0) { return; Loading Loading @@ -665,63 +669,6 @@ public class SettingsToPropertiesMapper { return propertyName; } /** * Get the flags that need to be staged in sys prop, only these with a real value * change needs to be staged in sys prop. Otherwise, the flag stage is useless and * create performance problem at sys prop side. * @param properties * @return a hash map of namespace name to actual flags to stage */ @VisibleForTesting static HashMap<String, HashMap<String, String>> getStagedFlagsWithValueChange( DeviceConfig.Properties properties) { // sort flags by actual namespace of the flag HashMap<String, HashMap<String, String>> stagedProps = new HashMap<>(); for (String flagName : properties.getKeyset()) { int idx = flagName.indexOf(NAMESPACE_REBOOT_STAGING_DELIMITER); if (idx == -1 || idx == flagName.length() - 1 || idx == 0) { logErr("invalid staged flag: " + flagName); continue; } String actualNamespace = flagName.substring(0, idx); String actualFlagName = flagName.substring(idx+1); HashMap<String, String> flagStagedValues = stagedProps.get(actualNamespace); if (flagStagedValues == null) { flagStagedValues = new HashMap<String, String>(); stagedProps.put(actualNamespace, flagStagedValues); } flagStagedValues.put(actualFlagName, properties.getString(flagName, null)); } // for each namespace, find flags with real flag value change HashMap<String, HashMap<String, String>> propsToStage = new HashMap<>(); for (HashMap.Entry<String, HashMap<String, String>> entry : stagedProps.entrySet()) { String actualNamespace = entry.getKey(); HashMap<String, String> flagStagedValues = entry.getValue(); Map<String, String> flagCurrentValues = Settings.Config.getStrings( actualNamespace, new ArrayList<String>(flagStagedValues.keySet())); HashMap<String, String> flagsToStage = new HashMap<>(); for (String flagName : flagStagedValues.keySet()) { String stagedValue = flagStagedValues.get(flagName); String currentValue = flagCurrentValues.get(flagName); if (stagedValue == null) { continue; } if (currentValue == null || !stagedValue.equalsIgnoreCase(currentValue)) { flagsToStage.put(flagName, stagedValue); } } if (!flagsToStage.isEmpty()) { propsToStage.put(actualNamespace, flagsToStage); } } return propsToStage; } private void setProperty(String key, String value) { // Check if need to clear the property if (value == null) { Loading
services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java +0 −55 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import android.content.ContentResolver; import android.os.SystemProperties; import android.provider.Settings; import android.provider.DeviceConfig.Properties; import android.text.TextUtils; import com.android.dx.mockito.inline.extended.ExtendedMockito; Loading @@ -43,7 +42,6 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; /** * Test SettingsToPropertiesMapper. Loading @@ -63,7 +61,6 @@ public class SettingsToPropertiesMapperTest { private HashMap<String, String> mSystemSettingsMap; private HashMap<String, String> mGlobalSettingsMap; private HashMap<String, String> mConfigSettingsMap; @Before public void setUp() throws Exception { Loading @@ -74,11 +71,9 @@ public class SettingsToPropertiesMapperTest { .spyStatic(SystemProperties.class) .spyStatic(Settings.Global.class) .spyStatic(SettingsToPropertiesMapper.class) .spyStatic(Settings.Config.class) .startMocking(); mSystemSettingsMap = new HashMap<>(); mGlobalSettingsMap = new HashMap<>(); mConfigSettingsMap = new HashMap<>(); // Mock SystemProperties setter and various getters doAnswer((Answer<Void>) invocationOnMock -> { Loading Loading @@ -106,21 +101,6 @@ public class SettingsToPropertiesMapperTest { } ).when(() -> Settings.Global.getString(any(), anyString())); // Mock Settings.Config getstrings method doAnswer((Answer<Map<String, String>>) invocationOnMock -> { String namespace = invocationOnMock.getArgument(0); List<String> flags = invocationOnMock.getArgument(1); HashMap<String, String> values = new HashMap<>(); for (String flag : flags) { String value = mConfigSettingsMap.get(namespace + "/" + flag); if (value != null) { values.put(flag, value); } } return values; } ).when(() -> Settings.Config.getStrings(anyString(), any())); mTestMapper = new SettingsToPropertiesMapper( mMockContentResolver, TEST_MAPPING, new String[] {}, new String[] {}); } Loading Loading @@ -259,39 +239,4 @@ public class SettingsToPropertiesMapperTest { Assert.assertTrue(categories.contains("category2")); Assert.assertTrue(categories.contains("category3")); } @Test public void testGetStagedFlagsWithValueChange() { // mock up what is in the setting already mConfigSettingsMap.put("namespace_1/flag_1", "true"); mConfigSettingsMap.put("namespace_1/flag_2", "true"); // mock up input String namespace = "staged"; Map<String, String> keyValueMap = new HashMap<>(); // case 1: existing prop, stage the same value keyValueMap.put("namespace_1*flag_1", "true"); // case 2: existing prop, stage a different value keyValueMap.put("namespace_1*flag_2", "false"); // case 3: new prop keyValueMap.put("namespace_2*flag_1", "true"); Properties props = new Properties(namespace, keyValueMap); HashMap<String, HashMap<String, String>> toStageProps = SettingsToPropertiesMapper.getStagedFlagsWithValueChange(props); HashMap<String, String> namespace_1_to_stage = toStageProps.get("namespace_1"); HashMap<String, String> namespace_2_to_stage = toStageProps.get("namespace_2"); Assert.assertTrue(namespace_1_to_stage != null); Assert.assertTrue(namespace_2_to_stage != null); String namespace_1_flag_1 = namespace_1_to_stage.get("flag_1"); String namespace_1_flag_2 = namespace_1_to_stage.get("flag_2"); String namespace_2_flag_1 = namespace_2_to_stage.get("flag_1"); Assert.assertTrue(namespace_1_flag_1 == null); Assert.assertTrue(namespace_1_flag_2 != null); Assert.assertTrue(namespace_2_flag_1 != null); Assert.assertTrue(namespace_1_flag_2.equals("false")); Assert.assertTrue(namespace_2_flag_1.equals("true")); } }