Skip to content

Device runtime: run pushed Codename One apps on a phone - #5561

Open
shai-almog wants to merge 146 commits into
masterfrom
device-runtime
Open

Device runtime: run pushed Codename One apps on a phone#5561
shai-almog wants to merge 146 commits into
masterfrom
device-runtime

Conversation

@shai-almog

@shai-almog shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Adds a device runtime: install one app on a phone, then push a project to it
from your IDE and watch it run natively in seconds. A third way to run a
Codename One app, alongside the simulator and a cloud device build.

Pushed classes are interpreted on the device against the framework already
compiled into it. Nothing is built, signed or installed between edits — the
edit-run loop measured 2.8 seconds end to end.

Try it

# install ~/cn1-device-runtime.apk on a phone (11MB, no native libs, any arch)
cd scripts/devruntime-ide-project
mvn -Ppush-lan package

The desktop finds the phone on the local network, shows a six-digit pairing
code you type once, and from then on it is edit-and-run. --device <address>
is there for networks that block a scan.

What is here

CodenameOne/src/com/codename1/interp/ the interpreter
Ports/{Android,iOSPort} per-platform linkers, iOS native bridge
vm/ByteCodeTranslator bundle writer, lambda desugaring, DevicePush tool
scripts/cn1-device-runtime/ the runtime app itself
scripts/devruntime-ide-project/ the project you open in an IDE
scripts/devruntime-probes/ 20 programs that found the defects worth knowing about
docs/developer-guide/Device-Runtime.asciidoc how and why

Decisions worth reviewing

Shims are generated over the whole API, never curated. A hand-maintained
list is a promise that applications only subclass what somebody anticipated, and
its failure mode is not an error message but an override that is silently never
called. The generator fails the build rather than pruning what will not compile
— a compile-and-drop loop once silently ate Interp_ui_Form.

Native-heavy subsystems are excluded from the shim set (ai, ar,
camera, surfaces, car, health, …). A shim is a compiled reference to the
class it extends, which is exactly what the build scans to decide what to link,
so generating the full API pulled 300MB of ML Kit, ARCore and CameraX natives
into an app that calls none of them. Cost: those types cannot be subclassed by
pushed code; calling them degrades to isSupported() == false, which is the
runtime's existing contract for a cn1lib without its native half.

iOS keeps shims rather than runtime vtable synthesis. Synthesis would make
14 more types extensible on iOS only, and Android cannot follow — so the usable
capability, the intersection, does not move. InterpHostVtableSynthesisIntegrationTest
stays for the day that changes.

synchronized uses the real object monitor, not a private lock table, which
is what makes wait/notify work.

Framework fixes that fell out

  • AndroidImplementation.getHostOrIP() returned dummy0's IPv6 link-local
    instead of a usable IPv4 — affects any caller.
  • CodenameOneImplementation.getResourceAsStream gained a local-resource hook,
    so a pushed program's theme.res is found by Resources.openLayered, which
    never passes through Display.

Verification

4798 core · 506 translator · 52 interpreter · SpotBugs 0 · 20-program device
battery green on an Android emulator and the iOS simulator, including a
four-file, three-package app entered through Lifecycle rather than main.

Every probe exists because something plausible turned out not to work; the
README records which defect each was written for.

Review rounds

Eleven findings from codex, all real, all fixed and each answered on its thread.
The two that mattered most:

  • Pairing handed out a bearer token. The peer id travelled in the clear on
    every push and never rotated, so one captured frame authorised arbitrary code
    on that phone forever. v2 is gone rather than deprecated. v3 derives a 256-bit
    secret on both ends from (typed code, peerId, deviceId) — never transmitted —
    and every connection answers a fresh challenge whose MAC covers the bundle.
    Authentication happens before the approval prompt, so nobody can raise dialogs
    on a stranger's phone until they tap Approve to stop them. What it still does
    not defeat is a passive observer of the pairing exchange itself, and the docs
    say so.
  • A failed class initializer left the class looking initialized, so later
    reads returned whatever half of it had been assigned. Four states and an owning
    thread now, per JLS 12.4.2.

Shipping it

.github/workflows/device-runtime-store.yml runs Mondays and on demand,
uploading to Play internal testing and TestFlight. It does not promote to
production and does not submit for review — a weekly automatic release would
put unread builds in front of the public and queue an iOS review every week
whether anything changed or not. Promotion stays one deliberate command.

Without credentials the job names the missing secrets and stops rather than
publishing half a release; none exist yet, so today it is a no-op that says so.

Listing text is in fastlane's layout (scripts/cn1-device-runtime/fastlane/) so
supply and deliver consume it directly, with store/privacy.md for both
stores' data forms and store/README.md for the secrets, the pre-submission
checklist and the review-risk assessment.

The compliance point that matters: this app runs code it did not ship with,
which is Guideline 2.5.2 — permitted for tools that develop or test code, and
only while the source is "completely viewable and editable by the user". The
runtime refuses to load a bundle whose sources it lacks, and shows them under
View source. Removing that screen makes the app unsubmittable, which is why
the code says so where the screen is defined.

Not done

NativeLookup stubbing covers the Java half of a cn1lib; the native half
reports unsupported. Resource push covers theme.res, CSS and images.

Screenshots for both stores, the Play content rating questionnaire, Apple's
privacy manifest and the console listings themselves are human steps, listed in
store/README.md.

🤖 Generated with Claude Code

Loading
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.

2 participants