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