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 static java.util.Objects.requireNonNull;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.app.PendingIntent;
24 import android.app.slice.Slice;
25 import android.credentials.GetCredentialResponse;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 
29 import com.android.internal.util.Preconditions;
30 
31 /**
32  * A credential entry that is to be displayed on the account selector that is presented to the
33  * user.
34  *
35  * <p>If user selects this entry, the corresponding {@link PendingIntent},
36  * set on the {@code slice} will be invoked to launch activities that require some user engagement
37  * before getting the credential corresponding to this entry, e.g. authentication,
38  * confirmation etc. The extras associated with the resulting {@link android.app.Activity} will
39  * also contain the complete credential request containing all required parameters. This request
40  * can be retrieved against {@link CredentialProviderService#EXTRA_GET_CREDENTIAL_REQUEST}.
41  *
42  * Once the activity fulfills the required user engagement, the {@link android.app.Activity}
43  * result should be set to {@link android.app.Activity#RESULT_OK}, and the
44  * {@link CredentialProviderService#EXTRA_GET_CREDENTIAL_RESPONSE} must be set with a
45  * {@link GetCredentialResponse} object.
46  */
47 public final class CredentialEntry implements Parcelable {
48     /** The request option that corresponds to this entry. **/
49     private final @Nullable String mBeginGetCredentialOptionId;
50 
51     /** The type of the credential entry to be shown on the UI. */
52     private final @NonNull String mType;
53 
54 
55     /** The object containing display content to be shown along with this credential entry
56      * on the UI. */
57     private final @NonNull Slice mSlice;
58 
59     /**
60      * Creates an entry that is associated with a {@link BeginGetCredentialOption} request.
61      * Providers must use this constructor when they extend from {@link CredentialProviderService}
62      * to respond to query phase {@link CredentialProviderService#onBeginGetCredential}
63      * credential retrieval requests.
64      *
65      * @param beginGetCredentialOptionId the beginGetCredentialOptionId to be retrieved from
66      * {@link BeginGetCredentialOption#getId()} - the request option for which this CredentialEntry
67      *                                   is being constructed This helps maintain an association
68      *                                   such that when the user selects this entry, providers can
69      *                                   receive the complete corresponding
70      *                                   {@link GetCredentialRequest}.
71      * @param type the type of the credential for which this credential entry is being created
72      * @param slice the slice containing the metadata to be shown on the UI, must be constructed
73      *              through the {@link androidx.credentials.provider} Jetpack library;
74      *              If constructed manually, the {@code slice} object must
75      *              contain the non-null properties of the
76      *              {@link androidx.credentials.provider.CredentialEntry} class populated as slice
77      *              items against specific hints as used in the class's {@code toSlice} method,
78      *              since the Android System uses this library to parse the {@code slice} and
79      *              extract the required attributes
80      *
81      * @throws IllegalArgumentException If {@code beginGetCredentialOptionId} or {@code type}
82      * is null, or empty
83      */
CredentialEntry(@onNull String beginGetCredentialOptionId, @NonNull String type, @NonNull Slice slice)84     public CredentialEntry(@NonNull String beginGetCredentialOptionId, @NonNull String type,
85             @NonNull Slice slice) {
86         mBeginGetCredentialOptionId = Preconditions.checkStringNotEmpty(
87                 beginGetCredentialOptionId, "beginGetCredentialOptionId must not be "
88                         + "null, or empty");
89         mType = Preconditions.checkStringNotEmpty(type, "type must not be null, or "
90                 + "empty");
91         mSlice = requireNonNull(slice, "slice must not be null");
92     }
93 
94     /**
95      * Creates an entry that is associated with a {@link BeginGetCredentialOption} request.
96      * Providers must use this constructor when they extend from {@link CredentialProviderService}
97      * to respond to query phase {@link CredentialProviderService#onBeginGetCredential}
98      * credential retrieval requests.
99      *
100      * @param beginGetCredentialOption the request option for which this credential entry is
101      *                                 being constructed This helps maintain an association,
102      *                                 such that when the user selects this entry, providers
103      *                                 can receive the complete corresponding request.
104      * @param slice the slice containing the metadata to be shown on the UI. Must be
105      *              constructed through the androidx.credentials jetpack library.
106      */
CredentialEntry(@onNull BeginGetCredentialOption beginGetCredentialOption, @NonNull Slice slice)107     public CredentialEntry(@NonNull BeginGetCredentialOption beginGetCredentialOption,
108             @NonNull Slice slice) {
109         requireNonNull(beginGetCredentialOption, "beginGetCredentialOption must not"
110                 + " be null");
111         mBeginGetCredentialOptionId = Preconditions.checkStringNotEmpty(
112                 beginGetCredentialOption.getId(), "Id in beginGetCredentialOption "
113                         + "must not be null");
114         mType = Preconditions.checkStringNotEmpty(beginGetCredentialOption.getType(),
115                 "type in beginGetCredentialOption must not be null");
116         mSlice = requireNonNull(slice, "slice must not be null");
117     }
118 
119     /**
120      * Creates an entry that is independent of an incoming {@link BeginGetCredentialOption}
121      * request. Providers must use this constructor for constructing entries to be registered
122      * with the framework outside of the span of an API call.
123      *
124      * @param type the type of the credential
125      * @param slice the slice containing the metadata to be shown on the UI. Must be
126      *              constructed through the androidx.credentials jetpack library.
127      *
128      */
CredentialEntry(@onNull String type, @NonNull Slice slice)129     public CredentialEntry(@NonNull String type, @NonNull Slice slice) {
130         mBeginGetCredentialOptionId = null;
131         mType = requireNonNull(type, "type must not be null");
132         mSlice = requireNonNull(slice, "slice must not be null");
133     }
134 
CredentialEntry(@onNull Parcel in)135     private CredentialEntry(@NonNull Parcel in) {
136         requireNonNull(in, "parcel must not be null");
137         mType = in.readString8();
138         mSlice = in.readTypedObject(Slice.CREATOR);
139         mBeginGetCredentialOptionId = in.readString8();
140     }
141 
142     @NonNull
143     public static final Creator<CredentialEntry> CREATOR =
144             new Creator<CredentialEntry>() {
145                 @Override
146                 public CredentialEntry createFromParcel(@NonNull Parcel in) {
147                     return new CredentialEntry(in);
148                 }
149 
150                 @Override
151                 public CredentialEntry[] newArray(int size) {
152                     return new CredentialEntry[size];
153                 }
154             };
155 
156     @Override
describeContents()157     public int describeContents() {
158         return 0;
159     }
160 
161     @Override
writeToParcel(@onNull Parcel dest, int flags)162     public void writeToParcel(@NonNull Parcel dest, int flags) {
163         dest.writeString8(mType);
164         dest.writeTypedObject(mSlice, flags);
165         dest.writeString8(mBeginGetCredentialOptionId);
166     }
167 
168     /**
169      * Returns the id of the {@link BeginGetCredentialOption} for which this credential
170      * entry has been constructed.
171      */
172     @NonNull
getBeginGetCredentialOptionId()173     public String getBeginGetCredentialOptionId() {
174         return mBeginGetCredentialOptionId;
175     }
176 
177     /**
178      * Returns the specific credential type of the entry.
179      */
180     @NonNull
getType()181     public String getType() {
182         return mType;
183     }
184 
185     /**
186      * Returns the {@link Slice} object containing UI display content to be shown for this entry.
187      */
188     @NonNull
getSlice()189     public Slice getSlice() {
190         return mSlice;
191     }
192 }
193