1 /* 2 * Copyright (C) 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.service.credentials; 18 19 import android.Manifest; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresPermission; 23 import android.content.pm.ParceledListSlice; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 27 import com.android.internal.util.Preconditions; 28 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.Objects; 32 33 /** 34 * Response to a {@link BeginCreateCredentialRequest}. 35 */ 36 public final class BeginCreateCredentialResponse implements Parcelable { 37 private final @NonNull ParceledListSlice<CreateEntry> mCreateEntries; 38 private final @Nullable RemoteEntry mRemoteCreateEntry; 39 40 /** 41 * Creates an empty response instance, to be used when there are no {@link CreateEntry} 42 * to return. 43 */ BeginCreateCredentialResponse()44 public BeginCreateCredentialResponse() { 45 this(/*createEntries=*/new ParceledListSlice<>(new ArrayList<>()), 46 /*remoteCreateEntry=*/null); 47 } 48 BeginCreateCredentialResponse(@onNull Parcel in)49 private BeginCreateCredentialResponse(@NonNull Parcel in) { 50 mCreateEntries = in.readParcelable( 51 null, android.content.pm.ParceledListSlice.class); 52 mRemoteCreateEntry = in.readTypedObject(RemoteEntry.CREATOR); 53 } 54 55 @Override writeToParcel(@onNull Parcel dest, int flags)56 public void writeToParcel(@NonNull Parcel dest, int flags) { 57 dest.writeParcelable(mCreateEntries, flags); 58 dest.writeTypedObject(mRemoteCreateEntry, flags); 59 } 60 61 @Override describeContents()62 public int describeContents() { 63 return 0; 64 } 65 66 public static final @NonNull Creator<BeginCreateCredentialResponse> CREATOR = 67 new Creator<BeginCreateCredentialResponse>() { 68 @Override 69 public BeginCreateCredentialResponse createFromParcel(@NonNull Parcel in) { 70 return new BeginCreateCredentialResponse(in); 71 } 72 73 @Override 74 public BeginCreateCredentialResponse[] newArray(int size) { 75 return new BeginCreateCredentialResponse[size]; 76 } 77 }; 78 BeginCreateCredentialResponse( @onNull ParceledListSlice<CreateEntry> createEntries, @Nullable RemoteEntry remoteCreateEntry)79 /* package-private */ BeginCreateCredentialResponse( 80 @NonNull ParceledListSlice<CreateEntry> createEntries, 81 @Nullable RemoteEntry remoteCreateEntry) { 82 this.mCreateEntries = createEntries; 83 com.android.internal.util.AnnotationValidations.validate( 84 NonNull.class, null, mCreateEntries); 85 this.mRemoteCreateEntry = remoteCreateEntry; 86 } 87 88 /** Returns the list of create entries to be displayed on the UI. */ getCreateEntries()89 public @NonNull List<CreateEntry> getCreateEntries() { 90 return mCreateEntries.getList(); 91 } 92 93 /** Returns the remote create entry to be displayed on the UI. */ getRemoteCreateEntry()94 public @Nullable RemoteEntry getRemoteCreateEntry() { 95 return mRemoteCreateEntry; 96 } 97 98 /** 99 * A builder for {@link BeginCreateCredentialResponse} 100 */ 101 @SuppressWarnings("WeakerAccess") /* synthetic access */ 102 public static final class Builder { 103 private @NonNull List<CreateEntry> mCreateEntries = new ArrayList<>(); 104 private @Nullable RemoteEntry mRemoteCreateEntry; 105 106 /** 107 * Sets the list of create entries to be shown on the UI. 108 * 109 * @throws IllegalArgumentException If {@code createEntries} is empty. 110 * @throws NullPointerException If {@code createEntries} is null, or any of its elements 111 * are null. 112 */ setCreateEntries(@onNull List<CreateEntry> createEntries)113 public @NonNull Builder setCreateEntries(@NonNull List<CreateEntry> createEntries) { 114 Preconditions.checkCollectionNotEmpty(createEntries, "createEntries"); 115 mCreateEntries = Preconditions.checkCollectionElementsNotNull( 116 createEntries, "createEntries"); 117 return this; 118 } 119 120 /** 121 * Adds an entry to the list of create entries to be shown on the UI. 122 * 123 * @throws NullPointerException If {@code createEntry} is null. 124 */ addCreateEntry(@onNull CreateEntry createEntry)125 public @NonNull Builder addCreateEntry(@NonNull CreateEntry createEntry) { 126 mCreateEntries.add(Objects.requireNonNull(createEntry)); 127 return this; 128 } 129 130 /** 131 * Sets a remote create entry to be shown on the UI. Provider must set this entry if they 132 * wish to create the credential on a different device. 133 * 134 * <p> When constructing the {@link CreateEntry} object, the {@code pendingIntent} must be 135 * set such that it leads to an activity that can provide UI to fulfill the request on 136 * a remote device. When user selects this {@code remoteCreateEntry}, the system will 137 * invoke the {@code pendingIntent} set on the {@link CreateEntry}. 138 * 139 * <p> Once the remote credential flow is complete, the {@link android.app.Activity} 140 * result should be set to {@link android.app.Activity#RESULT_OK} and an extra with the 141 * {@link CredentialProviderService#EXTRA_CREATE_CREDENTIAL_RESPONSE} key should be populated 142 * with a {@link android.credentials.CreateCredentialResponse} object. 143 * 144 * <p> Note that as a provider service you will only be able to set a remote entry if : 145 * - Provider service possesses the 146 * {@link Manifest.permission#PROVIDE_REMOTE_CREDENTIALS} permission. 147 * - Provider service is configured as the provider that can provide remote entries. 148 * 149 * If the above conditions are not met, setting back {@link BeginCreateCredentialResponse} 150 * on the callback from {@link CredentialProviderService#onBeginCreateCredential} 151 * will throw a {@link SecurityException}. 152 */ 153 @RequiresPermission(Manifest.permission.PROVIDE_REMOTE_CREDENTIALS) setRemoteCreateEntry(@ullable RemoteEntry remoteCreateEntry)154 public @NonNull Builder setRemoteCreateEntry(@Nullable RemoteEntry remoteCreateEntry) { 155 mRemoteCreateEntry = remoteCreateEntry; 156 return this; 157 } 158 159 /** 160 * Builds a new instance of {@link BeginCreateCredentialResponse}. 161 */ build()162 public @NonNull BeginCreateCredentialResponse build() { 163 return new BeginCreateCredentialResponse( 164 new ParceledListSlice<>(mCreateEntries), 165 mRemoteCreateEntry); 166 } 167 } 168 } 169