Skip to content

Prototype remote raw-memory engine + live address tracking for MIDlet Memory Editor - #109

Draft
H3nb wants to merge 56 commits into
alpha-pre-emulation-speedfrom
feature/true-memory-editor
Draft

Prototype remote raw-memory engine + live address tracking for MIDlet Memory Editor#109
H3nb wants to merge 56 commits into
alpha-pre-emulation-speedfrom
feature/true-memory-editor

Conversation

@H3nb

@H3nb H3nb commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Status

Important

Draft / physical-device prototype. Do not merge yet.

The cross-process architecture is now proven on the physical Android 16 device. Same-UID :memory_engine -> :midlet /proc/<pid>/maps, process_vm_readv, process_vm_writev, readback and restore all pass without root in multiple MIDlets.

:midlet
├─ MicroActivity + running J2ME game
│  └─ classic reusable Memory Editor View overlay
├─ MemoryTargetBridgeService
└─ libjlprobe.so
   ├─ target PID / runtime generation
   └─ target-local maps + mincore resident ART runs
        │
        │ Binder / same app UID
        ▼
:memory_engine
├─ MemoryEngineService
├─ RemoteMemoryScanService
└─ libjlremote.so
   ├─ remote Search candidate DB
   ├─ remote Next Scan
   ├─ relocation recovery
   ├─ remote live value reads
   ├─ visible-candidate live address tracker
   └─ exact remote writes + independent readback

No SYSTEM_ALERT_WINDOW, no second UI Activity, and no root requirement. The overlay remains a child View of MicroActivity and dies naturally with the MIDlet.

The PR base remains alpha-pre-emulation-speed; emulation-speed commit 4a4a1142b59e5c748b69de06597e80476e9ae4e8 remains excluded from the performance baseline.

Physical evidence

Remote access gate passed in both Bounce and Green Farm. A later Green Farm run demonstrated a real remote scanner host PID distinct from the MIDlet target PID.

Explicit Int32

backend=remote-memory-engine
remoteTargetPid=24888
scannerHostProcess=:memory_engine
scannerHostPid=25981
scope=Java Fast
operationType=Int32
residentRuns=56
residentBytes=12361728
bytesRead=12361728
readFailures=0
matchesSeen=1
retained=1
durationMs=52
relocationTracking=true
relocationContexts=1
gcDependency=false

The authoritative candidate was remotely edited and independent readback succeeded; the game value changed.

A normal direct refinement stayed remote and cheap:

bytesRead=88
retained=1
durationMs=0
relocationAttempted=false

Auto relocation

relocationOriginal=24
relocationOriginalGroups=11
relocationFreshMatches=52
relocationFreshGroups=25
relocationRecovered=4
relocationRecoveredGroups=1
relocationAmbiguous=0
retained=4

This is the intended semantic: the 52 fresh exact matches remained only a recovery pool; one logical old address group survived as four typed aliases.

Target-heap isolation

On the normal remote path, the in-process fallback scanner is not started. Candidate vectors, scan buffers, refinement state, relocation pools and visible-address recovery live in native memory in :memory_engine.

:midlet keeps only the game, classic overlay, target bridge and libjlprobe.so. mincore() remains target-side because it operates on the caller address space; compressed resident runs are then scanned remotely.

Live UI polling remains OFF by default so TextView/string churn does not become a new target ART-GC source. Physical testing observed UI warm-up GC Δ: 0.

Search semantics

  • Java Fast / Java Thorough.
  • Auto, Int8, Int16, UInt16, Int32, Int64, Float32, Float64.
  • Native candidate buckets in :memory_engine.
  • Exact remote typed write guarded by expected-current + independent readback.
  • No Edit All / mass write.
  • runtime-generation invalidation.

Next Scan first direct-refines retained addresses. If it returns zero and relocation context exists, the engine refreshes target resident runs and builds a fresh exact-value recovery pool. The fresh pool never becomes the result set by itself.

A valid no-match ends at 0 candidates. Previous stale results are not retained simply because relocation identity was unavailable.

Recovered buckets now retain fresh-pool per-type Seen provenance in diagnostics; global relocation counts/groups remain the authoritative relocation summary.

Remote live address tracking

A raw address is treated as a mutable binding rather than logical identity. For up to 32 visible unique address groups, libjlremote.so tracks:

sourceAddress
currentAddress
previousAddress
type/alias mask
temporal context signature
state
confidence
relocation count

States:

UNTRACKED
STABLE
SUSPECT
RELOCATED / Rebound
AMBIGUOUS
LOST

The temporal signature uses 64 bytes before + 64 bytes after the value, excludes the 8-byte maximum target span, uses 4-byte lanes, ignores zero/0xFFFFFFFF, and learns which lanes remain stable over time.

GC telemetry is not proof of movement. The existing address remains STABLE if its identity still validates. Recovery starts only when that binding fails.

Recovery is bounded:

  • max 32 visible groups;
  • max 6 informative anchors per group;
  • bounded candidate tests;
  • resident ART runs only;
  • one shared recovery scan for suspect visible groups;
  • ambiguous/lost states are backed off rather than rescanned every tick;
  • unique A -> B rebind rebases the identity signature to B.

SUSPECT, AMBIGUOUS, and LOST bindings are exposed as unreadable and cannot pass the exact-write guard.

ANR-safe live pipeline

Full live-address recovery is never executed on the synchronous Binder/UI path.

Refresh #1
  -> return current published snapshot immediately
  -> queue validation/recovery in :memory_engine worker

engine worker
  -> validate A
  -> if necessary recover A -> B
  -> publish B

Refresh #2 / next Live tick
  -> UI receives B

The one-tick delay is intentional. It keeps full resident-ART recovery away from Android input dispatch.

Diagnostics now include:

liveAddressBackend=remote-memory-engine
liveTrackedGroups=
liveTrackLimit=32
liveStable=
liveRelocated=
liveSuspect=
liveAmbiguous=
liveLost=
liveValidationReads=
liveRecoveryScans=
liveRecoveryBytes=
liveRebinds=
liveAmbiguousTotal=
liveLostTotal=

The separate EventQueue ANR hardening remains: Android main/UI-thread events do not enter the synchronous immediate MIDlet callback path that previously waited behind a long game paint callback.

Next physical-device gate

Green Farm explicit Int32 live address

  1. Wait for ENG✓, then confirm Backend: :memory_engine.
  2. Java Fast + Int32 New Search the known coin value; retain one candidate and record address A.
  3. Keep Live OFF initially.
  4. Return to gameplay and change the coin normally; allow normal runtime/GC activity.
  5. Reopen Memory Editor and press Refresh once. This queues asynchronous validation/recovery.
  6. Wait briefly and press Refresh again (or enable Live and wait for the next tick).
  7. If the address stayed put: same A, updated value, liveStable > 0, liveRebinds=0.
  8. If ART moved it: UI should show B, liveRecoveryScans > 0, liveRebinds > 0, and liveRelocated/subsequent liveStable evidence.
  9. Edit the displayed current address and verify the game changes.

Then repeat Auto refinement and the >25k regression. Search semantics must remain independent of visible live binding, and large untracked sets must never stick on the previous result.

There is still no Freeze and no blind mass write. Freeze remains gated on repeated successful live-address tests and must later suspend writes whenever identity is suspect/ambiguous/lost. Issue #108 remains the research/acceptance gate.

@H3nb H3nb changed the title Prototype true raw-memory editor Prototype true raw-memory editor (Int32 feasibility gate) Aug 26, 2026
@H3nb
H3nb force-pushed the feature/true-memory-editor branch from 71fcb25 to 06c6ee1 Compare August 26, 2026 04:40
@H3nb H3nb changed the title Prototype true raw-memory editor (Int32 feasibility gate) Prototype true raw-memory editor (typed adaptive device-test gate) Aug 26, 2026
@H3nb H3nb changed the title Prototype true raw-memory editor (typed adaptive device-test gate) Prototype true raw-memory editor with GC-resilient refinement Aug 26, 2026
@H3nb H3nb changed the title Prototype true raw-memory editor with GC-resilient refinement Prototype true raw-memory editor with GC-resilient address tracking Aug 26, 2026
@H3nb H3nb changed the title Prototype true raw-memory editor with GC-resilient address tracking Prototype true raw-memory editor with same-process stability test Aug 26, 2026
Rebuild the raw Memory Editor scanner core directly on 640fa1d, the parent of the emulation-speed commit. Keep only the native scanner, AIDL/service contract, managed self-test/session state, and debug service declaration. Deliberately omit the previous Activity/Compose editor UI so the next commit can replace it with a lower-allocation in-MicroActivity overlay.
@H3nb
H3nb force-pushed the feature/true-memory-editor branch from 88d877c to 794e6ca Compare August 26, 2026 18:11
@H3nb
H3nb changed the base branch from alpha to alpha-pre-emulation-speed August 26, 2026 18:11
H3nb added 16 commits August 27, 2026 02:15
Install a debug-only bootstrap in :midlet that attaches a tiny trigger to MicroActivity and lazily warms a reusable classic-View editor before New Search is enabled. Keep subsequent game/editor round trips to VISIBLE/GONE, make result observation manual by default, preallocate result rows and primitive grouping buffers, retain optional Live mode as an explicit higher-allocation trade-off, and document the Android 16 vs Android 11 address-stability test.
Temporarily allow Android CI to run for the dedicated alpha-pre-emulation-speed PR base so the exact experimental tree can be linted, tested, and assembled. This workflow-only change will be removed after validation.
Treat ART GC-count growth as telemetry, not proof that retained addresses moved. Add a thin JNI alias facade so an early GC-triggered relocation request first tests the existing raw candidates, while a direct no-match immediately followed by relocation does not repeat the direct pass. Keep the established native scanner and locking implementation unchanged.
Keep only the tiny trigger state before first use, allocate all result buffers/callbacks/builders and reusable Views during a one-time pre-search warm-up, and avoid rebuilding layout/status on ordinary same-orientation reopen. Keep live observation opt-in and make the same-Activity panel visibly translucent over the running MIDlet.
@H3nb H3nb changed the title Prototype true raw-memory editor with same-process stability test Prototype libjlmem raw-memory editor with isolated overlay and live addresses Aug 27, 2026
@H3nb H3nb changed the title Prototype libjlmem raw-memory editor with isolated overlay and live addresses Prototype remote memory engine with in-MIDlet overlay and raw access gate Aug 27, 2026
@H3nb H3nb changed the title Prototype remote memory engine with in-MIDlet overlay and raw access gate Prototype remote raw-memory engine for MIDlet Memory Editor Aug 27, 2026
@H3nb H3nb changed the title Prototype remote raw-memory engine for MIDlet Memory Editor Prototype remote raw-memory engine + live address tracking for MIDlet Memory Editor Aug 27, 2026
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