1 /*
2  * Copyright (C) 2019 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.statusbar.phone
18 
19 import android.content.Context
20 import android.content.pm.PackageManager
21 import android.hardware.biometrics.BiometricSourceType
22 import android.provider.Settings
23 import com.android.systemui.Dumpable
24 import com.android.systemui.dump.DumpManager
25 import com.android.systemui.plugins.statusbar.StatusBarStateController
26 import com.android.systemui.statusbar.NotificationLockscreenUserManager
27 import com.android.systemui.statusbar.StatusBarState
28 import com.android.systemui.statusbar.policy.KeyguardStateController
29 import com.android.systemui.tuner.TunerService
30 import java.io.FileDescriptor
31 import java.io.PrintWriter
32 import javax.inject.Inject
33 import javax.inject.Singleton
34 
35 @Singleton
36 open class KeyguardBypassController : Dumpable {
37 
38     private val mKeyguardStateController: KeyguardStateController
39     private val statusBarStateController: StatusBarStateController
40     private var hasFaceFeature: Boolean
41     private var pendingUnlock: PendingUnlock? = null
42 
43     /**
44      * Pending unlock info:
45      *
46      * The pending unlock type which is set if the bypass was blocked when it happened.
47      *
48      * Whether the pending unlock type is strong biometric or non-strong biometric
49      * (i.e. weak or convenience).
50      */
51     private data class PendingUnlock(
52         val pendingUnlockType: BiometricSourceType,
53         val isStrongBiometric: Boolean
54     )
55 
56     lateinit var unlockController: BiometricUnlockController
57     var isPulseExpanding = false
58 
59     /**
60      * If face unlock dismisses the lock screen or keeps user on keyguard for the current user.
61      */
62     var bypassEnabled: Boolean = false
63         get() = field && mKeyguardStateController.isFaceAuthEnabled
64         private set
65 
66     var bouncerShowing: Boolean = false
67     var launchingAffordance: Boolean = false
68     var qSExpanded = false
69         set(value) {
70             val changed = field != value
71             field = value
72             if (changed && !value) {
73                 maybePerformPendingUnlock()
74             }
75         }
76 
77     @Inject
78     constructor(
79         context: Context,
80         tunerService: TunerService,
81         statusBarStateController: StatusBarStateController,
82         lockscreenUserManager: NotificationLockscreenUserManager,
83         keyguardStateController: KeyguardStateController,
84         dumpManager: DumpManager
85     ) {
86         this.mKeyguardStateController = keyguardStateController
87         this.statusBarStateController = statusBarStateController
88 
89         hasFaceFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)
90         if (!hasFaceFeature) {
91             return
92         }
93 
94         dumpManager.registerDumpable("KeyguardBypassController", this)
95         statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
onStateChangednull96             override fun onStateChanged(newState: Int) {
97                 if (newState != StatusBarState.KEYGUARD) {
98                     pendingUnlock = null
99                 }
100             }
101         })
102 
103         val dismissByDefault = if (context.resources.getBoolean(
104                         com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) 1 else 0
105         tunerService.addTunable(object : TunerService.Tunable {
onTuningChangednull106             override fun onTuningChanged(key: String?, newValue: String?) {
107                 bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0
108             }
109         }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
110         lockscreenUserManager.addUserChangedListener(
111                 object : NotificationLockscreenUserManager.UserChangedListener {
onUserChangednull112                     override fun onUserChanged(userId: Int) {
113                         pendingUnlock = null
114                     }
115                 })
116     }
117 
118     /**
119      * Notify that the biometric unlock has happened.
120      *
121      * @return false if we can not wake and unlock right now
122      */
onBiometricAuthenticatednull123     fun onBiometricAuthenticated(
124         biometricSourceType: BiometricSourceType,
125         isStrongBiometric: Boolean
126     ): Boolean {
127         if (bypassEnabled) {
128             val can = canBypass()
129             if (!can && (isPulseExpanding || qSExpanded)) {
130                 pendingUnlock = PendingUnlock(biometricSourceType, isStrongBiometric)
131             }
132             return can
133         }
134         return true
135     }
136 
maybePerformPendingUnlocknull137     fun maybePerformPendingUnlock() {
138         if (pendingUnlock != null) {
139             if (onBiometricAuthenticated(pendingUnlock!!.pendingUnlockType,
140                             pendingUnlock!!.isStrongBiometric)) {
141                 unlockController.startWakeAndUnlock(pendingUnlock!!.pendingUnlockType,
142                         pendingUnlock!!.isStrongBiometric)
143                 pendingUnlock = null
144             }
145         }
146     }
147 
148     /**
149      * If keyguard can be dismissed because of bypass.
150      */
canBypassnull151     fun canBypass(): Boolean {
152         if (bypassEnabled) {
153             return when {
154                 bouncerShowing -> true
155                 statusBarStateController.state != StatusBarState.KEYGUARD -> false
156                 launchingAffordance -> false
157                 isPulseExpanding || qSExpanded -> false
158                 else -> true
159             }
160         }
161         return false
162     }
163 
164     /**
165      * If shorter animations should be played when unlocking.
166      */
canPlaySubtleWindowAnimationsnull167     fun canPlaySubtleWindowAnimations(): Boolean {
168         if (bypassEnabled) {
169             return when {
170                 statusBarStateController.state != StatusBarState.KEYGUARD -> false
171                 qSExpanded -> false
172                 else -> true
173             }
174         }
175         return false
176     }
177 
onStartedGoingToSleepnull178     fun onStartedGoingToSleep() {
179         pendingUnlock = null
180     }
181 
dumpnull182     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
183         pw.println("KeyguardBypassController:")
184         if (pendingUnlock != null) {
185             pw.println("  mPendingUnlock.pendingUnlockType: ${pendingUnlock!!.pendingUnlockType}")
186             pw.println("  mPendingUnlock.isStrongBiometric: ${pendingUnlock!!.isStrongBiometric}")
187         } else {
188             pw.println("  mPendingUnlock: $pendingUnlock")
189         }
190         pw.println("  bypassEnabled: $bypassEnabled")
191         pw.println("  canBypass: ${canBypass()}")
192         pw.println("  bouncerShowing: $bouncerShowing")
193         pw.println("  isPulseExpanding: $isPulseExpanding")
194         pw.println("  launchingAffordance: $launchingAffordance")
195         pw.println("  qSExpanded: $qSExpanded")
196         pw.println("  hasFaceFeature: $hasFaceFeature")
197     }
198 
199     companion object {
200         const val BYPASS_PANEL_FADE_DURATION = 67
201     }
202 }
203