Loading services/credentials/java/com/android/server/credentials/GetRequestSession.java +36 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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 Loading @@ -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; } } services/credentials/java/com/android/server/credentials/ProviderGetSession.java +31 −4 Original line number Diff line number Diff line Loading @@ -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: Loading Loading @@ -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 Loading Loading @@ -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); } Loading services/credentials/java/com/android/server/credentials/ProviderSession.java +3 −2 Original line number Diff line number Diff line Loading @@ -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; } /** Loading Loading @@ -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. */ Loading services/credentials/java/com/android/server/credentials/RequestSession.java +1 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading
services/credentials/java/com/android/server/credentials/GetRequestSession.java +36 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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 Loading @@ -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; } }
services/credentials/java/com/android/server/credentials/ProviderGetSession.java +31 −4 Original line number Diff line number Diff line Loading @@ -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: Loading Loading @@ -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 Loading Loading @@ -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); } Loading
services/credentials/java/com/android/server/credentials/ProviderSession.java +3 −2 Original line number Diff line number Diff line Loading @@ -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; } /** Loading Loading @@ -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. */ Loading
services/credentials/java/com/android/server/credentials/RequestSession.java +1 −1 Original line number Diff line number Diff line Loading @@ -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; Loading