OSDN Git Service

Merge "docs: Add documentation for equals() method" into qt-dev am: 732a127636
[android-x86/frameworks-base.git] / core / java / android / provider / DeviceConfig.java
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.HashMap;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Set;
47 import java.util.concurrent.Executor;
48
49 /**
50  * Device level configuration parameters which can be tuned by a separate configuration service.
51  * 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.
53  *
54  * @hide
55  */
56 @SystemApi
57 @TestApi
58 public 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
66     /**
67      * 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     /**
76      * Namespace for all activity manager related features that are used at the native level.
77      * These features are applied at reboot.
78      *
79      * @hide
80      */
81     @SystemApi
82     public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT =
83             "activity_manager_native_boot";
84
85     /**
86      * 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     /**
95      * 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     /**
103      * Namespace for autofill feature that provides suggestions across all apps when
104      * the user interacts with input fields.
105      *
106      * @hide
107      */
108     @SystemApi
109     @TestApi
110     public static final String NAMESPACE_AUTOFILL = "autofill";
111
112     /**
113      * Namespace for all networking connectivity related features.
114      *
115      * @hide
116      */
117     @SystemApi
118     public static final String NAMESPACE_CONNECTIVITY = "connectivity";
119
120     /**
121      * Namespace for content capture feature used by on-device machine intelligence
122      * to provide suggestions in a privacy-safe manner.
123      *
124      * @hide
125      */
126     @SystemApi
127     @TestApi
128     public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
129
130     /**
131      * Namespace for how dex runs. The feature requires a reboot to reach a clean state.
132      *
133      * @hide
134      */
135     @SystemApi
136     public static final String NAMESPACE_DEX_BOOT = "dex_boot";
137
138     /**
139      * Namespace for display manager related features. The names to access the properties in this
140      * namespace should be defined in {@link android.hardware.display.DisplayManager}.
141      *
142      * @hide
143      */
144     public static final String NAMESPACE_DISPLAY_MANAGER = "display_manager";
145
146     /**
147      * Namespace for all Game Driver features.
148      *
149      * @hide
150      */
151     @SystemApi
152     public static final String NAMESPACE_GAME_DRIVER = "game_driver";
153
154     /**
155      * Namespace for all input-related features that are used at the native level.
156      * These features are applied at reboot.
157      *
158      * @hide
159      */
160     @SystemApi
161     public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
162
163     /**
164      * Namespace for attention-based features provided by on-device machine intelligence.
165      *
166      * @hide
167      */
168     @SystemApi
169     public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention";
170
171     /**
172      * Definitions for properties related to Content Suggestions.
173      *
174      * @hide
175      */
176     public static final String NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS =
177             "intelligence_content_suggestions";
178
179     /**
180      * Namespace for all media native related features.
181      *
182      * @hide
183      */
184     @SystemApi
185     public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
186
187     /**
188      * Namespace for all netd related features.
189      *
190      * @hide
191      */
192     @SystemApi
193     public static final String NAMESPACE_NETD_NATIVE = "netd_native";
194
195     /**
196      * Namespace for Rollback flags that are applied immediately.
197      *
198      * @hide
199      */
200     @SystemApi @TestApi
201     public static final String NAMESPACE_ROLLBACK = "rollback";
202
203     /**
204      * Namespace for Rollback flags that are applied after a reboot.
205      *
206      * @hide
207      */
208     @SystemApi @TestApi
209     public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
210
211     /**
212      * Namespace for all runtime related features that don't require a reboot to become active.
213      * There are no feature flags using NAMESPACE_RUNTIME.
214      *
215      * @hide
216      */
217     @SystemApi
218     public static final String NAMESPACE_RUNTIME = "runtime";
219
220     /**
221      * Namespace for all runtime related features that require system properties for accessing
222      * the feature flags from C++ or Java language code. One example is the app image startup
223      * cache feature use_app_image_startup_cache.
224      *
225      * @hide
226      */
227     @SystemApi
228     public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
229
230     /**
231      * Namespace for all runtime native boot related features. Boot in this case refers to the
232      * fact that the properties only take affect after rebooting the device.
233      *
234      * @hide
235      */
236     @SystemApi
237     public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
238
239     /**
240      * Namespace for system scheduler related features. These features will be applied
241      * immediately upon change.
242      *
243      * @hide
244      */
245     @SystemApi
246     public static final String NAMESPACE_SCHEDULER = "scheduler";
247
248     /**
249      * Namespace for storage-related features.
250      *
251      * @hide
252      */
253     @SystemApi
254     public static final String NAMESPACE_STORAGE = "storage";
255
256     /**
257      * Namespace for System UI related features.
258      *
259      * @hide
260      */
261     @SystemApi
262     public static final String NAMESPACE_SYSTEMUI = "systemui";
263
264     /**
265      * Telephony related properties.
266      *
267      * @hide
268      */
269     @SystemApi
270     public static final String NAMESPACE_TELEPHONY = "telephony";
271
272     /**
273      * Namespace for TextClassifier related features.
274      *
275      * @hide
276      * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
277      */
278     @SystemApi
279     public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
280
281     /**
282      * Namespace for contacts provider related features.
283      *
284      * @hide
285      */
286     public static final String NAMESPACE_CONTACTS_PROVIDER = "contacts_provider";
287
288     /**
289      * Namespace for settings ui related features
290      *
291      * @hide
292      */
293     public static final String NAMESPACE_SETTINGS_UI = "settings_ui";
294
295     /**
296      * Namespace for window manager related features. The names to access the properties in this
297      * namespace should be defined in {@link WindowManager}.
298      *
299      * @hide
300      */
301     @TestApi
302     public static final String NAMESPACE_WINDOW_MANAGER = "android:window_manager";
303
304     /**
305      * List of namespaces which can be read without READ_DEVICE_CONFIG permission
306      *
307      * @hide
308      */
309     @NonNull
310     private static final List<String> PUBLIC_NAMESPACES =
311             Arrays.asList(NAMESPACE_TEXTCLASSIFIER, NAMESPACE_RUNTIME);
312     /**
313      * Privacy related properties definitions.
314      *
315      * @hide
316      */
317     @SystemApi
318     @TestApi
319     public static final String NAMESPACE_PRIVACY = "privacy";
320
321     /**
322      * Interface for accessing keys belonging to {@link #NAMESPACE_WINDOW_MANAGER}.
323      * @hide
324      */
325     @TestApi
326     public interface WindowManager {
327
328         /**
329          * Key for accessing the system gesture exclusion limit (an integer in dp).
330          *
331          * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER
332          * @hide
333          */
334         @TestApi
335         String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp";
336
337         /**
338          * Key for controlling whether system gestures are implicitly excluded by windows requesting
339          * sticky immersive mode from apps that are targeting an SDK prior to Q.
340          *
341          * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER
342          * @hide
343          */
344         @TestApi
345         String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE =
346                 "system_gestures_excluded_by_pre_q_sticky_immersive";
347
348         /**
349          * The minimum duration between gesture exclusion logging for a given window in
350          * milliseconds.
351          *
352          * Events that happen in-between will be silently dropped.
353          *
354          * A non-positive value disables logging.
355          *
356          * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER
357          * @hide
358          */
359         String KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS =
360                 "system_gesture_exclusion_log_debounce_millis";
361     }
362
363     private static final Object sLock = new Object();
364     @GuardedBy("sLock")
365     private static ArrayMap<OnPropertyChangedListener, Pair<String, Executor>> sSingleListeners =
366             new ArrayMap<>();
367     @GuardedBy("sLock")
368     private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
369             new ArrayMap<>();
370     @GuardedBy("sLock")
371     private static Map<String, Pair<ContentObserver, Integer>> sNamespaces = new HashMap<>();
372     private static final String TAG = "DeviceConfig";
373
374     // Should never be invoked
375     private DeviceConfig() {
376     }
377
378     /**
379      * Look up the value of a property for a particular namespace.
380      *
381      * @param namespace The namespace containing the property to look up.
382      * @param name      The name of the property to look up.
383      * @return the corresponding value, or null if not present.
384      * @hide
385      */
386     @SystemApi
387     @TestApi
388     @RequiresPermission(READ_DEVICE_CONFIG)
389     public static String getProperty(@NonNull String namespace, @NonNull String name) {
390         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
391         String compositeName = createCompositeName(namespace, name);
392         return Settings.Config.getString(contentResolver, compositeName);
393     }
394
395     /**
396      * Look up the String value of a property for a particular namespace.
397      *
398      * @param namespace    The namespace containing the property to look up.
399      * @param name         The name of the property to look up.
400      * @param defaultValue The value to return if the property does not exist or has no non-null
401      *                     value.
402      * @return the corresponding value, or defaultValue if none exists.
403      * @hide
404      */
405     @SystemApi
406     @TestApi
407     @RequiresPermission(READ_DEVICE_CONFIG)
408     public static String getString(@NonNull String namespace, @NonNull String name,
409             @Nullable String defaultValue) {
410         String value = getProperty(namespace, name);
411         return value != null ? value : defaultValue;
412     }
413
414     /**
415      * Look up the boolean value of a property for a particular namespace.
416      *
417      * @param namespace The namespace containing the property to look up.
418      * @param name      The name of the property to look up.
419      * @param defaultValue The value to return if the property does not exist or has no non-null
420      *                     value.
421      * @return the corresponding value, or defaultValue if none exists.
422      * @hide
423      */
424     @SystemApi
425     @TestApi
426     @RequiresPermission(READ_DEVICE_CONFIG)
427     public static boolean getBoolean(@NonNull String namespace, @NonNull String name,
428             boolean defaultValue) {
429         String value = getProperty(namespace, name);
430         return value != null ? Boolean.parseBoolean(value) : defaultValue;
431     }
432
433     /**
434      * Look up the int value of a property for a particular namespace.
435      *
436      * @param namespace The namespace containing the property to look up.
437      * @param name      The name of the property to look up.
438      * @param defaultValue The value to return if the property does not exist, has no non-null
439      *                     value, or fails to parse into an int.
440      * @return the corresponding value, or defaultValue if either none exists or it does not parse.
441      * @hide
442      */
443     @SystemApi
444     @TestApi
445     @RequiresPermission(READ_DEVICE_CONFIG)
446     public static int getInt(@NonNull String namespace, @NonNull String name, int defaultValue) {
447         String value = getProperty(namespace, name);
448         if (value == null) {
449             return defaultValue;
450         }
451         try {
452             return Integer.parseInt(value);
453         } catch (NumberFormatException e) {
454             Log.e(TAG, "Parsing integer failed for " + namespace + ":" + name);
455             return defaultValue;
456         }
457     }
458
459     /**
460      * Look up the long value of a property for a particular namespace.
461      *
462      * @param namespace The namespace containing the property to look up.
463      * @param name      The name of the property to look up.
464      * @param defaultValue The value to return if the property does not exist, has no non-null
465      *                     value, or fails to parse into a long.
466      * @return the corresponding value, or defaultValue if either none exists or it does not parse.
467      * @hide
468      */
469     @SystemApi
470     @TestApi
471     @RequiresPermission(READ_DEVICE_CONFIG)
472     public static long getLong(@NonNull String namespace, @NonNull String name, long defaultValue) {
473         String value = getProperty(namespace, name);
474         if (value == null) {
475             return defaultValue;
476         }
477         try {
478             return Long.parseLong(value);
479         } catch (NumberFormatException e) {
480             Log.e(TAG, "Parsing long failed for " + namespace + ":" + name);
481             return defaultValue;
482         }
483     }
484
485     /**
486      * Look up the float value of a property for a particular namespace.
487      *
488      * @param namespace The namespace containing the property to look up.
489      * @param name      The name of the property to look up.
490      * @param defaultValue The value to return if the property does not exist, has no non-null
491      *                     value, or fails to parse into a float.
492      * @return the corresponding value, or defaultValue if either none exists or it does not parse.
493      * @hide
494      */
495     @SystemApi
496     @TestApi
497     @RequiresPermission(READ_DEVICE_CONFIG)
498     public static float getFloat(@NonNull String namespace, @NonNull String name,
499             float defaultValue) {
500         String value = getProperty(namespace, name);
501         if (value == null) {
502             return defaultValue;
503         }
504         try {
505             return Float.parseFloat(value);
506         } catch (NumberFormatException e) {
507             Log.e(TAG, "Parsing float failed for " + namespace + ":" + name);
508             return defaultValue;
509         }
510     }
511
512     /**
513      * Create a new property with the the provided name and value in the provided namespace, or
514      * update the value of such a property if it already exists. The same name can exist in multiple
515      * namespaces and might have different values in any or all namespaces.
516      * <p>
517      * The method takes an argument indicating whether to make the value the default for this
518      * property.
519      * <p>
520      * All properties stored for a particular scope can be reverted to their default values
521      * by passing the namespace to {@link #resetToDefaults(int, String)}.
522      *
523      * @param namespace   The namespace containing the property to create or update.
524      * @param name        The name of the property to create or update.
525      * @param value       The value to store for the property.
526      * @param makeDefault Whether to make the new value the default one.
527      * @return True if the value was set, false if the storage implementation throws errors.
528      * @hide
529      * @see #resetToDefaults(int, String).
530      */
531     @SystemApi
532     @TestApi
533     @RequiresPermission(WRITE_DEVICE_CONFIG)
534     public static boolean setProperty(@NonNull String namespace, @NonNull String name,
535             @Nullable String value, boolean makeDefault) {
536         String compositeName = createCompositeName(namespace, name);
537         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
538         return Settings.Config.putString(contentResolver, compositeName, value, makeDefault);
539     }
540
541     /**
542      * Reset properties to their default values.
543      * <p>
544      * The method accepts an optional namespace parameter. If provided, only properties set within
545      * that namespace will be reset. Otherwise, all properties will be reset.
546      *
547      * @param resetMode The reset mode to use.
548      * @param namespace Optionally, the specific namespace which resets will be limited to.
549      * @hide
550      * @see #setProperty(String, String, String, boolean)
551      */
552     @SystemApi
553     @TestApi
554     @RequiresPermission(WRITE_DEVICE_CONFIG)
555     public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) {
556         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
557         Settings.Config.resetToDefaults(contentResolver, resetMode, namespace);
558     }
559
560     /**
561      * Add a listener for property changes.
562      * <p>
563      * This listener will be called whenever properties in the specified namespace change. Callbacks
564      * will be made on the specified executor. Future calls to this method with the same listener
565      * will replace the old namespace and executor. Remove the listener entirely by calling
566      * {@link #removeOnPropertyChangedListener(OnPropertyChangedListener)}.
567      *
568      * @param namespace                 The namespace containing properties to monitor.
569      * @param executor                  The executor which will be used to run callbacks.
570      * @param onPropertyChangedListener The listener to add.
571      * @hide
572      * @see #removeOnPropertyChangedListener(OnPropertyChangedListener)
573      * @removed
574      */
575     @SystemApi
576     @TestApi
577     @RequiresPermission(READ_DEVICE_CONFIG)
578     public static void addOnPropertyChangedListener(
579             @NonNull String namespace,
580             @NonNull @CallbackExecutor Executor executor,
581             @NonNull OnPropertyChangedListener onPropertyChangedListener) {
582         enforceReadPermission(ActivityThread.currentApplication().getApplicationContext(),
583                 namespace);
584         synchronized (sLock) {
585             Pair<String, Executor> oldNamespace = sSingleListeners.get(onPropertyChangedListener);
586             if (oldNamespace == null) {
587                 // Brand new listener, add it to the list.
588                 sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
589                 incrementNamespace(namespace);
590             } else if (namespace.equals(oldNamespace.first)) {
591                 // Listener is already registered for this namespace, update executor just in case.
592                 sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
593             } else {
594                 // Update this listener from an old namespace to the new one.
595                 decrementNamespace(sSingleListeners.get(onPropertyChangedListener).first);
596                 sSingleListeners.put(onPropertyChangedListener, new Pair<>(namespace, executor));
597                 incrementNamespace(namespace);
598             }
599         }
600     }
601
602     /**
603      * Add a listener for property changes.
604      * <p>
605      * This listener will be called whenever properties in the specified namespace change. Callbacks
606      * will be made on the specified executor. Future calls to this method with the same listener
607      * will replace the old namespace and executor. Remove the listener entirely by calling
608      * {@link #removeOnPropertiesChangedListener(OnPropertiesChangedListener)}.
609      *
610      * @param namespace                   The namespace containing properties to monitor.
611      * @param executor                    The executor which will be used to run callbacks.
612      * @param onPropertiesChangedListener The listener to add.
613      * @hide
614      * @see #removeOnPropertiesChangedListener(OnPropertiesChangedListener)
615      */
616     @SystemApi
617     @TestApi
618     @RequiresPermission(READ_DEVICE_CONFIG)
619     public static void addOnPropertiesChangedListener(
620             @NonNull String namespace,
621             @NonNull @CallbackExecutor Executor executor,
622             @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
623         enforceReadPermission(ActivityThread.currentApplication().getApplicationContext(),
624                 namespace);
625         synchronized (sLock) {
626             Pair<String, Executor> oldNamespace = sListeners.get(onPropertiesChangedListener);
627             if (oldNamespace == null) {
628                 // Brand new listener, add it to the list.
629                 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
630                 incrementNamespace(namespace);
631             } else if (namespace.equals(oldNamespace.first)) {
632                 // Listener is already registered for this namespace, update executor just in case.
633                 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
634             } else {
635                 // Update this listener from an old namespace to the new one.
636                 decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
637                 sListeners.put(onPropertiesChangedListener, new Pair<>(namespace, executor));
638                 incrementNamespace(namespace);
639             }
640         }
641     }
642
643     /**
644      * Remove a listener for property changes. The listener will receive no further notification of
645      * property changes.
646      *
647      * @param onPropertyChangedListener The listener to remove.
648      * @hide
649      * @see #addOnPropertyChangedListener(String, Executor, OnPropertyChangedListener)
650      * @removed
651      */
652     @SystemApi
653     @TestApi
654     public static void removeOnPropertyChangedListener(
655             @NonNull OnPropertyChangedListener onPropertyChangedListener) {
656         Preconditions.checkNotNull(onPropertyChangedListener);
657         synchronized (sLock) {
658             if (sSingleListeners.containsKey(onPropertyChangedListener)) {
659                 decrementNamespace(sSingleListeners.get(onPropertyChangedListener).first);
660                 sSingleListeners.remove(onPropertyChangedListener);
661             }
662         }
663     }
664
665     /**
666      * Remove a listener for property changes. The listener will receive no further notification of
667      * property changes.
668      *
669      * @param onPropertiesChangedListener The listener to remove.
670      * @hide
671      * @see #addOnPropertiesChangedListener(String, Executor, OnPropertiesChangedListener)
672      */
673     @SystemApi
674     @TestApi
675     public static void removeOnPropertiesChangedListener(
676             @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
677         Preconditions.checkNotNull(onPropertiesChangedListener);
678         synchronized (sLock) {
679             if (sListeners.containsKey(onPropertiesChangedListener)) {
680                 decrementNamespace(sListeners.get(onPropertiesChangedListener).first);
681                 sListeners.remove(onPropertiesChangedListener);
682             }
683         }
684     }
685
686     private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
687         Preconditions.checkNotNull(namespace);
688         Preconditions.checkNotNull(name);
689         return namespace + "/" + name;
690     }
691
692     private static Uri createNamespaceUri(@NonNull String namespace) {
693         Preconditions.checkNotNull(namespace);
694         return CONTENT_URI.buildUpon().appendPath(namespace).build();
695     }
696
697     /**
698      * Increment the count used to represent the number of listeners subscribed to the given
699      * namespace. If this is the first (i.e. incrementing from 0 to 1) for the given namespace, a
700      * ContentObserver is registered.
701      *
702      * @param namespace The namespace to increment the count for.
703      */
704     @GuardedBy("sLock")
705     private static void incrementNamespace(@NonNull String namespace) {
706         Preconditions.checkNotNull(namespace);
707         Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
708         if (namespaceCount != null) {
709             sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second + 1));
710         } else {
711             // This is a new namespace, register a ContentObserver for it.
712             ContentObserver contentObserver = new ContentObserver(null) {
713                 @Override
714                 public void onChange(boolean selfChange, Uri uri) {
715                     if (uri != null) {
716                         handleChange(uri);
717                     }
718                 }
719             };
720             ActivityThread.currentApplication().getContentResolver()
721                     .registerContentObserver(createNamespaceUri(namespace), true, contentObserver);
722             sNamespaces.put(namespace, new Pair<>(contentObserver, 1));
723         }
724     }
725
726     /**
727      * Decrement the count used to represent the number of listeners subscribed to the given
728      * namespace. If this is the final decrement call (i.e. decrementing from 1 to 0) for the given
729      * namespace, the ContentObserver that had been tracking it will be removed.
730      *
731      * @param namespace The namespace to decrement the count for.
732      */
733     @GuardedBy("sLock")
734     private static void decrementNamespace(@NonNull String namespace) {
735         Preconditions.checkNotNull(namespace);
736         Pair<ContentObserver, Integer> namespaceCount = sNamespaces.get(namespace);
737         if (namespaceCount == null) {
738             // This namespace is not registered and does not need to be decremented
739             return;
740         } else if (namespaceCount.second > 1) {
741             sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second - 1));
742         } else {
743             // Decrementing a namespace to zero means we no longer need its ContentObserver.
744             ActivityThread.currentApplication().getContentResolver()
745                     .unregisterContentObserver(namespaceCount.first);
746             sNamespaces.remove(namespace);
747         }
748     }
749
750     private static void handleChange(@NonNull Uri uri) {
751         Preconditions.checkNotNull(uri);
752         List<String> pathSegments = uri.getPathSegments();
753         // pathSegments(0) is "config"
754         final String namespace = pathSegments.get(1);
755         final String name = pathSegments.get(2);
756         final String value;
757         try {
758             value = getProperty(namespace, name);
759         } catch (SecurityException e) {
760             // Silently failing to not crash binder or listener threads.
761             Log.e(TAG, "OnPropertyChangedListener update failed: permission violation.");
762             return;
763         }
764         synchronized (sLock) {
765             // OnPropertiesChangedListeners
766             for (int i = 0; i < sListeners.size(); i++) {
767                 if (namespace.equals(sListeners.valueAt(i).first)) {
768                     final int j = i;
769                     sListeners.valueAt(i).second.execute(new Runnable() {
770                         @Override
771                         public void run() {
772                             Map<String, String> propertyMap = new HashMap(1);
773                             propertyMap.put(name, value);
774                             sListeners.keyAt(j)
775                                     .onPropertiesChanged(new Properties(namespace, propertyMap));
776                         }
777
778                     });
779                 }
780             }
781             // OnPropertyChangedListeners
782             for (int i = 0; i < sSingleListeners.size(); i++) {
783                 if (namespace.equals(sSingleListeners.valueAt(i).first)) {
784                     final int j = i;
785                     sSingleListeners.valueAt(i).second.execute(new Runnable() {
786                         @Override
787                         public void run() {
788                             sSingleListeners.keyAt(j).onPropertyChanged(namespace, name, value);
789                         }
790
791                     });
792                 }
793             }
794         }
795     }
796
797
798     /**
799      * Enforces READ_DEVICE_CONFIG permission if namespace is not one of public namespaces.
800      * @hide
801      */
802     public static void enforceReadPermission(Context context, String namespace) {
803         if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
804                 != PackageManager.PERMISSION_GRANTED) {
805             if (!PUBLIC_NAMESPACES.contains(namespace)) {
806                 throw new SecurityException("Permission denial: reading from settings requires:"
807                         + READ_DEVICE_CONFIG);
808             }
809         }
810     }
811
812
813     /**
814      * Interface for monitoring single property changes.
815      * <p>
816      * Override {@link #onPropertyChanged(String, String, String)} to handle callbacks for changes.
817      *
818      * @hide
819      * @removed
820      */
821     @SystemApi
822     @TestApi
823     public interface OnPropertyChangedListener {
824         /**
825          * Called when a property has changed.
826          *
827          * @param namespace The namespace containing the property which has changed.
828          * @param name      The name of the property which has changed.
829          * @param value     The new value of the property which has changed.
830          */
831         void onPropertyChanged(@NonNull String namespace, @NonNull String name,
832                 @Nullable String value);
833     }
834
835     /**
836      * Interface for monitoring changes to properties.
837      * <p>
838      * Override {@link #onPropertiesChanged(Properties)} to handle callbacks for changes.
839      *
840      * @hide
841      */
842     @SystemApi
843     @TestApi
844     public interface OnPropertiesChangedListener {
845         /**
846          * Called when one or more properties have changed.
847          *
848          * @param properties Contains the complete collection of properties which have changed for a
849          *                   single namespace.
850          */
851         void onPropertiesChanged(@NonNull Properties properties);
852     }
853
854     /**
855      * A mapping of properties to values, as well as a single namespace which they all belong to.
856      *
857      * @hide
858      */
859     @SystemApi
860     @TestApi
861     public static class Properties {
862         private final String mNamespace;
863         private final HashMap<String, String> mMap;
864
865         /**
866          * Create a mapping of properties to values and the namespace they belong to.
867          *
868          * @param namespace The namespace these properties belong to.
869          * @param keyValueMap A map between property names and property values.
870          */
871         Properties(@NonNull String namespace, @Nullable Map<String, String> keyValueMap) {
872             Preconditions.checkNotNull(namespace);
873             mNamespace = namespace;
874             mMap = new HashMap();
875             if (keyValueMap != null) {
876                 mMap.putAll(keyValueMap);
877             }
878         }
879
880         /**
881          * @return the namespace all properties within this instance belong to.
882          */
883         @NonNull
884         public String getNamespace() {
885             return mNamespace;
886         }
887
888         /**
889          * @return the non-null set of property names.
890          */
891         @NonNull
892         public Set<String> getKeyset() {
893             return mMap.keySet();
894         }
895
896         /**
897          * Look up the String value of a property.
898          *
899          * @param name         The name of the property to look up.
900          * @param defaultValue The value to return if the property has not been defined.
901          * @return the corresponding value, or defaultValue if none exists.
902          */
903         @Nullable
904         public String getString(@NonNull String name, @Nullable String defaultValue) {
905             Preconditions.checkNotNull(name);
906             String value = mMap.get(name);
907             return value != null ? value : defaultValue;
908         }
909
910         /**
911          * Look up the boolean value of a property.
912          *
913          * @param name         The name of the property to look up.
914          * @param defaultValue The value to return if the property has not been defined.
915          * @return the corresponding value, or defaultValue if none exists.
916          */
917         public boolean getBoolean(@NonNull String name, boolean defaultValue) {
918             Preconditions.checkNotNull(name);
919             String value = mMap.get(name);
920             return value != null ? Boolean.parseBoolean(value) : defaultValue;
921         }
922
923         /**
924          * Look up the int value of a property.
925          *
926          * @param name         The name of the property to look up.
927          * @param defaultValue The value to return if the property has not been defined or fails to
928          *                     parse into an int.
929          * @return the corresponding value, or defaultValue if no valid int is available.
930          */
931         public int getInt(@NonNull String name, int defaultValue) {
932             Preconditions.checkNotNull(name);
933             String value = mMap.get(name);
934             if (value == null) {
935                 return defaultValue;
936             }
937             try {
938                 return Integer.parseInt(value);
939             } catch (NumberFormatException e) {
940                 Log.e(TAG, "Parsing int failed for " + name);
941                 return defaultValue;
942             }
943         }
944
945         /**
946          * Look up the long value of a property.
947          *
948          * @param name         The name of the property to look up.
949          * @param defaultValue The value to return if the property has not been defined. or fails to
950          *                     parse into a long.
951          * @return the corresponding value, or defaultValue if no valid long is available.
952          */
953         public long getLong(@NonNull String name, long defaultValue) {
954             Preconditions.checkNotNull(name);
955             String value = mMap.get(name);
956             if (value == null) {
957                 return defaultValue;
958             }
959             try {
960                 return Long.parseLong(value);
961             } catch (NumberFormatException e) {
962                 Log.e(TAG, "Parsing long failed for " + name);
963                 return defaultValue;
964             }
965         }
966
967         /**
968          * Look up the int value of a property.
969          *
970          * @param name         The name of the property to look up.
971          * @param defaultValue The value to return if the property has not been defined. or fails to
972          *                     parse into a float.
973          * @return the corresponding value, or defaultValue if no valid float is available.
974          */
975         public float getFloat(@NonNull String name, float defaultValue) {
976             Preconditions.checkNotNull(name);
977             String value = mMap.get(name);
978             if (value == null) {
979                 return defaultValue;
980             }
981             try {
982                 return Float.parseFloat(value);
983             } catch (NumberFormatException e) {
984                 Log.e(TAG, "Parsing float failed for " + name);
985                 return defaultValue;
986             }
987         }
988     }
989 }