Developing Face Recognition Biometric Authorization in Android App
Biometric face recognition on Android — not Face ID. Apple — infrared projection of 30,000 points, hardware Secure Enclave, guaranteed protection class. Android — range from infrared sensor on Pixel 8 to simple front camera on budget devices. And this very range creates most problems during development.
Biometric classes and why it matters
Android divides biometrics into three security classes:
- Class 1 (Convenience) — weak: front camera without infrared sensor, typically 2D photo recognition. Not suitable for financial operations.
- Class 2 (Weak) — medium: somewhat better, but still no Secure Enclave guarantee.
- Class 3 (Strong) — hardware guarantee, result verified by TEE (Trusted Execution Environment) or Secure Element.
Call BiometricManager.canAuthenticate(BIOMETRIC_STRONG) returns BIOMETRIC_ERROR_NONE_ENROLLED or BIOMETRIC_ERROR_NO_HARDWARE on devices where face registered as Class 1. This means: cannot just ask for "face biometrics" — must explicitly check class and decide.
For banking and fintech apps accept only BIOMETRIC_STRONG. For others — can accept BIOMETRIC_WEAK with user warning.
Implementation via BiometricPrompt
API identical to fingerprint authentication — same BiometricPrompt, same CryptoObject. Difference that BiometricManager.canAuthenticate() returns correct class for face, and system itself chooses what to show user — fingerprint prompt or face prompt.
Important nuance: on some devices (Honor, some MIUI versions) system shows face and fingerprint prompt simultaneously. This is standard behavior — system offers first available method.
val biometricManager = BiometricManager.from(context)
val canAuthenticate = biometricManager.canAuthenticate(
BiometricManager.Authenticators.BIOMETRIC_STRONG
)
when (canAuthenticate) {
BiometricManager.BIOMETRIC_SUCCESS -> launchBiometricPrompt()
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> showFallback()
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> showTemporaryError()
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> promptEnrollment()
}
promptEnrollment() — open system biometric settings via Intent(Settings.ACTION_BIOMETRIC_ENROLL) with extras EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED = BIOMETRIC_STRONG.
Prompt lifecycle and Fragment back stack
BiometricPrompt tied to FragmentActivity. If user taps "back" while showing prompt — calls onAuthenticationError with code ERROR_USER_CANCELED. But if Activity recreated (screen rotation) while prompt open — prompt disappears without callback. Solution: track isAuthenticating flag in ViewModel and restart prompt at onResume if flag active.
Device fragmentation: what we check manually
| Manufacturer | Feature |
|---|---|
| Samsung (One UI 5+) | Face ID Class Strong on flagships, Weak on budget |
| Xiaomi / MIUI 14 | canAuthenticate may return wrong class — need extra check |
| Pixel 6+ | Full-featured Strong via under-display sensor or front IR |
| Huawei (HMS) | Own FaceManager API when no GMS |
Huawei without GMS — separate story. There BiometricPrompt doesn't work. Connect com.huawei.hms:base and use HuaweiBiometricManager via reflection or conditional compilation under HMS flavor.
Security: spoofing attacks
Face recognition via 2D photo vulnerable to photo attack. Google Play Protect doesn't block apps with Class 1 biometrics, but financial regulators (PCI DSS, Central Bank requirements) explicitly forbid using it for payment authorization. Never use Class 1 for money operations or sensitive data.
If client insists on supporting old budget devices — offer two-factor scheme: face (Class 1) + PIN. This formally satisfies 2FA requirements without compromising security.
Work stages and timeframe
Audit target device pool → determine minimum biometric class → implement KeyStore + CryptoObject flow → handle HMS scenario (if needed) → test on physical devices of different manufacturers → document limitations per model.
Timeframe: 5–10 business days. Testing on real devices of different vendors takes significant time — emulator nearly useless here.







