diff options
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()); |