package de.niklasmerz.cordova.biometric; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.app.Activity; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.util.Log; import androidx.biometric.BiometricManager; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaInterface; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CordovaWebView; import org.apache.cordova.PluginResult; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; public class Fingerprint extends CordovaPlugin { private static final String TAG = "Fingerprint"; private static final int REQUEST_CODE_BIOMETRIC = 1; private CallbackContext mCallbackContext = null; private PromptInfo.Builder mPromptInfoBuilder; public void initialize(CordovaInterface cordova, CordovaWebView webView) { super.initialize(cordova, webView); Log.v(TAG, "Init Fingerprint"); mPromptInfoBuilder = new PromptInfo.Builder( this.getApplicationLabel(cordova.getActivity()) ); } public boolean execute(final String action, JSONArray args, CallbackContext callbackContext) { this.mCallbackContext = callbackContext; Log.v(TAG, "Fingerprint action: " + action); if ("authenticate".equals(action)) { executeAuthenticate(args); return true; } else if ("registerBiometricSecret".equals(action)) { executeRegisterBiometricSecret(args); return true; } else if ("loadBiometricSecret".equals(action)) { executeLoadBiometricSecret(args); return true; } else if ("isAvailable".equals(action)) { executeIsAvailable(); return true; } return false; } private void executeIsAvailable() { PluginError error = canAuthenticate(); if (error != null) { sendError(error); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){ sendSuccess("biometric"); } else { sendSuccess("finger"); } } private void executeRegisterBiometricSecret(JSONArray args) { // should at least contains the secret if (args == null) { sendError(PluginError.BIOMETRIC_ARGS_PARSING_FAILED); return; } this.runBiometricActivity(args, BiometricActivityType.REGISTER_SECRET); } private void executeLoadBiometricSecret(JSONArray args) { this.runBiometricActivity(args, BiometricActivityType.LOAD_SECRET); } private void executeAuthenticate(JSONArray args) { this.runBiometricActivity(args, BiometricActivityType.JUST_AUTHENTICATE); } private void runBiometricActivity(JSONArray args, BiometricActivityType type) { PluginError error = canAuthenticate(); if (error != null) { sendError(error); return; } cordova.getActivity().runOnUiThread(() -> { mPromptInfoBuilder.parseArgs(args, type); Intent intent = new Intent(cordova.getActivity().getApplicationContext(), BiometricActivity.class); intent.putExtras(mPromptInfoBuilder.build().getBundle()); this.cordova.startActivityForResult(this, intent, REQUEST_CODE_BIOMETRIC); }); PluginResult pluginResult = new PluginResult(PluginResult.Status.NO_RESULT); pluginResult.setKeepCallback(true); this.mCallbackContext.sendPluginResult(pluginResult); } @Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if (requestCode != REQUEST_CODE_BIOMETRIC) { return; } if (resultCode != Activity.RESULT_OK) { sendError(intent); return; } sendSuccess(intent); } private void sendSuccess(Intent intent) { if (intent != null && intent.getExtras() != null) { sendSuccess(intent.getExtras().getString(PromptInfo.SECRET_EXTRA)); } else { sendSuccess("biometric_success"); } } private void sendError(Intent intent) { if (intent != null) { Bundle extras = intent.getExtras(); sendError(extras.getInt("code"), extras.getString("message")); } else { sendError(PluginError.BIOMETRIC_DISMISSED); } } private PluginError canAuthenticate() { int error = BiometricManager.from(cordova.getContext()).canAuthenticate(); switch (error) { case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE: case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE: return PluginError.BIOMETRIC_HARDWARE_NOT_SUPPORTED; case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED: return PluginError.BIOMETRIC_NOT_ENROLLED; case BiometricManager.BIOMETRIC_SUCCESS: default: return null; } } private void sendError(int code, String message) { JSONObject resultJson = new JSONObject(); try { resultJson.put("code", code); resultJson.put("message", message); PluginResult result = new PluginResult(PluginResult.Status.ERROR, resultJson); result.setKeepCallback(true); cordova.getActivity().runOnUiThread(() -> Fingerprint.this.mCallbackContext.sendPluginResult(result)); } catch (JSONException e) { Log.e(TAG, e.getMessage(), e); } } private void sendError(PluginError error) { sendError(error.getValue(), error.getMessage()); } private void sendSuccess(String message) { cordova.getActivity().runOnUiThread(() -> this.mCallbackContext.success(message)); } private String getApplicationLabel(Context context) { try { PackageManager packageManager = context.getPackageManager(); ApplicationInfo app = packageManager .getApplicationInfo(context.getPackageName(), 0); return packageManager.getApplicationLabel(app).toString(); } catch (PackageManager.NameNotFoundException e) { return null; } } }