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

Commit 2de210ac authored by Simranjit Kohli's avatar Simranjit Kohli
Browse files

[Autofill PCC Integration] : Implement core logic

This CL establishes connection to the remote classification service,
and uses it's results to merge with provider results.

Test: To be added in a follow-up cl. Tested AiAi connection, and it works.

Change-Id: I6311269e5cc078b3739f4bfab73c9ae66670177b
parent 79321ffc
Loading
Loading
Loading
Loading
+134 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.service.autofill;

import static android.view.autofill.Helper.sDebug;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -33,6 +34,8 @@ import android.widget.RemoteViews;

import com.android.internal.util.Preconditions;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
import java.util.regex.Pattern;
@@ -112,6 +115,55 @@ import java.util.regex.Pattern;
 * </ol>
 */
public final class Dataset implements Parcelable {
    /**
     * This dataset is picked because of unknown reason.
     * @hide
     */
    public static final int PICK_REASON_UNKNOWN = 0;
    /**
     * This dataset is picked because of autofill provider detection was chosen.
     * @hide
     */
    public static final int PICK_REASON_AUTOFILL_PROVIDER_DETECTION = 1;
    /**
     * This dataset is picked because of PCC detection was chosen.
     * @hide
     */
    public static final int PICK_REASON_PCC_DETECTION = 2;
    /**
     * This dataset is picked because of Framework detection was chosen.
     * @hide
     */
    public static final int PICK_REASON_FRAMEWORK_DETECTION = 3;
    /**
     * This dataset is picked because of Autofill Provider being a fallback.
     * @hide
     */
    public static final int PICK_REASON_AUTOFILL_PROVIDER_FALLBACK = 4;
    /**
     * This dataset is picked because of PCC detection being a fallback.
     * @hide
     */
    public static final int PICK_REASON_PCC_DETECTION_FALLBACK = 5;
    /**
     * This dataset is picked because of Framework detection being a fallback.
     * @hide
     */
    public static final int PICK_REASON_FRAMEWORK_FALLBACK = 6;

    @IntDef(prefix = { "PICK_REASON_" }, value = {
            PICK_REASON_UNKNOWN,
            PICK_REASON_AUTOFILL_PROVIDER_DETECTION,
            PICK_REASON_PCC_DETECTION,
            PICK_REASON_FRAMEWORK_DETECTION,
            PICK_REASON_AUTOFILL_PROVIDER_FALLBACK,
            PICK_REASON_PCC_DETECTION_FALLBACK,
            PICK_REASON_FRAMEWORK_FALLBACK,
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface DatasetEligibleReason{}

    private @DatasetEligibleReason int mEligibleReason;

    private final ArrayList<AutofillId> mFieldIds;
    private final ArrayList<AutofillValue> mFieldValues;
@@ -130,6 +182,67 @@ public final class Dataset implements Parcelable {
    private final IntentSender mAuthentication;
    @Nullable String mId;

    /**
     * Constructor to copy the dataset, but replaces the AutofillId with the given input.
     * Useful to modify the field type, and provide autofillId.
     * @hide
     */
    public Dataset(
            ArrayList<AutofillId> fieldIds,
            ArrayList<AutofillValue> fieldValues,
            ArrayList<RemoteViews> fieldPresentations,
            ArrayList<RemoteViews> fieldDialogPresentations,
            ArrayList<InlinePresentation> fieldInlinePresentations,
            ArrayList<InlinePresentation> fieldInlineTooltipPresentations,
            ArrayList<DatasetFieldFilter> fieldFilters,
            ArrayList<String> autofillDatatypes,
            ClipData fieldContent,
            RemoteViews presentation,
            RemoteViews dialogPresentation,
            @Nullable InlinePresentation inlinePresentation,
            @Nullable  InlinePresentation inlineTooltipPresentation,
            @Nullable String id,
            IntentSender authentication) {
        mFieldIds = fieldIds;
        mFieldValues = fieldValues;
        mFieldPresentations = fieldPresentations;
        mFieldDialogPresentations = fieldDialogPresentations;
        mFieldInlinePresentations = fieldInlinePresentations;
        mFieldInlineTooltipPresentations = fieldInlineTooltipPresentations;
        mAutofillDatatypes = autofillDatatypes;
        mFieldFilters = fieldFilters;
        mFieldContent = fieldContent;
        mPresentation = presentation;
        mDialogPresentation = dialogPresentation;
        mInlinePresentation = inlinePresentation;
        mInlineTooltipPresentation = inlineTooltipPresentation;
        mAuthentication = authentication;
        mId = id;
    }

    /**
     * Constructor to copy the dataset, but replaces the AutofillId with the given input.
     * Useful to modify the field type, and provide autofillId.
     * @hide
     */
    public Dataset(Dataset dataset, ArrayList<AutofillId> ids) {
        mFieldIds = ids;
        mFieldValues = dataset.mFieldValues;
        mFieldPresentations = dataset.mFieldPresentations;
        mFieldDialogPresentations = dataset.mFieldDialogPresentations;
        mFieldInlinePresentations = dataset.mFieldInlinePresentations;
        mFieldInlineTooltipPresentations = dataset.mFieldInlineTooltipPresentations;
        mFieldFilters = dataset.mFieldFilters;
        mFieldContent = dataset.mFieldContent;
        mPresentation = dataset.mPresentation;
        mDialogPresentation = dataset.mDialogPresentation;
        mInlinePresentation = dataset.mInlinePresentation;
        mInlineTooltipPresentation = dataset.mInlineTooltipPresentation;
        mAuthentication = dataset.mAuthentication;
        mId = dataset.mId;
        mAutofillDatatypes = dataset.mAutofillDatatypes;
    }

    private Dataset(Builder builder) {
        mFieldIds = builder.mFieldIds;
        mFieldValues = builder.mFieldValues;
@@ -291,6 +404,22 @@ public final class Dataset implements Parcelable {
        return mId;
    }

    /**
     * Sets the reason as to why this dataset is eligible
     * @hide
     */
    public void setEligibleReasonReason(@DatasetEligibleReason int eligibleReason) {
        this.mEligibleReason = eligibleReason;
    }

    /**
     * Get the reason as to why this dataset is eligible.
     * @hide
     */
    public @DatasetEligibleReason int getEligibleReason() {
        return mEligibleReason;
    }

    /**
     * A builder for {@link Dataset} objects. You must provide at least
     * one value for a field or set an authentication intent.
@@ -1147,6 +1276,7 @@ public final class Dataset implements Parcelable {
        parcel.writeParcelable(mFieldContent, flags);
        parcel.writeParcelable(mAuthentication, flags);
        parcel.writeString(mId);
        parcel.writeInt(mEligibleReason);
    }

    public static final @NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() {
@@ -1181,6 +1311,7 @@ public final class Dataset implements Parcelable {
            final IntentSender authentication = parcel.readParcelable(null,
                    android.content.IntentSender.class);
            final String datasetId = parcel.readString();
            final int eligibleReason = parcel.readInt();

            // Always go through the builder to ensure the data ingested by
            // the system obeys the contract of the builder to avoid attacks
@@ -1243,7 +1374,9 @@ public final class Dataset implements Parcelable {
            }
            builder.setAuthentication(authentication);
            builder.setId(datasetId);
            return builder.build();
            Dataset dataset = builder.build();
            dataset.mEligibleReason = eligibleReason;
            return dataset;
        }

        @Override
+83 −0
Original line number Diff line number Diff line
@@ -117,6 +117,80 @@ public final class FillResponse implements Parcelable {
    private final boolean mShowSaveDialogIcon;
    private final @Nullable FieldClassification[] mDetectedFieldTypes;

    /**
    * Creates a shollow copy of the provided FillResponse.
    *
    * @hide
    */
    public static FillResponse shallowCopy(FillResponse r, List<Dataset> datasets) {
        return new FillResponse(
                (datasets != null) ? new ParceledListSlice<>(datasets) : null,
                r.mSaveInfo,
                r.mClientState,
                r.mPresentation,
                r.mInlinePresentation,
                r.mInlineTooltipPresentation,
                r.mDialogPresentation,
                r.mDialogHeader,
                r.mHeader,
                r.mFooter,
                r.mAuthentication,
                r.mAuthenticationIds,
                r.mIgnoredIds,
                r.mFillDialogTriggerIds,
                r.mDisableDuration,
                r.mFieldClassificationIds,
                r.mFlags,
                r.mRequestId,
                r.mUserData,
                r.mCancelIds,
                r.mSupportsInlineSuggestions,
                r.mIconResourceId,
                r.mServiceDisplayNameResourceId,
                r.mShowFillDialogIcon,
                r.mShowSaveDialogIcon,
                r.mDetectedFieldTypes);
    }

    private FillResponse(ParceledListSlice<Dataset> datasets, SaveInfo saveInfo, Bundle clientState,
            RemoteViews presentation, InlinePresentation inlinePresentation,
            InlinePresentation inlineTooltipPresentation, RemoteViews dialogPresentation,
            RemoteViews dialogHeader, RemoteViews header, RemoteViews footer,
            IntentSender authentication, AutofillId[] authenticationIds, AutofillId[] ignoredIds,
            AutofillId[] fillDialogTriggerIds, long disableDuration,
            AutofillId[] fieldClassificationIds, int flags, int requestId, UserData userData,
            int[] cancelIds, boolean supportsInlineSuggestions, int iconResourceId,
            int serviceDisplayNameResourceId, boolean showFillDialogIcon,
            boolean showSaveDialogIcon,
            FieldClassification[] detectedFieldTypes) {
        mDatasets = datasets;
        mSaveInfo = saveInfo;
        mClientState = clientState;
        mPresentation = presentation;
        mInlinePresentation = inlinePresentation;
        mInlineTooltipPresentation = inlineTooltipPresentation;
        mDialogPresentation = dialogPresentation;
        mDialogHeader = dialogHeader;
        mHeader = header;
        mFooter = footer;
        mAuthentication = authentication;
        mAuthenticationIds = authenticationIds;
        mIgnoredIds = ignoredIds;
        mFillDialogTriggerIds = fillDialogTriggerIds;
        mDisableDuration = disableDuration;
        mFieldClassificationIds = fieldClassificationIds;
        mFlags = flags;
        mRequestId = requestId;
        mUserData = userData;
        mCancelIds = cancelIds;
        mSupportsInlineSuggestions = supportsInlineSuggestions;
        mIconResourceId = iconResourceId;
        mServiceDisplayNameResourceId = serviceDisplayNameResourceId;
        mShowFillDialogIcon = showFillDialogIcon;
        mShowSaveDialogIcon = showSaveDialogIcon;
        mDetectedFieldTypes = detectedFieldTypes;
    }

    private FillResponse(@NonNull Builder builder) {
        mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
        mSaveInfo = builder.mSaveInfo;
@@ -673,6 +747,15 @@ public final class FillResponse implements Parcelable {
            return this;
        }

        /**
         * @hide
         */
        @NonNull
        public Builder setDatasets(ArrayList<Dataset> dataset) {
            mDatasets = dataset;
            return this;
        }

        /**
         * Sets the {@link SaveInfo} associated with this response.
         *
+6 −27
Original line number Diff line number Diff line
@@ -81,12 +81,6 @@ public class AutofillFeatureFlags {
    public static final String DEVICE_CONFIG_AUTOFILL_DIALOG_ENABLED =
            "autofill_dialog_enabled";

    /**
     * Indicates that PCC Autofill detection feature is enabled or not.
     */
    public static final String DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS =
            "pcc_classification_hints";

    /**
     * Sets the autofill hints allowed list for the fields that can trigger the fill dialog
     * feature at Activity starting.
@@ -190,6 +184,12 @@ public class AutofillFeatureFlags {
     */
    public static final String DEVICE_CONFIG_PREFER_PROVIDER_OVER_PCC = "prefer_provider_over_pcc";

    /**
     * Indicates the Autofill Hints that would be requested by the service from the Autofill
     * Provider.
     */
    public static final String DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS =
            "pcc_classification_hints";

    /**
     * Use data from secondary source if primary not present .
@@ -212,11 +212,9 @@ public class AutofillFeatureFlags {
            "autofill_inline_tooltip_first_show_delay";

    private static final String DIALOG_HINTS_DELIMITER = ":";
    private static final String PCC_HINTS_DELIMITER = ",";

    private static final boolean DEFAULT_HAS_FILL_DIALOG_UI_FEATURE = false;
    private static final String DEFAULT_FILL_DIALOG_ENABLED_HINTS = "";
    private static final String DEFAULT_PCC_FEATURE_PROVIDER_HINTS = "";


    // CREDENTIAL MANAGER DEFAULTS
@@ -249,25 +247,6 @@ public class AutofillFeatureFlags {
                DEFAULT_HAS_FILL_DIALOG_UI_FEATURE);
    }

    /**
     * The list of datatypes that is supported by framework
     * detection.
     *
     * @hide
     */
    public static String[] getTypeHintsForProvider() {
        final String typeHints = DeviceConfig.getString(
                DeviceConfig.NAMESPACE_AUTOFILL,
                DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS,
                DEFAULT_PCC_FEATURE_PROVIDER_HINTS);
        if (TextUtils.isEmpty(typeHints)) {
            return new String[0];
        }

        return ArrayUtils.filter(typeHints.split(PCC_HINTS_DELIMITER), String[]::new,
                (str) -> !TextUtils.isEmpty(str));
    }

    /**
     * Gets fill dialog enabled hints.
     *
+29 −0
Original line number Diff line number Diff line
@@ -150,6 +150,12 @@ public final class AutofillManagerService
    @NonNull
    final FrameworkResourcesServiceNameResolver mAugmentedAutofillResolver;

    /**
     * Object used to set the name of the field classification service.
     */
    @NonNull
    final FrameworkResourcesServiceNameResolver mFieldClassificationResolver;

    private final AutoFillUI mUi;

    private final LocalLog mRequestsHistory = new LocalLog(20);
@@ -245,6 +251,15 @@ public final class AutofillManagerService
        mAugmentedAutofillResolver.setOnTemporaryServiceNameChangedCallback(
                (u, s, t) -> onAugmentedServiceNameChanged(u, s, t));

        mFieldClassificationResolver = new FrameworkResourcesServiceNameResolver(getContext(),
                com.android.internal.R.string.config_defaultFieldClassificationService);
        if (sVerbose) {
            Slog.v(TAG, "Resolving FieldClassificationService to serviceName: "
                    + mFieldClassificationResolver.readServiceName(0));
        }
        mFieldClassificationResolver.setOnTemporaryServiceNameChangedCallback(
                (u, s, t) -> onFieldClassificationServiceNameChanged(u, s, t));

        if (mSupportedSmartSuggestionModes != AutofillManager.FLAG_SMART_SUGGESTION_OFF) {
            final List<UserInfo> users = getSupportedUsers();
            for (int i = 0; i < users.size(); i++) {
@@ -358,6 +373,20 @@ public final class AutofillManagerService
        }
    }

    private void onFieldClassificationServiceNameChanged(
            @UserIdInt int userId, @Nullable String serviceName, boolean isTemporary) {
        synchronized (mLock) {
            final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
            if (service == null) {
                // If we cannot get the service from the services cache, it will call
                // updateRemoteAugmentedAutofillService() finally. Skip call this update again.
                getServiceForUserLocked(userId);
            } else {
                service.updateRemoteFieldClassificationService();
            }
        }
    }

    @Override // from AbstractMasterSystemService
    protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
            boolean disabled) {
+125 −3
Original line number Diff line number Diff line
@@ -161,6 +161,17 @@ final class AutofillManagerServiceImpl
    /** When was {@link PruneTask} last executed? */
    private long mLastPrune = 0;

    /**
     * Reference to the {@link RemoteFieldClassificationService}, is set on demand.
     */
    @GuardedBy("mLock")
    @Nullable
    private RemoteFieldClassificationService mRemoteFieldClassificationService;

    @GuardedBy("mLock")
    @Nullable
    private ServiceInfo mRemoteFieldClassificationServiceInfo;

    /**
     * Reference to the {@link RemoteAugmentedAutofillService}, is set on demand.
     */
@@ -1051,10 +1062,11 @@ final class AutofillManagerServiceImpl
        }
        pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
                .getString(R.string.config_defaultAutofillService));
        pw.println();

        pw.print(prefix); pw.println("mAugmentedAutofillNamer: ");
        pw.print(prefix2); mMaster.mAugmentedAutofillResolver.dumpShort(pw, mUserId); pw.println();

        pw.print(prefix); pw.println("mAugmentedAutofillName: ");
        pw.print(prefix2); mMaster.mAugmentedAutofillResolver.dumpShort(pw, mUserId);
        pw.println();
        if (mRemoteAugmentedAutofillService != null) {
            pw.print(prefix); pw.println("RemoteAugmentedAutofillService: ");
            mRemoteAugmentedAutofillService.dump(prefix2, pw);
@@ -1063,6 +1075,27 @@ final class AutofillManagerServiceImpl
            pw.print(prefix); pw.print("RemoteAugmentedAutofillServiceInfo: ");
            pw.println(mRemoteAugmentedAutofillServiceInfo);
        }
        pw.println();

        pw.print(prefix); pw.println("mFieldClassificationService for system detection");
        pw.print(prefix2); pw.print("Default component: "); pw.println(getContext()
                .getString(R.string.config_defaultFieldClassificationService));
        pw.print(prefix2); mMaster.mFieldClassificationResolver.dumpShort(pw, mUserId);
        pw.println();

        if (mRemoteFieldClassificationService != null) {
            pw.print(prefix); pw.println("RemoteFieldClassificationService: ");
            mRemoteFieldClassificationService.dump(prefix2, pw);
        } else {
            pw.print(prefix); pw.println("mRemoteFieldClassificationService: null");
        }
        if (mRemoteFieldClassificationServiceInfo != null) {
            pw.print(prefix); pw.print("RemoteFieldClassificationServiceInfo: ");
            pw.println(mRemoteFieldClassificationServiceInfo);
        } else {
            pw.print(prefix); pw.println("mRemoteFieldClassificationServiceInfo: null");
        }
        pw.println();

        pw.print(prefix); pw.print("Field classification enabled: ");
            pw.println(isFieldClassificationEnabledLocked());
@@ -1629,6 +1662,95 @@ final class AutofillManagerServiceImpl
        }
    }

    @GuardedBy("mLock")
    @Nullable RemoteFieldClassificationService getRemoteFieldClassificationServiceLocked() {
        if (mRemoteFieldClassificationService == null) {
            final String serviceName = mMaster.mFieldClassificationResolver.getServiceName(mUserId);
            if (serviceName == null) {
                if (mMaster.verbose) {
                    Slog.v(TAG, "getRemoteFieldClassificationServiceLocked(): not set");
                }
                return null;
            }
            if (sVerbose) {
                Slog.v(TAG, "getRemoteFieldClassificationServiceLocked serviceName: "
                        + serviceName);
            }
            boolean sTemporaryFieldDetectionService =
                    mMaster.mFieldClassificationResolver.isTemporary(mUserId);
            final Pair<ServiceInfo, ComponentName> pair = RemoteFieldClassificationService
                    .getComponentName(serviceName, mUserId, sTemporaryFieldDetectionService);
            if (pair == null) {
                Slog.w(TAG, "RemoteFieldClassificationService.getComponentName returned null "
                        + "with serviceName: " + serviceName);
                return null;
            }

            mRemoteFieldClassificationServiceInfo = pair.first;
            final ComponentName componentName = pair.second;
            if (sVerbose) {
                Slog.v(TAG, "getRemoteFieldClassificationServiceLocked(): " + componentName);
            }
            final int serviceUid = mRemoteFieldClassificationServiceInfo.applicationInfo.uid;
            mRemoteFieldClassificationService = new RemoteFieldClassificationService(getContext(),
                    componentName, serviceUid, mUserId);
        }

        return mRemoteFieldClassificationService;
    }

    @GuardedBy("mLock")
    @Nullable RemoteFieldClassificationService
            getRemoteFieldClassificationServiceIfCreatedLocked() {
        return mRemoteFieldClassificationService;
    }

    /**
     * Called when the {@link AutofillManagerService#mAugmentedAutofillResolver}
     * changed (among other places).
     */
    void updateRemoteFieldClassificationService() {
        synchronized (mLock) {
            if (mRemoteFieldClassificationService != null) {
                if (sVerbose) {
                    Slog.v(TAG, "updateRemoteFieldClassificationService(): "
                            + "destroying old remote service");
                }
                mRemoteFieldClassificationService.unbind();

                mRemoteFieldClassificationService = null;
                mRemoteFieldClassificationServiceInfo = null;
            }

            final boolean available = isFieldClassificationServiceAvailableLocked();
            if (sVerbose) Slog.v(TAG, "updateRemoteFieldClassificationService(): " + available);

            if (available) {
                mRemoteFieldClassificationService = getRemoteFieldClassificationServiceLocked();
            }
        }
    }

    private boolean isFieldClassificationServiceAvailableLocked() {
        if (mMaster.verbose) {
            Slog.v(TAG, "isAugmentedAutofillService(): "
                    + "setupCompleted=" + isSetupCompletedLocked()
                    + ", disabled=" + isDisabledByUserRestrictionsLocked()
                    + ", augmentedService="
                    + mMaster.mAugmentedAutofillResolver.getServiceName(mUserId));
        }
        if (!isSetupCompletedLocked() || isDisabledByUserRestrictionsLocked()
                || mMaster.mAugmentedAutofillResolver.getServiceName(mUserId) == null) {
            return false;
        }
        return true;
    }

    boolean isRemoteClassificationServiceForUserLocked(int callingUid) {
        return mRemoteFieldClassificationServiceInfo != null
                && mRemoteFieldClassificationServiceInfo.applicationInfo.uid == callingUid;
    }

    @Override
    public String toString() {
        return "AutofillManagerServiceImpl: [userId=" + mUserId
Loading