blob: 92650e114a660f755f388296ac90672b8b591ee9 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.provider;
import static android.Manifest.permission.READ_DEVICE_CONFIG;
import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.provider.Settings.ResetMode;
import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
/**
* Device level configuration parameters which can be tuned by a separate configuration service.
*
* @hide
*/
@SystemApi
@TestApi
public final class DeviceConfig {
/**
* The content:// style URL for the config table.
*
* @hide
*/
public static final Uri CONTENT_URI = Uri.parse("content://" + Settings.AUTHORITY + "/config");
/**
* Namespace for all Game Driver features.
*
* @hide
*/
@SystemApi
public static final String NAMESPACE_GAME_DRIVER = "game_driver";
/**
* Namespace for autofill feature that provides suggestions across all apps when
* the user interacts with input fields.
*
* @hide
*/
@SystemApi
public static final String NAMESPACE_AUTOFILL = "autofill";
/**
* ContentCapture-related properties definitions.
*
* @hide
*/
@SystemApi
public interface ContentCapture {
String NAMESPACE = "content_capture";
/**
* Property used by {@code com.android.server.SystemServer} on start to decide whether
* the Content Capture service should be created or not.
*
* <p>Possible values are:
*
* <ul>
* <li>If set to {@code default}, it will only be set if the OEM provides and defines the
* service name by overlaying {@code config_defaultContentCaptureService} (this is the
* "default" mode)
* <li>If set to {@code always}, it will always be enabled, even when the resource is not
* overlaid (this is useful during development and to run the CTS tests on AOSP builds).
* <li>Otherwise, it's explicitly disabled (this could work as a "kill switch" so OEMs
* can disable it remotely in case of emergency by setting to something else (like
* {@code "false"}); notice that it's also disabled if the OEM doesn't explicitly set one
* of the values above).
* </ul>
*
* @hide
*/
// TODO(b/121153631): revert back to SERVICE_EXPLICITLY_ENABLED approach
String PROPERTY_CONTENTCAPTURE_ENABLED = "enable_contentcapture";
}
/**
* Namespace for content capture feature used by on-device machine intelligence
* to provide suggestions in a privacy-safe manner.
*
* @hide
*/
@SystemApi
public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
/**
* Namespace for all input-related features that are used at the native level.
* These features are applied at reboot.
*
* @hide
*/
@SystemApi
public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
/**
* Namespace for all netd related features.
*
* @hide
*/
@SystemApi
public static final String NAMESPACE_NETD_NATIVE = "netd_native";
/**
* Namespace for features related to the ExtServices Notification Assistant.
* These features are applied immediately.
*
* @hide
*/
@SystemApi
public interface NotificationAssistant {
String NAMESPACE = "notification_assistant";
/**
* Whether the Notification Assistant should generate replies for notifications.
*/
String GENERATE_REPLIES = "generate_replies";
/**
* Whether the Notification Assistant should generate contextual actions for notifications.
*/
String GENERATE_ACTIONS = "generate_actions";
}
/**
* Namespace for all runtime native related features.
*
* @hide
*/
@SystemApi
public interface RuntimeNative {
String NAMESPACE = "runtime_native";
}
/**
* Namespace for all activity manager related features that are used at the native level.
* These features are applied at reboot.
*
* @hide
*/
@SystemApi
public interface ActivityManagerNativeBoot {
String NAMESPACE = "activity_manager_native_boot";
String OFFLOAD_QUEUE_ENABLED = "offload_queue_enabled";
}
/**
* Namespace for attention-based features provided by on-device machine intelligence.
*
* @hide
*/
@SystemApi
public interface IntelligenceAttention {
String NAMESPACE = "intelligence_attention";
/** If {@code true}, enables the attention features. */
String ATTENTION_ENABLED = "attention_enabled";
/** Settings for the attention features. */
String ATTENTION_SETTINGS = "attention_settings";
}
/**
* Privacy related properties definitions.
*
* @hide
*/
@SystemApi
@TestApi
public interface Privacy {
String NAMESPACE = "privacy";
/**
* Whether to show the Permissions Hub.
*
* @hide
*/
@SystemApi
String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
/**
* Whether to show location access check notifications.
*/
String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled";
}
/**
* Telephony related properties definitions.
*
* @hide
*/
@SystemApi
public interface Telephony {
String NAMESPACE = "telephony";
/**
* Ringer ramping time in milliseconds.
*/
String RAMPING_RINGER_DURATION = "ramping_ringer_duration";
/**
* Whether to apply ramping ringer on incoming phone calls.
*/
String RAMPING_RINGER_ENABLED = "ramping_ringer_enabled";
/**
* Vibration time in milliseconds before ramping ringer starts.
*/
String RAMPING_RINGER_VIBRATION_DURATION = "ramping_ringer_vibration_duration";
}
/**
* Namespace for how dex runs. The feature may requires reboot to a clean state.
*
* @hide
*/
@SystemApi
public interface DexBoot {
String NAMESPACE = "dex_boot";
String PRIV_APPS_OOB_ENABLED = "priv_apps_oob_enabled";
String PRIV_APPS_OOB_WHITELIST = "priv_apps_oob_whitelist";
}
/**
* Namespace for activity manager related features. These features will be applied
* immediately upon change.
*
* @hide
*/
@SystemApi
public interface ActivityManager {
String NAMESPACE = "activity_manager";
/**
* App compaction flags. See {@link com.android.server.am.AppCompactor}.
*/
String KEY_USE_COMPACTION = "use_compaction";
String KEY_COMPACT_ACTION_1 = "compact_action_1";
String KEY_COMPACT_ACTION_2 = "compact_action_2";
String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
String KEY_COMPACT_STATSD_SAMPLE_RATE = "compact_statsd_sample_rate";
/**
* Maximum number of cached processes. See
* {@link com.android.server.am.ActivityManagerConstants}.
*/
String KEY_MAX_CACHED_PROCESSES = "max_cached_processes";
}
/**
* Namespace for {@link AttentionManagerService} related features.
*
* @hide
*/
@SystemApi
public interface AttentionManagerService {
String NAMESPACE = "attention_manager_service";
/** If {@code true}, enables {@link AttentionManagerService} features. */
String SERVICE_ENABLED = "service_enabled";
/** Allows a CTS to inject a fake implementation. */
String COMPONENT_NAME = "component_name";
}
/**
* Namespace for storage-related features.
*
* @hide
*/
@SystemApi
public interface Storage {
String NAMESPACE = "storage";
/**
* If {@code 1}, enables the isolated storage feature. If {@code -1},
* disables the isolated storage feature. If {@code 0}, uses the default
* value from the build system.
*/
String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled";
}
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static Map<OnPropertyChangedListener, Pair<String, Executor>> sListeners =
new HashMap<>();
@GuardedBy("sLock")
private static Map<String, Pair<ContentObserver, Integer>> sNamespaces = new HashMap<>();
// Should never be invoked
private DeviceConfig() {
}
/**
* Look up the value of a property for a particular namespace.
*
* @param namespace The namespace containing the property to look up.
* @param name The name of the property to look up.
* @return the corresponding value, or null if not present.
* @hide
*/
@SystemApi
@RequiresPermission(READ_DEVICE_CONFIG)
public static String getProperty(String namespace, String name) {
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
String compositeName = createCompositeName(namespace, name);
return Settings.Config.getString(contentResolver, compositeName);
}
/**
* Create a new property with the 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
* namespaces and might have different values in any or all namespaces.
* <p>
* The method takes an argument indicating whether to make the value the default for this
* property.
* <p>
* All properties stored for a particular scope can be reverted to their default values
* by passing the namespace to {@link #resetToDefaults(int, String)}.
*
* @param namespace The namespace containing the property to create or update.
* @param name The name of the property to create or update.
* @param value The value to store for the property.
* @param makeDefault Whether to make the new value the default one.
* @return True if the value was set, false if the storage implementation throws errors.
* @hide
* @see #resetToDefaults(int, String).
*/
@SystemApi
@TestApi
@RequiresPermission(WRITE_DEVICE_CONFIG)
public static boolean setProperty(
String namespace, String name, String value, boolean makeDefault) {
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
String compositeName = createCompositeName(namespace, name);
return Settings.Config.putString(contentResolver, compositeName, value, makeDefault);
}
/**
* Reset properties to their default values.
* <p>
* The method accepts an optional namespace parameter. If provided, only properties set within
* that namespace will be reset. Otherwise, all properties will be reset.
*
* @param resetMode The reset mode to use.
* @param namespace Optionally, the specific namespace which resets will be limited to.
* @hide
* @see #setProperty(String, String, String, boolean)
*/
@SystemApi
@TestApi
@RequiresPermission(WRITE_DEVICE_CONFIG)
public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) {
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
Settings.Config.resetToDefaults(contentResolver, resetMode, 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 #removeOnPropertyChangedListener(OnPropertyChangedListener)}.
*
* @param namespace The namespace containing properties to monitor.
* @param executor The executor which will be used to run callbacks.
* @param onPropertyChangedListener The listener to add.
* @hide
* @see #removeOnPropertyChangedListener(OnPropertyChangedListener)
*/
@SystemApi
@RequiresPermission(READ_DEVICE_CONFIG)
public static void addOnPropertyChangedListener(
@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);
if (oldNamespace == null) {
// Brand new listener, add it to the list.
sListeners.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));
} else {
// Update this listener from an old namespace to the new one.
decrementNamespace(sListeners.get(onPropertyChangedListener).first);
sListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
incrementNamespace(namespace);
}
}
}
/**
* Remove a listener for property changes. The listener will receive no further notification of
* property changes.
*
* @param onPropertyChangedListener The listener to remove.
* @hide
* @see #addOnPropertyChangedListener(String, Executor, OnPropertyChangedListener)
*/
@SystemApi
public static void removeOnPropertyChangedListener(
OnPropertyChangedListener onPropertyChangedListener) {
synchronized (sLock) {
if (sListeners.containsKey(onPropertyChangedListener)) {
decrementNamespace(sListeners.get(onPropertyChangedListener).first);
sListeners.remove(onPropertyChangedListener);
}
}
}
private static String createCompositeName(String namespace, String name) {
return namespace + "/" + name;
}
private static Uri createNamespaceUri(String namespace) {
return CONTENT_URI.buildUpon().appendPath(namespace).build();
}
/**
* Increment the count used to represent the number of listeners subscribed to the given
* namespace. If this is the first (i.e. incrementing from 0 to 1) for the given namespace, a
* ContentObserver is registered.
*
* @param namespace The namespace to increment the count for.
*/
@GuardedBy("sLock")
private static void incrementNamespace(String namespace) {
Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
if (namespaceCount != null) {
sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second + 1));
} else {
// This is a new namespace, register a ContentObserver for it.
ContentObserver contentObserver = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange, Uri uri) {
handleChange(uri);
}
};
ActivityThread.currentApplication().getContentResolver()
.registerContentObserver(createNamespaceUri(namespace), true, contentObserver);
sNamespaces.put(namespace, new Pair<>(contentObserver, 1));
}
}
/**
* Decrement the count used to represent th enumber of listeners subscribed to the given
* namespace. If this is the final decrement call (i.e. decrementing from 1 to 0) for the given
* namespace, the ContentObserver that had been tracking it will be removed.
*
* @param namespace The namespace to decrement the count for.
*/
@GuardedBy("sLock")
private static void decrementNamespace(String namespace) {
Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
if (namespaceCount == null) {
// This namespace is not registered and does not need to be decremented
return;
} else if (namespaceCount.second > 1) {
sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second - 1));
} else {
// Decrementing a namespace to zero means we no longer need its ContentObserver.
ActivityThread.currentApplication().getContentResolver()
.unregisterContentObserver(namespaceCount.first);
sNamespaces.remove(namespace);
}
}
private static void handleChange(Uri uri) {
List<String> pathSegments = uri.getPathSegments();
// pathSegments(0) is "config"
String namespace = pathSegments.get(1);
String name = pathSegments.get(2);
String value = getProperty(namespace, name);
synchronized (sLock) {
for (OnPropertyChangedListener listener : sListeners.keySet()) {
if (namespace.equals(sListeners.get(listener).first)) {
sListeners.get(listener).second.execute(new Runnable() {
@Override
public void run() {
listener.onPropertyChanged(namespace, name, value);
}
});
}
}
}
}
/**
* Interface for monitoring to properties.
* <p>
* Override {@link #onPropertyChanged(String, String, String)} to handle callbacks for changes.
*
* @hide
*/
@SystemApi
public interface OnPropertyChangedListener {
/**
* Called when a property has changed.
*
* @param namespace The namespace containing the property which has changed.
* @param name The name 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);
}
}