diff options
| author | 2022-12-19 22:38:49 +0000 | |
|---|---|---|
| committer | 2022-12-19 22:38:49 +0000 | |
| commit | 82a441a6f07e4d573f46bea1a6ac62ae9f41e8eb (patch) | |
| tree | 2c7be5f0c7412767cba7c6e2172708e2893e77e7 | |
| parent | 9ac32f43548a7e5dda0e172c678f35ec5c531c37 (diff) | |
| parent | 61ae713373b74a50154fb850da68cbafb6f95b7a (diff) | |
Merge "Add error propagation piping for get & create Test: Built & deployed locally"
11 files changed, 357 insertions, 192 deletions
diff --git a/core/java/android/service/credentials/CredentialProviderErrors.java b/core/java/android/service/credentials/CredentialProviderErrors.java new file mode 100644 index 000000000000..e9dc35bc1062 --- /dev/null +++ b/core/java/android/service/credentials/CredentialProviderErrors.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 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 android.service.credentials; + +/** + * Contains custom error codes to be used internally for various credential + * provider error states. + * + * @hide + */ +public class CredentialProviderErrors { +    public static final int ERROR_UNKNOWN = 0; + +    /** +     * For internal use only. +     * Error code to be used when the provider request times out. +     * +     * @hide +     */ +    public static final int ERROR_TIMEOUT = 1; + +    /** +     * For internal use only. +     * Error code to be used when the async task is canceled internally. +     * +     * @hide +     */ +    public static final int ERROR_TASK_CANCELED = 2; + +    /** +     * For internal use only. +     * Error code to be used when an exception is received from the provider. +     * +     * @hide +     */ +    public static final int ERROR_PROVIDER_FAILURE = 3; +} diff --git a/core/java/android/service/credentials/CredentialProviderException.java b/core/java/android/service/credentials/CredentialProviderException.java deleted file mode 100644 index 969bcb57ff90..000000000000 --- a/core/java/android/service/credentials/CredentialProviderException.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2022 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 android.service.credentials; - -import android.annotation.IntDef; -import android.annotation.NonNull; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Contains custom exceptions to be used by credential providers on failure. - * - * @hide - */ -public class CredentialProviderException extends Exception { -    public static final int ERROR_UNKNOWN = 0; - -    /** -     * For internal use only. -     * Error code to be used when the provider request times out. -     * -     * @hide -     */ -    public static final int ERROR_TIMEOUT = 1; - -    /** -     * For internal use only. -     * Error code to be used when the async task is canceled internally. -     * -     * @hide -     */ -    public static final int ERROR_TASK_CANCELED = 2; - -    /** -     * For internal use only. -     * Error code to be used when the provider encounters a failure while processing the request. -     * -     * @hide -     */ -    public static final int ERROR_PROVIDER_FAILURE = 3; - -    private final int mErrorCode; - -    /** -     * @hide -     */ -    @IntDef(prefix = {"ERROR_"}, value = { -            ERROR_UNKNOWN, -            ERROR_TIMEOUT, -            ERROR_TASK_CANCELED -    }) -    @Retention(RetentionPolicy.SOURCE) -    public @interface CredentialProviderError { } - -    public CredentialProviderException(@CredentialProviderError int errorCode, -            @NonNull String message, @NonNull Throwable cause) { -        super(message, cause); -        mErrorCode = errorCode; -    } - -    public CredentialProviderException(@CredentialProviderError int errorCode, -            @NonNull String message) { -        super(message); -        mErrorCode = errorCode; -    } - -    public CredentialProviderException(@CredentialProviderError int errorCode, -            @NonNull Throwable cause) { -        super(cause); -        mErrorCode = errorCode; -    } - -    public CredentialProviderException(@CredentialProviderError int errorCode) { -        super(); -        mErrorCode = errorCode; -    } - -    public @CredentialProviderError int getErrorCode() { -        return mErrorCode; -    } -} diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java index 3d337b896bb7..d9dc4ed6c766 100644 --- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java @@ -83,16 +83,6 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR          }      } -    private void respondToClientAndFinish(CreateCredentialResponse response) { -        Log.i(TAG, "respondToClientAndFinish"); -        try { -            mClientCallback.onResponse(response); -        } catch (RemoteException e) { -            e.printStackTrace(); -        } -        finishSession(); -    } -      @Override      public void onProviderStatusChanged(ProviderSession.Status status,              ComponentName componentName) { @@ -101,10 +91,47 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR      @Override      public void onFinalResponseReceived(ComponentName componentName, -            CreateCredentialResponse response) { +            @Nullable CreateCredentialResponse response) {          Log.i(TAG, "onFinalCredentialReceived from: " + componentName.flattenToString());          if (response != null) { -            respondToClientAndFinish(response); +            respondToClientWithResponseAndFinish(response); +        } else { +            // TODO("Replace with properly defined error type) +            respondToClientWithErrorAndFinish("unknown_type", +                    "Invalid response"); +        } +    } + +    @Override +    public void onFinalErrorReceived(ComponentName componentName, String errorType, +            String message) { +        respondToClientWithErrorAndFinish(errorType, message); +    } + +    @Override +    public void onUiCancellation() { +        // TODO("Replace with properly defined error type") +        respondToClientWithErrorAndFinish("user_cancelled", +                "User cancelled the selector"); +    } + +    private void respondToClientWithResponseAndFinish(CreateCredentialResponse response) { +        Log.i(TAG, "respondToClientWithResponseAndFinish"); +        try { +            mClientCallback.onResponse(response); +        } catch (RemoteException e) { +            Log.i(TAG, "Issue while responding to client: " + e.getMessage());          } +        finishSession(); +    } + +    private void respondToClientWithErrorAndFinish(String errorType, String errorMsg) { +        Log.i(TAG, "respondToClientWithErrorAndFinish"); +        try { +            mClientCallback.onError(errorType, errorMsg); +        } catch (RemoteException e) { +            Log.i(TAG, "Issue while responding to client: " + e.getMessage()); +        } +        finishSession();      }  } diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index b4d7632371e7..2c226da72455 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -175,12 +175,20 @@ public final class CredentialManagerService              // Initiate all provider sessions              List<ProviderSession> providerSessions = -                    initiateProviderSessions( -                            session, -                            request.getGetCredentialOptions().stream() -                                    .map(GetCredentialOption::getType) -                                    .collect(Collectors.toList())); -            // TODO : Return error when no providers available +                    initiateProviderSessions(session, request.getGetCredentialOptions() +                            .stream().map(GetCredentialOption::getType) +                            .collect(Collectors.toList())); + +            if (providerSessions.isEmpty()) { +                try { +                    // TODO("Replace with properly defined error type") +                    callback.onError("unknown_type", +                            "No providers available to fulfill request."); +                } catch (RemoteException e) { +                    Log.i(TAG, "Issue invoking onError on IGetCredentialCallback " +                            + "callback: " + e.getMessage()); +                } +            }              // Iterate over all provider sessions and invoke the request              providerSessions.forEach(providerGetSession -> { @@ -212,7 +220,17 @@ public final class CredentialManagerService              // Initiate all provider sessions              List<ProviderSession> providerSessions =                      initiateProviderSessions(session, List.of(request.getType())); -            // TODO : Return error when no providers available + +            if (providerSessions.isEmpty()) { +                try { +                    // TODO("Replace with properly defined error type") +                    callback.onError("unknown_type", +                            "No providers available to fulfill request."); +                } catch (RemoteException e) { +                    Log.i(TAG, "Issue invoking onError on ICreateCredentialCallback " +                            + "callback: " + e.getMessage()); +                } +            }              // Iterate over all provider sessions and invoke the request              providerSessions.forEach( diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java index c092b3af9699..6e070f940060 100644 --- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java @@ -88,15 +88,27 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest      @Override      public void onFinalResponseReceived(ComponentName componentName, -            GetCredentialResponse response) { +            @Nullable GetCredentialResponse response) {          Log.i(TAG, "onFinalCredentialReceived from: " + componentName.flattenToString());          if (response != null) { -            respondToClientAndFinish(response); +            respondToClientWithResponseAndFinish(response); +        } else { +            // TODO("Replace with no credentials/unknown type when ready) +            respondToClientWithErrorAndFinish("unknown_type", +                    "Invalid response from provider");          }      } -    private void respondToClientAndFinish(GetCredentialResponse response) { -        Log.i(TAG, "respondToClientAndFinish"); +    //TODO: Try moving the three error & response methods below to RequestSession to be shared +    // between get & create. +    @Override +    public void onFinalErrorReceived(ComponentName componentName, String errorType, +            String message) { +        respondToClientWithErrorAndFinish(errorType, message); +    } + +    private void respondToClientWithResponseAndFinish(GetCredentialResponse response) { +        Log.i(TAG, "respondToClientWithResponseAndFinish");          try {              mClientCallback.onResponse(response);          } catch (RemoteException e) { @@ -104,4 +116,21 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest          }          finishSession();      } + +    private void respondToClientWithErrorAndFinish(String errorType, String errorMsg) { +        Log.i(TAG, "respondToClientWithErrorAndFinish"); +        try { +            mClientCallback.onError(errorType, errorMsg); +        } catch (RemoteException e) { +            e.printStackTrace(); +        } +        finishSession(); +    } + +    @Override +    public void onUiCancellation() { +        // TODO("Replace with properly defined error type") +        respondToClientWithErrorAndFinish("user_canceled", +                "User cancelled the selector"); +    }  } diff --git a/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java b/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java index 7f9e57a5bdb6..8796314b5454 100644 --- a/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java +++ b/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java @@ -18,7 +18,9 @@ package com.android.server.credentials;  import android.app.Activity;  import android.content.Intent; +import android.credentials.CreateCredentialException;  import android.credentials.CreateCredentialResponse; +import android.credentials.GetCredentialException;  import android.credentials.GetCredentialResponse;  import android.credentials.ui.ProviderPendingIntentResponse;  import android.service.credentials.CredentialProviderService; @@ -31,7 +33,7 @@ import android.service.credentials.CredentialsResponseContent;   */  public class PendingIntentResultHandler {      /** Returns true if the result is successful and may contain result extras. */ -    public static boolean isSuccessfulResponse( +    public static boolean isValidResponse(              ProviderPendingIntentResponse pendingIntentResponse) {          //TODO: Differentiate based on extra_error in the resultData          return pendingIntentResponse.getResultCode() == Activity.RESULT_OK; @@ -66,4 +68,28 @@ public class PendingIntentResultHandler {                  CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,                  GetCredentialResponse.class);      } + +    /** Extract the {@link CreateCredentialException} from the +     * given pending intent . */ +    public static CreateCredentialException extractCreateCredentialException( +            Intent resultData) { +        if (resultData == null) { +            return null; +        } +        return resultData.getParcelableExtra( +                CredentialProviderService.EXTRA_CREATE_CREDENTIAL_EXCEPTION, +                CreateCredentialException.class); +    } + +    /** Extract the {@link GetCredentialException} from the +     * given pending intent . */ +    public static GetCredentialException extractGetCredentialException( +            Intent resultData) { +        if (resultData == null) { +            return null; +        } +        return resultData.getParcelableExtra( +                CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, +                GetCredentialException.class); +    }  } diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java index 32e85b099c22..855f274d5c98 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java @@ -23,6 +23,7 @@ import android.app.PendingIntent;  import android.content.Context;  import android.content.Intent;  import android.content.pm.Signature; +import android.credentials.CreateCredentialException;  import android.credentials.ui.CreateCredentialProviderData;  import android.credentials.ui.Entry;  import android.credentials.ui.ProviderPendingIntentResponse; @@ -58,6 +59,8 @@ public final class ProviderCreateSession extends ProviderSession<      /** The complete request to be used in the second round. */      private final CreateCredentialRequest mCompleteRequest; +    private CreateCredentialException mProviderException; +      /** Creates a new provider session to be used by the request session. */      @Nullable public static ProviderCreateSession createNewSession(              Context context, @@ -124,8 +127,11 @@ public final class ProviderCreateSession extends ProviderSession<      /** Called when the provider response resulted in a failure. */      @Override -    public void onProviderResponseFailure(int errorCode, @Nullable String errorType, -            @Nullable CharSequence message) { +    public void onProviderResponseFailure(int errorCode, @Nullable Exception exception) { +        if (exception instanceof CreateCredentialException) { +            // Store query phase exception for aggregation with final response +            mProviderException = (CreateCredentialException) exception; +        }          updateStatusAndInvokeCallback(toStatus(errorCode));      } @@ -177,16 +183,20 @@ public final class ProviderCreateSession extends ProviderSession<                  if (mUiSaveEntries.containsKey(entryKey)) {                      onSaveEntrySelected(providerPendingIntentResponse);                  } else { -                    //TODO: Handle properly                      Log.i(TAG, "Unexpected save entry key"); +                    // TODO("Replace with no credentials error type"); +                    invokeCallbackWithError("unknown_type", +                            "Issue while retrieving credential");                  }                  break;              case REMOTE_ENTRY_KEY:                  if (mUiRemoteEntry.first.equals(entryKey)) {                      onRemoteEntrySelected(providerPendingIntentResponse);                  } else { -                    //TODO: Handle properly                      Log.i(TAG, "Unexpected remote entry key"); +                    // TODO("Replace with unknown/no credentials exception") +                    invokeCallbackWithError("unknown_type", +                            "Issue while retrieving credential");                  }                  break;              default: @@ -227,19 +237,53 @@ public final class ProviderCreateSession extends ProviderSession<      }      private void onSaveEntrySelected(ProviderPendingIntentResponse pendingIntentResponse) { -        if (pendingIntentResponse == null) { +        CreateCredentialException exception = maybeGetPendingIntentException( +                pendingIntentResponse); +        if (exception != null) { +            invokeCallbackWithError( +                    exception.errorType, +                    exception.getMessage()); +            return; +        } +        android.credentials.CreateCredentialResponse credentialResponse = +                PendingIntentResultHandler.extractCreateCredentialResponse( +                        pendingIntentResponse.getResultData()); +        if (credentialResponse != null) { +            mCallbacks.onFinalResponseReceived(mComponentName, credentialResponse);              return; -            //TODO: Handle failure if pending intent is null +        } else { +            Log.i(TAG, "onSaveEntrySelected - no response or error found in pending " +                    + "intent response"); +            invokeCallbackWithError( +                    // TODO("Replace with unknown/no credentials exception") +                    "unknown", +                    "Issue encountered while retrieving the credential"); +        } +    } + +    private void invokeCallbackWithError(String errorType, @Nullable String message) { +        mCallbacks.onFinalErrorReceived(mComponentName, errorType, message); +    } + +    @Nullable +    private CreateCredentialException maybeGetPendingIntentException( +            ProviderPendingIntentResponse pendingIntentResponse) { +        if (pendingIntentResponse == null) { +            Log.i(TAG, "pendingIntentResponse is null"); +            return null;          } -        if (PendingIntentResultHandler.isSuccessfulResponse(pendingIntentResponse)) { -            android.credentials.CreateCredentialResponse credentialResponse = -                    PendingIntentResultHandler.extractCreateCredentialResponse( -                            pendingIntentResponse.getResultData()); -            if (credentialResponse != null) { -                mCallbacks.onFinalResponseReceived(mComponentName, credentialResponse); -                return; +        if (PendingIntentResultHandler.isValidResponse(pendingIntentResponse)) { +            CreateCredentialException exception = PendingIntentResultHandler +                    .extractCreateCredentialException(pendingIntentResponse.getResultData()); +            if (exception != null) { +                Log.i(TAG, "Pending intent contains provider exception"); +                return exception;              } +        } else { +            Log.i(TAG, "Pending intent result code not Activity.RESULT_OK"); +            // TODO("Update with unknown exception when ready") +            return new CreateCredentialException("unknown");          } -        //TODO: Handle failure case is pending intent response does not have a credential +        return null;      }  } diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index f55c457b3122..42f872d2659c 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -22,7 +22,7 @@ import android.annotation.UserIdInt;  import android.app.PendingIntent;  import android.content.Context;  import android.content.Intent; -import android.content.pm.Signature; +import android.credentials.GetCredentialException;  import android.credentials.GetCredentialOption;  import android.credentials.GetCredentialResponse;  import android.credentials.ui.Entry; @@ -80,6 +80,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential      /** The complete request to be used in the second round. */      private final GetCredentialRequest mCompleteRequest; +    private GetCredentialException mProviderException; +      /** Creates a new provider session to be used by the request session. */      @Nullable public static ProviderGetSession createNewSession(              Context context, @@ -131,7 +133,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential          if (!filteredOptions.isEmpty()) {              return new GetCredentialRequest.Builder(                      new CallingAppInfo(clientCallingPackage, -                            new ArraySet<Signature>())).setGetCredentialOptions( +                            new ArraySet<>())).setGetCredentialOptions(                      filteredOptions).build();          }          Log.i(TAG, "In createProviderRequest - returning null"); @@ -164,8 +166,10 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential      /** Called when the provider response resulted in a failure. */      @Override // Callback from the remote provider -    public void onProviderResponseFailure(int errorCode, @Nullable String errorType, -            @Nullable CharSequence message) { +    public void onProviderResponseFailure(int errorCode, Exception exception) { +        if (exception instanceof GetCredentialException) { +            mProviderException = (GetCredentialException) exception; +        }          updateStatusAndInvokeCallback(toStatus(errorCode));      } @@ -187,8 +191,10 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential              case CREDENTIAL_ENTRY_KEY:                  CredentialEntry credentialEntry = mUiCredentialEntries.get(entryKey);                  if (credentialEntry == null) { -                    Log.i(TAG, "Credential entry not found"); -                    //TODO: Handle properly +                    Log.i(TAG, "Unexpected credential entry key"); +                    // TODO("Replace with no credentials/unknown exception") +                    invokeCallbackWithError("unknown_type", +                            "Issue while retrieving credential");                      return;                  }                  onCredentialEntrySelected(credentialEntry, providerPendingIntentResponse); @@ -196,8 +202,10 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential              case ACTION_ENTRY_KEY:                  Action actionEntry = mUiActionsEntries.get(entryKey);                  if (actionEntry == null) { -                    Log.i(TAG, "Action entry not found"); -                    //TODO: Handle properly +                    Log.i(TAG, "Unexpected action entry key"); +                    // TODO("Replace with no credentials/unknown exception") +                    invokeCallbackWithError("unknown_type", +                            "Issue while retrieving credential");                      return;                  }                  onActionEntrySelected(providerPendingIntentResponse); @@ -206,16 +214,20 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential                  if (mUiAuthenticationAction.first.equals(entryKey)) {                      onAuthenticationEntrySelected(providerPendingIntentResponse);                  } else { -                    //TODO: Handle properly -                    Log.i(TAG, "Authentication entry not found"); +                    Log.i(TAG, "Unexpected authentication entry key"); +                    // TODO("Replace with no credentials/unknown exception") +                    invokeCallbackWithError("unknown_type", +                            "Issue while retrieving credential");                  }                  break;              case REMOTE_ENTRY_KEY:                  if (mUiRemoteEntry.first.equals(entryKey)) {                      onRemoteEntrySelected(providerPendingIntentResponse);                  } else { -                    //TODO: Handle properly -                    Log.i(TAG, "Remote entry not found"); +                    Log.i(TAG, "Unexpected remote entry key"); +                    // TODO("Replace with no credentials/unknown exception") +                    invokeCallbackWithError("unknown_type", +                            "Issue while retrieving credential");                  }                  break;              default: @@ -223,6 +235,11 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential          }      } +    private void invokeCallbackWithError(String errorType, @Nullable String errorMessage) { +        // TODO: Determine what the error message should be +        mCallbacks.onFinalErrorReceived(mComponentName, errorType, errorMessage); +    } +      @Override // Call from request session to data to be shown on the UI      @Nullable protected GetCredentialProviderData prepareUiData() throws IllegalArgumentException {          Log.i(TAG, "In prepareUiData"); @@ -329,39 +346,61 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential      private void onCredentialEntrySelected(CredentialEntry credentialEntry,              ProviderPendingIntentResponse providerPendingIntentResponse) {          if (providerPendingIntentResponse != null) { -            if (PendingIntentResultHandler.isSuccessfulResponse(providerPendingIntentResponse)) { -                // TODO: Remove credential extraction when flow is fully transitioned -                GetCredentialResponse getCredentialResponse = PendingIntentResultHandler -                        .extractGetCredentialResponse( -                                providerPendingIntentResponse.getResultData()); -                if (getCredentialResponse != null) { -                    mCallbacks.onFinalResponseReceived(mComponentName, getCredentialResponse); -                    return; -                } +            // Check if pending intent has an error +            GetCredentialException exception = maybeGetPendingIntentException( +                    providerPendingIntentResponse); +            if (exception != null) { +                invokeCallbackWithError(exception.errorType, +                        exception.getMessage()); +                return;              } -            // TODO: Handle other pending intent statuses + +            // Check if pending intent has a credential +            GetCredentialResponse getCredentialResponse = PendingIntentResultHandler +                    .extractGetCredentialResponse( +                            providerPendingIntentResponse.getResultData()); +            if (getCredentialResponse != null) { +                mCallbacks.onFinalResponseReceived(mComponentName, +                        getCredentialResponse); +                return; +            } + +            Log.i(TAG, "Pending intent response contains no credential, or error"); +            // TODO("Replace with no credentials/unknown error when ready) +            invokeCallbackWithError("unknown_type", +                    "Issue while retrieving credential");          }          Log.i(TAG, "CredentialEntry does not have a credential or a pending intent result"); -        // TODO: Propagate failure to client +        // TODO("Replace with no credentials/unknown error when ready) +        invokeCallbackWithError("unknown_type", +                "Error encountered while retrieving the credential");      }      private void onAuthenticationEntrySelected(              @Nullable ProviderPendingIntentResponse providerPendingIntentResponse) { -        if (providerPendingIntentResponse != null) { -            if (PendingIntentResultHandler.isSuccessfulResponse(providerPendingIntentResponse)) { -                CredentialsResponseContent content = PendingIntentResultHandler -                        .extractResponseContent(providerPendingIntentResponse -                                .getResultData()); -                if (content != null) { -                    onUpdateResponse( -                            BeginGetCredentialResponse.createWithResponseContent(content)); -                    return; -                } -            }              //TODO: Other provider intent statuses +        // Check if pending intent has an error +        GetCredentialException exception = maybeGetPendingIntentException( +                providerPendingIntentResponse); +        if (exception != null) { +            invokeCallbackWithError(exception.errorType, +                    exception.getMessage()); +            return;          } -        Log.i(TAG, "Display content not present in pending intent result"); -        // TODO: Propagate error to client + +        // Check if pending intent has the content +        CredentialsResponseContent content = PendingIntentResultHandler +                .extractResponseContent(providerPendingIntentResponse +                        .getResultData()); +        if (content != null) { +            onUpdateResponse(BeginGetCredentialResponse.createWithResponseContent(content)); +            return; +        } + +        Log.i(TAG, "No error or respond found in pending intent response"); +        // TODO("Replace with no credentials/unknown error when ready) +        invokeCallbackWithError("unknown type", "Issue" +                + " while retrieving credential");      }      private void onActionEntrySelected(ProviderPendingIntentResponse @@ -382,4 +421,26 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential              updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED);          }      } + +    @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 { +            Log.i(TAG, "Pending intent result code not Activity.RESULT_OK"); +            // TODO("Update with unknown exception when ready") +            return new GetCredentialException("unknown"); +        } +        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 ac360bd4fc9e..7ecae9d3bec9 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java @@ -24,7 +24,6 @@ import android.credentials.Credential;  import android.credentials.ui.ProviderData;  import android.credentials.ui.ProviderPendingIntentResponse;  import android.service.credentials.CredentialEntry; -import android.service.credentials.CredentialProviderException;  import android.service.credentials.CredentialProviderInfo;  import android.util.Pair; @@ -37,6 +36,8 @@ import java.util.UUID;   */  public abstract class ProviderSession<T, R>          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"; @@ -52,6 +53,7 @@ public abstract class ProviderSession<T, R>      @Nullable protected R mProviderResponse;      @Nullable protected Pair<String, CredentialEntry> mUiRemoteEntry; +      /**       * Returns true if the given status reflects that the provider state is ready to be shown       * on the credMan UI. @@ -95,8 +97,12 @@ public abstract class ProviderSession<T, R>          /** Called when status changes. */          void onProviderStatusChanged(Status status, ComponentName componentName); -        /** Called when the final credential to be returned to the client has been received. */ +        /** Called when the final credential is received through an entry selection. */          void onFinalResponseReceived(ComponentName componentName, V response); + +        /** Called when an error is received through an entry selection. */ +        void onFinalErrorReceived(ComponentName componentName, String errorType, +                @Nullable String message);      }      protected ProviderSession(@NonNull Context context, @NonNull CredentialProviderInfo info, @@ -129,8 +135,7 @@ public abstract class ProviderSession<T, R>      /** Converts exception to a provider session status. */      @NonNull -    public static Status toStatus( -            @CredentialProviderException.CredentialProviderError int errorCode) { +    public static Status toStatus(int errorCode) {          // TODO : Add more mappings as more flows are supported          return Status.CANCELED;      } diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java index 307d96a3bded..6049fd9a4a21 100644 --- a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java +++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java @@ -30,7 +30,7 @@ import android.service.credentials.BeginCreateCredentialRequest;  import android.service.credentials.BeginCreateCredentialResponse;  import android.service.credentials.BeginGetCredentialRequest;  import android.service.credentials.BeginGetCredentialResponse; -import android.service.credentials.CredentialProviderException; +import android.service.credentials.CredentialProviderErrors;  import android.service.credentials.CredentialProviderService;  import android.service.credentials.IBeginCreateCredentialCallback;  import android.service.credentials.IBeginGetCredentialCallback; @@ -72,8 +72,7 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr          /** Called when a successful response is received from the remote provider. */          void onProviderResponseSuccess(@Nullable T response);          /** Called when a failure response is received from the remote provider. */ -        void onProviderResponseFailure(int errorCode, @Nullable String errorType, -                @Nullable CharSequence message); +        void onProviderResponseFailure(int internalErrorCode, @Nullable Exception e);          /** Called when the remote provider service dies. */          void onProviderServiceDied(RemoteCredentialService service);      } @@ -208,36 +207,31 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr                  Log.i(TAG, "In RemoteCredentialService execute error is timeout");                  dispatchCancellationSignal(cancellationSink.get());                  callback.onProviderResponseFailure( -                        CredentialProviderException.ERROR_TIMEOUT, -                        null, -                        error.getMessage()); +                        CredentialProviderErrors.ERROR_TIMEOUT, +                        null);              } else if (error instanceof CancellationException) {                  Log.i(TAG, "In RemoteCredentialService execute error is cancellation");                  dispatchCancellationSignal(cancellationSink.get());                  callback.onProviderResponseFailure( -                        CredentialProviderException.ERROR_TASK_CANCELED, -                        null, -                        error.getMessage()); +                        CredentialProviderErrors.ERROR_TASK_CANCELED, +                        null);              } else if (error instanceof GetCredentialException) {                  Log.i(TAG, "In RemoteCredentialService execute error is provider get"                          + "error");                  callback.onProviderResponseFailure( -                        CredentialProviderException.ERROR_PROVIDER_FAILURE, -                        ((GetCredentialException) error).errorType, -                        error.getMessage()); +                        CredentialProviderErrors.ERROR_PROVIDER_FAILURE, +                        (GetCredentialException) error);              } else if (error instanceof CreateCredentialException) {                  Log.i(TAG, "In RemoteCredentialService execute error is provider create "                          + "error");                  callback.onProviderResponseFailure( -                        CredentialProviderException.ERROR_PROVIDER_FAILURE, -                        ((CreateCredentialException) error).errorType, -                        error.getMessage()); +                        CredentialProviderErrors.ERROR_PROVIDER_FAILURE, +                        (CreateCredentialException) error);              } else {                  Log.i(TAG, "In RemoteCredentialService execute error is unknown");                  callback.onProviderResponseFailure( -                        CredentialProviderException.ERROR_UNKNOWN, -                        null, -                        error.getMessage()); +                        CredentialProviderErrors.ERROR_UNKNOWN, +                        (Exception) error);              }          }      } diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java index 71fc67ce5afd..937fac98a0b3 100644 --- a/services/credentials/java/com/android/server/credentials/RequestSession.java +++ b/services/credentials/java/com/android/server/credentials/RequestSession.java @@ -93,6 +93,7 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan      @Override // from CredentialManagerUiCallbacks      public void onUiCancellation() { +        Log.i(TAG, "Ui canceled");          // User canceled the activity          finishSession();      } @@ -133,10 +134,12 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan      }      protected void finishSession() { +        Log.i(TAG, "finishing session");          clearProviderSessions();      }      protected void clearProviderSessions() { +        Log.i(TAG, "Clearing sessions");          //TODO: Implement          mProviders.clear();      } @@ -151,6 +154,9 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan      }      private void getProviderDataAndInitiateUi() { +        Log.i(TAG, "In getProviderDataAndInitiateUi"); +        Log.i(TAG, "In getProviderDataAndInitiateUi providers size: " + mProviders.size()); +          ArrayList<ProviderData> providerDataList = new ArrayList<>();          for (ProviderSession session : mProviders.values()) {              Log.i(TAG, "preparing data for : " + session.getComponentName());  |