blob: 9dc6806292608fc2787dc6a667415e31014889e9 [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 *
Victor Hsiehc6fff302019-10-11 10:13:50 -0700133 * @deprecated No longer used
Matt Pape2933d3d2019-03-11 15:42:55 -0700134 * @hide
135 */
Victor Hsiehc6fff302019-10-11 10:13:50 -0700136 @Deprecated
Matt Pape2933d3d2019-03-11 15:42:55 -0700137 @SystemApi
138 public static final String NAMESPACE_DEX_BOOT = "dex_boot";
139
140 /**
Long Ling260c5242019-08-08 16:05:19 -0700141 * Namespace for display manager related features. The names to access the properties in this
142 * namespace should be defined in {@link android.hardware.display.DisplayManager}.
143 *
144 * @hide
145 */
146 @SystemApi
147 public static final String NAMESPACE_DISPLAY_MANAGER = "display_manager";
148
149 /**
Matt Papebecd5aa2019-02-28 14:08:51 -0800150 * Namespace for all Game Driver features.
151 *
152 * @hide
153 */
154 @SystemApi
155 public static final String NAMESPACE_GAME_DRIVER = "game_driver";
156
157 /**
Siarhei Vishniakou39a1a982019-01-11 09:22:32 -0800158 * Namespace for all input-related features that are used at the native level.
159 * These features are applied at reboot.
160 *
161 * @hide
162 */
163 @SystemApi
164 public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
165
chenbrucedb279e82018-12-27 17:12:31 +0800166 /**
Matt Papef82bb8b2019-03-12 09:56:28 -0700167 * Namespace for attention-based features provided by on-device machine intelligence.
168 *
169 * @hide
170 */
171 @SystemApi
172 public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention";
173
174 /**
Anna Wasewicz3ecea552019-04-19 15:27:09 -0700175 * Definitions for properties related to Content Suggestions.
176 *
177 * @hide
178 */
179 public static final String NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS =
180 "intelligence_content_suggestions";
181
182 /**
Matt Papebecd5aa2019-02-28 14:08:51 -0800183 * Namespace for all media native related features.
184 *
185 * @hide
186 */
187 @SystemApi
188 public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
189
190 /**
chenbrucedb279e82018-12-27 17:12:31 +0800191 * Namespace for all netd related features.
192 *
193 * @hide
194 */
195 @SystemApi
196 public static final String NAMESPACE_NETD_NATIVE = "netd_native";
197
Gustav Sennton1aebc9b2019-01-22 17:56:18 +0000198 /**
Carmen Agimof54a6c892019-01-25 16:50:11 +0000199 * Namespace for features related to the Package Manager Service.
200 *
201 * @hide
202 */
203 @SystemApi
204 public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
205
206 /**
Matt Pape12187ae2019-03-14 13:37:32 -0700207 * Namespace for Rollback flags that are applied immediately.
208 *
209 * @hide
210 */
211 @SystemApi @TestApi
212 public static final String NAMESPACE_ROLLBACK = "rollback";
213
214 /**
215 * Namespace for Rollback flags that are applied after a reboot.
216 *
217 * @hide
218 */
219 @SystemApi @TestApi
220 public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
221
222 /**
Mathieu Chartier881c7a92019-03-14 16:04:45 -0700223 * Namespace for all runtime related features that don't require a reboot to become active.
224 * There are no feature flags using NAMESPACE_RUNTIME.
Matt Papee9b680a2019-03-05 15:53:13 -0800225 *
226 * @hide
227 */
228 @SystemApi
229 public static final String NAMESPACE_RUNTIME = "runtime";
230
231 /**
Mathieu Chartier881c7a92019-03-14 16:04:45 -0700232 * Namespace for all runtime related features that require system properties for accessing
233 * the feature flags from C++ or Java language code. One example is the app image startup
234 * cache feature use_app_image_startup_cache.
Matt Papee9b680a2019-03-05 15:53:13 -0800235 *
236 * @hide
237 */
238 @SystemApi
239 public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
240
241 /**
Matt Papebecd5aa2019-02-28 14:08:51 -0800242 * Namespace for all runtime native boot related features. Boot in this case refers to the
243 * fact that the properties only take affect after rebooting the device.
244 *
245 * @hide
246 */
247 @SystemApi
248 public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
249
250 /**
Matt Papef095f3c2019-03-15 13:17:45 -0700251 * Namespace for system scheduler related features. These features will be applied
252 * immediately upon change.
253 *
254 * @hide
255 */
256 @SystemApi
257 public static final String NAMESPACE_SCHEDULER = "scheduler";
258
259 /**
Matt Pape19ea8a62019-03-18 10:38:25 -0700260 * Namespace for storage-related features.
261 *
shafik91b34612019-09-23 15:41:44 +0100262 * @deprecated Replace storage namespace with storage_native_boot.
263 * @hide
264 */
265 @Deprecated
266 @SystemApi
267 public static final String NAMESPACE_STORAGE = "storage";
268
269 /**
270 * Namespace for storage-related features, including native and boot.
271 *
Matt Pape19ea8a62019-03-18 10:38:25 -0700272 * @hide
273 */
274 @SystemApi
shafik91b34612019-09-23 15:41:44 +0100275 public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
Matt Pape19ea8a62019-03-18 10:38:25 -0700276
277 /**
Gustav Senntonddd78b22019-02-18 17:58:12 +0000278 * Namespace for System UI related features.
Gustav Sennton1aebc9b2019-01-22 17:56:18 +0000279 *
280 * @hide
281 */
282 @SystemApi
Gustav Senntonddd78b22019-02-18 17:58:12 +0000283 public static final String NAMESPACE_SYSTEMUI = "systemui";
Gustav Sennton1aebc9b2019-01-22 17:56:18 +0000284
Yiwen Chen3d3ad022019-01-22 21:15:56 -0800285 /**
Matt Pape38ed4e72019-03-20 15:03:39 -0700286 * Telephony related properties.
287 *
288 * @hide
289 */
290 @SystemApi
291 public static final String NAMESPACE_TELEPHONY = "telephony";
292
293 /**
Tony Makfc374572019-03-05 14:46:24 +0000294 * Namespace for TextClassifier related features.
295 *
296 * @hide
Abodunrinwa Toki0634af32019-04-04 13:10:59 +0100297 * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
Tony Makfc374572019-03-05 14:46:24 +0000298 */
299 @SystemApi
300 public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
301
302 /**
Makoto Onuki87d39642019-04-04 14:42:23 -0700303 * Namespace for contacts provider related features.
304 *
305 * @hide
306 */
307 public static final String NAMESPACE_CONTACTS_PROVIDER = "contacts_provider";
308
309 /**
Lei Yu5d3461b2019-04-11 13:50:50 -0700310 * Namespace for settings ui related features
311 *
312 * @hide
313 */
314 public static final String NAMESPACE_SETTINGS_UI = "settings_ui";
315
316 /**
Adrian Roos1c2e9a12019-08-20 18:23:47 +0200317 * Namespace for android related features, i.e. for flags that affect not just a single
318 * component, but the entire system.
319 *
320 * The keys for this namespace are defined in {@link AndroidDeviceConfig}.
Adrian Roosbf3bc1b2019-06-18 16:13:53 +0200321 *
322 * @hide
323 */
324 @TestApi
Adrian Roos1c2e9a12019-08-20 18:23:47 +0200325 public static final String NAMESPACE_ANDROID = "android";
326
327 /**
328 * Namespace for window manager related features.
329 *
330 * @hide
331 */
332 public static final String NAMESPACE_WINDOW_MANAGER = "window_manager";
Adrian Roosbf3bc1b2019-06-18 16:13:53 +0200333
334 /**
Stanislav Zholnin55799502019-03-08 14:54:55 +0000335 * List of namespaces which can be read without READ_DEVICE_CONFIG permission
336 *
337 * @hide
338 */
339 @NonNull
340 private static final List<String> PUBLIC_NAMESPACES =
341 Arrays.asList(NAMESPACE_TEXTCLASSIFIER, NAMESPACE_RUNTIME);
342 /**
Joel Galenson489239b2019-01-23 10:05:03 -0800343 * Privacy related properties definitions.
344 *
345 * @hide
346 */
347 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800348 @TestApi
Svet Ganovd8eb8b22019-04-05 18:52:08 -0700349 public static final String NAMESPACE_PRIVACY = "privacy";
Joel Galenson489239b2019-01-23 10:05:03 -0800350
Adrian Roosbf3bc1b2019-06-18 16:13:53 +0200351 /**
Evan Seversonf7461f02019-10-18 10:51:23 -0700352 * Permission related properties definitions.
353 *
354 * @hide
355 */
356 @SystemApi
357 @TestApi
358 public static final String NAMESPACE_PERMISSIONS = "permissions";
359
Shu Chen36b19af2019-12-11 11:44:51 +0800360 /**
361 * Namespace for all widget related features.
362 *
363 * @hide
364 */
365 public static final String NAMESPACE_WIDGET = "widget";
366
Matt Pape1278d1c2018-12-11 13:03:49 -0800367 private static final Object sLock = new Object();
368 @GuardedBy("sLock")
Matt Papeea9881d2019-03-07 15:59:47 -0800369 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 Pape793b15c2019-10-07 13:17:20 -0700391 // Fetch all properties for the namespace at once and cache them in the local process, so we
392 // incur the cost of the IPC less often. Lookups happen much more frequently than updates,
393 // and we want to optimize the former.
394 return getProperties(namespace, name).getString(name, null);
395 }
396
397 /**
398 * Look up the values of multiple properties for a particular namespace. The lookup is atomic,
399 * such that the values of these properties cannot change between the time when the first is
400 * fetched and the time when the last is fetched.
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800401 * <p>
402 * Each call to {@link #setProperties(Properties)} is also atomic and ensures that either none
403 * or all of the change is picked up here, but never only part of it.
Matt Pape793b15c2019-10-07 13:17:20 -0700404 *
405 * @param namespace The namespace containing the properties to look up.
406 * @param names The names of properties to look up, or empty to fetch all properties for the
407 * given namespace.
408 * @return {@link Properties} object containing the requested properties. This reflects the
409 * state of these properties at the time of the lookup, and is not updated to reflect any
410 * future changes. The keyset of this Properties object will contain only the intersection
411 * of properties already set and properties requested via the names parameter. Properties
412 * that are already set but were not requested will not be contained here. Properties that
413 * are not set, but were requested will not be contained here either.
414 * @hide
415 */
416 @SystemApi
417 @NonNull
418 @RequiresPermission(READ_DEVICE_CONFIG)
419 public static Properties getProperties(@NonNull String namespace, @NonNull String ... names) {
Matt Pape1278d1c2018-12-11 13:03:49 -0800420 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
Matt Pape793b15c2019-10-07 13:17:20 -0700421 return new Properties(namespace,
422 Settings.Config.getStrings(contentResolver, namespace, Arrays.asList(names)));
Matt Pape1278d1c2018-12-11 13:03:49 -0800423 }
424
425 /**
Matt Pape043437c2019-02-20 10:30:47 -0800426 * Look up the String value of a property for a particular namespace.
427 *
Matt Papefc5389a2019-02-28 11:05:03 -0800428 * @param namespace The namespace containing the property to look up.
429 * @param name The name of the property to look up.
430 * @param defaultValue The value to return if the property does not exist or has no non-null
431 * value.
Matt Pape043437c2019-02-20 10:30:47 -0800432 * @return the corresponding value, or defaultValue if none exists.
433 * @hide
434 */
435 @SystemApi
436 @TestApi
437 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700438 public static String getString(@NonNull String namespace, @NonNull String name,
439 @Nullable String defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800440 String value = getProperty(namespace, name);
441 return value != null ? value : defaultValue;
442 }
443
444 /**
445 * Look up the boolean value of a property for a particular namespace.
446 *
447 * @param namespace The namespace containing the property to look up.
448 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800449 * @param defaultValue The value to return if the property does not exist or has no non-null
450 * value.
Matt Pape043437c2019-02-20 10:30:47 -0800451 * @return the corresponding value, or defaultValue if none exists.
452 * @hide
453 */
454 @SystemApi
455 @TestApi
456 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700457 public static boolean getBoolean(@NonNull String namespace, @NonNull String name,
458 boolean defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800459 String value = getProperty(namespace, name);
460 return value != null ? Boolean.parseBoolean(value) : defaultValue;
461 }
462
463 /**
464 * Look up the int value of a property for a particular namespace.
465 *
466 * @param namespace The namespace containing the property to look up.
467 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800468 * @param defaultValue The value to return if the property does not exist, has no non-null
469 * value, or fails to parse into an int.
Matt Pape043437c2019-02-20 10:30:47 -0800470 * @return the corresponding value, or defaultValue if either none exists or it does not parse.
471 * @hide
472 */
473 @SystemApi
474 @TestApi
475 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700476 public static int getInt(@NonNull String namespace, @NonNull String name, int defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800477 String value = getProperty(namespace, name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000478 if (value == null) {
479 return defaultValue;
480 }
Matt Pape043437c2019-02-20 10:30:47 -0800481 try {
482 return Integer.parseInt(value);
483 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000484 Log.e(TAG, "Parsing integer failed for " + namespace + ":" + name);
Matt Pape043437c2019-02-20 10:30:47 -0800485 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)
Matt Papec4ed9b32019-03-11 14:07:00 -0700502 public static long getLong(@NonNull String namespace, @NonNull String name, long defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800503 String value = getProperty(namespace, name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000504 if (value == null) {
505 return defaultValue;
506 }
Matt Pape043437c2019-02-20 10:30:47 -0800507 try {
508 return Long.parseLong(value);
509 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000510 Log.e(TAG, "Parsing long failed for " + namespace + ":" + name);
Matt Pape043437c2019-02-20 10:30:47 -0800511 return defaultValue;
512 }
513 }
514
515 /**
516 * Look up the float value of a property for a particular namespace.
517 *
518 * @param namespace The namespace containing the property to look up.
519 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800520 * @param defaultValue The value to return if the property does not exist, has no non-null
521 * value, or fails to parse into a float.
Matt Pape043437c2019-02-20 10:30:47 -0800522 * @return the corresponding value, or defaultValue if either none exists or it does not parse.
523 * @hide
524 */
525 @SystemApi
526 @TestApi
527 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700528 public static float getFloat(@NonNull String namespace, @NonNull String name,
529 float defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800530 String value = getProperty(namespace, name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000531 if (value == null) {
532 return defaultValue;
533 }
Matt Pape043437c2019-02-20 10:30:47 -0800534 try {
535 return Float.parseFloat(value);
536 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000537 Log.e(TAG, "Parsing float failed for " + namespace + ":" + name);
Matt Pape043437c2019-02-20 10:30:47 -0800538 return defaultValue;
539 }
540 }
541
542 /**
Matt Pape1278d1c2018-12-11 13:03:49 -0800543 * Create a new property with the the provided name and value in the provided namespace, or
544 * update the value of such a property if it already exists. The same name can exist in multiple
545 * namespaces and might have different values in any or all namespaces.
546 * <p>
547 * The method takes an argument indicating whether to make the value the default for this
548 * property.
549 * <p>
550 * All properties stored for a particular scope can be reverted to their default values
551 * by passing the namespace to {@link #resetToDefaults(int, String)}.
552 *
Alex Salocbd05e62019-01-23 18:45:22 -0800553 * @param namespace The namespace containing the property to create or update.
554 * @param name The name of the property to create or update.
555 * @param value The value to store for the property.
Matt Pape1278d1c2018-12-11 13:03:49 -0800556 * @param makeDefault Whether to make the new value the default one.
557 * @return True if the value was set, false if the storage implementation throws errors.
Matt Pape1278d1c2018-12-11 13:03:49 -0800558 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800559 * @see #resetToDefaults(int, String).
Matt Pape1278d1c2018-12-11 13:03:49 -0800560 */
561 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800562 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000563 @RequiresPermission(WRITE_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700564 public static boolean setProperty(@NonNull String namespace, @NonNull String name,
565 @Nullable String value, boolean makeDefault) {
Matt Papec4ed9b32019-03-11 14:07:00 -0700566 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
Matt Papec6001532019-10-24 15:50:32 -0700567 return Settings.Config.putString(contentResolver, namespace, name, value, makeDefault);
Matt Pape1278d1c2018-12-11 13:03:49 -0800568 }
569
570 /**
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800571 * Set all of the properties for a specific namespace. Pre-existing properties will be updated
572 * and new properties will be added if necessary. Any pre-existing properties for the specific
573 * namespace which are not part of the provided {@link Properties} object will be deleted from
574 * the namespace. These changes are all applied atomically, such that no calls to read or reset
575 * these properties can happen in the middle of this update.
576 * <p>
577 * Each call to {@link #getProperties(String, String...)} is also atomic and ensures that either
578 * none or all of this update is picked up, but never only part of it.
579 *
580 * @param properties the complete set of properties to set for a specific namespace.
581 * @hide
582 */
583 @SystemApi
584 @RequiresPermission(WRITE_DEVICE_CONFIG)
585 public static boolean setProperties(@NonNull Properties properties) {
586 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
587 return Settings.Config.setStrings(contentResolver, properties.getNamespace(),
588 properties.mMap);
589 }
590
591 /**
Matt Pape1278d1c2018-12-11 13:03:49 -0800592 * Reset properties to their default values.
593 * <p>
594 * The method accepts an optional namespace parameter. If provided, only properties set within
595 * that namespace will be reset. Otherwise, all properties will be reset.
596 *
597 * @param resetMode The reset mode to use.
598 * @param namespace Optionally, the specific namespace which resets will be limited to.
Matt Pape1278d1c2018-12-11 13:03:49 -0800599 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800600 * @see #setProperty(String, String, String, boolean)
Matt Pape1278d1c2018-12-11 13:03:49 -0800601 */
602 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800603 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000604 @RequiresPermission(WRITE_DEVICE_CONFIG)
Matt Pape1278d1c2018-12-11 13:03:49 -0800605 public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) {
606 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
607 Settings.Config.resetToDefaults(contentResolver, resetMode, namespace);
608 }
609
610 /**
611 * Add a listener for property changes.
612 * <p>
613 * This listener will be called whenever properties in the specified namespace change. Callbacks
614 * will be made on the specified executor. Future calls to this method with the same listener
615 * will replace the old namespace and executor. Remove the listener entirely by calling
Matt Papeea9881d2019-03-07 15:59:47 -0800616 * {@link #removeOnPropertiesChangedListener(OnPropertiesChangedListener)}.
617 *
618 * @param namespace The namespace containing properties to monitor.
619 * @param executor The executor which will be used to run callbacks.
620 * @param onPropertiesChangedListener The listener to add.
621 * @hide
622 * @see #removeOnPropertiesChangedListener(OnPropertiesChangedListener)
623 */
624 @SystemApi
625 @TestApi
626 @RequiresPermission(READ_DEVICE_CONFIG)
627 public static void addOnPropertiesChangedListener(
628 @NonNull String namespace,
629 @NonNull @CallbackExecutor Executor executor,
630 @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
Stanislav Zholnin55799502019-03-08 14:54:55 +0000631 enforceReadPermission(ActivityThread.currentApplication().getApplicationContext(),
632 namespace);
Matt Papeea9881d2019-03-07 15:59:47 -0800633 synchronized (sLock) {
634 Pair<String, Executor> oldNamespace = sListeners.get(onPropertiesChangedListener);
635 if (oldNamespace == null) {
636 // Brand new listener, add it to the list.
637 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
638 incrementNamespace(namespace);
639 } else if (namespace.equals(oldNamespace.first)) {
640 // Listener is already registered for this namespace, update executor just in case.
641 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
642 } else {
643 // Update this listener from an old namespace to the new one.
644 decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
645 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
Matt Pape1278d1c2018-12-11 13:03:49 -0800646 incrementNamespace(namespace);
647 }
648 }
649 }
650
651 /**
652 * Remove a listener for property changes. The listener will receive no further notification of
653 * property changes.
654 *
Matt Papeea9881d2019-03-07 15:59:47 -0800655 * @param onPropertiesChangedListener The listener to remove.
656 * @hide
657 * @see #addOnPropertiesChangedListener(String, Executor, OnPropertiesChangedListener)
658 */
659 @SystemApi
660 @TestApi
661 public static void removeOnPropertiesChangedListener(
662 @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
663 Preconditions.checkNotNull(onPropertiesChangedListener);
664 synchronized (sLock) {
665 if (sListeners.containsKey(onPropertiesChangedListener)) {
666 decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
667 sListeners.remove(onPropertiesChangedListener);
Matt Pape1278d1c2018-12-11 13:03:49 -0800668 }
669 }
670 }
671
Matt Papec4ed9b32019-03-11 14:07:00 -0700672 private static Uri createNamespaceUri(@NonNull String namespace) {
673 Preconditions.checkNotNull(namespace);
Matt Pape1278d1c2018-12-11 13:03:49 -0800674 return CONTENT_URI.buildUpon().appendPath(namespace).build();
675 }
676
677 /**
678 * Increment the count used to represent the number of listeners subscribed to the given
679 * namespace. If this is the first (i.e. incrementing from 0 to 1) for the given namespace, a
680 * ContentObserver is registered.
681 *
682 * @param namespace The namespace to increment the count for.
683 */
684 @GuardedBy("sLock")
Matt Papec4ed9b32019-03-11 14:07:00 -0700685 private static void incrementNamespace(@NonNull String namespace) {
686 Preconditions.checkNotNull(namespace);
Matt Pape1278d1c2018-12-11 13:03:49 -0800687 Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
688 if (namespaceCount != null) {
689 sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second + 1));
690 } else {
691 // This is a new namespace, register a ContentObserver for it.
692 ContentObserver contentObserver = new ContentObserver(null) {
693 @Override
694 public void onChange(boolean selfChange, Uri uri) {
Matt Papec4ed9b32019-03-11 14:07:00 -0700695 if (uri != null) {
696 handleChange(uri);
697 }
Matt Pape1278d1c2018-12-11 13:03:49 -0800698 }
699 };
700 ActivityThread.currentApplication().getContentResolver()
701 .registerContentObserver(createNamespaceUri(namespace), true, contentObserver);
702 sNamespaces.put(namespace, new Pair<>(contentObserver, 1));
703 }
704 }
705
706 /**
Stanislav Zholnin55799502019-03-08 14:54:55 +0000707 * Decrement the count used to represent the number of listeners subscribed to the given
Matt Pape1278d1c2018-12-11 13:03:49 -0800708 * namespace. If this is the final decrement call (i.e. decrementing from 1 to 0) for the given
709 * namespace, the ContentObserver that had been tracking it will be removed.
710 *
711 * @param namespace The namespace to decrement the count for.
712 */
713 @GuardedBy("sLock")
Matt Papec4ed9b32019-03-11 14:07:00 -0700714 private static void decrementNamespace(@NonNull String namespace) {
715 Preconditions.checkNotNull(namespace);
Matt Pape1278d1c2018-12-11 13:03:49 -0800716 Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
717 if (namespaceCount == null) {
718 // This namespace is not registered and does not need to be decremented
719 return;
720 } else if (namespaceCount.second > 1) {
721 sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second - 1));
722 } else {
723 // Decrementing a namespace to zero means we no longer need its ContentObserver.
724 ActivityThread.currentApplication().getContentResolver()
725 .unregisterContentObserver(namespaceCount.first);
726 sNamespaces.remove(namespace);
727 }
728 }
729
Matt Papec4ed9b32019-03-11 14:07:00 -0700730 private static void handleChange(@NonNull Uri uri) {
731 Preconditions.checkNotNull(uri);
Matt Pape1278d1c2018-12-11 13:03:49 -0800732 List<String> pathSegments = uri.getPathSegments();
733 // pathSegments(0) is "config"
Matt Pape1013d292019-02-27 15:20:50 -0800734 final String namespace = pathSegments.get(1);
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800735 Map<String, String> propertyMap = new ArrayMap<>();
Stanislav Zholnin55799502019-03-08 14:54:55 +0000736 try {
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800737 Properties allProperties = getProperties(namespace);
738 for (int i = 2; i < pathSegments.size(); ++i) {
739 String key = pathSegments.get(i);
740 propertyMap.put(key, allProperties.getString(key, null));
741 }
Stanislav Zholnin55799502019-03-08 14:54:55 +0000742 } catch (SecurityException e) {
743 // Silently failing to not crash binder or listener threads.
744 Log.e(TAG, "OnPropertyChangedListener update failed: permission violation.");
745 return;
746 }
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800747 Properties properties = new Properties(namespace, propertyMap);
748
Matt Pape1278d1c2018-12-11 13:03:49 -0800749 synchronized (sLock) {
Matt Papeea9881d2019-03-07 15:59:47 -0800750 for (int i = 0; i < sListeners.size(); i++) {
751 if (namespace.equals(sListeners.valueAt(i).first)) {
Pavel Grafovc2370732019-08-16 12:04:35 +0100752 final OnPropertiesChangedListener listener = sListeners.keyAt(i);
753 sListeners.valueAt(i).second.execute(() -> {
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800754 listener.onPropertiesChanged(properties);
Matt Papeea9881d2019-03-07 15:59:47 -0800755 });
756 }
757 }
Matt Pape1278d1c2018-12-11 13:03:49 -0800758 }
759 }
760
Stanislav Zholnin55799502019-03-08 14:54:55 +0000761 /**
762 * Enforces READ_DEVICE_CONFIG permission if namespace is not one of public namespaces.
763 * @hide
764 */
765 public static void enforceReadPermission(Context context, String namespace) {
766 if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
767 != PackageManager.PERMISSION_GRANTED) {
768 if (!PUBLIC_NAMESPACES.contains(namespace)) {
769 throw new SecurityException("Permission denial: reading from settings requires:"
770 + READ_DEVICE_CONFIG);
771 }
772 }
773 }
774
Matt Papeea9881d2019-03-07 15:59:47 -0800775 /**
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800776 * Interface for monitoring changes to properties. Implementations will receive callbacks when
777 * properties change, including a {@link Properties} object which contains a single namespace
778 * and all of the properties which changed for that namespace. This includes properties which
779 * were added, updated, or deleted. This is not necessarily a complete list of all properties
780 * belonging to the namespace, as properties which don't change are omitted.
Matt Papeea9881d2019-03-07 15:59:47 -0800781 * <p>
782 * Override {@link #onPropertiesChanged(Properties)} to handle callbacks for changes.
783 *
784 * @hide
785 */
786 @SystemApi
787 @TestApi
788 public interface OnPropertiesChangedListener {
Matt Pape1013d292019-02-27 15:20:50 -0800789 /**
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800790 * Called when one or more properties have changed, providing a Properties object with all
791 * of the changed properties. This object will contain only properties which have changed,
792 * not the complete set of all properties belonging to the namespace.
Matt Pape1013d292019-02-27 15:20:50 -0800793 *
794 * @param properties Contains the complete collection of properties which have changed for a
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800795 * single namespace. This includes only those which were added, updated,
796 * or deleted.
Matt Pape1013d292019-02-27 15:20:50 -0800797 */
Matt Papeea9881d2019-03-07 15:59:47 -0800798 void onPropertiesChanged(@NonNull Properties properties);
Matt Pape1013d292019-02-27 15:20:50 -0800799 }
800
801 /**
802 * A mapping of properties to values, as well as a single namespace which they all belong to.
803 *
804 * @hide
805 */
806 @SystemApi
807 @TestApi
808 public static class Properties {
809 private final String mNamespace;
810 private final HashMap<String, String> mMap;
811
812 /**
813 * Create a mapping of properties to values and the namespace they belong to.
814 *
815 * @param namespace The namespace these properties belong to.
816 * @param keyValueMap A map between property names and property values.
Dave Mankoff186bd742019-08-15 10:26:38 -0400817 * @hide
Matt Pape1013d292019-02-27 15:20:50 -0800818 */
Dave Mankoff186bd742019-08-15 10:26:38 -0400819 public Properties(@NonNull String namespace, @Nullable Map<String, String> keyValueMap) {
Matt Pape1013d292019-02-27 15:20:50 -0800820 Preconditions.checkNotNull(namespace);
821 mNamespace = namespace;
822 mMap = new HashMap();
823 if (keyValueMap != null) {
824 mMap.putAll(keyValueMap);
825 }
826 }
827
828 /**
829 * @return the namespace all properties within this instance belong to.
830 */
831 @NonNull
832 public String getNamespace() {
833 return mNamespace;
834 }
835
836 /**
837 * @return the non-null set of property names.
838 */
839 @NonNull
840 public Set<String> getKeyset() {
841 return mMap.keySet();
842 }
843
844 /**
845 * Look up the String value of a property.
846 *
847 * @param name The name of the property to look up.
848 * @param defaultValue The value to return if the property has not been defined.
849 * @return the corresponding value, or defaultValue if none exists.
850 */
851 @Nullable
852 public String getString(@NonNull String name, @Nullable String defaultValue) {
853 Preconditions.checkNotNull(name);
854 String value = mMap.get(name);
855 return value != null ? value : defaultValue;
856 }
857
858 /**
859 * Look up the boolean value of a property.
860 *
861 * @param name The name of the property to look up.
862 * @param defaultValue The value to return if the property has not been defined.
863 * @return the corresponding value, or defaultValue if none exists.
864 */
865 public boolean getBoolean(@NonNull String name, boolean defaultValue) {
866 Preconditions.checkNotNull(name);
867 String value = mMap.get(name);
868 return value != null ? Boolean.parseBoolean(value) : defaultValue;
869 }
870
871 /**
872 * Look up the int value of a property.
873 *
874 * @param name The name of the property to look up.
875 * @param defaultValue The value to return if the property has not been defined or fails to
876 * parse into an int.
877 * @return the corresponding value, or defaultValue if no valid int is available.
878 */
879 public int getInt(@NonNull String name, int defaultValue) {
880 Preconditions.checkNotNull(name);
881 String value = mMap.get(name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000882 if (value == null) {
883 return defaultValue;
884 }
Matt Pape1013d292019-02-27 15:20:50 -0800885 try {
886 return Integer.parseInt(value);
887 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000888 Log.e(TAG, "Parsing int failed for " + name);
Matt Pape1013d292019-02-27 15:20:50 -0800889 return defaultValue;
890 }
891 }
892
893 /**
894 * Look up the long value of a property.
895 *
896 * @param name The name of the property to look up.
897 * @param defaultValue The value to return if the property has not been defined. or fails to
898 * parse into a long.
899 * @return the corresponding value, or defaultValue if no valid long is available.
900 */
901 public long getLong(@NonNull String name, long defaultValue) {
902 Preconditions.checkNotNull(name);
903 String value = mMap.get(name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000904 if (value == null) {
905 return defaultValue;
906 }
Matt Pape1013d292019-02-27 15:20:50 -0800907 try {
908 return Long.parseLong(value);
909 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000910 Log.e(TAG, "Parsing long failed for " + name);
Matt Pape1013d292019-02-27 15:20:50 -0800911 return defaultValue;
912 }
913 }
914
915 /**
916 * Look up the int value of a property.
917 *
918 * @param name The name of the property to look up.
919 * @param defaultValue The value to return if the property has not been defined. or fails to
920 * parse into a float.
921 * @return the corresponding value, or defaultValue if no valid float is available.
922 */
923 public float getFloat(@NonNull String name, float defaultValue) {
924 Preconditions.checkNotNull(name);
925 String value = mMap.get(name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000926 if (value == null) {
927 return defaultValue;
928 }
Matt Pape1013d292019-02-27 15:20:50 -0800929 try {
930 return Float.parseFloat(value);
931 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000932 Log.e(TAG, "Parsing float failed for " + name);
Matt Pape1013d292019-02-27 15:20:50 -0800933 return defaultValue;
934 }
935 }
Matt Pape1278d1c2018-12-11 13:03:49 -0800936 }
937}