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

Commit 481db7cc authored by Felipe Leme's avatar Felipe Leme
Browse files

Fixed Augmented Autofill workflow so the UI is closed when another field is focused.

Test: atest AugmentedLoginActivityTest#testAugmentedAutoFill_multipleRequests \
            AugmentedLoginActivityTest#testAugmentedAutoFill_rotateDevice
Test: atest CtsAutoFillServiceTestCases  # sanity check

Fixes: 128638902

Change-Id: I09d2a87f3dff72afbc339f2c295343419905f45b
parent 289cd11c
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -405,7 +405,6 @@ public abstract class AugmentedAutofillService extends Service {
        private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue,
                @NonNull IFillCallback callback) {
            synchronized (mLock) {
                // TODO(b/123099468): should we close the popupwindow if the focused id changed?
                mFocusedId = focusedId;
                mFocusedValue = focusedValue;
                if (mCallback != null) {
+1 −4
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.service.autofill.FillRequest.FLAG_AUGMENTED_AUTOFILL_REQUE
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.view.autofill.Helper.sDebug;
import static android.view.autofill.Helper.sVerbose;
import static android.view.autofill.Helper.toList;

import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.IntDef;
@@ -1872,10 +1873,6 @@ public final class AutofillManager {
        }
    }

    private <T> ArrayList<T> toList(@Nullable Set<T> set) {
        return set == null ? null : new ArrayList<T>(set);
    }

    /**
     * Notifies that a non-autofillable view was entered because the activity is whitelisted for
     * augmented autofill.
+12 −1
Original line number Diff line number Diff line
@@ -19,7 +19,9 @@ package android.view.autofill;
import android.annotation.NonNull;
import android.annotation.Nullable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;

/** @hide */
public final class Helper {
@@ -62,7 +64,8 @@ public final class Helper {
    }

    /**
     * Convers a collaction of {@link AutofillId AutofillIds} to an array.
     * Converts a collaction of {@link AutofillId AutofillIds} to an array.
     *
     * @param collection The collection.
     * @return The array.
     */
@@ -75,6 +78,14 @@ public final class Helper {
        return array;
    }

    /**
     * Converts a Set to a List.
     */
    @Nullable
    public static <T> ArrayList<T> toList(@Nullable Set<T> set) {
        return set == null ? null : new ArrayList<T>(set);
    }

    private Helper() {
        throw new UnsupportedOperationException("contains static members only");
    }
+3 −3
Original line number Diff line number Diff line
@@ -211,8 +211,8 @@ public final class Helper {
     * Gets the {@link AutofillId} of the autofillable nodes in the {@code structure}.
     */
    @NonNull
    static ArrayList<AutofillId> getAutofillableIds(@NonNull AssistStructure structure) {
        final ArrayList<AutofillId> ids = new ArrayList<>();
    static ArraySet<AutofillId> getAutofillableIds(@NonNull AssistStructure structure) {
        final ArraySet<AutofillId> ids = new ArraySet<>();
        final int size = structure.getWindowNodeCount();
        for (int i = 0; i < size; i++) {
            final WindowNode node = structure.getWindowNodeAt(i);
@@ -222,7 +222,7 @@ public final class Helper {
    }

    private static void addAutofillableIds(@NonNull ViewNode node,
            @NonNull ArrayList<AutofillId> ids) {
            @NonNull ArraySet<AutofillId> ids) {
        if (node.getAutofillType() != View.AUTOFILL_TYPE_NONE) {
            ids.add(node.getAutofillId());
        }
+60 −10
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
import static android.view.autofill.Helper.toList;

import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.autofill.Helper.getNumericValue;
@@ -266,6 +267,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
    @GuardedBy("mLock")
    private ArrayList<LogMaker> mAugmentedRequestsLogs;


    /**
     * List of autofill ids of autofillable fields present in the AssistStructure that can be used
     * to trigger new augmented autofill requests (because the "standard" service was not interested
     * on autofilling the app.
     */
    @GuardedBy("mLock")
    private ArraySet<AutofillId> mAugmentedAutofillableIds;

    /**
     * Receiver of assist data from the app's {@link Activity}.
     */
@@ -2327,12 +2337,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                    return;
                }

                if (mAugmentedAutofillableIds != null && mAugmentedAutofillableIds.contains(id)) {
                    // View was already reported when server could not handle a response, but it
                    // triggered augmented autofill

                    if (sDebug) Slog.d(TAG, "updateLocked(" + id + "): augmented-autofillable");

                    // Update the view states first...
                    mCurrentViewId = viewState.id;
                    viewState.setCurrentValue(value);

                    // ...then trigger the augmented autofill UI
                    triggerAugmentedAutofillLocked();
                    return;
                }

                requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);

                // Remove the UI if the ViewState has changed.
                if (!Objects.equals(mCurrentViewId, viewState.id)) {
                    mUi.hideFillUi(this);
                    mCurrentViewId = viewState.id;
                    hideAugmentedAutofillLocked(viewState);
                }

                // If the ViewState is ready to be displayed, onReady() will be called.
@@ -2340,8 +2366,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                break;
            case ACTION_VIEW_EXITED:
                if (Objects.equals(mCurrentViewId, viewState.id)) {
                    if (sVerbose) Slog.d(TAG, "Exiting view " + id);
                    if (sVerbose) Slog.v(TAG, "Exiting view " + id);
                    mUi.hideFillUi(this);
                    hideAugmentedAutofillLocked(viewState);
                    mCurrentViewId = null;
                }
                break;
@@ -2350,6 +2377,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        }
    }

    @GuardedBy("mLock")
    private void hideAugmentedAutofillLocked(@NonNull ViewState viewState) {
        if ((viewState.getState()
                & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) {
            viewState.resetState(ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL);
            cancelAugmentedAutofillLocked();
        }
    }

    /**
     * Checks whether a view should be ignored.
     */
@@ -2430,14 +2466,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
    }

    private void notifyUnavailableToClient(int sessionFinishedState,
            @Nullable ArrayList<AutofillId> autofillableIds) {
            @Nullable ArraySet<AutofillId> autofillableIds) {
        synchronized (mLock) {
            if (mCurrentViewId == null) return;
            try {
                if (mHasCallback) {
                    mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinishedState);
                } else if (sessionFinishedState != 0) {
                    mClient.setSessionFinished(sessionFinishedState, autofillableIds);
                    mClient.setSessionFinished(sessionFinishedState, toList(autofillableIds));
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e);
@@ -2560,7 +2596,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState

        final FillContext context = getFillContextByRequestIdLocked(requestId);

        final ArrayList<AutofillId> autofillableIds;
        final ArraySet<AutofillId> autofillableIds;
        if (context != null) {
            final AssistStructure structure = context.getStructure();
            autofillableIds = Helper.getAutofillableIds(structure);
@@ -2583,16 +2619,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            notifyUnavailableToClient(AutofillManager.STATE_FINISHED, autofillableIds);
            removeSelf();
        } else {
            // TODO(b/123099468, b/119638958): must set internal state so when user focus other
            // fields it does not generate a new call to the standard autofill service (right now
            // it does). In other words, must also pass the autofillableIds - we'll be handled in
            // a separate change (with new CTS tests to exercise this scenario).

            if (sVerbose) {
                Slog.v(TAG, "keeping session " + id + " when server returned null but "
                        + "there is an AugmentedAutofill for user. AutofillableIds: "
                        + autofillableIds);
            }
            mAugmentedAutofillableIds = autofillableIds;
        }
    }

@@ -2660,7 +2692,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            return null;
        }

        final AutofillValue currentValue = mViewStates.get(mCurrentViewId).getCurrentValue();
        final ViewState viewState = mViewStates.get(mCurrentViewId);
        viewState.setState(ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL);
        final AutofillValue currentValue = viewState.getCurrentValue();

        // TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize
        // further AFM -> AFMS calls.
@@ -2681,6 +2715,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        return mAugmentedAutofillDestroyer;
    }

    @GuardedBy("mLock")
    private void cancelAugmentedAutofillLocked() {
        final RemoteAugmentedAutofillService remoteService = mService
                .getRemoteAugmentedAutofillServiceLocked();
        if (remoteService == null) {
            Slog.w(TAG, "cancelAugmentedAutofillLocked(): no service for user");
            return;
        }
        if (sVerbose) Slog.v(TAG, "cancelAugmentedAutofillLocked() on " + mCurrentViewId);
        remoteService.onDestroyAutofillWindowsRequest();
    }

    @GuardedBy("mLock")
    private void processResponseLocked(@NonNull FillResponse newResponse,
            @Nullable Bundle newClientState, int flags) {
@@ -2967,6 +3013,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            pw.println(mAugmentedRequestsLogs.size());
        }

        if (mAugmentedAutofillableIds != null) {
            pw.print(prefix); pw.print("mAugmentedAutofillableIds: ");
            pw.println(mAugmentedAutofillableIds);
        }
        mRemoteFillService.dump(prefix, pw);
    }

Loading