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.plugins.statusbar.StatusBarStateController
24 import com.android.systemui.statusbar.NotificationLockscreenUserManager
25 import com.android.systemui.statusbar.StatusBarState
26 import com.android.systemui.tuner.TunerService
27 import java.io.PrintWriter
28 import javax.inject.Inject
29 import javax.inject.Singleton
30 
31 @Singleton
32 class KeyguardBypassController {
33 
34     private val unlockMethodCache: UnlockMethodCache
35     private val statusBarStateController: StatusBarStateController
36     private var hasFaceFeature: Boolean
37 
38     /**
39      * The pending unlock type which is set if the bypass was blocked when it happened.
40      */
41     private var pendingUnlockType: BiometricSourceType? = null
42 
43     lateinit var unlockController: BiometricUnlockController
44     var isPulseExpanding = false
45 
46     /**
47      * If face unlock dismisses the lock screen or keeps user on keyguard for the current user.
48      */
49     var bypassEnabled: Boolean = false
50         get() = field && unlockMethodCache.isFaceAuthEnabled
51         private set
52 
53     var bouncerShowing: Boolean = false
54     var launchingAffordance: Boolean = false
55     var qSExpanded = false
56         set(value) {
57             val changed = field != value
58             field = value
59             if (changed && !value) {
60                 maybePerformPendingUnlock()
61             }
62         }
63 
64     @Inject
65     constructor(
66         context: Context,
67         tunerService: TunerService,
68         statusBarStateController: StatusBarStateController,
69         lockscreenUserManager: NotificationLockscreenUserManager
70     ) {
71         unlockMethodCache = UnlockMethodCache.getInstance(context)
72         this.statusBarStateController = statusBarStateController
73 
74         hasFaceFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)
75         if (!hasFaceFeature) {
76             return
77         }
78 
79         statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
onStateChangednull80             override fun onStateChanged(newState: Int) {
81                 if (newState != StatusBarState.KEYGUARD) {
82                     pendingUnlockType = null
83                 }
84             }
85         })
86 
87         val dismissByDefault = if (context.resources.getBoolean(
88                         com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) 1 else 0
89         tunerService.addTunable(object : TunerService.Tunable {
onTuningChangednull90             override fun onTuningChanged(key: String?, newValue: String?) {
91                 bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0
92             }
93         }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
<lambda>null94         lockscreenUserManager.addUserChangedListener { pendingUnlockType = null }
95     }
96 
97     /**
98      * Notify that the biometric unlock has happened.
99      *
100      * @return false if we can not wake and unlock right now
101      */
onBiometricAuthenticatednull102     fun onBiometricAuthenticated(biometricSourceType: BiometricSourceType): Boolean {
103         if (bypassEnabled) {
104             val can = canBypass()
105             if (!can && (isPulseExpanding || qSExpanded)) {
106                 pendingUnlockType = biometricSourceType
107             }
108             return can
109         }
110         return true
111     }
112 
maybePerformPendingUnlocknull113     fun maybePerformPendingUnlock() {
114         if (pendingUnlockType != null) {
115             if (onBiometricAuthenticated(pendingUnlockType!!)) {
116                 unlockController.startWakeAndUnlock(pendingUnlockType)
117                 pendingUnlockType = null
118             }
119         }
120     }
121 
122     /**
123      * If keyguard can be dismissed because of bypass.
124      */
canBypassnull125     fun canBypass(): Boolean {
126         if (bypassEnabled) {
127             return when {
128                 bouncerShowing -> true
129                 statusBarStateController.state != StatusBarState.KEYGUARD -> false
130                 launchingAffordance -> false
131                 isPulseExpanding || qSExpanded -> false
132                 else -> true
133             }
134         }
135         return false
136     }
137 
138     /**
139      * If shorter animations should be played when unlocking.
140      */
canPlaySubtleWindowAnimationsnull141     fun canPlaySubtleWindowAnimations(): Boolean {
142         if (bypassEnabled) {
143             return when {
144                 statusBarStateController.state != StatusBarState.KEYGUARD -> false
145                 qSExpanded -> false
146                 else -> true
147             }
148         }
149         return false
150     }
151 
onStartedGoingToSleepnull152     fun onStartedGoingToSleep() {
153         pendingUnlockType = null
154     }
155 
dumpnull156     fun dump(pw: PrintWriter) {
157         pw.println("KeyguardBypassController:")
158         pw.print("  pendingUnlockType: "); pw.println(pendingUnlockType)
159         pw.print("  bypassEnabled: "); pw.println(bypassEnabled)
160         pw.print("  canBypass: "); pw.println(canBypass())
161         pw.print("  bouncerShowing: "); pw.println(bouncerShowing)
162         pw.print("  isPulseExpanding: "); pw.println(isPulseExpanding)
163         pw.print("  launchingAffordance: "); pw.println(launchingAffordance)
164         pw.print("  qSExpanded: "); pw.println(qSExpanded)
165         pw.print("  hasFaceFeature: "); pw.println(hasFaceFeature)
166     }
167 
168     companion object {
169         const val BYPASS_PANEL_FADE_DURATION = 67
170     }
171 }
172