Skip to content

Nearby devices: UWB ranging, companion association and nearby transport - #5589

Merged
shai-almog merged 94 commits into
masterfrom
feature/nearby-devices
Aug 24, 2026
Merged

Nearby devices: UWB ranging, companion association and nearby transport#5589
shai-almog merged 94 commits into
masterfrom
feature/nearby-devices

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

The framework can talk to a heart-rate strap, commission a Matter light and drive a watch, but it cannot answer "how far away is that thing, and in which direction", cannot use the OS's own companion-device association flow, and cannot move a payload to the phone next to it without hand-rolling sockets.

A sweep for UWB, NearbyInteraction, MultipeerConnectivity, CompanionDeviceManager, AccessorySetupKit and Nearby Connections across this repo and BuildDaemon returns exactly one hit: a doc comment in WiFiDirect.java saying the whole area is out of scope. This closes that.

What this adds

com.codename1.nearby, in three packages:

Package What it answers Backed by
.ranging how far away, and which way Nearby Interaction / Jetpack UWB
.companion which device is yours AccessorySetupKit / CompanionDeviceManager
.transport how to send it something MultipeerConnectivity / Nearby Connections

Three packages rather than one because the build scanner uses package prefix as the permission boundary and has no way to express an exclusion. An app that only wants to know how far away its keyring tag is gets no Play Services dependency, no local-network prompt and no companion permissions. Referencing com.codename1.nearby itself costs nothing.

The shape follows com.codename1.home: entry points that never return null, an inert fallback rather than a null check at every call site, and a flat primitives-and-strings SPI so an Objective-C port never constructs a Java object.

Decisions worth defending

A session is prepared, then started. Both platforms require the two devices to swap a token over a channel they already share before any radio ranging can begin, so there is no honest one-call form. A session ranges exactly one peer — a hard limit of Apple's NINearbyPeerConfiguration, not a simplification.

The canonical measurement is azimuth and elevation in degrees. Android reports those directly; iOS reports a unit vector and the port derives the angles with atan2(x, -z), so the same application code reads the same on both. There is no zero-argument getDistance() — meters read as feet is the accident HealthQuantity and TraitValue already exist to prevent. Nothing in the API requires a cast, since a failed cast does not throw under ParparVM.

The transport does not cross ecosystems, and says so. Nearby Connections is Android-to-Android and MultipeerConnectivity is Apple-to-Apple. The package documentation states that plainly and names the two things that do work across the divide — BLE L2CAP channels and Bonjour plus sockets — because an API that looked portable and silently never found the peer would be worse than an honest limitation.

Desktop, simulator and JavaScript get a real implementation, not a stub, reporting LOCAL_ONLY. Almost none of a ranging feature is about radios, and a port that answered NOT_SUPPORTED would make all of it testable only on a pair of phones. It never completes inline, and its peers really move — a bounded random walk seeded from the bridge's own session counter, so the Nth session of a fresh bridge walks the same path every run. It will not drop a peer or suspend a session behind your back; those are controls, because a simulation that fired them unpredictably would make every test using it flaky.

Two honest limitations, documented at the point of the code. startObservingPresence returns false on iOS: AccessorySetupKit reports an accessory being added to the app's set, which is not the same event as it coming into range. And MultipeerConnectivity refuses a service type outside 1-15 lowercase characters by raising, so a reverse-DNS id that is legal on Android is folded to fit — and the builder logs the result, because com.example.chat and com.example.charts fold to the same thing.

Bugs the verification caught

Each of these would have shipped silently:

  • NINearbyObject.distance/.direction are scalars with NaN sentinels in Objective-C, not the optionals they are in Swift. A != nil test compiles and always takes the has-a-value branch — the app renders an arrow pointing at NaN.
  • AssociationInfo.getDeviceMacAddressAsString() does not exist on the public Android SDK; the public one returns a MacAddress. Nothing in this repo compiles that package (same as impl/android/ar and .cipher), so only a hand-rolled compile against android-35 found it.
  • ConnectionRequest collided with com.codename1.io.ConnectionRequest — an app doing networking and nearby transport could not import both. Renamed IncomingConnection. Found by writing the first guide snippet.
  • WatchNativeBuilder's own guard test failed and named the three frameworks it did not know how to classify for the watch slice.
  • SpotBugs found an indexOf(...) > 0 that worked only because both matching method names share a prefix; the cast-semantics gate found a cast inside a catch (Throwable).

Verification

  • 5264 core tests, 903 plugin tests, 37 catalog tests — all green. 80 of them are new.
  • SpotBugs zero findings in core-unittests, ios, android and codenameone-maven-plugin.
  • check-cast-semantics, check-native-signatures (0 fatal), check-copyright-headers, check-package-info, check-since-tags all pass.
  • Prose gates: vale 0 errors / 0 warnings, LanguageTool 0 matches over the rendered guide, asciidoctor clean at --failure-level WARN, snippet validator clean.
  • A real iOS app build. The generated Xcode project has all four defines flipped, all three frameworks linked and the right plist keys; CN1Nearby.m compiles clean against the real generated headers, whose 22 callback signatures match the hand-written ones exactly. Every configuration (none, each of the three alone, all three) was also syntax-checked for arm64-apple-ios and arm64-apple-tvos with zero errors and zero warnings.

What is not verified

No hardware. Nothing ran on a device — no two UWB phones, no UWB tag, no iOS 18 accessory. Distance and direction correctness against real radios is untested, and so is the association picker.

The BuildDaemon half is a separate PR; without it, cloud builds will not match local ones.

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com

🤖 Generated with Claude Code

shai-almog and others added 8 commits August 23, 2026 11:06
…simulation

The framework can talk to a heart-rate strap, commission a Matter light and
drive a watch, but it cannot answer "how far away is that thing, and in which
direction", cannot use the OS's own companion-device association flow, and
cannot move a payload to the phone next to it without hand-rolling sockets. A
sweep for UWB, NearbyInteraction, MultipeerConnectivity, CompanionDeviceManager
and Nearby Connections across this repo returns exactly one hit, and it is a
doc comment in WiFiDirect saying the whole area is out of scope.

This is the portable half of fixing that: the model, the three facades, the
bridge each port will implement, the wire format between them, and a real
simulated implementation for the desktop and JavaScript ports. No native code
and nothing in the builders, so no behaviour changes on any device -- the base
CodenameOneImplementation returns no bridge and every entry point degrades to
NOT_SUPPORTED.

The shape follows com.codename1.home: entry points that never return null, an
inert fallback rather than a null check at every call site, and a flat
primitives-and-strings SPI so an Objective-C port never constructs a Java
object.

Four decisions worth defending.

Three packages, not one. The build server decides what native machinery an app
gets by scanning bytecode for package prefixes and has no way to express an
exclusion, so the package boundary has to be the permission boundary. Ranging
costs a framework and two privacy strings on iOS and a Jetpack dependency on
Android; transport costs Play Services and the whole Bluetooth and Wi-Fi
permission set. An app that only wants to know how far away its keyring tag is
must not pay for the second.

A session is prepared, then started. Both platforms require the two devices to
swap a token over some channel they already share before any radio ranging can
begin, so there is no honest one-call form. Preparing yields the local token;
starting takes the peer's. A session ranges exactly one peer, which is a hard
limit of Apple's NINearbyPeerConfiguration rather than a simplification.

The canonical measurement is azimuth and elevation in degrees. Android reports
those directly; iOS reports a unit vector, and the port derives the angles from
it with atan2(x, -z) so both platforms answer the same question. There is no
zero-argument getDistance, because metres read as feet is the accident
HealthQuantity and TraitValue already exist to prevent. Nothing in the API
requires a cast, since a failed cast does not throw under ParparVM.

The transport does not cross ecosystems, and says so. Nearby Connections is
Android-to-Android and MultipeerConnectivity is Apple-to-Apple. The package
documentation states that plainly and names the two things that do work across
the divide -- BLE L2CAP channels and Bonjour plus sockets -- because an API
that looked portable and silently never found the peer would be worse than an
honest limitation.

The desktop, simulator and JavaScript ports get LocalNearbyBridge rather than a
stub, reporting LOCAL_ONLY. Almost none of a ranging feature is about radios,
and a port that answered NOT_SUPPORTED would make all of it testable only on a
pair of phones. It follows the two rules a mock would not: it never completes
inline, and its peers really move -- a bounded random walk seeded from the
bridge's own session counter, so the Nth session of a fresh bridge walks the
same path every run whatever ran before it. It will not drop a peer or suspend
a session behind your back; those are controls the simulator panel drives,
because a simulation that fired them unpredictably would make every test using
it flaky.

PendingMap moves from com.codename1.impl.home to com.codename1.impl.async and
is shared rather than copied, the way EdtResult and OneShot already were.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Carries com.codename1.nearby onto Nearby Interaction, MultipeerConnectivity
and AccessorySetupKit. Behaviour is unchanged for every existing app: the
three CN1_NEARBY_* defines are commented out, and the #else half of
CN1Nearby.m answers every native with "unsupported" so a build that never
touched the package links exactly as before and carries none of the three
frameworks' symbols.

Three gates, not one. An app that only wants to know how far away its tag is
must not link MultipeerConnectivity, because linking it obliges
NSLocalNetworkUsageDescription and puts a local-network prompt in front of a
user who never asked for one. So CN1_NEARBY_RANGING, CN1_NEARBY_TRANSPORT and
CN1_NEARBY_COMPANION are separate, the three isSupported answers are separate,
and a tvOS build reports a working transport with no ranging rather than
reporting the whole feature missing.

Four things here that would have been silent failures.

NINearbyObject.distance and .direction are plain scalars in Objective-C, not
the optionals they are in Swift, and "not available" is signalled in-band as
NaN. Testing them for nil compiles and always takes the has-a-value branch, so
the app renders an arrow pointing at NaN. Both are NaN-tested instead.

The direction vector is folded to azimuth and elevation with atan2(x, -z),
because Apple's frame puts forward at negative z. Android reports those two
angles directly, and this is the conversion that makes the same application
code read the same on both platforms.

fromNSString, toNSString and nsDataToByteArr live in IOSNative.m and no shared
header exports them, so they are declared per translation unit as
CN1Bluetooth.m and CN1Camera.m do. Without that the file compiles with an
implicit declaration and reads the result out of the wrong register.

A session that dies before its start request was answered fails that request
explicitly. A caller holding an AsyncResource that never settles has no way to
find out, which is worse than being told the session failed.

Two honest limitations, both documented at the point of the code rather than
buried. startObservingPresence returns false: AccessorySetupKit reports an
accessory being added to or removed from the app's set, which is not the same
event as it coming into range, and reporting those as presence would tell an
app a device in a drawer is nearby. And MultipeerConnectivity refuses a
service type outside 1-15 lowercase characters by raising -- which on a device
is a crash rather than an error an app can show -- so a reverse-DNS service id
that is legal on Android is folded into something legal here.

Every configuration was syntax-checked against the iOS 26.2 SDK for
arm64-apple-ios and arm64-apple-tvos: no defines, each of the three alone, and
all three together, with zero errors and zero warnings in each. All 29 natives
resolve under scripts/check-native-signatures.sh, and all 22 callback call
sites were checked mechanically against the Java methods they name -- the
verifier does not cover that direction, and a typo there is a link error that
only an app build would find.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Carries com.codename1.nearby onto CompanionDeviceManager, Jetpack UWB and
Nearby Connections. Behaviour is unchanged for every existing app: the shell
that ships in the port jar finds no backend and reports all three halves
unsupported.

The implementation is not in the port jar, and cannot be. That jar is compiled
against an android.jar from 2017, and everything this needs is newer:
CompanionDeviceService is API 31, AssociationInfo and getMyAssociations are
API 33, and androidx.core.uwb and play-services-nearby are gradle dependencies
the build only adds for an app that referenced the matching package. So
com.codename1.impl.android.nearby is excluded from the port jar compile in the
three mirrored places -- build.xml, nbproject/project.properties and
maven/android/pom.xml -- ships as sources, and compiles inside the generated
app where a modern compileSdk and those dependencies exist. That is what
com.codename1.impl.android.ar and .cipher already do, and the reason the load
is reflective and its failure is a shrug: for most apps the package is not
there at all.

The reflection is two levels deep, and the second level is what makes the
package boundary real. An app that only associates accessories has neither
gradle dependency, so the coordinator reaches the UWB and Nearby Connections
classes reflectively too -- either can be absent without costing the app the
other two halves.

Ranging goes through androidx.core.uwb:uwb-rxjava3 rather than the base
library. androidx.core.uwb is a Kotlin coroutines API whose prepareSession
returns a Flow and whose session getters are suspend functions; consuming
either from the port's Java means hand-writing a Continuation. The rxjava3
artifact is the same library's own Java-facing wrapper, so this stays ordinary
Java rather than machinery to get subtly wrong.

The Android token carries what the controlee has to join. Apple's Nearby
Interaction negotiates channel and session parameters itself, so its token is
one opaque blob; Android's controller picks the complex channel and the
session id and the controlee has to be told both plus the address. So the
token packs address, channel, preamble, session id and key -- the same shape
RangingToken.forUwbAddress builds for an accessory, which is why accessory
ranging and peer ranging are one code path here.

Three smaller decisions. A GENERIC association asks for no device profile at
all rather than a harmless-looking one, because a profile is a request for
elevated privileges and shows the user a stronger prompt. The association is
read back from the platform after the chooser returns rather than out of the
result intent, because API 33 and later carry an id and a display name the
intent extra does not -- and that id is what disassociate and presence
observation take. And CN1CompanionDeviceService drops an event for an
association the app has stopped watching: the platform keeps watching across
process restarts, so it will deliver events nobody asked for any more.

The port module builds clean and SpotBugs reports zero findings. The jar was
inspected to confirm AndroidNearbyBridge.class is present and the nearby
package ships as .java only, exactly as ar/AndroidARImpl.java does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…pt-in

Turns a class reference into the native machinery it needs, on both
platforms. An app that never names com.codename1.nearby is untouched.

Three catalog entries rather than one, because the three packages cost three
different things and the scanner matches on a prefix with no way to express an
exclusion -- so the package boundary is the only opt-in a developer performs.
Ranging links NearbyInteraction and adds androidx.core.uwb; transport links
MultipeerConnectivity and turns on Nearby Connections; companion links
AccessorySetupKit. An app that only wants to know how far away its tag is does
not get a local-network prompt it cannot explain.

The Android permissions are NOT in that table, and cannot be. UWB_RANGING
exists only from API 31, the transport needs the Android 12 Bluetooth split
with maxSdkVersion caps, and NEARBY_WIFI_DEVICES needs usesPermissionFlags
from 33. NearbyManifestFragments owns all of it, following
BluetoothManifestFragments down to the quote-delimited duplicate suppression
-- which matters more here than there, because an app using both
com.codename1.bluetooth and com.codename1.nearby.transport runs both injectors
over one string. There is a test for exactly that collision.

Ranging's minSdk is 23, not 31. androidx.core.uwb runs down to 23 and reports
the feature absent below 31, so raising the whole app to 31 would have cost
far more than the feature is worth. This corrects a claim I made before
reading the AAR's own manifest.

Presence observation is tracked separately from association, and it is a
method call rather than a class reference -- an app that associates a device
and one that also asks the platform to watch for it name exactly the same
classes. Only the second earns the background companion permissions and the
CompanionDeviceService element, because background privileges an app never
uses are privileges a user is asked about for nothing.

Two things the scanner cannot see, both handled by naming them rather than
guessing. The device profile arrives as an enum constant, which is a field
reference, and Executor.visitFieldInsn is an empty override -- so
REQUEST_COMPANION_PROFILE_WATCH comes from android.nearby.watchProfile,
defaulted off. And com.apple.developer.nearby-interaction is injected only
when ios.nearby.background asks for it: an entitlement the App ID does not
carry fails codesigning with an error naming the entitlement and not the
reason it appeared, which is the trap com.apple.developer.homekit already
sets.

NSBonjourServices is derived through the same fold CN1Nearby.m applies at
runtime, because iOS refuses a browse whose registered service type the plist
did not declare and the refusal is a silent "no peers found". Both halves are
now covered by one test, and the builder logs the derived type every time --
com.example.chat and com.example.charts both fold to com-example-cha, and two
apps sharing a service type would discover each other's peers.

WatchNativeBuilder's guard test earned its keep: it failed on this change and
named the three frameworks it did not know how to classify. NearbyInteraction
is present on watchOS and is still weak-linked there, because the watch slice
undoes CN1_NEARBY_RANGING and never calls into it.

903 plugin tests and 37 catalog tests pass, and SpotBugs reports zero findings
-- including one it found in this change, an indexOf(...) > 0 that happened to
work only because both method names it matched start with a prefix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…y has

AssociationInfo.getDeviceMacAddressAsString() does not exist on the public
Android SDK -- the string-returning form is a hidden API. The public one is
getDeviceMacAddress(), which returns an android.net.MacAddress whose
toString() is the colon-separated lowercase form that
startObservingDevicePresence and disassociate take.

Nothing in this repository compiles com.codename1.impl.android.nearby: like
com.codename1.impl.android.ar and .cipher before it, the package is excluded
from the port jar and only compiles inside a generated app. So this was found
by compiling those five files by hand against android-35 plus the real
androidx.core.uwb, uwb-rxjava3 and play-services-nearby artifacts, which is
the check that stands in for the app build here -- a contaminated shared ~/.m2
from another checkout is currently breaking the sample app's CSS step for
unrelated reasons. All 23 classes compile clean now.

Worth recording: the same gap covers ar/ and cipher/ and is not new, but it is
real. A method that does not exist on the public SDK is the kind of thing that
compiles nowhere in this repo and fails in every customer's build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A capability matrix by platform, then quick-start, ranging, accessory ranging,
companion association, transport, and how to develop the whole thing without
hardware. Five compiled snippets rather than inline listings, so the code in
the guide is code that builds.

Three things the chapter says out loud rather than leaving a reader to
discover: the transport does not cross ecosystems and here is what to use
instead; ranging needs a channel the two devices already share, so it is
designed to be used alongside com.codename1.bluetooth; and background ranging
needs an entitlement Apple grants on the App ID, which is why nothing injects
it for you.

Writing the examples found two real API problems, which is the argument for
writing them.

com.codename1.nearby.transport.ConnectionRequest is renamed
IncomingConnection. com.codename1.io.ConnectionRequest is one of the most
widely used classes in the framework, and an app doing both networking and
nearby transport -- which is most of them -- could not import both. The
compiler said "reference to ConnectionRequest is ambiguous" on the very first
snippet that used it.

And the example called GattCharacteristic.writeValue, which does not exist;
the method is write. A guide snippet that does not compile is a guide snippet
that teaches the wrong thing, which is why they live under a compiled source
root.

Every prose gate passes: vale reports zero errors and zero warnings,
LanguageTool reports zero matches over the rendered guide, asciidoctor renders
at --failure-level WARN, the paragraph-capitalization check passes and the
snippet validator finds nothing. British spellings were normalized to US
across the whole feature, code and prose alike, because LanguageTool runs
en-US and the neighbouring javadoc already says "meters".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
scripts/check-cast-semantics.sh flagged the cast in AndroidNearbyBridge: it sat
inside a catch(Throwable), which under ParparVM is a handler that never runs --
a failed CHECKCAST does not throw there, it hands the wrong object to the next
instruction.

Android is not ParparVM, so nothing was going to break on a device. The gate is
repo-wide anyway, and rightly: a rule that holds everywhere is one rule, and a
rule with a per-port exemption is a rule nobody can apply without first working
out which port they are in. The baseline is a ratchet of existing debt rather
than an allow-list, so new code does not add to it.

Both reflective loads now test with instanceof and branch. The one in
AndroidNearbyBackend is fixed the same way even though the checker cannot see
it -- that file is in the package excluded from the port jar, so it compiles
nowhere the checker looks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Several CodenameOne checkouts live on this machine and all install
com.codenameone:*:8.0-SNAPSHOT. Sharing ~/.m2 or /tmp/cn1-local-repo lets a
build in another checkout overwrite this one's core jar mid-build, and the
symptom is a "cannot find symbol" on a class this branch just added -- which
reads like a corrupt incremental build rather than a collision. It cost real
time twice in this branch alone.

Build with -Dmaven.repo.local=$(pwd)/.m2-repo instead.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e03ebc385f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/nearby/transport/NearbyTransport.java Outdated
Comment thread CodenameOne/src/com/codename1/nearby/ranging/RangingSession.java Outdated
@shai-almog

shai-almog commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator Author

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

@github-actions

github-actions Bot commented Aug 23, 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

@github-actions

github-actions Bot commented Aug 23, 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.

@shai-almog

shai-almog commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 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 63ms / native 5ms = 12.6x speedup
SIMD float-mul (64K x300) java 63ms / native 5ms = 12.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
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 199.000 ms
Base64 CN1 decode 134.000 ms
Base64 SIMD encode 99.000 ms
Base64 encode ratio (SIMD/CN1) 0.497x (50.3% faster)
Base64 SIMD decode 99.000 ms
Base64 decode ratio (SIMD/CN1) 0.739x (26.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 30.000 ms
Image createMask (SIMD on) 25.000 ms
Image createMask ratio (SIMD on/off) 0.833x (16.7% faster)
Image applyMask (SIMD off) 191.000 ms
Image applyMask (SIMD on) 68.000 ms
Image applyMask ratio (SIMD on/off) 0.356x (64.4% faster)
Image modifyAlpha (SIMD off) 73.000 ms
Image modifyAlpha (SIMD on) 64.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.877x (12.3% faster)
Image modifyAlpha removeColor (SIMD off) 58.000 ms
Image modifyAlpha removeColor (SIMD on) 34.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.586x (41.4% faster)

@shai-almog

shai-almog commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 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 65ms / native 7ms = 9.2x speedup
SIMD float-mul (64K x300) java 62ms / native 3ms = 20.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
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 188.000 ms
Base64 CN1 decode 128.000 ms
Base64 SIMD encode 101.000 ms
Base64 encode ratio (SIMD/CN1) 0.537x (46.3% faster)
Base64 SIMD decode 99.000 ms
Base64 decode ratio (SIMD/CN1) 0.773x (22.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 22.000 ms
Image createMask (SIMD on) 15.000 ms
Image createMask ratio (SIMD on/off) 0.682x (31.8% faster)
Image applyMask (SIMD off) 44.000 ms
Image applyMask (SIMD on) 34.000 ms
Image applyMask ratio (SIMD on/off) 0.773x (22.7% faster)
Image modifyAlpha (SIMD off) 302.000 ms
Image modifyAlpha (SIMD on) 37.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.123x (87.7% faster)
Image modifyAlpha removeColor (SIMD off) 50.000 ms
Image modifyAlpha removeColor (SIMD on) 38.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.760x (24.0% faster)

@shai-almog

shai-almog commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 9.03% (8909/98658 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46074/521644), branch 3.46% (1707/49379), complexity 3.44% (1814/52720), method 5.26% (1461/27762), class 10.53% (390/3702)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.03% (8909/98658 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46074/521644), branch 3.46% (1707/49379), complexity 3.44% (1814/52720), method 5.26% (1461/27762), class 10.53% (390/3702)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 262ms / native 159ms = 1.6x speedup
SIMD float-mul (64K x300) java 151ms / native 107ms = 1.4x 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 96.000 ms
Base64 CN1 decode 90.000 ms
Base64 native encode 363.000 ms
Base64 encode ratio (CN1/native) 0.264x (73.6% faster)
Base64 native decode 298.000 ms
Base64 decode ratio (CN1/native) 0.302x (69.8% faster)
Image encode benchmark status skipped (SIMD unsupported)

@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 528 total, 0 failed, 54 skipped

Benchmark Results

  • Execution Time: 13573 ms

  • Hotspots (Top 20 sampled methods):

    • 8.86% com.codename1.tools.translator.Parser.addToConstantPool (107 samples)
    • 6.54% java.util.ArrayList.indexOf (79 samples)
    • 4.22% com.codename1.tools.translator.BytecodeMethod.addToConstantPool (51 samples)
    • 4.06% com.codename1.tools.translator.Parser.cn1EnsureSubclassIndex (49 samples)
    • 3.89% com.codename1.tools.translator.ByteCodeClass.hasDeclaredMethod (47 samples)
    • 3.64% java.lang.StringBuilder.append (44 samples)
    • 3.15% com.codename1.tools.translator.ByteCodeClass.fillVirtualMethodTable (38 samples)
    • 3.06% com.codename1.tools.translator.Parser.classIndex (37 samples)
    • 2.65% org.objectweb.asm.tree.analysis.Analyzer.analyze (32 samples)
    • 2.32% com.codename1.tools.translator.BytecodeMethod.equals (28 samples)
    • 1.90% com.codename1.tools.translator.BytecodeMethod.optimize (23 samples)
    • 1.82% java.lang.System.identityHashCode (22 samples)
    • 1.82% com.codename1.tools.translator.Parser.generateClassAndMethodIndexHeader (22 samples)
    • 1.57% com.codename1.tools.translator.bytecodes.Invoke.resolveDirectTarget (19 samples)
    • 1.57% java.lang.StringCoding.encode (19 samples)
    • 1.49% java.util.HashMap.hash (18 samples)
    • 1.32% java.lang.String.equals (16 samples)
    • 1.24% com.codename1.tools.translator.NativeSymbolIndex.<init> (15 samples)
    • 1.24% org.objectweb.asm.ClassReader.readCode (15 samples)
    • 1.16% java.lang.Object.hashCode (14 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.

@shai-almog

shai-almog commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 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 Aug 23, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 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 commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 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 54ms / native 4ms = 13.5x speedup
SIMD float-mul (64K x300) java 56ms / native 4ms = 14.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
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 245.000 ms
Base64 CN1 decode 128.000 ms
Base64 SIMD encode 65.000 ms
Base64 encode ratio (SIMD/CN1) 0.265x (73.5% faster)
Base64 SIMD decode 63.000 ms
Base64 decode ratio (SIMD/CN1) 0.492x (50.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 12.000 ms
Image createMask (SIMD on) 8.000 ms
Image createMask ratio (SIMD on/off) 0.667x (33.3% faster)
Image applyMask (SIMD off) 23.000 ms
Image applyMask (SIMD on) 17.000 ms
Image applyMask ratio (SIMD on/off) 0.739x (26.1% faster)
Image modifyAlpha (SIMD off) 16.000 ms
Image modifyAlpha (SIMD on) 10.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.625x (37.5% faster)
Image modifyAlpha removeColor (SIMD off) 20.000 ms
Image modifyAlpha removeColor (SIMD on) 128.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 6.400x (540.0% slower)

@shai-almog

shai-almog commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator Author

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

PMD is a zero-forbidden-findings gate and this change arrived with 102. Fixed
rather than excluded: @OverRide on every method that overrides one (including
the ones whose declaration spans two lines, which a single-line pass misses),
for-each in place of the indexed loops, and the redundant `private` on an enum
constructor. Which methods genuinely override was decided by javac rather than
by eye -- annotate everything, compile, and remove whatever the compiler
rejects -- so the result is exactly the set it agrees with. PMD and SpotBugs
both report zero now, over 5267 green tests.

Five review findings, all real, all fixed with a regression test where one was
possible.

**Transport permission requests never settled.** NearbyTransport parked its
resource in the transport's own pending map, while every bridge answers
through Ranging.deliverPermissionResult, which searched only the ranging map.
The id was dropped and the caller waited forever -- precisely what the SPI
documentation calls worse than an outright failure. Permissions now live in one
shared map on NearbyRequests, which is what request ids coming from a single
counter always allowed.

**A failed start wedged a ranging session for good.** The flag that makes a
concurrent start answer BUSY was set before the bridge call and cleared only on
success, so a rejected token left the session answering BUSY to every retry --
and retrying after a bad token exchange is the obvious thing to do. In-flight
starts are now tracked by request id, because the failure path only ever learns
the id.

**The declared Bonjour type did not match the one registered at runtime.** The
plist carried a type folded from the package name while the native folded the
serviceId the app passed to startAdvertising, so iOS browsed a type it had
never been told about and answered with silence. The plist is now the
authority: ios.nearby.serviceType takes a comma-separated list of the service
ids the app uses, all of them are declared, and CN1Nearby.m checks its folded
argument against NSBonjourServices and fails with a message naming the hint
rather than browsing into the void.

**Deleting CN1CompanionDeviceService broke every ranging-only build.**
AndroidNearbyBackend calls its register/unregister unconditionally, the whole
package is excluded from the port jar, and no other definition exists, so
javac had nothing to resolve. It is retained; the manifest still names it only
when presence is used.

**The Android transport answered "granted" without asking.** Nearby
Connections refuses to start without its runtime grants and nothing on the
advertise/discover path checked them, so requestPermissions resolved while the
first real operation failed for a permission the user never saw. It now checks
and requests, following AndroidBluetooth down to running the blocking check on
the EDT.

Chasing the last of those turned up one more the review did not: the
device-profile gates had GLASSES and COMPUTER swapped. GLASSES is API 34 and
COMPUTER is 33, not the order the enum happens to declare them in, and passing
the platform a profile string it does not know throws. Both were checked
against the SDK's own api-versions.xml, as was the companion foreground-service
permission -- API 31, not 33, which is the same file's answer to the daemon-side
review comment.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dc1ebd0b53

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/nearby/AndroidNearbyBackend.java Outdated
Comment thread Ports/Android/src/com/codename1/impl/android/nearby/AndroidUwbRanging.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1Nearby.m Outdated
Comment thread Ports/iOSPort/nativeSources/CN1Nearby.m Outdated
Comment thread Ports/iOSPort/nativeSources/CN1Nearby.m Outdated
The hint became a comma-separated list of the service ids an app passes to
startAdvertising, because iOS browses only the Bonjour types declared in the
Info.plist and the build cannot see those strings. The runtime now refuses an
undeclared id with a message naming it, rather than browsing into the void, so
the guide says to list them and says what happens when one is missing.

vale, LanguageTool, asciidoctor, the paragraph check and the snippet validator
all still report clean.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc58488e2c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/nearby/AndroidNearbyTransport.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1Nearby.m
Five more findings, all real.

**Android permissions went to whichever backend happened to be loaded.** An
app using ranging and transport had its discovery, advertise and connect bits
answered by the UWB backend, which knows only UWB_RANGING, ignores the rest
and reports success -- so the transport grants were never requested and the
first advertise failed for a permission the user never saw. Splitting the
request in two instead needs the two answers joined into the one result the
caller waits on, which the SPI's single reply id cannot express. So the
coordinator now owns the whole permission flow: these are platform permission
strings and checkForPermission is in the always-compiled half of the port, so
one list, one pass, one answer.

**UWB_RANGING was inspected, never requested.** Same fix, same place: it is in
the coordinator's list and goes through the real runtime request. The two
backends' permission methods are now unreachable stubs that say so.

**The iOS authentication token was fabricated from public metadata.** It
hashed the two display names and the service type -- all of which a relay
observes and can reproduce on both of its sessions, so it would have shown
matching digits at both ends while relaying. The public API documents that
comparison as the defence against exactly that, which makes a guessable token
worse than none. MultipeerConnectivity exposes nothing to bind one to, so iOS
now reports empty, and the API doc, the guide and the capability matrix all
say so.

**A rejected iOS invitation reported a disconnection.** MCSessionStateNotConnected
covers both, and an app that was inviting waited forever for the connected or
failed answer that never came. Peers that actually reached Connected are
tracked, so only they can disconnect; everything else is a connection failure.

**The iOS service type was cached forever.** Stopping discovery for one
service and starting it for another carried on browsing the first. It is
reassigned on every call now.

Two on the daemon side. The companion foreground-service permission moves to
API 31 (verified in the SDK's api-versions.xml, which also caught GLASSES and
COMPUTER being swapped in my own profile gates -- 34 and 33, not the order the
enum declares them). And legacy Play Services mode is refused for the nearby
transport rather than compiling Nearby Connections against a 6.5.87 monolith
that predates the API.

The remaining comment asked for com.apple.developer.nearby-interaction on every
ranging build. I disagree and the reasoning is in the code at the gate: the
entitlement arrived in iOS 16 while foreground ranging shipped in 14, NIError
declares no missing-entitlement code, and Apple documents the capability as
permitting Nearby Interaction in the background. Injecting it unconditionally
would fail codesigning for every app whose App ID lacks the capability, which
is a far worse failure than one build hint.

Local: 5267 core tests, 908 plugin tests, 503 daemon tests, PMD 0, SpotBugs 0
across core/android/ios/plugin, both daemon guards, and every prose gate. The
iOS native still compiles clean in all four configurations plus tvOS.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0766c4f55d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/nearby/AndroidUwbRanging.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1Nearby.m Outdated
Comment thread Ports/iOSPort/nativeSources/CN1Nearby.m
Comment thread Ports/iOSPort/nativeSources/CN1Nearby.m
Two real bugs and one push-back.

**An Android file payload was handed to the app before it arrived.** Nearby
Connections calls onPayloadReceived when a FILE transfer is ANNOUNCED, not when
it completes -- the file on disk is partial at that point, and the app was also
told about transfers that later failed or were cancelled. Incoming files are
now held until the terminal update names them SUCCESS, which is what
payloadReceived's complete-payload contract requires.

**iOS advertising and browsing failures were dropped.** MultipeerConnectivity
rejects both asynchronously, after startAdvertising has already resolved true,
and the delegate discarded the error -- so an app believed it was advertising
when it was not and no second signal was ever coming. Both request ids are held
so the late failure can settle them, and cleared on stop so an unrelated error
cannot fail a request that already settled.

The third comment said Jetpack UWB reports azimuth and elevation in radians and
asked for Math.toDegrees. It does not, and the conversion would turn a
90-degree bearing into 1.57. androidx.core.uwb's own KDoc on RangingPosition
reads "The azimuth angle in degrees of the ranging device", and disassembling
UwbClientSessionScopeAospImpl shows the backend float copied straight into the
androidx measurement with no conversion anywhere in the library. The reasoning
is in the code beside the delivery.

Chasing that did turn up something real in the same lines, from the sentence
right after the one quoted: Android's azimuth range is [-90, 90], not the
[-180, 180] my public documentation promised. Apple's direction vector folds to
the full circle and so distinguishes a peer in front from one behind; Android's
azimuth cannot. RangingUpdate now says that instead of implying a range neither
platform delivers.

PMD 0, SpotBugs 0 across core, android and ios, 5267 core tests green, and the
native still compiles clean in every configuration plus tvOS.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c9e80fc276

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1Nearby.m Outdated
Comment thread Ports/Android/src/com/codename1/impl/android/nearby/AndroidNearbyTransport.java Outdated
Comment thread CodenameOne/src/com/codename1/nearby/ranging/Ranging.java
Five findings, all real.

**A short Android token payload read past the end of the array.**
RangingToken.fromByteArray validates only the outer frame, so a peer could
hand over a well-formed envelope with a two-byte payload and the decoder's
unguarded readInt walked off it. An ArrayIndexOutOfBoundsException is not an
IllegalArgumentException, so it escaped startRanging's handler into
application code and left the start resource pending forever. Every read is
bounds-checked now, and the message names the field that ran out.

**Presence was silently lost on Android 12 and 12L.** The AssociationInfo
callbacks are API 33; those releases deliver through onDeviceAppeared(String)
and onDeviceDisappeared(String). startObservingPresence accepts API 31, so it
reported the watch as accepted and then nothing ever arrived. Both String
overloads are implemented -- the address IS the association id below 33, which
is what the backend encodes there, so the two match without a lookup.

**Every iOS byte payload arrived as id 0.** MultipeerConnectivity carries raw
bytes and nothing else, so the sender's id was never transmitted and no app
could tell two payloads apart or match one to its progress events, which
Payload.getId() promises it can. The id is framed into four leading bytes and
stripped on receipt; both ends of an MPC session are Codename One, so the
framing is symmetric by construction.

**requestConnection ignored the local name it was given on iOS.** The MCPeerID
had been built at discovery time from the device name, and the invite path
never looked at the argument -- so the ordinary discover-then-invite flow
showed the peer the wrong name. MCPeerID is immutable and the session,
advertiser and browser are all bound to it, so applying a new name is a
rebuild of the lot; done only when nothing is connected, because renaming
under a live session would drop it.

**Stopping a session mid-accessory-handshake hung the caller.** startAccessory
is answered from didGenerateShareableConfigurationData, and clearing the
delegate silenced both that and didInvalidateWithError, so the AsyncResource
waited forever. A pending start is failed before the delegate goes.

PMD 0, SpotBugs 0 across core, android and ios, 5267 core tests green, and the
native compiles clean in all four configurations plus tvOS.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9b3a0db878

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

- A presence event is persisted only when nobody can receive it. A live
  app with a listener registered sees the event and is done with it;
  persisting it as well meant the NEXT launch replayed a sighting the app
  had already acted on, as though it had happened while the app was away.
  The durable copy exists for the one case the in-memory backlog cannot
  survive -- an event delivered into a process with no listener yet, which
  may be reclaimed before it gets one -- and CompanionDevices now answers
  which case this is.

- A nearby define that was never enabled fails the build. replaceInFile
  is a String.replace, so a marker that is not there is a silent no-op: a
  port override or an older staged header let the build finish with the
  native compiled out, and the app shipped reporting the feature
  unsupported for usage the scanner had plainly found, with nothing
  anywhere saying why. An already-enabled define is treated as the
  success it is.

- The tvOS plist reads the EFFECTIVE purpose string, the same one
  IPhoneBuilder validates. ios.plistInject wins over the hint, so an app
  that declared a perfectly good disclosure there left the hint blank --
  and the tvOS plist, reading the hint, omitted the key entirely.
The glasses profile is an API 34 constant, and the nearby floor is 33 --
which reads like a build that cannot compile. It can: AndroidNearbyBackend
never names AssociationRequest.DEVICE_PROFILE_GLASSES. It writes the role
name that constant inlines to, behind a runtime SDK_INT check, exactly so
this floor does not have to move for a hint that costs the app nothing at
compile time. Raising it to 34 would raise it for every companion build.

Written down because the question comes up at the floor, not at the
literal, and a comment in the port is not where anyone reads it.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f058ef88f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1Nearby.m Outdated
- An injected AccessorySetupKit array has to carry what nearby needs. The
  key being present was taken as the app having handled it, so an empty
  array, a malformed one, or one simply missing Bluetooth went through:
  the build succeeded, the generated entry was skipped as redundant, and
  AccessorySetupKit never offered its picker on the device. Every value
  the build would have written must now be in the array the app declared,
  and the failure names the missing ones -- which is what the Bonjour
  merge already does with NSBonjourServices.

- A failed file send cancels only its OWN transfers. progressByPayload is
  keyed by the portable payload id, which two overlapping sends of the
  same immutable Payload share, so cancelling by that key reached into a
  separately accepted send and cancelled its transfers too -- a send that
  had started perfectly well ended as CANCELED because an unrelated one
  could not reach every recipient. Cancelling the whole payload is still
  what cancel() does, which is the one place it is right.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d3b1a2726d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/nearby/AndroidNearbyTransport.java Outdated
- An incoming connection owns the service it was negotiated through.
  Nearby says which side started it, and treating any existing mapping as
  authoritative labelled a peer that answered THIS advertisement with the
  service discovery had seen it under -- so the connection, its lifecycle
  events and every payload on it were reported against the wrong service,
  which in an app running two of them routes the peer to the wrong
  protocol. An outgoing connection still keeps what discovery recorded,
  because that path carries the discoveryServiceId field, which may have
  moved on since the endpoint was found.

- Receiving a file copies it off the callback thread. localPathFor copies
  the whole file when scoped storage gives only a content URI, and Nearby
  delivers that callback on the main thread -- so the transfer meant for
  large data froze the UI for as long as the copy took, and a big enough
  one was within reach of an ANR. The delivery reads what it needs out of
  the callback first, since none of it outlives the call.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a1df8c6c2b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/nearby/AndroidNearbyBackend.java Outdated
Comment thread Ports/Android/src/com/codename1/impl/android/nearby/NearbyPresenceStore.java Outdated
- An association snapshot carries the presence it was last reported with.
  getAssociations encoded every one as absent, so an app following
  CompanionDevice.isPresent()'s own instruction to re-read the association
  turned a device that had just appeared back into one that was not there.
  The platform has no query for this -- presence arrives as an event and
  nowhere else -- so the last event is the only answer there is, and the
  store that already sees every event is where it is kept.

- A stale association failure is ignored. The platform can answer long
  after an activity recreation released that request and a new chooser
  took the slot, and releaseResultListener is not owner-checked: the
  stale failure tore down the LIVE request's listener, so its chooser
  result went nowhere and its resource never settled. launch() checks the
  same thing for the same reason.

- The persisted backlog is read, written and cleared under one lock. The
  service persists on its callback thread while the backend restores on
  the thread that built it, so the restore could read the stored rows,
  the service append one to what it had read, and the restore then delete
  the whole key -- taking with it an event it never returned. If the
  process died before the in-memory copy reached a listener that event
  was gone, which is precisely the cold start this store exists for.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a1843d86f4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/nearby/AndroidNearbyBackend.java Outdated
Comment thread CodenameOne/src/com/codename1/impl/nearby/LocalNearbyBridge.java
- Building the Android bridge is serialized. Two threads reaching nearby
  for the first time both saw null and both built a backend; only one was
  kept, and the loser could already have prepared a UWB session or taken
  the companion chooser slot in state nothing could reach again -- so a
  later start or stop could not find its session, and the radio it had
  opened stayed open.

- A restored presence event seeds the presence cache. Delivering it
  straight to CompanionDevices left getAssociations answering "absent"
  for the very device the listener had just been told had appeared, which
  is the contradiction the cache was added to remove. The replay goes
  through the store now, which is what knows both.

- A provisional inbound accept is failed rather than disconnected. An
  accepted endpoint enters `connected` immediately, so a stop or a
  disconnect before its confirmation hop reported a DISCONNECTION for a
  connection the app had never been told it had -- while the outcome
  accept() documents, connected or connectionFailed, never arrived. The
  inbound path has its own reservation now, the way the outbound one has
  had since the connecting list was added.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45fa2ba1d6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/nearby/AndroidNearbyTransport.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1Nearby.m
- Starting advertising or discovery replaces what was running. Nearby has
  one advertiser and one discoverer per client and refuses a second start
  as "already advertising", so a start issued while an earlier one was
  live was rejected -- and the earlier one went on broadcasting the
  service the app had moved off. The generation guards could not save it:
  the earlier start had already been answered, so nothing was left marked
  unowned for a failure to clean up. The simulated bridge and the iOS
  port both replace an existing start; this is Android doing the same.

- Discovery and connection services are separate mappings. One map could
  not be both, and the fix that labelled an inbound connection correctly
  did it by writing over what discovery had recorded -- so a connection
  that came and went left the endpoint's later endpointLost naming a
  service its endpointFound never mentioned. Connection and payload
  events read the negotiated service; discovery events read the
  discovered one.

- A late start refusal tears down the object it refused. MultipeerConnectivity
  promises no deadline for didNotStart, and the grace period that answers
  a start is a heuristic -- nothing else reports success, so waiting a
  moment for a refusal that does not come is the only positive signal
  there is. One arriving afterwards cannot un-resolve the resource: the
  SPI has one channel per request and it has been used. What it must not
  do is leave an advertiser installed that says it is advertising when
  nothing is, which made the next start think it was replacing a live
  operation and every later stop think it had something to stop.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eaf85c755d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/nearby/AndroidUwbRanging.java Outdated
subscribeOn defers the subscription to an io thread, and the timer was
armed on the calling thread the moment subscribe() returned. A saturated
or delayed scheduler therefore let it answer "ranging started" before
rangingResultsObservable had asked for anything at all -- and the error
that followed arrived as an invalidation, after the caller had already
been told its start succeeded, which is the opposite of what start()
documents.

It is armed from doOnSubscribe now, placed UPSTREAM of subscribeOn
deliberately: that is what puts the callback on the thread the
subscription actually happens on.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dda7299cf2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1Nearby.m
Comment thread Ports/iOSPort/nativeSources/CN1Nearby.m
- iOS keeps the discovery service apart from the negotiated one, as
  Android now does. One map could not be both: browsing A while
  advertising B, a peer that connected through B and was then found or
  lost by the browser had its entry rewritten to A, so every payload and
  disconnection on that live B connection carried a service it had
  nothing to do with. Connection events read the service the connection
  was negotiated through -- the advertised one for an invitation this
  device answered, the discovered one for an invitation it sent -- and
  discovery events read what the browser saw.

- Stopping the transport REJECTS the invitations it is dropping. The
  handler is the only thing that tells the initiator its invitation was
  answered, so releasing it unanswered left that peer's requestConnection
  waiting on MultipeerConnectivity's own timeout instead of hearing
  immediately that it was refused -- which is what stopping means for an
  invitation nobody is going to look at. Answered outside the lock, since
  the handler runs framework code.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ebd58b71b1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

An event that arrived with no listener registered was persisted AND
parked. When a listener appeared later in the same process the parked
copy was replayed, but the durable row stayed on disk -- so the next
launch restored it and delivered an appearance the app had already
handled. The hasPresenceListener gate does not cover this: the listener
did not exist when the event arrived.

CompanionDevices now tells the port when the backlog has been handed to
a listener, which is the only moment that can be known from there:
parking happens in that class and so does the replay. The Android store
forgets its rows on that signal, and the rows a PREVIOUS process left
cannot be caught by it -- the restore takes all of those when the
backend is built, before any listener can register.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc040054d1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/nearby/AndroidNearbyTransport.java Outdated
@shai-almog

shai-almog commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator Author

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

Benchmark Results

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

Build and Run Timing

Metric Duration
Simulator Boot 91000 ms
Simulator Boot (Run) 3000 ms
App Install 19000 ms
App Launch 62000 ms
Test Execution 609000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 70ms / native 4ms = 17.5x speedup
SIMD float-mul (64K x300) java 82ms / native 10ms = 8.2x 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 274.000 ms
Base64 CN1 decode 239.000 ms
Base64 native encode 1553.000 ms
Base64 encode ratio (CN1/native) 0.176x (82.4% faster)
Base64 native decode 1060.000 ms
Base64 decode ratio (CN1/native) 0.225x (77.5% faster)
Base64 SIMD encode 90.000 ms
Base64 encode ratio (SIMD/CN1) 0.328x (67.2% faster)
Base64 SIMD decode 68.000 ms
Base64 decode ratio (SIMD/CN1) 0.285x (71.5% faster)
Base64 encode ratio (SIMD/native) 0.058x (94.2% faster)
Base64 decode ratio (SIMD/native) 0.064x (93.6% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 58.000 ms
Image createMask (SIMD on) 31.000 ms
Image createMask ratio (SIMD on/off) 0.534x (46.6% faster)
Image applyMask (SIMD off) 140.000 ms
Image applyMask (SIMD on) 203.000 ms
Image applyMask ratio (SIMD on/off) 1.450x (45.0% slower)
Image modifyAlpha (SIMD off) 264.000 ms
Image modifyAlpha (SIMD on) 201.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.761x (23.9% faster)
Image modifyAlpha removeColor (SIMD off) 170.000 ms
Image modifyAlpha removeColor (SIMD on) 236.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.388x (38.8% slower)

discoveryServiceId is the service discovery is running for NOW, and a
restart for another one moves it. Passing that labelled a connection to
an endpoint found under the OLD service with the new one -- and since
the connection service became a mapping of its own, nothing was left to
correct it: the connected, payload and disconnection events all named a
service the peer was never discovered on. The per-endpoint mapping is
the source now, with the field as the fallback for an endpoint nothing
recorded, which is one that arrived through advertising.

iOS had the same defect in the same place, introduced when its two
mappings were split, and gets the same fix.

Also: the JavaSE simulator capture waits for the inspector's PROPERTIES
to be populated. The details panel below settles empty and is already
waited on; the properties settle the other way round -- the inspector
selects a component and fills Class, UUID, Coordinates, Padding and
Margin in, and the reference holds them populated. A capture taken
before the selection propagated showed that layout with every value
blank, which is not a state the simulator settles in, and failed four
screenshots in one run on a slow runner while the commits either side
passed. Measured on both sides: the reference draws 2625 dark pixels in
that band and the unpopulated capture 93, so the threshold sits an order
of magnitude clear of the failure.
@shai-almog

shai-almog commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator Author

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

Benchmark Results

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

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 54ms / native 3ms = 18.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 160.000 ms
Base64 CN1 decode 118.000 ms
Base64 native encode 714.000 ms
Base64 encode ratio (CN1/native) 0.224x (77.6% faster)
Base64 native decode 649.000 ms
Base64 decode ratio (CN1/native) 0.182x (81.8% faster)
Base64 SIMD encode 48.000 ms
Base64 encode ratio (SIMD/CN1) 0.300x (70.0% faster)
Base64 SIMD decode 46.000 ms
Base64 decode ratio (SIMD/CN1) 0.390x (61.0% faster)
Base64 encode ratio (SIMD/native) 0.067x (93.3% faster)
Base64 decode ratio (SIMD/native) 0.071x (92.9% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 8.000 ms
Image createMask (SIMD on) 4.000 ms
Image createMask ratio (SIMD on/off) 0.500x (50.0% faster)
Image applyMask (SIMD off) 88.000 ms
Image applyMask (SIMD on) 75.000 ms
Image applyMask ratio (SIMD on/off) 0.852x (14.8% faster)
Image modifyAlpha (SIMD off) 91.000 ms
Image modifyAlpha (SIMD on) 76.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.835x (16.5% faster)
Image modifyAlpha removeColor (SIMD off) 110.000 ms
Image modifyAlpha removeColor (SIMD on) 65.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.591x (40.9% faster)

@shai-almog

shai-almog commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator Author

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

@shai-almog

shai-almog commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 217 screenshots: 217 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@shai-almog

shai-almog commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator Author

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

Benchmark Results

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

Build and Run Timing

Metric Duration
Simulator Boot 87000 ms
Simulator Boot (Run) 1000 ms
App Install 17000 ms
App Launch 46000 ms
Test Execution 489000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 84ms / native 11ms = 7.6x speedup
SIMD float-mul (64K x300) java 85ms / native 3ms = 28.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 387.000 ms
Base64 CN1 decode 144.000 ms
Base64 native encode 2935.000 ms
Base64 encode ratio (CN1/native) 0.132x (86.8% faster)
Base64 native decode 955.000 ms
Base64 decode ratio (CN1/native) 0.151x (84.9% faster)
Base64 SIMD encode 117.000 ms
Base64 encode ratio (SIMD/CN1) 0.302x (69.8% faster)
Base64 SIMD decode 83.000 ms
Base64 decode ratio (SIMD/CN1) 0.576x (42.4% faster)
Base64 encode ratio (SIMD/native) 0.040x (96.0% faster)
Base64 decode ratio (SIMD/native) 0.087x (91.3% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 9.000 ms
Image createMask (SIMD on) 4.000 ms
Image createMask ratio (SIMD on/off) 0.444x (55.6% faster)
Image applyMask (SIMD off) 76.000 ms
Image applyMask (SIMD on) 85.000 ms
Image applyMask ratio (SIMD on/off) 1.118x (11.8% slower)
Image modifyAlpha (SIMD off) 67.000 ms
Image modifyAlpha (SIMD on) 48.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.716x (28.4% faster)
Image modifyAlpha removeColor (SIMD off) 210.000 ms
Image modifyAlpha removeColor (SIMD on) 43.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.205x (79.5% faster)

Signed-off-by: Shai Almog <67850168+shai-almog@users.noreply.github.com>
@shai-almog
shai-almog merged commit a536d38 into master Aug 24, 2026
44 of 51 checks passed
@shai-almog
shai-almog deleted the feature/nearby-devices branch August 24, 2026 19:07
shai-almog added a commit that referenced this pull request Aug 24, 2026
The .gitignore conflict was two additions at the same place, not a disagreement:
master's per-checkout Maven repository and this branch's generated guide table.
Both kept.

The catalog gate then found what it is for. #5589 (nearby devices) added six
hints with no rows -- android.nearby.{watch,computer,glasses}Profile and
ios.nearby.{serviceType,background,accessoryServices} -- and two plist entries,
NSNearbyInteraction{,AllowOnce}UsageDescription, which the cross-check added
last round caught because a key we INJECT needs a row of its own rather than
cover from the ios.NS*UsageDescription wildcard. Each type and default is read
off the call site: the profiles are compared with equalsIgnoreCase against
"true", accessoryServices splits on commas, serviceType falls back to the
package name, and background defaults off because the entitlement it requests
has to be on the provisioning profile or every ranging app fails to sign.

The merge also exposed two false positives in my own miner, both from the same
mistake -- associating a variable name across a whole file:

- IPhoneBuilder builds an Xcode setting key called
  "PRODUCT_BUNDLE_IDENTIFIER" + qualifier, and that assignment was attributed to
  an unrelated getArg(key, ...) three thousand lines ABOVE it. An assembly now
  only explains a call that follows it.
- Ordering alone was not enough: three `for (String key : request.getArgs())`
  loops below that assignment picked it up instead. A rebinding -- a for-each
  variable, a declaration, an assignment with no literal -- now shadows an
  earlier assembly, and a declaration that IS the assembly does not cancel
  itself.

Seven computed sites, all real, none new.

Co-Authored-By: Claude Opus 5 (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