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

Commit ea9881d8 authored by Matt Pape's avatar Matt Pape
Browse files

Add DeviceConfig.onPropertiesChangedListener.

Add a listener which replies with an object containing one or many flags
as well as parsing methods. This should replace the previous listener,
but we need to keep the old one around for now because Google Play
services is reliant on it.

Bug: 126414261
Test: atest FrameworksCoreTests:DeviceConfigTest

Change-Id: Ie8d32eced077a8df17be824e94271bd6b15fae3d
parent 54a6bf01
Loading
Loading
Loading
Loading
+7 −2
Original line number Original line Diff line number Diff line
@@ -5851,6 +5851,7 @@ package android.provider {
  }
  }
  public final class DeviceConfig {
  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 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 boolean getBoolean(String, String, boolean);
    method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(String, String, float);
    method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(String, String, float);
@@ -5858,7 +5859,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 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 getProperty(String, String);
    method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(String, 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 void resetToDefaults(int, @Nullable String);
    method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(String, String, String, boolean);
    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";
    field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
@@ -5893,8 +5895,11 @@ package android.provider {
    field public static final String NAMESPACE = "intelligence_attention";
    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 {
  public static interface DeviceConfig.OnPropertyChangedListener {
    method public default void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties);
    method public void onPropertyChanged(String, String, String);
    method public void onPropertyChanged(String, String, String);
  }
  }
+7 −2
Original line number Original line Diff line number Diff line
@@ -2007,6 +2007,7 @@ package android.provider {
  }
  }


  public final class DeviceConfig {
  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 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 boolean getBoolean(String, String, boolean);
    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static float getFloat(String, String, float);
    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static float getFloat(String, String, float);
@@ -2014,15 +2015,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 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 getProperty(String, String);
    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getString(String, 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 void resetToDefaults(int, @Nullable String);
    method @RequiresPermission("android.permission.WRITE_DEVICE_CONFIG") public static boolean setProperty(String, String, String, boolean);
    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_AUTOFILL = "autofill";
    field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
    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 {
  public static interface DeviceConfig.OnPropertyChangedListener {
    method public default void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties);
    method public void onPropertyChanged(String, String, String);
    method public void onPropertyChanged(String, String, String);
  }
  }


+110 −27
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ import android.content.ContentResolver;
import android.database.ContentObserver;
import android.database.ContentObserver;
import android.net.Uri;
import android.net.Uri;
import android.provider.Settings.ResetMode;
import android.provider.Settings.ResetMode;
import android.util.ArrayMap;
import android.util.Pair;
import android.util.Pair;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
@@ -398,8 +399,11 @@ public final class DeviceConfig {


    private static final Object sLock = new Object();
    private static final Object sLock = new Object();
    @GuardedBy("sLock")
    @GuardedBy("sLock")
    private static Map<OnPropertyChangedListener, Pair<String, Executor>> sListeners =
    private static ArrayMap<OnPropertyChangedListener, Pair<String, Executor>> sSingleListeners =
            new HashMap<>();
            new ArrayMap<>();
    @GuardedBy("sLock")
    private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
            new ArrayMap<>();
    @GuardedBy("sLock")
    @GuardedBy("sLock")
    private static Map<String, Pair<ContentObserver, Integer>> sNamespaces = new HashMap<>();
    private static Map<String, Pair<ContentObserver, Integer>> sNamespaces = new HashMap<>();


@@ -597,20 +601,58 @@ public final class DeviceConfig {
            @NonNull String namespace,
            @NonNull String namespace,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull OnPropertyChangedListener onPropertyChangedListener) {
            @NonNull OnPropertyChangedListener onPropertyChangedListener) {
        // TODO enforce READ_DEVICE_CONFIG permission
        synchronized (sLock) {
        synchronized (sLock) {
            Pair<String, Executor> oldNamespace = sListeners.get(onPropertyChangedListener);
            Pair<String, Executor> oldNamespace = sSingleListeners.get(onPropertyChangedListener);
            if (oldNamespace == null) {
            if (oldNamespace == null) {
                // Brand new listener, add it to the list.
                // Brand new listener, add it to the list.
                sListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
                sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
                incrementNamespace(namespace);
                incrementNamespace(namespace);
            } else if (namespace.equals(oldNamespace.first)) {
            } else if (namespace.equals(oldNamespace.first)) {
                // Listener is already registered for this namespace, update executor just in case.
                // 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 {
            } else {
                // Update this listener from an old namespace to the new one.
                // Update this listener from an old namespace to the new one.
                decrementNamespace(sListeners.get(onPropertyChangedListener).first);
                decrementNamespace(sSingleListeners.get(onPropertyChangedListener).first);
                sListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
                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);
                incrementNamespace(namespace);
            }
            }
        }
        }
@@ -627,11 +669,33 @@ public final class DeviceConfig {
    @SystemApi
    @SystemApi
    @TestApi
    @TestApi
    public static void removeOnPropertyChangedListener(
    public static void removeOnPropertyChangedListener(
            OnPropertyChangedListener onPropertyChangedListener) {
            @NonNull OnPropertyChangedListener onPropertyChangedListener) {
        Preconditions.checkNotNull(onPropertyChangedListener);
        synchronized (sLock) {
        synchronized (sLock) {
            if (sListeners.containsKey(onPropertyChangedListener)) {
            if (sSingleListeners.containsKey(onPropertyChangedListener)) {
                decrementNamespace(sListeners.get(onPropertyChangedListener).first);
                decrementNamespace(sSingleListeners.get(onPropertyChangedListener).first);
                sListeners.remove(onPropertyChangedListener);
                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 name = pathSegments.get(2);
        final String value = getProperty(namespace, name);
        final String value = getProperty(namespace, name);
        synchronized (sLock) {
        synchronized (sLock) {
            for (final OnPropertyChangedListener listener : sListeners.keySet()) {
            // OnPropertiesChangedListeners
                if (namespace.equals(sListeners.get(listener).first)) {
            for (int i = 0; i < sListeners.size(); i++) {
                    sListeners.get(listener).second.execute(new Runnable() {
                if (namespace.equals(sListeners.valueAt(i).first)) {
                    final int j = i;
                    sListeners.valueAt(i).second.execute(new Runnable() {
                        @Override
                        @Override
                        public void run() {
                        public void run() {
                            Map<String, String> propertyMap = new HashMap(1);
                            Map<String, String> propertyMap = new HashMap(1);
                            propertyMap.put(name, value);
                            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>
     * <p>
     * Override {@link #onPropertyChanged(String, String, String)} to handle callbacks for changes.
     * 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.
         * @param value     The new value of the property which has changed.
         */
         */
        void onPropertyChanged(String namespace, String name, String value);
        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.
         * Called when one or more properties have changed.
         *
         *
         * @param properties Contains the complete collection of properties which have changed for a
         * @param properties Contains the complete collection of properties which have changed for a
         *                   single namespace.
         *                   single namespace.
         */
         */
        default void onPropertiesChanged(@NonNull Properties properties) {
        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));
            }
        }
    }
    }


    /**
    /**
+20 −22
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package android.provider;
package android.provider;


import static android.provider.DeviceConfig.OnPropertiesChangedListener;
import static android.provider.DeviceConfig.OnPropertyChangedListener;
import static android.provider.DeviceConfig.OnPropertyChangedListener;


import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertThat;
@@ -224,29 +225,31 @@ public class DeviceConfigTest {
    }
    }


    @Test
    @Test
    public void testListener_propertiesCallback() throws InterruptedException {
    public void testOnPropertiesChangedListener() throws InterruptedException {
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        final CountDownLatch countDownLatch = new CountDownLatch(1);


        OnPropertyChangedListener changeListener = new OnPropertyChangedListener() {
        OnPropertiesChangedListener changeListener = (properties) -> {
            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.getNamespace()).isEqualTo(sNamespace);
                assertThat(properties.getKeyset().size()).isEqualTo(1);
            assertThat(properties.getKeyset()).contains(sKey);
            assertThat(properties.getKeyset()).contains(sKey);
            assertThat(properties.getString(sKey, "default_value")).isEqualTo(sValue);
            assertThat(properties.getString(sKey, "default_value")).isEqualTo(sValue);
            countDownLatch.countDown();
            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
    @Test
    public void testListener_legacyCallback() throws InterruptedException {
    public void testOnPropertyChangedListener() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        CountDownLatch countDownLatch = new CountDownLatch(1);


        OnPropertyChangedListener changeListener = (namespace, name, value) -> {
        OnPropertyChangedListener changeListener = (namespace, name, value) -> {
@@ -256,12 +259,6 @@ public class DeviceConfigTest {
            countDownLatch.countDown();
            countDownLatch.countDown();
        };
        };


        testListener(countDownLatch, changeListener);

    }

    private void testListener(CountDownLatch countDownLatch,
            OnPropertyChangedListener changeListener) {
        try {
        try {
            DeviceConfig.addOnPropertyChangedListener(sNamespace,
            DeviceConfig.addOnPropertyChangedListener(sNamespace,
                    ActivityThread.currentApplication().getMainExecutor(), changeListener);
                    ActivityThread.currentApplication().getMainExecutor(), changeListener);
@@ -273,6 +270,7 @@ public class DeviceConfigTest {
        } finally {
        } finally {
            DeviceConfig.removeOnPropertyChangedListener(changeListener);
            DeviceConfig.removeOnPropertyChangedListener(changeListener);
        }
        }

    }
    }


    private static boolean deleteViaContentProvider(String namespace, String key) {
    private static boolean deleteViaContentProvider(String namespace, String key) {