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