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