Troubleshooting¶
getClassification() always returns INSUFFICIENT or UNDETERMINED¶
Cause: One of the following:
- The user has not consented, or declined, so nothing is recorded.
- The session has fewer actions than the model asks for, 8 with the built-in model: the result is
INSUFFICIENT. See Evidence. - Capture was paused and never resumed.
- The SDK cannot score:
readyisfalseand the result isINSUFFICIENT. - The result is
UNDETERMINED: the score lies betweenbotThresholdandhumanThreshold.
Solution: Check each in turn:
import ai.dea.sdk.DeaSdk
import android.util.Log
val dea = DeaSdk.instance!!
Log.d("MyApp", "consent: ${dea.consent.decision}") // must be GRANTED
Log.d("MyApp", "ready: ${dea.ready}") // must be true
Log.d("MyApp", "fault: ${dea.coreFault}") // must be null
Log.d("MyApp", "answer: ${dea.getClassification()}") // INSUFFICIENT means fewer actions than the model asks for
Make sure every pause() has a matching resume().
getClassification() throws ThresholdMissing¶
Cause: The model the SDK loaded sets no bot threshold. It is a model file from before thresholds were part of the model, and the SDK has no other threshold to fall back on.
Solution: Catch the exception and treat the session as undetermined.
ThresholdMissing.modelVersion names the model that is loaded; send it to DEA support.
Nothing is logged after touching the screen with debug = true¶
Cause: Nothing is being recorded. Usually the user has not consented yet; otherwise capture is
paused, events excludes the input you are testing, or the touch was inside a dialog or popup
window, which the SDK does not capture.
Solution: Ask for consent — see User Consent. If
consent is granted, check for a pause() without a matching resume(), and check
events.
Debug lines are written to standard output. In Logcat they appear under the tag System.out,
prefixed [dea]; make sure your Logcat filter does not hide that tag.
Touches on the first screen are not captured¶
Cause: DeaSdk.init was called from an activity that was already showing, with a Context
other than that activity — for example applicationContext. The SDK finds the windows of
activities started after init, and of the activity passed to init, but not of others already
on screen.
Solution: Call DeaSdk.init in Application.onCreate(). If you must initialize from an
activity, pass the activity itself.
ready is false and coreFault says there is no core in this build¶
Cause: The SDK's native library is missing from your APK for the device's ABI — usually because
abiFilters, ABI splits or a packaging rule removed it.
Solution: Keep the ABIs your users' devices need, and do not exclude the SDK's .so files
(libdea_jni.so and libdea_core.so):
// app/build.gradle.kts
android {
defaultConfig {
ndk {
abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64")
}
}
}
The SDK ships arm64-v8a, armeabi-v7a, x86 and x86_64.
ready is false for another reason¶
Cause: The scoring engine packaged in your app is not the one the SDK expects. This should not
happen with a published release. It can happen when another dependency brings its own copy of the
SDK's native libraries and a packaging rule such as pickFirsts keeps that copy.
Solution: Make sure Gradle resolved ai.dea:dea-sdk and ai.dea:dea-base to the same
version, and that no other dependency bundles libdea_core.so:
If they match, send the coreFault text to DEA support.
A second DeaSdk.init call ignores its options¶
Cause: DeaSdk.init was called more than once. The first call's options stay in effect, and
later calls return the running instance without a warning.
Solution: Call DeaSdk.init once, in Application.onCreate(). If you really need different
options, call DeaSdk.instance?.dispose() first.
The user is asked for consent on every launch¶
Cause: The answer is not being stored. The SDK was started with consent = Consent(), the
answer was recorded with remember = false, or your app asks without checking
consent.isAnswered first.
Solution: Leave the consent option at its default and
remember at true unless the device is shared, and only ask when
DeaSdk.instance?.consent?.isAnswered == false. DeaConsentDialog.show does that check for you.
attest() returns FAILED with NetworkOnMainThreadException in detail¶
Cause: attest() was called on the main thread. It makes network requests and blocks.
Solution: Call it from a background thread:
The same applies to renewAttestation().
attest() returns UNSUPPORTED¶
Cause: One of the following:
- No
appKeywas passed toDeaSdk.init.attestation.detailthen says so, and no request was made. - No signing key could be created: the device runs Android 5.x (API 21 or 22), which the SDK does not create keys on, or its keystore refused.
Solution: Pass the appKey DEA issued with your appId — see
Options: appKey. Otherwise, test on a device running
Android 6.0 or later.
attest() passes but platformAttested is false¶
Cause: No Play Integrity token went with the registration. This is expected on emulators without Google Play, on devices without Google Play services, and when Play refuses the request — most often because the build was not installed from Google Play or signed with a key registered in Play Console.
Solution: Test on a real device with a build distributed through Google Play — for example an internal testing track.
attest() returns FAILED and detail says attestation is paused¶
Cause: An earlier registration that used a Play Integrity token failed, and the SDK is waiting before it tries again: one hour after the first failure, doubling up to seven days. The wait survives app restarts.
Solution: Fix what made the earlier attempt fail, then call forgetAttestation() to end the
wait, and attest() again.
attest() returns FAILED¶
Cause: DEA refused the registration, or could not be reached.
Solution: Read attestation.detail. It carries DEA's response, or the network error. Also
check that playIntegrityProject is the project number DEA issued for your app.