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