Merge "Add DeviceConfig.setProperties method for atomic writing."
This commit is contained in:
@@ -391,8 +391,9 @@ public final class DeviceConfig {
|
||||
* Look up the values of multiple properties for a particular namespace. The lookup is atomic,
|
||||
* such that the values of these properties cannot change between the time when the first is
|
||||
* fetched and the time when the last is fetched.
|
||||
*
|
||||
* TODO: reference setProperties when it is added.
|
||||
* <p>
|
||||
* Each call to {@link #setProperties(Properties)} is also atomic and ensures that either none
|
||||
* or all of the change is picked up here, but never only part of it.
|
||||
*
|
||||
* @param namespace The namespace containing the properties to look up.
|
||||
* @param names The names of properties to look up, or empty to fetch all properties for the
|
||||
@@ -559,6 +560,27 @@ public final class DeviceConfig {
|
||||
return Settings.Config.putString(contentResolver, namespace, name, value, makeDefault);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all of the properties for a specific namespace. Pre-existing properties will be updated
|
||||
* and new properties will be added if necessary. Any pre-existing properties for the specific
|
||||
* namespace which are not part of the provided {@link Properties} object will be deleted from
|
||||
* the namespace. These changes are all applied atomically, such that no calls to read or reset
|
||||
* these properties can happen in the middle of this update.
|
||||
* <p>
|
||||
* Each call to {@link #getProperties(String, String...)} is also atomic and ensures that either
|
||||
* none or all of this update is picked up, but never only part of it.
|
||||
*
|
||||
* @param properties the complete set of properties to set for a specific namespace.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(WRITE_DEVICE_CONFIG)
|
||||
public static boolean setProperties(@NonNull Properties properties) {
|
||||
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
|
||||
return Settings.Config.setStrings(contentResolver, properties.getNamespace(),
|
||||
properties.mMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset properties to their default values.
|
||||
* <p>
|
||||
@@ -703,23 +725,26 @@ public final class DeviceConfig {
|
||||
List<String> pathSegments = uri.getPathSegments();
|
||||
// pathSegments(0) is "config"
|
||||
final String namespace = pathSegments.get(1);
|
||||
final String name = pathSegments.get(2);
|
||||
final String value;
|
||||
Map<String, String> propertyMap = new ArrayMap<>();
|
||||
try {
|
||||
value = getProperty(namespace, name);
|
||||
Properties allProperties = getProperties(namespace);
|
||||
for (int i = 2; i < pathSegments.size(); ++i) {
|
||||
String key = pathSegments.get(i);
|
||||
propertyMap.put(key, allProperties.getString(key, null));
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
// Silently failing to not crash binder or listener threads.
|
||||
Log.e(TAG, "OnPropertyChangedListener update failed: permission violation.");
|
||||
return;
|
||||
}
|
||||
Properties properties = new Properties(namespace, propertyMap);
|
||||
|
||||
synchronized (sLock) {
|
||||
for (int i = 0; i < sListeners.size(); i++) {
|
||||
if (namespace.equals(sListeners.valueAt(i).first)) {
|
||||
final OnPropertiesChangedListener listener = sListeners.keyAt(i);
|
||||
sListeners.valueAt(i).second.execute(() -> {
|
||||
Map<String, String> propertyMap = new ArrayMap<>(1);
|
||||
propertyMap.put(name, value);
|
||||
listener.onPropertiesChanged(new Properties(namespace, propertyMap));
|
||||
listener.onPropertiesChanged(properties);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -741,7 +766,11 @@ public final class DeviceConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for monitoring changes to properties.
|
||||
* Interface for monitoring changes to properties. Implementations will receive callbacks when
|
||||
* properties change, including a {@link Properties} object which contains a single namespace
|
||||
* and all of the properties which changed for that namespace. This includes properties which
|
||||
* were added, updated, or deleted. This is not necessarily a complete list of all properties
|
||||
* belonging to the namespace, as properties which don't change are omitted.
|
||||
* <p>
|
||||
* Override {@link #onPropertiesChanged(Properties)} to handle callbacks for changes.
|
||||
*
|
||||
@@ -751,10 +780,13 @@ public final class DeviceConfig {
|
||||
@TestApi
|
||||
public interface OnPropertiesChangedListener {
|
||||
/**
|
||||
* Called when one or more properties have changed.
|
||||
* Called when one or more properties have changed, providing a Properties object with all
|
||||
* of the changed properties. This object will contain only properties which have changed,
|
||||
* not the complete set of all properties belonging to the namespace.
|
||||
*
|
||||
* @param properties Contains the complete collection of properties which have changed for a
|
||||
* single namespace.
|
||||
* single namespace. This includes only those which were added, updated,
|
||||
* or deleted.
|
||||
*/
|
||||
void onPropertiesChanged(@NonNull Properties properties);
|
||||
}
|
||||
|
||||
@@ -1960,6 +1960,11 @@ public final class Settings {
|
||||
*/
|
||||
public static final String CALL_METHOD_PREFIX_KEY = "_prefix";
|
||||
|
||||
/**
|
||||
* @hide - String argument extra to the fast-path call()-based requests
|
||||
*/
|
||||
public static final String CALL_METHOD_FLAGS_KEY = "_flags";
|
||||
|
||||
/** @hide - Private call() method to write to 'system' table */
|
||||
public static final String CALL_METHOD_PUT_SYSTEM = "PUT_system";
|
||||
|
||||
@@ -1972,6 +1977,9 @@ public final class Settings {
|
||||
/** @hide - Private call() method to write to 'configuration' table */
|
||||
public static final String CALL_METHOD_PUT_CONFIG = "PUT_config";
|
||||
|
||||
/** @hide - Private call() method to write to and delete from the 'configuration' table */
|
||||
public static final String CALL_METHOD_SET_ALL_CONFIG = "SET_ALL_config";
|
||||
|
||||
/** @hide - Private call() method to delete from the 'system' table */
|
||||
public static final String CALL_METHOD_DELETE_SYSTEM = "DELETE_system";
|
||||
|
||||
@@ -2304,21 +2312,23 @@ public final class Settings {
|
||||
private final String mCallGetCommand;
|
||||
private final String mCallSetCommand;
|
||||
private final String mCallListCommand;
|
||||
private final String mCallSetAllCommand;
|
||||
|
||||
@GuardedBy("this")
|
||||
private GenerationTracker mGenerationTracker;
|
||||
|
||||
public NameValueCache(Uri uri, String getCommand, String setCommand,
|
||||
ContentProviderHolder providerHolder) {
|
||||
this(uri, getCommand, setCommand, null, providerHolder);
|
||||
this(uri, getCommand, setCommand, null, null, providerHolder);
|
||||
}
|
||||
|
||||
NameValueCache(Uri uri, String getCommand, String setCommand, String listCommand,
|
||||
ContentProviderHolder providerHolder) {
|
||||
String setAllCommand, ContentProviderHolder providerHolder) {
|
||||
mUri = uri;
|
||||
mCallGetCommand = getCommand;
|
||||
mCallSetCommand = setCommand;
|
||||
mCallListCommand = listCommand;
|
||||
mCallSetAllCommand = setAllCommand;
|
||||
mProviderHolder = providerHolder;
|
||||
}
|
||||
|
||||
@@ -2344,6 +2354,26 @@ public final class Settings {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean setStringsForPrefix(ContentResolver cr, String prefix,
|
||||
HashMap<String, String> keyValues) {
|
||||
if (mCallSetAllCommand == null) {
|
||||
// This NameValueCache does not support atomically setting multiple flags
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Bundle args = new Bundle();
|
||||
args.putString(CALL_METHOD_PREFIX_KEY, prefix);
|
||||
args.putSerializable(CALL_METHOD_FLAGS_KEY, keyValues);
|
||||
IContentProvider cp = mProviderHolder.getProvider(cr);
|
||||
cp.call(cr.getPackageName(), cr.getFeatureId(), mProviderHolder.mUri.getAuthority(),
|
||||
mCallSetAllCommand, null, args);
|
||||
} catch (RemoteException e) {
|
||||
// Not supported by the remote side
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
|
||||
final boolean isSelf = (userHandle == UserHandle.myUserId());
|
||||
@@ -13718,6 +13748,7 @@ public final class Settings {
|
||||
CALL_METHOD_GET_CONFIG,
|
||||
CALL_METHOD_PUT_CONFIG,
|
||||
CALL_METHOD_LIST_CONFIG,
|
||||
CALL_METHOD_SET_ALL_CONFIG,
|
||||
sProviderHolder);
|
||||
|
||||
/**
|
||||
@@ -13791,6 +13822,29 @@ public final class Settings {
|
||||
value, null, makeDefault, resolver.getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all name/value pairs for the provided namespace and save new name/value pairs in
|
||||
* their place.
|
||||
*
|
||||
* @param resolver to access the database with.
|
||||
* @param namespace to which the names should be set.
|
||||
* @param keyValues map of key names (without the prefix) to values.
|
||||
* @return
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
|
||||
static boolean setStrings(@NonNull ContentResolver resolver, @NonNull String namespace,
|
||||
@NonNull Map<String, String> keyValues) {
|
||||
HashMap<String, String> compositeKeyValueMap = new HashMap<>(keyValues.keySet().size());
|
||||
for (Map.Entry<String, String> entry : keyValues.entrySet()) {
|
||||
compositeKeyValueMap.put(
|
||||
createCompositeName(namespace, entry.getKey()), entry.getValue());
|
||||
}
|
||||
return sNameValueCache.setStringsForPrefix(resolver, createPrefix(namespace),
|
||||
compositeKeyValueMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the values to their defaults.
|
||||
* <p>
|
||||
|
||||
Reference in New Issue
Block a user