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

Commit 61ba41b3 authored by Simranjit Kohli's avatar Simranjit Kohli Committed by Automerger Merge Worker
Browse files

Merge "[Autofill PCC]: Allow Providers to set both field and type Allow...

Merge "[Autofill PCC]: Allow Providers to set both field and type Allow providers to set both field and type while creating a dataset. Also, fix NPE, and issue a request if no detection results available. Test: atest android.autofillservice.cts.unittests.DatasetTest BUG: 270423491" into udc-dev am: 041ef18b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21675600



Change-Id: I08adcc17f79aee68f0edc544db2b3c2aa14b13c4
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents fd8c7108 041ef18b
Loading
Loading
Loading
Loading
+81 −55
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.content.ClipData;
import android.content.IntentSender;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
@@ -434,14 +435,14 @@ public final class Dataset implements Parcelable {
     * one value for a field or set an authentication intent.
     */
    public static final class Builder {
        private ArrayList<AutofillId> mFieldIds;
        private ArrayList<AutofillValue> mFieldValues;
        private ArrayList<RemoteViews> mFieldPresentations;
        private ArrayList<RemoteViews> mFieldDialogPresentations;
        private ArrayList<InlinePresentation> mFieldInlinePresentations;
        private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations;
        private ArrayList<DatasetFieldFilter> mFieldFilters;
        private ArrayList<String> mAutofillDatatypes;
        private ArrayList<AutofillId> mFieldIds = new ArrayList<>();
        private ArrayList<AutofillValue> mFieldValues = new ArrayList();
        private ArrayList<RemoteViews> mFieldPresentations = new ArrayList();
        private ArrayList<RemoteViews> mFieldDialogPresentations = new ArrayList();
        private ArrayList<InlinePresentation> mFieldInlinePresentations = new ArrayList();
        private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations = new ArrayList();
        private ArrayList<DatasetFieldFilter> mFieldFilters = new ArrayList();
        private ArrayList<String> mAutofillDatatypes = new ArrayList();
        @Nullable private ClipData mFieldContent;
        private RemoteViews mPresentation;
        private RemoteViews mDialogPresentation;
@@ -451,6 +452,15 @@ public final class Dataset implements Parcelable {
        private boolean mDestroyed;
        @Nullable private String mId;

        /**
         * Usually, a field will be associated with a single autofill id and/or datatype.
         * There could be null field value corresponding to different autofill ids or datatye
         * values, but the implementation is ok with duplicating that information.
         * This map is just for the purpose of optimization, to reduce the size of the pelled data
         * over the binder transaction.
         */
        private ArrayMap<Field, Integer> mFieldToIndexdMap = new ArrayMap<>();

        /**
         * Creates a new builder.
         *
@@ -1051,29 +1061,40 @@ public final class Dataset implements Parcelable {
         */
        public @NonNull Builder setField(@NonNull AutofillId id, @Nullable Field field) {
            throwIfDestroyed();

            if (mFieldToIndexdMap.containsKey(field)) {
                int index = mFieldToIndexdMap.get(field);
                if (mFieldIds.get(index) == null) {
                    mFieldIds.set(index, id);
                    return this;
                }
                // if the Autofill Id is already set, ignore and proceed as if setting in a new
                // value.
            }
            int index;
            if (field == null) {
                setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
                index = setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
            } else {
                final DatasetFieldFilter filter = field.getDatasetFieldFilter();
                final Presentations presentations = field.getPresentations();
                if (presentations == null) {
                    setLifeTheUniverseAndEverything(id, field.getValue(), null, null, null,
                    index = setLifeTheUniverseAndEverything(id, field.getValue(), null, null, null,
                            filter, null);
                } else {
                    setLifeTheUniverseAndEverything(id, field.getValue(),
                    index = setLifeTheUniverseAndEverything(id, field.getValue(),
                            presentations.getMenuPresentation(),
                            presentations.getInlinePresentation(),
                            presentations.getInlineTooltipPresentation(), filter,
                            presentations.getDialogPresentation());
                }
            }
            mFieldToIndexdMap.put(field, index);
            return this;
        }

        /**
         * Adds a field to this Dataset with a specific type and no
         * AutofillId. This is used to send back Field information
         * when Autofilling with platform detections is on.
         * Adds a field to this Dataset with a specific type. This is used to send back Field
         * information when Autofilling with platform detections is on.
         * Platform detections are on when receiving a populated list from
         * FillRequest#getHints().
         *
@@ -1086,9 +1107,6 @@ public final class Dataset implements Parcelable {
         * has two credential pairs, then two Datasets should be created,
         * and so on.
         *
         * Using this will remove any data populated with
         * setField(@NonNull AutofillId id, @Nullable Field field).
         *
         * @param hint An autofill hint returned from {@link
         *         FillRequest#getHints()}.
         *
@@ -1102,19 +1120,29 @@ public final class Dataset implements Parcelable {
        public @NonNull Dataset.Builder setField(@NonNull String hint, @NonNull Field field) {
            throwIfDestroyed();

            if (mFieldToIndexdMap.containsKey(field)) {
                int index = mFieldToIndexdMap.get(field);
                if (mAutofillDatatypes.get(index) == null) {
                    mAutofillDatatypes.set(index, hint);
                    return this;
                }
                // if the hint is already set, ignore and proceed as if setting in a new hint.
            }

            int index;
            final DatasetFieldFilter filter = field.getDatasetFieldFilter();
            final Presentations presentations = field.getPresentations();
            if (presentations == null) {
                setLifeTheUniverseAndEverything(hint, field.getValue(), null, null, null,
                index = setLifeTheUniverseAndEverything(hint, field.getValue(), null, null, null,
                        filter, null);
            } else {
                setLifeTheUniverseAndEverything(hint, field.getValue(),
                index = setLifeTheUniverseAndEverything(hint, field.getValue(),
                        presentations.getMenuPresentation(),
                        presentations.getInlinePresentation(),
                        presentations.getInlineTooltipPresentation(), filter,
                        presentations.getDialogPresentation());
            }

            mFieldToIndexdMap.put(field, index);
            return this;
        }

@@ -1172,40 +1200,45 @@ public final class Dataset implements Parcelable {
            return this;
        }

        private void setLifeTheUniverseAndEverything(String datatype,
        /** Returns the index at which this id was modified or inserted */
        private int setLifeTheUniverseAndEverything(@NonNull String datatype,
                @Nullable AutofillValue value,
                @Nullable RemoteViews presentation,
                @Nullable InlinePresentation inlinePresentation,
                @Nullable InlinePresentation tooltip,
                @Nullable DatasetFieldFilter filter,
                @Nullable RemoteViews dialogPresentation) {
            if (mAutofillDatatypes == null) {
                mFieldValues = new ArrayList<>();
                mFieldPresentations = new ArrayList<>();
                mFieldDialogPresentations = new ArrayList<>();
                mFieldInlinePresentations = new ArrayList<>();
                mFieldInlineTooltipPresentations = new ArrayList<>();
                mFieldFilters = new ArrayList<>();
                mAutofillDatatypes = new ArrayList<>();
                mFieldIds = null;
            Objects.requireNonNull(datatype, "datatype cannot be null");
            final int existingIdx = mAutofillDatatypes.indexOf(datatype);
            if (existingIdx >= 0) {
                mAutofillDatatypes.add(datatype);
                mFieldValues.set(existingIdx, value);
                mFieldPresentations.set(existingIdx, presentation);
                mFieldDialogPresentations.set(existingIdx, dialogPresentation);
                mFieldInlinePresentations.set(existingIdx, inlinePresentation);
                mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
                mFieldFilters.set(existingIdx, filter);
                return existingIdx;
            }
            mFieldIds.add(null);
            mAutofillDatatypes.add(datatype);
            mFieldValues.add(value);
            mFieldPresentations.add(presentation);
            mFieldDialogPresentations.add(dialogPresentation);
            mFieldInlinePresentations.add(inlinePresentation);
            mFieldInlineTooltipPresentations.add(tooltip);
            mFieldFilters.add(filter);
            mAutofillDatatypes.add(datatype);
            return mFieldIds.size() - 1;
        }

        private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
        /** Returns the index at which this id was modified or inserted */
        private int setLifeTheUniverseAndEverything(@NonNull AutofillId id,
                @Nullable AutofillValue value, @Nullable RemoteViews presentation,
                @Nullable InlinePresentation inlinePresentation,
                @Nullable InlinePresentation tooltip,
                @Nullable DatasetFieldFilter filter,
                @Nullable RemoteViews dialogPresentation) {
            Objects.requireNonNull(id, "id cannot be null");
            if (mFieldIds != null) {
            final int existingIdx = mFieldIds.indexOf(id);
            if (existingIdx >= 0) {
                mFieldValues.set(existingIdx, value);
@@ -1214,25 +1247,17 @@ public final class Dataset implements Parcelable {
                mFieldInlinePresentations.set(existingIdx, inlinePresentation);
                mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
                mFieldFilters.set(existingIdx, filter);
                    return;
                }
            } else {
                mFieldIds = new ArrayList<>();
                mFieldValues = new ArrayList<>();
                mFieldPresentations = new ArrayList<>();
                mFieldDialogPresentations = new ArrayList<>();
                mFieldInlinePresentations = new ArrayList<>();
                mFieldInlineTooltipPresentations = new ArrayList<>();
                mFieldFilters = new ArrayList<>();
                mAutofillDatatypes = null;
                return existingIdx;
            }
            mFieldIds.add(id);
            mAutofillDatatypes.add(null);
            mFieldValues.add(value);
            mFieldPresentations.add(presentation);
            mFieldDialogPresentations.add(dialogPresentation);
            mFieldInlinePresentations.add(inlinePresentation);
            mFieldInlineTooltipPresentations.add(tooltip);
            mFieldFilters.add(filter);
            return mFieldIds.size() - 1;
        }

        /**
@@ -1249,11 +1274,12 @@ public final class Dataset implements Parcelable {
            throwIfDestroyed();
            mDestroyed = true;
            if (mFieldIds == null && mAutofillDatatypes == null) {
                throw new IllegalStateException("at least one value must be set");
                throw new IllegalStateException("at least one of field or datatype must be set");
            }
            if (mFieldIds != null && mAutofillDatatypes != null) {
                if (mFieldIds.size() > 0 && mAutofillDatatypes.size() > 0) {
                    throw new IllegalStateException("both field and datatype were populated");
                if (mFieldIds.size() == 0 && mAutofillDatatypes.size() == 0) {
                    throw new IllegalStateException(
                            "at least one of field or datatype must be set");
                }
            }
            if (mFieldContent != null) {
+80 −8
Original line number Diff line number Diff line
@@ -742,6 +742,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            return Collections.EMPTY_LIST;
        }
        final String typeHints = mService.getMaster().getPccProviderHints();
        if (sVerbose) {
            Slog.v(TAG, "TypeHints flag:" + typeHints);
        }
        if (TextUtils.isEmpty(typeHints)) {
            return new ArrayList<>();
        }
@@ -757,7 +760,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        @GuardedBy("mLock")
        void maybeRequestFieldClassificationFromServiceLocked() {
            if (mClassificationState.mPendingFieldClassificationRequest == null) {
                Log.w(TAG, "Received AssistData without pending classification request");
                Slog.w(TAG, "Received AssistData without pending classification request");
                return;
            }

@@ -791,7 +794,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            final int requestId = receiverExtras.getInt(EXTRA_REQUEST_ID);

            if (sVerbose) {
                Slog.v(TAG, "New structure for requestId " + requestId + ": " + structure);
                Slog.v(TAG, "New structure for PCC Detection: requestId " + requestId + ": "
                        + structure);
            }

            synchronized (mLock) {
@@ -1125,6 +1129,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        // structure is taken. This causes only one fill request per burst of focus changes.
        cancelCurrentRequestLocked();

        if (mClassificationState.mHintsToAutofillIdMap == null) {
            if (sVerbose) {
                Slog.v(TAG, "triggering field classification");
            }
            requestAssistStructureForPccLocked(flags | FLAG_PCC_DETECTION);
        }

        // Only ask IME to create inline suggestions request when
        // 1. Autofill provider supports it or client enabled client suggestions.
        // 2. The render service is available.
@@ -1376,7 +1387,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
    @Override
    public void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
            @NonNull String servicePackageName, int requestFlags) {

        final AutofillId[] fieldClassificationIds;

        final LogMaker requestLog;
@@ -1609,10 +1619,68 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        Set<Dataset> eligibleDatasets = new ArraySet<>();
        Set<AutofillId> eligibleAutofillIds = new ArraySet<>();
        for (Dataset dataset : response.getDatasets()) {
            if (dataset.getFieldIds() == null) continue;
            if (dataset.getFieldIds() == null || dataset.getFieldIds().isEmpty()) continue;
            if (dataset.getAutofillDatatypes() != null
                    && dataset.getAutofillDatatypes().size() > 0) {
                continue;
                    && !dataset.getAutofillDatatypes().isEmpty()) {
                // This dataset has information relevant for detection too, so we should filter
                // them out. It's possible that some fields are applicable to hints only, as such,
                // they need to be filtered off.
                // TODO(b/266379948): Verify the logic and add tests
                // Update dataset to only have non-null fieldValues

                // Figure out if we need to process results.
                boolean conversionRequired = false;
                int newSize = dataset.getFieldIds().size();
                for (AutofillId id : dataset.getFieldIds()) {
                    if (id == null) {
                        conversionRequired = true;
                        newSize--;
                    }
                }

                if (conversionRequired) {
                    ArrayList<AutofillId> fieldIds = new ArrayList<>(newSize);
                    ArrayList<AutofillValue> fieldValues = new ArrayList<>(newSize);
                    ArrayList<RemoteViews> fieldPresentations = new ArrayList<>(newSize);
                    ArrayList<RemoteViews> fieldDialogPresentations = new ArrayList<>(newSize);
                    ArrayList<InlinePresentation> fieldInlinePresentations =
                            new ArrayList<>(newSize);
                    ArrayList<InlinePresentation> fieldInlineTooltipPresentations =
                            new ArrayList<>(newSize);
                    ArrayList<Dataset.DatasetFieldFilter> fieldFilters = new ArrayList<>(newSize);

                    for (int i = 0; i < dataset.getFieldIds().size(); i++) {
                        AutofillId id = dataset.getFieldIds().get(i);
                        if (id != null) {
                            // Copy over
                            fieldIds.add(id);
                            fieldValues.add(dataset.getFieldValues().get(i));
                            fieldPresentations.add(dataset.getFieldPresentation(i));
                            fieldDialogPresentations.add(dataset.getFieldDialogPresentation(i));
                            fieldInlinePresentations.add(dataset.getFieldInlinePresentation(i));
                            fieldInlineTooltipPresentations.add(
                                    dataset.getFieldInlineTooltipPresentation(i));
                            fieldFilters.add(dataset.getFilter(i));
                        }
                    }
                    dataset =
                            new Dataset(
                                    fieldIds,
                                    fieldValues,
                                    fieldPresentations,
                                    fieldDialogPresentations,
                                    fieldInlinePresentations,
                                    fieldInlineTooltipPresentations,
                                    fieldFilters,
                                    new ArrayList<>(),
                                    dataset.getFieldContent(),
                                    null,
                                    null,
                                    null,
                                    null,
                                    dataset.getId(),
                                    dataset.getAuthentication());
                }
            }
            eligibleDatasets.add(dataset);
            for (AutofillId id : dataset.getFieldIds()) {
@@ -1639,6 +1707,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            ArrayMap<String, Set<AutofillId>> hintsToAutofillIdMap =
                    mClassificationState.mHintsToAutofillIdMap;

            // TODO(266379948): Handle group hints too.
            ArrayMap<String, Set<AutofillId>> groupHintsToAutofillIdMap =
                    mClassificationState.mGroupHintsToAutofillIdMap;

@@ -1649,7 +1718,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState

            for (int i = 0; i < datasets.size(); i++) {
                Dataset dataset = datasets.get(i);
                if (dataset.getAutofillDatatypes() == null) continue;
                if (dataset.getAutofillDatatypes() == null
                        || dataset.getAutofillDatatypes().isEmpty()) continue;
                if (dataset.getFieldIds() != null && dataset.getFieldIds().size() > 0) continue;

                ArrayList<AutofillId> fieldIds = new ArrayList<>();
@@ -1661,6 +1731,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                ArrayList<Dataset.DatasetFieldFilter> fieldFilters = new ArrayList<>();

                for (int j = 0; j < dataset.getAutofillDatatypes().size(); j++) {
                    if (dataset.getAutofillDatatypes().get(0) == null) continue;
                    String hint = dataset.getAutofillDatatypes().get(j);

                    if (hintsToAutofillIdMap.containsKey(hint)) {
@@ -4560,7 +4631,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        if (mResponses == null) {
            // Set initial capacity as 2 to handle cases where service always requires auth.
            // TODO: add a metric for number of responses set by server, so we can use its average
            // as the initial array capacitiy.
            // as the initial array capacity.
            mResponses = new SparseArray<>(2);
        }
        mResponses.put(requestId, newResponse);
@@ -4982,6 +5053,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            mClassificationGroupHintsMap = new ArrayMap<>();
            mHintsToAutofillIdMap = new ArrayMap<>();
            mGroupHintsToAutofillIdMap = new ArrayMap<>();
            mClassificationCombinedHintsMap = new ArrayMap<>();
            Set<android.service.assist.classification.FieldClassification> classifications =
                    response.getClassifications();