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 import java.util.ArrayList; 35 import java.util.List; 36 37 /** 38 * A request to retrieve the user credential, potentially launching UI flows to let the user pick 39 * from different credential sources. 40 */ 41 public final class GetCredentialRequest implements Parcelable { 42 43 /** 44 * The list of credential requests. 45 */ 46 @NonNull 47 private final List<CredentialOption> mCredentialOptions; 48 49 /** 50 * The top request level data. 51 */ 52 @NonNull 53 private final Bundle mData; 54 55 /** 56 * The origin of the calling app. Callers of this special API (e.g. browsers) 57 * can set this origin for an app different from their own, to be able to get credentials 58 * on behalf of that app. 59 */ 60 @Nullable 61 private String mOrigin; 62 63 /** 64 * True/False value to determine if the calling app info should be 65 * removed from the request that is sent to the providers. 66 * Developers must set this to false if they wish to remove the 67 * {@link android.service.credentials.CallingAppInfo} from the query phases requests that 68 * providers receive. 69 * If not set, the default value will be true and the calling app info will be 70 * propagated to the providers. 71 */ 72 private final boolean mAlwaysSendAppInfoToProvider; 73 74 /** 75 * Returns the list of credential options to be requested. 76 */ 77 @NonNull getCredentialOptions()78 public List<CredentialOption> getCredentialOptions() { 79 return mCredentialOptions; 80 } 81 82 /** 83 * Returns the top request level data. 84 */ 85 @NonNull getData()86 public Bundle getData() { 87 return mData; 88 } 89 90 /** 91 * Returns the origin of the calling app if set otherwise returns null. 92 */ 93 @Nullable getOrigin()94 public String getOrigin() { 95 return mOrigin; 96 } 97 98 /** 99 * Returns a value to determine if the calling app info should be always 100 * sent to the provider in every phase (if true), or should be removed 101 * from the query phase, and only sent as part of the request in the final phase, 102 * after the user has made a selection on the UI (if false). 103 */ alwaysSendAppInfoToProvider()104 public boolean alwaysSendAppInfoToProvider() { 105 return mAlwaysSendAppInfoToProvider; 106 } 107 108 @Override writeToParcel(@onNull Parcel dest, int flags)109 public void writeToParcel(@NonNull Parcel dest, int flags) { 110 dest.writeTypedList(mCredentialOptions, flags); 111 dest.writeBundle(mData); 112 dest.writeBoolean(mAlwaysSendAppInfoToProvider); 113 dest.writeString8(mOrigin); 114 } 115 116 @Override describeContents()117 public int describeContents() { 118 return 0; 119 } 120 121 @Override toString()122 public String toString() { 123 return "GetCredentialRequest {credentialOption=" + mCredentialOptions 124 + ", data=" + mData 125 + ", alwaysSendAppInfoToProvider=" 126 + mAlwaysSendAppInfoToProvider 127 + ", origin=" + mOrigin 128 + "}"; 129 } 130 GetCredentialRequest(@onNull List<CredentialOption> credentialOptions, @NonNull Bundle data, @NonNull boolean alwaysSendAppInfoToProvider, String origin)131 private GetCredentialRequest(@NonNull List<CredentialOption> credentialOptions, 132 @NonNull Bundle data, @NonNull boolean alwaysSendAppInfoToProvider, String origin) { 133 Preconditions.checkCollectionNotEmpty( 134 credentialOptions, 135 /*valueName=*/ "credentialOptions"); 136 Preconditions.checkCollectionElementsNotNull( 137 credentialOptions, 138 /*valueName=*/ "credentialOptions"); 139 mCredentialOptions = credentialOptions; 140 mData = requireNonNull(data, 141 "data must not be null"); 142 mAlwaysSendAppInfoToProvider = alwaysSendAppInfoToProvider; 143 mOrigin = origin; 144 } 145 GetCredentialRequest(@onNull Parcel in)146 private GetCredentialRequest(@NonNull Parcel in) { 147 List<CredentialOption> credentialOptions = new ArrayList<CredentialOption>(); 148 in.readTypedList(credentialOptions, CredentialOption.CREATOR); 149 mCredentialOptions = credentialOptions; 150 AnnotationValidations.validate(NonNull.class, null, mCredentialOptions); 151 152 Bundle data = in.readBundle(); 153 mData = data; 154 AnnotationValidations.validate(NonNull.class, null, mData); 155 156 mAlwaysSendAppInfoToProvider = in.readBoolean(); 157 mOrigin = in.readString8(); 158 } 159 160 @NonNull public static final Parcelable.Creator<GetCredentialRequest> CREATOR = 161 new Parcelable.Creator<>() { 162 @Override 163 public GetCredentialRequest[] newArray(int size) { 164 return new GetCredentialRequest[size]; 165 } 166 167 @Override 168 public GetCredentialRequest createFromParcel(@NonNull Parcel in) { 169 return new GetCredentialRequest(in); 170 } 171 }; 172 173 /** A builder for {@link GetCredentialRequest}. */ 174 public static final class Builder { 175 176 @NonNull 177 private List<CredentialOption> mCredentialOptions = new ArrayList<>(); 178 179 @NonNull 180 private final Bundle mData; 181 182 @NonNull 183 private boolean mAlwaysSendAppInfoToProvider = true; 184 185 private String mOrigin; 186 187 /** 188 * @param data the top request level data 189 */ Builder(@onNull Bundle data)190 public Builder(@NonNull Bundle data) { 191 mData = requireNonNull(data, "data must not be null"); 192 } 193 194 /** 195 * Adds a specific type of {@link CredentialOption}. 196 */ 197 @NonNull addCredentialOption(@onNull CredentialOption credentialOption)198 public Builder addCredentialOption(@NonNull CredentialOption credentialOption) { 199 mCredentialOptions.add(requireNonNull( 200 credentialOption, "credentialOption must not be null")); 201 return this; 202 } 203 204 /** 205 * Sets a true/false value to determine if the calling app info should be 206 * removed from the request that is sent to the providers. 207 * 208 * Developers must set this to false if they wish to remove the 209 * {@link android.service.credentials.CallingAppInfo} from the query phases requests that 210 * providers receive. Note that the calling app info will still be sent in the 211 * final phase after the user has made a selection on the UI. 212 * 213 * If not set, the default value will be true and the calling app info will be 214 * propagated to the providers in every phase. 215 */ 216 @SuppressLint("MissingGetterMatchingBuilder") 217 @NonNull setAlwaysSendAppInfoToProvider(boolean value)218 public Builder setAlwaysSendAppInfoToProvider(boolean value) { 219 mAlwaysSendAppInfoToProvider = value; 220 return this; 221 } 222 223 /** 224 * Sets the list of {@link CredentialOption}. 225 */ 226 @NonNull setCredentialOptions( @onNull List<CredentialOption> credentialOptions)227 public Builder setCredentialOptions( 228 @NonNull List<CredentialOption> credentialOptions) { 229 Preconditions.checkCollectionElementsNotNull( 230 credentialOptions, 231 /*valueName=*/ "credentialOptions"); 232 mCredentialOptions = new ArrayList<>(credentialOptions); 233 return this; 234 } 235 236 /** 237 * Sets the origin of the calling app. Callers of this special setter (e.g. browsers) 238 * can set this origin for an app different from their own, to be able to get 239 * credentials on behalf of that app. The permission check only happens later when this 240 * instance is passed and processed by the Credential Manager. 241 */ 242 @SuppressLint({"MissingGetterMatchingBuilder", "AndroidFrameworkRequiresPermission"}) 243 @RequiresPermission(CREDENTIAL_MANAGER_SET_ORIGIN) 244 @NonNull setOrigin(@onNull String origin)245 public Builder setOrigin(@NonNull String origin) { 246 mOrigin = origin; 247 return this; 248 } 249 250 /** 251 * Builds a {@link GetCredentialRequest}. 252 * 253 * @throws IllegalArgumentException If credentialOptions is empty. 254 */ 255 @NonNull build()256 public GetCredentialRequest build() { 257 Preconditions.checkCollectionNotEmpty( 258 mCredentialOptions, 259 /*valueName=*/ "credentialOptions"); 260 Preconditions.checkCollectionElementsNotNull( 261 mCredentialOptions, 262 /*valueName=*/ "credentialOptions"); 263 return new GetCredentialRequest(mCredentialOptions, mData, 264 mAlwaysSendAppInfoToProvider, mOrigin); 265 } 266 } 267 } 268