blob: abf34ca2d21ad07b757c1d2f0e1d990c66d4a5be [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
Matt Pape1278d1c2018-12-11 13:03:49 -0800360 private static final Object sLock = new Object();
361 @GuardedBy("sLock")
Matt Papeea9881d2019-03-07 15:59:47 -0800362 private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
363 new ArrayMap<>();
Matt Pape1278d1c2018-12-11 13:03:49 -0800364 @GuardedBy("sLock")
365 private static Map<String, Pair<ContentObserver, Integer>> sNamespaces = new HashMap<>();
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000366 private static final String TAG = "DeviceConfig";
Matt Pape1278d1c2018-12-11 13:03:49 -0800367
368 // Should never be invoked
369 private DeviceConfig() {
370 }
371
372 /**
373 * Look up the value of a property for a particular namespace.
374 *
375 * @param namespace The namespace containing the property to look up.
Alex Salocbd05e62019-01-23 18:45:22 -0800376 * @param name The name of the property to look up.
Matt Pape1278d1c2018-12-11 13:03:49 -0800377 * @return the corresponding value, or null if not present.
Matt Pape1278d1c2018-12-11 13:03:49 -0800378 * @hide
379 */
380 @SystemApi
Felipe Lemed03283d2019-02-05 09:42:42 -0800381 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000382 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700383 public static String getProperty(@NonNull String namespace, @NonNull String name) {
Matt Pape793b15c2019-10-07 13:17:20 -0700384 // Fetch all properties for the namespace at once and cache them in the local process, so we
385 // incur the cost of the IPC less often. Lookups happen much more frequently than updates,
386 // and we want to optimize the former.
387 return getProperties(namespace, name).getString(name, null);
388 }
389
390 /**
391 * Look up the values of multiple properties for a particular namespace. The lookup is atomic,
392 * such that the values of these properties cannot change between the time when the first is
393 * fetched and the time when the last is fetched.
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800394 * <p>
395 * Each call to {@link #setProperties(Properties)} is also atomic and ensures that either none
396 * or all of the change is picked up here, but never only part of it.
Matt Pape793b15c2019-10-07 13:17:20 -0700397 *
398 * @param namespace The namespace containing the properties to look up.
399 * @param names The names of properties to look up, or empty to fetch all properties for the
400 * given namespace.
401 * @return {@link Properties} object containing the requested properties. This reflects the
402 * state of these properties at the time of the lookup, and is not updated to reflect any
403 * future changes. The keyset of this Properties object will contain only the intersection
404 * of properties already set and properties requested via the names parameter. Properties
405 * that are already set but were not requested will not be contained here. Properties that
406 * are not set, but were requested will not be contained here either.
407 * @hide
408 */
409 @SystemApi
410 @NonNull
411 @RequiresPermission(READ_DEVICE_CONFIG)
412 public static Properties getProperties(@NonNull String namespace, @NonNull String ... names) {
Matt Pape1278d1c2018-12-11 13:03:49 -0800413 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
Matt Pape793b15c2019-10-07 13:17:20 -0700414 return new Properties(namespace,
415 Settings.Config.getStrings(contentResolver, namespace, Arrays.asList(names)));
Matt Pape1278d1c2018-12-11 13:03:49 -0800416 }
417
418 /**
Matt Pape043437c2019-02-20 10:30:47 -0800419 * Look up the String value of a property for a particular namespace.
420 *
Matt Papefc5389a2019-02-28 11:05:03 -0800421 * @param namespace The namespace containing the property to look up.
422 * @param name The name of the property to look up.
423 * @param defaultValue The value to return if the property does not exist or has no non-null
424 * value.
Matt Pape043437c2019-02-20 10:30:47 -0800425 * @return the corresponding value, or defaultValue if none exists.
426 * @hide
427 */
428 @SystemApi
429 @TestApi
430 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700431 public static String getString(@NonNull String namespace, @NonNull String name,
432 @Nullable String defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800433 String value = getProperty(namespace, name);
434 return value != null ? value : defaultValue;
435 }
436
437 /**
438 * Look up the boolean value of a property for a particular namespace.
439 *
440 * @param namespace The namespace containing the property to look up.
441 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800442 * @param defaultValue The value to return if the property does not exist or has no non-null
443 * value.
Matt Pape043437c2019-02-20 10:30:47 -0800444 * @return the corresponding value, or defaultValue if none exists.
445 * @hide
446 */
447 @SystemApi
448 @TestApi
449 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700450 public static boolean getBoolean(@NonNull String namespace, @NonNull String name,
451 boolean defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800452 String value = getProperty(namespace, name);
453 return value != null ? Boolean.parseBoolean(value) : defaultValue;
454 }
455
456 /**
457 * Look up the int value of a property for a particular namespace.
458 *
459 * @param namespace The namespace containing the property to look up.
460 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800461 * @param defaultValue The value to return if the property does not exist, has no non-null
462 * value, or fails to parse into an int.
Matt Pape043437c2019-02-20 10:30:47 -0800463 * @return the corresponding value, or defaultValue if either none exists or it does not parse.
464 * @hide
465 */
466 @SystemApi
467 @TestApi
468 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700469 public static int getInt(@NonNull String namespace, @NonNull String name, int defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800470 String value = getProperty(namespace, name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000471 if (value == null) {
472 return defaultValue;
473 }
Matt Pape043437c2019-02-20 10:30:47 -0800474 try {
475 return Integer.parseInt(value);
476 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000477 Log.e(TAG, "Parsing integer failed for " + namespace + ":" + name);
Matt Pape043437c2019-02-20 10:30:47 -0800478 return defaultValue;
479 }
480 }
481
482 /**
483 * Look up the long value of a property for a particular namespace.
484 *
485 * @param namespace The namespace containing the property to look up.
486 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800487 * @param defaultValue The value to return if the property does not exist, has no non-null
488 * value, or fails to parse into a long.
Matt Pape043437c2019-02-20 10:30:47 -0800489 * @return the corresponding value, or defaultValue if either none exists or it does not parse.
490 * @hide
491 */
492 @SystemApi
493 @TestApi
494 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700495 public static long getLong(@NonNull String namespace, @NonNull String name, long defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800496 String value = getProperty(namespace, name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000497 if (value == null) {
498 return defaultValue;
499 }
Matt Pape043437c2019-02-20 10:30:47 -0800500 try {
501 return Long.parseLong(value);
502 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000503 Log.e(TAG, "Parsing long failed for " + namespace + ":" + name);
Matt Pape043437c2019-02-20 10:30:47 -0800504 return defaultValue;
505 }
506 }
507
508 /**
509 * Look up the float value of a property for a particular namespace.
510 *
511 * @param namespace The namespace containing the property to look up.
512 * @param name The name of the property to look up.
Matt Papefc5389a2019-02-28 11:05:03 -0800513 * @param defaultValue The value to return if the property does not exist, has no non-null
514 * value, or fails to parse into a float.
Matt Pape043437c2019-02-20 10:30:47 -0800515 * @return the corresponding value, or defaultValue if either none exists or it does not parse.
516 * @hide
517 */
518 @SystemApi
519 @TestApi
520 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Papec4ed9b32019-03-11 14:07:00 -0700521 public static float getFloat(@NonNull String namespace, @NonNull String name,
522 float defaultValue) {
Matt Pape043437c2019-02-20 10:30:47 -0800523 String value = getProperty(namespace, name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000524 if (value == null) {
525 return defaultValue;
526 }
Matt Pape043437c2019-02-20 10:30:47 -0800527 try {
528 return Float.parseFloat(value);
529 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000530 Log.e(TAG, "Parsing float failed for " + namespace + ":" + name);
Matt Pape043437c2019-02-20 10:30:47 -0800531 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 Papec4ed9b32019-03-11 14:07:00 -0700557 public static boolean setProperty(@NonNull String namespace, @NonNull String name,
558 @Nullable String value, boolean makeDefault) {
Matt Papec4ed9b32019-03-11 14:07:00 -0700559 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
Matt Papec6001532019-10-24 15:50:32 -0700560 return Settings.Config.putString(contentResolver, namespace, name, value, makeDefault);
Matt Pape1278d1c2018-12-11 13:03:49 -0800561 }
562
563 /**
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800564 * Set all of the properties for a specific namespace. Pre-existing properties will be updated
565 * and new properties will be added if necessary. Any pre-existing properties for the specific
566 * namespace which are not part of the provided {@link Properties} object will be deleted from
567 * the namespace. These changes are all applied atomically, such that no calls to read or reset
568 * these properties can happen in the middle of this update.
569 * <p>
570 * Each call to {@link #getProperties(String, String...)} is also atomic and ensures that either
571 * none or all of this update is picked up, but never only part of it.
572 *
573 * @param properties the complete set of properties to set for a specific namespace.
574 * @hide
575 */
576 @SystemApi
577 @RequiresPermission(WRITE_DEVICE_CONFIG)
578 public static boolean setProperties(@NonNull Properties properties) {
579 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
580 return Settings.Config.setStrings(contentResolver, properties.getNamespace(),
581 properties.mMap);
582 }
583
584 /**
Matt Pape1278d1c2018-12-11 13:03:49 -0800585 * Reset properties to their default values.
586 * <p>
587 * The method accepts an optional namespace parameter. If provided, only properties set within
588 * that namespace will be reset. Otherwise, all properties will be reset.
589 *
590 * @param resetMode The reset mode to use.
591 * @param namespace Optionally, the specific namespace which resets will be limited to.
Matt Pape1278d1c2018-12-11 13:03:49 -0800592 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800593 * @see #setProperty(String, String, String, boolean)
Matt Pape1278d1c2018-12-11 13:03:49 -0800594 */
595 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800596 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000597 @RequiresPermission(WRITE_DEVICE_CONFIG)
Matt Pape1278d1c2018-12-11 13:03:49 -0800598 public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) {
599 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
600 Settings.Config.resetToDefaults(contentResolver, resetMode, namespace);
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
Matt Papeea9881d2019-03-07 15:59:47 -0800609 * {@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 *
Matt Papeea9881d2019-03-07 15:59:47 -0800648 * @param onPropertiesChangedListener The listener to remove.
649 * @hide
650 * @see #addOnPropertiesChangedListener(String, Executor, OnPropertiesChangedListener)
651 */
652 @SystemApi
653 @TestApi
654 public static void removeOnPropertiesChangedListener(
655 @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
656 Preconditions.checkNotNull(onPropertiesChangedListener);
657 synchronized (sLock) {
658 if (sListeners.containsKey(onPropertiesChangedListener)) {
659 decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
660 sListeners.remove(onPropertiesChangedListener);
Matt Pape1278d1c2018-12-11 13:03:49 -0800661 }
662 }
663 }
664
Matt Papec4ed9b32019-03-11 14:07:00 -0700665 private static Uri createNamespaceUri(@NonNull String namespace) {
666 Preconditions.checkNotNull(namespace);
Matt Pape1278d1c2018-12-11 13:03:49 -0800667 return CONTENT_URI.buildUpon().appendPath(namespace).build();
668 }
669
670 /**
671 * Increment the count used to represent the number of listeners subscribed to the given
672 * namespace. If this is the first (i.e. incrementing from 0 to 1) for the given namespace, a
673 * ContentObserver is registered.
674 *
675 * @param namespace The namespace to increment the count for.
676 */
677 @GuardedBy("sLock")
Matt Papec4ed9b32019-03-11 14:07:00 -0700678 private static void incrementNamespace(@NonNull String namespace) {
679 Preconditions.checkNotNull(namespace);
Matt Pape1278d1c2018-12-11 13:03:49 -0800680 Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
681 if (namespaceCount != null) {
682 sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second + 1));
683 } else {
684 // This is a new namespace, register a ContentObserver for it.
685 ContentObserver contentObserver = new ContentObserver(null) {
686 @Override
687 public void onChange(boolean selfChange, Uri uri) {
Matt Papec4ed9b32019-03-11 14:07:00 -0700688 if (uri != null) {
689 handleChange(uri);
690 }
Matt Pape1278d1c2018-12-11 13:03:49 -0800691 }
692 };
693 ActivityThread.currentApplication().getContentResolver()
694 .registerContentObserver(createNamespaceUri(namespace), true, contentObserver);
695 sNamespaces.put(namespace, new Pair<>(contentObserver, 1));
696 }
697 }
698
699 /**
Stanislav Zholnin55799502019-03-08 14:54:55 +0000700 * Decrement the count used to represent the number of listeners subscribed to the given
Matt Pape1278d1c2018-12-11 13:03:49 -0800701 * namespace. If this is the final decrement call (i.e. decrementing from 1 to 0) for the given
702 * namespace, the ContentObserver that had been tracking it will be removed.
703 *
704 * @param namespace The namespace to decrement the count for.
705 */
706 @GuardedBy("sLock")
Matt Papec4ed9b32019-03-11 14:07:00 -0700707 private static void decrementNamespace(@NonNull String namespace) {
708 Preconditions.checkNotNull(namespace);
Matt Pape1278d1c2018-12-11 13:03:49 -0800709 Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
710 if (namespaceCount == null) {
711 // This namespace is not registered and does not need to be decremented
712 return;
713 } else if (namespaceCount.second > 1) {
714 sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second - 1));
715 } else {
716 // Decrementing a namespace to zero means we no longer need its ContentObserver.
717 ActivityThread.currentApplication().getContentResolver()
718 .unregisterContentObserver(namespaceCount.first);
719 sNamespaces.remove(namespace);
720 }
721 }
722
Matt Papec4ed9b32019-03-11 14:07:00 -0700723 private static void handleChange(@NonNull Uri uri) {
724 Preconditions.checkNotNull(uri);
Matt Pape1278d1c2018-12-11 13:03:49 -0800725 List<String> pathSegments = uri.getPathSegments();
726 // pathSegments(0) is "config"
Matt Pape1013d292019-02-27 15:20:50 -0800727 final String namespace = pathSegments.get(1);
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800728 Map<String, String> propertyMap = new ArrayMap<>();
Stanislav Zholnin55799502019-03-08 14:54:55 +0000729 try {
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800730 Properties allProperties = getProperties(namespace);
731 for (int i = 2; i < pathSegments.size(); ++i) {
732 String key = pathSegments.get(i);
733 propertyMap.put(key, allProperties.getString(key, null));
734 }
Stanislav Zholnin55799502019-03-08 14:54:55 +0000735 } catch (SecurityException e) {
736 // Silently failing to not crash binder or listener threads.
737 Log.e(TAG, "OnPropertyChangedListener update failed: permission violation.");
738 return;
739 }
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800740 Properties properties = new Properties(namespace, propertyMap);
741
Matt Pape1278d1c2018-12-11 13:03:49 -0800742 synchronized (sLock) {
Matt Papeea9881d2019-03-07 15:59:47 -0800743 for (int i = 0; i < sListeners.size(); i++) {
744 if (namespace.equals(sListeners.valueAt(i).first)) {
Pavel Grafovc2370732019-08-16 12:04:35 +0100745 final OnPropertiesChangedListener listener = sListeners.keyAt(i);
746 sListeners.valueAt(i).second.execute(() -> {
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800747 listener.onPropertiesChanged(properties);
Matt Papeea9881d2019-03-07 15:59:47 -0800748 });
749 }
750 }
Matt Pape1278d1c2018-12-11 13:03:49 -0800751 }
752 }
753
Stanislav Zholnin55799502019-03-08 14:54:55 +0000754 /**
755 * Enforces READ_DEVICE_CONFIG permission if namespace is not one of public namespaces.
756 * @hide
757 */
758 public static void enforceReadPermission(Context context, String namespace) {
759 if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
760 != PackageManager.PERMISSION_GRANTED) {
761 if (!PUBLIC_NAMESPACES.contains(namespace)) {
762 throw new SecurityException("Permission denial: reading from settings requires:"
763 + READ_DEVICE_CONFIG);
764 }
765 }
766 }
767
Matt Papeea9881d2019-03-07 15:59:47 -0800768 /**
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800769 * Interface for monitoring changes to properties. Implementations will receive callbacks when
770 * properties change, including a {@link Properties} object which contains a single namespace
771 * and all of the properties which changed for that namespace. This includes properties which
772 * were added, updated, or deleted. This is not necessarily a complete list of all properties
773 * belonging to the namespace, as properties which don't change are omitted.
Matt Papeea9881d2019-03-07 15:59:47 -0800774 * <p>
775 * Override {@link #onPropertiesChanged(Properties)} to handle callbacks for changes.
776 *
777 * @hide
778 */
779 @SystemApi
780 @TestApi
781 public interface OnPropertiesChangedListener {
Matt Pape1013d292019-02-27 15:20:50 -0800782 /**
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800783 * Called when one or more properties have changed, providing a Properties object with all
784 * of the changed properties. This object will contain only properties which have changed,
785 * not the complete set of all properties belonging to the namespace.
Matt Pape1013d292019-02-27 15:20:50 -0800786 *
787 * @param properties Contains the complete collection of properties which have changed for a
Matt Pape4bd0a6a2019-11-19 12:17:45 -0800788 * single namespace. This includes only those which were added, updated,
789 * or deleted.
Matt Pape1013d292019-02-27 15:20:50 -0800790 */
Matt Papeea9881d2019-03-07 15:59:47 -0800791 void onPropertiesChanged(@NonNull Properties properties);
Matt Pape1013d292019-02-27 15:20:50 -0800792 }
793
794 /**
795 * A mapping of properties to values, as well as a single namespace which they all belong to.
796 *
797 * @hide
798 */
799 @SystemApi
800 @TestApi
801 public static class Properties {
802 private final String mNamespace;
803 private final HashMap<String, String> mMap;
804
805 /**
806 * Create a mapping of properties to values and the namespace they belong to.
807 *
808 * @param namespace The namespace these properties belong to.
809 * @param keyValueMap A map between property names and property values.
Dave Mankoff186bd742019-08-15 10:26:38 -0400810 * @hide
Matt Pape1013d292019-02-27 15:20:50 -0800811 */
Dave Mankoff186bd742019-08-15 10:26:38 -0400812 public Properties(@NonNull String namespace, @Nullable Map<String, String> keyValueMap) {
Matt Pape1013d292019-02-27 15:20:50 -0800813 Preconditions.checkNotNull(namespace);
814 mNamespace = namespace;
815 mMap = new HashMap();
816 if (keyValueMap != null) {
817 mMap.putAll(keyValueMap);
818 }
819 }
820
821 /**
822 * @return the namespace all properties within this instance belong to.
823 */
824 @NonNull
825 public String getNamespace() {
826 return mNamespace;
827 }
828
829 /**
830 * @return the non-null set of property names.
831 */
832 @NonNull
833 public Set<String> getKeyset() {
834 return mMap.keySet();
835 }
836
837 /**
838 * Look up the String value of a property.
839 *
840 * @param name The name of the property to look up.
841 * @param defaultValue The value to return if the property has not been defined.
842 * @return the corresponding value, or defaultValue if none exists.
843 */
844 @Nullable
845 public String getString(@NonNull String name, @Nullable String defaultValue) {
846 Preconditions.checkNotNull(name);
847 String value = mMap.get(name);
848 return value != null ? value : defaultValue;
849 }
850
851 /**
852 * Look up the boolean value of a property.
853 *
854 * @param name The name of the property to look up.
855 * @param defaultValue The value to return if the property has not been defined.
856 * @return the corresponding value, or defaultValue if none exists.
857 */
858 public boolean getBoolean(@NonNull String name, boolean defaultValue) {
859 Preconditions.checkNotNull(name);
860 String value = mMap.get(name);
861 return value != null ? Boolean.parseBoolean(value) : defaultValue;
862 }
863
864 /**
865 * Look up the int value of a property.
866 *
867 * @param name The name of the property to look up.
868 * @param defaultValue The value to return if the property has not been defined or fails to
869 * parse into an int.
870 * @return the corresponding value, or defaultValue if no valid int is available.
871 */
872 public int getInt(@NonNull String name, int defaultValue) {
873 Preconditions.checkNotNull(name);
874 String value = mMap.get(name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000875 if (value == null) {
876 return defaultValue;
877 }
Matt Pape1013d292019-02-27 15:20:50 -0800878 try {
879 return Integer.parseInt(value);
880 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000881 Log.e(TAG, "Parsing int failed for " + name);
Matt Pape1013d292019-02-27 15:20:50 -0800882 return defaultValue;
883 }
884 }
885
886 /**
887 * Look up the long value of a property.
888 *
889 * @param name The name of the property to look up.
890 * @param defaultValue The value to return if the property has not been defined. or fails to
891 * parse into a long.
892 * @return the corresponding value, or defaultValue if no valid long is available.
893 */
894 public long getLong(@NonNull String name, long defaultValue) {
895 Preconditions.checkNotNull(name);
896 String value = mMap.get(name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000897 if (value == null) {
898 return defaultValue;
899 }
Matt Pape1013d292019-02-27 15:20:50 -0800900 try {
901 return Long.parseLong(value);
902 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000903 Log.e(TAG, "Parsing long failed for " + name);
Matt Pape1013d292019-02-27 15:20:50 -0800904 return defaultValue;
905 }
906 }
907
908 /**
909 * Look up the int value of a property.
910 *
911 * @param name The name of the property to look up.
912 * @param defaultValue The value to return if the property has not been defined. or fails to
913 * parse into a float.
914 * @return the corresponding value, or defaultValue if no valid float is available.
915 */
916 public float getFloat(@NonNull String name, float defaultValue) {
917 Preconditions.checkNotNull(name);
918 String value = mMap.get(name);
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000919 if (value == null) {
920 return defaultValue;
921 }
Matt Pape1013d292019-02-27 15:20:50 -0800922 try {
923 return Float.parseFloat(value);
924 } catch (NumberFormatException e) {
Stanislav Zholninffea1de2019-03-07 18:06:40 +0000925 Log.e(TAG, "Parsing float failed for " + name);
Matt Pape1013d292019-02-27 15:20:50 -0800926 return defaultValue;
927 }
928 }
Matt Pape1278d1c2018-12-11 13:03:49 -0800929 }
930}