Skip to content

Native engineering

A Sensor Protocol Core That Does Not Own the Socket

The native host owns connectivity. A shared C++ core turns bytes into validated events. That boundary made framing, timeouts and uncertain sensor state easier to reason about.

3 min read

Senior AI Engineer · M.S. Computer Science, Tsinghua University · See the products I built

In this article · 6 sections
GolfJoy native app landscape shot display with four large metrics from its Google Play listing
In the product. Official Google Play interface image with sample sensor values. The native host turns protocol events into readable practice data. This is not a field-test capture.Open full size ↗

The engineering decisions

  • Keep sockets, threads and timers outside the shared parser.
  • Validate frame boundaries and coordinate data before producing app events.
  • Clear stale state and distinguish synthetic parser tests from real-device compatibility.

Separate the transport from the protocol

A mobile sensor integration has at least two different problems: maintaining a connection on a phone, and interpreting the bytes arriving through it. I kept the shared protocol implementation in a pure C++17 library. It owns no socket, thread or timer. The host supplies byte sequences and the current time, and receives events.

Android integrates that boundary through NDK/JNI, while the iOS host uses Objective-C++ alongside Swift. The shared core does not need to pretend that those platforms have identical connection lifecycles. It only needs a precise contract for parsing and event assembly. Injecting the clock also makes timeout behavior reproducible in a desktop test.

Android and iOS hosts supply bytes and an injected clock to a bounded C++ parser and shot assembler, then receive validated events
How it works. Shared protocol logic sits between platform-owned transport and platform-owned UI. A decoded event still needs the appropriate trust and unit handling.Open full size ↗

A read is not a packet

The parser handles split frames, multiple frames in one incoming buffer, garbage before a frame and stream termination. It checks the declared size and checksum within a bounded payload model. A transport delivering some bytes does not establish that those bytes form a complete, valid message.

The shot assembler then associates related reports and uses injected-time deadlines to decide when an assembly cannot continue. Unknown encrypted report types are surfaced as non-fatal events instead of taking down the connection. That keeps protocol evolution separate from corruption: an unfamiliar message and a malformed known message are different situations.

Unknown is a real UI state

Ball-placement reports carry readiness, detected centers and a region polygon. The decoder validates counts, array lengths and finite coordinates before exposing the data. A malformed report invalidates the affected field. An unknown report cannot establish that the device is ready or that the ball is centered.

A no-ball report clears the last visible dot. Connection changes and a new shot clear positions too. Keeping an attractive but stale marker on screen would suggest knowledge the device no longer provides. Likewise, a connection without a region map needs an explicit waiting state rather than a guessed boundary.

GolfJoy native app displaying a sensor-relative ball position and measured boundary
In the product. Official Google Play sample interface. The marker and boundary illustrate sensor-relative placement guidance, not verified physical left/right orientation.Open full size ↗
State transitions the interface must respect
ObservationRequired interpretation
Valid ball + region dataShow the measured sensor-relative position.
No ball detectedRemove the previous position.
Connection change or new shotDiscard stale placement state.
Missing region or unknown readinessShow uncertainty, not an invented ready state.
Malformed coordinatesInvalidate the affected field.

Use the measured plane, not an assumed world axis

The placement view projects ball centers and the actual region polygon onto the polygon’s own plane. It does not assume that XY or XZ must be the ground. The available protocol also does not define which world axis points toward the player, so the display is sensor-relative.

That constraint changes the language of the UI. An arrow may point toward the measured boundary, but it must not promise physical left or right, or a real-world distance that the source does not establish. A ball outside camera visibility has no knowable current location. The correct response is to remove the dot, not extrapolate an appealing trajectory.

Decoding is neither authentication nor unit conversion

The core hands raw numeric values to the platform boundary, where unit conversion happens once. That avoids a yard-to-meter conversion being applied in both the native layer and the UI. The code also distinguishes absent values from a numeric zero when the underlying measurement is unavailable.

The protocol’s encryption compatibility material is not authentication. Successfully decoding a packet is not a reason to trust it for account state or a leaderboard. The integration needs those trust boundaries even when the checksum and parser have accepted the frame.

Synthetic tests establish parser behavior

The desktop suite runs with address and undefined-behavior sanitizers when available. Its synthetic vectors can establish behavior for split frames, malformed lengths and deterministic timeouts. They cannot by themselves establish compatibility with every device or firmware.

Real-device acceptance needs captured inputs compared with the vendor tool’s displayed values. Region replay after a late connection, outside-region tracking and physical orientation require device-specific checks. The project also has separate September Android shot-decoding evidence. I keep that observation distinct from an assertion that every placement behavior or the iOS host has passed the same physical tests.

Project context and evidence

Written from my 2026 GolfJoy native protocol-core design and validation records. Public links below support the product and attribution context. Internal project records are summarized, with customer records, credentials, and employer source code omitted.

All articles ↗