summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/test-current.txt3
-rw-r--r--core/java/android/view/View.java27
-rw-r--r--core/java/android/view/ViewGroup.java4
-rw-r--r--core/java/android/view/autofill/AutofillFeatureFlags.java85
-rw-r--r--core/java/android/view/autofill/AutofillManager.java147
5 files changed, 263 insertions, 3 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 84ac868c7e17..bc88922b2d15 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3212,6 +3212,9 @@ 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_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";
+ field public static final String DEVICE_CONFIG_TRIGGER_FILL_REQUEST_ON_UNIMPORTANT_VIEW = "trigger_fill_request_on_unimportant_view";
}
public final class AutofillId implements android.os.Parcelable {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c73cfc22104b..5ac98198530c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10244,6 +10244,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return mContext.getSystemService(AutofillManager.class);
}
+ /**
+ * Check whether current activity / package is in denylist.If it's in the denylist,
+ * then the views marked as not important for autofill are not eligible for autofill.
+ */
+ final boolean isActivityDeniedForAutofillForUnimportantView() {
+ final AutofillManager afm = getAutofillManager();
+ // keep behavior same with denylist feature not enabled
+ if (afm == null) return true;
+ return afm.isActivityDeniedForAutofillForUnimportantView();
+ }
+
+ /**
+ * Check whether current view matches autofillable heuristics
+ */
+ final boolean isMatchingAutofillableHeuristics() {
+ final AutofillManager afm = getAutofillManager();
+ // keep default behavior
+ if (afm == null) return false;
+ return afm.isMatchingAutofillableHeuristics(this);
+ }
+
private boolean isAutofillable() {
if (getAutofillType() == AUTOFILL_TYPE_NONE) return false;
@@ -10252,6 +10273,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
&& isCredential()) return false;
if (!isImportantForAutofill()) {
+ // If view matches heuristics and is not denied, it will be treated same as view that's
+ // important for autofill
+ if (isMatchingAutofillableHeuristics()
+ && !isActivityDeniedForAutofillForUnimportantView()) {
+ return getAutofillViewId() > LAST_APP_AUTOFILL_ID;
+ }
// View is not important for "regular" autofill, so we must check if Augmented Autofill
// is enabled for the activity
final AutofillOptions options = mContext.getAutofillOptions();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 46b2cfc3f7a7..0e4ac0109f8e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3723,7 +3723,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final View child = (preorderedList == null)
? mChildren[childIndex] : preorderedList.get(childIndex);
if ((flags & AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0
- || child.isImportantForAutofill()) {
+ || child.isImportantForAutofill()
+ || (child.isMatchingAutofillableHeuristics()
+ && !child.isActivityDeniedForAutofillForUnimportantView())) {
list.add(child);
} else if (child instanceof ViewGroup) {
((ViewGroup) child).populateChildrenForAutofill(list, flags);
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index 59ad15139de1..cba399f996b2 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -16,13 +16,18 @@
package android.view.autofill;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.provider.DeviceConfig;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.view.View;
import com.android.internal.util.ArrayUtils;
+import java.util.Arrays;
+import java.util.Set;
+
/**
* Feature flags associated with autofill.
* @hide
@@ -119,8 +124,6 @@ public class AutofillFeatureFlags {
public static final String DEVICE_CONFIG_AUTOFILL_CREDENTIAL_MANAGER_SUPPRESS_FILL_DIALOG =
"autofill_credential_manager_suppress_fill_dialog";
-
-
/**
* Indicates whether credential manager tagged views should suppress save dialog.
* This flag is further gated by {@link #DEVICE_CONFIG_AUTOFILL_CREDENTIAL_MANAGER_ENABLED}
@@ -131,6 +134,50 @@ public class AutofillFeatureFlags {
"autofill_credential_manager_suppress_save_dialog";
// END CREDENTIAL MANAGER FLAGS //
+ // START AUTOFILL FOR ALL APPS FLAGS //
+ /**
+ * Sets the list of activities and packages denied for autofill
+ *
+ * The list is {@code ";"} colon delimited. Activities under a package is separated by
+ * {@code ","}. Each package name much be followed by a {@code ":"}. Each package entry must be
+ * ends with a {@code ";"}
+ *
+ * <p>For example, a list with only 1 package would be, {@code Package1:;}. A list with one
+ * denied activity {@code Activity1} under {@code Package1} and a full denied package
+ * {@code Package2} would be {@code Package1:Activity1;Package2:;}
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String DEVICE_CONFIG_PACKAGE_DENYLIST_FOR_UNIMPORTANT_VIEW =
+ "package_deny_list_for_unimportant_view";
+
+ /**
+ * Whether the heuristics check for view is enabled
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String DEVICE_CONFIG_TRIGGER_FILL_REQUEST_ON_UNIMPORTANT_VIEW =
+ "trigger_fill_request_on_unimportant_view";
+
+ /**
+ * Continas imeAction ids that is irrelevant for autofill. For example, ime_action_search. We
+ * use this to avoid trigger fill request on unimportant views.
+ *
+ * The list is {@code ","} delimited.
+ *
+ * <p> For example, a imeAction list could be "2,3,4", corresponding to ime_action definition
+ * in {@link android.view.inputmethod.EditorInfo.java}</p>
+ *
+ * @hide
+ */
+ @TestApi
+ @SuppressLint("IntentName")
+ public static final String DEVICE_CONFIG_NON_AUTOFILLABLE_IME_ACTION_IDS =
+ "non_autofillable_ime_action_ids";
+ // END AUTOFILL FOR ALL APPS FLAGS //
+
/**
* Sets a value of delay time to show up the inline tooltip view.
*
@@ -221,4 +268,38 @@ public class AutofillFeatureFlags {
DEVICE_CONFIG_AUTOFILL_CREDENTIAL_MANAGER_SUPPRESS_FILL_DIALOG,
DEFAULT_CREDENTIAL_MANAGER_SUPPRESS_FILL_DIALOG);
}
+
+ /**
+ * Whether triggering fill request on unimportant view is enabled.
+ *
+ * @hide
+ */
+ public static boolean isTriggerFillRequestOnUnimportantViewEnabled() {
+ return DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_AUTOFILL,
+ DEVICE_CONFIG_TRIGGER_FILL_REQUEST_ON_UNIMPORTANT_VIEW, false);
+ }
+
+ /**
+ * Get the non-autofillable ime actions from flag. This will be used in filtering
+ * condition to trigger fill request.
+ *
+ * @hide
+ */
+ public static Set<String> getNonAutofillableImeActionIdSetFromFlag() {
+ final String mNonAutofillableImeActions = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_AUTOFILL, DEVICE_CONFIG_NON_AUTOFILLABLE_IME_ACTION_IDS, "");
+ return new ArraySet<>(Arrays.asList(mNonAutofillableImeActions.split(",")));
+ }
+
+ /**
+ * Get denylist string from flag
+ *
+ * @hide
+ */
+ public static String getDenylistStringFromFlag() {
+ return DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_AUTOFILL,
+ DEVICE_CONFIG_PACKAGE_DENYLIST_FOR_UNIMPORTANT_VIEW, "");
+ }
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 58e7a70b155c..2ad01ed99b13 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -86,8 +86,13 @@ import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodManager;
+import android.widget.CheckBox;
+import android.widget.DatePicker;
import android.widget.EditText;
+import android.widget.RadioGroup;
+import android.widget.Spinner;
import android.widget.TextView;
+import android.widget.TimePicker;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -657,6 +662,23 @@ public final class AutofillManager {
private final boolean mIsFillDialogEnabled;
+ // Indicate whether trigger fill request on unimportant views is enabled
+ private boolean mIsTriggerFillRequestOnUnimportantViewEnabled = false;
+
+ // A set containing all non-autofillable ime actions passed by flag
+ private Set<String> mNonAutofillableImeActionIdSet = new ArraySet<>();
+
+ // If a package is fully denied, then all views that marked as not
+ // important for autofill will not trigger fill request
+ private boolean mIsPackageFullyDeniedForAutofillForUnimportantView = false;
+
+ // If a package is partially denied, autofill manager will check whether
+ // current activity is in deny set to decide whether to trigger fill request
+ private boolean mIsPackagePartiallyDeniedForAutofillForUnimportantView = false;
+
+ // A deny set read from device config
+ private Set<String> mDeniedActivitiySet = new ArraySet<>();
+
// Indicates whether called the showAutofillDialog() method.
private boolean mShowAutofillDialogCalled = false;
@@ -816,9 +838,134 @@ public final class AutofillManager {
sDebug = (mOptions.loggingLevel & FLAG_ADD_CLIENT_DEBUG) != 0;
sVerbose = (mOptions.loggingLevel & FLAG_ADD_CLIENT_VERBOSE) != 0;
}
+
+ mIsTriggerFillRequestOnUnimportantViewEnabled =
+ AutofillFeatureFlags.isTriggerFillRequestOnUnimportantViewEnabled();
+
+ mNonAutofillableImeActionIdSet =
+ AutofillFeatureFlags.getNonAutofillableImeActionIdSetFromFlag();
+
+ final String denyListString = AutofillFeatureFlags.getDenylistStringFromFlag();
+
+ final String packageName = mContext.getPackageName();
+
+ mIsPackageFullyDeniedForAutofillForUnimportantView =
+ isPackageFullyDeniedForAutofillForUnimportantView(denyListString, packageName);
+
+ mIsPackagePartiallyDeniedForAutofillForUnimportantView =
+ isPackagePartiallyDeniedForAutofillForUnimportantView(denyListString, packageName);
+
+ if (mIsPackagePartiallyDeniedForAutofillForUnimportantView) {
+ setDeniedActivitySetWithDenyList(denyListString, packageName);
+ }
+ }
+
+ private boolean isPackageFullyDeniedForAutofillForUnimportantView(
+ @NonNull String denyListString, @NonNull String packageName) {
+ // If "PackageName:;" is in the string, then it means the package name is in denylist
+ // and there are no activities specified under it. That means the package is fully
+ // denied for autofill
+ return denyListString.indexOf(packageName + ":;") != -1;
+ }
+
+ private boolean isPackagePartiallyDeniedForAutofillForUnimportantView(
+ @NonNull String denyListString, @NonNull String packageName) {
+ // This check happens after checking package is not fully denied. If "PackageName:" instead
+ // is in denylist, then it means there are specific activities to be denied. So the package
+ // is partially denied for autofill
+ return denyListString.indexOf(packageName + ":") != -1;
+ }
+
+ /**
+ * Get the denied activitiy names under specified package from denylist and set it in field
+ * mDeniedActivitiySet
+ *
+ * If using parameter as the example below, the denied activity set would be set to
+ * Set{Activity1,Activity2}.
+ *
+ * @param denyListString Denylist that is got from device config. For example,
+ * "Package1:Activity1,Activity2;Package2:;"
+ * @param packageName Specify to extract activities under which package.For example,
+ * "Package1:;"
+ */
+ private void setDeniedActivitySetWithDenyList(
+ @NonNull String denyListString, @NonNull String packageName) {
+ // 1. Get the index of where the Package name starts
+ final int packageInStringIndex = denyListString.indexOf(packageName + ":");
+
+ // 2. Get the ";" index after this index of package
+ final int firstNextSemicolonIndex = denyListString.indexOf(";", packageInStringIndex);
+
+ // 3. Get the activity names substring between the indexes
+ final int activityStringStartIndex = packageInStringIndex + packageName.length() + 1;
+ if (activityStringStartIndex < firstNextSemicolonIndex) {
+ Log.e(TAG, "Failed to get denied activity names from denylist because it's wrongly "
+ + "formatted");
+ }
+ final String activitySubstring =
+ denyListString.substring(activityStringStartIndex, firstNextSemicolonIndex);
+
+ // 4. Split the activity name substring
+ final String[] activityStringArray = activitySubstring.split(",");
+
+ // 5. Set the denied activity set
+ mDeniedActivitiySet = new ArraySet<>(Arrays.asList(activityStringArray));
+
+ return;
+ }
+
+ /**
+ * Check whether autofill is denied for current activity or package. Used when a view is marked
+ * as not important for autofill, if current activity or package is denied, then the view won't
+ * trigger fill request.
+ *
+ * @hide
+ */
+ public final boolean isActivityDeniedForAutofillForUnimportantView() {
+ if (mIsPackageFullyDeniedForAutofillForUnimportantView) {
+ return true;
+ }
+ if (mIsPackagePartiallyDeniedForAutofillForUnimportantView) {
+ final AutofillClient client = getClient();
+ if (client == null) {
+ return false;
+ }
+ final ComponentName clientActivity = client.autofillClientGetComponentName();
+ if (mDeniedActivitiySet.contains(clientActivity.flattenToShortString())) {
+ return true;
+ }
+ }
+ return false;
}
/**
+ * Check whether view matches autofill-able heuristics
+ *
+ * @hide
+ */
+ public final boolean isMatchingAutofillableHeuristics(@NonNull View view) {
+ if (!mIsTriggerFillRequestOnUnimportantViewEnabled) {
+ return false;
+ }
+ if (view instanceof EditText) {
+ final int actionId = ((EditText) view).getImeOptions();
+ if (mNonAutofillableImeActionIdSet.contains(String.valueOf(actionId))) {
+ return false;
+ }
+ return true;
+ }
+ if (view instanceof CheckBox
+ || view instanceof Spinner
+ || view instanceof DatePicker
+ || view instanceof TimePicker
+ || view instanceof RadioGroup) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
* @hide
*/
public void enableCompatibilityMode() {