Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1d08d49e authored by Reema Bajwa's avatar Reema Bajwa
Browse files

Cancel session when client callback dies

Bug: 308470501
Test: Built & deployed locally

Change-Id: I20b32ba3ac7a995efd6adde2cfeb9d75e56e7959
parent 0299a755
Loading
Loading
Loading
Loading
+39 −0
Original line number Diff line number Diff line
@@ -23,12 +23,14 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.credentials.CredentialProviderInfo;
import android.credentials.flags.Flags;
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.IInterface;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -94,6 +96,9 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential

    private final Set<ComponentName> mEnabledProviders;

    private final RequestSessionDeathRecipient mDeathRecipient =
            new RequestSessionDeathRecipient();

    protected PendingIntent mPendingIntent;

    @NonNull
@@ -141,11 +146,26 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
        mRequestSessionMetric.collectInitialPhaseMetricInfo(timestampStarted,
                mCallingUid, ApiName.getMetricCodeFromRequestInfo(mRequestType));
        setCancellationListener();
        if (Flags.clearSessionEnabled()) {
            setUpClientCallbackListener();
        }
    }

    private void setUpClientCallbackListener() {
        if (mClientCallback != null && mClientCallback instanceof IInterface) {
            IInterface callback = (IInterface) mClientCallback;
            try {
                callback.asBinder().linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
                Slog.e(TAG, e.getMessage());
            }
        }
    }

    private void setCancellationListener() {
        mCancellationSignal.setOnCancelListener(
                () -> {
                    Slog.d(TAG, "Cancellation invoked from the client - clearing session");
                    boolean isUiActive = maybeCancelUi();
                    finishSession(!isUiActive);
                }
@@ -168,6 +188,17 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
        return false;
    }

    private boolean isUiWaitingForData() {
        // Technically, the status can also be IN_PROGRESS when the user has made a selection
        // so this an over estimation, but safe to do so as it is used for cancellation
        // propagation to the provider in a very narrow time frame. If provider has
        // already responded, cancellation is not an issue as the cancellation listener
        // is independent of the service binding.
        // TODO(b/313512500): Do not propagate cancelation if provider has responded in
        // query phase.
        return mCredentialManagerUi.getStatus() == CredentialManagerUi.UiStatus.IN_PROGRESS;
    }

    public abstract ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
            RemoteCredentialService remoteCredentialService);

@@ -373,4 +404,12 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
        return chosenProviderSession != null && chosenProviderSession.mProviderInfo != null
                && chosenProviderSession.mProviderInfo.isPrimary();
    }

    private class RequestSessionDeathRecipient implements IBinder.DeathRecipient {
        @Override
        public void binderDied() {
            Slog.d(TAG, "Client binder died - clearing session");
            finishSession(isUiWaitingForData());
        }
    }
}