License setup — Android
You added co.deepvoiceai:dvai-bridge to your Gradle build. You want to ship a release APK or AAB. Here's the licensing path.
TL;DR
Drop dvai-license.jwt into your app's src/main/assets/ folder. At DVAIBridge.start(...), the SDK reads it from the APK assets, verifies the ES256 signature offline, and unlocks production behaviour. debug build variants ignore license problems.
Where the file goes
Add the license file as an Android asset:
app/
src/
main/
assets/
dvai-license.jwtIt ships inside the APK — accessible via context.assets.open(...).
Alternative locations the SDK also checks — in priority order:
- Inline JWT passed to
StartOptions(licenseToken = "...")— useful when the token comes from your account flow at runtime. - A path passed to
StartOptions(licenseKeyPath = ...)— e.g. a file your app downloaded intoContext.filesDir. assets/dvai-license.jwt— auto-discovered.
Code: with vs. without
Default — license bundled in assets/:
import co.deepvoiceai.bridge.DVAIBridge
import co.deepvoiceai.bridge.StartOptions
val bound = DVAIBridge.start(StartOptions(
backend = BackendKind.Llama,
modelPath = modelFile.absolutePath
))
println(bound.baseUrl) // http://127.0.0.1:38883/v1
println(bound.licenseStatus) // LicenseStatus.Commercial(licensee = "Acme", ...)Inline JWT — downloaded at runtime, stored in EncryptedSharedPreferences:
val token = encryptedPrefs.getString("dvai_license_jwt", null)!!
val bound = DVAIBridge.start(StartOptions(
backend = BackendKind.Llama,
modelPath = modelFile.absolutePath,
licenseToken = token
))Explicit file path:
val licensePath = File(context.filesDir, "dvai-license.jwt").absolutePath
val bound = DVAIBridge.start(StartOptions(
backend = BackendKind.Llama,
modelPath = modelFile.absolutePath,
licenseKeyPath = licensePath
))Native license fields land in v3.3
In v3.2.x, the Android SDK ships without licenseToken / licenseKeyPath on StartOptions. The Capacitor-wrapped path runs the JS validator automatically; pure-native Kotlin apps in v3.2 ship under a "dev preview" allowance. Native Android validation arrives in v3.3 — track the milestone for the release date and pin to v3.3+ to enforce on Android.
What happens without a license
In release build variants, start(...) throws DVAIBridgeError.LicenseRequired with a verbose message:
try {
val bound = DVAIBridge.start(...)
} catch (e: DVAIBridgeError.LicenseRequired) {
// e.message is a multi-line string with resolution steps.
Log.e("dvai", e.message ?: "")
showSnackbar("License required: ${e.shortReason}")
}Testing locally without a license
debug build variants skip license checks automatically. The SDK detects debug mode via BuildConfig.DEBUG and ApplicationInfo.FLAG_DEBUGGABLE.
Force dev mode explicitly:
System.setProperty("DVAI_FORCE_DEV", "1")Rehearse the release code path inside a debug build:
System.setProperty("DVAI_FORCE_PROD", "1")When validation fails
| Error reason fragment | What's wrong | Fix |
|---|---|---|
asset not found | dvai-license.jwt isn't in src/main/assets/ | Drop it in, rebuild |
signature did not verify | Token tampered with or wrong key | Re-download from your licensor |
does not authorise platform "android" | License missing "android" in platforms claim | Re-issue covering Android |
audience entries ... do not match | Package name doesn't match aud entries | Re-issue with your applicationId, or use a wildcard pattern |
expired | Past exp | Renew |
The runtime audience on Android is your application's packageName — e.g. com.acme.app. License templates typically ship both your package name and a "*" fallback for trials.
See also
- License setup index
- Pre-init inspection — run
LicenseValidatorstandalone for a settings-screen license status withoutDVAIBridge.start(). - Android Native SDK — full SDK reference.
- Capacitor — if you ship via Capacitor instead of native Kotlin.
