Integrating Fingerprint Authentication on Android: APIs, Implementation Steps, and Advanced Usage
Google’s fingerprint API, introduced in Android 6.0, can be integrated via the legacy FingerprintManager or the newer BiometricPrompt, using a wrapper that abstracts version differences, handling permissions, hardware checks, authentication callbacks, and optional reflection to retrieve fingerprint IDs and enrolled fingerprint lists for advanced login scenarios.
Google opened the fingerprint authentication API starting from Android 6.0. This article guides developers through the basic integration of fingerprint verification, the transition to the newer BiometricPrompt API, and advanced techniques such as obtaining fingerprint IDs and lists via reflection.
1. Basic APIs
From Android 6.0 the system provides FingerprintManager . It offers public methods to check hardware support, enrollment status, start authentication, cancel authentication, and receive callbacks. After Android 9.0 Google recommends using BiometricPrompt instead of FingerprintManager because the former cannot customize the dialog UI but is the official solution.
2. Demo Structure
The demo consists of a wrapper class FingerprintManagerWrapper that hides version differences. Two concrete implementations, FingerprintVersionM (Android 6.0‑8.0) and FingerprintVersionP (Android 9.0+), both implement the common interface IFingerprintInterface with startAuth() and canceAuth() methods.
public class FingerprintManagerWrapper {
private IFingerprintInterface mFingerprintImp;
public FingerprintManagerWrapper() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
mFingerprintImp = new FingerprintVersionP();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mFingerprintImp = new FingerprintVersionM();
}
} catch (Exception e) { /* handle */ }
}
/**
* Check if the device supports fingerprint hardware.
*/
public boolean isHardwareDetected() {
boolean isHardwareSupport = false;
try {
if (mFingerprintManager != null) {
isHardwareSupport = mFingerprintManager.isHardwareDetected();
}
} catch (Exception e) { Log.e(TAG, "isHardwareDetected err", e); }
return isHardwareSupport;
}
/**
* Check whether any fingerprints have been enrolled.
*/
public boolean hasEnrolledFingerprints() {
boolean hasEnrolled = false;
try {
if (mFingerprintManager != null) {
hasEnrolled = mFingerprintManager.hasEnrolledFingerprints();
}
} catch (Exception e) { Log.e(TAG, "hasEnrolledFingerprints err", e); }
return hasEnrolled;
}
public void startAuth(FingerprintManagerWrapper.FingerVerifyResultListener listener) {
Log.i(TAG, "------startFingerAuthenticate() enter --------");
if (!isHardwareDetected()) { Log.e(TAG, "------hardware not detected--------"); return; }
if (!hasEnrolledFingerprints()) { Log.e(TAG, "-----no fingerprints enrolled--------"); return; }
mFingerprintImp.startAuth(listener);
}
}3. Version‑Specific Implementations
public class FingerprintVersionM implements IFingerprintInterface {
@Override
public void startAuth(FingerprintManagerWrapper.FingerVerifyResultListener listener) {
CancellationSignal mCancellationSignal = new CancellationSignal();
MyAuthenticationCallback authenticationCallback = new MyAuthenticationCallback();
mFingerprintManager.authenticate(null, mCancellationSignal, 0, authenticationCallback, null);
}
@Override
public void canceAuth() { /* cancel logic */ }
} public class FingerprintVersionP implements IFingerprintInterface {
@Override
public void startAuth(FingerprintManagerWrapper.FingerVerifyResultListener listener) {
CancellationSignal mCancellationSignal = new CancellationSignal();
BiometricPrompt.AuthenticationCallback mAuthenticationCallback = new BiometricPrompt.AuthenticationCallback() {};
mBiometricPrompt.authenticate(mCancellationSignal, mContext.getMainExecutor(), mAuthenticationCallback);
}
@Override
public void canceAuth() { /* cancel logic */ }
}4. Callback Implementation
private class MyAuthenticationCallback extends AuthenticationCallback {
@Override
public void onAuthenticationFailed() { /* failure handling */ }
@Override
public void onAuthenticationSucceeded(AuthenticationResult result) { /* success handling */ }
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { /* help handling */ }
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) { /* error handling */ }
}5. Integration Steps (6 steps)
Add fingerprint permissions in AndroidManifest.xml : <!-- Android 9.0+ --> <uses-permission android:name="android.permission.USE_BIOMETRIC"/> <!-- Android 6.0‑8.0 --> <uses-permission android:name="android.permission.USE_FINGERPRINT"/> <!-- For obtaining fingerprint IDs (requires system permission) --> <uses-permission android:name="android.permission.MANAGE_FINGERPRINT"/>
Check at runtime whether the device supports fingerprint and whether fingerprints are enrolled (methods in the wrapper).
Call FingerprintManagerWrapper.startAuth() to launch authentication.
Implement version‑specific classes ( FingerprintVersionM and FingerprintVersionP ) as shown above.
Register the authentication callback ( MyAuthenticationCallback ) in both version implementations.
In the Activity’s onStop() method cancel the authentication: @Override protected void onStop() { super.onStop(); mCancellationSignal.cancel(); }
6. Advanced: Obtaining Fingerprint ID via Reflection
private static int getFingerId(AuthenticationResult result) {
int fingerId = -1;
try {
Field field = result.getClass().getDeclaredField("mFingerprint");
field.setAccessible(true);
Object fingerPrint = field.get(result);
Class
clzz = Class.forName("android.hardware.fingerprint.Fingerprint");
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
Class
supClass = clzz.getSuperclass();
Method getBiometricId = supClass.getDeclaredMethod("getBiometricId");
fingerId = (int) getBiometricId.invoke(fingerPrint);
} else {
Method getFingerId = clzz.getDeclaredMethod("getFingerId");
fingerId = (int) getFingerId.invoke(fingerPrint);
}
} catch (Exception e) { Log.e(TAG, "", e); }
return fingerId;
}For Android 6.0 the fingerprint object contains mFingerprintId and a public getFingerprintId() method. For Android 9.0+ the ID is stored in the superclass as mBiometricId with getBiometricId() .
7. Advanced: Getting the List of Enrolled Fingerprints
public static Object getEnrolledFingerprints(FingerprintManager fm) {
try {
if (fm != null) {
return invokeMethod(fm, "getEnrolledFingerprints");
}
} catch (Exception e) { VLog.e(TAG, "getEnrolledFingerprints()", e); }
return null;
}The returned object can be converted to JSON (using Gson) and mapped to a unified bean AccountFingerprint that contains fields for both Android 6.0 ( mFingerId ) and Android 9.0+ ( mBiometricId ).
8. Practical Scenario – Fingerprint Login
After obtaining the fingerprint ID and the list of fingerprints, a typical use case is to bind the device’s fingerprint data to a user account on the server. During login the client sends the fingerprint ID together with the account and device identifier; the server validates the binding and returns the authentication result.
9. Summary
The article covered:
Basic fingerprint integration using FingerprintManager and the newer BiometricPrompt .
Step‑by‑step guide to add permissions, check hardware, start/cancel authentication, and handle callbacks.
Advanced reflection techniques to retrieve hidden fingerprint IDs and the full list of enrolled fingerprints, enabling scenarios such as fingerprint‑based login.
These techniques allow developers to implement secure and user‑friendly biometric authentication across Android 6.0‑10+ devices.
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.