diff --git a/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.cpp b/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.cpp index 9499073f..608bd870 100644 --- a/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.cpp +++ b/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.cpp @@ -56,6 +56,26 @@ namespace MobileGL::MG_Remote { static_cast(::MobileGL::Wire::SurfaceOpKind::SetWindowHandle) == 10 && static_cast(::MobileGL::Wire::SurfaceOpKind::InitCapabilities) == 11); static_assert(static_cast(::MobileGL::Wire::WindowKind::MetalLayer) == 6); + // P12's append, on the same terms: the value is wire ABI. + static_assert(static_cast(::MobileGL::Wire::WindowKind::ServerOwned) == 7); + // The frame's refusal codes ARE the wire's SurfaceRefusal (SurfaceControlFrame.h), pinned + // value by value so a schema edit that renumbers either side fails here. + using Server::SurfaceRefusalCode; + static_assert(static_cast(SurfaceRefusalCode::None) == + static_cast(::MobileGL::Wire::SurfaceRefusal::None)); + static_assert(static_cast(SurfaceRefusalCode::NoServerDisplay) == + static_cast(::MobileGL::Wire::SurfaceRefusal::NoServerDisplay)); + static_assert(static_cast(SurfaceRefusalCode::NoServerWindow) == + static_cast(::MobileGL::Wire::SurfaceRefusal::NoServerWindow)); + static_assert(static_cast(SurfaceRefusalCode::SurfaceModeMismatch) == + static_cast(::MobileGL::Wire::SurfaceRefusal::SurfaceModeMismatch)); + static_assert(static_cast(SurfaceRefusalCode::ServerOwnedOnSetWindowHandle) == + static_cast(::MobileGL::Wire::SurfaceRefusal::ServerOwnedOnSetWindowHandle)); + // The frame-local ServerOwned tag names no WindowBackend, by construction (D2: no + // BackendObject.h enumerator for it - that header is in the pull build, gate G1). + static_assert(Server::kServerOwnedWindowBackend >= + static_cast(MG_Backend::WindowBackend::WindowBackendCount), + "kServerOwnedWindowBackend collides with a real WindowBackend"); bool OpNeedsAWindowBackend(SurfaceControlOp op) { return op == SurfaceControlOp::CreateWindowSurface || op == SurfaceControlOp::SetWindowHandle; @@ -72,6 +92,8 @@ namespace MobileGL::MG_Remote { case SurfaceWireError::UnknownWindowKind: return "UnknownWindowKind"; case SurfaceWireError::MetalLayerArrived: return "MetalLayerArrived"; case SurfaceWireError::AndroidNativeWindowArrived: return "AndroidNativeWindowArrived"; + case SurfaceWireError::ServerOwnedTokenNotZero: return "ServerOwnedTokenNotZero"; + case SurfaceWireError::ServerOwnedOnSetWindowHandle: return "ServerOwnedOnSetWindowHandle"; } return ""; } @@ -130,17 +152,28 @@ namespace MobileGL::MG_Remote { : SurfaceWireError::InprocOnlyOpOnTheWire; } ::MobileGL::Wire::WindowKind windowKind = ::MobileGL::Wire::WindowKind::None; + Uint64 nativeToken = frame.nativeToken; if (frame.kind == SurfaceControlOp::CreatePbufferSurface) { // The op implies the shape; the frame carries no windowBackend for it. windowKind = ::MobileGL::Wire::WindowKind::Pbuffer; } else if (OpNeedsAWindowBackend(frame.kind)) { - if (!WireWindowKindForWindowBackend( - static_cast(frame.windowBackend), &windowKind)) { + if (frame.windowBackend == Server::kServerOwnedWindowBackend) { + // P12 (D2): the SERVER's window. Only a creation may ask for it - the server picks + // the window, so there is nothing for SetWindowHandle to set - and the token is 0 + // whatever the frame carried: the client's own window value (it may have none) + // never reaches the wire (Rule G/H). + if (frame.kind != SurfaceControlOp::CreateWindowSurface) { + return SurfaceWireError::ServerOwnedOnSetWindowHandle; + } + windowKind = ::MobileGL::Wire::WindowKind::ServerOwned; + nativeToken = 0; + } else if (!WireWindowKindForWindowBackend( + static_cast(frame.windowBackend), &windowKind)) { return SurfaceWireError::UnknownWindowKind; } } const auto op = ::MobileGL::Wire::CreateSurfaceOp( - *builder, frame.seq, kind, frame.display, frame.surface, windowKind, frame.nativeToken, + *builder, frame.seq, kind, frame.display, frame.surface, windowKind, nativeToken, frame.width, frame.height, frame.swapInterval, frame.readSurface, frame.context); const auto envelope = ::MobileGL::Wire::CreateCtrlEnvelope(*builder, ::MobileGL::Wire::CtrlMsg::SurfaceOp, op.Union()); ::MobileGL::Wire::FinishCtrlEnvelopeBuffer(*builder, envelope); @@ -163,6 +196,23 @@ namespace MobileGL::MG_Remote { decoded.height = op.height(); decoded.swapInterval = op.swapInterval(); if (OpNeedsAWindowBackend(kind)) { + if (op.windowKind() == ::MobileGL::Wire::WindowKind::ServerOwned) { + // P12 (D2). Asked BEFORE the Android refusal below, which it is not: the window is + // the server's own. A creation only (SetWindowHandle may not name it - a named + // refusal, not corruption: nothing about the bytes is malformed), and a token of 0 + // only (anything else is a value from the peer's window system trying to cross, + // Rule G - that one is corruption). + if (kind != SurfaceControlOp::CreateWindowSurface) { + return SurfaceWireError::ServerOwnedOnSetWindowHandle; + } + if (op.nativeToken() != 0) { + return SurfaceWireError::ServerOwnedTokenNotZero; + } + decoded.windowBackend = Server::kServerOwnedWindowBackend; + decoded.nativeToken = 0; + *frame = decoded; + return SurfaceWireError::None; + } if (op.windowKind() == ::MobileGL::Wire::WindowKind::AndroidNativeWindow) { // The one refusal that is not corruption: the window kind is LEGAL and the token // is an ANativeWindow*, which names memory in the CLIENT's process. Real window @@ -175,8 +225,10 @@ namespace MobileGL::MG_Remote { } MG_Backend::WindowBackend backend; if (!WindowBackendForWireWindowKind(op.windowKind(), &backend)) { + // THE UPPER BOUND IS THE LAST APPENDED KIND (P12: ServerOwned), for the reason + // SurfaceControlOpForWireKind's bound gives. return ::flatbuffers::IsOutRange(op.windowKind(), ::MobileGL::Wire::WindowKind::None, - ::MobileGL::Wire::WindowKind::MetalLayer) + ::MobileGL::Wire::WindowKind::ServerOwned) ? SurfaceWireError::UnknownWindowKind : SurfaceWireError::WindowKindNamesNoBackend; } @@ -189,8 +241,10 @@ namespace MobileGL::MG_Remote { void EncodeSurfaceReplyFrame(const SurfaceControlFrame& frame, flatbuffers::FlatBufferBuilder* builder) { - const auto reply = ::MobileGL::Wire::CreateSurfaceReply(*builder, frame.seq, frame.ok, frame.eglMajor, - frame.eglMinor, /*defaultFb=*/0, frame.eventHead); + // P12: the reply's surface geometry and refusal (SurfaceReply.width/height/refusal). + const auto reply = ::MobileGL::Wire::CreateSurfaceReply( + *builder, frame.seq, frame.ok, frame.eglMajor, frame.eglMinor, /*defaultFb=*/0, frame.eventHead, + frame.width, frame.height, static_cast<::MobileGL::Wire::SurfaceRefusal>(frame.refusal)); const auto envelope = ::MobileGL::Wire::CreateCtrlEnvelope(*builder, ::MobileGL::Wire::CtrlMsg::SurfaceReply, reply.Union()); ::MobileGL::Wire::FinishCtrlEnvelopeBuffer(*builder, envelope); @@ -202,6 +256,9 @@ namespace MobileGL::MG_Remote { frame->eglMajor = reply.eglMajor(); frame->eglMinor = reply.eglMinor(); frame->eventHead = reply.eventHead(); + frame->width = reply.width(); + frame->height = reply.height(); + frame->refusal = static_cast(reply.refusal()); } MobileGLResult ServerApplyWireSurfaceOp(const ::MobileGL::Wire::SurfaceOp& op, SurfaceControlFrame* replyOut) { @@ -211,10 +268,39 @@ namespace MobileGL::MG_Remote { if (SessionLatched()) return MOBILEGL_ERR_PROTOCOL_MISMATCH; SurfaceControlFrame frame; const SurfaceWireError error = DecodeWireSurfaceOp(op, &frame); + if (error == SurfaceWireError::ServerOwnedOnSetWindowHandle) { + // P12 (D2): A NAMED REFUSAL, NOT A LATCH. Nothing in the bytes is malformed - the op + // simply asks for something the protocol does not do (the server chooses its own + // window at CreateWindowSurface; there is nothing for SetWindowHandle to set). So it + // is answered ok=false, by name, and the session goes on, armed or not. + MGLOG_E("MG_Remote server: Refuse ServerOwned on SetWindowHandle - a wire SetWindowHandle " + "(seq %llu) named WindowKind::ServerOwned. The server-owned window is chosen by the " + "server when CreateWindowSurface asks for it; SetWindowHandle may not name it " + "(refusal ServerOwnedOnSetWindowHandle, session not latched)", + static_cast(op.seq())); + if (replyOut != nullptr) { + *replyOut = SurfaceControlFrame{}; + replyOut->seq = op.seq(); + replyOut->ok = false; + replyOut->refusal = static_cast(Server::SurfaceRefusalCode::ServerOwnedOnSetWindowHandle); + } + return MOBILEGL_ERR_UNSUPPORTED; + } if (error != SurfaceWireError::None) { // PH-1 (3): the op's bytes are the peer's. Each of the three refusals latches in an // armed session child and the op is answered with no dispatch; unarmed they die. - if (error == SurfaceWireError::AndroidNativeWindowArrived) { + if (error == SurfaceWireError::ServerOwnedTokenNotZero) { + // P12 (D2): WindowKind::ServerOwned names the SERVER's window, so its token is 0 by + // definition. A non-zero one is a value from the peer's window system trying to + // cross (Rule G) - the peer's corrupt bytes, latched like the rows beside it. + (void)SessionLatch(MGFatalFamily::ProtocolCorruption, + "MGPipe: Fatal{ProtocolCorruption, \"SurfaceOp.nativeToken\"} - a wire %s named " + "WindowKind::ServerOwned with native token 0x%llx. The server-owned window is the " + "server's own and its token is always 0: no value from the client's window system " + "crosses (Rule G)", + ::MobileGL::Wire::EnumNameSurfaceOpKind(op.kind()), + static_cast(op.nativeToken())); + } else if (error == SurfaceWireError::AndroidNativeWindowArrived) { (void)SessionLatch(MGFatalFamily::UnmigratedSurface, "MGPipe: Fatal{UnmigratedSurface, \"AndroidNativeWindow@P12\"} - a wire " "SurfaceOp (%s) named an ANativeWindow*, which is a pointer into the " diff --git a/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.h b/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.h index 58b73568..0796e24e 100644 --- a/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.h +++ b/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.h @@ -30,6 +30,14 @@ // Fatal{ProtocolCorruption, "SurfaceOp"}. P6's control pump is the intended caller; P5f's unit // tests drive it directly. MetalLayerArrived likewise dies // Fatal{UnmigratedSurface, "MetalLayer@P12"}: CAMetalLayer* is also process-local. +// +// P12 (on-screen server window). WindowKind::ServerOwned is the one window kind whose window is +// NOT the client's: the server substitutes its own (ServerLoop.cpp's ServerOwned arm). The codec +// maps it onto the frame-local Server::kServerOwnedWindowBackend and writes/reads nativeToken 0, +// always - so the client's window value never reaches the wire. A ServerOwned op with a non-zero +// token is a peer's corrupt bytes (Fatal{ProtocolCorruption, "SurfaceOp.nativeToken"}, latched); +// SetWindowHandle naming it is a named refusal (SurfaceRefusal::ServerOwnedOnSetWindowHandle), +// answered and not latched. #pragma once #include @@ -56,6 +64,10 @@ namespace MobileGL::MG_Remote { UnknownWindowKind, // a WindowKind the mapping table does not know MetalLayerArrived, // -> Fatal{UnmigratedSurface, "MetalLayer@P12"} AndroidNativeWindowArrived, // -> Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"} + // P12 (on-screen server window), D2. WindowKind::ServerOwned is legal on + // CreateWindowSurface only, and only with nativeToken 0: + ServerOwnedTokenNotZero, // -> Fatal{ProtocolCorruption, "SurfaceOp.nativeToken"}, latched + ServerOwnedOnSetWindowHandle, // -> a NAMED REFUSAL (reply ok=false), not a latch }; const char* SurfaceWireErrorName(SurfaceWireError error); diff --git a/MobileGL/MG_Remote/Protocol/generated/protocol_generated.h b/MobileGL/MG_Remote/Protocol/generated/protocol_generated.h index 97fecd26..3dd6897a 100644 --- a/MobileGL/MG_Remote/Protocol/generated/protocol_generated.h +++ b/MobileGL/MG_Remote/Protocol/generated/protocol_generated.h @@ -337,11 +337,12 @@ enum class WindowKind : uint8_t { Surfaceless = 4, Pbuffer = 5, MetalLayer = 6, + ServerOwned = 7, MIN = None, - MAX = MetalLayer + MAX = ServerOwned }; -inline const WindowKind (&EnumValuesWindowKind())[7] { +inline const WindowKind (&EnumValuesWindowKind())[8] { static const WindowKind values[] = { WindowKind::None, WindowKind::AndroidNativeWindow, @@ -349,13 +350,14 @@ inline const WindowKind (&EnumValuesWindowKind())[7] { WindowKind::Win32Hwnd, WindowKind::Surfaceless, WindowKind::Pbuffer, - WindowKind::MetalLayer + WindowKind::MetalLayer, + WindowKind::ServerOwned }; return values; } inline const char * const *EnumNamesWindowKind() { - static const char * const names[8] = { + static const char * const names[9] = { "None", "AndroidNativeWindow", "X11", @@ -363,17 +365,57 @@ inline const char * const *EnumNamesWindowKind() { "Surfaceless", "Pbuffer", "MetalLayer", + "ServerOwned", nullptr }; return names; } inline const char *EnumNameWindowKind(WindowKind e) { - if (::flatbuffers::IsOutRange(e, WindowKind::None, WindowKind::MetalLayer)) return ""; + if (::flatbuffers::IsOutRange(e, WindowKind::None, WindowKind::ServerOwned)) return ""; const size_t index = static_cast(e); return EnumNamesWindowKind()[index]; } +enum class SurfaceRefusal : uint8_t { + None = 0, + NoServerDisplay = 1, + NoServerWindow = 2, + SurfaceModeMismatch = 3, + ServerOwnedOnSetWindowHandle = 4, + MIN = None, + MAX = ServerOwnedOnSetWindowHandle +}; + +inline const SurfaceRefusal (&EnumValuesSurfaceRefusal())[5] { + static const SurfaceRefusal values[] = { + SurfaceRefusal::None, + SurfaceRefusal::NoServerDisplay, + SurfaceRefusal::NoServerWindow, + SurfaceRefusal::SurfaceModeMismatch, + SurfaceRefusal::ServerOwnedOnSetWindowHandle + }; + return values; +} + +inline const char * const *EnumNamesSurfaceRefusal() { + static const char * const names[6] = { + "None", + "NoServerDisplay", + "NoServerWindow", + "SurfaceModeMismatch", + "ServerOwnedOnSetWindowHandle", + nullptr + }; + return names; +} + +inline const char *EnumNameSurfaceRefusal(SurfaceRefusal e) { + if (::flatbuffers::IsOutRange(e, SurfaceRefusal::None, SurfaceRefusal::ServerOwnedOnSetWindowHandle)) return ""; + const size_t index = static_cast(e); + return EnumNamesSurfaceRefusal()[index]; +} + enum class AuxRequestKind : uint8_t { None = 0, FenceClientWait = 1, @@ -1820,7 +1862,10 @@ struct SurfaceReply FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { VT_EGLMAJOR = 8, VT_EGLMINOR = 10, VT_DEFAULTFB = 12, - VT_EVENTHEAD = 14 + VT_EVENTHEAD = 14, + VT_WIDTH = 16, + VT_HEIGHT = 18, + VT_REFUSAL = 20 }; uint64_t seq() const { return GetField(VT_SEQ, 0); @@ -1840,6 +1885,15 @@ struct SurfaceReply FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { uint64_t eventHead() const { return GetField(VT_EVENTHEAD, 0); } + int32_t width() const { + return GetField(VT_WIDTH, 0); + } + int32_t height() const { + return GetField(VT_HEIGHT, 0); + } + MobileGL::Wire::SurfaceRefusal refusal() const { + return static_cast(GetField(VT_REFUSAL, 0)); + } template bool Verify(::flatbuffers::VerifierTemplate &verifier) const { return VerifyTableStart(verifier) && @@ -1850,6 +1904,9 @@ struct SurfaceReply FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { VerifyOffset(verifier, VT_DEFAULTFB) && verifier.VerifyTable(defaultFb()) && VerifyField(verifier, VT_EVENTHEAD, 8) && + VerifyField(verifier, VT_WIDTH, 4) && + VerifyField(verifier, VT_HEIGHT, 4) && + VerifyField(verifier, VT_REFUSAL, 1) && verifier.EndTable(); } }; @@ -1876,6 +1933,15 @@ struct SurfaceReplyBuilder { void add_eventHead(uint64_t eventHead) { fbb_.AddElement(SurfaceReply::VT_EVENTHEAD, eventHead, 0); } + void add_width(int32_t width) { + fbb_.AddElement(SurfaceReply::VT_WIDTH, width, 0); + } + void add_height(int32_t height) { + fbb_.AddElement(SurfaceReply::VT_HEIGHT, height, 0); + } + void add_refusal(MobileGL::Wire::SurfaceRefusal refusal) { + fbb_.AddElement(SurfaceReply::VT_REFUSAL, static_cast(refusal), 0); + } explicit SurfaceReplyBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); @@ -1894,13 +1960,19 @@ inline ::flatbuffers::Offset CreateSurfaceReply( int32_t eglMajor = 0, int32_t eglMinor = 0, ::flatbuffers::Offset defaultFb = 0, - uint64_t eventHead = 0) { + uint64_t eventHead = 0, + int32_t width = 0, + int32_t height = 0, + MobileGL::Wire::SurfaceRefusal refusal = MobileGL::Wire::SurfaceRefusal::None) { SurfaceReplyBuilder builder_(_fbb); builder_.add_eventHead(eventHead); builder_.add_seq(seq); + builder_.add_height(height); + builder_.add_width(width); builder_.add_defaultFb(defaultFb); builder_.add_eglMinor(eglMinor); builder_.add_eglMajor(eglMajor); + builder_.add_refusal(refusal); builder_.add_ok(ok); return builder_.Finish(); } diff --git a/MobileGL/MG_Remote/Protocol/mg_protocol_base.h b/MobileGL/MG_Remote/Protocol/mg_protocol_base.h index 27d1765e..edf4e928 100644 --- a/MobileGL/MG_Remote/Protocol/mg_protocol_base.h +++ b/MobileGL/MG_Remote/Protocol/mg_protocol_base.h @@ -65,7 +65,9 @@ extern "C" { // bump: `python3 scripts/ci/protocol_revision_pin.py --write` records the new row. // 1 P7 wave 2-F, PH-7 (4): Welcome.dataNonce and the DataBind message. // 2 P7 (p7/spawnhang): the SurfaceProgress message. -#define MOBILEGL_PROTOCOL_CONTROL_REVISION 2 +// 3 P12 (on-screen server window): WindowKind.ServerOwned, the SurfaceRefusal enum and +// SurfaceReply.width/height/refusal. +#define MOBILEGL_PROTOCOL_CONTROL_REVISION 3 #define MOBILEGL_ABI_VERSION(major, minor) (((uint32_t)(major) << 16) | (uint32_t)(minor)) #define MOBILEGL_ABI_MAJOR_OF(version) ((uint32_t)(version) >> 16) diff --git a/MobileGL/MG_Remote/Protocol/protocol.fbs b/MobileGL/MG_Remote/Protocol/protocol.fbs index ac552158..db1f9487 100644 --- a/MobileGL/MG_Remote/Protocol/protocol.fbs +++ b/MobileGL/MG_Remote/Protocol/protocol.fbs @@ -247,6 +247,22 @@ enum WindowKind : ubyte { // the two enums may only meet through the explicit mapping table in Protocol/SurfaceOpCodec.cpp // - Surfaceless/Pbuffer are surface SHAPES, not window backends, and answer "no backend" there. MetalLayer = 6, + // P12 (on-screen server window), appended. THE WINDOW IS THE SERVER'S OWN: the client asks the + // server to create the surface on the window the server process owns (the display Activity's + // SurfaceView) and has none of its own to name, so `nativeToken` is always 0 and no pointer + // crosses (Rule G/H). Legal on CreateWindowSurface only; SetWindowHandle may not name it. + ServerOwned = 7, +} + +// P12, appended: WHY A SURFACE OP WAS DECLINED, so the client can name the refusal in its own log +// instead of reading a bare `ok = false`. None of these latches the session: each is a +// configuration or ordering answer, not a peer's corrupt bytes. +enum SurfaceRefusal : ubyte { + None = 0, + NoServerDisplay = 1, // ServerOwned asked of a server that owns no display + NoServerWindow = 2, // the server owns a display, but no window came within its wait + SurfaceModeMismatch = 3, // the session's surface mode (on-screen / offscreen) is the other one + ServerOwnedOnSetWindowHandle = 4, // SetWindowHandle named the server's own window } table SurfaceOp { @@ -255,7 +271,7 @@ table SurfaceOp { display: ulong; surface: ulong; windowKind: WindowKind; - nativeToken: ulong; // X11 XID / HWND; Android transfers the window out of band + nativeToken: ulong; // X11 XID / HWND; 0 for ServerOwned (the server substitutes its own window) width: int; height: int; swapInterval: int; @@ -275,6 +291,14 @@ table SurfaceReply { defaultFb: DefaultFramebufferInfo; // P6.5: all preceding data-plane events must arrive before this reply is observed. eventHead: ulong; + // P12, appended. The surface's REAL extent as the server has it - for a ServerOwned + // CreateWindowSurface, the server window's size - so a headless client knows its surface + // geometry when eglCreateWindowSurface returns, before any backend has published a + // surface-changed event (Magma creates its swapchain only at the first MakeCurrent). 0/0 for + // every other op. + width: int; + height: int; + refusal: SurfaceRefusal; } // --------------------------------------------------------------------------- diff --git a/MobileGL/MG_Remote/Server/SurfaceControlFrame.cpp b/MobileGL/MG_Remote/Server/SurfaceControlFrame.cpp index 01bba314..2f5c5dbd 100644 --- a/MobileGL/MG_Remote/Server/SurfaceControlFrame.cpp +++ b/MobileGL/MG_Remote/Server/SurfaceControlFrame.cpp @@ -53,4 +53,15 @@ namespace MobileGL::MG_Remote::Server { } } + const char* SurfaceRefusalCodeName(SurfaceRefusalCode code) { + switch (code) { + case SurfaceRefusalCode::None: return "None"; + case SurfaceRefusalCode::NoServerDisplay: return "NoServerDisplay"; + case SurfaceRefusalCode::NoServerWindow: return "NoServerWindow"; + case SurfaceRefusalCode::SurfaceModeMismatch: return "SurfaceModeMismatch"; + case SurfaceRefusalCode::ServerOwnedOnSetWindowHandle: return "ServerOwnedOnSetWindowHandle"; + } + return ""; + } + } // namespace MobileGL::MG_Remote::Server diff --git a/MobileGL/MG_Remote/Server/SurfaceControlFrame.h b/MobileGL/MG_Remote/Server/SurfaceControlFrame.h index 5107dc6f..c7811b4a 100644 --- a/MobileGL/MG_Remote/Server/SurfaceControlFrame.h +++ b/MobileGL/MG_Remote/Server/SurfaceControlFrame.h @@ -30,7 +30,9 @@ // // PROCESS-LOCAL WINDOW OBJECTS ARE REFUSED BY NAME on the wire: ANativeWindow* and // CAMetalLayer* mean nothing in the server process. SurfaceOpCodec names their refusals -// AndroidNativeWindow@P12 and MetalLayer@P12; real window arrival is P12. +// AndroidNativeWindow@P12 and MetalLayer@P12, and they stay (Rule H): P12 does not carry a +// client's window across, it gives the SERVER a window of its own (kServerOwnedWindowBackend +// below, WindowKind::ServerOwned on the wire). #pragma once #include @@ -72,6 +74,33 @@ namespace MobileGL::MG_Remote::Server { ProbeForTesting = 14, // MG_Test's arbitrary-work seam through the same channel }; + // P12 (on-screen server window), D2. THE SERVER-OWNED WINDOW, AS A FRAME-LOCAL TAG. + // + // A client that sets MOBILEGL_IPC_SURFACE=server has no window the server could use (it may + // have none at all), so its CreateWindowSurface names the SERVER's window instead: the wire's + // WindowKind::ServerOwned with nativeToken 0. Inside a process the request travels in the + // frame's `windowBackend` slot as this value - deliberately NOT a MG_Backend::WindowBackend + // enumerator (BackendObject.h is in the pull build, gate G1), and deliberately outside every + // range a WindowBackend could grow to, so a frame carrying it that ever reached + // UnpackWindowHandle would be refused by WindowBackendFromFrameValue's range check rather than + // cast. The server's ServerOwned arm (ServerLoop.cpp) consumes it before that point and + // substitutes its own window there; no pointer crosses the wire (Rule G/H). + inline constexpr Int kServerOwnedWindowBackend = 0x10000; + + // P12. Why the server declined a surface op, carried back in the reply half (`refusal`). The + // values ARE the wire's SurfaceRefusal (protocol.fbs); SurfaceOpCodec pins the agreement with + // static_asserts, the way it pins SurfaceControlOp against SurfaceOpKind. None of them latches + // the session. + enum class SurfaceRefusalCode : Uint8 { + None = 0, + NoServerDisplay = 1, // ServerOwned asked of a server that owns no display + NoServerWindow = 2, // a display, but no window within the wait + SurfaceModeMismatch = 3, // D4: the session's surface mode is the other one + ServerOwnedOnSetWindowHandle = 4, // SetWindowHandle named the server's own window + }; + + const char* SurfaceRefusalCodeName(SurfaceRefusalCode code); + struct SurfaceControlFrame { SurfaceControlOp kind = SurfaceControlOp::None; // Minted by the poster (RunSurfaceControlFrame), echoed back with the reply. 0 means @@ -97,6 +126,10 @@ namespace MobileGL::MG_Remote::Server { Int eglMajor = 0; Int eglMinor = 0; Uint64 eventHead = 0; // data-plane delivery fence carried by SurfaceReply + // P12: a SurfaceRefusalCode (0 = none). `width`/`height` above double as the reply's + // surface geometry: a ServerOwned CreateWindowSurface answers with the server window's + // real extent there (SurfaceReply.width/height). + Uint8 refusal = 0; }; namespace Detail { @@ -111,7 +144,8 @@ namespace MobileGL::MG_Remote::Server { // Exhaustive by the language's aggregate decomposition rule: adding ANY member // without extending this binding is a compile error, so new fields cannot evade it. const auto& [kind, seq, display, surface, readSurface, context, windowBackend, - nativeToken, width, height, swapInterval, ok, eglMajor, eglMinor, eventHead] = frame; + nativeToken, width, height, swapInterval, ok, eglMajor, eglMinor, eventHead, + refusal] = frame; return IsSurfaceControlValue && IsSurfaceControlValue && IsSurfaceControlValue && IsSurfaceControlValue && IsSurfaceControlValue && IsSurfaceControlValue && @@ -119,7 +153,7 @@ namespace MobileGL::MG_Remote::Server { IsSurfaceControlValue && IsSurfaceControlValue && IsSurfaceControlValue && IsSurfaceControlValue && IsSurfaceControlValue && IsSurfaceControlValue && - IsSurfaceControlValue; + IsSurfaceControlValue && IsSurfaceControlValue; } } // namespace Detail diff --git a/MobileGL/MG_Test/Wire/PeerLatchTest.cpp b/MobileGL/MG_Test/Wire/PeerLatchTest.cpp index f6b6539d..5365ca62 100644 --- a/MobileGL/MG_Test/Wire/PeerLatchTest.cpp +++ b/MobileGL/MG_Test/Wire/PeerLatchTest.cpp @@ -1262,6 +1262,14 @@ namespace { {"SurfaceOpUnknownKind", "SurfaceOpCodec.cpp", "SurfaceOp", Outcome::Latched, "Fatal{ProtocolCorruption, \"SurfaceOp\"} - a wire surface op failed validation: UnknownOpKind", false, [](Client::ClientSession& c, PeerReport& r) { return SendSurfaceOp(c, r, 200, 0); }}, + // P12 (D2): WindowKind::ServerOwned names the SERVER's window, so its token is 0 by + // definition; SendSurfaceOp's 0x1234 is a client value trying to cross (Rule G). + {"SurfaceOpServerOwnedNonZeroToken", "SurfaceOpCodec.cpp", "SurfaceOp.nativeToken", Outcome::Latched, + "Fatal{ProtocolCorruption, \"SurfaceOp.nativeToken\"}", false, + [](Client::ClientSession& c, PeerReport& r) { + return SendSurfaceOp(c, r, kCreateWindowSurface, + static_cast(::MobileGL::Wire::WindowKind::ServerOwned)); + }}, // ===== the D11 / PH bounds F2 landed (driver's rows, now with a next session) {"D11CreateRenderStateCsoSlot", "PipeApply.cpp", "CreateRenderState.Cso.Slot", Outcome::Fatal, "Fatal{ProtocolCorruption, \"CreateRenderState.Cso.Slot\"}", false, diff --git a/scripts/ci/protocol_revision_pins.json b/scripts/ci/protocol_revision_pins.json index fb01fcde..b8443b0e 100644 --- a/scripts/ci/protocol_revision_pins.json +++ b/scripts/ci/protocol_revision_pins.json @@ -1,4 +1,5 @@ { "1": "7fc859f7fa756818401f4162e7d49a25d430b91e44d89c297321e3cb36c69638", - "2": "b30928e6fc93ddc015fd86f634a6820f775a6f2f6de5a848d273981e706b2597" + "2": "b30928e6fc93ddc015fd86f634a6820f775a6f2f6de5a848d273981e706b2597", + "3": "da09f0acb48cf245703f28556ef5a38bc9e25241ebbf7b6baa9ab1765b7ddd50" }