Merge "Add DeviceConfig.onPropertiesChangedListener."
This commit is contained in:
@@ -5845,6 +5845,7 @@ package android.provider {
|
||||
}
|
||||
|
||||
public final class DeviceConfig {
|
||||
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
|
||||
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertyChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertyChangedListener);
|
||||
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(String, String, boolean);
|
||||
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(String, String, float);
|
||||
@@ -5852,7 +5853,8 @@ package android.provider {
|
||||
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static long getLong(String, String, long);
|
||||
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getProperty(String, String);
|
||||
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(String, String, String);
|
||||
method public static void removeOnPropertyChangedListener(android.provider.DeviceConfig.OnPropertyChangedListener);
|
||||
method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
|
||||
method public static void removeOnPropertyChangedListener(@NonNull android.provider.DeviceConfig.OnPropertyChangedListener);
|
||||
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
|
||||
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(String, String, String, boolean);
|
||||
field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
|
||||
@@ -5887,8 +5889,11 @@ package android.provider {
|
||||
field public static final String NAMESPACE = "intelligence_attention";
|
||||
}
|
||||
|
||||
public static interface DeviceConfig.OnPropertiesChangedListener {
|
||||
method public void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties);
|
||||
}
|
||||
|
||||
public static interface DeviceConfig.OnPropertyChangedListener {
|
||||
method public default void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties);
|
||||
method public void onPropertyChanged(String, String, String);
|
||||
}
|
||||
|
||||
|
||||
@@ -2002,6 +2002,7 @@ package android.provider {
|
||||
}
|
||||
|
||||
public final class DeviceConfig {
|
||||
method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
|
||||
method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static void addOnPropertyChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertyChangedListener);
|
||||
method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static boolean getBoolean(String, String, boolean);
|
||||
method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static float getFloat(String, String, float);
|
||||
@@ -2009,15 +2010,19 @@ package android.provider {
|
||||
method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static long getLong(String, String, long);
|
||||
method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getProperty(String, String);
|
||||
method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getString(String, String, String);
|
||||
method public static void removeOnPropertyChangedListener(android.provider.DeviceConfig.OnPropertyChangedListener);
|
||||
method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
|
||||
method public static void removeOnPropertyChangedListener(@NonNull android.provider.DeviceConfig.OnPropertyChangedListener);
|
||||
method @RequiresPermission("android.permission.WRITE_DEVICE_CONFIG") public static void resetToDefaults(int, @Nullable String);
|
||||
method @RequiresPermission("android.permission.WRITE_DEVICE_CONFIG") public static boolean setProperty(String, String, String, boolean);
|
||||
field public static final String NAMESPACE_AUTOFILL = "autofill";
|
||||
field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
|
||||
}
|
||||
|
||||
public static interface DeviceConfig.OnPropertiesChangedListener {
|
||||
method public void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties);
|
||||
}
|
||||
|
||||
public static interface DeviceConfig.OnPropertyChangedListener {
|
||||
method public default void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties);
|
||||
method public void onPropertyChanged(String, String, String);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.content.ContentResolver;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings.ResetMode;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
@@ -398,8 +399,11 @@ public final class DeviceConfig {
|
||||
|
||||
private static final Object sLock = new Object();
|
||||
@GuardedBy("sLock")
|
||||
private static Map<OnPropertyChangedListener, Pair<String, Executor>> sListeners =
|
||||
new HashMap<>();
|
||||
private static ArrayMap<OnPropertyChangedListener, Pair<String, Executor>> sSingleListeners =
|
||||
new ArrayMap<>();
|
||||
@GuardedBy("sLock")
|
||||
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
|
||||
new ArrayMap<>();
|
||||
@GuardedBy("sLock")
|
||||
private static Map<String, Pair<ContentObserver, Integer>> sNamespaces = new HashMap<>();
|
||||
|
||||
@@ -597,20 +601,58 @@ public final class DeviceConfig {
|
||||
@NonNull String namespace,
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull OnPropertyChangedListener onPropertyChangedListener) {
|
||||
// TODO enforce READ_DEVICE_CONFIG permission
|
||||
synchronized (sLock) {
|
||||
Pair<String, Executor> oldNamespace = sListeners.get(onPropertyChangedListener);
|
||||
Pair<String, Executor> oldNamespace = sSingleListeners.get(onPropertyChangedListener);
|
||||
if (oldNamespace == null) {
|
||||
// Brand new listener, add it to the list.
|
||||
sListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
|
||||
sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
|
||||
incrementNamespace(namespace);
|
||||
} else if (namespace.equals(oldNamespace.first)) {
|
||||
// Listener is already registered for this namespace, update executor just in case.
|
||||
sListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
|
||||
sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
|
||||
} else {
|
||||
// Update this listener from an old namespace to the new one.
|
||||
decrementNamespace(sListeners.get(onPropertyChangedListener).first);
|
||||
sListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
|
||||
decrementNamespace(sSingleListeners.get(onPropertyChangedListener).first);
|
||||
sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
|
||||
incrementNamespace(namespace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for property changes.
|
||||
* <p>
|
||||
* This listener will be called whenever properties in the specified namespace change. Callbacks
|
||||
* will be made on the specified executor. Future calls to this method with the same listener
|
||||
* will replace the old namespace and executor. Remove the listener entirely by calling
|
||||
* {@link #removeOnPropertiesChangedListener(OnPropertiesChangedListener)}.
|
||||
*
|
||||
* @param namespace The namespace containing properties to monitor.
|
||||
* @param executor The executor which will be used to run callbacks.
|
||||
* @param onPropertiesChangedListener The listener to add.
|
||||
* @hide
|
||||
* @see #removeOnPropertiesChangedListener(OnPropertiesChangedListener)
|
||||
*/
|
||||
@SystemApi
|
||||
@TestApi
|
||||
@RequiresPermission(READ_DEVICE_CONFIG)
|
||||
public static void addOnPropertiesChangedListener(
|
||||
@NonNull String namespace,
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
|
||||
synchronized (sLock) {
|
||||
Pair<String, Executor> oldNamespace = sListeners.get(onPropertiesChangedListener);
|
||||
if (oldNamespace == null) {
|
||||
// Brand new listener, add it to the list.
|
||||
sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
|
||||
incrementNamespace(namespace);
|
||||
} else if (namespace.equals(oldNamespace.first)) {
|
||||
// Listener is already registered for this namespace, update executor just in case.
|
||||
sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
|
||||
} else {
|
||||
// Update this listener from an old namespace to the new one.
|
||||
decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
|
||||
sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
|
||||
incrementNamespace(namespace);
|
||||
}
|
||||
}
|
||||
@@ -627,11 +669,33 @@ public final class DeviceConfig {
|
||||
@SystemApi
|
||||
@TestApi
|
||||
public static void removeOnPropertyChangedListener(
|
||||
OnPropertyChangedListener onPropertyChangedListener) {
|
||||
@NonNull OnPropertyChangedListener onPropertyChangedListener) {
|
||||
Preconditions.checkNotNull(onPropertyChangedListener);
|
||||
synchronized (sLock) {
|
||||
if (sListeners.containsKey(onPropertyChangedListener)) {
|
||||
decrementNamespace(sListeners.get(onPropertyChangedListener).first);
|
||||
sListeners.remove(onPropertyChangedListener);
|
||||
if (sSingleListeners.containsKey(onPropertyChangedListener)) {
|
||||
decrementNamespace(sSingleListeners.get(onPropertyChangedListener).first);
|
||||
sSingleListeners.remove(onPropertyChangedListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a listener for property changes. The listener will receive no further notification of
|
||||
* property changes.
|
||||
*
|
||||
* @param onPropertiesChangedListener The listener to remove.
|
||||
* @hide
|
||||
* @see #addOnPropertiesChangedListener(String, Executor, OnPropertiesChangedListener)
|
||||
*/
|
||||
@SystemApi
|
||||
@TestApi
|
||||
public static void removeOnPropertiesChangedListener(
|
||||
@NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
|
||||
Preconditions.checkNotNull(onPropertiesChangedListener);
|
||||
synchronized (sLock) {
|
||||
if (sListeners.containsKey(onPropertiesChangedListener)) {
|
||||
decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
|
||||
sListeners.remove(onPropertiesChangedListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -700,14 +764,30 @@ public final class DeviceConfig {
|
||||
final String name = pathSegments.get(2);
|
||||
final String value = getProperty(namespace, name);
|
||||
synchronized (sLock) {
|
||||
for (final OnPropertyChangedListener listener : sListeners.keySet()) {
|
||||
if (namespace.equals(sListeners.get(listener).first)) {
|
||||
sListeners.get(listener).second.execute(new Runnable() {
|
||||
// OnPropertiesChangedListeners
|
||||
for (int i = 0; i < sListeners.size(); i++) {
|
||||
if (namespace.equals(sListeners.valueAt(i).first)) {
|
||||
final int j = i;
|
||||
sListeners.valueAt(i).second.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Map<String, String> propertyMap = new HashMap(1);
|
||||
propertyMap.put(name, value);
|
||||
listener.onPropertiesChanged(new Properties(namespace, propertyMap));
|
||||
sListeners.keyAt(j)
|
||||
.onPropertiesChanged(new Properties(namespace, propertyMap));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
// OnPropertyChangedListeners
|
||||
for (int i = 0; i < sSingleListeners.size(); i++) {
|
||||
if (namespace.equals(sSingleListeners.valueAt(i).first)) {
|
||||
final int j = i;
|
||||
sSingleListeners.valueAt(i).second.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
sSingleListeners.keyAt(j).onPropertyChanged(namespace, name, value);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -717,7 +797,7 @@ public final class DeviceConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for monitoring to properties.
|
||||
* Interface for monitoring single property changes.
|
||||
* <p>
|
||||
* Override {@link #onPropertyChanged(String, String, String)} to handle callbacks for changes.
|
||||
*
|
||||
@@ -734,22 +814,25 @@ public final class DeviceConfig {
|
||||
* @param value The new value of the property which has changed.
|
||||
*/
|
||||
void onPropertyChanged(String namespace, String name, String value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for monitoring changes to properties.
|
||||
* <p>
|
||||
* Override {@link #onPropertiesChanged(Properties)} to handle callbacks for changes.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@TestApi
|
||||
public interface OnPropertiesChangedListener {
|
||||
/**
|
||||
* Called when one or more properties have changed.
|
||||
*
|
||||
* @param properties Contains the complete collection of properties which have changed for a
|
||||
* single namespace.
|
||||
* single namespace.
|
||||
*/
|
||||
default void onPropertiesChanged(@NonNull Properties properties) {
|
||||
// During the transitional period, this method calls the old one to ensure legacy
|
||||
// callers continue to function as expected. Ignore this if you are implementing it for
|
||||
// yourself.
|
||||
String namespace = properties.getNamespace();
|
||||
for (String name : properties.getKeyset()) {
|
||||
onPropertyChanged(namespace, name, properties.getString(name, null));
|
||||
}
|
||||
}
|
||||
void onPropertiesChanged(@NonNull Properties properties);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.provider;
|
||||
|
||||
import static android.provider.DeviceConfig.OnPropertiesChangedListener;
|
||||
import static android.provider.DeviceConfig.OnPropertyChangedListener;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
@@ -224,29 +225,31 @@ public class DeviceConfigTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListener_propertiesCallback() throws InterruptedException {
|
||||
public void testOnPropertiesChangedListener() throws InterruptedException {
|
||||
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
|
||||
OnPropertyChangedListener changeListener = new OnPropertyChangedListener() {
|
||||
public void onPropertyChanged(String namespace, String name, String value) {
|
||||
// ignore legacy callback
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPropertiesChanged(DeviceConfig.Properties properties) {
|
||||
assertThat(properties.getNamespace()).isEqualTo(sNamespace);
|
||||
assertThat(properties.getKeyset().size()).isEqualTo(1);
|
||||
assertThat(properties.getKeyset()).contains(sKey);
|
||||
assertThat(properties.getString(sKey, "default_value")).isEqualTo(sValue);
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
OnPropertiesChangedListener changeListener = (properties) -> {
|
||||
assertThat(properties.getNamespace()).isEqualTo(sNamespace);
|
||||
assertThat(properties.getKeyset()).contains(sKey);
|
||||
assertThat(properties.getString(sKey, "default_value")).isEqualTo(sValue);
|
||||
countDownLatch.countDown();
|
||||
};
|
||||
|
||||
testListener(countDownLatch, changeListener);
|
||||
try {
|
||||
DeviceConfig.addOnPropertiesChangedListener(sNamespace,
|
||||
ActivityThread.currentApplication().getMainExecutor(), changeListener);
|
||||
DeviceConfig.setProperty(sNamespace, sKey, sValue, 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 testListener_legacyCallback() throws InterruptedException {
|
||||
public void testOnPropertyChangedListener() throws InterruptedException {
|
||||
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
|
||||
OnPropertyChangedListener changeListener = (namespace, name, value) -> {
|
||||
@@ -256,12 +259,6 @@ public class DeviceConfigTest {
|
||||
countDownLatch.countDown();
|
||||
};
|
||||
|
||||
testListener(countDownLatch, changeListener);
|
||||
|
||||
}
|
||||
|
||||
private void testListener(CountDownLatch countDownLatch,
|
||||
OnPropertyChangedListener changeListener) {
|
||||
try {
|
||||
DeviceConfig.addOnPropertyChangedListener(sNamespace,
|
||||
ActivityThread.currentApplication().getMainExecutor(), changeListener);
|
||||
@@ -273,6 +270,7 @@ public class DeviceConfigTest {
|
||||
} finally {
|
||||
DeviceConfig.removeOnPropertyChangedListener(changeListener);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static boolean deleteViaContentProvider(String namespace, String key) {
|
||||
|
||||
Reference in New Issue
Block a user