Skip to content

Commit 6a70397

Browse files
s00dcursoragent
andauthored
Feat/android jni usb layer (#35)
* feat(android): Rust-first USB layer via JNI Replace @command SerialPortManager with UsbBridge/UsbNative JNI path, vendored usb-serial 3.10.0, SIOM lifecycle fixes, and unified watch/exchange API. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): serialize fail/shutdown on usb-io thread Apply cubic PR #34 feedback: route SIOM onError through io executor, shutdown executor after cleanup task, reject I/O after shutdown. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(example): ignore src-tauri .gradle cache Co-authored-by: Cursor <cursoragent@cursor.com> * chore: repo hygiene Stop tracking IDE metadata and npm lockfiles; publish correct LICENSE paths. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(queue): errors no longer halt the TX queue Remove permanent halted flag; stop_on_error only aborts the current at_phases batch. clear_halt resets drain waiters after cancel_exchange. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(desktop): lock order, Opening-state open, async enable_mux Avoid map locks across I/O; open blocks outside serialports mutex with Opening guard; eager RX hub start; virtual watch/close without AB deadlock. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(rx): all reads via RX hub; restart + disconnect fail-fast Route desktop/mobile reads through RxHubShared (read_slot, idle, exchange waiter-before-write). Remove port_io_yield; hub stop checks stop_rx during poll reads; replay idle bytes into active exchanges; fix exchange_waiter re-entrant lock in feed_bytes. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(android): poll read via RX hub, drop JNI pollRead Remove Kotlin/Rust poll-read path; Android RX feeds MobileRxHub via SIOM. Mobile exchange/read use hub read_request like desktop. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: panic/poison hardening Add lock_or_recover for background-thread mutexes; drop unsafe Send/Sync on Error and release-profile panic=abort. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): JNI exceptions, session path re-key, PendingIntent, teardown leaks with_local_frame + Java exception clear; open returns canonical session path; Android 14 PendingIntent flags; CoalescingRxSink shutdown on close; hub shutdown in close_all; singlePortPerDevice parity; remove mobile_response. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(js)!: onUrc, Uint8Array raw, watch-preserving change() Preserve onUrc across watch rebuilds; exchange raw is Uint8Array with timedOut flag; change() reopens and re-establishes watch; isOpen guards on control-line writes; apply canonical path from open(). Co-authored-by: Cursor <cursoragent@cursor.com> * test: consolidate mocks + coverage Unify desktop mocks in mock_serial.rs; add hub read_slot/idle/restart and PTY watchdog tests; JS exchange/mux/log-level suites; Kotlin coalescer cleanup test. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: v3 migration, Android bridge guide, remove debug.md Document Uint8Array raw, timedOut, canonical open path, RX hub testing, UsbBridge/UsbPortSession, and auto-reconnect TOC updates. Co-authored-by: Cursor <cursoragent@cursor.com> * ci: add fmt, clippy, pnpm check and build to test workflow Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): cancel_exchange wakes hub waiter; remove dead listening field Co-authored-by: Cursor <cursoragent@cursor.com> * fix: lock_or_recover in watch_registry and hub channel paths Co-authored-by: Cursor <cursoragent@cursor.com> * test: read_request guards, stop_on_error, disconnect fail-fast, auto-reconnect edges Co-authored-by: Cursor <cursoragent@cursor.com> * docs: CHANGELOG unreleased, fix usbserial UPDATE.md, README gaps Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(rust): split monolith into at/exchange/hub/port/state/api/android Reorganize the plugin core into focused modules so desktop and mobile targets share exchange/hub logic while Android JNI lives under android/. Gate desktop-only unit tests and CMUX session tests for cargo ndk. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(android): JNI debug harness and sharedTest fakes Expose testHarness* JNI entry points on MobileBridge and UsbBridge integration-test helpers. Move FakeUsb/RecordingRxSink to sharedTest for unit and instrumented reuse. Co-authored-by: Cursor <cursoragent@cursor.com> * test(android): Kotlin↔JNI↔Rust integration tests Add stable instrumented tests for RX/TX/error chains through JniSerialRxSink, a Parcel-based FakeUsb for on-device runs, and sync script into gen/android. Co-authored-by: Cursor <cursoragent@cursor.com> * ci: add android integration emulator job and windows check Run connectedUniversalDebugAndroidTest on macOS with API 34 emulator and cross-check the Windows MSVC target in a dedicated job. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: integration tests guide and module layout notes Document JNI integration test workflow, sharedTest layout, and unreleased changelog entries for the Rust reorg and Android test harness. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(example): add synced androidTest sources in gen/android Checked-in copy of JNI integration tests for Gradle/CI without a pre-build sync step; canonical sources remain in android-integration/. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(desktop): unblock hub drain when watch is active Return Ok(0) from poll_read_port on benign read timeout so hub_loop can finish rx_prepare drain and AT exchange does not hang with watch. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(android): pure Rust USB serial via android-usb-serial Replace vendored usb-serial-for-android with the android-usb-serial crate (nusb drivers). Kotlin keeps only UsbFdBridge for enumerate, permission, and fd handoff; Rust owns claim, I/O, and multi-port probe. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(changelog): update changelog for version 3.0.0 release * chore(changelog): update changelog for version 2.24.1 release * chore(deps): update slab version to 0.4.12 in Cargo.lock and add unix-specific tests for closing ports and CMUX session routing * ci: fix Android targets and migrate integration to Ubuntu Install aarch64-linux-android for host matrix cargo check/clippy. Move android-integration off Apple Silicon macOS (emulator HVF unsupported) to ubuntu KVM with x86_64 AVD and a prebuild/test script split. Co-authored-by: Cursor <cursoragent@cursor.com> * ci: relink file: API package after build for vue-tsc pnpm snapshots file: deps at install; dist-js appears only after rollup, so example vue-tsc missed types in CI. Relink after build. Co-authored-by: Cursor <cursoragent@cursor.com> * ci: install libudev before host android-usb-serial tests Golden parity links serialport/nusb on linux-gnu and needs -ludev; apt was running after the host cargo test step. Co-authored-by: Cursor <cursoragent@cursor.com> * docs(android-usb-serial): rustdoc; fix PTY concurrency flake Keep PTY master draining while exchange waits so Linux CI does not tear down with Broken pipe or starve the RX hub on concurrent write. Co-authored-by: Cursor <cursoragent@cursor.com> * ci: drop flaky Android emulator integration job Keep Robolectric, Android cargo check/clippy, and local scripts. Co-authored-by: Cursor <cursoragent@cursor.com> * ci: restore android-integration prebuild without emulator Keep JNI harness build/sync in CI; device androidTest stays local. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 10e1c46 commit 6a70397

1,412 files changed

Lines changed: 140483 additions & 16294 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ root = true
44
end_of_line = lf
55
insert_final_newline = true
66

7-
[*.{js,ts,svelte,html,json,mjs,cjs}]
7+
[*.{js,ts,html,json,mjs,cjs,vue}]
88
charset = utf-8
99
indent_style = space
1010
indent_size = 2

.github/workflows/test.yml

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,31 +67,59 @@ jobs:
6767
uses: dtolnay/rust-toolchain@stable
6868
with:
6969
components: rustfmt, clippy
70+
targets: aarch64-linux-android
7071

7172
- name: Install cargo-nextest
7273
run: cargo install cargo-nextest --locked --force
7374

75+
# Before host golden/nextest: linux serialport/nusb need -ludev (and GTK for tauri later).
7476
- name: Install system dependencies (Ubuntu)
7577
if: matrix.os == 'ubuntu-latest'
7678
run: |
7779
sudo apt-get update
78-
sudo apt-get install -y pkg-config libglib2.0-dev libudev-dev libgtk-3-dev libgdk-pixbuf2.0-dev libwebkit2gtk-4.1-dev
80+
sudo apt-get install -y openjdk-17-jdk pkg-config libglib2.0-dev libudev-dev libgtk-3-dev libgdk-pixbuf2.0-dev libwebkit2gtk-4.1-dev
7981
8082
- name: Install system dependencies (macOS)
8183
if: matrix.os == 'macos-latest'
8284
run: |
8385
brew install pkg-config
8486
87+
- name: Check android-usb-serial (Android target)
88+
run: cargo check -p android-usb-serial --target aarch64-linux-android
8589

90+
- name: Check plugin (Android target)
91+
run: cargo check -p tauri-plugin-serialplugin --target aarch64-linux-android
92+
93+
- name: Golden parity (host)
94+
run: cargo test -p android-usb-serial --features fake-transport
95+
96+
- name: Clippy android-usb-serial (Android target)
97+
run: cargo clippy -p android-usb-serial --target aarch64-linux-android -- -D warnings
98+
99+
- name: Robolectric (Android module)
100+
if: matrix.os == 'ubuntu-latest'
101+
run: cd android && ./gradlew test
86102

87103
- name: Install dependencies
88104
run: |
89105
pnpm install
90106
107+
- name: Rustfmt
108+
run: cargo fmt --all -- --check
109+
110+
- name: Clippy
111+
run: cargo clippy --workspace --all-targets -- -D warnings
112+
113+
- name: TypeScript check
114+
run: pnpm check
115+
116+
- name: Build JS
117+
run: pnpm build
118+
91119
- name: Run Rust tests
92120
env:
93121
NEXTEST_EXPERIMENTAL_LIBTEST_JSON: 1
94-
run: cargo nextest run --message-format=libtest-json-plus > test-results.jsonx
122+
run: cargo nextest run --workspace --message-format=libtest-json-plus > test-results.jsonx
95123

96124
- name: Run JavaScript tests
97125
run: pnpm test
@@ -106,3 +134,99 @@ jobs:
106134
test-results/
107135
test-results.jsonx
108136
if-no-files-found: warn
137+
138+
android-integration:
139+
# Build/sync JNI integration harness only — no emulator (device tests stay local).
140+
runs-on: ubuntu-latest
141+
timeout-minutes: 60
142+
143+
steps:
144+
- uses: actions/checkout@v4
145+
146+
- name: Use Node.js 20.x
147+
uses: actions/setup-node@v4
148+
with:
149+
node-version: 20.x
150+
151+
- name: Install pnpm
152+
uses: pnpm/action-setup@v2
153+
with:
154+
version: 9.15.4
155+
156+
- name: Setup Java 17
157+
uses: actions/setup-java@v4
158+
with:
159+
distribution: temurin
160+
java-version: 17
161+
162+
- name: Install Rust
163+
uses: dtolnay/rust-toolchain@stable
164+
with:
165+
targets: x86_64-linux-android
166+
167+
- name: Setup Android SDK
168+
uses: android-actions/setup-android@v3
169+
170+
- name: Install Android SDK packages
171+
run: |
172+
yes | sdkmanager --licenses
173+
sdkmanager \
174+
"platform-tools" \
175+
"platforms;android-34" \
176+
"build-tools;34.0.0" \
177+
"ndk;27.2.12479018"
178+
179+
- name: Install cargo-ndk
180+
run: cargo install cargo-ndk --locked
181+
182+
- name: Install dependencies
183+
run: pnpm install
184+
185+
- name: Build JS
186+
run: pnpm build
187+
188+
# file: link is snapshotted at install; refresh hardlinks after dist-js appears
189+
- name: Relink workspace packages
190+
run: pnpm install
191+
192+
- name: Prebuild Android integration artifacts
193+
env:
194+
ANDROID_INTEGRATION_NDK_ARCH: x86_64
195+
ANDROID_NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/27.2.12479018
196+
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/27.2.12479018
197+
run: chmod +x scripts/android-integration-ci.sh && ./scripts/android-integration-ci.sh prebuild
198+
199+
windows:
200+
runs-on: windows-latest
201+
timeout-minutes: 30
202+
203+
steps:
204+
- uses: actions/checkout@v4
205+
206+
- name: Install Rust
207+
uses: dtolnay/rust-toolchain@stable
208+
with:
209+
components: rustfmt, clippy
210+
211+
- name: Rustfmt
212+
run: cargo fmt --all -- --check
213+
214+
- name: Clippy (Windows target)
215+
run: cargo clippy --all-targets -- -D warnings
216+
217+
- name: Cargo check Windows
218+
run: cargo check --target x86_64-pc-windows-msvc
219+
220+
- name: Install pnpm
221+
uses: pnpm/action-setup@v2
222+
with:
223+
version: 9.15.4
224+
225+
- name: Install dependencies
226+
run: pnpm install
227+
228+
- name: TypeScript check
229+
run: pnpm check
230+
231+
- name: Run JavaScript tests
232+
run: pnpm test

.idea/.gitignore

Lines changed: 0 additions & 8 deletions
This file was deleted.

.idea/modules.xml

Lines changed: 0 additions & 8 deletions
This file was deleted.

.idea/process.iml

Lines changed: 0 additions & 17 deletions
This file was deleted.

.idea/vcs.xml

Lines changed: 0 additions & 6 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,85 @@
22

33
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
44

5+
6+
## [3.0.0](https://github.com/s00d/tauri-plugin-serialplugin/compare/v2.24.1...v3.0.0) (2026-07-08)
7+
8+
Major v3 release: Channel-based **`watch()`** API, native AT **`exchange`** / FIFO queue, unified **RX hub** on desktop and Android, and a **hard cut** from vendored Java `usb-serial-for-android` to the pure-Rust **`android-usb-serial`** crate (nusb).
9+
10+
### Breaking Changes
11+
12+
* **Streaming API:** `startListening()` / `listen()` / `disconnected()` / `cancelListen()` removed. Use **`watch({ onData, onDisconnect?, onError?, onUrc? })`**`SerialEvent` over Tauri [`Channel`](https://v2.tauri.app/develop/calling-frontend/) (desktop + Android).
13+
* **Capabilities:** `SerialPort.getCapabilities()` (`transport`, `platform`, `version`); commands `capabilities`, `watch`, `unwatch` replace `start_listening` / `stop_listening`.
14+
* **Removed:** `available_ports_direct` — use `available_ports()`.
15+
* **Removed `AtCommandQueue` / `port.at`:** Use **`sendAt()`**, **`sendAtPhases()`**, **`sendSmsPdu()`**, **`cancelAt()`**, **`configureAtSession()`**.
16+
* **`onUrc` removed from `AtSessionOptions`:** Use **`watch({ onUrc })`** for live URC lines.
17+
* **`exchange()`** returns **`ExchangeResponse`**, not `Uint8Array`. Use `.raw` for bytes.
18+
* **`clearRx: true`** maps to **`purge`**; default is now **`drain`** when unset.
19+
* **Android USB stack:** Vendored `android/usbserial` Java tree **removed**. Kotlin no longer runs serial I/O commands on the main thread — only **`UsbFdBridge`** (enumerate, permission, fd). All drivers and bulk I/O live in Rust (`android-usb-serial` + nusb).
20+
* **Rust module layout:** Removed compat top-level modules (`desktop_api`, `mobile_api`, `port_rx_hub`, `exchange_runtime`, …). Use `api::serial`, `hub`, `exchange`, `port`, `state`, `android`.
21+
* **guest-js:** `AtCommandResult.raw` / `ExchangeResponse.raw` are `Uint8Array`; `timedOut` is a native `boolean`.
22+
* **`rust-version`:** **1.79** minimum.
23+
24+
### Migration (v2 → v3)
25+
26+
| v2 | v3 |
27+
|----|-----|
28+
| `startListening()` + `listen(fn)` | `watch({ onData: fn })` |
29+
| `disconnected(fn)` | `watch({ onDisconnect: fn })` |
30+
| `cancelListen()` / `stopListening()` | `handle.unwatch()` |
31+
| `port.at.enable()` / `enqueue()` | `sendAt()` / `sendAtPhases()` |
32+
| Kotlin `UsbBridge` + Java drivers | `UsbFdBridge` fd → Rust `driver_host` |
33+
34+
**Android app requirements:** `uses-feature android.hardware.usb.host`, `device_filter.xml`, runtime USB permission before `openDevice()`. Do **not** `claimInterface()` in Kotlin before handing fd to Rust. See [`crates/android-usb-serial/README.md`](crates/android-usb-serial/README.md).
35+
36+
### Features
37+
38+
* **`android-usb-serial` crate:** Pure Rust USB serial on Android via nusb; **567** golden parity fixtures; drivers for FTDI, CP21xx, CH34x, PL2303, CDC-ACM, GSM modem, Chrome CCD ([20b54c0](https://github.com/s00d/tauri-plugin-serialplugin/commit/20b54c0bbc17227465da71728333cf93d88103aa)).
39+
* **`UsbFdBridge`:** Kotlin provides USB fd only; Rust `driver_host` owns probe, claim, drivers, RX reader, and I/O.
40+
* **Unified `api::serial`:** Single `serialport::SerialPort` facade on desktop and Android; `PortRxHub` poll-loop on both platforms ([4071ff6](https://github.com/s00d/tauri-plugin-serialplugin/commit/4071ff6)).
41+
* **Rust-first Android JNI** ([29faa29](https://github.com/s00d/tauri-plugin-serialplugin/commit/29faa29)): bulk-IN reader thread, chunked write, CMUX virtual paths through Rust session.
42+
* **Unified RX hub (desktop + Android):** Single consumer per port; `watch`, `exchange`, `read`, and drain share one hub ([7024fc0](https://github.com/s00d/tauri-plugin-serialplugin/commit/7024fc0), [caa5700](https://github.com/s00d/tauri-plugin-serialplugin/commit/caa5700)).
43+
* **`take_idle_bytes`:** Stale RX in the hub idle buffer is replayed into the next `exchange` after write.
44+
* **Native FIFO queue (Rust + Android):** All `exchange` / AT jobs on one port serialize in FIFO order; parallel invokes **wait** instead of `"Exchange already in progress"` ([e1eb63f](https://github.com/s00d/tauri-plugin-serialplugin/commit/e1eb63f)).
45+
* **Native `exchange` / `cancel_exchange`:** Write + read-until terminators, idle silence, wall timeout, max response size; structured `ExchangeResponse` (`status`, `lines`, `solicitedBody`, `urcLines`, `raw`).
46+
* **Line-framed AT completion:** Final line `OK` / `ERROR` / `+CME ERROR` / `+CMS ERROR`; `completionMode: 'substring'` for legacy/binary.
47+
* **`rxPrepare`:** Default **`drain`**; **`purge`** opt-in; **`none`** unchanged.
48+
* **`watchAvailablePorts()` / `watch_ports`:** Hotplug via Channel — `snapshot`, then `added` / `removed` (desktop poll; Android USB attach/detach).
49+
* **`open()` canonical path:** Returns session key (Android device path / `device#N` for multi-port).
50+
* **CMUX virtual `exchange`:** Paths `physical#dlci=N` routed through Rust CMUX session like desktop.
51+
* **`usb-driver-tester`:** Standalone hardware self-test app under `examples/usb-driver-tester/`.
52+
* **guest-js:** Modular v3 SDK; auto-reconnect restores **`open()` + `watch()`** after disconnect.
53+
* **macOS:** `available_ports({ singlePortPerDevice: true })` — one path per device (prefers `/dev/cu.*`).
54+
* **Extended AT grammar:** Vendor prefixes, V.250 finals, `derive_solicited_prefixes(command)`; `ExchangeDemux` for live URC before echo.
55+
* **`pauseWatch` default `false`** — watch stays on during AT; pass `pauseWatch: true` for legacy behavior.
56+
57+
### Bug Fixes
58+
59+
* **Android CH340 / weak OTG:** Bulk IN reader starts after line/DTR setup; in-flight URBs reduced to 2; clearer detach reason in logs.
60+
* **Android enumerate:** Kotlin exports `interfaces[]`; Rust expands multi-port paths (`device#N`) via `ProbeTable` without opening fd.
61+
* **Android write after listen:** `EndpointPair::write` no longer re-opens bulk IN owned by `SerialReader` (`endpoint already in use`).
62+
* **Android Kotlin fd bridge:** Removed pre-`claimInterface` (fixes `io interface is busy` with nusb `detach_and_claim`).
63+
* **Android logcat:** Rust plugin logs use tag `SerialPlugin` via `__android_log_write`.
64+
* **Android:** USB permission `PendingIntent` uses `FLAG_MUTABLE` on API 31+; detach + IO errors → `SerialEvent::Disconnect` on Channel ([#27](https://github.com/s00d/tauri-plugin-serialplugin/issues/27)).
65+
* **Android:** `cancel_exchange` wakes hub waiter; CMUX virtual cancel clears DLCI TX queue ([c6c94dd](https://github.com/s00d/tauri-plugin-serialplugin/commit/c6c94dd)).
66+
* **Android:** JNI exceptions, session path re-key, teardown leaks ([e9fc307](https://github.com/s00d/tauri-plugin-serialplugin/commit/e9fc307)).
67+
* **Android:** Serialize fail/shutdown on dedicated usb-io thread ([cd74b84](https://github.com/s00d/tauri-plugin-serialplugin/commit/cd74b84)).
68+
* **desktop:** `write` / `write_binary` flush via `write_all` ([#29](https://github.com/s00d/tauri-plugin-serialplugin/issues/29)).
69+
* **desktop:** Unblock hub drain when watch is active ([55f1cb3](https://github.com/s00d/tauri-plugin-serialplugin/commit/55f1cb3)).
70+
* **desktop:** Lock order, Opening-state open, async `enable_mux` ([3a4bd88](https://github.com/s00d/tauri-plugin-serialplugin/commit/3a4bd88)).
71+
* **desktop (Windows):** Enrich truncated USB `serial_number` from WMI ([#23](https://github.com/s00d/tauri-plugin-serialplugin/issues/23)).
72+
* **Hub / watch:** `lock_or_recover` in watch_registry and hub channel paths ([2071e15](https://github.com/s00d/tauri-plugin-serialplugin/commit/2071e15)); panic/poison hardening ([8672de9](https://github.com/s00d/tauri-plugin-serialplugin/commit/8672de9)).
73+
* **TX queue:** Errors no longer halt the port queue until reopen ([e1eb63f](https://github.com/s00d/tauri-plugin-serialplugin/commit/e1eb63f)).
74+
* **guest-js:** `readBinary` requires open port; watch-preserving `change()` ([9b22af7](https://github.com/s00d/tauri-plugin-serialplugin/commit/9b22af7)).
75+
76+
### Build / CI / Docs
77+
78+
* **CI:** `cargo fmt`, `clippy`, `pnpm check` / `build`, Windows `cargo check`, Android integration emulator job ([1aa1682](https://github.com/s00d/tauri-plugin-serialplugin/commit/1aa1682), [2e70ecf](https://github.com/s00d/tauri-plugin-serialplugin/commit/2e70ecf)).
79+
* **Tests:** Kotlin↔JNI↔Rust instrumented integration tests with `FakeTransport` ([412b4a5](https://github.com/s00d/tauri-plugin-serialplugin/commit/412b4a5), [be77e2a](https://github.com/s00d/tauri-plugin-serialplugin/commit/be77e2a)); Jest v3 lifecycle + Rust contract tests.
80+
* **Docs:** v3 migration guide, Android bridge docs, [`examples/serialport-test/ANDROID.md`](examples/serialport-test/ANDROID.md), [`android-usb-serial` Android usage](crates/android-usb-serial/README.md#using-on-android) ([c2d1e23](https://github.com/s00d/tauri-plugin-serialplugin/commit/c2d1e23), [b3cc7d2](https://github.com/s00d/tauri-plugin-serialplugin/commit/b3cc7d2)).
81+
* **Workspace:** `android-test-harness` feature wires `FakeTransport` for instrumented tests.
82+
83+
584
### [2.24.1](https://github.com/s00d/tauri-plugin-serialplugin/compare/v2.24.0...v2.24.1) (2026-07-08)
685

786
## [2.24.0](https://github.com/s00d/tauri-plugin-serialplugin/compare/v2.21.1...v2.24.0) (2026-07-08)
@@ -41,7 +120,7 @@ All notable changes to this project will be documented in this file. See [standa
41120
* **Lifecycle:** `SerialPlugin` registers `Application.ActivityLifecycleCallbacks` and runs `SerialPortManager.cleanup()` when the host `Activity` is destroyed (close USB ports, unregister permission receiver, shut down IO executor).
42121
* **Listening:** Incoming data is coalesced in `BufferedEmitter` / `SerialByteAccumulator` before `serialData` events; flush interval via `serialDataFlushIntervalMs` (native clamp typically 10–2000 ms).
43122
* **USB serial (usb-serial-for-android):** Read/write use configured timeouts; `clearBuffer` maps to `purgeHwBuffers` when supported; `setFlowControl` (RTS/CTS, XON/XOFF); `SerialInputOutputManager` errors trigger `serialError` and port cleanup.
44-
* **`bytesToRead` / `bytesToWrite` (Android):** `bytesToRead` returns bytes buffered **in the plugin** only while `startListening` is active (not the kernel queue — not exposed by `UsbSerialPort`). `bytesToWrite` is `0` (writes complete synchronously from the app’s perspective). Documented in JSDoc on the JS API.
123+
* **`bytesToRead` / `bytesToWrite` (Android):** With active **`watch`**, `bytesToRead` is the plugin-side buffer before the next Channel flush; `bytesToWrite` is typically `0`.
45124
* **Tooling:** Gradle wrapper and `android/` settings for local `./gradlew test`; JVM unit tests use a real `org.json` artifact (Android JSON stubs break `JSONObject.put` in tests). Kotlin tests cover models, JSON helpers, emit pipeline, `BufferedEmitter`.
46125

47126
### Features

0 commit comments

Comments
 (0)