Integrate the tenant-delivered Android AAR, activate a device-bound signed license, and submit stride-safe CameraX frames.
Status
Evaluation
Integration
Embedded SDK
Distribution
Tenant download grant
Version
0.1.0-RC4
The Kotlin SDK path stays focused on the customer workflow: capture a barcode, process it with Vision Scan, and render a verified result.
Prerequisites
Android API 26 or newer
JDK 17
An authenticated tenant and activated device
A tenant download grant for the RC2 evaluation ZIP
1. Install from the granted Maven repository
Install from the granted Maven repository
// settings.gradle.kts: repository extracted from the granted ZIP
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("sdk-release/maven") }
}
}
// app/build.gradle.kts
dependencies {
implementation("com.visionscan.sdk:vision-scan-android-sdk:0.1.0-RC4")
}
Compare the complete ZIP SHA-256 with the authenticated catalog, then extract its Maven repository. Keep the bundled OpenCV wrapper and POM dependencies; the SDK AAR alone is incomplete. Server HMAC signing keys are never customer inputs.
2. Apply the backend-signed license
Apply the backend-signed license
// Worker: IDs come from trusted authenticated enrollment, not user entry.
VisionScanSdk.initializeLeaseStore(noBackupFilesDir, deviceId)
// Requires the previously enrolled hardware installation key.
VisionScanSdk.initializeHardwareBinding(applicationContext)
val result = VisionScanSdk.applySignedLicenseJws(
jws = signedLicenseJws,
expectedTenantId = tenantId,
expectedDeviceId = deviceId
)
check(result.isAllowed) { result.denialReason ?: "License denied" }
Resolve tenantId from the authenticated tenant and deviceId from device activation. Enroll the SDK hardware proof with a server-issued challenge first; then fetch the bound JWS and trusted JWKS through authenticated control-plane networking. Signed claims are verified against this enrollment context, never treated as self-authorizing decoded data. Encrypted-key-only initialization is not an RC2 API.
3. Create a live session
Create a live session
VisionScanSdk.createSession(tenantId, siteId, ScanMode.LIVE_STREAM).use { session ->
val yPlane = YPlaneExtractor.extractYPlane(imageProxy)
val result = session.processFrame(imageProxy.width, imageProxy.height, 1, yPlane)
val observations = result.observations
}
Close each ImageProxy in a finally block. The full CameraX analyzer is in the Android first-scan guide.
Errors and recovery
Condition
Resolution
License is denied
Refresh the signed JWS and confirm tenant, device, feature, expiry, revision, and trusted key bindings.
No observations are returned
Confirm the native bridge is packaged, use the correct scan mode, and inspect frame quality without treating an empty result as an error.
Production checklist
Verify the complete ZIP against the authenticated catalog SHA-256
Keep bearer tokens and license JWS values out of logs
Exercise revoked, expired, missing-feature, and offline-grace failures
Benchmark on the target device for the full work window
Scan modes
The three Android evaluation workflows. Mode is set on VisionSessionConfig.scanMode and paired with a frame profile; use a separate appropriately licensed session for dense rescue or batch receiving.
Every decode flows through the same deterministic path: the real native decoder and the deterministic GS1 parser produce the observations, and the host only reads them.
Return type
VisionSession.getLatestObservations(): List<BarcodeObservation> — the immutable observations produced by the most recently accepted frame. An empty list is a valid no-decode result.
Observation fields
rawPayload, symbology, GTIN, dependent lot/expiry/serial evidence, OCR provenance, and confidence. GTIN is the primary identity; lot, expiration, and serial remain GTIN-scoped dependent evidence.
OCR rescue
isOcrRescue marks OCR-derived evidence. OCR-only GTIN or serial evidence requires review and must never auto-commit.
Malformed input
Malformed native observations fail deterministically instead of producing sample data. Native absence, malformed decoder output, entitlement denial, or an undecoded frame never produces a sample GTIN.
Public contract reference
These are the callable methods and HTTP operations used by this guide. Inputs, results, lifecycle rules, and denial behavior are part of the versioned customer contract.
Hardware enrollment proof and bound lease admission
PRXV-0132-S10: Android hardware-only installation keys, current app signers and signed lease binding. Wire contract: contracts/json/sdk-hardware-binding.schema.json. Backend attestation and atomic activation remain release gates.
Create an app-scoped non-exportable hardware P-256 key and signed enrollment evidence.
Inputs for VisionScanSdk.createEnrollmentProof(context: Context, challenge: VisionScanEnrollmentChallenge): VisionScanHardwareBindingProof
Input
Type
Required
Meaning
context
Context
Yes
Real application context supplies package and current signing identity.
challenge
VisionScanEnrollmentChallenge
Yes
Trusted server-issued bounded nonce and origin; no caller-generated enrollment authority.
Result
Restricted proof JSON containing public SPKI, DER signature and bounded attestation chain; never a private key.
Lifecycle
Worker only; once per approved installation. Existing keys are never overwritten; recovery requires admin approval.
Trust boundary
No networking. Server validates chain trust, revocation, app identity and challenge and consumes it atomically. Client labels are not authority; no proof in logs or telemetry.
Require signed app/signer/key matching before commercial lease admission.
Inputs for VisionScanSdk.initializeHardwareBinding(context: Context)
Input
Type
Required
Meaning
context
Context
Yes
Platform app context reads the existing installation key and current signers.
Result
Strict binding mode, with old process admission cleared even if setup fails.
Lifecycle
Call on a worker after lease-store initialization and before applying a bound lease. Store reinitialization retains strict mode.
Trust boundary
Legacy unconfigured hosts remain compatible. This is not an encrypted-key activation API. Unsupported Swift/C++ hosts reject bound leases rather than ignore them.
Failure behavior
Missing/non-hardware key rejects setup; signed binding absence/mismatch rejects admission; key loss or signer change stops feature checks.
Android license lifecycle
The host stores only a verified device-bound lease and never constructs entitlement claims.
VisionScanSdk.initializeLeaseStore(storageDir: File, hostDeviceId: String): Unit
method
Configure device-bound persistence used for offline grace and anti-rollback checks.
Inputs for VisionScanSdk.initializeLeaseStore(storageDir: File, hostDeviceId: String): Unit
Input
Type
Required
Meaning
storageDir
File
Yes
A host-owned no-backup directory; do not use a shared or user-exportable location.
hostDeviceId
String
Yes
The stable device identifier activated for the authenticated tenant.
Result
Replaces the process entitlement manager with one backed by the device-bound lease store.
Lifecycle
Call once after authenticated device enrollment and before applying or refreshing a license.
Trust boundary
Store verified leases only; bearer tokens remain in host-owned secure credential storage.
Failure behavior
Blank or unstable device identity prevents a durable binding.
Unreadable storage causes later lease persistence or offline checks to fail closed.
Missing, unverified, wrong-tenant, expired, revoked, or feature-incomplete entitlement throws VisionScanEntitlementDeniedException.
A missing native bridge throws VisionScanNativeUnavailableException.
VisionSession.submitFrame(width, height, format, buffer, profile?): Int
method
Compatibility entry point that submits one bounded luminance frame to the native pipeline, optionally with an explicit preprocessing profile.
Inputs for VisionSession.submitFrame(width, height, format, buffer, profile?): Int
Input
Type
Required
Meaning
width / height
Int
Yes
Positive pixel dimensions matching the normalized buffer.
format
Int
Yes
The SDK-supported image format code; CameraX integrations submit extracted luminance.
buffer
ByteArray
Yes
A stride-correct frame. Use YPlaneExtractor for padded CameraX Y planes.
profile
StillImageBatchScanProfile
No
Use liveSweep for responsive camera work or stillImageDense for licensed rescue/batch input.
Result
A native status code. It is not a barcode count; prefer processFrame for same-frame observations.
Lifecycle
Submit only while the session is open. Live mode may drop stale frames; accepted batch input must not be silently dropped.
Trust boundary
The host must bound decoded image size and must not retain or upload image bytes without policy and consent.
Failure behavior
Closed sessions throw IllegalStateException.
Invalid dimensions, format, buffer, entitlement, or native state return or throw a deterministic failure.
VisionSession.processFrame(width, height, format, buffer, profile?): VisionFrameProcessResult
method
Process one bounded luminance frame through the SDK-owned native scan pipeline and return the status plus observations from that same accepted frame.
Inputs for VisionSession.processFrame(width, height, format, buffer, profile?): VisionFrameProcessResult
Input
Type
Required
Meaning
width / height
Int
Yes
Positive pixel dimensions matching the normalized buffer.
format
Int
Yes
The SDK-supported image format code; CameraX integrations submit extracted luminance.
buffer
ByteArray
Yes
A stride-correct frame. Use YPlaneExtractor for padded CameraX Y planes.
profile
StillImageBatchScanProfile
No
Use liveSweep for responsive camera work or stillImageDense for licensed rescue/batch input.
Result
VisionFrameProcessResult(status, observations, tracking), where observations are native-decoded evidence from the same accepted frame and tracking contains native stable tracks, active/distinct/stable counts, duplicate-payload physical track count, dense-selection hints, exact target matches, pick state, and overlay-ready quadrilaterals.
Lifecycle
Use for new live and batch capture integrations. Live mode may drop stale frames by policy; accepted batch input must not be silently dropped.
Trust boundary
The host must bound decoded image size and must not retain or upload image bytes without policy and consent.
Failure behavior
Closed sessions throw IllegalStateException.
Invalid dimensions, format, buffer, entitlement, or native state return or throw a deterministic failure.
VisionSession.setFindTargets(targets: List<VisionScanTarget>): Unit
method
Install exact decoded-payload targets for SDK-owned find/pick matching.
Inputs for VisionSession.setFindTargets(targets: List<VisionScanTarget>): Unit
Input
Type
Required
Meaning
targets
List<VisionScanTarget>
Yes
Zero or more payload labels with required stable observation counts. Empty clears targets.
Result
Targets are applied to the native session tracker; subsequent processFrame results update tracking.targetMatchCount and per-track targetMatched/targetLabel.
Lifecycle
Call after session creation and before or during scanning. Targets are session-local and cleared on close.
Trust boundary
Matching is exact against deterministic decoder payloads; geometry and OCR-only rescue never synthesize a target value.
Failure behavior
Closed sessions throw IllegalStateException.
A missing native target matcher throws VisionScanNativeUnavailableException.
Return immutable observations produced by the most recently accepted frame.
Result
Zero or more BarcodeObservation values containing rawPayload, symbology, GTIN, dependent lot/expiry/serial evidence, OCR provenance, and confidence.
Lifecycle
Read after submitFrame succeeds and before the session is closed.
Trust boundary
OCR-only GTIN or serial evidence requires review and must never auto-commit; backend receipt validation remains authoritative.
Failure behavior
An empty list is a valid no-decode result.
Closed sessions throw IllegalStateException.
Malformed native observations fail deterministically instead of producing sample data.
Native MatrixScan sessions
PRXV-0122-S01: one native session owns bounded location/decode, tracks and state. Result schema: contracts/schemas/matrix-scan-result.schema.json; formats: contracts/matrix-scan-capabilities.json.
Caller-owned readable luma; fixed dimensions per session; no concurrent mutation. Maximum axis 8192 and 32000000 pixels.
Result
Typed MatrixScanResult with industry-neutral byte payloads and input-pixel quads. visibleTracks excludes lost/unstable tracks and failed-frame boxes.
Lifecycle
Synchronous worker call. Native state is session-owned; close after the operation. Do not feed accepted still/burst inputs through dropping live adapters.
Trust boundary
Verified license checked on every call. Payload is not necessarily UTF-8; counts are not backend receipt quantity.
Failure behavior
Invalid frame and decoder/capacity/cancellation statuses are explicit.
Incompatible native JSON version, encoding, mode or malformed typed result raises a redacted IllegalStateException.
Adapt live CameraX ImageProxy input without host pixel copying, tracking or decoder orchestration.
Inputs for MatrixScanAnalyzer(session: MatrixScanSession, listener: MatrixScanCameraListener): ImageAnalysis.Analyzer, AutoCloseable
Input
Type
Required
Meaning
session
MatrixScanSession
Yes
Borrowed authorized native session; analyzer never closes it.
listener
MatrixScanCameraListener
Yes
Worker callbacks onResult(MatrixScanCameraResult) and onError(MatrixScanCameraError). Keep callbacks short; callback exceptions propagate after release.
Result
One typed snapshot per processed frame, no retained camera buffer. Every delivered ImageProxy is closed.
Lifecycle
YUV_420_888, KEEP_ONLY_LATEST, single worker. Concurrent/closed live calls are released without decoding. close stops new work; admitted work still delivers its worker callback/evidence. Host stops/unbinds, closes session on worker, then shuts down executor. Discard stale UI painting only.
Trust boundary
No logging or media persistence. Borrowed session keeps license checks; analyzer adds no Scandit dependency.
Failure behavior
INVALID_IMAGE, ENTITLEMENT_DENIED and PROCESSING_FAILED carry no raw exception/payload.
Native result statuses remain available in frame.result; no decode is a normal empty result.
Map only current stable native quads to PreviewView pixels through CameraX CoordinateTransform.
Inputs for MatrixScanCameraResult.mapVisibleTracks(previewTransform: OutputTransform): List<MatrixScanOverlay>
Input
Type
Required
Meaning
previewTransform
androidx.camera.view.transform.OutputTransform
Yes
Non-null PreviewView transform from the same ViewPort and camera binding as this frame.
Result
MatrixScanOverlay(track: MatrixTrack, quad: List<MatrixPoint>): original native track and four mapped preview-pixel points; original track.quad unchanged.
Lifecycle
Map/render on UI after preview layout. Discard previous-binding callbacks; do not rotate or crop mapped geometry twice. Raw frame timestamp is frame.timestampNs.
Trust boundary
Transforms do not infer payload, identity, target matching or count.
Failure behavior
Invalid/non-invertible/non-finite transform fails; never substitute guessed rectangles.
Wrong camera binding/ViewPort is caller misuse; clear overlays and rebind rather than painting stale geometry.
Licensed native MatrixScan usage reporting
PRXV-0132-S04: opt-in, content-free native acceptance/processing deltas to the existing tenant-authenticated ingestion API. Metrics schema: contracts/json/matrix-scan-usage.schema.json. Live staging delivery and analytics projections remain validation/downstream gates.
Attach SDK-owned usage collection to subsequently created MatrixScan sessions, with independent bounded reporting work.
Inputs for VisionScanSdk.startMatrixTelemetry(options: MatrixTelemetryOptions, tokenProvider: () -> String): MatrixScanTelemetry
Input
Type
Required
Meaning
options
MatrixTelemetryOptions
Yes
Trusted HTTPS control-plane origin, actual SDK version, bounded interval/capacity and optional encrypted host storage.
tokenProvider
() -> String
Yes
Host-owned enrolled tenant access token with telemetry permission; worker-only, never raw license/JWS.
Result
Lifecycle handle with typed status, queued/dropped/rejected counters, flushAsync, close, isStopped and awaitStopped.
Lifecycle
Verified UUID-device license first; reporting before native session creation. Close on logout/switch, then awaitStopped on a cleanup worker before reusing storage or restarting. One reporter/file writer.
Trust boundary
No tenant authority in request body; server checks claims and device licensing. No barcode value, image, quad, track ID, key/token or arbitrary diagnostic text. Opt-in, no redirects; five fixed mode buckets. Telemetry is not a billing or receiving authority.
Failure behavior
Invalid origin/version/bounds or missing verified UUID-device license rejects start.
Transport failure retains stable IDs; only matching per-event outcomes retire events.
Queue saturation delays new counters in fixed buckets; shutdown overflow and terminal rejections expose statistics losses, never receipt loss.
Corrupt/foreign encrypted spool stops delivery without overwriting it.
Review the release support matrix before deployment. Support status and contract failure behavior remain authoritative even when an example is copied.