From aaaf0fad4cb2ab187ab3d8cd72b7be251569a45b Mon Sep 17 00:00:00 2001 From: Forrest Dunlap Date: Fri, 13 Aug 2021 12:56:12 -0700 Subject: [PATCH 1/3] Re-enable DeviceConfig onPropertiesChangedListener tests. Test: atest android.provider.DeviceConfigTest Bug: 142727848 Change-Id: Iea47f2c5bba7a20cb76ec03dd417d06db2930c1e --- .../android/provider/DeviceConfigTest.java | 128 ++++++++++-------- 1 file changed, 68 insertions(+), 60 deletions(-) diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java index fd7753b66a466..5b3be58ae0de2 100644 --- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java +++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java @@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.testng.Assert.assertThrows; +import android.app.ActivityThread; import android.content.ContentResolver; import android.os.Bundle; import android.platform.test.annotations.Presubmit; @@ -35,6 +36,12 @@ import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + /** Tests that ensure appropriate settings are backed up. */ @Presubmit @RunWith(AndroidJUnit4.class) @@ -701,66 +708,67 @@ public class DeviceConfigTest { assertThat(result.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE); } - // TODO(mpape): resolve b/142727848 and re-enable listener tests -// @Test -// public void onPropertiesChangedListener_setPropertyCallback() throws InterruptedException { -// final CountDownLatch countDownLatch = new CountDownLatch(1); -// -// DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> { -// assertThat(properties.getNamespace()).isEqualTo(NAMESPACE); -// assertThat(properties.getKeyset()).contains(KEY); -// assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE); -// countDownLatch.countDown(); -// }; -// -// try { -// DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, -// ActivityThread.currentApplication().getMainExecutor(), changeListener); -// DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); -// assertThat(countDownLatch.await( -// WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); -// } catch (InterruptedException e) { -// Assert.fail(e.getMessage()); -// } finally { -// DeviceConfig.removeOnPropertiesChangedListener(changeListener); -// } -// } -// -// @Test -// public void onPropertiesChangedListener_setPropertiesCallback() throws InterruptedException { -// final CountDownLatch countDownLatch = new CountDownLatch(1); -// DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); -// DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false); -// -// Map keyValues = new HashMap<>(2); -// keyValues.put(KEY, VALUE2); -// keyValues.put(KEY3, VALUE3); -// Properties setProperties = new Properties(NAMESPACE, keyValues); -// -// DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> { -// assertThat(properties.getNamespace()).isEqualTo(NAMESPACE); -// assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3); -// // KEY updated from VALUE to VALUE2 -// assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE2); -// // KEY2 deleted (returns default_value) -// assertThat(properties.getString(KEY2, "default_value")).isEqualTo("default_value"); -// //KEY3 added with VALUE3 -// assertThat(properties.getString(KEY3, "default_value")).isEqualTo(VALUE3); -// countDownLatch.countDown(); -// }; -// -// try { -// DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, -// ActivityThread.currentApplication().getMainExecutor(), changeListener); -// DeviceConfig.setProperties(setProperties); -// assertThat(countDownLatch.await( -// WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); -// } catch (InterruptedException e) { -// Assert.fail(e.getMessage()); -// } finally { -// DeviceConfig.removeOnPropertiesChangedListener(changeListener); -// } -// } + @Test + public void onPropertiesChangedListener_setPropertyCallback() throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + + DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> { + assertThat(properties.getNamespace()).isEqualTo(NAMESPACE); + assertThat(properties.getKeyset()).contains(KEY); + assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE); + countDownLatch.countDown(); + }; + + try { + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, + Objects.requireNonNull(ActivityThread.currentApplication()).getMainExecutor(), + changeListener); + DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); + assertThat(countDownLatch.await( + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); + } catch (InterruptedException e) { + Assert.fail(e.getMessage()); + } finally { + DeviceConfig.removeOnPropertiesChangedListener(changeListener); + } + } + + @Test + public void onPropertiesChangedListener_setPropertiesCallback() { + final CountDownLatch countDownLatch = new CountDownLatch(1); + DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); + DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false); + + Map keyValues = new HashMap<>(2); + keyValues.put(KEY, VALUE2); + keyValues.put(KEY3, VALUE3); + Properties setProperties = new Properties(NAMESPACE, keyValues); + + DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> { + assertThat(properties.getNamespace()).isEqualTo(NAMESPACE); + assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3); + // KEY updated from VALUE to VALUE2 + assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE2); + // KEY2 deleted (returns default_value) + assertThat(properties.getString(KEY2, "default_value")).isEqualTo("default_value"); + //KEY3 added with VALUE3 + assertThat(properties.getString(KEY3, "default_value")).isEqualTo(VALUE3); + countDownLatch.countDown(); + }; + + try { + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, + Objects.requireNonNull(ActivityThread.currentApplication()).getMainExecutor(), + changeListener); + DeviceConfig.setProperties(setProperties); + assertThat(countDownLatch.await( + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); + } catch (InterruptedException | DeviceConfig.BadConfigException e) { + Assert.fail(e.getMessage()); + } finally { + DeviceConfig.removeOnPropertiesChangedListener(changeListener); + } + } @Test public void syncDisabling() throws Exception { From 738b7e6ce55d731690abb264a2d62ad20641ec7c Mon Sep 17 00:00:00 2001 From: Forrest Dunlap Date: Mon, 23 Aug 2021 17:30:16 -0700 Subject: [PATCH 2/3] Add DeviceConfigInterface.java Test: atest android.provider.DeviceConfigTest Bug: 142727848 Change-Id: I275edf3c4be26c8e09ba1a853e80bf54adea5e1f --- .../provider/DeviceConfigInterface.java | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 core/java/android/provider/DeviceConfigInterface.java diff --git a/core/java/android/provider/DeviceConfigInterface.java b/core/java/android/provider/DeviceConfigInterface.java new file mode 100644 index 0000000000000..0a888f40517fd --- /dev/null +++ b/core/java/android/provider/DeviceConfigInterface.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2021 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 android.provider; + +import static android.provider.Settings.ResetMode; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.provider.DeviceConfig.BadConfigException; +import android.provider.DeviceConfig.Properties; + +import java.util.concurrent.Executor; + +/** + * Abstraction around {@link DeviceConfig} to allow faking device configuration in tests. + * + * @hide + */ +public interface DeviceConfigInterface { + + /** + * @hide + * @see DeviceConfig#getProperty + */ + @Nullable + String getProperty(@NonNull String namespace, @NonNull String name); + + /** + * @hide + * @see DeviceConfig#getProperties + */ + @NonNull + Properties getProperties(@NonNull String namespace, @NonNull String... names); + + /** + * @hide + * @see DeviceConfig#setProperty + */ + boolean setProperty(@NonNull String namespace, @NonNull String name, @Nullable String value, + boolean makeDefault); + + /** + * @hide + * @see DeviceConfig#setProperties + */ + boolean setProperties(@NonNull Properties properties) throws BadConfigException; + + /** + * @hide + * @see DeviceConfig#deleteProperty + */ + boolean deleteProperty(@NonNull String namespace, @NonNull String name); + + /** + * @hide + * @see DeviceConfig#resetToDefaults + */ + void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace); + + /** + * @hide + * @see DeviceConfig#getString + */ + @NonNull + String getString(@NonNull String namespace, @NonNull String name, @NonNull String defaultValue); + + /** + * @hide + * @see DeviceConfig#getInt + */ + int getInt(@NonNull String namespace, @NonNull String name, int defaultValue); + + /** + * @hide + * @see DeviceConfig#getLong + */ + long getLong(@NonNull String namespace, @NonNull String name, long defaultValue); + + /** + * @hide + * @see DeviceConfig#getBoolean + */ + boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue); + + /** + * @hide + * @see DeviceConfig#getFloat + */ + float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue); + + /** + * @hide + * @see DeviceConfig#addOnPropertiesChangedListener + */ + void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor, + @NonNull DeviceConfig.OnPropertiesChangedListener listener); + + /** + * @hide + * @see DeviceConfig#removeOnPropertiesChangedListener + */ + void removeOnPropertiesChangedListener( + @NonNull DeviceConfig.OnPropertiesChangedListener listener); + + /** + * Calls through to the real {@link DeviceConfig}. + * + * @hide + */ + @NonNull + DeviceConfigInterface REAL = new DeviceConfigInterface() { + @Override + public String getProperty(String namespace, String name) { + return DeviceConfig.getProperty(namespace, name); + } + + @Override + public DeviceConfig.Properties getProperties(@NonNull String namespace, + @NonNull String... names) { + return DeviceConfig.getProperties(namespace, names); + } + + @Override + public boolean setProperty(@NonNull String namespace, + @NonNull String name, + @Nullable String value, boolean makeDefault) { + return DeviceConfig.setProperty(namespace, name, value, makeDefault); + } + + @Override + public boolean setProperties(@NonNull Properties properties) + throws BadConfigException { + return DeviceConfig.setProperties(properties); + } + + @Override + public boolean deleteProperty(@NonNull String namespace, + @NonNull String name) { + return DeviceConfig.deleteProperty(namespace, name); + } + + @Override + public void resetToDefaults(int resetMode, @Nullable String namespace) { + DeviceConfig.resetToDefaults(resetMode, namespace); + } + + @Override + public String getString(String namespace, String name, String defaultValue) { + return DeviceConfig.getString(namespace, name, defaultValue); + } + + @Override + public int getInt(String namespace, String name, int defaultValue) { + return DeviceConfig.getInt(namespace, name, defaultValue); + } + + @Override + public long getLong(String namespace, String name, long defaultValue) { + return DeviceConfig.getLong(namespace, name, defaultValue); + } + + @Override + public boolean getBoolean(@NonNull String namespace, @NonNull String name, + boolean defaultValue) { + return DeviceConfig.getBoolean(namespace, name, defaultValue); + } + + @Override + public float getFloat(@NonNull String namespace, @NonNull String name, + float defaultValue) { + return DeviceConfig.getFloat(namespace, name, defaultValue); + } + + @Override + public void addOnPropertiesChangedListener(String namespace, Executor executor, + DeviceConfig.OnPropertiesChangedListener listener) { + DeviceConfig.addOnPropertiesChangedListener(namespace, executor, listener); + } + + @Override + public void removeOnPropertiesChangedListener( + DeviceConfig.OnPropertiesChangedListener listener) { + DeviceConfig.removeOnPropertiesChangedListener(listener); + } + }; +} From 5ecbf52e3128a3d96fe5bf288aaf583c7f370f6b Mon Sep 17 00:00:00 2001 From: Forrest Dunlap Date: Mon, 23 Aug 2021 18:10:12 -0700 Subject: [PATCH 3/3] Remove DeviceConfigInterface from Services & refactor FakeDeviceConfigInterface to use android.provider.DeviceConfigInterface Test: manual atest Bug: 142727848 Change-Id: I6bf41df5b30a52887f0f9755c1332c244f19c791 --- services/core/Android.bp | 7 - .../server/display/DisplayModeDirector.java | 2 +- .../server/utils/DeviceConfigInterface.java | 121 ------------------ .../server/wm/HighRefreshRateDenylist.java | 2 +- .../server/wm/WindowManagerConstants.java | 2 +- .../server/wm/WindowManagerService.java | 2 +- services/tests/servicestests/Android.bp | 2 - .../testutils/FakeDeviceConfigInterface.java | 69 +++++++++- 8 files changed, 66 insertions(+), 141 deletions(-) delete mode 100644 services/core/java/com/android/server/utils/DeviceConfigInterface.java diff --git a/services/core/Android.bp b/services/core/Android.bp index c87a6fa0b98b3..6505c20e0d0f3 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -207,10 +207,3 @@ prebuilt_etc { name: "protolog.conf.json.gz", src: ":services.core.json.gz", } - -filegroup { - name: "services.core-sources-deviceconfig-interface", - srcs: [ - "java/com/android/server/utils/DeviceConfigInterface.java", - ], -} diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index a25cfd8f3ba44..e16a241e65729 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -41,6 +41,7 @@ import android.os.Message; import android.os.SystemClock; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.provider.DeviceConfigInterface; import android.provider.Settings; import android.text.TextUtils; import android.util.IndentingPrintWriter; @@ -61,7 +62,6 @@ import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.sensors.SensorManagerInternal; import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener; import com.android.server.statusbar.StatusBarManagerInternal; -import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; import java.text.SimpleDateFormat; diff --git a/services/core/java/com/android/server/utils/DeviceConfigInterface.java b/services/core/java/com/android/server/utils/DeviceConfigInterface.java deleted file mode 100644 index ff609031b57c5..0000000000000 --- a/services/core/java/com/android/server/utils/DeviceConfigInterface.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2019 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.server.utils; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.provider.DeviceConfig; - -import java.util.concurrent.Executor; - -/** - * Abstraction around {@link DeviceConfig} to allow faking device configuration in tests. - */ -public interface DeviceConfigInterface { - /** - * @see DeviceConfig#getProperty - */ - @Nullable - String getProperty(@NonNull String namespace, @NonNull String name); - - /** - * @see DeviceConfig#getString - */ - @NonNull - String getString(@NonNull String namespace, @NonNull String name, @NonNull String defaultValue); - - /** - * @see DeviceConfig#getInt - */ - int getInt(@NonNull String namespace, @NonNull String name, int defaultValue); - - /** - * @see DeviceConfig#getLong - */ - long getLong(@NonNull String namespace, @NonNull String name, long defaultValue); - - /** - * @see DeviceConfig#getBoolean - */ - boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue); - - /** - * @see DeviceConfig#getFloat - */ - float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue); - - /** - * @see DeviceConfig#addOnPropertiesChangedListener - */ - void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor, - @NonNull DeviceConfig.OnPropertiesChangedListener listener); - - /** - * @see DeviceConfig#removeOnPropertiesChangedListener - */ - void removeOnPropertiesChangedListener( - @NonNull DeviceConfig.OnPropertiesChangedListener listener); - - /** - * Calls through to the real {@link DeviceConfig}. - */ - DeviceConfigInterface REAL = new DeviceConfigInterface() { - @Override - public String getProperty(String namespace, String name) { - return DeviceConfig.getProperty(namespace, name); - } - - @Override - public String getString(String namespace, String name, String defaultValue) { - return DeviceConfig.getString(namespace, name, defaultValue); - } - - @Override - public int getInt(String namespace, String name, int defaultValue) { - return DeviceConfig.getInt(namespace, name, defaultValue); - } - - @Override - public long getLong(String namespace, String name, long defaultValue) { - return DeviceConfig.getLong(namespace, name, defaultValue); - } - - @Override - public boolean getBoolean(@NonNull String namespace, @NonNull String name, - boolean defaultValue) { - return DeviceConfig.getBoolean(namespace, name, defaultValue); - } - - @Override - public float getFloat(@NonNull String namespace, @NonNull String name, - float defaultValue) { - return DeviceConfig.getFloat(namespace, name, defaultValue); - } - - @Override - public void addOnPropertiesChangedListener(String namespace, Executor executor, - DeviceConfig.OnPropertiesChangedListener listener) { - DeviceConfig.addOnPropertiesChangedListener(namespace, executor, listener); - } - - @Override - public void removeOnPropertiesChangedListener( - DeviceConfig.OnPropertiesChangedListener listener) { - DeviceConfig.removeOnPropertiesChangedListener(listener); - } - }; -} diff --git a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java index 92baadf5ee69a..5e8d795ce955d 100644 --- a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java +++ b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java @@ -22,12 +22,12 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.Resources; import android.provider.DeviceConfig; +import android.provider.DeviceConfigInterface; import android.util.ArraySet; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; -import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java index a5ebf9ac74b9b..bdbcd166dc0bf 100644 --- a/services/core/java/com/android/server/wm/WindowManagerConstants.java +++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java @@ -21,9 +21,9 @@ import static android.provider.AndroidDeviceConfig.KEY_SYSTEM_GESTURE_EXCLUSION_ import android.provider.AndroidDeviceConfig; import android.provider.DeviceConfig; +import android.provider.DeviceConfigInterface; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; import java.util.Objects; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f24d74a2e356e..10d28da65b0ab 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -206,6 +206,7 @@ import android.os.SystemService; import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; +import android.provider.DeviceConfigInterface; import android.provider.Settings; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; @@ -300,7 +301,6 @@ import com.android.server.input.InputManagerService; import com.android.server.policy.WindowManagerPolicy; import com.android.server.policy.WindowManagerPolicy.ScreenOffListener; import com.android.server.power.ShutdownThread; -import com.android.server.utils.DeviceConfigInterface; import com.android.server.utils.PriorityDump; import java.io.BufferedWriter; diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index c19155f517433..11e1230df7c19 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -144,7 +144,6 @@ java_library { "utils/**/*.java", "utils/**/*.kt", "utils-mockito/**/*.kt", - ":services.core-sources-deviceconfig-interface", ], static_libs: [ "junit", @@ -161,7 +160,6 @@ java_library { "utils/**/*.java", "utils/**/*.kt", "utils-mockito/**/*.kt", - ":services.core-sources-deviceconfig-interface", ], static_libs: [ "junit", diff --git a/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java index a67f64596ef51..161232161b915 100644 --- a/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java +++ b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java @@ -18,13 +18,14 @@ package com.android.server.testutils; import android.annotation.NonNull; import android.provider.DeviceConfig; +import android.provider.DeviceConfigInterface; import android.util.ArrayMap; import android.util.Pair; import com.android.internal.util.Preconditions; -import com.android.server.utils.DeviceConfigInterface; import java.lang.reflect.Constructor; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; @@ -33,10 +34,17 @@ import java.util.concurrent.TimeUnit; public class FakeDeviceConfigInterface implements DeviceConfigInterface { + private static final String COMPOSITE_DELIMITER = "/"; private Map mProperties = new HashMap<>(); private ArrayMap> mListeners = new ArrayMap<>(); + private static String createCompositeName(@NonNull String namespace, @NonNull String name) { + Preconditions.checkNotNull(namespace); + Preconditions.checkNotNull(name); + return namespace + COMPOSITE_DELIMITER + name; + } + public void clearProperties() { mProperties.clear(); } @@ -90,6 +98,59 @@ public class FakeDeviceConfigInterface implements DeviceConfigInterface { return mProperties.get(createCompositeName(namespace, name)); } + @Override + public DeviceConfig.Properties getProperties(String namespace, String... names) { + if (!mProperties.keySet().contains(namespace)) { + return new DeviceConfig.Properties(namespace, null); + } + DeviceConfig.Properties.Builder propertiesBuilder = new DeviceConfig.Properties.Builder( + namespace); + + for (String compositeName : mProperties.keySet()) { + if (compositeName.split(COMPOSITE_DELIMITER).length != 2) { + continue; + } + + String existingPropertyNamespace = compositeName.split(COMPOSITE_DELIMITER)[0]; + String existingPropertyName = compositeName.split(COMPOSITE_DELIMITER)[1]; + + if ((names.length == 0 && existingPropertyNamespace.equals(namespace)) || Arrays.asList( + names).contains(compositeName)) { + propertiesBuilder.setString(existingPropertyName, mProperties.get(compositeName)); + } + } + + return propertiesBuilder.build(); + } + + @Override + public boolean setProperty(String namespace, String name, String value, boolean makeDefault) { + putPropertyAndNotify(namespace, name, value); + return true; + } + + @Override + public boolean setProperties(DeviceConfig.Properties properties) + throws DeviceConfig.BadConfigException { + for (String property : properties.getKeyset()) { + String compositeName = createCompositeName(properties.getNamespace(), property); + putPropertyAndNotify(properties.getNamespace(), compositeName, + properties.getString(property, "")); + } + return true; + } + + @Override + public boolean deleteProperty(String namespace, String name) { + mProperties.remove(createCompositeName(namespace, name)); + return true; + } + + @Override + public void resetToDefaults(int resetMode, String namespace) { + clearProperties(); + } + @Override public String getString(String namespace, String name, String defaultValue) { String value = getProperty(namespace, name); @@ -166,10 +227,4 @@ public class FakeDeviceConfigInterface implements DeviceConfigInterface { DeviceConfig.OnPropertiesChangedListener listener) { mListeners.remove(listener); } - - private static String createCompositeName(@NonNull String namespace, @NonNull String name) { - Preconditions.checkNotNull(namespace); - Preconditions.checkNotNull(name); - return namespace + "/" + name; - } }