summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/service/textclassifier/TextClassifierService.java25
-rw-r--r--core/java/android/view/textclassifier/ConfigParser.java264
-rw-r--r--core/java/android/view/textclassifier/TextClassificationConstants.java187
-rw-r--r--core/java/android/view/textclassifier/TextClassificationManager.java57
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java138
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java321
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java2
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java2
-rw-r--r--services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java219
9 files changed, 328 insertions, 887 deletions
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 20dc2beeaa48..8dca69f856e5 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -37,7 +37,6 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteException;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
import android.view.textclassifier.ConversationActions;
@@ -437,28 +436,16 @@ public abstract class TextClassifierService extends Service {
/**
* Returns the component name of the system default textclassifier service if it can be found
* on the system. Otherwise, returns null.
- * @hide
- */
- public static ComponentName getServiceComponentName(@NonNull Context context) {
- return getServiceComponentName(context, new TextClassificationConstants(
- () -> Settings.Global.getString(
- context.getContentResolver(),
- Settings.Global.TEXT_CLASSIFIER_CONSTANTS)));
- }
-
- /**
- * Returns the component name of the system default textclassifier service if it can be found
- * on the system. Otherwise, returns null.
- * @param context the text classification context
- * @param settings TextClassifier specific settings.
*
+ * @param context the text classification context
* @hide
*/
@Nullable
- public static ComponentName getServiceComponentName(@NonNull Context context,
- @NonNull TextClassificationConstants settings) {
+ public static ComponentName getServiceComponentName(@NonNull Context context) {
+ final TextClassificationConstants settings = TextClassificationManager.getSettings(context);
// get override TextClassifierService package name
- String packageName = settings.getTextClassifierServiceName();
+ String packageName = settings.getTextClassifierServicePackageOverride();
+
ComponentName serviceComponent = null;
final boolean isOverrideService = !TextUtils.isEmpty(packageName);
if (isOverrideService) {
@@ -468,7 +455,7 @@ public abstract class TextClassifierService extends Service {
if (serviceComponent != null) {
return serviceComponent;
}
- // If no TextClassifierService overrode or invalid override package name, read the first
+ // If no TextClassifierService override or invalid override package name, read the first
// package defined in the config
final String[] packages = context.getPackageManager().getSystemTextClassifierPackages();
if (packages.length == 0 || TextUtils.isEmpty(packages[0])) {
diff --git a/core/java/android/view/textclassifier/ConfigParser.java b/core/java/android/view/textclassifier/ConfigParser.java
deleted file mode 100644
index 9b51458d27d9..000000000000
--- a/core/java/android/view/textclassifier/ConfigParser.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.provider.DeviceConfig;
-import android.util.ArrayMap;
-import android.util.KeyValueListParser;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.internal.util.Preconditions;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Supplier;
-
-/**
- * Retrieves settings from {@link DeviceConfig} and {@link android.provider.Settings}.
- * It will try DeviceConfig first and then Settings.
- *
- * @hide
- */
-@VisibleForTesting(visibility = Visibility.PACKAGE)
-public final class ConfigParser {
- private static final String TAG = "ConfigParser";
-
- public static final boolean ENABLE_DEVICE_CONFIG = true;
-
- private static final String STRING_LIST_DELIMITER = ":";
-
- private final Supplier<String> mLegacySettingsSupplier;
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private final Map<String, Object> mCache = new ArrayMap<>();
- @GuardedBy("mLock")
- private @Nullable KeyValueListParser mSettingsParser; // Call getLegacySettings() instead.
-
- public ConfigParser(Supplier<String> legacySettingsSupplier) {
- mLegacySettingsSupplier = Preconditions.checkNotNull(legacySettingsSupplier);
- }
-
- private KeyValueListParser getLegacySettings() {
- synchronized (mLock) {
- if (mSettingsParser == null) {
- final String legacySettings = mLegacySettingsSupplier.get();
- try {
- mSettingsParser = new KeyValueListParser(',');
- mSettingsParser.setString(legacySettings);
- } catch (IllegalArgumentException e) {
- // Failed to parse the settings string, log this and move on with defaults.
- Log.w(TAG, "Bad text_classifier_constants: " + legacySettings);
- }
- }
- return mSettingsParser;
- }
- }
-
- /**
- * Reads a boolean setting through the cache.
- */
- public boolean getBoolean(String key, boolean defaultValue) {
- synchronized (mLock) {
- final Object cached = mCache.get(key);
- if (cached instanceof Boolean) {
- return (boolean) cached;
- }
- final boolean value;
- if (ENABLE_DEVICE_CONFIG) {
- value = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- key,
- getLegacySettings().getBoolean(key, defaultValue));
- } else {
- value = getLegacySettings().getBoolean(key, defaultValue);
- }
- mCache.put(key, value);
- return value;
- }
- }
-
- /**
- * Reads an integer setting through the cache.
- */
- public int getInt(String key, int defaultValue) {
- synchronized (mLock) {
- final Object cached = mCache.get(key);
- if (cached instanceof Integer) {
- return (int) cached;
- }
- final int value;
- if (ENABLE_DEVICE_CONFIG) {
- value = DeviceConfig.getInt(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- key,
- getLegacySettings().getInt(key, defaultValue));
- } else {
- value = getLegacySettings().getInt(key, defaultValue);
- }
- mCache.put(key, value);
- return value;
- }
- }
-
- /**
- * Reads a float setting through the cache.
- */
- public float getFloat(String key, float defaultValue) {
- synchronized (mLock) {
- final Object cached = mCache.get(key);
- if (cached instanceof Float) {
- return (float) cached;
- }
- final float value;
- if (ENABLE_DEVICE_CONFIG) {
- value = DeviceConfig.getFloat(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- key,
- getLegacySettings().getFloat(key, defaultValue));
- } else {
- value = getLegacySettings().getFloat(key, defaultValue);
- }
- mCache.put(key, value);
- return value;
- }
- }
-
- /**
- * Reads a string setting through the cache.
- */
- public String getString(String key, String defaultValue) {
- synchronized (mLock) {
- final Object cached = mCache.get(key);
- if (cached instanceof String) {
- return (String) cached;
- }
- final String value;
- if (ENABLE_DEVICE_CONFIG) {
- value = DeviceConfig.getString(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- key,
- getLegacySettings().getString(key, defaultValue));
- } else {
- value = getLegacySettings().getString(key, defaultValue);
- }
- mCache.put(key, value);
- return value;
- }
- }
-
- /**
- * Reads a string list setting through the cache.
- */
- public List<String> getStringList(String key, List<String> defaultValue) {
- synchronized (mLock) {
- final Object cached = mCache.get(key);
- if (cached instanceof List) {
- final List asList = (List) cached;
- if (asList.isEmpty()) {
- return Collections.emptyList();
- } else if (asList.get(0) instanceof String) {
- return (List<String>) cached;
- }
- }
- final List<String> value;
- if (ENABLE_DEVICE_CONFIG) {
- value = getDeviceConfigStringList(
- key,
- getSettingsStringList(key, defaultValue));
- } else {
- value = getSettingsStringList(key, defaultValue);
- }
- mCache.put(key, value);
- return value;
- }
- }
-
- /**
- * Reads a float array through the cache. The returned array should be expected to be of the
- * same length as that of the defaultValue.
- */
- public float[] getFloatArray(String key, float[] defaultValue) {
- synchronized (mLock) {
- final Object cached = mCache.get(key);
- if (cached instanceof float[]) {
- return (float[]) cached;
- }
- final float[] value;
- if (ENABLE_DEVICE_CONFIG) {
- value = getDeviceConfigFloatArray(
- key,
- getSettingsFloatArray(key, defaultValue));
- } else {
- value = getSettingsFloatArray(key, defaultValue);
- }
- mCache.put(key, value);
- return value;
- }
- }
-
- private List<String> getSettingsStringList(String key, List<String> defaultValue) {
- return parse(mSettingsParser.getString(key, null), defaultValue);
- }
-
- private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) {
- return parse(
- DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
- defaultValue);
- }
-
- private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) {
- return parse(
- DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
- defaultValue);
- }
-
- private float[] getSettingsFloatArray(String key, float[] defaultValue) {
- return parse(mSettingsParser.getString(key, null), defaultValue);
- }
-
- private static List<String> parse(@Nullable String listStr, List<String> defaultValue) {
- if (listStr != null) {
- return Collections.unmodifiableList(
- Arrays.asList(listStr.split(STRING_LIST_DELIMITER)));
- }
- return defaultValue;
- }
-
- private static float[] parse(@Nullable String arrayStr, float[] defaultValue) {
- if (arrayStr != null) {
- final String[] split = arrayStr.split(STRING_LIST_DELIMITER);
- if (split.length != defaultValue.length) {
- return defaultValue;
- }
- final float[] result = new float[split.length];
- for (int i = 0; i < split.length; i++) {
- try {
- result[i] = Float.parseFloat(split[i]);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
- return result;
- } else {
- return defaultValue;
- }
- }
-}
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 9f8671aad313..ed69513b7f69 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -16,38 +16,31 @@
package android.view.textclassifier;
+import android.annotation.Nullable;
+import android.provider.DeviceConfig;
+
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
-import java.util.function.Supplier;
/**
* TextClassifier specific settings.
- * This is encoded as a key=value list, separated by commas.
- * <p>
- * Example of setting the values for testing.
- * <p>
- * <pre>
- * adb shell settings put global text_classifier_constants \
- * model_dark_launch_enabled=true,smart_selection_enabled=true, \
- * entity_list_default=phone:address, \
- * lang_id_context_settings=20:1.0:0.4
- * </pre>
- * <p>
- * Settings are also available in device config. These take precedence over those in settings
- * global.
- * <p>
+ *
+ * <p>Currently, this class does not guarantee co-diverted flags are updated atomically.
+ *
* <pre>
* adb shell cmd device_config put textclassifier system_textclassifier_enabled true
* </pre>
*
- * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
- * @see android.provider.DeviceConfig.NAMESPACE_TEXTCLASSIFIER
+ * @see android.provider.DeviceConfig#NAMESPACE_TEXTCLASSIFIER
* @hide
*/
// TODO: Rename to TextClassifierSettings.
public final class TextClassificationConstants {
+ private static final String DELIMITER = ":";
/**
* Whether the smart linkify feature is enabled.
@@ -56,11 +49,12 @@ public final class TextClassificationConstants {
/**
* Whether SystemTextClassifier is enabled.
*/
- private static final String SYSTEM_TEXT_CLASSIFIER_ENABLED = "system_textclassifier_enabled";
+ static final String SYSTEM_TEXT_CLASSIFIER_ENABLED = "system_textclassifier_enabled";
/**
* Whether TextClassifierImpl is enabled.
*/
- private static final String LOCAL_TEXT_CLASSIFIER_ENABLED = "local_textclassifier_enabled";
+ @VisibleForTesting
+ static final String LOCAL_TEXT_CLASSIFIER_ENABLED = "local_textclassifier_enabled";
/**
* Enable smart selection without a visible UI changes.
*/
@@ -82,7 +76,8 @@ public final class TextClassificationConstants {
/**
* Max length of text that suggestSelection can accept.
*/
- private static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
+ @VisibleForTesting
+ static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
"suggest_selection_max_range_length";
/**
* Max length of text that classifyText can accept.
@@ -101,7 +96,8 @@ public final class TextClassificationConstants {
* A colon(:) separated string that specifies the default entities types for
* generateLinks when hint is not given.
*/
- private static final String ENTITY_LIST_DEFAULT = "entity_list_default";
+ @VisibleForTesting
+ static final String ENTITY_LIST_DEFAULT = "entity_list_default";
/**
* A colon(:) separated string that specifies the default entities types for
* generateLinks when the text is in a not editable UI widget.
@@ -127,7 +123,8 @@ public final class TextClassificationConstants {
/**
* Threshold to accept a suggested language from LangID model.
*/
- private static final String LANG_ID_THRESHOLD_OVERRIDE = "lang_id_threshold_override";
+ @VisibleForTesting
+ static final String LANG_ID_THRESHOLD_OVERRIDE = "lang_id_threshold_override";
/**
* Whether to enable {@link android.view.textclassifier.TemplateIntentFactory}.
*/
@@ -156,7 +153,8 @@ public final class TextClassificationConstants {
* Reject all text less than minimumTextSize with penalizeRatio=0
* @see {@code TextClassifierImpl#detectLanguages(String, int, int)} for reference.
*/
- private static final String LANG_ID_CONTEXT_SETTINGS = "lang_id_context_settings";
+ @VisibleForTesting
+ static final String LANG_ID_CONTEXT_SETTINGS = "lang_id_context_settings";
/**
* The TextClassifierService which would like to use. Example of setting the package:
@@ -164,9 +162,9 @@ public final class TextClassificationConstants {
* adb shell cmd device_config put textclassifier textclassifier_service_package_override \
* com.android.textclassifier
* </pre>
- *
*/
- private static final String TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE =
+ @VisibleForTesting
+ static final String TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE =
"textclassifier_service_package_override";
private static final String DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = null;
@@ -213,142 +211,124 @@ public final class TextClassificationConstants {
private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true;
private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[] {20f, 1.0f, 0.4f};
- private final ConfigParser mConfigParser;
-
- public TextClassificationConstants(Supplier<String> legacySettingsSupplier) {
- mConfigParser = new ConfigParser(legacySettingsSupplier);
- }
-
- public String getTextClassifierServiceName() {
- return mConfigParser.getString(
+ @Nullable
+ public String getTextClassifierServicePackageOverride() {
+ return DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE);
}
public boolean isLocalTextClassifierEnabled() {
- return mConfigParser.getBoolean(
- LOCAL_TEXT_CLASSIFIER_ENABLED,
- LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ LOCAL_TEXT_CLASSIFIER_ENABLED, LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
}
public boolean isSystemTextClassifierEnabled() {
- return mConfigParser.getBoolean(
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
SYSTEM_TEXT_CLASSIFIER_ENABLED,
SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
}
public boolean isModelDarkLaunchEnabled() {
- return mConfigParser.getBoolean(
- MODEL_DARK_LAUNCH_ENABLED,
- MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ MODEL_DARK_LAUNCH_ENABLED, MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
}
public boolean isSmartSelectionEnabled() {
- return mConfigParser.getBoolean(
- SMART_SELECTION_ENABLED,
- SMART_SELECTION_ENABLED_DEFAULT);
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ SMART_SELECTION_ENABLED, SMART_SELECTION_ENABLED_DEFAULT);
}
public boolean isSmartTextShareEnabled() {
- return mConfigParser.getBoolean(
- SMART_TEXT_SHARE_ENABLED,
- SMART_TEXT_SHARE_ENABLED_DEFAULT);
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ SMART_TEXT_SHARE_ENABLED, SMART_TEXT_SHARE_ENABLED_DEFAULT);
}
public boolean isSmartLinkifyEnabled() {
- return mConfigParser.getBoolean(
- SMART_LINKIFY_ENABLED,
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, SMART_LINKIFY_ENABLED,
SMART_LINKIFY_ENABLED_DEFAULT);
}
public boolean isSmartSelectionAnimationEnabled() {
- return mConfigParser.getBoolean(
- SMART_SELECT_ANIMATION_ENABLED,
- SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ SMART_SELECT_ANIMATION_ENABLED, SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
}
public int getSuggestSelectionMaxRangeLength() {
- return mConfigParser.getInt(
- SUGGEST_SELECTION_MAX_RANGE_LENGTH,
- SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
+ return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ SUGGEST_SELECTION_MAX_RANGE_LENGTH, SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
}
public int getClassifyTextMaxRangeLength() {
- return mConfigParser.getInt(
- CLASSIFY_TEXT_MAX_RANGE_LENGTH,
- CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
+ return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ CLASSIFY_TEXT_MAX_RANGE_LENGTH, CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
}
public int getGenerateLinksMaxTextLength() {
- return mConfigParser.getInt(
- GENERATE_LINKS_MAX_TEXT_LENGTH,
- GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
+ return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ GENERATE_LINKS_MAX_TEXT_LENGTH, GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
}
public int getGenerateLinksLogSampleRate() {
- return mConfigParser.getInt(
- GENERATE_LINKS_LOG_SAMPLE_RATE,
- GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
+ return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ GENERATE_LINKS_LOG_SAMPLE_RATE, GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
}
public List<String> getEntityListDefault() {
- return mConfigParser.getStringList(
- ENTITY_LIST_DEFAULT,
- ENTITY_LIST_DEFAULT_VALUE);
+ return getDeviceConfigStringList(ENTITY_LIST_DEFAULT, ENTITY_LIST_DEFAULT_VALUE);
}
public List<String> getEntityListNotEditable() {
- return mConfigParser.getStringList(
- ENTITY_LIST_NOT_EDITABLE,
- ENTITY_LIST_DEFAULT_VALUE);
+ return getDeviceConfigStringList(ENTITY_LIST_NOT_EDITABLE, ENTITY_LIST_DEFAULT_VALUE);
}
public List<String> getEntityListEditable() {
- return mConfigParser.getStringList(
- ENTITY_LIST_EDITABLE,
- ENTITY_LIST_DEFAULT_VALUE);
+ return getDeviceConfigStringList(ENTITY_LIST_EDITABLE, ENTITY_LIST_DEFAULT_VALUE);
}
public List<String> getInAppConversationActionTypes() {
- return mConfigParser.getStringList(
+ return getDeviceConfigStringList(
IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
}
public List<String> getNotificationConversationActionTypes() {
- return mConfigParser.getStringList(
+ return getDeviceConfigStringList(
NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
}
public float getLangIdThresholdOverride() {
- return mConfigParser.getFloat(
+ return DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
LANG_ID_THRESHOLD_OVERRIDE,
LANG_ID_THRESHOLD_OVERRIDE_DEFAULT);
}
public boolean isTemplateIntentFactoryEnabled() {
- return mConfigParser.getBoolean(
+ return DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
TEMPLATE_INTENT_FACTORY_ENABLED,
TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT);
}
public boolean isTranslateInClassificationEnabled() {
- return mConfigParser.getBoolean(
+ return DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
TRANSLATE_IN_CLASSIFICATION_ENABLED,
TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT);
}
public boolean isDetectLanguagesFromTextEnabled() {
- return mConfigParser.getBoolean(
+ return DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
DETECT_LANGUAGES_FROM_TEXT_ENABLED,
DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT);
}
public float[] getLangIdContextSettings() {
- return mConfigParser.getFloatArray(
- LANG_ID_CONTEXT_SETTINGS,
- LANG_ID_CONTEXT_SETTINGS_DEFAULT);
+ return getDeviceConfigFloatArray(
+ LANG_ID_CONTEXT_SETTINGS, LANG_ID_CONTEXT_SETTINGS_DEFAULT);
}
void dump(IndentingPrintWriter pw) {
@@ -356,7 +336,7 @@ public final class TextClassificationConstants {
pw.increaseIndent();
pw.printPair("classify_text_max_range_length", getClassifyTextMaxRangeLength())
.println();
- pw.printPair("detect_language_from_text_enabled", isDetectLanguagesFromTextEnabled())
+ pw.printPair("detect_languages_from_text_enabled", isDetectLanguagesFromTextEnabled())
.println();
pw.printPair("entity_list_default", getEntityListDefault())
.println();
@@ -396,8 +376,47 @@ public final class TextClassificationConstants {
.println();
pw.printPair("translate_in_classification_enabled", isTranslateInClassificationEnabled())
.println();
- pw.printPair("textclassifier_service_package_override", getTextClassifierServiceName())
- .println();
+ pw.printPair("textclassifier_service_package_override",
+ getTextClassifierServicePackageOverride()).println();
pw.decreaseIndent();
}
+
+ private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) {
+ return parse(
+ DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
+ defaultValue);
+ }
+
+ private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) {
+ return parse(
+ DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
+ defaultValue);
+ }
+
+ private static List<String> parse(@Nullable String listStr, List<String> defaultValue) {
+ if (listStr != null) {
+ return Collections.unmodifiableList(Arrays.asList(listStr.split(DELIMITER)));
+ }
+ return defaultValue;
+ }
+
+ private static float[] parse(@Nullable String arrayStr, float[] defaultValue) {
+ if (arrayStr != null) {
+ final String[] split = arrayStr.split(DELIMITER);
+ if (split.length != defaultValue.length) {
+ return defaultValue;
+ }
+ final float[] result = new float[split.length];
+ for (int i = 0; i < split.length; i++) {
+ try {
+ result[i] = Float.parseFloat(split[i]);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+ return result;
+ } else {
+ return defaultValue;
+ }
+ }
} \ No newline at end of file
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index 3e0f7eef33a7..7c25bf0f277d 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -22,11 +22,9 @@ import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.content.Context;
-import android.database.ContentObserver;
import android.os.ServiceManager;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
-import android.provider.Settings;
import android.service.textclassifier.TextClassifierService;
import android.view.textclassifier.TextClassifier.TextClassifierType;
@@ -36,6 +34,7 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import java.lang.ref.WeakReference;
+import java.util.Set;
/**
* Interface to the text classification service.
@@ -46,7 +45,7 @@ public final class TextClassificationManager {
private static final String LOG_TAG = "TextClassificationManager";
private static final TextClassificationConstants sDefaultSettings =
- new TextClassificationConstants(() -> null);
+ new TextClassificationConstants();
private final Object mLock = new Object();
private final TextClassificationSessionFactory mDefaultSessionFactory =
@@ -132,10 +131,7 @@ public final class TextClassificationManager {
private TextClassificationConstants getSettings() {
synchronized (mLock) {
if (mSettings == null) {
- mSettings = new TextClassificationConstants(
- () -> Settings.Global.getString(
- getApplicationContext().getContentResolver(),
- Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+ mSettings = new TextClassificationConstants();
}
return mSettings;
}
@@ -201,11 +197,7 @@ public final class TextClassificationManager {
try {
// Note that fields could be null if the constructor threw.
if (mSettingsObserver != null) {
- getApplicationContext().getContentResolver()
- .unregisterContentObserver(mSettingsObserver);
- if (ConfigParser.ENABLE_DEVICE_CONFIG) {
- DeviceConfig.removeOnPropertiesChangedListener(mSettingsObserver);
- }
+ DeviceConfig.removeOnPropertiesChangedListener(mSettingsObserver);
}
} finally {
super.finalize();
@@ -250,7 +242,7 @@ public final class TextClassificationManager {
private boolean isSystemTextClassifierEnabled() {
return getSettings().isSystemTextClassifierEnabled()
- && TextClassifierService.getServiceComponentName(mContext, getSettings()) != null;
+ && TextClassifierService.getServiceComponentName(mContext) != null;
}
/** @hide */
@@ -262,6 +254,12 @@ public final class TextClassificationManager {
private void invalidate() {
synchronized (mLock) {
mSettings = null;
+ invalidateTextClassifiers();
+ }
+ }
+
+ private void invalidateTextClassifiers() {
+ synchronized (mLock) {
mLocalTextClassifier = null;
mSystemTextClassifier = null;
}
@@ -293,40 +291,29 @@ public final class TextClassificationManager {
}
}
- private static final class SettingsObserver extends ContentObserver
- implements DeviceConfig.OnPropertiesChangedListener {
+ private static final class SettingsObserver implements
+ DeviceConfig.OnPropertiesChangedListener {
private final WeakReference<TextClassificationManager> mTcm;
SettingsObserver(TextClassificationManager tcm) {
- super(null);
mTcm = new WeakReference<>(tcm);
- tcm.getApplicationContext().getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.TEXT_CLASSIFIER_CONSTANTS),
- false /* notifyForDescendants */,
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ ActivityThread.currentApplication().getMainExecutor(),
this);
- if (ConfigParser.ENABLE_DEVICE_CONFIG) {
- DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- ActivityThread.currentApplication().getMainExecutor(),
- this);
- }
- }
-
- @Override
- public void onChange(boolean selfChange) {
- invalidateSettings();
}
@Override
public void onPropertiesChanged(Properties properties) {
- invalidateSettings();
- }
-
- private void invalidateSettings() {
final TextClassificationManager tcm = mTcm.get();
if (tcm != null) {
- tcm.invalidate();
+ final Set<String> keys = properties.getKeyset();
+ if (keys.contains(TextClassificationConstants.SYSTEM_TEXT_CLASSIFIER_ENABLED)
+ || keys.contains(
+ TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED)) {
+ tcm.invalidateTextClassifiers();
+ }
}
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
deleted file mode 100644
index d54ce51c3c37..000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.provider.DeviceConfig;
-import android.support.test.uiautomator.UiDevice;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.util.function.Supplier;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ConfigParserTest {
- private static final Supplier<String> SETTINGS =
- () -> "int=42,float=12.3,boolean=true,string=abc";
- private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
- "device_config delete " + DeviceConfig.NAMESPACE_TEXTCLASSIFIER;
- private static final String[] DEVICE_CONFIG_KEYS = new String[]{
- "boolean",
- "string",
- "int",
- "float"
- };
-
- private ConfigParser mConfigParser;
-
- @Before
- public void setup() throws IOException {
- mConfigParser = new ConfigParser(SETTINGS);
- clearDeviceConfig();
- }
-
- @After
- public void tearDown() throws IOException {
- clearDeviceConfig();
- }
-
- @Test
- public void getBoolean_deviceConfig() {
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- "boolean",
- "false",
- false);
- boolean value = mConfigParser.getBoolean("boolean", true);
- assertThat(value).isFalse();
- }
-
- @Test
- public void getBoolean_settings() {
- boolean value = mConfigParser.getBoolean(
- "boolean",
- false);
- assertThat(value).isTrue();
- }
-
- @Test
- public void getInt_deviceConfig() {
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- "int",
- "1",
- false);
- int value = mConfigParser.getInt("int", 0);
- assertThat(value).isEqualTo(1);
- }
-
- @Test
- public void getInt_settings() {
- int value = mConfigParser.getInt("int", 0);
- assertThat(value).isEqualTo(42);
- }
-
- @Test
- public void getFloat_deviceConfig() {
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- "float",
- "3.14",
- false);
- float value = mConfigParser.getFloat("float", 0);
- assertThat(value).isWithin(0.0001f).of(3.14f);
- }
-
- @Test
- public void getFloat_settings() {
- float value = mConfigParser.getFloat("float", 0);
- assertThat(value).isWithin(0.0001f).of(12.3f);
- }
-
- @Test
- public void getString_deviceConfig() {
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- "string",
- "hello",
- false);
- String value = mConfigParser.getString("string", "");
- assertThat(value).isEqualTo("hello");
- }
-
- @Test
- public void getString_settings() {
- String value = mConfigParser.getString("string", "");
- assertThat(value).isEqualTo("abc");
- }
-
- private static void clearDeviceConfig() throws IOException {
- UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- for (String key : DEVICE_CONFIG_KEYS) {
- uiDevice.executeShellCommand(CLEAR_DEVICE_CONFIG_KEY_CMD + " " + key);
- }
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index 64fb14190ce2..82fa73f76207 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -16,230 +16,141 @@
package android.view.textclassifier;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import android.provider.DeviceConfig;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Assert;
+import com.google.common.primitives.Floats;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TextClassificationConstantsTest {
-
private static final float EPSILON = 0.0001f;
@Test
- public void testLoadFromString() {
- final String s = "local_textclassifier_enabled=true,"
- + "system_textclassifier_enabled=true,"
- + "model_dark_launch_enabled=true,"
- + "smart_selection_enabled=true,"
- + "smart_text_share_enabled=true,"
- + "smart_linkify_enabled=true,"
- + "smart_select_animation_enabled=true,"
- + "suggest_selection_max_range_length=10,"
- + "classify_text_max_range_length=11,"
- + "generate_links_max_text_length=12,"
- + "generate_links_log_sample_rate=13,"
- + "entity_list_default=phone,"
- + "entity_list_not_editable=address:flight,"
- + "entity_list_editable=date:datetime,"
- + "in_app_conversation_action_types_default=text_reply,"
- + "notification_conversation_action_types_default=send_email:call_phone,"
- + "lang_id_threshold_override=0.3,"
- + "lang_id_context_settings=10:1:0.5,"
- + "detect_language_from_text_enabled=true,"
- + "template_intent_factory_enabled=true,"
- + "translate_in_classification_enabled=true";
- final TextClassificationConstants constants = new TextClassificationConstants(() -> s);
-
- assertWithMessage("local_textclassifier_enabled")
- .that(constants.isLocalTextClassifierEnabled()).isTrue();
- assertWithMessage("system_textclassifier_enabled")
- .that(constants.isSystemTextClassifierEnabled()).isTrue();
- assertWithMessage("model_dark_launch_enabled")
- .that(constants.isModelDarkLaunchEnabled()).isTrue();
- assertWithMessage("smart_selection_enabled")
- .that(constants.isSmartSelectionEnabled()).isTrue();
- assertWithMessage("smart_text_share_enabled")
- .that(constants.isSmartTextShareEnabled()).isTrue();
- assertWithMessage("smart_linkify_enabled")
- .that(constants.isSmartLinkifyEnabled()).isTrue();
- assertWithMessage("smart_select_animation_enabled")
- .that(constants.isSmartSelectionAnimationEnabled()).isTrue();
- assertWithMessage("suggest_selection_max_range_length")
- .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(10);
- assertWithMessage("classify_text_max_range_length")
- .that(constants.getClassifyTextMaxRangeLength()).isEqualTo(11);
- assertWithMessage("generate_links_max_text_length")
- .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(12);
- assertWithMessage("generate_links_log_sample_rate")
- .that(constants.getGenerateLinksLogSampleRate()).isEqualTo(13);
- assertWithMessage("entity_list_default")
- .that(constants.getEntityListDefault())
- .containsExactly("phone");
- assertWithMessage("entity_list_not_editable")
- .that(constants.getEntityListNotEditable())
- .containsExactly("address", "flight");
- assertWithMessage("entity_list_editable")
- .that(constants.getEntityListEditable())
- .containsExactly("date", "datetime");
- assertWithMessage("in_app_conversation_action_types_default")
- .that(constants.getInAppConversationActionTypes())
- .containsExactly("text_reply");
- assertWithMessage("notification_conversation_action_types_default")
- .that(constants.getNotificationConversationActionTypes())
- .containsExactly("send_email", "call_phone");
- assertWithMessage("lang_id_threshold_override")
- .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(0.3f);
- Assert.assertArrayEquals("lang_id_context_settings",
- constants.getLangIdContextSettings(), new float[]{10, 1, 0.5f}, EPSILON);
- assertWithMessage("detect_language_from_text_enabled")
- .that(constants.isLocalTextClassifierEnabled()).isTrue();
- assertWithMessage("template_intent_factory_enabled")
- .that(constants.isLocalTextClassifierEnabled()).isTrue();
- assertWithMessage("translate_in_classification_enabled")
- .that(constants.isLocalTextClassifierEnabled()).isTrue();
+ public void testLoadFromDeviceConfig_booleanValue() throws Exception {
+ // Saves config original value.
+ final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED);
+
+ final TextClassificationConstants constants = new TextClassificationConstants();
+ try {
+ // Sets and checks different value.
+ setDeviceConfig(TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED, "false");
+ assertWithMessage(TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED)
+ .that(constants.isLocalTextClassifierEnabled()).isFalse();
+ } finally {
+ // Restores config original value.
+ setDeviceConfig(TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED,
+ originalValue);
+ }
+ }
+
+ @Test
+ public void testLoadFromDeviceConfig_IntValue() throws Exception {
+ // Saves config original value.
+ final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH);
+
+ final TextClassificationConstants constants = new TextClassificationConstants();
+ try {
+ // Sets and checks different value.
+ setDeviceConfig(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH, "8");
+ assertWithMessage(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH)
+ .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(8);
+ } finally {
+ // Restores config original value.
+ setDeviceConfig(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH,
+ originalValue);
+ }
+ }
+
+ @Test
+ public void testLoadFromDeviceConfig_StringValue() throws Exception {
+ // Saves config original value.
+ final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE);
+
+ final TextClassificationConstants constants = new TextClassificationConstants();
+ try {
+ // Sets and checks different value.
+ final String testTextClassifier = "com.example.textclassifier";
+ setDeviceConfig(TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
+ testTextClassifier);
+ assertWithMessage(TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE)
+ .that(constants.getTextClassifierServicePackageOverride()).isEqualTo(
+ testTextClassifier);
+ } finally {
+ // Restores config original value.
+ setDeviceConfig(TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
+ originalValue);
+ }
+ }
+
+ @Test
+ public void testLoadFromDeviceConfig_FloatValue() throws Exception {
+ // Saves config original value.
+ final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE);
+
+ final TextClassificationConstants constants = new TextClassificationConstants();
+ try {
+ // Sets and checks different value.
+ setDeviceConfig(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE, "2");
+ assertWithMessage(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE)
+ .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f);
+ } finally {
+ // Restores config original value.
+ setDeviceConfig(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE, originalValue);
+ }
}
@Test
- public void testLoadFromString_differentValues() {
- final String testTextClassifier = "com.example.textclassifier";
- final String s = "local_textclassifier_enabled=false,"
- + "system_textclassifier_enabled=false,"
- + "model_dark_launch_enabled=false,"
- + "smart_selection_enabled=false,"
- + "smart_text_share_enabled=false,"
- + "smart_linkify_enabled=false,"
- + "smart_select_animation_enabled=false,"
- + "suggest_selection_max_range_length=8,"
- + "classify_text_max_range_length=7,"
- + "generate_links_max_text_length=6,"
- + "generate_links_log_sample_rate=5,"
- + "entity_list_default=email:url,"
- + "entity_list_not_editable=date,"
- + "entity_list_editable=flight,"
- + "in_app_conversation_action_types_default=view_map:track_flight,"
- + "notification_conversation_action_types_default=share_location,"
- + "lang_id_threshold_override=2,"
- + "lang_id_context_settings=30:0.5:0.3,"
- + "detect_language_from_text_enabled=false,"
- + "template_intent_factory_enabled=false,"
- + "textclassifier_service_package_override=" + testTextClassifier + ","
- + "translate_in_classification_enabled=false";
- final TextClassificationConstants constants = new TextClassificationConstants(() -> s);
-
- assertWithMessage("local_textclassifier_enabled")
- .that(constants.isLocalTextClassifierEnabled()).isFalse();
- assertWithMessage("system_textclassifier_enabled")
- .that(constants.isSystemTextClassifierEnabled()).isFalse();
- assertWithMessage("model_dark_launch_enabled")
- .that(constants.isModelDarkLaunchEnabled()).isFalse();
- assertWithMessage("smart_selection_enabled")
- .that(constants.isSmartSelectionEnabled()).isFalse();
- assertWithMessage("smart_text_share_enabled")
- .that(constants.isSmartTextShareEnabled()).isFalse();
- assertWithMessage("smart_linkify_enabled")
- .that(constants.isSmartLinkifyEnabled()).isFalse();
- assertWithMessage("smart_select_animation_enabled")
- .that(constants.isSmartSelectionAnimationEnabled()).isFalse();
- assertWithMessage("suggest_selection_max_range_length")
- .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(8);
- assertWithMessage("classify_text_max_range_length")
- .that(constants.getClassifyTextMaxRangeLength()).isEqualTo(7);
- assertWithMessage("generate_links_max_text_length")
- .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(6);
- assertWithMessage("generate_links_log_sample_rate")
- .that(constants.getGenerateLinksLogSampleRate()).isEqualTo(5);
- assertWithMessage("entity_list_default")
- .that(constants.getEntityListDefault())
- .containsExactly("email", "url");
- assertWithMessage("entity_list_not_editable")
- .that(constants.getEntityListNotEditable())
- .containsExactly("date");
- assertWithMessage("entity_list_editable")
- .that(constants.getEntityListEditable())
- .containsExactly("flight");
- assertWithMessage("in_app_conversation_action_types_default")
- .that(constants.getInAppConversationActionTypes())
- .containsExactly("view_map", "track_flight");
- assertWithMessage("notification_conversation_action_types_default")
- .that(constants.getNotificationConversationActionTypes())
- .containsExactly("share_location");
- assertWithMessage("lang_id_threshold_override")
- .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f);
- Assert.assertArrayEquals("lang_id_context_settings",
- constants.getLangIdContextSettings(), new float[]{30, 0.5f, 0.3f}, EPSILON);
- assertWithMessage("detect_language_from_text_enabled")
- .that(constants.isLocalTextClassifierEnabled()).isFalse();
- assertWithMessage("template_intent_factory_enabled")
- .that(constants.isLocalTextClassifierEnabled()).isFalse();
- assertWithMessage("translate_in_classification_enabled")
- .that(constants.isLocalTextClassifierEnabled()).isFalse();
- assertWithMessage("textclassifier_service_package_override")
- .that(constants.getTextClassifierServiceName()).isEqualTo(
- testTextClassifier);
+ public void testLoadFromDeviceConfig_StringList() throws Exception {
+ // Saves config original value.
+ final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ TextClassificationConstants.ENTITY_LIST_DEFAULT);
+
+ final TextClassificationConstants constants = new TextClassificationConstants();
+ try {
+ // Sets and checks different value.
+ setDeviceConfig(TextClassificationConstants.ENTITY_LIST_DEFAULT, "email:url");
+ assertWithMessage(TextClassificationConstants.ENTITY_LIST_DEFAULT)
+ .that(constants.getEntityListDefault())
+ .containsExactly("email", "url");
+ } finally {
+ // Restores config original value.
+ setDeviceConfig(TextClassificationConstants.ENTITY_LIST_DEFAULT, originalValue);
+ }
}
@Test
- public void testLoadFromString_defaultValues() {
- final TextClassificationConstants constants = new TextClassificationConstants(() -> "");
-
- assertWithMessage("local_textclassifier_enabled")
- .that(constants.isLocalTextClassifierEnabled()).isTrue();
- assertWithMessage("system_textclassifier_enabled")
- .that(constants.isSystemTextClassifierEnabled()).isTrue();
- assertWithMessage("model_dark_launch_enabled")
- .that(constants.isModelDarkLaunchEnabled()).isFalse();
- assertWithMessage("smart_selection_enabled")
- .that(constants.isSmartSelectionEnabled()).isTrue();
- assertWithMessage("smart_text_share_enabled")
- .that(constants.isSmartTextShareEnabled()).isTrue();
- assertWithMessage("smart_linkify_enabled")
- .that(constants.isSmartLinkifyEnabled()).isTrue();
- assertWithMessage("smart_select_animation_enabled")
- .that(constants.isSmartSelectionAnimationEnabled()).isTrue();
- assertWithMessage("suggest_selection_max_range_length")
- .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(10 * 1000);
- assertWithMessage("classify_text_max_range_length")
- .that(constants.getClassifyTextMaxRangeLength()).isEqualTo(10 * 1000);
- assertWithMessage("generate_links_max_text_length")
- .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(100 * 1000);
- assertWithMessage("generate_links_log_sample_rate")
- .that(constants.getGenerateLinksLogSampleRate()).isEqualTo(100);
- assertWithMessage("entity_list_default")
- .that(constants.getEntityListDefault())
- .containsExactly("address", "email", "url", "phone", "date", "datetime", "flight");
- assertWithMessage("entity_list_not_editable")
- .that(constants.getEntityListNotEditable())
- .containsExactly("address", "email", "url", "phone", "date", "datetime", "flight");
- assertWithMessage("entity_list_editable")
- .that(constants.getEntityListEditable())
- .containsExactly("address", "email", "url", "phone", "date", "datetime", "flight");
- assertWithMessage("in_app_conversation_action_types_default")
- .that(constants.getInAppConversationActionTypes())
- .containsExactly("text_reply", "create_reminder", "call_phone", "open_url",
- "send_email", "send_sms", "track_flight", "view_calendar", "view_map",
- "add_contact", "copy");
- assertWithMessage("notification_conversation_action_types_default")
- .that(constants.getNotificationConversationActionTypes())
- .containsExactly("text_reply", "create_reminder", "call_phone", "open_url",
- "send_email", "send_sms", "track_flight", "view_calendar", "view_map",
- "add_contact", "copy");
- assertWithMessage("lang_id_threshold_override")
- .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(-1f);
- Assert.assertArrayEquals("lang_id_context_settings",
- constants.getLangIdContextSettings(), new float[]{20, 1, 0.4f}, EPSILON);
- assertWithMessage("detect_language_from_text_enabled")
- .that(constants.isLocalTextClassifierEnabled()).isTrue();
- assertWithMessage("template_intent_factory_enabled")
- .that(constants.isLocalTextClassifierEnabled()).isTrue();
- assertWithMessage("translate_in_classification_enabled")
- .that(constants.isLocalTextClassifierEnabled()).isTrue();
- assertWithMessage("textclassifier_service_package_override")
- .that(constants.getTextClassifierServiceName()).isNull();
+ public void testLoadFromDeviceConfig_FloatList() throws Exception {
+ // Saves config original value.
+ final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS);
+
+ final TextClassificationConstants constants = new TextClassificationConstants();
+ try {
+ // Sets and checks different value.
+ setDeviceConfig(TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS, "30:0.5:0.3");
+ assertThat(Floats.asList(constants.getLangIdContextSettings())).containsExactly(30f,
+ 0.5f, 0.3f).inOrder();
+ } finally {
+ // Restores config original value.
+ setDeviceConfig(TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS, originalValue);
+ }
+ }
+
+ private void setDeviceConfig(String key, String value) {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key,
+ value, /* makeDefault */ false);
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index b3f2bbe92c07..8faf790549d5 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -78,7 +78,7 @@ public class TextClassificationManagerTest {
TextClassifier fallback = TextClassifier.NO_OP;
TextClassifier classifier = new TextClassifierImpl(
- fakeContext, new TextClassificationConstants(() -> null), fallback);
+ fakeContext, new TextClassificationConstants(), fallback);
String text = "Contact me at +12122537077";
String classifiedText = "+12122537077";
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index deb0f182156e..2304ba6f6da4 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -69,7 +69,7 @@ public class TextClassifierTest {
public String mTextClassifierType;
private static final TextClassificationConstants TC_CONSTANTS =
- new TextClassificationConstants(() -> "");
+ new TextClassificationConstants();
private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
private static final String NO_TYPE = null;
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 7d905bae4491..6a986b9da515 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -24,15 +24,12 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.service.textclassifier.ITextClassifierCallback;
import android.service.textclassifier.ITextClassifierService;
import android.service.textclassifier.TextClassifierService;
@@ -41,7 +38,6 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.textclassifier.ConfigParser;
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
@@ -143,7 +139,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
private TextClassificationManagerService(Context context) {
mContext = Preconditions.checkNotNull(context);
mLock = new Object();
- mSettingsListener = new TextClassifierSettingsListener(mContext, this);
+ mSettingsListener = new TextClassifierSettingsListener(mContext);
}
private void startListenSettings() {
@@ -171,10 +167,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
Slog.d(LOG_TAG, "Unable to bind TextClassifierService at suggestSelection.");
callback.onFailure();
} else if (userState.isBoundLocked()) {
- if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
- Slog.d(LOG_TAG,
- "Only allow to see own content for non-default service at "
- + "suggestSelection.");
+ if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+ "suggestSelection")) {
return;
}
userState.mService.onSuggestSelection(sessionId, request, callback);
@@ -203,10 +197,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
Slog.d(LOG_TAG, "Unable to bind TextClassifierService at classifyText.");
callback.onFailure();
} else if (userState.isBoundLocked()) {
- if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
- Slog.d(LOG_TAG,
- "Only allow to see own content for non-default service at "
- + "classifyText.");
+ if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(), "classifyText")) {
return;
}
userState.mService.onClassifyText(sessionId, request, callback);
@@ -235,10 +226,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
Slog.d(LOG_TAG, "Unable to bind TextClassifierService at generateLinks.");
callback.onFailure();
} else if (userState.isBoundLocked()) {
- if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
- Slog.d(LOG_TAG,
- "Only allow to see own content for non-default service at "
- + "generateLinks.");
+ if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+ "generateLinks")) {
return;
}
userState.mService.onGenerateLinks(sessionId, request, callback);
@@ -262,10 +251,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
synchronized (mLock) {
UserState userState = getUserStateLocked(userId);
if (userState.isBoundLocked()) {
- if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
- Slog.d(LOG_TAG,
- "Only allow to see own content for non-default service at "
- + "selectionEvent.");
+ if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+ "selectionEvent")) {
return;
}
userState.mService.onSelectionEvent(sessionId, event);
@@ -293,10 +280,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
synchronized (mLock) {
UserState userState = getUserStateLocked(userId);
if (userState.isBoundLocked()) {
- if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
- Slog.d(LOG_TAG,
- "Only allow to see own content for non-default service at "
- + "textClassifierEvent.");
+ if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+ "textClassifierEvent")) {
return;
}
userState.mService.onTextClassifierEvent(sessionId, event);
@@ -325,10 +310,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
Slog.d(LOG_TAG, "Unable to bind TextClassifierService at detectLanguage.");
callback.onFailure();
} else if (userState.isBoundLocked()) {
- if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
- Slog.d(LOG_TAG,
- "Only allow to see own content for non-default service at "
- + "detectLanguage.");
+ if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+ "detectLanguage")) {
return;
}
userState.mService.onDetectLanguage(sessionId, request, callback);
@@ -358,10 +341,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
"Unable to bind TextClassifierService at suggestConversationActions.");
callback.onFailure();
} else if (userState.isBoundLocked()) {
- if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
- Slog.d(LOG_TAG,
- "Only allow to see own content for non-default service at "
- + "suggestConversationActions.");
+ if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+ "suggestConversationActions")) {
return;
}
userState.mService.onSuggestConversationActions(sessionId, request, callback);
@@ -387,10 +368,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
synchronized (mLock) {
UserState userState = getUserStateLocked(userId);
if (userState.isBoundLocked()) {
- if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
- Slog.d(LOG_TAG,
- "Only allow to see own content for non-default service at "
- + "createTextClassificationSession.");
+ if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+ "createTextClassificationSession")) {
return;
}
userState.mService.onCreateTextClassificationSession(
@@ -422,10 +401,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
UserState userState = getUserStateLocked(userId);
if (userState.isBoundLocked()) {
- if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
- Slog.d(LOG_TAG,
- "Only allow to see own content for non-default service at "
- + "destroyTextClassificationSession.");
+ if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+ "destroyTextClassificationSession")) {
return;
}
userState.mService.onDestroyTextClassificationSession(sessionId);
@@ -488,6 +465,29 @@ public final class TextClassificationManagerService extends ITextClassifierServi
}
}
+ private void unbindServiceIfNecessary() {
+ final ComponentName serviceComponentName =
+ TextClassifierService.getServiceComponentName(mContext);
+ if (serviceComponentName == null) {
+ // It should not occur if we had defined default service names in config.xml
+ Slog.w(LOG_TAG, "No default configured system TextClassifierService.");
+ return;
+ }
+ synchronized (mLock) {
+ final int size = mUserStates.size();
+ for (int i = 0; i < size; i++) {
+ UserState userState = mUserStates.valueAt(i);
+ // Only unbind for a new service
+ if (userState.isCurrentlyBoundToComponentLocked(serviceComponentName)) {
+ return;
+ }
+ if (userState.isBoundLocked()) {
+ userState.unbindLocked();
+ }
+ }
+ }
+ }
+
private static final class PendingRequest implements IBinder.DeathRecipient {
private final int mUid;
@@ -582,48 +582,6 @@ public final class TextClassificationManagerService extends ITextClassifierServi
}
}
- private TextClassificationConstants getTextClassifierSettings(Context context) {
- synchronized (mLock) {
- if (mSettings == null) {
- mSettings = new TextClassificationConstants(
- () -> Settings.Global.getString(
- context.getContentResolver(),
- Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
- }
- return mSettings;
- }
- }
-
- private void invalidateSettings() {
- synchronized (mLock) {
- mSettings = null;
- }
- }
-
- private void unbindServiceIfNeeded() {
- final ComponentName serviceComponentName =
- TextClassifierService.getServiceComponentName(mContext,
- getTextClassifierSettings(mContext));
- if (serviceComponentName == null) {
- // It should not occur if we had defined default service name in config
- Slog.w(LOG_TAG, "No default configured system TextClassifierService.");
- return;
- }
- synchronized (mLock) {
- final int size = mUserStates.size();
- for (int i = 0; i < size; i++) {
- UserState userState = mUserStates.valueAt(i);
- // Only unbind for a new service
- if (userState.isServiceCurrentBoundLocked(serviceComponentName)) {
- return;
- }
- if (userState.isBoundLocked()) {
- userState.unbindLocked();
- }
- }
- }
- }
-
private final class UserState {
@UserIdInt final int mUserId;
@GuardedBy("mLock")
@@ -635,9 +593,9 @@ public final class TextClassificationManagerService extends ITextClassifierServi
@GuardedBy("mLock")
boolean mBinding;
@GuardedBy("mLock")
- ComponentName mBoundServiceComponent = null;
+ ComponentName mBoundComponentName = null;
@GuardedBy("mLock")
- boolean mIsBoundToDefaultService;
+ boolean mBoundToDefaultTrustService;
@GuardedBy("mLock")
int mBoundServiceUid;
@@ -660,10 +618,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
PendingRequest request;
while ((request = mPendingRequests.poll()) != null) {
if (isBoundLocked()) {
- if (!isRequestAcceptedLocked(request.mUid)) {
- Slog.d(LOG_TAG,
- "Only allow to see own content for non-default service at "
- + request.mName);
+ if (!checkRequestAcceptedLocked(request.mUid, request.mName)) {
return;
}
request.mRequest.run();
@@ -687,15 +642,15 @@ public final class TextClassificationManagerService extends ITextClassifierServi
}
@GuardedBy("mLock")
- private boolean isServiceCurrentBoundLocked(@NonNull ComponentName componentName) {
- return (mBoundServiceComponent != null
- && mBoundServiceComponent.getPackageName().equals(
+ private boolean isCurrentlyBoundToComponentLocked(@NonNull ComponentName componentName) {
+ return (mBoundComponentName != null
+ && mBoundComponentName.getPackageName().equals(
componentName.getPackageName()));
}
@GuardedBy("mLock")
private void unbindLocked() {
- Slog.d(LOG_TAG, "unbinding to " + mBoundServiceComponent + " for " + mUserId);
+ Slog.v(LOG_TAG, "unbinding from " + mBoundComponentName + " for " + mUserId);
mContext.unbindService(mConnection);
mConnection.cleanupService();
mConnection = null;
@@ -716,8 +671,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
final long identity = Binder.clearCallingIdentity();
try {
final ComponentName componentName =
- TextClassifierService.getServiceComponentName(mContext,
- getTextClassifierSettings(mContext));
+ TextClassifierService.getServiceComponentName(mContext);
if (componentName == null) {
// Might happen if the storage is encrypted and the user is not unlocked
return false;
@@ -742,8 +696,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
pw.printPair("context", mContext);
pw.printPair("userId", mUserId);
synchronized (mLock) {
- pw.printPair("BoundServiceComponent", mBoundServiceComponent);
- pw.printPair("isBoundToDefaultService", mIsBoundToDefaultService);
+ pw.printPair("boundComponentName", mBoundComponentName);
+ pw.printPair("boundToDefaultTrustService", mBoundToDefaultTrustService);
pw.printPair("boundServiceUid", mBoundServiceUid);
pw.printPair("binding", mBinding);
pw.printPair("numberRequests", mPendingRequests.size());
@@ -751,14 +705,17 @@ public final class TextClassificationManagerService extends ITextClassifierServi
}
@GuardedBy("mLock")
- private boolean isRequestAcceptedLocked(int requestUid) {
- if (mIsBoundToDefaultService) {
+ private boolean checkRequestAcceptedLocked(int requestUid, @NonNull String methodName) {
+ if (mBoundToDefaultTrustService || (requestUid == mBoundServiceUid)) {
return true;
}
- return (requestUid == mBoundServiceUid);
+ Slog.w(LOG_TAG, String.format(
+ "[%s] Non-default TextClassifierServices may only see text from the same uid.",
+ methodName));
+ return false;
}
- private boolean isDefaultService(@NonNull ComponentName currentService) {
+ private boolean isDefaultTrustService(@NonNull ComponentName currentService) {
final String[] defaultServiceNames =
mContext.getPackageManager().getSystemTextClassifierPackages();
final String servicePackageName = currentService.getPackageName();
@@ -789,16 +746,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi
}
@GuardedBy("mLock")
- private void updateServiceInfoLocked(@Nullable ComponentName componentName, int userId) {
- mBoundServiceComponent = componentName;
- mIsBoundToDefaultService = (mBoundServiceComponent != null && isDefaultService(
- mBoundServiceComponent));
- mBoundServiceUid = getServiceUid(mBoundServiceComponent, userId);
+ private void updateServiceInfoLocked(int userId, @Nullable ComponentName componentName) {
+ mBoundComponentName = componentName;
+ mBoundToDefaultTrustService = (mBoundComponentName != null && isDefaultTrustService(
+ mBoundComponentName));
+ mBoundServiceUid = getServiceUid(mBoundComponentName, userId);
}
private final class TextClassifierServiceConnection implements ServiceConnection {
- @UserIdInt final int mUserId;
+ @UserIdInt private final int mUserId;
TextClassifierServiceConnection(int userId) {
mUserId = userId;
@@ -840,60 +797,42 @@ public final class TextClassificationManagerService extends ITextClassifierServi
synchronized (mLock) {
mService = service;
mBinding = false;
- updateServiceInfoLocked(name, mUserId);
+ updateServiceInfoLocked(mUserId, name);
handlePendingRequestsLocked();
}
}
}
}
- private final class TextClassifierSettingsListener extends ContentObserver
- implements DeviceConfig.OnPropertiesChangedListener {
+ private final class TextClassifierSettingsListener implements
+ DeviceConfig.OnPropertiesChangedListener {
@NonNull private final Context mContext;
+ @NonNull private final TextClassificationConstants mSettings;
@Nullable private String mServicePackageName;
- TextClassifierSettingsListener(Context context, TextClassificationManagerService service) {
- super(null);
+ TextClassifierSettingsListener(Context context) {
mContext = context;
- mServicePackageName =
- getTextClassifierSettings(mContext).getTextClassifierServiceName();
+ mSettings = TextClassificationManager.getSettings(mContext);
+ mServicePackageName = mSettings.getTextClassifierServicePackageOverride();
}
public void registerObserver() {
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.TEXT_CLASSIFIER_CONSTANTS),
- false /* notifyForDescendants */,
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ mContext.getMainExecutor(),
this);
- if (ConfigParser.ENABLE_DEVICE_CONFIG) {
- DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- mContext.getMainExecutor(),
- this);
- }
}
- private void updateChange() {
- final String overrideServiceName = getTextClassifierSettings(
- mContext).getTextClassifierServiceName();
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ final String overrideServiceName = mSettings.getTextClassifierServicePackageOverride();
+
if (TextUtils.equals(overrideServiceName, mServicePackageName)) {
return;
}
mServicePackageName = overrideServiceName;
- unbindServiceIfNeeded();
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- super.onChange(selfChange, uri);
- invalidateSettings();
- updateChange();
- }
-
- @Override
- public void onPropertiesChanged(DeviceConfig.Properties properties) {
- invalidateSettings();
- updateChange();
+ unbindServiceIfNecessary();
}
}
}