/* * 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 android.service.credentials; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.credentials.ClearCredentialStateException; import android.credentials.CreateCredentialException; import android.credentials.GetCredentialException; import android.os.CancellationSignal; import android.os.Handler; import android.os.IBinder; import android.os.ICancellationSignal; import android.os.Looper; import android.os.OutcomeReceiver; import android.os.RemoteException; import android.util.Slog; import java.util.Objects; /** * Service to be extended by credential providers, in order to return user credentials * to the framework. */ public abstract class CredentialProviderService extends Service { /** * Intent extra: The {@link android.credentials.CreateCredentialRequest} attached with * the {@code pendingIntent} that is invoked when the user selects a {@link CreateEntry} * returned as part of the {@link BeginCreateCredentialResponse} * *
* Type: {@link android.service.credentials.CreateCredentialRequest} */ public static final String EXTRA_CREATE_CREDENTIAL_REQUEST = "android.service.credentials.extra.CREATE_CREDENTIAL_REQUEST"; /** * Intent extra: The {@link GetCredentialRequest} attached with * the {@code pendingIntent} that is invoked when the user selects a {@link CredentialEntry} * returned as part of the {@link BeginGetCredentialResponse} * *
* Type: {@link GetCredentialRequest} */ public static final String EXTRA_GET_CREDENTIAL_REQUEST = "android.service.credentials.extra.GET_CREDENTIAL_REQUEST"; /** * Intent extra: The result of a create flow operation, to be set on finish of the * {@link android.app.Activity} invoked through the {@code pendingIntent} set on * a {@link CreateEntry}. * *
* Type: {@link android.credentials.CreateCredentialResponse} */ public static final String EXTRA_CREATE_CREDENTIAL_RESPONSE = "android.service.credentials.extra.CREATE_CREDENTIAL_RESPONSE"; /** * Intent extra: The result of a get credential flow operation, to be set on finish of the * {@link android.app.Activity} invoked through the {@code pendingIntent} set on * a {@link CredentialEntry}. * *
* Type: {@link android.credentials.GetCredentialResponse} */ public static final String EXTRA_GET_CREDENTIAL_RESPONSE = "android.service.credentials.extra.GET_CREDENTIAL_RESPONSE"; /** * Intent extra: The result of an authentication flow, to be set on finish of the * {@link android.app.Activity} invoked through the {@link android.app.PendingIntent} set on * an authentication {@link Action}, as part of the original * {@link BeginGetCredentialResponse}. This result should contain the actual content, * including credential entries and action entries, to be shown on the selector. * *
* Type: {@link BeginGetCredentialResponse} */ public static final String EXTRA_BEGIN_GET_CREDENTIAL_RESPONSE = "android.service.credentials.extra.BEGIN_GET_CREDENTIAL_RESPONSE"; /** * Intent extra: The failure exception set at the final stage of a get flow. * This exception is set at the finishing result of the {@link android.app.Activity} * invoked by the {@link PendingIntent} , when a user selects the {@link CredentialEntry} * that contained the {@link PendingIntent} in question. * *
The result must be set through {@link android.app.Activity#setResult} as an intent extra * *
* Type: {@link android.credentials.GetCredentialException} */ public static final String EXTRA_GET_CREDENTIAL_EXCEPTION = "android.service.credentials.extra.GET_CREDENTIAL_EXCEPTION"; /** * Intent extra: The failure exception set at the final stage of a create flow. * This exception is set at the finishing result of the {@link android.app.Activity} * invoked by the {@link PendingIntent} , when a user selects the {@link CreateEntry} * that contained the {@link PendingIntent} in question. * *
* Type: {@link android.credentials.CreateCredentialException} */ public static final String EXTRA_CREATE_CREDENTIAL_EXCEPTION = "android.service.credentials.extra.CREATE_CREDENTIAL_EXCEPTION"; /** * Intent extra: The {@link BeginGetCredentialRequest} attached with * the {@code pendingIntent} that is invoked when the user selects an * authentication entry (intending to unlock the provider app) on the UI. * *
When a provider app receives a {@link BeginGetCredentialRequest} through the * {@link CredentialProviderService#onBeginGetCredential} call, it can construct the * {@link BeginGetCredentialResponse} with either an authentication {@link Action} (if the app * is locked), or a {@link BeginGetCredentialResponse} (if the app is unlocked). In the former * case, i.e. the app is locked, user will be shown the authentication action. When selected, * the underlying {@link PendingIntent} will be invoked which will lead the user to provider's * unlock activity. This pending intent will also contain the original * {@link BeginGetCredentialRequest} to be retrieved and processed after the unlock * flow is complete. * *
After the app is unlocked, the {@link BeginGetCredentialResponse} must be constructed * using a {@link BeginGetCredentialResponse}, which must be set on an {@link Intent} as an * intent extra against CredentialProviderService#EXTRA_CREDENTIALS_RESPONSE_CONTENT}. * This intent should then be set as a result through {@link android.app.Activity#setResult} * before finishing the activity. * *
* Type: {@link BeginGetCredentialRequest}
*/
public static final String EXTRA_BEGIN_GET_CREDENTIAL_REQUEST =
"android.service.credentials.extra.BEGIN_GET_CREDENTIAL_REQUEST";
/**
* The key to autofillId associated with the requested credential option and the corresponding
* credential entry. The associated autofillId will be contained inside the candidate query
* bundle of {@link android.credentials.CredentialOption} if requested through the
* {@link com.android.credentialmanager.autofill.CredentialAutofillService}. The resulting
* credential entry will contain the autofillId inside its framework extras intent.
*
* @hide
*/
public static final String EXTRA_AUTOFILL_ID =
"android.service.credentials.extra.AUTOFILL_ID";
private static final String TAG = "CredProviderService";
/**
* Name under which a Credential Provider service component publishes information
* about itself. This meta-data must reference an XML resource containing
* an
* <{@link android.R.styleable#CredentialProvider credential-provider}>
* tag.
*
* For example (AndroidManifest.xml):
*
*
*
* For example (xml/provider.xml):
*
*
*/
public static final String SERVICE_META_DATA = "android.credentials.provider";
/** @hide */
public static final String TEST_SYSTEM_PROVIDER_META_DATA_KEY =
"android.credentials.testsystemprovider";
private Handler mHandler;
/**
* The {@link Intent} that must be declared as handled by the service. The service must also
* require the {android.Manifest.permission#BIND_CREDENTIAL_PROVIDER_SERVICE} permission
* so that only the system can bind to it.
*/
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE =
"android.service.credentials.CredentialProviderService";
/**
* The {@link Intent} that must be declared as handled by a system credential provider
* service.
*
*
The service must also require the
* {android.Manifest.permission#BIND_CREDENTIAL_PROVIDER_SERVICE} permission
* so that only the system can bind to it.
*
* @hide
*/
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String SYSTEM_SERVICE_INTERFACE =
"android.service.credentials.system.CredentialProviderService";
@CallSuper
@Override
public void onCreate() {
super.onCreate();
mHandler = new Handler(Looper.getMainLooper(), null, true);
}
@Override
@NonNull public final IBinder onBind(@NonNull Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
return mInterface.asBinder();
}
Slog.w(TAG, "Failed to bind with intent: " + intent);
return null;
}
private final ICredentialProviderService mInterface = new ICredentialProviderService.Stub() {
@Override
public void onBeginGetCredential(BeginGetCredentialRequest request,
IBeginGetCredentialCallback callback) {
Objects.requireNonNull(request);
Objects.requireNonNull(callback);
ICancellationSignal transport = CancellationSignal.createTransport();
try {
callback.onCancellable(transport);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
mHandler.sendMessage(obtainMessage(
CredentialProviderService::onBeginGetCredential,
CredentialProviderService.this, request,
CancellationSignal.fromTransport(transport),
new OutcomeReceiver This API denotes a query stage request for getting user's credentials from a given
* credential provider. The request contains a list of
* {@link BeginGetCredentialOption} that have parameters to be used for
* populating candidate credentials, as a list of {@link CredentialEntry} to be set
* on the {@link BeginGetCredentialResponse}. This list is then shown to the user on a
* selector.
*
* If a {@link PendingIntent} is set on a {@link CredentialEntry}, and the user selects that
* entry, a {@link GetCredentialRequest} with all parameters needed to get the actual
* {@link android.credentials.Credential} will be sent as part of the {@link Intent} fired
* through the {@link PendingIntent}.
* @param request the request for the provider to handle
* @param cancellationSignal signal for providers to listen to any cancellation requests from
* the android system
* @param callback object used to relay the response of the credentials request
*/
public abstract void onBeginGetCredential(@NonNull BeginGetCredentialRequest request,
@NonNull CancellationSignal cancellationSignal,
@NonNull OutcomeReceiver<
BeginGetCredentialResponse, GetCredentialException> callback);
/**
* Called by the android system to create a credential.
* @param request The credential creation request for the provider to handle.
* @param cancellationSignal Signal for providers to listen to any cancellation requests from
* the android system.
* @param callback Object used to relay the response of the credential creation request.
*/
public abstract void onBeginCreateCredential(@NonNull BeginCreateCredentialRequest request,
@NonNull CancellationSignal cancellationSignal,
@NonNull OutcomeReceiver