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_QUERY_CANDIDATE_CREDENTIALS;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.RequiresPermission;
24 import android.app.Activity;
25 import android.app.PendingIntent;
26 import android.os.CancellationSignal;
27 import android.os.OutcomeReceiver;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 import android.util.ArraySet;
31 
32 import java.util.Set;
33 import java.util.concurrent.Executor;
34 
35 
36 /**
37  * An internal response object that prefetches user app credentials and provides metadata about
38  * them.
39  *
40  * @hide
41  */
42 public final class PrepareGetCredentialResponseInternal implements Parcelable {
43     private static final String TAG = "CredentialManager";
44 
45     private final boolean mHasQueryApiPermission;
46     @Nullable
47     private final ArraySet<String> mCredentialResultTypes;
48     private final boolean mHasAuthenticationResults;
49     private final boolean mHasRemoteResults;
50     /**
51      * The pending intent to be launched to finalize the user credential. If null, the callback
52      * will fail with {@link GetCredentialException#TYPE_NO_CREDENTIAL}.
53      */
54     @Nullable
55     private final PendingIntent mPendingIntent;
56 
57     @Nullable
getPendingIntent()58     public PendingIntent getPendingIntent() {
59         return mPendingIntent;
60     }
61 
62     /**
63      * Returns true if the user has any candidate credentials for the given {@code credentialType},
64      * and false otherwise.
65      */
66     @RequiresPermission(CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS)
hasCredentialResults(@onNull String credentialType)67     public boolean hasCredentialResults(@NonNull String credentialType) {
68         if (!mHasQueryApiPermission) {
69             throw new SecurityException(
70                     "caller doesn't have the permission to query credential results");
71         }
72         if (mCredentialResultTypes == null) {
73             return false;
74         }
75         return mCredentialResultTypes.contains(credentialType);
76     }
77 
78     /**
79      * Returns true if the user has any candidate authentication actions (locked credential
80      * supplier), and false otherwise.
81      */
82     @RequiresPermission(CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS)
hasAuthenticationResults()83     public boolean hasAuthenticationResults() {
84         if (!mHasQueryApiPermission) {
85             throw new SecurityException(
86                     "caller doesn't have the permission to query authentication results");
87         }
88         return mHasAuthenticationResults;
89     }
90 
91     /**
92      * Returns true if the user has any candidate remote credential results, and false otherwise.
93      */
94     @RequiresPermission(CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS)
hasRemoteResults()95     public boolean hasRemoteResults() {
96         if (!mHasQueryApiPermission) {
97             throw new SecurityException(
98                     "caller doesn't have the permission to query remote results");
99         }
100         return mHasRemoteResults;
101     }
102 
103     @Override
writeToParcel(@onNull Parcel dest, int flags)104     public void writeToParcel(@NonNull Parcel dest, int flags) {
105         dest.writeBoolean(mHasQueryApiPermission);
106         dest.writeArraySet(mCredentialResultTypes);
107         dest.writeBoolean(mHasAuthenticationResults);
108         dest.writeBoolean(mHasRemoteResults);
109         dest.writeTypedObject(mPendingIntent, flags);
110     }
111 
112     @Override
describeContents()113     public int describeContents() {
114         return 0;
115     }
116 
117     /**
118      * Constructs a {@link PrepareGetCredentialResponseInternal}.
119      *
120      * @param hasQueryApiPermission    whether caller has the permission to query the credential
121      *                                 result metadata
122      * @param credentialResultTypes    a set of credential types that each has candidate credentials
123      *                                 found, or null if the caller doesn't have the permission to
124      *                                 this information
125      * @param hasAuthenticationResults whether the user has any candidate authentication actions, or
126      *                                 false if the caller doesn't have the permission to this
127      *                                 information
128      * @param hasRemoteResults         whether the user has any candidate remote options, or false
129      *                                 if the caller doesn't have the permission to this information
130      * @param pendingIntent            the pending intent to be launched during
131      *                                 {@link #show(Activity, CancellationSignal, Executor,
132      *                                 OutcomeReceiver)}} to
133      *                                 finalize the user credential
134      * @hide
135      */
PrepareGetCredentialResponseInternal(boolean hasQueryApiPermission, @Nullable Set<String> credentialResultTypes, boolean hasAuthenticationResults, boolean hasRemoteResults, @Nullable PendingIntent pendingIntent)136     public PrepareGetCredentialResponseInternal(boolean hasQueryApiPermission,
137             @Nullable Set<String> credentialResultTypes,
138             boolean hasAuthenticationResults, boolean hasRemoteResults,
139             @Nullable PendingIntent pendingIntent) {
140         mHasQueryApiPermission = hasQueryApiPermission;
141         mCredentialResultTypes = new ArraySet<>(credentialResultTypes);
142         mHasAuthenticationResults = hasAuthenticationResults;
143         mHasRemoteResults = hasRemoteResults;
144         mPendingIntent = pendingIntent;
145     }
146 
PrepareGetCredentialResponseInternal(@onNull Parcel in)147     private PrepareGetCredentialResponseInternal(@NonNull Parcel in) {
148         mHasQueryApiPermission = in.readBoolean();
149         mCredentialResultTypes = (ArraySet<String>) in.readArraySet(null);
150         mHasAuthenticationResults = in.readBoolean();
151         mHasRemoteResults = in.readBoolean();
152         mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
153     }
154 
155     public static final @NonNull Creator<PrepareGetCredentialResponseInternal> CREATOR =
156             new Creator<>() {
157         @Override
158         public PrepareGetCredentialResponseInternal[] newArray(int size) {
159             return new PrepareGetCredentialResponseInternal[size];
160         }
161 
162         @Override
163         public PrepareGetCredentialResponseInternal createFromParcel(@NonNull Parcel in) {
164             return new PrepareGetCredentialResponseInternal(in);
165         }
166     };
167 }
168