diff options
4 files changed, 71 insertions, 8 deletions
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java index 5e167712a8e8..32b14d773b95 100644 --- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java @@ -44,7 +44,6 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest IGetCredentialCallback> implements ProviderSession.ProviderInternalCallback<GetCredentialResponse> { private static final String TAG = "GetRequestSession"; - public GetRequestSession(Context context, int userId, int callingUid, IGetCredentialCallback callback, GetCredentialRequest request, CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal) { @@ -173,6 +172,12 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest public void onProviderStatusChanged(ProviderSession.Status status, ComponentName componentName) { Log.i(TAG, "in onStatusChanged with status: " + status); + // Auth entry was selected, and it did not have any underlying credentials + if (status == ProviderSession.Status.NO_CREDENTIALS_FROM_AUTH_ENTRY) { + handleEmptyAuthenticationSelection(componentName); + return; + } + // For any other status, we check if all providers are done and then invoke UI if needed if (!isAnyProviderPending()) { // 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 @@ -186,4 +191,34 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest } } } + + private void handleEmptyAuthenticationSelection(ComponentName componentName) { + // Update auth entry statuses across different provider sessions + mProviders.keySet().forEach(key -> { + ProviderGetSession session = (ProviderGetSession) mProviders.get(key); + if (!session.mComponentName.equals(componentName)) { + session.updateAuthEntriesStatusFromAnotherSession(); + } + }); + + // Invoke UI since it needs to show a snackbar if last auth entry, or a status on each + // auth entries along with other valid entries + getProviderDataAndInitiateUi(); + + // Respond to client if all auth entries are empty and nothing else to show on the UI + if (providerDataContainsEmptyAuthEntriesOnly()) { + respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL, + "No credentials available"); + } + } + + private boolean providerDataContainsEmptyAuthEntriesOnly() { + for (String key : mProviders.keySet()) { + ProviderGetSession session = (ProviderGetSession) mProviders.get(key); + if (!session.containsEmptyAuthEntriesOnly()) { + return false; + } + } + return true; + } } diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index 12074c7494bf..8c94b0ada2a8 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -241,13 +241,14 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential if (additionalContentReceived) { Log.i(TAG, "Additional content received - removing authentication entry"); mProviderResponseDataHandler.removeAuthenticationAction(entryKey); + if (!mProviderResponseDataHandler.isEmptyResponse()) { + updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED); + } } else { Log.i(TAG, "Additional content not received"); mProviderResponseDataHandler .updateAuthEntryWithNoCredentialsReceived(entryKey); - } - if (!mProviderResponseDataHandler.isEmptyResponse()) { - updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED); + updateStatusAndInvokeCallback(Status.NO_CREDENTIALS_FROM_AUTH_ENTRY); } break; case REMOTE_ENTRY_KEY: @@ -456,6 +457,27 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential GetCredentialException.TYPE_UNKNOWN, null); } + /** Update auth entries status based on an auth entry selected from a different session. */ + public void updateAuthEntriesStatusFromAnotherSession() { + // Pass null for entryKey if the auth entry selected belongs to a different session + mProviderResponseDataHandler.updateAuthEntryWithNoCredentialsReceived(/*entryKey=*/null); + } + + /** Returns true if the provider response contains empty auth entries only, false otherwise. **/ + public boolean containsEmptyAuthEntriesOnly() { + // We do not consider action entries here because if actions are the only entries, + // we don't show the UI + return mProviderResponseDataHandler.mUiCredentialEntries.isEmpty() + && mProviderResponseDataHandler.mUiRemoteEntry == null + && mProviderResponseDataHandler.mUiAuthenticationEntries + .values().stream().allMatch( + e -> e.second.getStatus() == AuthenticationEntry + .STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT + || e.second.getStatus() + == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT + ); + } + private class ProviderResponseDataHandler { private final ComponentName mExpectedRemoteEntryProviderService; @NonNull @@ -610,7 +632,12 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential ? null : mUiCredentialEntries.get(entryKey).first; } - public void updateAuthEntryWithNoCredentialsReceived(String entryKey) { + public void updateAuthEntryWithNoCredentialsReceived(@Nullable String entryKey) { + if (entryKey == null) { + // Auth entry from a different provider was selected by the user. + updatePreviousMostRecentAuthEntry(); + return; + } updatePreviousMostRecentAuthEntry(); updateMostRecentAuthEntry(entryKey); } diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java index 2f9d57872c3c..ecddcf30f88d 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java @@ -66,7 +66,8 @@ public abstract class ProviderSession<T, R> * on the credMan UI. */ public static boolean isUiInvokingStatus(Status status) { - return status == Status.CREDENTIALS_RECEIVED || status == Status.SAVE_ENTRIES_RECEIVED; + return status == Status.CREDENTIALS_RECEIVED || status == Status.SAVE_ENTRIES_RECEIVED + || status == Status.NO_CREDENTIALS_FROM_AUTH_ENTRY; } /** @@ -140,7 +141,7 @@ public abstract class ProviderSession<T, R> PENDING_INTENT_INVOKED, CREDENTIAL_RECEIVED_FROM_SELECTION, SAVE_ENTRIES_RECEIVED, CANCELED, - NO_CREDENTIALS, EMPTY_RESPONSE, COMPLETE + NO_CREDENTIALS, EMPTY_RESPONSE, NO_CREDENTIALS_FROM_AUTH_ENTRY, COMPLETE } /** Converts exception to a provider session status. */ diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java index f0d05c5dfd05..f8bbfcfb6a4d 100644 --- a/services/credentials/java/com/android/server/credentials/RequestSession.java +++ b/services/credentials/java/com/android/server/credentials/RequestSession.java @@ -216,7 +216,7 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan * Returns true if at least one provider is ready for UI invocation, and no * provider is pending a response. */ - boolean isUiInvocationNeeded() { + protected boolean isUiInvocationNeeded() { for (ProviderSession session : mProviders.values()) { if (ProviderSession.isUiInvokingStatus(session.getStatus())) { return true; |