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 androidx.credentials 18 19 import android.content.ComponentName 20 import android.os.Bundle 21 import androidx.annotation.RequiresApi 22 import androidx.credentials.internal.FrameworkClassParsingException 23 24 /** 25 * Encapsulates a request to get a user credential. 26 * 27 * An application can construct such a request by adding one or more types of [CredentialOption], 28 * and then call [CredentialManager.getCredential] to launch framework UI flows to allow the user to 29 * consent to using a previously saved credential for the given application. 30 * 31 * @param credentialOptions the list of [CredentialOption] from which the user can choose one to 32 * authenticate to the app 33 * @param origin the origin of a different application if the request is being made on behalf of 34 * that application (Note: for API level >=34, setting a non-null value for this parameter, will 35 * throw a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present) 36 * @param preferIdentityDocUi the value which signals if the UI should be tailored to display an 37 * identity document like driver license etc 38 * @param preferUiBrandingComponentName a service [ComponentName] from which the Credential Selector 39 * UI will pull its label and icon to render top level branding (Note: your app must have the 40 * permission android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS to specify this, or it 41 * would not take effect; also this bit may not take effect for Android API level 33 and below, 42 * depending on the pre-34 provider(s) you have chosen 43 * @param preferImmediatelyAvailableCredentials true if you prefer the operation to return 44 * immediately when there is no available credentials instead of falling back to discovering 45 * remote options, and false (default) otherwise 46 * @property credentialOptions the list of [CredentialOption] from which the user can choose one to 47 * authenticate to the app 48 * @property origin the origin of a different application if the request is being made on behalf of 49 * that application. For API level >=34, setting a non-null value for this parameter, will throw a 50 * SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present. 51 * @property preferIdentityDocUi the value which signals if the UI should be tailored to display an 52 * identity document like driver license etc. 53 * @property preferUiBrandingComponentName a service [ComponentName] from which the Credential 54 * Selector UI will pull its label and icon to render top level branding. Your app must have the 55 * permission android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS to specify this, or it 56 * would not take effect. Notice that this bit may not take effect for Android API level 33 and 57 * below, depending on the pre-34 provider(s) you have chosen. 58 * @property preferImmediatelyAvailableCredentials true if you prefer the operation to return 59 * immediately when there is no available credentials instead of falling back to discovering 60 * remote options, and false (default) otherwise 61 * @throws IllegalArgumentException If [credentialOptions] is empty or contains 62 * [GetRestoreCredentialOption] with another option (i.e. [GetPasswordOption] or 63 * [GetPublicKeyCredentialOption]). 64 */ 65 @OptIn(ExperimentalDigitalCredentialApi::class) 66 class GetCredentialRequest 67 @JvmOverloads 68 constructor( 69 val credentialOptions: List<CredentialOption>, 70 val origin: String? = null, 71 val preferIdentityDocUi: Boolean = false, 72 val preferUiBrandingComponentName: ComponentName? = null, 73 @get:JvmName("preferImmediatelyAvailableCredentials") 74 val preferImmediatelyAvailableCredentials: Boolean = false, 75 ) { 76 77 init { <lambda>null78 require(credentialOptions.isNotEmpty()) { "credentialOptions should not be empty" } 79 if (credentialOptions.size > 1) { 80 val digitalCredentialOptionCount = <lambda>null81 credentialOptions.count { it is GetDigitalCredentialOption } 82 if ( 83 digitalCredentialOptionCount > 0 && 84 digitalCredentialOptionCount != credentialOptions.size 85 ) { 86 throw IllegalArgumentException( 87 "Digital Credential Option cannot be used with other credential option." 88 ) 89 } optionnull90 for (option in credentialOptions) { 91 if (option is GetRestoreCredentialOption) { 92 throw IllegalArgumentException( 93 "Only a single GetRestoreCredentialOption should be provided." 94 ) 95 } 96 } 97 } 98 } 99 100 /** A builder for [GetCredentialRequest]. */ 101 class Builder { 102 private var credentialOptions: MutableList<CredentialOption> = mutableListOf() 103 private var origin: String? = null 104 private var preferIdentityDocUi: Boolean = false 105 private var preferImmediatelyAvailableCredentials: Boolean = false 106 private var preferUiBrandingComponentName: ComponentName? = null 107 108 /** Adds a specific type of [CredentialOption]. */ addCredentialOptionnull109 fun addCredentialOption(credentialOption: CredentialOption): Builder { 110 credentialOptions.add(credentialOption) 111 return this 112 } 113 114 /** Sets the list of [CredentialOption]. */ setCredentialOptionsnull115 fun setCredentialOptions(credentialOptions: List<CredentialOption>): Builder { 116 this.credentialOptions = credentialOptions.toMutableList() 117 return this 118 } 119 120 /** 121 * Sets the [origin] of a different application if the request is being made on behalf of 122 * that application. For API level >=34, setting a non-null value for this parameter, will 123 * throw a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not 124 * present. 125 */ setOriginnull126 fun setOrigin(origin: String): Builder { 127 this.origin = origin 128 return this 129 } 130 131 /** 132 * Sets whether you prefer the operation to return immediately when there is no available 133 * credentials instead of falling back to discovering remote options. The default value is 134 * false. 135 */ 136 @Suppress("MissingGetterMatchingBuilder") setPreferImmediatelyAvailableCredentialsnull137 fun setPreferImmediatelyAvailableCredentials( 138 preferImmediatelyAvailableCredentials: Boolean 139 ): Builder { 140 this.preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials 141 return this 142 } 143 144 /** 145 * Sets service [ComponentName] from which the Credential Selector UI will pull its label 146 * and icon to render top level branding. Your app must have the permission 147 * android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS to specify this, or it would 148 * not take effect. Notice that this bit may not take effect for Android API level 33 and 149 * below, depending on the pre-34 provider(s) you have chosen. 150 */ setPreferUiBrandingComponentNamenull151 fun setPreferUiBrandingComponentName(component: ComponentName?): Builder { 152 this.preferUiBrandingComponentName = component 153 return this 154 } 155 156 /** 157 * Sets the [Boolean] preferIdentityDocUi to true if the requester wants to prefer using a 158 * UI suited for Identity Documents like mDocs, Driving License etc. 159 */ 160 @Suppress("MissingGetterMatchingBuilder") setPreferIdentityDocUinull161 fun setPreferIdentityDocUi(preferIdentityDocUi: Boolean): Builder { 162 this.preferIdentityDocUi = preferIdentityDocUi 163 return this 164 } 165 166 /** 167 * Builds a [GetCredentialRequest]. 168 * 169 * @throws IllegalArgumentException If [credentialOptions] is empty 170 */ buildnull171 fun build(): GetCredentialRequest { 172 return GetCredentialRequest( 173 credentialOptions.toList(), 174 origin, 175 preferIdentityDocUi, 176 preferUiBrandingComponentName, 177 preferImmediatelyAvailableCredentials 178 ) 179 } 180 } 181 182 companion object { 183 internal const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS = 184 "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS" 185 private const val BUNDLE_KEY_PREFER_IDENTITY_DOC_UI = 186 "androidx.credentials.BUNDLE_KEY_PREFER_IDENTITY_DOC_UI" 187 private const val BUNDLE_KEY_PREFER_UI_BRANDING_COMPONENT_NAME = 188 "androidx.credentials.BUNDLE_KEY_PREFER_UI_BRANDING_COMPONENT_NAME" 189 190 /** 191 * Returns the request metadata as a `Bundle`. 192 * 193 * This API should only be used by OEM services and library groups. 194 * 195 * Note: this is not the equivalent of the complete request itself. For example, it does not 196 * include the request's `credentialOptions` or `origin`. 197 */ 198 @JvmStatic getRequestMetadataBundlenull199 fun getRequestMetadataBundle(request: GetCredentialRequest): Bundle { 200 val bundle = Bundle() 201 bundle.putBoolean(BUNDLE_KEY_PREFER_IDENTITY_DOC_UI, request.preferIdentityDocUi) 202 bundle.putBoolean( 203 BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS, 204 request.preferImmediatelyAvailableCredentials 205 ) 206 bundle.putParcelable( 207 BUNDLE_KEY_PREFER_UI_BRANDING_COMPONENT_NAME, 208 request.preferUiBrandingComponentName 209 ) 210 return bundle 211 } 212 213 /** 214 * Parses the [request] into an instance of [GetCredentialRequest]. 215 * 216 * It is recommended to construct a GetCredentialRequest by direct constructor calls, 217 * instead of using this API. This API should only be used by a small subset of system apps 218 * that reconstruct an existing object for user interactions such as collecting consents. 219 * 220 * @param request the framework GetCredentialRequest object 221 */ 222 @RequiresApi(34) 223 @JvmStatic createFromnull224 fun createFrom(request: android.credentials.GetCredentialRequest): GetCredentialRequest { 225 return createFrom( 226 request.credentialOptions.map { CredentialOption.createFrom(it) }, 227 request.origin, 228 request.data 229 ) 230 } 231 232 /** 233 * Parses the raw data into an instance of [GetCredentialRequest]. 234 * 235 * It is recommended to construct a GetCredentialRequest by direct constructor calls, 236 * instead of using this API. This API should only be used by a small subset of system apps 237 * that reconstruct an existing object for user interactions such as collecting consents. 238 * 239 * @param credentialOptions matches [GetCredentialRequest.credentialOptions] 240 * @param origin matches [GetCredentialRequest.origin] 241 * @param metadata request metadata serialized as a Bundle using [getRequestMetadataBundle] 242 */ 243 @JvmStatic createFromnull244 fun createFrom( 245 credentialOptions: List<CredentialOption>, 246 origin: String?, 247 metadata: Bundle 248 ): GetCredentialRequest { 249 try { 250 val preferIdentityDocUi = metadata.getBoolean(BUNDLE_KEY_PREFER_IDENTITY_DOC_UI) 251 val preferImmediatelyAvailableCredentials = 252 metadata.getBoolean(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS) 253 @Suppress("DEPRECATION") 254 val preferUiBrandingComponentName = 255 metadata.getParcelable<ComponentName>( 256 BUNDLE_KEY_PREFER_UI_BRANDING_COMPONENT_NAME 257 ) 258 val getCredentialBuilder = 259 Builder() 260 .setCredentialOptions(credentialOptions) 261 .setPreferIdentityDocUi(preferIdentityDocUi) 262 .setPreferUiBrandingComponentName(preferUiBrandingComponentName) 263 .setPreferImmediatelyAvailableCredentials( 264 preferImmediatelyAvailableCredentials 265 ) 266 if (origin != null) { 267 getCredentialBuilder.setOrigin(origin) 268 } 269 return getCredentialBuilder.build() 270 } catch (e: Exception) { 271 throw FrameworkClassParsingException() 272 } 273 } 274 } 275 } 276