Increase the timeout in SystemTextClassifier to 1 minute
TetxClassifier APIs are supposed to call on a worker thread
(@WorkerThread), so there is no reason to enforce a short timeout like
2s.
We are seeing timeouts in some emulator tests.
SuggestConversationActions takes around 3s on the emulator.
I can imagine this can also happen on slow device or the input is
sufficiently long.
Also
- Fixed a bug that forgot to call callback.onFailure() when the caller
is not allowed to see the request.
- mOnServiceFailure should be @NonNull. It is important to invoke
callback.onFailure() to notify the client that the request has just
failed. Currently, there is only callsite that creates the
PendingRequest object and it is not null.
Test: atest -p external/libtextclassifier --all-abi
Test: atest TextClassifierServiceSwapTest
Fixes: 171543321
Change-Id: I24ca3e8d116073650ecf588593fc1e4beba86a45
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 8eac1c1..2c844eb 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -89,7 +89,7 @@
try {
request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<TextSelection> callback =
- new BlockingCallback<>("textselection");
+ new BlockingCallback<>("textselection", mSettings);
mManagerService.onSuggestSelection(mSessionId, request, callback);
final TextSelection selection = callback.get();
if (selection != null) {
@@ -112,7 +112,7 @@
try {
request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<TextClassification> callback =
- new BlockingCallback<>("textclassification");
+ new BlockingCallback<>("textclassification", mSettings);
mManagerService.onClassifyText(mSessionId, request, callback);
final TextClassification classification = callback.get();
if (classification != null) {
@@ -142,7 +142,7 @@
try {
request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<TextLinks> callback =
- new BlockingCallback<>("textlinks");
+ new BlockingCallback<>("textlinks", mSettings);
mManagerService.onGenerateLinks(mSessionId, request, callback);
final TextLinks links = callback.get();
if (links != null) {
@@ -193,7 +193,7 @@
try {
request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<TextLanguage> callback =
- new BlockingCallback<>("textlanguage");
+ new BlockingCallback<>("textlanguage", mSettings);
mManagerService.onDetectLanguage(mSessionId, request, callback);
final TextLanguage textLanguage = callback.get();
if (textLanguage != null) {
@@ -213,7 +213,7 @@
try {
request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<ConversationActions> callback =
- new BlockingCallback<>("conversation-actions");
+ new BlockingCallback<>("conversation-actions", mSettings);
mManagerService.onSuggestConversationActions(mSessionId, request, callback);
final ConversationActions conversationActions = callback.get();
if (conversationActions != null) {
@@ -279,8 +279,8 @@
extends ITextClassifierCallback.Stub {
private final ResponseReceiver<T> mReceiver;
- BlockingCallback(String name) {
- mReceiver = new ResponseReceiver<>(name);
+ BlockingCallback(String name, TextClassificationConstants settings) {
+ mReceiver = new ResponseReceiver<>(name, settings);
}
@Override
@@ -303,10 +303,12 @@
private final CountDownLatch mLatch = new CountDownLatch(1);
private final String mName;
+ private final TextClassificationConstants mSettings;
private T mResponse;
- private ResponseReceiver(String name) {
+ private ResponseReceiver(String name, TextClassificationConstants settings) {
mName = name;
+ mSettings = settings;
}
public void onSuccess(T response) {
@@ -327,7 +329,9 @@
// NOTE that TextClassifier calls should preferably always be called on a worker thread.
if (Looper.myLooper() != Looper.getMainLooper()) {
try {
- boolean success = mLatch.await(2, TimeUnit.SECONDS);
+ boolean success = mLatch.await(
+ mSettings.getSystemTextClassifierApiTimeoutInSecond(),
+ TimeUnit.SECONDS);
if (!success) {
Log.w(LOG_TAG, "Timeout in ResponseReceiver.get(): " + mName);
}
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index adb6fea..2975afc 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -82,6 +82,14 @@
static final String TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE =
"textclassifier_service_package_override";
+ /**
+ * The timeout value in seconds used by {@link SystemTextClassifier} for each TextClassifier
+ * API calls.
+ */
+ @VisibleForTesting
+ static final String SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND =
+ "system_textclassifier_api_timeout_in_second";
+
private static final String DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = null;
private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
@@ -91,6 +99,7 @@
private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
private static final boolean SMART_SELECT_ANIMATION_ENABLED_DEFAULT = true;
private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
+ private static final long SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND_DEFAULT = 60;
@Nullable
public String getTextClassifierServicePackageOverride() {
@@ -140,27 +149,27 @@
GENERATE_LINKS_MAX_TEXT_LENGTH, GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
}
+ public long getSystemTextClassifierApiTimeoutInSecond() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND,
+ SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND_DEFAULT);
+ }
+
void dump(IndentingPrintWriter pw) {
pw.println("TextClassificationConstants:");
pw.increaseIndent();
- pw.printPair("generate_links_max_text_length", getGenerateLinksMaxTextLength())
- .println();
- pw.printPair("local_textclassifier_enabled", isLocalTextClassifierEnabled())
- .println();
- pw.printPair("model_dark_launch_enabled", isModelDarkLaunchEnabled())
- .println();
- pw.printPair("smart_linkify_enabled", isSmartLinkifyEnabled())
- .println();
- pw.printPair("smart_select_animation_enabled", isSmartSelectionAnimationEnabled())
- .println();
- pw.printPair("smart_selection_enabled", isSmartSelectionEnabled())
- .println();
- pw.printPair("smart_text_share_enabled", isSmartTextShareEnabled())
- .println();
- pw.printPair("system_textclassifier_enabled", isSystemTextClassifierEnabled())
- .println();
- pw.printPair("textclassifier_service_package_override",
+ pw.print(GENERATE_LINKS_MAX_TEXT_LENGTH, getGenerateLinksMaxTextLength()).println();
+ pw.print(LOCAL_TEXT_CLASSIFIER_ENABLED, isLocalTextClassifierEnabled()).println();
+ pw.print(MODEL_DARK_LAUNCH_ENABLED, isModelDarkLaunchEnabled()).println();
+ pw.print(SMART_LINKIFY_ENABLED, isSmartLinkifyEnabled()).println();
+ pw.print(SMART_SELECT_ANIMATION_ENABLED, isSmartSelectionAnimationEnabled()).println();
+ pw.print(SMART_SELECTION_ENABLED, isSmartSelectionEnabled()).println();
+ pw.print(SMART_TEXT_SHARE_ENABLED, isSmartTextShareEnabled()).println();
+ pw.print(SYSTEM_TEXT_CLASSIFIER_ENABLED, isSystemTextClassifierEnabled()).println();
+ pw.print(TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
getTextClassifierServicePackageOverride()).println();
+ pw.print(SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND,
+ getSystemTextClassifierApiTimeoutInSecond()).println();
pw.decreaseIndent();
}
}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index 2f21b7f..16e750a 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -16,7 +16,7 @@
package android.view.textclassifier;
-import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.common.truth.Truth.assertThat;
import android.provider.DeviceConfig;
@@ -26,73 +26,63 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.function.Consumer;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TextClassificationConstantsTest {
- private static final float EPSILON = 0.0001f;
@Test
- 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);
- }
+ public void booleanSettings() {
+ assertSettings(
+ TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED,
+ "false",
+ settings -> assertThat(settings.isLocalTextClassifierEnabled()).isFalse());
}
@Test
- public void testLoadFromDeviceConfig_IntValue() throws Exception {
- // Saves config original value.
- final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH);
-
- final TextClassificationConstants constants = new TextClassificationConstants();
- try {
- // Sets and checks different value.
- setDeviceConfig(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH, "8");
- assertWithMessage(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH)
- .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(8);
- } finally {
- // Restores config original value.
- setDeviceConfig(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH,
- originalValue);
- }
+ public void intSettings() {
+ assertSettings(
+ TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH,
+ "128",
+ settings -> assertThat(settings.getGenerateLinksMaxTextLength()).isEqualTo(128));
}
@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);
+ public void stringSettings() {
+ assertSettings(
+ TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
+ "com.example.textclassifier",
+ settings -> assertThat(
+ settings.getTextClassifierServicePackageOverride())
+ .isEqualTo("com.example.textclassifier"));
+ }
- final TextClassificationConstants constants = new TextClassificationConstants();
+ @Test
+ public void longSettings() {
+ assertSettings(
+ TextClassificationConstants.SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND,
+ "1",
+ settings -> assertThat(
+ settings.getSystemTextClassifierApiTimeoutInSecond())
+ .isEqualTo(1));
+ }
+
+ private static void assertSettings(
+ String key, String value, Consumer<TextClassificationConstants> settingsConsumer) {
+ final String originalValue =
+ DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key);
+ TextClassificationConstants settings = 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);
+ setDeviceConfig(key, value);
+ settingsConsumer.accept(settings);
} finally {
- // Restores config original value.
- setDeviceConfig(TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
- originalValue);
+ setDeviceConfig(key, originalValue);
}
}
- private void setDeviceConfig(String key, String value) {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key,
- value, /* makeDefault */ false);
+ private static void setDeviceConfig(String key, String value) {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, value, /* makeDefault */ false);
}
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 363e86d..93ba758 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -66,7 +66,6 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -458,6 +457,9 @@
callback.onFailure();
} else if (serviceState.isBoundLocked()) {
if (!serviceState.checkRequestAcceptedLocked(Binder.getCallingUid(), methodName)) {
+ Slog.w(LOG_TAG, String.format("UID %d is not allowed to see the %s request",
+ Binder.getCallingUid(), methodName));
+ callback.onFailure();
return;
}
textClassifierServiceConsumer.accept(serviceState.mService);
@@ -497,7 +499,7 @@
private final IBinder mBinder;
@NonNull
private final Runnable mRequest;
- @Nullable
+ @NonNull
private final Runnable mOnServiceFailure;
@GuardedBy("mLock")
@NonNull
@@ -516,7 +518,7 @@
* @param uid the calling uid of the request.
*/
PendingRequest(@Nullable String name,
- @NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure,
+ @NonNull ThrowingRunnable request, @NonNull ThrowingRunnable onServiceFailure,
@Nullable IBinder binder,
@NonNull TextClassificationManagerService service,
@NonNull ServiceState serviceState, int uid) {
@@ -524,7 +526,8 @@
mRequest =
logOnFailure(Objects.requireNonNull(request), "handling pending request");
mOnServiceFailure =
- logOnFailure(onServiceFailure, "notifying callback of service failure");
+ logOnFailure(Objects.requireNonNull(onServiceFailure),
+ "notifying callback of service failure");
mBinder = binder;
mService = service;
mServiceState = Objects.requireNonNull(serviceState);
@@ -799,9 +802,7 @@
request -> {
Slog.w(LOG_TAG,
String.format("Pending request[%s] is dropped", request.mName));
- if (request.mOnServiceFailure != null) {
- request.mOnServiceFailure.run();
- }
+ request.mOnServiceFailure.run();
});
@Nullable
@GuardedBy("mLock")
@@ -843,15 +844,16 @@
while ((request = mPendingRequests.poll()) != null) {
if (isBoundLocked()) {
if (!checkRequestAcceptedLocked(request.mUid, request.mName)) {
- return;
- }
- request.mRequest.run();
- } else {
- if (request.mOnServiceFailure != null) {
- Slog.d(LOG_TAG, "Unable to bind TextClassifierService for PendingRequest "
- + request.mName);
+ Slog.w(LOG_TAG, String.format("UID %d is not allowed to see the %s request",
+ request.mUid, request.mName));
request.mOnServiceFailure.run();
+ } else {
+ request.mRequest.run();
}
+ } else {
+ Slog.d(LOG_TAG, "Unable to bind TextClassifierService for PendingRequest "
+ + request.mName);
+ request.mOnServiceFailure.run();
}
if (request.mBinder != null) {