1 /* 2 * Copyright (C) 2021 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 com.android.server.appsearch; 18 19 import android.annotation.NonNull; 20 import android.os.Build; 21 import android.os.Bundle; 22 import android.provider.DeviceConfig; 23 import android.provider.DeviceConfig.OnPropertiesChangedListener; 24 25 import com.android.internal.annotations.GuardedBy; 26 import com.android.internal.annotations.VisibleForTesting; 27 import com.android.server.appsearch.external.localstorage.IcingOptionsConfig; 28 29 import java.util.Objects; 30 import java.util.concurrent.Executor; 31 32 /** 33 * Implementation of {@link AppSearchConfig} using {@link DeviceConfig}. 34 * 35 * <p>Though the latest flag values can always be retrieved by calling {@link 36 * DeviceConfig#getProperty}, we want to cache some of those values. For example, the sampling 37 * intervals for logging, they are needed for each api call and it would be a little expensive to 38 * call {@link DeviceConfig#getProperty} every time. 39 * 40 * <p>Listener is registered to DeviceConfig keep the cached value up to date. 41 * 42 * <p>This class is thread-safe. 43 * 44 * @hide 45 */ 46 public final class FrameworkAppSearchConfig implements AppSearchConfig { 47 private static volatile FrameworkAppSearchConfig sConfig; 48 49 /* 50 * Keys for ALL the flags stored in DeviceConfig. 51 */ 52 public static final String KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 53 "min_time_interval_between_samples_millis"; 54 public static final String KEY_SAMPLING_INTERVAL_DEFAULT = "sampling_interval_default"; 55 public static final String KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS = 56 "sampling_interval_for_batch_call_stats"; 57 public static final String KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS = 58 "sampling_interval_for_put_document_stats"; 59 public static final String KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS = 60 "sampling_interval_for_initialize_stats"; 61 public static final String KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS = 62 "sampling_interval_for_search_stats"; 63 public static final String KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS = 64 "sampling_interval_for_global_search_stats"; 65 public static final String KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS = 66 "sampling_interval_for_optimize_stats"; 67 public static final String KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES = 68 "limit_config_max_document_size_bytes"; 69 public static final String KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT = 70 "limit_config_max_document_count"; 71 public static final String KEY_LIMIT_CONFIG_MAX_SUGGESTION_COUNT = 72 "limit_config_max_suggestion_count"; 73 public static final String KEY_BYTES_OPTIMIZE_THRESHOLD = "bytes_optimize_threshold"; 74 public static final String KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS = "time_optimize_threshold"; 75 public static final String KEY_DOC_COUNT_OPTIMIZE_THRESHOLD = "doc_count_optimize_threshold"; 76 public static final String KEY_MIN_TIME_OPTIMIZE_THRESHOLD_MILLIS = 77 "min_time_optimize_threshold"; 78 public static final String KEY_API_CALL_STATS_LIMIT = "api_call_stats_limit"; 79 public static final String KEY_DENYLIST = "denylist"; 80 public static final String KEY_RATE_LIMIT_ENABLED = "rate_limit_enabled"; 81 public static final String KEY_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY = 82 "rate_limit_task_queue_total_capacity"; 83 public static final String KEY_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE = 84 "rate_limit_task_queue_per_package_capacity_percentage"; 85 public static final String KEY_RATE_LIMIT_API_COSTS = "rate_limit_api_costs"; 86 87 public static final String KEY_ICING_MAX_TOKEN_LENGTH = "icing_max_token_length"; 88 public static final String KEY_ICING_INDEX_MERGE_SIZE = "icing_index_merge_size"; 89 public static final String KEY_ICING_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT = 90 "icing_document_store_namespace_id_fingerprint"; 91 public static final String KEY_ICING_OPTIMIZE_REBUILD_INDEX_THRESHOLD = 92 "icing_optimize_rebuild_index_threshold"; 93 public static final String KEY_ICING_COMPRESSION_LEVEL = "icing_compression_level"; 94 public static final String KEY_ICING_USE_READ_ONLY_SEARCH = "icing_use_read_only_search"; 95 public static final String KEY_ICING_USE_PRE_MAPPING_WITH_FILE_BACKED_VECTOR = 96 "icing_use_pre_mapping_with_file_backed_vector"; 97 public static final String KEY_ICING_USE_PERSISTENT_HASHMAP = "icing_use_persistent_hashmap"; 98 public static final String KEY_ICING_MAX_PAGE_BYTES_LIMIT = "icing_max_page_bytes_limit"; 99 100 /** 101 * This config does not need to be cached in FrameworkAppSearchConfig as it is only accessed 102 * statically. AppSearch retrieves this directly from DeviceConfig when needed. 103 */ 104 public static final String KEY_USE_FIXED_EXECUTOR_SERVICE = "use_fixed_executor_service"; 105 106 // Array contains all the corresponding keys for the cached values. 107 private static final String[] KEYS_TO_ALL_CACHED_VALUES = { 108 KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, 109 KEY_SAMPLING_INTERVAL_DEFAULT, 110 KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, 111 KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, 112 KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS, 113 KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS, 114 KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS, 115 KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS, 116 KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES, 117 KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT, 118 KEY_LIMIT_CONFIG_MAX_SUGGESTION_COUNT, 119 KEY_BYTES_OPTIMIZE_THRESHOLD, 120 KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS, 121 KEY_DOC_COUNT_OPTIMIZE_THRESHOLD, 122 KEY_MIN_TIME_OPTIMIZE_THRESHOLD_MILLIS, 123 KEY_API_CALL_STATS_LIMIT, 124 KEY_DENYLIST, 125 KEY_RATE_LIMIT_ENABLED, 126 KEY_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY, 127 KEY_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE, 128 KEY_RATE_LIMIT_API_COSTS, 129 KEY_ICING_MAX_TOKEN_LENGTH, 130 KEY_ICING_INDEX_MERGE_SIZE, 131 KEY_ICING_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT, 132 KEY_ICING_OPTIMIZE_REBUILD_INDEX_THRESHOLD, 133 KEY_ICING_COMPRESSION_LEVEL, 134 KEY_ICING_USE_READ_ONLY_SEARCH, 135 KEY_ICING_USE_PRE_MAPPING_WITH_FILE_BACKED_VECTOR, 136 KEY_ICING_USE_PERSISTENT_HASHMAP, 137 KEY_ICING_MAX_PAGE_BYTES_LIMIT 138 }; 139 140 // Lock needed for all the operations in this class. 141 private final Object mLock = new Object(); 142 143 /** 144 * Bundle to hold all the cached flag values corresponding to 145 * {@link FrameworkAppSearchConfig#KEYS_TO_ALL_CACHED_VALUES}. 146 */ 147 @GuardedBy("mLock") 148 private final Bundle mBundleLocked = new Bundle(); 149 150 @GuardedBy("mLock") 151 private Denylist mDenylistLocked = Denylist.EMPTY_INSTANCE; 152 153 @GuardedBy("mLock") 154 private AppSearchRateLimitConfig mRateLimitConfigLocked = AppSearchRateLimitConfig.create( 155 DEFAULT_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY, 156 DEFAULT_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE, 157 DEFAULT_RATE_LIMIT_API_COSTS_STRING); 158 159 @GuardedBy("mLock") 160 private boolean mIsClosedLocked = false; 161 162 /** Listener to update cached flag values from DeviceConfig. */ 163 private final OnPropertiesChangedListener mOnDeviceConfigChangedListener = 164 properties -> { 165 if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_APPSEARCH)) { 166 return; 167 } 168 169 updateCachedValues(properties); 170 }; 171 FrameworkAppSearchConfig()172 private FrameworkAppSearchConfig() { 173 } 174 175 /** 176 * Creates an instance of {@link FrameworkAppSearchConfig}. 177 * 178 * @param executor used to fetch and cache the flag values from DeviceConfig during creation or 179 * config change. 180 */ 181 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) 182 @NonNull create(@onNull Executor executor)183 public static FrameworkAppSearchConfig create(@NonNull Executor executor) { 184 Objects.requireNonNull(executor); 185 FrameworkAppSearchConfig configManager = new FrameworkAppSearchConfig(); 186 configManager.initialize(executor); 187 return configManager; 188 } 189 190 /** 191 * Gets an instance of {@link FrameworkAppSearchConfig} to be used. 192 * 193 * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the 194 * existing instance will be returned. 195 */ 196 @NonNull getInstance(@onNull Executor executor)197 public static FrameworkAppSearchConfig getInstance(@NonNull Executor executor) { 198 Objects.requireNonNull(executor); 199 if (sConfig == null) { 200 synchronized (FrameworkAppSearchConfig.class) { 201 if (sConfig == null) { 202 sConfig = create(executor); 203 } 204 } 205 } 206 return sConfig; 207 } 208 209 /** 210 * Returns whether or not to use a fixed executor service for AppSearch. This config is only 211 * queried statically and is therefore retrieved directly from DeviceConfig. 212 */ getUseFixedExecutorService()213 public static boolean getUseFixedExecutorService() { 214 return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_APPSEARCH, 215 KEY_USE_FIXED_EXECUTOR_SERVICE, DEFAULT_USE_FIXED_EXECUTOR_SERVICE); 216 } 217 218 /** 219 * Initializes the {@link FrameworkAppSearchConfig} 220 * 221 * <p>It fetches the custom properties from DeviceConfig if available. 222 * 223 * @param executor listener would be run on to handle P/H flag change. 224 */ initialize(@onNull Executor executor)225 private void initialize(@NonNull Executor executor) { 226 executor.execute(() -> { 227 // Attach the callback to get updates on those properties. 228 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APPSEARCH, 229 executor, 230 mOnDeviceConfigChangedListener); 231 232 DeviceConfig.Properties properties = DeviceConfig.getProperties( 233 DeviceConfig.NAMESPACE_APPSEARCH, KEYS_TO_ALL_CACHED_VALUES); 234 updateCachedValues(properties); 235 }); 236 } 237 238 // TODO(b/173532925) check this will be called. If we have a singleton instance for this 239 // class, probably we don't need it. 240 @Override close()241 public void close() { 242 synchronized (mLock) { 243 if (mIsClosedLocked) { 244 return; 245 } 246 247 DeviceConfig.removeOnPropertiesChangedListener(mOnDeviceConfigChangedListener); 248 mIsClosedLocked = true; 249 } 250 } 251 252 @Override getCachedMinTimeIntervalBetweenSamplesMillis()253 public long getCachedMinTimeIntervalBetweenSamplesMillis() { 254 synchronized (mLock) { 255 throwIfClosedLocked(); 256 return mBundleLocked.getLong(KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, 257 DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS); 258 } 259 } 260 261 @Override getCachedSamplingIntervalDefault()262 public int getCachedSamplingIntervalDefault() { 263 synchronized (mLock) { 264 throwIfClosedLocked(); 265 return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_DEFAULT, DEFAULT_SAMPLING_INTERVAL); 266 } 267 } 268 269 @Override getCachedSamplingIntervalForBatchCallStats()270 public int getCachedSamplingIntervalForBatchCallStats() { 271 synchronized (mLock) { 272 throwIfClosedLocked(); 273 return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, 274 getCachedSamplingIntervalDefault()); 275 } 276 } 277 278 @Override getCachedSamplingIntervalForPutDocumentStats()279 public int getCachedSamplingIntervalForPutDocumentStats() { 280 synchronized (mLock) { 281 throwIfClosedLocked(); 282 return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, 283 getCachedSamplingIntervalDefault()); 284 } 285 } 286 287 @Override getCachedSamplingIntervalForInitializeStats()288 public int getCachedSamplingIntervalForInitializeStats() { 289 synchronized (mLock) { 290 throwIfClosedLocked(); 291 return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS, 292 getCachedSamplingIntervalDefault()); 293 } 294 } 295 296 @Override getCachedSamplingIntervalForSearchStats()297 public int getCachedSamplingIntervalForSearchStats() { 298 synchronized (mLock) { 299 throwIfClosedLocked(); 300 return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS, 301 getCachedSamplingIntervalDefault()); 302 } 303 } 304 305 @Override getCachedSamplingIntervalForGlobalSearchStats()306 public int getCachedSamplingIntervalForGlobalSearchStats() { 307 synchronized (mLock) { 308 throwIfClosedLocked(); 309 return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS, 310 getCachedSamplingIntervalDefault()); 311 } 312 } 313 314 @Override getCachedSamplingIntervalForOptimizeStats()315 public int getCachedSamplingIntervalForOptimizeStats() { 316 synchronized (mLock) { 317 throwIfClosedLocked(); 318 return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS, 319 getCachedSamplingIntervalDefault()); 320 } 321 } 322 323 @Override getMaxDocumentSizeBytes()324 public int getMaxDocumentSizeBytes() { 325 synchronized (mLock) { 326 throwIfClosedLocked(); 327 return mBundleLocked.getInt(KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES, 328 DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES); 329 } 330 } 331 332 @Override getMaxDocumentCount()333 public int getMaxDocumentCount() { 334 synchronized (mLock) { 335 throwIfClosedLocked(); 336 return mBundleLocked.getInt(KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT, 337 DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT); 338 } 339 } 340 341 @Override getMaxSuggestionCount()342 public int getMaxSuggestionCount() { 343 synchronized (mLock) { 344 throwIfClosedLocked(); 345 return mBundleLocked.getInt(KEY_LIMIT_CONFIG_MAX_SUGGESTION_COUNT, 346 DEFAULT_LIMIT_CONFIG_MAX_SUGGESTION_COUNT); 347 } 348 } 349 350 @Override getCachedBytesOptimizeThreshold()351 public int getCachedBytesOptimizeThreshold() { 352 synchronized (mLock) { 353 throwIfClosedLocked(); 354 return mBundleLocked.getInt(KEY_BYTES_OPTIMIZE_THRESHOLD, 355 DEFAULT_BYTES_OPTIMIZE_THRESHOLD); 356 } 357 } 358 359 @Override getCachedTimeOptimizeThresholdMs()360 public int getCachedTimeOptimizeThresholdMs() { 361 synchronized (mLock) { 362 throwIfClosedLocked(); 363 return mBundleLocked.getInt(KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS, 364 DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS); 365 } 366 } 367 368 @Override getCachedDocCountOptimizeThreshold()369 public int getCachedDocCountOptimizeThreshold() { 370 synchronized (mLock) { 371 throwIfClosedLocked(); 372 return mBundleLocked.getInt(KEY_DOC_COUNT_OPTIMIZE_THRESHOLD, 373 DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD); 374 } 375 } 376 377 @Override getCachedMinTimeOptimizeThresholdMs()378 public int getCachedMinTimeOptimizeThresholdMs() { 379 synchronized (mLock) { 380 throwIfClosedLocked(); 381 return mBundleLocked.getInt(KEY_MIN_TIME_OPTIMIZE_THRESHOLD_MILLIS, 382 DEFAULT_MIN_TIME_OPTIMIZE_THRESHOLD_MILLIS); 383 } 384 } 385 386 @Override getCachedApiCallStatsLimit()387 public int getCachedApiCallStatsLimit() { 388 synchronized (mLock) { 389 throwIfClosedLocked(); 390 return mBundleLocked.getInt(KEY_API_CALL_STATS_LIMIT, 391 DEFAULT_API_CALL_STATS_LIMIT); 392 } 393 } 394 395 @Override getCachedDenylist()396 public Denylist getCachedDenylist() { 397 synchronized (mLock) { 398 throwIfClosedLocked(); 399 return mDenylistLocked; 400 } 401 } 402 403 @Override getMaxTokenLength()404 public int getMaxTokenLength() { 405 synchronized (mLock) { 406 throwIfClosedLocked(); 407 return mBundleLocked.getInt(KEY_ICING_MAX_TOKEN_LENGTH, 408 IcingOptionsConfig.DEFAULT_MAX_TOKEN_LENGTH); 409 } 410 } 411 412 @Override getIndexMergeSize()413 public int getIndexMergeSize() { 414 synchronized (mLock) { 415 throwIfClosedLocked(); 416 return mBundleLocked.getInt(KEY_ICING_INDEX_MERGE_SIZE, 417 IcingOptionsConfig.DEFAULT_INDEX_MERGE_SIZE); 418 } 419 } 420 421 @Override getDocumentStoreNamespaceIdFingerprint()422 public boolean getDocumentStoreNamespaceIdFingerprint() { 423 synchronized (mLock) { 424 throwIfClosedLocked(); 425 return mBundleLocked.getBoolean(KEY_ICING_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT, 426 IcingOptionsConfig.DEFAULT_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT); 427 } 428 } 429 430 @Override getOptimizeRebuildIndexThreshold()431 public float getOptimizeRebuildIndexThreshold() { 432 synchronized (mLock) { 433 throwIfClosedLocked(); 434 return mBundleLocked.getFloat(KEY_ICING_OPTIMIZE_REBUILD_INDEX_THRESHOLD, 435 IcingOptionsConfig.DEFAULT_OPTIMIZE_REBUILD_INDEX_THRESHOLD); 436 } 437 } 438 439 @Override getCompressionLevel()440 public int getCompressionLevel() { 441 synchronized (mLock) { 442 throwIfClosedLocked(); 443 return mBundleLocked.getInt(KEY_ICING_COMPRESSION_LEVEL, 444 IcingOptionsConfig.DEFAULT_COMPRESSION_LEVEL); 445 } 446 } 447 448 @Override getAllowCircularSchemaDefinitions()449 public boolean getAllowCircularSchemaDefinitions() { 450 // TODO(b/282108040) add flag(default on) to cover this feature in case a bug is discovered. 451 synchronized (mLock) { 452 throwIfClosedLocked(); 453 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 454 } 455 } 456 457 @Override getUseReadOnlySearch()458 public boolean getUseReadOnlySearch() { 459 synchronized (mLock) { 460 throwIfClosedLocked(); 461 return mBundleLocked.getBoolean(KEY_ICING_USE_READ_ONLY_SEARCH, 462 DEFAULT_ICING_CONFIG_USE_READ_ONLY_SEARCH); 463 } 464 } 465 466 @Override getUsePreMappingWithFileBackedVector()467 public boolean getUsePreMappingWithFileBackedVector() { 468 synchronized (mLock) { 469 throwIfClosedLocked(); 470 return mBundleLocked.getBoolean(KEY_ICING_USE_PRE_MAPPING_WITH_FILE_BACKED_VECTOR, 471 IcingOptionsConfig.DEFAULT_USE_PREMAPPING_WITH_FILE_BACKED_VECTOR); 472 } 473 } 474 475 @Override getUsePersistentHashMap()476 public boolean getUsePersistentHashMap() { 477 synchronized (mLock) { 478 throwIfClosedLocked(); 479 return mBundleLocked.getBoolean(KEY_ICING_USE_PERSISTENT_HASHMAP, 480 IcingOptionsConfig.DEFAULT_USE_PERSISTENT_HASH_MAP); 481 } 482 } 483 484 @Override getMaxPageBytesLimit()485 public int getMaxPageBytesLimit() { 486 synchronized (mLock) { 487 throwIfClosedLocked(); 488 return mBundleLocked.getInt(KEY_ICING_MAX_PAGE_BYTES_LIMIT, 489 IcingOptionsConfig.DEFAULT_MAX_PAGE_BYTES_LIMIT); 490 } 491 } 492 493 @Override getCachedRateLimitEnabled()494 public boolean getCachedRateLimitEnabled() { 495 synchronized (mLock) { 496 throwIfClosedLocked(); 497 return mBundleLocked.getBoolean(KEY_RATE_LIMIT_ENABLED, DEFAULT_RATE_LIMIT_ENABLED); 498 } 499 } 500 501 @Override getCachedRateLimitConfig()502 public AppSearchRateLimitConfig getCachedRateLimitConfig() { 503 synchronized (mLock) { 504 throwIfClosedLocked(); 505 return mRateLimitConfigLocked; 506 } 507 } 508 509 @GuardedBy("mLock") throwIfClosedLocked()510 private void throwIfClosedLocked() { 511 if (mIsClosedLocked) { 512 throw new IllegalStateException("Trying to use a closed AppSearchConfig instance."); 513 } 514 } 515 updateCachedValues(@onNull DeviceConfig.Properties properties)516 private void updateCachedValues(@NonNull DeviceConfig.Properties properties) { 517 for (String key : properties.getKeyset()) { 518 updateCachedValue(key, properties); 519 } 520 updateDerivedClasses(); 521 } 522 updateCachedValue(@onNull String key, @NonNull DeviceConfig.Properties properties)523 private void updateCachedValue(@NonNull String key, 524 @NonNull DeviceConfig.Properties properties) { 525 if (properties.getString(key, /*defaultValue=*/ null) == null) { 526 // Key is missing or value is just null. That is not expected if the key is 527 // defined in the configuration. 528 // 529 // We choose NOT to put the default value in the bundle. 530 // Instead, we let the getters handle what default value should be returned. 531 // 532 // Also we keep the old value in the bundle. So getters can still 533 // return last valid value. 534 return; 535 } 536 537 switch (key) { 538 case KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS: 539 synchronized (mLock) { 540 mBundleLocked.putLong(key, 541 properties.getLong(key, 542 DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS)); 543 } 544 break; 545 case KEY_SAMPLING_INTERVAL_DEFAULT: 546 case KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS: 547 case KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS: 548 case KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS: 549 case KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS: 550 case KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS: 551 case KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS: 552 synchronized (mLock) { 553 mBundleLocked.putInt(key, properties.getInt(key, DEFAULT_SAMPLING_INTERVAL)); 554 } 555 break; 556 case KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES: 557 synchronized (mLock) { 558 mBundleLocked.putInt( 559 key, 560 properties.getInt(key, DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES)); 561 } 562 break; 563 case KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT: 564 synchronized (mLock) { 565 mBundleLocked.putInt( 566 key, 567 properties.getInt(key, DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT)); 568 } 569 break; 570 case KEY_LIMIT_CONFIG_MAX_SUGGESTION_COUNT: 571 synchronized (mLock) { 572 mBundleLocked.putInt( 573 key, 574 properties.getInt(key, DEFAULT_LIMIT_CONFIG_MAX_SUGGESTION_COUNT)); 575 } 576 break; 577 case KEY_BYTES_OPTIMIZE_THRESHOLD: 578 synchronized (mLock) { 579 mBundleLocked.putInt(key, properties.getInt(key, 580 DEFAULT_BYTES_OPTIMIZE_THRESHOLD)); 581 } 582 break; 583 case KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS: 584 synchronized (mLock) { 585 mBundleLocked.putInt(key, properties.getInt(key, 586 DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS)); 587 } 588 break; 589 case KEY_DOC_COUNT_OPTIMIZE_THRESHOLD: 590 synchronized (mLock) { 591 mBundleLocked.putInt(key, properties.getInt(key, 592 DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD)); 593 } 594 break; 595 case KEY_MIN_TIME_OPTIMIZE_THRESHOLD_MILLIS: 596 synchronized (mLock) { 597 mBundleLocked.putInt(key, properties.getInt(key, 598 DEFAULT_MIN_TIME_OPTIMIZE_THRESHOLD_MILLIS)); 599 } 600 break; 601 case KEY_API_CALL_STATS_LIMIT: 602 synchronized (mLock) { 603 mBundleLocked.putInt(key, 604 properties.getInt(key, DEFAULT_API_CALL_STATS_LIMIT)); 605 } 606 break; 607 case KEY_DENYLIST: 608 String denylistString = properties.getString(key, /* defaultValue= */ ""); 609 Denylist denylist = 610 denylistString.isEmpty() ? Denylist.EMPTY_INSTANCE : Denylist.create( 611 denylistString); 612 synchronized (mLock) { 613 mDenylistLocked = denylist; 614 } 615 case KEY_RATE_LIMIT_ENABLED: 616 synchronized (mLock) { 617 mBundleLocked.putBoolean(key, properties.getBoolean(key, 618 DEFAULT_RATE_LIMIT_ENABLED)); 619 } 620 break; 621 case KEY_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY: 622 synchronized (mLock) { 623 mBundleLocked.putInt(key, properties.getInt(key, 624 DEFAULT_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY)); 625 } 626 break; 627 case KEY_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE: 628 synchronized (mLock) { 629 mBundleLocked.putFloat(key, properties.getFloat(key, 630 DEFAULT_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE)); 631 } 632 break; 633 case KEY_RATE_LIMIT_API_COSTS: 634 synchronized (mLock) { 635 mBundleLocked.putString(key, properties.getString(key, 636 DEFAULT_RATE_LIMIT_API_COSTS_STRING)); 637 } 638 break; 639 case KEY_ICING_MAX_TOKEN_LENGTH: 640 synchronized (mLock) { 641 mBundleLocked.putInt(key, properties.getInt(key, 642 IcingOptionsConfig.DEFAULT_MAX_TOKEN_LENGTH)); 643 } 644 break; 645 case KEY_ICING_INDEX_MERGE_SIZE: 646 synchronized (mLock) { 647 mBundleLocked.putInt(key, properties.getInt(key, 648 IcingOptionsConfig.DEFAULT_INDEX_MERGE_SIZE)); 649 } 650 break; 651 case KEY_ICING_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT: 652 synchronized (mLock) { 653 mBundleLocked.putBoolean(key, properties.getBoolean(key, 654 IcingOptionsConfig.DEFAULT_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT)); 655 } 656 break; 657 case KEY_ICING_OPTIMIZE_REBUILD_INDEX_THRESHOLD: 658 synchronized (mLock) { 659 mBundleLocked.putFloat(key, properties.getFloat(key, 660 IcingOptionsConfig.DEFAULT_OPTIMIZE_REBUILD_INDEX_THRESHOLD)); 661 } 662 break; 663 case KEY_ICING_COMPRESSION_LEVEL: 664 synchronized (mLock) { 665 mBundleLocked.putInt(key, properties.getInt(key, 666 IcingOptionsConfig.DEFAULT_COMPRESSION_LEVEL)); 667 } 668 break; 669 case KEY_ICING_USE_READ_ONLY_SEARCH: 670 synchronized (mLock) { 671 mBundleLocked.putBoolean(key, properties.getBoolean(key, 672 DEFAULT_ICING_CONFIG_USE_READ_ONLY_SEARCH)); 673 } 674 break; 675 case KEY_ICING_USE_PRE_MAPPING_WITH_FILE_BACKED_VECTOR: 676 synchronized (mLock) { 677 mBundleLocked.putBoolean(key, properties.getBoolean(key, 678 IcingOptionsConfig.DEFAULT_USE_PREMAPPING_WITH_FILE_BACKED_VECTOR)); 679 } 680 break; 681 case KEY_ICING_USE_PERSISTENT_HASHMAP: 682 synchronized (mLock) { 683 mBundleLocked.putBoolean(key, properties.getBoolean(key, 684 IcingOptionsConfig.DEFAULT_USE_PERSISTENT_HASH_MAP)); 685 } 686 break; 687 case KEY_ICING_MAX_PAGE_BYTES_LIMIT: 688 synchronized (mLock) { 689 mBundleLocked.putInt(key, properties.getInt(key, 690 IcingOptionsConfig.DEFAULT_MAX_PAGE_BYTES_LIMIT)); 691 } 692 break; 693 default: 694 break; 695 } 696 } 697 updateDerivedClasses()698 private void updateDerivedClasses() { 699 if (getCachedRateLimitEnabled()) { 700 synchronized (mLock) { 701 int taskQueueTotalCapacity = mBundleLocked.getInt( 702 KEY_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY, 703 DEFAULT_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY); 704 float taskQueuePerPackagePercentage = mBundleLocked.getFloat( 705 KEY_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE, 706 DEFAULT_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE); 707 String apiCostsString = mBundleLocked.getString(KEY_RATE_LIMIT_API_COSTS, 708 DEFAULT_RATE_LIMIT_API_COSTS_STRING); 709 mRateLimitConfigLocked = mRateLimitConfigLocked.rebuildIfNecessary( 710 taskQueueTotalCapacity, taskQueuePerPackagePercentage, apiCostsString); 711 } 712 } 713 } 714 } 715