Skip to content

API Reference

import ai.dea.sdk.ClassificationResult
import ai.dea.sdk.DeaConfig
import ai.dea.sdk.DeaSdk

Start the SDK with DeaSdk.init(context, config), then reach it anywhere with DeaSdk.instance. Types that come from the companion library (Consent, SessionInfo, CaptureEvent, …) are in the ai.dea.base package; attestation types are in ai.dea.sdk.attest, proof types in ai.dea.sdk.proof, and model types in ai.dea.sdk.model.

DeaSdk

init

fun init(
    context: Context,
    config: DeaConfig = DeaConfig(),
    targets: TargetReader = ViewTargetReader(),
): DeaSdk

Starts the SDK and capture. Returns the running instance if one exists. Never throws. Call it on the main thread, and pass your Application: the SDK keeps the Context it is given. See Options.

instance

val instance: DeaSdk?

The running instance, or null before init and after dispose().

ready

val ready: Boolean

Whether the SDK is able to score. When false, capture continues, getClassification() answers INSUFFICIENT, and coreFault says why.

coreFault

val coreFault: String?

Why the SDK cannot score, or null.

getClassification

fun getClassification(): Classification

Whether the session reads as a person, a bot, or neither yet, with the score and the model's thresholds behind the answer. One snapshot: it covers every interaction so far, and its fields always agree with each other. Synchronous; safe on the main thread. See Human Scoring.

Throws: ThresholdMissing when the loaded model sets no bot threshold. When the SDK cannot score (ready is false) it does not throw; it answers INSUFFICIENT with a null value.

val consent: Consent

The user's current consent answer. See User Consent.

setConsent

fun setConsent(granted: Boolean, remember: Boolean = true)
Parameter Type Description
granted Boolean Whether the user agreed
remember Boolean false keeps the answer for this launch only and stores nothing

Takes effect immediately. Withdrawing discards anything captured but not yet processed. The session is kept either way.

pause

fun pause()

Stops recording. The session is kept. See Sensitive Screens.

resume

fun resume()

Starts recording again after pause(). Does not override consent.

captureEvent

fun captureEvent(name: String, data: Map<String, Any?>? = null): Boolean
Parameter Type Description
name String 1 to 64 characters; otherwise the event is dropped
data Map<String, Any?>? Optional; at most 1024 bytes as JSON, or the event is dropped

Returns: Boolean — true when the event was recorded.

See Custom Events.

captureKey

fun captureKey(
    keyClass: KeyClass,
    pressed: Boolean = true,
    mods: Int = 0,
    repeat: Boolean = false,
    field: String? = null,
)
Parameter Type Description
keyClass KeyClass Which kind of key: KeyClass.of(character) turns a typed character into one
pressed Boolean false reports a release. A soft keyboard has none to report; send the press alone
mods Int Modifier keys held, as KeyMod bits; 0 on a soft keyboard
repeat Boolean Whether the press is a key repeat
field String? The name of the text field typed into, as for a view's target name

Reports one key press that your app saw. The SDK sees no key events of its own, so key presses are recorded only when your app reports them, and only their class — never the character. Records nothing unless events includes CaptureEvent.KEY_DOWN (and CaptureEvent.KEY_UP for releases), and nothing while capture is paused or before consent. Never call it for a password field.

setMotion

fun setMotion(on: Boolean)
val motion: Boolean

Switches the motion sensors off, or back on, for the running app. While off, the SDK reads no motion data and sessionInfo.device.sensorRate is 0; touch capture and the session continue. motion says whether the sensors are on.

reset

fun reset()

Ends the current session and starts a new one. See Sessions.

sessionInfo

val sessionInfo: SessionInfo

The current session id, device and SDK version.

modelInfo

val modelInfo: ModelInfo

Which model is scoring: its version (0 for the built-in model) and its source: EMBEDDED, CACHE (downloaded at an earlier launch) or NETWORK (downloaded during this run). See Model options.

checkForModel

fun checkForModel(force: Boolean = false): ModelCheck

Checks DEA's model host now, if a day has passed since the last check; force = true checks anyway. The SDK already checks at every launch and on returning to the foreground, so most apps never call this. Never throws. Blocking — call it off the main thread. Answers with ModelCheckOutcome.CANCELLED when model updates are off.

addModelListener

fun addModelListener(listener: (ModelInfo) -> Unit): () -> Unit

Calls listener each time a newer model starts scoring during this run, on a background thread. Returns the unsubscribe.

attest

fun attest(): Attestation

Registers the device key with DEA and turns on automatic renewal. Cached until near expiry. Never throws. Blocking — call it off the main thread. Answers UNSUPPORTED without a request when no appKey was given. See Attest.

renewAttestation

fun renewAttestation(): Attestation

Registers again now. A failure keeps the registration already held. Blocking.

lastAttestation

val lastAttestation: Attestation?

The last attestation result, or null before the first attest().

storedAttestation

fun storedAttestation(): Attestation?

The registration this install already holds, if it is still fresh, read from storage without a request; null otherwise. A fresh one turns on automatic renewal.

forgetAttestation

fun forgetAttestation()

Forgets the stored registration and any wait after refused attempts, and stops automatic renewal. The device key is kept.

nextAttestationRefresh

val nextAttestationRefresh: Long?

When the registration will next be renewed automatically, in epoch seconds.

onAttestationEvent

var onAttestationEvent: ((AttestRefresher.Event) -> Unit)?

Called on every automatic renewal decision: Scheduled(afterSeconds), Fired(verdict) or Idle. Set it before the first attest().

Ads

Android has no ad session object. These three calls each sign one ad event — the session's current score, signed with the device key — and send it or return it. For DEA to accept a proof, pass appKey, set registryAppId to your appId, and register the device key with attest(). See Ads.

sendProof

fun sendProof(
    event: ProofEvent,
    sessionId: String? = null,
    attestation: Attestation? = null,
    adUnit: String? = null,
    appScopedUid: String? = null,
    extra: Map<String, Any?> = emptyMap(),
    update: Boolean = false,
): RecordOutcome
Parameter Type Description
event ProofEvent The ad event
sessionId String? The ad session's id, signed into the proof. "" for an impression, so DEA opens the session and answers with its id; that id for the click and the end. null signs the capture session's id (sessionInfo.session), which is not an ad session's id
attestation Attestation? Sets the tier claim only: B for a PASSED registration that carried a Play Integrity token and has not expired, C otherwise. null takes the registration the SDK holds from attest()
adUnit String? The ad unit, signed into the proof. Pass the same one for an ad's three events
appScopedUid String? Signed into the proof as app_scoped_uid; left out when null
extra Map<String, Any?> Unsigned fields sent in the request body beside the proof. They never replace the proof or the SDK version
update Boolean true sends the proof as an update of the ad session sessionId names, PUT /api/sessions/{sessionId}, rather than POST /api/sessions. Use it for AD_ENDED

Fetches a challenge from DEA, signs the event, and sends the proof. The proof carries the score the scoring engine holds, when its evidence ends, how many interactions are behind it, and the model version; your app cannot set those. Its appId is registryAppId, or the app id DEA's challenge names when it names one. If the challenge fails, the proof is signed and sent without DEA's nonce.

When DEA cannot be reached or answers 5xx, the proof is sent again after 2 seconds, doubling to 30, plus up to 1 second: at most five sends in all, and none after the proof expires 90 seconds after signing. A 4xx is final. Blocking through the retries — call it off the main thread.

Refused without a request when no appKey was given. Signs nothing when the device has no key, when the scoring engine is not running, or when the session is not sufficient yet (getClassification().result is INSUFFICIENT): an impression declared then is not recorded. The click is not checked against the user's touches.

Returns: RecordOutcome — whether DEA accepted the proof, the proof, and what DEA or the SDK said. See Ads: Find out why a proof was not accepted.

record

fun record(
    event: ProofEvent,
    sessionId: String? = null,
    attestation: Attestation? = null,
    adUnit: String? = null,
    appScopedUid: String? = null,
    extra: Map<String, Any?> = emptyMap(),
    update: Boolean = false,
): Boolean

The same send as sendProof, with the same parameters. Blocking.

Returns: Boolean — true when DEA accepted the proof. To read DEA's session id after an impression, use sendProof.

getProof

fun getProof(
    event: ProofEvent,
    sessionId: String? = null,
    attestation: Attestation? = null,
    adUnit: String? = null,
    appScopedUid: String? = null,
): SignedProof?

Signs one event as sendProof does, and returns the proof without sending it, for an app that delivers proofs itself. The parameters are sendProof's. Blocking: it fetches the challenge. Not refused without appKey.

Returns: SignedProof? — the proof, or null when nothing was signed: no device key, the scoring engine is not running, or not enough evidence yet. No reason is given; sendProof's said gives it.

dispose

fun dispose()

Stops capture, drops anything not yet processed, and releases the SDK.

DeaConsentDialog

show

fun show(
    activity: Activity,
    sdk: DeaSdk? = DeaSdk.instance,
    copy: ConsentCopy = ConsentCopy(),
    remember: Boolean = true,
    ask: Boolean = true,
    onResult: (Boolean) -> Unit = {},
)
Parameter Type Description
activity Activity A showing activity to attach the dialog to
sdk DeaSdk? The SDK to record the answer on
copy ConsentCopy The dialog's text
remember Boolean false clears the stored answer, asks on every call, and keeps each answer for this launch only
ask Boolean false reads the stored answer without showing anything
onResult (Boolean) -> Unit Called on the main thread with the answer

Shows the consent dialog unless an answer is stored, and records the answer. Call it on the main thread. See User Consent.

Functions

View.deaTarget

fun View.deaTarget(name: String? = null, bucket: TargetBucket? = null)

Declares what a view is: its name, and which kind of control it is. Overrides what the SDK would infer. See Options: targets.

Types

DeaConfig

See Options. The proofs of the Ads calls also read registryAppId: the appId signed inside each proof. Set it to your appId; left null, proofs carry a development id that DEA refuses.

Classification

Field Type Description
result ClassificationResult The answer
value Double? The session's score, 0 to 1; null exactly when result is INSUFFICIENT
actions Int Interactions behind value; the result stays INSUFFICIENT until there are as many as the loaded model asks for, 8 for the built-in model
botThreshold Double? Below this, a sufficient session is a bot. The loaded model's; null when the SDK cannot score
humanThreshold Double? At or above this, a sufficient session is a person. The loaded model's; null when the model sets none
modelVersion Int? The model the thresholds come from, 0 for the built-in one; null when the SDK cannot score

The thresholds come from the loaded model only. No account setting and no built-in constant changes them.

ClassificationResult

Checked in this order:

Value When
INSUFFICIENT No scoring engine, or fewer actions than the loaded model asks for. value is null
HUMAN value at or above humanThreshold
BOT value below botThreshold
HUMAN No humanThreshold, and value at or above botThreshold
UNDETERMINED value between the two thresholds

Migrating to this release: Classification.sufficient is gone, and ClassificationResult has a fourth value.

Before This release
c.sufficient == false c.result == ClassificationResult.INSUFFICIENT
c.value while not sufficient: a number null
ClassificationResult has three values Four: handle INSUFFICIENT in every exhaustive when
No humanThreshold, value at or above botThreshold: UNDETERMINED HUMAN

ThresholdMissing

class ThresholdMissing(val modelVersion: Int) : Exception

Thrown by getClassification() when the loaded model sets no bot threshold — a model file older than thresholds. modelVersion names it. There is no fallback threshold: catch it and treat the session as undetermined.

Member Type Description
decision ConsentDecision UNKNOWN, GRANTED or DENIED
isAnswered Boolean Whether the user has answered
isGranted Boolean Whether the user agreed
forget() Unit Clears the stored answer so the user is asked again
onChange(listener) () -> Unit Calls listener with the new decision when it changes; returns the unsubscribe
Consent() constructor An answer kept for this launch only
Consent(store) constructor An answer kept in your own ConsentStore
Consent.granted() Consent An answer that is already yes

ConsentStore

interface ConsentStore {
    fun read(): ConsentDecision
    fun write(decision: ConsentDecision)
}

Where a Consent keeps its answer. write(ConsentDecision.UNKNOWN) means the answer was forgotten.

ConsentCopy

The consent dialog's text: title, body, recorded, notRecorded, recordedLabel, notRecordedLabel, closing, accept, decline. Every field has an English default. See User Consent.

CaptureEvent

POINTER, SCROLL, MOTION, LIFECYCLE, CUSTOM, KEY_DOWN, KEY_UP. The default, CAPTURE_EVENTS, is the first five. See Options: events.

KeyClass

LETTER, DIGIT, SPACE, DELETE, NAVIGATION, MODIFIER, OTHER (punctuation, Enter and anything else). KeyClass.of(char: String) classifies a typed character as LETTER, DIGIT, SPACE or OTHER.

TargetBucket

UNKNOWN, BUTTON, LINK, INPUT, SCROLLER, CANVAS, BACKGROUND.

SessionInfo

Field Type Description
session String Current session id, a UUID
device DeviceRecord Current device information
sdk.version String SDK version
sdk.core String Which scoring engine build is running; none when the SDK cannot score
startedAt Long When the session started, Unix milliseconds

DeviceRecord

Field Type Description
surface Surface Screen size in dp (width, height)
pixelRatio Double Physical pixels per dp
rotation Int Screen rotation: 0, 90, 180 or 270
maxTouchPoints Int How many touches the screen declares it can follow at once
sensorRate Int The requested motion rate, in Hz, once motion data has arrived; 0 before that and while motion is off
os Int Operating system code
runtime Int SDK runtime code

ModelInfo

Field Type Description
version Int The model's version; 0 for the built-in model
source ModelSource EMBEDDED, CACHE or NETWORK

ModelCheck

Field Type Description
outcome ModelCheckOutcome NOT_DUE, UNCHANGED, NOT_PUBLISHED, NOT_SUPPORTED, LOADED, REFUSED, FAILED or CANCELLED
detail String? What went wrong, when something did

Attestation

Field Type Description
supported Boolean Whether the SDK could register: false when no key could be created, or no appKey was given
verdict Verdict PASSED, FAILED or UNSUPPORTED
platformAttested Boolean Whether a Play Integrity token went with an accepted registration
platformTokenSpent Boolean Whether this attempt obtained a Play Integrity token, whatever DEA answered
integrity String? DEA's reading of the device's integrity, when DEA returned one
expiresAt Long? Expiry, epoch seconds
kid String? Key identifier
tier String? Trust tier assigned by DEA
backing String? Where the key is kept: strongbox, tee or software
detail String? Why the SDK did not register, or what DEA or the network answered when it failed

ProofEvent

Value wire The event
AD_IMPRESSION ad.impression The ad is on screen
AD_CLICK ad.click The ad was clicked
AD_ENDED ad.ended The ad is gone

wire is the event_type signed into the proof. There are no other values.

RecordOutcome

Field Type Description
accepted Boolean Whether DEA accepted the proof
signed SignedProof? The proof that was signed, or null when nothing was
isSigned Boolean Whether signed is not null
said String? DEA's answer as it came, or why DEA was never reached or did not accept
sessionId String? The session_id in DEA's answer, or null when it named none. After an impression, the ad session's id: pass it to the click and the end

SignedProof

Member Type Description
proof String The compact JWS, header.payload.signature, signed with the device key (ES256)
score Double The score signed into the proof, 0 to 1
evidenceAt Long When the evidence behind score ends, epoch seconds
windows Int How many scored interactions are behind score
sessionId String The session_id signed into the proof
event ProofEvent The event signed into the proof
modelVersion Int? The version of the model that produced score, 0 for the built-in one; null when the proof carries none
composedBy ProofSource Who composed what was signed: CORE, the scoring engine, for every proof DeaSdk signs
composedBytes Int? How many bytes were signed
header JSONObject? The signed header, decoded: alg, typ, kid
claims JSONObject? The signed claims, decoded
signature String? The signature segment, base64url

score, evidenceAt, windows and modelVersion are read back out of the signed claims.