Loading core/api/current.txt +13 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,7 @@ package android { field public static final String CLEAR_APP_CACHE = "android.permission.CLEAR_APP_CACHE"; field public static final String CONFIGURE_WIFI_DISPLAY = "android.permission.CONFIGURE_WIFI_DISPLAY"; field public static final String CONTROL_LOCATION_UPDATES = "android.permission.CONTROL_LOCATION_UPDATES"; field public static final String CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS = "android.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS"; field public static final String CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS = "android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS"; field public static final String CREDENTIAL_MANAGER_SET_ORIGIN = "android.permission.CREDENTIAL_MANAGER_SET_ORIGIN"; field public static final String DELETE_CACHE_FILES = "android.permission.DELETE_CACHE_FILES"; Loading Loading @@ -13669,7 +13670,9 @@ package android.credentials { method public void clearCredentialState(@NonNull android.credentials.ClearCredentialStateRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException>); method public void createCredential(@NonNull android.credentials.CreateCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CreateCredentialException>); method public void getCredential(@NonNull android.credentials.GetCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>); method public void getCredential(@NonNull android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>); method public boolean isEnabledCredentialProviderService(@NonNull android.content.ComponentName); method public void prepareGetCredential(@NonNull android.credentials.GetCredentialRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.PrepareGetCredentialResponse,android.credentials.GetCredentialException>); method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest); method public void unregisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest); } Loading Loading @@ -13734,6 +13737,16 @@ package android.credentials { field @NonNull public static final android.os.Parcelable.Creator<android.credentials.GetCredentialResponse> CREATOR; } public final class PrepareGetCredentialResponse { method @NonNull public android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle getPendingGetCredentialHandle(); method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasAuthenticationResults(); method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasCredentialResults(@NonNull String); method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasRemoteResults(); } public static final class PrepareGetCredentialResponse.PendingGetCredentialHandle { } public final class RegisterCredentialDescriptionRequest implements android.os.Parcelable { ctor public RegisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription); ctor public RegisterCredentialDescriptionRequest(@NonNull java.util.Set<android.credentials.CredentialDescription>); core/java/android/credentials/CredentialManager.java +114 −19 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package android.credentials; import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN; import static java.util.Objects.requireNonNull; import android.annotation.CallbackExecutor; Loading Loading @@ -167,37 +165,83 @@ public final class CredentialManager { } /** * Gets a {@link GetPendingCredentialResponse} that can launch the credential retrieval UI flow * to request a user credential for your app. * Launches the remaining flows to retrieve an app credential from the user, after the * completed prefetch work corresponding to the given {@code pendingGetCredentialHandle}. * * @param request the request specifying type(s) of credentials to get from the user * <p>The execution can potentially launch UI flows to collect user consent to using a * credential, display a picker when multiple credentials exist, etc. * * <p>Use this API to complete the full credential retrieval operation after you initiated a * request through the {@link #prepareGetCredential( * GetCredentialRequest, CancellationSignal, Executor, OutcomeReceiver)} API. * * @param pendingGetCredentialHandle the handle representing the pending operation to resume * @param activity the activity used to launch any UI needed * @param cancellationSignal an optional signal that allows for cancelling this call * @param executor the callback will take place on this {@link Executor} * @param callback the callback invoked when the request succeeds or fails */ public void getCredential( @NonNull PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, @NonNull Activity activity, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) { requireNonNull(pendingGetCredentialHandle, "pendingGetCredentialHandle must not be null"); requireNonNull(activity, "activity must not be null"); requireNonNull(executor, "executor must not be null"); requireNonNull(callback, "callback must not be null"); if (cancellationSignal != null && cancellationSignal.isCanceled()) { Log.w(TAG, "getCredential already canceled"); return; } pendingGetCredentialHandle.show(activity, cancellationSignal, executor, callback); } /** * Prepare for a get-credential operation. Returns a {@link PrepareGetCredentialResponse} that * can launch the credential retrieval UI flow to request a user credential for your app. * * @hide * <p>This API doesn't invoke any UI. It only performs the preparation work so that you can * later launch the remaining get-credential operation (involves UIs) through the {@link * #getCredential(PrepareGetCredentialResponse.PendingGetCredentialHandle, Activity, * CancellationSignal, Executor, OutcomeReceiver)} API which incurs less latency compared to * the {@link #getCredential(GetCredentialRequest, Activity, CancellationSignal, Executor, * OutcomeReceiver)} API that executes the whole operation in one call. * * @param request the request specifying type(s) of credentials to get from the user * @param cancellationSignal an optional signal that allows for cancelling this call * @param executor the callback will take place on this {@link Executor} * @param callback the callback invoked when the request succeeds or fails */ public void getPendingCredential( public void prepareGetCredential( @NonNull GetCredentialRequest request, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver< GetPendingCredentialResponse, GetCredentialException> callback) { PrepareGetCredentialResponse, GetCredentialException> callback) { requireNonNull(request, "request must not be null"); requireNonNull(executor, "executor must not be null"); requireNonNull(callback, "callback must not be null"); if (cancellationSignal != null && cancellationSignal.isCanceled()) { Log.w(TAG, "getPendingCredential already canceled"); Log.w(TAG, "prepareGetCredential already canceled"); return; } ICancellationSignal cancelRemote = null; GetCredentialTransportPendingUseCase getCredentialTransport = new GetCredentialTransportPendingUseCase(); try { cancelRemote = mService.executeGetPendingCredential( mService.executePrepareGetCredential( request, new GetPendingCredentialTransport(executor, callback), new PrepareGetCredentialTransport( executor, callback, getCredentialTransport), getCredentialTransport, mContext.getOpPackageName()); } catch (RemoteException e) { e.rethrowFromSystemServer(); Loading Loading @@ -484,23 +528,27 @@ public final class CredentialManager { } } private static class GetPendingCredentialTransport extends IGetPendingCredentialCallback.Stub { private static class PrepareGetCredentialTransport extends IPrepareGetCredentialCallback.Stub { // TODO: listen for cancellation to release callback. private final Executor mExecutor; private final OutcomeReceiver< GetPendingCredentialResponse, GetCredentialException> mCallback; PrepareGetCredentialResponse, GetCredentialException> mCallback; private final GetCredentialTransportPendingUseCase mGetCredentialTransport; private GetPendingCredentialTransport( private PrepareGetCredentialTransport( Executor executor, OutcomeReceiver<GetPendingCredentialResponse, GetCredentialException> callback) { OutcomeReceiver<PrepareGetCredentialResponse, GetCredentialException> callback, GetCredentialTransportPendingUseCase getCredentialTransport) { mExecutor = executor; mCallback = callback; mGetCredentialTransport = getCredentialTransport; } @Override public void onResponse(GetPendingCredentialResponse response) { mExecutor.execute(() -> mCallback.onResult(response)); public void onResponse(PrepareGetCredentialResponseInternal response) { mExecutor.execute(() -> mCallback.onResult( new PrepareGetCredentialResponse(response, mGetCredentialTransport))); } @Override Loading @@ -510,6 +558,51 @@ public final class CredentialManager { } } /** @hide */ protected static class GetCredentialTransportPendingUseCase extends IGetCredentialCallback.Stub { @Nullable private PrepareGetCredentialResponse.GetPendingCredentialInternalCallback mCallback = null; private GetCredentialTransportPendingUseCase() {} public void setCallback( PrepareGetCredentialResponse.GetPendingCredentialInternalCallback callback) { if (mCallback == null) { mCallback = callback; } else { throw new IllegalStateException("callback has already been set once"); } } @Override public void onPendingIntent(PendingIntent pendingIntent) { if (mCallback != null) { mCallback.onPendingIntent(pendingIntent); } else { Log.d(TAG, "Unexpected onPendingIntent call before the show invocation"); } } @Override public void onResponse(GetCredentialResponse response) { if (mCallback != null) { mCallback.onResponse(response); } else { Log.d(TAG, "Unexpected onResponse call before the show invocation"); } } @Override public void onError(String errorType, String message) { if (mCallback != null) { mCallback.onError(errorType, message); } else { Log.d(TAG, "Unexpected onError call before the show invocation"); } } } private static class GetCredentialTransport extends IGetCredentialCallback.Stub { // TODO: listen for cancellation to release callback. Loading @@ -535,7 +628,8 @@ public final class CredentialManager { TAG, "startIntentSender() failed for intent:" + pendingIntent.getIntentSender(), e); // TODO: propagate the error. mExecutor.execute(() -> mCallback.onError( new GetCredentialException(GetCredentialException.TYPE_UNKNOWN))); } } Loading Loading @@ -577,7 +671,8 @@ public final class CredentialManager { TAG, "startIntentSender() failed for intent:" + pendingIntent.getIntentSender(), e); // TODO: propagate the error. mExecutor.execute(() -> mCallback.onError( new CreateCredentialException(CreateCredentialException.TYPE_UNKNOWN))); } } Loading core/java/android/credentials/ICredentialManager.aidl +2 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ import android.credentials.UnregisterCredentialDescriptionRequest; import android.credentials.IClearCredentialStateCallback; import android.credentials.ICreateCredentialCallback; import android.credentials.IGetCredentialCallback; import android.credentials.IGetPendingCredentialCallback; import android.credentials.IPrepareGetCredentialCallback; import android.credentials.ISetEnabledProvidersCallback; import android.content.ComponentName; import android.os.ICancellationSignal; Loading @@ -41,7 +41,7 @@ interface ICredentialManager { @nullable ICancellationSignal executeGetCredential(in GetCredentialRequest request, in IGetCredentialCallback callback, String callingPackage); @nullable ICancellationSignal executeGetPendingCredential(in GetCredentialRequest request, in IGetPendingCredentialCallback callback, String callingPackage); @nullable ICancellationSignal executePrepareGetCredential(in GetCredentialRequest request, in IPrepareGetCredentialCallback prepareGetCredentialCallback, in IGetCredentialCallback getCredentialCallback, String callingPackage); @nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage); Loading core/java/android/credentials/IGetPendingCredentialCallback.aidl→core/java/android/credentials/IPrepareGetCredentialCallback.aidl +4 −4 Original line number Diff line number Diff line Loading @@ -17,14 +17,14 @@ package android.credentials; import android.app.PendingIntent; import android.credentials.GetPendingCredentialResponse; import android.credentials.PrepareGetCredentialResponseInternal; /** * Listener for a executeGetPendingCredential request. * Listener for a executePrepareGetCredential request. * * @hide */ interface IGetPendingCredentialCallback { oneway void onResponse(in GetPendingCredentialResponse response); interface IPrepareGetCredentialCallback { oneway void onResponse(in PrepareGetCredentialResponseInternal response); oneway void onError(String errorType, String message); } No newline at end of file core/java/android/credentials/PrepareGetCredentialResponse.java 0 → 100644 +180 −0 Original line number Diff line number Diff line /* * 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.Activity; import android.app.PendingIntent; import android.content.IntentSender; 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(PendingGetCredentialHandle, Activity, 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 { /** * A handle that represents a pending get-credential operation. Pass this handle to {@link * CredentialManager#getCredential(PendingGetCredentialHandle, Activity, 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 Activity activity, @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 { activity.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0); } 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 { activity.startIntentSender(mPendingIntent.getIntentSender(), null, 0, 0, 0); } 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(PendingGetCredentialHandle, Activity, * 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); } } Loading
core/api/current.txt +13 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,7 @@ package android { field public static final String CLEAR_APP_CACHE = "android.permission.CLEAR_APP_CACHE"; field public static final String CONFIGURE_WIFI_DISPLAY = "android.permission.CONFIGURE_WIFI_DISPLAY"; field public static final String CONTROL_LOCATION_UPDATES = "android.permission.CONTROL_LOCATION_UPDATES"; field public static final String CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS = "android.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS"; field public static final String CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS = "android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS"; field public static final String CREDENTIAL_MANAGER_SET_ORIGIN = "android.permission.CREDENTIAL_MANAGER_SET_ORIGIN"; field public static final String DELETE_CACHE_FILES = "android.permission.DELETE_CACHE_FILES"; Loading Loading @@ -13669,7 +13670,9 @@ package android.credentials { method public void clearCredentialState(@NonNull android.credentials.ClearCredentialStateRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException>); method public void createCredential(@NonNull android.credentials.CreateCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CreateCredentialException>); method public void getCredential(@NonNull android.credentials.GetCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>); method public void getCredential(@NonNull android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>); method public boolean isEnabledCredentialProviderService(@NonNull android.content.ComponentName); method public void prepareGetCredential(@NonNull android.credentials.GetCredentialRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.PrepareGetCredentialResponse,android.credentials.GetCredentialException>); method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest); method public void unregisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest); } Loading Loading @@ -13734,6 +13737,16 @@ package android.credentials { field @NonNull public static final android.os.Parcelable.Creator<android.credentials.GetCredentialResponse> CREATOR; } public final class PrepareGetCredentialResponse { method @NonNull public android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle getPendingGetCredentialHandle(); method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasAuthenticationResults(); method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasCredentialResults(@NonNull String); method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasRemoteResults(); } public static final class PrepareGetCredentialResponse.PendingGetCredentialHandle { } public final class RegisterCredentialDescriptionRequest implements android.os.Parcelable { ctor public RegisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription); ctor public RegisterCredentialDescriptionRequest(@NonNull java.util.Set<android.credentials.CredentialDescription>);
core/java/android/credentials/CredentialManager.java +114 −19 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package android.credentials; import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN; import static java.util.Objects.requireNonNull; import android.annotation.CallbackExecutor; Loading Loading @@ -167,37 +165,83 @@ public final class CredentialManager { } /** * Gets a {@link GetPendingCredentialResponse} that can launch the credential retrieval UI flow * to request a user credential for your app. * Launches the remaining flows to retrieve an app credential from the user, after the * completed prefetch work corresponding to the given {@code pendingGetCredentialHandle}. * * @param request the request specifying type(s) of credentials to get from the user * <p>The execution can potentially launch UI flows to collect user consent to using a * credential, display a picker when multiple credentials exist, etc. * * <p>Use this API to complete the full credential retrieval operation after you initiated a * request through the {@link #prepareGetCredential( * GetCredentialRequest, CancellationSignal, Executor, OutcomeReceiver)} API. * * @param pendingGetCredentialHandle the handle representing the pending operation to resume * @param activity the activity used to launch any UI needed * @param cancellationSignal an optional signal that allows for cancelling this call * @param executor the callback will take place on this {@link Executor} * @param callback the callback invoked when the request succeeds or fails */ public void getCredential( @NonNull PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, @NonNull Activity activity, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) { requireNonNull(pendingGetCredentialHandle, "pendingGetCredentialHandle must not be null"); requireNonNull(activity, "activity must not be null"); requireNonNull(executor, "executor must not be null"); requireNonNull(callback, "callback must not be null"); if (cancellationSignal != null && cancellationSignal.isCanceled()) { Log.w(TAG, "getCredential already canceled"); return; } pendingGetCredentialHandle.show(activity, cancellationSignal, executor, callback); } /** * Prepare for a get-credential operation. Returns a {@link PrepareGetCredentialResponse} that * can launch the credential retrieval UI flow to request a user credential for your app. * * @hide * <p>This API doesn't invoke any UI. It only performs the preparation work so that you can * later launch the remaining get-credential operation (involves UIs) through the {@link * #getCredential(PrepareGetCredentialResponse.PendingGetCredentialHandle, Activity, * CancellationSignal, Executor, OutcomeReceiver)} API which incurs less latency compared to * the {@link #getCredential(GetCredentialRequest, Activity, CancellationSignal, Executor, * OutcomeReceiver)} API that executes the whole operation in one call. * * @param request the request specifying type(s) of credentials to get from the user * @param cancellationSignal an optional signal that allows for cancelling this call * @param executor the callback will take place on this {@link Executor} * @param callback the callback invoked when the request succeeds or fails */ public void getPendingCredential( public void prepareGetCredential( @NonNull GetCredentialRequest request, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver< GetPendingCredentialResponse, GetCredentialException> callback) { PrepareGetCredentialResponse, GetCredentialException> callback) { requireNonNull(request, "request must not be null"); requireNonNull(executor, "executor must not be null"); requireNonNull(callback, "callback must not be null"); if (cancellationSignal != null && cancellationSignal.isCanceled()) { Log.w(TAG, "getPendingCredential already canceled"); Log.w(TAG, "prepareGetCredential already canceled"); return; } ICancellationSignal cancelRemote = null; GetCredentialTransportPendingUseCase getCredentialTransport = new GetCredentialTransportPendingUseCase(); try { cancelRemote = mService.executeGetPendingCredential( mService.executePrepareGetCredential( request, new GetPendingCredentialTransport(executor, callback), new PrepareGetCredentialTransport( executor, callback, getCredentialTransport), getCredentialTransport, mContext.getOpPackageName()); } catch (RemoteException e) { e.rethrowFromSystemServer(); Loading Loading @@ -484,23 +528,27 @@ public final class CredentialManager { } } private static class GetPendingCredentialTransport extends IGetPendingCredentialCallback.Stub { private static class PrepareGetCredentialTransport extends IPrepareGetCredentialCallback.Stub { // TODO: listen for cancellation to release callback. private final Executor mExecutor; private final OutcomeReceiver< GetPendingCredentialResponse, GetCredentialException> mCallback; PrepareGetCredentialResponse, GetCredentialException> mCallback; private final GetCredentialTransportPendingUseCase mGetCredentialTransport; private GetPendingCredentialTransport( private PrepareGetCredentialTransport( Executor executor, OutcomeReceiver<GetPendingCredentialResponse, GetCredentialException> callback) { OutcomeReceiver<PrepareGetCredentialResponse, GetCredentialException> callback, GetCredentialTransportPendingUseCase getCredentialTransport) { mExecutor = executor; mCallback = callback; mGetCredentialTransport = getCredentialTransport; } @Override public void onResponse(GetPendingCredentialResponse response) { mExecutor.execute(() -> mCallback.onResult(response)); public void onResponse(PrepareGetCredentialResponseInternal response) { mExecutor.execute(() -> mCallback.onResult( new PrepareGetCredentialResponse(response, mGetCredentialTransport))); } @Override Loading @@ -510,6 +558,51 @@ public final class CredentialManager { } } /** @hide */ protected static class GetCredentialTransportPendingUseCase extends IGetCredentialCallback.Stub { @Nullable private PrepareGetCredentialResponse.GetPendingCredentialInternalCallback mCallback = null; private GetCredentialTransportPendingUseCase() {} public void setCallback( PrepareGetCredentialResponse.GetPendingCredentialInternalCallback callback) { if (mCallback == null) { mCallback = callback; } else { throw new IllegalStateException("callback has already been set once"); } } @Override public void onPendingIntent(PendingIntent pendingIntent) { if (mCallback != null) { mCallback.onPendingIntent(pendingIntent); } else { Log.d(TAG, "Unexpected onPendingIntent call before the show invocation"); } } @Override public void onResponse(GetCredentialResponse response) { if (mCallback != null) { mCallback.onResponse(response); } else { Log.d(TAG, "Unexpected onResponse call before the show invocation"); } } @Override public void onError(String errorType, String message) { if (mCallback != null) { mCallback.onError(errorType, message); } else { Log.d(TAG, "Unexpected onError call before the show invocation"); } } } private static class GetCredentialTransport extends IGetCredentialCallback.Stub { // TODO: listen for cancellation to release callback. Loading @@ -535,7 +628,8 @@ public final class CredentialManager { TAG, "startIntentSender() failed for intent:" + pendingIntent.getIntentSender(), e); // TODO: propagate the error. mExecutor.execute(() -> mCallback.onError( new GetCredentialException(GetCredentialException.TYPE_UNKNOWN))); } } Loading Loading @@ -577,7 +671,8 @@ public final class CredentialManager { TAG, "startIntentSender() failed for intent:" + pendingIntent.getIntentSender(), e); // TODO: propagate the error. mExecutor.execute(() -> mCallback.onError( new CreateCredentialException(CreateCredentialException.TYPE_UNKNOWN))); } } Loading
core/java/android/credentials/ICredentialManager.aidl +2 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ import android.credentials.UnregisterCredentialDescriptionRequest; import android.credentials.IClearCredentialStateCallback; import android.credentials.ICreateCredentialCallback; import android.credentials.IGetCredentialCallback; import android.credentials.IGetPendingCredentialCallback; import android.credentials.IPrepareGetCredentialCallback; import android.credentials.ISetEnabledProvidersCallback; import android.content.ComponentName; import android.os.ICancellationSignal; Loading @@ -41,7 +41,7 @@ interface ICredentialManager { @nullable ICancellationSignal executeGetCredential(in GetCredentialRequest request, in IGetCredentialCallback callback, String callingPackage); @nullable ICancellationSignal executeGetPendingCredential(in GetCredentialRequest request, in IGetPendingCredentialCallback callback, String callingPackage); @nullable ICancellationSignal executePrepareGetCredential(in GetCredentialRequest request, in IPrepareGetCredentialCallback prepareGetCredentialCallback, in IGetCredentialCallback getCredentialCallback, String callingPackage); @nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage); Loading
core/java/android/credentials/IGetPendingCredentialCallback.aidl→core/java/android/credentials/IPrepareGetCredentialCallback.aidl +4 −4 Original line number Diff line number Diff line Loading @@ -17,14 +17,14 @@ package android.credentials; import android.app.PendingIntent; import android.credentials.GetPendingCredentialResponse; import android.credentials.PrepareGetCredentialResponseInternal; /** * Listener for a executeGetPendingCredential request. * Listener for a executePrepareGetCredential request. * * @hide */ interface IGetPendingCredentialCallback { oneway void onResponse(in GetPendingCredentialResponse response); interface IPrepareGetCredentialCallback { oneway void onResponse(in PrepareGetCredentialResponseInternal response); oneway void onError(String errorType, String message); } No newline at end of file
core/java/android/credentials/PrepareGetCredentialResponse.java 0 → 100644 +180 −0 Original line number Diff line number Diff line /* * 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.Activity; import android.app.PendingIntent; import android.content.IntentSender; 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(PendingGetCredentialHandle, Activity, 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 { /** * A handle that represents a pending get-credential operation. Pass this handle to {@link * CredentialManager#getCredential(PendingGetCredentialHandle, Activity, 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 Activity activity, @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 { activity.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0); } 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 { activity.startIntentSender(mPendingIntent.getIntentSender(), null, 0, 0, 0); } 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(PendingGetCredentialHandle, Activity, * 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); } }