1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.credentials;
18 
19 import static java.util.Objects.requireNonNull;
20 
21 import android.annotation.CallbackExecutor;
22 import android.annotation.Hide;
23 import android.annotation.IntDef;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.annotation.RequiresFeature;
27 import android.annotation.RequiresPermission;
28 import android.annotation.SystemService;
29 import android.annotation.TestApi;
30 import android.app.ActivityOptions;
31 import android.app.PendingIntent;
32 import android.content.ComponentName;
33 import android.content.Context;
34 import android.content.IntentSender;
35 import android.content.pm.PackageManager;
36 import android.os.Binder;
37 import android.os.Bundle;
38 import android.os.CancellationSignal;
39 import android.os.IBinder;
40 import android.os.ICancellationSignal;
41 import android.os.OutcomeReceiver;
42 import android.os.RemoteException;
43 import android.provider.DeviceConfig;
44 import android.util.Log;
45 
46 import java.lang.annotation.Retention;
47 import java.lang.annotation.RetentionPolicy;
48 import java.util.List;
49 import java.util.concurrent.Executor;
50 
51 /**
52  * Manages user authentication flows.
53  *
54  * <p>Note that an application should call the Jetpack CredentialManager apis instead of directly
55  * calling these framework apis.
56  *
57  * <p>The CredentialManager apis launch framework UI flows for a user to register a new credential
58  * or to consent to a saved credential from supported credential providers, which can then be used
59  * to authenticate to the app.
60  */
61 @SystemService(Context.CREDENTIAL_SERVICE)
62 @RequiresFeature(PackageManager.FEATURE_CREDENTIALS)
63 public final class CredentialManager {
64     /** @hide **/
65     @Hide
66     public static final String TAG = "CredentialManager";
67     private static final Bundle OPTIONS_SENDER_BAL_OPTIN = ActivityOptions.makeBasic()
68             .setPendingIntentBackgroundActivityStartMode(
69                     ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle();
70 
71     /** @hide */
72     @IntDef(
73             flag = true,
74             prefix = {"PROVIDER_FILTER_"},
75             value = {
76                 PROVIDER_FILTER_ALL_PROVIDERS,
77                 PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY,
78                 PROVIDER_FILTER_USER_PROVIDERS_ONLY,
79                 // By default the returned list of providers will not include any providers that
80                 // have been hidden by device policy. However, there are some cases where we want
81                 // them to show up (e.g. settings) so this will return the list of providers with
82                 // the hidden ones included.
83                 PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN,
84             })
85     @Retention(RetentionPolicy.SOURCE)
86     public @interface ProviderFilter {}
87 
88     /**
89      * Returns both system and user credential providers.
90      *
91      * @hide
92      */
93     @TestApi public static final int PROVIDER_FILTER_ALL_PROVIDERS = 0;
94 
95     /**
96      * Returns system credential providers only.
97      *
98      * @hide
99      */
100     @TestApi public static final int PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY = 1;
101 
102     /**
103      * Returns user credential providers only.
104      *
105      * @hide
106      */
107     @TestApi public static final int PROVIDER_FILTER_USER_PROVIDERS_ONLY = 2;
108 
109     /**
110      * Returns user credential providers only. This will include providers that
111      * have been disabled by the device policy.
112      *
113      * @hide
114      */
115     public static final int PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN = 3;
116 
117     private final Context mContext;
118     private final ICredentialManager mService;
119 
120     /**
121      * Flag to enable and disable Credential Manager.
122      *
123      * @hide
124      */
125     public static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER =
126             "enable_credential_manager";
127 
128     /**
129      * Flag to enable and disable Credential Description api.
130      *
131      * @hide
132      */
133     private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API =
134             "enable_credential_description_api";
135 
136     /**
137      * @hide
138      */
139     @Hide
140     public static final String EXTRA_AUTOFILL_RESULT_RECEIVER =
141             "android.credentials.AUTOFILL_RESULT_RECEIVER";
142 
143     /**
144      * @hide instantiated by ContextImpl.
145      */
CredentialManager(Context context, ICredentialManager service)146     public CredentialManager(Context context, ICredentialManager service) {
147         mContext = context;
148         mService = service;
149     }
150 
151     /**
152      * Returns a list of candidate credentials returned from credential manager providers
153      *
154      * @param request the request specifying type(s) of credentials to get from the
155      *                credential providers
156      * @param cancellationSignal an optional signal that allows for cancelling this call
157      * @param executor the callback will take place on this {@link Executor}
158      * @param callback the callback invoked when the request succeeds or fails
159      *
160      * @hide
161      */
162     @Hide
getCandidateCredentials( @onNull GetCredentialRequest request, @NonNull String callingPackage, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<GetCandidateCredentialsResponse, GetCandidateCredentialsException> callback, @NonNull IBinder clientCallback )163     public void getCandidateCredentials(
164             @NonNull GetCredentialRequest request,
165             @NonNull String callingPackage,
166             @Nullable CancellationSignal cancellationSignal,
167             @CallbackExecutor @NonNull Executor executor,
168             @NonNull OutcomeReceiver<GetCandidateCredentialsResponse,
169                     GetCandidateCredentialsException> callback,
170             @NonNull IBinder clientCallback
171     ) {
172         requireNonNull(request, "request must not be null");
173         requireNonNull(callingPackage, "callingPackage must not be null");
174         requireNonNull(executor, "executor must not be null");
175         requireNonNull(callback, "callback must not be null");
176 
177         if (cancellationSignal != null && cancellationSignal.isCanceled()) {
178             Log.w(TAG, "getCandidateCredentials already canceled");
179             return;
180         }
181 
182         ICancellationSignal cancelRemote = null;
183         try {
184             cancelRemote =
185                     mService.getCandidateCredentials(
186                             request,
187                             new GetCandidateCredentialsTransport(executor, callback),
188                             clientCallback,
189                             callingPackage);
190         } catch (RemoteException e) {
191             e.rethrowFromSystemServer();
192         }
193 
194         if (cancellationSignal != null && cancelRemote != null) {
195             cancellationSignal.setRemote(cancelRemote);
196         }
197     }
198 
199     /**
200      * Launches the necessary flows to retrieve an app credential from the user.
201      *
202      * <p>The execution can potentially launch UI flows to collect user consent to using a
203      * credential, display a picker when multiple credentials exist, etc.
204      * Callers (e.g. browsers) may optionally set origin in {@link GetCredentialRequest} for an
205      * app different from their own, to be able to get credentials on behalf of that app. They would
206      * need additional permission {@code CREDENTIAL_MANAGER_SET_ORIGIN}
207      * to use this functionality
208      *
209      * @param context the context used to launch any UI needed; use an activity context to make sure
210      *                the UI will be launched within the same task stack
211      * @param request the request specifying type(s) of credentials to get from the user
212      * @param cancellationSignal an optional signal that allows for cancelling this call
213      * @param executor the callback will take place on this {@link Executor}
214      * @param callback the callback invoked when the request succeeds or fails
215      */
getCredential( @onNull Context context, @NonNull GetCredentialRequest request, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback)216     public void getCredential(
217             @NonNull Context context,
218             @NonNull GetCredentialRequest request,
219             @Nullable CancellationSignal cancellationSignal,
220             @CallbackExecutor @NonNull Executor executor,
221             @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
222         requireNonNull(request, "request must not be null");
223         requireNonNull(context, "context must not be null");
224         requireNonNull(executor, "executor must not be null");
225         requireNonNull(callback, "callback must not be null");
226 
227         if (cancellationSignal != null && cancellationSignal.isCanceled()) {
228             Log.w(TAG, "getCredential already canceled");
229             return;
230         }
231 
232         ICancellationSignal cancelRemote = null;
233         try {
234             cancelRemote =
235                     mService.executeGetCredential(
236                             request,
237                             new GetCredentialTransport(context, executor, callback),
238                             mContext.getOpPackageName());
239         } catch (RemoteException e) {
240             e.rethrowFromSystemServer();
241         }
242 
243         if (cancellationSignal != null && cancelRemote != null) {
244             cancellationSignal.setRemote(cancelRemote);
245         }
246     }
247 
248     /**
249      * Launches the remaining flows to retrieve an app credential from the user, after the
250      * completed prefetch work corresponding to the given {@code pendingGetCredentialHandle}.
251      *
252      * <p>The execution can potentially launch UI flows to collect user consent to using a
253      * credential, display a picker when multiple credentials exist, etc.
254      *
255      * <p>Use this API to complete the full credential retrieval operation after you initiated a
256      * request through the {@link #prepareGetCredential(
257      * GetCredentialRequest, CancellationSignal, Executor, OutcomeReceiver)} API.
258      *
259      * @param context the context used to launch any UI needed; use an activity context to make sure
260      *                the UI will be launched within the same task stack
261      * @param pendingGetCredentialHandle the handle representing the pending operation to resume
262      * @param cancellationSignal an optional signal that allows for cancelling this call
263      * @param executor the callback will take place on this {@link Executor}
264      * @param callback the callback invoked when the request succeeds or fails
265      */
getCredential( @onNull Context context, @NonNull PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback)266     public void getCredential(
267             @NonNull Context context,
268             @NonNull PrepareGetCredentialResponse.PendingGetCredentialHandle
269             pendingGetCredentialHandle,
270             @Nullable CancellationSignal cancellationSignal,
271             @CallbackExecutor @NonNull Executor executor,
272             @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
273         requireNonNull(pendingGetCredentialHandle, "pendingGetCredentialHandle must not be null");
274         requireNonNull(context, "context must not be null");
275         requireNonNull(executor, "executor must not be null");
276         requireNonNull(callback, "callback must not be null");
277 
278         if (cancellationSignal != null && cancellationSignal.isCanceled()) {
279             Log.w(TAG, "getCredential already canceled");
280             return;
281         }
282 
283         pendingGetCredentialHandle.show(context, cancellationSignal, executor, callback);
284     }
285 
286     /**
287      * Prepare for a get-credential operation. Returns a {@link PrepareGetCredentialResponse} that
288      * can launch the credential retrieval UI flow to request a user credential for your app.
289      *
290      * <p>This API doesn't invoke any UI. It only performs the preparation work so that you can
291      * later launch the remaining get-credential operation (involves UIs) through the {@link
292      * #getCredential(Context, PrepareGetCredentialResponse.PendingGetCredentialHandle,
293      * CancellationSignal, Executor, OutcomeReceiver)} API which incurs less latency compared to
294      * the {@link #getCredential(Context, GetCredentialRequest, CancellationSignal, Executor,
295      * OutcomeReceiver)} API that executes the whole operation in one call.
296      *
297      * @param request            the request specifying type(s) of credentials to get from the user
298      * @param cancellationSignal an optional signal that allows for cancelling this call
299      * @param executor           the callback will take place on this {@link Executor}
300      * @param callback           the callback invoked when the request succeeds or fails
301      */
prepareGetCredential( @onNull GetCredentialRequest request, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver< PrepareGetCredentialResponse, GetCredentialException> callback)302     public void prepareGetCredential(
303             @NonNull GetCredentialRequest request,
304             @Nullable CancellationSignal cancellationSignal,
305             @CallbackExecutor @NonNull Executor executor,
306             @NonNull OutcomeReceiver<
307                     PrepareGetCredentialResponse, GetCredentialException> callback) {
308         requireNonNull(request, "request must not be null");
309         requireNonNull(executor, "executor must not be null");
310         requireNonNull(callback, "callback must not be null");
311 
312         if (cancellationSignal != null && cancellationSignal.isCanceled()) {
313             Log.w(TAG, "prepareGetCredential already canceled");
314             return;
315         }
316 
317         ICancellationSignal cancelRemote = null;
318         GetCredentialTransportPendingUseCase getCredentialTransport =
319                 new GetCredentialTransportPendingUseCase();
320         try {
321             cancelRemote =
322                     mService.executePrepareGetCredential(
323                             request,
324                             new PrepareGetCredentialTransport(
325                                     executor, callback, getCredentialTransport),
326                             getCredentialTransport,
327                             mContext.getOpPackageName());
328         } catch (RemoteException e) {
329             e.rethrowFromSystemServer();
330         }
331 
332         if (cancellationSignal != null && cancelRemote != null) {
333             cancellationSignal.setRemote(cancelRemote);
334         }
335     }
336 
337     /**
338      * Launches the necessary flows to register an app credential for the user.
339      *
340      * <p>The execution can potentially launch UI flows to collect user consent to creating or
341      * storing the new credential, etc.
342      * Callers (e.g. browsers) may optionally set origin in {@link CreateCredentialRequest} for an
343      * app different from their own, to be able to get credentials on behalf of that app. They would
344      * need additional permission {@code CREDENTIAL_MANAGER_SET_ORIGIN}
345      * to use this functionality
346      *
347      * @param context the context used to launch any UI needed; use an activity context to make sure
348      *                the UI will be launched within the same task stack
349      * @param request the request specifying type(s) of credentials to get from the user
350      * @param cancellationSignal an optional signal that allows for cancelling this call
351      * @param executor the callback will take place on this {@link Executor}
352      * @param callback the callback invoked when the request succeeds or fails
353      */
createCredential( @onNull Context context, @NonNull CreateCredentialRequest request, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback)354     public void createCredential(
355             @NonNull Context context,
356             @NonNull CreateCredentialRequest request,
357             @Nullable CancellationSignal cancellationSignal,
358             @CallbackExecutor @NonNull Executor executor,
359             @NonNull
360                     OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback) {
361         requireNonNull(request, "request must not be null");
362         requireNonNull(context, "context must not be null");
363         requireNonNull(executor, "executor must not be null");
364         requireNonNull(callback, "callback must not be null");
365 
366         if (cancellationSignal != null && cancellationSignal.isCanceled()) {
367             Log.w(TAG, "createCredential already canceled");
368             return;
369         }
370 
371         ICancellationSignal cancelRemote = null;
372         try {
373             cancelRemote =
374                     mService.executeCreateCredential(
375                             request,
376                             new CreateCredentialTransport(context, executor, callback),
377                             mContext.getOpPackageName());
378         } catch (RemoteException e) {
379             e.rethrowFromSystemServer();
380         }
381 
382         if (cancellationSignal != null && cancelRemote != null) {
383             cancellationSignal.setRemote(cancelRemote);
384         }
385     }
386 
387     /**
388      * Clears the current user credential state from all credential providers.
389      *
390      * <p>You should invoked this api after your user signs out of your app to notify all credential
391      * providers that any stored credential session for the given app should be cleared.
392      *
393      * <p>A credential provider may have stored an active credential session and use it to limit
394      * sign-in options for future get-credential calls. For example, it may prioritize the active
395      * credential over any other available credential. When your user explicitly signs out of your
396      * app and in order to get the holistic sign-in options the next time, you should call this API
397      * to let the provider clear any stored credential session.
398      *
399      * @param request the request data
400      * @param cancellationSignal an optional signal that allows for cancelling this call
401      * @param executor the callback will take place on this {@link Executor}
402      * @param callback the callback invoked when the request succeeds or fails
403      */
clearCredentialState( @onNull ClearCredentialStateRequest request, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<Void, ClearCredentialStateException> callback)404     public void clearCredentialState(
405             @NonNull ClearCredentialStateRequest request,
406             @Nullable CancellationSignal cancellationSignal,
407             @CallbackExecutor @NonNull Executor executor,
408             @NonNull OutcomeReceiver<Void, ClearCredentialStateException> callback) {
409         requireNonNull(request, "request must not be null");
410         requireNonNull(executor, "executor must not be null");
411         requireNonNull(callback, "callback must not be null");
412 
413         if (cancellationSignal != null && cancellationSignal.isCanceled()) {
414             Log.w(TAG, "clearCredentialState already canceled");
415             return;
416         }
417 
418         ICancellationSignal cancelRemote = null;
419         try {
420             cancelRemote =
421                     mService.clearCredentialState(
422                             request,
423                             new ClearCredentialStateTransport(executor, callback),
424                             mContext.getOpPackageName());
425         } catch (RemoteException e) {
426             e.rethrowFromSystemServer();
427         }
428 
429         if (cancellationSignal != null && cancelRemote != null) {
430             cancellationSignal.setRemote(cancelRemote);
431         }
432     }
433 
434     /**
435      * Sets a list of all user configurable credential providers registered on the system. This API
436      * is intended for settings apps.
437      *
438      * @param primaryProviders the primary providers that user selected for saving credentials. In
439      *                         the most case, there should be only one primary provider, However,
440      *                         if there are more than one CredentialProviderService in the same APK,
441      *                         they should be passed in altogether.
442      * @param providers the list of enabled providers.
443      * @param userId the user ID to configure credential manager for
444      * @param executor the callback will take place on this {@link Executor}
445      * @param callback the callback invoked when the request succeeds or fails
446      * @hide
447      */
448     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
setEnabledProviders( @onNull List<String> primaryProviders, @NonNull List<String> providers, int userId, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<Void, SetEnabledProvidersException> callback)449     public void setEnabledProviders(
450             @NonNull List<String> primaryProviders,
451             @NonNull List<String> providers,
452             int userId,
453             @CallbackExecutor @NonNull Executor executor,
454             @NonNull OutcomeReceiver<Void, SetEnabledProvidersException> callback) {
455         requireNonNull(executor, "executor must not be null");
456         requireNonNull(callback, "callback must not be null");
457         requireNonNull(providers, "providers must not be null");
458         requireNonNull(primaryProviders, "primaryProviders must not be null");
459 
460         try {
461             mService.setEnabledProviders(
462                     primaryProviders,
463                     providers, userId, new SetEnabledProvidersTransport(executor, callback));
464         } catch (RemoteException e) {
465             e.rethrowFromSystemServer();
466         }
467     }
468 
469     /**
470      * Returns {@code true} if the calling application provides a CredentialProviderService that is
471      * enabled for the current user, or {@code false} otherwise. CredentialProviderServices are
472      * enabled on a per-service basis so the individual component name of the service should be
473      * passed in here. <strong>Usage of this API is encouraged in API level 35 and above. It
474      * may throw a NullPointerException on certain devices running other API versions.</strong>
475      *
476      * @throws IllegalArgumentException if the componentName package does not match the calling
477      * package name this call will throw an exception
478      *
479      * @throws NullPointerException Usage of this API is discouraged as it is not fully
480      * functional, and may throw a NullPointerException on certain devices and/or API versions
481      *
482      * @param componentName the component name to check is enabled
483      */
isEnabledCredentialProviderService(@onNull ComponentName componentName)484     public boolean isEnabledCredentialProviderService(@NonNull ComponentName componentName) {
485         requireNonNull(componentName, "componentName must not be null");
486 
487         try {
488             return mService.isEnabledCredentialProviderService(
489                     componentName, mContext.getOpPackageName());
490         } catch (RemoteException e) {
491             throw e.rethrowFromSystemServer();
492         }
493     }
494 
495     /**
496      * Returns the list of CredentialProviderInfo for all discovered credential providers on this
497      * device but will include test system providers as well.
498      *
499      * @hide
500      */
501     @NonNull
502     @TestApi
503     @RequiresPermission(
504       anyOf = {
505         android.Manifest.permission.QUERY_ALL_PACKAGES,
506         android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS
507       })
getCredentialProviderServicesForTesting( @roviderFilter int providerFilter)508     public List<CredentialProviderInfo> getCredentialProviderServicesForTesting(
509              @ProviderFilter int providerFilter) {
510         try {
511             return mService.getCredentialProviderServicesForTesting(providerFilter);
512         } catch (RemoteException e) {
513             throw e.rethrowFromSystemServer();
514         }
515     }
516 
517     /**
518      * Returns the list of CredentialProviderInfo for all discovered credential providers on this
519      * device.
520      *
521      * @hide
522      */
523     @NonNull
524     @RequiresPermission(
525       anyOf = {
526         android.Manifest.permission.QUERY_ALL_PACKAGES,
527         android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS
528       })
getCredentialProviderServices( int userId, @ProviderFilter int providerFilter)529     public List<CredentialProviderInfo> getCredentialProviderServices(
530             int userId, @ProviderFilter int providerFilter) {
531         try {
532             return mService.getCredentialProviderServices(userId, providerFilter);
533         } catch (RemoteException e) {
534             throw e.rethrowFromSystemServer();
535         }
536     }
537 
538     /**
539      * Returns whether the service is enabled.
540      *
541      * @hide
542      */
543     @TestApi
isServiceEnabled(@onNull Context context)544     public static boolean isServiceEnabled(@NonNull Context context) {
545         requireNonNull(context, "context must not be null");
546         if (context == null) {
547             return false;
548         }
549         CredentialManager credentialManager =
550                 (CredentialManager) context.getSystemService(Context.CREDENTIAL_SERVICE);
551         if (credentialManager != null) {
552             return credentialManager.isServiceEnabled();
553         }
554         return false;
555     }
556 
557     /**
558      * Returns whether the service is enabled.
559      *
560      * @hide
561      */
isServiceEnabled()562     private boolean isServiceEnabled() {
563         try {
564             return mService.isServiceEnabled();
565         } catch (RemoteException e) {
566             return false;
567         }
568     }
569 
570     /**
571      * Returns whether the credential description api is enabled.
572      *
573      * @hide
574      */
isCredentialDescriptionApiEnabled(Context context)575     public static boolean isCredentialDescriptionApiEnabled(Context context) {
576         if (context == null) {
577             return false;
578         }
579         CredentialManager credentialManager =
580                 (CredentialManager) context.getSystemService(Context.CREDENTIAL_SERVICE);
581         if (credentialManager != null) {
582             return credentialManager.isCredentialDescriptionApiEnabled();
583         }
584         return false;
585     }
586 
isCredentialDescriptionApiEnabled()587     private boolean isCredentialDescriptionApiEnabled() {
588         return DeviceConfig.getBoolean(
589                 DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API, false);
590     }
591 
592     /**
593      * Registers a {@link CredentialDescription} for an actively provisioned {@link Credential} a
594      * CredentialProvider has. This registry will then be used to determine where to fetch the
595      * requested {@link Credential} from. Not all credential types will be supported. The
596      * distinction will be made by the JetPack layer. For the types that are supported, JetPack will
597      * add a new key-value pair into {@link GetCredentialRequest}. These will not be persistent on
598      * the device. The Credential Providers will need to call this API again upon device reboot.
599      *
600      * @param request the request data
601      * @throws {@link UnsupportedOperationException} if the feature has not been enabled.
602      * @throws {@link com.android.server.credentials.NonCredentialProviderCallerException} if the
603      *     calling package name is not also listed as a Credential Provider.
604      * @throws {@link IllegalArgumentException} if the calling Credential Provider can not handle
605      *     one or more of the Credential Types that are sent for registration.
606      */
registerCredentialDescription( @onNull RegisterCredentialDescriptionRequest request)607     public void registerCredentialDescription(
608             @NonNull RegisterCredentialDescriptionRequest request) {
609         requireNonNull(request, "request must not be null");
610 
611         try {
612             mService.registerCredentialDescription(request, mContext.getOpPackageName());
613         } catch (RemoteException e) {
614             e.rethrowFromSystemServer();
615         }
616     }
617 
618     /**
619      * Unregisters a {@link CredentialDescription} for an actively provisioned {@link Credential}
620      * that has been registered previously.
621      *
622      * @param request the request data
623      * @throws {@link UnsupportedOperationException} if the feature has not been enabled.
624      */
unregisterCredentialDescription( @onNull UnregisterCredentialDescriptionRequest request)625     public void unregisterCredentialDescription(
626             @NonNull UnregisterCredentialDescriptionRequest request) {
627         requireNonNull(request, "request must not be null");
628 
629         try {
630             mService.unregisterCredentialDescription(request, mContext.getOpPackageName());
631         } catch (RemoteException e) {
632             e.rethrowFromSystemServer();
633         }
634     }
635 
636     private static class PrepareGetCredentialTransport extends IPrepareGetCredentialCallback.Stub {
637         // TODO: listen for cancellation to release callback.
638 
639         private final Executor mExecutor;
640         private final OutcomeReceiver<
641                 PrepareGetCredentialResponse, GetCredentialException> mCallback;
642         private final GetCredentialTransportPendingUseCase mGetCredentialTransport;
643 
PrepareGetCredentialTransport( Executor executor, OutcomeReceiver<PrepareGetCredentialResponse, GetCredentialException> callback, GetCredentialTransportPendingUseCase getCredentialTransport)644         private PrepareGetCredentialTransport(
645                 Executor executor,
646                 OutcomeReceiver<PrepareGetCredentialResponse, GetCredentialException> callback,
647                 GetCredentialTransportPendingUseCase getCredentialTransport) {
648             mExecutor = executor;
649             mCallback = callback;
650             mGetCredentialTransport = getCredentialTransport;
651         }
652 
653         @Override
onResponse(PrepareGetCredentialResponseInternal response)654         public void onResponse(PrepareGetCredentialResponseInternal response) {
655             final long identity = Binder.clearCallingIdentity();
656             try {
657                 mExecutor.execute(() -> mCallback.onResult(
658                         new PrepareGetCredentialResponse(response, mGetCredentialTransport)));
659             } finally {
660                 Binder.restoreCallingIdentity(identity);
661             }
662         }
663 
664         @Override
onError(String errorType, String message)665         public void onError(String errorType, String message) {
666             final long identity = Binder.clearCallingIdentity();
667             try {
668                 mExecutor.execute(
669                         () -> mCallback.onError(new GetCredentialException(errorType, message)));
670             }  finally {
671                 Binder.restoreCallingIdentity(identity);
672             }
673         }
674     }
675 
676     /** @hide */
677     protected static class GetCredentialTransportPendingUseCase
678             extends IGetCredentialCallback.Stub {
679         @Nullable private PrepareGetCredentialResponse.GetPendingCredentialInternalCallback
680                 mCallback = null;
681 
GetCredentialTransportPendingUseCase()682         private GetCredentialTransportPendingUseCase() {}
683 
setCallback( PrepareGetCredentialResponse.GetPendingCredentialInternalCallback callback)684         public void setCallback(
685                 PrepareGetCredentialResponse.GetPendingCredentialInternalCallback callback) {
686             if (mCallback == null) {
687                 mCallback = callback;
688             } else {
689                 throw new IllegalStateException("callback has already been set once");
690             }
691         }
692 
693         @Override
onPendingIntent(PendingIntent pendingIntent)694         public void onPendingIntent(PendingIntent pendingIntent) {
695             if (mCallback != null) {
696                 mCallback.onPendingIntent(pendingIntent);
697             } else {
698                 Log.d(TAG, "Unexpected onPendingIntent call before the show invocation");
699             }
700         }
701 
702         @Override
onResponse(GetCredentialResponse response)703         public void onResponse(GetCredentialResponse response) {
704             if (mCallback != null) {
705                 final long identity = Binder.clearCallingIdentity();
706                 try {
707                     mCallback.onResponse(response);
708                 } finally {
709                     Binder.restoreCallingIdentity(identity);
710                 }
711             } else {
712                 Log.d(TAG, "Unexpected onResponse call before the show invocation");
713             }
714         }
715 
716         @Override
onError(String errorType, String message)717         public void onError(String errorType, String message) {
718             if (mCallback != null) {
719                 final long identity = Binder.clearCallingIdentity();
720                 try {
721                     mCallback.onError(errorType, message);
722                 } finally {
723                     Binder.restoreCallingIdentity(identity);
724                 }
725             } else {
726                 Log.d(TAG, "Unexpected onError call before the show invocation");
727             }
728         }
729     }
730 
731     private static class GetCandidateCredentialsTransport
732             extends IGetCandidateCredentialsCallback.Stub {
733 
734         private final Executor mExecutor;
735         private final OutcomeReceiver<GetCandidateCredentialsResponse,
736                 GetCandidateCredentialsException> mCallback;
737 
GetCandidateCredentialsTransport( Executor executor, OutcomeReceiver<GetCandidateCredentialsResponse, GetCandidateCredentialsException> callback)738         private GetCandidateCredentialsTransport(
739                 Executor executor,
740                 OutcomeReceiver<GetCandidateCredentialsResponse,
741                         GetCandidateCredentialsException> callback) {
742             mExecutor = executor;
743             mCallback = callback;
744         }
745 
746         @Override
onResponse(GetCandidateCredentialsResponse response)747         public void onResponse(GetCandidateCredentialsResponse response) {
748             final long identity = Binder.clearCallingIdentity();
749             try {
750                 mExecutor.execute(() -> mCallback.onResult(response));
751             } finally {
752                 Binder.restoreCallingIdentity(identity);
753             }
754         }
755 
756         @Override
onError(String errorType, String message)757         public void onError(String errorType, String message) {
758             final long identity = Binder.clearCallingIdentity();
759             try {
760                 mExecutor.execute(
761                         () -> mCallback.onError(new GetCandidateCredentialsException(
762                                 errorType, message)));
763             } finally {
764                 Binder.restoreCallingIdentity(identity);
765             }
766         }
767     }
768 
769     private static class GetCredentialTransport extends IGetCredentialCallback.Stub {
770         // TODO: listen for cancellation to release callback.
771 
772         private final Context mContext;
773         private final Executor mExecutor;
774         private final OutcomeReceiver<GetCredentialResponse, GetCredentialException> mCallback;
775 
GetCredentialTransport( Context context, Executor executor, OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback)776         private GetCredentialTransport(
777                 Context context,
778                 Executor executor,
779                 OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
780             mContext = context;
781             mExecutor = executor;
782             mCallback = callback;
783         }
784 
785         @Override
onPendingIntent(PendingIntent pendingIntent)786         public void onPendingIntent(PendingIntent pendingIntent) {
787             try {
788                 mContext.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0,
789                         OPTIONS_SENDER_BAL_OPTIN);
790             } catch (IntentSender.SendIntentException e) {
791                 Log.e(
792                         TAG,
793                         "startIntentSender() failed for intent:" + pendingIntent.getIntentSender(),
794                         e);
795                 final long identity = Binder.clearCallingIdentity();
796                 try {
797                     mExecutor.execute(() -> mCallback.onError(
798                             new GetCredentialException(GetCredentialException.TYPE_UNKNOWN)));
799                 } finally {
800                     Binder.restoreCallingIdentity(identity);
801                 }
802             }
803         }
804 
805         @Override
onResponse(GetCredentialResponse response)806         public void onResponse(GetCredentialResponse response) {
807             final long identity = Binder.clearCallingIdentity();
808             try {
809                 mExecutor.execute(() -> mCallback.onResult(response));
810             } finally {
811                 Binder.restoreCallingIdentity(identity);
812             }
813         }
814 
815         @Override
onError(String errorType, String message)816         public void onError(String errorType, String message) {
817             final long identity = Binder.clearCallingIdentity();
818             try {
819                 mExecutor.execute(
820                         () -> mCallback.onError(new GetCredentialException(errorType, message)));
821             } finally {
822                 Binder.restoreCallingIdentity(identity);
823             }
824         }
825     }
826 
827     private static class CreateCredentialTransport extends ICreateCredentialCallback.Stub {
828         // TODO: listen for cancellation to release callback.
829 
830         private final Context mContext;
831         private final Executor mExecutor;
832         private final OutcomeReceiver<CreateCredentialResponse, CreateCredentialException>
833                 mCallback;
834 
CreateCredentialTransport( Context context, Executor executor, OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback)835         private CreateCredentialTransport(
836                 Context context,
837                 Executor executor,
838                 OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback) {
839             mContext = context;
840             mExecutor = executor;
841             mCallback = callback;
842         }
843 
844         @Override
onPendingIntent(PendingIntent pendingIntent)845         public void onPendingIntent(PendingIntent pendingIntent) {
846             try {
847                 mContext.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0,
848                         OPTIONS_SENDER_BAL_OPTIN);
849             } catch (IntentSender.SendIntentException e) {
850                 Log.e(
851                         TAG,
852                         "startIntentSender() failed for intent:" + pendingIntent.getIntentSender(),
853                         e);
854                 final long identity = Binder.clearCallingIdentity();
855                 try {
856                     mExecutor.execute(() -> mCallback.onError(
857                             new CreateCredentialException(CreateCredentialException.TYPE_UNKNOWN)));
858                 } finally {
859                     Binder.restoreCallingIdentity(identity);
860                 }
861             }
862         }
863 
864         @Override
onResponse(CreateCredentialResponse response)865         public void onResponse(CreateCredentialResponse response) {
866             final long identity = Binder.clearCallingIdentity();
867             try {
868                 mExecutor.execute(() -> mCallback.onResult(response));
869             } finally {
870                 Binder.restoreCallingIdentity(identity);
871             }
872         }
873 
874         @Override
onError(String errorType, String message)875         public void onError(String errorType, String message) {
876             final long identity = Binder.clearCallingIdentity();
877             try {
878                 mExecutor.execute(
879                         () -> mCallback.onError(new CreateCredentialException(errorType, message)));
880             } finally {
881                 Binder.restoreCallingIdentity(identity);
882             }
883         }
884     }
885 
886     private static class ClearCredentialStateTransport extends IClearCredentialStateCallback.Stub {
887         // TODO: listen for cancellation to release callback.
888 
889         private final Executor mExecutor;
890         private final OutcomeReceiver<Void, ClearCredentialStateException> mCallback;
891 
ClearCredentialStateTransport( Executor executor, OutcomeReceiver<Void, ClearCredentialStateException> callback)892         private ClearCredentialStateTransport(
893                 Executor executor, OutcomeReceiver<Void, ClearCredentialStateException> callback) {
894             mExecutor = executor;
895             mCallback = callback;
896         }
897 
898         @Override
onSuccess()899         public void onSuccess() {
900             final long identity = Binder.clearCallingIdentity();
901             try {
902                 mCallback.onResult(null);
903             } finally {
904                 Binder.restoreCallingIdentity(identity);
905             }
906         }
907 
908         @Override
onError(String errorType, String message)909         public void onError(String errorType, String message) {
910             final long identity = Binder.clearCallingIdentity();
911             try {
912                 mExecutor.execute(
913                         () -> mCallback.onError(
914                                 new ClearCredentialStateException(errorType, message)));
915             } finally {
916                 Binder.restoreCallingIdentity(identity);
917             }
918         }
919     }
920 
921     private static class SetEnabledProvidersTransport extends ISetEnabledProvidersCallback.Stub {
922         // TODO: listen for cancellation to release callback.
923 
924         private final Executor mExecutor;
925         private final OutcomeReceiver<Void, SetEnabledProvidersException> mCallback;
926 
SetEnabledProvidersTransport( Executor executor, OutcomeReceiver<Void, SetEnabledProvidersException> callback)927         private SetEnabledProvidersTransport(
928                 Executor executor, OutcomeReceiver<Void, SetEnabledProvidersException> callback) {
929             mExecutor = executor;
930             mCallback = callback;
931         }
932 
onResponse(Void result)933         public void onResponse(Void result) {
934             final long identity = Binder.clearCallingIdentity();
935             try {
936                 mExecutor.execute(() -> mCallback.onResult(result));
937             } finally {
938                 Binder.restoreCallingIdentity(identity);
939             }
940         }
941 
942         @Override
onResponse()943         public void onResponse() {
944             final long identity = Binder.clearCallingIdentity();
945             try {
946                 mExecutor.execute(() -> mCallback.onResult(null));
947             } finally {
948                 Binder.restoreCallingIdentity(identity);
949             }
950         }
951 
952         @Override
onError(String errorType, String message)953         public void onError(String errorType, String message) {
954             final long identity = Binder.clearCallingIdentity();
955             try {
956                 mExecutor.execute(
957                         () -> mCallback.onError(
958                                 new SetEnabledProvidersException(errorType, message)));
959             } finally {
960                 Binder.restoreCallingIdentity(identity);
961             }
962         }
963     }
964 }
965