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

Commit cc93a450 authored by Reema Bajwa's avatar Reema Bajwa
Browse files

Fix more options crash

Before this fix, the pending intent was created in the Systenm
Server and a fillInIntent was created in the CredentialAutofillService.
This intent carried icons which could be of type bitmap too. This caused
a crash when invoking startIntentSender from the AutofillManager
because it is not possible to crete an intent with a bitmap in one process
and invoke it from another (cannot write file descriptor).
With this fix, the System Server only passes an intent with the request
Info. A pending intent per autofillId is genrated in CredentialAutofillService
and the underlying intent with which the pending intent is created contains
the icons.

Test: Cts
Bug: 325008438

Change-Id: Ic46984fed6cea0fd2e4af69e5d6dee82cdfc1d20
parent 2cd1ebbd
Loading
Loading
Loading
Loading
+11 −21
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ package android.credentials;

import android.annotation.Hide;
import android.annotation.NonNull;
import android.app.PendingIntent;
import android.content.Intent;
import android.credentials.selection.GetCredentialProviderData;
import android.os.Parcel;
import android.os.Parcelable;
@@ -39,32 +39,22 @@ public final class GetCandidateCredentialsResponse implements Parcelable {
    @NonNull
    private final List<GetCredentialProviderData> mCandidateProviderDataList;

    private final PendingIntent mPendingIntent;

    /**
     * @hide
     */
    @Hide
    public GetCandidateCredentialsResponse(
            GetCredentialResponse getCredentialResponse
    ) {
        mCandidateProviderDataList = null;
        mPendingIntent = null;
    }
    @NonNull
    private final Intent mIntent;

    /**
     * @hide
     */
    @Hide
    public GetCandidateCredentialsResponse(
            List<GetCredentialProviderData> candidateProviderDataList,
            PendingIntent pendingIntent
            @NonNull List<GetCredentialProviderData> candidateProviderDataList,
            @NonNull Intent intent
    ) {
        Preconditions.checkCollectionNotEmpty(
                candidateProviderDataList,
                /*valueName=*/ "candidateProviderDataList");
        mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList);
        mPendingIntent = pendingIntent;
        mIntent = intent;
    }

    /**
@@ -81,8 +71,9 @@ public final class GetCandidateCredentialsResponse implements Parcelable {
     *
     * @hide
     */
    public PendingIntent getPendingIntent() {
        return mPendingIntent;
    @NonNull
    public Intent getIntent() {
        return mIntent;
    }

    protected GetCandidateCredentialsResponse(Parcel in) {
@@ -91,14 +82,13 @@ public final class GetCandidateCredentialsResponse implements Parcelable {
        mCandidateProviderDataList = candidateProviderDataList;

        AnnotationValidations.validate(NonNull.class, null, mCandidateProviderDataList);

        mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
        mIntent = in.readTypedObject(Intent.CREATOR);
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeTypedList(mCandidateProviderDataList);
        dest.writeTypedObject(mPendingIntent, flags);
        dest.writeTypedObject(mIntent, flags);
    }

    @Override
+26 −19
Original line number Diff line number Diff line
@@ -211,7 +211,7 @@ class CredentialAutofillService : AutofillService() {
        autofillIdToProvidersMap.forEach { (autofillId, providers) ->
            validFillResponse = processProvidersForAutofillId(
                    filLRequest, autofillId, providers, entryIconMap, fillResponseBuilder,
                    getCredResponse.pendingIntent)
                    getCredResponse.intent)
                    .or(validFillResponse)
        }
        if (!validFillResponse) {
@@ -227,7 +227,7 @@ class CredentialAutofillService : AutofillService() {
            providerDataList: ArrayList<GetCredentialProviderData>,
            entryIconMap: Map<String, Icon>,
            fillResponseBuilder: FillResponse.Builder,
            bottomSheetPendingIntent: PendingIntent?
            bottomSheetIntent: Intent
    ): Boolean {
        val providerList = GetFlowUtils.toProviderList(
            providerDataList,
@@ -265,6 +265,8 @@ class CredentialAutofillService : AutofillService() {
                }
            }
        }
        bottomSheetIntent.putExtra(
                ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, providerDataList)
        providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@{
            val primaryEntry = it.sortedCredentialEntryList.first()
            val pendingIntent = primaryEntry.pendingIntent
@@ -324,16 +326,16 @@ class CredentialAutofillService : AutofillService() {
            datasetAdded = true
            i++

            if (i == lastDropdownDatasetIndex && bottomSheetPendingIntent != null) {
                addDropdownMoreOptionsPresentation(bottomSheetPendingIntent, autofillId,
            if (i == lastDropdownDatasetIndex) {
                addDropdownMoreOptionsPresentation(bottomSheetIntent, autofillId,
                        fillResponseBuilder)
            }
        }
        val pinnedSpec = getLastInlinePresentationSpec(inlinePresentationSpecs,
                inlinePresentationSpecsCount)
        if (datasetAdded && bottomSheetPendingIntent != null && pinnedSpec != null) {
            addPinnedInlineSuggestion(bottomSheetPendingIntent, pinnedSpec, autofillId,
                    fillResponseBuilder, providerDataList)
        if (datasetAdded && pinnedSpec != null) {
            addPinnedInlineSuggestion(pinnedSpec, autofillId,
                    fillResponseBuilder, bottomSheetIntent)
        }
        return datasetAdded
    }
@@ -364,13 +366,14 @@ class CredentialAutofillService : AutofillService() {
    }

    private fun addDropdownMoreOptionsPresentation(
            bottomSheetPendingIntent: PendingIntent,
            bottomSheetIntent: Intent,
            autofillId: AutofillId,
            fillResponseBuilder: FillResponse.Builder
    ) {
        val presentationBuilder = Presentations.Builder()
                .setMenuPresentation(
                        RemoteViewsFactory.createMoreSignInOptionsPresentation(this))
        val pendingIntent = setUpBottomSheetPendingIntent(bottomSheetIntent)

        fillResponseBuilder.addDataset(
                Dataset.Builder()
@@ -379,7 +382,7 @@ class CredentialAutofillService : AutofillService() {
                                Field.Builder().setPresentations(
                                        presentationBuilder.build())
                                        .build())
                        .setAuthentication(bottomSheetPendingIntent.intentSender)
                        .setAuthentication(pendingIntent.intentSender)
                        .build()
        )
    }
@@ -395,37 +398,41 @@ class CredentialAutofillService : AutofillService() {
    }

    private fun addPinnedInlineSuggestion(
            bottomSheetPendingIntent: PendingIntent,
            spec: InlinePresentationSpec,
            autofillId: AutofillId,
            fillResponseBuilder: FillResponse.Builder,
            providerDataList: ArrayList<GetCredentialProviderData>
            bottomSheetIntent: Intent
    ) {
        val pendingIntent = setUpBottomSheetPendingIntent(bottomSheetIntent)

        val dataSetBuilder = Dataset.Builder()
        val sliceBuilder = InlineSuggestionUi
                .newContentBuilder(bottomSheetPendingIntent)
                .newContentBuilder(pendingIntent)
                .setStartIcon(Icon.createWithResource(this,
                        com.android.credentialmanager.R.drawable.more_horiz_24px))
        val presentationBuilder = Presentations.Builder()
                .setInlinePresentation(InlinePresentation(
                        sliceBuilder.build().slice, spec, /* pinned= */ true))

        val extrasIntent = Intent()
        extrasIntent.putExtra(ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, providerDataList)

        fillResponseBuilder.addDataset(
                dataSetBuilder
                        .setField(
                                autofillId,
                                Field.Builder().setPresentations(
                                        presentationBuilder.build())
                                        .build())
                        .setAuthentication(bottomSheetPendingIntent.intentSender)
                        .setCredentialFillInIntent(extrasIntent)
                                        presentationBuilder.build()
                                ).build()
                        )
                        .setAuthentication(pendingIntent.intentSender)
                        .build()
        )
    }

    private fun setUpBottomSheetPendingIntent(intent: Intent): PendingIntent {
        intent.setAction(java.util.UUID.randomUUID().toString())
        return PendingIntent.getActivity(this, /*requestCode=*/0, intent,
                PendingIntent.FLAG_MUTABLE, /*options=*/null)
    }

    /**
     *  Maps Autofill Id to provider list. For example, passing in a provider info
     *
+7 −3
Original line number Diff line number Diff line
@@ -193,7 +193,6 @@ import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;

import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
@@ -2821,8 +2820,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        mSessionFlags.mExpiredResponse = false;

        final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
        final Serializable exception = data.getSerializable(
                CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION);
        final GetCredentialException exception = data.getSerializable(
                CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
                GetCredentialException.class);

        final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
        if (sDebug) {
@@ -5126,6 +5126,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                toIpcFriendlyResultReceiver(resultReceiver);

        Intent metadataIntent = dataset.getCredentialFillInIntent();
        if (metadataIntent == null) {
            metadataIntent = new Intent();
        }

        metadataIntent.putExtra(
                android.credentials.selection.Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
                ipcFriendlyResultReceiver);
+20 −30
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.server.credentials;
import static android.credentials.selection.Constants.EXTRA_FINAL_RESPONSE_RECEIVER;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -161,26 +160,6 @@ public class CredentialManagerUi {
     */
    public PendingIntent createPendingIntent(
            RequestInfo requestInfo, ArrayList<ProviderData> providerDataList) {
        return createPendingIntent(requestInfo, providerDataList, /*forAutofill=*/ false);
    }

    /**
     * Creates a {@link PendingIntent} to be used to invoke the credential manager selector UI,
     * by the calling app process. This intent is invoked from the Autofill flow, when the user
     * requests to bring up the 'All Options' page of the credential bottom-sheet. When the user
     * clicks on the pinned entry, the intent will bring up the 'All Options' page of the
     * bottom-sheet. The provider data list is processed by the credential autofill service for
     * each autofill id and passed in as an auth extra.
     *
     * @param requestInfo            the information about the request
     */
    public PendingIntent createPendingIntentForAutofill(RequestInfo requestInfo) {
        return createPendingIntent(requestInfo, /*providerDataList=*/ null, /*forAutofill=*/ true);
    }

    private PendingIntent createPendingIntent(
            RequestInfo requestInfo, @Nullable ArrayList<ProviderData> providerDataList,
            boolean forAutofill) {
        List<CredentialProviderInfo> allProviders =
                CredentialProviderInfoFactory.getCredentialProviderServices(
                        mContext,
@@ -196,15 +175,9 @@ public class CredentialManagerUi {
                        disabledProvider.getComponentName().flattenToString())).toList();

        Intent intent;
        if (forAutofill) {
            intent = IntentFactory.createCredentialSelectorIntentForAutofill(
                    mContext, requestInfo, new ArrayList<>(disabledProviderDataList),
                    mResultReceiver);
        } else {
        intent = IntentFactory.createCredentialSelectorIntent(
                mContext, requestInfo, providerDataList,
                new ArrayList<>(disabledProviderDataList), mResultReceiver);
        }
        intent.setAction(UUID.randomUUID().toString());
        //TODO: Create unique pending intent using request code and cancel any pre-existing pending
        // intents
@@ -213,4 +186,21 @@ public class CredentialManagerUi {
                PendingIntent.FLAG_MUTABLE, /*options=*/null,
                UserHandle.of(mUserId));
    }

    /**
     * Creates an {@link Intent} to be used to invoke the credential manager selector UI,
     * by the calling app process. This intent is invoked from the Autofill flow, when the user
     * requests to bring up the 'All Options' page of the credential bottom-sheet. When the user
     * clicks on the pinned entry, the intent will bring up the 'All Options' page of the
     * bottom-sheet. The provider data list is processed by the credential autofill service for
     * each autofill id and passed in as extras in the pending intent set as authentication
     * of the pinned entry.
     *
     * @param requestInfo            the information about the request
     */
    public Intent createIntentForAutofill(RequestInfo requestInfo) {
        return IntentFactory.createCredentialSelectorIntentForAutofill(
                mContext, requestInfo, new ArrayList<>(),
                mResultReceiver);
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.credentials.Constants;
import android.credentials.CredentialProviderInfo;
import android.credentials.GetCandidateCredentialsException;
@@ -114,8 +115,7 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ
            return;
        }

        cancelExistingPendingIntent();
        mPendingIntent = mCredentialManagerUi.createPendingIntentForAutofill(
        Intent intent = mCredentialManagerUi.createIntentForAutofill(
                RequestInfo.newGetRequestInfo(
                        mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
                        PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
@@ -129,7 +129,7 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ

        try {
            invokeClientCallbackSuccess(new GetCandidateCredentialsResponse(
                    candidateProviderDataList, mPendingIntent));
                    candidateProviderDataList, intent));
        } catch (RemoteException e) {
            Slog.e(TAG, "Issue while responding to client with error : " + e);
        }