Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 51f6cd70 authored by Felipe Leme's avatar Felipe Leme
Browse files

Implemented multiple matches on FieldClassification.getMatches()

Test: atest CtsAutoFillServiceTestCases:FieldsClassificationTest

Bug:70291841

Change-Id: Icc015d7c76f0f11e398c3093b4ea070c8f35f589
parent 01232ca5
Loading
Loading
Loading
Loading
+29 −10
Original line number Diff line number Diff line
@@ -25,8 +25,9 @@ import android.view.autofill.Helper;

import com.android.internal.util.Preconditions;

import com.google.android.collect.Lists;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
@@ -36,15 +37,24 @@ import java.util.List;
// TODO(b/70291841): let caller handle Parcelable...
public final class FieldClassification implements Parcelable {

    private final Match mMatch;
    private final ArrayList<Match> mMatches;

    /** @hide */
    public FieldClassification(@NonNull Match match) {
        mMatch = Preconditions.checkNotNull(match);
    public FieldClassification(@NonNull ArrayList<Match> matches) {
        mMatches = Preconditions.checkNotNull(matches);
        Collections.sort(mMatches, new Comparator<Match>() {
            @Override
            public int compare(Match o1, Match o2) {
                if (o1.mScore > o2.mScore) return -1;
                if (o1.mScore < o2.mScore) return 1;
                return 0;
            }}
        );
    }

    /**
     * Gets the {@link Match matches} with the highest {@link Match#getScore() scores}.
     * Gets the {@link Match matches} with the highest {@link Match#getScore() scores} (sorted in
     * descending order).
     *
     * <p><b>Note:</b> There's no guarantee of how many matches will be returned. In fact,
     * the Android System might return just the top match to minimize the impact of field
@@ -52,14 +62,14 @@ public final class FieldClassification implements Parcelable {
     */
    @NonNull
    public List<Match> getMatches() {
        return Lists.newArrayList(mMatch);
        return mMatches;
    }

    @Override
    public String toString() {
        if (!sDebug) return super.toString();

        return "FieldClassification: " + mMatch;
        return "FieldClassification: " + mMatches;
    }

    /////////////////////////////////////
@@ -73,7 +83,10 @@ public final class FieldClassification implements Parcelable {

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        mMatch.writeToParcel(parcel);
        parcel.writeInt(mMatches.size());
        for (int i = 0; i < mMatches.size(); i++) {
            mMatches.get(i).writeToParcel(parcel);
        }
    }

    public static final Parcelable.Creator<FieldClassification> CREATOR =
@@ -81,7 +94,13 @@ public final class FieldClassification implements Parcelable {

        @Override
        public FieldClassification createFromParcel(Parcel parcel) {
            return new FieldClassification(Match.readFromParcel(parcel));
            final int size = parcel.readInt();
            final ArrayList<Match> matches = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                matches.add(i, Match.readFromParcel(parcel));
            }

            return new FieldClassification(matches);
        }

        @Override
+15 −13
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.autofill.FieldClassification.Match;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -157,7 +156,7 @@ public final class FillEventHistory implements Parcelable {
                final AutofillId[] detectedFields = event.mDetectedFieldIds;
                parcel.writeParcelableArray(detectedFields, flags);
                if (detectedFields != null) {
                    Match.writeArrayToParcel(parcel, event.mDetectedMatches);
                    parcel.writeParcelableArray(event.mDetectedFieldClassifications, flags);
                }
            }
        }
@@ -251,7 +250,7 @@ public final class FillEventHistory implements Parcelable {
        @Nullable private final ArrayList<ArrayList<String>> mManuallyFilledDatasetIds;

        @Nullable private final AutofillId[] mDetectedFieldIds;
        @Nullable private final Match[] mDetectedMatches;
        @Nullable private final FieldClassification[] mDetectedFieldClassifications;

        /**
         * Returns the type of the event.
@@ -370,11 +369,11 @@ public final class FillEventHistory implements Parcelable {
            final ArrayMap<AutofillId, FieldClassification> map = new ArrayMap<>(size);
            for (int i = 0; i < size; i++) {
                final AutofillId id = mDetectedFieldIds[i];
                final Match match = mDetectedMatches[i];
                final FieldClassification fc = mDetectedFieldClassifications[i];
                if (sVerbose) {
                    Log.v(TAG, "getFieldsClassification[" + i + "]: id=" + id + ", match=" + match);
                    Log.v(TAG, "getFieldsClassification[" + i + "]: id=" + id + ", fc=" + fc);
                }
                map.put(id, new FieldClassification(match));
                map.put(id, fc);
            }
            return map;
        }
@@ -455,7 +454,7 @@ public final class FillEventHistory implements Parcelable {
         * and belonged to datasets.
         * @param manuallyFilledDatasetIds The ids of datasets that had values matching the
         * respective entry on {@code manuallyFilledFieldIds}.
         * @param detectedMatches the field classification matches.
         * @param detectedFieldClassifications the field classification matches.
         *
         * @throws IllegalArgumentException If the length of {@code changedFieldIds} and
         * {@code changedDatasetIds} doesn't match.
@@ -471,7 +470,8 @@ public final class FillEventHistory implements Parcelable {
                @Nullable ArrayList<String> changedDatasetIds,
                @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
                @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
                @Nullable AutofillId[] detectedFieldIds, @Nullable Match[] detectedMatches) {
                @Nullable AutofillId[] detectedFieldIds,
                @Nullable FieldClassification[] detectedFieldClassifications) {
            mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_CONTEXT_COMMITTED,
                    "eventType");
            mDatasetId = datasetId;
@@ -496,7 +496,7 @@ public final class FillEventHistory implements Parcelable {
            mManuallyFilledDatasetIds = manuallyFilledDatasetIds;

            mDetectedFieldIds = detectedFieldIds;
            mDetectedMatches = detectedMatches;
            mDetectedFieldClassifications = detectedFieldClassifications;
        }

        @Override
@@ -510,7 +510,8 @@ public final class FillEventHistory implements Parcelable {
                    + ", manuallyFilledFieldIds=" + mManuallyFilledFieldIds
                    + ", manuallyFilledDatasetIds=" + mManuallyFilledDatasetIds
                    + ", detectedFieldIds=" + Arrays.toString(mDetectedFieldIds)
                    + ", detectedMaches =" + Arrays.toString(mDetectedMatches)
                    + ", detectedFieldClassifications ="
                        + Arrays.toString(mDetectedFieldClassifications)
                    + "]";
        }
    }
@@ -548,15 +549,16 @@ public final class FillEventHistory implements Parcelable {
                        }
                        final AutofillId[] detectedFieldIds = parcel.readParcelableArray(null,
                                AutofillId.class);
                        final Match[] detectedMatches = (detectedFieldIds != null)
                                ? Match.readArrayFromParcel(parcel)
                        final FieldClassification[] detectedFieldClassifications =
                                (detectedFieldIds != null)
                                ? parcel.readParcelableArray(null, FieldClassification.class)
                                : null;

                        selection.addEvent(new Event(eventType, datasetId, clientState,
                                selectedDatasetIds, ignoredDatasets,
                                changedFieldIds, changedDatasetIds,
                                manuallyFilledFieldIds, manuallyFilledDatasetIds,
                                detectedFieldIds, detectedMatches));
                                detectedFieldIds, detectedFieldClassifications));
                    }
                    return selection;
                }
+25 −15
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ import android.os.UserManager;
import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
import android.service.autofill.FieldClassification;
import android.service.autofill.FieldClassification.Match;
import android.service.autofill.FillEventHistory;
import android.service.autofill.FillEventHistory.Event;
@@ -81,6 +82,7 @@ import com.android.server.autofill.ui.AutoFillUI;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
@@ -720,37 +722,45 @@ final class AutofillManagerServiceImpl {
            @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
            @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
            @Nullable ArrayList<AutofillId> detectedFieldIdsList,
            @Nullable ArrayList<Match> detectedMatchesList,
            @Nullable ArrayList<FieldClassification> detectedFieldClassificationsList,
            @NonNull String appPackageName) {

        synchronized (mLock) {
            if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
                AutofillId[] detectedFieldsIds = null;
                Match[] detectedMatches = null;
                FieldClassification[] detectedFieldClassifications = null;
                if (detectedFieldIdsList != null) {
                    detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
                    detectedFieldIdsList.toArray(detectedFieldsIds);
                    detectedMatches = new Match[detectedMatchesList.size()];
                    detectedMatchesList.toArray(detectedMatches);
                    detectedFieldClassifications =
                            new FieldClassification[detectedFieldClassificationsList.size()];
                    detectedFieldClassificationsList.toArray(detectedFieldClassifications);

                    final int size = detectedMatchesList.size();
                    final int numberFields = detectedFieldsIds.length;
                    int totalSize = 0;
                    float totalScore = 0;
                    for (int i = 0; i < size; i++) {
                        totalScore += detectedMatches[i].getScore();
                    for (int i = 0; i < numberFields; i++) {
                        final FieldClassification fc = detectedFieldClassifications[i];
                        final List<Match> matches = fc.getMatches();
                        final int size = matches.size();
                        totalSize += size;
                        for (int j = 0; j < size; j++) {
                            totalScore += matches.get(j).getScore();
                        }
                    }
                    final int averageScore = (int) ((totalScore * 100) / size);
                    mMetricsLogger.write(
                            Helper.newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
                                    appPackageName, getServicePackageName())
                            .setCounterValue(size)
                            .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE, averageScore));

                    final int averageScore = (int) ((totalScore * 100) / totalSize);
                    mMetricsLogger.write(Helper
                            .newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
                                    appPackageName, getServicePackageName())
                            .setCounterValue(numberFields)
                            .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE,
                                    averageScore));
                }
                mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
                        clientState, selectedDatasets, ignoredDatasets,
                        changedFieldIds, changedDatasetIds,
                        manuallyFilledFieldIds, manuallyFilledDatasetIds,
                        detectedFieldsIds, detectedMatches));
                        detectedFieldsIds, detectedFieldClassifications));
            }
        }
    }
+26 −21
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ import android.service.autofill.SaveRequest;
import android.service.autofill.UserData;
import android.service.autofill.ValueFinder;
import android.service.autofill.EditDistanceScorer;
import android.service.autofill.FieldClassification;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
@@ -961,15 +962,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        final UserData userData = mService.getUserData();

        final ArrayList<AutofillId> detectedFieldIds;
        final ArrayList<Match> detectedMatches;
        final ArrayList<FieldClassification> detectedFieldClassifications;

        if (userData != null) {
            final int maxFieldsSize = UserData.getMaxFieldClassificationIdsSize();
            detectedFieldIds = new ArrayList<>(maxFieldsSize);
            detectedMatches = new ArrayList<>(maxFieldsSize);
            detectedFieldClassifications = new ArrayList<>(maxFieldsSize);
        } else {
            detectedFieldIds = null;
            detectedMatches = null;
            detectedFieldClassifications = null;
        }

        for (int i = 0; i < mViewStates.size(); i++) {
@@ -1078,8 +1079,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState

                    // Sets field classification score for field
                    if (userData!= null) {
                        setScore(detectedFieldIds, detectedMatches, userData, viewState.id,
                                currentValue);
                        setScore(detectedFieldIds, detectedFieldClassifications, userData,
                                viewState.id, currentValue);
                    }
                } // else
            } // else
@@ -1093,7 +1094,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                    + ", changedDatasetIds=" + changedDatasetIds
                    + ", manuallyFilledIds=" + manuallyFilledIds
                    + ", detectedFieldIds=" + detectedFieldIds
                    + ", detectedMatches=" + detectedMatches
                    + ", detectedFieldClassifications=" + detectedFieldClassifications
                    );
        }

@@ -1116,16 +1117,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        mService.logContextCommitted(id, mClientState, mSelectedDatasetIds, ignoredDatasets,
                changedFieldIds, changedDatasetIds,
                manuallyFilledFieldIds, manuallyFilledDatasetIds,
                detectedFieldIds, detectedMatches, mComponentName.getPackageName());
                detectedFieldIds, detectedFieldClassifications, mComponentName.getPackageName());
    }

    /**
     * Adds the top score match to {@code detectedFieldsIds} and {@code detectedMatches} for
     * 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,
            @NonNull ArrayList<Match> detectedMatches, @NonNull UserData userData,
            @NonNull AutofillId fieldId, @NonNull AutofillValue currentValue) {
            @NonNull ArrayList<FieldClassification> detectedFieldClassifications,
            @NonNull UserData userData, @NonNull AutofillId fieldId,
            @NonNull AutofillValue currentValue) {

        final String[] userValues = userData.getValues();
        final String[] remoteIds = userData.getRemoteIds();
@@ -1138,23 +1140,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                    + valuesLength + ", ids.length = " + idsLength);
            return;
        }
        String remoteId = null;
        float topScore = 0;

        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);
            if (score > topScore) {
                topScore = score;
                remoteId = remoteIds[i];
            if (score > 0) {
                if (sVerbose) {
                    Slog.v(TAG, "adding score " + score + " at index " + i + " and id " + fieldId);
                }
                if (matches == null) {
                    matches = new ArrayList<>(userValues.length);
                }

        if (remoteId != null && topScore > 0) {
            if (sVerbose) Slog.v(TAG, "setScores(): top score for #" + fieldId + " is " + topScore);
                matches.add(new Match(remoteId, score));
            }
            else if (sVerbose) Slog.v(TAG, "skipping score 0 at index " + i + " and id " + fieldId);
        }
        if (matches != null) {
            detectedFieldIds.add(fieldId);
            detectedMatches.add(new Match(remoteId, topScore));
        } else if (sVerbose) {
            Slog.v(TAG, "setScores(): no top score for #" + fieldId + ": " + topScore);
            detectedFieldClassifications.add(new FieldClassification(matches));
        }
    }