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