Propagate ancellation signal to cred man service
If developer cancels the get/create request, we propagate to the remote cancellation signal and clear the session. This change includes manually checking before sending back a response to the develoeper, before invoking the UI and before processing the repsonse of UI, and then propagating the cancellation signal to the providers. Could follow up with adding a listener that proactively cancels the session in addition to the hook points checked manually. Test: Built & deployed locally Bug: 264944428 Change-Id: Ia4c0c4d0e758433400d4f086b482fdb273120968
This commit is contained in:
@@ -23,6 +23,7 @@ import android.credentials.ClearCredentialStateRequest;
|
||||
import android.credentials.IClearCredentialStateCallback;
|
||||
import android.credentials.ui.ProviderData;
|
||||
import android.credentials.ui.RequestInfo;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.RemoteException;
|
||||
import android.service.credentials.CallingAppInfo;
|
||||
import android.service.credentials.CredentialProviderInfo;
|
||||
@@ -41,9 +42,9 @@ public final class ClearRequestSession extends RequestSession<ClearCredentialSta
|
||||
|
||||
public ClearRequestSession(Context context, int userId, int callingUid,
|
||||
IClearCredentialStateCallback callback, ClearCredentialStateRequest request,
|
||||
CallingAppInfo callingAppInfo) {
|
||||
CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal) {
|
||||
super(context, userId, callingUid, request, callback, RequestInfo.TYPE_UNDEFINED,
|
||||
callingAppInfo);
|
||||
callingAppInfo, cancellationSignal);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,6 +112,12 @@ public final class ClearRequestSession extends RequestSession<ClearCredentialSta
|
||||
|
||||
private void respondToClientWithResponseAndFinish() {
|
||||
Log.i(TAG, "respondToClientWithResponseAndFinish");
|
||||
if (isSessionCancelled()) {
|
||||
// TODO: Differentiate btw cancelled and false
|
||||
logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ true);
|
||||
finishSession(/*propagateCancellation=*/true);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mClientCallback.onSuccess();
|
||||
logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ true);
|
||||
@@ -118,18 +125,24 @@ public final class ClearRequestSession extends RequestSession<ClearCredentialSta
|
||||
Log.i(TAG, "Issue while propagating the response to the client");
|
||||
logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ false);
|
||||
}
|
||||
finishSession();
|
||||
finishSession(/*propagateCancellation=*/false);
|
||||
}
|
||||
|
||||
private void respondToClientWithErrorAndFinish(String errorType, String errorMsg) {
|
||||
Log.i(TAG, "respondToClientWithErrorAndFinish");
|
||||
if (isSessionCancelled()) {
|
||||
// TODO: Differentiate btw cancelled and false
|
||||
logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ true);
|
||||
finishSession(/*propagateCancellation=*/true);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mClientCallback.onError(errorType, errorMsg);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ false);
|
||||
finishSession();
|
||||
finishSession(/*propagateCancellation=*/false);
|
||||
}
|
||||
|
||||
private void processResponses() {
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.credentials.CredentialManager;
|
||||
import android.credentials.ICreateCredentialCallback;
|
||||
import android.credentials.ui.ProviderData;
|
||||
import android.credentials.ui.RequestInfo;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.RemoteException;
|
||||
import android.service.credentials.CallingAppInfo;
|
||||
import android.service.credentials.CredentialProviderInfo;
|
||||
@@ -47,9 +48,10 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
|
||||
CreateRequestSession(@NonNull Context context, int userId, int callingUid,
|
||||
CreateCredentialRequest request,
|
||||
ICreateCredentialCallback callback,
|
||||
CallingAppInfo callingAppInfo) {
|
||||
CallingAppInfo callingAppInfo,
|
||||
CancellationSignal cancellationSignal) {
|
||||
super(context, userId, callingUid, request, callback, RequestInfo.TYPE_CREATE,
|
||||
callingAppInfo);
|
||||
callingAppInfo, cancellationSignal);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,6 +121,12 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
|
||||
|
||||
private void respondToClientWithResponseAndFinish(CreateCredentialResponse response) {
|
||||
Log.i(TAG, "respondToClientWithResponseAndFinish");
|
||||
if (isSessionCancelled()) {
|
||||
// TODO: Differentiate btw cancelled and false
|
||||
logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ true);
|
||||
finishSession(/*propagateCancellation=*/true);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mClientCallback.onResponse(response);
|
||||
logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ true);
|
||||
@@ -126,18 +134,24 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
|
||||
Log.i(TAG, "Issue while responding to client: " + e.getMessage());
|
||||
logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ false);
|
||||
}
|
||||
finishSession();
|
||||
finishSession(/*propagateCancellation=*/false);
|
||||
}
|
||||
|
||||
private void respondToClientWithErrorAndFinish(String errorType, String errorMsg) {
|
||||
Log.i(TAG, "respondToClientWithErrorAndFinish");
|
||||
if (isSessionCancelled()) {
|
||||
// TODO: Differentiate btw cancelled and false
|
||||
logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ true);
|
||||
finishSession(/*propagateCancellation=*/true);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mClientCallback.onError(errorType, errorMsg);
|
||||
} catch (RemoteException e) {
|
||||
Log.i(TAG, "Issue while responding to client: " + e.getMessage());
|
||||
}
|
||||
logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ false);
|
||||
finishSession();
|
||||
finishSession(/*propagateCancellation=*/false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -196,7 +196,6 @@ public final class CredentialManagerService
|
||||
}
|
||||
|
||||
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private List<CredentialManagerServiceImpl> getOrConstructSystemServiceListLock(
|
||||
int resolvedUserId) {
|
||||
@@ -338,7 +337,8 @@ public final class CredentialManagerService
|
||||
callingUid,
|
||||
callback,
|
||||
request,
|
||||
constructCallingAppInfo(callingPackage, userId));
|
||||
constructCallingAppInfo(callingPackage, userId),
|
||||
CancellationSignal.fromTransport(cancelTransport));
|
||||
|
||||
// Initiate all provider sessions
|
||||
List<ProviderSession> providerSessions =
|
||||
@@ -360,8 +360,6 @@ public final class CredentialManagerService
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over all provider sessions and invoke the request
|
||||
providerSessions.forEach(ProviderSession::invokeSession);
|
||||
return cancelTransport;
|
||||
}
|
||||
@@ -385,7 +383,8 @@ public final class CredentialManagerService
|
||||
callingUid,
|
||||
request,
|
||||
callback,
|
||||
constructCallingAppInfo(callingPackage, userId));
|
||||
constructCallingAppInfo(callingPackage, userId),
|
||||
CancellationSignal.fromTransport(cancelTransport));
|
||||
|
||||
// Initiate all provider sessions
|
||||
List<ProviderSession> providerSessions =
|
||||
@@ -405,8 +404,7 @@ public final class CredentialManagerService
|
||||
}
|
||||
|
||||
// Iterate over all provider sessions and invoke the request
|
||||
providerSessions.forEach(
|
||||
ProviderSession::invokeSession);
|
||||
providerSessions.forEach(ProviderSession::invokeSession);
|
||||
return cancelTransport;
|
||||
}
|
||||
|
||||
@@ -497,7 +495,8 @@ public final class CredentialManagerService
|
||||
callingUid,
|
||||
callback,
|
||||
request,
|
||||
constructCallingAppInfo(callingPackage, userId));
|
||||
constructCallingAppInfo(callingPackage, userId),
|
||||
CancellationSignal.fromTransport(cancelTransport));
|
||||
|
||||
// Initiate all provider sessions
|
||||
// TODO: Determine if provider needs to have clear capability in their manifest
|
||||
@@ -518,8 +517,7 @@ public final class CredentialManagerService
|
||||
}
|
||||
|
||||
// Iterate over all provider sessions and invoke the request
|
||||
providerSessions.forEach(
|
||||
ProviderSession::invokeSession);
|
||||
providerSessions.forEach(ProviderSession::invokeSession);
|
||||
return cancelTransport;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.credentials.GetCredentialResponse;
|
||||
import android.credentials.IGetCredentialCallback;
|
||||
import android.credentials.ui.ProviderData;
|
||||
import android.credentials.ui.RequestInfo;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.RemoteException;
|
||||
import android.service.credentials.CallingAppInfo;
|
||||
import android.service.credentials.CredentialProviderInfo;
|
||||
@@ -43,8 +44,9 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest
|
||||
|
||||
public GetRequestSession(Context context, int userId, int callingUid,
|
||||
IGetCredentialCallback callback, GetCredentialRequest request,
|
||||
CallingAppInfo callingAppInfo) {
|
||||
super(context, userId, callingUid, request, callback, RequestInfo.TYPE_GET, callingAppInfo);
|
||||
CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal) {
|
||||
super(context, userId, callingUid, request, callback, RequestInfo.TYPE_GET,
|
||||
callingAppInfo, cancellationSignal);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,6 +104,12 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest
|
||||
}
|
||||
|
||||
private void respondToClientWithResponseAndFinish(GetCredentialResponse response) {
|
||||
if (isSessionCancelled()) {
|
||||
// TODO: Differentiate btw cancelled and false
|
||||
logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ false);
|
||||
finishSession(/*propagateCancellation=*/true);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mClientCallback.onResponse(response);
|
||||
logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ true);
|
||||
@@ -109,18 +117,22 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest
|
||||
Log.i(TAG, "Issue while responding to client with a response : " + e.getMessage());
|
||||
logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ false);
|
||||
}
|
||||
finishSession();
|
||||
finishSession(/*propagateCancellation=*/false);
|
||||
}
|
||||
|
||||
private void respondToClientWithErrorAndFinish(String errorType, String errorMsg) {
|
||||
if (isSessionCancelled()) {
|
||||
logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ false);
|
||||
finishSession(/*propagateCancellation=*/true);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mClientCallback.onError(errorType, errorMsg);
|
||||
} catch (RemoteException e) {
|
||||
Log.i(TAG, "Issue while responding to client with error : " + e.getMessage());
|
||||
|
||||
}
|
||||
logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ false);
|
||||
finishSession();
|
||||
finishSession(/*propagateCancellation=*/false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,8 +23,11 @@ import android.content.Context;
|
||||
import android.credentials.Credential;
|
||||
import android.credentials.ui.ProviderData;
|
||||
import android.credentials.ui.ProviderPendingIntentResponse;
|
||||
import android.os.ICancellationSignal;
|
||||
import android.os.RemoteException;
|
||||
import android.service.credentials.CredentialEntry;
|
||||
import android.service.credentials.CredentialProviderInfo;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.UUID;
|
||||
@@ -49,6 +52,7 @@ public abstract class ProviderSession<T, R>
|
||||
@NonNull protected Status mStatus = Status.NOT_STARTED;
|
||||
@NonNull protected final ProviderInternalCallback mCallbacks;
|
||||
@Nullable protected Credential mFinalCredentialResponse;
|
||||
@Nullable protected ICancellationSignal mProviderCancellationSignal;
|
||||
@NonNull protected final T mProviderRequest;
|
||||
@Nullable protected R mProviderResponse;
|
||||
@NonNull protected Boolean mProviderResponseSet = false;
|
||||
@@ -151,6 +155,18 @@ public abstract class ProviderSession<T, R>
|
||||
return mFinalCredentialResponse;
|
||||
}
|
||||
|
||||
/** Propagates cancellation signal to the remote provider service. */
|
||||
public void cancelProviderRemoteSession() {
|
||||
try {
|
||||
if (mProviderCancellationSignal != null) {
|
||||
mProviderCancellationSignal.cancel();
|
||||
}
|
||||
setStatus(Status.CANCELED);
|
||||
} catch (RemoteException e) {
|
||||
Log.i(TAG, "Issue while cancelling provider session: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected void setStatus(@NonNull Status status) {
|
||||
mStatus = status;
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr
|
||||
* @param callback the callback to be used to send back the provider response to the
|
||||
* {@link ProviderGetSession} class that maintains provider state
|
||||
*/
|
||||
public void onBeginGetCredential(@NonNull BeginGetCredentialRequest request,
|
||||
public ICancellationSignal onBeginGetCredential(@NonNull BeginGetCredentialRequest request,
|
||||
ProviderCallbacks<BeginGetCredentialResponse> callback) {
|
||||
Log.i(TAG, "In onGetCredentials in RemoteCredentialService");
|
||||
AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
|
||||
@@ -149,6 +149,8 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr
|
||||
futureRef.set(connectThenExecute);
|
||||
connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() ->
|
||||
handleExecutionResponse(result, error, cancellationSink, callback)));
|
||||
|
||||
return cancellationSink.get();
|
||||
}
|
||||
|
||||
/** Main entry point to be called for executing a beginCreateCredential call on the remote
|
||||
@@ -157,7 +159,7 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr
|
||||
* @param callback the callback to be used to send back the provider response to the
|
||||
* {@link ProviderCreateSession} class that maintains provider state
|
||||
*/
|
||||
public void onCreateCredential(@NonNull BeginCreateCredentialRequest request,
|
||||
public ICancellationSignal onCreateCredential(@NonNull BeginCreateCredentialRequest request,
|
||||
ProviderCallbacks<BeginCreateCredentialResponse> callback) {
|
||||
Log.i(TAG, "In onCreateCredential in RemoteCredentialService");
|
||||
AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
|
||||
@@ -196,6 +198,8 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr
|
||||
futureRef.set(connectThenExecute);
|
||||
connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() ->
|
||||
handleExecutionResponse(result, error, cancellationSink, callback)));
|
||||
|
||||
return cancellationSink.get();
|
||||
}
|
||||
|
||||
/** Main entry point to be called for executing a clearCredentialState call on the remote
|
||||
@@ -204,7 +208,7 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr
|
||||
* @param callback the callback to be used to send back the provider response to the
|
||||
* {@link ProviderClearSession} class that maintains provider state
|
||||
*/
|
||||
public void onClearCredentialState(@NonNull ClearCredentialStateRequest request,
|
||||
public ICancellationSignal onClearCredentialState(@NonNull ClearCredentialStateRequest request,
|
||||
ProviderCallbacks<Void> callback) {
|
||||
Log.i(TAG, "In onClearCredentialState in RemoteCredentialService");
|
||||
AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
|
||||
@@ -243,6 +247,8 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr
|
||||
futureRef.set(connectThenExecute);
|
||||
connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() ->
|
||||
handleExecutionResponse(result, error, cancellationSink, callback)));
|
||||
|
||||
return cancellationSink.get();
|
||||
}
|
||||
|
||||
private <T> void handleExecutionResponse(T result,
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.content.Context;
|
||||
import android.credentials.ui.ProviderData;
|
||||
import android.credentials.ui.UserSelectionDialogResult;
|
||||
import android.os.Binder;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
@@ -83,13 +84,16 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan
|
||||
private final int mCallingUid;
|
||||
@NonNull
|
||||
protected final CallingAppInfo mClientAppInfo;
|
||||
@NonNull
|
||||
protected final CancellationSignal mCancellationSignal;
|
||||
|
||||
protected final Map<String, ProviderSession> mProviders = new HashMap<>();
|
||||
|
||||
protected RequestSession(@NonNull Context context,
|
||||
@UserIdInt int userId, int callingUid, @NonNull T clientRequest, U clientCallback,
|
||||
@NonNull String requestType,
|
||||
CallingAppInfo callingAppInfo) {
|
||||
CallingAppInfo callingAppInfo,
|
||||
CancellationSignal cancellationSignal) {
|
||||
mContext = context;
|
||||
mUserId = userId;
|
||||
mCallingUid = callingUid;
|
||||
@@ -97,6 +101,7 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan
|
||||
mClientCallback = clientCallback;
|
||||
mRequestType = requestType;
|
||||
mClientAppInfo = callingAppInfo;
|
||||
mCancellationSignal = cancellationSignal;
|
||||
mHandler = new Handler(Looper.getMainLooper(), null, true);
|
||||
mRequestId = new Binder();
|
||||
mCredentialManagerUi = new CredentialManagerUi(mContext,
|
||||
@@ -112,6 +117,10 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan
|
||||
|
||||
@Override // from CredentialManagerUiCallbacks
|
||||
public void onUiSelection(UserSelectionDialogResult selection) {
|
||||
if (isSessionCancelled()) {
|
||||
finishSession(/*propagateCancellation=*/true);
|
||||
return;
|
||||
}
|
||||
String providerId = selection.getProviderId();
|
||||
Log.i(TAG, "onUiSelection, providerId: " + providerId);
|
||||
ProviderSession providerSession = mProviders.get(providerId);
|
||||
@@ -127,18 +136,19 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan
|
||||
@Override // from CredentialManagerUiCallbacks
|
||||
public void onUiCancellation(boolean isUserCancellation) {
|
||||
Log.i(TAG, "Ui canceled. Canceled by user: " + isUserCancellation);
|
||||
if (isSessionCancelled()) {
|
||||
finishSession(/*propagateCancellation=*/true);
|
||||
return;
|
||||
}
|
||||
// User canceled the activity
|
||||
finishSession();
|
||||
finishSession(/*propagateCancellation=*/false);
|
||||
}
|
||||
|
||||
protected void finishSession() {
|
||||
protected void finishSession(boolean propagateCancellation) {
|
||||
Log.i(TAG, "finishing session");
|
||||
clearProviderSessions();
|
||||
}
|
||||
|
||||
protected void clearProviderSessions() {
|
||||
Log.i(TAG, "Clearing sessions");
|
||||
//TODO: Implement
|
||||
if (propagateCancellation) {
|
||||
mProviders.values().forEach(ProviderSession::cancelProviderRemoteSession);
|
||||
}
|
||||
mProviders.clear();
|
||||
}
|
||||
|
||||
@@ -178,6 +188,10 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan
|
||||
isSuccessful ? METRICS_API_STATUS_SUCCESS : METRICS_API_STATUS_FAILURE);
|
||||
}
|
||||
|
||||
protected boolean isSessionCancelled() {
|
||||
return mCancellationSignal.isCanceled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if at least one provider is ready for UI invocation, and no
|
||||
* provider is pending a response.
|
||||
@@ -197,6 +211,11 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan
|
||||
Log.i(TAG, "In getProviderDataAndInitiateUi");
|
||||
Log.i(TAG, "In getProviderDataAndInitiateUi providers size: " + mProviders.size());
|
||||
|
||||
if (isSessionCancelled()) {
|
||||
finishSession(/*propagateCancellation=*/true);
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<ProviderData> providerDataList = new ArrayList<>();
|
||||
for (ProviderSession session : mProviders.values()) {
|
||||
Log.i(TAG, "preparing data for : " + session.getComponentName());
|
||||
@@ -208,7 +227,11 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan
|
||||
}
|
||||
if (!providerDataList.isEmpty()) {
|
||||
Log.i(TAG, "provider list not empty about to initiate ui");
|
||||
launchUiWithProviderData(providerDataList);
|
||||
if (isSessionCancelled()) {
|
||||
Log.i(TAG, "In getProviderDataAndInitiateUi but session has been cancelled");
|
||||
} else {
|
||||
launchUiWithProviderData(providerDataList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user