iLive Docs
SDK Reference

Android SDK Reference

Complete public API surface for the iLive Android SDK.

Android SDK Reference

This page documents the public API of the iLive Android SDK: entry points, configuration, result types, and face comparison helpers. For a step-by-step integration walkthrough, see the Android quickstart.

Installation

The SDK ships as two Gradle modules — ilive-core (engine, configuration, results) and ilive-ui (drop-in LivenessActivity). Include whichever you need:

// app/build.gradle.kts
dependencies {
    implementation(project(":ilive-core"))
    implementation(project(":ilive-ui")) // optional — only for the drop-in UI
}

Minimum Android API is 26 (Android 8.0). See the quickstart for full Gradle setup and model asset placement.

Entry points

There are two ways to run a liveness session:

  1. Drop-in UILivenessActivity from ilive-ui. Launches a full-screen flow with camera, guidance, challenges, and a result screen. Fastest path to integration.
  2. Custom UILivenessEngine from ilive-core. You drive the camera and UI; the engine consumes frames and returns a verdict. Use when you need your own design or camera stack.

Top-level helpers live on the ILive object:

MemberSignaturePurpose
versionconst val StringSDK version string.
createEnginesuspend fun createEngine(context, config = default): LivenessEngineFactory for the custom-UI engine.
isDeviceSupportedfun isDeviceSupported(context): DeviceSupportResultChecks OS version and camera availability.
compareFacesfun compareFaces(context, referencePhoto, probePhoto, threshold = 0.45f): MatchResult1:1 comparison of two JPEGs.
compareFaceWithEmbeddingfun compareFaceWithEmbedding(context, referenceEmbedding, probePhoto, threshold = 0.45f): MatchResultCompare a stored embedding against a new JPEG.

Drop-in UI: LivenessActivity

com.ilive.sdk.ui.LivenessActivity is a ComponentActivity. The recommended way to launch it is with LivenessContract, a type-safe ActivityResultContract that returns a sealed LivenessOutcome.

import com.ilive.sdk.ui.LivenessContract
import com.ilive.sdk.ui.LivenessOutcome
 
private val launcher = registerForActivityResult(LivenessContract()) { outcome ->
    when (outcome) {
        is LivenessOutcome.Success   -> handleResult(outcome.result)   // full LivenessResult
        is LivenessOutcome.Error     -> show(outcome.message)
        LivenessOutcome.Cancelled    -> /* user backed out */ Unit
    }
}
 
launcher.launch(
    LivenessContract.Input(
        config = ILiveConfig.Builder().build(),  // optional
        passiveMode = true,
    )
)

LivenessContract.Input fields:

FieldTypeDefaultMeaning
configILiveConfig?null (SDK defaults)Configuration for the session.
passiveModeBooleanfalsetrue runs without user challenges; false runs the challenge sequence.

LivenessOutcome variants:

VariantWhen it firesPayload
Success(result)Activity finished with a verdict.Full LivenessResult Parcelable.
CancelledUser backed out / RESULT_CANCELED.
Error(message)Activity reported a failure or delivered no result.Human-readable String.

The activity also places the LivenessResult in the result Intent under LivenessActivity.EXTRA_RESULT as a Parcelable extra, and the following lightweight summary extras:

Intent extraKey constantType
Verdict string (PASS/FAIL/RETRY)LivenessActivity.VERDICT_KEYString
Session idLivenessActivity.SESSION_ID_KEYString
ConfidenceLivenessActivity.CONFIDENCE_KEYFloat

Legacy launch path (deprecated)

LivenessActivity.pendingResult is deprecated and will be removed in a future release. Use LivenessContract instead — it is race-safe across activity instances and delivers the result as a Parcelable intent extra.

Pre-existing integrations may still launch the activity with ActivityResultContracts.StartActivityForResult and read the static LivenessActivity.pendingResult holder. This path keeps working for one release cycle:

private val launcher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { activityResult ->
    @Suppress("DEPRECATION")
    val result = LivenessActivity.pendingResult
    @Suppress("DEPRECATION")
    LivenessActivity.pendingResult = null
    if (result != null && result.verdict == Verdict.PASS) {
        // result.icaoPhoto, result.faceEmbedding, result.attestation
    }
}
 
launcher.launch(Intent(this, LivenessActivity::class.java).apply {
    putExtra(LivenessActivity.EXTRA_PASSIVE_MODE, true)
})

Custom UI: LivenessEngine

com.ilive.sdk.LivenessEngine exposes the full capture pipeline without a bundled UI.

MethodSignatureNotes
Createsuspend LivenessEngine.create(context, config): LivenessEnginePrefer ILive.createEngine(...).
Initializesuspend fun initialize()Loads on-device models. Call once before processFrame.
Process framefun processFrame(bitmap: Bitmap): TrackingResultFeed every preview frame. Returns face detection + landmarks.
Start challengesfun startChallenges(listener: ChallengeEventListener)Kicks off the interactive challenge sequence.
Evaluate (active)suspend fun evaluateVerdict(): LivenessResultCall after AllComplete.
Evaluate (passive)suspend fun evaluatePassiveVerdict(): LivenessResultSkips challenges; scores purely from captured frames.
Releasefun release()Free resources. Idempotent.
sessionIdStringUUID for this session.
currentStageLivenessStageINITIALIZING, READY, CHALLENGES, PROCESSING, RESULT.
val engine = ILive.createEngine(context, config)
engine.initialize()
 
// For each preview Bitmap:
val tracking = engine.processFrame(bitmap)
 
// Passive flow:
val result = engine.evaluatePassiveVerdict()
engine.release()

Configuration: ILiveConfig

ILiveConfig is built via ILiveConfig.Builder(). All fields are optional; defaults match the values shipped in the SDK.

Challenges and verdict thresholds

FieldTypeDefaultDescription
challengeCountInt (3–6)3Number of challenges run in active mode.
challengeTypesSet<ChallengeType>all 8Pool of challenges to draw from. Must contain at least challengeCount entries.
challengeTimeoutSecondsInt (4–15)8Per-challenge timeout.
transitionDelayMsInt (300–3000)500Pause between challenges.
maxRetriesPerChallengeInt (0–2)1In-session retry count for a failed challenge.
passThresholdFloat0.70Minimum confidence for PASS.
retryThresholdFloat0.45Minimum confidence for RETRY (must be less than passThreshold).
antispoofFloorFloat0.30Anti-spoof layer veto floor.
deepfakeFloorFloat0.20Deepfake layer veto floor.
layerWeightsLayerWeightsbalancedPer-layer weight vector. Normalized automatically.

ChallengeType values: BLINK, TURN_LEFT, TURN_RIGHT, NOD, SMILE, MOUTH_OPEN, EYEBROW_RAISE, EYE_FOLLOW.

Voice prompts

FieldTypeDefaultDescription
voicePromptsEnabledBooleanfalseSpeak challenge instructions.
voiceRateFloat1.0Speech rate multiplier.
voiceLanguageString"en-US"BCP-47 locale tag.

Photo extraction

FieldTypeDefaultDescription
photoExtractionEnabledBooleantrueProduce an ICAO-style still on PASS.
photoWidthInt480Output width in pixels.
photoHeightInt600Output height in pixels.
photoJpegQualityInt (0–100)95JPEG quality.

Security

FieldTypeDefaultDescription
attestationKeyBase64String?nullBase64-encoded HMAC key used to sign the result payload. Canonical form across all SDKs. When null, no attestation is produced.
attestationKeyByteArray? (deprecated)nullDeprecated raw-bytes setter, retained for one release. Prefer attestationKeyBase64.
frameBundleEncryptionKeyByteArray? (exactly 32 bytes)nullAES key used to encrypt the captured frame bundle.
frameBundleFrameCountInt (4–16)8Number of frames included in the encrypted bundle.

Timeouts

FieldTypeDefault
modelLoadTimeoutSecondsInt10
cameraInitTimeoutSecondsInt5
totalSessionTimeoutSecondsInt120
val config = ILiveConfig.Builder()
    .challengeCount(4)
    .passThreshold(0.75f)
    .voicePromptsEnabled(true)
    .attestationKeyBase64("MTIzNDU2Nzg5MDEyMzQ1Ng==")
    .build()

Results: LivenessResult

Returned by evaluateVerdict() and evaluatePassiveVerdict().

FieldTypeDescription
sessionIdStringUUID assigned when the engine was created.
verdictVerdictPASS, FAIL, or RETRY.
confidenceFloatWeighted aggregate confidence (0.0–1.0).
layerScoresList<LayerScore>Per-layer breakdown (anti-spoof, challenge, face-consistency, deepfake, motion).
retryHintString?User-facing guidance when verdict == RETRY.
failureReasonString?Diagnostic reason when verdict == FAIL.
icaoPhotoByteArray?JPEG still of the subject, present on PASS when photo extraction is enabled.
photoQualityScoreFloat?Quality rating for icaoPhoto (0.0–1.0).
faceEmbeddingFloatArray?512-dimensional face embedding from the best frame. Reusable with compareFaceWithEmbedding.
attestationAttestation?Signed payload (payload, signature, algorithm) when attestationKeyBase64 was set.
encryptedFrameBundleFrameBundle?Encrypted captured frames (ciphertext, iv, frameCount, keyId) when a frame-bundle key was set.
metadataSessionMetadataDuration, challenge timings, device model, OS version, SDK version.

Face recognition: ILive.compareFaces()

One-shot 1:1 face comparison. Loads the face-matching model, runs the comparison, and releases it.

fun compareFaces(
    context: Context,
    referencePhoto: ByteArray,   // JPEG bytes
    probePhoto: ByteArray,       // JPEG bytes
    threshold: Float = 0.45f
): FaceRecognition.MatchResult
 
fun compareFaceWithEmbedding(
    context: Context,
    referenceEmbedding: FloatArray, // 512 floats, e.g. from LivenessResult.faceEmbedding
    probePhoto: ByteArray,
    threshold: Float = 0.45f
): FaceRecognition.MatchResult

MatchResult fields: similarity: Float (cosine similarity, 0.0–1.0), isMatch: Boolean (similarity >= threshold), threshold: Float.

// Compare an ID document photo against the captured liveness photo.
val match = ILive.compareFaces(
    context = this,
    referencePhoto = idDocumentJpeg,
    probePhoto = livenessResult.icaoPhoto!!
)
 
// Later: compare the stored embedding against a new selfie.
val match2 = ILive.compareFaceWithEmbedding(
    context = this,
    referenceEmbedding = livenessResult.faceEmbedding!!,
    probePhoto = newSelfieJpeg
)

Error handling

All SDK-raised exceptions extend com.ilive.sdk.common.ILiveError:

ErrorWhen it fires
CameraInitFailedCamera could not be opened.
CameraPermissionDeniedandroid.permission.CAMERA not granted.
ModelLoadFailed(modelName)A model asset failed to load.
ModelChecksumMismatch(modelName, expected, actual)Shipped model asset was tampered with.
UnknownModel(modelName)Model name not recognized.
InferenceFailed(modelName)A model returned an error during processing.
SessionTimeout(stage)Session exceeded the timeout for the given stage.
NoFaceDetectedNo face found in any captured frame.
InvalidConfiguration(detail)ILiveConfig.Builder.build() rejected the configuration.
EngineNotInitializedprocessFrame/evaluate* called before initialize().
EngineAlreadyReleasedEngine method called after release().
InternalError(detail)Unexpected internal failure.

See also