diff --git a/api/current.txt b/api/current.txt index 2ef4a392ef230..eeee2477f2dd4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -40506,6 +40506,7 @@ package android.service.autofill { public final class UserData implements android.os.Parcelable { method public int describeContents(); method public java.lang.String getFieldClassificationAlgorithm(); + method public java.lang.String getFieldClassificationAlgorithmForCategory(java.lang.String); method public java.lang.String getId(); method public static int getMaxCategoryCount(); method public static int getMaxFieldClassificationIdsSize(); @@ -40521,6 +40522,7 @@ package android.service.autofill { method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String); method public android.service.autofill.UserData build(); method public android.service.autofill.UserData.Builder setFieldClassificationAlgorithm(java.lang.String, android.os.Bundle); + method public android.service.autofill.UserData.Builder setFieldClassificationAlgorithmForCategory(java.lang.String, java.lang.String, android.os.Bundle); } public abstract interface Validator { diff --git a/api/system-current.txt b/api/system-current.txt index 57b7eed0b4dac..892cdea4c35e7 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4871,7 +4871,10 @@ package android.service.autofill { public abstract class AutofillFieldClassificationService extends android.app.Service { method public android.os.IBinder onBind(android.content.Intent); - method public float[][] onGetScores(java.lang.String, android.os.Bundle, java.util.List, java.util.List); + method public float[][] onCalculateScores(java.util.List, java.util.List, java.util.List, java.lang.String, android.os.Bundle, java.util.Map, java.util.Map); + method public deprecated float[][] onGetScores(java.lang.String, android.os.Bundle, java.util.List, java.util.List); + field public static final java.lang.String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE"; + field public static final java.lang.String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH"; field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService"; field public static final java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms"; field public static final java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm"; diff --git a/api/test-current.txt b/api/test-current.txt index 46cbb52f6efa4..55c8df82eae12 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1066,6 +1066,15 @@ package android.security.keystore { package android.service.autofill { + public abstract class AutofillFieldClassificationService extends android.app.Service { + method public android.os.IBinder onBind(android.content.Intent); + field public static final java.lang.String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE"; + field public static final java.lang.String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH"; + field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService"; + field public static final java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms"; + field public static final java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm"; + } + public final class CharSequenceTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation { method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception; } @@ -1122,6 +1131,10 @@ package android.service.autofill { method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue); } + public final class UserData implements android.os.Parcelable { + method public android.util.ArrayMap getFieldClassificationAlgorithms(); + } + public abstract interface ValueFinder { method public default java.lang.String findByAutofillId(android.view.autofill.AutofillId); method public abstract android.view.autofill.AutofillValue findRawValueByAutofillId(android.view.autofill.AutofillId); diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java index 1cd76d2e9ec9f..834ec4d24a9e2 100644 --- a/core/java/android/service/autofill/AutofillFieldClassificationService.java +++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java @@ -20,6 +20,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.Service; import android.content.Intent; import android.os.Bundle; @@ -35,6 +36,7 @@ import android.view.autofill.AutofillValue; import java.util.Arrays; import java.util.List; +import java.util.Map; /** * A service that calculates field classification scores. @@ -51,6 +53,7 @@ import java.util.List; * {@hide} */ @SystemApi +@TestApi public abstract class AutofillFieldClassificationService extends Service { private static final String TAG = "AutofillFieldClassificationService"; @@ -75,17 +78,32 @@ public abstract class AutofillFieldClassificationService extends Service { public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms"; + /** + * Field classification algorithm that computes the edit distance between two Strings. + * + *

Service implementation must provide this algorithm.

+ */ + public static final String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE"; + + /** + * Field classification algorithm that computes whether the last four digits between two + * Strings match exactly. + * + *

Service implementation must provide this algorithm.

+ */ + public static final String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH"; /** {@hide} **/ public static final String EXTRA_SCORES = "scores"; private AutofillFieldClassificationServiceWrapper mWrapper; - private void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs, - List actualValues, String[] userDataValues) { + private void calculateScores(RemoteCallback callback, List actualValues, + String[] userDataValues, String[] categoryIds, String defaultAlgorithm, + Bundle defaultArgs, Map algorithms, Map args) { final Bundle data = new Bundle(); - final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues, - Arrays.asList(userDataValues)); + final float[][] scores = onCalculateScores(actualValues, Arrays.asList(userDataValues), + Arrays.asList(categoryIds), defaultAlgorithm, defaultArgs, algorithms, args); if (scores != null) { data.putParcelable(EXTRA_SCORES, new Scores(scores)); } @@ -169,9 +187,12 @@ public abstract class AutofillFieldClassificationService extends Service { * @return the calculated scores of {@code actualValues} x {@code userDataValues}. * * {@hide} + * + * @deprecated Use {@link AutofillFieldClassificationService#onCalculateScores} instead. */ @Nullable @SystemApi + @Deprecated public float[][] onGetScores(@Nullable String algorithm, @Nullable Bundle algorithmOptions, @NonNull List actualValues, @NonNull List userDataValues) { @@ -179,16 +200,98 @@ public abstract class AutofillFieldClassificationService extends Service { return null; } + /** + * Calculates field classification scores in a batch. + * + *

A field classification score is a {@code float} representing how well an + * {@link AutofillValue} matches a expected value predicted by an autofill service + * —a full match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}. + * + *

The exact score depends on the algorithm used to calculate it—the service must + * provide at least one default algorithm (which is used when the algorithm is not specified + * or is invalid), but it could provide more (in which case the algorithm name should be + * specified by the caller when calculating the scores). + * + *

For example, if the service provides an algorithm named {@code EXACT_MATCH} that + * returns {@code 1.0} if all characters match or {@code 0.0} otherwise, a call to: + * + *

+     * HashMap algorithms = new HashMap<>();
+     * algorithms.put("email", "EXACT_MATCH");
+     * algorithms.put("phone", "EXACT_MATCH");
+     *
+     * HashMap args = new HashMap<>();
+     * args.put("email", null);
+     * args.put("phone", null);
+     *
+     * service.onCalculateScores(Arrays.asList(AutofillValue.forText("email1"),
+     * AutofillValue.forText("PHONE1")), Arrays.asList("email1", "phone1"),
+     * Array.asList("email", "phone"), algorithms, args);
+     * 
+ * + *

Returns: + * + *

+     * [
+     *   [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+     *   [0.0, 0.0]  // "PHONE1" compared against ["email1", "phone1"]
+     * ];
+     * 
+ * + *

If the same algorithm allows the caller to specify whether the comparisons should be + * case sensitive by passing a boolean option named {@code "case_sensitive"}, then a call to: + * + *

+     * Bundle algorithmOptions = new Bundle();
+     * algorithmOptions.putBoolean("case_sensitive", false);
+     * args.put("phone", algorithmOptions);
+     *
+     * service.onCalculateScores(Arrays.asList(AutofillValue.forText("email1"),
+     * AutofillValue.forText("PHONE1")), Arrays.asList("email1", "phone1"),
+     * Array.asList("email", "phone"), algorithms, args);
+     * 
+ * + *

Returns: + * + *

+     * [
+     *   [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+     *   [0.0, 1.0]  // "PHONE1" compared against ["email1", "phone1"]
+     * ];
+     * 
+ * + * @param actualValues values entered by the user. + * @param userDataValues values predicted from the user data. + * @param categoryIds category Ids correspoinding to userDataValues + * @param defaultAlgorithm default field classification algorithm + * @param algorithms array of field classification algorithms + * @return the calculated scores of {@code actualValues} x {@code userDataValues}. + * + * {@hide} + */ + @Nullable + @SystemApi + public float[][] onCalculateScores(@NonNull List actualValues, + @NonNull List userDataValues, @NonNull List categoryIds, + @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs, + @Nullable Map algorithms, @Nullable Map args) { + Log.e(TAG, "service implementation (" + getClass() + + " does not implement onCalculateScore()"); + return null; + } + private final class AutofillFieldClassificationServiceWrapper extends IAutofillFieldClassificationService.Stub { @Override - public void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs, - List actualValues, String[] userDataValues) - throws RemoteException { + public void calculateScores(RemoteCallback callback, List actualValues, + String[] userDataValues, String[] categoryIds, String defaultAlgorithm, + Bundle defaultArgs, Map algorithms, Map args) + throws RemoteException { mHandler.sendMessage(obtainMessage( - AutofillFieldClassificationService::getScores, + AutofillFieldClassificationService::calculateScores, AutofillFieldClassificationService.this, - callback, algorithmName, algorithmArgs, actualValues, userDataValues)); + callback, actualValues, userDataValues, categoryIds, defaultAlgorithm, + defaultArgs, algorithms, args)); } } diff --git a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl index 398557d5ad2e6..2cd24f96a22d6 100644 --- a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl +++ b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl @@ -20,6 +20,7 @@ import android.os.Bundle; import android.os.RemoteCallback; import android.view.autofill.AutofillValue; import java.util.List; +import java.util.Map; /** * Service used to calculate match scores for Autofill Field Classification. @@ -27,6 +28,8 @@ import java.util.List; * @hide */ oneway interface IAutofillFieldClassificationService { - void getScores(in RemoteCallback callback, String algorithmName, in Bundle algorithmArgs, - in List actualValues, in String[] userDataValues); + void calculateScores(in RemoteCallback callback, in List actualValues, + in String[] userDataValues, in String[] categoryIds, + in String defaultAlgorithm, in Bundle defaultArgs, + in Map algorithms, in Map args); } diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java index fccb85b957fa6..37f192366f813 100644 --- a/core/java/android/service/autofill/UserData.java +++ b/core/java/android/service/autofill/UserData.java @@ -24,6 +24,7 @@ import static android.view.autofill.Helper.sDebug; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.app.ActivityThread; import android.content.ContentResolver; import android.os.Bundle; @@ -32,6 +33,7 @@ import android.os.Parcelable; import android.provider.Settings; import android.service.autofill.FieldClassification.Match; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.view.autofill.AutofillManager; @@ -57,28 +59,57 @@ public final class UserData implements Parcelable { private static final int DEFAULT_MAX_VALUE_LENGTH = 100; private final String mId; - private final String mAlgorithm; - private final Bundle mAlgorithmArgs; private final String[] mCategoryIds; private final String[] mValues; + private final String mDefaultAlgorithm; + private final Bundle mDefaultArgs; + private final ArrayMap mCategoryAlgorithms; + private final ArrayMap mCategoryArgs; + private UserData(Builder builder) { mId = builder.mId; - mAlgorithm = builder.mAlgorithm; - mAlgorithmArgs = builder.mAlgorithmArgs; mCategoryIds = new String[builder.mCategoryIds.size()]; builder.mCategoryIds.toArray(mCategoryIds); mValues = new String[builder.mValues.size()]; builder.mValues.toArray(mValues); + builder.mValues.toArray(mValues); + + mDefaultAlgorithm = builder.mDefaultAlgorithm; + mDefaultArgs = builder.mDefaultArgs; + mCategoryAlgorithms = builder.mCategoryAlgorithms; + mCategoryArgs = builder.mCategoryArgs; } /** - * Gets the name of the algorithm that is used to calculate - * {@link Match#getScore() match scores}. + * Gets the name of the default algorithm that is used to calculate + * {@link Match#getScore()} match scores}. */ @Nullable public String getFieldClassificationAlgorithm() { - return mAlgorithm; + return mDefaultAlgorithm; + } + + /** @hide */ + public Bundle getDefaultFieldClassificationArgs() { + return mDefaultArgs; + } + + /** + * Gets the name of the algorithm corresponding to the specific autofill category + * that is used to calculate {@link Match#getScore() match scores} + * + * @param categoryId autofill field category + * + * @return String name of algorithm, null if none found. + */ + @Nullable + public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) { + Preconditions.checkNotNull(categoryId); + if (mCategoryAlgorithms == null || !mCategoryAlgorithms.containsKey(categoryId)) { + return null; + } + return mCategoryAlgorithms.get(categoryId); } /** @@ -88,11 +119,6 @@ public final class UserData implements Parcelable { return mId; } - /** @hide */ - public Bundle getAlgorithmArgs() { - return mAlgorithmArgs; - } - /** @hide */ public String[] getCategoryIds() { return mCategoryIds; @@ -103,12 +129,30 @@ public final class UserData implements Parcelable { return mValues; } + /** @hide */ + @TestApi + public ArrayMap getFieldClassificationAlgorithms() { + return mCategoryAlgorithms; + } + + /** @hide */ + public ArrayMap getFieldClassificationArgs() { + return mCategoryArgs; + } + /** @hide */ public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("id: "); pw.print(mId); - pw.print(prefix); pw.print("Algorithm: "); pw.print(mAlgorithm); - pw.print(" Args: "); pw.println(mAlgorithmArgs); - + pw.print(prefix); pw.print("Default Algorithm: "); pw.print(mDefaultAlgorithm); + pw.print(prefix); pw.print("Default Args"); pw.print(mDefaultArgs); + if (mCategoryAlgorithms != null && mCategoryAlgorithms.size() > 0) { + pw.print(prefix); pw.print("Algorithms per category: "); + for (int i = 0; i < mCategoryAlgorithms.size(); i++) { + pw.print(prefix); pw.print(prefix); pw.print(mCategoryAlgorithms.keyAt(i)); + pw.print(": "); pw.println(Helper.getRedacted(mCategoryAlgorithms.valueAt(i))); + pw.print("args="); pw.print(mCategoryArgs.get(mCategoryAlgorithms.keyAt(i))); + } + } // Cannot disclose field ids or values because they could contain PII pw.print(prefix); pw.print("Field ids size: "); pw.println(mCategoryIds.length); for (int i = 0; i < mCategoryIds.length; i++) { @@ -139,8 +183,13 @@ public final class UserData implements Parcelable { private final String mId; private final ArrayList mCategoryIds; private final ArrayList mValues; - private String mAlgorithm; - private Bundle mAlgorithmArgs; + private String mDefaultAlgorithm; + private Bundle mDefaultArgs; + + // Map of autofill field categories to fleid classification algorithms and args + private ArrayMap mCategoryAlgorithms; + private ArrayMap mCategoryArgs; + private boolean mDestroyed; // Non-persistent array used to limit the number of unique ids. @@ -148,7 +197,6 @@ public final class UserData implements Parcelable { // Non-persistent array used to ignore duplaicated value/category pairs. private final ArraySet mUniqueValueCategoryPairs; - /** * Creates a new builder for the user data used for field * classification. @@ -169,7 +217,7 @@ public final class UserData implements Parcelable { * {@link AutofillManager#getUserData()}). * * @param value value of the user data. - * @param categoryId string used to identify the category the value is associated with. + * @param categoryId autofill field category. * * @throws IllegalArgumentException if any of the following occurs: *
    @@ -189,13 +237,15 @@ public final class UserData implements Parcelable { mCategoryIds = new ArrayList<>(maxUserDataSize); mValues = new ArrayList<>(maxUserDataSize); mUniqueValueCategoryPairs = new ArraySet<>(maxUserDataSize); + mUniqueCategoryIds = new ArraySet<>(getMaxCategoryCount()); addMapping(value, categoryId); } /** - * Sets the algorithm used for field classification. + * Sets the default algorithm used for + * field classification. * *

    The currently available algorithms can be retrieve through * {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}. @@ -212,8 +262,40 @@ public final class UserData implements Parcelable { public Builder setFieldClassificationAlgorithm(@Nullable String name, @Nullable Bundle args) { throwIfDestroyed(); - mAlgorithm = name; - mAlgorithmArgs = args; + mDefaultAlgorithm = name; + mDefaultArgs = args; + return this; + } + + /** + * Sets the algorithm used for field classification + * for the specified category. + * + *

    The currently available algorithms can be retrieved through + * {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}. + * + *

    If not set, the + * {@link AutofillManager#getDefaultFieldClassificationAlgorithm() default algorithm} is + * used instead. + * + * @param categoryId autofill field category. + * @param name name of the algorithm or {@code null} to used default. + * @param args optional arguments to the algorithm. + * + * @return this builder + */ + public Builder setFieldClassificationAlgorithmForCategory(@NonNull String categoryId, + @Nullable String name, @Nullable Bundle args) { + throwIfDestroyed(); + Preconditions.checkNotNull(categoryId); + if (mCategoryAlgorithms == null) { + mCategoryAlgorithms = new ArrayMap<>(getMaxCategoryCount()); + } + if (mCategoryArgs == null) { + mCategoryArgs = new ArrayMap<>(getMaxCategoryCount()); + } + mCategoryAlgorithms.put(categoryId, name); + mCategoryArgs.put(categoryId, args); return this; } @@ -317,8 +399,7 @@ public final class UserData implements Parcelable { public String toString() { if (!sDebug) return super.toString(); - final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId) - .append(", algorithm=").append(mAlgorithm); + final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId); // Cannot disclose category ids or values because they could contain PII builder.append(", categoryIds="); Helper.appendRedacted(builder, mCategoryIds); @@ -341,8 +422,10 @@ public final class UserData implements Parcelable { parcel.writeString(mId); parcel.writeStringArray(mCategoryIds); parcel.writeStringArray(mValues); - parcel.writeString(mAlgorithm); - parcel.writeBundle(mAlgorithmArgs); + parcel.writeString(mDefaultAlgorithm); + parcel.writeBundle(mDefaultArgs); + parcel.writeMap(mCategoryAlgorithms); + parcel.writeMap(mCategoryArgs); } public static final Parcelable.Creator CREATOR = @@ -355,10 +438,28 @@ public final class UserData implements Parcelable { final String id = parcel.readString(); final String[] categoryIds = parcel.readStringArray(); final String[] values = parcel.readStringArray(); + final String defaultAlgorithm = parcel.readString(); + final Bundle defaultArgs = parcel.readBundle(); + final ArrayMap categoryAlgorithms = new ArrayMap<>(); + parcel.readMap(categoryAlgorithms, String.class.getClassLoader()); + final ArrayMap categoryArgs = new ArrayMap<>(); + parcel.readMap(categoryArgs, Bundle.class.getClassLoader()); + final Builder builder = new Builder(id, values[0], categoryIds[0]) - .setFieldClassificationAlgorithm(parcel.readString(), parcel.readBundle()); + .setFieldClassificationAlgorithm(defaultAlgorithm, defaultArgs); + for (int i = 1; i < categoryIds.length; i++) { - builder.add(values[i], categoryIds[i]); + String categoryId = categoryIds[i]; + builder.add(values[i], categoryId); + } + + final int size = categoryAlgorithms.size(); + if (size > 0) { + for (int i = 0; i < size; i++) { + final String categoryId = categoryAlgorithms.keyAt(i); + builder.setFieldClassificationAlgorithmForCategory(categoryId, + categoryAlgorithms.valueAt(i), categoryArgs.get(categoryId)); + } } return builder.build(); } diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml index 617e49ac11203..a9a5450c536e8 100644 --- a/packages/ExtServices/res/values/strings.xml +++ b/packages/ExtServices/res/values/strings.xml @@ -22,5 +22,6 @@ EDIT_DISTANCE EDIT_DISTANCE + EXACT_MATCH diff --git a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java index 9ba7e092f34b2..e379db842b3ba 100644 --- a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java +++ b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java @@ -15,8 +15,6 @@ */ package android.ext.services.autofill; -import static android.ext.services.autofill.EditDistanceScorer.DEFAULT_ALGORITHM; - import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; @@ -26,27 +24,72 @@ import android.view.autofill.AutofillValue; import com.android.internal.util.ArrayUtils; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassificationService { private static final String TAG = "AutofillFieldClassificationServiceImpl"; + private static final String DEFAULT_ALGORITHM = REQUIRED_ALGORITHM_EDIT_DISTANCE; + @Nullable @Override - public float[][] onGetScores(@Nullable String algorithmName, - @Nullable Bundle algorithmArgs, @NonNull List actualValues, - @NonNull List userDataValues) { + /** @hide */ + public float[][] onCalculateScores(@NonNull List actualValues, + @NonNull List userDataValues, @NonNull List categoryIds, + @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs, + @Nullable Map algorithms, @Nullable Map args) { if (ArrayUtils.isEmpty(actualValues) || ArrayUtils.isEmpty(userDataValues)) { - Log.w(TAG, "getScores(): empty currentvalues (" + actualValues + ") or userValues (" - + userDataValues + ")"); + Log.w(TAG, "calculateScores(): empty currentvalues (" + actualValues + + ") or userValues (" + userDataValues + ")"); return null; } - if (algorithmName != null && !algorithmName.equals(DEFAULT_ALGORITHM)) { - Log.w(TAG, "Ignoring invalid algorithm (" + algorithmName + ") and using " - + DEFAULT_ALGORITHM + " instead"); - } - return EditDistanceScorer.getScores(actualValues, userDataValues); + return calculateScores(actualValues, userDataValues, categoryIds, defaultAlgorithm, + defaultArgs, (HashMap) algorithms, + (HashMap) args); + } + + /** @hide */ + public float[][] calculateScores(@NonNull List actualValues, + @NonNull List userDataValues, @NonNull List categoryIds, + @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs, + @Nullable HashMap algorithms, + @Nullable HashMap args) { + final int actualValuesSize = actualValues.size(); + final int userDataValuesSize = userDataValues.size(); + final float[][] scores = new float[actualValuesSize][userDataValuesSize]; + + for (int j = 0; j < userDataValuesSize; j++) { + final String categoryId = categoryIds.get(j); + String algorithmName = defaultAlgorithm; + Bundle arg = defaultArgs; + if (algorithms != null && algorithms.containsKey(categoryId)) { + algorithmName = algorithms.get(categoryId); + } + if (args != null && args.containsKey(categoryId)) { + arg = args.get(categoryId); + } + + if (algorithmName == null || (!algorithmName.equals(DEFAULT_ALGORITHM) + && !algorithmName.equals(REQUIRED_ALGORITHM_EXACT_MATCH))) { + Log.w(TAG, "algorithmName is " + algorithmName + ", defaulting to " + + DEFAULT_ALGORITHM); + algorithmName = DEFAULT_ALGORITHM; + } + + for (int i = 0; i < actualValuesSize; i++) { + if (algorithmName.equals(DEFAULT_ALGORITHM)) { + scores[i][j] = EditDistanceScorer.calculateScore(actualValues.get(i), + userDataValues.get(j)); + } else { + scores[i][j] = ExactMatch.calculateScore(actualValues.get(i), + userDataValues.get(j), arg); + } + } + } + return scores; } } diff --git a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java b/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java index 302b16022c267..6a47901aa58ec 100644 --- a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java +++ b/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java @@ -17,13 +17,10 @@ package android.ext.services.autofill; import android.annotation.NonNull; import android.annotation.Nullable; -import android.util.Log; import android.view.autofill.AutofillValue; import com.android.internal.annotations.VisibleForTesting; -import java.util.List; - final class EditDistanceScorer { private static final String TAG = "EditDistanceScorer"; @@ -31,15 +28,14 @@ final class EditDistanceScorer { // TODO(b/70291841): STOPSHIP - set to false before launching private static final boolean DEBUG = true; - static final String DEFAULT_ALGORITHM = "EDIT_DISTANCE"; - /** * Gets the field classification score of 2 values based on the edit distance between them. * *

    The score is defined as: @(max_length - edit_distance) / max_length */ @VisibleForTesting - static float getScore(@Nullable AutofillValue actualValue, @Nullable String userDataValue) { + static float calculateScore(@Nullable AutofillValue actualValue, + @Nullable String userDataValue) { if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0; final String actualValueText = actualValue.getTextValue().toString(); @@ -123,26 +119,5 @@ final class EditDistanceScorer { return d[m][n]; } - /** - * Gets the scores in a batch. - */ - static float[][] getScores(@NonNull List actualValues, - @NonNull List userDataValues) { - final int actualValuesSize = actualValues.size(); - final int userDataValuesSize = userDataValues.size(); - if (DEBUG) { - Log.d(TAG, "getScores() will return a " + actualValuesSize + "x" - + userDataValuesSize + " matrix for " + DEFAULT_ALGORITHM); - } - final float[][] scores = new float[actualValuesSize][userDataValuesSize]; - - for (int i = 0; i < actualValuesSize; i++) { - for (int j = 0; j < userDataValuesSize; j++) { - final float score = getScore(actualValues.get(i), userDataValues.get(j)); - scores[i][j] = score; - } - } - return scores; - } } diff --git a/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java b/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java new file mode 100644 index 0000000000000..3e55c5c59e024 --- /dev/null +++ b/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2018 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.ext.services.autofill; + +import android.annotation.Nullable; +import android.os.Bundle; +import android.view.autofill.AutofillValue; + +import com.android.internal.annotations.VisibleForTesting; + +final class ExactMatch { + + /** + * Gets the field classification score of 2 values based on whether they are an exact match + * + * @return {@code 1.0} if the two values are an exact match, {@code 0.0} otherwise. + */ + @VisibleForTesting + static float calculateScore(@Nullable AutofillValue actualValue, + @Nullable String userDataValue, @Nullable Bundle args) { + if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0; + + final String actualValueText = actualValue.getTextValue().toString(); + + final int suffixLength; + if (args != null) { + suffixLength = args.getInt("suffix", -1); + + if (suffixLength < 0) { + throw new IllegalArgumentException("suffix argument is invalid"); + } + + final String actualValueSuffix; + if (suffixLength < actualValueText.length()) { + actualValueSuffix = actualValueText.substring(actualValueText.length() + - suffixLength); + } else { + actualValueSuffix = actualValueText; + } + + final String userDataValueSuffix; + if (suffixLength < userDataValue.length()) { + userDataValueSuffix = userDataValue.substring(userDataValue.length() + - suffixLength); + } else { + userDataValueSuffix = userDataValue; + } + + return (actualValueSuffix.equalsIgnoreCase(userDataValueSuffix)) ? 1 : 0; + } else { + return actualValueText.equalsIgnoreCase(userDataValue) ? 1 : 0; + } + } +} diff --git a/packages/ExtServices/tests/Android.mk b/packages/ExtServices/tests/Android.mk index 0a95b858a93e0..a57fa9458f085 100644 --- a/packages/ExtServices/tests/Android.mk +++ b/packages/ExtServices/tests/Android.mk @@ -12,7 +12,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ mockito-target-minus-junit4 \ espresso-core \ truth-prebuilt \ - testables + testables \ + testng # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java index 48c076e67e784..6fda4c73792b2 100644 --- a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java @@ -16,14 +16,21 @@ package android.ext.services.autofill; +import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_EXACT_MATCH; +import static android.view.autofill.AutofillValue.forText; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import android.os.Bundle; +import android.view.autofill.AutofillValue; + import org.junit.Test; import java.util.Arrays; import java.util.Collections; - -import static com.google.common.truth.Truth.assertThat; - -import android.view.autofill.AutofillValue; +import java.util.HashMap; +import java.util.List; /** * Contains the base tests that does not rely on the specific algorithm implementation. @@ -34,26 +41,78 @@ public class AutofillFieldClassificationServiceImplTest { new AutofillFieldClassificationServiceImpl(); @Test - public void testOnGetScores_nullActualValues() { - assertThat(mService.onGetScores(null, null, null, Arrays.asList("whatever"))).isNull(); + public void testOnCalculateScores_nullActualValues() { + assertThat(mService.onCalculateScores(null, null, null, null, null, null, null)).isNull(); } @Test - public void testOnGetScores_emptyActualValues() { - assertThat(mService.onGetScores(null, null, Collections.emptyList(), - Arrays.asList("whatever"))).isNull(); + public void testOnCalculateScores_emptyActualValues() { + assertThat(mService.onCalculateScores(Collections.emptyList(), Arrays.asList("whatever"), + null, null, null, null, null)).isNull(); } @Test - public void testOnGetScores_nullUserDataValues() { - assertThat(mService.onGetScores(null, null, - Arrays.asList(AutofillValue.forText("whatever")), null)).isNull(); + public void testOnCalculateScores_nullUserDataValues() { + assertThat(mService.onCalculateScores(Arrays.asList(AutofillValue.forText("whatever")), + null, null, null, null, null, null)).isNull(); } @Test - public void testOnGetScores_emptyUserDataValues() { - assertThat(mService.onGetScores(null, null, - Arrays.asList(AutofillValue.forText("whatever")), Collections.emptyList())) - .isNull(); + public void testOnCalculateScores_emptyUserDataValues() { + assertThat(mService.onCalculateScores(Arrays.asList(AutofillValue.forText("whatever")), + Collections.emptyList(), null, null, null, null, null)) + .isNull(); + } + + @Test + public void testCalculateScores() { + final List actualValues = Arrays.asList(forText("A"), forText("b"), + forText("dude")); + final List userDataValues = Arrays.asList("a", "b", "B", "ab", "c", "dude", + "sweet_dude", "dude_sweet"); + final List categoryIds = Arrays.asList("cat", "cat", "cat", "cat", "cat", "last4", + "last4", "last4"); + final HashMap algorithms = new HashMap<>(1); + algorithms.put("last4", REQUIRED_ALGORITHM_EXACT_MATCH); + + final Bundle last4Bundle = new Bundle(); + last4Bundle.putInt("suffix", 4); + + final HashMap args = new HashMap<>(1); + args.put("last4", last4Bundle); + + final float[][] expectedScores = new float[][] { + new float[] { 1F, 0F, 0F, 0.5F, 0F, 0F, 0F, 0F }, + new float[] { 0F, 1F, 1F, 0.5F, 0F, 0F, 0F, 0F }, + new float[] { 0F, 0F, 0F, 0F , 0F, 1F, 1F, 0F } + }; + final float[][] actualScores = mService.onCalculateScores(actualValues, userDataValues, + categoryIds, null, null, algorithms, args); + + // Unfortunately, Truth does not have an easy way to compare float matrices and show useful + // messages in case of error, so we need to check. + assertWithMessage("actual=%s, expected=%s", toString(actualScores), + toString(expectedScores)).that(actualScores.length).isEqualTo(3); + for (int i = 0; i < 3; i++) { + assertWithMessage("actual=%s, expected=%s", toString(actualScores), + toString(expectedScores)).that(actualScores[i].length).isEqualTo(8); + } + + for (int i = 0; i < actualScores.length; i++) { + final float[] line = actualScores[i]; + for (int j = 0; j < line.length; j++) { + float cell = line[j]; + assertWithMessage("wrong score at [%s, %s]", i, j).that(cell).isWithin(0.01F) + .of(expectedScores[i][j]); + } + } + } + + public static String toString(float[][] matrix) { + final StringBuilder string = new StringBuilder("[ "); + for (int i = 0; i < matrix.length; i++) { + string.append(Arrays.toString(matrix[i])).append(" "); + } + return string.append(" ]").toString(); } } diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java index afe223641d375..9b9d4be59929e 100644 --- a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java @@ -15,107 +15,67 @@ */ package android.ext.services.autofill; -import static android.ext.services.autofill.EditDistanceScorer.getScore; -import static android.ext.services.autofill.EditDistanceScorer.getScores; -import static android.view.autofill.AutofillValue.forText; +import static android.ext.services.autofill.EditDistanceScorer.calculateScore; import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; import android.view.autofill.AutofillValue; import org.junit.Test; -import java.util.Arrays; -import java.util.List; - public class EditDistanceScorerTest { @Test - public void testGetScore_nullValue() { - assertFloat(getScore(null, "D'OH!"), 0); + public void testCalculateScore_nullValue() { + assertFloat(calculateScore(null, "D'OH!"), 0); } @Test - public void testGetScore_nonTextValue() { - assertFloat(getScore(AutofillValue.forToggle(true), "D'OH!"), 0); + public void testCalculateScore_nonTextValue() { + assertFloat(calculateScore(AutofillValue.forToggle(true), "D'OH!"), 0); } @Test - public void testGetScore_nullUserData() { - assertFloat(getScore(AutofillValue.forText("D'OH!"), null), 0); + public void testCalculateScore_nullUserData() { + assertFloat(calculateScore(AutofillValue.forText("D'OH!"), null), 0); } @Test - public void testGetScore_fullMatch() { - assertFloat(getScore(AutofillValue.forText("D'OH!"), "D'OH!"), 1); - assertFloat(getScore(AutofillValue.forText(""), ""), 1); + public void testCalculateScore_fullMatch() { + assertFloat(calculateScore(AutofillValue.forText("D'OH!"), "D'OH!"), 1); + assertFloat(calculateScore(AutofillValue.forText(""), ""), 1); } @Test - public void testGetScore_fullMatchMixedCase() { - assertFloat(getScore(AutofillValue.forText("D'OH!"), "D'oH!"), 1); + public void testCalculateScore_fullMatchMixedCase() { + assertFloat(calculateScore(AutofillValue.forText("D'OH!"), "D'oH!"), 1); } @Test - public void testGetScore_mismatchDifferentSizes() { - assertFloat(getScore(AutofillValue.forText("X"), "Xy"), 0.50F); - assertFloat(getScore(AutofillValue.forText("Xy"), "X"), 0.50F); - assertFloat(getScore(AutofillValue.forText("One"), "MoreThanOne"), 0.27F); - assertFloat(getScore(AutofillValue.forText("MoreThanOne"), "One"), 0.27F); - assertFloat(getScore(AutofillValue.forText("1600 Amphitheatre Parkway"), + public void testCalculateScore_mismatchDifferentSizes() { + assertFloat(calculateScore(AutofillValue.forText("X"), "Xy"), 0.50F); + assertFloat(calculateScore(AutofillValue.forText("Xy"), "X"), 0.50F); + assertFloat(calculateScore(AutofillValue.forText("One"), "MoreThanOne"), 0.27F); + assertFloat(calculateScore(AutofillValue.forText("MoreThanOne"), "One"), 0.27F); + assertFloat(calculateScore(AutofillValue.forText("1600 Amphitheatre Parkway"), "1600 Amphitheatre Pkwy"), 0.88F); - assertFloat(getScore(AutofillValue.forText("1600 Amphitheatre Pkwy"), + assertFloat(calculateScore(AutofillValue.forText("1600 Amphitheatre Pkwy"), "1600 Amphitheatre Parkway"), 0.88F); } @Test - public void testGetScore_partialMatch() { - assertFloat(getScore(AutofillValue.forText("Dude"), "Dxxx"), 0.25F); - assertFloat(getScore(AutofillValue.forText("Dude"), "DUxx"), 0.50F); - assertFloat(getScore(AutofillValue.forText("Dude"), "DUDx"), 0.75F); - assertFloat(getScore(AutofillValue.forText("Dxxx"), "Dude"), 0.25F); - assertFloat(getScore(AutofillValue.forText("DUxx"), "Dude"), 0.50F); - assertFloat(getScore(AutofillValue.forText("DUDx"), "Dude"), 0.75F); - } - - @Test - public void testGetScores() { - final List actualValues = Arrays.asList(forText("A"), forText("b")); - final List userDataValues = Arrays.asList("a", "B", "ab", "c"); - final float[][] expectedScores = new float[][] { - new float[] { 1F, 0F, 0.5F, 0F }, - new float[] { 0F, 1F, 0.5F, 0F } - }; - final float[][] actualScores = getScores(actualValues, userDataValues); - - // Unfortunately, Truth does not have an easy way to compare float matrices and show useful - // messages in case of error, so we need to check. - assertWithMessage("actual=%s, expected=%s", toString(actualScores), - toString(expectedScores)).that(actualScores.length).isEqualTo(2); - assertWithMessage("actual=%s, expected=%s", toString(actualScores), - toString(expectedScores)).that(actualScores[0].length).isEqualTo(4); - assertWithMessage("actual=%s, expected=%s", toString(actualScores), - toString(expectedScores)).that(actualScores[1].length).isEqualTo(4); - for (int i = 0; i < actualScores.length; i++) { - final float[] line = actualScores[i]; - for (int j = 0; j < line.length; j++) { - float cell = line[j]; - assertWithMessage("wrong score at [%s, %s]", i, j).that(cell).isWithin(0.01F) - .of(expectedScores[i][j]); - } - } + public void testCalculateScore_partialMatch() { + assertFloat(calculateScore(AutofillValue.forText("Dude"), "Dxxx"), 0.25F); + assertFloat(calculateScore(AutofillValue.forText("Dude"), "DUxx"), 0.50F); + assertFloat(calculateScore(AutofillValue.forText("Dude"), "DUDx"), 0.75F); + assertFloat(calculateScore(AutofillValue.forText("Dxxx"), "Dude"), 0.25F); + assertFloat(calculateScore(AutofillValue.forText("DUxx"), "Dude"), 0.50F); + assertFloat(calculateScore(AutofillValue.forText("DUDx"), "Dude"), 0.75F); } public static void assertFloat(float actualValue, float expectedValue) { assertThat(actualValue).isWithin(0.01F).of(expectedValue); } - public static String toString(float[][] matrix) { - final StringBuilder string = new StringBuilder("[ "); - for (int i = 0; i < matrix.length; i++) { - string.append(Arrays.toString(matrix[i])).append(" "); - } - return string.append(" ]").toString(); - } + } diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java new file mode 100644 index 0000000000000..bf5e1609fa8bd --- /dev/null +++ b/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2018 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.ext.services.autofill; + +import static android.ext.services.autofill.ExactMatch.calculateScore; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; + +import android.os.Bundle; +import android.view.autofill.AutofillValue; + +import org.junit.Test; + +public class ExactMatchTest { + + private Bundle last4Bundle() { + final Bundle bundle = new Bundle(); + bundle.putInt("suffix", 4); + return bundle; + } + + @Test + public void testCalculateScore_nullValue() { + assertFloat(calculateScore(null, "TEST", null), 0); + } + + @Test + public void testCalculateScore_nonTextValue() { + assertFloat(calculateScore(AutofillValue.forToggle(true), "TEST", null), 0); + } + + @Test + public void testCalculateScore_nullUserData() { + assertFloat(calculateScore(AutofillValue.forText("TEST"), null, null), 0); + } + + @Test + public void testCalculateScore_succeedMatchMixedCases_last4() { + final Bundle last4 = last4Bundle(); + assertFloat(calculateScore(AutofillValue.forText("TEST"), "1234 test", last4), 1); + assertFloat(calculateScore(AutofillValue.forText("test"), "1234 TEST", last4), 1); + } + + @Test + public void testCalculateScore_mismatchDifferentSizes_last4() { + final Bundle last4 = last4Bundle(); + assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST1", last4), 0); + assertFloat(calculateScore(AutofillValue.forText(""), "TEST", last4), 0); + assertFloat(calculateScore(AutofillValue.forText("TEST"), "", last4), 0); + } + + @Test + public void testCalculateScore_match() { + final Bundle last4 = last4Bundle(); + assertFloat(calculateScore(AutofillValue.forText("1234 1234 1234 1234"), + "xxxx xxxx xxxx 1234", last4), 1); + assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST", null), 1); + assertFloat(calculateScore(AutofillValue.forText("TEST 1234"), "1234", last4), 1); + assertFloat(calculateScore(AutofillValue.forText("TEST"), "test", null), 1); + } + + @Test + public void testCalculateScore_badBundle() { + final Bundle bundle = new Bundle(); + bundle.putInt("suffix", -2); + assertThrows(IllegalArgumentException.class, () -> calculateScore( + AutofillValue.forText("TEST"), "TEST", bundle)); + + final Bundle largeBundle = new Bundle(); + largeBundle.putInt("suffix", 10); + assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST", largeBundle), 1); + + final Bundle stringBundle = new Bundle(); + stringBundle.putString("suffix", "value"); + assertThrows(IllegalArgumentException.class, () -> calculateScore( + AutofillValue.forText("TEST"), "TEST", stringBundle)); + + } + + public static void assertFloat(float actualValue, float expectedValue) { + assertThat(actualValue).isWithin(0.01F).of(expectedValue); + } +} diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index e8887e7a2ebe1..612c9294f5d11 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -484,15 +484,15 @@ public final class AutofillManagerService } // Called by Shell command. - void getScore(@Nullable String algorithmName, @NonNull String value1, + void calculateScore(@Nullable String algorithmName, @NonNull String value1, @NonNull String value2, @NonNull RemoteCallback callback) { enforceCallingPermissionForManagement(); final FieldClassificationStrategy strategy = new FieldClassificationStrategy(getContext(), UserHandle.USER_CURRENT); - strategy.getScores(callback, algorithmName, null, - Arrays.asList(AutofillValue.forText(value1)), new String[] { value2 }); + strategy.calculateScores(callback, Arrays.asList(AutofillValue.forText(value1)), + new String[] { value2 }, null, algorithmName, null, null, null); } // Called by Shell command. diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java index 35c51027df86b..c562fb1b7dde3 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java @@ -233,7 +233,7 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { final String value2 = getNextArgRequired(); final CountDownLatch latch = new CountDownLatch(1); - mService.getScore(algorithm, value1, value2, new RemoteCallback((result) -> { + mService.calculateScore(algorithm, value1, value2, new RemoteCallback((result) -> { final Scores scores = result.getParcelable(EXTRA_SCORES); if (scores == null) { pw.println("no score"); diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java index 293f908e27089..9db6254a8baab 100644 --- a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java +++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java @@ -15,11 +15,12 @@ */ package com.android.server.autofill; -import static com.android.server.autofill.Helper.sDebug; -import static com.android.server.autofill.Helper.sVerbose; import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS; import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM; +import static com.android.server.autofill.Helper.sDebug; +import static com.android.server.autofill.Helper.sVerbose; + import android.Manifest; import android.annotation.MainThread; import android.annotation.NonNull; @@ -40,6 +41,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.service.autofill.AutofillFieldClassificationService; import android.service.autofill.IAutofillFieldClassificationService; +import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.view.autofill.AutofillValue; @@ -257,13 +259,13 @@ final class FieldClassificationStrategy { return parser.get(res, resourceId); } - //TODO(b/70291841): rename this method (and all others in the chain) to something like - // calculateScores() ? - void getScores(RemoteCallback callback, @Nullable String algorithmName, - @Nullable Bundle algorithmArgs, @NonNull List actualValues, - @NonNull String[] userDataValues) { - connectAndRun((service) -> service.getScores(callback, algorithmName, - algorithmArgs, actualValues, userDataValues)); + void calculateScores(RemoteCallback callback, @NonNull List actualValues, + @NonNull String[] userDataValues, @NonNull String[] categoryIds, + @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs, + @Nullable ArrayMap algorithms, + @Nullable ArrayMap args) { + connectAndRun((service) -> service.calculateScores(callback, actualValues, + userDataValues, categoryIds, defaultAlgorithm, defaultArgs, algorithms, args)); } void dump(String prefix, PrintWriter pw) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index d76a5dff36d39..d87ad97889c09 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -1394,6 +1394,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final String[] userValues = userData.getValues(); final String[] categoryIds = userData.getCategoryIds(); + final String defaultAlgorithm = userData.getFieldClassificationAlgorithm(); + final Bundle defaultArgs = userData.getDefaultFieldClassificationArgs(); + + final ArrayMap algorithms = userData.getFieldClassificationAlgorithms(); + final ArrayMap args = userData.getFieldClassificationArgs(); + // Sanity check if (userValues == null || categoryIds == null || userValues.length != categoryIds.length) { final int valuesLength = userValues == null ? -1 : userValues.length; @@ -1409,8 +1415,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList detectedFieldClassifications = new ArrayList<>( maxFieldsSize); - final String algorithm = userData.getFieldClassificationAlgorithm(); - final Bundle algorithmArgs = userData.getAlgorithmArgs(); final int viewsSize = viewStates.size(); // First, we get all scores. @@ -1497,7 +1501,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mComponentName, mCompatMode); }); - fcStrategy.getScores(callback, algorithm, algorithmArgs, currentValues, userValues); + fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds, + defaultAlgorithm, defaultArgs, algorithms, args); } /**