blob: ea50ae8105352eb547ca981e83ff3ff90918a9dc [file] [log] [blame]
Matt Pape1278d1c2018-12-11 13:03:49 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.provider;
18
Stanislav Zholnin596437f2018-12-28 15:34:23 +000019import static android.Manifest.permission.READ_DEVICE_CONFIG;
20import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
21
Matt Pape1278d1c2018-12-11 13:03:49 -080022import android.annotation.CallbackExecutor;
23import android.annotation.NonNull;
24import android.annotation.Nullable;
Stanislav Zholnin596437f2018-12-28 15:34:23 +000025import android.annotation.RequiresPermission;
Matt Pape1278d1c2018-12-11 13:03:49 -080026import android.annotation.SystemApi;
Philip P. Moltmann25995fe2019-01-25 14:33:11 -080027import android.annotation.TestApi;
Matt Pape1278d1c2018-12-11 13:03:49 -080028import android.app.ActivityThread;
29import android.content.ContentResolver;
Stanislav Zholnin55799502019-03-08 14:54:55 +000030import android.content.Context;
31import android.content.pm.PackageManager;
Matt Pape1278d1c2018-12-11 13:03:49 -080032import android.database.ContentObserver;
33import android.net.Uri;
34import android.provider.Settings.ResetMode;
Matt Papeea9881d2019-03-07 15:59:47 -080035import android.util.ArrayMap;
Stanislav Zholninffea1de2019-03-07 18:06:40 +000036import android.util.Log;
Matt Pape1278d1c2018-12-11 13:03:49 -080037import android.util.Pair;
38
39import com.android.internal.annotations.GuardedBy;
Matt Pape1013d292019-02-27 15:20:50 -080040import com.android.internal.util.Preconditions;
Matt Pape1278d1c2018-12-11 13:03:49 -080041
Stanislav Zholnin55799502019-03-08 14:54:55 +000042import java.util.Arrays;
Matt Pape1278d1c2018-12-11 13:03:49 -080043import java.util.HashMap;
44import java.util.List;
45import java.util.Map;
Matt Pape1013d292019-02-27 15:20:50 -080046import java.util.Set;
Matt Pape1278d1c2018-12-11 13:03:49 -080047import java.util.concurrent.Executor;
48
49/**
50 * Device level configuration parameters which can be tuned by a separate configuration service.
Patrick Baumannb8168232019-06-11 10:17:49 -070051 * Namespaces that end in "_native" such as {@link #NAMESPACE_NETD_NATIVE} are intended to be used
52 * by native code and should be pushed to system properties to make them accessible.
Matt Pape1278d1c2018-12-11 13:03:49 -080053 *
54 * @hide
55 */
56@SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -080057@TestApi
Matt Pape1278d1c2018-12-11 13:03:49 -080058public final class DeviceConfig {
59 /**
60 * The content:// style URL for the config table.
61 *
62 * @hide
63 */
64 public static final Uri CONTENT_URI = Uri.parse("content://" + Settings.AUTHORITY + "/config");
65
Siarhei Vishniakou39a1a982019-01-11 09:22:32 -080066 /**
Matt Pape40074da2019-02-12 13:53:26 -080067 * Namespace for activity manager related features. These features will be applied
68 * immediately upon change.
69 *
70 * @hide
71 */
72 @SystemApi
73 public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
74
75 /**
Matt Papebecd5aa2019-02-28 14:08:51 -080076 * Namespace for all activity manager related features that are used at the native level.
77 * These features are applied at reboot.
Alex Salocbd05e62019-01-23 18:45:22 -080078 *
Peiyong Lin31fa8ac2019-01-09 13:35:48 -080079 * @hide
80 */
81 @SystemApi
Matt Papebecd5aa2019-02-28 14:08:51 -080082 public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT =
83 "activity_manager_native_boot";
Peiyong Lin31fa8ac2019-01-09 13:35:48 -080084
85 /**
Andrei Onea3b433f52019-03-13 11:59:53 +000086 * Namespace for all app compat related features. These features will be applied
87 * immediately upon change.
88 *
89 * @hide
90 */
91 @SystemApi
92 public static final String NAMESPACE_APP_COMPAT = "app_compat";
93
94 /**
Matt Papeaea28312019-03-07 13:51:38 -080095 * Namespace for AttentionManagerService related features.
96 *
97 * @hide
98 */
99 @SystemApi
100 public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service";
101
102 /**
Perumaal S5c016da2019-01-16 21:33:22 -0800103 * Namespace for autofill feature that provides suggestions across all apps when
104 * the user interacts with input fields.
105 *
106 * @hide
107 */
108 @SystemApi
Felipe Lemecc510272019-02-07 14:56:27 -0800109 @TestApi
Perumaal S5c016da2019-01-16 21:33:22 -0800110 public static final String NAMESPACE_AUTOFILL = "autofill";
111
112 /**
Xiao Ma61089ea2019-03-07 13:39:26 +0900113 * Namespace for all networking connectivity related features.
114 *
115 * @hide
116 */
117 @SystemApi
118 public static final String NAMESPACE_CONNECTIVITY = "connectivity";
119
120 /**
Felipe Leme14ef4612019-02-07 12:24:38 -0800121 * Namespace for content capture feature used by on-device machine intelligence
122 * to provide suggestions in a privacy-safe manner.
Felipe Leme70bcf382019-01-24 14:55:03 -0800123 *
124 * @hide
125 */
126 @SystemApi
Felipe Lemed03283d2019-02-05 09:42:42 -0800127 @TestApi
Felipe Leme14ef4612019-02-07 12:24:38 -0800128 public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
Felipe Leme70bcf382019-01-24 14:55:03 -0800129
130 /**
Matt Pape2933d3d2019-03-11 15:42:55 -0700131 * Namespace for how dex runs. The feature requires a reboot to reach a clean state.
132 *
133 * @hide
134 */
135 @SystemApi
136 public static final String NAMESPACE_DEX_BOOT = "dex_boot";
137
138 /**
Matt Papebecd5aa2019-02-28 14:08:51 -0800139 * Namespace for all Game Driver features.
140 *
141 * @hide
142 */
143 @SystemApi
144 public static final String NAMESPACE_GAME_DRIVER = "game_driver";
145
146 /**
Siarhei Vishniakou39a1a982019-01-11 09:22:32 -0800147 * Namespace for all input-related features that are used at the native level.
148 * These features are applied at reboot.
149 *
150 * @hide
151 */
152 @SystemApi
153 public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
154
chenbrucedb279e82018-12-27 17:12:31 +0800155 /**
Matt Papef82bb8b2019-03-12 09:56:28 -0700156 * Namespace for attention-based features provided by on-device machine intelligence.
157 *
158 * @hide
159 */
160 @SystemApi
161 public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention";
162
163 /**
Anna Wasewicz3ecea552019-04-19 15:27:09 -0700164 * Definitions for properties related to Content Suggestions.
165 *
166 * @hide
167 */
168 public static final String NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS =
169 "intelligence_content_suggestions";
170
171 /**
Matt Papebecd5aa2019-02-28 14:08:51 -0800172 * Namespace for all media native related features.
173 *
174 * @hide
175 */
176 @SystemApi
177 public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
178
179 /**
chenbrucedb279e82018-12-27 17:12:31 +0800180 * Namespace for all netd related features.
181 *
182 * @hide
183 */
184 @SystemApi
185 public static final String NAMESPACE_NETD_NATIVE = "netd_native";
186
Gustav Sennton1aebc9b2019-01-22 17:56:18 +0000187 /**
Matt Pape12187ae2019-03-14 13:37:32 -0700188 * Namespace for Rollback flags that are applied immediately.
189 *
190 * @hide
191 */
192 @SystemApi @TestApi
193 public static final String NAMESPACE_ROLLBACK = "rollback";
194
195 /**
196 * Namespace for Rollback flags that are applied after a reboot.
197 *
198 * @hide
199 */
200 @SystemApi @TestApi
201 public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
202
203 /**
Mathieu Chartier881c7a92019-03-14 16:04:45 -0700204 * Namespace for all runtime related features that don't require a reboot to become active.
205 * There are no feature flags using NAMESPACE_RUNTIME.
Matt Papee9b680a2019-03-05 15:53:13 -0800206 *
207 * @hide
208 */
209 @SystemApi
210 public static final String NAMESPACE_RUNTIME = "runtime";
211
212 /**
Mathieu Chartier881c7a92019-03-14 16:04:45 -0700213 * Namespace for all runtime related features that require system properties for accessing
214 * the feature flags from C++ or Java language code. One example is the app image startup
215 * cache feature use_app_image_startup_cache.
Matt Papee9b680a2019-03-05 15:53:13 -0800216 *
217 * @hide
218 */
219 @SystemApi
220 public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
221
222 /**
Matt Papebecd5aa2019-02-28 14:08:51 -0800223 * Namespace for all runtime native boot related features. Boot in this case refers to the
224 * fact that the properties only take affect after rebooting the device.
225 *
226 * @hide
227 */
228 @SystemApi
229 public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
230
231 /**
Matt Papef095f3c2019-03-15 13:17:45 -0700232 * Namespace for system scheduler related features. These features will be applied
233 * immediately upon change.
234 *
235 * @hide
236 */
237 @SystemApi
238 public static final String NAMESPACE_SCHEDULER = "scheduler";
239
240 /**
Matt Pape19ea8a62019-03-18 10:38:25 -0700241 * Namespace for storage-related features.
242 *
243 * @hide
244 */
245 @SystemApi
246 public static final String NAMESPACE_STORAGE = "storage";
247
248 /**
Gustav Senntonddd78b22019-02-18 17:58:12 +0000249 * Namespace for System UI related features.
Gustav Sennton1aebc9b2019-01-22 17:56:18 +0000250 *
251 * @hide
252 */
253 @SystemApi
Gustav Senntonddd78b22019-02-18 17:58:12 +0000254 public static final String NAMESPACE_SYSTEMUI = "systemui";
Gustav Sennton1aebc9b2019-01-22 17:56:18 +0000255
Yiwen Chen3d3ad022019-01-22 21:15:56 -0800256 /**
Matt Pape38ed4e72019-03-20 15:03:39 -0700257 * Telephony related properties.
258 *
259 * @hide
260 */
261 @SystemApi
262 public static final String NAMESPACE_TELEPHONY = "telephony";
263
264 /**
Tony Makfc374572019-03-05 14:46:24 +0000265 * Namespace for TextClassifier related features.
266 *
267 * @hide
Abodunrinwa Toki0634af32019-04-04 13:10:59 +0100268 * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
Tony Makfc374572019-03-05 14:46:24 +0000269 */
270 @SystemApi
271 public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
272
273 /**
Makoto Onuki87d39642019-04-04 14:42:23 -0700274 * Namespace for contacts provider related features.
275 *
276 * @hide
277 */
278 public static final String NAMESPACE_CONTACTS_PROVIDER = "contacts_provider";
279
280 /**
Lei Yu5d3461b2019-04-11 13:50:50 -0700281 * Namespace for settings ui related features
282 *
283 * @hide
284 */
285 public static final String NAMESPACE_SETTINGS_UI = "settings_ui";
286
287 /**
Adrian Roos506267d2019-06-18 16:13:53 +0200288 * Namespace for window manager related features. The names to access the properties in this
289 * namespace should be defined in {@link WindowManager}.
290 *
291 * @hide
292 */
293 @TestApi
294 public static final String NAMESPACE_WINDOW_MANAGER = "android:window_manager";
295
296 /**
Stanislav Zholnin55799502019-03-08 14:54:55 +0000297 * List of namespaces which can be read without READ_DEVICE_CONFIG permission
298 *
299 * @hide
300 */
301 @NonNull
302 private static final List<String> PUBLIC_NAMESPACES =
303 Arrays.asList(NAMESPACE_TEXTCLASSIFIER, NAMESPACE_RUNTIME);
304 /**
Joel Galenson489239b2019-01-23 10:05:03 -0800305 * Privacy related properties definitions.
306 *
307 * @hide
308 */
309 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800310 @TestApi
Svet Ganovd8eb8b22019-04-05 18:52:08 -0700311 public static final String NAMESPACE_PRIVACY = "privacy";
Joel Galenson489239b2019-01-23 10:05:03 -0800312
Adrian Roos506267d2019-06-18 16:13:53 +0200313 /**
314 * Interface for accessing keys belonging to {@link #NAMESPACE_WINDOW_MANAGER}.
315 * @hide
316 */
317 @TestApi
318 public interface WindowManager {
319
320 /**
321 * Key for accessing the system gesture exclusion limit (an integer in dp).
322 *
323 * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER
324 * @hide
325 */
326 @TestApi
327 String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp";
Adrian Roos019a52b2019-07-02 16:47:44 +0200328
329 /**
330 * Key for controlling whether system gestures are implicitly excluded by windows requesting
331 * sticky immersive mode from apps that are targeting an SDK prior to Q.
332 *
333 * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER
334 * @hide
335 */
336 @TestApi
337 String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE =
338 "system_gestures_excluded_by_pre_q_sticky_immersive";
Michael Wright6a51b352019-07-04 19:17:19 +0100339
340 /**
Adrian Roos5f2c9a12019-07-03 18:31:46 +0200341 * The minimum duration between gesture exclusion logging for a given window in
342 * milliseconds.
343 *
344 * Events that happen in-between will be silently dropped.
345 *
346 * A non-positive value disables logging.
347 *
348 * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER
349 * @hide
350 */
351 String KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS =
352 "system_gesture_exclusion_log_debounce_millis";
353
354 /**
Michael Wright6a51b352019-07-04 19:17:19 +0100355 * Key for controlling which packages are explicitly blocked from running at refresh rates
356 * higher than 60hz.
357 *
358 * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER
359 * @hide
360 */
361 String KEY_HIGH_REFRESH_RATE_BLACKLIST = "high_refresh_rate_blacklist";
Adrian Roos506267d2019-06-18 16:13:53 +0200362 }
363
Matt Pape1278d1c2018-12-11 13:03:49 -0800364 private static final Object sLock = new Object();
365 @GuardedBy("sLock")
Matt Papeea9881d2019-03-07 15:59:47 -0800366 private static ArrayMap<OnPropertyChangedListener, Pair<String, Executor>> sSingleListeners =
367 new ArrayMap<>();
368 @GuardedBy("sLock")
369 private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
370 new ArrayMap<>();
Matt Pape1278d1c2018-12-11 13:03:49 -0800371 @GuardedBy("sLock")
372 private static Map<String, Pair<ContentObserver, Integer>> sNamespaces = new HashMap<>();
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000373 private static final String TAG = "DeviceConfig";
Matt Pape1278d1c2018-12-11 13:03:49 -0800374
375 // Should never be invoked
376 private DeviceConfig() {
377 }
378
379 /**
380 * Look up the value of a property for a particular namespace.
381 *
382 * @param namespace The namespace containing the property to look up.
Alex Salocbd05e62019-01-23 18:45:22 -0800383 * @param name The name of the property to look up.
Matt Pape1278d1c2018-12-11 13:03:49 -0800384 * @return the corresponding value, or null if not present.
Matt Pape1278d1c2018-12-11 13:03:49 -0800385 * @hide
386 */
387 @SystemApi
Felipe Lemed03283d2019-02-05 09:42:42 -0800388 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000389 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700390 public static String getProperty(@NonNull String namespace, @NonNull String name) {
Matt Pape1278d1c2018-12-11 13:03:49 -0800391 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
392 String compositeName = createCompositeName(namespace, name);
393 return Settings.Config.getString(contentResolver, compositeName);
394 }
395
396 /**
Matt Pape043437c2019-02-20 10:30:47 -0800397 * Look up the String value of a property for a particular namespace.
398 *
Matt Papefc5389a2019-02-28 11:05:03 -0800399 * @param namespace The namespace containing the property to look up.
400 * @param name The name of the property to look up.
401 * @param defaultValue The value to return if the property does not exist or has no non-null
402 * value.
Matt Pape043437c2019-02-20 10:30:47 -0800403 * @return the corresponding value, or defaultValue if none exists.
404 * @hide
405 */
406 @SystemApi
407 @TestApi
408 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700409 public static String getString(@NonNull String namespace, @NonNull String name,
410 @Nullable String defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800411 String value = getProperty(namespace, name);
412 return value != null ? value : defaultValue;
413 }
414
415 /**
416 * Look up the boolean value of a property for a particular namespace.
417 *
418 * @param namespace The namespace containing the property to look up.
419 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800420 * @param defaultValue The value to return if the property does not exist or has no non-null
421 * value.
Matt Pape043437c2019-02-20 10:30:47 -0800422 * @return the corresponding value, or defaultValue if none exists.
423 * @hide
424 */
425 @SystemApi
426 @TestApi
427 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700428 public static boolean getBoolean(@NonNull String namespace, @NonNull String name,
429 boolean defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800430 String value = getProperty(namespace, name);
431 return value != null ? Boolean.parseBoolean(value) : defaultValue;
432 }
433
434 /**
435 * Look up the int value of a property for a particular namespace.
436 *
437 * @param namespace The namespace containing the property to look up.
438 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800439 * @param defaultValue The value to return if the property does not exist, has no non-null
440 * value, or fails to parse into an int.
Matt Pape043437c2019-02-20 10:30:47 -0800441 * @return the corresponding value, or defaultValue if either none exists or it does not parse.
442 * @hide
443 */
444 @SystemApi
445 @TestApi
446 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700447 public static int getInt(@NonNull String namespace, @NonNull String name, int defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800448 String value = getProperty(namespace, name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000449 if (value == null) {
450 return defaultValue;
451 }
Matt Pape043437c2019-02-20 10:30:47 -0800452 try {
453 return Integer.parseInt(value);
454 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000455 Log.e(TAG, "Parsing integer failed for " + namespace + ":" + name);
Matt Pape043437c2019-02-20 10:30:47 -0800456 return defaultValue;
457 }
458 }
459
460 /**
461 * Look up the long value of a property for a particular namespace.
462 *
463 * @param namespace The namespace containing the property to look up.
464 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800465 * @param defaultValue The value to return if the property does not exist, has no non-null
466 * value, or fails to parse into a long.
Matt Pape043437c2019-02-20 10:30:47 -0800467 * @return the corresponding value, or defaultValue if either none exists or it does not parse.
468 * @hide
469 */
470 @SystemApi
471 @TestApi
472 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700473 public static long getLong(@NonNull String namespace, @NonNull String name, long defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800474 String value = getProperty(namespace, name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000475 if (value == null) {
476 return defaultValue;
477 }
Matt Pape043437c2019-02-20 10:30:47 -0800478 try {
479 return Long.parseLong(value);
480 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000481 Log.e(TAG, "Parsing long failed for " + namespace + ":" + name);
Matt Pape043437c2019-02-20 10:30:47 -0800482 return defaultValue;
483 }
484 }
485
486 /**
487 * Look up the float value of a property for a particular namespace.
488 *
489 * @param namespace The namespace containing the property to look up.
490 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800491 * @param defaultValue The value to return if the property does not exist, has no non-null
492 * value, or fails to parse into a float.
Matt Pape043437c2019-02-20 10:30:47 -0800493 * @return the corresponding value, or defaultValue if either none exists or it does not parse.
494 * @hide
495 */
496 @SystemApi
497 @TestApi
498 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700499 public static float getFloat(@NonNull String namespace, @NonNull String name,
500 float defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800501 String value = getProperty(namespace, name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000502 if (value == null) {
503 return defaultValue;
504 }
Matt Pape043437c2019-02-20 10:30:47 -0800505 try {
506 return Float.parseFloat(value);
507 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000508 Log.e(TAG, "Parsing float failed for " + namespace + ":" + name);
Matt Pape043437c2019-02-20 10:30:47 -0800509 return defaultValue;
510 }
511 }
512
513 /**
Matt Pape1278d1c2018-12-11 13:03:49 -0800514 * Create a new property with the the provided name and value in the provided namespace, or
515 * update the value of such a property if it already exists. The same name can exist in multiple
516 * namespaces and might have different values in any or all namespaces.
517 * <p>
518 * The method takes an argument indicating whether to make the value the default for this
519 * property.
520 * <p>
521 * All properties stored for a particular scope can be reverted to their default values
522 * by passing the namespace to {@link #resetToDefaults(int, String)}.
523 *
Alex Salocbd05e62019-01-23 18:45:22 -0800524 * @param namespace The namespace containing the property to create or update.
525 * @param name The name of the property to create or update.
526 * @param value The value to store for the property.
Matt Pape1278d1c2018-12-11 13:03:49 -0800527 * @param makeDefault Whether to make the new value the default one.
528 * @return True if the value was set, false if the storage implementation throws errors.
Matt Pape1278d1c2018-12-11 13:03:49 -0800529 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800530 * @see #resetToDefaults(int, String).
Matt Pape1278d1c2018-12-11 13:03:49 -0800531 */
532 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800533 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000534 @RequiresPermission(WRITE_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700535 public static boolean setProperty(@NonNull String namespace, @NonNull String name,
536 @Nullable String value, boolean makeDefault) {
Matt Pape1278d1c2018-12-11 13:03:49 -0800537 String compositeName = createCompositeName(namespace, name);
Matt Papec4ed9b32019-03-11 14:07:00 -0700538 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
Matt Pape1278d1c2018-12-11 13:03:49 -0800539 return Settings.Config.putString(contentResolver, compositeName, value, makeDefault);
540 }
541
542 /**
543 * Reset properties to their default values.
544 * <p>
545 * The method accepts an optional namespace parameter. If provided, only properties set within
546 * that namespace will be reset. Otherwise, all properties will be reset.
547 *
548 * @param resetMode The reset mode to use.
549 * @param namespace Optionally, the specific namespace which resets will be limited to.
Matt Pape1278d1c2018-12-11 13:03:49 -0800550 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800551 * @see #setProperty(String, String, String, boolean)
Matt Pape1278d1c2018-12-11 13:03:49 -0800552 */
553 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800554 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000555 @RequiresPermission(WRITE_DEVICE_CONFIG)
Matt Pape1278d1c2018-12-11 13:03:49 -0800556 public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) {
557 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
558 Settings.Config.resetToDefaults(contentResolver, resetMode, namespace);
559 }
560
561 /**
562 * Add a listener for property changes.
563 * <p>
564 * This listener will be called whenever properties in the specified namespace change. Callbacks
565 * will be made on the specified executor. Future calls to this method with the same listener
566 * will replace the old namespace and executor. Remove the listener entirely by calling
567 * {@link #removeOnPropertyChangedListener(OnPropertyChangedListener)}.
568 *
Alex Salocbd05e62019-01-23 18:45:22 -0800569 * @param namespace The namespace containing properties to monitor.
570 * @param executor The executor which will be used to run callbacks.
Matt Pape1278d1c2018-12-11 13:03:49 -0800571 * @param onPropertyChangedListener The listener to add.
Matt Pape1278d1c2018-12-11 13:03:49 -0800572 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800573 * @see #removeOnPropertyChangedListener(OnPropertyChangedListener)
Matt Pape1220c342019-04-15 13:17:21 -0700574 * @removed
Matt Pape1278d1c2018-12-11 13:03:49 -0800575 */
576 @SystemApi
Felipe Lemecc510272019-02-07 14:56:27 -0800577 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000578 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Pape1278d1c2018-12-11 13:03:49 -0800579 public static void addOnPropertyChangedListener(
580 @NonNull String namespace,
581 @NonNull @CallbackExecutor Executor executor,
582 @NonNull OnPropertyChangedListener onPropertyChangedListener) {
Stanislav Zholnin55799502019-03-08 14:54:55 +0000583 enforceReadPermission(ActivityThread.currentApplication().getApplicationContext(),
584 namespace);
Matt Pape1278d1c2018-12-11 13:03:49 -0800585 synchronized (sLock) {
Matt Papeea9881d2019-03-07 15:59:47 -0800586 Pair<String, Executor> oldNamespace = sSingleListeners.get(onPropertyChangedListener);
Matt Pape1278d1c2018-12-11 13:03:49 -0800587 if (oldNamespace == null) {
588 // Brand new listener, add it to the list.
Matt Papeea9881d2019-03-07 15:59:47 -0800589 sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
Matt Pape1278d1c2018-12-11 13:03:49 -0800590 incrementNamespace(namespace);
591 } else if (namespace.equals(oldNamespace.first)) {
592 // Listener is already registered for this namespace, update executor just in case.
Matt Papeea9881d2019-03-07 15:59:47 -0800593 sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
Matt Pape1278d1c2018-12-11 13:03:49 -0800594 } else {
595 // Update this listener from an old namespace to the new one.
Matt Papeea9881d2019-03-07 15:59:47 -0800596 decrementNamespace(sSingleListeners.get(onPropertyChangedListener).first);
597 sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
598 incrementNamespace(namespace);
599 }
600 }
601 }
602
603 /**
604 * Add a listener for property changes.
605 * <p>
606 * This listener will be called whenever properties in the specified namespace change. Callbacks
607 * will be made on the specified executor. Future calls to this method with the same listener
608 * will replace the old namespace and executor. Remove the listener entirely by calling
609 * {@link #removeOnPropertiesChangedListener(OnPropertiesChangedListener)}.
610 *
611 * @param namespace The namespace containing properties to monitor.
612 * @param executor The executor which will be used to run callbacks.
613 * @param onPropertiesChangedListener The listener to add.
614 * @hide
615 * @see #removeOnPropertiesChangedListener(OnPropertiesChangedListener)
616 */
617 @SystemApi
618 @TestApi
619 @RequiresPermission(READ_DEVICE_CONFIG)
620 public static void addOnPropertiesChangedListener(
621 @NonNull String namespace,
622 @NonNull @CallbackExecutor Executor executor,
623 @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
Stanislav Zholnin55799502019-03-08 14:54:55 +0000624 enforceReadPermission(ActivityThread.currentApplication().getApplicationContext(),
625 namespace);
Matt Papeea9881d2019-03-07 15:59:47 -0800626 synchronized (sLock) {
627 Pair<String, Executor> oldNamespace = sListeners.get(onPropertiesChangedListener);
628 if (oldNamespace == null) {
629 // Brand new listener, add it to the list.
630 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
631 incrementNamespace(namespace);
632 } else if (namespace.equals(oldNamespace.first)) {
633 // Listener is already registered for this namespace, update executor just in case.
634 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
635 } else {
636 // Update this listener from an old namespace to the new one.
637 decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
638 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
Matt Pape1278d1c2018-12-11 13:03:49 -0800639 incrementNamespace(namespace);
640 }
641 }
642 }
643
644 /**
645 * Remove a listener for property changes. The listener will receive no further notification of
646 * property changes.
647 *
648 * @param onPropertyChangedListener The listener to remove.
Matt Pape1278d1c2018-12-11 13:03:49 -0800649 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800650 * @see #addOnPropertyChangedListener(String, Executor, OnPropertyChangedListener)
Matt Pape1220c342019-04-15 13:17:21 -0700651 * @removed
Matt Pape1278d1c2018-12-11 13:03:49 -0800652 */
653 @SystemApi
Felipe Lemecc510272019-02-07 14:56:27 -0800654 @TestApi
Matt Pape1278d1c2018-12-11 13:03:49 -0800655 public static void removeOnPropertyChangedListener(
Matt Papeea9881d2019-03-07 15:59:47 -0800656 @NonNull OnPropertyChangedListener onPropertyChangedListener) {
657 Preconditions.checkNotNull(onPropertyChangedListener);
Matt Pape1278d1c2018-12-11 13:03:49 -0800658 synchronized (sLock) {
Matt Papeea9881d2019-03-07 15:59:47 -0800659 if (sSingleListeners.containsKey(onPropertyChangedListener)) {
660 decrementNamespace(sSingleListeners.get(onPropertyChangedListener).first);
661 sSingleListeners.remove(onPropertyChangedListener);
662 }
663 }
664 }
665
666 /**
667 * Remove a listener for property changes. The listener will receive no further notification of
668 * property changes.
669 *
670 * @param onPropertiesChangedListener The listener to remove.
671 * @hide
672 * @see #addOnPropertiesChangedListener(String, Executor, OnPropertiesChangedListener)
673 */
674 @SystemApi
675 @TestApi
676 public static void removeOnPropertiesChangedListener(
677 @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
678 Preconditions.checkNotNull(onPropertiesChangedListener);
679 synchronized (sLock) {
680 if (sListeners.containsKey(onPropertiesChangedListener)) {
681 decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
682 sListeners.remove(onPropertiesChangedListener);
Matt Pape1278d1c2018-12-11 13:03:49 -0800683 }
684 }
685 }
686
Matt Papec4ed9b32019-03-11 14:07:00 -0700687 private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
688 Preconditions.checkNotNull(namespace);
689 Preconditions.checkNotNull(name);
Matt Pape1278d1c2018-12-11 13:03:49 -0800690 return namespace + "/" + name;
691 }
692
Matt Papec4ed9b32019-03-11 14:07:00 -0700693 private static Uri createNamespaceUri(@NonNull String namespace) {
694 Preconditions.checkNotNull(namespace);
Matt Pape1278d1c2018-12-11 13:03:49 -0800695 return CONTENT_URI.buildUpon().appendPath(namespace).build();
696 }
697
698 /**
699 * Increment the count used to represent the number of listeners subscribed to the given
700 * namespace. If this is the first (i.e. incrementing from 0 to 1) for the given namespace, a
701 * ContentObserver is registered.
702 *
703 * @param namespace The namespace to increment the count for.
704 */
705 @GuardedBy("sLock")
Matt Papec4ed9b32019-03-11 14:07:00 -0700706 private static void incrementNamespace(@NonNull String namespace) {
707 Preconditions.checkNotNull(namespace);
Matt Pape1278d1c2018-12-11 13:03:49 -0800708 Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
709 if (namespaceCount != null) {
710 sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second + 1));
711 } else {
712 // This is a new namespace, register a ContentObserver for it.
713 ContentObserver contentObserver = new ContentObserver(null) {
714 @Override
715 public void onChange(boolean selfChange, Uri uri) {
Matt Papec4ed9b32019-03-11 14:07:00 -0700716 if (uri != null) {
717 handleChange(uri);
718 }
Matt Pape1278d1c2018-12-11 13:03:49 -0800719 }
720 };
721 ActivityThread.currentApplication().getContentResolver()
722 .registerContentObserver(createNamespaceUri(namespace), true, contentObserver);
723 sNamespaces.put(namespace, new Pair<>(contentObserver, 1));
724 }
725 }
726
727 /**
Stanislav Zholnin55799502019-03-08 14:54:55 +0000728 * Decrement the count used to represent the number of listeners subscribed to the given
Matt Pape1278d1c2018-12-11 13:03:49 -0800729 * namespace. If this is the final decrement call (i.e. decrementing from 1 to 0) for the given
730 * namespace, the ContentObserver that had been tracking it will be removed.
731 *
732 * @param namespace The namespace to decrement the count for.
733 */
734 @GuardedBy("sLock")
Matt Papec4ed9b32019-03-11 14:07:00 -0700735 private static void decrementNamespace(@NonNull String namespace) {
736 Preconditions.checkNotNull(namespace);
Matt Pape1278d1c2018-12-11 13:03:49 -0800737 Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
738 if (namespaceCount == null) {
739 // This namespace is not registered and does not need to be decremented
740 return;
741 } else if (namespaceCount.second > 1) {
742 sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second - 1));
743 } else {
744 // Decrementing a namespace to zero means we no longer need its ContentObserver.
745 ActivityThread.currentApplication().getContentResolver()
746 .unregisterContentObserver(namespaceCount.first);
747 sNamespaces.remove(namespace);
748 }
749 }
750
Matt Papec4ed9b32019-03-11 14:07:00 -0700751 private static void handleChange(@NonNull Uri uri) {
752 Preconditions.checkNotNull(uri);
Matt Pape1278d1c2018-12-11 13:03:49 -0800753 List<String> pathSegments = uri.getPathSegments();
754 // pathSegments(0) is "config"
Matt Pape1013d292019-02-27 15:20:50 -0800755 final String namespace = pathSegments.get(1);
756 final String name = pathSegments.get(2);
Stanislav Zholnin55799502019-03-08 14:54:55 +0000757 final String value;
758 try {
759 value = getProperty(namespace, name);
760 } catch (SecurityException e) {
761 // Silently failing to not crash binder or listener threads.
762 Log.e(TAG, "OnPropertyChangedListener update failed: permission violation.");
763 return;
764 }
Matt Pape1278d1c2018-12-11 13:03:49 -0800765 synchronized (sLock) {
Matt Papeea9881d2019-03-07 15:59:47 -0800766 // OnPropertiesChangedListeners
767 for (int i = 0; i < sListeners.size(); i++) {
768 if (namespace.equals(sListeners.valueAt(i).first)) {
769 final int j = i;
770 sListeners.valueAt(i).second.execute(new Runnable() {
Matt Pape1278d1c2018-12-11 13:03:49 -0800771 @Override
772 public void run() {
Matt Pape1013d292019-02-27 15:20:50 -0800773 Map<String, String> propertyMap = new HashMap(1);
774 propertyMap.put(name, value);
Matt Papeea9881d2019-03-07 15:59:47 -0800775 sListeners.keyAt(j)
776 .onPropertiesChanged(new Properties(namespace, propertyMap));
777 }
778
779 });
780 }
781 }
782 // OnPropertyChangedListeners
783 for (int i = 0; i < sSingleListeners.size(); i++) {
784 if (namespace.equals(sSingleListeners.valueAt(i).first)) {
785 final int j = i;
786 sSingleListeners.valueAt(i).second.execute(new Runnable() {
787 @Override
788 public void run() {
789 sSingleListeners.keyAt(j).onPropertyChanged(namespace, name, value);
Matt Pape1278d1c2018-12-11 13:03:49 -0800790 }
791
792 });
793 }
794 }
795 }
796 }
797
Stanislav Zholnin55799502019-03-08 14:54:55 +0000798
799 /**
800 * Enforces READ_DEVICE_CONFIG permission if namespace is not one of public namespaces.
801 * @hide
802 */
803 public static void enforceReadPermission(Context context, String namespace) {
804 if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
805 != PackageManager.PERMISSION_GRANTED) {
806 if (!PUBLIC_NAMESPACES.contains(namespace)) {
807 throw new SecurityException("Permission denial: reading from settings requires:"
808 + READ_DEVICE_CONFIG);
809 }
810 }
811 }
812
813
Matt Pape1278d1c2018-12-11 13:03:49 -0800814 /**
Matt Papeea9881d2019-03-07 15:59:47 -0800815 * Interface for monitoring single property changes.
Matt Pape1278d1c2018-12-11 13:03:49 -0800816 * <p>
817 * Override {@link #onPropertyChanged(String, String, String)} to handle callbacks for changes.
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800818 *
819 * @hide
Matt Pape1220c342019-04-15 13:17:21 -0700820 * @removed
Matt Pape1278d1c2018-12-11 13:03:49 -0800821 */
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800822 @SystemApi
Felipe Lemecc510272019-02-07 14:56:27 -0800823 @TestApi
Matt Pape1278d1c2018-12-11 13:03:49 -0800824 public interface OnPropertyChangedListener {
825 /**
826 * Called when a property has changed.
827 *
828 * @param namespace The namespace containing the property which has changed.
Alex Salocbd05e62019-01-23 18:45:22 -0800829 * @param name The name of the property which has changed.
830 * @param value The new value of the property which has changed.
Matt Pape1278d1c2018-12-11 13:03:49 -0800831 */
Matt Papec4ed9b32019-03-11 14:07:00 -0700832 void onPropertyChanged(@NonNull String namespace, @NonNull String name,
833 @Nullable String value);
Matt Papeea9881d2019-03-07 15:59:47 -0800834 }
Matt Pape1013d292019-02-27 15:20:50 -0800835
Matt Papeea9881d2019-03-07 15:59:47 -0800836 /**
837 * Interface for monitoring changes to properties.
838 * <p>
839 * Override {@link #onPropertiesChanged(Properties)} to handle callbacks for changes.
840 *
841 * @hide
842 */
843 @SystemApi
844 @TestApi
845 public interface OnPropertiesChangedListener {
Matt Pape1013d292019-02-27 15:20:50 -0800846 /**
847 * Called when one or more properties have changed.
848 *
849 * @param properties Contains the complete collection of properties which have changed for a
Matt Papeea9881d2019-03-07 15:59:47 -0800850 * single namespace.
Matt Pape1013d292019-02-27 15:20:50 -0800851 */
Matt Papeea9881d2019-03-07 15:59:47 -0800852 void onPropertiesChanged(@NonNull Properties properties);
Matt Pape1013d292019-02-27 15:20:50 -0800853 }
854
855 /**
856 * A mapping of properties to values, as well as a single namespace which they all belong to.
857 *
858 * @hide
859 */
860 @SystemApi
861 @TestApi
862 public static class Properties {
863 private final String mNamespace;
864 private final HashMap<String, String> mMap;
865
866 /**
867 * Create a mapping of properties to values and the namespace they belong to.
868 *
869 * @param namespace The namespace these properties belong to.
870 * @param keyValueMap A map between property names and property values.
871 */
872 Properties(@NonNull String namespace, @Nullable Map<String, String> keyValueMap) {
873 Preconditions.checkNotNull(namespace);
874 mNamespace = namespace;
875 mMap = new HashMap();
876 if (keyValueMap != null) {
877 mMap.putAll(keyValueMap);
878 }
879 }
880
881 /**
882 * @return the namespace all properties within this instance belong to.
883 */
884 @NonNull
885 public String getNamespace() {
886 return mNamespace;
887 }
888
889 /**
890 * @return the non-null set of property names.
891 */
892 @NonNull
893 public Set<String> getKeyset() {
894 return mMap.keySet();
895 }
896
897 /**
898 * Look up the String value of a property.
899 *
900 * @param name The name of the property to look up.
901 * @param defaultValue The value to return if the property has not been defined.
902 * @return the corresponding value, or defaultValue if none exists.
903 */
904 @Nullable
905 public String getString(@NonNull String name, @Nullable String defaultValue) {
906 Preconditions.checkNotNull(name);
907 String value = mMap.get(name);
908 return value != null ? value : defaultValue;
909 }
910
911 /**
912 * Look up the boolean value of a property.
913 *
914 * @param name The name of the property to look up.
915 * @param defaultValue The value to return if the property has not been defined.
916 * @return the corresponding value, or defaultValue if none exists.
917 */
918 public boolean getBoolean(@NonNull String name, boolean defaultValue) {
919 Preconditions.checkNotNull(name);
920 String value = mMap.get(name);
921 return value != null ? Boolean.parseBoolean(value) : defaultValue;
922 }
923
924 /**
925 * Look up the int value of a property.
926 *
927 * @param name The name of the property to look up.
928 * @param defaultValue The value to return if the property has not been defined or fails to
929 * parse into an int.
930 * @return the corresponding value, or defaultValue if no valid int is available.
931 */
932 public int getInt(@NonNull String name, int defaultValue) {
933 Preconditions.checkNotNull(name);
934 String value = mMap.get(name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000935 if (value == null) {
936 return defaultValue;
937 }
Matt Pape1013d292019-02-27 15:20:50 -0800938 try {
939 return Integer.parseInt(value);
940 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000941 Log.e(TAG, "Parsing int failed for " + name);
Matt Pape1013d292019-02-27 15:20:50 -0800942 return defaultValue;
943 }
944 }
945
946 /**
947 * Look up the long value of a property.
948 *
949 * @param name The name of the property to look up.
950 * @param defaultValue The value to return if the property has not been defined. or fails to
951 * parse into a long.
952 * @return the corresponding value, or defaultValue if no valid long is available.
953 */
954 public long getLong(@NonNull String name, long defaultValue) {
955 Preconditions.checkNotNull(name);
956 String value = mMap.get(name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000957 if (value == null) {
958 return defaultValue;
959 }
Matt Pape1013d292019-02-27 15:20:50 -0800960 try {
961 return Long.parseLong(value);
962 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000963 Log.e(TAG, "Parsing long failed for " + name);
Matt Pape1013d292019-02-27 15:20:50 -0800964 return defaultValue;
965 }
966 }
967
968 /**
969 * Look up the int value of a property.
970 *
971 * @param name The name of the property to look up.
972 * @param defaultValue The value to return if the property has not been defined. or fails to
973 * parse into a float.
974 * @return the corresponding value, or defaultValue if no valid float is available.
975 */
976 public float getFloat(@NonNull String name, float defaultValue) {
977 Preconditions.checkNotNull(name);
978 String value = mMap.get(name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000979 if (value == null) {
980 return defaultValue;
981 }
Matt Pape1013d292019-02-27 15:20:50 -0800982 try {
983 return Float.parseFloat(value);
984 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000985 Log.e(TAG, "Parsing float failed for " + name);
Matt Pape1013d292019-02-27 15:20:50 -0800986 return defaultValue;
987 }
988 }
Matt Pape1278d1c2018-12-11 13:03:49 -0800989 }
990}