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
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index be60946..447c67f 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -23,6 +23,7 @@
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 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 @@
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 @@
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() {
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 351afb9..2345e3f 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -27,6 +27,7 @@
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 @@
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 @@
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 @@
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
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index ff72ed7..c9da4b2 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -196,7 +196,6 @@
}
-
@GuardedBy("mLock")
private List<CredentialManagerServiceImpl> getOrConstructSystemServiceListLock(
int resolvedUserId) {
@@ -338,7 +337,8 @@
callingUid,
callback,
request,
- constructCallingAppInfo(callingPackage, userId));
+ constructCallingAppInfo(callingPackage, userId),
+ CancellationSignal.fromTransport(cancelTransport));
// Initiate all provider sessions
List<ProviderSession> providerSessions =
@@ -360,8 +360,6 @@
+ e.getMessage());
}
}
-
- // Iterate over all provider sessions and invoke the request
providerSessions.forEach(ProviderSession::invokeSession);
return cancelTransport;
}
@@ -385,7 +383,8 @@
callingUid,
request,
callback,
- constructCallingAppInfo(callingPackage, userId));
+ constructCallingAppInfo(callingPackage, userId),
+ CancellationSignal.fromTransport(cancelTransport));
// Initiate all provider sessions
List<ProviderSession> providerSessions =
@@ -405,8 +404,7 @@
}
// Iterate over all provider sessions and invoke the request
- providerSessions.forEach(
- ProviderSession::invokeSession);
+ providerSessions.forEach(ProviderSession::invokeSession);
return cancelTransport;
}
@@ -497,7 +495,8 @@
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 @@
}
// Iterate over all provider sessions and invoke the request
- providerSessions.forEach(
- ProviderSession::invokeSession);
+ providerSessions.forEach(ProviderSession::invokeSession);
return cancelTransport;
}
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index e3a27ec..e732c23 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -25,6 +25,7 @@
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 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 @@
}
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 @@
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
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index 8e0d6f8..58596b8 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -23,8 +23,11 @@
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 @@
@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 @@
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;
}
diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
index 8cad6ac..2dea8bd 100644
--- a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
+++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
@@ -110,7 +110,7 @@
* @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 @@
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 @@
* @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 @@
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 @@
* @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 @@
futureRef.set(connectThenExecute);
connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() ->
handleExecutionResponse(result, error, cancellationSink, callback)));
+
+ return cancellationSink.get();
}
private <T> void handleExecutionResponse(T result,
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index f92ffe2..9f1bd8f 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -29,6 +29,7 @@
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 @@
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 @@
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 @@
@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 @@
@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 @@
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 @@
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 @@
}
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);
+ }
}
}
}