Files
MobileGL/docs/archive/notes/p12/map-docs-contract.md
T

139 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design docs and contracts for a server-owned on-screen window (P12 subset)
Paths are relative to `C:\Users\geekerwan\AndroidStudioProjects\FoldCraftLauncher\MobileGL-disagg\` at 9f669e52. **[V]** means I read it in the doc or code. **[I]** means it is my inference.
## 1. Decisions already made about the server-owned window
- **P12 scope was made smaller** [V] (`docs/Disaggregated/ROADMAP.md:46`, `P6-ENDSTATE-REVIEW.md:313`, row 6):
- The Surface-passing half is deleted: no Java `Surface` → Messenger/AIDL → `ANativeWindow_fromSurface`.
- The server app owns its own SurfaceView and the client has no window. The docs say minSdk 26's lack of an NDK call to flatten an `ANativeWindow` "no longer matters, since nothing needs flattening".
- P12 items, verbatim:
- an appended `WindowKind::ServerOwned`;
- one `CreateEGLWindowSurface` arm per backend;
- the four `m_windowHandle` stores plus the `sameHandle` dedup key;
- DirectGLES `g_Display/g_Surface/g_Context` de-globalisation;
- the `kEventSurfaceChanged` Width/Height gap;
- the client stops sending `SetWindowHandle` unconditionally;
- the server listens on `unix:` and `tcp://`;
- server lifecycle bound to the Activity;
- the freezer;
- multi-context;
- the FCL env / plugin switch table.
- **Rule H is permanent** [V] (`MobileGL/MG_Remote/CONTRACT-P6.md:43-45`, ENDSTATE:400). A value from the peer's window system never crosses, "not until P12". P12 gives the *server* its own display. So the `AndroidNativeWindow@P12` and `MetalLayer@P12` refusals stay forever. Rule G (CONTRACT-P6:31-37) allows only tokens.
- **Append timing** [V] (CONTRACT-P6:448-449): "Do not append `WindowKind::ServerOwned` in P6… P12 appends it." Today the enum ends at `MetalLayer = 6` (`protocol.fbs:239-250`), so the new value is 7.
- **Where to latch the window** [V] (CONTRACT-P6:458-466; `docs/Disaggregated/notes/p6/a6-audit-rows.md:292`):
- one substitution at `RegisterEGLWindowSurface` (`MG_Backend/BackendObject.cpp:235`);
- one refusal at the wire `SetWindowHandle` case (now `Server/ServerLoop.cpp:1232-1241`);
- one preserve in `ResetEGLRuntimeState` (`:365`);
- a new `sameHandle` key (`BackendObject_DirectGLES.cpp:962-964`) based on the surface token, not the window pointer (A8-10, rows:307).
- **Other a6 rows owned by P12** [V]:
- A8-5 and A8-6 (rows:302-303): two `MakeEGLCurrent` paths reach `ActivateEGLSurface` → `SetWindowHandle(surfaceState->Window)` (`BackendObject.cpp:283`) with no wire op in front.
- A8-7 (rows:304): a pbuffer create or a client release zeroes the window.
- A8-8 (rows:305): the Width/Height writes at `:206-207` are dead today and become a clobber in P12.
- A8-15 (rows:312): a client `eglTerminate` must not zero a Service-owned window.
- Review additions (rows:331-332): the incoming-handle allow-lists at `BackendObject_DirectGLES.cpp:955-960` and `BackendObject_DirectVulkan.cpp:415-419` would refuse every create unless re-keyed.
- A4-32 (rows:180): `SetWindowHandle` is a redundant blocking round trip.
- **Default-framebuffer shape** [V]: `kEventSurfaceChanged` is the only carrier. `SurfaceReply.defaultFb` is burned (CONTRACT-P6:75).
- **Magma is closer** [V] (ENDSTATE:313): rotation and preTransform are already server-side and keyed on the server's own surface.
- **Where the offscreen boundary came from** [V] (`P6-CONTRACT-DRAFT.md:104-109`; `P6-SPAWN-PLAN.md:36-42`; `CONTRACT-P65.md:7`): pbuffer / surfaceless is the P6 and P6.5 path, and real windows are excluded.
- **P12 acceptance gates** [V] (ROADMAP:46):
- (a) FCL, same-machine spawn, Adreno 830, both backends in-world; killing the server gives a clean device-lost latch.
- (b) a client on another machine over TCP reaches in-world on the Redmi, and the P6.5 mandatory numbers are recorded.
- **TLS** is kept as an open P12 item [V] (ROADMAP:279).
## 2. Constraints and gates the new path must satisfy
| Constraint | Source [V] | What it means here |
|---|---|---|
| G1: pull build 0/0/0/0 symbols, `.text` unchanged (`0xa52203`) | ROADMAP:15, :113; CONTRACT-P6:570; STATUS:23 | `BackendObject*.cpp`, `DirectGLES.cpp` (`g_*` at :15814-15817) and `MG_Impl/Init.cpp` compile into the pull build. Edits need `#if MOBILEGL_BUILD_DISAGGREGATED` with the pull arm byte-identical. Precedent: `BackendObject_DirectGLES.cpp:974-977` |
| G2/G14: test names may only be added | ROADMAP:15 | New lanes are additive |
| S5 named refusals | CONTRACT-P6:597; tests `SurfaceControlFrameTest.cpp:366,:388`, `PeerLatchTest.cpp:1250-1257` (`Outcome::Latched`) | AndroidNativeWindow and MetalLayer must stay refused |
| Handshake answers with `Refuse`, never aborts | CONTRACT-P6:249-257; CONTRACT-P65:15 | `RefuseCode` is append-only (`protocol.fbs:74-84`, max `MalformedHello = 8`) |
| Fatal census | CONTRACT-P7:79 | Scan roots include `MG_Backend`. `abort_sites` is a down-ratchet (79). Every `Fatal{Word` needs a `FatalFamilies.def` row; `UnmigratedSurface` already exists (:65) |
| Link ratchet at 171; a rising commit is refused | CONTRACT-P7:138; CONTRACT-P6:704-710 | Server-side code must not start calling client-only symbols |
| fbs is append-only; commit the generated header; flatc-check | CONTRACT-P6:93-102 (`Create*` arity trap); ARCHITECTURE:396-397 | Use builders with explicit `add_*` if appending table fields |
| Control-schema revision | `Protocol/mg_protocol_base.h:54-68` | Bump 2 → 3 and run `protocol_revision_pin.py --write`. `wireFingerprint` moves (`CapsCodec.cpp:480-485`), so the APK and the client redeploy together. An old server sends a new client no Refuse (CONTRACT-P7:182) |
| No branching on link kind above `Transport/` | ARCHITECTURE:451; CONTRACT-P6:640-641 | On-screen vs pbuffer must not be keyed off tcp vs shm |
| One session at a time (`Refuse{Busy}`); one guest per process | CONTRACT-P65:45; CONTRACT-P6:726-734 (D9: `eglTerminate` is process-wide) | — |
| TCP arrival order | CONTRACT-P65:39 | `SurfaceReply.eventHead` is how `SurfaceChanged` delivery is ordered today |
| Control-reply bound | CONTRACT-P6:334-340 | 5000 ms, reset by a `SurfaceProgress` every 250 ms, capped at 120 s. A server blocked waiting for `surfaceCreated` must heartbeat or it reads as "alive but silent" |
| Arm proof (ID-124) and red-once per package (R-16) | CONTRACT-P6:575-577; ROADMAP:12 | Each lane must log which arm actually ran |
| Offscreen stays a gate | CONTRACT-P7:111 (gate 3 runs `--use-pbuffer`) | This path must survive unchanged |
| Don't fix `dev` bugs in passing | ROADMAP:16 | — |
## 3. Documented lifecycle expectations
- **Activity-bound lifecycle** is named but not specified [V] (ROADMAP:46).
- **Freezer** [V]: SPAWN-PLAN:53 and :113 say the full handling is P12's and the diagnostic is P6's; `P6-CONTRACT-DRAFT.md:128-129` says the same. CONTRACT-P6:329-340 rules that a frozen peer gets a named diagnostic, not a device-lost latch. No doc defines what P12's "full handling" is.
- **Death** [V]: the latch comes only from `PeerHungUp()`, never from a timeout (CONTRACT-P6:310-312, :371-378). `MOBILEGL_IPC_RESPAWN=1` is a named refusal (CONTRACT-P6:312).
- **Today's form** [V]:
- a foreground `dataSync` Service in `:mglsrv` (trace manifest:11-15) that exec's the supervisor, which forks one child per session (CONTRACT-P65:45, :49; ROADMAP:103);
- clean client close is a half-close (CONTRACT-P65:47);
- `onDestroy` destroys only the supervisor, so session children leak. This finding is explicitly deferred "→ P12" (`notes/p65/code-review-findings.md:9`; `MobileGLServerService.java:96-101`).
- **EGL teardown hazard** [V]: "Espryt teardown kills the process-default EGL display, so the bench must never share a process with the POST UI" (main `AndroidManifest.xml:25-28`).
## 4. What the docs require be recorded when this lands
- [V] ROADMAP P12 row status plus a milestone entry (the pattern at :230-231); `CURRENT_STAGE_STATUS.md` on every merge, push or device window (:3); red-once per package in notes (CONTRACT-P7:138); the five-part stage-exit gate (ARCHITECTURE §13.2); one Codex review at stage close (ROADMAP:17).
- [V] A comment row in `mg_protocol_base.h` plus the pins json.
- [I] Dispositions for a6 rows A8-3…A8-15 and for CONTRACT-P6 §7.2 D8.
- [I] By precedent: a new `MG_Remote/CONTRACT-P12.md` and an integrator decision log (the ID-P7-n pattern).
- [I] Rewrite ARCHITECTURE §15.2. Do not edit ENDSTATE; it says it is frozen (:22).
## 5. Contradictions between docs and the tree that matter here
1. **Stale P12 path.** `ARCHITECTURE.md:526` still describes the deleted Surface → AIDL → `:mgl` → `ANativeWindow_fromSurface` path. `protocol.fbs:258` still says "Android transfers the window out of band".
2. **Process name.** The docs say `:mgl` (ARCHITECTURE:526, CONTRACT-P6:667, SPAWN-PLAN:122). The tree uses `:mglsrv` (trace manifest:15).
3. **The D8 whitelist and the client-side mirror did NOT land** [V]:
- `SurfaceOpCodec.cpp:113-123` and `:165-185` still accept X11, Win32Hwnd and None and copy `nativeToken`.
- `ServerLoop.cpp:1050-1058` casts it to `void*`.
- The string `"X11@P12"` exists only in CONTRACT-P6:440.
- `BackendObject_Remote.cpp:215` still posts `SetWindowHandle` unconditionally, with no client refusal.
- Consequence: a WSL client that announces X11 sends an XID straight into Android's `eglCreateWindowSurface`.
4. **Abort vs latch.** ROADMAP:46 says the first `eglCreateWindowSurface` "aborts" the server. Since Ph it latches the session instead (`ServerMain.cpp:363` arms the latch; `SurfaceOpCodec.cpp:215-219`).
5. **IPC order.** ROADMAP:19 has P6.5 → Ph → **P12** → P9. `CURRENT_STAGE_STATUS.md:73` and HANDOFF:22 have P9 → P10 → P11 → P12.
6. **Rule H.** `CURRENT_STAGE_PROGRESS.md:244` describes P12 as "real windows cross processes", which Rule H contradicts.
7. **Drifted line numbers.** The DirectGLES `SurfaceChanged` site is now `DirectGLES.cpp:15939-15950`, not :15793-15805. It still posts format only with W/H = 0, so the client keeps the 512×512 placeholder (`MG_Impl/Init.cpp:30-38`). The ServerLoop citations moved (:875-882 → :1050-1058; :1015 → :1232). The BackendObject.cpp citations still resolve.
8. **Death knobs.** ARCHITECTURE:533 describes RESPAWN / IDLE_EXIT as working; CONTRACT-P6:305-312 says they are parsed nowhere.
## OPEN QUESTIONS / RISKS
1. **[I, critical] Process topology.** "Server app owns a SurfaceView, nothing to flatten" only holds if rendering runs inside the ART process that owns that Surface. Today's server is an exec'd native child forked per session, and on minSdk 26 it cannot get that `ANativeWindow`. The options are:
- run the session in-process (via JNI), which runs into D9 (`eglTerminate` is process-wide) and the manifest's warning that Espryt teardown kills the process-default EGL display;
- have the child render into AHardwareBuffers and pass them over SCM_RIGHTS for the Activity to present;
- some other approach. No doc decides this.
2. **Choosing the mode per session is undecided.** Options: a supervisor flag, a `Hello`/`Welcome` field, or a caps bit. Any schema field means another revision bump.
3. **Same-phone gate (a):** if the server app is in the foreground, FCL is backgrounded, and neither input forwarding nor the freezer's effect on the *client* is in any doc.
4. **Server-initiated resize or rotation** has no RPC to carry `eventHead` (CONTRACT-P65:39). The Width/Height `SurfaceChanged` would only be seen at the client's drain points.
5. The freezer's "full handling" is undefined.
6. [I] The `dataSync` foreground-service type may be time-limited on newer Android versions.
7. Multi-context and `g_*` de-globalisation are probably unnecessary while there is one session per process.
## MINIMAL CHANGE LIST
- **`MG_Remote/Protocol/protocol.fbs`:** append `ServerOwned = 7` to `WindowKind`; regenerate `generated/protocol_generated.h`; any mode field is appended the same way.
- **`Protocol/mg_protocol_base.h:68`:** `MOBILEGL_PROTOCOL_CONTROL_REVISION` → 3, plus a comment row; update the pins json.
- **`Protocol/SurfaceOpCodec.cpp`:**
- update the static_assert (:58) and the `IsOutRange` bound (:178-179);
- accept `ServerOwned` only with `nativeToken == 0` and only on a display-owning server; otherwise a named refusal;
- land D8 (refuse X11, Win32Hwnd and None).
- **`MG_Remote/Client/BackendObject_Remote.cpp:210-216`:**
- drop `ServerSetWindowHandle`;
- send `CreateWindowSurface` as `ServerOwned` when the server announces a display;
- otherwise keep today's offscreen path.
- **`Server/ServerLoop.cpp`:** in `UnpackWindowHandle` (:1050), substitute the server's window for `ServerOwned`; `SetWindowHandle` (:1232) becomes a named no-op or refusal.
- **`MG_Backend/BackendObject.cpp`:** substitute at :235, preserve at :365, ignore client extents at :206-207. All under `#if` so G1 holds.
- **`BackendObject_DirectGLES.cpp:955-964`:** new allow-list and a `sameHandle` keyed on the surface token.
- **`BackendObject_DirectVulkan.cpp:415-419`:** the equivalent allow-list change.
- **`DirectGLES.cpp:15939-15950`:** fill Width/Height from `eglQuerySurface` when the server owns the window.
- **android-plugin (trace flavour):**
- add an Activity with a SurfaceView and the window hand-off chosen in Open Question 1;
- add a mode extra on `MobileGLServerService`;
- fix the `onDestroy` child leak.
- **Tests:** keep S5. Add red-once tests for:
- `ServerOwned` with a non-zero token;
- `ServerOwned` sent to an offscreen-only server;
- the X11 whitelist.
Add an arm-proof log line `window=server-owned|pbuffer`.
- **Docs:** see §4, and fix the contradictions in §5, items 1, 2, 5 and 6.