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

This commit is contained in:
Reema Bajwa
2023-03-24 16:14:22 +00:00
committed by Android (Google) Code Review
3 changed files with 118 additions and 29 deletions

View File

@@ -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,32 +226,33 @@ 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) {
try {
mPrepareGetCredentialCallback.onResponse(
new PrepareGetCredentialResponseInternal(
false, null,
false, false,
getUiIntent()));
} catch (Exception e) {
Log.e(TAG, "EXCEPTION while mPendingCallback.onResponse", e);
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);
}
mIsInitialQuery = false;
} else {
getProviderDataAndInitiateUi();
constructEmptyPendingResponseAndInvokeCallback(hasQueryCandidatePermission);
}
mIsInitialQuery = false;
} else {
if (mIsInitialQuery) {
try {
mPrepareGetCredentialCallback.onResponse(
new PrepareGetCredentialResponseInternal(
false, null, false, false, null));
} catch (Exception e) {
Log.e(TAG, "EXCEPTION while mPendingCallback.onResponse", e);
}
mIsInitialQuery = false;
// TODO(273308895): should also clear session here
// 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");
@@ -256,6 +261,68 @@ public class PrepareGetRequestSession extends RequestSession<GetCredentialReques
}
}
private void constructPendingResponseAndInvokeCallback(boolean hasPermission,
Set<String> credentialTypes,
boolean hasAuthenticationResults, boolean hasRemoteResults, PendingIntent uiIntent) {
try {
mPrepareGetCredentialCallback.onResponse(
new PrepareGetCredentialResponseInternal(
hasPermission,
credentialTypes, hasAuthenticationResults, hasRemoteResults, uiIntent));
} catch (RemoteException e) {
Log.e(TAG, "EXCEPTION while mPendingCallback.onResponse", e);
}
}
private void constructEmptyPendingResponseAndInvokeCallback(
boolean hasQueryCandidatePermission) {
try {
mPrepareGetCredentialCallback.onResponse(
new PrepareGetCredentialResponseInternal(
hasQueryCandidatePermission,
/*credentialResultTypes=*/ null,
/*hasAuthenticationResults=*/false,
/*hasRemoteResults=*/ false,
/*pendingIntent=*/ null));
} catch (RemoteException e) {
Log.e(TAG, "EXCEPTION while mPendingCallback.onResponse", e);
}
}
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() {
ArrayList<ProviderData> providerDataList = new ArrayList<>();
for (ProviderSession session : mProviders.values()) {

View File

@@ -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 :

View File

@@ -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,