Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d77e0d99 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Maintain auth entry status across providers" into udc-dev

parents 18c20c4d ed194f32
Loading
Loading
Loading
Loading
+36 −1
Original line number Diff line number Diff line
@@ -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;
    }
}
+31 −4
Original line number Diff line number Diff line
@@ -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);
        }
+3 −2
Original line number Diff line number Diff line
@@ -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. */
+1 −1
Original line number Diff line number Diff line
@@ -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;