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