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_ALLOWED_PROVIDERS; 20 21 import static java.util.Objects.requireNonNull; 22 23 import android.annotation.NonNull; 24 import android.annotation.SuppressLint; 25 import android.content.ComponentName; 26 import android.os.Bundle; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 import android.util.ArraySet; 30 31 import androidx.annotation.RequiresPermission; 32 33 import com.android.internal.util.AnnotationValidations; 34 import com.android.internal.util.Preconditions; 35 36 import java.util.Set; 37 38 /** 39 * Information about a specific type of credential to be requested during a {@link 40 * CredentialManager#getCredential} operation. 41 */ 42 public final class CredentialOption implements Parcelable { 43 44 /** 45 * Bundle key to the list of elements keys supported/requested. Framework will use this key 46 * to determine which types of Credentials will utilize Credential Registry when filtering 47 * Credential Providers to ping. 48 */ 49 public static final String SUPPORTED_ELEMENT_KEYS = "android.credentials" 50 + ".GetCredentialOption.SUPPORTED_ELEMENT_KEYS"; 51 52 /** 53 * The requested credential type. 54 */ 55 @NonNull 56 private final String mType; 57 58 /** 59 * The full request data. 60 */ 61 @NonNull 62 private final Bundle mCredentialRetrievalData; 63 64 /** 65 * The partial request data that will be sent to the provider during the initial credential 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 * A list of {@link ComponentName}s corresponding to the providers that this option must be 78 * queried against. 79 */ 80 @NonNull 81 private final ArraySet<ComponentName> mAllowedProviders; 82 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 request data. 94 */ 95 @NonNull getCredentialRetrievalData()96 public Bundle getCredentialRetrievalData() { 97 return mCredentialRetrievalData; 98 } 99 100 /** 101 * Returns the partial request data that will be sent to the provider during the initial 102 * credential candidate query stage. 103 * 104 * For security reason, a provider will receive the request data in two stages. First it gets 105 * this partial request that do not contain sensitive user information; it uses this 106 * information to provide credential candidates that the [@code CredentialManager] will show to 107 * the user. Next, the full request data, {@link #getCredentialRetrievalData()}, will be sent to 108 * a provider only if the user further grants the consent by choosing a candidate from the 109 * provider. 110 */ 111 @NonNull getCandidateQueryData()112 public Bundle getCandidateQueryData() { 113 return mCandidateQueryData; 114 } 115 116 /** 117 * Returns true if the request must only be fulfilled by a system provider, and false 118 * otherwise. 119 */ isSystemProviderRequired()120 public boolean isSystemProviderRequired() { 121 return mIsSystemProviderRequired; 122 } 123 124 /** 125 * Returns the set of {@link ComponentName} corresponding to providers that must receive 126 * this option. 127 */ 128 @NonNull getAllowedProviders()129 public Set<ComponentName> getAllowedProviders() { 130 return mAllowedProviders; 131 } 132 133 @Override writeToParcel(@onNull Parcel dest, int flags)134 public void writeToParcel(@NonNull Parcel dest, int flags) { 135 dest.writeString8(mType); 136 dest.writeBundle(mCredentialRetrievalData); 137 dest.writeBundle(mCandidateQueryData); 138 dest.writeBoolean(mIsSystemProviderRequired); 139 dest.writeArraySet(mAllowedProviders); 140 } 141 142 @Override describeContents()143 public int describeContents() { 144 return 0; 145 } 146 147 @Override toString()148 public String toString() { 149 return "CredentialOption {" 150 + "type=" + mType 151 + ", requestData=" + mCredentialRetrievalData 152 + ", candidateQueryData=" + mCandidateQueryData 153 + ", isSystemProviderRequired=" + mIsSystemProviderRequired 154 + ", allowedProviders=" + mAllowedProviders 155 + "}"; 156 } 157 158 /** 159 * Constructs a {@link CredentialOption}. 160 * 161 * @param type the requested credential type 162 * @param credentialRetrievalData the request data 163 * @param candidateQueryData the partial request data that will be sent to the provider 164 * during the initial credential candidate query stage 165 * @param isSystemProviderRequired whether the request must only be fulfilled by a system 166 * provider 167 * @throws IllegalArgumentException If type is empty. 168 */ CredentialOption( @onNull String type, @NonNull Bundle credentialRetrievalData, @NonNull Bundle candidateQueryData, boolean isSystemProviderRequired, @NonNull ArraySet<ComponentName> allowedProviders)169 private CredentialOption( 170 @NonNull String type, 171 @NonNull Bundle credentialRetrievalData, 172 @NonNull Bundle candidateQueryData, 173 boolean isSystemProviderRequired, 174 @NonNull ArraySet<ComponentName> allowedProviders) { 175 mType = Preconditions.checkStringNotEmpty(type, "type must not be empty"); 176 mCredentialRetrievalData = requireNonNull(credentialRetrievalData, 177 "requestData must not be null"); 178 mCandidateQueryData = requireNonNull(candidateQueryData, 179 "candidateQueryData must not be null"); 180 mIsSystemProviderRequired = isSystemProviderRequired; 181 mAllowedProviders = requireNonNull(allowedProviders, "providerFilterSer must" 182 + "not be empty"); 183 } 184 185 /** 186 * Constructs a {@link CredentialOption}. 187 * 188 * @param type the requested credential type 189 * @param credentialRetrievalData the request data 190 * @param candidateQueryData the partial request data that will be sent to the provider 191 * during the initial credential candidate query stage 192 * @param isSystemProviderRequired whether the request must only be fulfilled by a system 193 * provider 194 * @throws IllegalArgumentException If type is empty, or null. 195 * @throws NullPointerException If {@code credentialRetrievalData}, or 196 * {@code candidateQueryData} is null. 197 * 198 * @hide 199 */ CredentialOption( @onNull String type, @NonNull Bundle credentialRetrievalData, @NonNull Bundle candidateQueryData, boolean isSystemProviderRequired)200 public CredentialOption( 201 @NonNull String type, 202 @NonNull Bundle credentialRetrievalData, 203 @NonNull Bundle candidateQueryData, 204 boolean isSystemProviderRequired) { 205 this( 206 type, 207 credentialRetrievalData, 208 candidateQueryData, 209 isSystemProviderRequired, 210 new ArraySet<>() 211 ); 212 } 213 CredentialOption(@onNull Parcel in)214 private CredentialOption(@NonNull Parcel in) { 215 String type = in.readString8(); 216 Bundle data = in.readBundle(); 217 Bundle candidateQueryData = in.readBundle(); 218 boolean isSystemProviderRequired = in.readBoolean(); 219 220 mType = type; 221 AnnotationValidations.validate(NonNull.class, null, mType); 222 mCredentialRetrievalData = data; 223 AnnotationValidations.validate(NonNull.class, null, mCredentialRetrievalData); 224 mCandidateQueryData = candidateQueryData; 225 AnnotationValidations.validate(NonNull.class, null, mCandidateQueryData); 226 mIsSystemProviderRequired = isSystemProviderRequired; 227 mAllowedProviders = (ArraySet<ComponentName>) in.readArraySet(null); 228 AnnotationValidations.validate(NonNull.class, null, mAllowedProviders); 229 } 230 231 @NonNull 232 public static final Parcelable.Creator<CredentialOption> CREATOR = new Parcelable.Creator<>() { 233 @Override 234 public CredentialOption[] newArray(int size) { 235 return new CredentialOption[size]; 236 } 237 238 @Override 239 public CredentialOption createFromParcel(@NonNull Parcel in) { 240 return new CredentialOption(in); 241 } 242 }; 243 244 /** A builder for {@link CredentialOption}. */ 245 public static final class Builder { 246 247 @NonNull 248 private String mType; 249 250 @NonNull 251 private Bundle mCredentialRetrievalData; 252 253 @NonNull 254 private Bundle mCandidateQueryData; 255 256 private boolean mIsSystemProviderRequired = false; 257 258 @NonNull 259 private ArraySet<ComponentName> mAllowedProviders = new ArraySet<>(); 260 261 /** 262 * @param type the type of the credential option 263 * @param credentialRetrievalData the full request data 264 * @param candidateQueryData the partial request data that will be sent to the provider 265 * during the initial credential candidate query stage. 266 * @throws IllegalArgumentException If {@code type} is null, or empty 267 * @throws NullPointerException If {@code credentialRetrievalData}, or 268 * {@code candidateQueryData} is null 269 */ Builder(@onNull String type, @NonNull Bundle credentialRetrievalData, @NonNull Bundle candidateQueryData)270 public Builder(@NonNull String type, @NonNull Bundle credentialRetrievalData, 271 @NonNull Bundle candidateQueryData) { 272 mType = Preconditions.checkStringNotEmpty(type, "type must not be " 273 + "null, or empty"); 274 mCredentialRetrievalData = requireNonNull(credentialRetrievalData, 275 "credentialRetrievalData must not be null"); 276 mCandidateQueryData = requireNonNull(candidateQueryData, 277 "candidateQueryData must not be null"); 278 } 279 280 /** 281 * Sets a true/false value corresponding to whether this option must be serviced by 282 * system credentials providers only. 283 */ 284 @SuppressLint("MissingGetterMatchingBuilder") 285 @NonNull setIsSystemProviderRequired(boolean isSystemProviderRequired)286 public Builder setIsSystemProviderRequired(boolean isSystemProviderRequired) { 287 mIsSystemProviderRequired = isSystemProviderRequired; 288 return this; 289 } 290 291 /** 292 * Adds a provider {@link ComponentName} to be queried while gathering credentials from 293 * credential providers on the device. 294 * 295 * If no candidate providers are specified, all user configured and system credential 296 * providers will be queried in the candidate query phase. 297 * 298 * If an invalid component name is provided, or a service corresponding to the 299 * component name does not exist on the device, that component name is ignored. 300 * If all component names are invalid, or not present on the device, no providers 301 * are queried and no credentials are retrieved. 302 * 303 * @throws NullPointerException If {@code allowedProvider} is null 304 */ 305 @RequiresPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS) 306 @NonNull addAllowedProvider(@onNull ComponentName allowedProvider)307 public Builder addAllowedProvider(@NonNull ComponentName allowedProvider) { 308 mAllowedProviders.add(requireNonNull(allowedProvider, 309 "allowedProvider must not be null")); 310 return this; 311 } 312 313 /** 314 * Sets a set of provider {@link ComponentName} to be queried while gathering credentials 315 * from credential providers on the device. 316 * 317 * If no candidate providers are specified, all user configured and system credential 318 * providers will be queried in the candidate query phase. 319 * 320 * If an invalid component name is provided, or a service corresponding to the 321 * component name does not exist on the device, that component name is ignored. 322 * If all component names are invalid, or not present on the device, no providers 323 * are queried and no credentials are retrieved. 324 * 325 * @throws NullPointerException If {@code allowedProviders} is null, or any of its 326 * elements are null. 327 */ 328 @RequiresPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS) 329 @NonNull setAllowedProviders(@onNull Set<ComponentName> allowedProviders)330 public Builder setAllowedProviders(@NonNull Set<ComponentName> allowedProviders) { 331 Preconditions.checkCollectionElementsNotNull( 332 allowedProviders, 333 /*valueName=*/ "allowedProviders"); 334 mAllowedProviders = new ArraySet<>(allowedProviders); 335 return this; 336 } 337 338 /** 339 * Builds a {@link CredentialOption}. 340 */ 341 @NonNull build()342 public CredentialOption build() { 343 return new CredentialOption(mType, mCredentialRetrievalData, mCandidateQueryData, 344 mIsSystemProviderRequired, mAllowedProviders); 345 } 346 } 347 } 348