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

Commit 79f2408e authored by Matt Pape's avatar Matt Pape Committed by Android (Google) Code Review
Browse files

Merge "Add DeviceConfig.onPropertiesChangedListener."

parents 81d86eea ea9881d8
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -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);
  }
+7 −2
Original line number Diff line number Diff line
@@ -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);
  }

+110 −27
Original line number Diff line number Diff line
@@ -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.
         */
        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);
    }

    /**
+20 −22
Original line number Diff line number Diff line
@@ -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) {
        OnPropertiesChangedListener changeListener = (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();
            }
        };

        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) {