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.systemui.biometrics.data.repository 18 19 import android.hardware.biometrics.PromptInfo 20 import android.util.Log 21 import com.android.systemui.biometrics.AuthController 22 import com.android.systemui.biometrics.shared.model.PromptKind 23 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 24 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 25 import com.android.systemui.dagger.SysUISingleton 26 import javax.inject.Inject 27 import kotlinx.coroutines.channels.awaitClose 28 import kotlinx.coroutines.flow.Flow 29 import kotlinx.coroutines.flow.MutableStateFlow 30 import kotlinx.coroutines.flow.StateFlow 31 import kotlinx.coroutines.flow.asStateFlow 32 import kotlinx.coroutines.flow.combine 33 import kotlinx.coroutines.flow.distinctUntilChanged 34 import kotlinx.coroutines.flow.flatMapLatest 35 import kotlinx.coroutines.flow.map 36 37 /** 38 * A repository for the global state of BiometricPrompt. 39 * 40 * There is never more than one instance of the prompt at any given time. 41 */ 42 interface PromptRepository { 43 44 /** If the prompt is showing. */ 45 val isShowing: Flow<Boolean> 46 47 /** The app-specific details to show in the prompt. */ 48 val promptInfo: StateFlow<PromptInfo?> 49 50 /** The user that the prompt is for. */ 51 val userId: StateFlow<Int?> 52 53 /** The request that the prompt is for. */ 54 val requestId: StateFlow<Long?> 55 56 /** The gatekeeper challenge, if one is associated with this prompt. */ 57 val challenge: StateFlow<Long?> 58 59 /** The kind of prompt to use (biometric, pin, pattern, etc.). */ 60 val promptKind: StateFlow<PromptKind> 61 62 /** The package name that the prompt is called from. */ 63 val opPackageName: StateFlow<String?> 64 65 /** 66 * If explicit confirmation is required. 67 * 68 * Note: overlaps/conflicts with [PromptInfo.isConfirmationRequested], which needs clean up. 69 */ 70 val isConfirmationRequired: Flow<Boolean> 71 72 /** Update the prompt configuration, which should be set before [isShowing]. */ 73 fun setPrompt( 74 promptInfo: PromptInfo, 75 userId: Int, 76 requestId: Long, 77 gatekeeperChallenge: Long?, 78 kind: PromptKind, 79 opPackageName: String, 80 ) 81 82 /** Unset the prompt info. */ 83 fun unsetPrompt(requestId: Long) 84 } 85 86 @SysUISingleton 87 class PromptRepositoryImpl 88 @Inject 89 constructor( 90 private val faceSettings: FaceSettingsRepository, 91 private val authController: AuthController, 92 ) : PromptRepository { 93 <lambda>null94 override val isShowing: Flow<Boolean> = conflatedCallbackFlow { 95 val callback = 96 object : AuthController.Callback { 97 override fun onBiometricPromptShown() = 98 trySendWithFailureLogging(true, TAG, "set isShowing") 99 100 override fun onBiometricPromptDismissed() = 101 trySendWithFailureLogging(false, TAG, "unset isShowing") 102 } 103 authController.addCallback(callback) 104 trySendWithFailureLogging(authController.isShowing, TAG, "update isShowing") 105 awaitClose { authController.removeCallback(callback) } 106 } 107 108 private val _promptInfo: MutableStateFlow<PromptInfo?> = MutableStateFlow(null) 109 override val promptInfo = _promptInfo.asStateFlow() 110 111 private val _challenge: MutableStateFlow<Long?> = MutableStateFlow(null) 112 override val challenge: StateFlow<Long?> = _challenge.asStateFlow() 113 114 private val _userId: MutableStateFlow<Int?> = MutableStateFlow(null) 115 override val userId = _userId.asStateFlow() 116 117 private val _requestId: MutableStateFlow<Long?> = MutableStateFlow(null) 118 override val requestId = _requestId.asStateFlow() 119 120 private val _promptKind: MutableStateFlow<PromptKind> = MutableStateFlow(PromptKind.None) 121 override val promptKind = _promptKind.asStateFlow() 122 123 private val _opPackageName: MutableStateFlow<String?> = MutableStateFlow(null) 124 override val opPackageName = _opPackageName.asStateFlow() 125 126 private val _faceSettings = idnull127 _userId.map { id -> faceSettings.forUser(id) }.distinctUntilChanged() 128 private val _faceSettingAlwaysRequireConfirmation = <lambda>null129 _faceSettings.flatMapLatest { it.alwaysRequireConfirmationInApps }.distinctUntilChanged() 130 <lambda>null131 private val _isConfirmationRequired = _promptInfo.map { it?.isConfirmationRequested ?: false } 132 override val isConfirmationRequired = 133 combine(_isConfirmationRequired, _faceSettingAlwaysRequireConfirmation) { appRequiresConfirmationnull134 appRequiresConfirmation, 135 forceRequireConfirmation -> 136 forceRequireConfirmation || appRequiresConfirmation 137 } 138 .distinctUntilChanged() 139 140 override fun setPrompt( 141 promptInfo: PromptInfo, 142 userId: Int, 143 requestId: Long, 144 gatekeeperChallenge: Long?, 145 kind: PromptKind, 146 opPackageName: String, 147 ) { 148 _promptKind.value = kind 149 _userId.value = userId 150 _requestId.value = requestId 151 _challenge.value = gatekeeperChallenge 152 _promptInfo.value = promptInfo 153 _opPackageName.value = opPackageName 154 } 155 unsetPromptnull156 override fun unsetPrompt(requestId: Long) { 157 if (requestId == _requestId.value) { 158 _promptInfo.value = null 159 _userId.value = null 160 _requestId.value = null 161 _challenge.value = null 162 _promptKind.value = PromptKind.None 163 _opPackageName.value = null 164 } else { 165 Log.w(TAG, "Ignoring unsetPrompt - requestId mismatch") 166 } 167 } 168 169 companion object { 170 private const val TAG = "PromptRepositoryImpl" 171 } 172 } 173