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.display.data.repository.DeviceStateRepository
25 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
26 import com.android.systemui.keyguard.shared.model.KeyguardState
27 import javax.inject.Inject
28 import kotlinx.coroutines.CoroutineScope
29 import kotlinx.coroutines.Job
30 import kotlinx.coroutines.cancel
31 import kotlinx.coroutines.flow.Flow
32 import kotlinx.coroutines.flow.SharingStarted
33 import kotlinx.coroutines.flow.catch
34 import kotlinx.coroutines.flow.distinctUntilChanged
35 import kotlinx.coroutines.flow.launchIn
36 import kotlinx.coroutines.flow.map
37 import kotlinx.coroutines.flow.onEach
38 import kotlinx.coroutines.flow.shareIn
39 import kotlinx.coroutines.launch
40 
41 /**
42  * Aggregates UI/device state that is not directly related to biometrics, but is often useful for
43  * logging or optimization purposes (fold state, screen state, etc.)
44  */
45 interface LogContextInteractor {
46 
47     /** If the device is showing aod. */
48     val isAod: Flow<Boolean>
49 
50     /** If the device is currently awake with the screen on. */
51     val isAwake: Flow<Boolean>
52 
53     /** Current device fold state, defined as [IBiometricContextListener.FoldState]. */
54     val foldState: Flow<Int>
55 
56     /** Current display state, defined as [AuthenticateOptions.DisplayState] */
57     val displayState: Flow<Int>
58 
59     /** If touches on the fingerprint sensor should be ignored by the HAL. */
60     val isHardwareIgnoringTouches: Flow<Boolean>
61 
62     /**
63      * Add a permanent context listener.
64      *
65      * Use this method for registering remote context listeners. Use the properties exposed via this
66      * class directly within SysUI.
67      */
68     fun addBiometricContextListener(listener: IBiometricContextListener): Job
69 }
70 
71 @SysUISingleton
72 class LogContextInteractorImpl
73 @Inject
74 constructor(
75     @Application private val applicationScope: CoroutineScope,
76     deviceStateRepository: DeviceStateRepository,
77     keyguardTransitionInteractor: KeyguardTransitionInteractor,
78     udfpsOverlayInteractor: UdfpsOverlayInteractor,
79 ) : LogContextInteractor {
80 
81     override val displayState =
<lambda>null82         keyguardTransitionInteractor.startedKeyguardTransitionStep.map {
83             when (it.to) {
84                 KeyguardState.LOCKSCREEN,
85                 KeyguardState.OCCLUDED,
86                 KeyguardState.ALTERNATE_BOUNCER,
87                 KeyguardState.PRIMARY_BOUNCER -> AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN
88                 KeyguardState.AOD -> AuthenticateOptions.DISPLAY_STATE_AOD
89                 KeyguardState.OFF,
90                 KeyguardState.DOZING -> AuthenticateOptions.DISPLAY_STATE_NO_UI
91                 KeyguardState.DREAMING -> AuthenticateOptions.DISPLAY_STATE_SCREENSAVER
92                 else -> AuthenticateOptions.DISPLAY_STATE_UNKNOWN
93             }
94         }
95 
96     override val isHardwareIgnoringTouches: Flow<Boolean> =
shouldHandlenull97         udfpsOverlayInteractor.shouldHandleTouches.map { shouldHandle -> !shouldHandle }
98 
99     override val isAod =
<lambda>null100         displayState.map { it == AuthenticateOptions.DISPLAY_STATE_AOD }.distinctUntilChanged()
101 
102     override val isAwake =
103         displayState
<lambda>null104             .map {
105                 when (it) {
106                     AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN,
107                     AuthenticateOptions.DISPLAY_STATE_SCREENSAVER,
108                     AuthenticateOptions.DISPLAY_STATE_UNKNOWN -> true
109                     else -> false
110                 }
111             }
112             .distinctUntilChanged()
113 
114     override val foldState: Flow<Int> =
115         deviceStateRepository.state
<lambda>null116             .map {
117                 when (it) {
118                     DeviceStateRepository.DeviceState.UNFOLDED,
119                     DeviceStateRepository.DeviceState.REAR_DISPLAY,
120                     DeviceStateRepository.DeviceState.CONCURRENT_DISPLAY ->
121                         IBiometricContextListener.FoldState.FULLY_OPENED
122                     DeviceStateRepository.DeviceState.FOLDED ->
123                         IBiometricContextListener.FoldState.FULLY_CLOSED
124                     DeviceStateRepository.DeviceState.HALF_FOLDED ->
125                         IBiometricContextListener.FoldState.HALF_OPENED
126                     else -> IBiometricContextListener.FoldState.UNKNOWN
127                 }
128             }
129             .distinctUntilChanged()
130             .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1)
131 
addBiometricContextListenernull132     override fun addBiometricContextListener(listener: IBiometricContextListener): Job {
133         return applicationScope.launch {
134             foldState
135                 .onEach { state -> listener.onFoldChanged(state) }
136                 .catch { t -> Log.w(TAG, "failed to notify new fold state", t) }
137                 .launchIn(this)
138 
139             displayState
140                 .distinctUntilChanged()
141                 .onEach { state -> listener.onDisplayStateChanged(state) }
142                 .catch { t -> Log.w(TAG, "failed to notify new display state", t) }
143                 .launchIn(this)
144 
145             isHardwareIgnoringTouches
146                 .distinctUntilChanged()
147                 .onEach { state -> listener.onHardwareIgnoreTouchesChanged(state) }
148                 .catch { t -> Log.w(TAG, "failed to notify new set ignore state", t) }
149                 .launchIn(this)
150 
151             listener.asBinder().linkToDeath({ cancel() }, 0)
152         }
153     }
154 
155     companion object {
156         private const val TAG = "ContextRepositoryImpl"
157     }
158 }
159