1 /* 2 * Copyright (C) 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 com.android.systemui.bouncer.data.repository 18 19 import android.annotation.SuppressLint 20 import android.os.Build 21 import android.util.Log 22 import com.android.keyguard.KeyguardSecurityModel 23 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN 24 import com.android.systemui.bouncer.shared.model.BouncerDismissActionModel 25 import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel 26 import com.android.systemui.dagger.SysUISingleton 27 import com.android.systemui.dagger.qualifiers.Application 28 import com.android.systemui.log.dagger.BouncerTableLog 29 import com.android.systemui.log.table.TableLogBuffer 30 import com.android.systemui.log.table.logDiffsForTable 31 import com.android.systemui.util.time.SystemClock 32 import javax.inject.Inject 33 import kotlinx.coroutines.CoroutineScope 34 import kotlinx.coroutines.flow.Flow 35 import kotlinx.coroutines.flow.MutableSharedFlow 36 import kotlinx.coroutines.flow.MutableStateFlow 37 import kotlinx.coroutines.flow.StateFlow 38 import kotlinx.coroutines.flow.asSharedFlow 39 import kotlinx.coroutines.flow.asStateFlow 40 import kotlinx.coroutines.flow.filterNotNull 41 import kotlinx.coroutines.flow.launchIn 42 import kotlinx.coroutines.flow.map 43 import kotlinx.coroutines.flow.onEach 44 45 /** 46 * Encapsulates app state for the lock screen primary and alternate bouncer. 47 * 48 * Make sure to add newly added flows to the logger. 49 */ 50 interface KeyguardBouncerRepository { 51 /** Values associated with the PrimaryBouncer (pin/pattern/password) input. */ 52 val primaryBouncerShow: StateFlow<Boolean> 53 val primaryBouncerShowingSoon: StateFlow<Boolean> 54 val primaryBouncerStartingToHide: StateFlow<Boolean> 55 val primaryBouncerStartingDisappearAnimation: MutableSharedFlow<Runnable?> 56 isPrimaryBouncerStartingDisappearAnimationnull57 fun isPrimaryBouncerStartingDisappearAnimation(): Boolean 58 59 fun isDebuggable(): Boolean 60 61 /** Determines if we want to instantaneously show the primary bouncer instead of translating. */ 62 val primaryBouncerScrimmed: StateFlow<Boolean> 63 64 /** 65 * Set how much of the notification panel is showing on the screen. 66 * 67 * ``` 68 * 0f = panel fully hidden = bouncer fully showing 69 * 1f = panel fully showing = bouncer fully hidden 70 * ``` 71 */ 72 val panelExpansionAmount: StateFlow<Float> 73 val keyguardPosition: StateFlow<Float?> 74 val isBackButtonEnabled: StateFlow<Boolean?> 75 76 /** 77 * Triggers when the user has successfully used biometrics to authenticate. True = biometrics 78 * used to authenticate is Class 3, else false. When null, biometrics haven't authenticated the 79 * device. 80 */ 81 val keyguardAuthenticatedBiometrics: StateFlow<Boolean?> 82 83 /** 84 * Triggers when the given userId (Int) has successfully used primary authentication to 85 * authenticate 86 */ 87 val keyguardAuthenticatedPrimaryAuth: Flow<Int> 88 89 /** Triggers when the given userId (Int) has requested the bouncer when already authenticated */ 90 val userRequestedBouncerWhenAlreadyAuthenticated: Flow<Int> 91 92 val showMessage: StateFlow<BouncerShowMessageModel?> 93 val resourceUpdateRequests: StateFlow<Boolean> 94 val alternateBouncerVisible: StateFlow<Boolean> 95 96 /** Last shown security mode of the primary bouncer (ie: pin/pattern/password/SIM) */ 97 val lastShownSecurityMode: StateFlow<KeyguardSecurityModel.SecurityMode> 98 99 /** Action that should be run right after the bouncer is dismissed. */ 100 var bouncerDismissActionModel: BouncerDismissActionModel? 101 102 var lastAlternateBouncerVisibleTime: Long 103 104 fun setPrimaryScrimmed(isScrimmed: Boolean) 105 106 fun setPrimaryShow(isShowing: Boolean) 107 108 fun setPrimaryShowingSoon(showingSoon: Boolean) 109 110 fun setPrimaryStartingToHide(startingToHide: Boolean) 111 112 fun setPrimaryStartDisappearAnimation(runnable: Runnable?) 113 114 fun setPanelExpansion(panelExpansion: Float) 115 116 fun setKeyguardPosition(keyguardPosition: Float) 117 118 fun setResourceUpdateRequests(willUpdateResources: Boolean) 119 120 fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) 121 122 fun setKeyguardAuthenticatedBiometrics(keyguardAuthenticatedBiometrics: Boolean?) 123 124 suspend fun setKeyguardAuthenticatedPrimaryAuth(userId: Int) 125 126 suspend fun setUserRequestedBouncerWhenAlreadyAuthenticated(userId: Int) 127 128 fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) 129 130 fun setAlternateVisible(isVisible: Boolean) 131 132 fun setLastShownSecurityMode(securityMode: KeyguardSecurityModel.SecurityMode) 133 } 134 135 @SysUISingleton 136 open class KeyguardBouncerRepositoryImpl 137 @Inject 138 constructor( 139 private val clock: SystemClock, 140 @Application private val applicationScope: CoroutineScope, 141 @BouncerTableLog private val buffer: TableLogBuffer, 142 ) : KeyguardBouncerRepository { 143 override var bouncerDismissActionModel: BouncerDismissActionModel? = null 144 145 /** Values associated with the PrimaryBouncer (pin/pattern/password) input. */ 146 private val _primaryBouncerShow = MutableStateFlow(false) 147 override val primaryBouncerShow = _primaryBouncerShow.asStateFlow() 148 private val _primaryBouncerShowingSoon = MutableStateFlow(false) 149 override val primaryBouncerShowingSoon = _primaryBouncerShowingSoon.asStateFlow() 150 private val _primaryBouncerStartingToHide = MutableStateFlow(false) 151 override val primaryBouncerStartingToHide = _primaryBouncerStartingToHide.asStateFlow() 152 153 @SuppressLint("SharedFlowCreation") 154 override val primaryBouncerStartingDisappearAnimation = 155 MutableSharedFlow<Runnable?>(extraBufferCapacity = 2, replay = 1) 156 157 override fun isPrimaryBouncerStartingDisappearAnimation(): Boolean { 158 val replayCache = primaryBouncerStartingDisappearAnimation.replayCache 159 return if (!replayCache.isEmpty()) { 160 replayCache.last() != null 161 } else { 162 false 163 } 164 } 165 166 /** Determines if we want to instantaneously show the primary bouncer instead of translating. */ 167 private val _primaryBouncerScrimmed = MutableStateFlow(false) 168 override val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow() 169 170 /** 171 * Set how much of the notification panel is showing on the screen. 172 * 173 * ``` 174 * 0f = panel fully hidden = bouncer fully showing 175 * 1f = panel fully showing = bouncer fully hidden 176 * ``` 177 */ 178 private val _panelExpansionAmount = MutableStateFlow(EXPANSION_HIDDEN) 179 override val panelExpansionAmount = _panelExpansionAmount.asStateFlow() 180 private val _keyguardPosition = MutableStateFlow<Float?>(null) 181 override val keyguardPosition = _keyguardPosition.asStateFlow() 182 private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null) 183 override val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow() 184 185 /** Whether the user is already unlocked by biometrics */ 186 private val _keyguardAuthenticatedBiometrics = MutableStateFlow<Boolean?>(null) 187 override val keyguardAuthenticatedBiometrics = _keyguardAuthenticatedBiometrics.asStateFlow() 188 189 /** Whether the user is unlocked via a primary authentication method (pin/pattern/password). */ 190 private val _keyguardAuthenticatedPrimaryAuth = MutableSharedFlow<Int>() 191 override val keyguardAuthenticatedPrimaryAuth: Flow<Int> = 192 _keyguardAuthenticatedPrimaryAuth.asSharedFlow() 193 194 /** Whether the user requested to show the bouncer when device is already authenticated */ 195 @SuppressLint("SharedFlowCreation") 196 private val _userRequestedBouncerWhenAlreadyAuthenticated = MutableSharedFlow<Int>() 197 override val userRequestedBouncerWhenAlreadyAuthenticated: Flow<Int> = 198 _userRequestedBouncerWhenAlreadyAuthenticated.asSharedFlow() 199 200 private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null) 201 override val showMessage = _showMessage.asStateFlow() 202 private val _lastShownSecurityMode = 203 MutableStateFlow(KeyguardSecurityModel.SecurityMode.Invalid) 204 override val lastShownSecurityMode: StateFlow<KeyguardSecurityModel.SecurityMode> = 205 _lastShownSecurityMode.asStateFlow() 206 207 private val _resourceUpdateRequests = MutableStateFlow(false) 208 override val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow() 209 210 /** Values associated with the AlternateBouncer */ 211 private val _alternateBouncerVisible = MutableStateFlow(false) 212 override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow() 213 override var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE 214 215 init { 216 setUpLogging() 217 } 218 219 override fun setPrimaryScrimmed(isScrimmed: Boolean) { 220 _primaryBouncerScrimmed.value = isScrimmed 221 } 222 223 override fun setAlternateVisible(isVisible: Boolean) { 224 if (isVisible && !_alternateBouncerVisible.value) { 225 lastAlternateBouncerVisibleTime = clock.uptimeMillis() 226 } else if (!isVisible) { 227 lastAlternateBouncerVisibleTime = NOT_VISIBLE 228 } 229 _alternateBouncerVisible.value = isVisible 230 } 231 232 override fun setPrimaryShow(isShowing: Boolean) { 233 _primaryBouncerShow.value = isShowing 234 } 235 236 override fun setPrimaryShowingSoon(showingSoon: Boolean) { 237 _primaryBouncerShowingSoon.value = showingSoon 238 } 239 240 override fun setPrimaryStartingToHide(startingToHide: Boolean) { 241 _primaryBouncerStartingToHide.value = startingToHide 242 } 243 244 override fun setPrimaryStartDisappearAnimation(runnable: Runnable?) { 245 primaryBouncerStartingDisappearAnimation.tryEmit(runnable) 246 } 247 248 override fun setPanelExpansion(panelExpansion: Float) { 249 _panelExpansionAmount.value = panelExpansion 250 } 251 252 override fun setKeyguardPosition(keyguardPosition: Float) { 253 _keyguardPosition.value = keyguardPosition 254 } 255 256 override fun setResourceUpdateRequests(willUpdateResources: Boolean) { 257 _resourceUpdateRequests.value = willUpdateResources 258 } 259 260 override fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) { 261 _showMessage.value = bouncerShowMessageModel 262 } 263 264 override fun setKeyguardAuthenticatedBiometrics(keyguardAuthenticatedBiometrics: Boolean?) { 265 _keyguardAuthenticatedBiometrics.value = keyguardAuthenticatedBiometrics 266 } 267 268 override suspend fun setKeyguardAuthenticatedPrimaryAuth(userId: Int) { 269 _keyguardAuthenticatedPrimaryAuth.emit(userId) 270 } 271 272 override suspend fun setUserRequestedBouncerWhenAlreadyAuthenticated(userId: Int) { 273 _userRequestedBouncerWhenAlreadyAuthenticated.emit(userId) 274 } 275 276 override fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) { 277 _isBackButtonEnabled.value = isBackButtonEnabled 278 } 279 280 override fun setLastShownSecurityMode(securityMode: KeyguardSecurityModel.SecurityMode) { 281 _lastShownSecurityMode.value = securityMode 282 } 283 284 override fun isDebuggable() = Build.IS_DEBUGGABLE 285 286 /** Sets up logs for state flows. */ 287 private fun setUpLogging() { 288 if (!isDebuggable()) { 289 return 290 } 291 292 primaryBouncerShow 293 .logDiffsForTable(buffer, "", "PrimaryBouncerShow", false) 294 .onEach { Log.d(TAG, "Keyguard Bouncer is ${if (it) "showing" else "hiding."}") } 295 .launchIn(applicationScope) 296 primaryBouncerShowingSoon 297 .logDiffsForTable(buffer, "", "PrimaryBouncerShowingSoon", false) 298 .launchIn(applicationScope) 299 primaryBouncerStartingToHide 300 .logDiffsForTable(buffer, "", "PrimaryBouncerStartingToHide", false) 301 .launchIn(applicationScope) 302 primaryBouncerStartingDisappearAnimation 303 .map { it != null } 304 .logDiffsForTable(buffer, "", "PrimaryBouncerStartingDisappearAnimation", false) 305 .launchIn(applicationScope) 306 primaryBouncerScrimmed 307 .logDiffsForTable(buffer, "", "PrimaryBouncerScrimmed", false) 308 .launchIn(applicationScope) 309 panelExpansionAmount 310 .map { (it * 1000).toInt() } 311 .logDiffsForTable(buffer, "", "PanelExpansionAmountMillis", -1) 312 .launchIn(applicationScope) 313 keyguardPosition 314 .filterNotNull() 315 .map { it.toInt() } 316 .logDiffsForTable(buffer, "", "KeyguardPosition", -1) 317 .launchIn(applicationScope) 318 isBackButtonEnabled 319 .filterNotNull() 320 .logDiffsForTable(buffer, "", "IsBackButtonEnabled", false) 321 .launchIn(applicationScope) 322 showMessage 323 .map { it?.message } 324 .logDiffsForTable(buffer, "", "ShowMessage", null) 325 .launchIn(applicationScope) 326 resourceUpdateRequests 327 .logDiffsForTable(buffer, "", "ResourceUpdateRequests", false) 328 .launchIn(applicationScope) 329 alternateBouncerVisible 330 .logDiffsForTable(buffer, "", "AlternateBouncerVisible", false) 331 .launchIn(applicationScope) 332 lastShownSecurityMode 333 .map { it.name } 334 .logDiffsForTable(buffer, "", "lastShownSecurityMode", null) 335 .launchIn(applicationScope) 336 } 337 338 companion object { 339 private const val NOT_VISIBLE = -1L 340 private const val TAG = "KeyguardBouncerRepositoryImpl" 341 } 342 } 343