1 /*
<lambda>null2  * Copyright (C) 2023 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 com.android.credentialmanager.client.impl
18 
19 import android.app.Activity
20 import android.content.Context
21 import android.content.Intent
22 import android.credentials.selection.BaseDialogResult
23 import android.credentials.selection.BaseDialogResult.RESULT_CODE_DIALOG_USER_CANCELED
24 import android.credentials.selection.ProviderPendingIntentResponse
25 import android.credentials.selection.UserSelectionDialogResult
26 import android.os.Bundle
27 import android.os.IBinder
28 import android.os.ResultReceiver
29 import android.util.Log
30 import com.android.credentialmanager.TAG
31 import com.android.credentialmanager.model.Request
32 import com.android.credentialmanager.parse
33 import com.android.credentialmanager.client.CredentialManagerClient
34 import com.android.credentialmanager.model.EntryInfo
35 
36 import dagger.hilt.android.qualifiers.ApplicationContext
37 import kotlinx.coroutines.flow.MutableStateFlow
38 import kotlinx.coroutines.flow.StateFlow
39 import javax.inject.Inject
40 
41 class CredentialManagerClientImpl @Inject constructor(
42     @ApplicationContext private val context: Context,
43 ) : CredentialManagerClient {
44 
45     private val _requests = MutableStateFlow<Request?>(null)
46     override val requests: StateFlow<Request?> = _requests
47 
48 
49     override fun updateRequest(intent: Intent) {
50         val request: Request
51         try {
52             request = intent.parse(context)
53         } catch (e: Exception) {
54             sendError(BaseDialogResult.RESULT_CODE_DATA_PARSING_FAILURE)
55             return
56         }
57         Log.d(TAG, "Request parsed: $request, client instance: $this")
58         if (request is Request.Cancel || request is Request.Close) {
59             if (request.token != null && request.token != _requests.value?.token) {
60                 Log.w(TAG, "drop terminate request for previous session.")
61                 return
62             }
63         }
64         _requests.value = request
65     }
66 
67     override fun sendError(resultCode: Int) {
68         Log.w(TAG, "Error occurred, resultCode: $resultCode, current request: ${ requests.value }")
69         requests.value?.sendCancellationCode(resultCode)
70     }
71 
72     override fun sendResult(result: UserSelectionDialogResult) {
73         val currentRequest = requests.value
74         check(currentRequest is Request.Get) { "current request is not get." }
75         currentRequest.resultReceiver?.let { receiver ->
76             val resultDataBundle = Bundle()
77             UserSelectionDialogResult.addToBundle(result, resultDataBundle)
78             receiver.send(
79                 BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
80                 resultDataBundle
81             )
82         }
83     }
84 
85     override fun sendEntrySelectionResult(
86         entryInfo: EntryInfo,
87         resultCode: Int?,
88         resultData: Intent?,
89         isAutoSelected: Boolean,
90     ): Boolean {
91         Log.d(TAG, "sendEntrySelectionResult, resultCode: $resultCode, resultData: $resultData," +
92                 " entryInfo: $entryInfo")
93         val currentRequest = requests.value
94         check(currentRequest is Request.Get) { "current request is not get." }
95         if (resultCode == Activity.RESULT_CANCELED) {
96             if (isAutoSelected) {
97                 currentRequest.sendCancellationCode(RESULT_CODE_DIALOG_USER_CANCELED)
98             }
99             return isAutoSelected
100         }
101         val userSelectionDialogResult = UserSelectionDialogResult(
102             currentRequest.token,
103             entryInfo.providerId,
104             entryInfo.entryKey,
105             entryInfo.entrySubkey,
106             if (resultCode != null) ProviderPendingIntentResponse(
107                 resultCode,
108                 resultData
109             ) else null
110         )
111         sendResult(userSelectionDialogResult)
112         return entryInfo.shouldTerminateUiUponSuccessfulProviderResult
113     }
114 
115     private fun Request.sendCancellationCode(cancelCode: Int) {
116         sendCancellationCode(
117             cancelCode = cancelCode,
118             requestToken = token,
119             resultReceiver = resultReceiver
120         )
121     }
122 
123     private fun sendCancellationCode(
124         cancelCode: Int,
125         requestToken: IBinder?,
126         resultReceiver: ResultReceiver?
127     ) {
128         if (requestToken != null && resultReceiver != null) {
129             val resultData = Bundle()
130             BaseDialogResult.addToBundle(BaseDialogResult(requestToken), resultData)
131             resultReceiver.send(cancelCode, resultData)
132         }
133     }
134 }
135