• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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