Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ ownCloud admins and users.
## Summary

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

## Details

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

* Bugfix - App crashes at start when biometrics fail: [#7134](https://github.com/owncloud/enterprise/issues/7134)

The crash that happened when biometrics failed due to a system error has been
handled. In this case, an error is shown and pattern or passcode unlock are used
instead of biometrics.

https://github.com/owncloud/enterprise/issues/7134
https://github.com/owncloud/android/pull/4564

# Changelog for ownCloud Android Client [4.5.0] (2025-03-24)

The following sections list the changes in ownCloud Android Client 4.5.0 relevant to
Expand Down
7 changes: 7 additions & 0 deletions changelog/unreleased/4564
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Bugfix: App crashes at start when biometrics fail

The crash that happened when biometrics failed due to a system error has been handled.
In this case, an error is shown and pattern or passcode unlock are used instead of biometrics.

https://github.com/owncloud/enterprise/issues/7134
https://github.com/owncloud/android/pull/4564
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
*
* @author David González Verdugo
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
* Copyright (C) 2023 ownCloud GmbH.
* Copyright (C) 2025 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -66,7 +67,7 @@ class BiometricActivity : AppCompatActivity() {
if (biometricManager.canAuthenticate(BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS) {
showBiometricPrompt()
} else {
authError()
authError(biometricHasFailed = true)
}
}

Expand All @@ -82,13 +83,13 @@ class BiometricActivity : AppCompatActivity() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Timber.e("onAuthenticationError ($errorCode): $errString")
authError()
authError(biometricHasFailed = errorCode != BiometricPrompt.ERROR_NEGATIVE_BUTTON)
}

override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
if (result.cryptoObject?.cipher != cryptoObject.cipher) {
authError()
authError(biometricHasFailed = true)
} else {
if (biometricViewModel.shouldAskForNewPassCode()) {
biometricViewModel.removePassCode()
Expand All @@ -110,14 +111,19 @@ class BiometricActivity : AppCompatActivity() {
})

// Displays the "log in" prompt.
biometricPrompt.authenticate(promptInfo, cryptoObject)
try {
biometricPrompt.authenticate(promptInfo, cryptoObject)
} catch (e: Exception) {
Timber.e(e, "cryptoObject property has not been initialized correctly")
authError(biometricHasFailed = true)
}
}

private fun authError() {
private fun authError(biometricHasFailed: Boolean) {
if (PassCodeManager.isPassCodeEnabled()) {
PassCodeManager.onBiometricCancelled(this)
PassCodeManager.onBiometricCancelled(this, biometricHasFailed)
} else if (PatternManager.isPatternEnabled()) {
PatternManager.onBiometricCancelled(this)
PatternManager.onBiometricCancelled(this, biometricHasFailed)
}

finish()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ object BiometricManager {
activity.startActivity(i)
} else if (isPassCodeEnabled()) {
// Cancel biometric lock and use passcode unlock method
PassCodeManager.onBiometricCancelled(activity)
PassCodeManager.onBiometricCancelled(activity = activity, biometricHasFailed = false)
visibleActivities.add(activity.javaClass)
} else if (isPatternEnabled()) {
// Cancel biometric lock and use pattern unlock method
PatternManager.onBiometricCancelled(activity)
PatternManager.onBiometricCancelled(activity = activity, biometricHasFailed = false)
visibleActivities.add(activity.javaClass)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
* @author Juan Carlos Garrote Gascón
* @author David Crespo Ríos
* @author Aitor Ballesteros Pavón
* @author Jorge Aguado Recio
*
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2024 ownCloud GmbH.
* Copyright (C) 2025 ownCloud GmbH.
* <p>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -44,6 +45,7 @@ import com.owncloud.android.R
import com.owncloud.android.databinding.PasscodelockBinding
import com.owncloud.android.domain.utils.Event
import com.owncloud.android.extensions.showBiometricDialog
import com.owncloud.android.extensions.showMessageInSnackbar
import com.owncloud.android.presentation.documentsprovider.DocumentsProviderUtils.notifyDocumentsProviderRoots
import com.owncloud.android.presentation.security.biometric.BiometricStatus
import com.owncloud.android.presentation.security.biometric.BiometricViewModel
Expand Down Expand Up @@ -94,6 +96,10 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom

setContentView(binding.root)

if (intent.getBooleanExtra(BIOMETRIC_HAS_FAILED, false)) {
showMessageInSnackbar(message = getString(R.string.biometric_not_available))
}

numberOfPasscodeDigits = passCodeViewModel.getPassCode()?.length ?: passCodeViewModel.getNumberOfPassCodeDigits()
passCodeEditTexts = arrayOfNulls(numberOfPasscodeDigits)

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

private const val NUM_ATTEMPTS_WITHOUT_TIMER = 3

const val BIOMETRIC_HAS_FAILED = "BIOMETRIC_HAS_FAILED"

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* ownCloud Android client application
*
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
* Copyright (C) 2023 ownCloud GmbH.
* Copyright (C) 2025 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -52,7 +53,7 @@ object PassCodeManager {
return
}

askUserForPasscode(activity)
askUserForPasscode(activity = activity, biometricHasFailed = false)
} else if (preferencesProvider.getBoolean(PassCodeActivity.PREFERENCE_MIGRATION_REQUIRED, false)) {
val intent = Intent(appContext, PassCodeActivity::class.java).apply {
action = PassCodeActivity.ACTION_CREATE
Expand Down Expand Up @@ -86,15 +87,16 @@ object PassCodeManager {
fun isPassCodeEnabled(): Boolean =
preferencesProvider.getBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, false)

private fun askUserForPasscode(activity: Activity) {
private fun askUserForPasscode(activity: Activity, biometricHasFailed: Boolean) {
val i = Intent(appContext, PassCodeActivity::class.java).apply {
action = PassCodeActivity.ACTION_CHECK
flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_SINGLE_TOP
putExtra(PassCodeActivity.BIOMETRIC_HAS_FAILED, biometricHasFailed)
}
activity.startActivity(i)
}

fun onBiometricCancelled(activity: Activity) {
askUserForPasscode(activity)
fun onBiometricCancelled(activity: Activity, biometricHasFailed: Boolean) {
askUserForPasscode(activity, biometricHasFailed)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
* @author David González Verdugo
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
* Copyright (C) 2021 ownCloud GmbH.
* @author Jorge Aguado Recio
*
* Copyright (C) 2025 ownCloud GmbH.
* <p>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -40,6 +42,7 @@ import com.owncloud.android.R
import com.owncloud.android.data.providers.implementation.OCSharedPreferencesProvider
import com.owncloud.android.databinding.ActivityPatternLockBinding
import com.owncloud.android.extensions.showBiometricDialog
import com.owncloud.android.extensions.showMessageInSnackbar
import com.owncloud.android.presentation.documentsprovider.DocumentsProviderUtils.notifyDocumentsProviderRoots
import com.owncloud.android.presentation.security.PREFERENCE_LAST_UNLOCK_TIMESTAMP
import com.owncloud.android.presentation.security.biometric.BiometricStatus
Expand Down Expand Up @@ -76,6 +79,10 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {

setContentView(binding.root)

if (intent.getBooleanExtra(BIOMETRIC_HAS_FAILED, false)) {
showMessageInSnackbar(message = getString(R.string.biometric_not_available))
}

binding.patternLockView.clearPattern()

// Allow or disallow touches with other visible windows
Expand Down Expand Up @@ -354,5 +361,6 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
private const val KEY_PATTERN_STRING = "PATTERN_STRING"
private const val PATTERN_HEADER_VIEW_TEXT = "PATTERN_HEADER_VIEW_TEXT"
private const val PATTERN_EXP_VIEW_STATE = "PATTERN_EXP_VIEW_STATE"
const val BIOMETRIC_HAS_FAILED = "BIOMETRIC_HAS_FAILED"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* ownCloud Android client application
*
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
* Copyright (C) 2023 ownCloud GmbH.
* Copyright (C) 2025 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -52,7 +53,7 @@ object PatternManager {
return
}

askUserForPattern(activity)
askUserForPattern(activity = activity, biometricHasFailed = false)
}

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

private fun askUserForPattern(activity: Activity) {
private fun askUserForPattern(activity: Activity, biometricHasFailed: Boolean) {
val i = Intent(appContext, PatternActivity::class.java).apply {
action = PatternActivity.ACTION_CHECK
flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_SINGLE_TOP
putExtra(PatternActivity.BIOMETRIC_HAS_FAILED, biometricHasFailed)
}
activity.startActivity(i)
}

fun onBiometricCancelled(activity: Activity) {
askUserForPattern(activity)
fun onBiometricCancelled(activity: Activity, biometricHasFailed: Boolean) {
askUserForPattern(activity, biometricHasFailed)
}
}
1 change: 1 addition & 0 deletions owncloudApp/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@
<string name="biometric_prompt_title">Biometric log in</string>
<string name="biometric_prompt_subtitle">Log in using your biometric credential.</string>
<string name="biometric_dialog_title">Do you want to additionally activate biometric security?</string>
<string name="biometric_not_available">Biometric unlock is not available because system could not provide</string>

<string name="media_notif_ticker">%1$s music player</string>
<string name="media_state_playing">%1$s (playing)</string>
Expand Down
Loading