Skip to content

Commit 0065268

Browse files
authored
Merge pull request #4564 from owncloud/fix/biometrics_crash_at_start
[FIX] App crashes at start when biometrics fail
2 parents 8cecf7b + 05b5072 commit 0065268

File tree

9 files changed

+66
-22
lines changed

9 files changed

+66
-22
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ ownCloud admins and users.
3636
## Summary
3737

3838
* Bugfix - Confusing behaviour when creating new files using apps provider: [#4560](https://github.com/owncloud/android/issues/4560)
39+
* Bugfix - App crashes at start when biometrics fail: [#7134](https://github.com/owncloud/enterprise/issues/7134)
3940

4041
## Details
4142

@@ -47,6 +48,15 @@ ownCloud admins and users.
4748
https://github.com/owncloud/android/issues/4560
4849
https://github.com/owncloud/android/pull/4562
4950

51+
* Bugfix - App crashes at start when biometrics fail: [#7134](https://github.com/owncloud/enterprise/issues/7134)
52+
53+
The crash that happened when biometrics failed due to a system error has been
54+
handled. In this case, an error is shown and pattern or passcode unlock are used
55+
instead of biometrics.
56+
57+
https://github.com/owncloud/enterprise/issues/7134
58+
https://github.com/owncloud/android/pull/4564
59+
5060
# Changelog for ownCloud Android Client [4.5.0] (2025-03-24)
5161

5262
The following sections list the changes in ownCloud Android Client 4.5.0 relevant to

changelog/unreleased/4564

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Bugfix: App crashes at start when biometrics fail
2+
3+
The crash that happened when biometrics failed due to a system error has been handled.
4+
In this case, an error is shown and pattern or passcode unlock are used instead of biometrics.
5+
6+
https://github.com/owncloud/enterprise/issues/7134
7+
https://github.com/owncloud/android/pull/4564

owncloudApp/src/main/java/com/owncloud/android/presentation/security/biometric/BiometricActivity.kt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
*
44
* @author David González Verdugo
55
* @author Juan Carlos Garrote Gascón
6+
* @author Jorge Aguado Recio
67
*
7-
* Copyright (C) 2023 ownCloud GmbH.
8+
* Copyright (C) 2025 ownCloud GmbH.
89
*
910
* This program is free software: you can redistribute it and/or modify
1011
* it under the terms of the GNU General Public License version 2,
@@ -66,7 +67,7 @@ class BiometricActivity : AppCompatActivity() {
6667
if (biometricManager.canAuthenticate(BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS) {
6768
showBiometricPrompt()
6869
} else {
69-
authError()
70+
authError(biometricHasFailed = true)
7071
}
7172
}
7273

@@ -82,13 +83,13 @@ class BiometricActivity : AppCompatActivity() {
8283
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
8384
super.onAuthenticationError(errorCode, errString)
8485
Timber.e("onAuthenticationError ($errorCode): $errString")
85-
authError()
86+
authError(biometricHasFailed = errorCode != BiometricPrompt.ERROR_NEGATIVE_BUTTON)
8687
}
8788

8889
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
8990
super.onAuthenticationSucceeded(result)
9091
if (result.cryptoObject?.cipher != cryptoObject.cipher) {
91-
authError()
92+
authError(biometricHasFailed = true)
9293
} else {
9394
if (biometricViewModel.shouldAskForNewPassCode()) {
9495
biometricViewModel.removePassCode()
@@ -110,14 +111,19 @@ class BiometricActivity : AppCompatActivity() {
110111
})
111112

112113
// Displays the "log in" prompt.
113-
biometricPrompt.authenticate(promptInfo, cryptoObject)
114+
try {
115+
biometricPrompt.authenticate(promptInfo, cryptoObject)
116+
} catch (e: Exception) {
117+
Timber.e(e, "cryptoObject property has not been initialized correctly")
118+
authError(biometricHasFailed = true)
119+
}
114120
}
115121

116-
private fun authError() {
122+
private fun authError(biometricHasFailed: Boolean) {
117123
if (PassCodeManager.isPassCodeEnabled()) {
118-
PassCodeManager.onBiometricCancelled(this)
124+
PassCodeManager.onBiometricCancelled(this, biometricHasFailed)
119125
} else if (PatternManager.isPatternEnabled()) {
120-
PatternManager.onBiometricCancelled(this)
126+
PatternManager.onBiometricCancelled(this, biometricHasFailed)
121127
}
122128

123129
finish()

owncloudApp/src/main/java/com/owncloud/android/presentation/security/biometric/BiometricManager.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,11 @@ object BiometricManager {
6060
activity.startActivity(i)
6161
} else if (isPassCodeEnabled()) {
6262
// Cancel biometric lock and use passcode unlock method
63-
PassCodeManager.onBiometricCancelled(activity)
63+
PassCodeManager.onBiometricCancelled(activity = activity, biometricHasFailed = false)
6464
visibleActivities.add(activity.javaClass)
6565
} else if (isPatternEnabled()) {
6666
// Cancel biometric lock and use pattern unlock method
67-
PatternManager.onBiometricCancelled(activity)
67+
PatternManager.onBiometricCancelled(activity = activity, biometricHasFailed = false)
6868
visibleActivities.add(activity.javaClass)
6969
}
7070

owncloudApp/src/main/java/com/owncloud/android/presentation/security/passcode/PassCodeActivity.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
* @author Juan Carlos Garrote Gascón
1111
* @author David Crespo Ríos
1212
* @author Aitor Ballesteros Pavón
13+
* @author Jorge Aguado Recio
1314
*
1415
* Copyright (C) 2011 Bartek Przybylski
15-
* Copyright (C) 2024 ownCloud GmbH.
16+
* Copyright (C) 2025 ownCloud GmbH.
1617
* <p>
1718
* This program is free software: you can redistribute it and/or modify
1819
* it under the terms of the GNU General Public License version 2,
@@ -44,6 +45,7 @@ import com.owncloud.android.R
4445
import com.owncloud.android.databinding.PasscodelockBinding
4546
import com.owncloud.android.domain.utils.Event
4647
import com.owncloud.android.extensions.showBiometricDialog
48+
import com.owncloud.android.extensions.showMessageInSnackbar
4749
import com.owncloud.android.presentation.documentsprovider.DocumentsProviderUtils.notifyDocumentsProviderRoots
4850
import com.owncloud.android.presentation.security.biometric.BiometricStatus
4951
import com.owncloud.android.presentation.security.biometric.BiometricViewModel
@@ -94,6 +96,10 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
9496

9597
setContentView(binding.root)
9698

99+
if (intent.getBooleanExtra(BIOMETRIC_HAS_FAILED, false)) {
100+
showMessageInSnackbar(message = getString(R.string.biometric_not_available))
101+
}
102+
97103
numberOfPasscodeDigits = passCodeViewModel.getPassCode()?.length ?: passCodeViewModel.getNumberOfPassCodeDigits()
98104
passCodeEditTexts = arrayOfNulls(numberOfPasscodeDigits)
99105

@@ -474,5 +480,7 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
474480

475481
private const val NUM_ATTEMPTS_WITHOUT_TIMER = 3
476482

483+
const val BIOMETRIC_HAS_FAILED = "BIOMETRIC_HAS_FAILED"
484+
477485
}
478486
}

owncloudApp/src/main/java/com/owncloud/android/presentation/security/passcode/PassCodeManager.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
* ownCloud Android client application
33
*
44
* @author Juan Carlos Garrote Gascón
5+
* @author Jorge Aguado Recio
56
*
6-
* Copyright (C) 2023 ownCloud GmbH.
7+
* Copyright (C) 2025 ownCloud GmbH.
78
*
89
* This program is free software: you can redistribute it and/or modify
910
* it under the terms of the GNU General Public License version 2,
@@ -52,7 +53,7 @@ object PassCodeManager {
5253
return
5354
}
5455

55-
askUserForPasscode(activity)
56+
askUserForPasscode(activity = activity, biometricHasFailed = false)
5657
} else if (preferencesProvider.getBoolean(PassCodeActivity.PREFERENCE_MIGRATION_REQUIRED, false)) {
5758
val intent = Intent(appContext, PassCodeActivity::class.java).apply {
5859
action = PassCodeActivity.ACTION_CREATE
@@ -86,15 +87,16 @@ object PassCodeManager {
8687
fun isPassCodeEnabled(): Boolean =
8788
preferencesProvider.getBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, false)
8889

89-
private fun askUserForPasscode(activity: Activity) {
90+
private fun askUserForPasscode(activity: Activity, biometricHasFailed: Boolean) {
9091
val i = Intent(appContext, PassCodeActivity::class.java).apply {
9192
action = PassCodeActivity.ACTION_CHECK
9293
flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_SINGLE_TOP
94+
putExtra(PassCodeActivity.BIOMETRIC_HAS_FAILED, biometricHasFailed)
9395
}
9496
activity.startActivity(i)
9597
}
9698

97-
fun onBiometricCancelled(activity: Activity) {
98-
askUserForPasscode(activity)
99+
fun onBiometricCancelled(activity: Activity, biometricHasFailed: Boolean) {
100+
askUserForPasscode(activity, biometricHasFailed)
99101
}
100102
}

owncloudApp/src/main/java/com/owncloud/android/presentation/security/pattern/PatternActivity.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
* @author David González Verdugo
77
* @author Abel García de Prada
88
* @author Juan Carlos Garrote Gascón
9-
* Copyright (C) 2021 ownCloud GmbH.
9+
* @author Jorge Aguado Recio
10+
*
11+
* Copyright (C) 2025 ownCloud GmbH.
1012
* <p>
1113
* This program is free software: you can redistribute it and/or modify
1214
* it under the terms of the GNU General Public License version 2,
@@ -40,6 +42,7 @@ import com.owncloud.android.R
4042
import com.owncloud.android.data.providers.implementation.OCSharedPreferencesProvider
4143
import com.owncloud.android.databinding.ActivityPatternLockBinding
4244
import com.owncloud.android.extensions.showBiometricDialog
45+
import com.owncloud.android.extensions.showMessageInSnackbar
4346
import com.owncloud.android.presentation.documentsprovider.DocumentsProviderUtils.notifyDocumentsProviderRoots
4447
import com.owncloud.android.presentation.security.PREFERENCE_LAST_UNLOCK_TIMESTAMP
4548
import com.owncloud.android.presentation.security.biometric.BiometricStatus
@@ -76,6 +79,10 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
7679

7780
setContentView(binding.root)
7881

82+
if (intent.getBooleanExtra(BIOMETRIC_HAS_FAILED, false)) {
83+
showMessageInSnackbar(message = getString(R.string.biometric_not_available))
84+
}
85+
7986
binding.patternLockView.clearPattern()
8087

8188
// Allow or disallow touches with other visible windows
@@ -354,5 +361,6 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
354361
private const val KEY_PATTERN_STRING = "PATTERN_STRING"
355362
private const val PATTERN_HEADER_VIEW_TEXT = "PATTERN_HEADER_VIEW_TEXT"
356363
private const val PATTERN_EXP_VIEW_STATE = "PATTERN_EXP_VIEW_STATE"
364+
const val BIOMETRIC_HAS_FAILED = "BIOMETRIC_HAS_FAILED"
357365
}
358366
}

owncloudApp/src/main/java/com/owncloud/android/presentation/security/pattern/PatternManager.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
* ownCloud Android client application
33
*
44
* @author Juan Carlos Garrote Gascón
5+
* @author Jorge Aguado Recio
56
*
6-
* Copyright (C) 2023 ownCloud GmbH.
7+
* Copyright (C) 2025 ownCloud GmbH.
78
*
89
* This program is free software: you can redistribute it and/or modify
910
* it under the terms of the GNU General Public License version 2,
@@ -52,7 +53,7 @@ object PatternManager {
5253
return
5354
}
5455

55-
askUserForPattern(activity)
56+
askUserForPattern(activity = activity, biometricHasFailed = false)
5657
}
5758

5859
visibleActivities.add(activity.javaClass)
@@ -79,15 +80,16 @@ object PatternManager {
7980
fun isPatternEnabled(): Boolean =
8081
preferencesProvider.getBoolean(PatternActivity.PREFERENCE_SET_PATTERN, false)
8182

82-
private fun askUserForPattern(activity: Activity) {
83+
private fun askUserForPattern(activity: Activity, biometricHasFailed: Boolean) {
8384
val i = Intent(appContext, PatternActivity::class.java).apply {
8485
action = PatternActivity.ACTION_CHECK
8586
flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_SINGLE_TOP
87+
putExtra(PatternActivity.BIOMETRIC_HAS_FAILED, biometricHasFailed)
8688
}
8789
activity.startActivity(i)
8890
}
8991

90-
fun onBiometricCancelled(activity: Activity) {
91-
askUserForPattern(activity)
92+
fun onBiometricCancelled(activity: Activity, biometricHasFailed: Boolean) {
93+
askUserForPattern(activity, biometricHasFailed)
9294
}
9395
}

owncloudApp/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@
321321
<string name="biometric_prompt_title">Biometric log in</string>
322322
<string name="biometric_prompt_subtitle">Log in using your biometric credential.</string>
323323
<string name="biometric_dialog_title">Do you want to additionally activate biometric security?</string>
324+
<string name="biometric_not_available">Biometric unlock is not available because system could not provide</string>
324325

325326
<string name="media_notif_ticker">%1$s music player</string>
326327
<string name="media_state_playing">%1$s (playing)</string>

0 commit comments

Comments
 (0)