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

Commit 9a15c0f5 authored by Felipe Leme's avatar Felipe Leme
Browse files

Autofill optimization on notifyViewEntered().

When an autofill service returns a null FillResponse, AutofillManager is kept
in a "zombie" state where the session is finished but a new one must be started
if the user taps another view, so it covers the scenarios where a new view is
added (and the service can autofill it). But we can optimize this workflow by
ignoring views that were already visited before.

Test: atest CtsAutoFillServiceTestCases:LoginActivityTest#testMultipleIterationsAfterServiceReturnedNoDatasets
Test: atest CtsAutoFillServiceTestCases:LoginActivityTest

Bug: 73078981

Change-Id: If8b01aca41f5d1613663002bb6b589fb1cf549df
parent 90f285ba
Loading
Loading
Loading
Loading
+58 −22
Original line number Diff line number Diff line
@@ -356,6 +356,13 @@ public final class AutofillManager {
    @GuardedBy("mLock")
    @Nullable private ArraySet<AutofillId> mFillableIds;

    /**
     * Views that were already "entered" - if they're entered again when the session is not active,
     * they're ignored
     * */
    @GuardedBy("mLock")
    @Nullable private ArraySet<AutofillId> mEnteredIds;

    /** If set, session is commited when the field is clicked. */
    @GuardedBy("mLock")
    @Nullable private AutofillId mSaveTriggerId;
@@ -711,17 +718,29 @@ public final class AutofillManager {
    }

    @GuardedBy("mLock")
    private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
    private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
        if (isDisabledByServiceLocked()) {
            if (sVerbose) {
                Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
                        + ") on state " + getStateAsStringLocked());
                Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
                        + ") on state " + getStateAsStringLocked() + " because disabled by svc");
            }
            return true;
        }
        if (isFinishedLocked()) {
            // Session already finished: ignore if automatic request and view already entered
            if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
                    && mEnteredIds.contains(id)) {
                if (sVerbose) {
                    Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
                            + ") on state " + getStateAsStringLocked()
                            + " because view was already entered: " + mEnteredIds);
                }
                return true;
            }
        if (sVerbose && isFinishedLocked()) {
            Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view
                    + ") on state " + getStateAsStringLocked());
        }
        if (sVerbose) {
            Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
                    + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
        }
        return false;
    }
@@ -753,7 +772,8 @@ public final class AutofillManager {
    /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
    @GuardedBy("mLock")
    private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
        if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
        final AutofillId id = getAutofillId(view);
        if (shouldIgnoreViewEnteredLocked(id, flags)) return null;

        AutofillCallback callback = null;

@@ -766,7 +786,6 @@ public final class AutofillManager {
        } else {
            // don't notify entered when Activity is already in background
            if (!isClientDisablingEnterExitEvent()) {
                final AutofillId id = getAutofillId(view);
                final AutofillValue value = view.getAutofillValue();

                if (!isActiveLocked()) {
@@ -776,6 +795,7 @@ public final class AutofillManager {
                    // Update focus on existing session.
                    updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
                }
                addEnteredIdLocked(id);
            }
        }
        return callback;
@@ -900,8 +920,9 @@ public final class AutofillManager {
    @GuardedBy("mLock")
    private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
                                                     int flags) {
        final AutofillId id = getAutofillId(view, virtualId);
        AutofillCallback callback = null;
        if (shouldIgnoreViewEnteredLocked(view, flags)) return callback;
        if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;

        ensureServiceClientAddedIfNeededLocked();

@@ -912,8 +933,6 @@ public final class AutofillManager {
        } else {
            // don't notify entered when Activity is already in background
            if (!isClientDisablingEnterExitEvent()) {
                final AutofillId id = getAutofillId(view, virtualId);

                if (!isActiveLocked()) {
                    // Starts new session.
                    startSessionLocked(id, bounds, null, flags);
@@ -921,11 +940,20 @@ public final class AutofillManager {
                    // Update focus on existing session.
                    updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
                }
                addEnteredIdLocked(id);
            }
        }
        return callback;
    }

    @GuardedBy("mLock")
    private void addEnteredIdLocked(@NonNull AutofillId id) {
        if (mEnteredIds == null) {
            mEnteredIds = new ArraySet<>(1);
        }
        mEnteredIds.add(id);
    }

    /**
     * Called when a virtual view that supports autofill is exited.
     *
@@ -992,9 +1020,9 @@ public final class AutofillManager {
            }

            if (!mEnabled || !isActiveLocked()) {
                if (sVerbose && mEnabled) {
                    Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
                            + getStateAsStringLocked());
                if (sVerbose) {
                    Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
                            + "): ignoring on state " + getStateAsStringLocked());
                }
                return;
            }
@@ -1024,6 +1052,10 @@ public final class AutofillManager {
        }
        synchronized (mLock) {
            if (!mEnabled || !isActiveLocked()) {
                if (sVerbose) {
                    Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
                            + "): ignoring on state " + getStateAsStringLocked());
                }
                return;
            }

@@ -1032,7 +1064,6 @@ public final class AutofillManager {
        }
    }


    /**
     * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
     *
@@ -1392,7 +1423,8 @@ public final class AutofillManager {
        if (sVerbose) {
            Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
                    + ", flags=" + flags + ", state=" + getStateAsStringLocked()
                    + ", compatMode=" + isCompatibilityModeEnabledLocked());
                    + ", compatMode=" + isCompatibilityModeEnabledLocked()
                    + ", enteredIds=" + mEnteredIds);
        }
        if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
            if (sVerbose) {
@@ -1429,7 +1461,7 @@ public final class AutofillManager {
            throw e.rethrowFromSystemServer();
        }

        resetSessionLocked();
        resetSessionLocked(/* resetEnteredIds= */ true);
    }

    @GuardedBy("mLock")
@@ -1444,22 +1476,25 @@ public final class AutofillManager {
            throw e.rethrowFromSystemServer();
        }

        resetSessionLocked();
        resetSessionLocked(/* resetEnteredIds= */ true);
    }

    @GuardedBy("mLock")
    private void resetSessionLocked() {
    private void resetSessionLocked(boolean resetEnteredIds) {
        mSessionId = NO_SESSION;
        mState = STATE_UNKNOWN;
        mTrackedViews = null;
        mFillableIds = null;
        mSaveTriggerId = null;
        if (resetEnteredIds) {
            mEnteredIds = null;
        }
    }

    @GuardedBy("mLock")
    private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
            int flags) {
        if (sVerbose && action != ACTION_VIEW_EXITED) {
        if (sVerbose) {
            Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
                    + ", value=" + value + ", action=" + action + ", flags=" + flags);
        }
@@ -1629,7 +1664,7 @@ public final class AutofillManager {
            mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
            if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
                // Reset the session state
                resetSessionLocked();
                resetSessionLocked(/* resetEnteredIds= */ true);
            }
            if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
                // Reset connection to system
@@ -1825,7 +1860,7 @@ public final class AutofillManager {
    private void setSessionFinished(int newState) {
        synchronized (mLock) {
            if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
            resetSessionLocked();
            resetSessionLocked(/* resetEnteredIds= */ false);
            mState = newState;
        }
    }
@@ -1953,6 +1988,7 @@ public final class AutofillManager {
            pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
        }
        pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
        pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
        pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
        pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
        pw.print(pfx); pw.print("compat mode enabled: "); pw.println(