Chapter 67 Android and Apple Platform Evidence
Platform support is not one fact. A source tree may contain platform branches, cross-compile, run tests in an emulator, package an application, draw a frame, and survive real hardware—or stop at any rung in between. Android retains recorded emulator evidence. Alpha.1 contains declared native macOS, iOS device-link and iOS simulator-launch routes, while tvOS remains outside the implemented host. Workflow source establishes those routes; without the tag’s Actions run records this edition does not promote each route to observed success.
Target OS, selected platform implementation and renderer are separate. The Apple and Android routes described here use the SDL3 platform implementation unless configured otherwise; iOS is a target OS, not a fifth CNA_PLATFORM value. The iOS smoke selects SDL_RENDERER; native METAL remains macOS-only at the tag.
67.1 Android application structure
The Devices demo contains a complete Gradle and NDK application rather than a sketch. Its 39 files include a Gradle wrapper, manifest, SDLActivity subclass, SDL Java glue, launcher resources, and native build. The JNI-side CMake file uses add_subdirectory(<cna-root>), so producing the APK compiles the CNA library as part of the application. The recorded configuration uses compile SDK 35, NDK 30, minimum SDK 24, and only the arm64-v8a ABI.
Android-specific production code is concentrated rather than scattered. The Devices module contains about 2,090 lines of NDK sensor implementation using ASensorManager, an ALooper, event queues and rollback. Each AndroidSensorBridge can own a dedicated polling thread; the Motion backend aggregates six bridge instances, so the single std::thread construction site can produce several concurrent workers. CNA itself uses no JNI. Its only Android-specific link adds android and log to the Devices module; the root CMake file contains no Android branch. Portable sensor mathematics stays outside __ANDROID__ guards so native tests can exercise it, while the I/O boundary is platform-gated.
67.2 Recorded Android execution
Two emulator campaigns are recorded. On 4 July 2026 an NDK-built CnaTests executable was pushed to a Medium Phone AVD. All 230 Net/Gamer/ENet/Packet cases and 1,831 other cases passed before an unrelated TitleContainer test reached the no-Activity crash described below; surviving build-system repairs corroborate the run. On 5 July the Devices application produced a 7.3 MB APK, installed and started it, captured the emulator screen, and injected sensor data through the emulator console. The commit record says the real UI and its sensor-event flash were visible in that screenshot. That injection exercised SDL’s Accelerometer/Gyroscope delivery; Compass/Motion’s CNA-owned NDK bridge and six-source fusion were not separately driven.
Those are meaningful execution results, but the repository contains no APK, AAB, logcat log, Android screenshot, or machine-readable test result from them. The strongest honest wording is therefore “recorded emulator execution,” not a continuously reproducible Android gate. No CNA binary has been evidenced on physical Android hardware at the audited revision.
Android graphics is a separate and still open boundary, but “never attempted” is too broad. The July Devices APK’s nested CNA build used Android’s default SDL_RENDERER, and its commit records screenshot-confirmed UI presentation. That is historical renderer execution, not a retained pixel oracle: no screenshot survives in the repository, no graphics-specific assertion was made, and the later adaptation was not rebuilt at the audited pin. WebGPU is the only renderer containing an explicit Android native-window surface path, and it has not run there. The strongest claim is thus recorded pre-adaptation SDL-renderer presentation on an AVD, not current Android graphics verification and not evidence for OpenGL or WebGPU.
67.3 Lifecycle coverage is explicit but partial
The platform event contract carries will-enter-background, did-enter-foreground, low-memory and terminating events. Game suspends between the first two, dispatches low-memory to content cleanup and exits on termination. That is source-level lifecycle policy plus portable tests; it does not prove every mobile host delivers the events in the expected order or that a game’s GPU resources survive a background transition.
This is best expressed as a state-machine gap. Launch-to-visible behavior covers only the straight path. A useful mobile test must drive background, foreground, low-memory, and process recreation transitions, then verify saved state and graphics-resource reconstruction. An emulator can provide most of this evidence before physical-device testing adds drivers, thermal behavior, and vendor-specific lifecycle timing.
67.4 Assets expose a deeper integration gap
TitleContainer first uses std::filesystem; on Android it can fall back to SDL_LoadFile and wrap the bytes in a memory stream. Historical execution found that fallback could segfault without a real Activity/JNI context. More importantly, no production consumer calls TitleContainer: ContentManager builds Content/<asset> directly from the current working directory.
The fallback is therefore both fragile and unreachable from the main content path. The same CWD-relative content root is a latent issue for Android packages and macOS application bundles. A complete mobile asset contract should choose one route, base it on the package location or platform asset API, and test it through ContentManager rather than through an isolated helper.
67.5 macOS: native routes are declared
The Metal renderer keeps Objective-C++ behind a pure-C++ PIMPL boundary. Objective-C++, Metal, QuartzCore, and Foundation are enabled only when the selected identity is METAL. This is a sound portability seam, but source isolation is not native validation.
The Metal CTest prefix is much broader than three registrations. Portable Metal policy gtests are discovered individually and also collected in Metal_PortableHelpers; a recorded Linux HEADLESS run consequently reports 207/207 for ctest -R ’M̂etal’ while executing no Objective-C++, Apple framework, shader compiler, or GPU command. Only the separate Metal_Smoke and Metal_Capabilities registrations require a METAL configuration. The renderer identity of the build must accompany every quoted result.
There is genuine historical Apple evidence: a macOS 14/Xcode 15.4 workflow on the original feature tree ran 143 tests, passing 136 and failing seven. The later adaptation changed interfaces, ownership, transfer policy, registration, and supported behavior. That run is valuable history, not evidence for alpha.1.
At the tag, the dedicated automatic Metal workflow configures METAL on macOS 14, builds the native target, enables Metal shader validation/debug-layer variables and runs the Metal CTest prefix plus a renderer-definition control. The separate Apple workflow builds an SDL_RENDERER macOS suite and a self-contained .app, checks Mach-O dependency paths and launches it for a success marker. These definitions are materially stronger than the old stale-filter route, but this edition did not retrieve workflow-run artifacts and therefore records declared gates rather than a new pixel/Retina/frame-pacing observation.
67.6 iOS: final-link and simulator route; tvOS remains a non-claim
The tag now contains an iOS toolchain, application metadata and a minimal UIKit/SDL-main/Game smoke application. Its workflow matrix final-links SDL_RENDERER application bundles for arm64 device and arm64 simulator, inspects Mach-O platform load commands, verifies the SDL entry symbols and absence of build-machine dynamic dependencies, then ad-hoc signs, installs and launches the simulator bundle until a one-frame success marker appears. Tests/examples and Net are deliberately off; no physical device is run. This is an executable route in source, not evidence that the tag’s matrix ran successfully unless its Actions record is also retained.
tvOS has no corresponding toolchain/application/workflow route and remains an explicit non-claim. Native METAL is also not an iOS claim: the available smoke uses SDL_RENDERER over the SDL3 platform implementation.
The next durable milestone is intentionally narrower than “mobile support”: archive a fresh Android graphics build and emulator frame with logs; retain alpha.1 Metal and Apple workflow artifacts; add a physical iOS-device result; then design, rather than infer, a tvOS host. Each step should state which rung it proves. That makes platform progress cumulative without allowing yesterday’s partial result to masquerade as today’s complete one.