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

Commit 847959fc authored by Reema Bajwa's avatar Reema Bajwa
Browse files

Expose credential manager APIs in View classes

For apps built on tradional View based frameworks, developers
will be expected to set GetCredentialRequest on given views.
For apps built on other frameworks like webview & compose, developers
will set high level requests on View counterparts in that framework, and
then the framework implementation will provide a virtual viewstrucuture to
the autofill manager, including setting the request on virtual view nodes.

Design doc: https://docs.google.com/document/d/16pQ34SsxctR4naB36MsL3gyBfMKbg7luJVWhZrzsQZU/edit?resourcekey=0-0pX-n_b-y9onKdlipfySPw&tab=t.0#bookmark=id.5tn70i6wnp7j
Compose doc: https://docs.google.com/document/d/1fQJiIswR-mG94FIgYhRBG93F-lQ4nh3Xw_88ykT-kjw/edit?usp=sharing

Test: Cts
Bug: 318846139

Change-Id: I55b2a1468fdf3d8e6e810e28b22ea587d03787b2
parent 90b68205
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -52078,6 +52078,7 @@ package android.view {
    method public final void cancelPendingInputEvents();
    method public boolean checkInputConnectionProxy(android.view.View);
    method public void clearAnimation();
    method @FlaggedApi("autofill_credman_dev_integration") public void clearCredentialManagerRequest();
    method public void clearFocus();
    method public void clearViewTranslationCallback();
    method public static int combineMeasuredStates(int, int);
@@ -52186,6 +52187,8 @@ package android.view {
    method public CharSequence getContentDescription();
    method @UiContext public final android.content.Context getContext();
    method protected android.view.ContextMenu.ContextMenuInfo getContextMenuInfo();
    method @FlaggedApi("autofill_credman_dev_integration") @Nullable public final android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException> getCredentialManagerCallback();
    method @FlaggedApi("autofill_credman_dev_integration") @Nullable public final android.credentials.GetCredentialRequest getCredentialManagerRequest();
    method public final boolean getDefaultFocusHighlightEnabled();
    method public static int getDefaultSize(int, int);
    method public android.view.Display getDisplay();
@@ -52568,6 +52571,7 @@ package android.view {
    method public void setContentCaptureSession(@Nullable android.view.contentcapture.ContentCaptureSession);
    method public void setContentDescription(CharSequence);
    method public void setContextClickable(boolean);
    method @FlaggedApi("autofill_credman_dev_integration") public void setCredentialManagerRequest(@NonNull android.credentials.GetCredentialRequest, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
    method public void setDefaultFocusHighlightEnabled(boolean);
    method @Deprecated public void setDrawingCacheBackgroundColor(@ColorInt int);
    method @Deprecated public void setDrawingCacheEnabled(boolean);
@@ -53441,8 +53445,11 @@ package android.view {
    method public abstract int addChildCount(int);
    method public abstract void asyncCommit();
    method public abstract android.view.ViewStructure asyncNewChild(int);
    method @FlaggedApi("autofill_credman_dev_integration") public void clearCredentialManagerRequest();
    method @Nullable public abstract android.view.autofill.AutofillId getAutofillId();
    method public abstract int getChildCount();
    method @FlaggedApi("autofill_credman_dev_integration") @Nullable public android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException> getCredentialManagerCallback();
    method @FlaggedApi("autofill_credman_dev_integration") @Nullable public android.credentials.GetCredentialRequest getCredentialManagerRequest();
    method public abstract android.os.Bundle getExtras();
    method public abstract CharSequence getHint();
    method public abstract CharSequence getText();
@@ -53467,6 +53474,7 @@ package android.view {
    method public abstract void setClickable(boolean);
    method public abstract void setContentDescription(CharSequence);
    method public abstract void setContextClickable(boolean);
    method @FlaggedApi("autofill_credman_dev_integration") public void setCredentialManagerRequest(@NonNull android.credentials.GetCredentialRequest, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
    method public abstract void setDataIsSensitive(boolean);
    method public abstract void setDimens(int, int, int, int, int, int);
    method public abstract void setElevation(float);
+65 −0
Original line number Diff line number Diff line
package android.app.assist;

import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -7,6 +8,9 @@ import android.annotation.SystemApi;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.GetCredentialResponse;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.net.Uri;
@@ -15,6 +19,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.OutcomeReceiver;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PooledStringReader;
@@ -637,6 +642,12 @@ public class AssistStructure implements Parcelable {
        AutofillId mAutofillId;
        @View.AutofillType int mAutofillType = View.AUTOFILL_TYPE_NONE;
        @Nullable String[] mAutofillHints;

        @Nullable GetCredentialRequest mGetCredentialRequest;

        @Nullable OutcomeReceiver<GetCredentialResponse, GetCredentialException>
                mGetCredentialCallback;

        AutofillValue mAutofillValue;
        CharSequence[] mAutofillOptions;
        boolean mSanitized;
@@ -1261,6 +1272,32 @@ public class AssistStructure implements Parcelable {
            return mIsCredential;
        }

        /**
         * Returns the request associated with this node
         * @return
         *
         * @hide
         */
        @FlaggedApi("autofill_credman_dev_integration")
        @Nullable
        public GetCredentialRequest getCredentialManagerRequest() {
            return mGetCredentialRequest;
        }

        /**
         *
         * @return
         *
         * @hide
         *
         */
        @FlaggedApi("autofill_credman_dev_integration")
        @Nullable
        public OutcomeReceiver<GetCredentialResponse,
                GetCredentialException> getCredentialManagerCallback() {
            return mGetCredentialCallback;
        }

        /**
         * Gets the {@link android.text.InputType} bits of this structure.
         *
@@ -2139,6 +2176,19 @@ public class AssistStructure implements Parcelable {
            }
        }

        @Nullable
        @Override
        public GetCredentialRequest getCredentialManagerRequest() {
            return mNode.mGetCredentialRequest;
        }

        @Nullable
        @Override
        public OutcomeReceiver<
                GetCredentialResponse, GetCredentialException> getCredentialManagerCallback() {
            return mNode.mGetCredentialCallback;
        }

        @Override
        public void asyncCommit() {
            synchronized (mAssist) {
@@ -2203,6 +2253,13 @@ public class AssistStructure implements Parcelable {
            mNode.mIsCredential = isCredential;
        }

        @Override
        public void setCredentialManagerRequest(@NonNull GetCredentialRequest request,
                @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
            mNode.mGetCredentialRequest = request;
            mNode.mGetCredentialCallback = callback;
        }

        @Override
        public void setReceiveContentMimeTypes(@Nullable String[] mimeTypes) {
            mNode.mReceiveContentMimeTypes = mimeTypes;
@@ -2523,6 +2580,14 @@ public class AssistStructure implements Parcelable {
                    + ", isCredential=" + node.isCredential()
            );
        }
        GetCredentialRequest getCredentialRequest = node.getCredentialManagerRequest();
        if (getCredentialRequest == null) {
            Log.i(TAG, prefix + " NO Credential Manager Request");
        } else {
            Log.i(TAG, prefix + "  GetCredentialRequest: no. of options= "
                    + getCredentialRequest.getCredentialOptions().size()
            );
        }

        final int NCHILDREN = node.getChildCount();
        if (NCHILDREN > 0) {
+15 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.credentials.CredentialProviderService;
import android.view.autofill.AutofillId;

import com.android.internal.util.AnnotationValidations;

@@ -35,6 +37,7 @@ public final class GetCredentialResponse implements Parcelable {
    @NonNull
    private final Credential mCredential;


    /**
     * Returns the credential that can be used to authenticate the user, or {@code null} if no
     * credential is available.
@@ -59,6 +62,18 @@ public final class GetCredentialResponse implements Parcelable {
        return "GetCredentialResponse {" + "credential=" + mCredential + "}";
    }

    /**
     *
     * @return
     *
     * @hide
     */
    public AutofillId getAutofillId() {
        return mCredential.getData().getParcelable(
                CredentialProviderService.EXTRA_AUTOFILL_ID,
                AutofillId.class);
    }

    /**
     * Constructs a {@link GetCredentialResponse}.
     *
+121 −0
Original line number Diff line number Diff line
@@ -79,6 +79,10 @@ import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.credentials.CredentialManager;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.GetCredentialResponse;
import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.Canvas;
@@ -111,6 +115,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.OutcomeReceiver;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteCallback;
@@ -1034,6 +1039,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    private static String sTraceRequestLayoutClass;
    @Nullable
    private ViewCredentialHandler mViewCredentialHandler;
    /** Used to avoid computing the full strings each time when layout tracing is enabled. */
    @Nullable
    private ViewTraversalTracingStrings mTracingStrings;
@@ -6899,6 +6908,64 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        mScrollCache.fadingEdgeLength = length;
    }
    /**
     * Clears the request and callback previously set
     * through {@link View#setCredentialManagerRequest}.
     * Once this API is invoked, there will be no request fired to {@link CredentialManager}
     * on future view focus events.
     *
     * @see #setCredentialManagerRequest
     */
    @FlaggedApi("autofill_credman_dev_integration")
    public void clearCredentialManagerRequest() {
        if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
            Log.v(AUTOFILL_LOG_TAG, "clearCredentialManagerRequest called");
        }
        mViewCredentialHandler = null;
    }
    /**
     * Sets a {@link CredentialManager} request to retrieve credentials, when the user focuses
     * on this given view.
     *
     * When this view is focused, the given {@code request} will be fired to
     * {@link CredentialManager}, which will fetch content from all
     * {@link android.service.credentials.CredentialProviderService} services on the
     * device, and then display credential options to the user on a relevant UI
     * (dropdown, keyboard suggestions etc.).
     *
     * When the user selects a credential, the final {@link GetCredentialResponse} will be
     * propagated to the given {@code callback}. Developers are expected to handle the response
     * programmatically and perform a relevant action, e.g. signing in the user.
     *
     * <p> For details on how to  build a Credential Manager request, please see
     * {@link GetCredentialRequest}.
     *
     * <p> This API should be called at any point before the user focuses on the view, e.g. during
     * {@code onCreate} of an Activity.
     *
     * @param request the request to be fired when this view is entered
     * @param callback to be invoked when either a response or an exception needs to be
     *                 propagated for the given view
     */
    @FlaggedApi("autofill_credman_dev_integration")
    public void setCredentialManagerRequest(@NonNull GetCredentialRequest request,
            @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
        Preconditions.checkNotNull(request, "request must not be null");
        Preconditions.checkNotNull(callback, "request must not be null");
        mViewCredentialHandler = new ViewCredentialHandler(request, callback);
    }
    /**
     *
     * @hide
     */
    @Nullable
    public ViewCredentialHandler getViewCredentialHandler() {
        return mViewCredentialHandler;
    }
    /**
     * Returns the size of the horizontal faded edges used to indicate that more
     * content in this view is visible.
@@ -9364,6 +9431,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                structure.setAutofillValue(getAutofillValue());
                structure.setIsCredential(isCredential());
            }
            if (getViewCredentialHandler() != null) {
                structure.setCredentialManagerRequest(
                        getViewCredentialHandler().getRequest(),
                        getViewCredentialHandler().getCallback());
            }
            structure.setImportantForAutofill(getImportantForAutofill());
            structure.setReceiveContentMimeTypes(getReceiveContentMimeTypes());
        }
@@ -9780,6 +9852,53 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return mAutofillId;
    }
    /**
     * Returns the {@link GetCredentialRequest} associated with the view.
     * If the return value is null, that means no request has been set
     * on the view and no {@link CredentialManager} flow will be invoked
     * when this view is focused. Traditioanl autofill flows will still
     * work, autofilling content if applicable, from
     * the active {@link android.service.autofill.AutofillService} on
     * the device.
     *
     * <p>See {@link #setCredentialManagerRequest} for more info.
     *
     * @return The credential request associated with this View.
     */
    @FlaggedApi("autofill_credman_dev_integration")
    @Nullable
    public final GetCredentialRequest getCredentialManagerRequest() {
        if (mViewCredentialHandler == null) {
            return null;
        }
        return mViewCredentialHandler.getRequest();
    }
    /**
     * Returns the callback that has previously been set up on this view through
     * the {@link #setCredentialManagerRequest} API.
     * If the return value is null, that means no callback, or request, has been set
     * on the view and no {@link CredentialManager} flow will be invoked
     * when this view is focused. Traditioanl autofill flows will still
     * work, and autofillable content will still be returned through the
     * {@link #autofill(AutofillValue)} )} API.
     *
     * <p>See {@link #setCredentialManagerRequest} for more info.
     *
     * @return The callback associated with this view that will be invoked on a response from
     * {@link CredentialManager} .
     */
    @FlaggedApi("autofill_credman_dev_integration")
    @Nullable
    public final OutcomeReceiver<GetCredentialResponse,
            GetCredentialException> getCredentialManagerCallback() {
        if (mViewCredentialHandler == null) {
            return null;
        }
        return mViewCredentialHandler.getCallback();
    }
    /**
     * Sets the unique, logical identifier of this view in the activity, for autofill purposes.
     *
@@ -10618,6 +10737,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            structure.setAutofillId(new AutofillId(getAutofillId(),
                    AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId())));
        }
        structure.setCredentialManagerRequest(getCredentialManagerRequest(),
                getCredentialManagerCallback());
        CharSequence cname = info.getClassName();
        structure.setClassName(cname != null ? cname.toString() : null);
        structure.setContentDescription(info.getContentDescription());
+46 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 android.view;

import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.GetCredentialResponse;
import android.os.OutcomeReceiver;

/**
 * @hide
 */
public class ViewCredentialHandler {
    private GetCredentialRequest mRequest;

    private OutcomeReceiver<GetCredentialResponse, GetCredentialException> mCallback;

    ViewCredentialHandler(GetCredentialRequest request,
            OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
        mRequest = request;
        mCallback = callback;
    }

    public GetCredentialRequest getRequest() {
        return mRequest;
    }

    public OutcomeReceiver<GetCredentialResponse,
            GetCredentialException> getCallback() {
        return mCallback;
    }
}
Loading