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

Commit fad30008 authored by Haoran Zhang's avatar Haoran Zhang
Browse files

Autofill For All Apps - Add in autofill allowlist

bug: 265186343
Test: atest AutofillForAllAppsTest#testAddingActivityToAllowlist_ViewWithImeActionGoStillTriggerFillRequest
      atest AutofillForAllAppsTest#testAddingPacakgeToAllowlist_ViewWithImeActionGoStillTriggerFillRequest

Change-Id: I439124fc69b54796f74245b01d6d37c6f4484bfb
parent ab0b751a
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -149,6 +149,15 @@ public class AutofillFeatureFlags {
    public static final String DEVICE_CONFIG_PACKAGE_DENYLIST_FOR_UNIMPORTANT_VIEW =
            "package_deny_list_for_unimportant_view";

    /**
     * Sets the list of activities and packages allowed for autofill. The format is same with
     * {@link #DEVICE_CONFIG_PACKAGE_DENYLIST_FOR_UNIMPORTANT_VIEW}
     *
     * @hide
     */
    public static final String DEVICE_CONFIG_PACKAGE_AND_ACTIVITY_ALLOWLIST_FOR_TRIGGERING_FILL_REQUEST =
            "package_and_activity_allowlist_for_triggering_fill_request";

    /**
     * Whether the heuristics check for view is enabled
     */
@@ -183,6 +192,7 @@ public class AutofillFeatureFlags {
     */
    public static final String DEVICE_CONFIG_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES =
            "should_enable_autofill_on_all_view_types";

    // END AUTOFILL FOR ALL APPS FLAGS //


@@ -378,6 +388,16 @@ public class AutofillFeatureFlags {
            DEVICE_CONFIG_PACKAGE_DENYLIST_FOR_UNIMPORTANT_VIEW, "");
    }

    /**
     * Get autofill allowlist from flag
     *
     * @hide
     */
    public static String getAllowlistStringFromFlag() {
        return DeviceConfig.getString(
            DeviceConfig.NAMESPACE_AUTOFILL,
            DEVICE_CONFIG_PACKAGE_AND_ACTIVITY_ALLOWLIST_FOR_TRIGGERING_FILL_REQUEST, "");
    }

    // START AUTOFILL PCC CLASSIFICATION FUNCTIONS

+97 −41
Original line number Diff line number Diff line
@@ -694,7 +694,18 @@ public final class AutofillManager {
    private boolean mIsPackagePartiallyDeniedForAutofill = false;

    // A deny set read from device config
    private Set<String> mDeniedActivitiySet = new ArraySet<>();
    private Set<String> mDeniedActivitySet = new ArraySet<>();

    // If a package is fully allowed, all views in package will skip the heuristic check
    private boolean mIsPackageFullyAllowedForAutofill = false;

    // If a package is partially denied, autofill manager will check whether
    // current activity is in allowed activity set. If it's allowed activity, then autofill manager
    // will skip the heuristic check
    private boolean mIsPackagePartiallyAllowedForAutofill = false;

    // An allowed activity set read from device config
    private Set<String> mAllowedActivitySet = new ArraySet<>();

    // Indicates whether called the showAutofillDialog() method.
    private boolean mShowAutofillDialogCalled = false;
@@ -873,19 +884,34 @@ public final class AutofillManager {
            AutofillFeatureFlags.getNonAutofillableImeActionIdSetFromFlag();

        final String denyListString = AutofillFeatureFlags.getDenylistStringFromFlag();
        final String allowlistString = AutofillFeatureFlags.getAllowlistStringFromFlag();

        final String packageName = mContext.getPackageName();

        mIsPackageFullyDeniedForAutofill =
            isPackageFullyDeniedForAutofill(denyListString, packageName);
            isPackageFullyAllowedOrDeniedForAutofill(denyListString, packageName);

        mIsPackageFullyAllowedForAutofill =
            isPackageFullyAllowedOrDeniedForAutofill(allowlistString, packageName);

        if (!mIsPackageFullyDeniedForAutofill) {
            mIsPackagePartiallyDeniedForAutofill =
                isPackagePartiallyDeniedForAutofill(denyListString, packageName);
                isPackagePartiallyDeniedOrAllowedForAutofill(denyListString, packageName);
        }

        if (!mIsPackageFullyAllowedForAutofill) {
            mIsPackagePartiallyAllowedForAutofill =
                isPackagePartiallyDeniedOrAllowedForAutofill(allowlistString, packageName);
        }

        if (mIsPackagePartiallyDeniedForAutofill) {
            setDeniedActivitySetWithDenyList(denyListString, packageName);
            mDeniedActivitySet = getDeniedOrAllowedActivitySetFromString(
                    denyListString, packageName);
        }

        if (mIsPackagePartiallyAllowedForAutofill) {
            mAllowedActivitySet = getDeniedOrAllowedActivitySetFromString(
                    allowlistString, packageName);
        }
    }

@@ -921,59 +947,59 @@ public final class AutofillManager {
        return true;
    }

    private boolean isPackageFullyDeniedForAutofill(
            @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 isPackageFullyAllowedOrDeniedForAutofill(
            @NonNull String listString, @NonNull String packageName) {
        // If "PackageName:;" is in the string, then it the package is fully denied or allowed for
        // autofill, depending on which string is passed to this function
        return listString.indexOf(packageName + ":;") != -1;
    }

    private boolean isPackagePartiallyDeniedForAutofill(
            @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;
    private boolean isPackagePartiallyDeniedOrAllowedForAutofill(
            @NonNull String listString, @NonNull String packageName) {
        // If "PackageName:" is in string when "PackageName:;" is not, then it means there are
        // specific activities to be allowed or denied. So the package is partially allowed or
        // denied for autofill.
        return listString.indexOf(packageName + ":") != -1;
    }

    /**
     * Get the denied activitiy names under specified package from denylist and set it in field
     * mDeniedActivitiySet
     * Get the denied or allowed activitiy names under specified package from the list string and
     * set it in fields accordingly
     *
     * If using parameter as the example below, the denied activity set would be set to
     * Set{Activity1,Activity2}.
     * For example, if the package name is Package1, and the string is
     * "Package1:Activity1,Activity2;", then the extracted activity set would be
     * {Activity1, Activity2}
     *
     * @param denyListString Denylist that is got from device config. For example,
     * @param listString 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:;"
     * @param packageName Specify which package to extract.For example, "Package1"
     *
     * @return the extracted activity set, For example, {Activity1, Activity2}
     */
    private void setDeniedActivitySetWithDenyList(
            @NonNull String denyListString, @NonNull String packageName) {
    private Set<String> getDeniedOrAllowedActivitySetFromString(
            @NonNull String listString, @NonNull String packageName) {
        // 1. Get the index of where the Package name starts
        final int packageInStringIndex = denyListString.indexOf(packageName + ":");
        final int packageInStringIndex = listString.indexOf(packageName + ":");

        // 2. Get the ";" index after this index of package
        final int firstNextSemicolonIndex = denyListString.indexOf(";", packageInStringIndex);
        final int firstNextSemicolonIndex = listString.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 "
            Log.e(TAG, "Failed to get denied activity names from list because it's wrongly "
                    + "formatted");
            return;
            return new ArraySet<>();
        }
        final String activitySubstring =
                denyListString.substring(activityStringStartIndex, firstNextSemicolonIndex);
                listString.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;
        // 5. return the extracted activities in a set
        return new ArraySet<>(Arrays.asList(activityStringArray));
    }

    /**
@@ -992,7 +1018,32 @@ public final class AutofillManager {
                return false;
            }
            final ComponentName clientActivity = client.autofillClientGetComponentName();
            if (mDeniedActivitiySet.contains(clientActivity.flattenToShortString())) {
            if (mDeniedActivitySet.contains(clientActivity.flattenToShortString())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check whether current activity is allowlisted for autofill.
     *
     * If it is, the view in current activity will bypass heuristic check when checking whether it's
     * autofillable
     *
     * @hide
     */
    public boolean isActivityAllowedForAutofill() {
        if (mIsPackageFullyAllowedForAutofill) {
            return true;
        }
        if (mIsPackagePartiallyAllowedForAutofill) {
            final AutofillClient client = getClient();
            if (client == null) {
                return false;
            }
            final ComponentName clientActivity = client.autofillClientGetComponentName();
            if (mAllowedActivitySet.contains(clientActivity.flattenToShortString())) {
                return true;
            }
        }
@@ -1009,17 +1060,22 @@ public final class AutofillManager {
     * @hide
     */
    public boolean isAutofillable(View view) {
        if (isActivityDeniedForAutofill()) {
            Log.d(TAG, "view is not autofillable - activity denied for autofill");
            return false;
        }

        // Duplicate the autofill type check here because ViewGroup will call this function to
        // decide whether to include view in assist structure.
        // Also keep the autofill type check inside View#IsAutofillable() to serve as an early out
        // or if other functions need to call it.
        if (view.getAutofillType() == View.AUTOFILL_TYPE_NONE) return false;

        if (isActivityDeniedForAutofill()) {
            Log.d(TAG, "view is not autofillable - activity denied for autofill");
            return false;
        }

        if (isActivityAllowedForAutofill()) {
            Log.d(TAG, "view is autofillable - activity allowed for autofill");
            return true;
        }

        if (view instanceof EditText) {
            return isPassingImeActionCheck((EditText) view);
        }
@@ -1037,7 +1093,7 @@ public final class AutofillManager {
            || view instanceof RadioGroup) {
            return true;
        }

        Log.d(TAG, "view is not autofillable - not important and filtered by view type check");
        return false;
    }