• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.CallbackExecutor;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.RequiresPermission;
25 import android.app.Activity;
26 import android.app.PendingIntent;
27 import android.content.Context;
28 import android.content.IntentSender;
29 import android.os.CancellationSignal;
30 import android.os.OutcomeReceiver;
31 import android.util.Log;
32 
33 import java.util.concurrent.Executor;
34 
35 
36 /**
37  * A response object that prefetches user app credentials and provides metadata about them. It can
38  * then be used to issue the full credential retrieval flow via the
39  * {@link CredentialManager#getCredential(PendingGetCredentialHandle, Activity, CancellationSignal,
40  * Executor, OutcomeReceiver)} method to perform the remaining flows such as consent collection
41  * and credential selection, to officially retrieve a credential.
42  */
43 public final class PrepareGetCredentialResponse {
44 
45     /**
46      * A handle that represents a pending get-credential operation. Pass this handle to {@link
47      * CredentialManager#getCredential(PendingGetCredentialHandle, Activity, CancellationSignal,
48      * Executor, OutcomeReceiver)} to perform the remaining flows to officially retrieve a
49      * credential.
50      */
51     public static final class PendingGetCredentialHandle {
52         @NonNull
53         private final CredentialManager.GetCredentialTransportPendingUseCase
54                 mGetCredentialTransport;
55         /**
56          * The pending intent to be launched to finalize the user credential. If null, the callback
57          * will fail with {@link GetCredentialException#TYPE_NO_CREDENTIAL}.
58          */
59         @Nullable
60         private final PendingIntent mPendingIntent;
61 
62         /** @hide */
PendingGetCredentialHandle( @onNull CredentialManager.GetCredentialTransportPendingUseCase transport, @Nullable PendingIntent pendingIntent)63         PendingGetCredentialHandle(
64                 @NonNull CredentialManager.GetCredentialTransportPendingUseCase transport,
65                 @Nullable PendingIntent pendingIntent) {
66             mGetCredentialTransport = transport;
67             mPendingIntent = pendingIntent;
68         }
69 
70         /** @hide */
show(@onNull Context context, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback)71         void show(@NonNull Context context, @Nullable CancellationSignal cancellationSignal,
72                 @CallbackExecutor @NonNull Executor executor,
73                 @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
74             if (mPendingIntent == null) {
75                 executor.execute(() -> callback.onError(
76                         new GetCredentialException(GetCredentialException.TYPE_NO_CREDENTIAL)));
77                 return;
78             }
79 
80             mGetCredentialTransport.setCallback(new GetPendingCredentialInternalCallback() {
81                 @Override
82                 public void onPendingIntent(PendingIntent pendingIntent) {
83                     try {
84                         context.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
85                     } catch (IntentSender.SendIntentException e) {
86                         Log.e(TAG, "startIntentSender() failed for intent for show()", e);
87                         executor.execute(() -> callback.onError(
88                                 new GetCredentialException(GetCredentialException.TYPE_UNKNOWN)));
89                     }
90                 }
91 
92                 @Override
93                 public void onResponse(GetCredentialResponse response) {
94                     executor.execute(() -> callback.onResult(response));
95                 }
96 
97                 @Override
98                 public void onError(String errorType, String message) {
99                     executor.execute(
100                             () -> callback.onError(new GetCredentialException(errorType, message)));
101                 }
102             });
103 
104             try {
105                 context.startIntentSender(mPendingIntent.getIntentSender(), null, 0, 0, 0);
106             } catch (IntentSender.SendIntentException e) {
107                 Log.e(TAG, "startIntentSender() failed for intent for show()", e);
108                 executor.execute(() -> callback.onError(
109                         new GetCredentialException(GetCredentialException.TYPE_UNKNOWN)));
110             }
111         }
112     }
113     private static final String TAG = "CredentialManager";
114 
115     @NonNull private final PrepareGetCredentialResponseInternal mResponseInternal;
116 
117     @NonNull private final PendingGetCredentialHandle mPendingGetCredentialHandle;
118 
119     /**
120      * Returns true if the user has any candidate credentials for the given {@code credentialType},
121      * and false otherwise.
122      */
123     @RequiresPermission(CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS)
hasCredentialResults(@onNull String credentialType)124     public boolean hasCredentialResults(@NonNull String credentialType) {
125         return mResponseInternal.hasCredentialResults(credentialType);
126     }
127 
128     /**
129      * Returns true if the user has any candidate authentication actions (locked credential
130      * supplier), and false otherwise.
131      */
132     @RequiresPermission(CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS)
hasAuthenticationResults()133     public boolean hasAuthenticationResults() {
134         return mResponseInternal.hasAuthenticationResults();
135     }
136 
137     /**
138      * Returns true if the user has any candidate remote credential results, and false otherwise.
139      */
140     @RequiresPermission(CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS)
hasRemoteResults()141     public boolean hasRemoteResults() {
142         return mResponseInternal.hasRemoteResults();
143     }
144 
145     /**
146      * Returns a handle that represents this pending get-credential operation. Pass this handle to
147      * {@link CredentialManager#getCredential(PendingGetCredentialHandle, Activity,
148      * CancellationSignal, Executor, OutcomeReceiver)} to perform the remaining flows to officially
149      * retrieve a credential.
150      */
151     @NonNull
getPendingGetCredentialHandle()152     public PendingGetCredentialHandle getPendingGetCredentialHandle() {
153         return mPendingGetCredentialHandle;
154     }
155 
156     /**
157      * Constructs a {@link PrepareGetCredentialResponse}.
158      *
159      * @param responseInternal       whether caller has the permission to query the credential
160      *                               result metadata
161      * @param getCredentialTransport the transport for the operation to finalaze a credential
162      * @hide
163      */
PrepareGetCredentialResponse( @onNull PrepareGetCredentialResponseInternal responseInternal, @NonNull CredentialManager.GetCredentialTransportPendingUseCase getCredentialTransport)164     protected PrepareGetCredentialResponse(
165             @NonNull PrepareGetCredentialResponseInternal responseInternal,
166             @NonNull CredentialManager.GetCredentialTransportPendingUseCase
167                     getCredentialTransport) {
168         mResponseInternal = responseInternal;
169         mPendingGetCredentialHandle = new PendingGetCredentialHandle(
170                 getCredentialTransport, responseInternal.getPendingIntent());
171     }
172 
173     /** @hide */
174     protected interface GetPendingCredentialInternalCallback {
onPendingIntent(@onNull PendingIntent pendingIntent)175         void onPendingIntent(@NonNull PendingIntent pendingIntent);
176 
onResponse(@onNull GetCredentialResponse response)177         void onResponse(@NonNull GetCredentialResponse response);
178 
onError(@onNull String errorType, @Nullable String message)179         void onError(@NonNull String errorType, @Nullable String message);
180     }
181 }
182