Skip to content

First-class Bluetooth: core API, all ports, simulator debugging and build detection#5399

Open
shai-almog wants to merge 30 commits into
masterfrom
first-class-bluetooth
Open

First-class Bluetooth: core API, all ports, simulator debugging and build detection#5399
shai-almog wants to merge 30 commits into
masterfrom
first-class-bluetooth

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Makes Bluetooth a first-class citizen of the framework: a typed, Java-idiomatic API in com.codename1.bluetooth with deep native support on every platform we can reach, a scriptable simulator with a real debugging UI, automatic permission/dependency injection in the builders, developer-guide coverage and tests throughout.

Today's only Bluetooth story is the bluetoothle-codenameone cn1lib — a port of a Cordova plugin. It carries the web-shaped design that made sense there and not here: results arrive as untyped Maps parsed from JSON strings, callbacks are correlated by method-name strings (so only one call per operation can be in flight), values travel base64-encoded, and errors are map keys rather than typed exceptions. It also only ever exposed BLE central: peripheral mode and bonding sat unbridged in the vendored native code, L2CAP and Classic Bluetooth were absent, and only Android/iOS had real implementations. That cn1lib is left untouched by this PR.

The API

Follows the NFC facade pattern — Bluetooth.getInstance()Display.getBluetooth()CodenameOneImplementation.getBluetooth() (null default), with no-op fallbacks so app code never null-checks or branches on platform.

Bluetooth bt = Bluetooth.getInstance();
if (!bt.isLeSupported()) return;

bt.requestPermissions(BluetoothPermission.SCAN, BluetoothPermission.CONNECT)
  .onResult((granted, err) -> {
      if (err != null || !granted) return;
      bt.getLE().startScan(
          new ScanSettings().addFilter(
              new ScanFilter().setServiceUuid(BluetoothUuid.fromShort(0x180D))),
          result -> result.getPeripheral().connect().onResult((p, e) -> {
              if (e != null) return;
              p.discoverServices().onResult((services, de) -> {
                  p.getCharacteristic(BluetoothUuid.fromShort(0x180D),
                                      BluetoothUuid.fromShort(0x2A37))
                   .subscribe((ch, value) -> updateHeartRate(value));
              });
          }));
  });

Packages are split by role and aligned to the permission model so build-time detection can be fine-grained: com.codename1.bluetooth (facade, BluetoothUuid, typed BluetoothError/BluetoothException, adapter state, permissions), .le (scanning, GATT client, L2CAP), .le.server (GATT server + advertising — referencing this package is what earns BLUETOOTH_ADVERTISE), .gatt (client model), .classic (discovery + RFCOMM). All core code is Java-5-compatible; listener interfaces are single-method so app code can use lambdas.

The cn1lib's structural flaws are fixed rather than papered over:

  • Concurrency: every call mints its own AsyncResource. A per-peripheral operation queue serializes GATT ops toward the platform stack (Android silently drops concurrent requests) with a safety timeout, so a lost platform callback fails one operation instead of wedging the connection. Ops on different peripherals run fully concurrently.
  • Scanning: any number of BleScan handles share one platform scan; core demultiplexes results per handle's filters with per-handle duplicate suppression, and stops the platform scan when the last handle stops.
  • Subscriptions: multiple listeners per characteristic, with the CCCD written only on the 0→1 and 1→0 transitions.
  • Errors: typed BluetoothException everywhere; unsupported features fail fast with NOT_SUPPORTED and are branchable up-front via capability queries (isLeSupported, isClassicSupported, isPeripheralModeSupported, isL2capSupported).
  • Threading: every callback lands on the EDT; only RFCOMM/L2CAP streams block and are used off-EDT.

Platform coverage

Android iOS / Mac Catalyst Simulator (mock) Simulator (native) JavaScript
BLE central chooser-based
BLE peripheral ✅ (not tvOS/watchOS)
L2CAP ✅ (API 29+)
Classic RFCOMM — (not exposed by iOS)
  • AndroidAndroidBluetooth + GATT client/server, RFCOMM, and AndroidL2capCompat (reflection for the API-29 L2CAP symbols against the API-27 compile jar). Android-12 runtime permission split; onCharacteristicChanged buffers copied immediately; gatt.close() always after disconnect.
  • iOS / Mac CatalystCN1Bluetooth.h/.m (~1900 lines, no ARC) gated behind CN1_INCLUDE_BLUETOOTH with linkable #else stubs, so apps that never touch Bluetooth link no CoreBluetooth symbol. Dedicated serial dispatch queue, controller-lifetime CBPeripheral retention, peripheral role compiled out on tvOS/watchOS. Both define paths verified with clang -fsyntax-only against the iphonesimulator and appletvsimulator SDKs.
  • JavaSE simulator — two interchangeable, runtime-switchable backends: a scriptable virtual stack, and real host hardware via a small Rust btleplug helper (Ports/JavaSE/native/cn1-ble-helper, line-delimited JSON over stdin/stdout). The default build never touches cargo; prebuilt binaries come from cn1-binaries (companion PR) and a missing helper degrades to "backend unavailable".
  • JavaScript — Web Bluetooth through the worker host bridge (12 __cn1_bt_*__ host calls). Web Bluetooth is chooser-based rather than scan-based, so startScan opens the browser chooser and delivers the single user-picked device; the user-gesture requirement is handled by parking and re-firing on the next real gesture. Divergences (fixed MTU, no RSSI/peripheral/classic/L2CAP) are typed and documented.

Simulator debugging

Simulate → Bluetooth Simulation opens a device/GATT tree (including an "App as Peripheral" node showing your own GATT server, advertising state and live subscriptions), a hex/UTF-8 characteristic value editor with push-notify, adapter toggle, latency control, failure injection (any operation × any BluetoothError), a backend selector, and an event log of every Bluetooth call the app makes.

Bluetooth Simulation window

The same world is scriptable from tests/app code via BluetoothSimulator, and drivable cross-platform through CN.execute("bluetooth:itemN") hooks.

Record and replay: capture real Bluetooth traffic from the host radio, run it through a deterministic, structure-preserving PII scrambler (synthetic addresses, Device-XXXX names, same-length randomized payloads, SIG UUIDs kept, custom UUIDs consistently remapped), and replay it through the whole stack in tests. Two real ambient scans (17 and 8 devices) are captured and shipped as fixtures; capture is available from the CLI (scripts/bluetooth/capture-fixture.sh) and the debug window's "Record from real hardware" button. Fixtures are leak-checked at capture time and in CI.

Build-time detection

The bytecode scanner keys on the permission-aligned package layout, so a central-only app never carries BLUETOOTH_ADVERTISE and a non-Bluetooth app sees no manifest change at all:

Detected usage Android iOS
any com.codename1.bluetooth BLUETOOTH (capped at API 30), bluetooth_le feature NSBluetooth*UsageDescription defaults, CoreBluetooth link, CN1_INCLUDE_BLUETOOTH
scanning BLUETOOTH_SCAN (+neverForLocation), BLUETOOTH_ADMIN, ACCESS_FINE_LOCATION
connecting / GATT / streams BLUETOOTH_CONNECT
com.codename1.bluetooth.le.server BLUETOOTH_ADVERTISE (+CONNECT) eligible for bluetooth-peripheral background mode
com.codename1.bluetooth.classic bluetooth feature — (runtime reports unsupported)

New hints: android.bluetooth.neverForLocation (default true — beacon apps that derive location from scans must set it false), android.bluetooth.required, ios.bluetooth.background. Fragment assembly lives in the testable BluetoothManifestFragments; dedup is quote-delimited, so BLUETOOTH_SCAN can't be suppressed by a pre-existing BLUETOOTH (the exact trap a naive contains walks into) and projects still carrying the old cn1lib's merged android.xpermissions don't get duplicates. The BuildDaemon twin is in a companion PR.

Tests

All deterministic by construction — fakes complete only when a test drains them, and the virtual stack runs on a manual scheduler with a virtual clock (source-scanned to keep wall-clock out of it).

  • core-unittests: 4033 green — 10 new suites covering the fallback contract, UUIDs, raw advertisement parsing, scan demux, connection lifecycle, op-queue serialization/timeout, CCCD multiplexing, EDT delivery of every callback type, and error mapping.
  • javase: 169 green — virtual stack, GATT, peripheral mode, RFCOMM/L2CAP streams, failure scripting, fixture scrambler invariants and recorded-trace replay, headless window checks.
  • codenameone-maven-plugin: 268 green — detection matrix including the substring-dedup regression.

Also in this PR

Two latent iOS bugs found while building the port and fixed here:

  • CN1WebAuthn.m defined its non-void trampolines without ParparVM's _R_<returnType> suffix (webauthnSupported__ vs the generated webauthnSupported___R_boolean), so any app actually using WebAuthn would have failed to link once the define flipped.
  • IOSNfc's dead-code-elimination touch block ran before its field initializers (static init is textual order), so the sentinel callbacks synchronized on a null map during class init.

Follow-ups (not in this PR)

  • Real-device sanity runs on physical Android/iOS hardware, and an RFCOMM echo between two Android devices.
  • The Linux/Windows helper binaries in the cn1-binaries PR are built but unvalidated on their target OS (macOS was smoke-tested against a live radio).
  • iOS background scanning needs ios.bluetooth.background plus service-filtered scans; CoreBluetooth state restoration is deliberately out of scope for v1.

🤖 Generated with Claude Code

shai-almog and others added 15 commits July 17, 2026 08:12
New com.codename1.bluetooth package (Java 5, NFC facade pattern):
BluetoothUuid (core has no java.util.UUID), BluetoothError/Exception
(typed errors + raw GATT status), AdapterState + listener, permission/
device-type/bond-state enums, abstract BluetoothDevice, Bluetooth entry
facade with capability queries and EDT-dispatched adapter events.
Impl hooks: CodenameOneImplementation.getBluetooth() (null default) +
Display.getBluetooth().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
com.codename1.bluetooth.le: BluetoothLE base with a core scan registry
that multiplexes any number of concurrent BleScan handles over one
platform scan (per-handle filter demux + duplicate suppression);
BlePeripheral with the per-peripheral GattOperationQueue (serialized
do* SPI, safety timeout so lost platform callbacks never wedge the
queue), connection lifecycle with typed events, CCCD-multiplexed
subscriptions, MTU/RSSI/priority/bond ops and L2CAP channels.
com.codename1.bluetooth.gatt: client-side GattService/Characteristic/
Descriptor model with convenience ops routed through the owning
peripheral's queue. AdvertisementData includes a pure-core AD-structure
parser. Bluetooth.getLE() no-op fallback wired.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
com.codename1.bluetooth.le.server: GattServer with EDT-dispatched
GattServerListener (+ GattServerAdapter that fail-fast rejects
unhandled requests), local GATT model (GattLocalService/Characteristic/
Descriptor), read/write request envelopes, BleCentral and advertising
types (AdvertiseSettings/Data, BleAdvertisement). Isolated in its own
package so build detection can key ADVERTISE permissions on the prefix.
com.codename1.bluetooth.classic: BluetoothClassic facade (discovery,
bonding, discoverable request, RFCOMM connect/listen), ClassicDiscovery
handle, stream-based RfcommConnection/RfcommServer.
BluetoothLE gains the peripheral-role entry points (openGattServer,
startAdvertising, openL2capServer); Bluetooth.getClassic() fallback
wired; package-info docs for all five packages.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bytecode-scanner detection for com.codename1.bluetooth keyed on the
permission-aligned package layout: scan/connect/peripheral/classic flags
(+ facade-method refinement via usesClassMethod). Android injection goes
through the new testable BluetoothManifestFragments helper handling the
Android 12 split (BLUETOOTH_SCAN neverForLocation flag per the
android.bluetooth.neverForLocation hint default true, legacy perms
capped at maxSdkVersion 30, BLUETOOTH_CONNECT/ADVERTISE, bluetooth_le
uses-feature with android.bluetooth.required hint) with quote-delimited
dedup safe against old-BLE-cn1lib merged xpermissions. iOS: CoreBluetooth
weak-link + CN1_INCLUDE_BLUETOOTH define flip + opt-in
ios.bluetooth.background merge into ios.background_modes; NSBluetooth*
plist defaults ride the new AiDependencyTable entry (Android perms
deliberately absent there). ANDROID_PERMISSIONS whitelist gains the
Android 12 constants. 10 new fragment tests + 2 table tests, all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AndroidBluetooth facade (adapter-state receiver, Android 12 runtime
permission split with string-literal S+ constants, requestEnable via
intent-result) + AndroidBluetoothLE (scan multiplex over
BluetoothLeScanner, peripheral cache, connected/bonded lookups) +
AndroidBlePeripheral GATT client (single-pending-slot callbacks under
the core op queue, CCCD writes complete doSetNotifications, immediate
onCharacteristicChanged buffer copy, gatt.close() always after
disconnect) + AndroidGattServerImpl (serialized onServiceAdded /
onNotificationSent queues, auto-injected CCCD, static-value serving,
long-read slicing) + AndroidRfcomm classic (discovery receivers,
discoverable intent, socket connect/accept on daemon threads) +
AndroidL2capCompat (cached reflection for API-29 L2CAP against the
API-27 compile jar). Wired via AndroidImplementation.getBluetooth().
Core doc notes: fireScanFailed cleanup contract + Android
getConnectedPeripherals filter approximation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Scripted FakeBluetooth/FakeBluetoothLE/FakeBlePeripheral fakes
(synchronous-on-demand: nothing completes until the test drains the
pending-op queue; scriptable failures) installed via
TestCodenameOneImplementation.setBluetooth + reset hook. Ten suites:
fallback contract (no-op base instances), BluetoothUuid, raw
advertisement parsing, scan demux (multi-handle filters, dedup,
last-stop platform teardown, scan-failed fan-out), connection
lifecycle + service cache, op-queue serialization (cancel-skip,
timeout without wedging), CCCD subscribe multiplexing, EDT delivery
of every callback type, error mapping, adapter lifecycle. Full module:
4033 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Headless SimulatedBluetoothStack (single-scheduler engine: peripheral
registry, per-op failNext deques, latency, event log, scan feeds,
GATT client ops, app-side GATT server/advertising/L2CAP/RFCOMM
registries, virtual central + virtual RFCOMM/L2CAP clients over piped
streams, reset) with Auto/Manual schedulers (virtual clock, pump/
advance) and fluent VirtualPeripheral/Service/Characteristic/Descriptor
builders. CN1 adapters behind the BleBackend SPI seam (future native
btleplug backend plugs in there; switchBackend + cn1.bluetooth.backend
property), SimBlePeripheral/SimGattServer/classic/RFCOMM/L2CAP
adapters, JavaSEBluetooth with JavaSENfc-style sim flags, public
BluetoothSimulator scriptable facade, JavaSEPort.getBluetooth wiring.
45 new deterministic tests incl. source-scan guards (no Swing/wall-
clock in the stack core); javase module green at 124 tests.
ScanResult.getTimestamp doc softened to ordered-not-wall-clock.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IOSBluetooth (request-id registry, CB-authorization permissions, lazy
adapter monitor, dead-code-elimination touch block ordered after field
init), IOSBluetoothLE/IOSBlePeripheral (13 do* methods over 32 new
IOSNative declarations; aggregated one-shot GATT DB transfer; MTU from
maximumWriteValueLength; iOS-semantics no-op bond/priority),
IOSGattServer (local-id-keyed model, parked-CBATTRequest handle table,
isReadyToUpdateSubscribers notify retry), advertising with Java-side
timeout, L2CAP blocking streams over native handles. CN1Bluetooth.h/.m
(~1900 lines, no ARC): whole body under #ifdef CN1_INCLUDE_BLUETOOTH
with linkable #else stubs for all trampolines, serial dispatch queue,
controller-lifetime CBPeripheral retention, NSString/NSData conversion
before dispatch_async, tvOS/watchOS peripheral-role gating; commented
define added to CodenameOne_GLViewController.h (flipped by builders).
Both define paths compile (clang -fsyntax-only vs iphonesimulator +
appletvsimulator SDKs); maven ios module green.
Core follow-ups from port friction: ScanFilter getters (ports push
filters to platform scanners - iOS background scanning needs them),
platform notes on AdvertiseData (iOS drops manufacturer/service data)
and GattServerListener descriptor requests (never fire on iOS).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hand-coded Swing debugger for the virtual Bluetooth stack: peripheral/
GATT JTree with an App-as-Peripheral root (advertising state, published
services, central subscriptions), card-based editors (adapter toggle,
latency, 12-op x BluetoothError failure injection, backend selector;
peripheral live editing + remote-disconnect; characteristic hex/UTF-8
value editor + push-notify), capped event-log table fed by
StackEventListener with coalesced tree refreshes and expansion
preservation, BluetoothSim.* prefs. Simulate-menu items in both menu
constructions. BluetoothSimulatorHooks (8 hooks incl. API-only
primeReadFailure; canonical demo peripheral AA:BB:CC:DD:EE:01 shared
with the toolbar) published via META-INF/codenameone/
simulator-hooks.properties -> CN.execute("bluetooth:itemN").
6 headless window tests; SimulatorHookLoaderTest fixture now hides
classpath hook files so its exact-count assertions stay hermetic.
javase module green at 130 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ports/JavaSE/native/cn1-ble-helper: btleplug 0.11/tokio Rust helper
speaking line-delimited JSON (v1 protocol in PROTOCOL.md: capabilities
handshake, scan with full advertisement payloads, connect/discover/
read/write char+descriptor/subscribe/rssi(last-seen)/state events,
typed error codes; central-only). NativeBleBackend implements the
BleBackend SPI: helper resolution (cn1.bluetooth.helperPath property ->
bundled OS-keyed classpath resource -> PATH), reader/writer threads,
requestId correlation, crash-mid-flight -> typed IO_ERROR + adapter
UNSUPPORTED, shutdown hook; NativeBlePeripheral maps do* onto protocol
commands with the canonical-GATT-model pattern. JavaSEBluetooth
'native' switch now constructs it (runtime-toggleable vs simulator).
Packaging: optional cn1-binaries/ble/{macos,linux,windows} resource
mapping + opt-in -Pbuild-ble-helper cargo profile (default build
cargo-free). Tests: wire codec, resolution order, fake-helper
subprocess lifecycle (deterministic), hardware smoke gated on
-Dcn1.ble.smoke=true (verified live on this machine: POWERED_ON, 6
peripherals). javase module: 151 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JSBluetooth/JSBluetoothLE/JSBlePeripheral route the core SPI through 12
__cn1_bt_*__ host calls registered in browser_bridge.js (device/
attribute handle tables main-thread side; worker holds string ids +
iids). Chooser-as-scan: startScan opens the browser device chooser
built from the ScanSettings filters (all filter service uuids collected
into optionalServices), delivers the single user-picked device as one
ScanResult, then stays silently active; chooser cancel ->
fireScanFailed(USER_CANCELED). One-shot full GATT DB discovery per
connect; notifications copy DataView bytes before posting through the
worker-callback channel; gattserverdisconnected -> typed connection
event. requestDevice user-gesture handling: immediate attempt (relayed
activation usually holds), SecurityError parks the job for the next
real capture-phase gesture, 30s -> typed USER_CANCELED. All port.js
bindings null-safe against stale translator-jar browser_bridge (typed
NOT_SUPPORTED fallback, never a raw throw). MTU fixed 512, RSSI/bond/
L2CAP/peripheral/classic typed NOT_SUPPORTED. Verified: javac against
real jars, node --check, and a full local hellocodenameone bundle
build with the classes registered in translated_app.js.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New Bluetooth.asciidoc (NFC-chapter template): capability matrix per
platform, six include-backed quick starts (scan+connect, read/write,
notifications, peripheral mode, L2CAP, classic RFCOMM), auto-injected
permissions + build-hints tables (neverForLocation beacon callout),
simulator section (Bluetooth Simulation window, bluetooth:itemN hooks
table, BluetoothSimulator scripting as a simulator-only listing,
record-and-replay), testing section, closing platform-behaviour matrix.
Six BluetoothJava00NSnippet classes compile in the docs/demos build.
Validated: snippet-include gate (616 blocks), demos process-classes,
asciidoctor, vale, LanguageTool, capitalization - all clean.
Simulator screenshots (bluetooth-simulator-devices/log.png) to follow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Captured from the live Swing window: device/GATT tree with the
characteristic value editor, and the adapter card (failure injection,
backend selector, latency) over a populated event log.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BluetoothFixture (versioned JSON model, hand-rolled writer + core
JSONParser reader), FixtureScrambler (seeded pure function: stable
synthetic ids, length-class-preserving names, same-length randomized
payloads with company IDs kept, SIG UUIDs verbatim, memoized custom-
UUID substitution, findLeaks no-PII invariant), FixtureRecorder (drives
any BleBackend with bounded waits; optional GATT capture degrades to
scan-only), FixtureCaptureMain CLI + scripts/bluetooth/
capture-fixture.sh (scrambles always; exit-3 leak gate; real ids to
stderr only). SimulatedBluetoothStack.loadFixture replays the RSSI/
advertisement timeline on the stack scheduler (deterministic under
ManualScheduler); BluetoothSimulator.loadFixture overloads; debug
window gains Record from real hardware (dialog -> native backend ->
scrambled import + optional save). VirtualPeripheral service-data/
tx-power; core AdvertisementData.getServiceDataUuids().
Shipped fixtures captured on real hardware via the Rust helper:
ambient-scan.json (17 devices, rich RSSI timelines, Apple/Samsung
manufacturer data, SIG service data) + ambient-scan-2.json (8 devices;
GATT capture failed on all nearby devices - random-address privacy -
so a second scan-only trace per spec), both leak-checked. 18 new
deterministic tests incl. shipped-fixture invariant + full
record->scramble->replay->GATT-read loop; javase module: 169 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CN1WebAuthn.m defined its three non-void trampolines without the
ParparVM _R_<returnType> suffix (webauthnSupported__ vs the generated
header's webauthnSupported___R_boolean etc.), so any app actually using
WebAuthn would fail linking once CN1_INCLUDE_WEBAUTHN flips. Renamed
all six definitions (real + stub paths) to the generated convention.
IOSNfc's dead-code-elimination touch block ran before the REQUESTS/
TAGS field initializers (static init is textual order), making the
sentinel callbacks synchronize on null during class init; moved the
block after the fields with a comment pinning the order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 395 total, 0 failed, 14 skipped

Benchmark Results

  • Execution Time: 14818 ms

  • Hotspots (Top 20 sampled methods):

    • 22.52% java.util.ArrayList.indexOf (299 samples)
    • 8.21% com.codename1.tools.translator.BytecodeMethod.addToConstantPool (109 samples)
    • 3.61% java.lang.StringBuilder.append (48 samples)
    • 2.79% com.codename1.tools.translator.ByteCodeClass.hasDeclaredMethod (37 samples)
    • 2.48% com.codename1.tools.translator.Parser.generateClassAndMethodIndexHeader (33 samples)
    • 2.18% com.codename1.tools.translator.Parser.cn1EnsureSubclassIndex (29 samples)
    • 1.96% sun.nio.ch.FileDispatcherImpl.read0 (26 samples)
    • 1.88% org.objectweb.asm.tree.analysis.Analyzer.findSubroutine (25 samples)
    • 1.66% com.codename1.tools.translator.MethodDependencyGraph.getCallers (22 samples)
    • 1.58% com.codename1.tools.translator.bytecodes.Invoke.resolveDirectTarget (21 samples)
    • 1.58% java.util.HashMap.put (21 samples)
    • 1.43% java.util.TreeMap.getEntry (19 samples)
    • 1.43% java.lang.String.equals (19 samples)
    • 1.36% com.codename1.tools.translator.BytecodeMethod.appendCMethodPrefix (18 samples)
    • 1.20% org.objectweb.asm.ClassReader.readCode (16 samples)
    • 1.05% com.codename1.tools.translator.BytecodeMethod.equals (14 samples)
    • 1.05% java.lang.System.identityHashCode (14 samples)
    • 1.05% com.codename1.tools.translator.ByteCodeClass.markDependent (14 samples)
    • 0.98% org.objectweb.asm.tree.analysis.Analyzer.analyze (13 samples)
    • 0.98% com.codename1.tools.translator.Parser.cullMethods (13 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

shai-almog and others added 2 commits July 17, 2026 14:31
The ble/ resource mapping copies the whole directory; exclude **/*.md so
the maintainer docs that live next to the binaries in cn1-binaries stay
out of every app's jar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The check-copyright-headers gate flagged 14 files. Thirteen genuinely
lacked the GPLv2 + Classpath Exception header: the five new bluetooth
package-info files (which followed com/codename1/nfc/package-info.java,
predating the gate), the six new developer-guide snippets, and two
pre-existing files this branch touches (TestCodenameOneImplementation,
browser_bridge.js) that the gate checks because they are modified.

The fourteenth, AndroidImplementation.java, has a complete and correct
Codename One header and was failing purely because the file is CRLF: the
stray carriage return defeated both the leading '/*' comparison and the
'$'-anchored copyright-line regex. Rewriting a 15k-line file's line
endings to satisfy the checker would be pure churn, so the checker now
strips CR before matching -- it judges header text, not line endings.
Verified it still rejects CRLF files with missing or bogus headers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 145 screenshots: 145 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 71ms / native 5ms = 14.2x speedup
SIMD float-mul (64K x300) java 61ms / native 5ms = 12.2x speedup
SIMD kernel correctness PASS (native result == scalar reference)

@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.

Native Android coverage

  • 📊 Line coverage: 11.16% (11872/106403 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 10.05% (58918/586427), branch 5.15% (2766/53660), complexity 4.94% (2772/56100), method 7.41% (2133/28797), class 11.60% (476/4102)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 11.16% (11872/106403 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 10.05% (58918/586427), branch 5.15% (2766/53660), complexity 4.94% (2772/56100), method 7.41% (2133/28797), class 11.60% (476/4102)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 158ms / native 73ms = 2.1x speedup
SIMD float-mul (64K x300) java 74ms / native 53ms = 1.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 62.000 ms
Base64 CN1 decode 74.000 ms
Base64 native encode 273.000 ms
Base64 encode ratio (CN1/native) 0.227x (77.3% faster)
Base64 native decode 250.000 ms
Base64 decode ratio (CN1/native) 0.296x (70.4% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 146 screenshots: 146 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 146 screenshots: 146 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog shai-almog closed this Jul 17, 2026
@shai-almog shai-almog reopened this Jul 17, 2026
@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 145 screenshots: 145 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 57ms / native 3ms = 19.0x speedup
SIMD float-mul (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)

@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 145 screenshots: 145 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 64ms / native 5ms = 12.8x speedup
SIMD float-mul (64K x300) java 62ms / native 4ms = 15.5x speedup
SIMD kernel correctness PASS (native result == scalar reference)

@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 176 screenshots: 176 matched.
✅ JavaScript-port screenshot tests passed.

macOS ships a universal Mach-O binary covering x86_64 and arm64, but ELF
and PE have no fat-binary format, so a single per-OS binary left Linux on
ARM (Raspberry Pi, ARM servers) and Windows on ARM (Snapdragon X,
Surface) with no helper at all -- they silently reported the native
backend unavailable.

helperResourcePath now takes os.arch and resolves
ble/{linux,windows}/{x64,arm64}/; macOS keeps its single universal file.
normalizeArch maps the aliases (amd64/x86_64/x64 -> x64,
aarch64/arm64 -> arm64) and returns null for architectures with no
bundled binary (32-bit x86/ARM), which falls through to the PATH lookup
instead of failing; the resolution trace now reports the unmatched
os.arch. Tests cover the arch keying, macOS ignoring arch, the alias
matrix and the PATH fall-through: javase module 172 green.
Binaries land in the companion cn1-binaries PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
shai-almog and others added 3 commits July 17, 2026 17:53
Three failure classes the Ant/CLDC11 build and SpotBugs caught but the
Maven build did not (Maven compiles core against the full JDK and my
mvn test runs never invoked SpotBugs):

Device API surface (CLDC11.jar is signature-only): Character.forDigit
does not exist on device -> BluetoothUuid uses an explicit hex[] table;
java.util.Timer has only the no-arg constructor -> GattOperationQueue
schedules each timeout on its own Timer (mirroring Display.setTimeout)
and cancels both task and timer, and BlePeripheral tracks its connect
timer to cancel it. This broke the Ant core build and every downstream
iOS/Mac native job (ParparVM emitted a call to the absent
java_lang_Character_forDigit).

SpotBugs (runs in the core-unittests verify phase, not in mvn test):
five SIC_INNER_SHOULD_BE_STATIC_ANON dispatch Runnables/TimerTask moved
into static helpers so they carry no synthetic outer reference (the NFC
code's established pattern), and one DM_DEFAULT_ENCODING - the UTF-8
advertisement decoder now throws instead of falling back to the
platform default encoding, which would corrupt names.

core Ant build green; core-unittests spotbugs 0 bugs; 4033 + 172 tests
still green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IOSBluetooth.bytesToHex used Character.forDigit, which vm/JavaAPI does
not define, so ParparVM would emit a call to an undeclared
java_lang_Character_forDigit C function and fail the Xcode build --
the same break the core BluetoothUuid fix addressed, in the iOS port.
The iOS jobs failed on core BluetoothUuid first, so this would have
surfaced on the next run; caught it by scanning the port Java against
vm/JavaAPI. Character.digit (used in hexToBytes) IS present, so that
path is unchanged. new Timer(true) in IOSBleAdvertisement is fine --
vm/JavaAPI declares the daemon constructor (only CLDC11, which core
compiles against, lacks it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nine anonymous inner classes (permission/enable/discoverable Runnables,
an AdvertiseCallback, RFCOMM connect/listen/L2CAP-server Thread bodies,
the discovery ClassicDiscovery + BroadcastReceiver) used only static
references and captured final locals, never the enclosing instance, so
each held an unused synthetic outer reference. Extracted every one into
a private static factory taking the captured locals as final params
(the BlePeripheral.connectTimeoutTask pattern); behavior, threading and
ordering unchanged. android module SpotBugs: 9 -> 0, matching the
port's clean baseline. The android spotbugs is report-only
(failOnError=false) so this didn't fail CI, but it was a regression in
the quality report.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@shai-almog shai-almog closed this Jul 17, 2026
@shai-almog shai-almog reopened this Jul 17, 2026
# Conflicts:
#	vm/ByteCodeTranslator/src/javascript/browser_bridge.js
@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 143 screenshots: 143 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 148 screenshots: 148 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 265 seconds

Build and Run Timing

Metric Duration
Simulator Boot 61000 ms
Simulator Boot (Run) 1000 ms
App Install 13000 ms
App Launch 2000 ms
Test Execution 716000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD float-mul (64K x300) java 70ms / native 3ms = 23.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 157.000 ms
Base64 CN1 decode 119.000 ms
Base64 native encode 255.000 ms
Base64 encode ratio (CN1/native) 0.616x (38.4% faster)
Base64 native decode 342.000 ms
Base64 decode ratio (CN1/native) 0.348x (65.2% faster)
Base64 SIMD encode 63.000 ms
Base64 encode ratio (SIMD/CN1) 0.401x (59.9% faster)
Base64 SIMD decode 46.000 ms
Base64 decode ratio (SIMD/CN1) 0.387x (61.3% faster)
Base64 encode ratio (SIMD/native) 0.247x (75.3% faster)
Base64 decode ratio (SIMD/native) 0.135x (86.5% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 6.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.500x (50.0% faster)
Image applyMask (SIMD off) 47.000 ms
Image applyMask (SIMD on) 38.000 ms
Image applyMask ratio (SIMD on/off) 0.809x (19.1% faster)
Image modifyAlpha (SIMD off) 38.000 ms
Image modifyAlpha (SIMD on) 30.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.789x (21.1% faster)
Image modifyAlpha removeColor (SIMD off) 38.000 ms
Image modifyAlpha removeColor (SIMD on) 33.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.868x (13.2% faster)

@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Apple Watch (watchOS / Core Graphics)

Compared 216 screenshots: 215 matched, 1 missing actual.

  • DesktopMode — missing actual screenshot. Actual screenshot missing (test did not produce output).

    No preview available for this screenshot.

shai-almog and others added 4 commits July 17, 2026 20:25
CI's static-analysis gate (generate-quality-report.py forbidden_pmd_rules)
failed on 65 forbidden PMD violations in the new core Bluetooth code --
a gate mvn test does not run. Fixed: 53 MissingOverride (added @OverRide
throughout, incl. interface impls -- the core Ant build is javac.source=1.6
so this compiles), 4 ForLoopCanBeForeach (indexed loops -> enhanced-for
where the index was unused), 3 UnnecessaryConstructor (removed empty
holder/no-op constructors; the compiler-generated default is identical),
3 CompareObjectsWithEquals (deliberate identity == on in-flight op/
registration objects, kept with the repo's //NOPMD convention),
1 PreserveStackTrace (chain the cause in AdvertisementData.utf8),
1 UnnecessaryImport. PMD re-run: 0 forbidden; ant jar + core-unittests
4033 tests + 0 SpotBugs green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New maven module codenameone-native-ble (package com.codename1.bluetooth.
helper, dep: core only): the transport-agnostic cn1-ble-helper client so
the native Win32/Linux ports can reuse it. HelperTransport abstraction
(start/readLine/writeLine/close), BleBackend SPI, transport-agnostic
HelperBleBackend (Wire JSON protocol + PendingOp + reader thread) and
HelperBlePeripheral, plus MockHelperTransport for subprocess-free tests.
All runtime types constrained to vm/JavaAPI (ParparVM translates the
module for the native ports): ConcurrentHashMap -> synchronized HashMap/
HashSet, java.util.Base64 -> hand-rolled translatable codec in Wire,
classpath-extraction HelperBinaryResolver moved to JavaSE (JVM-only).
JavaSE refactor (unchanged behaviour, 180 tests green): NativeBleBackend
is now a thin HelperBleBackend subclass supplying a ProcessTransport (the
one place ProcessBuilder lives on JavaSE); SimulatorBleBackend/fixtures
repointed to the module. Module: 14 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
These native ports have full threading but no java.lang.ProcessBuilder
(absent from vm/JavaAPI), so add a small child-process bridge for the
shared cn1-ble-helper client to spawn/talk to the helper: procSpawn/
procRead/procWrite/procCloseStdin/procClose/procIsAlive on LinuxNative +
WindowsNative, implemented in nativeSources over posix_spawn+pipes
(cn1_linux_subprocess.c) and CreateProcess+anonymous pipes
(cn1_windows_subprocess.c). Modeled line-for-line on the existing socket
bridge: ParparVM mangled C names, CN1_YIELD_THREAD/RESUME around every
blocking syscall + a keep-alive anchor on byte[] buffers, opaque long
handles, manual malloc/free. Java compiles on both ports (mvn -pl
maven/linux,maven/windows compile); the C is CI-native-build verified
(Linux .c passes gcc -fsyntax-only against stubbed headers).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wires HelperBluetooth (the shared module's transport-agnostic facade)
over each port's native subprocess bridge, giving the native desktop
ports real BLE central (scan / connect / GATT client / notifications)
via the cn1-ble-helper subprocess -- btleplug -> BlueZ on Linux, WinRT
on Windows. Linux/WindowsBluetoothTransport frame the shared
NativeSubprocessTransport onto the port's proc* natives;
Linux/WindowsImplementation.getBluetooth() returns HelperBluetooth
(replacing the no-op fallback these ports inherited). The helper is
resolved by the cn1.bluetooth.helperPath property else the bare name via
the OS PATH search (posix_spawnp/CreateProcess). Peripheral/L2CAP/classic
report unsupported (btleplug is central-only). jar-with-dependencies
folds codenameone-native-ble into each port jar for ParparVM
translation; both ports compile locally. The module's FixtureReplayE2ETest
drives a scrambled real capture (all 8 devices) through this exact helper
stack + a connect/discover/read over a mock transport -- no hardware.

Fixes the earlier gap where these ports were stubbed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 142 screenshots: 142 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 221 seconds

Build and Run Timing

Metric Duration
Simulator Boot 53000 ms
Simulator Boot (Run) 0 ms
App Install 12000 ms
App Launch 2000 ms
Test Execution 734000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 55ms / native 3ms = 18.3x speedup
SIMD float-mul (64K x300) java 60ms / native 4ms = 15.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 155.000 ms
Base64 CN1 decode 115.000 ms
Base64 native encode 370.000 ms
Base64 encode ratio (CN1/native) 0.419x (58.1% faster)
Base64 native decode 217.000 ms
Base64 decode ratio (CN1/native) 0.530x (47.0% faster)
Base64 SIMD encode 47.000 ms
Base64 encode ratio (SIMD/CN1) 0.303x (69.7% faster)
Base64 SIMD decode 44.000 ms
Base64 decode ratio (SIMD/CN1) 0.383x (61.7% faster)
Base64 encode ratio (SIMD/native) 0.127x (87.3% faster)
Base64 decode ratio (SIMD/native) 0.203x (79.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 7.000 ms
Image createMask (SIMD on) 1.000 ms
Image createMask ratio (SIMD on/off) 0.143x (85.7% faster)
Image applyMask (SIMD off) 36.000 ms
Image applyMask (SIMD on) 27.000 ms
Image applyMask ratio (SIMD on/off) 0.750x (25.0% faster)
Image modifyAlpha (SIMD off) 32.000 ms
Image modifyAlpha (SIMD on) 31.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.969x (3.1% faster)
Image modifyAlpha removeColor (SIMD off) 37.000 ms
Image modifyAlpha removeColor (SIMD on) 32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.865x (13.5% faster)

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

The com.codename1.bluetooth.helper client (HelperBleBackend, transports,
Wire codec, HelperBluetooth/LE facades) lived in the maven-only
native-ble module. The Ant core build compiles the native ports against
CLDC11 and cannot see Maven modules, so JavaSEBluetooth and the native
Linux/Windows ports failed with "package com.codename1.bluetooth.helper
does not exist". Core is the only artifact both build systems share, so
the helper now lives there.

- Relocate the 10 helper client classes into
  CodenameOne/src/com/codename1/bluetooth/helper/ (compiled against the
  CLDC11 device API: no Thread.setDaemon, hand-rolled Base64 via Wire).
- Move the mock transport + Wire/backend/fixture-replay tests into
  core-unittests; delete the native-ble module and drop its registration
  from the root pom and the javase/linux/windows port poms.
- Fix 4 SpotBugs SIC_INNER findings surfaced now that the helper is
  scanned under core-unittests: share a static NO_OP PendingOp for
  fire-and-forget scanStop, make booleanOp static, and extract the
  duplicated characteristic/descriptor read completion into a static
  bytesOp factory.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 147 screenshots: 147 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 292 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 65ms / native 2ms = 32.5x speedup
SIMD float-mul (64K x300) java 70ms / native 3ms = 23.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 149.000 ms
Base64 CN1 decode 112.000 ms
Base64 native encode 552.000 ms
Base64 encode ratio (CN1/native) 0.270x (73.0% faster)
Base64 native decode 263.000 ms
Base64 decode ratio (CN1/native) 0.426x (57.4% faster)
Base64 SIMD encode 46.000 ms
Base64 encode ratio (SIMD/CN1) 0.309x (69.1% faster)
Base64 SIMD decode 42.000 ms
Base64 decode ratio (SIMD/CN1) 0.375x (62.5% faster)
Base64 encode ratio (SIMD/native) 0.083x (91.7% faster)
Base64 decode ratio (SIMD/native) 0.160x (84.0% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 8.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.250x (75.0% faster)
Image applyMask (SIMD off) 47.000 ms
Image applyMask (SIMD on) 36.000 ms
Image applyMask ratio (SIMD on/off) 0.766x (23.4% faster)
Image modifyAlpha (SIMD off) 44.000 ms
Image modifyAlpha (SIMD on) 54.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.227x (22.7% slower)
Image modifyAlpha removeColor (SIMD off) 64.000 ms
Image modifyAlpha removeColor (SIMD on) 50.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.781x (21.9% faster)

shai-almog and others added 3 commits July 17, 2026 22:05
The check-package-info gate requires every documented package under
CodenameOne/src to carry a package-info.java. The helper client only
came under that scan once it moved from the native-ble module into core,
so the gate failed with "com/codename1/bluetooth/helper: missing
package-info.java". Document the package as internal port-support (not
public API) to match the rest of the bluetooth tree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Java-25 markdown-docs gate (validate-java25-markdown-docs.sh) rejects
classic /** javadoc markers anywhere under CodenameOne/src. The helper
client used /** because the native-ble module it came from was not
scanned; now that it lives in core, convert its doc comments to the ///
markdown style the rest of the tree uses. Comment-only change -- core
still compiles and the gate passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The bluetooth.helper client came from the native-ble module, whose PMD
config was laxer than the forbidden-rule gate that scans CodenameOne/src.
Once relocated into core it tripped 79 forbidden violations:

- 77 MissingOverride: add @OverRide to every method that implements an
  interface (BleBackend, HelperTransport, PendingOp/ScanSink anonymous
  classes) or overrides a BlePeripheral SPI method. Core compiles with
  -source 1.6, so @OverRide on interface implementations is legal; the
  clean compile confirms none were added to a non-overriding method.
- 2 CompareObjectsWithEquals: the transport-identity and scan-sink-identity
  checks are deliberate reference comparisons, suppressed with
  //NOPMD CompareObjectsWithEquals.

Also fix a genuine test race in HelperBleBackendMockTransportTest: the
connect future errors/completes synchronously, but the peripheral's
CONNECTING->DISCONNECTED/CONNECTED transition is delivered on the EDT,
after isDone() is already true. On the helper-crash path failAllPending()
fails the connect op before handleHelperDied() fires DISCONNECTED, so a
test thread that reads getConnectionState() right after isDone() can still
observe CONNECTING. Await the state transition instead of asserting it
synchronously, matching the adapter/state awaits already used in this
suite. Production behaviour is correct -- an app's own connect callback
runs on the EDT after the state has settled. Verified 8/8 deterministic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant