blob: ce28c3063d04070ad9236bd5f7d21e949c4042df [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;
33import android.util.Pair;
34
35import com.android.internal.annotations.GuardedBy;
36
37import java.util.HashMap;
38import java.util.List;
39import java.util.Map;
40import java.util.concurrent.Executor;
41
42/**
43 * Device level configuration parameters which can be tuned by a separate configuration service.
44 *
45 * @hide
46 */
47@SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -080048@TestApi
Matt Pape1278d1c2018-12-11 13:03:49 -080049public final class DeviceConfig {
50 /**
51 * The content:// style URL for the config table.
52 *
53 * @hide
54 */
55 public static final Uri CONTENT_URI = Uri.parse("content://" + Settings.AUTHORITY + "/config");
56
Siarhei Vishniakou39a1a982019-01-11 09:22:32 -080057 /**
Peiyong Lin31fa8ac2019-01-09 13:35:48 -080058 * Namespace for all Game Driver features.
Alex Salocbd05e62019-01-23 18:45:22 -080059 *
Peiyong Lin31fa8ac2019-01-09 13:35:48 -080060 * @hide
61 */
62 @SystemApi
63 public static final String NAMESPACE_GAME_DRIVER = "game_driver";
64
65 /**
Perumaal S5c016da2019-01-16 21:33:22 -080066 * Namespace for autofill feature that provides suggestions across all apps when
67 * the user interacts with input fields.
68 *
69 * @hide
70 */
71 @SystemApi
Felipe Lemecc510272019-02-07 14:56:27 -080072 @TestApi
Perumaal S5c016da2019-01-16 21:33:22 -080073 public static final String NAMESPACE_AUTOFILL = "autofill";
74
75 /**
Felipe Leme14ef4612019-02-07 12:24:38 -080076 * Namespace for content capture feature used by on-device machine intelligence
77 * to provide suggestions in a privacy-safe manner.
Felipe Leme70bcf382019-01-24 14:55:03 -080078 *
79 * @hide
80 */
81 @SystemApi
Felipe Lemed03283d2019-02-05 09:42:42 -080082 @TestApi
Felipe Leme14ef4612019-02-07 12:24:38 -080083 public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
Felipe Leme70bcf382019-01-24 14:55:03 -080084
85 /**
Siarhei Vishniakou39a1a982019-01-11 09:22:32 -080086 * Namespace for all input-related features that are used at the native level.
87 * These features are applied at reboot.
88 *
89 * @hide
90 */
91 @SystemApi
92 public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
93
chenbrucedb279e82018-12-27 17:12:31 +080094 /**
95 * Namespace for all netd related features.
96 *
97 * @hide
98 */
99 @SystemApi
100 public static final String NAMESPACE_NETD_NATIVE = "netd_native";
101
Gustav Sennton1aebc9b2019-01-22 17:56:18 +0000102 /**
103 * Namespace for features related to the ExtServices Notification Assistant.
104 * These features are applied immediately.
105 *
106 * @hide
107 */
108 @SystemApi
Gustav Sennton73a8c1b2019-01-23 18:15:39 +0000109 public interface NotificationAssistant {
110 String NAMESPACE = "notification_assistant";
111 /**
112 * Whether the Notification Assistant should generate replies for notifications.
113 */
114 String GENERATE_REPLIES = "generate_replies";
115 /**
116 * Whether the Notification Assistant should generate contextual actions for notifications.
117 */
118 String GENERATE_ACTIONS = "generate_actions";
Tony Make1a27ac2019-01-31 16:32:19 +0000119
120 String MAX_MESSAGES_TO_EXTRACT = "max_messages_to_extract";
121
122 String MAX_SUGGESTIONS = "max_suggestions";
Gustav Sennton73a8c1b2019-01-23 18:15:39 +0000123 }
Gustav Sennton1aebc9b2019-01-22 17:56:18 +0000124
Yiwen Chen3d3ad022019-01-22 21:15:56 -0800125 /**
Mathieu Chartier743b3632019-02-07 13:14:19 -0800126 * Namespace for all runtime related features.
127 *
128 * @hide
129 */
130 @SystemApi
131 public interface Runtime {
132 String NAMESPACE = "runtime";
133
134 /**
135 * Whether or not we use the precompiled layout.
136 */
137 String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled";
138 }
139
140 /**
Mathieu Chartier7f11eb92019-01-25 16:08:21 -0800141 * Namespace for all runtime native related features.
142 *
143 * @hide
144 */
145 @SystemApi
146 public interface RuntimeNative {
147 String NAMESPACE = "runtime_native";
Mathieu Chartier0bccbf72019-01-30 15:56:17 -0800148
149 /**
150 * Zygote flags. See {@link com.internal.os.Zygote}.
151 */
152
153 /**
154 * If {@code true}, enables the blastula pool feature.
155 *
156 * @hide for internal use only
157 */
158 String BLASTULA_POOL_ENABLED = "blastula_pool_enabled";
159
160 /**
161 * The maximum number of processes to keep in the blastula pool.
162 *
163 * @hide for internal use only
164 */
165 String BLASTULA_POOL_SIZE_MAX = "blastula_pool_size_max";
166
167 /**
168 * The minimum number of processes to keep in the blastula pool.
169 *
170 * @hide for internal use only
171 */
Chris Wailes2c953c82019-02-21 11:09:01 -0800172 String BLASTULA_POOL_SIZE_MIN = "blastula_pool_size_min";
Mathieu Chartier0bccbf72019-01-30 15:56:17 -0800173
174 /**
175 * The threshold used to determine if the pool should be refilled.
176 *
177 * @hide for internal use only
178 */
179 String BLASTULA_POOL_REFILL_THRESHOLD = "blastula_refill_threshold";
Mathieu Chartier7f11eb92019-01-25 16:08:21 -0800180 }
181
182 /**
Mathieu Chartierb31138f2019-02-01 16:51:12 -0800183 * Namespace for all runtime native boot related features. Boot in this case refers to the
184 * fact that the properties only take affect after rebooting the device.
185 *
186 * @hide
187 */
188 @SystemApi
189 public interface RuntimeNativeBoot {
190 String NAMESPACE = "runtime_native_boot";
191 }
192
193 /**
Dongwon Kang5ebb2652019-01-30 15:26:46 -0800194 * Namespace for all media native related features.
195 *
196 * @hide
197 */
198 @SystemApi
199 public interface MediaNative {
Dongwon Kang2ee08912019-02-26 14:16:08 -0800200 /** The flag namespace for media native features. */
Dongwon Kang5ebb2652019-01-30 15:26:46 -0800201 String NAMESPACE = "media_native";
202 }
203
204 /**
Ng Zhi Ane1bff3f2019-01-17 12:38:17 -0800205 * Namespace for all activity manager related features that are used at the native level.
206 * These features are applied at reboot.
207 *
208 * @hide
209 */
210 @SystemApi
211 public interface ActivityManagerNativeBoot {
212 String NAMESPACE = "activity_manager_native_boot";
213 String OFFLOAD_QUEUE_ENABLED = "offload_queue_enabled";
214 }
215
216 /**
Alex Salocbd05e62019-01-23 18:45:22 -0800217 * Namespace for attention-based features provided by on-device machine intelligence.
218 *
219 * @hide
220 */
221 @SystemApi
222 public interface IntelligenceAttention {
223 String NAMESPACE = "intelligence_attention";
Alex Salo440fe3d2019-01-25 11:50:38 -0800224
225 /** If {@code true}, enables the attention features. */
Alex Saloc7b9c082019-01-29 13:10:55 -0800226 String ATTENTION_ENABLED = "attention_enabled";
Alex Salo440fe3d2019-01-25 11:50:38 -0800227
228 /** Settings for the attention features. */
Alex Saloc7b9c082019-01-29 13:10:55 -0800229 String ATTENTION_SETTINGS = "attention_settings";
Alex Salocbd05e62019-01-23 18:45:22 -0800230 }
231
232 /**
Joel Galenson489239b2019-01-23 10:05:03 -0800233 * Privacy related properties definitions.
234 *
235 * @hide
236 */
237 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800238 @TestApi
Joel Galenson489239b2019-01-23 10:05:03 -0800239 public interface Privacy {
240 String NAMESPACE = "privacy";
241
242 /**
243 * Whether to show the Permissions Hub.
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800244 *
245 * @hide
Joel Galenson489239b2019-01-23 10:05:03 -0800246 */
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800247 @SystemApi
Joel Galensond382ad42019-01-29 15:38:39 -0800248 String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800249
250 /**
251 * Whether to show location access check notifications.
252 */
Joel Galensonc4d89212019-01-30 10:02:24 -0800253 String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled";
Michael Groover0dbc35d2019-01-29 17:34:41 -0800254
255 /**
256 * Whether to disable the new device identifier access restrictions.
257 *
258 * @hide
259 */
260 String PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED =
261 "device_identifier_access_restrictions_disabled";
Joel Galenson489239b2019-01-23 10:05:03 -0800262 }
263
264 /**
Yiwen Chen3d3ad022019-01-22 21:15:56 -0800265 * Telephony related properties definitions.
266 *
267 * @hide
268 */
269 @SystemApi
270 public interface Telephony {
271 String NAMESPACE = "telephony";
272 /**
Yiwen Chen3d3ad022019-01-22 21:15:56 -0800273 * Ringer ramping time in milliseconds.
274 */
Yiwen Chen555b83d2019-01-28 14:13:25 -0800275 String RAMPING_RINGER_DURATION = "ramping_ringer_duration";
276 /**
277 * Whether to apply ramping ringer on incoming phone calls.
278 */
279 String RAMPING_RINGER_ENABLED = "ramping_ringer_enabled";
280 /**
281 * Vibration time in milliseconds before ramping ringer starts.
282 */
283 String RAMPING_RINGER_VIBRATION_DURATION = "ramping_ringer_vibration_duration";
Yiwen Chen3d3ad022019-01-22 21:15:56 -0800284 }
285
Victor Hsieh293595f2019-01-10 09:33:21 -0800286 /**
Victor Hsieh51c44032019-01-29 11:57:00 -0800287 * Namespace for how dex runs. The feature may requires reboot to a clean state.
Victor Hsieh293595f2019-01-10 09:33:21 -0800288 *
289 * @hide
290 */
291 @SystemApi
Victor Hsieh51c44032019-01-29 11:57:00 -0800292 public interface DexBoot {
293 String NAMESPACE = "dex_boot";
294 String PRIV_APPS_OOB_ENABLED = "priv_apps_oob_enabled";
295 String PRIV_APPS_OOB_WHITELIST = "priv_apps_oob_whitelist";
Victor Hsieh293595f2019-01-10 09:33:21 -0800296 }
297
Ben Murdochc26a5a82019-01-16 10:05:58 +0000298 /**
299 * Namespace for activity manager related features. These features will be applied
300 * immediately upon change.
301 *
302 * @hide
303 */
304 @SystemApi
305 public interface ActivityManager {
306 String NAMESPACE = "activity_manager";
307
308 /**
309 * App compaction flags. See {@link com.android.server.am.AppCompactor}.
310 */
311 String KEY_USE_COMPACTION = "use_compaction";
312 String KEY_COMPACT_ACTION_1 = "compact_action_1";
313 String KEY_COMPACT_ACTION_2 = "compact_action_2";
314 String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
315 String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
316 String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
317 String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
Ben Murdoch2384df72019-01-28 16:13:55 +0000318 String KEY_COMPACT_STATSD_SAMPLE_RATE = "compact_statsd_sample_rate";
Ben Murdochc26a5a82019-01-16 10:05:58 +0000319
320 /**
321 * Maximum number of cached processes. See
322 * {@link com.android.server.am.ActivityManagerConstants}.
323 */
324 String KEY_MAX_CACHED_PROCESSES = "max_cached_processes";
325 }
326
Jeff Sharkey5d0c55c2019-01-24 14:32:31 -0700327 /**
Alex Salo440fe3d2019-01-25 11:50:38 -0800328 * Namespace for {@link AttentionManagerService} related features.
329 *
330 * @hide
331 */
332 @SystemApi
333 public interface AttentionManagerService {
334 String NAMESPACE = "attention_manager_service";
335
336 /** If {@code true}, enables {@link AttentionManagerService} features. */
Alex Saloc7b9c082019-01-29 13:10:55 -0800337 String SERVICE_ENABLED = "service_enabled";
Alex Salo440fe3d2019-01-25 11:50:38 -0800338
339 /** Allows a CTS to inject a fake implementation. */
Alex Saloc7b9c082019-01-29 13:10:55 -0800340 String COMPONENT_NAME = "component_name";
Alex Salo440fe3d2019-01-25 11:50:38 -0800341 }
342
343 /**
Mohammad Samiul Islam794f03d2019-01-11 15:43:15 +0000344 * Namespace for Rollback.
345 *
346 * @hide
347 */
348 @SystemApi
349 public interface Rollback {
350 String NAMESPACE = "rollback";
351
shafik0ad18b82019-01-24 16:27:24 +0000352 String BOOT_NAMESPACE = "rollback_boot";
353
Mohammad Samiul Islam794f03d2019-01-11 15:43:15 +0000354 /**
355 * Timeout in milliseconds for enabling package rollback.
356 */
357 String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout";
shafik0ad18b82019-01-24 16:27:24 +0000358
359 /**
360 * The lifetime duration of rollback packages in millis
361 */
362 String ROLLBACK_LIFETIME_IN_MILLIS = "rollback_lifetime_in_millis";
Mohammad Samiul Islam794f03d2019-01-11 15:43:15 +0000363 }
364
365 /**
Jeff Sharkey5d0c55c2019-01-24 14:32:31 -0700366 * Namespace for storage-related features.
367 *
368 * @hide
369 */
370 @SystemApi
371 public interface Storage {
372 String NAMESPACE = "storage";
373
374 /**
375 * If {@code 1}, enables the isolated storage feature. If {@code -1},
376 * disables the isolated storage feature. If {@code 0}, uses the default
377 * value from the build system.
378 */
379 String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled";
380 }
381
Hongyi Zhang29301d32019-01-18 17:07:38 -0800382 /**
383 * Namespace for system scheduler related features. These features will be applied
384 * immediately upon change.
385 *
386 * @hide
387 */
388 @SystemApi
389 public interface Scheduler {
390 String NAMESPACE = "scheduler";
391
392 /**
393 * Flag for enabling fast metrics collection in system scheduler.
394 * A flag value of '' or '0' means the fast metrics collection is not
395 * enabled. Otherwise fast metrics collection is enabled and flag value
396 * is the order id.
397 */
398 String ENABLE_FAST_METRICS_COLLECTION = "enable_fast_metrics_collection";
399 }
400
Matt Pape1278d1c2018-12-11 13:03:49 -0800401 private static final Object sLock = new Object();
402 @GuardedBy("sLock")
403 private static Map<OnPropertyChangedListener, Pair<String, Executor>> sListeners =
404 new HashMap<>();
405 @GuardedBy("sLock")
406 private static Map<String, Pair<ContentObserver, Integer>> sNamespaces = new HashMap<>();
407
408 // Should never be invoked
409 private DeviceConfig() {
410 }
411
412 /**
413 * Look up the value of a property for a particular namespace.
414 *
415 * @param namespace The namespace containing the property to look up.
Alex Salocbd05e62019-01-23 18:45:22 -0800416 * @param name The name of the property to look up.
Matt Pape1278d1c2018-12-11 13:03:49 -0800417 * @return the corresponding value, or null if not present.
Matt Pape1278d1c2018-12-11 13:03:49 -0800418 * @hide
419 */
420 @SystemApi
Felipe Lemed03283d2019-02-05 09:42:42 -0800421 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000422 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Pape1278d1c2018-12-11 13:03:49 -0800423 public static String getProperty(String namespace, String name) {
424 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
425 String compositeName = createCompositeName(namespace, name);
426 return Settings.Config.getString(contentResolver, compositeName);
427 }
428
429 /**
430 * Create a new property with the the provided name and value in the provided namespace, or
431 * update the value of such a property if it already exists. The same name can exist in multiple
432 * namespaces and might have different values in any or all namespaces.
433 * <p>
434 * The method takes an argument indicating whether to make the value the default for this
435 * property.
436 * <p>
437 * All properties stored for a particular scope can be reverted to their default values
438 * by passing the namespace to {@link #resetToDefaults(int, String)}.
439 *
Alex Salocbd05e62019-01-23 18:45:22 -0800440 * @param namespace The namespace containing the property to create or update.
441 * @param name The name of the property to create or update.
442 * @param value The value to store for the property.
Matt Pape1278d1c2018-12-11 13:03:49 -0800443 * @param makeDefault Whether to make the new value the default one.
444 * @return True if the value was set, false if the storage implementation throws errors.
Matt Pape1278d1c2018-12-11 13:03:49 -0800445 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800446 * @see #resetToDefaults(int, String).
Matt Pape1278d1c2018-12-11 13:03:49 -0800447 */
448 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800449 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000450 @RequiresPermission(WRITE_DEVICE_CONFIG)
Matt Pape1278d1c2018-12-11 13:03:49 -0800451 public static boolean setProperty(
452 String namespace, String name, String value, boolean makeDefault) {
453 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
454 String compositeName = createCompositeName(namespace, name);
455 return Settings.Config.putString(contentResolver, compositeName, value, makeDefault);
456 }
457
458 /**
459 * Reset properties to their default values.
460 * <p>
461 * The method accepts an optional namespace parameter. If provided, only properties set within
462 * that namespace will be reset. Otherwise, all properties will be reset.
463 *
464 * @param resetMode The reset mode to use.
465 * @param namespace Optionally, the specific namespace which resets will be limited to.
Matt Pape1278d1c2018-12-11 13:03:49 -0800466 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800467 * @see #setProperty(String, String, String, boolean)
Matt Pape1278d1c2018-12-11 13:03:49 -0800468 */
469 @SystemApi
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800470 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000471 @RequiresPermission(WRITE_DEVICE_CONFIG)
Matt Pape1278d1c2018-12-11 13:03:49 -0800472 public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) {
473 ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
474 Settings.Config.resetToDefaults(contentResolver, resetMode, namespace);
475 }
476
477 /**
478 * Add a listener for property changes.
479 * <p>
480 * This listener will be called whenever properties in the specified namespace change. Callbacks
481 * will be made on the specified executor. Future calls to this method with the same listener
482 * will replace the old namespace and executor. Remove the listener entirely by calling
483 * {@link #removeOnPropertyChangedListener(OnPropertyChangedListener)}.
484 *
Alex Salocbd05e62019-01-23 18:45:22 -0800485 * @param namespace The namespace containing properties to monitor.
486 * @param executor The executor which will be used to run callbacks.
Matt Pape1278d1c2018-12-11 13:03:49 -0800487 * @param onPropertyChangedListener The listener to add.
Matt Pape1278d1c2018-12-11 13:03:49 -0800488 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800489 * @see #removeOnPropertyChangedListener(OnPropertyChangedListener)
Matt Pape1278d1c2018-12-11 13:03:49 -0800490 */
491 @SystemApi
Felipe Lemecc510272019-02-07 14:56:27 -0800492 @TestApi
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000493 @RequiresPermission(READ_DEVICE_CONFIG)
Matt Pape1278d1c2018-12-11 13:03:49 -0800494 public static void addOnPropertyChangedListener(
495 @NonNull String namespace,
496 @NonNull @CallbackExecutor Executor executor,
497 @NonNull OnPropertyChangedListener onPropertyChangedListener) {
Stanislav Zholnin596437f2018-12-28 15:34:23 +0000498 // TODO enforce READ_DEVICE_CONFIG permission
Matt Pape1278d1c2018-12-11 13:03:49 -0800499 synchronized (sLock) {
500 Pair<String, Executor> oldNamespace = sListeners.get(onPropertyChangedListener);
501 if (oldNamespace == null) {
502 // Brand new listener, add it to the list.
503 sListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
504 incrementNamespace(namespace);
505 } else if (namespace.equals(oldNamespace.first)) {
506 // Listener is already registered for this namespace, update executor just in case.
507 sListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
508 } else {
509 // Update this listener from an old namespace to the new one.
510 decrementNamespace(sListeners.get(onPropertyChangedListener).first);
511 sListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
512 incrementNamespace(namespace);
513 }
514 }
515 }
516
517 /**
518 * Remove a listener for property changes. The listener will receive no further notification of
519 * property changes.
520 *
521 * @param onPropertyChangedListener The listener to remove.
Matt Pape1278d1c2018-12-11 13:03:49 -0800522 * @hide
Alex Salocbd05e62019-01-23 18:45:22 -0800523 * @see #addOnPropertyChangedListener(String, Executor, OnPropertyChangedListener)
Matt Pape1278d1c2018-12-11 13:03:49 -0800524 */
525 @SystemApi
Felipe Lemecc510272019-02-07 14:56:27 -0800526 @TestApi
Matt Pape1278d1c2018-12-11 13:03:49 -0800527 public static void removeOnPropertyChangedListener(
528 OnPropertyChangedListener onPropertyChangedListener) {
529 synchronized (sLock) {
530 if (sListeners.containsKey(onPropertyChangedListener)) {
531 decrementNamespace(sListeners.get(onPropertyChangedListener).first);
532 sListeners.remove(onPropertyChangedListener);
533 }
534 }
535 }
536
537 private static String createCompositeName(String namespace, String name) {
538 return namespace + "/" + name;
539 }
540
541 private static Uri createNamespaceUri(String namespace) {
542 return CONTENT_URI.buildUpon().appendPath(namespace).build();
543 }
544
545 /**
546 * Increment the count used to represent the number of listeners subscribed to the given
547 * namespace. If this is the first (i.e. incrementing from 0 to 1) for the given namespace, a
548 * ContentObserver is registered.
549 *
550 * @param namespace The namespace to increment the count for.
551 */
552 @GuardedBy("sLock")
553 private static void incrementNamespace(String namespace) {
554 Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
555 if (namespaceCount != null) {
556 sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second + 1));
557 } else {
558 // This is a new namespace, register a ContentObserver for it.
559 ContentObserver contentObserver = new ContentObserver(null) {
560 @Override
561 public void onChange(boolean selfChange, Uri uri) {
562 handleChange(uri);
563 }
564 };
565 ActivityThread.currentApplication().getContentResolver()
566 .registerContentObserver(createNamespaceUri(namespace), true, contentObserver);
567 sNamespaces.put(namespace, new Pair<>(contentObserver, 1));
568 }
569 }
570
571 /**
572 * Decrement the count used to represent th enumber of listeners subscribed to the given
573 * namespace. If this is the final decrement call (i.e. decrementing from 1 to 0) for the given
574 * namespace, the ContentObserver that had been tracking it will be removed.
575 *
576 * @param namespace The namespace to decrement the count for.
577 */
578 @GuardedBy("sLock")
579 private static void decrementNamespace(String namespace) {
580 Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
581 if (namespaceCount == null) {
582 // This namespace is not registered and does not need to be decremented
583 return;
584 } else if (namespaceCount.second > 1) {
585 sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second - 1));
586 } else {
587 // Decrementing a namespace to zero means we no longer need its ContentObserver.
588 ActivityThread.currentApplication().getContentResolver()
589 .unregisterContentObserver(namespaceCount.first);
590 sNamespaces.remove(namespace);
591 }
592 }
593
594 private static void handleChange(Uri uri) {
595 List<String> pathSegments = uri.getPathSegments();
596 // pathSegments(0) is "config"
597 String namespace = pathSegments.get(1);
598 String name = pathSegments.get(2);
599 String value = getProperty(namespace, name);
600 synchronized (sLock) {
601 for (OnPropertyChangedListener listener : sListeners.keySet()) {
602 if (namespace.equals(sListeners.get(listener).first)) {
603 sListeners.get(listener).second.execute(new Runnable() {
604 @Override
605 public void run() {
606 listener.onPropertyChanged(namespace, name, value);
607 }
608
609 });
610 }
611 }
612 }
613 }
614
615 /**
616 * Interface for monitoring to properties.
617 * <p>
618 * Override {@link #onPropertyChanged(String, String, String)} to handle callbacks for changes.
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800619 *
620 * @hide
Matt Pape1278d1c2018-12-11 13:03:49 -0800621 */
Philip P. Moltmann25995fe2019-01-25 14:33:11 -0800622 @SystemApi
Felipe Lemecc510272019-02-07 14:56:27 -0800623 @TestApi
Matt Pape1278d1c2018-12-11 13:03:49 -0800624 public interface OnPropertyChangedListener {
625 /**
626 * Called when a property has changed.
627 *
628 * @param namespace The namespace containing the property which has changed.
Alex Salocbd05e62019-01-23 18:45:22 -0800629 * @param name The name of the property which has changed.
630 * @param value The new value of the property which has changed.
Matt Pape1278d1c2018-12-11 13:03:49 -0800631 */
632 void onPropertyChanged(String namespace, String name, String value);
633 }
634}