• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2024 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.keyguard.data.repository
18 
19 import android.annotation.IntDef
20 import android.content.res.Resources
21 import android.provider.Settings
22 import com.android.systemui.dagger.SysUISingleton
23 import com.android.systemui.dagger.qualifiers.Background
24 import com.android.systemui.dagger.qualifiers.Main
25 import com.android.systemui.dump.DumpManager
26 import com.android.systemui.keyguard.shared.model.DevicePosture
27 import com.android.systemui.keyguard.shared.model.DevicePosture.UNKNOWN
28 import com.android.systemui.res.R
29 import com.android.systemui.util.kotlin.FlowDumperImpl
30 import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
31 import javax.inject.Inject
32 import kotlinx.coroutines.CoroutineDispatcher
33 import kotlinx.coroutines.flow.Flow
34 import kotlinx.coroutines.flow.combine
35 import kotlinx.coroutines.flow.distinctUntilChanged
36 import kotlinx.coroutines.flow.flowOf
37 import kotlinx.coroutines.flow.flowOn
38 import kotlinx.coroutines.flow.map
39 
40 @SysUISingleton
41 class KeyguardBypassRepository
42 @Inject
43 constructor(
44     @Main resources: Resources,
45     biometricSettingsRepository: BiometricSettingsRepository,
46     devicePostureRepository: DevicePostureRepository,
47     dumpManager: DumpManager,
48     secureSettingsRepository: UserAwareSecureSettingsRepository,
49     @Background backgroundDispatcher: CoroutineDispatcher,
50 ) : FlowDumperImpl(dumpManager) {
51 
52     @get:BypassOverride
53     private val bypassOverride: Int by lazy {
54         resources.getInteger(R.integer.config_face_unlock_bypass_override)
55     }
56 
57     private val configFaceAuthSupportedPosture: DevicePosture by lazy {
58         DevicePosture.toPosture(resources.getInteger(R.integer.config_face_auth_supported_posture))
59     }
60 
61     private var bypassEnabledSetting: Flow<Boolean> =
62         secureSettingsRepository
63             .boolSetting(
64                 name = Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD,
65                 defaultValue =
66                     resources.getBoolean(
67                         com.android.internal.R.bool.config_faceAuthDismissesKeyguard
68                     ),
69             )
70             .flowOn(backgroundDispatcher)
71             .dumpWhileCollecting("bypassEnabledSetting")
72 
73     private val overrideFaceBypassSetting: Flow<Boolean> =
74         when (bypassOverride) {
75             FACE_UNLOCK_BYPASS_ALWAYS -> flowOf(true)
76             FACE_UNLOCK_BYPASS_NEVER -> flowOf(false)
77             else -> bypassEnabledSetting
78         }
79 
80     private val isPostureAllowedForFaceAuth: Flow<Boolean> =
81         when (configFaceAuthSupportedPosture) {
82             UNKNOWN -> flowOf(true)
83             else ->
84                 devicePostureRepository.currentDevicePosture
85                     .map { posture -> posture == configFaceAuthSupportedPosture }
86                     .distinctUntilChanged()
87         }
88 
89     /**
90      * Whether bypass is available.
91      *
92      * Bypass is the ability to skip the lockscreen when the device is unlocked using non-primary
93      * authentication types like face unlock, instead of requiring the user to explicitly dismiss
94      * the lockscreen by swiping after the device is already unlocked.
95      *
96      * "Available" refers to a combination of the user setting to skip the lockscreen being set,
97      * whether hard-wired OEM-overridable configs allow the feature, whether a foldable is in the
98      * right foldable posture, and other such things. It does _not_ model this based on more
99      * runtime-like states of the UI.
100      */
101     val isBypassAvailable: Flow<Boolean> =
102         combine(
103                 overrideFaceBypassSetting,
104                 biometricSettingsRepository.isFaceAuthEnrolledAndEnabled,
105                 isPostureAllowedForFaceAuth,
106             ) {
107                 bypassOverride: Boolean,
108                 isFaceEnrolledAndEnabled: Boolean,
109                 isPostureAllowedForFaceAuth: Boolean ->
110                 bypassOverride && isFaceEnrolledAndEnabled && isPostureAllowedForFaceAuth
111             }
112             .distinctUntilChanged()
113             .dumpWhileCollecting("isBypassAvailable")
114 
115     @IntDef(FACE_UNLOCK_BYPASS_NO_OVERRIDE, FACE_UNLOCK_BYPASS_ALWAYS, FACE_UNLOCK_BYPASS_NEVER)
116     @Retention(AnnotationRetention.SOURCE)
117     private annotation class BypassOverride
118 
119     companion object {
120         private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0
121         private const val FACE_UNLOCK_BYPASS_ALWAYS = 1
122         private const val FACE_UNLOCK_BYPASS_NEVER = 2
123 
124         private const val TAG = "KeyguardBypassRepository"
125     }
126 }
127