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

Commit b7ef16f5 authored by Reema Bajwa's avatar Reema Bajwa
Browse files

Add clear cred implementaion

Test: Built locally

Change-Id: Iab1d87994b1f2199065f102f157d2f431eae832f
parent 2800296d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ public class RequestInfo implements Parcelable {
    public static final @NonNull String EXTRA_REQUEST_INFO =
            "android.credentials.ui.extra.REQUEST_INFO";

    /** Type value for any request that does not require UI. */
    public static final @NonNull String TYPE_UNDEFINED = "android.credentials.ui.TYPE_UNDEFINED";
    /** Type value for an executeGetCredential request. */
    public static final @NonNull String TYPE_GET = "android.credentials.ui.TYPE_GET";
    /** Type value for an executeCreateCredential request. */
+142 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.server.credentials;

import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.credentials.ClearCredentialStateRequest;
import android.credentials.IClearCredentialStateCallback;
import android.credentials.ui.ProviderData;
import android.credentials.ui.RequestInfo;
import android.os.RemoteException;
import android.service.credentials.CredentialProviderInfo;
import android.util.Log;

import java.util.ArrayList;

/**
 * Central session for a single clearCredentialState request. This class listens to the
 * responses from providers, and updates the provider(S) state.
 */
public final class ClearRequestSession extends RequestSession<ClearCredentialStateRequest,
        IClearCredentialStateCallback>
        implements ProviderSession.ProviderInternalCallback<Void> {
    private static final String TAG = "GetRequestSession";

    public ClearRequestSession(Context context, int userId,
            IClearCredentialStateCallback callback, ClearCredentialStateRequest request,
            String callingPackage) {
        super(context, userId, request, callback, RequestInfo.TYPE_UNDEFINED, callingPackage);
    }

    /**
     * Creates a new provider session, and adds it list of providers that are contributing to
     * this session.
     * @return the provider session created within this request session, for the given provider
     * info.
     */
    @Override
    @Nullable
    public ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
            RemoteCredentialService remoteCredentialService) {
        ProviderClearSession providerClearSession = ProviderClearSession
                .createNewSession(mContext, mUserId, providerInfo,
                        this, remoteCredentialService);
        if (providerClearSession != null) {
            Log.i(TAG, "In startProviderSession - provider session created and being added");
            mProviders.put(providerClearSession.getComponentName().flattenToString(),
                    providerClearSession);
        }
        return providerClearSession;
    }

    @Override // from provider session
    public void onProviderStatusChanged(ProviderSession.Status status,
            ComponentName componentName) {
        Log.i(TAG, "in onStatusChanged with status: " + status);
        if (ProviderSession.isTerminatingStatus(status)) {
            Log.i(TAG, "in onStatusChanged terminating status");
            onProviderTerminated(componentName);
        } else if (ProviderSession.isCompletionStatus(status)) {
            Log.i(TAG, "in onStatusChanged isCompletionStatus status");
            onProviderResponseComplete(componentName);
        }
    }

    @Override
    public void onFinalResponseReceived(ComponentName componentName, Void response) {
        respondToClientWithResponseAndFinish();
    }

    @Override
    protected void onProviderResponseComplete(ComponentName componentName) {
        if (!isAnyProviderPending()) {
            onFinalResponseReceived(componentName, null);
        }
    }

    @Override
    protected void onProviderTerminated(ComponentName componentName) {
        if (!isAnyProviderPending()) {
            processResponses();
        }
    }

    @Override
    protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
        //Not applicable for clearCredential as UI is not needed
    }

    @Override
    public void onFinalErrorReceived(ComponentName componentName, String errorType,
            String message) {
        //Not applicable for clearCredential as response is not picked by the user
    }

    private void respondToClientWithResponseAndFinish() {
        Log.i(TAG, "respondToClientWithResponseAndFinish");
        try {
            mClientCallback.onSuccess();
        } catch (RemoteException e) {
            Log.i(TAG, "Issue while propagating the response to the client");
        }
        finishSession();
    }

    private void respondToClientWithErrorAndFinish(String errorType, String errorMsg) {
        Log.i(TAG, "respondToClientWithErrorAndFinish");
        try {
            mClientCallback.onError(errorType, errorMsg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        finishSession();
    }
    private void processResponses() {
        for (ProviderSession session: mProviders.values()) {
            if (session.isProviderResponseSet()) {
                // If even one provider responded successfully, send back the response
                // TODO: Aggregate other exceptions
                respondToClientWithResponseAndFinish();
                return;
            }
        }
        // TODO: Replace with properly defined error type
        respondToClientWithErrorAndFinish("unknown", "All providers failed");
    }
}
+38 −2
Original line number Diff line number Diff line
@@ -314,9 +314,45 @@ public final class CredentialManagerService
        @Override
        public ICancellationSignal clearCredentialState(ClearCredentialStateRequest request,
                IClearCredentialStateCallback callback, String callingPackage) {
            // TODO: implement.
            Log.i(TAG, "clearCredentialSession");
            Log.i(TAG, "starting clearCredentialState with callingPackage: " + callingPackage);
            // TODO : Implement cancellation
            ICancellationSignal cancelTransport = CancellationSignal.createTransport();

            // New request session, scoped for this request only.
            final ClearRequestSession session =
                    new ClearRequestSession(
                            getContext(),
                            UserHandle.getCallingUserId(),
                            callback,
                            request,
                            callingPackage);

            // Initiate all provider sessions
            // TODO: Determine if provider needs to have clear capability in their manifest
            List<ProviderSession> providerSessions =
                    initiateProviderSessions(session, List.of());

            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 IClearCredentialStateCallback "
                            + "callback: " + e.getMessage());
                }
            }

            // Iterate over all provider sessions and invoke the request
            providerSessions.forEach(
                    providerClearSession -> {
                        providerClearSession
                                .getRemoteCredentialService()
                                .onClearCredentialState(
                                        (android.service.credentials.ClearCredentialStateRequest)
                                                providerClearSession.getProviderRequest(),
                                        /* callback= */ providerClearSession);
                    });
            return cancelTransport;
        }
    }
+2 −4
Original line number Diff line number Diff line
@@ -76,7 +76,7 @@ public final class CredentialManagerServiceImpl extends
    @GuardedBy("mLock")
    public ProviderSession initiateProviderSessionForRequestLocked(
            RequestSession requestSession, List<String> requestOptions) {
        if (!isServiceCapableLocked(requestOptions)) {
        if (!requestOptions.isEmpty() && !isServiceCapableLocked(requestOptions)) {
            Log.i(TAG, "Service is not capable");
            return null;
        }
@@ -88,9 +88,7 @@ public final class CredentialManagerServiceImpl extends
        }
        final RemoteCredentialService remoteService = new RemoteCredentialService(
                getContext(), mInfo.getServiceInfo().getComponentName(), mUserId);
        ProviderSession providerSession =
                requestSession.initiateProviderSession(mInfo, remoteService);
        return providerSession;
        return requestSession.initiateProviderSession(mInfo, remoteService);
    }

    /** Return true if at least one capability found. */
+119 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.server.credentials;

import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.credentials.ClearCredentialStateException;
import android.credentials.ui.ProviderData;
import android.credentials.ui.ProviderPendingIntentResponse;
import android.service.credentials.CallingAppInfo;
import android.service.credentials.ClearCredentialStateRequest;
import android.service.credentials.CredentialProviderInfo;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;

/**
 * Central provider session that listens for provider callbacks, and maintains provider state.
 *
 * @hide
 */
public final class ProviderClearSession extends ProviderSession<ClearCredentialStateRequest,
        Void>
        implements
        RemoteCredentialService.ProviderCallbacks<Void> {
    private static final String TAG = "ProviderClearSession";

    private ClearCredentialStateException mProviderException;

    /** Creates a new provider session to be used by the request session. */
    @Nullable public static ProviderClearSession createNewSession(
            Context context,
            @UserIdInt int userId,
            CredentialProviderInfo providerInfo,
            ClearRequestSession clearRequestSession,
            RemoteCredentialService remoteCredentialService) {
        ClearCredentialStateRequest providerRequest =
                createProviderRequest(
                        clearRequestSession.mClientRequest,
                        clearRequestSession.mClientCallingPackage);
        return new ProviderClearSession(context, providerInfo, clearRequestSession, userId,
                    remoteCredentialService, providerRequest);
    }

    @Nullable
    private static ClearCredentialStateRequest createProviderRequest(
            android.credentials.ClearCredentialStateRequest clientRequest,
            String clientCallingPackage
    ) {
        // TODO: Determine if provider needs to declare clear capability in manifest
        return new ClearCredentialStateRequest(
                new CallingAppInfo(clientCallingPackage, new ArraySet<>()),
                clientRequest.getData());
    }

    public ProviderClearSession(Context context,
            CredentialProviderInfo info,
            ProviderInternalCallback callbacks,
            int userId, RemoteCredentialService remoteCredentialService,
            ClearCredentialStateRequest providerRequest) {
        super(context, info, providerRequest, callbacks, userId, remoteCredentialService);
        setStatus(Status.PENDING);
    }

    @Override
    public void onProviderResponseSuccess(@Nullable Void response) {
        Log.i(TAG, "in onProviderResponseSuccess");
        mProviderResponseSet = true;
        updateStatusAndInvokeCallback(Status.COMPLETE);
    }

    /** Called when the provider response resulted in a failure. */
    @Override // Callback from the remote provider
    public void onProviderResponseFailure(int errorCode, Exception exception) {
        if (exception instanceof ClearCredentialStateException) {
            mProviderException = (ClearCredentialStateException) exception;
        }
        updateStatusAndInvokeCallback(toStatus(errorCode));
    }

    /** Called when provider service dies. */
    @Override // Callback from the remote provider
    public void onProviderServiceDied(RemoteCredentialService service) {
        if (service.getComponentName().equals(mProviderInfo.getServiceInfo().getComponentName())) {
            updateStatusAndInvokeCallback(Status.SERVICE_DEAD);
        } else {
            Slog.i(TAG, "Component names different in onProviderServiceDied - "
                    + "this should not happen");
        }
    }

    @Nullable
    @Override
    protected ProviderData prepareUiData() {
        //Not applicable for clearCredential as response is not picked by the user
        return null;
    }

    @Override
    protected void onUiEntrySelected(String entryType, String entryId,
            ProviderPendingIntentResponse providerPendingIntentResponse) {
        //Not applicable for clearCredential as response is not picked by the user
    }
}
Loading