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

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

Trigger Augmented Autofill when the standard service used FillResponse.disableAutofill()

Test: atest CtsAutoFillServiceTestCases:android.autofillservice.cts.augmented.DisableAutofillTest
Test: atest CtsAutoFillServiceTestCases # sanity check

Fixes: 123099744

Change-Id: Ic40727a72f931d7a062dedf02fa83d091648498b
parent d6b17862
Loading
Loading
Loading
Loading
+38 −9
Original line number Diff line number Diff line
@@ -228,6 +228,9 @@ public final class AutofillManager {
    /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
    /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
    /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;

    /** @hide */ public static final int FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;

    /** @hide */
    public static final int DEFAULT_LOGGING_LEVEL = Build.IS_DEBUGGABLE
            ? AutofillManager.FLAG_ADD_CLIENT_DEBUG
@@ -307,8 +310,8 @@ public final class AutofillManager {

    /**
     * Same as {@link #STATE_UNKNOWN}, but used on
     * {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
     * the URL bar changed on client mode
     * {@link AutofillManagerClient#setSessionFinished(int, List)} when the session was finished
     * because the URL bar changed on client mode
     *
     * @hide
     */
@@ -316,8 +319,8 @@ public final class AutofillManager {

    /**
     * Same as {@link #STATE_UNKNOWN}, but used on
     * {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
     * the service failed to fullfil a request.
     * {@link AutofillManagerClient#setSessionFinished(int, List)} when the session was finished
     * because the service failed to fullfil a request.
     *
     * @hide
     */
@@ -436,7 +439,7 @@ public final class AutofillManager {
     * There is currently no session running.
     * {@hide}
     */
    public static final int NO_SESSION = Integer.MIN_VALUE;
    public static final int NO_SESSION = Integer.MAX_VALUE;

    private final IAutoFillManager mService;

@@ -513,6 +516,10 @@ public final class AutofillManager {
    @Nullable
    private final AutofillOptions mOptions;

    /** When set, session is only used for augmented autofill requests. */
    @GuardedBy("mLock")
    private boolean mForAugmentedAutofillOnly;

    /** @hide */
    public interface AutofillClient {
        /**
@@ -940,9 +947,8 @@ public final class AutofillManager {
        ensureServiceClientAddedIfNeededLocked();

        if (!mEnabled) {
            if (sVerbose) {
                Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
            }
            if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");

            if (mCallback != null) {
                callback = mCallback;
            }
@@ -1025,6 +1031,12 @@ public final class AutofillManager {
    private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
            boolean isVisible, boolean virtual) {
        synchronized (mLock) {
            if (mForAugmentedAutofillOnly) {
                if (sVerbose) {
                    Log.v(TAG,  "notifyViewVisibilityChanged(): ignoring on augmented only mode");
                }
                return;
            }
            if (mEnabled && isActiveLocked()) {
                final AutofillId id = virtual ? getAutofillId(view, virtualId)
                        : view.getAutofillId();
@@ -1168,6 +1180,10 @@ public final class AutofillManager {
        AutofillValue value = null;

        synchronized (mLock) {
            if (mForAugmentedAutofillOnly) {
                if (sVerbose) Log.v(TAG,  "notifyValueChanged(): ignoring on augmented only mode");
                return;
            }
            // If the session is gone some fields might still be highlighted, hence we have to
            // remove the isAutofilled property even if no sessions are active.
            if (mLastAutofilledData == null) {
@@ -1221,6 +1237,10 @@ public final class AutofillManager {
            return;
        }
        synchronized (mLock) {
            if (mForAugmentedAutofillOnly) {
                if (sVerbose) Log.v(TAG,  "notifyValueChanged(): ignoring on augmented only mode");
                return;
            }
            if (!mEnabled || !isActiveLocked()) {
                if (sVerbose) {
                    Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
@@ -1676,14 +1696,20 @@ public final class AutofillManager {
            if (client == null) return; // NOTE: getClient() already logged it..

            final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
            final ComponentName componentName = client.autofillClientGetComponentName();
            mService.startSession(client.autofillClientGetActivityToken(),
                    mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
                    mCallback != null, flags, client.autofillClientGetComponentName(),
                    mCallback != null, flags, componentName,
                    isCompatibilityModeEnabledLocked(), receiver);
            mSessionId = receiver.getIntResult();
            if (mSessionId != NO_SESSION) {
                mState = STATE_ACTIVE;
            }
            final int extraFlags = receiver.getOptionalExtraIntResult(0);
            if ((extraFlags & FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY) != 0) {
                if (sDebug) Log.d(TAG, "startSession(" + componentName + "): for augmented only");
                mForAugmentedAutofillOnly = true;
            }
            client.autofillClientResetableStateAvailable();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
@@ -2400,6 +2426,9 @@ public final class AutofillManager {
            pw.print(pfx); pw.print("entered ids for augmented autofill: ");
            pw.println(mEnteredForAugmentedAutofillIds);
        }
        if (mForAugmentedAutofillOnly) {
            pw.print(pfx); pw.println("For Augmented Autofill Only");
        }
        pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
        pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
        if (mOptions != null) {
+25 −0
Original line number Diff line number Diff line
@@ -96,6 +96,19 @@ public final class SyncResultReceiver extends IResultReceiver.Stub {
        return mBundle == null ? null : mBundle.getParcelable(EXTRA);
    }

    /**
     * Gets the optional result from an operation that returns an extra {@code int} (besides the
     * result code).
     *
     * @return value set in the bundle, or {@code defaultValue} when not set.
     */
    public int getOptionalExtraIntResult(int defaultValue) throws TimeoutException {
        waitResult();
        if (mBundle == null || !mBundle.containsKey(EXTRA)) return defaultValue;

        return mBundle.getInt(EXTRA);
    }

    @Override
    public void send(int resultCode, Bundle resultData) {
        mResult = resultCode;
@@ -136,6 +149,18 @@ public final class SyncResultReceiver extends IResultReceiver.Stub {
        return bundle;
    }

    /**
     * Creates a bundle for an {@code int} value so it can be retrieved by
     * {@link #getParcelableResult()} - typically used to return an extra {@code int} (as the 1st
     * is returned as the result code).
     */
    @NonNull
    public static Bundle bundleFor(int value) {
        final Bundle bundle = new Bundle();
        bundle.putInt(EXTRA, value);
        return bundle;
    }

    /** @hide */
    public static final class TimeoutException extends RemoteException {
        private TimeoutException(String msg) {
+17 −3
Original line number Diff line number Diff line
@@ -647,6 +647,14 @@ public final class AutofillManagerService
        send(receiver, value ? 1 : 0);
    }

    private void send(@NonNull IResultReceiver receiver, int value1, int value2) {
        try {
            receiver.send(value1, SyncResultReceiver.bundleFor(value2));
        } catch (RemoteException e) {
            Slog.w(TAG, "Error async reporting result to client: " + e);
        }
    }

    @Nullable
    @VisibleForTesting
    static Map<String, String[]> getWhitelistedCompatModePackages(String setting) {
@@ -967,15 +975,21 @@ public final class AutofillManagerService
            // TODO(b/113281366): add a callback method on AM to be notified when a task is finished
            // so we can clean up sessions kept alive
            final int taskId = mAm.getTaskIdForActivity(activityToken, false);
            final int sessionId;
            final long result;
            synchronized (mLock) {
                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
                sessionId = service.startSessionLocked(activityToken, taskId, getCallingUid(),
                result = service.startSessionLocked(activityToken, taskId, getCallingUid(),
                        appCallback, autofillId, bounds, value, hasCallback, componentName,
                        compatMode, mAllowInstantService, flags);
            }
            final int sessionId = (int) result;
            final int resultFlags = (int) (result >> 32);
            if (resultFlags != 0) {
                send(receiver, sessionId, resultFlags);
            } else {
                send(receiver, sessionId);
            }
        }

        @Override
        public void getFillEventHistory(@NonNull IResultReceiver receiver) throws RemoteException {
+58 −24
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.autofill;

import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY;
import static android.view.autofill.AutofillManager.NO_SESSION;

import static com.android.server.autofill.Helper.sDebug;
@@ -53,6 +54,7 @@ import android.service.autofill.FieldClassification;
import android.service.autofill.FieldClassification.Match;
import android.service.autofill.FillEventHistory;
import android.service.autofill.FillEventHistory.Event;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
import android.service.autofill.UserData;
@@ -221,7 +223,7 @@ final class AutofillManagerServiceImpl
                    session.removeSelfLocked();
                }
            }
            sendStateToClients(false);
            sendStateToClients(/* resetClient= */ false);
        }
        updateRemoteAugmentedAutofillService();
        return enabledChanged;
@@ -278,8 +280,15 @@ final class AutofillManagerServiceImpl
        }
    }

    /**
     * Starts a new session.
     *
     * @return {@code long} whose right-most 32 bits represent the session id (which is always
     * non-negative), and the left-most contains extra flags (currently either {@code 0} or
     * {@link FillRequest#FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY}).
     */
    @GuardedBy("mLock")
    int startSessionLocked(@NonNull IBinder activityToken, int taskId, int uid,
    long startSessionLocked(@NonNull IBinder activityToken, int taskId, int uid,
            @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
            @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
            @NonNull ComponentName componentName, boolean compatMode,
@@ -289,33 +298,48 @@ final class AutofillManagerServiceImpl
        }

        final String shortComponentName = componentName.toShortString();
        boolean forAugmentedAutofillOnly = false;

        if (isAutofillDisabledLocked(componentName)) {
            // Service disabled autofill; that means no session, unless the activity is whitelisted
            // for augmented autofill
            if (isWhitelistedForAugmentedAutofillLocked(componentName)) {
                if (sDebug) {
                Slog.d(TAG, "startSession(" + shortComponentName
                        + "): ignored because disabled by service");
                    Slog.d(TAG, "startSession(" + shortComponentName + "): disabled by service but "
                            + "whitelisted for augmented autofill");
                }
                forAugmentedAutofillOnly = true;

            } else {
                if (sDebug) {
                    Slog.d(TAG, "startSession(" + shortComponentName + "): ignored because "
                            + "disabled by service and not whitelisted for augmented autofill");
                }
                final IAutoFillManagerClient client = IAutoFillManagerClient.Stub
                        .asInterface(appCallbackToken);
                try {
                    client.setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE,
                            /* autofillableIds= */ null);
                } catch (RemoteException e) {
                Slog.w(TAG, "Could not notify " + shortComponentName + " that it's disabled: " + e);
                    Slog.w(TAG,
                            "Could not notify " + shortComponentName + " that it's disabled: " + e);
                }

                return NO_SESSION;
            }
        }

        if (sVerbose) Slog.v(TAG, "startSession(): token=" + activityToken + ", flags=" + flags);
        if (sVerbose) {
            Slog.v(TAG, "startSession(): token=" + activityToken + ", flags=" + flags
                    + ", forAugmentedAutofillOnly=" + forAugmentedAutofillOnly);
        }

        // Occasionally clean up abandoned sessions
        pruneAbandonedSessionsLocked();

        final Session newSession = createSessionByTokenLocked(activityToken, taskId, uid,
                appCallbackToken, hasCallback, componentName, compatMode,
                bindInstantServiceAllowed, flags);
                bindInstantServiceAllowed, forAugmentedAutofillOnly, flags);
        if (newSession == null) {
            return NO_SESSION;
        }
@@ -324,13 +348,21 @@ final class AutofillManagerServiceImpl
                "id=" + newSession.id + " uid=" + uid + " a=" + shortComponentName
                + " s=" + mInfo.getServiceInfo().packageName
                + " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds
                + " hc=" + hasCallback + " f=" + flags;
                + " hc=" + hasCallback + " f=" + flags + " aa=" + forAugmentedAutofillOnly;
        mMaster.logRequestLocked(historyItem);

        newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags);

        if (forAugmentedAutofillOnly) {
            // Must embed the flag in the response, at the high-end side of the long.
            // (session is always positive, so we don't have to worry about the signal bit)
            final long extraFlags = ((long) FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY) << 32;
            final long result = extraFlags | newSession.id;
            return result;
        } else {
            return newSession.id;
        }
    }

    /**
     * Remove abandoned sessions if needed.
@@ -435,7 +467,7 @@ final class AutofillManagerServiceImpl
    private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int taskId, int uid,
            @NonNull IBinder appCallbackToken, boolean hasCallback,
            @NonNull ComponentName componentName, boolean compatMode,
            boolean bindInstantServiceAllowed, int flags) {
            boolean bindInstantServiceAllowed, boolean forAugmentedAutofillOnly, int flags) {
        // use random ids so that one app cannot know that another app creates sessions
        int sessionId;
        int tries = 0;
@@ -446,15 +478,17 @@ final class AutofillManagerServiceImpl
                return null;
            }

            sessionId = sRandom.nextInt();
        } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0);
            sessionId = Math.abs(sRandom.nextInt());
        } while (sessionId == 0 || sessionId == NO_SESSION
                || mSessions.indexOfKey(sessionId) >= 0);

        assertCallerLocked(componentName, compatMode);

        final Session newSession = new Session(this, mUi, getContext(), mHandler, mUserId, mLock,
                sessionId, taskId, uid, activityToken, appCallbackToken, hasCallback,
                mUiLatencyHistory, mWtfHistory, mInfo.getServiceInfo().getComponentName(),
                componentName, compatMode, bindInstantServiceAllowed, flags);
                componentName, compatMode, bindInstantServiceAllowed, forAugmentedAutofillOnly,
                flags);
        mSessions.put(newSession.id, newSession);

        return newSession;
@@ -634,7 +668,7 @@ final class AutofillManagerServiceImpl
            remoteFillServices.valueAt(i).destroy();
        }

        sendStateToClients(true);
        sendStateToClients(/* resetclient=*/ true);
        if (mClients != null) {
            mClients.kill();
            mClients = null;
+43 −15
Original line number Diff line number Diff line
@@ -138,7 +138,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState

    private static AtomicInteger sIdCounter = new AtomicInteger();

    /** ID of the session */
    /**
     * ID of the session.
     *
     * <p>It's always a positive number, to make it easier to embed it in a long.
     */
    public final int id;

    /** uid the session is for */
@@ -276,6 +280,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
    @GuardedBy("mLock")
    private ArraySet<AutofillId> mAugmentedAutofillableIds;

    /**
     * When {@code true}, the session was created only to handle Augmented Autofill requests (i.e.,
     * the session would not have existed otherwsie).
     */
    @GuardedBy("mLock")
    private boolean mForAugmentedAutofillOnly;

    /**
     * Receiver of assist data from the app's {@link Activity}.
     */
@@ -538,11 +549,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
     */
    @GuardedBy("mLock")
    private void requestNewFillResponseLocked(int flags) {

        if ((flags & FLAG_AUGMENTED_AUTOFILL_REQUEST) != 0) {
        if (mForAugmentedAutofillOnly || (flags & FLAG_AUGMENTED_AUTOFILL_REQUEST) != 0) {
            // TODO(b/122858578): log metrics
            if (sVerbose) {
                Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead");
                Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead "
                        + "(mForAugmentedAutofillOnly=" + mForAugmentedAutofillOnly
                        + ", flags=" + flags + ")");
            }
            triggerAugmentedAutofillLocked();
            return;
@@ -564,8 +576,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        mRequestLogs.put(requestId, log);

        if (sVerbose) {
            Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId="
                    + requestId + ", flags=" + flags);
            Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId=" + requestId
                    + ", flags=" + flags);
        }

        // If the focus changes very quickly before the first request is returned each focus change
@@ -598,7 +610,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
            @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory,
            @NonNull LocalLog wtfHistory, @NonNull ComponentName serviceComponentName,
            @NonNull ComponentName componentName, boolean compatMode,
            boolean bindInstantServiceAllowed, int flags) {
            boolean bindInstantServiceAllowed, boolean forAugmentedAutofillOnly, int flags) {
        if (sessionId < 0) {
            wtf(null, "Non-positive sessionId: %s", sessionId);
        }
        id = sessionId;
        mFlags = flags;
        this.taskId = taskId;
@@ -616,6 +631,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        mWtfHistory = wtfHistory;
        mComponentName = componentName;
        mCompatMode = compatMode;
        mForAugmentedAutofillOnly = forAugmentedAutofillOnly;
        setClientLocked(client);

        mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
@@ -727,6 +743,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        final long disableDuration = response.getDisableDuration();
        if (disableDuration > 0) {
            final int flags = response.getFlags();
            if ((flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0) {
                mService.disableAutofillForActivity(mComponentName, disableDuration,
                        id, mCompatMode);
            } else {
                mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration,
                        id, mCompatMode);
            }
            // Although "standard" autofill is disabled, it might still trigger augmented autofill
            if (triggerAugmentedAutofillLocked() != null) {
                mForAugmentedAutofillOnly = true;
                if (sDebug) {
                    Slog.d(TAG, "Service disabled autofill for " + mComponentName
                            + ", but session is kept for augmented autofill only");
                }
                return;
            }
            if (sDebug) {
                final StringBuilder message = new StringBuilder("Service disabled autofill for ")
                                .append(mComponentName)
@@ -735,13 +767,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                TimeUtils.formatDuration(disableDuration, message);
                Slog.d(TAG, message.toString());
            }
            if ((flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0) {
                mService.disableAutofillForActivity(mComponentName, disableDuration,
                        id, mCompatMode);
            } else {
                mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration,
                        id, mCompatMode);
            }
            sessionFinishedState = AutofillManager.STATE_DISABLED_BY_SERVICE;
        }

@@ -3005,6 +3030,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
                mSaveOnAllViewsInvisible);
        pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
        if (mForAugmentedAutofillOnly) {
            pw.print(prefix); pw.println("For Augmented Autofill Only");
        }
        if (mAugmentedAutofillDestroyer != null) {
            pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer");
        }