summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2018-01-04 19:00:40 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2018-01-04 19:00:40 +0000
commit1e8a6d36bcc99aed82f9d788750f103f8b74fa43 (patch)
tree4d8cc8bc9e10ba6d10addc5b86a7c65842ee24ca
parentfe214f5cc8a5e7a6fb3537ad6048a1078137e84d (diff)
parent27f4573d136949abeacb00f7246ff9911e9cb105 (diff)
Merge "Refactored Field Classification score logic so it can be moved to ExtServices."
-rw-r--r--api/current.txt17
-rw-r--r--api/test-current.txt8
-rw-r--r--core/java/android/service/autofill/EditDistanceScorer.java67
-rw-r--r--core/java/android/service/autofill/FieldClassification.aidl (renamed from core/java/android/service/autofill/Scorer.java)19
-rw-r--r--core/java/android/service/autofill/FieldClassification.java33
-rw-r--r--core/java/android/service/autofill/InternalScorer.java40
-rw-r--r--core/java/android/service/autofill/UserData.aidl1
-rw-r--r--core/java/android/service/autofill/UserData.java69
-rw-r--r--core/java/android/view/autofill/AutofillManager.java48
-rw-r--r--core/java/android/view/autofill/IAutoFillManager.aidl2
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java30
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java124
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java18
13 files changed, 313 insertions, 163 deletions
diff --git a/api/current.txt b/api/current.txt
index 10b5c6785065..32deabb97850 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -37728,18 +37728,12 @@ package android.service.autofill {
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
}
- public final class EditDistanceScorer implements android.os.Parcelable android.service.autofill.Scorer {
- method public int describeContents();
- method public static android.service.autofill.EditDistanceScorer getInstance();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR;
- }
-
public final class FieldClassification {
method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
}
public static final class FieldClassification.Match {
+ method public java.lang.String getAlgorithm();
method public java.lang.String getRemoteId();
method public float getScore();
}
@@ -37891,9 +37885,6 @@ package android.service.autofill {
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
}
- public abstract interface Scorer {
- }
-
public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
method public int describeContents();
@@ -37906,6 +37897,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 static int getMaxFieldClassificationIdsSize();
method public static int getMaxUserDataSize();
method public static int getMaxValueLength();
@@ -37915,9 +37907,10 @@ package android.service.autofill {
}
public static final class UserData.Builder {
- ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String);
+ ctor public UserData.Builder(java.lang.String, java.lang.String);
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);
}
public abstract interface Validator {
@@ -48550,6 +48543,8 @@ package android.view.autofill {
method public void commit();
method public void disableAutofillServices();
method public android.content.ComponentName getAutofillServiceComponentName();
+ method public java.util.List<java.lang.String> getAvailableFieldClassificationAlgorithms();
+ method public java.lang.String getDefaultFieldClassificationAlgorithm();
method public android.service.autofill.UserData getUserData();
method public boolean hasEnabledAutofillServices();
method public boolean isAutofillSupported();
diff --git a/api/test-current.txt b/api/test-current.txt
index d56b0856c028..a0f981a7b5a9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -473,7 +473,8 @@ package android.service.autofill {
method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
}
- public final class EditDistanceScorer extends android.service.autofill.InternalScorer implements android.os.Parcelable android.service.autofill.Scorer {
+ public final class EditDistanceScorer {
+ method public static android.service.autofill.EditDistanceScorer getInstance();
method public float getScore(android.view.autofill.AutofillValue, java.lang.String);
}
@@ -489,11 +490,6 @@ package android.service.autofill {
ctor public InternalSanitizer();
}
- public abstract class InternalScorer implements android.os.Parcelable android.service.autofill.Scorer {
- ctor public InternalScorer();
- method public abstract float getScore(android.view.autofill.AutofillValue, java.lang.String);
- }
-
public abstract class InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
ctor public InternalTransformation();
}
diff --git a/core/java/android/service/autofill/EditDistanceScorer.java b/core/java/android/service/autofill/EditDistanceScorer.java
index 0706b377bbe8..97a386866665 100644
--- a/core/java/android/service/autofill/EditDistanceScorer.java
+++ b/core/java/android/service/autofill/EditDistanceScorer.java
@@ -16,8 +16,7 @@
package android.service.autofill;
import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
+import android.annotation.TestApi;
import android.view.autofill.AutofillValue;
/**
@@ -25,13 +24,20 @@ import android.view.autofill.AutofillValue;
* by the user and the expected value predicted by an autofill service.
*/
// TODO(b/70291841): explain algorithm once it's fully implemented
-public final class EditDistanceScorer extends InternalScorer implements Scorer, Parcelable {
+/** @hide */
+@TestApi
+public final class EditDistanceScorer {
private static final EditDistanceScorer sInstance = new EditDistanceScorer();
+ /** @hide */
+ public static final String NAME = "EDIT_DISTANCE";
+
/**
* Gets the singleton instance.
*/
+ @TestApi
+ /** @hide */
public static EditDistanceScorer getInstance() {
return sInstance;
}
@@ -39,59 +45,32 @@ public final class EditDistanceScorer extends InternalScorer implements Scorer,
private EditDistanceScorer() {
}
- /** @hide */
- @Override
- public float getScore(@NonNull AutofillValue actualValue, @NonNull String userData) {
- if (actualValue == null || !actualValue.isText() || userData == null) return 0;
+ /**
+ * Returns the classification score between an actual {@link AutofillValue} filled
+ * by the user and the expected value predicted by an autofill service.
+ *
+ * <p>A full-match is {@code 1.0} (representing 100%), a full mismatch is {@code 0.0} and
+ * partial mathces are something in between, typically using edit-distance algorithms.
+ *
+ * @hide
+ */
+ @TestApi
+ public float getScore(@NonNull AutofillValue actualValue, @NonNull String userDataValue) {
+ if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0;
// TODO(b/70291841): implement edit distance - currently it's returning either 0, 100%, or
// partial match when number of chars match
final String textValue = actualValue.getTextValue().toString();
final int total = textValue.length();
- if (total != userData.length()) return 0F;
+ if (total != userDataValue.length()) return 0F;
int matches = 0;
for (int i = 0; i < total; i++) {
if (Character.toLowerCase(textValue.charAt(i)) == Character
- .toLowerCase(userData.charAt(i))) {
+ .toLowerCase(userDataValue.charAt(i))) {
matches++;
}
}
return ((float) matches) / total;
}
-
- /////////////////////////////////////
- // Object "contract" methods. //
- /////////////////////////////////////
- @Override
- public String toString() {
- return "EditDistanceScorer";
- }
-
- /////////////////////////////////////
- // Parcelable "contract" methods. //
- /////////////////////////////////////
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- // Do nothing
- }
-
- public static final Parcelable.Creator<EditDistanceScorer> CREATOR =
- new Parcelable.Creator<EditDistanceScorer>() {
- @Override
- public EditDistanceScorer createFromParcel(Parcel parcel) {
- return EditDistanceScorer.getInstance();
- }
-
- @Override
- public EditDistanceScorer[] newArray(int size) {
- return new EditDistanceScorer[size];
- }
- };
}
diff --git a/core/java/android/service/autofill/Scorer.java b/core/java/android/service/autofill/FieldClassification.aidl
index c4018558b3ed..42f7f0252d6d 100644
--- a/core/java/android/service/autofill/Scorer.java
+++ b/core/java/android/service/autofill/FieldClassification.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
+/**
+ * Copyright (c) 2017, 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
+ * 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,
@@ -13,16 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.service.autofill;
-/**
- * Helper class used to calculate a score.
- *
- * <p>Typically used to calculate the
- * <a href="AutofillService.html#FieldClassification">field classification</a> score between an
- * actual {@link android.view.autofill.AutofillValue} filled by the user and the expected value
- * predicted by an autofill service.
- */
-public interface Scorer {
+package android.service.autofill;
-}
+parcelable FieldClassification.AlgorithmInfo;
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index 001b2917aadf..536180335a83 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -106,18 +106,20 @@ public final class FieldClassification {
/**
* Represents the score of a {@link UserData} entry for the field.
*
- * <p>The score is defined by {@link #getScore()} and the entry is identified by
- * {@link #getRemoteId()}.
+ * <p>The score is calculated by the given {@link #getAlgorithm() algorithm} and
+ * the entry is identified by {@link #getRemoteId()}.
*/
public static final class Match {
private final String mRemoteId;
private final float mScore;
+ private final String mAlgorithm;
/** @hide */
- public Match(String remoteId, float score) {
+ public Match(String remoteId, float score, String algorithm) {
mRemoteId = Preconditions.checkNotNull(remoteId);
mScore = score;
+ mAlgorithm = algorithm;
}
/**
@@ -140,29 +142,46 @@ public final class FieldClassification {
* <li>Any other value is a partial match.
* </ul>
*
- * <p>How the score is calculated depends on the algorithm used by the {@link Scorer}
- * implementation.
+ * <p>How the score is calculated depends on the
+ * {@link UserData.Builder#setFieldClassificationAlgorithm(String, android.os.Bundle)
+ * algorithm} used.
*/
public float getScore() {
return mScore;
}
+ /**
+ * Gets the algorithm used to calculate this score.
+ *
+ * <p>Typically, this is either the algorithm set by
+ * {@link UserData.Builder#setFieldClassificationAlgorithm(String, android.os.Bundle)},
+ * or the
+ * {@link android.view.autofill.AutofillManager#getDefaultFieldClassificationAlgorithm()}.
+ */
+ @NonNull
+ public String getAlgorithm() {
+ return mAlgorithm;
+ }
+
@Override
public String toString() {
if (!sDebug) return super.toString();
final StringBuilder string = new StringBuilder("Match: remoteId=");
Helper.appendRedacted(string, mRemoteId);
- return string.append(", score=").append(mScore).toString();
+ return string.append(", score=").append(mScore)
+ .append(", algorithm=").append(mAlgorithm)
+ .toString();
}
private void writeToParcel(@NonNull Parcel parcel) {
parcel.writeString(mRemoteId);
parcel.writeFloat(mScore);
+ parcel.writeString(mAlgorithm);
}
private static Match readFromParcel(@NonNull Parcel parcel) {
- return new Match(parcel.readString(), parcel.readFloat());
+ return new Match(parcel.readString(), parcel.readFloat(), parcel.readString());
}
}
}
diff --git a/core/java/android/service/autofill/InternalScorer.java b/core/java/android/service/autofill/InternalScorer.java
deleted file mode 100644
index 0da5afc2331d..000000000000
--- a/core/java/android/service/autofill/InternalScorer.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2017 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.service.autofill;
-
-import android.annotation.NonNull;
-import android.annotation.TestApi;
-import android.os.Parcelable;
-import android.view.autofill.AutofillValue;
-
-/**
- * Superclass of all scorer the system understands. As this is not public all
- * subclasses have to implement {@link Scorer} again.
- *
- * @hide
- */
-@TestApi
-public abstract class InternalScorer implements Scorer, Parcelable {
-
- /**
- * Returns the classification score between an actual {@link AutofillValue} filled
- * by the user and the expected value predicted by an autofill service.
- *
- * <p>A full-match is {@code 1.0} (representing 100%), a full mismatch is {@code 0.0} and
- * partial mathces are something in between, typically using edit-distance algorithms.
- */
- public abstract float getScore(@NonNull AutofillValue actualValue, @NonNull String userData);
-}
diff --git a/core/java/android/service/autofill/UserData.aidl b/core/java/android/service/autofill/UserData.aidl
index 76016ded424a..19282e0e7c85 100644
--- a/core/java/android/service/autofill/UserData.aidl
+++ b/core/java/android/service/autofill/UserData.aidl
@@ -17,4 +17,3 @@
package android.service.autofill;
parcelable UserData;
-parcelable UserData.Constraints;
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index f0cc360fe075..2f9225acc520 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -25,10 +25,13 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.content.ContentResolver;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Settings;
+import android.service.autofill.FieldClassification.Match;
import android.util.Log;
+import android.view.autofill.AutofillManager;
import android.view.autofill.Helper;
import com.android.internal.util.Preconditions;
@@ -49,21 +52,32 @@ public final class UserData implements Parcelable {
private static final int DEFAULT_MIN_VALUE_LENGTH = 5;
private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
- private final InternalScorer mScorer;
+ private final String mAlgorithm;
+ private final Bundle mAlgorithmArgs;
private final String[] mRemoteIds;
private final String[] mValues;
private UserData(Builder builder) {
- mScorer = builder.mScorer;
+ mAlgorithm = builder.mAlgorithm;
+ mAlgorithmArgs = builder.mAlgorithmArgs;
mRemoteIds = new String[builder.mRemoteIds.size()];
builder.mRemoteIds.toArray(mRemoteIds);
mValues = new String[builder.mValues.size()];
builder.mValues.toArray(mValues);
}
+ /**
+ * Gets the name of the algorithm that is used to calculate
+ * {@link Match#getScore() match scores}.
+ */
+ @Nullable
+ public String getFieldClassificationAlgorithm() {
+ return mAlgorithm;
+ }
+
/** @hide */
- public InternalScorer getScorer() {
- return mScorer;
+ public Bundle getAlgorithmArgs() {
+ return mAlgorithmArgs;
}
/** @hide */
@@ -78,7 +92,9 @@ public final class UserData implements Parcelable {
/** @hide */
public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("Scorer: "); pw.println(mScorer);
+ pw.print(prefix); pw.print("Algorithm: "); pw.print(mAlgorithm);
+ pw.print(" Args: "); pw.println(mAlgorithmArgs);
+
// Cannot disclose remote ids or values because they could contain PII
pw.print(prefix); pw.print("Remote ids size: "); pw.println(mRemoteIds.length);
for (int i = 0; i < mRemoteIds.length; i++) {
@@ -105,9 +121,10 @@ public final class UserData implements Parcelable {
* A builder for {@link UserData} objects.
*/
public static final class Builder {
- private final InternalScorer mScorer;
private final ArrayList<String> mRemoteIds;
private final ArrayList<String> mValues;
+ private String mAlgorithm;
+ private Bundle mAlgorithmArgs;
private boolean mDestroyed;
/**
@@ -120,13 +137,9 @@ public final class UserData implements Parcelable {
* <li>{@code value} is empty
* <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}
* <li>the length of {@code value} is higher than {@link UserData#getMaxValueLength()}
- * <li>{@code scorer} is not instance of a class provided by the Android System.
* </ol>
*/
- public Builder(@NonNull Scorer scorer, @NonNull String remoteId, @NonNull String value) {
- Preconditions.checkArgument((scorer instanceof InternalScorer),
- "not provided by Android System: " + scorer);
- mScorer = (InternalScorer) scorer;
+ public Builder(@NonNull String remoteId, @NonNull String value) {
checkValidRemoteId(remoteId);
checkValidValue(value);
final int capacity = getMaxUserDataSize();
@@ -137,6 +150,31 @@ public final class UserData implements Parcelable {
}
/**
+ * Sets the algorithm used for <a href="#FieldClassification">field classification</a>.
+ *
+ * <p>The currently available algorithms can be retrieve through
+ * {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}.
+ *
+ * <p><b>Note: </b>The available algorithms in the Android System can change dinamically,
+ * so it's not guaranteed that the algorithm set here is the one that will be effectually
+ * used. If the algorithm set here is not available at runtime, the
+ * {@link AutofillManager#getDefaultFieldClassificationAlgorithm()} is used instead.
+ * You can verify which algorithm was used by calling
+ * {@link FieldClassification.Match#getAlgorithm()}.
+ *
+ * @param name name of the algorithm or {@code null} to used default.
+ * @param args optional arguments to the algorithm.
+ *
+ * @return this builder
+ */
+ public Builder setFieldClassificationAlgorithm(@Nullable String name,
+ @Nullable Bundle args) {
+ mAlgorithm = name;
+ mAlgorithmArgs = args;
+ return this;
+ }
+
+ /**
* Adds a new value for user data.
*
* @param remoteId unique string used to identify the user data.
@@ -211,7 +249,7 @@ public final class UserData implements Parcelable {
public String toString() {
if (!sDebug) return super.toString();
- final StringBuilder builder = new StringBuilder("UserData: [scorer=").append(mScorer);
+ final StringBuilder builder = new StringBuilder("UserData: [algorithm=").append(mAlgorithm);
// Cannot disclose remote ids or values because they could contain PII
builder.append(", remoteIds=");
Helper.appendRedacted(builder, mRemoteIds);
@@ -231,9 +269,10 @@ public final class UserData implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeParcelable(mScorer, flags);
parcel.writeStringArray(mRemoteIds);
parcel.writeStringArray(mValues);
+ parcel.writeString(mAlgorithm);
+ parcel.writeBundle(mAlgorithmArgs);
}
public static final Parcelable.Creator<UserData> CREATOR =
@@ -243,10 +282,10 @@ public final class UserData implements Parcelable {
// Always go through the builder to ensure the data ingested by
// the system obeys the contract of the builder to avoid attacks
// using specially crafted parcels.
- final InternalScorer scorer = parcel.readParcelable(null);
final String[] remoteIds = parcel.readStringArray();
final String[] values = parcel.readStringArray();
- final Builder builder = new Builder(scorer, remoteIds[0], values[0]);
+ final Builder builder = new Builder(remoteIds[0], values[0])
+ .setFieldClassificationAlgorithm(parcel.readString(), parcel.readBundle());
for (int i = 1; i < remoteIds.length; i++) {
builder.add(remoteIds[i], values[i]);
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 269745455517..78b41c6f4c7b 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -53,6 +53,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -1027,7 +1028,9 @@ public final class AutofillManager {
* Gets the user data used for
* <a href="AutofillService.html#FieldClassification">field classification</a>.
*
- * <p><b>Note:</b> This method should only be called by an app providing an autofill service.
+ * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
+ * and it's ignored if the caller currently doesn't have an enabled autofill service for
+ * the user.
*
* @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
* reset or if the caller currently does not have an enabled autofill service for the user.
@@ -1079,6 +1082,49 @@ public final class AutofillManager {
}
/**
+ * Gets the name of the default algorithm used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>.
+ *
+ * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
+ * set.
+ *
+ * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
+ * and it's ignored if the caller currently doesn't have an enabled autofill service for
+ * the user.
+ */
+ @Nullable
+ public String getDefaultFieldClassificationAlgorithm() {
+ try {
+ return mService.getDefaultFieldClassificationAlgorithm();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return null;
+ }
+ }
+
+ /**
+ * Gets the name of all algorithms currently available for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>.
+ *
+ * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
+ * and it's ignored if the caller currently doesn't have an enabled autofill service for
+ * the user.
+ *
+ * @return list of all algorithms currently available, or an empty list if the caller currently
+ * does not have an enabled autofill service for the user.
+ */
+ @NonNull
+ public List<String> getAvailableFieldClassificationAlgorithms() {
+ try {
+ final List<String> names = mService.getAvailableFieldClassificationAlgorithms();
+ return names != null ? names : Collections.emptyList();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return null;
+ }
+ }
+
+ /**
* Returns {@code true} if autofill is supported by the current device and
* is supported for this user.
*
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 38bb311986e8..1afa35e80a26 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -58,4 +58,6 @@ interface IAutoFillManager {
void setUserData(in UserData userData);
boolean isFieldClassificationEnabled();
ComponentName getAutofillServiceComponentName();
+ List<String> getAvailableFieldClassificationAlgorithms();
+ String getDefaultFieldClassificationAlgorithm();
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index e1cb154c88f5..cac7fedd0b00 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -479,7 +479,7 @@ public final class AutofillManagerService extends SystemService {
if (service != null) {
service.destroySessionsLocked();
service.updateLocked(disabled);
- if (!service.isEnabled()) {
+ if (!service.isEnabledLocked()) {
removeCachedServiceLocked(userId);
}
}
@@ -621,6 +621,34 @@ public final class AutofillManagerService extends SystemService {
}
@Override
+ public String getDefaultFieldClassificationAlgorithm() throws RemoteException {
+ final int userId = UserHandle.getCallingUserId();
+
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ return service.getDefaultFieldClassificationAlgorithm(getCallingUid());
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public List<String> getAvailableFieldClassificationAlgorithms() throws RemoteException {
+ final int userId = UserHandle.getCallingUserId();
+
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ return service.getAvailableFieldClassificationAlgorithms(getCallingUid());
+ }
+ }
+
+ return null;
+ }
+
+ @Override
public ComponentName getAutofillServiceComponentName() throws RemoteException {
final int userId = UserHandle.getCallingUserId();
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4cdfd625a8f0..65984dd5fdbe 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -51,6 +51,7 @@ import android.os.UserManager;
import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
+import android.service.autofill.EditDistanceScorer;
import android.service.autofill.FieldClassification;
import android.service.autofill.FieldClassification.Match;
import android.service.autofill.FillEventHistory;
@@ -63,6 +64,8 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.LocalLog;
+import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -81,6 +84,7 @@ import com.android.server.autofill.ui.AutoFillUI;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Random;
@@ -105,7 +109,10 @@ final class AutofillManagerServiceImpl {
private final AutoFillUI mUi;
private final MetricsLogger mMetricsLogger = new MetricsLogger();
+ @GuardedBy("mLock")
private RemoteCallbackList<IAutoFillManagerClient> mClients;
+
+ @GuardedBy("mLock")
private AutofillServiceInfo mInfo;
private static final Random sRandom = new Random();
@@ -113,25 +120,74 @@ final class AutofillManagerServiceImpl {
private final LocalLog mRequestsHistory;
private final LocalLog mUiLatencyHistory;
+ // TODO(b/70939974): temporary, will be moved to ExtServices
+ static final class FieldClassificationAlgorithmService {
+
+ /**
+ * Gets the name of all available algorithms.
+ */
+ @NonNull
+ public List<String> getAvailableAlgorithms() {
+ return Arrays.asList(EditDistanceScorer.NAME);
+ }
+
+ /**
+ * Gets the default algorithm that's used when an algorithm is not specified or is invalid.
+ */
+ @NonNull
+ public String getDefaultAlgorithm() {
+ return EditDistanceScorer.NAME;
+ }
+
+ /**
+ * Gets a field classification score.
+ *
+ * @param algorithmName algorithm to be used. If invalid, the default algorithm will be used
+ * instead.
+ * @param algorithmArgs optional arguments to be passed to the algorithm.
+ * @param actualValue value entered by the user.
+ * @param userDataValue value from the user data.
+ *
+ * @return pair containing the algorithm used and the score.
+ */
+ // TODO(b/70939974): use parcelable instead of pair
+ Pair<String, Float> getScore(@NonNull String algorithmName, @Nullable Bundle algorithmArgs,
+ @NonNull AutofillValue actualValue, @NonNull String userDataValue) {
+ if (!EditDistanceScorer.NAME.equals(algorithmName)) {
+ Log.w(TAG, "Ignoring invalid algorithm (" + algorithmName + ") and using "
+ + EditDistanceScorer.NAME + " instead");
+ }
+ return new Pair<>(EditDistanceScorer.NAME,
+ EditDistanceScorer.getInstance().getScore(actualValue, userDataValue));
+ }
+ }
+
+ private final FieldClassificationAlgorithmService mFcService =
+ new FieldClassificationAlgorithmService();
+
/**
* Apps disabled by the service; key is package name, value is when they will be enabled again.
*/
+ @GuardedBy("mLock")
private ArrayMap<String, Long> mDisabledApps;
/**
* Activities disabled by the service; key is component name, value is when they will be enabled
* again.
*/
+ @GuardedBy("mLock")
private ArrayMap<ComponentName, Long> mDisabledActivities;
/**
* Whether service was disabled for user due to {@link UserManager} restrictions.
*/
+ @GuardedBy("mLock")
private boolean mDisabled;
/**
* Data used for field classification.
*/
+ @GuardedBy("mLock")
private UserData mUserData;
/**
@@ -235,7 +291,7 @@ final class AutofillManagerServiceImpl {
}
void updateLocked(boolean disabled) {
- final boolean wasEnabled = isEnabled();
+ final boolean wasEnabled = isEnabledLocked();
if (sVerbose) {
Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
+ ", mSetupComplete= " + mSetupComplete
@@ -274,7 +330,7 @@ final class AutofillManagerServiceImpl {
Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e);
mInfo = null;
}
- final boolean isEnabled = isEnabled();
+ final boolean isEnabled = isEnabledLocked();
if (wasEnabled != isEnabled) {
if (!isEnabled) {
final int sessionCount = mSessions.size();
@@ -292,7 +348,7 @@ final class AutofillManagerServiceImpl {
mClients = new RemoteCallbackList<>();
}
mClients.register(client);
- return isEnabled();
+ return isEnabledLocked();
}
void removeClientLocked(IAutoFillManagerClient client) {
@@ -302,7 +358,7 @@ final class AutofillManagerServiceImpl {
}
void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) {
- if (!isEnabled()) {
+ if (!isEnabledLocked()) {
return;
}
final Session session = mSessions.get(sessionId);
@@ -312,7 +368,7 @@ final class AutofillManagerServiceImpl {
}
void setHasCallback(int sessionId, int uid, boolean hasIt) {
- if (!isEnabled()) {
+ if (!isEnabledLocked()) {
return;
}
final Session session = mSessions.get(sessionId);
@@ -327,7 +383,7 @@ final class AutofillManagerServiceImpl {
@NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
@NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
int flags, @NonNull ComponentName componentName) {
- if (!isEnabled()) {
+ if (!isEnabledLocked()) {
return 0;
}
@@ -388,7 +444,7 @@ final class AutofillManagerServiceImpl {
}
void finishSessionLocked(int sessionId, int uid) {
- if (!isEnabled()) {
+ if (!isEnabledLocked()) {
return;
}
@@ -411,7 +467,7 @@ final class AutofillManagerServiceImpl {
}
void cancelSessionLocked(int sessionId, int uid) {
- if (!isEnabled()) {
+ if (!isEnabledLocked()) {
return;
}
@@ -799,14 +855,15 @@ final class AutofillManagerServiceImpl {
// Called by AutofillManager
void setUserData(int callingUid, UserData userData) {
synchronized (mLock) {
- if (isCalledByServiceLocked("setUserData", callingUid)) {
- mUserData = userData;
- // Log it
- int numberFields = mUserData == null ? 0: mUserData.getRemoteIds().length;
- mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED,
- getServicePackageName(), null)
- .setCounterValue(numberFields));
+ if (!isCalledByServiceLocked("setUserData", callingUid)) {
+ return;
}
+ mUserData = userData;
+ // Log it
+ int numberFields = mUserData == null ? 0: mUserData.getRemoteIds().length;
+ mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED,
+ getServicePackageName(), null)
+ .setCounterValue(numberFields));
}
}
@@ -917,6 +974,11 @@ final class AutofillManagerServiceImpl {
pw.println();
mUserData.dump(prefix2, pw);
}
+
+ pw.print(prefix); pw.print("Available Field Classification algorithms: ");
+ pw.println(mFcService.getAvailableAlgorithms());
+ pw.print(prefix); pw.print("Default Field Classification algorithm: ");
+ pw.println(mFcService.getDefaultAlgorithm());
}
void destroySessionsLocked() {
@@ -964,11 +1026,13 @@ final class AutofillManagerServiceImpl {
final IAutoFillManagerClient client = clients.getBroadcastItem(i);
try {
final boolean resetSession;
+ final boolean isEnabled;
synchronized (mLock) {
resetSession = resetClient || isClientSessionDestroyedLocked(client);
+ isEnabled = isEnabledLocked();
}
int flags = 0;
- if (isEnabled()) {
+ if (isEnabled) {
flags |= AutofillManager.SET_STATE_FLAG_ENABLED;
}
if (resetSession) {
@@ -1004,7 +1068,7 @@ final class AutofillManagerServiceImpl {
return true;
}
- boolean isEnabled() {
+ boolean isEnabledLocked() {
return mSetupComplete && mInfo != null && !mDisabled;
}
@@ -1093,9 +1157,9 @@ final class AutofillManagerServiceImpl {
}
// Called by AutofillManager, checks UID.
- boolean isFieldClassificationEnabled(int uid) {
+ boolean isFieldClassificationEnabled(int callingUid) {
synchronized (mLock) {
- if (!isCalledByServiceLocked("isFieldClassificationEnabled", uid)) {
+ if (!isCalledByServiceLocked("isFieldClassificationEnabled", callingUid)) {
return false;
}
return isFieldClassificationEnabledLocked();
@@ -1110,6 +1174,28 @@ final class AutofillManagerServiceImpl {
mUserId) == 1;
}
+ FieldClassificationAlgorithmService getFieldClassificationService() {
+ return mFcService;
+ }
+
+ List<String> getAvailableFieldClassificationAlgorithms(int callingUid) {
+ synchronized (mLock) {
+ if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) {
+ return null;
+ }
+ }
+ return mFcService.getAvailableAlgorithms();
+ }
+
+ String getDefaultFieldClassificationAlgorithm(int callingUid) {
+ synchronized (mLock) {
+ if (!isCalledByServiceLocked("getDefaultFCAlgorithm()", callingUid)) {
+ return null;
+ }
+ }
+ return mFcService.getDefaultAlgorithm();
+ }
+
@Override
public String toString() {
return "AutofillManagerServiceImpl: [userId=" + mUserId
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 01f908407691..96296907b2c5 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -69,6 +69,7 @@ import android.service.autofill.FieldClassification;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -84,6 +85,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.HandlerCaller;
import com.android.internal.util.ArrayUtils;
+import com.android.server.autofill.AutofillManagerServiceImpl.FieldClassificationAlgorithmService;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.autofill.ui.PendingUi;
@@ -1088,7 +1090,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Sets field classification score for field
if (userData!= null) {
- setScore(detectedFieldIds, detectedFieldClassifications, userData,
+ setFieldClassificationScore(mService.getFieldClassificationService(),
+ detectedFieldIds, detectedFieldClassifications, userData,
viewState.id, currentValue);
}
} // else
@@ -1133,7 +1136,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* Adds the matches to {@code detectedFieldsIds} and {@code detectedFieldClassifications} for
* {@code fieldId} based on its {@code currentValue} and {@code userData}.
*/
- private static void setScore(@NonNull ArrayList<AutofillId> detectedFieldIds,
+ private static void setFieldClassificationScore(
+ @NonNull AutofillManagerServiceImpl.FieldClassificationAlgorithmService service,
+ @NonNull ArrayList<AutofillId> detectedFieldIds,
@NonNull ArrayList<FieldClassification> detectedFieldClassifications,
@NonNull UserData userData, @NonNull AutofillId fieldId,
@NonNull AutofillValue currentValue) {
@@ -1150,11 +1155,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
+ final String algorithm = userData.getFieldClassificationAlgorithm();
+ final Bundle algorithmArgs = userData.getAlgorithmArgs();
ArrayList<Match> matches = null;
for (int i = 0; i < userValues.length; i++) {
String remoteId = remoteIds[i];
final String value = userValues[i];
- final float score = userData.getScorer().getScore(currentValue, value);
+ final Pair<String, Float> result = service.getScore(algorithm, algorithmArgs,
+ currentValue, value);
+ final String actualAlgorithm = result.first;
+ final float score = result.second;
if (score > 0) {
if (sVerbose) {
Slog.v(TAG, "adding score " + score + " at index " + i + " and id " + fieldId);
@@ -1162,7 +1172,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (matches == null) {
matches = new ArrayList<>(userValues.length);
}
- matches.add(new Match(remoteId, score));
+ matches.add(new Match(remoteId, score, actualAlgorithm));
}
else if (sVerbose) Slog.v(TAG, "skipping score 0 at index " + i + " and id " + fieldId);
}