[DeviceConfig] add an API to delete a property
Currently there is only a shell command to delete a property. Adding an API to do the same. BUG: 184001163 Test: atest android.provider.DeviceConfigTest Test: atest CtsDeviceConfigTestCases Test: atest com.android.server.testables.TestableDeviceConfigTest Change-Id: I43d7177c509501efd65e32ab3ad000d17d6ffb6c
This commit is contained in:
@@ -9079,6 +9079,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 addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
|
||||||
|
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean deleteProperty(@NonNull String, @NonNull String);
|
||||||
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
|
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
|
||||||
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
|
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
|
||||||
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
|
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
|
||||||
|
|||||||
@@ -794,7 +794,7 @@ public final class DeviceConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new property with the the provided name and value in the provided namespace, or
|
* Create a new property with the provided name and value in the provided namespace, or
|
||||||
* update the value of such a property if it already exists. The same name can exist in multiple
|
* update the value of such a property if it already exists. The same name can exist in multiple
|
||||||
* namespaces and might have different values in any or all namespaces.
|
* namespaces and might have different values in any or all namespaces.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -843,6 +843,22 @@ public final class DeviceConfig {
|
|||||||
properties.mMap);
|
properties.mMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a property with the provided name and value in the provided namespace
|
||||||
|
*
|
||||||
|
* @param namespace The namespace containing the property to create or update.
|
||||||
|
* @param name The name of the property to create or update.
|
||||||
|
* @return True if the property was deleted or it did not exist in the first place.
|
||||||
|
* False if the storage implementation throws errors.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@SystemApi
|
||||||
|
@RequiresPermission(WRITE_DEVICE_CONFIG)
|
||||||
|
public static boolean deleteProperty(@NonNull String namespace, @NonNull String name) {
|
||||||
|
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
|
||||||
|
return Settings.Config.deleteString(contentResolver, namespace, name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset properties to their default values by removing the underlying values.
|
* Reset properties to their default values by removing the underlying values.
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@@ -2770,6 +2770,7 @@ public final class Settings {
|
|||||||
// for the fast path of retrieving settings.
|
// for the fast path of retrieving settings.
|
||||||
private final String mCallGetCommand;
|
private final String mCallGetCommand;
|
||||||
private final String mCallSetCommand;
|
private final String mCallSetCommand;
|
||||||
|
private final String mCallDeleteCommand;
|
||||||
private final String mCallListCommand;
|
private final String mCallListCommand;
|
||||||
private final String mCallSetAllCommand;
|
private final String mCallSetAllCommand;
|
||||||
|
|
||||||
@@ -2781,17 +2782,19 @@ public final class Settings {
|
|||||||
private GenerationTracker mGenerationTracker;
|
private GenerationTracker mGenerationTracker;
|
||||||
|
|
||||||
<T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
|
<T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
|
||||||
String setCommand, ContentProviderHolder providerHolder, Class<T> callerClass) {
|
String setCommand, String deleteCommand, ContentProviderHolder providerHolder,
|
||||||
this(uri, getCommand, setCommand, null, null, providerHolder,
|
Class<T> callerClass) {
|
||||||
|
this(uri, getCommand, setCommand, deleteCommand, null, null, providerHolder,
|
||||||
callerClass);
|
callerClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
|
private <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
|
||||||
String setCommand, String listCommand, String setAllCommand,
|
String setCommand, String deleteCommand, String listCommand, String setAllCommand,
|
||||||
ContentProviderHolder providerHolder, Class<T> callerClass) {
|
ContentProviderHolder providerHolder, Class<T> callerClass) {
|
||||||
mUri = uri;
|
mUri = uri;
|
||||||
mCallGetCommand = getCommand;
|
mCallGetCommand = getCommand;
|
||||||
mCallSetCommand = setCommand;
|
mCallSetCommand = setCommand;
|
||||||
|
mCallDeleteCommand = deleteCommand;
|
||||||
mCallListCommand = listCommand;
|
mCallListCommand = listCommand;
|
||||||
mCallSetAllCommand = setAllCommand;
|
mCallSetAllCommand = setAllCommand;
|
||||||
mProviderHolder = providerHolder;
|
mProviderHolder = providerHolder;
|
||||||
@@ -2849,6 +2852,20 @@ public final class Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean deleteStringForUser(ContentResolver cr, String name, final int userHandle) {
|
||||||
|
try {
|
||||||
|
Bundle arg = new Bundle();
|
||||||
|
arg.putInt(CALL_METHOD_USER_KEY, userHandle);
|
||||||
|
IContentProvider cp = mProviderHolder.getProvider(cr);
|
||||||
|
cp.call(cr.getAttributionSource(),
|
||||||
|
mProviderHolder.mUri.getAuthority(), mCallDeleteCommand, name, arg);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.w(TAG, "Can't delete key " + name + " in " + mUri, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
|
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
|
||||||
// Check if the target settings key is readable. Reject if the caller is not system and
|
// Check if the target settings key is readable. Reject if the caller is not system and
|
||||||
@@ -3311,6 +3328,7 @@ public final class Settings {
|
|||||||
CONTENT_URI,
|
CONTENT_URI,
|
||||||
CALL_METHOD_GET_SYSTEM,
|
CALL_METHOD_GET_SYSTEM,
|
||||||
CALL_METHOD_PUT_SYSTEM,
|
CALL_METHOD_PUT_SYSTEM,
|
||||||
|
CALL_METHOD_DELETE_SYSTEM,
|
||||||
sProviderHolder,
|
sProviderHolder,
|
||||||
System.class);
|
System.class);
|
||||||
|
|
||||||
@@ -5631,6 +5649,7 @@ public final class Settings {
|
|||||||
CONTENT_URI,
|
CONTENT_URI,
|
||||||
CALL_METHOD_GET_SECURE,
|
CALL_METHOD_GET_SECURE,
|
||||||
CALL_METHOD_PUT_SECURE,
|
CALL_METHOD_PUT_SECURE,
|
||||||
|
CALL_METHOD_DELETE_SECURE,
|
||||||
sProviderHolder,
|
sProviderHolder,
|
||||||
Secure.class);
|
Secure.class);
|
||||||
|
|
||||||
@@ -15022,6 +15041,7 @@ public final class Settings {
|
|||||||
CONTENT_URI,
|
CONTENT_URI,
|
||||||
CALL_METHOD_GET_GLOBAL,
|
CALL_METHOD_GET_GLOBAL,
|
||||||
CALL_METHOD_PUT_GLOBAL,
|
CALL_METHOD_PUT_GLOBAL,
|
||||||
|
CALL_METHOD_DELETE_GLOBAL,
|
||||||
sProviderHolder,
|
sProviderHolder,
|
||||||
Global.class);
|
Global.class);
|
||||||
|
|
||||||
@@ -16564,6 +16584,7 @@ public final class Settings {
|
|||||||
DeviceConfig.CONTENT_URI,
|
DeviceConfig.CONTENT_URI,
|
||||||
CALL_METHOD_GET_CONFIG,
|
CALL_METHOD_GET_CONFIG,
|
||||||
CALL_METHOD_PUT_CONFIG,
|
CALL_METHOD_PUT_CONFIG,
|
||||||
|
CALL_METHOD_DELETE_CONFIG,
|
||||||
CALL_METHOD_LIST_CONFIG,
|
CALL_METHOD_LIST_CONFIG,
|
||||||
CALL_METHOD_SET_ALL_CONFIG,
|
CALL_METHOD_SET_ALL_CONFIG,
|
||||||
sProviderHolder,
|
sProviderHolder,
|
||||||
@@ -16672,6 +16693,26 @@ public final class Settings {
|
|||||||
throw new DeviceConfig.BadConfigException();
|
throw new DeviceConfig.BadConfigException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a name/value pair from the database for the specified namespace.
|
||||||
|
*
|
||||||
|
* @param resolver to access the database with.
|
||||||
|
* @param namespace to delete the name/value pair from.
|
||||||
|
* @param name to delete.
|
||||||
|
* @return true if the value was deleted, false on database errors. If the name/value pair
|
||||||
|
* did not exist, return True.
|
||||||
|
*
|
||||||
|
* @see #resetToDefaults(ContentResolver, int, String)
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
|
||||||
|
static boolean deleteString(@NonNull ContentResolver resolver, @NonNull String namespace,
|
||||||
|
@NonNull String name) {
|
||||||
|
return sNameValueCache.deleteStringForUser(resolver,
|
||||||
|
createCompositeName(namespace, name), resolver.getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the values to their defaults.
|
* Reset the values to their defaults.
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@@ -832,4 +832,80 @@ public class DeviceConfigTest {
|
|||||||
return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
|
return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteProperty_nullNamespace() {
|
||||||
|
try {
|
||||||
|
DeviceConfig.deleteProperty(null, KEY);
|
||||||
|
Assert.fail("Null namespace should have resulted in an NPE.");
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteProperty_nullName() {
|
||||||
|
try {
|
||||||
|
DeviceConfig.deleteProperty(NAMESPACE, null);
|
||||||
|
Assert.fail("Null name should have resulted in an NPE.");
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deletePropertyString() {
|
||||||
|
final String value = "new_value";
|
||||||
|
final String default_value = "default";
|
||||||
|
DeviceConfig.setProperty(NAMESPACE, KEY, value, false);
|
||||||
|
DeviceConfig.deleteProperty(NAMESPACE, KEY);
|
||||||
|
final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value);
|
||||||
|
assertThat(result).isEqualTo(default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deletePropertyBoolean() {
|
||||||
|
final boolean value = true;
|
||||||
|
final boolean default_value = false;
|
||||||
|
DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
|
||||||
|
DeviceConfig.deleteProperty(NAMESPACE, KEY);
|
||||||
|
final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
|
||||||
|
assertThat(result).isEqualTo(default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deletePropertyInt() {
|
||||||
|
final int value = 123;
|
||||||
|
final int default_value = 999;
|
||||||
|
DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
|
||||||
|
DeviceConfig.deleteProperty(NAMESPACE, KEY);
|
||||||
|
final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
|
||||||
|
assertThat(result).isEqualTo(default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deletePropertyLong() {
|
||||||
|
final long value = 456789;
|
||||||
|
final long default_value = 123456;
|
||||||
|
DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
|
||||||
|
DeviceConfig.deleteProperty(NAMESPACE, KEY);
|
||||||
|
final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
|
||||||
|
assertThat(result).isEqualTo(default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deletePropertyFloat() {
|
||||||
|
final float value = 456.789f;
|
||||||
|
final float default_value = 123.456f;
|
||||||
|
DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
|
||||||
|
DeviceConfig.deleteProperty(NAMESPACE, KEY);
|
||||||
|
final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
|
||||||
|
assertThat(result).isEqualTo(default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteProperty_empty() {
|
||||||
|
assertThat(DeviceConfig.deleteProperty(NAMESPACE, KEY)).isTrue();
|
||||||
|
final String result = DeviceConfig.getString(NAMESPACE, KEY, null);
|
||||||
|
assertThat(result).isNull();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,15 @@ public final class TestableDeviceConfig implements StaticMockFixture {
|
|||||||
}
|
}
|
||||||
).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean()));
|
).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean()));
|
||||||
|
|
||||||
|
doAnswer((Answer<Boolean>) invocationOnMock -> {
|
||||||
|
String namespace = invocationOnMock.getArgument(0);
|
||||||
|
String name = invocationOnMock.getArgument(1);
|
||||||
|
mKeyValueMap.remove(getKey(namespace, name));
|
||||||
|
invokeListeners(namespace, getProperties(namespace, name, null));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
).when(() -> DeviceConfig.deleteProperty(anyString(), anyString()));
|
||||||
|
|
||||||
doAnswer((Answer<Boolean>) invocationOnMock -> {
|
doAnswer((Answer<Boolean>) invocationOnMock -> {
|
||||||
Properties properties = invocationOnMock.getArgument(0);
|
Properties properties = invocationOnMock.getArgument(0);
|
||||||
String namespace = properties.getNamespace();
|
String namespace = properties.getNamespace();
|
||||||
|
|||||||
@@ -102,6 +102,20 @@ public class TestableDeviceConfigTest {
|
|||||||
assertThat(DeviceConfig.getProperty(sNamespace, newKey)).isEqualTo(newValue);
|
assertThat(DeviceConfig.getProperty(sNamespace, newKey)).isEqualTo(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteProperty() {
|
||||||
|
DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
|
||||||
|
assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
|
||||||
|
DeviceConfig.deleteProperty(sNamespace, sKey);
|
||||||
|
assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isNull();
|
||||||
|
String newNamespace = "namespace2";
|
||||||
|
String newValue = "value2";
|
||||||
|
DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
|
||||||
|
assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isEqualTo(newValue);
|
||||||
|
DeviceConfig.deleteProperty(newNamespace, sKey);
|
||||||
|
assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getProperties_empty() {
|
public void getProperties_empty() {
|
||||||
String newKey = "key2";
|
String newKey = "key2";
|
||||||
@@ -189,6 +203,27 @@ public class TestableDeviceConfigTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListener_deleteProperty() throws InterruptedException {
|
||||||
|
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
OnPropertiesChangedListener changeListener = (properties) -> {
|
||||||
|
assertThat(properties.getNamespace()).isEqualTo(sNamespace);
|
||||||
|
assertThat(properties.getKeyset()).containsExactly(sKey);
|
||||||
|
assertThat(properties.getString(sKey, "bogus_value")).isEqualTo("bogus_value");
|
||||||
|
assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
|
||||||
|
countDownLatch.countDown();
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
DeviceConfig.addOnPropertiesChangedListener(sNamespace,
|
||||||
|
ActivityThread.currentApplication().getMainExecutor(), changeListener);
|
||||||
|
DeviceConfig.deleteProperty(sNamespace, sKey);
|
||||||
|
assertThat(countDownLatch.await(
|
||||||
|
WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
|
||||||
|
} finally {
|
||||||
|
DeviceConfig.removeOnPropertiesChangedListener(changeListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user