1 /* <lambda>null2 * 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.biometrics.domain.interactor 18 19 import android.hardware.biometrics.AuthenticateOptions 20 import android.hardware.biometrics.IBiometricContextListener 21 import android.util.Log 22 import com.android.systemui.dagger.SysUISingleton 23 import com.android.systemui.dagger.qualifiers.Application 24 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor 25 import com.android.systemui.display.data.repository.DeviceStateRepository 26 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor 27 import com.android.systemui.keyguard.shared.model.KeyguardState 28 import com.android.systemui.keyguard.shared.model.TransitionStep 29 import com.android.systemui.scene.shared.flag.SceneContainerFlag 30 import dagger.Lazy 31 import javax.inject.Inject 32 import kotlinx.coroutines.CoroutineScope 33 import kotlinx.coroutines.Job 34 import kotlinx.coroutines.cancel 35 import kotlinx.coroutines.flow.Flow 36 import kotlinx.coroutines.flow.SharingStarted 37 import kotlinx.coroutines.flow.catch 38 import kotlinx.coroutines.flow.combine 39 import kotlinx.coroutines.flow.distinctUntilChanged 40 import kotlinx.coroutines.flow.launchIn 41 import kotlinx.coroutines.flow.map 42 import kotlinx.coroutines.flow.onEach 43 import kotlinx.coroutines.flow.shareIn 44 import com.android.app.tracing.coroutines.launchTraced as launch 45 46 /** 47 * Aggregates UI/device state that is not directly related to biometrics, but is often useful for 48 * logging or optimization purposes (fold state, screen state, etc.) 49 */ 50 interface LogContextInteractor { 51 52 /** If the device is showing aod. */ 53 val isAod: Flow<Boolean> 54 55 /** If the device is currently awake with the screen on. */ 56 val isAwake: Flow<Boolean> 57 58 /** Current device fold state, defined as [IBiometricContextListener.FoldState]. */ 59 val foldState: Flow<Int> 60 61 /** Current display state, defined as [AuthenticateOptions.DisplayState] */ 62 val displayState: Flow<Int> 63 64 /** If touches on the fingerprint sensor should be ignored by the HAL. */ 65 val isHardwareIgnoringTouches: Flow<Boolean> 66 67 /** 68 * Add a permanent context listener. 69 * 70 * Use this method for registering remote context listeners. Use the properties exposed via this 71 * class directly within SysUI. 72 */ 73 fun addBiometricContextListener(listener: IBiometricContextListener): Job 74 } 75 76 @SysUISingleton 77 class LogContextInteractorImpl 78 @Inject 79 constructor( 80 @Application private val applicationScope: CoroutineScope, 81 deviceStateRepository: DeviceStateRepository, 82 keyguardTransitionInteractor: KeyguardTransitionInteractor, 83 udfpsOverlayInteractor: UdfpsOverlayInteractor, 84 deviceEntryInteractor: Lazy<DeviceEntryInteractor>, 85 ) : LogContextInteractor { 86 <lambda>null87 override val displayState: Flow<Int> by lazy { 88 if (SceneContainerFlag.isEnabled) { 89 combine( 90 deviceEntryInteractor.get().isDeviceEntered, 91 keyguardTransitionInteractor.startedKeyguardTransitionStep, 92 ) { isDeviceEntered, transitionStep -> 93 if (isDeviceEntered) { 94 AuthenticateOptions.DISPLAY_STATE_UNKNOWN 95 } else { 96 transitionStep.toAuthenticateOptions( 97 // Here when isDeviceEntered=false which always means that the device is on 98 // top of the keyguard. Therefore, any KeyguardState that doesn't have a 99 // more specific mapping as a sub-state of keyguard, maps to LOCKSCREEN 100 // instead of UNKNOWN, because it _is_ a known display state and that 101 // display state is undeniably LOCKSCREEN. 102 default = AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN 103 ) 104 } 105 } 106 } else { 107 keyguardTransitionInteractor.startedKeyguardTransitionStep.map { transitionStep -> 108 transitionStep.toAuthenticateOptions( 109 default = AuthenticateOptions.DISPLAY_STATE_UNKNOWN 110 ) 111 } 112 } 113 } 114 115 override val isHardwareIgnoringTouches: Flow<Boolean> = shouldHandlenull116 udfpsOverlayInteractor.shouldHandleTouches.map { shouldHandle -> !shouldHandle } 117 118 override val isAod = <lambda>null119 displayState.map { it == AuthenticateOptions.DISPLAY_STATE_AOD }.distinctUntilChanged() 120 121 override val isAwake = 122 displayState <lambda>null123 .map { 124 when (it) { 125 AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN, 126 AuthenticateOptions.DISPLAY_STATE_SCREENSAVER, 127 AuthenticateOptions.DISPLAY_STATE_UNKNOWN -> true 128 else -> false 129 } 130 } 131 .distinctUntilChanged() 132 133 override val foldState: Flow<Int> = 134 deviceStateRepository.state <lambda>null135 .map { 136 when (it) { 137 DeviceStateRepository.DeviceState.UNFOLDED, 138 DeviceStateRepository.DeviceState.REAR_DISPLAY, 139 DeviceStateRepository.DeviceState.CONCURRENT_DISPLAY -> 140 IBiometricContextListener.FoldState.FULLY_OPENED 141 DeviceStateRepository.DeviceState.FOLDED -> 142 IBiometricContextListener.FoldState.FULLY_CLOSED 143 DeviceStateRepository.DeviceState.HALF_FOLDED -> 144 IBiometricContextListener.FoldState.HALF_OPENED 145 else -> IBiometricContextListener.FoldState.UNKNOWN 146 } 147 } 148 .distinctUntilChanged() 149 .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1) 150 addBiometricContextListenernull151 override fun addBiometricContextListener(listener: IBiometricContextListener): Job { 152 return applicationScope.launch { 153 foldState 154 .onEach { state -> listener.onFoldChanged(state) } 155 .catch { t -> Log.w(TAG, "failed to notify new fold state", t) } 156 .launchIn(this) 157 158 displayState 159 .distinctUntilChanged() 160 .onEach { state -> listener.onDisplayStateChanged(state) } 161 .catch { t -> Log.w(TAG, "failed to notify new display state", t) } 162 .launchIn(this) 163 164 isHardwareIgnoringTouches 165 .distinctUntilChanged() 166 .onEach { state -> listener.onHardwareIgnoreTouchesChanged(state) } 167 .catch { t -> Log.w(TAG, "failed to notify new set ignore state", t) } 168 .launchIn(this) 169 170 listener.asBinder().linkToDeath({ cancel() }, 0) 171 } 172 } 173 toAuthenticateOptionsnull174 private fun TransitionStep.toAuthenticateOptions(default: Int): Int { 175 return when (this.to) { 176 KeyguardState.LOCKSCREEN, 177 KeyguardState.OCCLUDED, 178 KeyguardState.ALTERNATE_BOUNCER, 179 KeyguardState.PRIMARY_BOUNCER -> AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN 180 KeyguardState.AOD -> AuthenticateOptions.DISPLAY_STATE_AOD 181 KeyguardState.OFF, 182 KeyguardState.DOZING -> AuthenticateOptions.DISPLAY_STATE_NO_UI 183 KeyguardState.DREAMING -> AuthenticateOptions.DISPLAY_STATE_SCREENSAVER 184 KeyguardState.GONE -> AuthenticateOptions.DISPLAY_STATE_UNKNOWN 185 else -> default 186 } 187 } 188 189 companion object { 190 private const val TAG = "ContextRepositoryImpl" 191 } 192 } 193