189 lines
7.8 KiB
Java
189 lines
7.8 KiB
Java
![]() |
/*
|
||
|
* Copyright 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 android.credentials;
|
||
|
|
||
|
import static android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS;
|
||
|
|
||
|
import android.annotation.CallbackExecutor;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.annotation.RequiresPermission;
|
||
|
import android.app.ActivityOptions;
|
||
|
import android.app.PendingIntent;
|
||
|
import android.content.Context;
|
||
|
import android.content.IntentSender;
|
||
|
import android.os.Bundle;
|
||
|
import android.os.CancellationSignal;
|
||
|
import android.os.OutcomeReceiver;
|
||
|
import android.util.Log;
|
||
|
|
||
|
import java.util.concurrent.Executor;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* A response object that prefetches user app credentials and provides metadata about them. It can
|
||
|
* then be used to issue the full credential retrieval flow via the
|
||
|
* {@link CredentialManager#getCredential(Context, PendingGetCredentialHandle, CancellationSignal,
|
||
|
* Executor, OutcomeReceiver)} method to perform the remaining flows such as consent collection
|
||
|
* and credential selection, to officially retrieve a credential.
|
||
|
*/
|
||
|
public final class PrepareGetCredentialResponse {
|
||
|
|
||
|
private static final Bundle OPTIONS_SENDER_BAL_OPTIN = ActivityOptions.makeBasic()
|
||
|
.setPendingIntentBackgroundActivityStartMode(
|
||
|
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle();
|
||
|
|
||
|
/**
|
||
|
* A handle that represents a pending get-credential operation. Pass this handle to {@link
|
||
|
* CredentialManager#getCredential(Context, PendingGetCredentialHandle, CancellationSignal,
|
||
|
* Executor, OutcomeReceiver)} to perform the remaining flows to officially retrieve a
|
||
|
* credential.
|
||
|
*/
|
||
|
public static final class PendingGetCredentialHandle {
|
||
|
@NonNull
|
||
|
private final CredentialManager.GetCredentialTransportPendingUseCase
|
||
|
mGetCredentialTransport;
|
||
|
/**
|
||
|
* The pending intent to be launched to finalize the user credential. If null, the callback
|
||
|
* will fail with {@link GetCredentialException#TYPE_NO_CREDENTIAL}.
|
||
|
*/
|
||
|
@Nullable
|
||
|
private final PendingIntent mPendingIntent;
|
||
|
|
||
|
/** @hide */
|
||
|
PendingGetCredentialHandle(
|
||
|
@NonNull CredentialManager.GetCredentialTransportPendingUseCase transport,
|
||
|
@Nullable PendingIntent pendingIntent) {
|
||
|
mGetCredentialTransport = transport;
|
||
|
mPendingIntent = pendingIntent;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
void show(@NonNull Context context, @Nullable CancellationSignal cancellationSignal,
|
||
|
@CallbackExecutor @NonNull Executor executor,
|
||
|
@NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
|
||
|
if (mPendingIntent == null) {
|
||
|
executor.execute(() -> callback.onError(
|
||
|
new GetCredentialException(GetCredentialException.TYPE_NO_CREDENTIAL)));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
mGetCredentialTransport.setCallback(new GetPendingCredentialInternalCallback() {
|
||
|
@Override
|
||
|
public void onPendingIntent(PendingIntent pendingIntent) {
|
||
|
try {
|
||
|
context.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0,
|
||
|
OPTIONS_SENDER_BAL_OPTIN);
|
||
|
} catch (IntentSender.SendIntentException e) {
|
||
|
Log.e(TAG, "startIntentSender() failed for intent for show()", e);
|
||
|
executor.execute(() -> callback.onError(
|
||
|
new GetCredentialException(GetCredentialException.TYPE_UNKNOWN)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onResponse(GetCredentialResponse response) {
|
||
|
executor.execute(() -> callback.onResult(response));
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onError(String errorType, String message) {
|
||
|
executor.execute(
|
||
|
() -> callback.onError(new GetCredentialException(errorType, message)));
|
||
|
}
|
||
|
});
|
||
|
|
||
|
try {
|
||
|
context.startIntentSender(mPendingIntent.getIntentSender(), null, 0, 0, 0,
|
||
|
OPTIONS_SENDER_BAL_OPTIN);
|
||
|
} catch (IntentSender.SendIntentException e) {
|
||
|
Log.e(TAG, "startIntentSender() failed for intent for show()", e);
|
||
|
executor.execute(() -> callback.onError(
|
||
|
new GetCredentialException(GetCredentialException.TYPE_UNKNOWN)));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
private static final String TAG = "CredentialManager";
|
||
|
|
||
|
@NonNull private final PrepareGetCredentialResponseInternal mResponseInternal;
|
||
|
|
||
|
@NonNull private final PendingGetCredentialHandle mPendingGetCredentialHandle;
|
||
|
|
||
|
/**
|
||
|
* Returns true if the user has any candidate credentials for the given {@code credentialType},
|
||
|
* and false otherwise.
|
||
|
*/
|
||
|
@RequiresPermission(CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS)
|
||
|
public boolean hasCredentialResults(@NonNull String credentialType) {
|
||
|
return mResponseInternal.hasCredentialResults(credentialType);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the user has any candidate authentication actions (locked credential
|
||
|
* supplier), and false otherwise.
|
||
|
*/
|
||
|
@RequiresPermission(CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS)
|
||
|
public boolean hasAuthenticationResults() {
|
||
|
return mResponseInternal.hasAuthenticationResults();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the user has any candidate remote credential results, and false otherwise.
|
||
|
*/
|
||
|
@RequiresPermission(CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS)
|
||
|
public boolean hasRemoteResults() {
|
||
|
return mResponseInternal.hasRemoteResults();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a handle that represents this pending get-credential operation. Pass this handle to
|
||
|
* {@link CredentialManager#getCredential(Context, PendingGetCredentialHandle,
|
||
|
* CancellationSignal, Executor, OutcomeReceiver)} to perform the remaining flows to officially
|
||
|
* retrieve a credential.
|
||
|
*/
|
||
|
@NonNull
|
||
|
public PendingGetCredentialHandle getPendingGetCredentialHandle() {
|
||
|
return mPendingGetCredentialHandle;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Constructs a {@link PrepareGetCredentialResponse}.
|
||
|
*
|
||
|
* @param responseInternal whether caller has the permission to query the credential
|
||
|
* result metadata
|
||
|
* @param getCredentialTransport the transport for the operation to finalaze a credential
|
||
|
* @hide
|
||
|
*/
|
||
|
protected PrepareGetCredentialResponse(
|
||
|
@NonNull PrepareGetCredentialResponseInternal responseInternal,
|
||
|
@NonNull CredentialManager.GetCredentialTransportPendingUseCase
|
||
|
getCredentialTransport) {
|
||
|
mResponseInternal = responseInternal;
|
||
|
mPendingGetCredentialHandle = new PendingGetCredentialHandle(
|
||
|
getCredentialTransport, responseInternal.getPendingIntent());
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
protected interface GetPendingCredentialInternalCallback {
|
||
|
void onPendingIntent(@NonNull PendingIntent pendingIntent);
|
||
|
|
||
|
void onResponse(@NonNull GetCredentialResponse response);
|
||
|
|
||
|
void onError(@NonNull String errorType, @Nullable String message);
|
||
|
}
|
||
|
}
|