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 ServiceAppSearchConfig} 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 FrameworkServiceAppSearchConfig implements ServiceAppSearchConfig { 47 private static volatile FrameworkServiceAppSearchConfig 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 public static final String KEY_ICING_INTEGER_INDEX_BUCKET_SPLIT_THRESHOLD = 100 "icing_integer_index_bucket_split_threshold"; 101 public static final String KEY_ICING_LITE_INDEX_SORT_AT_INDEXING = 102 "icing_lite_index_sort_at_indexing"; 103 public static final String KEY_ICING_LITE_INDEX_SORT_SIZE = "icing_lite_index_sort_size"; 104 public static final String KEY_SHOULD_RETRIEVE_PARENT_INFO = "should_retrieve_parent_info"; 105 public static final String KEY_USE_NEW_QUALIFIED_ID_JOIN_INDEX = 106 "use_new_qualified_id_join_index"; 107 public static final String KEY_BUILD_PROPERTY_EXISTENCE_METADATA_HITS = 108 "build_property_existence_metadata_hits"; 109 public static final String KEY_APP_FUNCTION_CALL_TIMEOUT_MILLIS = 110 "app_function_call_timeout_millis"; 111 public static final String KEY_FULLY_PERSIST_JOB_INTERVAL = "fully_persist_job_interval"; 112 113 /** 114 * This config does not need to be cached in FrameworkServiceAppSearchConfig as it is only 115 * accessed statically. AppSearch retrieves this directly from DeviceConfig when needed. 116 */ 117 public static final String KEY_USE_FIXED_EXECUTOR_SERVICE = "use_fixed_executor_service"; 118 119 // Array contains all the corresponding keys for the cached values. 120 private static final String[] KEYS_TO_ALL_CACHED_VALUES = { 121 KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, 122 KEY_SAMPLING_INTERVAL_DEFAULT, 123 KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, 124 KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, 125 KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS, 126 KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS, 127 KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS, 128 KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS, 129 KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES, 130 KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT, 131 KEY_LIMIT_CONFIG_MAX_SUGGESTION_COUNT, 132 KEY_BYTES_OPTIMIZE_THRESHOLD, 133 KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS, 134 KEY_DOC_COUNT_OPTIMIZE_THRESHOLD, 135 KEY_MIN_TIME_OPTIMIZE_THRESHOLD_MILLIS, 136 KEY_API_CALL_STATS_LIMIT, 137 KEY_DENYLIST, 138 KEY_RATE_LIMIT_ENABLED, 139 KEY_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY, 140 KEY_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE, 141 KEY_RATE_LIMIT_API_COSTS, 142 KEY_ICING_MAX_TOKEN_LENGTH, 143 KEY_ICING_INDEX_MERGE_SIZE, 144 KEY_ICING_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT, 145 KEY_ICING_OPTIMIZE_REBUILD_INDEX_THRESHOLD, 146 KEY_ICING_COMPRESSION_LEVEL, 147 KEY_ICING_USE_READ_ONLY_SEARCH, 148 KEY_ICING_USE_PRE_MAPPING_WITH_FILE_BACKED_VECTOR, 149 KEY_ICING_USE_PERSISTENT_HASHMAP, 150 KEY_ICING_MAX_PAGE_BYTES_LIMIT, 151 KEY_ICING_INTEGER_INDEX_BUCKET_SPLIT_THRESHOLD, 152 KEY_ICING_LITE_INDEX_SORT_AT_INDEXING, 153 KEY_ICING_LITE_INDEX_SORT_SIZE, 154 KEY_SHOULD_RETRIEVE_PARENT_INFO, 155 KEY_USE_NEW_QUALIFIED_ID_JOIN_INDEX, 156 KEY_BUILD_PROPERTY_EXISTENCE_METADATA_HITS, 157 KEY_APP_FUNCTION_CALL_TIMEOUT_MILLIS, 158 KEY_FULLY_PERSIST_JOB_INTERVAL 159 }; 160 161 // Lock needed for all the operations in this class. 162 private final Object mLock = new Object(); 163 164 /** 165 * Bundle to hold all the cached flag values corresponding to {@link 166 * FrameworkServiceAppSearchConfig#KEYS_TO_ALL_CACHED_VALUES}. 167 */ 168 @GuardedBy("mLock") 169 private final Bundle mBundleLocked = new Bundle(); 170 171 @GuardedBy("mLock") 172 private Denylist mDenylistLocked = Denylist.EMPTY_INSTANCE; 173 174 @GuardedBy("mLock") 175 private AppSearchRateLimitConfig mRateLimitConfigLocked = 176 AppSearchRateLimitConfig.create( 177 DEFAULT_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY, 178 DEFAULT_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE, 179 DEFAULT_RATE_LIMIT_API_COSTS_STRING); 180 181 @GuardedBy("mLock") 182 private boolean mIsClosedLocked = false; 183 184 /** Listener to update cached flag values from DeviceConfig. */ 185 private final OnPropertiesChangedListener mOnDeviceConfigChangedListener = 186 properties -> { 187 if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_APPSEARCH)) { 188 return; 189 } 190 191 updateCachedValues(properties); 192 }; 193 FrameworkServiceAppSearchConfig()194 private FrameworkServiceAppSearchConfig() {} 195 196 /** 197 * Creates an instance of {@link FrameworkServiceAppSearchConfig}. 198 * 199 * @param executor used to fetch and cache the flag values from DeviceConfig during creation or 200 * config change. 201 */ 202 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) 203 @NonNull create(@onNull Executor executor)204 public static FrameworkServiceAppSearchConfig create(@NonNull Executor executor) { 205 Objects.requireNonNull(executor); 206 FrameworkServiceAppSearchConfig configManager = new FrameworkServiceAppSearchConfig(); 207 configManager.initialize(executor); 208 return configManager; 209 } 210 211 /** 212 * Gets an instance of {@link FrameworkServiceAppSearchConfig} to be used. 213 * 214 * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the 215 * existing instance will be returned. 216 */ 217 @NonNull getInstance(@onNull Executor executor)218 public static FrameworkServiceAppSearchConfig getInstance(@NonNull Executor executor) { 219 Objects.requireNonNull(executor); 220 if (sConfig == null) { 221 synchronized (FrameworkServiceAppSearchConfig.class) { 222 if (sConfig == null) { 223 sConfig = create(executor); 224 } 225 } 226 } 227 return sConfig; 228 } 229 230 /** 231 * Returns whether or not to use a fixed executor service for AppSearch. This config is only 232 * queried statically and is therefore retrieved directly from DeviceConfig. 233 */ getUseFixedExecutorService()234 public static boolean getUseFixedExecutorService() { 235 return DeviceConfig.getBoolean( 236 DeviceConfig.NAMESPACE_APPSEARCH, 237 KEY_USE_FIXED_EXECUTOR_SERVICE, 238 DEFAULT_USE_FIXED_EXECUTOR_SERVICE); 239 } 240 241 /** 242 * Initializes the {@link FrameworkServiceAppSearchConfig} 243 * 244 * <p>It fetches the custom properties from DeviceConfig if available. 245 * 246 * @param executor listener would be run on to handle P/H flag change. 247 */ initialize(@onNull Executor executor)248 private void initialize(@NonNull Executor executor) { 249 executor.execute( 250 () -> { 251 // Attach the callback to get updates on those properties. 252 DeviceConfig.addOnPropertiesChangedListener( 253 DeviceConfig.NAMESPACE_APPSEARCH, 254 executor, 255 mOnDeviceConfigChangedListener); 256 257 DeviceConfig.Properties properties = 258 DeviceConfig.getProperties( 259 DeviceConfig.NAMESPACE_APPSEARCH, KEYS_TO_ALL_CACHED_VALUES); 260 updateCachedValues(properties); 261 }); 262 } 263 264 // TODO(b/173532925) check this will be called. If we have a singleton instance for this 265 // class, probably we don't need it. 266 @Override close()267 public void close() { 268 synchronized (mLock) { 269 if (mIsClosedLocked) { 270 return; 271 } 272 273 DeviceConfig.removeOnPropertiesChangedListener(mOnDeviceConfigChangedListener); 274 mIsClosedLocked = true; 275 } 276 } 277 278 @Override getCachedMinTimeIntervalBetweenSamplesMillis()279 public long getCachedMinTimeIntervalBetweenSamplesMillis() { 280 synchronized (mLock) { 281 throwIfClosedLocked(); 282 return mBundleLocked.getLong( 283 KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, 284 DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS); 285 } 286 } 287 288 @Override getCachedSamplingIntervalDefault()289 public int getCachedSamplingIntervalDefault() { 290 synchronized (mLock) { 291 throwIfClosedLocked(); 292 return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_DEFAULT, DEFAULT_SAMPLING_INTERVAL); 293 } 294 } 295 296 @Override getCachedSamplingIntervalForBatchCallStats()297 public int getCachedSamplingIntervalForBatchCallStats() { 298 synchronized (mLock) { 299 throwIfClosedLocked(); 300 return mBundleLocked.getInt( 301 KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, getCachedSamplingIntervalDefault()); 302 } 303 } 304 305 @Override getCachedSamplingIntervalForPutDocumentStats()306 public int getCachedSamplingIntervalForPutDocumentStats() { 307 synchronized (mLock) { 308 throwIfClosedLocked(); 309 return mBundleLocked.getInt( 310 KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, 311 getCachedSamplingIntervalDefault()); 312 } 313 } 314 315 @Override getCachedSamplingIntervalForInitializeStats()316 public int getCachedSamplingIntervalForInitializeStats() { 317 synchronized (mLock) { 318 throwIfClosedLocked(); 319 return mBundleLocked.getInt( 320 KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS, getCachedSamplingIntervalDefault()); 321 } 322 } 323 324 @Override getCachedSamplingIntervalForSearchStats()325 public int getCachedSamplingIntervalForSearchStats() { 326 synchronized (mLock) { 327 throwIfClosedLocked(); 328 return mBundleLocked.getInt( 329 KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS, getCachedSamplingIntervalDefault()); 330 } 331 } 332 333 @Override getCachedSamplingIntervalForGlobalSearchStats()334 public int getCachedSamplingIntervalForGlobalSearchStats() { 335 synchronized (mLock) { 336 throwIfClosedLocked(); 337 return mBundleLocked.getInt( 338 KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS, 339 getCachedSamplingIntervalDefault()); 340 } 341 } 342 343 @Override getCachedSamplingIntervalForOptimizeStats()344 public int getCachedSamplingIntervalForOptimizeStats() { 345 synchronized (mLock) { 346 throwIfClosedLocked(); 347 return mBundleLocked.getInt( 348 KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS, getCachedSamplingIntervalDefault()); 349 } 350 } 351 352 @Override getMaxDocumentSizeBytes()353 public int getMaxDocumentSizeBytes() { 354 synchronized (mLock) { 355 throwIfClosedLocked(); 356 return mBundleLocked.getInt( 357 KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES, 358 DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES); 359 } 360 } 361 362 @Override getMaxDocumentCount()363 public int getMaxDocumentCount() { 364 synchronized (mLock) { 365 throwIfClosedLocked(); 366 return mBundleLocked.getInt( 367 KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT, DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT); 368 } 369 } 370 371 @Override getMaxSuggestionCount()372 public int getMaxSuggestionCount() { 373 synchronized (mLock) { 374 throwIfClosedLocked(); 375 return mBundleLocked.getInt( 376 KEY_LIMIT_CONFIG_MAX_SUGGESTION_COUNT, 377 DEFAULT_LIMIT_CONFIG_MAX_SUGGESTION_COUNT); 378 } 379 } 380 381 @Override getCachedBytesOptimizeThreshold()382 public int getCachedBytesOptimizeThreshold() { 383 synchronized (mLock) { 384 throwIfClosedLocked(); 385 return mBundleLocked.getInt( 386 KEY_BYTES_OPTIMIZE_THRESHOLD, DEFAULT_BYTES_OPTIMIZE_THRESHOLD); 387 } 388 } 389 390 @Override getCachedTimeOptimizeThresholdMs()391 public int getCachedTimeOptimizeThresholdMs() { 392 synchronized (mLock) { 393 throwIfClosedLocked(); 394 return mBundleLocked.getInt( 395 KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS, DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS); 396 } 397 } 398 399 @Override getCachedDocCountOptimizeThreshold()400 public int getCachedDocCountOptimizeThreshold() { 401 synchronized (mLock) { 402 throwIfClosedLocked(); 403 return mBundleLocked.getInt( 404 KEY_DOC_COUNT_OPTIMIZE_THRESHOLD, DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD); 405 } 406 } 407 408 @Override getCachedMinTimeOptimizeThresholdMs()409 public int getCachedMinTimeOptimizeThresholdMs() { 410 synchronized (mLock) { 411 throwIfClosedLocked(); 412 return mBundleLocked.getInt( 413 KEY_MIN_TIME_OPTIMIZE_THRESHOLD_MILLIS, 414 DEFAULT_MIN_TIME_OPTIMIZE_THRESHOLD_MILLIS); 415 } 416 } 417 418 @Override getCachedApiCallStatsLimit()419 public int getCachedApiCallStatsLimit() { 420 synchronized (mLock) { 421 throwIfClosedLocked(); 422 return mBundleLocked.getInt(KEY_API_CALL_STATS_LIMIT, DEFAULT_API_CALL_STATS_LIMIT); 423 } 424 } 425 426 @Override getCachedDenylist()427 public Denylist getCachedDenylist() { 428 synchronized (mLock) { 429 throwIfClosedLocked(); 430 return mDenylistLocked; 431 } 432 } 433 434 @Override getMaxTokenLength()435 public int getMaxTokenLength() { 436 synchronized (mLock) { 437 throwIfClosedLocked(); 438 return mBundleLocked.getInt( 439 KEY_ICING_MAX_TOKEN_LENGTH, IcingOptionsConfig.DEFAULT_MAX_TOKEN_LENGTH); 440 } 441 } 442 443 @Override getIndexMergeSize()444 public int getIndexMergeSize() { 445 synchronized (mLock) { 446 throwIfClosedLocked(); 447 return mBundleLocked.getInt( 448 KEY_ICING_INDEX_MERGE_SIZE, IcingOptionsConfig.DEFAULT_INDEX_MERGE_SIZE); 449 } 450 } 451 452 @Override getDocumentStoreNamespaceIdFingerprint()453 public boolean getDocumentStoreNamespaceIdFingerprint() { 454 synchronized (mLock) { 455 throwIfClosedLocked(); 456 return mBundleLocked.getBoolean( 457 KEY_ICING_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT, 458 IcingOptionsConfig.DEFAULT_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT); 459 } 460 } 461 462 @Override getOptimizeRebuildIndexThreshold()463 public float getOptimizeRebuildIndexThreshold() { 464 synchronized (mLock) { 465 throwIfClosedLocked(); 466 return mBundleLocked.getFloat( 467 KEY_ICING_OPTIMIZE_REBUILD_INDEX_THRESHOLD, 468 IcingOptionsConfig.DEFAULT_OPTIMIZE_REBUILD_INDEX_THRESHOLD); 469 } 470 } 471 472 @Override getCompressionLevel()473 public int getCompressionLevel() { 474 synchronized (mLock) { 475 throwIfClosedLocked(); 476 return mBundleLocked.getInt( 477 KEY_ICING_COMPRESSION_LEVEL, IcingOptionsConfig.DEFAULT_COMPRESSION_LEVEL); 478 } 479 } 480 481 @Override getAllowCircularSchemaDefinitions()482 public boolean getAllowCircularSchemaDefinitions() { 483 // TODO(b/282108040) add flag(default on) to cover this feature in case a bug is discovered. 484 synchronized (mLock) { 485 throwIfClosedLocked(); 486 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 487 } 488 } 489 490 @Override getUseReadOnlySearch()491 public boolean getUseReadOnlySearch() { 492 synchronized (mLock) { 493 throwIfClosedLocked(); 494 return mBundleLocked.getBoolean( 495 KEY_ICING_USE_READ_ONLY_SEARCH, DEFAULT_ICING_CONFIG_USE_READ_ONLY_SEARCH); 496 } 497 } 498 499 @Override getUsePreMappingWithFileBackedVector()500 public boolean getUsePreMappingWithFileBackedVector() { 501 synchronized (mLock) { 502 throwIfClosedLocked(); 503 return mBundleLocked.getBoolean( 504 KEY_ICING_USE_PRE_MAPPING_WITH_FILE_BACKED_VECTOR, 505 IcingOptionsConfig.DEFAULT_USE_PREMAPPING_WITH_FILE_BACKED_VECTOR); 506 } 507 } 508 509 @Override getUsePersistentHashMap()510 public boolean getUsePersistentHashMap() { 511 synchronized (mLock) { 512 throwIfClosedLocked(); 513 return mBundleLocked.getBoolean( 514 KEY_ICING_USE_PERSISTENT_HASHMAP, 515 IcingOptionsConfig.DEFAULT_USE_PERSISTENT_HASH_MAP); 516 } 517 } 518 519 @Override getMaxPageBytesLimit()520 public int getMaxPageBytesLimit() { 521 synchronized (mLock) { 522 throwIfClosedLocked(); 523 return mBundleLocked.getInt( 524 KEY_ICING_MAX_PAGE_BYTES_LIMIT, 525 IcingOptionsConfig.DEFAULT_MAX_PAGE_BYTES_LIMIT); 526 } 527 } 528 529 @Override getCachedRateLimitEnabled()530 public boolean getCachedRateLimitEnabled() { 531 synchronized (mLock) { 532 throwIfClosedLocked(); 533 return mBundleLocked.getBoolean(KEY_RATE_LIMIT_ENABLED, DEFAULT_RATE_LIMIT_ENABLED); 534 } 535 } 536 537 @Override getCachedRateLimitConfig()538 public AppSearchRateLimitConfig getCachedRateLimitConfig() { 539 synchronized (mLock) { 540 throwIfClosedLocked(); 541 return mRateLimitConfigLocked; 542 } 543 } 544 545 @Override getAppFunctionCallTimeoutMillis()546 public long getAppFunctionCallTimeoutMillis() { 547 synchronized (mLock) { 548 throwIfClosedLocked(); 549 return mBundleLocked.getLong( 550 KEY_APP_FUNCTION_CALL_TIMEOUT_MILLIS, DEFAULT_APP_FUNCTION_CALL_TIMEOUT_MILLIS); 551 } 552 } 553 554 @Override getCachedFullyPersistJobIntervalMillis()555 public long getCachedFullyPersistJobIntervalMillis() { 556 synchronized (mLock) { 557 throwIfClosedLocked(); 558 return mBundleLocked.getLong( 559 KEY_FULLY_PERSIST_JOB_INTERVAL, DEFAULT_FULLY_PERSIST_JOB_INTERVAL); 560 } 561 } 562 563 @Override getIntegerIndexBucketSplitThreshold()564 public int getIntegerIndexBucketSplitThreshold() { 565 synchronized (mLock) { 566 throwIfClosedLocked(); 567 return mBundleLocked.getInt( 568 KEY_ICING_INTEGER_INDEX_BUCKET_SPLIT_THRESHOLD, 569 DEFAULT_INTEGER_INDEX_BUCKET_SPLIT_THRESHOLD); 570 } 571 } 572 573 @Override getLiteIndexSortAtIndexing()574 public boolean getLiteIndexSortAtIndexing() { 575 synchronized (mLock) { 576 throwIfClosedLocked(); 577 return mBundleLocked.getBoolean( 578 KEY_ICING_LITE_INDEX_SORT_AT_INDEXING, DEFAULT_LITE_INDEX_SORT_AT_INDEXING); 579 } 580 } 581 582 @Override getLiteIndexSortSize()583 public int getLiteIndexSortSize() { 584 synchronized (mLock) { 585 throwIfClosedLocked(); 586 return mBundleLocked.getInt( 587 KEY_ICING_LITE_INDEX_SORT_SIZE, DEFAULT_LITE_INDEX_SORT_SIZE); 588 } 589 } 590 591 @Override getUseNewQualifiedIdJoinIndex()592 public boolean getUseNewQualifiedIdJoinIndex() { 593 synchronized (mLock) { 594 throwIfClosedLocked(); 595 return mBundleLocked.getBoolean( 596 KEY_USE_NEW_QUALIFIED_ID_JOIN_INDEX, DEFAULT_USE_NEW_QUALIFIED_ID_JOIN_INDEX); 597 } 598 } 599 600 @Override getBuildPropertyExistenceMetadataHits()601 public boolean getBuildPropertyExistenceMetadataHits() { 602 // This option is always true in Framework due to trunk stable frozen flags. 603 return true; 604 } 605 606 @Override shouldStoreParentInfoAsSyntheticProperty()607 public boolean shouldStoreParentInfoAsSyntheticProperty() { 608 // This option is always true in Framework. 609 return true; 610 } 611 612 @Override shouldRetrieveParentInfo()613 public boolean shouldRetrieveParentInfo() { 614 synchronized (mLock) { 615 throwIfClosedLocked(); 616 return mBundleLocked.getBoolean( 617 KEY_SHOULD_RETRIEVE_PARENT_INFO, DEFAULT_SHOULD_RETRIEVE_PARENT_INFO); 618 } 619 } 620 621 @GuardedBy("mLock") throwIfClosedLocked()622 private void throwIfClosedLocked() { 623 if (mIsClosedLocked) { 624 throw new IllegalStateException("Trying to use a closed AppSearchConfig instance."); 625 } 626 } 627 updateCachedValues(@onNull DeviceConfig.Properties properties)628 private void updateCachedValues(@NonNull DeviceConfig.Properties properties) { 629 for (String key : properties.getKeyset()) { 630 updateCachedValue(key, properties); 631 } 632 updateDerivedClasses(); 633 } 634 updateCachedValue( @onNull String key, @NonNull DeviceConfig.Properties properties)635 private void updateCachedValue( 636 @NonNull String key, @NonNull DeviceConfig.Properties properties) { 637 if (properties.getString(key, /* defaultValue= */ null) == null) { 638 // Key is missing or value is just null. That is not expected if the key is 639 // defined in the configuration. 640 // 641 // We choose NOT to put the default value in the bundle. 642 // Instead, we let the getters handle what default value should be returned. 643 // 644 // Also we keep the old value in the bundle. So getters can still 645 // return last valid value. 646 return; 647 } 648 649 switch (key) { 650 case KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS: 651 synchronized (mLock) { 652 mBundleLocked.putLong( 653 key, 654 properties.getLong( 655 key, DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS)); 656 } 657 break; 658 case KEY_SAMPLING_INTERVAL_DEFAULT: 659 case KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS: 660 case KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS: 661 case KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS: 662 case KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS: 663 case KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS: 664 case KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS: 665 synchronized (mLock) { 666 mBundleLocked.putInt(key, properties.getInt(key, DEFAULT_SAMPLING_INTERVAL)); 667 } 668 break; 669 case KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES: 670 synchronized (mLock) { 671 mBundleLocked.putInt( 672 key, 673 properties.getInt(key, DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES)); 674 } 675 break; 676 case KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT: 677 synchronized (mLock) { 678 mBundleLocked.putInt( 679 key, properties.getInt(key, DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT)); 680 } 681 break; 682 case KEY_LIMIT_CONFIG_MAX_SUGGESTION_COUNT: 683 synchronized (mLock) { 684 mBundleLocked.putInt( 685 key, properties.getInt(key, DEFAULT_LIMIT_CONFIG_MAX_SUGGESTION_COUNT)); 686 } 687 break; 688 case KEY_BYTES_OPTIMIZE_THRESHOLD: 689 synchronized (mLock) { 690 mBundleLocked.putInt( 691 key, properties.getInt(key, DEFAULT_BYTES_OPTIMIZE_THRESHOLD)); 692 } 693 break; 694 case KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS: 695 synchronized (mLock) { 696 mBundleLocked.putInt( 697 key, properties.getInt(key, DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS)); 698 } 699 break; 700 case KEY_DOC_COUNT_OPTIMIZE_THRESHOLD: 701 synchronized (mLock) { 702 mBundleLocked.putInt( 703 key, properties.getInt(key, DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD)); 704 } 705 break; 706 case KEY_MIN_TIME_OPTIMIZE_THRESHOLD_MILLIS: 707 synchronized (mLock) { 708 mBundleLocked.putInt( 709 key, 710 properties.getInt(key, DEFAULT_MIN_TIME_OPTIMIZE_THRESHOLD_MILLIS)); 711 } 712 break; 713 case KEY_API_CALL_STATS_LIMIT: 714 synchronized (mLock) { 715 mBundleLocked.putInt(key, properties.getInt(key, DEFAULT_API_CALL_STATS_LIMIT)); 716 } 717 break; 718 case KEY_DENYLIST: 719 String denylistString = properties.getString(key, /* defaultValue= */ ""); 720 Denylist denylist = 721 denylistString.isEmpty() 722 ? Denylist.EMPTY_INSTANCE 723 : Denylist.create(denylistString); 724 synchronized (mLock) { 725 mDenylistLocked = denylist; 726 } 727 break; 728 case KEY_RATE_LIMIT_ENABLED: 729 synchronized (mLock) { 730 mBundleLocked.putBoolean( 731 key, properties.getBoolean(key, DEFAULT_RATE_LIMIT_ENABLED)); 732 } 733 break; 734 case KEY_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY: 735 synchronized (mLock) { 736 mBundleLocked.putInt( 737 key, 738 properties.getInt(key, DEFAULT_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY)); 739 } 740 break; 741 case KEY_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE: 742 synchronized (mLock) { 743 mBundleLocked.putFloat( 744 key, 745 properties.getFloat( 746 key, 747 DEFAULT_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE)); 748 } 749 break; 750 case KEY_RATE_LIMIT_API_COSTS: 751 synchronized (mLock) { 752 mBundleLocked.putString( 753 key, properties.getString(key, DEFAULT_RATE_LIMIT_API_COSTS_STRING)); 754 } 755 break; 756 case KEY_ICING_MAX_TOKEN_LENGTH: 757 synchronized (mLock) { 758 mBundleLocked.putInt( 759 key, 760 properties.getInt(key, IcingOptionsConfig.DEFAULT_MAX_TOKEN_LENGTH)); 761 } 762 break; 763 case KEY_ICING_INDEX_MERGE_SIZE: 764 synchronized (mLock) { 765 mBundleLocked.putInt( 766 key, 767 properties.getInt(key, IcingOptionsConfig.DEFAULT_INDEX_MERGE_SIZE)); 768 } 769 break; 770 case KEY_ICING_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT: 771 synchronized (mLock) { 772 mBundleLocked.putBoolean( 773 key, 774 properties.getBoolean( 775 key, 776 IcingOptionsConfig 777 .DEFAULT_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT)); 778 } 779 break; 780 case KEY_ICING_OPTIMIZE_REBUILD_INDEX_THRESHOLD: 781 synchronized (mLock) { 782 mBundleLocked.putFloat( 783 key, 784 properties.getFloat( 785 key, 786 IcingOptionsConfig.DEFAULT_OPTIMIZE_REBUILD_INDEX_THRESHOLD)); 787 } 788 break; 789 case KEY_ICING_COMPRESSION_LEVEL: 790 synchronized (mLock) { 791 mBundleLocked.putInt( 792 key, 793 properties.getInt(key, IcingOptionsConfig.DEFAULT_COMPRESSION_LEVEL)); 794 } 795 break; 796 case KEY_ICING_USE_READ_ONLY_SEARCH: 797 synchronized (mLock) { 798 mBundleLocked.putBoolean( 799 key, 800 properties.getBoolean(key, DEFAULT_ICING_CONFIG_USE_READ_ONLY_SEARCH)); 801 } 802 break; 803 case KEY_ICING_USE_PRE_MAPPING_WITH_FILE_BACKED_VECTOR: 804 synchronized (mLock) { 805 mBundleLocked.putBoolean( 806 key, 807 properties.getBoolean( 808 key, 809 IcingOptionsConfig 810 .DEFAULT_USE_PREMAPPING_WITH_FILE_BACKED_VECTOR)); 811 } 812 break; 813 case KEY_ICING_USE_PERSISTENT_HASHMAP: 814 synchronized (mLock) { 815 mBundleLocked.putBoolean( 816 key, 817 properties.getBoolean( 818 key, IcingOptionsConfig.DEFAULT_USE_PERSISTENT_HASH_MAP)); 819 } 820 break; 821 case KEY_ICING_MAX_PAGE_BYTES_LIMIT: 822 synchronized (mLock) { 823 mBundleLocked.putInt( 824 key, 825 properties.getInt( 826 key, IcingOptionsConfig.DEFAULT_MAX_PAGE_BYTES_LIMIT)); 827 } 828 break; 829 case KEY_ICING_INTEGER_INDEX_BUCKET_SPLIT_THRESHOLD: 830 synchronized (mLock) { 831 mBundleLocked.putInt( 832 key, 833 properties.getInt( 834 key, 835 IcingOptionsConfig 836 .DEFAULT_INTEGER_INDEX_BUCKET_SPLIT_THRESHOLD)); 837 } 838 break; 839 case KEY_ICING_LITE_INDEX_SORT_AT_INDEXING: 840 synchronized (mLock) { 841 mBundleLocked.putBoolean( 842 key, 843 properties.getBoolean( 844 key, IcingOptionsConfig.DEFAULT_LITE_INDEX_SORT_AT_INDEXING)); 845 } 846 break; 847 case KEY_ICING_LITE_INDEX_SORT_SIZE: 848 synchronized (mLock) { 849 mBundleLocked.putInt( 850 key, 851 properties.getInt( 852 key, IcingOptionsConfig.DEFAULT_LITE_INDEX_SORT_SIZE)); 853 } 854 break; 855 case KEY_SHOULD_RETRIEVE_PARENT_INFO: 856 synchronized (mLock) { 857 mBundleLocked.putBoolean( 858 key, properties.getBoolean(key, DEFAULT_SHOULD_RETRIEVE_PARENT_INFO)); 859 } 860 break; 861 case KEY_USE_NEW_QUALIFIED_ID_JOIN_INDEX: 862 synchronized (mLock) { 863 mBundleLocked.putBoolean( 864 key, 865 properties.getBoolean(key, DEFAULT_USE_NEW_QUALIFIED_ID_JOIN_INDEX)); 866 } 867 break; 868 case KEY_APP_FUNCTION_CALL_TIMEOUT_MILLIS: 869 synchronized (mLock) { 870 mBundleLocked.putLong( 871 key, properties.getLong(key, DEFAULT_APP_FUNCTION_CALL_TIMEOUT_MILLIS)); 872 } 873 break; 874 case KEY_FULLY_PERSIST_JOB_INTERVAL: 875 synchronized (mLock) { 876 mBundleLocked.putLong( 877 key, properties.getLong(key, DEFAULT_FULLY_PERSIST_JOB_INTERVAL)); 878 } 879 break; 880 case KEY_BUILD_PROPERTY_EXISTENCE_METADATA_HITS: 881 // TODO(b/309826655) Set this value properly in main branch 882 // fall throw to default since we never turn this feature on in udc-mainline-prod 883 default: 884 break; 885 } 886 } 887 updateDerivedClasses()888 private void updateDerivedClasses() { 889 if (getCachedRateLimitEnabled()) { 890 synchronized (mLock) { 891 int taskQueueTotalCapacity = 892 mBundleLocked.getInt( 893 KEY_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY, 894 DEFAULT_RATE_LIMIT_TASK_QUEUE_TOTAL_CAPACITY); 895 float taskQueuePerPackagePercentage = 896 mBundleLocked.getFloat( 897 KEY_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE, 898 DEFAULT_RATE_LIMIT_TASK_QUEUE_PER_PACKAGE_CAPACITY_PERCENTAGE); 899 String apiCostsString = 900 mBundleLocked.getString( 901 KEY_RATE_LIMIT_API_COSTS, DEFAULT_RATE_LIMIT_API_COSTS_STRING); 902 mRateLimitConfigLocked = 903 mRateLimitConfigLocked.rebuildIfNecessary( 904 taskQueueTotalCapacity, 905 taskQueuePerPackagePercentage, 906 apiCostsString); 907 } 908 } 909 } 910 } 911