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 android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN; 20 21 import static java.util.Objects.requireNonNull; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SuppressLint; 27 import android.os.Bundle; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 31 import com.android.internal.util.AnnotationValidations; 32 import com.android.internal.util.Preconditions; 33 34 /** 35 * A request to register a specific type of user credential, potentially launching UI flows to 36 * collect user consent and any other operation needed. 37 */ 38 public final class CreateCredentialRequest implements Parcelable { 39 40 /** 41 * True/false value to determine if the calling app info should be 42 * sent to the provider at every stage. 43 * 44 * Developers must set this to false if they wish to remove the 45 * {@link android.service.credentials.CallingAppInfo} from the query phase request 46 * that providers receive. Note, that providers will still receive the app info in 47 * the final phase after the user has selected the entry. 48 */ 49 private final boolean mAlwaysSendAppInfoToProvider; 50 51 52 /** 53 * The requested credential type. 54 */ 55 @NonNull 56 private final String mType; 57 58 /** 59 * The full credential creation request data. 60 */ 61 @NonNull 62 private final Bundle mCredentialData; 63 64 /** 65 * The partial request data that will be sent to the provider during the initial creation 66 * candidate query stage. 67 */ 68 @NonNull 69 private final Bundle mCandidateQueryData; 70 71 /** 72 * Determines whether the request must only be fulfilled by a system provider. 73 */ 74 private final boolean mIsSystemProviderRequired; 75 76 /** 77 * The origin of the calling app. Callers of this special API (e.g. browsers) 78 * can set this origin for an app different from their own, to be able to get credentials 79 * on behalf of that app. 80 */ 81 @Nullable 82 private final String mOrigin; 83 84 /** 85 * Returns the requested credential type. 86 */ 87 @NonNull getType()88 public String getType() { 89 return mType; 90 } 91 92 /** 93 * Returns the full credential creation request data. 94 * 95 * For security reason, a provider will receive the request data in two stages. First it gets 96 * a partial request, {@link #getCandidateQueryData()} that do not contain sensitive user 97 * information; it uses this information to provide credential creation candidates that the 98 * [@code CredentialManager] will show to the user. Next, this full request data will be sent to 99 * a provider only if the user further grants the consent by choosing a candidate from the 100 * provider. 101 */ 102 @NonNull getCredentialData()103 public Bundle getCredentialData() { 104 return mCredentialData; 105 } 106 107 /** 108 * Returns the partial request data that will be sent to the provider during the initial 109 * creation candidate query stage. 110 * 111 * For security reason, a provider will receive the request data in two stages. First it gets 112 * this partial request that do not contain sensitive user information; it uses this information 113 * to provide credential creation candidates that the [@code CredentialManager] will show to 114 * the user. Next, the full request data, {@link #getCredentialData()}, will be sent to a 115 * provider only if the user further grants the consent by choosing a candidate from the 116 * provider. 117 */ 118 @NonNull getCandidateQueryData()119 public Bundle getCandidateQueryData() { 120 return mCandidateQueryData; 121 } 122 123 /** 124 * Returns true if the request must only be fulfilled by a system provider, and false 125 * otherwise. 126 */ isSystemProviderRequired()127 public boolean isSystemProviderRequired() { 128 return mIsSystemProviderRequired; 129 } 130 131 /** 132 * Return true/false value to determine if the calling app info should always be sent 133 * to providers (if true), or removed from the query phase (if false). 134 */ alwaysSendAppInfoToProvider()135 public boolean alwaysSendAppInfoToProvider() { 136 return mAlwaysSendAppInfoToProvider; 137 } 138 139 /** 140 * Returns the origin of the calling app if set otherwise returns null. 141 */ 142 @Nullable getOrigin()143 public String getOrigin() { 144 return mOrigin; 145 } 146 147 @Override writeToParcel(@onNull Parcel dest, int flags)148 public void writeToParcel(@NonNull Parcel dest, int flags) { 149 dest.writeString8(mType); 150 dest.writeBundle(mCredentialData); 151 dest.writeBundle(mCandidateQueryData); 152 dest.writeBoolean(mIsSystemProviderRequired); 153 dest.writeBoolean(mAlwaysSendAppInfoToProvider); 154 dest.writeString8(mOrigin); 155 } 156 157 @Override describeContents()158 public int describeContents() { 159 return 0; 160 } 161 162 @Override toString()163 public String toString() { 164 return "CreateCredentialRequest {" 165 + "type=" + mType 166 + ", credentialData=" + mCredentialData 167 + ", candidateQueryData=" + mCandidateQueryData 168 + ", isSystemProviderRequired=" + mIsSystemProviderRequired 169 + ", alwaysSendAppInfoToProvider=" 170 + mAlwaysSendAppInfoToProvider 171 + ", origin=" + mOrigin 172 + "}"; 173 } 174 175 /** 176 * Constructs a {@link CreateCredentialRequest}. 177 * 178 * @param type the requested credential type 179 * @param credentialData the full credential creation request data 180 * @param candidateQueryData the partial request data that will be sent to the provider 181 * during the initial creation candidate query stage 182 * @param isSystemProviderRequired whether the request must only be fulfilled by a system 183 * provider 184 * @param alwaysSendAppInfoToProvider whether the 185 * {@link android.service.credentials.CallingAppInfo} should be propagated to the provider 186 * at every stage of the request. If set to false, 187 * the calling app info will be removed from 188 * the query phase, and will only be sent along 189 * with the final request, after the user has selected 190 * an entry on the UI. 191 * @param origin the origin of the calling app. Callers of this special setter (e.g. browsers) 192 * can set this origin for an app different from their own, to be able to get 193 * credentials on behalf of that app. 194 * 195 * @throws IllegalArgumentException If type is empty. 196 */ CreateCredentialRequest( @onNull String type, @NonNull Bundle credentialData, @NonNull Bundle candidateQueryData, boolean isSystemProviderRequired, boolean alwaysSendAppInfoToProvider, @NonNull String origin)197 private CreateCredentialRequest( 198 @NonNull String type, 199 @NonNull Bundle credentialData, 200 @NonNull Bundle candidateQueryData, 201 boolean isSystemProviderRequired, 202 boolean alwaysSendAppInfoToProvider, 203 @NonNull String origin) { 204 mType = Preconditions.checkStringNotEmpty(type, "type must not be empty"); 205 mCredentialData = requireNonNull(credentialData, "credentialData must not be null"); 206 mCandidateQueryData = requireNonNull(candidateQueryData, 207 "candidateQueryData must not be null"); 208 mIsSystemProviderRequired = isSystemProviderRequired; 209 mAlwaysSendAppInfoToProvider = alwaysSendAppInfoToProvider; 210 mOrigin = origin; 211 } 212 CreateCredentialRequest(@onNull Parcel in)213 private CreateCredentialRequest(@NonNull Parcel in) { 214 String type = in.readString8(); 215 Bundle credentialData = in.readBundle(); 216 Bundle candidateQueryData = in.readBundle(); 217 boolean isSystemProviderRequired = in.readBoolean(); 218 boolean alwaysSendAppInfoToProvider = in.readBoolean(); 219 mOrigin = in.readString8(); 220 221 mType = type; 222 AnnotationValidations.validate(NonNull.class, null, mType); 223 mCredentialData = credentialData; 224 AnnotationValidations.validate(NonNull.class, null, mCredentialData); 225 mCandidateQueryData = candidateQueryData; 226 AnnotationValidations.validate(NonNull.class, null, mCandidateQueryData); 227 mIsSystemProviderRequired = isSystemProviderRequired; 228 mAlwaysSendAppInfoToProvider = alwaysSendAppInfoToProvider; 229 } 230 231 public static final @NonNull Parcelable.Creator<CreateCredentialRequest> CREATOR = 232 new Parcelable.Creator<CreateCredentialRequest>() { 233 @Override 234 public CreateCredentialRequest[] newArray(int size) { 235 return new CreateCredentialRequest[size]; 236 } 237 238 @Override 239 public CreateCredentialRequest createFromParcel(@NonNull Parcel in) { 240 return new CreateCredentialRequest(in); 241 } 242 }; 243 244 /** A builder for {@link CreateCredentialRequest}. */ 245 public static final class Builder { 246 247 private boolean mAlwaysSendAppInfoToProvider = true; 248 249 @NonNull 250 private String mType; 251 252 @NonNull 253 private final Bundle mCredentialData; 254 255 @NonNull 256 private final Bundle mCandidateQueryData; 257 258 private boolean mIsSystemProviderRequired; 259 260 private String mOrigin; 261 262 /** 263 * @param type the type of the credential to be stored 264 * @param credentialData the full credential creation request data, which must at minimum 265 * contain the required fields observed at the 266 * {@link androidx.credentials.CreateCredentialRequest} Bundle conversion static methods, 267 * because they are required for properly displaying the system credential selector UI 268 * @param candidateQueryData the partial request data that will be sent to the provider 269 * during the initial creation candidate query stage 270 */ Builder( @onNull String type, @NonNull Bundle credentialData, @NonNull Bundle candidateQueryData)271 public Builder( 272 @NonNull String type, 273 @NonNull Bundle credentialData, 274 @NonNull Bundle candidateQueryData) { 275 mType = Preconditions.checkStringNotEmpty(type, 276 "type must not be null or empty"); 277 mCredentialData = requireNonNull(credentialData, 278 "credentialData must not be null"); 279 mCandidateQueryData = requireNonNull(candidateQueryData, 280 "candidateQueryData must not be null"); 281 } 282 283 /** 284 * Sets a true/false value to determine if the calling app info should be 285 * removed from the request that is sent to the providers. 286 * 287 * Developers must set this to false if they wish to remove the 288 * {@link android.service.credentials.CallingAppInfo} from the query phases requests that 289 * providers receive. Note that the calling app info will still be sent in the 290 * final phase after the user has made a selection on the UI. 291 * 292 * If not set, the default value will be true and the calling app info will be 293 * propagated to the providers in every phase. 294 */ 295 @SuppressLint("MissingGetterMatchingBuilder") 296 @NonNull setAlwaysSendAppInfoToProvider(boolean value)297 public CreateCredentialRequest.Builder setAlwaysSendAppInfoToProvider(boolean value) { 298 mAlwaysSendAppInfoToProvider = value; 299 return this; 300 } 301 302 /** 303 * Sets whether the request must only be fulfilled by a system provider. 304 * This defaults to false 305 */ 306 @SuppressLint("MissingGetterMatchingBuilder") 307 @NonNull setIsSystemProviderRequired(boolean value)308 public CreateCredentialRequest.Builder setIsSystemProviderRequired(boolean value) { 309 mIsSystemProviderRequired = value; 310 return this; 311 } 312 313 /** 314 * Sets the origin of the calling app. Callers of this special setter (e.g. browsers) 315 * can set this origin for an app different from their own, to be able to get 316 * credentials on behalf of that app. The permission check only happens later when this 317 * instance is passed and processed by the Credential Manager. 318 */ 319 @SuppressLint({"MissingGetterMatchingBuilder", "AndroidFrameworkRequiresPermission"}) 320 @RequiresPermission(CREDENTIAL_MANAGER_SET_ORIGIN) 321 @NonNull setOrigin(@onNull String origin)322 public CreateCredentialRequest.Builder setOrigin(@NonNull String origin) { 323 mOrigin = origin; 324 return this; 325 } 326 327 /** 328 * Builds a {@link GetCredentialRequest}. 329 * 330 * @throws IllegalArgumentException If credentialOptions is empty. 331 */ 332 @NonNull build()333 public CreateCredentialRequest build() { 334 Preconditions.checkStringNotEmpty( 335 mType, 336 "type must not be empty"); 337 338 return new CreateCredentialRequest(mType, mCredentialData, mCandidateQueryData, 339 mIsSystemProviderRequired, mAlwaysSendAppInfoToProvider, mOrigin); 340 } 341 } 342 } 343