On-device liveness detection
The widget runs Google ML Kit face detection on every preview frame to drive on-device challenge gating ("blink now", "turn left") and to capture a hero frame only when the user is aligned with the oval. The server remains the source of truth for the verdict; on-device ML is a UX layer.
What runs on-device
- ML Kit face detection (FastMode, classification enabled for eye / smile probabilities)
- Per-challenge detectors: blink (eye-open probability), head turn (head Euler angle), smile (smile probability)
- Alignment check against the on-screen oval before challenges start
What runs on the server
- The authoritative anti-spoof and deepfake checks against the captured frame(s)
- All scoring —
is_live,score,thresholdcome from/v1/liveness/check, never the device
If on-device gating accepts a challenge but the server rejects the frame as a spoof, the server wins. result.isLive reflects the server's decision.
Why no enable / disable toggle
The v1 enableOnDeviceLiveness flag is gone as of v2. On-device ML Kit is always on — it's tightly coupled to the widget's challenge UX (without it, "blink now" prompts can't progress automatically). Customization moved into LivenessConfig: adjust allowedChallenges and challengeCount to control which detectors run.
Integrators that want a server-only flow with no on-device gating should build their own capture UI and call client.livenessCheckImage(File) directly. The widget's value is the on-device coupling — there's no point owning the widget if you're going to bypass that layer.
Footprint
The ML Kit dependency adds ~10 MB to the APK regardless of which detectors are in allowedChallenges. It's a runtime gate, not a compile-time one.