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

Commit d04a697e authored by Felipe Leme's avatar Felipe Leme
Browse files

Optimize ViewStructure for autofill by removing irrelevant nodes.

Test: CtsAutoFillServiceTestCases (with new tests) pass
Fixes: 35840787

Change-Id: Iaa2c1907c8383b4a820fd7204e67fa2d276ad2b8
parent 0fd8e4af
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -710,6 +710,7 @@ package android {
    field public static final int imeSubtypeMode = 16843501; // 0x10102ed
    field public static final int immersive = 16843456; // 0x10102c0
    field public static final int importantForAccessibility = 16843690; // 0x10103aa
    field public static final int importantForAutofill = 16844123; // 0x101055b
    field public static final int inAnimation = 16843127; // 0x1010177
    field public static final int includeFontPadding = 16843103; // 0x101015f
    field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -45099,6 +45100,7 @@ package android.view {
    method protected int getHorizontalScrollbarHeight();
    method public int getId();
    method public int getImportantForAccessibility();
    method public int getImportantForAutofill();
    method public boolean getKeepScreenOn();
    method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
    method public int getLabelFor();
@@ -45228,6 +45230,7 @@ package android.view {
    method public boolean isHorizontalScrollBarEnabled();
    method public boolean isHovered();
    method public boolean isImportantForAccessibility();
    method public final boolean isImportantForAutofill();
    method public boolean isInEditMode();
    method public boolean isInLayout();
    method public boolean isInTouchMode();
@@ -45410,6 +45413,7 @@ package android.view {
    method public void setHovered(boolean);
    method public void setId(int);
    method public void setImportantForAccessibility(int);
    method public void setImportantForAutofill(int);
    method public void setKeepScreenOn(boolean);
    method public void setKeyboardNavigationCluster(boolean);
    method public void setLabelFor(int);
@@ -45575,6 +45579,9 @@ package android.view {
    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
    field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
    field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
    field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
    field public static final int INVISIBLE = 4; // 0x4
    field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
    field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
+7 −0
Original line number Diff line number Diff line
@@ -822,6 +822,7 @@ package android {
    field public static final int imeSubtypeMode = 16843501; // 0x10102ed
    field public static final int immersive = 16843456; // 0x10102c0
    field public static final int importantForAccessibility = 16843690; // 0x10103aa
    field public static final int importantForAutofill = 16844123; // 0x101055b
    field public static final int inAnimation = 16843127; // 0x1010177
    field public static final int includeFontPadding = 16843103; // 0x101015f
    field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -48559,6 +48560,7 @@ package android.view {
    method protected int getHorizontalScrollbarHeight();
    method public int getId();
    method public int getImportantForAccessibility();
    method public int getImportantForAutofill();
    method public boolean getKeepScreenOn();
    method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
    method public int getLabelFor();
@@ -48688,6 +48690,7 @@ package android.view {
    method public boolean isHorizontalScrollBarEnabled();
    method public boolean isHovered();
    method public boolean isImportantForAccessibility();
    method public final boolean isImportantForAutofill();
    method public boolean isInEditMode();
    method public boolean isInLayout();
    method public boolean isInTouchMode();
@@ -48870,6 +48873,7 @@ package android.view {
    method public void setHovered(boolean);
    method public void setId(int);
    method public void setImportantForAccessibility(int);
    method public void setImportantForAutofill(int);
    method public void setKeepScreenOn(boolean);
    method public void setKeyboardNavigationCluster(boolean);
    method public void setLabelFor(int);
@@ -49035,6 +49039,9 @@ package android.view {
    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
    field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
    field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
    field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
    field public static final int INVISIBLE = 4; // 0x4
    field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
    field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
+7 −0
Original line number Diff line number Diff line
@@ -710,6 +710,7 @@ package android {
    field public static final int imeSubtypeMode = 16843501; // 0x10102ed
    field public static final int immersive = 16843456; // 0x10102c0
    field public static final int importantForAccessibility = 16843690; // 0x10103aa
    field public static final int importantForAutofill = 16844123; // 0x101055b
    field public static final int inAnimation = 16843127; // 0x1010177
    field public static final int includeFontPadding = 16843103; // 0x101015f
    field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -45459,6 +45460,7 @@ package android.view {
    method protected int getHorizontalScrollbarHeight();
    method public int getId();
    method public int getImportantForAccessibility();
    method public int getImportantForAutofill();
    method public boolean getKeepScreenOn();
    method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
    method public int getLabelFor();
@@ -45589,6 +45591,7 @@ package android.view {
    method public boolean isHorizontalScrollBarEnabled();
    method public boolean isHovered();
    method public boolean isImportantForAccessibility();
    method public final boolean isImportantForAutofill();
    method public boolean isInEditMode();
    method public boolean isInLayout();
    method public boolean isInTouchMode();
@@ -45773,6 +45776,7 @@ package android.view {
    method public void setHovered(boolean);
    method public void setId(int);
    method public void setImportantForAccessibility(int);
    method public void setImportantForAutofill(int);
    method public void setKeepScreenOn(boolean);
    method public void setKeyboardNavigationCluster(boolean);
    method public void setLabelFor(int);
@@ -45938,6 +45942,9 @@ package android.view {
    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
    field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
    field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
    field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
    field public static final int INVISIBLE = 4; // 0x4
    field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
    field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
+156 −1
Original line number Diff line number Diff line
@@ -1174,6 +1174,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    public static final int AUTOFILL_TYPE_DATE = 4;
    /** @hide */
    @IntDef({
            IMPORTANT_FOR_AUTOFILL_AUTO,
            IMPORTANT_FOR_AUTOFILL_YES,
            IMPORTANT_FOR_AUTOFILL_NO
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface AutofillImportance {}
    /**
     * Automatically determine whether a view is important for auto-fill.
     */
    public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0x0;
    /**
     * The view is important for important for auto-fill.
     */
    public static final int IMPORTANT_FOR_AUTOFILL_YES = 0x1;
    /**
     * The view is not important for auto-fill.
     */
    public static final int IMPORTANT_FOR_AUTOFILL_NO = 0x2;
    /**
     * This view is enabled. Interpretation varies by subclass.
     * Use with ENABLED_MASK when calling setFlags.
@@ -2745,7 +2769,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     *                1                  PFLAG3_FINGER_DOWN
     *               1                   PFLAG3_FOCUSED_BY_DEFAULT
     *             11                    PFLAG3_AUTO_FILL_MODE_MASK
     *           xx                      * NO LONGER NEEDED, SHOULD BE REUSED *
     *           11                      PFLAG3_IMPORTANT_FOR_AUTOFILL
     *          1                        PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE
     *         1                         PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED
     *        1                          PFLAG3_TEMPORARY_DETACH
@@ -2982,6 +3006,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    private static final int PFLAG3_AUTO_FILL_MODE_MASK = (AUTO_FILL_MODE_INHERIT
            | AUTO_FILL_MODE_AUTO | AUTO_FILL_MODE_MANUAL) << PFLAG3_AUTO_FILL_MODE_SHIFT;
    /**
     * Shift for the bits in {@link #mPrivateFlags3} related to the
     * "importantForAutofill" attribute.
     */
    static final int PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT = 21;
    /**
     * Mask for obtaining the bits which specify how to determine
     * whether a view is important for autofill.
     */
    static final int PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK = (IMPORTANT_FOR_AUTOFILL_AUTO
            | IMPORTANT_FOR_AUTOFILL_YES | IMPORTANT_FOR_AUTOFILL_NO)
            << PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT;
    /**
     * Whether this view has rendered elements that overlap (see {@link
     * #hasOverlappingRendering()}, {@link #forceHasOverlappingRendering(boolean)}, and
@@ -5009,6 +5047,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                        setAutoFillHint(a.getInt(attr, AUTO_FILL_HINT_NONE));
                    }
                    break;
                case R.styleable.View_importantForAutofill:
                    if (a.peekValue(attr) != null) {
                        setImportantForAutofill(a.getInt(attr, IMPORTANT_FOR_AUTOFILL_AUTO));
                    }
                    break;
            }
        }
@@ -7443,6 +7486,118 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return null;
    }
    /**
     * Gets the mode for determining whether this View is important for autofill.
     *
     * <p>See {@link #setImportantForAutofill(int)} for more info about this mode.
     *
     * @return {@link #IMPORTANT_FOR_AUTOFILL_AUTO} by default, or value passed to
     * {@link #setImportantForAutofill(int)}.
     *
     * @attr ref android.R.styleable#View_importantForAutofill
     */
    @ViewDebug.ExportedProperty(mapping = {
            @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_AUTO, to = "auto"),
            @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_YES, to = "yes"),
            @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_NO, to = "no")})
    public @AutofillImportance int getImportantForAutofill() {
        return (mPrivateFlags3
                & PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK) >> PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT;
    }
    /**
     * Sets the mode for determining whether this View is important for autofill.
     *
     * <p>See {@link #setImportantForAutofill(int)} for more info about this mode.
     *
     * @param mode {@link #IMPORTANT_FOR_AUTOFILL_AUTO}, {@link #IMPORTANT_FOR_AUTOFILL_YES},
     * or {@link #IMPORTANT_FOR_AUTOFILL_NO}.
     *
     * @attr ref android.R.styleable#View_importantForAutofill
     */
    public void setImportantForAutofill(@AutofillImportance int mode) {
        mPrivateFlags3 &= ~PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK;
        mPrivateFlags3 |= (mode << PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT)
                & PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK;
    }
    /**
     * Hints the Android System whether the {@link android.app.assist.AssistStructure.ViewNode}
     * associated with this View should be included in a {@link ViewStructure} used for
     * autofill purposes.
     *
     * <p>Generally speaking, a view is important for autofill if:
     * <ol>
     * <li>The view can-be autofilled by an {@link android.service.autofill.AutoFillService}.
     * <li>The view contents can help an {@link android.service.autofill.AutoFillService} to
     * autofill other views.
     * <ol>
     *
     * <p>For example, view containers should typically return {@code false} for performance reasons
     * (since the important info is provided by their children), but if the container is actually
     * whose children are part of a compound view, it should return {@code true} (and then override
     * {@link #dispatchProvideAutoFillStructure(ViewStructure, int)} to simply call
     * {@link #onProvideAutoFillStructure(ViewStructure, int)} so its children are not included in
     * the structure). On the other hand, views representing labels or editable fields should
     * typically return {@code true}, but in some cases they could return {@code false} (for
     * example, if they're part of a "Captcha" mechanism).
     *
     * <p>By default, this method returns {@code true} if {@link #getImportantForAutofill()} returns
     * {@link #IMPORTANT_FOR_AUTOFILL_YES}, {@code false } if it returns
     * {@link #IMPORTANT_FOR_AUTOFILL_NO}, and use some heuristics to define the importance when it
     * returns {@link #IMPORTANT_FOR_AUTOFILL_AUTO}. Hence, it should rarely be overridden - Views
     * should use {@link #setImportantForAutofill(int)} instead.
     *
     * <p><strong>Note:</strong> returning {@code false} does not guarantee the view will be
     * excluded from the structure; for example, if the user explicitly requested auto-fill, the
     * View might be always included.
     *
     * <p>This decision applies just for the view, not its children - if the view children are not
     * important for autofill, the view should override
     * {@link #dispatchProvideAutoFillStructure(ViewStructure, int)} to simply call
     * {@link #onProvideAutoFillStructure(ViewStructure, int)} (instead of calling
     * {@link #dispatchProvideAutoFillStructure(ViewStructure, int)} for each child).
     *
     * @return whether the view is considered important for autofill.
     *
     * @see #IMPORTANT_FOR_AUTOFILL_AUTO
     * @see #IMPORTANT_FOR_AUTOFILL_YES
     * @see #IMPORTANT_FOR_AUTOFILL_NO
     */
    public final boolean isImportantForAutofill() {
        final int flag = getImportantForAutofill();
        // First, check if view explicity set it to YES or NO
        if ((flag & IMPORTANT_FOR_AUTOFILL_YES) != 0) {
            return true;
        }
        if ((flag & IMPORTANT_FOR_AUTOFILL_NO) != 0) {
            return false;
        }
        // Then use some heuristics to handle AUTO.
        // Always include views that have a explicity resource id.
        final int id = mID;
        if (id != NO_ID && !isViewIdGenerated(id)) {
            final Resources res = getResources();
            String entry = null;
            String pkg = null;
            try {
                entry = res.getResourceEntryName(id);
                pkg = res.getResourcePackageName(id);
            } catch (Resources.NotFoundException e) {
                // ignore
            }
            if (entry != null && pkg != null && pkg.equals(mContext.getPackageName())) {
                return true;
            }
        }
        // Otherwise, assume it's not important...
        return false;
    }
    @Nullable
    private AutoFillManager getAutoFillManager() {
        return mContext.getSystemService(AutoFillManager.class);
+122 −66
Original line number Diff line number Diff line
@@ -595,6 +595,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager

    public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        initViewGroup();
        initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
    }
@@ -3341,27 +3342,62 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        dispatchProvideStructureForAssistOrAutoFill(structure, true);
    }

    /** @hide */
    private ArrayList<View> getChildrenForAutofill() {
        final ArrayList<View> list = new ArrayList<>();
        populateChildrenForAutofill(list);
        return list;
    }

    /** @hide */
    private void populateChildrenForAutofill(ArrayList<View> list) {
        final int count = mChildrenCount;
        for (int i = 0; i < count; i++) {
            final View child = mChildren[i];
            if (child.isImportantForAutofill()) {
                list.add(child);
            } else if (child instanceof ViewGroup) {
                ((ViewGroup) child).populateChildrenForAutofill(list);
            }
        }
    }

    private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure,
            boolean forAutoFill) {
        boolean blocked = forAutoFill ? isAutoFillBlocked() : isAssistBlocked();
        if (blocked || structure.getChildCount() != 0) {
            return;
        }
        final View[] childrenArray;
        final ArrayList<View> childrenList;
        final int childrenCount;

        if (forAutoFill) {
            childrenArray = null;
            // TODO(b/33197203): the current algorithm allocates a new list for each children that
            // is a view group; ideally, we should use mAttachInfo.mTempArrayList instead, but that
            // would complicated the algorithm a lot...
            childrenList = getChildrenForAutofill();

            childrenCount = childrenList.size();
        } else {
            childrenArray = mChildren;
            childrenList = null;
            childrenCount = getChildCount();
        }

        if (!blocked) {
            if (structure.getChildCount() == 0) {
                final int childrenCount = getChildCount();
        if (childrenCount > 0) {
            structure.setChildCount(childrenCount);
            ArrayList<View> preorderedList = buildOrderedChildList();
            boolean customOrder = preorderedList == null
                    && isChildrenDrawingOrderEnabled();
                    final View[] children = mChildren;
            for (int i = 0; i < childrenCount; i++) {
                int childIndex;
                try {
                    childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
                } catch (IndexOutOfBoundsException e) {
                    childIndex = i;
                            if (mContext.getApplicationInfo().targetSdkVersion
                                    < Build.VERSION_CODES.M) {
                    if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M) {
                        Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ "
                                + i + " of " + childrenCount, e);
                        // At least one app is failing when we call getChildDrawingOrder
@@ -3395,7 +3431,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                            // Build the final view list.
                            preorderedList = new ArrayList<>(childrenCount);
                            for (int j = 0; j < childrenCount; j++) {
                                        preorderedList.add(children[permutation[j]]);
                                final int index = permutation[j];
                                final View child = forAutoFill
                                        ? childrenList.get(index)
                                        : childrenArray[index];
                                preorderedList.add(child);
                            }
                        }
                    } else {
@@ -3403,8 +3443,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                    }
                }

                        final View child = getAndVerifyPreorderedView(
                                preorderedList, children, childIndex);
                final View child = forAutoFill
                        ? getAndVerifyPreorderedView(preorderedList, childrenList, childIndex)
                        : getAndVerifyPreorderedView(preorderedList, childrenArray, childIndex);
                final ViewStructure cstructure = structure.newChild(i);

                // Must explicitly check which recursive method to call.
@@ -3415,8 +3456,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                    child.dispatchProvideStructure(cstructure);
                }
            }
                    if (preorderedList != null) preorderedList.clear();
                }
            if (preorderedList != null) {
                preorderedList.clear();
            }
        }
    }
@@ -3436,6 +3477,21 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        return child;
    }

    private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList,
            ArrayList<View> children, int childIndex) {
        final View child;
        if (preorderedList != null) {
            child = preorderedList.get(childIndex);
            if (child == null) {
                throw new RuntimeException("Invalid preorderedList contained null child at index "
                        + childIndex);
            }
        } else {
            child = children.get(childIndex);
        }
        return child;
    }

    /** @hide */
    @Override
    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
Loading