blob: e4593e55a2c6466ca16d39853cd03b13378f08d8 [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;
30import android.database.ContentObserver;
31import android.net.Uri;
32import android.provider.Settings.ResetMode;
Matt Papeea9881d2019-03-07 15:59:47 -080033import android.util.ArrayMap;
Matt Pape1278d1c2018-12-11 13:03:49 -080034import android.util.Pair;
35
36import com.android.internal.annotations.GuardedBy;
Matt Pape1013d292019-02-27 15:20:50 -080037import com.android.internal.util.Preconditions;
Matt Pape1278d1c2018-12-11 13:03:49 -080038
39import java.util.HashMap;
40import java.util.List;
41import java.util.Map;
Matt Pape1013d292019-02-27 15:20:50 -080042import java.util.Set;
Matt Pape1278d1c2018-12-11 13:03:49 -080043import java.util.concurrent.Executor;
44
45/**
46 * Device level configuration parameters which can be tuned by a separate configuration service.
47 *
48 * @hide
49 */
50@SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -080051@TestApi
Matt Pape1278d1c2018-12-11 13:03:49 -080052public final class DeviceConfig {
53 /**
54 * The content:// style URL for the config table.
55 *
56 * @hide
57 */
58 public static final Uri CONTENT_URI = Uri.parse("content://" + Settings.AUTHORITY + "/config");
59
Siarhei Vishniakou39a1a982019-01-11 09:22:32 -080060 /**
Matt Pape40074da2019-02-12 13:53:26 -080061 * Namespace for activity manager related features. These features will be applied
62 * immediately upon change.
63 *
64 * @hide
65 */
66 @SystemApi
67 public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
68
69 /**
Matt Papebecd5aa2019-02-28 14:08:51 -080070 * Namespace for all activity manager related features that are used at the native level.
71 * These features are applied at reboot.
Alex Salocbd05e62019-01-23 18:45:22 -080072 *
Peiyong Lin31fa8ac2019-01-09 13:35:48 -080073 * @hide
74 */
75 @SystemApi
Matt Papebecd5aa2019-02-28 14:08:51 -080076 public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT =
77 "activity_manager_native_boot";
Peiyong Lin31fa8ac2019-01-09 13:35:48 -080078
79 /**
Perumaal S5c016da2019-01-16 21:33:22 -080080 * Namespace for autofill feature that provides suggestions across all apps when
81 * the user interacts with input fields.
82 *
83 * @hide
84 */
85 @SystemApi
Felipe Lemecc510272019-02-07 14:56:27 -080086 @TestApi
Perumaal S5c016da2019-01-16 21:33:22 -080087 public static final String NAMESPACE_AUTOFILL = "autofill";
88
89 /**
Xiao Ma61089ea2019-03-07 13:39:26 +090090 * Namespace for all networking connectivity related features.
91 *
92 * @hide
93 */
94 @SystemApi
95 public static final String NAMESPACE_CONNECTIVITY = "connectivity";
96
97 /**
Felipe Leme14ef4612019-02-07 12:24:38 -080098 * Namespace for content capture feature used by on-device machine intelligence
99 * to provide suggestions in a privacy-safe manner.
Felipe Leme70bcf382019-01-24 14:55:03 -0800100 *
101 * @hide
102 */
103 @SystemApi
Felipe Lemed03283d2019-02-05 09:42:42 -0800104 @TestApi
Felipe Leme14ef4612019-02-07 12:24:38 -0800105 public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
Felipe Leme70bcf382019-01-24 14:55:03 -0800106
107 /**
Matt Papebecd5aa2019-02-28 14:08:51 -0800108 * Namespace for all Game Driver features.
109 *
110 * @hide
111 */
112 @SystemApi
113 public static final String NAMESPACE_GAME_DRIVER = "game_driver";
114
115 /**
Siarhei Vishniakou39a1a982019-01-11 09:22:32 -0800116 * Namespace for all input-related features that are used at the native level.
117 * These features are applied at reboot.
118 *
119 * @hide
120 */
121 @SystemApi
122 public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
123
chenbrucedb279e82018-12-27 17:12:31 +0800124 /**
Matt Papebecd5aa2019-02-28 14:08:51 -0800125 * Namespace for all media native related features.
126 *
127 * @hide
128 */
129 @SystemApi
130 public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
131
132 /**
chenbrucedb279e82018-12-27 17:12:31 +0800133 * Namespace for all netd related features.
134 *
135 * @hide
136 */
137 @SystemApi
138 public static final String NAMESPACE_NETD_NATIVE = "netd_native";
139
Gustav Sennton1aebc9b2019-01-22 17:56:18 +0000140 /**
Matt Papebecd5aa2019-02-28 14:08:51 -0800141 * Namespace for all runtime native boot related features. Boot in this case refers to the
142 * fact that the properties only take affect after rebooting the device.
143 *
144 * @hide
145 */
146 @SystemApi
147 public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
148
149 /**
Gustav Senntonddd78b22019-02-18 17:58:12 +0000150 * Namespace for System UI related features.
Gustav Sennton1aebc9b2019-01-22 17:56:18 +0000151 *
152 * @hide
153 */
154 @SystemApi
Gustav Senntonddd78b22019-02-18 17:58:12 +0000155 public static final String NAMESPACE_SYSTEMUI = "systemui";
Gustav Sennton1aebc9b2019-01-22 17:56:18 +0000156
Yiwen Chen3d3ad022019-01-22 21:15:56 -0800157 /**
Tony Makfc374572019-03-05 14:46:24 +0000158 * Namespace for TextClassifier related features.
159 *
160 * @hide
161 */
162 @SystemApi
163 public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
164
165 /**
Mathieu Chartier743b3632019-02-07 13:14:19 -0800166 * Namespace for all runtime related features.
167 *
168 * @hide
169 */
170 @SystemApi
171 public interface Runtime {
172 String NAMESPACE = "runtime";
173
174 /**
175 * Whether or not we use the precompiled layout.
176 */
177 String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled";
178 }
179
180 /**
Mathieu Chartier7f11eb92019-01-25 16:08:21 -0800181 * Namespace for all runtime native related features.
182 *
183 * @hide
184 */
185 @SystemApi
186 public interface RuntimeNative {
187 String NAMESPACE = "runtime_native";
Mathieu Chartier0bccbf72019-01-30 15:56:17 -0800188
189 /**
190 * Zygote flags. See {@link com.internal.os.Zygote}.
191 */
192
193 /**
Chris Wailes7e797b62019-02-22 18:29:22 -0800194 * If {@code true}, enables the unspecialized app process (USAP) pool feature.
Mathieu Chartier0bccbf72019-01-30 15:56:17 -0800195 *
196 * @hide for internal use only
197 */
Chris Wailes7e797b62019-02-22 18:29:22 -0800198 String USAP_POOL_ENABLED = "usap_pool_enabled";
Mathieu Chartier0bccbf72019-01-30 15:56:17 -0800199
200 /**
Chris Wailes7e797b62019-02-22 18:29:22 -0800201 * The maximum number of processes to keep in the USAP pool.
Mathieu Chartier0bccbf72019-01-30 15:56:17 -0800202 *
203 * @hide for internal use only
204 */
Chris Wailes7e797b62019-02-22 18:29:22 -0800205 String USAP_POOL_SIZE_MAX = "usap_pool_size_max";
Mathieu Chartier0bccbf72019-01-30 15:56:17 -0800206
207 /**
Chris Wailes7e797b62019-02-22 18:29:22 -0800208 * The minimum number of processes to keep in the USAP pool.
Mathieu Chartier0bccbf72019-01-30 15:56:17 -0800209 *
210 * @hide for internal use only
211 */
Chris Wailes7e797b62019-02-22 18:29:22 -0800212 String USAP_POOL_SIZE_MIN = "usap_pool_size_min";
Mathieu Chartier0bccbf72019-01-30 15:56:17 -0800213
214 /**
215 * The threshold used to determine if the pool should be refilled.
216 *
217 * @hide for internal use only
218 */
Chris Wailes7e797b62019-02-22 18:29:22 -0800219 String USAP_POOL_REFILL_THRESHOLD = "usap_refill_threshold";
Mathieu Chartier7f11eb92019-01-25 16:08:21 -0800220 }
221
222 /**
Alex Salocbd05e62019-01-23 18:45:22 -0800223 * Namespace for attention-based features provided by on-device machine intelligence.
224 *
225 * @hide
226 */
227 @SystemApi
228 public interface IntelligenceAttention {
229 String NAMESPACE = "intelligence_attention";
Alex Salo440fe3d2019-01-25 11:50:38 -0800230
231 /** If {@code true}, enables the attention features. */
Alex Saloc7b9c082019-01-29 13:10:55 -0800232 String ATTENTION_ENABLED = "attention_enabled";
Alex Salo440fe3d2019-01-25 11:50:38 -0800233
234 /** Settings for the attention features. */
Alex Saloc7b9c082019-01-29 13:10:55 -0800235 String ATTENTION_SETTINGS = "attention_settings";
Alex Salocbd05e62019-01-23 18:45:22 -0800236 }
237
238 /**
Joel Galenson489239b2019-01-23 10:05:03 -0800239 * Privacy related properties definitions.
240 *
241 * @hide
242 */
243 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800244 @TestApi
Joel Galenson489239b2019-01-23 10:05:03 -0800245 public interface Privacy {
246 String NAMESPACE = "privacy";
247
248 /**
249 * Whether to show the Permissions Hub.
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800250 *
251 * @hide
Joel Galenson489239b2019-01-23 10:05:03 -0800252 */
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800253 @SystemApi
Joel Galensond382ad42019-01-29 15:38:39 -0800254 String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800255
256 /**
257 * Whether to show location access check notifications.
258 */
Joel Galensonc4d89212019-01-30 10:02:24 -0800259 String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled";
Michael Groover0dbc35d2019-01-29 17:34:41 -0800260
261 /**
262 * Whether to disable the new device identifier access restrictions.
263 *
264 * @hide
265 */
266 String PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED =
267 "device_identifier_access_restrictions_disabled";
Joel Galenson489239b2019-01-23 10:05:03 -0800268 }
269
270 /**
Yiwen Chen3d3ad022019-01-22 21:15:56 -0800271 * Telephony related properties definitions.
272 *
273 * @hide
274 */
275 @SystemApi
276 public interface Telephony {
277 String NAMESPACE = "telephony";
278 /**
Yiwen Chen3d3ad022019-01-22 21:15:56 -0800279 * Ringer ramping time in milliseconds.
280 */
Yiwen Chen555b83d2019-01-28 14:13:25 -0800281 String RAMPING_RINGER_DURATION = "ramping_ringer_duration";
282 /**
283 * Whether to apply ramping ringer on incoming phone calls.
284 */
285 String RAMPING_RINGER_ENABLED = "ramping_ringer_enabled";
286 /**
287 * Vibration time in milliseconds before ramping ringer starts.
288 */
289 String RAMPING_RINGER_VIBRATION_DURATION = "ramping_ringer_vibration_duration";
Yiwen Chen3d3ad022019-01-22 21:15:56 -0800290 }
291
Victor Hsieh293595f2019-01-10 09:33:21 -0800292 /**
Victor Hsieh51c44032019-01-29 11:57:00 -0800293 * Namespace for how dex runs. The feature may requires reboot to a clean state.
Victor Hsieh293595f2019-01-10 09:33:21 -0800294 *
295 * @hide
296 */
297 @SystemApi
Victor Hsieh51c44032019-01-29 11:57:00 -0800298 public interface DexBoot {
299 String NAMESPACE = "dex_boot";
300 String PRIV_APPS_OOB_ENABLED = "priv_apps_oob_enabled";
301 String PRIV_APPS_OOB_WHITELIST = "priv_apps_oob_whitelist";
Victor Hsieh293595f2019-01-10 09:33:21 -0800302 }
303
Ben Murdochc26a5a82019-01-16 10:05:58 +0000304 /**
Alex Salo440fe3d2019-01-25 11:50:38 -0800305 * Namespace for {@link AttentionManagerService} related features.
306 *
307 * @hide
308 */
309 @SystemApi
310 public interface AttentionManagerService {
311 String NAMESPACE = "attention_manager_service";
312
313 /** If {@code true}, enables {@link AttentionManagerService} features. */
Alex Saloc7b9c082019-01-29 13:10:55 -0800314 String SERVICE_ENABLED = "service_enabled";
Alex Salo440fe3d2019-01-25 11:50:38 -0800315
316 /** Allows a CTS to inject a fake implementation. */
Alex Saloc7b9c082019-01-29 13:10:55 -0800317 String COMPONENT_NAME = "component_name";
Alex Salo440fe3d2019-01-25 11:50:38 -0800318 }
319
320 /**
Mohammad Samiul Islam794f03d2019-01-11 15:43:15 +0000321 * Namespace for Rollback.
322 *
323 * @hide
324 */
325 @SystemApi
326 public interface Rollback {
shafik508c3d52019-03-06 16:30:57 +0000327
328 /**
329 * Namespace for flags that can be changed immediately after becoming available on device.
330 */
Mohammad Samiul Islam794f03d2019-01-11 15:43:15 +0000331 String NAMESPACE = "rollback";
332
shafik508c3d52019-03-06 16:30:57 +0000333 /**
334 * Namespace for flags that can be changed only after reboot.
335 */
shafik0ad18b82019-01-24 16:27:24 +0000336 String BOOT_NAMESPACE = "rollback_boot";
337
Mohammad Samiul Islam794f03d2019-01-11 15:43:15 +0000338 /**
shafik508c3d52019-03-06 16:30:57 +0000339 * Timeout duration in milliseconds for enabling package rollback. If we fail to enable
340 * rollback within that period, the install will proceed without rollback enabled.
341 *
342 * <p>If flag value is negative, the default value will be assigned.
343 *
344 * Flag type: {@code long}
345 * Namespace: Rollback.NAMESPACE
Mohammad Samiul Islam794f03d2019-01-11 15:43:15 +0000346 */
347 String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout";
shafik0ad18b82019-01-24 16:27:24 +0000348
shafik508c3d52019-03-06 16:30:57 +0000349 /**
350 * Lifetime duration of rollback packages in millis. A rollback will be available for
351 * at most that duration of time after a package is installed with
352 * {@link PackageInstaller.SessionParams#setEnableRollback()}.
353 *
354 * <p>If flag value is negative, the default value will be assigned.
355 *
356 * @see RollbackManager
357 *
358 * Flag type: {@code long}
359 * Namespace: Rollback.BOOT_NAMESPACE
360 */
shafik0ad18b82019-01-24 16:27:24 +0000361 String ROLLBACK_LIFETIME_IN_MILLIS = "rollback_lifetime_in_millis";
Mohammad Samiul Islam794f03d2019-01-11 15:43:15 +0000362 }
363
364 /**
Jeff Sharkey5d0c55c2019-01-24 14:32:31 -0700365 * Namespace for storage-related features.
366 *
367 * @hide
368 */
369 @SystemApi
370 public interface Storage {
371 String NAMESPACE = "storage";
372
373 /**
374 * If {@code 1}, enables the isolated storage feature. If {@code -1},
375 * disables the isolated storage feature. If {@code 0}, uses the default
376 * value from the build system.
377 */
378 String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled";
379 }
380
Hongyi Zhang29301d32019-01-18 17:07:38 -0800381 /**
382 * Namespace for system scheduler related features. These features will be applied
383 * immediately upon change.
384 *
385 * @hide
386 */
387 @SystemApi
388 public interface Scheduler {
389 String NAMESPACE = "scheduler";
390
391 /**
392 * Flag for enabling fast metrics collection in system scheduler.
393 * A flag value of '' or '0' means the fast metrics collection is not
394 * enabled. Otherwise fast metrics collection is enabled and flag value
395 * is the order id.
396 */
397 String ENABLE_FAST_METRICS_COLLECTION = "enable_fast_metrics_collection";
398 }
399
Matt Pape1278d1c2018-12-11 13:03:49 -0800400 private static final Object sLock = new Object();
401 @GuardedBy("sLock")
Matt Papeea9881d2019-03-07 15:59:47 -0800402 private static ArrayMap<OnPropertyChangedListener, Pair<String, Executor>> sSingleListeners =
403 new ArrayMap<>();
404 @GuardedBy("sLock")
405 private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
406 new ArrayMap<>();
Matt Pape1278d1c2018-12-11 13:03:49 -0800407 @GuardedBy("sLock")
408 private static Map<String, Pair<ContentObserver, Integer>> sNamespaces = new HashMap<>();
409
410 // Should never be invoked
411 private DeviceConfig() {
412 }
413
414 /**
415 * Look up the value of a property for a particular namespace.
416 *
417 * @param namespace The namespace containing the property to look up.
Alex Salocbd05e62019-01-23 18:45:22 -0800418 * @param name The name of the property to look up.
Matt Pape1278d1c2018-12-11 13:03:49 -0800419 * @return the corresponding value, or null if not present.
Matt Pape1278d1c2018-12-11 13:03:49 -0800420 * @hide
421 */
422 @SystemApi
Felipe Lemed03283d2019-02-05 09:42:42 -0800423 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000424 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Pape1278d1c2018-12-11 13:03:49 -0800425 public static String getProperty(String namespace, String name) {
426 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
427 String compositeName = createCompositeName(namespace, name);
428 return Settings.Config.getString(contentResolver, compositeName);
429 }
430
431 /**
Matt Pape043437c2019-02-20 10:30:47 -0800432 * Look up the String value of a property for a particular namespace.
433 *
Matt Papefc5389a2019-02-28 11:05:03 -0800434 * @param namespace The namespace containing the property to look up.
435 * @param name The name of the property to look up.
436 * @param defaultValue The value to return if the property does not exist or has no non-null
437 * value.
Matt Pape043437c2019-02-20 10:30:47 -0800438 * @return the corresponding value, or defaultValue if none exists.
439 * @hide
440 */
441 @SystemApi
442 @TestApi
443 @RequiresPermission(READ_DEVICE_CONFIG)
444 public static String getString(String namespace, String name, String defaultValue) {
445 String value = getProperty(namespace, name);
446 return value != null ? value : defaultValue;
447 }
448
449 /**
450 * Look up the boolean value of a property for a particular namespace.
451 *
452 * @param namespace The namespace containing the property to look up.
453 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800454 * @param defaultValue The value to return if the property does not exist or has no non-null
455 * value.
Matt Pape043437c2019-02-20 10:30:47 -0800456 * @return the corresponding value, or defaultValue if none exists.
457 * @hide
458 */
459 @SystemApi
460 @TestApi
461 @RequiresPermission(READ_DEVICE_CONFIG)
462 public static boolean getBoolean(String namespace, String name, boolean defaultValue) {
463 String value = getProperty(namespace, name);
464 return value != null ? Boolean.parseBoolean(value) : defaultValue;
465 }
466
467 /**
468 * Look up the int value of a property for a particular namespace.
469 *
470 * @param namespace The namespace containing the property to look up.
471 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800472 * @param defaultValue The value to return if the property does not exist, has no non-null
473 * value, or fails to parse into an int.
Matt Pape043437c2019-02-20 10:30:47 -0800474 * @return the corresponding value, or defaultValue if either none exists or it does not parse.
475 * @hide
476 */
477 @SystemApi
478 @TestApi
479 @RequiresPermission(READ_DEVICE_CONFIG)
480 public static int getInt(String namespace, String name, int defaultValue) {
481 String value = getProperty(namespace, name);
482 try {
483 return Integer.parseInt(value);
484 } catch (NumberFormatException e) {
485 return defaultValue;
486 }
487 }
488
489 /**
490 * Look up the long value of a property for a particular namespace.
491 *
492 * @param namespace The namespace containing the property to look up.
493 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800494 * @param defaultValue The value to return if the property does not exist, has no non-null
495 * value, or fails to parse into a long.
Matt Pape043437c2019-02-20 10:30:47 -0800496 * @return the corresponding value, or defaultValue if either none exists or it does not parse.
497 * @hide
498 */
499 @SystemApi
500 @TestApi
501 @RequiresPermission(READ_DEVICE_CONFIG)
502 public static long getLong(String namespace, String name, long defaultValue) {
503 String value = getProperty(namespace, name);
504 try {
505 return Long.parseLong(value);
506 } catch (NumberFormatException e) {
507 return defaultValue;
508 }
509 }
510
511 /**
512 * Look up the float value of a property for a particular namespace.
513 *
514 * @param namespace The namespace containing the property to look up.
515 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800516 * @param defaultValue The value to return if the property does not exist, has no non-null
517 * value, or fails to parse into a float.
Matt Pape043437c2019-02-20 10:30:47 -0800518 * @return the corresponding value, or defaultValue if either none exists or it does not parse.
519 * @hide
520 */
521 @SystemApi
522 @TestApi
523 @RequiresPermission(READ_DEVICE_CONFIG)
524 public static float getFloat(String namespace, String name, float defaultValue) {
525 String value = getProperty(namespace, name);
526 try {
527 return Float.parseFloat(value);
528 } catch (NumberFormatException e) {
529 return defaultValue;
530 } catch (NullPointerException e) {
531 return defaultValue;
532 }
533 }
534
535 /**
Matt Pape1278d1c2018-12-11 13:03:49 -0800536 * Create a new property with the the provided name and value in the provided namespace, or
537 * update the value of such a property if it already exists. The same name can exist in multiple
538 * namespaces and might have different values in any or all namespaces.
539 * <p>
540 * The method takes an argument indicating whether to make the value the default for this
541 * property.
542 * <p>
543 * All properties stored for a particular scope can be reverted to their default values
544 * by passing the namespace to {@link #resetToDefaults(int, String)}.
545 *
Alex Salocbd05e62019-01-23 18:45:22 -0800546 * @param namespace The namespace containing the property to create or update.
547 * @param name The name of the property to create or update.
548 * @param value The value to store for the property.
Matt Pape1278d1c2018-12-11 13:03:49 -0800549 * @param makeDefault Whether to make the new value the default one.
550 * @return True if the value was set, false if the storage implementation throws errors.
Matt Pape1278d1c2018-12-11 13:03:49 -0800551 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800552 * @see #resetToDefaults(int, String).
Matt Pape1278d1c2018-12-11 13:03:49 -0800553 */
554 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800555 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000556 @RequiresPermission(WRITE_DEVICE_CONFIG)
Matt Pape1278d1c2018-12-11 13:03:49 -0800557 public static boolean setProperty(
558 String namespace, String name, String value, boolean makeDefault) {
559 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
560 String compositeName = createCompositeName(namespace, name);
561 return Settings.Config.putString(contentResolver, compositeName, value, makeDefault);
562 }
563
564 /**
565 * Reset properties to their default values.
566 * <p>
567 * The method accepts an optional namespace parameter. If provided, only properties set within
568 * that namespace will be reset. Otherwise, all properties will be reset.
569 *
570 * @param resetMode The reset mode to use.
571 * @param namespace Optionally, the specific namespace which resets will be limited to.
Matt Pape1278d1c2018-12-11 13:03:49 -0800572 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800573 * @see #setProperty(String, String, String, boolean)
Matt Pape1278d1c2018-12-11 13:03:49 -0800574 */
575 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800576 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000577 @RequiresPermission(WRITE_DEVICE_CONFIG)
Matt Pape1278d1c2018-12-11 13:03:49 -0800578 public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) {
579 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
580 Settings.Config.resetToDefaults(contentResolver, resetMode, namespace);
581 }
582
583 /**
584 * Add a listener for property changes.
585 * <p>
586 * This listener will be called whenever properties in the specified namespace change. Callbacks
587 * will be made on the specified executor. Future calls to this method with the same listener
588 * will replace the old namespace and executor. Remove the listener entirely by calling
589 * {@link #removeOnPropertyChangedListener(OnPropertyChangedListener)}.
590 *
Alex Salocbd05e62019-01-23 18:45:22 -0800591 * @param namespace The namespace containing properties to monitor.
592 * @param executor The executor which will be used to run callbacks.
Matt Pape1278d1c2018-12-11 13:03:49 -0800593 * @param onPropertyChangedListener The listener to add.
Matt Pape1278d1c2018-12-11 13:03:49 -0800594 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800595 * @see #removeOnPropertyChangedListener(OnPropertyChangedListener)
Matt Pape1278d1c2018-12-11 13:03:49 -0800596 */
597 @SystemApi
Felipe Lemecc510272019-02-07 14:56:27 -0800598 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000599 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Pape1278d1c2018-12-11 13:03:49 -0800600 public static void addOnPropertyChangedListener(
601 @NonNull String namespace,
602 @NonNull @CallbackExecutor Executor executor,
603 @NonNull OnPropertyChangedListener onPropertyChangedListener) {
Matt Pape1278d1c2018-12-11 13:03:49 -0800604 synchronized (sLock) {
Matt Papeea9881d2019-03-07 15:59:47 -0800605 Pair<String, Executor> oldNamespace = sSingleListeners.get(onPropertyChangedListener);
Matt Pape1278d1c2018-12-11 13:03:49 -0800606 if (oldNamespace == null) {
607 // Brand new listener, add it to the list.
Matt Papeea9881d2019-03-07 15:59:47 -0800608 sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
Matt Pape1278d1c2018-12-11 13:03:49 -0800609 incrementNamespace(namespace);
610 } else if (namespace.equals(oldNamespace.first)) {
611 // Listener is already registered for this namespace, update executor just in case.
Matt Papeea9881d2019-03-07 15:59:47 -0800612 sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
Matt Pape1278d1c2018-12-11 13:03:49 -0800613 } else {
614 // Update this listener from an old namespace to the new one.
Matt Papeea9881d2019-03-07 15:59:47 -0800615 decrementNamespace(sSingleListeners.get(onPropertyChangedListener).first);
616 sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
617 incrementNamespace(namespace);
618 }
619 }
620 }
621
622 /**
623 * Add a listener for property changes.
624 * <p>
625 * This listener will be called whenever properties in the specified namespace change. Callbacks
626 * will be made on the specified executor. Future calls to this method with the same listener
627 * will replace the old namespace and executor. Remove the listener entirely by calling
628 * {@link #removeOnPropertiesChangedListener(OnPropertiesChangedListener)}.
629 *
630 * @param namespace The namespace containing properties to monitor.
631 * @param executor The executor which will be used to run callbacks.
632 * @param onPropertiesChangedListener The listener to add.
633 * @hide
634 * @see #removeOnPropertiesChangedListener(OnPropertiesChangedListener)
635 */
636 @SystemApi
637 @TestApi
638 @RequiresPermission(READ_DEVICE_CONFIG)
639 public static void addOnPropertiesChangedListener(
640 @NonNull String namespace,
641 @NonNull @CallbackExecutor Executor executor,
642 @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
643 synchronized (sLock) {
644 Pair<String, Executor> oldNamespace = sListeners.get(onPropertiesChangedListener);
645 if (oldNamespace == null) {
646 // Brand new listener, add it to the list.
647 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
648 incrementNamespace(namespace);
649 } else if (namespace.equals(oldNamespace.first)) {
650 // Listener is already registered for this namespace, update executor just in case.
651 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
652 } else {
653 // Update this listener from an old namespace to the new one.
654 decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
655 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
Matt Pape1278d1c2018-12-11 13:03:49 -0800656 incrementNamespace(namespace);
657 }
658 }
659 }
660
661 /**
662 * Remove a listener for property changes. The listener will receive no further notification of
663 * property changes.
664 *
665 * @param onPropertyChangedListener The listener to remove.
Matt Pape1278d1c2018-12-11 13:03:49 -0800666 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800667 * @see #addOnPropertyChangedListener(String, Executor, OnPropertyChangedListener)
Matt Pape1278d1c2018-12-11 13:03:49 -0800668 */
669 @SystemApi
Felipe Lemecc510272019-02-07 14:56:27 -0800670 @TestApi
Matt Pape1278d1c2018-12-11 13:03:49 -0800671 public static void removeOnPropertyChangedListener(
Matt Papeea9881d2019-03-07 15:59:47 -0800672 @NonNull OnPropertyChangedListener onPropertyChangedListener) {
673 Preconditions.checkNotNull(onPropertyChangedListener);
Matt Pape1278d1c2018-12-11 13:03:49 -0800674 synchronized (sLock) {
Matt Papeea9881d2019-03-07 15:59:47 -0800675 if (sSingleListeners.containsKey(onPropertyChangedListener)) {
676 decrementNamespace(sSingleListeners.get(onPropertyChangedListener).first);
677 sSingleListeners.remove(onPropertyChangedListener);
678 }
679 }
680 }
681
682 /**
683 * Remove a listener for property changes. The listener will receive no further notification of
684 * property changes.
685 *
686 * @param onPropertiesChangedListener The listener to remove.
687 * @hide
688 * @see #addOnPropertiesChangedListener(String, Executor, OnPropertiesChangedListener)
689 */
690 @SystemApi
691 @TestApi
692 public static void removeOnPropertiesChangedListener(
693 @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
694 Preconditions.checkNotNull(onPropertiesChangedListener);
695 synchronized (sLock) {
696 if (sListeners.containsKey(onPropertiesChangedListener)) {
697 decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
698 sListeners.remove(onPropertiesChangedListener);
Matt Pape1278d1c2018-12-11 13:03:49 -0800699 }
700 }
701 }
702
703 private static String createCompositeName(String namespace, String name) {
704 return namespace + "/" + name;
705 }
706
707 private static Uri createNamespaceUri(String namespace) {
708 return CONTENT_URI.buildUpon().appendPath(namespace).build();
709 }
710
711 /**
712 * Increment the count used to represent the number of listeners subscribed to the given
713 * namespace. If this is the first (i.e. incrementing from 0 to 1) for the given namespace, a
714 * ContentObserver is registered.
715 *
716 * @param namespace The namespace to increment the count for.
717 */
718 @GuardedBy("sLock")
719 private static void incrementNamespace(String namespace) {
720 Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
721 if (namespaceCount != null) {
722 sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second + 1));
723 } else {
724 // This is a new namespace, register a ContentObserver for it.
725 ContentObserver contentObserver = new ContentObserver(null) {
726 @Override
727 public void onChange(boolean selfChange, Uri uri) {
728 handleChange(uri);
729 }
730 };
731 ActivityThread.currentApplication().getContentResolver()
732 .registerContentObserver(createNamespaceUri(namespace), true, contentObserver);
733 sNamespaces.put(namespace, new Pair<>(contentObserver, 1));
734 }
735 }
736
737 /**
738 * Decrement the count used to represent th enumber of listeners subscribed to the given
739 * namespace. If this is the final decrement call (i.e. decrementing from 1 to 0) for the given
740 * namespace, the ContentObserver that had been tracking it will be removed.
741 *
742 * @param namespace The namespace to decrement the count for.
743 */
744 @GuardedBy("sLock")
745 private static void decrementNamespace(String namespace) {
746 Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
747 if (namespaceCount == null) {
748 // This namespace is not registered and does not need to be decremented
749 return;
750 } else if (namespaceCount.second > 1) {
751 sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second - 1));
752 } else {
753 // Decrementing a namespace to zero means we no longer need its ContentObserver.
754 ActivityThread.currentApplication().getContentResolver()
755 .unregisterContentObserver(namespaceCount.first);
756 sNamespaces.remove(namespace);
757 }
758 }
759
760 private static void handleChange(Uri uri) {
761 List<String> pathSegments = uri.getPathSegments();
762 // pathSegments(0) is "config"
Matt Pape1013d292019-02-27 15:20:50 -0800763 final String namespace = pathSegments.get(1);
764 final String name = pathSegments.get(2);
765 final String value = getProperty(namespace, name);
Matt Pape1278d1c2018-12-11 13:03:49 -0800766 synchronized (sLock) {
Matt Papeea9881d2019-03-07 15:59:47 -0800767 // OnPropertiesChangedListeners
768 for (int i = 0; i < sListeners.size(); i++) {
769 if (namespace.equals(sListeners.valueAt(i).first)) {
770 final int j = i;
771 sListeners.valueAt(i).second.execute(new Runnable() {
Matt Pape1278d1c2018-12-11 13:03:49 -0800772 @Override
773 public void run() {
Matt Pape1013d292019-02-27 15:20:50 -0800774 Map<String, String> propertyMap = new HashMap(1);
775 propertyMap.put(name, value);
Matt Papeea9881d2019-03-07 15:59:47 -0800776 sListeners.keyAt(j)
777 .onPropertiesChanged(new Properties(namespace, propertyMap));
778 }
779
780 });
781 }
782 }
783 // OnPropertyChangedListeners
784 for (int i = 0; i < sSingleListeners.size(); i++) {
785 if (namespace.equals(sSingleListeners.valueAt(i).first)) {
786 final int j = i;
787 sSingleListeners.valueAt(i).second.execute(new Runnable() {
788 @Override
789 public void run() {
790 sSingleListeners.keyAt(j).onPropertyChanged(namespace, name, value);
Matt Pape1278d1c2018-12-11 13:03:49 -0800791 }
792
793 });
794 }
795 }
796 }
797 }
798
799 /**
Matt Papeea9881d2019-03-07 15:59:47 -0800800 * Interface for monitoring single property changes.
Matt Pape1278d1c2018-12-11 13:03:49 -0800801 * <p>
802 * Override {@link #onPropertyChanged(String, String, String)} to handle callbacks for changes.
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800803 *
804 * @hide
Matt Pape1278d1c2018-12-11 13:03:49 -0800805 */
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800806 @SystemApi
Felipe Lemecc510272019-02-07 14:56:27 -0800807 @TestApi
Matt Pape1278d1c2018-12-11 13:03:49 -0800808 public interface OnPropertyChangedListener {
809 /**
810 * Called when a property has changed.
811 *
812 * @param namespace The namespace containing the property which has changed.
Alex Salocbd05e62019-01-23 18:45:22 -0800813 * @param name The name of the property which has changed.
814 * @param value The new value of the property which has changed.
Matt Pape1278d1c2018-12-11 13:03:49 -0800815 */
816 void onPropertyChanged(String namespace, String name, String value);
Matt Papeea9881d2019-03-07 15:59:47 -0800817 }
Matt Pape1013d292019-02-27 15:20:50 -0800818
Matt Papeea9881d2019-03-07 15:59:47 -0800819 /**
820 * Interface for monitoring changes to properties.
821 * <p>
822 * Override {@link #onPropertiesChanged(Properties)} to handle callbacks for changes.
823 *
824 * @hide
825 */
826 @SystemApi
827 @TestApi
828 public interface OnPropertiesChangedListener {
Matt Pape1013d292019-02-27 15:20:50 -0800829 /**
830 * Called when one or more properties have changed.
831 *
832 * @param properties Contains the complete collection of properties which have changed for a
Matt Papeea9881d2019-03-07 15:59:47 -0800833 * single namespace.
Matt Pape1013d292019-02-27 15:20:50 -0800834 */
Matt Papeea9881d2019-03-07 15:59:47 -0800835 void onPropertiesChanged(@NonNull Properties properties);
Matt Pape1013d292019-02-27 15:20:50 -0800836 }
837
838 /**
839 * A mapping of properties to values, as well as a single namespace which they all belong to.
840 *
841 * @hide
842 */
843 @SystemApi
844 @TestApi
845 public static class Properties {
846 private final String mNamespace;
847 private final HashMap<String, String> mMap;
848
849 /**
850 * Create a mapping of properties to values and the namespace they belong to.
851 *
852 * @param namespace The namespace these properties belong to.
853 * @param keyValueMap A map between property names and property values.
854 */
855 Properties(@NonNull String namespace, @Nullable Map<String, String> keyValueMap) {
856 Preconditions.checkNotNull(namespace);
857 mNamespace = namespace;
858 mMap = new HashMap();
859 if (keyValueMap != null) {
860 mMap.putAll(keyValueMap);
861 }
862 }
863
864 /**
865 * @return the namespace all properties within this instance belong to.
866 */
867 @NonNull
868 public String getNamespace() {
869 return mNamespace;
870 }
871
872 /**
873 * @return the non-null set of property names.
874 */
875 @NonNull
876 public Set<String> getKeyset() {
877 return mMap.keySet();
878 }
879
880 /**
881 * Look up the String value of a property.
882 *
883 * @param name The name of the property to look up.
884 * @param defaultValue The value to return if the property has not been defined.
885 * @return the corresponding value, or defaultValue if none exists.
886 */
887 @Nullable
888 public String getString(@NonNull String name, @Nullable String defaultValue) {
889 Preconditions.checkNotNull(name);
890 String value = mMap.get(name);
891 return value != null ? value : defaultValue;
892 }
893
894 /**
895 * Look up the boolean value of a property.
896 *
897 * @param name The name of the property to look up.
898 * @param defaultValue The value to return if the property has not been defined.
899 * @return the corresponding value, or defaultValue if none exists.
900 */
901 public boolean getBoolean(@NonNull String name, boolean defaultValue) {
902 Preconditions.checkNotNull(name);
903 String value = mMap.get(name);
904 return value != null ? Boolean.parseBoolean(value) : defaultValue;
905 }
906
907 /**
908 * Look up the int value of a property.
909 *
910 * @param name The name of the property to look up.
911 * @param defaultValue The value to return if the property has not been defined or fails to
912 * parse into an int.
913 * @return the corresponding value, or defaultValue if no valid int is available.
914 */
915 public int getInt(@NonNull String name, int defaultValue) {
916 Preconditions.checkNotNull(name);
917 String value = mMap.get(name);
918 try {
919 return Integer.parseInt(value);
920 } catch (NumberFormatException e) {
921 return defaultValue;
922 }
923 }
924
925 /**
926 * Look up the long value of a property.
927 *
928 * @param name The name of the property to look up.
929 * @param defaultValue The value to return if the property has not been defined. or fails to
930 * parse into a long.
931 * @return the corresponding value, or defaultValue if no valid long is available.
932 */
933 public long getLong(@NonNull String name, long defaultValue) {
934 Preconditions.checkNotNull(name);
935 String value = mMap.get(name);
936 try {
937 return Long.parseLong(value);
938 } catch (NumberFormatException e) {
939 return defaultValue;
940 }
941 }
942
943 /**
944 * Look up the int value of a property.
945 *
946 * @param name The name of the property to look up.
947 * @param defaultValue The value to return if the property has not been defined. or fails to
948 * parse into a float.
949 * @return the corresponding value, or defaultValue if no valid float is available.
950 */
951 public float getFloat(@NonNull String name, float defaultValue) {
952 Preconditions.checkNotNull(name);
953 String value = mMap.get(name);
954 try {
955 return Float.parseFloat(value);
956 } catch (NumberFormatException e) {
957 return defaultValue;
958 } catch (NullPointerException e) {
959 return defaultValue;
960 }
961 }
Matt Pape1278d1c2018-12-11 13:03:49 -0800962 }
963}