Add calling package name to TC requests.
- This is only set by the SystemTextClassifier.
- The system verifies that the package name is correct before
passing the request to the TCS. Note that the system is the
only direct client of the TCS.
- Sets package name to context.getOpPackageName(). See b/119921159
- Also adds an extra to a TextLink object.
Bug: 118690735
Bug: 119921159
Test: atest frameworks/base/core/tests/coretests/src/android/view/textclassifier
Change-Id: I1ac0fa421cd0f26e580a8e0768c3819a731bb471
diff --git a/api/current.txt b/api/current.txt
index 3967896..3422030 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -52044,6 +52044,7 @@
public static final class ConversationActions.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public java.util.List<android.view.textclassifier.ConversationActions.Message> getConversation();
method public java.util.List<java.lang.String> getHints();
method public int getMaxSuggestions();
@@ -52157,6 +52158,7 @@
public static final class TextClassification.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public android.os.LocaleList getDefaultLocales();
method public int getEndIndex();
method public android.os.Bundle getExtras();
@@ -52274,6 +52276,7 @@
public static final class TextLanguage.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public android.os.Bundle getExtras();
method public java.lang.CharSequence getText();
method public void writeToParcel(android.os.Parcel, int);
@@ -52305,6 +52308,7 @@
public static final class TextLinks.Builder {
ctor public TextLinks.Builder(java.lang.String);
method public android.view.textclassifier.TextLinks.Builder addLink(int, int, java.util.Map<java.lang.String, java.lang.Float>);
+ method public android.view.textclassifier.TextLinks.Builder addLink(int, int, java.util.Map<java.lang.String, java.lang.Float>, android.os.Bundle);
method public android.view.textclassifier.TextLinks build();
method public android.view.textclassifier.TextLinks.Builder clearTextLinks();
method public android.view.textclassifier.TextLinks.Builder setExtras(android.os.Bundle);
@@ -52312,6 +52316,7 @@
public static final class TextLinks.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextClassifier.EntityConfig getEntityConfig();
method public android.os.Bundle getExtras();
@@ -52334,6 +52339,7 @@
method public int getEnd();
method public java.lang.String getEntity(int);
method public int getEntityCount();
+ method public android.os.Bundle getExtras();
method public int getStart();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks.TextLink> CREATOR;
@@ -52368,6 +52374,7 @@
public static final class TextSelection.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public android.os.LocaleList getDefaultLocales();
method public int getEndIndex();
method public android.os.Bundle getExtras();
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 1a7b911..04b94b0 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -30,6 +30,7 @@
import android.text.SpannedString;
import android.util.ArraySet;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -654,6 +655,8 @@
@NonNull
@Hint
private final List<String> mHints;
+ @Nullable
+ private String mCallingPackageName;
private Request(
@NonNull List<Message> conversation,
@@ -666,15 +669,26 @@
mHints = hints;
}
- private Request(Parcel in) {
+ private static Request readFromParcel(Parcel in) {
List<Message> conversation = new ArrayList<>();
in.readParcelableList(conversation, null);
- mConversation = Collections.unmodifiableList(conversation);
- mTypeConfig = in.readParcelable(null);
- mMaxSuggestions = in.readInt();
+
+ TypeConfig typeConfig = in.readParcelable(null);
+
+ int maxSuggestions = in.readInt();
+
List<String> hints = new ArrayList<>();
in.readStringList(hints);
- mHints = Collections.unmodifiableList(hints);
+
+ String callingPackageName = in.readString();
+
+ Request request = new Request(
+ conversation,
+ typeConfig,
+ maxSuggestions,
+ hints);
+ request.setCallingPackageName(callingPackageName);
+ return request;
}
@Override
@@ -683,6 +697,7 @@
parcel.writeParcelable(mTypeConfig, flags);
parcel.writeInt(mMaxSuggestions);
parcel.writeStringList(mHints);
+ parcel.writeString(mCallingPackageName);
}
@Override
@@ -694,7 +709,7 @@
new Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
- return new Request(in);
+ return readFromParcel(in);
}
@Override
@@ -730,6 +745,26 @@
return mHints;
}
+ /**
+ * Sets the name of the package that is sending this request.
+ * <p>
+ * Package-private for SystemTextClassifier's use.
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
+ mCallingPackageName = callingPackageName;
+ }
+
+ /**
+ * Returns the name of the package that sent this request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
/** Builder object to construct the {@link Request} object. */
public static final class Builder {
@NonNull
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index f8fce62..c24489c 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -60,7 +60,7 @@
mSettings = Preconditions.checkNotNull(settings);
mFallback = context.getSystemService(TextClassificationManager.class)
.getTextClassifier(TextClassifier.LOCAL);
- mPackageName = Preconditions.checkNotNull(context.getPackageName());
+ mPackageName = Preconditions.checkNotNull(context.getOpPackageName());
}
/**
@@ -72,6 +72,7 @@
Preconditions.checkNotNull(request);
Utils.checkMainThread();
try {
+ request.setCallingPackageName(mPackageName);
final TextSelectionCallback callback = new TextSelectionCallback();
mManagerService.onSuggestSelection(mSessionId, request, callback);
final TextSelection selection = callback.mReceiver.get();
@@ -93,6 +94,7 @@
Preconditions.checkNotNull(request);
Utils.checkMainThread();
try {
+ request.setCallingPackageName(mPackageName);
final TextClassificationCallback callback = new TextClassificationCallback();
mManagerService.onClassifyText(mSessionId, request, callback);
final TextClassification classification = callback.mReceiver.get();
@@ -150,6 +152,7 @@
Utils.checkMainThread();
try {
+ request.setCallingPackageName(mPackageName);
final TextLanguageCallback callback = new TextLanguageCallback();
mManagerService.onDetectLanguage(mSessionId, request, callback);
final TextLanguage textLanguage = callback.mReceiver.get();
@@ -168,6 +171,7 @@
Utils.checkMainThread();
try {
+ request.setCallingPackageName(mPackageName);
final ConversationActionsCallback callback = new ConversationActionsCallback();
mManagerService.onSuggestConversationActions(mSessionId, request, callback);
final ConversationActions conversationActions = callback.mReceiver.get();
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index e0910c0..d9f7965 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -37,11 +37,13 @@
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.SpannedString;
import android.util.ArrayMap;
import android.view.View.OnClickListener;
import android.view.textclassifier.TextClassifier.EntityType;
import android.view.textclassifier.TextClassifier.Utils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -518,6 +520,7 @@
@Nullable private final LocaleList mDefaultLocales;
@Nullable private final ZonedDateTime mReferenceTime;
@NonNull private final Bundle mExtras;
+ @Nullable private String mCallingPackageName;
private Request(
CharSequence text,
@@ -578,6 +581,26 @@
}
/**
+ * Sets the name of the package that is sending this request.
+ * <p>
+ * For SystemTextClassifier's use.
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
+ mCallingPackageName = callingPackageName;
+ }
+
+ /**
+ * Returns the name of the package that sent this request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -660,7 +683,8 @@
*/
@NonNull
public Request build() {
- return new Request(mText, mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime,
+ return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
+ mDefaultLocales, mReferenceTime,
mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
}
@@ -672,25 +696,37 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mText.toString());
+ dest.writeCharSequence(mText);
dest.writeInt(mStartIndex);
dest.writeInt(mEndIndex);
- dest.writeInt(mDefaultLocales != null ? 1 : 0);
- if (mDefaultLocales != null) {
- mDefaultLocales.writeToParcel(dest, flags);
- }
- dest.writeInt(mReferenceTime != null ? 1 : 0);
- if (mReferenceTime != null) {
- dest.writeString(mReferenceTime.toString());
- }
+ dest.writeParcelable(mDefaultLocales, flags);
+ dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
+ dest.writeString(mCallingPackageName);
dest.writeBundle(mExtras);
}
+ private static Request readFromParcel(Parcel in) {
+ final CharSequence text = in.readCharSequence();
+ final int startIndex = in.readInt();
+ final int endIndex = in.readInt();
+ final LocaleList defaultLocales = in.readParcelable(null);
+ final String referenceTimeString = in.readString();
+ final ZonedDateTime referenceTime = referenceTimeString == null
+ ? null : ZonedDateTime.parse(referenceTimeString);
+ final String callingPackageName = in.readString();
+ final Bundle extras = in.readBundle();
+
+ final Request request = new Request(text, startIndex, endIndex,
+ defaultLocales, referenceTime, extras);
+ request.setCallingPackageName(callingPackageName);
+ return request;
+ }
+
public static final Parcelable.Creator<Request> CREATOR =
new Parcelable.Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
- return new Request(in);
+ return readFromParcel(in);
}
@Override
@@ -698,15 +734,6 @@
return new Request[size];
}
};
-
- private Request(Parcel in) {
- mText = in.readString();
- mStartIndex = in.readInt();
- mEndIndex = in.readInt();
- mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
- mReferenceTime = in.readInt() == 0 ? null : ZonedDateTime.parse(in.readString());
- mExtras = in.readBundle();
- }
}
@Override
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index d28459e..b1609fc 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -26,6 +26,7 @@
import android.os.Parcelable;
import android.util.ArrayMap;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.util.Locale;
@@ -90,7 +91,7 @@
* confidence to low confidence.
*
* @throws IndexOutOfBoundsException if the specified index is out of range.
- * @see #getLocaleCount() for the number of locales available.
+ * @see #getLocaleHypothesisCount() for the number of locales available.
*/
@NonNull
public ULocale getLocale(int index) {
@@ -222,11 +223,12 @@
};
private final CharSequence mText;
- private final Bundle mBundle;
+ private final Bundle mExtra;
+ @Nullable private String mCallingPackageName;
private Request(CharSequence text, Bundle bundle) {
mText = text;
- mBundle = bundle;
+ mExtra = bundle;
}
/**
@@ -238,6 +240,25 @@
}
/**
+ * Sets the name of the package that is sending this request.
+ * Package-private for SystemTextClassifier's use.
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
+ mCallingPackageName = callingPackageName;
+ }
+
+ /**
+ * Returns the name of the package that sent this request.
+ * This returns null if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
* Returns a bundle containing non-structured extra information about this request.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -246,7 +267,7 @@
*/
@NonNull
public Bundle getExtras() {
- return mBundle.deepCopy();
+ return mExtra.deepCopy();
}
@Override
@@ -257,13 +278,18 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeCharSequence(mText);
- dest.writeBundle(mBundle);
+ dest.writeString(mCallingPackageName);
+ dest.writeBundle(mExtra);
}
private static Request readFromParcel(Parcel in) {
- return new Request(
- in.readCharSequence(),
- in.readBundle());
+ final CharSequence text = in.readCharSequence();
+ final String callingPackageName = in.readString();
+ final Bundle extra = in.readBundle();
+
+ final Request request = new Request(text, extra);
+ request.setCallingPackageName(callingPackageName);
+ return request;
}
/**
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 1e42c41..ab34178 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -30,6 +30,7 @@
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.view.View;
+import android.view.textclassifier.TextClassifier.EntityConfig;
import android.view.textclassifier.TextClassifier.EntityType;
import android.widget.TextView;
@@ -205,27 +206,32 @@
private final EntityConfidence mEntityScores;
private final int mStart;
private final int mEnd;
- @Nullable final URLSpan mUrlSpan;
+ private final Bundle mExtras;
+ @Nullable private final URLSpan mUrlSpan;
/**
* Create a new TextLink.
*
* @param start The start index of the identified subsequence
* @param end The end index of the identified subsequence
- * @param entityScores A mapping of entity type to confidence score
+ * @param entityConfidence A mapping of entity type to confidence score
+ * @param extras A bundle containing custom data related to this TextLink
* @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled
*
- * @throws IllegalArgumentException if entityScores is null or empty
+ * @throws IllegalArgumentException if {@code entityConfidence} is null or empty
+ * @throws IllegalArgumentException if {@code start} is greater than {@code end}
*/
- TextLink(int start, int end, Map<String, Float> entityScores,
- @Nullable URLSpan urlSpan) {
- Preconditions.checkNotNull(entityScores);
- Preconditions.checkArgument(!entityScores.isEmpty());
+ private TextLink(int start, int end, @NonNull EntityConfidence entityConfidence,
+ @NonNull Bundle extras, @Nullable URLSpan urlSpan) {
+ Preconditions.checkNotNull(entityConfidence);
+ Preconditions.checkArgument(!entityConfidence.getEntities().isEmpty());
Preconditions.checkArgument(start <= end);
+ Preconditions.checkNotNull(extras);
mStart = start;
mEnd = end;
- mEntityScores = new EntityConfidence(entityScores);
+ mEntityScores = entityConfidence;
mUrlSpan = urlSpan;
+ mExtras = extras;
}
/**
@@ -274,6 +280,13 @@
return mEntityScores.getConfidenceScore(entityType);
}
+ /**
+ * Returns a bundle containing custom data related to this TextLink.
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
@Override
public String toString() {
return String.format(Locale.US,
@@ -291,13 +304,22 @@
mEntityScores.writeToParcel(dest, flags);
dest.writeInt(mStart);
dest.writeInt(mEnd);
+ dest.writeBundle(mExtras);
+ }
+
+ private static TextLink readFromParcel(Parcel in) {
+ final EntityConfidence entityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+ final int start = in.readInt();
+ final int end = in.readInt();
+ final Bundle extras = in.readBundle();
+ return new TextLink(start, end, entityConfidence, extras, null /* urlSpan */);
}
public static final Parcelable.Creator<TextLink> CREATOR =
new Parcelable.Creator<TextLink>() {
@Override
public TextLink createFromParcel(Parcel in) {
- return new TextLink(in);
+ return readFromParcel(in);
}
@Override
@@ -305,13 +327,6 @@
return new TextLink[size];
}
};
-
- private TextLink(Parcel in) {
- mEntityScores = EntityConfidence.CREATOR.createFromParcel(in);
- mStart = in.readInt();
- mEnd = in.readInt();
- mUrlSpan = null;
- }
}
/**
@@ -321,23 +336,21 @@
private final CharSequence mText;
@Nullable private final LocaleList mDefaultLocales;
- @Nullable private final TextClassifier.EntityConfig mEntityConfig;
+ @Nullable private final EntityConfig mEntityConfig;
private final boolean mLegacyFallback;
- private String mCallingPackageName;
+ @Nullable private String mCallingPackageName;
private final Bundle mExtras;
private Request(
CharSequence text,
LocaleList defaultLocales,
- TextClassifier.EntityConfig entityConfig,
+ EntityConfig entityConfig,
boolean legacyFallback,
- String callingPackageName,
Bundle extras) {
mText = text;
mDefaultLocales = defaultLocales;
mEntityConfig = entityConfig;
mLegacyFallback = legacyFallback;
- mCallingPackageName = callingPackageName;
mExtras = extras;
}
@@ -360,10 +373,10 @@
/**
* @return The config representing the set of entities to look for
- * @see Builder#setEntityConfig(TextClassifier.EntityConfig)
+ * @see Builder#setEntityConfig(EntityConfig)
*/
@Nullable
- public TextClassifier.EntityConfig getEntityConfig() {
+ public EntityConfig getEntityConfig() {
return mEntityConfig;
}
@@ -378,13 +391,26 @@
}
/**
- * Sets the name of the package that requested the links to get generated.
+ * Sets the name of the package that is sending this request.
+ * <p>
+ * Package-private for SystemTextClassifier's use.
+ * @hide
*/
- void setCallingPackageName(@Nullable String callingPackageName) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
mCallingPackageName = callingPackageName;
}
/**
+ * Returns the name of the package that sent this request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -404,9 +430,8 @@
private final CharSequence mText;
@Nullable private LocaleList mDefaultLocales;
- @Nullable private TextClassifier.EntityConfig mEntityConfig;
+ @Nullable private EntityConfig mEntityConfig;
private boolean mLegacyFallback = true; // Use legacy fall back by default.
- private String mCallingPackageName;
@Nullable private Bundle mExtras;
public Builder(@NonNull CharSequence text) {
@@ -434,7 +459,7 @@
* @return this builder
*/
@NonNull
- public Builder setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) {
+ public Builder setEntityConfig(@Nullable EntityConfig entityConfig) {
mEntityConfig = entityConfig;
return this;
}
@@ -455,18 +480,6 @@
}
/**
- * Sets the name of the package that requested the links to get generated.
- *
- * @return this builder
- * @hide
- */
- @NonNull
- public Builder setCallingPackageName(@Nullable String callingPackageName) {
- mCallingPackageName = callingPackageName;
- return this;
- }
-
- /**
* Sets the extended data.
*
* @return this builder
@@ -483,21 +496,11 @@
public Request build() {
return new Request(
mText, mDefaultLocales, mEntityConfig,
- mLegacyFallback, mCallingPackageName,
+ mLegacyFallback,
mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
}
- /**
- * @return the name of the package that requested the links to get generated.
- * TODO: make available as system API
- * @hide
- */
- @Nullable
- public String getCallingPackageName() {
- return mCallingPackageName;
- }
-
@Override
public int describeContents() {
return 0;
@@ -506,23 +509,30 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mText.toString());
- dest.writeInt(mDefaultLocales != null ? 1 : 0);
- if (mDefaultLocales != null) {
- mDefaultLocales.writeToParcel(dest, flags);
- }
- dest.writeInt(mEntityConfig != null ? 1 : 0);
- if (mEntityConfig != null) {
- mEntityConfig.writeToParcel(dest, flags);
- }
+ dest.writeParcelable(mDefaultLocales, flags);
+ dest.writeParcelable(mEntityConfig, flags);
dest.writeString(mCallingPackageName);
dest.writeBundle(mExtras);
}
+ private static Request readFromParcel(Parcel in) {
+ final String text = in.readString();
+ final LocaleList defaultLocales = in.readParcelable(null);
+ final EntityConfig entityConfig = in.readParcelable(null);
+ final String callingPackageName = in.readString();
+ final Bundle extras = in.readBundle();
+
+ final Request request = new Request(text, defaultLocales, entityConfig,
+ /* legacyFallback= */ true, extras);
+ request.setCallingPackageName(callingPackageName);
+ return request;
+ }
+
public static final Parcelable.Creator<Request> CREATOR =
new Parcelable.Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
- return new Request(in);
+ return readFromParcel(in);
}
@Override
@@ -530,16 +540,6 @@
return new Request[size];
}
};
-
- private Request(Parcel in) {
- mText = in.readString();
- mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
- mEntityConfig = in.readInt() == 0
- ? null : TextClassifier.EntityConfig.CREATOR.createFromParcel(in);
- mLegacyFallback = true;
- mCallingPackageName = in.readString();
- mExtras = in.readBundle();
- }
}
/**
@@ -645,9 +645,20 @@
* @throws IllegalArgumentException if entityScores is null or empty.
*/
@NonNull
- public Builder addLink(int start, int end, Map<String, Float> entityScores) {
- mLinks.add(new TextLink(start, end, entityScores, null));
- return this;
+ public Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores) {
+ return addLink(start, end, entityScores, Bundle.EMPTY, null);
+ }
+
+ /**
+ * Adds a TextLink.
+ *
+ * @see #addLink(int, int, Map)
+ * @param extras An optional bundle containing custom data related to this TextLink
+ */
+ @NonNull
+ public Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
+ @NonNull Bundle extras) {
+ return addLink(start, end, entityScores, extras, null);
}
/**
@@ -655,9 +666,15 @@
* @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
*/
@NonNull
- Builder addLink(int start, int end, Map<String, Float> entityScores,
+ Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
@Nullable URLSpan urlSpan) {
- mLinks.add(new TextLink(start, end, entityScores, urlSpan));
+ return addLink(start, end, entityScores, Bundle.EMPTY, urlSpan);
+ }
+
+ private Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
+ @NonNull Bundle extras, @Nullable URLSpan urlSpan) {
+ mLinks.add(new TextLink(
+ start, end, new EntityConfidence(entityScores), extras, urlSpan));
return this;
}
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index f236915..4a6f3e5 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -24,10 +24,12 @@
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.SpannedString;
import android.util.ArrayMap;
import android.view.textclassifier.TextClassifier.EntityType;
import android.view.textclassifier.TextClassifier.Utils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.util.Locale;
@@ -209,6 +211,7 @@
@Nullable private final LocaleList mDefaultLocales;
private final boolean mDarkLaunchAllowed;
private final Bundle mExtras;
+ @Nullable private String mCallingPackageName;
private Request(
CharSequence text,
@@ -270,6 +273,26 @@
}
/**
+ * Sets the name of the package that is sending this request.
+ * <p>
+ * Package-private for SystemTextClassifier's use.
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
+ mCallingPackageName = callingPackageName;
+ }
+
+ /**
+ * Returns the name of the package that sent this request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -355,7 +378,7 @@
*/
@NonNull
public Request build() {
- return new Request(mText, mStartIndex, mEndIndex,
+ return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
mDefaultLocales, mDarkLaunchAllowed,
mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
@@ -368,21 +391,33 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mText.toString());
+ dest.writeCharSequence(mText);
dest.writeInt(mStartIndex);
dest.writeInt(mEndIndex);
- dest.writeInt(mDefaultLocales != null ? 1 : 0);
- if (mDefaultLocales != null) {
- mDefaultLocales.writeToParcel(dest, flags);
- }
+ dest.writeParcelable(mDefaultLocales, flags);
+ dest.writeString(mCallingPackageName);
dest.writeBundle(mExtras);
}
+ private static Request readFromParcel(Parcel in) {
+ final CharSequence text = in.readCharSequence();
+ final int startIndex = in.readInt();
+ final int endIndex = in.readInt();
+ final LocaleList defaultLocales = in.readParcelable(null);
+ final String callingPackageName = in.readString();
+ final Bundle extras = in.readBundle();
+
+ final Request request = new Request(text, startIndex, endIndex, defaultLocales,
+ /* darkLaunchAllowed= */ false, extras);
+ request.setCallingPackageName(callingPackageName);
+ return request;
+ }
+
public static final Parcelable.Creator<Request> CREATOR =
new Parcelable.Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
- return new Request(in);
+ return readFromParcel(in);
}
@Override
@@ -390,15 +425,6 @@
return new Request[size];
}
};
-
- private Request(Parcel in) {
- mText = in.readString();
- mStartIndex = in.readInt();
- mEndIndex = in.readInt();
- mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
- mDarkLaunchAllowed = false;
- mExtras = in.readBundle();
- }
}
@Override
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index 91a5440..aaf7312 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -189,6 +189,7 @@
Instant.ofEpochMilli(946771200000L), // 2000-01-02
ZoneId.of("UTC"));
final String text = "text";
+ final String packageName = "packageName";
final TextClassification.Request reference =
new TextClassification.Request.Builder(text, 0, text.length())
@@ -196,6 +197,7 @@
.setReferenceTime(referenceTime)
.setExtras(BUNDLE)
.build();
+ reference.setCallingPackageName(packageName);
// Parcel and unparcel.
final Parcel parcel = Parcel.obtain();
@@ -204,12 +206,13 @@
final TextClassification.Request result =
TextClassification.Request.CREATOR.createFromParcel(parcel);
- assertEquals(text, result.getText());
+ assertEquals(text, result.getText().toString());
assertEquals(0, result.getStartIndex());
assertEquals(text.length(), result.getEndIndex());
assertEquals(referenceTime, result.getReferenceTime());
assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
assertEquals(referenceTime, result.getReferenceTime());
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+ assertEquals(packageName, result.getCallingPackageName());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
index 75ca769..1dcaed6 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -69,10 +69,12 @@
final String bundleKey = "experiment.str";
final Bundle bundle = new Bundle();
bundle.putString(bundleKey, "bundle");
+ final String packageName = "packageName";
final TextLanguage.Request reference = new TextLanguage.Request.Builder(text)
.setExtras(bundle)
.build();
+ reference.setCallingPackageName(packageName);
final Parcel parcel = Parcel.obtain();
reference.writeToParcel(parcel, 0);
@@ -81,5 +83,6 @@
assertEquals(text, result.getText());
assertEquals("bundle", result.getExtras().getString(bundleKey));
+ assertEquals(packageName, result.getCallingPackageName());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index f6ec0e6..f022d04 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -64,7 +64,7 @@
public void testParcel() {
final String fullText = "this is just a test";
final TextLinks reference = new TextLinks.Builder(fullText)
- .addLink(0, 4, getEntityScores(0.f, 0.f, 1.f))
+ .addLink(0, 4, getEntityScores(0.f, 0.f, 1.f), BUNDLE)
.addLink(5, 12, getEntityScores(.8f, .1f, .5f))
.setExtras(BUNDLE)
.build();
@@ -82,6 +82,7 @@
assertEquals(1, resultList.get(0).getEntityCount());
assertEquals(TextClassifier.TYPE_OTHER, resultList.get(0).getEntity(0));
assertEquals(1.f, resultList.get(0).getConfidenceScore(TextClassifier.TYPE_OTHER), 1e-7f);
+ assertEquals(BUNDLE_VALUE, resultList.get(0).getExtras().getString(BUNDLE_KEY));
assertEquals(5, resultList.get(1).getStart());
assertEquals(12, resultList.get(1).getEnd());
assertEquals(3, resultList.get(1).getEntityCount());
@@ -96,6 +97,7 @@
@Test
public void testParcelOptions() {
+ final String packageName = "packageName";
final TextClassifier.EntityConfig entityConfig = TextClassifier.EntityConfig.create(
Arrays.asList(TextClassifier.HINT_TEXT_IS_EDITABLE),
Arrays.asList("a", "b", "c"),
@@ -105,6 +107,7 @@
.setEntityConfig(entityConfig)
.setExtras(BUNDLE)
.build();
+ reference.setCallingPackageName(packageName);
// Parcel and unparcel.
final Parcel parcel = Parcel.obtain();
@@ -119,5 +122,6 @@
assertEquals(new HashSet<String>(Arrays.asList("a", "c")),
result.getEntityConfig().resolveEntityListModifications(Collections.emptyList()));
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+ assertEquals(packageName, result.getCallingPackageName());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 7ea5108b..2ea49f7 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -75,11 +75,13 @@
@Test
public void testParcelRequest() {
final String text = "text";
+ final String packageName = "packageName";
final TextSelection.Request reference =
new TextSelection.Request.Builder(text, 0, text.length())
.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
.setExtras(BUNDLE)
.build();
+ reference.setCallingPackageName(packageName);
// Parcel and unparcel.
final Parcel parcel = Parcel.obtain();
@@ -87,10 +89,11 @@
parcel.setDataPosition(0);
final TextSelection.Request result = TextSelection.Request.CREATOR.createFromParcel(parcel);
- assertEquals(text, result.getText());
+ assertEquals(text, result.getText().toString());
assertEquals(0, result.getStartIndex());
assertEquals(text.length(), result.getEndIndex());
assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+ assertEquals(packageName, result.getCallingPackageName());
}
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index c8a68b4..6b0419e 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -136,6 +135,7 @@
throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -158,6 +158,7 @@
throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -180,6 +181,7 @@
throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -199,7 +201,7 @@
public void onSelectionEvent(
TextClassificationSessionId sessionId, SelectionEvent event) throws RemoteException {
Preconditions.checkNotNull(event);
- validateInput(event.getPackageName(), mContext);
+ validateInput(mContext, event.getPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -220,6 +222,7 @@
ITextLanguageCallback callback) throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -242,6 +245,7 @@
IConversationActionsCallback callback) throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -263,7 +267,7 @@
throws RemoteException {
Preconditions.checkNotNull(sessionId);
Preconditions.checkNotNull(classificationContext);
- validateInput(classificationContext.getPackageName(), mContext);
+ validateInput(mContext, classificationContext.getPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -398,15 +402,17 @@
e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
}
- private static void validateInput(String packageName, Context context)
+ private static void validateInput(Context context, @Nullable String packageName)
throws RemoteException {
+ if (packageName == null) return;
+
try {
final int uid = context.getPackageManager()
.getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
Preconditions.checkArgument(Binder.getCallingUid() == uid);
- } catch (IllegalArgumentException | NullPointerException |
- PackageManager.NameNotFoundException e) {
- throw new RemoteException(e.getMessage());
+ } catch (Exception e) {
+ throw new RemoteException(
+ String.format("Invalid package: name=%s, error=%s", packageName, e));
}
}