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

Commit 24c76f0e authored by Daniel's avatar Daniel
Browse files

Credential Autofill Service integration

Current impl (Patchset 3) solves the following scenarios:

1. Credman only screen.
2. Autofill only screen.
3. Mixed screen, meaning the assistStructure contains both credential and autofill fields.

It does not solve the following scenario perfectly.

1. When the user goes from credman screen to autofill screen, and autofill manager decides to reuse the same session.

-> Autofill Provider will reuse the old assistStructure and fail to receive a fill response that contains the new screen.
-> Credman Provider will request a new assistStructure and a new fill response because a view that is not tracked has entered.
-> Credman Provider will return an error saying the assistStructure does not contain a viewNode that is credential.
-> Session will get destroyed and recreated with Autofill Provider being the primary provider.
-> Session returns autofill suggestions for the autofill screen.

This results in eventual availability but the user will have to jump between fields two times before it comes available. This will be addressed in the follow-up cl that will have each autofill provider maintain its own assist structure.

Test: atest CtsAttentionServiceDeviceTestCases, atest CtsAutoFillServiceTestCases:android.autofillservice.cts.inline.InlineLoginMixedCredentialActivityTest
Bug: 307413215
Change-Id: Ib9eb48ff6729d6c14960f4995d794849f02d9d35
parent d7e3b0dd
Loading
Loading
Loading
Loading
+16 −5
Original line number Diff line number Diff line
@@ -127,6 +127,12 @@ public final class FillRequest implements Parcelable {
     */
    public static final @RequestFlags int FLAG_SCREEN_HAS_CREDMAN_FIELD = 0x400;

    /**
     * Indicate whether the user has focused on a credman field view.
     * @hide
     */
    public static final @RequestFlags int FLAG_VIEW_REQUESTS_CREDMAN_SERVICE = 0x800;

    /** @hide */
    public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;

@@ -241,7 +247,8 @@ public final class FillRequest implements Parcelable {
        FLAG_IME_SHOWING,
        FLAG_RESET_FILL_DIALOG_STATE,
        FLAG_PCC_DETECTION,
        FLAG_SCREEN_HAS_CREDMAN_FIELD
        FLAG_SCREEN_HAS_CREDMAN_FIELD,
        FLAG_VIEW_REQUESTS_CREDMAN_SERVICE
    })
    @Retention(RetentionPolicy.SOURCE)
    @DataClass.Generated.Member
@@ -275,6 +282,8 @@ public final class FillRequest implements Parcelable {
                    return "FLAG_PCC_DETECTION";
            case FLAG_SCREEN_HAS_CREDMAN_FIELD:
                    return "FLAG_SCREEN_HAS_CREDMAN_FIELD";
            case FLAG_VIEW_REQUESTS_CREDMAN_SERVICE:
                    return "FLAG_VIEW_REQUESTS_CREDMAN_SERVICE";
            default: return Integer.toHexString(value);
        }
    }
@@ -368,7 +377,8 @@ public final class FillRequest implements Parcelable {
                        | FLAG_IME_SHOWING
                        | FLAG_RESET_FILL_DIALOG_STATE
                        | FLAG_PCC_DETECTION
                        | FLAG_SCREEN_HAS_CREDMAN_FIELD);
                        | FLAG_SCREEN_HAS_CREDMAN_FIELD
                        | FLAG_VIEW_REQUESTS_CREDMAN_SERVICE);
        this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
        this.mDelayedFillIntentSender = delayedFillIntentSender;

@@ -555,7 +565,8 @@ public final class FillRequest implements Parcelable {
                        | FLAG_IME_SHOWING
                        | FLAG_RESET_FILL_DIALOG_STATE
                        | FLAG_PCC_DETECTION
                        | FLAG_SCREEN_HAS_CREDMAN_FIELD);
                        | FLAG_SCREEN_HAS_CREDMAN_FIELD
                        | FLAG_VIEW_REQUESTS_CREDMAN_SERVICE);
        this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
        this.mDelayedFillIntentSender = delayedFillIntentSender;

@@ -577,10 +588,10 @@ public final class FillRequest implements Parcelable {
    };

    @DataClass.Generated(
            time = 1682097266850L,
            time = 1701010178309L,
            codegenVersion = "1.0.23",
            sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java",
            inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SCREEN_HAS_CREDMAN_FIELD\npublic static final  int INVALID_REQUEST_ID\nprivate final  int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mHints\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate  void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
            inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SCREEN_HAS_CREDMAN_FIELD\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_REQUESTS_CREDMAN_SERVICE\npublic static final  int INVALID_REQUEST_ID\nprivate final  int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mHints\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate  void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
    @Deprecated
    private void __metadata() {}

+11 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.service.autofill.FillRequest.FLAG_RESET_FILL_DIALOG_STATE;
import static android.service.autofill.FillRequest.FLAG_SCREEN_HAS_CREDMAN_FIELD;
import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG;
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
import static android.view.ContentInfo.SOURCE_AUTOFILL;
import static android.view.autofill.Helper.sDebug;
import static android.view.autofill.Helper.sVerbose;
@@ -61,7 +62,6 @@ import android.os.SystemClock;
import android.service.autofill.AutofillService;
import android.service.autofill.FillEventHistory;
import android.service.autofill.Flags;
import android.service.autofill.IFillCallback;
import android.service.autofill.UserData;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -729,6 +729,9 @@ public final class AutofillManager {
    // focus due to autofill showing biometric activity, password manager, or password breach check.
    private boolean mRelayoutFix;

    // Indicates whether the credman integration is enabled.
    private final boolean mIsCredmanIntegrationEnabled;

    // Indicates whether called the showAutofillDialog() method.
    private boolean mShowAutofillDialogCalled = false;

@@ -952,6 +955,7 @@ public final class AutofillManager {
                AutofillFeatureFlags.shouldAlwaysIncludeWebviewInAssistStructure();

        mRelayoutFix = Flags.relayout();
        mIsCredmanIntegrationEnabled = Flags.autofillCredmanIntegration();
    }

    /**
@@ -1804,7 +1808,9 @@ public final class AutofillManager {
            }
            return mCallback;
        }

        if (mIsCredmanIntegrationEnabled && isCredmanRequested(view)) {
            flags |= FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
        }
        mIsFillRequested.set(true);

        // don't notify entered when Activity is already in background
@@ -3384,6 +3390,9 @@ public final class AutofillManager {
    }

    private boolean isCredmanRequested(View view) {
        if (view == null) {
            return false;
        }
        if (view.isCredential()) {
            return true;
        }
+3 −14
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ package com.android.server.autofill;
import static android.service.autofill.FillEventHistory.Event.NO_SAVE_UI_REASON_NONE;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.FLAG_SCREEN_HAS_CREDMAN_FIELD;
import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.FLAG_ADD_CLIENT_ENABLED;
import static android.view.autofill.AutofillManager.FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
@@ -104,10 +104,6 @@ final class AutofillManagerServiceImpl
        extends AbstractPerUserSystemService<AutofillManagerServiceImpl, AutofillManagerService> {

    private static final String TAG = "AutofillManagerServiceImpl";

    private static final ComponentName CREDMAN_SERVICE_COMPONENT_NAME =
            new ComponentName("com.android.credentialmanager",
                    "com.android.credentialmanager.autofill.CredentialAutofillService");
    private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;

    /** Minimum interval to prune abandoned sessions */
@@ -536,22 +532,15 @@ final class AutofillManagerServiceImpl
                || mSessions.indexOfKey(sessionId) >= 0);

        assertCallerLocked(clientActivity, compatMode);

        ComponentName serviceComponentName = mInfo == null ? null
                : mInfo.getServiceInfo().getComponentName();

        if (isAutofillCredmanIntegrationEnabled()
                && ((flags & FLAG_SCREEN_HAS_CREDMAN_FIELD) != 0)) {
            // Hardcode to credential manager proxy service
            Slog.i(TAG, "Routing to CredentialAutofillService");
            serviceComponentName = CREDMAN_SERVICE_COMPONENT_NAME;
        }
        boolean isPrimaryCredential = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;

        final Session newSession = new Session(this, mUi, getContext(), mHandler, mUserId, mLock,
                sessionId, taskId, clientUid, clientActivityToken, clientCallback, hasCallback,
                mUiLatencyHistory, mWtfHistory, serviceComponentName,
                clientActivity, compatMode, bindInstantServiceAllowed, forAugmentedAutofillOnly,
                flags, mInputMethodManagerInternal);
                flags, mInputMethodManagerInternal, isPrimaryCredential);
        mSessions.put(newSession.id, newSession);

        return newSession;
+121 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.autofill;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentSender;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.util.Slog;

import java.util.Objects;


/**
 * Requests autofill response from a Remote Autofill Service. This autofill service can be
 * either a Credential Autofill Service or the user-opted autofill service.
 *
 * <p> With the credman integration, Autofill Framework handles two types of autofill flows -
 * regular autofill flow and the credman integrated autofill flow. With the credman integrated
 * autofill, the data source for the autofill is handled by the credential autofill proxy
 * service, which is hidden from users. By the time a session gets created, the framework
 * decides on one of the two flows by setting the remote fill service to be either the
 * user-elected autofill service or the hidden credential autofill service by looking at the
 * user-focused view's credential attribute. If the user needs both flows concurrently because
 * the screen has both regular autofill fields and credential fields, then secondary provider
 * handler will be used to fetch supplementary fill response. Depending on which remote fill
 * service the session was initially created with, the secondary provider handler will contain
 * the remaining autofill service. </p>
 *
 * @hide
 */
final class SecondaryProviderHandler implements RemoteFillService.FillServiceCallbacks {
    private static final String TAG = "SecondaryProviderHandler";

    private final RemoteFillService mRemoteFillService;
    private final SecondaryProviderCallback mCallback;
    private FillRequest mLastFillRequest;
    private int mLastFlag;

    SecondaryProviderHandler(
            @NonNull Context context, int userId, boolean bindInstantServiceAllowed,
            SecondaryProviderCallback callback, ComponentName componentName) {
        mRemoteFillService = new RemoteFillService(context, componentName, userId, this,
                bindInstantServiceAllowed);
        mCallback = callback;
        Slog.v(TAG, "Creating a secondary provider handler with component name, " + componentName);
    }
    @Override
    public void onServiceDied(RemoteFillService service) {
        mRemoteFillService.destroy();
    }

    @Override
    public void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
                                     @NonNull String servicePackageName, int requestFlags) {
        Slog.v(TAG, "Received a fill response: " + response);
        mCallback.onSecondaryFillResponse(response, mLastFlag);
    }

    @Override
    public void onFillRequestFailure(int requestId, @Nullable CharSequence message) {

    }

    @Override
    public void onFillRequestTimeout(int requestId) {

    }

    @Override
    public void onSaveRequestSuccess(@NonNull String servicePackageName,
                                     @Nullable IntentSender intentSender) {

    }

    @Override
    public void onSaveRequestFailure(@Nullable CharSequence message,
                                     @NonNull String servicePackageName) {

    }

    /**
     * Requests a new fill response. If the fill request is same as the last requested fill request,
     * then the request is duped.
     */
    public void onFillRequest(FillRequest pendingFillRequest, int flag) {
        if (Objects.equals(pendingFillRequest, mLastFillRequest)) {
            Slog.v(TAG, "Deduping fill request to secondary provider.");
            return;
        }
        Slog.v(TAG, "Requesting fill response to secondary provider.");
        mLastFlag = flag;
        mLastFillRequest = pendingFillRequest;
        mRemoteFillService.onFillRequest(pendingFillRequest);
    }

    public void destroy() {
        mRemoteFillService.destroy();
    }

    interface SecondaryProviderCallback {
        void onSecondaryFillResponse(@Nullable FillResponse fillResponse, int flags);
    }
}
+105 −19

File changed.

Preview size limit exceeded, changes collapsed.

Loading