From e633c7a861876d5720ac5db54167c9a4a8762c92 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Mon, 14 Dec 2020 17:51:18 +0100 Subject: [PATCH] Biometric unlock in priority and device unlock when biometric not available --- .../biometric/AdvancedUnlockFragment.kt | 19 ++++++------- .../biometric/AdvancedUnlockManager.kt | 27 +++++++++---------- .../AdvancedUnlockNotificationService.kt | 8 +++--- .../keepass/settings/PreferencesUtil.kt | 10 +++++++ .../main/res/values-v23/donottranslate.xml | 1 + 5 files changed, 38 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockFragment.kt b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockFragment.kt index b9441a145..b1b8adafe 100644 --- a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockFragment.kt @@ -151,7 +151,8 @@ class AdvancedUnlockFragment: StylishFragment(), AdvancedUnlockManager.AdvancedU fun loadDatabase(databaseUri: Uri?, autoOpenPrompt: Boolean) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // To get device credential unlock result, only if same database uri - if (databaseUri != null && mAdvancedUnlockEnabled) { + if (databaseUri != null + && mAdvancedUnlockEnabled) { activityResult?.let { if (databaseUri == databaseFileUri) { advancedUnlockManager?.onActivityResult(it.requestCode, it.resultCode) @@ -175,14 +176,7 @@ class AdvancedUnlockFragment: StylishFragment(), AdvancedUnlockManager.AdvancedU fun checkUnlockAvailability() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { allowOpenBiometricPrompt = true - if (PreferencesUtil.isDeviceCredentialUnlockEnable(requireContext())) { - mAdvancedUnlockInfoView?.setIconResource(R.drawable.bolt) - if (AdvancedUnlockManager.isDeviceSecure(requireContext())) { - selectMode() - } else { - toggleMode(Mode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED) - } - } else if (PreferencesUtil.isBiometricUnlockEnable(requireContext())) { + if (PreferencesUtil.isBiometricUnlockEnable(requireContext())) { mAdvancedUnlockInfoView?.setIconResource(R.drawable.fingerprint) // biometric not supported (by API level or hardware) so keep option hidden @@ -202,6 +196,13 @@ class AdvancedUnlockFragment: StylishFragment(), AdvancedUnlockManager.AdvancedU selectMode() } } + } else if (PreferencesUtil.isDeviceCredentialUnlockEnable(requireContext())) { + mAdvancedUnlockInfoView?.setIconResource(R.drawable.bolt) + if (AdvancedUnlockManager.isDeviceSecure(requireContext())) { + selectMode() + } else { + toggleMode(Mode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED) + } } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockManager.kt b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockManager.kt index 0788e6b3d..c183bc893 100644 --- a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockManager.kt +++ b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockManager.kt @@ -71,8 +71,8 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity) private var isKeyManagerInit = false - private val deviceCredentialUnlockEnable = PreferencesUtil.isDeviceCredentialUnlockEnable(retrieveContext()) private val biometricUnlockEnable = PreferencesUtil.isBiometricUnlockEnable(retrieveContext()) + private val deviceCredentialUnlockEnable = PreferencesUtil.isDeviceCredentialUnlockEnable(retrieveContext()) val isKeyManagerInitialized: Boolean get() { @@ -82,6 +82,10 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity) return isKeyManagerInit } + private fun isBiometricOperation(): Boolean { + return biometricUnlockEnable || isDeviceCredentialBiometricOperation() + } + // Since Android 30, device credential is also a biometric operation private fun isDeviceCredentialOperation(): Boolean { return Build.VERSION.SDK_INT < Build.VERSION_CODES.R @@ -93,13 +97,9 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity) && deviceCredentialUnlockEnable } - private fun isBiometricOperation(): Boolean { - return biometricUnlockEnable || isDeviceCredentialBiometricOperation() - } - init { if (isDeviceSecure(retrieveContext()) - && (deviceCredentialUnlockEnable || biometricUnlockEnable)) { + && (biometricUnlockEnable || deviceCredentialUnlockEnable)) { try { this.keyStore = KeyStore.getInstance(ADVANCED_UNLOCK_KEYSTORE) this.keyGenerator = KeyGenerator.getInstance(ADVANCED_UNLOCK_KEY_ALGORITHM, ADVANCED_UNLOCK_KEYSTORE) @@ -293,13 +293,7 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity) retrieveContext().getString(descriptionId) } ?: "" - if (cryptoPrompt.isDeviceCredentialOperation) { - val keyGuardManager = ContextCompat.getSystemService(retrieveContext(), KeyguardManager::class.java) - retrieveContext().startActivityForResult( - keyGuardManager?.createConfirmDeviceCredentialIntent(promptTitle, promptDescription), - REQUEST_DEVICE_CREDENTIAL) - } - else if (cryptoPrompt.isBiometricOperation) { + if (cryptoPrompt.isBiometricOperation) { val promptInfoExtractCredential = BiometricPrompt.PromptInfo.Builder().apply { setTitle(promptTitle) if (promptDescription.isNotEmpty()) @@ -311,11 +305,16 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity) setNegativeButtonText(retrieveContext().getString(android.R.string.cancel)) } }.build() - biometricPrompt?.authenticate( promptInfoExtractCredential, BiometricPrompt.CryptoObject(cryptoPrompt.cipher)) } + else if (cryptoPrompt.isDeviceCredentialOperation) { + val keyGuardManager = ContextCompat.getSystemService(retrieveContext(), KeyguardManager::class.java) + retrieveContext().startActivityForResult( + keyGuardManager?.createConfirmDeviceCredentialIntent(promptTitle, promptDescription), + REQUEST_DEVICE_CREDENTIAL) + } } @Synchronized diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/AdvancedUnlockNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/notifications/AdvancedUnlockNotificationService.kt index 60e860c0d..7974e24bb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/notifications/AdvancedUnlockNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/notifications/AdvancedUnlockNotificationService.kt @@ -62,12 +62,12 @@ class AdvancedUnlockNotificationService : NotificationService() { action = ACTION_REMOVE_KEYS } val pendingDeleteIntent = PendingIntent.getService(this, 0, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT) - val deviceCredential = PreferencesUtil.isDeviceCredentialUnlockEnable(this) + val biometricUnlockEnabled = PreferencesUtil.isBiometricUnlockEnable(this) val notificationBuilder = buildNewNotification().apply { - setSmallIcon(if (deviceCredential) { - R.drawable.notification_ic_device_unlock_24dp - } else { + setSmallIcon(if (biometricUnlockEnabled) { R.drawable.notification_ic_fingerprint_unlock_24dp + } else { + R.drawable.notification_ic_device_unlock_24dp }) intent?.let { setContentTitle(getString(R.string.advanced_unlock)) diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt index d4baa0d14..f9c2f415d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt @@ -26,6 +26,7 @@ import android.net.Uri import androidx.preference.PreferenceManager import com.kunzisoft.keepass.BuildConfig import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.biometric.AdvancedUnlockManager import com.kunzisoft.keepass.database.element.SortNodeEnum import com.kunzisoft.keepass.timeout.TimeoutHelper import java.util.* @@ -240,14 +241,23 @@ object PreferencesUtil { fun isBiometricUnlockEnable(context: Context): Boolean { val prefs = PreferenceManager.getDefaultSharedPreferences(context) + val biometricSupported = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + AdvancedUnlockManager.biometricUnlockSupported(context) + } else { + false + } return prefs.getBoolean(context.getString(R.string.biometric_unlock_enable_key), context.resources.getBoolean(R.bool.biometric_unlock_enable_default)) + && biometricSupported } fun isDeviceCredentialUnlockEnable(context: Context): Boolean { val prefs = PreferenceManager.getDefaultSharedPreferences(context) + // Priority to biometric unlock + val biometricAlreadySupported = isBiometricUnlockEnable(context) return prefs.getBoolean(context.getString(R.string.device_credential_unlock_enable_key), context.resources.getBoolean(R.bool.device_credential_unlock_enable_default)) + && !biometricAlreadySupported } fun isTempAdvancedUnlockEnable(context: Context): Boolean { diff --git a/app/src/main/res/values-v23/donottranslate.xml b/app/src/main/res/values-v23/donottranslate.xml index e761117a7..e1e50e325 100644 --- a/app/src/main/res/values-v23/donottranslate.xml +++ b/app/src/main/res/values-v23/donottranslate.xml @@ -19,4 +19,5 @@ --> true + true