blob: 49db5f710004574fc4b5953ea38fc63bd21da1a2 [file] [log] [blame]
/*
* 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.CredentialProviderInfo;
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.util.Log;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
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, int callingUid,
IClearCredentialStateCallback callback, ClearCredentialStateRequest request,
CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal) {
super(context, userId, callingUid, request, callback, RequestInfo.TYPE_UNDEFINED,
callingAppInfo, cancellationSignal);
}
/**
* 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) {
setChosenMetric(componentName);
respondToClientWithResponseAndFinish();
}
protected void onProviderResponseComplete(ComponentName componentName) {
if (!isAnyProviderPending()) {
onFinalResponseReceived(componentName, null);
}
}
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");
if (isSessionCancelled()) {
mChosenProviderMetric.setChosenProviderStatus(
MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
try {
mClientCallback.onSuccess();
logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
ApiStatus.METRICS_API_STATUS_SUCCESS);
} catch (RemoteException e) {
mChosenProviderMetric.setChosenProviderStatus(
MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_FAILURE);
Log.i(TAG, "Issue while propagating the response to the client");
logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
ApiStatus.METRICS_API_STATUS_FAILURE);
}
finishSession(/*propagateCancellation=*/false);
}
private void respondToClientWithErrorAndFinish(String errorType, String errorMsg) {
Log.i(TAG, "respondToClientWithErrorAndFinish");
if (isSessionCancelled()) {
logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
try {
mClientCallback.onError(errorType, errorMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
ApiStatus.METRICS_API_STATUS_FAILURE);
finishSession(/*propagateCancellation=*/false);
}
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");
}
@Override
public void onUiCancellation(boolean isUserCancellation) {
// Not needed since UI is not involved
}
@Override
public void onUiSelectorInvocationFailure() {
// Not needed since UI is not involved
}
}