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