On-device face detection
The liveness widget runs Google ML Kit face detection on every preview frame. It is a UX layer: it tells the user when they are aligned with the oval, shows when the requested action has been seen so the prompt can advance, and decides which camera frames are worth uploading. The verdict is always the server's.
What runs on-device
- ML Kit face detection in fast mode, with classification enabled for eye-open probabilities.
- Alignment against the on-screen oval before the challenge starts.
- One detector for the action the server issued: blink (eye-open probability) or head turn (head Euler angle, 15 degrees or more).
- Frame selection: a neutral baseline when the challenge starts, a frame the moment the detector sees the action in progress, a periodic fallback, and one frame after the action completes, up to ten in total.
What runs on the server
- The challenge itself:
POST /v1/liveness/challengespicks the action; the app cannot choose it. - The action verification, from the uploaded frames' landmarks.
- The anti-spoof models and the temporal checks across frames.
- All scoring:
is_live,score,thresholdandreasoncome from/v1/liveness/check, never from the device.
If the on-device detector accepts an action but the server does not, the
server wins, and result.isLive reflects the server's decision. Since
2.5.0 the widget re-runs the challenge once with a fresh token when the
server's reason is one the user can fix by trying again.
Why there is no enable / disable toggle
On-device detection cannot be turned off: without it, the "blink now"
prompt could not advance and the widget would not know which frames to
upload. Integrators that want a fully server-driven flow should build their
own capture UI and call the EkycClient liveness helpers directly, following
the capture rules in Liveness checks.
Footprint
The ML Kit dependency adds about 10 MB to the APK.