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=1vsm=2) - JNI native call dispatching &
libc++_shared.so - Two-tier connection liveness verification
- API 34 Compatibility (
FLAG_MUTABLE) - Progressive privilege escalation for
/devnodes - Label coordinate math (203 DPI calculations)
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.
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;
Component Stack & Layer Responsibilities
| Layer | Primary Files | Technical Role & Design Rationale |
|---|---|---|
| Presentation Layer | MainActivity.kt |
|
| 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.
|
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("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.
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)
PrintText(x=80, y=120, ...)
PrintBarcode1D(type=5, ...)
PrintLabel(copies=1, sets=1)
3.7 Pre-Print Liveness & Auto-Reconnect
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.
Traps printer into printing raw hex codes. Requires power-cycle or 3 feed button presses to exit.
Prints hardware config report (baud rate, density) and cleanly stops. Used by buildEscPosSelfTestCommand.
Continuously prints rolling 64-char dot test lines to verify printhead thermal elements.
Hardware Subsystems
Serial (UART/COM)
Port 1Nodes: /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 3Uses 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 4TCP 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 7Classic RFCOMM Serial Port Profile.
Format: MAC Hex (00:11:22:33:44:55)
Requires OS-level pairing prior to discovery via BluetoothAdapter.getBondedDevices().
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.
Fixes IllegalArgumentException: Targeting S+ requires FLAG_IMMUTABLE or FLAG_MUTABLE.
PendingIntent.FLAG_MUTABLE : 0;
Fixes SecurityException: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified.
Context.RECEIVER_NOT_EXPORTED);
7. Developer Setup & Debug
./gradlew clean assembleDebug
./gradlew installDebug
adb logcat -s PrintTest:V callBackOpenPrinter:V plog:V MainActivity:V
PrintTest: HAL diagnostics, probe resultsplog: Native SDK internal logger output
8. Engineering Roadmap
m=2 tests implemented.GetPrinterInfo() instead of assuming 203.