1 /*
<lambda>null2  * Copyright 2024 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 androidx.credentials.playservices.controllers.identitycredentials.createpublickeycredential
18 
19 import android.content.Context
20 import android.os.CancellationSignal
21 import androidx.credentials.CreatePublicKeyCredentialRequest
22 import androidx.credentials.CreatePublicKeyCredentialResponse
23 import androidx.credentials.CredentialManagerCallback
24 import androidx.credentials.PublicKeyCredential
25 import androidx.credentials.exceptions.CreateCredentialException
26 import androidx.credentials.exceptions.CreateCredentialUnknownException
27 import androidx.credentials.internal.toJetpackCreateException
28 import androidx.credentials.playservices.CredentialProviderPlayServicesImpl
29 import androidx.credentials.playservices.controllers.CredentialProviderController
30 import com.google.android.gms.identitycredentials.CreateCredentialRequest
31 import com.google.android.gms.identitycredentials.CreateCredentialResponse
32 import com.google.android.gms.identitycredentials.IdentityCredentialManager
33 import java.util.concurrent.Executor
34 
35 /** A controller to handle the CreateCredential flow with play services. */
36 internal class CreatePublicKeyCredentialController(private val context: Context) :
37     CredentialProviderController<
38         CreatePublicKeyCredentialRequest,
39         CreateCredentialRequest,
40         CreateCredentialResponse,
41         androidx.credentials.CreateCredentialResponse,
42         CreateCredentialException
43     >(context) {
44 
45     override fun invokePlayServices(
46         request: CreatePublicKeyCredentialRequest,
47         callback:
48             CredentialManagerCallback<
49                 androidx.credentials.CreateCredentialResponse,
50                 CreateCredentialException
51             >,
52         executor: Executor,
53         cancellationSignal: CancellationSignal?
54     ) {
55         if (CredentialProviderPlayServicesImpl.Companion.cancellationReviewer(cancellationSignal)) {
56             return
57         }
58 
59         val convertedRequest = this.convertRequestToPlayServices(request)
60         IdentityCredentialManager.Companion.getClient(context)
61             .createCredential(convertedRequest)
62             .addOnSuccessListener {
63                 val createCredentialResponse: CreateCredentialResponse? =
64                     it.createCredentialResponse
65                 if (createCredentialResponse == null) {
66                     cancelOrCallbackExceptionOrResult(cancellationSignal) {
67                         executor.execute { callback.onError(CreateCredentialUnknownException()) }
68                     }
69                 }
70                 if (createCredentialResponse != null) {
71                     try {
72                         val response =
73                             this.convertResponseToCredentialManager(createCredentialResponse)
74                         cancelOrCallbackExceptionOrResult(cancellationSignal) {
75                             executor.execute { callback.onResult(response) }
76                         }
77                     } catch (e: Exception) {
78                         cancelOrCallbackExceptionOrResult(cancellationSignal) {
79                             executor.execute {
80                                 callback.onError(toJetpackCreateException(e.message.toString()))
81                             }
82                         }
83                     }
84                 }
85             }
86             .addOnFailureListener { e ->
87                 cancelOrCallbackExceptionOrResult(cancellationSignal) {
88                     executor.execute {
89                         callback.onError(CreateCredentialUnknownException(e.message))
90                     }
91                 }
92             }
93     }
94 
95     public override fun convertRequestToPlayServices(
96         request: CreatePublicKeyCredentialRequest
97     ): CreateCredentialRequest {
98         return CreateCredentialRequest(
99             type = request.type,
100             credentialData = request.credentialData,
101             candidateQueryData = request.candidateQueryData,
102             origin = request.origin,
103             requestJson = request.requestJson,
104             resultReceiver = null
105         )
106     }
107 
108     override fun convertResponseToCredentialManager(
109         response: CreateCredentialResponse
110     ): androidx.credentials.CreateCredentialResponse {
111         when (response.type) {
112             PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> {
113                 // TODO(359049355): Replace with
114                 // CreatePublicKeyCredentialResponse.createFrom(response.data) after
115                 // making this API public
116                 try {
117                     val registrationResponseJson =
118                         response.data.getString(
119                             "androidx.credentials.BUNDLE_KEY_REGISTRATION_RESPONSE_JSON"
120                         )
121                     return CreatePublicKeyCredentialResponse(registrationResponseJson!!)
122                 } catch (_: NullPointerException) {
123                     throw CreateCredentialUnknownException()
124                 }
125             }
126             else -> throw CreateCredentialUnknownException()
127         }
128     }
129 
130     companion object {
131         /**
132          * Factory method for
133          * [androidx.credentials.playservices.controllers.identityauth.createpublickeycredential.CredentialProviderCreatePublicKeyCredentialController].
134          *
135          * @param context the calling context for this controller
136          * @return a credential provider controller for IdentityCredentialsCreatePublicKeyCredential
137          */
138         @JvmStatic
139         fun getInstance(context: Context): CreatePublicKeyCredentialController {
140             return CreatePublicKeyCredentialController(context)
141         }
142     }
143 }
144