1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.textclassifier.common; 18 19 import static java.util.concurrent.TimeUnit.HOURS; 20 21 import android.content.Context; 22 import android.content.pm.PackageManager; 23 import android.provider.DeviceConfig; 24 import android.provider.DeviceConfig.Properties; 25 import android.text.TextUtils; 26 import android.view.textclassifier.ConversationAction; 27 import android.view.textclassifier.TextClassifier; 28 import androidx.annotation.NonNull; 29 import com.android.textclassifier.utils.IndentingPrintWriter; 30 import com.google.common.annotations.VisibleForTesting; 31 import com.google.common.base.Splitter; 32 import com.google.common.collect.ImmutableList; 33 import com.google.common.collect.ImmutableMap; 34 import java.util.Arrays; 35 import java.util.Collections; 36 import java.util.List; 37 import javax.annotation.Nullable; 38 39 /** 40 * TextClassifier specific settings. 41 * 42 * <p>Currently, this class does not guarantee co-diverted flags are updated atomically. 43 * 44 * <p>Example of setting the values for testing. 45 * 46 * <pre> 47 * adb shell cmd device_config put textclassifier system_textclassifier_enabled true 48 * </pre> 49 * 50 * @see android.provider.DeviceConfig#NAMESPACE_TEXTCLASSIFIER 51 */ 52 public final class TextClassifierSettings { 53 private static final String TAG = "TextClassifierSettings"; 54 public static final String NAMESPACE = DeviceConfig.NAMESPACE_TEXTCLASSIFIER; 55 56 private static final String DELIMITER = ":"; 57 58 /** Whether the user language profile feature is enabled. */ 59 private static final String USER_LANGUAGE_PROFILE_ENABLED = "user_language_profile_enabled"; 60 61 /** Max length of text that suggestSelection can accept. */ 62 @VisibleForTesting 63 static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH = "suggest_selection_max_range_length"; 64 65 /** Max length of text that classifyText can accept. */ 66 private static final String CLASSIFY_TEXT_MAX_RANGE_LENGTH = "classify_text_max_range_length"; 67 68 /** Max length of text that generateLinks can accept. */ 69 private static final String GENERATE_LINKS_MAX_TEXT_LENGTH = "generate_links_max_text_length"; 70 71 /** Sampling rate for generateLinks logging. */ 72 private static final String GENERATE_LINKS_LOG_SAMPLE_RATE = "generate_links_log_sample_rate"; 73 74 /** 75 * Extra count that is added to some languages, e.g. system languages, when deducing the frequent 76 * languages in {@link 77 * com.android.textclassifier.ulp.LanguageProfileAnalyzer#getFrequentLanguages(int)}. 78 */ 79 80 /** 81 * A colon(:) separated string that specifies the default entities types for generateLinks when 82 * hint is not given. 83 */ 84 @VisibleForTesting static final String ENTITY_LIST_DEFAULT = "entity_list_default"; 85 86 /** 87 * A colon(:) separated string that specifies the default entities types for generateLinks when 88 * the text is in a not editable UI widget. 89 */ 90 private static final String ENTITY_LIST_NOT_EDITABLE = "entity_list_not_editable"; 91 92 /** 93 * A colon(:) separated string that specifies the default entities types for generateLinks when 94 * the text is in an editable UI widget. 95 */ 96 private static final String ENTITY_LIST_EDITABLE = "entity_list_editable"; 97 98 /** 99 * A colon(:) separated string that specifies the default action types for 100 * suggestConversationActions when the suggestions are used in an app. 101 */ 102 private static final String IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT = 103 "in_app_conversation_action_types_default"; 104 105 /** 106 * A colon(:) separated string that specifies the default action types for 107 * suggestConversationActions when the suggestions are used in a notification. 108 */ 109 private static final String NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT = 110 "notification_conversation_action_types_default"; 111 112 /** Threshold to accept a suggested language from LangID model. */ 113 @VisibleForTesting static final String LANG_ID_THRESHOLD_OVERRIDE = "lang_id_threshold_override"; 114 115 /** Whether to enable {@link com.android.textclassifier.intent.TemplateIntentFactory}. */ 116 @VisibleForTesting 117 static final String TEMPLATE_INTENT_FACTORY_ENABLED = "template_intent_factory_enabled"; 118 119 /** Whether to enable "translate" action in classifyText. */ 120 private static final String TRANSLATE_IN_CLASSIFICATION_ENABLED = 121 "translate_in_classification_enabled"; 122 123 /** 124 * Whether to detect the languages of the text in request by using langId for the native model. 125 */ 126 private static final String DETECT_LANGUAGES_FROM_TEXT_ENABLED = 127 "detect_languages_from_text_enabled"; 128 129 /** Whether to use models downloaded by config updater. */ 130 private static final String CONFIG_UPDATER_MODEL_ENABLED = "config_updater_model_enabled"; 131 132 /** Whether to enable model downloading with ModelDownloadManager */ 133 @VisibleForTesting 134 public static final String MODEL_DOWNLOAD_MANAGER_ENABLED = "model_download_manager_enabled"; 135 136 /** Type of network to download model manifest. A String value of androidx.work.NetworkType. */ 137 private static final String MANIFEST_DOWNLOAD_REQUIRED_NETWORK_TYPE = 138 "manifest_download_required_network_type"; 139 140 /** Max attempts allowed for a single ModelDownloader downloading task. */ 141 @VisibleForTesting 142 static final String MODEL_DOWNLOAD_WORKER_MAX_ATTEMPTS = "model_download_worker_max_attempts"; 143 144 /** Max attempts allowed for a certain manifest url. */ 145 @VisibleForTesting 146 public static final String MANIFEST_DOWNLOAD_MAX_ATTEMPTS = "manifest_download_max_attempts"; 147 148 @VisibleForTesting 149 static final String MODEL_DOWNLOAD_BACKOFF_DELAY_IN_MILLIS = 150 "model_download_backoff_delay_in_millis"; 151 152 private static final String MANIFEST_DOWNLOAD_REQUIRES_CHARGING = 153 "manifest_download_requires_charging"; 154 private static final String MANIFEST_DOWNLOAD_REQUIRES_DEVICE_IDLE = 155 "manifest_download_requires_device_idle"; 156 157 /** Flag name for manifest url is dynamically formatted based on model type and model language. */ 158 @VisibleForTesting public static final String MANIFEST_URL_TEMPLATE = "manifest_url_%s_%s"; 159 160 @VisibleForTesting public static final String MODEL_URL_BLOCKLIST = "model_url_blocklist"; 161 @VisibleForTesting public static final String MODEL_URL_BLOCKLIST_SEPARATOR = ","; 162 163 /** Flags to control multi-language support settings. */ 164 @VisibleForTesting 165 public static final String MULTI_LANGUAGE_SUPPORT_ENABLED = "multi_language_support_enabled"; 166 167 @VisibleForTesting 168 public static final String MULTI_LANGUAGE_MODELS_LIMIT = "multi_language_models_limit"; 169 170 @VisibleForTesting 171 public static final String ENABLED_MODEL_TYPES_FOR_MULTI_LANGUAGE_SUPPORT = 172 "enabled_model_types_for_multi_language_support"; 173 174 @VisibleForTesting 175 public static final String MULTI_ANNOTATOR_CACHE_ENABLED = "multi_annotator_cache_enabled"; 176 177 private static final String MULTI_ANNOTATOR_CACHE_SIZE = "multi_annotator_cache_size"; 178 179 /** List of locale tags to override LocaleList for TextClassifier. Testing/debugging only. */ 180 @VisibleForTesting 181 public static final String TESTING_LOCALE_LIST_OVERRIDE = "testing_locale_list_override"; 182 183 /** Sampling rate for TextClassifier API logging. */ 184 static final String TEXTCLASSIFIER_API_LOG_SAMPLE_RATE = "textclassifier_api_log_sample_rate"; 185 186 /** The size of the cache of the mapping of session id to text classification context. */ 187 private static final String SESSION_ID_TO_CONTEXT_CACHE_SIZE = "session_id_to_context_cache_size"; 188 189 /** 190 * A colon(:) separated string that specifies the configuration to use when including surrounding 191 * context text in language detection queries. 192 * 193 * <p>Format= minimumTextSize<int>:penalizeRatio<float>:textScoreRatio<float> 194 * 195 * <p>e.g. 20:1.0:0.4 196 * 197 * <p>Accept all text lengths with minimumTextSize=0 198 * 199 * <p>Reject all text less than minimumTextSize with penalizeRatio=0 200 * 201 * @see {@code TextClassifierImpl#detectLanguages(String, int, int)} for reference. 202 */ 203 @VisibleForTesting static final String LANG_ID_CONTEXT_SETTINGS = "lang_id_context_settings"; 204 205 /** Default threshold to translate the language of the context the user selects */ 206 private static final String TRANSLATE_ACTION_THRESHOLD = "translate_action_threshold"; 207 208 // Sync this with ConversationAction.TYPE_ADD_CONTACT; 209 public static final String TYPE_ADD_CONTACT = "add_contact"; 210 // Sync this with ConversationAction.COPY; 211 public static final String TYPE_COPY = "copy"; 212 213 private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000; 214 private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000; 215 private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000; 216 private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100; 217 218 private static final ImmutableList<String> ENTITY_LIST_DEFAULT_VALUE = 219 ImmutableList.of( 220 TextClassifier.TYPE_ADDRESS, 221 TextClassifier.TYPE_EMAIL, 222 TextClassifier.TYPE_PHONE, 223 TextClassifier.TYPE_URL, 224 TextClassifier.TYPE_DATE, 225 TextClassifier.TYPE_DATE_TIME, 226 TextClassifier.TYPE_FLIGHT_NUMBER); 227 private static final ImmutableList<String> CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = 228 ImmutableList.of( 229 ConversationAction.TYPE_TEXT_REPLY, 230 ConversationAction.TYPE_CREATE_REMINDER, 231 ConversationAction.TYPE_CALL_PHONE, 232 ConversationAction.TYPE_OPEN_URL, 233 ConversationAction.TYPE_SEND_EMAIL, 234 ConversationAction.TYPE_SEND_SMS, 235 ConversationAction.TYPE_TRACK_FLIGHT, 236 ConversationAction.TYPE_VIEW_CALENDAR, 237 ConversationAction.TYPE_VIEW_MAP, 238 TYPE_ADD_CONTACT, 239 TYPE_COPY); 240 241 /** 242 * < 0 : Not set. Use value from LangId model. 0 - 1: Override value in LangId model. 243 * 244 * @see EntityConfidence 245 */ 246 private static final float LANG_ID_THRESHOLD_OVERRIDE_DEFAULT = -1f; 247 248 private static final float TRANSLATE_ACTION_THRESHOLD_DEFAULT = 0.5f; 249 250 private static final boolean USER_LANGUAGE_PROFILE_ENABLED_DEFAULT = true; 251 private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true; 252 private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true; 253 private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true; 254 private static final boolean CONFIG_UPDATER_MODEL_ENABLED_DEFAULT = true; 255 private static final boolean MODEL_DOWNLOAD_MANAGER_ENABLED_DEFAULT = false; 256 private static final String MANIFEST_DOWNLOAD_REQUIRED_NETWORK_TYPE_DEFAULT = "UNMETERED"; 257 private static final int MODEL_DOWNLOAD_WORKER_MAX_ATTEMPTS_DEFAULT = 5; 258 private static final int MANIFEST_DOWNLOAD_MAX_ATTEMPTS_DEFAULT = 3; 259 private static final long MODEL_DOWNLOAD_BACKOFF_DELAY_IN_MILLIS_DEFAULT = HOURS.toMillis(1); 260 private static final boolean MANIFEST_DOWNLOAD_REQUIRES_DEVICE_IDLE_DEFAULT = false; 261 private static final boolean MANIFEST_DOWNLOAD_REQUIRES_CHARGING_WEAR_DEFAULT = true; 262 private static final boolean MANIFEST_DOWNLOAD_REQUIRES_CHARGING_DEFAULT = false; 263 private static final boolean MULTI_LANGUAGE_SUPPORT_ENABLED_DEFAULT = false; 264 private static final int MULTI_LANGUAGE_MODELS_LIMIT_DEFAULT = 2; 265 private static final ImmutableList<String> 266 ENABLED_MODEL_TYPES_FOR_MULTI_LANGUAGE_SUPPORT_DEFAULT = 267 ImmutableList.of(ModelType.ANNOTATOR); 268 private static final boolean MULTI_ANNOTATOR_CACHE_ENABLED_DEFAULT = false; 269 private static final int MULTI_ANNOTATOR_CACHE_SIZE_DEFAULT = 2; 270 private static final String MANIFEST_URL_DEFAULT = ""; 271 private static final String TESTING_LOCALE_LIST_OVERRIDE_DEFAULT = ""; 272 private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[] {20f, 1.0f, 0.4f}; 273 274 /** 275 * Sampling rate for API logging. For example, 100 means there is a 0.01 chance that the API call 276 * is the logged. 277 */ 278 private static final int TEXTCLASSIFIER_API_LOG_SAMPLE_RATE_DEFAULT = 10; 279 280 private static final int SESSION_ID_TO_CONTEXT_CACHE_SIZE_DEFAULT = 10; 281 282 // TODO(licha): Consider removing this. We can use real device config for testing. 283 /** DeviceConfig interface to facilitate testing. */ 284 @VisibleForTesting 285 public interface IDeviceConfig { getProperties(@onNull String namespace, @NonNull String... names)286 default Properties getProperties(@NonNull String namespace, @NonNull String... names) { 287 return new Properties.Builder(namespace).build(); 288 } 289 getInt(@onNull String namespace, @NonNull String name, @NonNull int defaultValue)290 default int getInt(@NonNull String namespace, @NonNull String name, @NonNull int defaultValue) { 291 return defaultValue; 292 } 293 getLong( @onNull String namespace, @NonNull String name, @NonNull long defaultValue)294 default long getLong( 295 @NonNull String namespace, @NonNull String name, @NonNull long defaultValue) { 296 return defaultValue; 297 } 298 getFloat( @onNull String namespace, @NonNull String name, @NonNull float defaultValue)299 default float getFloat( 300 @NonNull String namespace, @NonNull String name, @NonNull float defaultValue) { 301 return defaultValue; 302 } 303 getString( @onNull String namespace, @NonNull String name, @Nullable String defaultValue)304 default String getString( 305 @NonNull String namespace, @NonNull String name, @Nullable String defaultValue) { 306 return defaultValue; 307 } 308 getBoolean( @onNull String namespace, @NonNull String name, boolean defaultValue)309 default boolean getBoolean( 310 @NonNull String namespace, @NonNull String name, boolean defaultValue) { 311 return defaultValue; 312 } 313 } 314 315 private static final IDeviceConfig DEFAULT_DEVICE_CONFIG = 316 new IDeviceConfig() { 317 @Override 318 public Properties getProperties(@NonNull String namespace, @NonNull String... names) { 319 return DeviceConfig.getProperties(namespace, names); 320 } 321 322 @Override 323 public int getInt( 324 @NonNull String namespace, @NonNull String name, @NonNull int defaultValue) { 325 return DeviceConfig.getInt(namespace, name, defaultValue); 326 } 327 328 @Override 329 public long getLong( 330 @NonNull String namespace, @NonNull String name, @NonNull long defaultValue) { 331 return DeviceConfig.getLong(namespace, name, defaultValue); 332 } 333 334 @Override 335 public float getFloat( 336 @NonNull String namespace, @NonNull String name, @NonNull float defaultValue) { 337 return DeviceConfig.getFloat(namespace, name, defaultValue); 338 } 339 340 @Override 341 public String getString( 342 @NonNull String namespace, @NonNull String name, @NonNull String defaultValue) { 343 return DeviceConfig.getString(namespace, name, defaultValue); 344 } 345 346 @Override 347 public boolean getBoolean( 348 @NonNull String namespace, @NonNull String name, @NonNull boolean defaultValue) { 349 return DeviceConfig.getBoolean(namespace, name, defaultValue); 350 } 351 }; 352 353 private final IDeviceConfig deviceConfig; 354 private final boolean isWear; 355 TextClassifierSettings(Context context)356 public TextClassifierSettings(Context context) { 357 this( 358 DEFAULT_DEVICE_CONFIG, 359 context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); 360 } 361 362 @VisibleForTesting TextClassifierSettings(IDeviceConfig deviceConfig, boolean isWear)363 public TextClassifierSettings(IDeviceConfig deviceConfig, boolean isWear) { 364 this.deviceConfig = deviceConfig; 365 this.isWear = isWear; 366 } 367 getSuggestSelectionMaxRangeLength()368 public int getSuggestSelectionMaxRangeLength() { 369 return deviceConfig.getInt( 370 NAMESPACE, SUGGEST_SELECTION_MAX_RANGE_LENGTH, SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT); 371 } 372 getClassifyTextMaxRangeLength()373 public int getClassifyTextMaxRangeLength() { 374 return deviceConfig.getInt( 375 NAMESPACE, CLASSIFY_TEXT_MAX_RANGE_LENGTH, CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT); 376 } 377 getGenerateLinksMaxTextLength()378 public int getGenerateLinksMaxTextLength() { 379 return deviceConfig.getInt( 380 NAMESPACE, GENERATE_LINKS_MAX_TEXT_LENGTH, GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT); 381 } 382 getGenerateLinksLogSampleRate()383 public int getGenerateLinksLogSampleRate() { 384 return deviceConfig.getInt( 385 NAMESPACE, GENERATE_LINKS_LOG_SAMPLE_RATE, GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT); 386 } 387 getEntityListDefault()388 public List<String> getEntityListDefault() { 389 return getDeviceConfigStringList(ENTITY_LIST_DEFAULT, ENTITY_LIST_DEFAULT_VALUE); 390 } 391 getEntityListNotEditable()392 public List<String> getEntityListNotEditable() { 393 return getDeviceConfigStringList(ENTITY_LIST_NOT_EDITABLE, ENTITY_LIST_DEFAULT_VALUE); 394 } 395 getEntityListEditable()396 public List<String> getEntityListEditable() { 397 return getDeviceConfigStringList(ENTITY_LIST_EDITABLE, ENTITY_LIST_DEFAULT_VALUE); 398 } 399 getInAppConversationActionTypes()400 public List<String> getInAppConversationActionTypes() { 401 return getDeviceConfigStringList( 402 IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT, CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES); 403 } 404 getNotificationConversationActionTypes()405 public List<String> getNotificationConversationActionTypes() { 406 return getDeviceConfigStringList( 407 NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT, CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES); 408 } 409 getLangIdThresholdOverride()410 public float getLangIdThresholdOverride() { 411 return deviceConfig.getFloat( 412 NAMESPACE, LANG_ID_THRESHOLD_OVERRIDE, LANG_ID_THRESHOLD_OVERRIDE_DEFAULT); 413 } 414 getTranslateActionThreshold()415 public float getTranslateActionThreshold() { 416 return deviceConfig.getFloat( 417 NAMESPACE, TRANSLATE_ACTION_THRESHOLD, TRANSLATE_ACTION_THRESHOLD_DEFAULT); 418 } 419 isUserLanguageProfileEnabled()420 public boolean isUserLanguageProfileEnabled() { 421 return deviceConfig.getBoolean( 422 NAMESPACE, USER_LANGUAGE_PROFILE_ENABLED, USER_LANGUAGE_PROFILE_ENABLED_DEFAULT); 423 } 424 isTemplateIntentFactoryEnabled()425 public boolean isTemplateIntentFactoryEnabled() { 426 return deviceConfig.getBoolean( 427 NAMESPACE, TEMPLATE_INTENT_FACTORY_ENABLED, TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT); 428 } 429 isTranslateInClassificationEnabled()430 public boolean isTranslateInClassificationEnabled() { 431 return deviceConfig.getBoolean( 432 NAMESPACE, 433 TRANSLATE_IN_CLASSIFICATION_ENABLED, 434 TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT); 435 } 436 isDetectLanguagesFromTextEnabled()437 public boolean isDetectLanguagesFromTextEnabled() { 438 return deviceConfig.getBoolean( 439 NAMESPACE, DETECT_LANGUAGES_FROM_TEXT_ENABLED, DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT); 440 } 441 getLangIdContextSettings()442 public float[] getLangIdContextSettings() { 443 return getDeviceConfigFloatArray(LANG_ID_CONTEXT_SETTINGS, LANG_ID_CONTEXT_SETTINGS_DEFAULT); 444 } 445 isConfigUpdaterModelEnabled()446 public boolean isConfigUpdaterModelEnabled() { 447 return deviceConfig.getBoolean( 448 NAMESPACE, CONFIG_UPDATER_MODEL_ENABLED, CONFIG_UPDATER_MODEL_ENABLED_DEFAULT); 449 } 450 isModelDownloadManagerEnabled()451 public boolean isModelDownloadManagerEnabled() { 452 return deviceConfig.getBoolean( 453 NAMESPACE, MODEL_DOWNLOAD_MANAGER_ENABLED, MODEL_DOWNLOAD_MANAGER_ENABLED_DEFAULT); 454 } 455 456 /** Returns a string which represents a androidx.work.NetworkType enum. */ getManifestDownloadRequiredNetworkType()457 public String getManifestDownloadRequiredNetworkType() { 458 return deviceConfig.getString( 459 NAMESPACE, 460 MANIFEST_DOWNLOAD_REQUIRED_NETWORK_TYPE, 461 MANIFEST_DOWNLOAD_REQUIRED_NETWORK_TYPE_DEFAULT); 462 } 463 getModelDownloadWorkerMaxAttempts()464 public int getModelDownloadWorkerMaxAttempts() { 465 return deviceConfig.getInt( 466 NAMESPACE, MODEL_DOWNLOAD_WORKER_MAX_ATTEMPTS, MODEL_DOWNLOAD_WORKER_MAX_ATTEMPTS_DEFAULT); 467 } 468 getManifestDownloadMaxAttempts()469 public int getManifestDownloadMaxAttempts() { 470 return deviceConfig.getInt( 471 NAMESPACE, MANIFEST_DOWNLOAD_MAX_ATTEMPTS, MANIFEST_DOWNLOAD_MAX_ATTEMPTS_DEFAULT); 472 } 473 getModelDownloadBackoffDelayInMillis()474 public long getModelDownloadBackoffDelayInMillis() { 475 return deviceConfig.getLong( 476 NAMESPACE, 477 MODEL_DOWNLOAD_BACKOFF_DELAY_IN_MILLIS, 478 MODEL_DOWNLOAD_BACKOFF_DELAY_IN_MILLIS_DEFAULT); 479 } 480 getManifestDownloadRequiresDeviceIdle()481 public boolean getManifestDownloadRequiresDeviceIdle() { 482 return deviceConfig.getBoolean( 483 NAMESPACE, 484 MANIFEST_DOWNLOAD_REQUIRES_DEVICE_IDLE, 485 MANIFEST_DOWNLOAD_REQUIRES_DEVICE_IDLE_DEFAULT); 486 } 487 getManifestDownloadRequiresCharging()488 public boolean getManifestDownloadRequiresCharging() { 489 return deviceConfig.getBoolean( 490 NAMESPACE, 491 MANIFEST_DOWNLOAD_REQUIRES_CHARGING, 492 isWear 493 ? MANIFEST_DOWNLOAD_REQUIRES_CHARGING_WEAR_DEFAULT 494 : MANIFEST_DOWNLOAD_REQUIRES_CHARGING_DEFAULT); 495 } 496 497 /* Gets a list of models urls that should not be used. Usually used for a quick rollback. */ getModelUrlBlocklist()498 public ImmutableList<String> getModelUrlBlocklist() { 499 return ImmutableList.copyOf( 500 Splitter.on(MODEL_URL_BLOCKLIST_SEPARATOR) 501 .split(deviceConfig.getString(NAMESPACE, MODEL_URL_BLOCKLIST, ""))); 502 } 503 isMultiLanguageSupportEnabled()504 public boolean isMultiLanguageSupportEnabled() { 505 return deviceConfig.getBoolean( 506 NAMESPACE, MULTI_LANGUAGE_SUPPORT_ENABLED, MULTI_LANGUAGE_SUPPORT_ENABLED_DEFAULT); 507 } 508 getMultiLanguageModelsLimit()509 public int getMultiLanguageModelsLimit() { 510 return deviceConfig.getInt( 511 NAMESPACE, MULTI_LANGUAGE_MODELS_LIMIT, MULTI_LANGUAGE_MODELS_LIMIT_DEFAULT); 512 } 513 getEnabledModelTypesForMultiLanguageSupport()514 public List<String> getEnabledModelTypesForMultiLanguageSupport() { 515 return getDeviceConfigStringList( 516 ENABLED_MODEL_TYPES_FOR_MULTI_LANGUAGE_SUPPORT, 517 ENABLED_MODEL_TYPES_FOR_MULTI_LANGUAGE_SUPPORT_DEFAULT); 518 } 519 getMultiAnnotatorCacheEnabled()520 public boolean getMultiAnnotatorCacheEnabled() { 521 return deviceConfig.getBoolean( 522 NAMESPACE, MULTI_ANNOTATOR_CACHE_ENABLED, MULTI_ANNOTATOR_CACHE_ENABLED_DEFAULT); 523 } 524 getMultiAnnotatorCacheSize()525 public int getMultiAnnotatorCacheSize() { 526 return deviceConfig.getInt( 527 NAMESPACE, MULTI_ANNOTATOR_CACHE_SIZE, MULTI_ANNOTATOR_CACHE_SIZE_DEFAULT); 528 } 529 530 /** 531 * Gets all language variants and associated manifest url configured for a specific ModelType. 532 * 533 * <p>For a specific language, there can be many variants: de-CH, de-LI, zh-Hans, zh-Hant. There 534 * is no easy way to hardcode the list in client. Therefore, we parse all configured flag's name 535 * in DeviceConfig, and let the client to choose the best variant to download. 536 * 537 * <p>If one flag's value is empty, it will be ignored. 538 * 539 * @param modelType the type of model for the target url 540 * @return <localeTag, flagValue> map. 541 */ getLanguageTagAndManifestUrlMap( @odelType.ModelTypeDef String modelType)542 public ImmutableMap<String, String> getLanguageTagAndManifestUrlMap( 543 @ModelType.ModelTypeDef String modelType) { 544 String urlFlagBaseName = String.format(MANIFEST_URL_TEMPLATE, modelType, /* language */ ""); 545 Properties properties = deviceConfig.getProperties(NAMESPACE); 546 ImmutableMap.Builder<String, String> variantsMapBuilder = ImmutableMap.builder(); 547 for (String name : properties.getKeyset()) { 548 if (!name.startsWith(urlFlagBaseName)) { 549 continue; 550 } 551 String value = properties.getString(name, /* defaultValue= */ null); 552 if (!TextUtils.isEmpty(value)) { 553 String modelLanguageTag = name.substring(urlFlagBaseName.length()); 554 String urlFlagName = String.format(MANIFEST_URL_TEMPLATE, modelType, modelLanguageTag); 555 String urlFlagValue = deviceConfig.getString(NAMESPACE, urlFlagName, MANIFEST_URL_DEFAULT); 556 variantsMapBuilder.put(modelLanguageTag, urlFlagValue); 557 } 558 } 559 return variantsMapBuilder.buildOrThrow(); 560 } 561 getTestingLocaleListOverride()562 public String getTestingLocaleListOverride() { 563 return deviceConfig.getString( 564 NAMESPACE, TESTING_LOCALE_LIST_OVERRIDE, TESTING_LOCALE_LIST_OVERRIDE_DEFAULT); 565 } 566 getTextClassifierApiLogSampleRate()567 public int getTextClassifierApiLogSampleRate() { 568 return deviceConfig.getInt( 569 NAMESPACE, TEXTCLASSIFIER_API_LOG_SAMPLE_RATE, TEXTCLASSIFIER_API_LOG_SAMPLE_RATE_DEFAULT); 570 } 571 getSessionIdToContextCacheSize()572 public int getSessionIdToContextCacheSize() { 573 return deviceConfig.getInt( 574 NAMESPACE, SESSION_ID_TO_CONTEXT_CACHE_SIZE, SESSION_ID_TO_CONTEXT_CACHE_SIZE_DEFAULT); 575 } 576 dump(IndentingPrintWriter pw)577 public void dump(IndentingPrintWriter pw) { 578 pw.println("TextClassifierSettings:"); 579 pw.increaseIndent(); 580 pw.printPair(CLASSIFY_TEXT_MAX_RANGE_LENGTH, getClassifyTextMaxRangeLength()); 581 pw.printPair(DETECT_LANGUAGES_FROM_TEXT_ENABLED, isDetectLanguagesFromTextEnabled()); 582 pw.printPair(ENTITY_LIST_DEFAULT, getEntityListDefault()); 583 pw.printPair(ENTITY_LIST_EDITABLE, getEntityListEditable()); 584 pw.printPair(ENTITY_LIST_NOT_EDITABLE, getEntityListNotEditable()); 585 pw.printPair(GENERATE_LINKS_LOG_SAMPLE_RATE, getGenerateLinksLogSampleRate()); 586 pw.printPair(GENERATE_LINKS_MAX_TEXT_LENGTH, getGenerateLinksMaxTextLength()); 587 pw.printPair(IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT, getInAppConversationActionTypes()); 588 pw.printPair(LANG_ID_CONTEXT_SETTINGS, Arrays.toString(getLangIdContextSettings())); 589 pw.printPair(LANG_ID_THRESHOLD_OVERRIDE, getLangIdThresholdOverride()); 590 pw.printPair(TRANSLATE_ACTION_THRESHOLD, getTranslateActionThreshold()); 591 pw.printPair( 592 NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT, getNotificationConversationActionTypes()); 593 pw.printPair(SUGGEST_SELECTION_MAX_RANGE_LENGTH, getSuggestSelectionMaxRangeLength()); 594 pw.printPair(USER_LANGUAGE_PROFILE_ENABLED, isUserLanguageProfileEnabled()); 595 pw.printPair(TEMPLATE_INTENT_FACTORY_ENABLED, isTemplateIntentFactoryEnabled()); 596 pw.printPair(TRANSLATE_IN_CLASSIFICATION_ENABLED, isTranslateInClassificationEnabled()); 597 pw.printPair(CONFIG_UPDATER_MODEL_ENABLED, isConfigUpdaterModelEnabled()); 598 pw.printPair(MODEL_DOWNLOAD_MANAGER_ENABLED, isModelDownloadManagerEnabled()); 599 pw.printPair(MULTI_LANGUAGE_SUPPORT_ENABLED, isMultiLanguageSupportEnabled()); 600 pw.printPair(MULTI_LANGUAGE_MODELS_LIMIT, getMultiLanguageModelsLimit()); 601 pw.printPair( 602 ENABLED_MODEL_TYPES_FOR_MULTI_LANGUAGE_SUPPORT, 603 getEnabledModelTypesForMultiLanguageSupport()); 604 pw.printPair(MULTI_ANNOTATOR_CACHE_ENABLED, getMultiAnnotatorCacheEnabled()); 605 pw.printPair(MULTI_ANNOTATOR_CACHE_SIZE, getMultiAnnotatorCacheSize()); 606 pw.printPair(MANIFEST_DOWNLOAD_REQUIRED_NETWORK_TYPE, getManifestDownloadRequiredNetworkType()); 607 pw.printPair(MODEL_DOWNLOAD_WORKER_MAX_ATTEMPTS, getModelDownloadWorkerMaxAttempts()); 608 pw.printPair(MANIFEST_DOWNLOAD_MAX_ATTEMPTS, getManifestDownloadMaxAttempts()); 609 pw.printPair(MANIFEST_DOWNLOAD_REQUIRES_CHARGING, getManifestDownloadRequiresCharging()); 610 pw.printPair(MANIFEST_DOWNLOAD_REQUIRES_DEVICE_IDLE, getManifestDownloadRequiresDeviceIdle()); 611 pw.printPair(TESTING_LOCALE_LIST_OVERRIDE, getTestingLocaleListOverride()); 612 pw.decreaseIndent(); 613 pw.printPair(TEXTCLASSIFIER_API_LOG_SAMPLE_RATE, getTextClassifierApiLogSampleRate()); 614 pw.printPair(SESSION_ID_TO_CONTEXT_CACHE_SIZE, getSessionIdToContextCacheSize()); 615 pw.decreaseIndent(); 616 } 617 getDeviceConfigStringList(String key, List<String> defaultValue)618 private List<String> getDeviceConfigStringList(String key, List<String> defaultValue) { 619 return parse(deviceConfig.getString(NAMESPACE, key, null), defaultValue); 620 } 621 getDeviceConfigFloatArray(String key, float[] defaultValue)622 private float[] getDeviceConfigFloatArray(String key, float[] defaultValue) { 623 return parse(deviceConfig.getString(NAMESPACE, key, null), defaultValue); 624 } 625 parse(@ullable String listStr, List<String> defaultValue)626 private static List<String> parse(@Nullable String listStr, List<String> defaultValue) { 627 if (listStr != null) { 628 return Collections.unmodifiableList(Arrays.asList(listStr.split(DELIMITER))); 629 } 630 return defaultValue; 631 } 632 parse(@ullable String arrayStr, float[] defaultValue)633 private static float[] parse(@Nullable String arrayStr, float[] defaultValue) { 634 if (arrayStr != null) { 635 final List<String> split = Splitter.onPattern(DELIMITER).splitToList(arrayStr); 636 if (split.size() != defaultValue.length) { 637 return defaultValue; 638 } 639 final float[] result = new float[split.size()]; 640 for (int i = 0; i < split.size(); i++) { 641 try { 642 result[i] = Float.parseFloat(split.get(i)); 643 } catch (NumberFormatException e) { 644 return defaultValue; 645 } 646 } 647 return result; 648 } else { 649 return defaultValue; 650 } 651 } 652 } 653