Skip to content

Commit d8b2a44

Browse files
committed
Lock out user after 5 failed attempts
1 parent 197b72b commit d8b2a44

File tree

5 files changed

+39
-18
lines changed

5 files changed

+39
-18
lines changed

faceunlock/src/main/java/ax/nd/faceunlock/FaceAuthActivity.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,20 @@ import android.os.Bundle
44
import android.widget.TextView
55
import android.widget.Toast
66
import androidx.appcompat.app.AppCompatActivity
7+
import ax.nd.faceunlock.pref.Prefs
78
import ax.nd.faceunlock.service.FaceAuthServiceCallbacks
89
import ax.nd.faceunlock.service.FaceAuthServiceController
910

1011
class FaceAuthActivity : AppCompatActivity(), FaceAuthServiceCallbacks {
1112
private var controller: FaceAuthServiceController? = null
13+
private lateinit var prefs: Prefs
1214
private var startTime: Long = 0
1315

1416
override fun onCreate(savedInstanceState: Bundle?) {
1517
super.onCreate(savedInstanceState)
1618
setContentView(R.layout.activity_face_auth)
17-
controller = FaceAuthServiceController(this, this)
19+
prefs = FaceApplication.getApp().prefs
20+
controller = FaceAuthServiceController(this, prefs, this)
1821
}
1922

2023

faceunlock/src/main/java/ax/nd/faceunlock/pref/PrefKeys.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ object PrefKeys {
77
const val REQUIRE_PIN_ON_BOOT = "require_pin_on_boot"
88
const val BYPASS_KEYGUARD = "bypass_keyguard"
99
const val SHOW_STATUS_TEXT = "show_status_text"
10+
const val FAILED_UNLOCK_ATTEMPTS = "failed_unlock_attempts"
1011
}

faceunlock/src/main/java/ax/nd/faceunlock/pref/Prefs.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ class Prefs(context: Context) {
1414
val requirePinOnBoot = flowPrefs.getBoolean(PrefKeys.REQUIRE_PIN_ON_BOOT, false)
1515
val bypassKeyguard = flowPrefs.getBoolean(PrefKeys.BYPASS_KEYGUARD, true)
1616
val showStatusText = flowPrefs.getBoolean(PrefKeys.SHOW_STATUS_TEXT, true)
17+
val failedUnlockAttempts = flowPrefs.getInt(PrefKeys.FAILED_UNLOCK_ATTEMPTS, 0)
1718
}

faceunlock/src/main/java/ax/nd/faceunlock/service/FaceAuthServiceController.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@ import android.content.ServiceConnection
77
import android.os.IBinder
88
import android.os.RemoteException
99
import android.util.Log
10+
import ax.nd.faceunlock.pref.Prefs
1011
import ax.nd.faceunlock.stub.biometrics.BiometricFaceConstants
1112
import ax.nd.faceunlock.stub.face.FaceManager
1213
import com.android.internal.util.custom.faceunlock.IFaceService
1314
import com.android.internal.util.custom.faceunlock.IFaceServiceReceiver
15+
import kotlinx.coroutines.runBlocking
1416

1517
interface FaceAuthServiceCallbacks {
1618
fun onAuthed()
1719
fun onError(errId: Int, message: String)
1820
}
1921

20-
class FaceAuthServiceController(private val context: Context, private val cb: FaceAuthServiceCallbacks) {
22+
class FaceAuthServiceController(private val context: Context, private val prefs: Prefs, private val cb: FaceAuthServiceCallbacks) {
2123
private var serviceBound = false
2224
private val authCallback = object : IFaceServiceReceiver.Stub() {
2325
@Throws(RemoteException::class)
@@ -30,9 +32,14 @@ class FaceAuthServiceController(private val context: Context, private val cb: Fa
3032
override fun onAuthenticated(faceId: Int, userId: Int, token: ByteArray?) {
3133
if(userId == -1) {
3234
// onAuthenticated can still be called if the request times out
35+
// Commit this synchronously for security
36+
runBlocking {
37+
prefs.failedUnlockAttempts.setAndCommit(prefs.failedUnlockAttempts.get() + 1)
38+
}
3339
onError(BiometricFaceConstants.FACE_ERROR_TIMEOUT, 0)
3440
} else {
3541
Log.d(TAG, "Authentication OK: $faceId, $userId")
42+
prefs.failedUnlockAttempts.set(0)
3643
stopInternal(cancel = false)
3744
cb.onAuthed()
3845
}
@@ -82,6 +89,10 @@ class FaceAuthServiceController(private val context: Context, private val cb: Fa
8289
}
8390

8491
fun start() {
92+
if(prefs.failedUnlockAttempts.get() >= 5) {
93+
authCallback.onError(BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT, 0)
94+
return
95+
}
8596
if(!serviceBound) {
8697
serviceBound = true
8798
context.bindService(

faceunlock/src/main/java/ax/nd/faceunlock/service/LockscreenFaceAuthService.kt

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class LockscreenFaceAuthService : AccessibilityService(), FaceAuthServiceCallbac
5050

5151
private var active: Boolean = false
5252
private var lockStateReceiver: BroadcastReceiver? = null
53-
private var requirePinOnBootReceiver: BroadcastReceiver? = null
53+
private var unlockReceiver: BroadcastReceiver? = null
5454
private var controller: FaceAuthServiceController? = null
5555
private var keyguardManager: KeyguardManager? = null
5656
private var displayManager: DisplayManager? = null
@@ -63,6 +63,7 @@ class LockscreenFaceAuthService : AccessibilityService(), FaceAuthServiceCallbac
6363
private lateinit var prefs: Prefs
6464

6565
private var showStatusText = true
66+
private var booted = false
6667

6768
override fun onCreate() {
6869
super.onCreate()
@@ -73,7 +74,7 @@ class LockscreenFaceAuthService : AccessibilityService(), FaceAuthServiceCallbac
7374

7475
prefs = FaceApplication.getApp().prefs
7576

76-
controller = FaceAuthServiceController(this, this)
77+
controller = FaceAuthServiceController(this, prefs, this)
7778

7879
windowManager = getSystemService()
7980
keyguardManager = getSystemService()
@@ -85,12 +86,13 @@ class LockscreenFaceAuthService : AccessibilityService(), FaceAuthServiceCallbac
8586
textView?.setShadowLayer(10f, 0f, 0f, Color.BLACK)
8687
textView?.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
8788
textView?.setPadding(0, 16.dpToPx.toInt(), 0, 0)
89+
textView?.textAlignment = TextView.TEXT_ALIGNMENT_CENTER
8890

89-
if(prefs.requirePinOnBoot.get()) {
90-
setupRequirePinOnBootReceiver()
91-
} else {
91+
booted = !prefs.requirePinOnBoot.get()
92+
if(booted) {
9293
reconfigureUnlockHook()
9394
}
95+
setupUnlockReceiver()
9496

9597
serviceScope.launch {
9698
prefs.showStatusText.asFlow().collect { showStatusText ->
@@ -99,30 +101,33 @@ class LockscreenFaceAuthService : AccessibilityService(), FaceAuthServiceCallbac
99101
}
100102
}
101103

102-
private fun setupRequirePinOnBootReceiver() {
104+
private fun setupUnlockReceiver() {
103105
val intentFilter = IntentFilter().apply {
104106
addAction(Intent.ACTION_USER_PRESENT)
105107
}
106-
requirePinOnBootReceiver = object : BroadcastReceiver() {
108+
unlockReceiver = object : BroadcastReceiver() {
107109
override fun onReceive(p0: Context?, p1: Intent?) {
108-
unregisterRequirePinOnBootReceiver()
109-
reconfigureUnlockHook()
110+
if(!booted) {
111+
booted = true
112+
reconfigureUnlockHook()
113+
}
114+
prefs.failedUnlockAttempts.set(0)
110115
}
111116
}
112-
registerReceiver(requirePinOnBootReceiver, intentFilter)
117+
registerReceiver(unlockReceiver, intentFilter)
113118
}
114119

115-
private fun unregisterRequirePinOnBootReceiver() {
116-
requirePinOnBootReceiver?.let {
120+
private fun unregisterUnlockReceiver() {
121+
unlockReceiver?.let {
117122
unregisterReceiver(it)
118-
requirePinOnBootReceiver = null
123+
unlockReceiver = null
119124
}
120125
}
121126

122127
private fun reconfigureUnlockHook() {
123128
serviceScope.launch {
124129
prefs.earlyUnlockHook.asFlow().collect { earlyUnlock ->
125-
unregisterUnlockReceiver()
130+
unregisterLockStateReceiver()
126131
if(earlyUnlock) {
127132
registerEarlyUnlockReceiver()
128133
} else {
@@ -163,7 +168,7 @@ class LockscreenFaceAuthService : AccessibilityService(), FaceAuthServiceCallbac
163168
registerReceiver(lockStateReceiver, intentFilter)
164169
}
165170

166-
private fun unregisterUnlockReceiver() {
171+
private fun unregisterLockStateReceiver() {
167172
lockStateReceiver?.let {
168173
unregisterReceiver(it)
169174
lockStateReceiver = null
@@ -184,8 +189,8 @@ class LockscreenFaceAuthService : AccessibilityService(), FaceAuthServiceCallbac
184189
override fun onDestroy() {
185190
super.onDestroy()
186191

192+
unregisterLockStateReceiver()
187193
unregisterUnlockReceiver()
188-
unregisterRequirePinOnBootReceiver()
189194
hide()
190195
serviceJob.cancel()
191196
}

0 commit comments

Comments
 (0)