Capture exact SDK bytes before callback return and submit them through the existing process API only after same-server mobile-scanners 1.5 discovery. Preserve legacy server payloads and pending event retries; redact capture string representations. Verify 185 JVM tests, zero lint errors or warnings, debug/E2E builds and QA test compilation. Hardware AIM extraction and native delivery qualification remain open; the operator installation is unchanged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Co-Authored-By: Codex <noreply@openai.com>
46 lines
9.3 KiB
Markdown
46 lines
9.3 KiB
Markdown
# Zebra scanner input
|
|
|
|
Normal debug/release builds obtain scans through Zebra EMDK's device-provided service and private Binder callbacks. A package-targeted DataWedge broadcast is still forgeable by another installed application; destination signature checks do not establish sender identity. Production builds therefore contain no scan broadcast receiver.
|
|
|
|
The app first requires the system's `com.symbol.emdk` shared library and enabled, preinstalled `com.symbol.emdk.emdkservice/.EMDKService`. Vendor classes are loaded only after these checks, through an isolated wrapper. The optional shared-library manifest allows the same APK to install on non-Zebra devices, where explicit manual barcode entry remains available. The EMDK permission is a normal vendor permission, not a claim of signature-based authentication.
|
|
|
|
## Lifecycle and process contract
|
|
|
|
When verified discovery for the current server advertises mobile-scanners 1.5 or newer within major 1, each capture can also send `rawDataBase64` and `aimIdentifier`. The EMDK callback encodes its exact `getRawData()` output once before returning; the immutable event survives HTTP retries unchanged. Older servers receive the legacy event fields. Raw input is bounded to 8192 bytes; null, empty or oversized raw input is omitted rather than truncated. These are SDK bytes, which may include framing, not a claim of original symbol codewords. Current hardware capture leaves AIM absent: no prefix inference or scanner configuration change is implemented. Decoder qualification and native raw-byte acceptance remain required for full Echo fidelity.
|
|
|
|
The adapter acquires the default scanner only while the server-defined SCANNER widget is visible and its Activity is resumed. HARD trigger reads are armed after IDLE, with one outstanding read. A decoded string is passed unchanged to the existing ScanEvent and semantic process submission, with its EMDK symbology and adapter-owned `zebra-emdk` source. New reads wait until the prior event is confirmed or explicitly abandoned by the existing process flow. Manual events remain `manual`. Neither source label is server-side attestation.
|
|
|
|
An IDLE callback with no pending read also rearms after a trigger release or timeout without a decode. If IDLE precedes the data callback, accepting that data blocks further input and cancels any read armed in between. A rapid pause/resume during an earlier asynchronous manager-open request can temporarily encounter the one-manager guard; **Reconnect scanner** retries explicitly after the prior manager has released. An unconfirmed release remains blocked until the app restarts.
|
|
|
|
Null, empty or unsuccessful data collections do not submit a scan or tear down the hardware; subsequent IDLE and pending-read state control recovery. A cancellation collection after a successful decode cannot clear its acknowledgment gate. A successful collection with multiple barcodes rejects the whole batch, shows a one-barcode warning and permits a later single scan. SDK ERROR status, service loss and exceptions still release the hardware and expose recovery. This follows Zebra's [data/status callback example](https://techdocs.zebra.com/emdk-for-android/latest/tutorial/tutbasicscanningapi/); no payload or vendor error text is logged.
|
|
|
|
Pausing, leaving, logging out or changing server disposes the widget and releases the scanner and EMDK manager. Released generations ignore late status/data callbacks, and a late asynchronous manager-open callback releases that manager immediately. Cleanup attempts every owned resource even if one operation fails; a failed release never reports scanner readiness. Errors show manual fallback without printing scan contents or SDK exception messages.
|
|
|
|
Hardware input also requires the process surface to be interactive and its window focused. Drawer opening/partial swipes, Settings, Account, Diagnostics and leave/sign-out confirmation suspend input while preserving the active process and draft. Shared navigation and the host supply a nested live availability predicate; the widget observes it to cancel/rearm and checks it again with actual window focus before accepting a callback. This prevents callbacks arriving before recomposition from submitting behind an overlay. Closing the overlay rearms when the process has no pending request. Toolbar and Android Back share the host's live drawer priority, so Back closes the drawer without opening a process-leave confirmation behind it, even when authenticated content registers its handler later. Process Leave and explicit abandonment policy remain unchanged.
|
|
|
|
Zebra documents that acquiring BarcodeManager disables DataWedge scanning and releasing it restores DataWedge. The app does not edit persistent profiles or disable DataWedge globally. Physical acceptance must verify that another DataWedge application can scan after QQQ leaves/backgrounds. See [Zebra scanner lifecycle](https://techdocs.zebra.com/emdk-for-android/latest/guide/barcode_scanning_guide/) and [runtime ownership/compatibility](https://techdocs.zebra.com/emdk-for-android/latest/guide/about/).
|
|
|
|
## SDK provenance and terms
|
|
|
|
The owner explicitly accepted Zebra's EMDK terms on October31,2026. The SDK is proprietary vendor software under the [Zebra EULA](https://techdocs.zebra.com/emdk-for-android/eula/), not an Apache/MIT dependency. This acceptance does not relicense vendor code or constitute a general license-clearance opinion.
|
|
|
|
The build uses `compileOnly("com.symbol:emdk:11.0.134")` from [Zebra's documented Maven repository](https://techdocs.zebra.com/emdk-for-android/13-0/guide/setup/). Repository access is exclusive to that exact group/artifact. `verifyEmdkSdk` checks the selected SDK in each variant's actual compile classpath before compiler/preBuild tasks, requiring version11.0.134 and SHA256 `e0c6b481c25bf641333bf644de940f1ac7f3fc4f8d5c92ba4ae62ea2252c4e36`. There is no dynamic version or checksum bypass.
|
|
|
|
The SDK implementation is not bundled. Zebra documents11/13 runtime compatibility; the inspected TC22 has runtime13.0.23.4723. Its system framework explicitly binds the named preinstalled service and registers scan callbacks through Binder. This observation is not an audit of all Zebra firmware. Final APK inspection must confirm vendor implementation classes are absent and the standard packaged runtime notices remain complete. The SDK is a build prerequisite, not a falsely labeled OSS runtime entry.
|
|
|
|
AboutLibraries10.9.2 also collects compile classpaths, so its standard exclusion pattern matches only `com.symbol:emdk`. The checksum task first rejects that artifact in every resolvable runtime classpath, including tests; an accidental runtime dependency therefore fails before packaging and cannot be hidden by the notice exclusion. No permitted-license list was broadened. The separate SDK documentation records the proprietary terms and owner acceptance.
|
|
|
|
## Test boundary
|
|
|
|
Use the [leading-zero QR test image and verification steps](TEST-BARCODES.md) for the physical QR check.
|
|
|
|
Debug, release and isolated QA builds share `src/production` and the trusted factory. QA uses the `.qa` application suffix, existing debug signing and a separate test manifest for local test HTTP. Build its instrumentation with `-PscannerTestBuildType=qa :scanner-host:assembleQa :scanner-host:assembleQaAndroidTest`. Only `e2e` (the default) and `qa` are accepted instrumentation variants. Neither option changes production signing.
|
|
|
|
Only `src/e2e` supplies the exported synthetic broadcast adapter; its application ID has the `.e2e` suffix and its source label is `e2e-fixture`. It cannot be enabled in production through an intent or server setting. Existing synthetic payloads keep their fixture action for runner compatibility. The QA variant contains no synthetic ingress, so it exercises the same scanner trust boundary without installing over the operator's app.
|
|
|
|
`ScannerInputSessionTest` covers duplicate reads, no-decode/empty/canceled completion, multiple-barcode rejection/retry, IDLE-before-data ordering, raw-string preservation, acknowledgment gating, pause/resume generations, late callbacks, reconnect after busy acquisition, vendor pending-read state, read/cancel failures and failed-release readiness. It does not exercise Zebra hardware. Required physical acceptance remains: Code128/QR with leading zeros, repeated triggers, rotation during initial acquisition as well as ready state, background/resume, logout/leave, uncertain acknowledgment retry and DataWedge ownership restoration. A separately signed ordinary application must also send forged scan broadcasts to the foreground production-path app while the backend confirms zero resulting scan records. Shell UID injection alone is not that test.
|
|
|
|
Implementation/source tests and physical/packaged verification have separate evidence; do not claim the latter from a passing synthetic E2E fixture.
|
|
|
|
The standalone QA instrumentation class `io.qrun.mobile.scanner.ScannerOverlayTest` exercises the production widget and shared navigation with a private in-process fake driver and synthetic repository. It needs no backend configuration, probe app or operator scan, and is restricted to the `.qa` target. It tests overlay/focus suppression, callbacks before recomposition, preserved manual draft and dismissal recovery; it is not EMDK hardware acceptance. Its empty instrumentation Activity is declared only in the QA manifest and is not exported. The production widget has no intent, server setting or runtime toggle for injecting this driver.
|