Check query permission and populate attributes
Test: built locally and deployed + CTS tests Bug: 274654199 Change-Id: Iba268b986b4629ebea4110fb8bd9421001ac3e58
This commit is contained in:
@@ -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()) {
|
||||
|
||||
@@ -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 :
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user