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

Commit 8bba1636 authored by Reema Bajwa's avatar Reema Bajwa
Browse files

Add support for virtual views

Bug: 326314286
Test: Cts + local deployment

Change-Id: I918b10f9647ad488b2654f4d898efca71af4e653
parent b5cfbf4f
Loading
Loading
Loading
Loading
+64 −3
Original line number Diff line number Diff line
package android.app.assist;

import static android.credentials.Constants.FAILURE_CREDMAN_SELECTOR;
import static android.credentials.Constants.SUCCESS_CREDMAN_SELECTOR;
import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION;

import android.annotation.FlaggedApi;
@@ -20,14 +22,17 @@ import android.net.Uri;
import android.os.BadParcelableException;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.Looper;
import android.os.OutcomeReceiver;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PooledStringReader;
import android.os.PooledStringWriter;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.service.autofill.FillRequest;
import android.service.credentials.CredentialProviderService;
@@ -37,6 +42,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.view.View;
import android.view.View.AutofillImportance;
import android.view.ViewRootImpl;
@@ -652,6 +658,9 @@ public class AssistStructure implements Parcelable {
        @Nullable OutcomeReceiver<GetCredentialResponse, GetCredentialException>
                mGetCredentialCallback;

        @Nullable ResultReceiver mGetCredentialResultReceiver;


        AutofillValue mAutofillValue;
        CharSequence[] mAutofillOptions;
        boolean mSanitized;
@@ -916,6 +925,7 @@ public class AssistStructure implements Parcelable {
                mExtras = in.readBundle();
            }
            mGetCredentialRequest = in.readTypedObject(GetCredentialRequest.CREATOR);
            mGetCredentialResultReceiver = in.readTypedObject(ResultReceiver.CREATOR);
        }

        /**
@@ -1153,6 +1163,7 @@ public class AssistStructure implements Parcelable {
                out.writeBundle(mExtras);
            }
            out.writeTypedObject(mGetCredentialRequest, flags);
            out.writeTypedObject(mGetCredentialResultReceiver, flags);
            return flags;
        }

@@ -1295,9 +1306,8 @@ public class AssistStructure implements Parcelable {
         */
        @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
        @Nullable
        public OutcomeReceiver<GetCredentialResponse,
                GetCredentialException> getCredentialManagerCallback() {
            return mGetCredentialCallback;
        public ResultReceiver getCredentialManagerCallback() {
            return mGetCredentialResultReceiver;
        }

        /**
@@ -1894,6 +1904,7 @@ public class AssistStructure implements Parcelable {
        final AssistStructure mAssist;
        final ViewNode mNode;
        final boolean mAsync;
        private Handler mHandler;

        /**
         * Used to instantiate a builder for a stand-alone {@link ViewNode} which is not associated
@@ -2271,6 +2282,56 @@ public class AssistStructure implements Parcelable {
                option.getCandidateQueryData()
                        .putParcelableArrayList(CredentialProviderService.EXTRA_AUTOFILL_ID, ids);
            }
            setUpResultReceiver(callback);
        }

        private void setUpResultReceiver(
                OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {

            if (mHandler == null) {
                mHandler = new Handler(Looper.getMainLooper(), null, true);
            }
            final ResultReceiver resultReceiver = new ResultReceiver(mHandler) {
                @Override
                protected void onReceiveResult(int resultCode, Bundle resultData) {
                    if (resultCode == SUCCESS_CREDMAN_SELECTOR) {
                        Slog.d(TAG, "onReceiveResult from Credential Manager");
                        GetCredentialResponse getCredentialResponse =
                                resultData.getParcelable(
                                        CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
                                        GetCredentialResponse.class);

                        callback.onResult(getCredentialResponse);
                    } else if (resultCode == FAILURE_CREDMAN_SELECTOR) {
                        String[] exception =  resultData.getStringArray(
                                CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION);
                        if (exception != null && exception.length >= 2) {
                            Slog.w(TAG, "Credman bottom sheet from pinned "
                                    + "entry failed with: + " + exception[0] + " , "
                                    + exception[1]);
                            callback.onError(new GetCredentialException(
                                    exception[0], exception[1]));
                        }
                    } else {
                        Slog.d(TAG, "Unknown resultCode from credential "
                                + "manager bottom sheet: " + resultCode);
                    }
                }
            };
            ResultReceiver ipcFriendlyResultReceiver =
                    toIpcFriendlyResultReceiver(resultReceiver);
            mNode.mGetCredentialResultReceiver = ipcFriendlyResultReceiver;
        }

        private ResultReceiver toIpcFriendlyResultReceiver(ResultReceiver resultReceiver) {
            final Parcel parcel = Parcel.obtain();
            resultReceiver.writeToParcel(parcel, 0);
            parcel.setDataPosition(0);

            final ResultReceiver ipcFriendly = ResultReceiver.CREATOR.createFromParcel(parcel);
            parcel.recycle();

            return ipcFriendly;
        }

        @Override
+2 −1
Original line number Diff line number Diff line
@@ -315,7 +315,8 @@ public abstract class ViewStructure {
    /**
     * Add to this view's child count.  This increases the current child count by
     * <var>num</var> children beyond what was last set by {@link #setChildCount}
     * or {@link #addChildCount}.  The index at which the new child starts in the child
     * or {@link #addChildCount}.  The index at which the new
     * child starts in the child
     * array is returned.
     *
     * @param num The number of new children to add.
+0 −42
Original line number Diff line number Diff line
@@ -21,8 +21,6 @@ import android.app.assist.AssistStructure
import android.content.Context
import android.credentials.CredentialManager
import android.credentials.GetCredentialRequest
import android.credentials.GetCredentialResponse
import android.credentials.GetCredentialException
import android.credentials.GetCandidateCredentialsResponse
import android.credentials.GetCandidateCredentialsException
import android.credentials.CredentialOption
@@ -33,7 +31,6 @@ import android.graphics.drawable.Icon
import android.os.Bundle
import android.os.CancellationSignal
import android.os.OutcomeReceiver
import android.provider.Settings
import android.service.autofill.AutofillService
import android.service.autofill.Dataset
import android.service.autofill.Field
@@ -124,13 +121,10 @@ class CredentialAutofillService : AutofillService() {
        // TODO(b/324635774): Use callback for validating. If the request is coming
        // directly from the view, there should be a corresponding callback, otherwise
        // we should fail fast,
        val getCredCallback = getCredManCallback(structure)
        if (getCredRequest == null) {
            Log.i(TAG, "No credential manager request found")
            callback.onFailure("No credential manager request found")
            return
        } else if (getCredCallback == null) {
            Log.i(TAG, "No credential manager callback found")
        }
        val credentialManager: CredentialManager =
                getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager
@@ -524,42 +518,6 @@ class CredentialAutofillService : AutofillService() {
        TODO("Not yet implemented")
    }

    private fun getCredManCallback(structure: AssistStructure): OutcomeReceiver<
            GetCredentialResponse, GetCredentialException>? {
        return traverseStructureForCallback(structure)
    }

    private fun traverseStructureForCallback(
            structure: AssistStructure
    ): OutcomeReceiver<GetCredentialResponse, GetCredentialException>? {
        val windowNodes: List<AssistStructure.WindowNode> =
                structure.run {
                    (0 until windowNodeCount).map { getWindowNodeAt(it) }
                }

        windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
            return traverseNodeForCallback(windowNode.rootViewNode)
        }
        return null
    }

    private fun traverseNodeForCallback(
            viewNode: AssistStructure.ViewNode
    ): OutcomeReceiver<GetCredentialResponse, GetCredentialException>? {
        val children: List<AssistStructure.ViewNode> =
                viewNode.run {
                    (0 until childCount).map { getChildAt(it) }
                }

        children.forEach { childNode: AssistStructure.ViewNode ->
            if (childNode.isFocused() && childNode.credentialManagerCallback != null) {
                return childNode.credentialManagerCallback
            }
            return traverseNodeForCallback(childNode)
        }
        return null
    }

    private fun getCredManRequest(
            structure: AssistStructure,
            sessionId: Int,
+33 −1
Original line number Diff line number Diff line
@@ -3930,6 +3930,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        return mSessionFlags.mShowingSaveUi;
    }

    /**
     * Gets the latest non-empty value for the given id in the autofill contexts.
     */
    @GuardedBy("mLock")
    @Nullable
    private ViewNode getViewNodeFromContextsLocked(@NonNull AutofillId autofillId) {
        final int numContexts = mContexts.size();
        for (int i = numContexts - 1; i >= 0; i--) {
            final FillContext context = mContexts.get(i);
            final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(),
                    autofillId);
            if (node != null) {
                return node;
            }
        }
        return null;
    }

    /**
     * Gets the latest non-empty value for the given id in the autofill contexts.
     */
@@ -6419,7 +6437,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                    mClient.onGetCredentialException(id, viewId, exception.getType(),
                            exception.getMessage());
                } else if (response != null) {
                    if (viewId.isVirtualInt()) {
                        ViewNode viewNode = getViewNodeFromContextsLocked(viewId);
                        if (viewNode != null && viewNode.getCredentialManagerCallback() != null) {
                            Bundle resultData = new Bundle();
                            resultData.putParcelable(
                                    CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
                                    response);
                            viewNode.getCredentialManagerCallback().send(SUCCESS_CREDMAN_SELECTOR,
                                        resultData);
                        } else {
                            Slog.w(TAG, "View node not found after GetCredentialResponse");
                        }
                    } else {
                        mClient.onGetCredentialResponse(id, viewId, response);
                    }
                } else {
                    Slog.w(TAG, "sendCredentialManagerResponseToApp called with null response"
                            + "and exception");