Docs/Getting Started/Android Quick Start

Android Quick Start

From zero to a YES verdict on an Android device in ten minutes.

Note

Work through Prerequisites first. You will need a registered app (or a sandbox token for local development) and a plan for runtime permissions.


1. Add the Maven repo

In your project's root settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("https://raw.githubusercontent.com/octetproof/octet-sdk-android/mvn-repo")
        }
    }
}

The OctetSDK Maven artifacts live on the mvn-repo orphan branch of the public octet-sdk-android repository.


2. Add the dependency

In your app build.gradle.kts:

dependencies {
    implementation("com.octetproof:sdk:2.0.0")
}

Pin 2.0.0 or later. 2.0 is a major release. If you are on 1.x, read Migrating from 1.x first.

Sync Gradle. The SDK's foreground permissions merge into your manifest automatically. For proofs while the app is backgrounded, add ACCESS_BACKGROUND_LOCATION to your own manifest. See Prerequisites.


3. Request runtime permissions

The SDK refuses to start without ACCESS_FINE_LOCATION. Motion-classification confidence degrades without ACTIVITY_RECOGNITION. Request both before calling Octet.start(...):

ActivityCompat.requestPermissions(
    this,
    arrayOf(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACTIVITY_RECOGNITION,
    ),
    REQUEST_CODE
)

Wait for onRequestPermissionsResult(...) to confirm ACCESS_FINE_LOCATION was granted before continuing.


4. Start the SDK

Octet.start(...) bootstraps the license by attesting the app with Android hardware key attestation, then brings up the proof pipeline. On a real device, no key or token is needed (see Prerequisites).

import com.octetproof.sdk.api.Octet
import com.octetproof.sdk.api.OctetConfig

lifecycleScope.launch {
    val config = OctetConfig(licenseKey = "")   // required by the type, ignored by start
    val sdk = Octet.start(context = applicationContext, config = config)
    // sdk is ready
}

On an emulator or a locally built debug build, there is no hardware attestation. Mint a sandbox bypass token (see Prerequisites) and pass it:

val config = OctetConfig(licenseKey = "", sandboxBypassToken = "octet_sbx_…")

Octet.start(...) is a suspend function. Any license or bootstrap problem throws a typed LicenseError. See License Types and Attested bootstrap.


5. Ask your first question

import com.octetproof.sdk.api.OctetRegion
import com.octetproof.sdk.api.OctetVerdict
import java.time.Instant

val verdict = sdk.loc.isWithin(
    region = OctetRegion.country("US"),
    atTime = Instant.now()
)

when (verdict.result) {
    OctetVerdict.Result.YES ->
        println("YES, proof attached: ${verdict.proof != null}")
    OctetVerdict.Result.NO ->
        println("NO, provable negative")
    OctetVerdict.Result.INDETERMINATE ->
        println("INDETERMINATE, reason: ${verdict.reason}")
}

The predicate returns an OctetVerdict. Never treat INDETERMINATE as NO.


6. Close when done

When your proof flow ends, release the session and stop the location foreground service:

sdk.close()

See Session Lifecycle for why this matters.


7. What to expect

  • On a real device, outdoors, with cellular and GPS available, isWithin(country("US")) typically returns YES with an attached proof.
  • On the Android emulator the verdict will always be INDETERMINATE / NO_FIX with the message running on emulator — location proofs are unavailable in this environment. This is by design. Run on hardware to see the full flow.
  • On a real device, indoors, the first proof on Android usually arrives quickly via the cell-tower MCC signal even without GPS. See Concepts: Verdicts.

8. From here