Edited at

指紋認証APIのエラーハンドリングで気をつけること

More than 3 years have passed since last update.


概要

株式会社 Zaim で提供している家計簿アプリ Zaim では、12/16(水)にアプリの画面ロック機能の方法として指紋認証を追加しました。

capture.png

パスワードによるロックに比べて、とても使いやすいのでぜひ触ってみてください。(Android 6.0 以上の端末でのみ利用可能です。)

今回はこの機能の開発中にエラーハンドリングの部分でハマったので、注意点をまとめてみました。


指紋認証 API について

指紋認証APIについては、公式ブログおよび(手前味噌ですが)以下のブログなどで紹介されていますので、ここでは使い方などの説明は省きます。

また Google のサンプルコードも公開されています。


指紋センサーとのやり取り

指紋センサーによる読み取りを準備(warm up)するには、 FingerprintManager#authenticate() をコールします。

このメソッドがコールされた後、指紋センサーでスキャンすることが出来るようになります。この状態は、 AuthenticationCallback クラスの onAuthenticationError()onAuthenticationSucceeded() が呼ばれるまで維持されます。

authenticate() メソッドの Parameter は以下となっています。


  • crypto: オブジェクトの呼び出しに関連づけられる

  • cancel: 認証をキャンセルできるオブジェクト

  • flags: オプションフラグ(0をセットすべき)

  • callback: 認証イベントを受け取るオブジェクト

  • handler: callbackイベントを扱うオプションハンドラー

このうち、callback には次の4つのメソッドがあります。


  • onAuthenticationError : 以下のどれにも当てはまらない復帰不可能なエラーを検出したとき

  • onAuthenticationFailed : 指紋は読み取れたけど未登録と判断したとき

  • onAuthenticationHelp : 復帰可能なエラーを検出したとき

  • onAuthenticationSucceeded : 指紋を登録済みのものと判断したとき


エラーハンドリングが必要なケース

この中で onAuthenticationError は、以下の場合にコールされます。

1. CancellationSignal が発動したとき

2. 所定回数、指紋の読み取りに失敗したとき

Google のサンプルでは、上記1と2の発生を区別するために、以下のように mSelfCancelled というフラグで処理を分岐させています。

    // このメソッドはFragmentのonResume()で呼ばれます

public void startListening(FingerprintManager.CryptoObject cryptoObject) {
if (!isFingerprintAuthAvailable()) {
return;
}
mCancellationSignal = new CancellationSignal();
mSelfCancelled = false;
mFingerprintManager
.authenticate(cryptoObject, mCancellationSignal, 0 /* flags */, this, null);
mIcon.setImageResource(R.drawable.ic_fp_40px);
}

// このメソッドはFragmentのonPause()で呼ばれます
public void stopListening() {
if (mCancellationSignal != null) {
mSelfCancelled = true;
mCancellationSignal.cancel();
mCancellationSignal = null;
}
}

@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
// stopListening()が呼ばれなかったときはif文の中の処理が動きます
if (!mSelfCancelled) {
showError(errString);
mIcon.postDelayed(new Runnable() {
@Override
public void run() {
mCallback.onError();
}
}, ERROR_TIMEOUT_MILLIS);
}
}

ところが、1のケースには特殊なケースがありました。

authenticate() で指紋センサーの応答を待機しているときに、電源ボタンを押すと onPause()より早いタイミングで CancellationSignal が発動されます。

そのためサンプルコードのままだと、mSelfCancelled が false のまま onAuthenticationError() に入ってきてしまいます。

onAuthenticationError() の実装内容にもよりますが、そのことを考慮しておかないと意図しない挙動になるケースもありますので、注意した方がよさそうです。


回避するなら

電源ボタン押下による CancellationSignal 発動を考慮するのであれば、以下のように onAuthenticationError() を修正するのが簡単かと思います。

    @Override

public void onAuthenticationError(int errMsgId, CharSequence errString) {
// 電源ボタンを押したときなど
if (errMsgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
return;
}

// stopListening()が呼ばれなかったときはif文の中の処理が動きます
if (!mSelfCancelled) {
showError(errString);
mIcon.postDelayed(new Runnable() {
@Override
public void run() {
mCallback.onError();
}
}, ERROR_TIMEOUT_MILLIS);
}
}


まとめ

onAuthenticationError()が呼ばれるケースは、


  1. CancellationSignal が発動したとき


    • 明示的に CancellationSignal#cancel() を呼ぶ

    • 指紋センサー準備中に電源ボタンを押す



  2. 所定回数、指紋の読み取りに失敗したとき


該当のリファレンス