1 /*
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 
17 package android.provider;
18 
19 import static android.Manifest.permission.READ_DEVICE_CONFIG;
20 import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
21 
22 import android.annotation.CallbackExecutor;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SystemApi;
27 import android.annotation.TestApi;
28 import android.app.ActivityThread;
29 import android.content.ContentResolver;
30 import android.content.Context;
31 import android.content.pm.PackageManager;
32 import android.database.ContentObserver;
33 import android.net.Uri;
34 import android.provider.Settings.ResetMode;
35 import android.util.ArrayMap;
36 import android.util.Log;
37 import android.util.Pair;
38 
39 import com.android.internal.annotations.GuardedBy;
40 import com.android.internal.util.Preconditions;
41 
42 import java.util.Arrays;
43 import java.util.Collections;
44 import java.util.HashMap;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Set;
48 import java.util.concurrent.Executor;
49 
50 /**
51  * Device level configuration parameters which can be tuned by a separate configuration service.
52  * Namespaces that end in "_native" such as {@link #NAMESPACE_NETD_NATIVE} are intended to be used
53  * by native code and should be pushed to system properties to make them accessible.
54  *
55  * @hide
56  */
57 @SystemApi
58 @TestApi
59 public final class DeviceConfig {
60     /**
61      * The content:// style URL for the config table.
62      *
63      * @hide
64      */
65     public static final Uri CONTENT_URI = Uri.parse("content://" + Settings.AUTHORITY + "/config");
66 
67     /**
68      * Namespace for activity manager related features. These features will be applied
69      * immediately upon change.
70      *
71      * @hide
72      */
73     @SystemApi
74     public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
75 
76     /**
77      * Namespace for all activity manager related features that are used at the native level.
78      * These features are applied at reboot.
79      *
80      * @hide
81      */
82     @SystemApi
83     public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT =
84             "activity_manager_native_boot";
85 
86     /**
87      * Namespace for all app compat related features.  These features will be applied
88      * immediately upon change.
89      *
90      * @hide
91      */
92     @SystemApi
93     public static final String NAMESPACE_APP_COMPAT = "app_compat";
94 
95     /**
96      * Namespace for AttentionManagerService related features.
97      *
98      * @hide
99      */
100     @SystemApi
101     public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service";
102 
103     /**
104      * Namespace for autofill feature that provides suggestions across all apps when
105      * the user interacts with input fields.
106      *
107      * @hide
108      */
109     @SystemApi
110     @TestApi
111     public static final String NAMESPACE_AUTOFILL = "autofill";
112 
113     /**
114      * Namespace for blobstore feature that allows apps to share data blobs.
115      *
116      * @hide
117      */
118     @SystemApi
119     public static final String NAMESPACE_BLOBSTORE = "blobstore";
120 
121     /**
122      * Namespace for all networking connectivity related features.
123      *
124      * @hide
125      */
126     @SystemApi
127     public static final String NAMESPACE_CONNECTIVITY = "connectivity";
128 
129     /**
130      * Namespace for content capture feature used by on-device machine intelligence
131      * to provide suggestions in a privacy-safe manner.
132      *
133      * @hide
134      */
135     @SystemApi
136     @TestApi
137     public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
138 
139     /**
140      * Namespace for how dex runs. The feature requires a reboot to reach a clean state.
141      *
142      * @deprecated No longer used
143      * @hide
144      */
145     @Deprecated
146     @SystemApi
147     public static final String NAMESPACE_DEX_BOOT = "dex_boot";
148 
149     /**
150      * Namespace for display manager related features. The names to access the properties in this
151      * namespace should be defined in {@link android.hardware.display.DisplayManager}.
152      *
153      * @hide
154      */
155     @SystemApi
156     public static final String NAMESPACE_DISPLAY_MANAGER = "display_manager";
157 
158     /**
159      * Namespace for all Game Driver features.
160      *
161      * @hide
162      */
163     @SystemApi
164     public static final String NAMESPACE_GAME_DRIVER = "game_driver";
165 
166     /**
167      * Namespace for all input-related features that are used at the native level.
168      * These features are applied at reboot.
169      *
170      * @hide
171      */
172     @SystemApi
173     public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
174 
175     /**
176      * Namespace for attention-based features provided by on-device machine intelligence.
177      *
178      * @hide
179      */
180     @SystemApi
181     public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention";
182 
183     /**
184      * Definitions for properties related to Content Suggestions.
185      *
186      * @hide
187      */
188     public static final String NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS =
189             "intelligence_content_suggestions";
190 
191     /**
192      * Namespace for all media native related features.
193      *
194      * @hide
195      */
196     @SystemApi
197     public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
198 
199     /**
200      * Namespace for all netd related features.
201      *
202      * @hide
203      */
204     @SystemApi
205     public static final String NAMESPACE_NETD_NATIVE = "netd_native";
206 
207     /**
208      * Namespace for features related to the Package Manager Service.
209      *
210      * @hide
211      */
212     @SystemApi
213     public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
214 
215     /**
216      * Namespace for Rollback flags that are applied immediately.
217      *
218      * @hide
219      */
220     @SystemApi @TestApi
221     public static final String NAMESPACE_ROLLBACK = "rollback";
222 
223     /**
224      * Namespace for Rollback flags that are applied after a reboot.
225      *
226      * @hide
227      */
228     @SystemApi @TestApi
229     public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
230 
231     /**
232      * Namespace for all runtime related features that don't require a reboot to become active.
233      * There are no feature flags using NAMESPACE_RUNTIME.
234      *
235      * @hide
236      */
237     @SystemApi
238     public static final String NAMESPACE_RUNTIME = "runtime";
239 
240     /**
241      * Namespace for all runtime related features that require system properties for accessing
242      * the feature flags from C++ or Java language code. One example is the app image startup
243      * cache feature use_app_image_startup_cache.
244      *
245      * @hide
246      */
247     @SystemApi
248     public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
249 
250     /**
251      * Namespace for all runtime native boot related features. Boot in this case refers to the
252      * fact that the properties only take affect after rebooting the device.
253      *
254      * @hide
255      */
256     @SystemApi
257     public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
258 
259     /**
260      * Namespace for system scheduler related features. These features will be applied
261      * immediately upon change.
262      *
263      * @hide
264      */
265     @SystemApi
266     public static final String NAMESPACE_SCHEDULER = "scheduler";
267 
268     /**
269      * Namespace for settings statistics features.
270      *
271      * @hide
272      */
273     public static final String NAMESPACE_SETTINGS_STATS = "settings_stats";
274 
275     /**
276      * Namespace for storage-related features.
277      *
278      * @deprecated Replace storage namespace with storage_native_boot.
279      * @hide
280      */
281     @Deprecated
282     @SystemApi
283     public static final String NAMESPACE_STORAGE = "storage";
284 
285     /**
286      * Namespace for storage-related features, including native and boot.
287      *
288      * @hide
289      */
290     @SystemApi
291     public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
292 
293     /**
294      * Namespace for System UI related features.
295      *
296      * @hide
297      */
298     @SystemApi
299     public static final String NAMESPACE_SYSTEMUI = "systemui";
300 
301     /**
302      * Telephony related properties.
303      *
304      * @hide
305      */
306     @SystemApi
307     public static final String NAMESPACE_TELEPHONY = "telephony";
308 
309     /**
310      * Namespace for TextClassifier related features.
311      *
312      * @hide
313      * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
314      */
315     @SystemApi
316     public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
317 
318     /**
319      * Namespace for contacts provider related features.
320      *
321      * @hide
322      */
323     public static final String NAMESPACE_CONTACTS_PROVIDER = "contacts_provider";
324 
325     /**
326      * Namespace for settings ui related features
327      *
328      * @hide
329      */
330     public static final String NAMESPACE_SETTINGS_UI = "settings_ui";
331 
332     /**
333      * Namespace for android related features, i.e. for flags that affect not just a single
334      * component, but the entire system.
335      *
336      * The keys for this namespace are defined in {@link AndroidDeviceConfig}.
337      *
338      * @hide
339      */
340     @TestApi
341     public static final String NAMESPACE_ANDROID = "android";
342 
343     /**
344      * Namespace for window manager related features.
345      *
346      * @hide
347      */
348     public static final String NAMESPACE_WINDOW_MANAGER = "window_manager";
349 
350     /**
351      * Namespace for window manager features accessible by native code and
352      * loaded once per boot.
353      *
354      * @hide
355      */
356     @SystemApi
357     public static final String NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT = "window_manager_native_boot";
358 
359     /**
360      * List of namespaces which can be read without READ_DEVICE_CONFIG permission
361      *
362      * @hide
363      */
364     @NonNull
365     private static final List<String> PUBLIC_NAMESPACES =
366             Arrays.asList(NAMESPACE_TEXTCLASSIFIER, NAMESPACE_RUNTIME);
367     /**
368      * Privacy related properties definitions.
369      *
370      * @hide
371      */
372     @SystemApi
373     @TestApi
374     public static final String NAMESPACE_PRIVACY = "privacy";
375 
376     /**
377      * Namespace for biometrics related features
378      *
379      * @hide
380      */
381     @SystemApi
382     @TestApi
383     public static final String NAMESPACE_BIOMETRICS = "biometrics";
384 
385     /**
386      * Permission related properties definitions.
387      *
388      * @hide
389      */
390     @SystemApi
391     @TestApi
392     public static final String NAMESPACE_PERMISSIONS = "permissions";
393 
394     /**
395      * Namespace for all widget related features.
396      *
397      * @hide
398      */
399     public static final String NAMESPACE_WIDGET = "widget";
400 
401     /**
402      * Namespace for connectivity thermal power manager features.
403      *
404      * @hide
405      */
406     public static final String NAMESPACE_CONNECTIVITY_THERMAL_POWER_MANAGER =
407             "connectivity_thermal_power_manager";
408 
409     /**
410      * Namespace for configuration related features.
411      *
412      * @hide
413      */
414     public static final String NAMESPACE_CONFIGURATION = "configuration";
415 
416     private static final Object sLock = new Object();
417     @GuardedBy("sLock")
418     private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
419             new ArrayMap<>();
420     @GuardedBy("sLock")
421     private static Map<String, Pair<ContentObserver, Integer>> sNamespaces = new HashMap<>();
422     private static final String TAG = "DeviceConfig";
423 
424     // Should never be invoked
DeviceConfig()425     private DeviceConfig() {
426     }
427 
428     /**
429      * Look up the value of a property for a particular namespace.
430      *
431      * @param namespace The namespace containing the property to look up.
432      * @param name      The name of the property to look up.
433      * @return the corresponding value, or null if not present.
434      * @hide
435      */
436     @SystemApi
437     @TestApi
438     @RequiresPermission(READ_DEVICE_CONFIG)
getProperty(@onNull String namespace, @NonNull String name)439     public static String getProperty(@NonNull String namespace, @NonNull String name) {
440         // Fetch all properties for the namespace at once and cache them in the local process, so we
441         // incur the cost of the IPC less often. Lookups happen much more frequently than updates,
442         // and we want to optimize the former.
443         return getProperties(namespace, name).getString(name, null);
444     }
445 
446     /**
447      * Look up the values of multiple properties for a particular namespace. The lookup is atomic,
448      * such that the values of these properties cannot change between the time when the first is
449      * fetched and the time when the last is fetched.
450      * <p>
451      * Each call to {@link #setProperties(Properties)} is also atomic and ensures that either none
452      * or all of the change is picked up here, but never only part of it.
453      *
454      * @param namespace The namespace containing the properties to look up.
455      * @param names     The names of properties to look up, or empty to fetch all properties for the
456      *                  given namespace.
457      * @return {@link Properties} object containing the requested properties. This reflects the
458      *     state of these properties at the time of the lookup, and is not updated to reflect any
459      *     future changes. The keyset of this Properties object will contain only the intersection
460      *     of properties already set and properties requested via the names parameter. Properties
461      *     that are already set but were not requested will not be contained here. Properties that
462      *     are not set, but were requested will not be contained here either.
463      * @hide
464      */
465     @SystemApi
466     @TestApi
467     @NonNull
468     @RequiresPermission(READ_DEVICE_CONFIG)
getProperties(@onNull String namespace, @NonNull String ... names)469     public static Properties getProperties(@NonNull String namespace, @NonNull String ... names) {
470         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
471         return new Properties(namespace,
472                 Settings.Config.getStrings(contentResolver, namespace, Arrays.asList(names)));
473     }
474 
475     /**
476      * Look up the String value of a property for a particular namespace.
477      *
478      * @param namespace    The namespace containing the property to look up.
479      * @param name         The name of the property to look up.
480      * @param defaultValue The value to return if the property does not exist or has no non-null
481      *                     value.
482      * @return the corresponding value, or defaultValue if none exists.
483      * @hide
484      */
485     @SystemApi
486     @TestApi
487     @RequiresPermission(READ_DEVICE_CONFIG)
getString(@onNull String namespace, @NonNull String name, @Nullable String defaultValue)488     public static String getString(@NonNull String namespace, @NonNull String name,
489             @Nullable String defaultValue) {
490         String value = getProperty(namespace, name);
491         return value != null ? value : defaultValue;
492     }
493 
494     /**
495      * Look up the boolean value of a property for a particular namespace.
496      *
497      * @param namespace The namespace containing the property to look up.
498      * @param name      The name of the property to look up.
499      * @param defaultValue The value to return if the property does not exist or has no non-null
500      *                     value.
501      * @return the corresponding value, or defaultValue if none exists.
502      * @hide
503      */
504     @SystemApi
505     @TestApi
506     @RequiresPermission(READ_DEVICE_CONFIG)
getBoolean(@onNull String namespace, @NonNull String name, boolean defaultValue)507     public static boolean getBoolean(@NonNull String namespace, @NonNull String name,
508             boolean defaultValue) {
509         String value = getProperty(namespace, name);
510         return value != null ? Boolean.parseBoolean(value) : defaultValue;
511     }
512 
513     /**
514      * Look up the int value of a property for a particular namespace.
515      *
516      * @param namespace The namespace containing the property to look up.
517      * @param name      The name of the property to look up.
518      * @param defaultValue The value to return if the property does not exist, has no non-null
519      *                     value, or fails to parse into an int.
520      * @return the corresponding value, or defaultValue if either none exists or it does not parse.
521      * @hide
522      */
523     @SystemApi
524     @TestApi
525     @RequiresPermission(READ_DEVICE_CONFIG)
getInt(@onNull String namespace, @NonNull String name, int defaultValue)526     public static int getInt(@NonNull String namespace, @NonNull String name, int defaultValue) {
527         String value = getProperty(namespace, name);
528         if (value == null) {
529             return defaultValue;
530         }
531         try {
532             return Integer.parseInt(value);
533         } catch (NumberFormatException e) {
534             Log.e(TAG, "Parsing integer failed for " + namespace + ":" + name);
535             return defaultValue;
536         }
537     }
538 
539     /**
540      * Look up the long value of a property for a particular namespace.
541      *
542      * @param namespace The namespace containing the property to look up.
543      * @param name      The name of the property to look up.
544      * @param defaultValue The value to return if the property does not exist, has no non-null
545      *                     value, or fails to parse into a long.
546      * @return the corresponding value, or defaultValue if either none exists or it does not parse.
547      * @hide
548      */
549     @SystemApi
550     @TestApi
551     @RequiresPermission(READ_DEVICE_CONFIG)
getLong(@onNull String namespace, @NonNull String name, long defaultValue)552     public static long getLong(@NonNull String namespace, @NonNull String name, long defaultValue) {
553         String value = getProperty(namespace, name);
554         if (value == null) {
555             return defaultValue;
556         }
557         try {
558             return Long.parseLong(value);
559         } catch (NumberFormatException e) {
560             Log.e(TAG, "Parsing long failed for " + namespace + ":" + name);
561             return defaultValue;
562         }
563     }
564 
565     /**
566      * Look up the float value of a property for a particular namespace.
567      *
568      * @param namespace The namespace containing the property to look up.
569      * @param name      The name of the property to look up.
570      * @param defaultValue The value to return if the property does not exist, has no non-null
571      *                     value, or fails to parse into a float.
572      * @return the corresponding value, or defaultValue if either none exists or it does not parse.
573      * @hide
574      */
575     @SystemApi
576     @TestApi
577     @RequiresPermission(READ_DEVICE_CONFIG)
getFloat(@onNull String namespace, @NonNull String name, float defaultValue)578     public static float getFloat(@NonNull String namespace, @NonNull String name,
579             float defaultValue) {
580         String value = getProperty(namespace, name);
581         if (value == null) {
582             return defaultValue;
583         }
584         try {
585             return Float.parseFloat(value);
586         } catch (NumberFormatException e) {
587             Log.e(TAG, "Parsing float failed for " + namespace + ":" + name);
588             return defaultValue;
589         }
590     }
591 
592     /**
593      * Create a new property with the the provided name and value in the provided namespace, or
594      * update the value of such a property if it already exists. The same name can exist in multiple
595      * namespaces and might have different values in any or all namespaces.
596      * <p>
597      * The method takes an argument indicating whether to make the value the default for this
598      * property.
599      * <p>
600      * All properties stored for a particular scope can be reverted to their default values
601      * by passing the namespace to {@link #resetToDefaults(int, String)}.
602      *
603      * @param namespace   The namespace containing the property to create or update.
604      * @param name        The name of the property to create or update.
605      * @param value       The value to store for the property.
606      * @param makeDefault Whether to make the new value the default one.
607      * @return True if the value was set, false if the storage implementation throws errors.
608      * @hide
609      * @see #resetToDefaults(int, String).
610      */
611     @SystemApi
612     @TestApi
613     @RequiresPermission(WRITE_DEVICE_CONFIG)
setProperty(@onNull String namespace, @NonNull String name, @Nullable String value, boolean makeDefault)614     public static boolean setProperty(@NonNull String namespace, @NonNull String name,
615             @Nullable String value, boolean makeDefault) {
616         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
617         return Settings.Config.putString(contentResolver, namespace, name, value, makeDefault);
618     }
619 
620     /**
621      * Set all of the properties for a specific namespace. Pre-existing properties will be updated
622      * and new properties will be added if necessary. Any pre-existing properties for the specific
623      * namespace which are not part of the provided {@link Properties} object will be deleted from
624      * the namespace. These changes are all applied atomically, such that no calls to read or reset
625      * these properties can happen in the middle of this update.
626      * <p>
627      * Each call to {@link #getProperties(String, String...)} is also atomic and ensures that either
628      * none or all of this update is picked up, but never only part of it.
629      *
630      * @param properties the complete set of properties to set for a specific namespace.
631      * @throws BadConfigException if the provided properties are banned by RescueParty.
632      * @return True if the values were set, false otherwise.
633      * @hide
634      */
635     @SystemApi
636     @TestApi
637     @RequiresPermission(WRITE_DEVICE_CONFIG)
setProperties(@onNull Properties properties)638     public static boolean setProperties(@NonNull Properties properties) throws BadConfigException {
639         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
640         return Settings.Config.setStrings(contentResolver, properties.getNamespace(),
641                 properties.mMap);
642     }
643 
644     /**
645      * Reset properties to their default values.
646      * <p>
647      * The method accepts an optional namespace parameter. If provided, only properties set within
648      * that namespace will be reset. Otherwise, all properties will be reset.
649      *
650      * @param resetMode The reset mode to use.
651      * @param namespace Optionally, the specific namespace which resets will be limited to.
652      * @hide
653      * @see #setProperty(String, String, String, boolean)
654      */
655     @SystemApi
656     @TestApi
657     @RequiresPermission(WRITE_DEVICE_CONFIG)
resetToDefaults(@esetMode int resetMode, @Nullable String namespace)658     public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) {
659         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
660         Settings.Config.resetToDefaults(contentResolver, resetMode, namespace);
661     }
662 
663     /**
664      * Add a listener for property changes.
665      * <p>
666      * This listener will be called whenever properties in the specified namespace change. Callbacks
667      * will be made on the specified executor. Future calls to this method with the same listener
668      * will replace the old namespace and executor. Remove the listener entirely by calling
669      * {@link #removeOnPropertiesChangedListener(OnPropertiesChangedListener)}.
670      *
671      * @param namespace                   The namespace containing properties to monitor.
672      * @param executor                    The executor which will be used to run callbacks.
673      * @param onPropertiesChangedListener The listener to add.
674      * @hide
675      * @see #removeOnPropertiesChangedListener(OnPropertiesChangedListener)
676      */
677     @SystemApi
678     @TestApi
679     @RequiresPermission(READ_DEVICE_CONFIG)
addOnPropertiesChangedListener( @onNull String namespace, @NonNull @CallbackExecutor Executor executor, @NonNull OnPropertiesChangedListener onPropertiesChangedListener)680     public static void addOnPropertiesChangedListener(
681             @NonNull String namespace,
682             @NonNull @CallbackExecutor Executor executor,
683             @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
684         enforceReadPermission(ActivityThread.currentApplication().getApplicationContext(),
685                 namespace);
686         synchronized (sLock) {
687             Pair<String, Executor> oldNamespace = sListeners.get(onPropertiesChangedListener);
688             if (oldNamespace == null) {
689                 // Brand new listener, add it to the list.
690                 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
691                 incrementNamespace(namespace);
692             } else if (namespace.equals(oldNamespace.first)) {
693                 // Listener is already registered for this namespace, update executor just in case.
694                 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
695             } else {
696                 // Update this listener from an old namespace to the new one.
697                 decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
698                 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
699                 incrementNamespace(namespace);
700             }
701         }
702     }
703 
704     /**
705      * Remove a listener for property changes. The listener will receive no further notification of
706      * property changes.
707      *
708      * @param onPropertiesChangedListener The listener to remove.
709      * @hide
710      * @see #addOnPropertiesChangedListener(String, Executor, OnPropertiesChangedListener)
711      */
712     @SystemApi
713     @TestApi
removeOnPropertiesChangedListener( @onNull OnPropertiesChangedListener onPropertiesChangedListener)714     public static void removeOnPropertiesChangedListener(
715             @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
716         Preconditions.checkNotNull(onPropertiesChangedListener);
717         synchronized (sLock) {
718             if (sListeners.containsKey(onPropertiesChangedListener)) {
719                 decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
720                 sListeners.remove(onPropertiesChangedListener);
721             }
722         }
723     }
724 
createNamespaceUri(@onNull String namespace)725     private static Uri createNamespaceUri(@NonNull String namespace) {
726         Preconditions.checkNotNull(namespace);
727         return CONTENT_URI.buildUpon().appendPath(namespace).build();
728     }
729 
730     /**
731      * Increment the count used to represent the number of listeners subscribed to the given
732      * namespace. If this is the first (i.e. incrementing from 0 to 1) for the given namespace, a
733      * ContentObserver is registered.
734      *
735      * @param namespace The namespace to increment the count for.
736      */
737     @GuardedBy("sLock")
incrementNamespace(@onNull String namespace)738     private static void incrementNamespace(@NonNull String namespace) {
739         Preconditions.checkNotNull(namespace);
740         Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
741         if (namespaceCount != null) {
742             sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second + 1));
743         } else {
744             // This is a new namespace, register a ContentObserver for it.
745             ContentObserver contentObserver = new ContentObserver(null) {
746                 @Override
747                 public void onChange(boolean selfChange, Uri uri) {
748                     if (uri != null) {
749                         handleChange(uri);
750                     }
751                 }
752             };
753             ActivityThread.currentApplication().getContentResolver()
754                     .registerContentObserver(createNamespaceUri(namespace), true, contentObserver);
755             sNamespaces.put(namespace, new Pair<>(contentObserver, 1));
756         }
757     }
758 
759     /**
760      * Decrement the count used to represent the number of listeners subscribed to the given
761      * namespace. If this is the final decrement call (i.e. decrementing from 1 to 0) for the given
762      * namespace, the ContentObserver that had been tracking it will be removed.
763      *
764      * @param namespace The namespace to decrement the count for.
765      */
766     @GuardedBy("sLock")
decrementNamespace(@onNull String namespace)767     private static void decrementNamespace(@NonNull String namespace) {
768         Preconditions.checkNotNull(namespace);
769         Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
770         if (namespaceCount == null) {
771             // This namespace is not registered and does not need to be decremented
772             return;
773         } else if (namespaceCount.second > 1) {
774             sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second - 1));
775         } else {
776             // Decrementing a namespace to zero means we no longer need its ContentObserver.
777             ActivityThread.currentApplication().getContentResolver()
778                     .unregisterContentObserver(namespaceCount.first);
779             sNamespaces.remove(namespace);
780         }
781     }
782 
handleChange(@onNull Uri uri)783     private static void handleChange(@NonNull Uri uri) {
784         Preconditions.checkNotNull(uri);
785         List<String> pathSegments = uri.getPathSegments();
786         // pathSegments(0) is "config"
787         final String namespace = pathSegments.get(1);
788         Properties.Builder propBuilder = new Properties.Builder(namespace);
789         try {
790             Properties allProperties = getProperties(namespace);
791             for (int i = 2; i < pathSegments.size(); ++i) {
792                 String key = pathSegments.get(i);
793                 propBuilder.setString(key, allProperties.getString(key, null));
794             }
795         } catch (SecurityException e) {
796             // Silently failing to not crash binder or listener threads.
797             Log.e(TAG, "OnPropertyChangedListener update failed: permission violation.");
798             return;
799         }
800         Properties properties = propBuilder.build();
801 
802         synchronized (sLock) {
803             for (int i = 0; i < sListeners.size(); i++) {
804                 if (namespace.equals(sListeners.valueAt(i).first)) {
805                     final OnPropertiesChangedListener listener = sListeners.keyAt(i);
806                     sListeners.valueAt(i).second.execute(() -> {
807                         listener.onPropertiesChanged(properties);
808                     });
809                 }
810             }
811         }
812     }
813 
814     /**
815      * Enforces READ_DEVICE_CONFIG permission if namespace is not one of public namespaces.
816      * @hide
817      */
enforceReadPermission(Context context, String namespace)818     public static void enforceReadPermission(Context context, String namespace) {
819         if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
820                 != PackageManager.PERMISSION_GRANTED) {
821             if (!PUBLIC_NAMESPACES.contains(namespace)) {
822                 throw new SecurityException("Permission denial: reading from settings requires:"
823                         + READ_DEVICE_CONFIG);
824             }
825         }
826     }
827 
828     /**
829      * Returns list of namespaces that can be read without READ_DEVICE_CONFIG_PERMISSION;
830      * @hide
831      */
getPublicNamespaces()832     public static @NonNull List<String> getPublicNamespaces() {
833         return PUBLIC_NAMESPACES;
834     }
835 
836     /**
837      * Interface for monitoring changes to properties. Implementations will receive callbacks when
838      * properties change, including a {@link Properties} object which contains a single namespace
839      * and all of the properties which changed for that namespace. This includes properties which
840      * were added, updated, or deleted. This is not necessarily a complete list of all properties
841      * belonging to the namespace, as properties which don't change are omitted.
842      * <p>
843      * Override {@link #onPropertiesChanged(Properties)} to handle callbacks for changes.
844      *
845      * @hide
846      */
847     @SystemApi
848     @TestApi
849     public interface OnPropertiesChangedListener {
850         /**
851          * Called when one or more properties have changed, providing a Properties object with all
852          * of the changed properties. This object will contain only properties which have changed,
853          * not the complete set of all properties belonging to the namespace.
854          *
855          * @param properties Contains the complete collection of properties which have changed for a
856          *                   single namespace. This includes only those which were added, updated,
857          *                   or deleted.
858          */
onPropertiesChanged(@onNull Properties properties)859         void onPropertiesChanged(@NonNull Properties properties);
860     }
861 
862     /**
863      * Thrown by {@link #setProperties(Properties)} when a configuration is rejected. This
864      * happens if RescueParty has identified a bad configuration and reset the namespace.
865      *
866      * @hide
867      */
868     @SystemApi
869     @TestApi
870     public static class BadConfigException extends Exception {}
871 
872     /**
873      * A mapping of properties to values, as well as a single namespace which they all belong to.
874      *
875      * @hide
876      */
877     @SystemApi
878     @TestApi
879     public static class Properties {
880         private final String mNamespace;
881         private final HashMap<String, String> mMap;
882         private Set<String> mKeyset;
883 
884         /**
885          * Create a mapping of properties to values and the namespace they belong to.
886          *
887          * @param namespace The namespace these properties belong to.
888          * @param keyValueMap A map between property names and property values.
889          * @hide
890          */
Properties(@onNull String namespace, @Nullable Map<String, String> keyValueMap)891         public Properties(@NonNull String namespace, @Nullable Map<String, String> keyValueMap) {
892             Preconditions.checkNotNull(namespace);
893             mNamespace = namespace;
894             mMap = new HashMap();
895             if (keyValueMap != null) {
896                 mMap.putAll(keyValueMap);
897             }
898         }
899 
900         /**
901          * @return the namespace all properties within this instance belong to.
902          */
903         @NonNull
getNamespace()904         public String getNamespace() {
905             return mNamespace;
906         }
907 
908         /**
909          * @return the non-null set of property names.
910          */
911         @NonNull
getKeyset()912         public Set<String> getKeyset() {
913             if (mKeyset == null) {
914                 mKeyset = Collections.unmodifiableSet(mMap.keySet());
915             }
916             return mKeyset;
917         }
918 
919         /**
920          * Look up the String value of a property.
921          *
922          * @param name         The name of the property to look up.
923          * @param defaultValue The value to return if the property has not been defined.
924          * @return the corresponding value, or defaultValue if none exists.
925          */
926         @Nullable
getString(@onNull String name, @Nullable String defaultValue)927         public String getString(@NonNull String name, @Nullable String defaultValue) {
928             Preconditions.checkNotNull(name);
929             String value = mMap.get(name);
930             return value != null ? value : defaultValue;
931         }
932 
933         /**
934          * Look up the boolean value of a property.
935          *
936          * @param name         The name of the property to look up.
937          * @param defaultValue The value to return if the property has not been defined.
938          * @return the corresponding value, or defaultValue if none exists.
939          */
getBoolean(@onNull String name, boolean defaultValue)940         public boolean getBoolean(@NonNull String name, boolean defaultValue) {
941             Preconditions.checkNotNull(name);
942             String value = mMap.get(name);
943             return value != null ? Boolean.parseBoolean(value) : defaultValue;
944         }
945 
946         /**
947          * Look up the int value of a property.
948          *
949          * @param name         The name of the property to look up.
950          * @param defaultValue The value to return if the property has not been defined or fails to
951          *                     parse into an int.
952          * @return the corresponding value, or defaultValue if no valid int is available.
953          */
getInt(@onNull String name, int defaultValue)954         public int getInt(@NonNull String name, int defaultValue) {
955             Preconditions.checkNotNull(name);
956             String value = mMap.get(name);
957             if (value == null) {
958                 return defaultValue;
959             }
960             try {
961                 return Integer.parseInt(value);
962             } catch (NumberFormatException e) {
963                 Log.e(TAG, "Parsing int failed for " + name);
964                 return defaultValue;
965             }
966         }
967 
968         /**
969          * Look up the long value of a property.
970          *
971          * @param name         The name of the property to look up.
972          * @param defaultValue The value to return if the property has not been defined. or fails to
973          *                     parse into a long.
974          * @return the corresponding value, or defaultValue if no valid long is available.
975          */
getLong(@onNull String name, long defaultValue)976         public long getLong(@NonNull String name, long defaultValue) {
977             Preconditions.checkNotNull(name);
978             String value = mMap.get(name);
979             if (value == null) {
980                 return defaultValue;
981             }
982             try {
983                 return Long.parseLong(value);
984             } catch (NumberFormatException e) {
985                 Log.e(TAG, "Parsing long failed for " + name);
986                 return defaultValue;
987             }
988         }
989 
990         /**
991          * Look up the int value of a property.
992          *
993          * @param name         The name of the property to look up.
994          * @param defaultValue The value to return if the property has not been defined. or fails to
995          *                     parse into a float.
996          * @return the corresponding value, or defaultValue if no valid float is available.
997          */
getFloat(@onNull String name, float defaultValue)998         public float getFloat(@NonNull String name, float defaultValue) {
999             Preconditions.checkNotNull(name);
1000             String value = mMap.get(name);
1001             if (value == null) {
1002                 return defaultValue;
1003             }
1004             try {
1005                 return Float.parseFloat(value);
1006             } catch (NumberFormatException e) {
1007                 Log.e(TAG, "Parsing float failed for " + name);
1008                 return defaultValue;
1009             }
1010         }
1011 
1012         /**
1013          * Builder class for the construction of {@link Properties} objects.
1014          */
1015         public static final class Builder {
1016             @NonNull
1017             private final String mNamespace;
1018             @NonNull
1019             private final Map<String, String> mKeyValues = new HashMap<>();
1020 
1021             /**
1022              * Create a new Builders for the specified namespace.
1023              * @param namespace non null namespace.
1024              */
Builder(@onNull String namespace)1025             public Builder(@NonNull String namespace) {
1026                 mNamespace = namespace;
1027             }
1028 
1029             /**
1030              * Add a new property with the specified key and value.
1031              * @param name non null name of the property.
1032              * @param value nullable string value of the property.
1033              * @return this Builder object
1034              */
1035             @NonNull
setString(@onNull String name, @Nullable String value)1036             public Builder setString(@NonNull String name, @Nullable String value) {
1037                 mKeyValues.put(name, value);
1038                 return this;
1039             }
1040 
1041             /**
1042              * Add a new property with the specified key and value.
1043              * @param name non null name of the property.
1044              * @param value nullable string value of the property.
1045              * @return this Builder object
1046              */
1047             @NonNull
setBoolean(@onNull String name, boolean value)1048             public Builder setBoolean(@NonNull String name, boolean value) {
1049                 mKeyValues.put(name, Boolean.toString(value));
1050                 return this;
1051             }
1052 
1053             /**
1054              * Add a new property with the specified key and value.
1055              * @param name non null name of the property.
1056              * @param value int value of the property.
1057              * @return this Builder object
1058              */
1059             @NonNull
setInt(@onNull String name, int value)1060             public Builder setInt(@NonNull String name, int value) {
1061                 mKeyValues.put(name, Integer.toString(value));
1062                 return this;
1063             }
1064 
1065             /**
1066              * Add a new property with the specified key and value.
1067              * @param name non null name of the property.
1068              * @param value long value of the property.
1069              * @return this Builder object
1070              */
1071             @NonNull
setLong(@onNull String name, long value)1072             public Builder setLong(@NonNull String name, long value) {
1073                 mKeyValues.put(name, Long.toString(value));
1074                 return this;
1075             }
1076 
1077             /**
1078              * Add a new property with the specified key and value.
1079              * @param name non null name of the property.
1080              * @param value float value of the property.
1081              * @return this Builder object
1082              */
1083             @NonNull
setFloat(@onNull String name, float value)1084             public Builder setFloat(@NonNull String name, float value) {
1085                 mKeyValues.put(name, Float.toString(value));
1086                 return this;
1087             }
1088 
1089             /**
1090              * Create a new {@link Properties} object.
1091              * @return non null Properties.
1092              */
1093             @NonNull
build()1094             public Properties build() {
1095                 return new Properties(mNamespace, mKeyValues);
1096             }
1097         }
1098     }
1099 
1100 }
1101