Merge "Add CredentialDescription get impl."
diff --git a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
index fbdcc44..3d504ef 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
@@ -19,35 +19,72 @@
import android.credentials.CredentialDescription;
import android.credentials.RegisterCredentialDescriptionRequest;
import android.credentials.UnregisterCredentialDescriptionRequest;
+import android.service.credentials.CredentialEntry;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
/** Contains information on what CredentialProvider has what provisioned Credential. */
-public class CredentialDescriptionRegistry {
+public final class CredentialDescriptionRegistry {
private static final int MAX_ALLOWED_CREDENTIAL_DESCRIPTIONS = 128;
private static final int MAX_ALLOWED_ENTRIES_PER_PROVIDER = 16;
- private static SparseArray<CredentialDescriptionRegistry> sCredentialDescriptionSessionPerUser;
+ @GuardedBy("sLock")
+ private static final SparseArray<CredentialDescriptionRegistry>
+ sCredentialDescriptionSessionPerUser;
+ private static final ReentrantLock sLock;
static {
sCredentialDescriptionSessionPerUser = new SparseArray<>();
+ sLock = new ReentrantLock();
}
- // TODO(b/265992655): add a way to update CredentialRegistry when a user is removed.
- /** Get and/or create a {@link CredentialDescription} for the given user id. */
- public static CredentialDescriptionRegistry forUser(int userId) {
- CredentialDescriptionRegistry session =
- sCredentialDescriptionSessionPerUser.get(userId, null);
+ /** Represents the results of a given query into the registry. */
+ public static final class FilterResult {
+ final String mPackageName;
+ final List<CredentialEntry> mCredentialEntries;
- if (session == null) {
- session = new CredentialDescriptionRegistry();
- sCredentialDescriptionSessionPerUser.put(userId, session);
+ private FilterResult(String packageName,
+ List<CredentialEntry> credentialEntries) {
+ mPackageName = packageName;
+ mCredentialEntries = credentialEntries;
}
- return session;
+ }
+
+ /** Get and/or create a {@link CredentialDescription} for the given user id. */
+ @GuardedBy("sLock")
+ public static CredentialDescriptionRegistry forUser(int userId) {
+ sLock.lock();
+ try {
+ CredentialDescriptionRegistry session =
+ sCredentialDescriptionSessionPerUser.get(userId, null);
+
+ if (session == null) {
+ session = new CredentialDescriptionRegistry();
+ sCredentialDescriptionSessionPerUser.put(userId, session);
+ }
+ return session;
+ } finally {
+ sLock.unlock();
+ }
+ }
+
+ /** Clears an existing session for a given user identifier. */
+ @GuardedBy("sLock")
+ public static void clearUserSession(int userId) {
+ sLock.lock();
+ try {
+ sCredentialDescriptionSessionPerUser.remove(userId);
+ } finally {
+ sLock.unlock();
+ }
}
private Map<String, Set<CredentialDescription>> mCredentialDescriptions;
@@ -74,7 +111,7 @@
int size = mCredentialDescriptions.get(callingPackageName).size();
mCredentialDescriptions.get(callingPackageName)
.addAll(descriptions);
- mTotalDescriptionCount += size - mCredentialDescriptions.get(callingPackageName).size();
+ mTotalDescriptionCount += mCredentialDescriptions.get(callingPackageName).size() - size;
}
}
@@ -93,21 +130,33 @@
}
}
+ /** Returns package names and entries of a CredentialProviders that can satisfy a given
+ * {@link CredentialDescription}. */
+ public Set<FilterResult> getFilteredResultForProvider(String packageName,
+ List<String> flatRequestStrings) {
+ Set<FilterResult> result = new HashSet<>();
+ Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
+ for (CredentialDescription containedDescription: currentSet) {
+ if (flatRequestStrings.contains(containedDescription.getFlattenedRequestString())) {
+ result.add(new FilterResult(packageName, containedDescription
+ .getCredentialEntries()));
+ }
+ }
+ return result;
+ }
+
/** Returns package names of CredentialProviders that can satisfy a given
* {@link CredentialDescription}. */
- public Set<String> filterCredentials(String flatRequestString) {
-
+ public Set<String> getMatchingProviders(Set<String> flatRequestString) {
Set<String> result = new HashSet<>();
-
- for (String componentName: mCredentialDescriptions.keySet()) {
- Set<CredentialDescription> currentSet = mCredentialDescriptions.get(componentName);
- for (CredentialDescription containedDescription: currentSet) {
- if (flatRequestString.equals(containedDescription.getFlattenedRequestString())) {
- result.add(componentName);
+ for (String packageName: mCredentialDescriptions.keySet()) {
+ Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
+ for (CredentialDescription containedDescription : currentSet) {
+ if (flatRequestString.contains(containedDescription.getFlattenedRequestString())) {
+ result.add(packageName);
}
}
}
-
return result;
}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index ff72ed7..a1e80d0 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -61,13 +61,11 @@
import com.android.server.infra.SecureSettingsServiceNameResolver;
import java.util.ArrayList;
-import java.util.Collection;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
-import java.util.function.Function;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
/**
* Entry point service for credential management.
@@ -236,6 +234,7 @@
concatenatedServices.addAll(getOrConstructSystemServiceListLock(userId));
return concatenatedServices;
}
+
public static boolean isCredentialDescriptionApiEnabled() {
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API, false);
@@ -244,44 +243,38 @@
@SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked
// to be guarded by 'service.mLock', which is the same as mLock.
private List<ProviderSession> initiateProviderSessionsWithActiveContainers(
- RequestSession session,
- List<String> requestOptions, Set<ComponentName> activeCredentialContainers) {
+ GetRequestSession session,
+ List<String> requestOptions, Set<String> activeCredentialContainers) {
List<ProviderSession> providerSessions = new ArrayList<>();
// Invoke all services of a user to initiate a provider session
- runForUser((service) -> {
- if (activeCredentialContainers.contains(service.getComponentName())) {
- ProviderSession providerSession = service
- .initiateProviderSessionForRequestLocked(session, requestOptions);
- if (providerSession != null) {
- providerSessions.add(providerSession);
- }
- }
- });
+ for (String packageName: activeCredentialContainers) {
+ providerSessions.add(ProviderRegistryGetSession.createNewSession(
+ mContext,
+ UserHandle.getCallingUserId(),
+ session,
+ packageName,
+ requestOptions));
+ }
return providerSessions;
}
@NonNull
- private Set<String> getMatchingProviders(GetCredentialRequest request) {
+ private Set<String> getFilteredResultFromRegistry(List<CredentialOption> options) {
// Session for active/provisioned credential descriptions;
CredentialDescriptionRegistry registry = CredentialDescriptionRegistry
.forUser(UserHandle.getCallingUserId());
// All requested credential descriptions based on the given request.
Set<String> requestedCredentialDescriptions =
- request.getCredentialOptions().stream().map(
- credentialOption -> credentialOption
+ options.stream().map(
+ getCredentialOption -> getCredentialOption
.getCredentialRetrievalData()
.getString(CredentialOption
.FLATTENED_REQUEST))
.collect(Collectors.toSet());
// All requested credential descriptions based on the given request.
- return requestedCredentialDescriptions.stream()
- .map(registry::filterCredentials)
- .flatMap(
- (Function<Set<String>, Stream<String>>)
- Collection::stream)
- .collect(Collectors.toSet());
+ return registry.getMatchingProviders(requestedCredentialDescriptions);
}
@SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked
@@ -304,6 +297,13 @@
return providerSessions;
}
+ @Override
+ @GuardedBy("CredentialDescriptionRegistry.sLock")
+ public void onUserStopped(@NonNull TargetUser user) {
+ super.onUserStopped(user);
+ CredentialDescriptionRegistry.clearUserSession(user.getUserIdentifier());
+ }
+
private CallingAppInfo constructCallingAppInfo(String packageName, int userId) {
final PackageInfo packageInfo;
try {
@@ -340,13 +340,57 @@
request,
constructCallingAppInfo(callingPackage, userId));
- // Initiate all provider sessions
- List<ProviderSession> providerSessions =
- initiateProviderSessions(
- session,
- request.getCredentialOptions().stream()
- .map(CredentialOption::getType)
- .collect(Collectors.toList()));
+ List<ProviderSession> providerSessions;
+
+ if (isCredentialDescriptionApiEnabled()) {
+ List<CredentialOption> optionsThatRequireActiveCredentials =
+ request.getCredentialOptions().stream()
+ .filter(getCredentialOption ->
+ !TextUtils.isEmpty(getCredentialOption
+ .getCredentialRetrievalData().getString(
+ CredentialOption
+ .FLATTENED_REQUEST, null)))
+ .toList();
+
+ List<CredentialOption> optionsThatDoNotRequireActiveCredentials =
+ request.getCredentialOptions().stream()
+ .filter(getCredentialOption ->
+ TextUtils.isEmpty(getCredentialOption
+ .getCredentialRetrievalData().getString(
+ CredentialOption
+ .FLATTENED_REQUEST, null)))
+ .toList();
+
+ List<ProviderSession> sessionsWithoutRemoteService =
+ initiateProviderSessionsWithActiveContainers(session,
+ optionsThatRequireActiveCredentials
+ .stream().map(getCredentialOption ->
+ getCredentialOption.getCredentialRetrievalData()
+ .getString(CredentialOption
+ .FLATTENED_REQUEST))
+ .collect(Collectors.toList()),
+ getFilteredResultFromRegistry(optionsThatRequireActiveCredentials));
+
+ List<ProviderSession> sessionsWithRemoteService = initiateProviderSessions(
+ session,
+ optionsThatDoNotRequireActiveCredentials.stream()
+ .map(CredentialOption::getType)
+ .collect(Collectors.toList()));
+
+ Set<ProviderSession> all = new LinkedHashSet<>();
+ all.addAll(sessionsWithRemoteService);
+ all.addAll(sessionsWithoutRemoteService);
+
+ providerSessions = new ArrayList<>(all);
+ } else {
+ // Initiate all provider sessions
+ providerSessions =
+ initiateProviderSessions(
+ session,
+ request.getCredentialOptions().stream()
+ .map(CredentialOption::getType)
+ .collect(Collectors.toList()));
+ }
if (providerSessions.isEmpty()) {
try {
@@ -363,6 +407,7 @@
// Iterate over all provider sessions and invoke the request
providerSessions.forEach(ProviderSession::invokeSession);
+
return cancelTransport;
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
index b112649..b20f0cd 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
@@ -118,8 +118,8 @@
@Override
protected void invokeSession() {
- this.mRemoteCredentialService.onClearCredentialState(
- this.getProviderRequest(),
- /*callback=*/this);
+ if (mRemoteCredentialService != null) {
+ mRemoteCredentialService.onClearCredentialState(mProviderRequest, this);
+ }
}
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index cc5a8ab..ade40ad 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -51,6 +51,8 @@
// Key to be used as an entry key for a save entry
private static final String SAVE_ENTRY_KEY = "save_entry_key";
+ // Key to be used as an entry key for a remote entry
+ private static final String REMOTE_ENTRY_KEY = "remote_entry_key";
@NonNull
private final Map<String, CreateEntry> mUiSaveEntries = new HashMap<>();
@@ -199,9 +201,9 @@
@Override
protected void invokeSession() {
- this.mRemoteCredentialService.onCreateCredential(
- this.getProviderRequest(),
- /*callback=*/this);
+ if (mRemoteCredentialService != null) {
+ mRemoteCredentialService.onCreateCredential(mProviderRequest, this);
+ }
}
private List<Entry> prepareUiSaveEntries(@NonNull List<CreateEntry> saveEntries) {
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index dec3432..3ccead1 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -59,14 +59,14 @@
implements
RemoteCredentialService.ProviderCallbacks<BeginGetCredentialResponse> {
private static final String TAG = "ProviderGetSession";
-
- // Key to be used as an entry key for a credential entry
- private static final String CREDENTIAL_ENTRY_KEY = "credential_key";
-
// Key to be used as the entry key for an action entry
private static final String ACTION_ENTRY_KEY = "action_key";
// Key to be used as the entry key for the authentication entry
private static final String AUTHENTICATION_ACTION_ENTRY_KEY = "authentication_action_key";
+ // Key to be used as an entry key for a remote entry
+ private static final String REMOTE_ENTRY_KEY = "remote_entry_key";
+ // Key to be used as an entry key for a credential entry
+ private static final String CREDENTIAL_ENTRY_KEY = "credential_key";
@NonNull
private final Map<String, CredentialEntry> mUiCredentialEntries = new HashMap<>();
@@ -101,23 +101,8 @@
return null;
}
- private static BeginGetCredentialRequest constructQueryPhaseRequest(
- android.credentials.GetCredentialRequest filteredRequest,
- CallingAppInfo callingAppInfo
- ) {
- return new BeginGetCredentialRequest.Builder(callingAppInfo)
- .setBeginGetCredentialOptions(
- filteredRequest.getCredentialOptions().stream().map(
- option -> {
- return new BeginGetCredentialOption(
- option.getType(),
- option.getCandidateQueryData());
- }).collect(Collectors.toList()))
- .build();
- }
-
@Nullable
- private static android.credentials.GetCredentialRequest filterOptions(
+ protected static android.credentials.GetCredentialRequest filterOptions(
List<String> providerCapabilities,
android.credentials.GetCredentialRequest clientRequest
) {
@@ -142,6 +127,21 @@
return null;
}
+ private static BeginGetCredentialRequest constructQueryPhaseRequest(
+ android.credentials.GetCredentialRequest filteredRequest,
+ CallingAppInfo callingAppInfo
+ ) {
+ return new BeginGetCredentialRequest.Builder(callingAppInfo)
+ .setBeginGetCredentialOptions(
+ filteredRequest.getCredentialOptions().stream().map(
+ option -> {
+ return new BeginGetCredentialOption(
+ option.getType(),
+ option.getCandidateQueryData());
+ }).collect(Collectors.toList()))
+ .build();
+ }
+
public ProviderGetSession(Context context,
CredentialProviderInfo info,
ProviderInternalCallback<GetCredentialResponse> callbacks,
@@ -232,9 +232,9 @@
@Override
protected void invokeSession() {
- this.mRemoteCredentialService.onBeginGetCredential(
- this.getProviderRequest(),
- /*callback=*/this);
+ if (mRemoteCredentialService != null) {
+ mRemoteCredentialService.onBeginGetCredential(mProviderRequest, this);
+ }
}
@Override // Call from request session to data to be shown on the UI
@@ -379,6 +379,28 @@
invokeCallbackOnInternalInvalidState();
}
+ @Nullable
+ protected GetCredentialException maybeGetPendingIntentException(
+ ProviderPendingIntentResponse pendingIntentResponse) {
+ if (pendingIntentResponse == null) {
+ Log.i(TAG, "pendingIntentResponse is null");
+ return null;
+ }
+ if (PendingIntentResultHandler.isValidResponse(pendingIntentResponse)) {
+ GetCredentialException exception = PendingIntentResultHandler
+ .extractGetCredentialException(pendingIntentResponse.getResultData());
+ if (exception != null) {
+ Log.i(TAG, "Pending intent contains provider exception");
+ return exception;
+ }
+ } else if (PendingIntentResultHandler.isCancelledResponse(pendingIntentResponse)) {
+ return new GetCredentialException(GetCredentialException.TYPE_USER_CANCELED);
+ } else {
+ return new GetCredentialException(GetCredentialException.TYPE_NO_CREDENTIAL);
+ }
+ return null;
+ }
+
private void onAuthenticationEntrySelected(
@Nullable ProviderPendingIntentResponse providerPendingIntentResponse) {
//TODO: Other provider intent statuses
@@ -431,28 +453,6 @@
updateStatusAndInvokeCallback(Status.NO_CREDENTIALS);
}
- @Nullable
- private GetCredentialException maybeGetPendingIntentException(
- ProviderPendingIntentResponse pendingIntentResponse) {
- if (pendingIntentResponse == null) {
- Log.i(TAG, "pendingIntentResponse is null");
- return null;
- }
- if (PendingIntentResultHandler.isValidResponse(pendingIntentResponse)) {
- GetCredentialException exception = PendingIntentResultHandler
- .extractGetCredentialException(pendingIntentResponse.getResultData());
- if (exception != null) {
- Log.i(TAG, "Pending intent contains provider exception");
- return exception;
- }
- } else if (PendingIntentResultHandler.isCancelledResponse(pendingIntentResponse)) {
- return new GetCredentialException(GetCredentialException.TYPE_USER_CANCELED);
- } else {
- return new GetCredentialException(GetCredentialException.TYPE_NO_CREDENTIAL);
- }
- return null;
- }
-
/**
* When an invalid state occurs, e.g. entry mismatch or no response from provider,
* we send back a TYPE_UNKNOWN error as to the developer.
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
new file mode 100644
index 0000000..461f447
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.content.Intent;
+import android.credentials.CredentialOption;
+import android.credentials.GetCredentialException;
+import android.credentials.GetCredentialRequest;
+import android.credentials.GetCredentialResponse;
+import android.credentials.ui.Entry;
+import android.credentials.ui.GetCredentialProviderData;
+import android.credentials.ui.ProviderData;
+import android.credentials.ui.ProviderPendingIntentResponse;
+import android.service.credentials.CallingAppInfo;
+import android.service.credentials.CredentialEntry;
+import android.service.credentials.CredentialProviderService;
+import android.telecom.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Central provider session that utilizes {@link CredentialDescriptionRegistry} and therefor is able
+ * to bypass having to use a {@link RemoteCredentialService}.
+ *
+ * @hide
+ */
+public class ProviderRegistryGetSession extends ProviderSession<GetCredentialRequest,
+ Set<CredentialDescriptionRegistry.FilterResult>> {
+
+ private static final String TAG = "ProviderRegistryGetSession";
+ private static final String CREDENTIAL_ENTRY_KEY = "credential_key";
+
+ /** Creates a new provider session to be used by the request session. */
+ @Nullable
+ public static ProviderRegistryGetSession createNewSession(
+ @NonNull Context context,
+ @UserIdInt int userId,
+ @NonNull GetRequestSession getRequestSession,
+ @NonNull String credentialProviderPackageName,
+ @NonNull List<String> requestOptions) {
+ return new ProviderRegistryGetSession(
+ context,
+ userId,
+ getRequestSession,
+ getRequestSession.mClientRequest,
+ getRequestSession.mClientAppInfo,
+ credentialProviderPackageName,
+ requestOptions);
+ }
+
+ @NonNull
+ private final Map<String, CredentialEntry> mUiCredentialEntries = new HashMap<>();
+ @NonNull
+ private final CredentialDescriptionRegistry mCredentialDescriptionRegistry;
+ @NonNull
+ private final CallingAppInfo mCallingAppInfo;
+ @NonNull
+ private final String mCredentialProviderPackageName;
+ @NonNull
+ private final GetRequestSession mGetRequestSession;
+ @NonNull
+ private final List<String> mRequestOptions;
+ private List<CredentialEntry> mCredentialEntries;
+
+ protected ProviderRegistryGetSession(@NonNull Context context,
+ @NonNull int userId,
+ @NonNull GetRequestSession session,
+ @NonNull GetCredentialRequest request,
+ @NonNull CallingAppInfo callingAppInfo,
+ @NonNull String servicePackageName,
+ @NonNull List<String> requestOptions) {
+ super(context, null, request, session, userId, null);
+ mGetRequestSession = session;
+ mCredentialDescriptionRegistry = CredentialDescriptionRegistry.forUser(userId);
+ mCallingAppInfo = callingAppInfo;
+ mCredentialProviderPackageName = servicePackageName;
+ mRequestOptions = requestOptions;
+ }
+
+ private List<Entry> prepareUiCredentialEntries(
+ @NonNull List<CredentialEntry> credentialEntries) {
+ Log.i(TAG, "in prepareUiProviderDataWithCredentials");
+ List<Entry> credentialUiEntries = new ArrayList<>();
+
+ // Populate the credential entries
+ for (CredentialEntry credentialEntry : credentialEntries) {
+ String entryId = generateEntryId();
+ mUiCredentialEntries.put(entryId, credentialEntry);
+ Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId);
+ credentialUiEntries.add(new Entry(CREDENTIAL_ENTRY_KEY, entryId,
+ credentialEntry.getSlice(),
+ setUpFillInIntent(credentialEntry.getType())));
+ }
+ return credentialUiEntries;
+ }
+
+ private Intent setUpFillInIntent(String type) {
+ Intent intent = new Intent();
+ for (CredentialOption option : mProviderRequest.getCredentialOptions()) {
+ if (option.getType().equals(type)) {
+ intent.putExtra(
+ CredentialProviderService
+ .EXTRA_GET_CREDENTIAL_REQUEST,
+ new android.service.credentials.GetCredentialRequest(
+ mCallingAppInfo, option));
+ return intent;
+ }
+ }
+ return intent;
+ }
+
+ @Override
+ protected ProviderData prepareUiData() {
+ Log.i(TAG, "In prepareUiData");
+ if (!ProviderSession.isUiInvokingStatus(getStatus())) {
+ Log.i(TAG, "In prepareUiData - provider does not want to show UI: "
+ + mComponentName.flattenToString());
+ return null;
+ }
+ if (mProviderResponse == null) {
+ Log.i(TAG, "In prepareUiData response null");
+ throw new IllegalStateException("Response must be in completion mode");
+ }
+ return new GetCredentialProviderData.Builder(
+ mComponentName.flattenToString()).setActionChips(null)
+ .setCredentialEntries(prepareUiCredentialEntries(
+ mProviderResponse.stream().flatMap((Function<CredentialDescriptionRegistry
+ .FilterResult,
+ Stream<CredentialEntry>>) filterResult ->
+ filterResult.mCredentialEntries.stream())
+ .collect(Collectors.toList())))
+ .build();
+ }
+
+ @Override // Selection call from the request provider
+ protected void onUiEntrySelected(String entryType, String entryKey,
+ ProviderPendingIntentResponse providerPendingIntentResponse) {
+ switch (entryType) {
+ case CREDENTIAL_ENTRY_KEY:
+ CredentialEntry credentialEntry = mUiCredentialEntries.get(entryKey);
+ if (credentialEntry == null) {
+ Log.i(TAG, "Unexpected credential entry key");
+ return;
+ }
+ onCredentialEntrySelected(credentialEntry, providerPendingIntentResponse);
+ break;
+ default:
+ Log.i(TAG, "Unsupported entry type selected");
+ }
+ }
+
+ private void onCredentialEntrySelected(CredentialEntry credentialEntry,
+ ProviderPendingIntentResponse providerPendingIntentResponse) {
+ if (!mCredentialEntries.contains(credentialEntry)) {
+ invokeCallbackWithError("",
+ "");
+ }
+
+ if (providerPendingIntentResponse != null) {
+ // Check if pending intent has an error
+ GetCredentialException exception = maybeGetPendingIntentException(
+ providerPendingIntentResponse);
+ if (exception != null) {
+ invokeCallbackWithError(exception.getType(),
+ exception.getMessage());
+ return;
+ }
+
+ // Check if pending intent has a credential
+ GetCredentialResponse getCredentialResponse = PendingIntentResultHandler
+ .extractGetCredentialResponse(
+ providerPendingIntentResponse.getResultData());
+ if (getCredentialResponse != null) {
+ if (mCallbacks != null) {
+ mCallbacks.onFinalResponseReceived(mComponentName,
+ getCredentialResponse);
+ }
+ return;
+ }
+
+ Log.i(TAG, "Pending intent response contains no credential, or error");
+ }
+ Log.i(TAG, "CredentialEntry does not have a credential or a pending intent result");
+ }
+
+ @Override
+ public void onProviderResponseSuccess(
+ @Nullable Set<CredentialDescriptionRegistry.FilterResult> response) {
+ // No need to do anything since this class does not rely on a remote service.
+ }
+
+ @Override
+ public void onProviderResponseFailure(int internalErrorCode, @Nullable Exception e) {
+ // No need to do anything since this class does not rely on a remote service.
+ }
+
+ @Override
+ public void onProviderServiceDied(RemoteCredentialService service) {
+ // No need to do anything since this class does not rely on a remote service.
+ }
+
+ @Override
+ protected void invokeSession() {
+ mProviderResponse = mCredentialDescriptionRegistry
+ .getFilteredResultForProvider(mCredentialProviderPackageName,
+ mRequestOptions);
+ mCredentialEntries = mProviderResponse.stream().flatMap(
+ (Function<CredentialDescriptionRegistry.FilterResult,
+ Stream<CredentialEntry>>) filterResult
+ -> filterResult.mCredentialEntries.stream())
+ .collect(Collectors.toList());
+ setStatus(Status.CREDENTIALS_RECEIVED);
+ }
+
+ @Nullable
+ protected GetCredentialException maybeGetPendingIntentException(
+ ProviderPendingIntentResponse pendingIntentResponse) {
+ if (pendingIntentResponse == null) {
+ android.util.Log.i(TAG, "pendingIntentResponse is null");
+ return null;
+ }
+ if (PendingIntentResultHandler.isValidResponse(pendingIntentResponse)) {
+ GetCredentialException exception = PendingIntentResultHandler
+ .extractGetCredentialException(pendingIntentResponse.getResultData());
+ if (exception != null) {
+ android.util.Log.i(TAG, "Pending intent contains provider exception");
+ return exception;
+ }
+ } else if (PendingIntentResultHandler.isCancelledResponse(pendingIntentResponse)) {
+ return new GetCredentialException(GetCredentialException.TYPE_USER_CANCELED);
+ } else {
+ return new GetCredentialException(GetCredentialException.TYPE_NO_CREDENTIAL);
+ }
+ return null;
+ }
+}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index 8e0d6f8..c479c4e 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -38,16 +38,14 @@
implements RemoteCredentialService.ProviderCallbacks<R> {
private static final String TAG = "ProviderSession";
- // Key to be used as an entry key for a remote entry
- protected static final String REMOTE_ENTRY_KEY = "remote_entry_key";
@NonNull protected final Context mContext;
@NonNull protected final ComponentName mComponentName;
- @NonNull protected final CredentialProviderInfo mProviderInfo;
- @NonNull protected final RemoteCredentialService mRemoteCredentialService;
+ @Nullable protected final CredentialProviderInfo mProviderInfo;
+ @Nullable protected final RemoteCredentialService mRemoteCredentialService;
@NonNull protected final int mUserId;
@NonNull protected Status mStatus = Status.NOT_STARTED;
- @NonNull protected final ProviderInternalCallback mCallbacks;
+ @Nullable protected final ProviderInternalCallback mCallbacks;
@Nullable protected Credential mFinalCredentialResponse;
@NonNull protected final T mProviderRequest;
@Nullable protected R mProviderResponse;
@@ -109,9 +107,9 @@
protected ProviderSession(@NonNull Context context, @NonNull CredentialProviderInfo info,
@NonNull T providerRequest,
- @NonNull ProviderInternalCallback callbacks,
+ @Nullable ProviderInternalCallback callbacks,
@NonNull int userId,
- @NonNull RemoteCredentialService remoteCredentialService) {
+ @Nullable RemoteCredentialService remoteCredentialService) {
mContext = context;
mProviderInfo = info;
mProviderRequest = providerRequest;
@@ -165,7 +163,7 @@
return mComponentName;
}
- @NonNull
+ @Nullable
protected RemoteCredentialService getRemoteCredentialService() {
return mRemoteCredentialService;
}