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