• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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