diff --git a/core/api/current.txt b/core/api/current.txt index 6166f623f30ec..66d9bfc215a90 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -39475,6 +39475,7 @@ package android.service.autofill { method @NonNull public android.service.autofill.Dataset build(); method @NonNull public android.service.autofill.Dataset.Builder setAuthentication(@Nullable android.content.IntentSender); method @NonNull public android.service.autofill.Dataset.Builder setField(@NonNull android.view.autofill.AutofillId, @Nullable android.service.autofill.Field); + method @NonNull public android.service.autofill.Dataset.Builder setField(@NonNull String, @NonNull android.service.autofill.Field); method @NonNull public android.service.autofill.Dataset.Builder setId(@Nullable String); method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation); method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation); @@ -39583,6 +39584,7 @@ package android.service.autofill { method @Nullable public android.content.IntentSender getDelayedFillIntentSender(); method @NonNull public java.util.List getFillContexts(); method public int getFlags(); + method @NonNull public java.util.List getHints(); method public int getId(); method @Nullable public android.view.inputmethod.InlineSuggestionsRequest getInlineSuggestionsRequest(); method public void writeToParcel(@NonNull android.os.Parcel, int); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 6a4584b39135c..e0d50f07e9f30 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2524,6 +2524,7 @@ package android.service.autofill { public final class Dataset implements android.os.Parcelable { method @Nullable public android.content.IntentSender getAuthentication(); + method @Nullable public java.util.ArrayList getAutofillDatatypes(); method @Nullable public android.content.ClipData getFieldContent(); method @Nullable public java.util.ArrayList getFieldIds(); method @Nullable public java.util.ArrayList getFieldValues(); @@ -3273,6 +3274,7 @@ package android.view.autofill { field public static final String DEVICE_CONFIG_AUTOFILL_CREDENTIAL_MANAGER_IGNORE_VIEWS = "autofill_credential_manager_ignore_views"; field public static final String DEVICE_CONFIG_AUTOFILL_DIALOG_ENABLED = "autofill_dialog_enabled"; field public static final String DEVICE_CONFIG_AUTOFILL_PCC_CLASSIFICATION_ENABLED = "pcc_classification_enabled"; + field public static final String DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS = "pcc_classification_hints"; field public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes"; field public static final String DEVICE_CONFIG_NON_AUTOFILLABLE_IME_ACTION_IDS = "non_autofillable_ime_action_ids"; field public static final String DEVICE_CONFIG_PACKAGE_DENYLIST_FOR_UNIMPORTANT_VIEW = "package_deny_list_for_unimportant_view"; diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index e9f099accac13..0fa5e3e3b3237 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -120,6 +120,8 @@ public final class Dataset implements Parcelable { private final ArrayList mFieldInlinePresentations; private final ArrayList mFieldInlineTooltipPresentations; private final ArrayList mFieldFilters; + private final ArrayList mAutofillDatatypes; + @Nullable private final ClipData mFieldContent; private final RemoteViews mPresentation; private final RemoteViews mDialogPresentation; @@ -143,6 +145,14 @@ public final class Dataset implements Parcelable { mInlineTooltipPresentation = builder.mInlineTooltipPresentation; mAuthentication = builder.mAuthentication; mId = builder.mId; + mAutofillDatatypes = builder.mAutofillDatatypes; + } + + /** @hide */ + @TestApi + @SuppressLint({"ConcreteCollection", "NullableCollection"}) + public @Nullable ArrayList getAutofillDatatypes() { + return mAutofillDatatypes; } /** @hide */ @@ -293,6 +303,7 @@ public final class Dataset implements Parcelable { private ArrayList mFieldInlinePresentations; private ArrayList mFieldInlineTooltipPresentations; private ArrayList mFieldFilters; + private ArrayList mAutofillDatatypes; @Nullable private ClipData mFieldContent; private RemoteViews mPresentation; private RemoteViews mDialogPresentation; @@ -921,6 +932,55 @@ public final class Dataset implements Parcelable { 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. + * Platform detections are on when receiving a populated list from + * FillRequest#getHints(). + * + * Populate every field/type known for this user for this app. + * + * For example, if getHints() contains "username" and "password", + * a new Dataset should be created that calls this method twice, + * one for the username, then another for the password (assuming + * the only one credential pair is found for the user). If a user + * 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()}. + * + * @param field the fill information about the field. + * + * @throws IllegalStateException if {@link #build()} was already called + * or this builder also contains AutofillId information + * + * @return this builder. + */ + public @NonNull Dataset.Builder setField( + @NonNull String hint, @NonNull Field field) { + throwIfDestroyed(); + + final DatasetFieldFilter filter = field.getDatasetFieldFilter(); + final Presentations presentations = field.getPresentations(); + if (presentations == null) { + setLifeTheUniverseAndEverything(hint, field.getValue(), null, null, null, + filter, null); + } else { + setLifeTheUniverseAndEverything(hint, field.getValue(), + presentations.getMenuPresentation(), + presentations.getInlinePresentation(), + presentations.getInlineTooltipPresentation(), filter, + presentations.getDialogPresentation()); + } + + return this; + } + /** * Sets the value of a field with an explicit filter, and using an * {@link InlinePresentation} to visualize it as an inline suggestion. @@ -958,6 +1018,32 @@ public final class Dataset implements Parcelable { return this; } + private void setLifeTheUniverseAndEverything(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; + } + mFieldValues.add(value); + mFieldPresentations.add(presentation); + mFieldDialogPresentations.add(dialogPresentation); + mFieldInlinePresentations.add(inlinePresentation); + mFieldInlineTooltipPresentations.add(tooltip); + mFieldFilters.add(filter); + mAutofillDatatypes.add(datatype); + } + private void setLifeTheUniverseAndEverything(@NonNull AutofillId id, @Nullable AutofillValue value, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @@ -984,6 +1070,7 @@ public final class Dataset implements Parcelable { mFieldInlinePresentations = new ArrayList<>(); mFieldInlineTooltipPresentations = new ArrayList<>(); mFieldFilters = new ArrayList<>(); + mAutofillDatatypes = null; } mFieldIds.add(id); mFieldValues.add(value); @@ -1007,9 +1094,14 @@ public final class Dataset implements Parcelable { public @NonNull Dataset build() { throwIfDestroyed(); mDestroyed = true; - if (mFieldIds == null) { + if (mFieldIds == null && mAutofillDatatypes == null) { throw new IllegalStateException("at least one value 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 (mFieldContent != null) { if (mFieldIds.size() > 1) { throw new IllegalStateException( @@ -1051,6 +1143,7 @@ public final class Dataset implements Parcelable { parcel.writeTypedList(mFieldInlinePresentations, flags); parcel.writeTypedList(mFieldInlineTooltipPresentations, flags); parcel.writeTypedList(mFieldFilters, flags); + parcel.writeStringList(mAutofillDatatypes); parcel.writeParcelable(mFieldContent, flags); parcel.writeParcelable(mAuthentication, flags); parcel.writeString(mId); @@ -1081,6 +1174,8 @@ public final class Dataset implements Parcelable { parcel.createTypedArrayList(InlinePresentation.CREATOR); final ArrayList filters = parcel.createTypedArrayList(DatasetFieldFilter.CREATOR); + final ArrayList datatypes = + parcel.createStringArrayList(); final ClipData fieldContent = parcel.readParcelable(null, android.content.ClipData.class); final IntentSender authentication = parcel.readParcelable(null, @@ -1114,19 +1209,37 @@ public final class Dataset implements Parcelable { builder.setContent(ids.get(0), fieldContent); } final int inlinePresentationsSize = inlinePresentations.size(); - for (int i = 0; i < ids.size(); i++) { - final AutofillId id = ids.get(i); - final AutofillValue value = values.get(i); - final RemoteViews fieldPresentation = presentations.get(i); - final RemoteViews fieldDialogPresentation = dialogPresentations.get(i); - final InlinePresentation fieldInlinePresentation = - i < inlinePresentationsSize ? inlinePresentations.get(i) : null; - final InlinePresentation fieldInlineTooltipPresentation = - i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null; - final DatasetFieldFilter filter = filters.get(i); - builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, - fieldInlinePresentation, fieldInlineTooltipPresentation, filter, - fieldDialogPresentation); + + if (ids.size() == 0 && datatypes.size() > 0) { + for (int i = 0; i < ids.size(); i++) { + final String datatype = datatypes.get(i); + final AutofillValue value = values.get(i); + final RemoteViews fieldPresentation = presentations.get(i); + final RemoteViews fieldDialogPresentation = dialogPresentations.get(i); + final InlinePresentation fieldInlinePresentation = + i < inlinePresentationsSize ? inlinePresentations.get(i) : null; + final InlinePresentation fieldInlineTooltipPresentation = + i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null; + final DatasetFieldFilter filter = filters.get(i); + builder.setLifeTheUniverseAndEverything( + datatype, value, fieldPresentation, fieldInlinePresentation, + fieldInlineTooltipPresentation, filter, fieldDialogPresentation); + } + } else { + for (int i = 0; i < ids.size(); i++) { + final AutofillId id = ids.get(i); + final AutofillValue value = values.get(i); + final RemoteViews fieldPresentation = presentations.get(i); + final RemoteViews fieldDialogPresentation = dialogPresentations.get(i); + final InlinePresentation fieldInlinePresentation = + i < inlinePresentationsSize ? inlinePresentations.get(i) : null; + final InlinePresentation fieldInlineTooltipPresentation = + i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null; + final DatasetFieldFilter filter = filters.get(i); + builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, + fieldInlinePresentation, fieldInlineTooltipPresentation, filter, + fieldDialogPresentation); + } } builder.setAuthentication(authentication); builder.setId(datasetId); diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index eb5e893416c7f..4a848dd864638 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -140,6 +140,19 @@ public final class FillRequest implements Parcelable { */ private final @NonNull List mFillContexts; + /** + * Sends a list of datatypes for the Autofill Provider. + * + * If this is populated, Autofill Provider should return data + * for the autofill hints requested here, + * even though the Autofill Provider may not have detected these types. + * The hints would be part of HintConstants: + * https://developer.android.com/reference/androidx/autofill/HintConstants + * + * This is populated if the platform's field detection is enabled. + */ + private final @NonNull List mHints; + /** * Gets the latest client state bundle set by the service in a * {@link FillResponse.Builder#setClientState(Bundle) fill response}. @@ -196,6 +209,7 @@ public final class FillRequest implements Parcelable { private void onConstructed() { Preconditions.checkCollectionElementsNotNull(mFillContexts, "contexts"); + Preconditions.checkCollectionElementsNotNull(mHints, "hints"); } @@ -269,6 +283,11 @@ public final class FillRequest implements Parcelable { *

Note: Starting on Android {@link android.os.Build.VERSION_CODES#Q}, it could also * include contexts from requests whose {@link SaveInfo} had the * {@link SaveInfo#FLAG_DELAY_SAVE} flag. + * @param hints + * Autofill Provider should return data for the autofill hints requested here, + * even though the Autofill Provider may not have detected these types. + * The hints would be part of HintConstants: + * https://developer.android.com/reference/androidx/autofill/HintConstants * @param clientState * Gets the latest client state bundle set by the service in a * {@link FillResponse.Builder#setClientState(Bundle) fill response}. @@ -312,6 +331,7 @@ public final class FillRequest implements Parcelable { public FillRequest( int id, @NonNull List fillContexts, + @NonNull List hints, @Nullable Bundle clientState, @RequestFlags int flags, @Nullable InlineSuggestionsRequest inlineSuggestionsRequest, @@ -320,6 +340,9 @@ public final class FillRequest implements Parcelable { this.mFillContexts = fillContexts; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mFillContexts); + this.mHints = hints; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHints); this.mClientState = clientState; this.mFlags = flags; @@ -359,6 +382,17 @@ public final class FillRequest implements Parcelable { return mFillContexts; } + /** + * Autofill Provider should return data for the autofill hints requested here, + * even though the Autofill Provider may not have detected these types. + * The hints would be part of HintConstants: + * https://developer.android.com/reference/androidx/autofill/HintConstants + */ + @DataClass.Generated.Member + public @NonNull List getHints() { + return mHints; + } + /** * Gets the latest client state bundle set by the service in a * {@link FillResponse.Builder#setClientState(Bundle) fill response}. @@ -433,6 +467,7 @@ public final class FillRequest implements Parcelable { return "FillRequest { " + "id = " + mId + ", " + "fillContexts = " + mFillContexts + ", " + + "hints = " + mHints + ", " + "clientState = " + mClientState + ", " + "flags = " + requestFlagsToString(mFlags) + ", " + "inlineSuggestionsRequest = " + mInlineSuggestionsRequest + ", " + @@ -447,12 +482,13 @@ public final class FillRequest implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; - if (mClientState != null) flg |= 0x4; - if (mInlineSuggestionsRequest != null) flg |= 0x10; - if (mDelayedFillIntentSender != null) flg |= 0x20; + if (mClientState != null) flg |= 0x8; + if (mInlineSuggestionsRequest != null) flg |= 0x20; + if (mDelayedFillIntentSender != null) flg |= 0x40; dest.writeByte(flg); dest.writeInt(mId); dest.writeParcelableList(mFillContexts, flags); + dest.writeStringList(mHints); if (mClientState != null) dest.writeBundle(mClientState); dest.writeInt(mFlags); if (mInlineSuggestionsRequest != null) dest.writeTypedObject(mInlineSuggestionsRequest, flags); @@ -474,15 +510,20 @@ public final class FillRequest implements Parcelable { int id = in.readInt(); List fillContexts = new ArrayList<>(); in.readParcelableList(fillContexts, FillContext.class.getClassLoader()); - Bundle clientState = (flg & 0x4) == 0 ? null : in.readBundle(); + List hints = new ArrayList<>(); + in.readStringList(hints); + Bundle clientState = (flg & 0x8) == 0 ? null : in.readBundle(); int flags = in.readInt(); - InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x10) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR); - IntentSender delayedFillIntentSender = (flg & 0x20) == 0 ? null : (IntentSender) in.readTypedObject(IntentSender.CREATOR); + InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x20) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR); + IntentSender delayedFillIntentSender = (flg & 0x40) == 0 ? null : (IntentSender) in.readTypedObject(IntentSender.CREATOR); this.mId = id; this.mFillContexts = fillContexts; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mFillContexts); + this.mHints = hints; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHints); this.mClientState = clientState; this.mFlags = flags; @@ -517,10 +558,10 @@ public final class FillRequest implements Parcelable { }; @DataClass.Generated( - time = 1675460688829L, + time = 1675711417112L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java", - inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") + inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List mFillContexts\nprivate final @android.annotation.NonNull java.util.List mHints\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java index 5fe1d4f5ca5fe..828e466783d1e 100644 --- a/core/java/android/service/autofill/SaveInfo.java +++ b/core/java/android/service/autofill/SaveInfo.java @@ -308,7 +308,7 @@ public final class SaveInfo implements Parcelable { * username field, another for password). */ // TODO(b/113281366): improve documentation: add example, document relationship with other - // flagss, etc... + // flags, etc... public static final int FLAG_DELAY_SAVE = 0x4; /** @hide */ @@ -777,17 +777,13 @@ public final class SaveInfo implements Parcelable { /** * Builds a new {@link SaveInfo} instance. * - * @throws IllegalStateException if no - * {@link #Builder(int, AutofillId[]) required ids}, + * If no {@link #Builder(int, AutofillId[]) required ids}, * or {@link #setOptionalIds(AutofillId[]) optional ids}, or {@link #FLAG_DELAY_SAVE} - * were set + * were set, Save Dialog will only be triggered if platform detection is enabled, which + * is indicated when {@link FillRequest.getHints()} is not empty. */ public SaveInfo build() { throwIfDestroyed(); - Preconditions.checkState( - !ArrayUtils.isEmpty(mRequiredIds) || !ArrayUtils.isEmpty(mOptionalIds) - || (mFlags & FLAG_DELAY_SAVE) != 0, - "must have at least one required or optional id or FLAG_DELAYED_SAVE"); mDestroyed = true; return new SaveInfo(this); } diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java index e7c610b8bd8de..f3a8e8594e9cf 100644 --- a/core/java/android/view/autofill/AutofillFeatureFlags.java +++ b/core/java/android/view/autofill/AutofillFeatureFlags.java @@ -81,6 +81,12 @@ 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,9 +196,11 @@ 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 @@ -224,6 +232,25 @@ 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. * diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 2b529bfe5e32c..a23a58dc9477f 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -117,6 +117,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; import android.view.KeyEvent; +import android.view.autofill.AutofillFeatureFlags; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillManager.AutofillCommitReason; @@ -146,6 +147,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Function; @@ -564,6 +566,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mPendingInlineSuggestionsRequest.isServiceSupported()) { mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), mPendingFillRequest.getFillContexts(), + mPendingFillRequest.getHints(), mPendingFillRequest.getClientState(), mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest, @@ -672,8 +675,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList contexts = mergePreviousSessionLocked(/* forSave= */ false); + final List hints = getTypeHintsForProvider(); + mDelayedFillPendingIntent = createPendingIntent(requestId); - request = new FillRequest(requestId, contexts, mClientState, flags, + request = new FillRequest(requestId, contexts, hints, mClientState, flags, /*inlineSuggestionsRequest=*/ null, /*delayedFillIntentSender=*/ mDelayedFillPendingIntent == null ? null @@ -704,6 +709,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + /** + * Get the list of valid autofill hint types from Device flags + * Returns empty list if PCC is off or no types available + */ + private List getTypeHintsForProvider() { + if (!AutofillFeatureFlags.isAutofillPccClassificationEnabled()) { + return Collections.EMPTY_LIST; + } + + String[] typeHints = AutofillFeatureFlags.getTypeHintsForProvider(); + return List.copyOf(Set.of(typeHints)); + } + /** * Assist Data Receiver for PCC */