[MG_Remote, MG_Test, scripts] (P12): WindowKind::ServerOwned on the wire - control revision 3 with SurfaceRefusal and SurfaceReply width/height/refusal, the codec's ServerOwned rules (creation only, token 0, SetWindowHandle a named refusal) on a frame-local marker, and the non-zero-token latch row

This commit is contained in:
2026-09-23 23:55:23 -04:00
parent 9f669e5215
commit 457aebe7f0
9 changed files with 270 additions and 20 deletions
+93 -7
View File
@@ -56,6 +56,26 @@ namespace MobileGL::MG_Remote {
static_cast<Uint8>(::MobileGL::Wire::SurfaceOpKind::SetWindowHandle) == 10 &&
static_cast<Uint8>(::MobileGL::Wire::SurfaceOpKind::InitCapabilities) == 11);
static_assert(static_cast<Uint8>(::MobileGL::Wire::WindowKind::MetalLayer) == 6);
// P12's append, on the same terms: the value is wire ABI.
static_assert(static_cast<Uint8>(::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<Uint8>(SurfaceRefusalCode::None) ==
static_cast<Uint8>(::MobileGL::Wire::SurfaceRefusal::None));
static_assert(static_cast<Uint8>(SurfaceRefusalCode::NoServerDisplay) ==
static_cast<Uint8>(::MobileGL::Wire::SurfaceRefusal::NoServerDisplay));
static_assert(static_cast<Uint8>(SurfaceRefusalCode::NoServerWindow) ==
static_cast<Uint8>(::MobileGL::Wire::SurfaceRefusal::NoServerWindow));
static_assert(static_cast<Uint8>(SurfaceRefusalCode::SurfaceModeMismatch) ==
static_cast<Uint8>(::MobileGL::Wire::SurfaceRefusal::SurfaceModeMismatch));
static_assert(static_cast<Uint8>(SurfaceRefusalCode::ServerOwnedOnSetWindowHandle) ==
static_cast<Uint8>(::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<Int>(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 "<unknown SurfaceWireError>";
}
@@ -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<MG_Backend::WindowBackend>(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<MG_Backend::WindowBackend>(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<Uint8>(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<unsigned long long>(op.seq()));
if (replyOut != nullptr) {
*replyOut = SurfaceControlFrame{};
replyOut->seq = op.seq();
replyOut->ok = false;
replyOut->refusal = static_cast<Uint8>(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<unsigned long long>(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 "
@@ -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 <Includes.h>
@@ -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);
@@ -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<size_t>(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<size_t>(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<uint64_t>(VT_SEQ, 0);
@@ -1840,6 +1885,15 @@ struct SurfaceReply FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
uint64_t eventHead() const {
return GetField<uint64_t>(VT_EVENTHEAD, 0);
}
int32_t width() const {
return GetField<int32_t>(VT_WIDTH, 0);
}
int32_t height() const {
return GetField<int32_t>(VT_HEIGHT, 0);
}
MobileGL::Wire::SurfaceRefusal refusal() const {
return static_cast<MobileGL::Wire::SurfaceRefusal>(GetField<uint8_t>(VT_REFUSAL, 0));
}
template <bool B = false>
bool Verify(::flatbuffers::VerifierTemplate<B> &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<uint64_t>(verifier, VT_EVENTHEAD, 8) &&
VerifyField<int32_t>(verifier, VT_WIDTH, 4) &&
VerifyField<int32_t>(verifier, VT_HEIGHT, 4) &&
VerifyField<uint8_t>(verifier, VT_REFUSAL, 1) &&
verifier.EndTable();
}
};
@@ -1876,6 +1933,15 @@ struct SurfaceReplyBuilder {
void add_eventHead(uint64_t eventHead) {
fbb_.AddElement<uint64_t>(SurfaceReply::VT_EVENTHEAD, eventHead, 0);
}
void add_width(int32_t width) {
fbb_.AddElement<int32_t>(SurfaceReply::VT_WIDTH, width, 0);
}
void add_height(int32_t height) {
fbb_.AddElement<int32_t>(SurfaceReply::VT_HEIGHT, height, 0);
}
void add_refusal(MobileGL::Wire::SurfaceRefusal refusal) {
fbb_.AddElement<uint8_t>(SurfaceReply::VT_REFUSAL, static_cast<uint8_t>(refusal), 0);
}
explicit SurfaceReplyBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
@@ -1894,13 +1960,19 @@ inline ::flatbuffers::Offset<SurfaceReply> CreateSurfaceReply(
int32_t eglMajor = 0,
int32_t eglMinor = 0,
::flatbuffers::Offset<MobileGL::Wire::DefaultFramebufferInfo> 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();
}
@@ -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)
+25 -1
View File
@@ -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;
}
// ---------------------------------------------------------------------------
@@ -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 "<unknown SurfaceRefusalCode>";
}
} // namespace MobileGL::MG_Remote::Server
@@ -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 <Includes.h>
@@ -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<decltype(kind)> && IsSurfaceControlValue<decltype(seq)> &&
IsSurfaceControlValue<decltype(display)> && IsSurfaceControlValue<decltype(surface)> &&
IsSurfaceControlValue<decltype(readSurface)> && IsSurfaceControlValue<decltype(context)> &&
@@ -119,7 +153,7 @@ namespace MobileGL::MG_Remote::Server {
IsSurfaceControlValue<decltype(width)> && IsSurfaceControlValue<decltype(height)> &&
IsSurfaceControlValue<decltype(swapInterval)> && IsSurfaceControlValue<decltype(ok)> &&
IsSurfaceControlValue<decltype(eglMajor)> && IsSurfaceControlValue<decltype(eglMinor)> &&
IsSurfaceControlValue<decltype(eventHead)>;
IsSurfaceControlValue<decltype(eventHead)> && IsSurfaceControlValue<decltype(refusal)>;
}
} // namespace Detail
+8
View File
@@ -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<Uint8>(::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,
+2 -1
View File
@@ -1,4 +1,5 @@
{
"1": "7fc859f7fa756818401f4162e7d49a25d430b91e44d89c297321e3cb36c69638",
"2": "b30928e6fc93ddc015fd86f634a6820f775a6f2f6de5a848d273981e706b2597"
"2": "b30928e6fc93ddc015fd86f634a6820f775a6f2f6de5a848d273981e706b2597",
"3": "da09f0acb48cf245703f28556ef5a38bc9e25241ebbf7b6baa9ab1765b7ddd50"
}