diff options
| -rw-r--r-- | api/current.txt | 17 | ||||
| -rw-r--r-- | api/test-current.txt | 8 | ||||
| -rw-r--r-- | core/java/android/service/autofill/EditDistanceScorer.java | 67 | ||||
| -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.java | 33 | ||||
| -rw-r--r-- | core/java/android/service/autofill/InternalScorer.java | 40 | ||||
| -rw-r--r-- | core/java/android/service/autofill/UserData.aidl | 1 | ||||
| -rw-r--r-- | core/java/android/service/autofill/UserData.java | 69 | ||||
| -rw-r--r-- | core/java/android/view/autofill/AutofillManager.java | 48 | ||||
| -rw-r--r-- | core/java/android/view/autofill/IAutoFillManager.aidl | 2 | ||||
| -rw-r--r-- | services/autofill/java/com/android/server/autofill/AutofillManagerService.java | 30 | ||||
| -rw-r--r-- | services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java | 124 | ||||
| -rw-r--r-- | services/autofill/java/com/android/server/autofill/Session.java | 18 | 
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);          }  |