英文:
Device credential (PIN) using BiometricPrompt destroys calling activity
问题
我们目前在一些安卓设备上面临着奇怪的BiometricPrompt
行为。
问题是与品牌/制造商无关的,因为在一些三星设备上它可以工作,但在其他设备上不能。
问题:
为了批准用户在她的账户上进行的某些交易,我们要求她使用她的设备凭据或生物特征来进行身份验证。
底层我们正在使用BiometricPrompt
。
我们面临的问题是,即在调用BiometricPrompt.authenticate()
方法之后,设备凭据屏幕被显示出来(具体来说只有PIN),我们的底层活动被销毁(两者之间只有几毫秒),我们输入PIN码,点击继续,设备凭据屏幕消失,然后我们看到设备本身的主屏幕。
这个问题只发生在用户设置了PIN作为设备凭据的情况下。
图案和生物特征都能正常工作。
此外,PIN在其他一些安卓设备上也能工作。
代码:
以下是我们调用的代码:
suspend fun authenticate(
activity: FragmentActivity
): AuthenticationResult = suspendCoroutine { continuation ->
@Suppress("DEPRECATION") // 我们知道这个问题。如果可能的话,我们会使用“现代”方法。
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(activity.getString(R.string.biometric_prompt_title))
.setDeviceCredentialAllowed(true)
.build()
activity.createBiometricPrompt(continuation).authenticate(promptInfo)
}
我们的authenticate()
方法是在主线程上使用Dispatchers.Main
调用的。
以下是createBiometricPrompt()
方法的代码:
fun FragmentActivity.createBiometricPrompt(continuation: Continuation<AuthenticationResult>): BiometricPrompt {
return BiometricPrompt(
this,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
continuation.resume(AuthenticationResult.Error(errorCode, errString))
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
continuation.resume(AuthenticationResult.Succeeded(result))
}
override fun onAuthenticationFailed() {
Timber.i("onAuthenticationFailed")
}
}
)
}
因为我们向注册为第二因素的设备发送推送,所以我们创建一个PendingIntent
来呈现交易活动。
我们使用以下代码创建活动:
fun createPendingIntent(context: Context, transaction: Transaction): PendingIntent =
Intent(context, TransactionActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NO_HISTORY or
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
putExtra(INTENT_EXTRA_TRANSACTION_APPROVAL, transaction)
}.let { intent ->
val flags = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
PendingIntent.getActivity(context, 1484, intent, flags)
}
我们在登录期间使用相同的代码。
在那里,我们的代码在所有设备上都没有任何问题。
因此,我猜想我们在创建Intent
/PendingIntent
时可能做错了什么? 🤷
你们中有人有什么想法,可能是什么导致了我们的问题?
已尝试的解决方案:
- 在我们的活动中添加了日志。可以确认我们的活动的
onDestroy
被调用。 - 从
Intent
中删除了所有flags
。
英文:
we are currently facing a weird BiometricPrompt
behaviour on some Android devices.
The problem is brand/manufacturer independent because it works e.g. on some Samsung devices but on others doesn't.
The Issue
To approve some transaction made by the user to her account we require her to authenticate herself using her device credentials or biometry.
Under the hood we are using BiometricPrompt
.
The problem we are facing is that immediately after calling the BiometricPrompt.authenticate()
method the device credential screen is shown (specifically only PIN), our underlying activity gets destroyed (there are just some milliseconds in between), we enter the Pin-Code, click continue, the device credential screens disappears and we see the home screen of the device itself.
The problem only occurs while the users has set up a PIN as device credential.
Pattern and biometry are working fine.
Also, PIN is working on some other Android devices.
Code
Here is the code we are calling:
suspend fun authenticate(
activity: FragmentActivity
): AuthenticationResult = suspendCoroutine { continuation ->
@Suppress("DEPRECATION") // We are aware of this. If possible we use the "Modern" approach.
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(activity.getString(R.string.biometric_prompt_title))
.setDeviceCredentialAllowed(true)
.build()
activity.createBiometricPrompt(continuation).authenticate(promptInfo)
}
Our authenticate()
method is called on the main thread using Dispatchers.Main
.
Here is the code of the createBiometricPrompt()
method:
fun FragmentActivity.createBiometricPrompt(continuation: Continuation<AuthenticationResult>): BiometricPrompt {
return BiometricPrompt(
this,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
continuation.resume(AuthenticationResult.Error(errorCode, errString))
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
continuation.resume(AuthenticationResult.Succeeded(result))
}
override fun onAuthenticationFailed() {
Timber.i("onAuthenticationFailed")
}
}
)
}
Since we are sending a push to the device which is registered as the second factor we create a PendingIntent
to present the transaction activity.
We create the activity with the following code:
fun createPendingIntent(context: Context, transaction: Transaction): PendingIntent =
Intent(context, TransactionActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NO_HISTORY or
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
putExtra(INTENT_EXTRA_TRANSACTION_APPROVAL, transaction)
}.let { intent ->
val flags = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
PendingIntent.getActivity(context, 1484, intent, flags)
}
We use the same code during login.
There our code works without any issues on all devices.
Therefore my guess that we might do something wrong creating the Intent
/PendingIntent
? 🤷
Does any of you has an idea what might cause our problem?
Already tried solutions
- Added logs to our activity. Can confirm that
onDestroy
of our activity is called - removed all
flags
fromIntent
答案1
得分: 1
我刚找到了解决我的问题的方法。关于文档从通知启动活动,他们建议使用TaskStackBuilder
来构建您的PendingIntent
。使用TaskStackBuilder
时,我们的活动不会在PIN屏幕显示时被关闭。
英文:
I just found a solution to my problem.
Regarding the documentation here Start an Activity from a Notification they recommend using TaskStackBuilder
to build your PendingIntent
.
Using TaskStackBuilder
our activity isn't killed while the PIN screen gets/is shown.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论