Merge "Adding maximum input size checks"
This commit is contained in:
committed by
Android (Google) Code Review
commit
1d84b1734d
@@ -50313,6 +50313,7 @@ package android.view.textclassifier {
|
||||
method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence);
|
||||
method public default java.util.Collection<java.lang.String> getEntitiesForPreset(int);
|
||||
method public default android.view.textclassifier.logging.Logger getLogger(android.view.textclassifier.logging.Logger.Config);
|
||||
method public default int getMaxGenerateLinksTextLength();
|
||||
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options);
|
||||
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
|
||||
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
|
||||
|
||||
@@ -10457,6 +10457,9 @@ public final class Settings {
|
||||
* <pre>
|
||||
* smart_selection_dark_launch (boolean)
|
||||
* smart_selection_enabled_for_edit_text (boolean)
|
||||
* suggest_selection_max_range_length (int)
|
||||
* classify_text_max_range_length (int)
|
||||
* generate_links_max_text_length (int)
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
|
||||
@@ -644,7 +644,13 @@ public class Linkify {
|
||||
@Nullable Runnable modifyTextView) {
|
||||
Preconditions.checkNotNull(text);
|
||||
Preconditions.checkNotNull(classifier);
|
||||
final Supplier<TextLinks> supplier = () -> classifier.generateLinks(text, options);
|
||||
|
||||
// The input text may exceed the maximum length the text classifier can handle. In such
|
||||
// cases, we process the text up to the maximum length.
|
||||
final CharSequence truncatedText = text.subSequence(
|
||||
0, Math.min(text.length(), classifier.getMaxGenerateLinksTextLength()));
|
||||
|
||||
final Supplier<TextLinks> supplier = () -> classifier.generateLinks(truncatedText, options);
|
||||
final Consumer<TextLinks> consumer = links -> {
|
||||
if (links.getLinks().isEmpty()) {
|
||||
if (callback != null) {
|
||||
@@ -653,7 +659,8 @@ public class Linkify {
|
||||
return;
|
||||
}
|
||||
|
||||
final TextLinkSpan[] old = text.getSpans(0, text.length(), TextLinkSpan.class);
|
||||
// Remove spans only for the part of the text we generated links for.
|
||||
final TextLinkSpan[] old = text.getSpans(0, truncatedText.length(), TextLinkSpan.class);
|
||||
for (int i = old.length - 1; i >= 0; i--) {
|
||||
text.removeSpan(old[i]);
|
||||
}
|
||||
@@ -662,7 +669,8 @@ public class Linkify {
|
||||
? null : options.getSpanFactory();
|
||||
final @TextLinks.ApplyStrategy int applyStrategy = (options == null)
|
||||
? TextLinks.APPLY_STRATEGY_IGNORE : options.getApplyStrategy();
|
||||
final @TextLinks.Status int result = links.apply(text, applyStrategy, spanFactory);
|
||||
final @TextLinks.Status int result = links.apply(text, applyStrategy, spanFactory,
|
||||
true /*allowPrefix*/);
|
||||
if (result == TextLinks.STATUS_LINKS_APPLIED) {
|
||||
if (modifyTextView != null) {
|
||||
modifyTextView.run();
|
||||
|
||||
@@ -121,6 +121,15 @@ final class SystemTextClassifier implements TextClassifier {
|
||||
return mFallback.generateLinks(text, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public int getMaxGenerateLinksTextLength() {
|
||||
// TODO: retrieve this from the bound service.
|
||||
return mFallback.getMaxGenerateLinksTextLength();
|
||||
}
|
||||
|
||||
private static final class TextSelectionCallback extends ITextSelectionCallback.Stub {
|
||||
|
||||
final ResponseReceiver<TextSelection> mReceiver = new ResponseReceiver<>();
|
||||
|
||||
@@ -276,9 +276,11 @@ public interface TextClassifier {
|
||||
* @param text the text to generate annotations for
|
||||
* @param options configuration for link generation
|
||||
*
|
||||
* @throws IllegalArgumentException if text is null
|
||||
* @throws IllegalArgumentException if text is null or the text is too long for the
|
||||
* TextClassifier implementation.
|
||||
*
|
||||
* @see #generateLinks(CharSequence)
|
||||
* @see #getMaxGenerateLinksTextLength()
|
||||
*/
|
||||
@WorkerThread
|
||||
default TextLinks generateLinks(
|
||||
@@ -299,15 +301,27 @@ public interface TextClassifier {
|
||||
*
|
||||
* @param text the text to generate annotations for
|
||||
*
|
||||
* @throws IllegalArgumentException if text is null
|
||||
* @throws IllegalArgumentException if text is null or the text is too long for the
|
||||
* TextClassifier implementation.
|
||||
*
|
||||
* @see #generateLinks(CharSequence, TextLinks.Options)
|
||||
* @see #getMaxGenerateLinksTextLength()
|
||||
*/
|
||||
@WorkerThread
|
||||
default TextLinks generateLinks(@NonNull CharSequence text) {
|
||||
return generateLinks(text, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximal length of text that can be processed by generateLinks.
|
||||
*
|
||||
* @see #generateLinks(CharSequence)
|
||||
* @see #generateLinks(CharSequence, TextLinks.Options)
|
||||
*/
|
||||
default int getMaxGenerateLinksTextLength() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Collection} of the entity types in the specified preset.
|
||||
*
|
||||
@@ -461,6 +475,15 @@ public interface TextClassifier {
|
||||
checkMainThread(allowInMainThread);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if text is null; the text is too long or options is null
|
||||
*/
|
||||
public static void validate(@NonNull CharSequence text, int maxLength,
|
||||
boolean allowInMainThread) {
|
||||
validate(text, allowInMainThread);
|
||||
Preconditions.checkArgumentInRange(text.length(), 0, maxLength, "text.length()");
|
||||
}
|
||||
|
||||
private static void checkMainThread(boolean allowInMainThread) {
|
||||
if (!allowInMainThread && Looper.myLooper() == Looper.getMainLooper()) {
|
||||
Slog.w(DEFAULT_LOG_TAG, "TextClassifier called on main thread");
|
||||
|
||||
@@ -47,10 +47,19 @@ public final class TextClassifierConstants {
|
||||
"smart_selection_enabled_for_edit_text";
|
||||
private static final String SMART_LINKIFY_ENABLED =
|
||||
"smart_linkify_enabled";
|
||||
private static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
|
||||
"suggest_selection_max_range_length";
|
||||
private static final String CLASSIFY_TEXT_MAX_RANGE_LENGTH =
|
||||
"classify_text_max_range_length";
|
||||
private static final String GENERATE_LINKS_MAX_TEXT_LENGTH =
|
||||
"generate_links_max_text_length";
|
||||
|
||||
private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false;
|
||||
private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true;
|
||||
private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
|
||||
private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
|
||||
private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
|
||||
private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
|
||||
|
||||
/** Default settings. */
|
||||
static final TextClassifierConstants DEFAULT = new TextClassifierConstants();
|
||||
@@ -58,11 +67,17 @@ public final class TextClassifierConstants {
|
||||
private final boolean mDarkLaunch;
|
||||
private final boolean mSuggestSelectionEnabledForEditableText;
|
||||
private final boolean mSmartLinkifyEnabled;
|
||||
private final int mSuggestSelectionMaxRangeLength;
|
||||
private final int mClassifyTextMaxRangeLength;
|
||||
private final int mGenerateLinksMaxTextLength;
|
||||
|
||||
private TextClassifierConstants() {
|
||||
mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT;
|
||||
mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT;
|
||||
mSmartLinkifyEnabled = SMART_LINKIFY_ENABLED_DEFAULT;
|
||||
mSuggestSelectionMaxRangeLength = SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT;
|
||||
mClassifyTextMaxRangeLength = CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT;
|
||||
mGenerateLinksMaxTextLength = GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT;
|
||||
}
|
||||
|
||||
private TextClassifierConstants(@Nullable String settings) {
|
||||
@@ -82,6 +97,15 @@ public final class TextClassifierConstants {
|
||||
mSmartLinkifyEnabled = parser.getBoolean(
|
||||
SMART_LINKIFY_ENABLED,
|
||||
SMART_LINKIFY_ENABLED_DEFAULT);
|
||||
mSuggestSelectionMaxRangeLength = parser.getInt(
|
||||
SUGGEST_SELECTION_MAX_RANGE_LENGTH,
|
||||
SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
|
||||
mClassifyTextMaxRangeLength = parser.getInt(
|
||||
CLASSIFY_TEXT_MAX_RANGE_LENGTH,
|
||||
CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
|
||||
mGenerateLinksMaxTextLength = parser.getInt(
|
||||
GENERATE_LINKS_MAX_TEXT_LENGTH,
|
||||
GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
|
||||
}
|
||||
|
||||
static TextClassifierConstants loadFromString(String settings) {
|
||||
@@ -99,4 +123,16 @@ public final class TextClassifierConstants {
|
||||
public boolean isSmartLinkifyEnabled() {
|
||||
return mSmartLinkifyEnabled;
|
||||
}
|
||||
|
||||
public int getSuggestSelectionMaxRangeLength() {
|
||||
return mSuggestSelectionMaxRangeLength;
|
||||
}
|
||||
|
||||
public int getClassifyTextMaxRangeLength() {
|
||||
return mClassifyTextMaxRangeLength;
|
||||
}
|
||||
|
||||
public int getGenerateLinksMaxTextLength() {
|
||||
return mGenerateLinksMaxTextLength;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,9 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
@Nullable TextSelection.Options options) {
|
||||
Utils.validate(text, selectionStartIndex, selectionEndIndex, false /* allowInMainThread */);
|
||||
try {
|
||||
if (text.length() > 0) {
|
||||
final int rangeLength = selectionEndIndex - selectionStartIndex;
|
||||
if (text.length() > 0
|
||||
&& rangeLength <= getSettings().getSuggestSelectionMaxRangeLength()) {
|
||||
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
|
||||
final boolean darkLaunchAllowed = options != null && options.isDarkLaunchAllowed();
|
||||
final SmartSelection smartSelection = getSmartSelection(locales);
|
||||
@@ -183,7 +185,8 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
@Nullable TextClassification.Options options) {
|
||||
Utils.validate(text, startIndex, endIndex, false /* allowInMainThread */);
|
||||
try {
|
||||
if (text.length() > 0) {
|
||||
final int rangeLength = endIndex - startIndex;
|
||||
if (text.length() > 0 && rangeLength <= getSettings().getClassifyTextMaxRangeLength()) {
|
||||
final String string = text.toString();
|
||||
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
|
||||
final Calendar refTime = (options == null) ? null : options.getReferenceTime();
|
||||
@@ -207,7 +210,7 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
@Override
|
||||
public TextLinks generateLinks(
|
||||
@NonNull CharSequence text, @Nullable TextLinks.Options options) {
|
||||
Utils.validate(text, false /* allowInMainThread */);
|
||||
Utils.validate(text, getMaxGenerateLinksTextLength(), false /* allowInMainThread */);
|
||||
final String textString = text.toString();
|
||||
final TextLinks.Builder builder = new TextLinks.Builder(textString);
|
||||
|
||||
@@ -241,6 +244,12 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
return mFallback.generateLinks(text, options);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
@Override
|
||||
public int getMaxGenerateLinksTextLength() {
|
||||
return getSettings().getGenerateLinksMaxTextLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getEntitiesForPreset(@TextClassifier.EntityPreset int entityPreset) {
|
||||
switch (entityPreset) {
|
||||
|
||||
@@ -108,6 +108,7 @@ public final class TextLinks implements Parcelable {
|
||||
* @param text the text to apply the links to. Must match the original text
|
||||
* @param applyStrategy strategy for resolving link conflicts
|
||||
* @param spanFactory a factory to generate spans from TextLinks. Will use a default if null
|
||||
* @param allowPrefix whether to allow applying links only to a prefix of the text.
|
||||
*
|
||||
* @return a status code indicating whether or not the links were successfully applied
|
||||
*
|
||||
@@ -117,10 +118,12 @@ public final class TextLinks implements Parcelable {
|
||||
public int apply(
|
||||
@NonNull Spannable text,
|
||||
@ApplyStrategy int applyStrategy,
|
||||
@Nullable Function<TextLink, TextLinkSpan> spanFactory) {
|
||||
@Nullable Function<TextLink, TextLinkSpan> spanFactory,
|
||||
boolean allowPrefix) {
|
||||
Preconditions.checkNotNull(text);
|
||||
checkValidApplyStrategy(applyStrategy);
|
||||
if (!mFullText.equals(text.toString())) {
|
||||
final String textString = text.toString();
|
||||
if (!mFullText.equals(textString) && !(allowPrefix && textString.startsWith(mFullText))) {
|
||||
return STATUS_DIFFERENT_TEXT;
|
||||
}
|
||||
if (mLinks.isEmpty()) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package android.view.textclassifier;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.os.LocaleList;
|
||||
@@ -33,6 +34,8 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TextClassificationManagerTest {
|
||||
@@ -183,6 +186,7 @@ public class TextClassificationManagerTest {
|
||||
@Test
|
||||
public void testGenerateLinks_none_config() {
|
||||
if (isTextClassifierDisabled()) return;
|
||||
|
||||
String text = "The number is +12122537077. See you tonight!";
|
||||
assertThat(mClassifier.generateLinks(text, mLinksOptions.setEntityConfig(
|
||||
new TextClassifier.EntityConfig(TextClassifier.ENTITY_PRESET_NONE))),
|
||||
@@ -209,6 +213,25 @@ public class TextClassificationManagerTest {
|
||||
TextClassifier.TYPE_ADDRESS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateLinks_maxLength() {
|
||||
if (isTextClassifierDisabled()) return;
|
||||
char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength()];
|
||||
Arrays.fill(manySpaces, ' ');
|
||||
TextLinks links = mClassifier.generateLinks(new String(manySpaces), null);
|
||||
assertTrue(links.getLinks().isEmpty());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testGenerateLinks_tooLong() {
|
||||
if (isTextClassifierDisabled()) {
|
||||
throw new IllegalArgumentException("pass if disabled");
|
||||
}
|
||||
char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1];
|
||||
Arrays.fill(manySpaces, ' ');
|
||||
mClassifier.generateLinks(new String(manySpaces), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetTextClassifier() {
|
||||
TextClassifier classifier = mock(TextClassifier.class);
|
||||
|
||||
@@ -382,7 +382,7 @@ public class TextViewActivityTest {
|
||||
TextClassifier textClassifier = textClassificationManager.getTextClassifier();
|
||||
Spannable content = new SpannableString("Call me at +19148277737");
|
||||
TextLinks links = textClassifier.generateLinks(content);
|
||||
links.apply(content, TextLinks.APPLY_STRATEGY_REPLACE, null);
|
||||
links.apply(content, TextLinks.APPLY_STRATEGY_REPLACE, null, false /* allowPrefix */);
|
||||
|
||||
mActivityRule.runOnUiThread(() -> {
|
||||
textView.setText(content);
|
||||
|
||||
Reference in New Issue
Block a user