NearbyLoom

class NearbyLoom(api: NearbyApi, serviceId: String = DEFAULT_SERVICE_ID, policy: DeliveryPolicy = DeliveryPolicy.Reliable, handshakeTimeout: Duration = DEFAULT_HANDSHAKE_TIMEOUT, maxChunkPayload: Int = ChunkCodec.MAX_CHUNK_PAYLOAD) : Loom

Loom implementation backed by Google Nearby Connections.

All logic is GMS-free and lives in commonMain. The real binding that imports play-services-nearby is supplied by androidMain at construction time.

Concurrency and virtual-time correctness

Background coroutines are launched into a scope derived from the caller's coroutine context at open()/join() time: CoroutineScope(currentCoroutineContext() + SupervisorJob()). This means background work inherits the test dispatcher (and therefore runTest's virtual clock) when tests call these methods. The scope is stored and cancelled in NearbySeam.close so no coroutines leak between tests. There is no long-lived process-global scope.

Single-loom symmetric topology

One NearbyLoom handles both the advertiser role (open) and the discoverer role (join) through the same NearbyApi. This matches the us.tractat.kuilt.core.InMemoryLoom pattern and lets the conformance suite run a "one loom, one host, one joiner" scenario.

The roster belongs to the WEAVE, not to the loom (#1878)

Each weave mints its own peer-set MutableStateFlow, seeded with that weave's us.tractat.kuilt.core.freshPeerId and thereafter written only by that weave's own handshake and departures. A single loom-wide flow — what this loom used to keep — is never pruned of a finished weave's ids: NearbySeam.close must not write it (that write was the #1850 cross-peer edit) and disconnectLoop, the only eviction, is cancelled by the very tear that would need it to run. So a closed weave's id lingered forever and seeded the next weave, whose fresh seam reported a roster containing a peer it had never met and latched us.tractat.kuilt.core.SeamState.Woven with zero connections.

Both ends of one session still converge, because each learns the other from the handshake it already completed (ConnectedLink.remotePeerId) rather than by reading a flow the counterparty wrote. That was always the honest channel — the loom-wide flow was a single-process convenience of the fake harness, and on real hardware the two peers are on different devices with different looms and have never shared one.

No loom-level selfId — identity is minted per weave

Because one loom weaves both ends of a session (above), a loom-level identity would hand the host seam and the joiner seam the same PeerId — collapsing the roster to one entry, so Weaving would never reach Woven, and making every sendTo between them fail its own peer != selfId precondition. Each weave therefore mints its own us.tractat.kuilt.core.freshPeerId, exactly as us.tractat.kuilt.core.InMemoryLoom does. That is why the uniform-Loom-construction convention's selfId knob is deliberately absent here rather than defaulted (#1430); NearbyLoomKnobsTest pins the distinctness the omission rests on.

Parameters

api

NearbyApi to use (real GMS or fake for tests).

serviceId

Nearby Connections service ID. Must match on both devices.

policy

Delivery policy for every woven seam's inbound buffer.

handshakeTimeout

Ceiling on ONE connection's handshake, forwarded to ConnectStateMachine. Not a weave timeout — endpoint discovery sits outside it (see DEFAULT_HANDSHAKE_TIMEOUT).

maxChunkPayload

Per-chunk payload cap forwarded to ChunkCodec.

Constructors

Link copied to clipboard
constructor(api: NearbyApi, serviceId: String = DEFAULT_SERVICE_ID, policy: DeliveryPolicy = DeliveryPolicy.Reliable, handshakeTimeout: Duration = DEFAULT_HANDSHAKE_TIMEOUT, maxChunkPayload: Int = ChunkCodec.MAX_CHUNK_PAYLOAD)

Types

Link copied to clipboard
object Companion

Functions

Link copied to clipboard
Link copied to clipboard
open override fun capability(): TransportCapability
Link copied to clipboard
open suspend fun host(pattern: Pattern): Seam
Link copied to clipboard
open suspend fun join(tag: Tag): Seam
Link copied to clipboard
open suspend override fun weave(rendezvous: Rendezvous): Seam