# Multi-brand Android hardware services

> Scope update: the service-APK design below is reserved for the future payment application. uicu_home uses brand flavors for device management and excludes EMV, PIN-pad and printing. See chapter 12.

## Decision

Use one brand-independent uicu_home APK plus one installed UIC hardware-provider APK for the terminal brand. Share a versioned Android AIDL contract and Kotlin client library. Each provider wraps its own vendor SDK and owns hardware coordination. Use Android Binder IPC, not HTTP, MQTT or a local gRPC server for device hardware calls.

This is an architecture decision and implementation plan. No provider APK has been built, installed or benchmarked during this investigation. The connected device was inspected read-only.

## Verified evidence — 23 September 2026

- Connected NEWPOS terminal: Android 13 / API 33; ro.product.model reports NEW9810 while adb product/device reports NEW9830. Preserve both reported identities; do not guess the commercial model from one property.
- Supplied archive: C:/Users/fabal/OneDrive/Documents/Company/NEWPOS/9810_9830_SDK.zip. SHA-256: B0D9D2079E4307A0186C416F5B73CB8F00892B7349052554EC462862C74E231E.
- Archive contains sdk.jar and asmart_api_doc.zip (HTML API documentation).
- Inspected SDKManager and Beeper bytecode contains throwing method stubs. The terminal advertises the shared Java library com.pos.device via pm list libraries.
- Proposed NEWPOS packaging: compileOnly sdk.jar and an application uses-library declaration for com.pos.device with required=true. Confirm class loading, SDK initialization and vendor permissions on the actual device before claiming hardware support.
- Ten copied uicu_home Kotlin files reference com.nexgo. Dependencies include application startup, serial lookup, status/navigation controls, reboot, install and uninstall. This migration is broader than payment APIs.
- The archive inspection alone does not establish EMV contactless scheme coverage, payment application approval, privileged installation access or production signing requirements.

## Runtime and project layout

Logical call path:

    uicu_home / authorized payment application
        → UIC hardware client library
        → versioned AIDL / Android Binder
        → UIC NEWPOS provider OR UIC NEXGO provider
        → vendor SDK / terminal system services / secure hardware

Proposed projects under agents/hardware-services:

| Module | Output and ownership |
|---|---|
| hardware-contract | Android library: AIDL, typed request/event/result parcelables, documented semantics |
| hardware-client | Android library: verified binding, negotiation, reconnect and Kotlin async wrappers |
| provider-core | Android library: caller checks, sessions, bounded queues, deadlines and normalized errors |
| provider-newpos | APK containing NEWPOS adapter and any required vendor-specific activities |
| provider-nexgo | APK containing NEXGO adapter and required SDK dependencies |
| hardware-test-client | Lab APK: the same client contract, diagnostics and repeatable performance tests |

Separate package IDs, for example com.uic.hardware.newpos and com.uic.hardware.nexgo. Both implement the same AIDL package. No vendor classes, Android View objects or vendor enums appear in the public interface.

Use one provider process per installed brand, with separate interfaces and permissions for management and payments. Do not grant uicu_home payment access just because it needs serial, printing or reboot. An authorized payment app can use the same contract library later.

Bind explicitly to an approved package/component after verifying its signing certificate. Negotiate major/minor API version, provider version, SDK version, device identity and capabilities. Never select a provider solely because it advertises an intent action. Reject incompatible major versions; add new methods/fields compatibly without reordering existing AIDL methods.

Only a verified compatible provider is active. Missing provider or SDK failure gives a clear unavailable state; the launcher must still start without loading vendor classes.

## Common calls and callbacks

Names below are proposed contract semantics, not existing compiled interfaces.

| Area | Common calls | Callback/result examples |
|---|---|---|
| Discovery | getDescriptor, openSession, closeSession | READY, UNAVAILABLE, capabilities |
| Beeper | beep(requestId, pattern, deadline) | completed, failed |
| Printer | getPrinterStatus, submitPrintJob, cancelPrintJob | accepted, paperOut, overheated, completed, outcomeUnknown |
| Device management | getDeviceInfo, reboot, installPackage, uninstallPackage, setKioskPolicy | progress, completed, denied, unsupported |
| EMV configuration | validateProfile, applyProfile, getProfileRevision | validationErrors, appliedRevision, failed |
| EMV transaction | startTransaction, selectApplication, submitOnlineResponse, cancelTransaction | cardPrompt, applicationSelectionRequired, onlineRequired, completed |
| Secure PIN | beginPinEntry, cancelPinEntry, getKeyStatus | pinEntryStarted, cancelled, encryptedResult, failed |

Every asynchronous operation carries sessionId, requestId, monotonic deadline and ownership by authenticated caller UID. Events carry requestId, event sequence, standardized status and optional vendor diagnostic code. Payment prompts additionally carry a single-use prompt token. A response must match the active session, transaction, prompt and state; reject late or repeated replies.

Use typed parcelables and bounded byte arrays, not a generic execute(command, JSON) escape hatch. Requests use money in integer minor units with currency, never floating-point amounts. Publish explicit field size limits and a stable error vocabulary: UNSUPPORTED, NOT_READY, BUSY, INVALID_ARGUMENT, PERMISSION_DENIED, TIMEOUT, CANCELLED, HARDWARE_ERROR and OUTCOME_UNKNOWN.

Capabilities must describe what is available on this firmware/device and for this caller: printer width/formats, contact/contactless support, PIN modes and supported management operations. Return unsupported explicitly; never silently emulate a payment operation.

## Efficiency and concurrency

- Keep a bound connection during launcher/transaction use and initialize the vendor SDK once per provider process. Cache static identity and capability information; refresh on reconnect.
- Binder entry points perform caller checks and bounded validation, enqueue work, then return quickly. Never run blocking hardware calls on the main thread or Binder thread pool.
- Prefer a short synchronous admission response for operations needing explicit accepted/busy feedback; deliver execution progress/results through oneway callbacks. Oneway is asynchronous across processes, not a substitute for application queue limits.
- Use a bounded printer queue and a separate payment coordinator. Permit one payment transaction to own readers and PIN pad at a time. Serializing everything behind a long printer job would delay transaction processing.
- Respect vendor thread-affinity constraints. NEWPOS contains synchronized print entry points and step-oriented EMV APIs; do not assume every SDK method is reentrant.
- Send a complete receipt job rather than IPC per printed line/pixel. Transfer large images or packages with ParcelFileDescriptor; bound file size and decode dimensions and close descriptors deterministically. Do not send APK/bitmap bodies through Binder parcels.
- Keep APDU exchanges, kernel steps, offline authentication and time-critical PIN interactions in the provider. Cross-process events are for application decisions, UI prompts and final results.
- Never invoke a client callback while holding a hardware lock. Use bounded event delivery with ordering per session; coalesce optional progress, never silently discard a required decision or final result.
- No polling or permanent wake lock. Use binding for active clients; foreground execution only where an actual operation and platform rules require it. A service APK is not guaranteed to run forever.

Binder adds IPC overhead compared with an in-process SDK. This layout deliberately reduces the number of calls; no latency guarantee is claimed before measurement.

Measure on NEWPOS first: cold bind+SDK readiness, warm no-op IPC p50/p95/p99, beep request-to-SDK dispatch, callback delivery delay, print completion time, provider memory/idle CPU, concurrent print/payment admission, and restart recovery. Compare with the same vendor operation invoked directly in a lab baseline. Separate hardware duration from IPC overhead, and report device/firmware/build/sample count. Initial acceptance: zero dropped required events and zero main-thread hardware calls; numerical latency budgets follow the baseline.

## EMV and PIN ownership

The provider owns the transaction state machine. Do not expose each vendor EMV function as a remote method. A typical public lifecycle is idle → waiting for card → application selection → kernel processing → awaiting online response → completion/cancellation. Actual legal transitions and fallback rules depend on the kernel and transaction flow.

PIN entry may require vendor UI. NEWPOS exposes Ped.setPinPadView and showPinKeyBoard with View/Context parameters, which cannot simply cross an APK boundary. Host that UI in a provider-owned activity or use a vendor-supported secure entry surface. A foreground caller launches the approved explicit activity/session flow; do not assume a background service can show it.

Do not transmit clear PIN digits or secret key material through the common interface. Keep keys in secure hardware and refer to authorized key slots/handles. Only the authorized payment client receives the required encrypted result and associated metadata. Offline PIN verification results are distinct from online encrypted PIN blocks. Logs and TMS telemetry must exclude PIN data, full track data and unfiltered payment payloads.

EMV configuration is a validated, versioned profile containing required AIDs/CAPKs/terminal settings. Apply only while no transaction owns the kernel. Do not promise atomic replacement if the vendor exposes destructive stepwise APIs: validate/stage first, record application state and block transactions until a complete known profile is active. Key injection is a separate controlled workflow, not a generic TMS parameter push.

## Reliability and access control

Use signature-level permissions for UIC applications sharing an approved signing identity, and check Binder calling UID on each entry point before switching threads. If vendor signing requirements force different certificates, define an explicit certificate allowlist policy and verify packages belonging to the actual UID. Package-name strings supplied by the caller are not proof of identity. Verify the provider on the client side too.

Capture caller identity before dispatch and bind session/operation ownership to it. Split management and payment privileges. Export only necessary service/activity components, and expose no arbitrary shell execution interface.

Handle binder death and onServiceDisconnected: mark unavailable, cancel or safely close vendor sessions where possible, release ownership and reconnect with backoff. Client death must not leave PIN entry or the card reader locked.

Deduplicate operation IDs with a bounded journal. Never automatically repeat an uncertain print or payment after service death. Record OUTCOME_UNKNOWN and provide reconciliation/status lookup. Transport failure does not prove an operation was not executed. Reconnecting does not resume a payment unless the provider can establish its actual state.

## NEWPOS mapping from supplied SDK

| Common area | Inspected SDK APIs | Remaining validation |
|---|---|---|
| Initialize | SDKManager.init(Context, SDKManagerCallback), release, getSdkVersion | shared library loading, initialization result, permissions |
| Beep | Beeper.getInstance, beep, beepNonBlock | duration/frequency units, observed behavior |
| Printing | Printer.getStatus/getWidth, startPrint(PrintTask, PrinterCallback), cancelPrint | receipt conversion, callback meanings, actual print and paper-out behavior |
| EMV profiles | EMVHandler AID/CAPK and terminal configuration methods | supported kernels, profile completeness and safe replacement |
| EMV flow | EMVHandler application selection, readAppData, cardholderVerify, onlineTransaction | required callbacks, contactless orchestration and host-response flow |
| PIN | Ped.getPinBlock/getOfflinePin/cancelInputPin and secure UI APIs | keys, approved entry UI, formats, cancellation and permissions |

The NEWPOS shared library declaration is evidence-backed; successful use from an ordinary signed app is still a hardware test. Do not package the throwing stub JAR as the implementation.

## Migration and delivery order

1. Implement contract/client/provider-core plus NEWPOS descriptor, readiness, beep and printer status. Use a separate test client on the connected device.
2. Validate actual printing and callbacks, caller rejection, process death, queue bounds and latency. This proves the service architecture before EMV work.
3. Implement NEXGO against the same non-payment contract. Remove vendor imports from uicu_home startup, serial lookup and device management paths. Centralize binding in one application-scoped client.
4. Inventory all current privileged operations, including installation/uninstallation, system bars and kiosk controls. Map available capabilities; unsupported functions remain unavailable on NEWPOS.
5. Add payment session coordination and provider-owned PIN UI. Implement/validate EMV profiles and transaction flows with test cards and approved lab key provisioning.
6. Add provider identity/version/capabilities to TMS telemetry and provider compatibility metadata to the device model catalog. Brand/model/OS selects eligible packages; runtime capability negotiation still decides availability.

The current model catalog remains unchanged by this design. No payment, key injection, reboot, installation or device policy changes were performed during inspection.

## Sources

- Local NEWPOS SDK archive and its HTML API documentation; jar signatures/bytecode inspected with javap.
- Connected-device adb properties and shared-library list.
- Copied agents/uicu_home source (ten files reference com.nexgo).
- [Android AIDL](https://developer.android.com/develop/background-work/services/aidl): IPC contract and threading.
- [Bound services](https://developer.android.com/develop/background-work/services/bound-services): binding and lifecycle.
- [Binder transaction size](https://developer.android.com/reference/android/os/TransactionTooLargeException): shared parcel buffer and ambiguous partial failure.
- [Custom permissions](https://developer.android.com/guide/topics/permissions/defining): signature permissions and access control.
