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

Commit fbb43217 authored by Reema Bajwa's avatar Reema Bajwa Committed by Android (Google) Code Review
Browse files

Merge "Check query permission and populate attributes" into udc-dev

parents 09b960ae ca5a1ed0
Loading
Loading
Loading
Loading
+89 −22
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.credentials;

import android.Manifest;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -28,17 +29,20 @@ import android.credentials.GetCredentialResponse;
import android.credentials.IGetCredentialCallback;
import android.credentials.IPrepareGetCredentialCallback;
import android.credentials.PrepareGetCredentialResponseInternal;
import android.credentials.ui.GetCredentialProviderData;
import android.credentials.ui.ProviderData;
import android.credentials.ui.RequestInfo;
import android.os.CancellationSignal;
import android.os.RemoteException;
import android.service.credentials.CallingAppInfo;
import android.service.credentials.PermissionUtils;
import android.util.Log;

import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ProviderStatusForMetrics;

import java.util.ArrayList;
import java.util.Set;
import java.util.stream.Collectors;

/**
@@ -222,38 +226,101 @@ public class PrepareGetRequestSession extends RequestSession<GetCredentialReques
            // If all provider responses have been received, we can either need the UI,
            // or we need to respond with error. The only other case is the entry being
            // selected after the UI has been invoked which has a separate code path.
            if (isUiInvocationNeeded()) {
            if (mIsInitialQuery) {
                // First time in this state. UI shouldn't be invoked because developer wants to
                // punt it for later
                boolean hasQueryCandidatePermission = PermissionUtils.hasPermission(
                        mContext,
                        mClientAppInfo.getPackageName(),
                        Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS);
                if (isUiInvocationNeeded()) {
                    ArrayList<ProviderData> providerData = getProviderDataForUi();
                    if (!providerData.isEmpty()) {
                        constructPendingResponseAndInvokeCallback(hasQueryCandidatePermission,
                                getCredentialResultTypes(hasQueryCandidatePermission),
                                hasAuthenticationResults(providerData, hasQueryCandidatePermission),
                                hasRemoteResults(providerData, hasQueryCandidatePermission),
                                getUiIntent());
                    } else {
                        constructEmptyPendingResponseAndInvokeCallback(hasQueryCandidatePermission);
                    }
                } else {
                    constructEmptyPendingResponseAndInvokeCallback(hasQueryCandidatePermission);
                }
                mIsInitialQuery = false;
            } else {
                // Not the first time. This could be a result of a user selection leading to a UI
                // invocation again.
                if (isUiInvocationNeeded()) {
                    getProviderDataAndInitiateUi();
                } else {
                    respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL,
                            "No credentials available");
                }
            }
        }
    }

    private void constructPendingResponseAndInvokeCallback(boolean hasPermission,
            Set<String> credentialTypes,
            boolean hasAuthenticationResults, boolean hasRemoteResults, PendingIntent uiIntent) {
        try {
            mPrepareGetCredentialCallback.onResponse(
                    new PrepareGetCredentialResponseInternal(
                                        false, null,
                                        false, false,
                                        getUiIntent()));
                    } catch (Exception e) {
                            hasPermission,
                            credentialTypes, hasAuthenticationResults, hasRemoteResults, uiIntent));
        } catch (RemoteException e) {
            Log.e(TAG, "EXCEPTION while mPendingCallback.onResponse", e);
        }
                    mIsInitialQuery = false;
                } else {
                    getProviderDataAndInitiateUi();
    }
            } else {
                if (mIsInitialQuery) {

    private void constructEmptyPendingResponseAndInvokeCallback(
            boolean hasQueryCandidatePermission) {
        try {
            mPrepareGetCredentialCallback.onResponse(
                    new PrepareGetCredentialResponseInternal(
                                        false, null, false, false, null));
                    } catch (Exception e) {
                            hasQueryCandidatePermission,
                            /*credentialResultTypes=*/ null,
                            /*hasAuthenticationResults=*/false,
                            /*hasRemoteResults=*/ false,
                            /*pendingIntent=*/ null));
        } catch (RemoteException e) {
            Log.e(TAG, "EXCEPTION while mPendingCallback.onResponse", e);
        }
                    mIsInitialQuery = false;
                    // TODO(273308895): should also clear session here
                } else {
                    respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL,
                            "No credentials available");
    }

    private boolean hasRemoteResults(ArrayList<ProviderData> providerData,
            boolean hasQueryCandidatePermission) {
        if (!hasQueryCandidatePermission) {
            return false;
        }
        return providerData.stream()
                .map(data -> (GetCredentialProviderData) data)
                .anyMatch(getCredentialProviderData ->
                        getCredentialProviderData.getRemoteEntry() != null);
    }

    private boolean hasAuthenticationResults(ArrayList<ProviderData> providerData,
            boolean hasQueryCandidatePermission) {
        if (!hasQueryCandidatePermission) {
            return false;
        }
        return providerData.stream()
                .map(data -> (GetCredentialProviderData) data)
                .anyMatch(getCredentialProviderData ->
                        !getCredentialProviderData.getAuthenticationEntries().isEmpty());
    }

    @Nullable
    private Set<String> getCredentialResultTypes(boolean hasQueryCandidatePermission) {
        if (!hasQueryCandidatePermission) {
            return null;
        }
        return mProviders.values().stream()
                .map(session -> (ProviderGetSession) session)
                .flatMap(providerGetSession -> providerGetSession
                        .getCredentialEntryTypes().stream())
                .collect(Collectors.toSet());
    }

    private PendingIntent getUiIntent() {
+16 −0
Original line number Diff line number Diff line
@@ -47,9 +47,11 @@ import com.android.server.credentials.metrics.EntryEnum;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
@@ -338,6 +340,11 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
        }
    }

    @NonNull
    protected Set<String> getCredentialEntryTypes() {
        return mProviderResponseDataHandler.getCredentialEntryTypes();
    }

    @Override // Call from request session to data to be shown on the UI
    @Nullable
    protected GetCredentialProviderData prepareUiData() throws IllegalArgumentException {
@@ -575,6 +582,9 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
        private final Map<String, Pair<Action, AuthenticationEntry>> mUiAuthenticationEntries =
                new HashMap<>();

        @NonNull
        private final Set<String> mCredentialEntryTypes = new HashSet<>();

        @Nullable
        private Pair<String, Pair<RemoteEntry, Entry>> mUiRemoteEntry = null;

@@ -607,6 +617,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
                    id, credentialEntry.getSlice(),
                    setUpFillInIntent(credentialEntry.getBeginGetCredentialOptionId()));
            mUiCredentialEntries.put(id, new Pair<>(credentialEntry, entry));
            mCredentialEntryTypes.add(credentialEntry.getType());
        }

        public void addAction(Action action) {
@@ -703,6 +714,11 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
                    && response.getRemoteCredentialEntry() == null;
        }

        @NonNull
        public Set<String> getCredentialEntryTypes() {
            return mCredentialEntryTypes;
        }

        @Nullable
        public Action getAuthenticationAction(String entryKey) {
            return mUiAuthenticationEntries.get(entryKey) == null ? null :
+13 −7
Original line number Diff line number Diff line
@@ -236,16 +236,26 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan
    }

    void getProviderDataAndInitiateUi() {
        ArrayList<ProviderData> providerDataList = getProviderDataForUi();
        if (!providerDataList.isEmpty()) {
            Log.i(TAG, "provider list not empty about to initiate ui");
            MetricUtilities.logApiCalled(mProviders, ++mSequenceCounter);
            launchUiWithProviderData(providerDataList);
        }
    }

    @NonNull
    protected ArrayList<ProviderData> getProviderDataForUi() {
        Log.i(TAG, "In getProviderDataAndInitiateUi");
        Log.i(TAG, "In getProviderDataAndInitiateUi providers size: " + mProviders.size());
        ArrayList<ProviderData> providerDataList = new ArrayList<>();

        if (isSessionCancelled()) {
            MetricUtilities.logApiCalled(mProviders, ++mSequenceCounter);
            finishSession(/*propagateCancellation=*/true);
            return;
            return providerDataList;
        }

        ArrayList<ProviderData> providerDataList = new ArrayList<>();
        for (ProviderSession session : mProviders.values()) {
            Log.i(TAG, "preparing data for : " + session.getComponentName());
            ProviderData providerData = session.prepareUiData();
@@ -254,11 +264,7 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan
                providerDataList.add(providerData);
            }
        }
        if (!providerDataList.isEmpty()) {
            Log.i(TAG, "provider list not empty about to initiate ui");
            MetricUtilities.logApiCalled(mProviders, ++mSequenceCounter);
            launchUiWithProviderData(providerDataList);
        }
        return providerDataList;
    }

    protected void collectFinalPhaseMetricStatus(boolean hasException,