Add error propagation piping for get & create
Test: Built & deployed locally Change-Id: I67b57b228722ffce8a10e9269ef53ffa35fc18fc
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
//TODO: Handle failure if pending intent is null
|
||||
}
|
||||
if (PendingIntentResultHandler.isSuccessfulResponse(pendingIntentResponse)) {
|
||||
android.credentials.CreateCredentialResponse credentialResponse =
|
||||
PendingIntentResultHandler.extractCreateCredentialResponse(
|
||||
pendingIntentResponse.getResultData());
|
||||
if (credentialResponse != null) {
|
||||
mCallbacks.onFinalResponseReceived(mComponentName, credentialResponse);
|
||||
return;
|
||||
android.credentials.CreateCredentialResponse credentialResponse =
|
||||
PendingIntentResultHandler.extractCreateCredentialResponse(
|
||||
pendingIntentResponse.getResultData());
|
||||
if (credentialResponse != null) {
|
||||
mCallbacks.onFinalResponseReceived(mComponentName, credentialResponse);
|
||||
return;
|
||||
} 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.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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
@@ -330,39 +347,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
|
||||
@@ -383,4 +422,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user