はじめに
Android PからBiometricPrompt
というクラスが追加されました。これはバイオメトリック(生体認証)に関連する処理を引き受けてくれるもので、これを使うと今までアプリ側で実装していたダイアログの表示をシステム側で提供してくれるようになります。また、指紋認証だけでなく、虹彩認証や顔認証にも対応しており、ユーザが選択した方式を判別し、適切なダイアログを表示してくれるという優れものです。それに伴い、従来のFingerprintManager
はAndroid API 28より非推奨になりました。こんな便利な物を使わない手がないと、早速移行してみようと思ったのですが、BiometricPrompt
の最低サポートがAndroid API 28からなので、Min SDKバージョンが28より低い場合は完全に移行することが出来ません😇
今回はこの問題の解決方法と実装の仕方を簡単にまとめました。
解決方法
いきなり結論からです。android.hardware.biometrics.BiometricPrompt
ではなくandroidx.biometrics.BiometricsPrompt
を使います。ただし、AndroidXを使うので従来のAndroid Support Libraryとは共存出来ない点に注意してください。
実装方法
まず、依存関係をGradleに追加します。
dependencies {
...
implementation 'androidx.biometric:biometric:1.0.0-alpha04'
}
生体認証を使う為、Android Manifest
にパーミッションを追加します。
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
最後に認証ダイアログを呼び出す処理を実装します。
val executor = Executors.newSingleThreadExecutor()
val biometricPrompt = BiometricPrompt(this, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Log.d("Biometric", "Error")
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
Log.d("Biometric", "Succeeded")
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
Log.d("Biometric", "Failed")
}
})
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Title")
.setSubtitle("Sub title")
.setDescription("Description")
.setNegativeButtonText("Cancel")
.build()
biometricPrompt.authenticate(promptInfo)
これだけで、認証ダイアログを表示できます。
i | ii | iii |
---|---|---|
軽くコードの説明をします。
認証の結果を受け取るのはBiometricPrompt
インスタンス生成時に渡しているBiometricPrompt.AuthenticationCallback
になります。
関数名 | 内容 |
---|---|
onAuthenticationError | エラーが発生して操作が完了された時に呼び出される |
onAuthenticationSucceeded | 生体認証が成功した時に呼び出される |
onAuthenticationFailed | 生体認証が有効だが認識されない時に呼び出される |
キャンセルボタンのイベントはonAuthenticationError
メソッドでerrorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON
と比較することでキャッチすることができます。BIOMETRIC_ERROR
定数に関しては以下のものがあります。
定数名 | 内容 |
---|---|
ERROR_CANCELED | 生体認証が利用できない為、操作がキャンセルされた |
ERROR_HW_NOT_PRESENT | 端末に生体センサが搭載されていない |
ERROR_HW_UNAVAILABLE | 端末が利用できない |
ERROR_LOCKOUT | 生体認証に5回失敗した |
ERROR_LOCKOUT_PERMANENT |
ERROR_LOCKOUT の発生回数が多すぎる為、操作がキャンセルされた |
ERROR_NEGATIVE_BUTTON | ユーザがネガティブボタン(ダイアログのキャンセルボタン)を押した |
ERROR_NO_BIOMETRICS | ユーザの生体認証情報が登録されていない |
ERROR_NO_SPACE | 操作を完了するのに十分なストレージが用意されていない |
ERROR_TIMEOUT | 認証を実行するまでの時間が長すぎる為、タイムアウトが発生した |
ERROR_UNABLE_TO_PROCESS | 生体センサが画像を処理できない |
ERROR_USER_CANCELED | ユーザが操作をキャンセルした |
ERROR_VENDOR | いずれのエラーにも該当しない |
ERROR_CANCELED
は具体的に端末で使用するユーザを切り替えた場合(おそらく生体認証情報がリセットされる)や端末がロックされている場合、他のアプリが生体認証を利用中の場合に発生するみたいです。ERROR_HW_UNAVAILABLE
はどういったタイミングで発生するのかがわかりませんでした。ERROR_LOCKOUT
が発生した場合、30秒後に再度認証可能になるみたいです(知らなかった)。ERROR_UNABLE_TO_PROCESS
はおそらく虹彩認証や顔認証などのカメラを利用する場合に発生するのかと思います。
認証ダイアログのカスタマイズはBiometricPrompt.PromptInfo.Builder
で行います。指定できる項目は以下の通りです。
関数名 | 内容 |
---|---|
setTitle | タイトルを設定(必須) |
setSubtitle | サブタイトルを設定(任意) |
setDescription | 説明を設定(任意) |
setNegativeButtonText | ネガティブボタンに表示されるテキストを設定(必須) |
ダイアログの画像やメッセージといった細かい部分は流石に設定できないみたいですね。
また、認証ダイアログを表示する前に
private fun isBiometricEnabled(context: Context): Boolean =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& context.getSystemService(KeyguardManager::class.java).isKeyguardSecure
&& context.packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
のように端末が生体認証を利用出来るか判定した方が良さそうです。
まとめ
足早にですがBiometricPrompt
をAndroid Mから使う方法を解説しました。本来はBuild.VERSION.SDK_INT
でAndroidのバージョンを取得してFingerprintManager
との処理を切り分けるのが行儀がいいのですが、ぶっちゃけめんどくさい効率的に出来るのであれば致し方ないのかなと。また、虹彩認証と顔認証については、次期バージョンのAndroid Q(API 29)から利用出来るみたいです。今回のデモアプリをGitHubに公開しておきますので、参考にしていただければ嬉しいです。では。
参考文献
- Android Developers Docs: https://developer.android.com/reference/androidx/biometric/BiometricPrompt
- Android 9 features and APIs: https://developer.android.com/about/versions/pie/android-9.0
- Fingerprint Authentication using BiometricPrompt Compat: https://medium.com/mindorks/fingerprint-authentication-using-biometricprompt-compat-1466365b4795