Install
The Android SDK is published to Maven Central. Add it to your app module — the published POM carries its transitive dependencies, so they resolve automatically. Requires minSdk 24.
repositories {
mavenCentral()
}
dependencies {
implementation("io.github.miri-san-so:doorman-benny:0.1.0")
}app/build.gradle.kts
Two entry points
The SDK exposes two suspend functions, mirroring the web library. getDeviceId collects only the hardware-bound signals and returns the cross-surface hardware id — the same value the same physical device produces in a browser. getFingerprint runs the full pipeline and additionally returns the per-install composite fingerprint, the risk buckets, and the persistent device id.
Both are suspend functions and never throw — call them from a coroutine. On any uncaught error they return a well-formed result with the error attached.
import com.bennythedoorman.sdk.DoormanBenny
import kotlinx.coroutines.launch
lifecycleScope.launch {
// Cross-surface DEVICE id — hardware only, fast. Matches the web SDK on this device.
val device = DoormanBenny.getDeviceId(context)
println(device.id)
// Full result: composite fingerprint + risk + persistent id.
val fp = DoormanBenny.getFingerprint(context)
println(fp.fingerprint) // per-install composite
println(fp.hardwareFingerprint) // == device.id (cross-surface)
println(fp.persistentId?.id) // reinstall-persistent correlator
println(fp.automation.automationLikelihood) // low | medium | high
println(fp.consistency.spoofLikelihood) // low | medium | high
}Call from an Activity / Fragment coroutine scope.
What you get
hardwareFingerprint is the cross-surface device hash — use it to recognise the same physical device across a browser visit and your native app, with no manual identity stitching. fingerprint is the per-install composite. persistentId is a reinstall-stable correlator (seeded from the platform device identifier and cached in Keystore-encrypted storage) — a hint to accelerate matching, not an authoritative identity. The consistency and automation blocks carry the mobile risk signals, including tamper and emulator detection, surfaced as low / medium / high likelihoods.
Which signals the Android SDK collects
The native SDK runs the hardware-bound collectors plus the mobile-only sensor signal. The browser-engine signals (canvas, audio, WebGL, and the rest — they depend on browser APIs that don't exist in a native app) return absent on Android and contribute the absent sentinel to the composite fingerprint. The six hardware signals are what build the cross-surface hardwareFingerprint; sensor_calibration adds mobile-only entropy to the per-install composite only.
Per-signal coverage on Android
| Signal | Binding | Android |
|---|---|---|
| webgl_gpu_identity | hardware | collected |
| platform | hardware | collected |
| timezone | hardware | collected |
| fonts | hardware | collected |
| media_devices | hardware | collected |
| architecture | hardware | collected |
| sensor_calibration | hardware (mobile-only) | collected |
| audio | engine | absent |
| canvas | engine | absent |
| webgl_params | engine | absent |
| screen | engine | absent |
| math | engine | absent |
| css_probe | engine | absent |
| webgl_gpu_trace | engine | absent |
| webgpu | engine | absent |
| speech_voices | engine | absent |
| storage_quota | engine | absent |
| apple_pay | engine | absent |
| audio_base_latency | engine | absent |
| screen_frame | engine | absent |
| js_heap_size_limit | engine | absent |
| font_preferences | engine | absent |
| webgl_pixel_readback | engine | absent |
| user_agent_data | engine | absent |
| error_stack_engine | engine | absent |
| permissions | engine | absent |
| webrtc_sdp | engine | absent |
Hardware attestation (opt-in)
For tamper-resistant trust the SDK can produce hardware-backed attestation — Google Play Integrity plus an Android Keystore StrongBox key attestation. It is OFF by default and obtained through a dedicated call, never inside getFingerprint, because it is a slow, rate-limited operation and its inputs must come from your server to be replay-safe.
The returned payloads are opaque and are never part of any fingerprint hash — forward them to your backend for verification (Play Integrity token redemption and certificate-chain validation). Play Integrity additionally needs your Google Cloud project number.
import com.bennythedoorman.sdk.core.AttestationConfig
val attestation = DoormanBenny.getAttestation(
context,
nonce = serverNonce,
challenge = serverChallenge,
config = AttestationConfig(enabled = true, cloudProjectNumber = yourGcpProjectNumber),
)
// Send attestation.playIntegrity / attestation.strongBox to your backend to verify.Attestation is opt-in; nonce and challenge come from your server.
Where to go next
Read the Overview for how the two hashes differ and the three risk tracks. Read Fusion & cross-browser to understand how the cross-surface hardwareFingerprint is built. The iOS SDK is in preview — reach out for early access.
Last reviewed 2026-06-18

