nlsprint
Exhaustive Reference Manual

nlsprint Android App &
Hardware Integration

This manual provides an exhaustive engineering guide to the nlsprint Android application, its integration with the Newland / SNBC Label Printer SDK, and the underlying hardware communication stacks.

Core Focus Areas Documented

  • ESC/POS HexMode trap avoidance (m=1 vs m=2)
  • JNI native call dispatching & libc++_shared.so
  • Two-tier connection liveness verification
  • API 34 Compatibility (FLAG_MUTABLE)
  • Progressive privilege escalation for /dev nodes
  • Label coordinate math (203 DPI calculations)
01

Architectural Overview

The application is structured as a decoupled, multi-tier system separating modern UI paradigms from low-level native hardware routines. This isolation prevents UI thread blocking and manages complex JNI state.

graph TD UI[Jetpack Compose UI
MainActivity.kt] DISPATCHER[Kotlin Coroutines
Dispatchers.IO] ADAPTER[Hardware Adapter Layer
PrintTest.kt] JAVA_SDK[Java SDK Layer
LabelPrinterJavaSDK.jar] JNI[JNI Bridge
libLabelPrinterSDK.so] NATIVE_CORE[Native C++ Connection Factory] KERNEL[Linux Kernel & Android HAL] PRINTER[(Label Printer Hardware)] UI -->|Async user action| DISPATCHER DISPATCHER -->|Blocking calls| ADAPTER ADAPTER -->|Normalized ports & permissions| JAVA_SDK JAVA_SDK -->|JNI native methods| JNI JNI -->|C++ Connection objects| NATIVE_CORE NATIVE_CORE -->|Device nodes /dev/ttyS*| KERNEL NATIVE_CORE -->|libusb / UsbDeviceConnection| KERNEL NATIVE_CORE -->|BSD Sockets port 9100| KERNEL NATIVE_CORE -->|BlueZ / Fluoride RFCOMM| KERNEL KERNEL -->|Physical Signals| PRINTER classDef ui fill:#0f172a,stroke:#3b82f6,stroke-width:2px,color:#fff; classDef kotlin fill:#0f172a,stroke:#8b5cf6,stroke-width:2px,color:#fff; classDef java fill:#0f172a,stroke:#f59e0b,stroke-width:2px,color:#fff; classDef native fill:#0f172a,stroke:#10b981,stroke-width:2px,color:#fff; classDef hardware fill:#0f172a,stroke:#64748b,stroke-width:2px,color:#fff; class UI ui; class DISPATCHER,ADAPTER kotlin; class JAVA_SDK java; class JNI,NATIVE_CORE native; class KERNEL,PRINTER hardware;
02

Component Stack & Layer Responsibilities

Layer Primary Files Technical Role & Design Rationale
Presentation Layer MainActivity.kt
  • Reactive Jetpack Compose UI (Material 3).
  • Guards concurrent JNI calls via isPrinting locks.
  • Listens to real-time Android system broadcasts for USB/BT detachments.
  • Provides in-place "Reconnect" recovery actions.
Async Execution Kotlin Coroutines
(Dispatchers.IO)
Dispatches all hardware I/O off the Main thread, protecting against UI stutter and Android ANR watchdog termination.
Hardware Abstraction (HAL) PrintTest.kt Encapsulates vendor SDK. Implements two-tier liveness checking (isConnectionAlive()), auto-reconnect, raw write normalization, HexMode-safe test patterns, and SELinux chmod/su privilege escalation.
In-Tree USB Overrides UsbInterfaceImpl.java
USBEnum.java
Clean-room drop-in replacements patching legacy vendor JAR crashes on Android 12+ (FLAG_MUTABLE) and Android 14+ (RECEIVER_NOT_EXPORTED). Adds live isDeviceConnected() verification.
Vendor Java SDK & Native Binaries LabelPrinterJavaSDK.jar
jniLibs/*.so
Provides JNI bindings and precompiled binaries (libLabelPrinterSDK.so, etc.) for core printer communication and ZPL/EPL/TSPL interpretation. Requires CMake libc++_shared.so packaging.
03

End-to-End Program Workflows

Detailed lifecycles from library binding to print pipeline execution and auto-reconnection mechanics.

3.1 Initialization & Binding

The static initializer in PrintTest.kt automatically binds native dependencies to prevent UnsatisfiedLinkError crashes upon first access.

System.loadLibrary("ConfigFileINI")
System.loadLibrary("SimpleLogModule")
System.loadLibrary("LabelPrinterSDK")

3.3 Clean Connection Handshake

Legacy SDKs spammed cancel sequences on connect, inadvertently pulsing cash drawers (10 14 01) and feeding blank paper. Modern handshakes guarantee zero hardware side-effects.

// Input normalized before JNI call
val normalized = normalizePortInfo(port, info)
labelPrinter.ConnectPrinter(port, normalized, lang)

3.4 Label Template Formatting & Math

Standard printheads use 203 DPI (8 dots/mm). Commands are batched in memory before dispatch.

4" x 6" Shipping Label (203 DPI)

Width: 4 in * 203 = 812 dots
Height: 6 in * 203 = 1218 dots
SetLabelSize(812, 1218)
PrintText(x=80, y=120, ...)
PrintBarcode1D(type=5, ...)
PrintLabel(copies=1, sets=1)

3.7 Pre-Print Liveness & Auto-Reconnect

sequenceDiagram participant UI as MainActivity participant PT as PrintTest participant OS as Android OS participant DEV as Hardware UI->>PT: isConnectionAlive(300ms) note over PT,OS: Fast Tier: OS Peripheral Check PT->>OS: UsbManager.deviceList / BT state / /dev file check note over PT,DEV: Active Tier: Lightweight Probe PT->>DEV: WritePort(probeBytes) [e.g. ~HS, ^ee] DEV-->>PT: Response or write confirmation alt Probe Failed PT-->>UI: false UI->>PT: reconnect() with cached params PT-->>UI: true/false else Probe Succeeded PT-->>UI: true (Proceed with print) end

3.8 The ESC/POS HexMode Trap

In Epson ESC/POS firmware (e.g., Newland NLS-PP310), the test print command GS ( A pL pH n m relies critically on parameter m. Transmitting the wrong parameter bricks the printer into a raw hex dump mode requiring manual physical intervention.

m = 1 (0x01)
Danger: Hex Dump

Traps printer into printing raw hex codes. Requires power-cycle or 3 feed button presses to exit.

m = 2 (0x02)
Safe: Status Page

Prints hardware config report (baud rate, density) and cleanly stops. Used by buildEscPosSelfTestCommand.

m = 3 (0x03)
Safe: Pattern Test

Continuously prints rolling 64-char dot test lines to verify printhead thermal elements.

04

Hardware Subsystems

Serial (UART/COM)

Port 1

Nodes: /dev/ttyS*, /dev/ttyUSB*

Format: /dev/ttyS1|115200|0

Requires progressive SELinux permission escalation (chmod 666 via Java, shell, then su) because apps cannot natively open raw tty devices.

USB Host

Port 3

Uses Android UsbManager bulk transfers.

VID Whitelist: 0x154F, 0x03F0, 0x227D...

Connecting with wildcard "-1" claims first available. Using explicit names requires pre-granted runtime permissions to avoid NPEs.

Network (TCP RAW)

Port 4

TCP socket on AppSocket Port 9100.

Format: Bare IP (192.168.1.100)

Discovery utilizes UDP broadcasts across local subnet via NETEnum.NetsearchDevice().

Bluetooth SPP

Port 7

Classic RFCOMM Serial Port Profile.

Format: MAC Hex (00:11:22:33:44:55)

Requires OS-level pairing prior to discovery via BluetoothAdapter.getBondedDevices().

5-6

Native SDK & API 34 Compatibility

JNI Exception Propagation Warning

In SNBC::ConnectionUSB::connect(), native C++ invokes Java methods via CallIntMethod(). If the Java method throws an unchecked exception (e.g., NPE), the native library does not call env->ExceptionClear(). Android ART detects the pending exception during native execution and executes a hard SIGABRT process termination. This is why legacy SDK crashes cannot be caught by standard Kotlin try-catch.

Android 12+ / 14 Compatibility Patches

Legacy vendor JARs compile against Android 4.4. We use clean-room drop-in source replacements (UsbInterfaceImpl.java, USBEnum.java) to override the classpath and patch critical security changes.

Android 12+ (API 31)
PendingIntent Mutability

Fixes IllegalArgumentException: Targeting S+ requires FLAG_IMMUTABLE or FLAG_MUTABLE.

int flags = SDK_INT >= M ?
  PendingIntent.FLAG_MUTABLE : 0;
Android 14 (API 34)
Receiver Export Flags

Fixes SecurityException: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified.

registerReceiver(mReceiver, filter,
  Context.RECEIVER_NOT_EXPORTED);

7. Developer Setup & Debug

Clean and build debug APK
./gradlew clean assembleDebug
Install to attached device (ADB)
./gradlew installDebug
Filtered Logcat Debugging
adb logcat -s PrintTest:V callBackOpenPrinter:V plog:V MainActivity:V
  • PrintTest: HAL diagnostics, probe results
  • plog: Native SDK internal logger output

8. Engineering Roadmap

In-Tree Overrides
Done
Live connection checks and Android 12+/14+ API flags integrated.
HexMode Safety
Done
Two-tier liveness, auto-reconnect, and safe m=2 tests implemented.
ViewModel Migration
To Do
Move connection state/scopes to Android Architecture ViewModel + StateFlow.
Dynamic Calibration
To Do
Query real-time printer DPI via GetPrinterInfo() instead of assuming 203.