current

The current merged ambient context, over every frame on the stack. Lock-free; safe from any thread.

This is the ambient answer — what is on display, as far as the stack can tell without knowing where an event came from. A pipeline that does know should ask current with the event's origin instead, which is what stops a declaration on one surface from attributing an event on another.

setActive takes a frame out of this answer together with everything nested under it: a surface off display takes what is inside it off display too, so a screen declared in a composition inside a demoted pager page or a paused Activity — a TrackedScreen — is absent here for as long as its surface is, and so is a frame pushed under that surface while it is demoted. The origin-taking overload does the same, except within the origin's own surface — see recompute — and it is what makes this answer track the display rather than the most recent composition (#228). A host app enriching its own events should still prefer the origin-taking overload where it has one: this read cannot tell a sibling surface's declaration from the event's own.


The ambient context as seen from origin — the innermost frame the calling pipeline could attribute an event to. Main thread only, unlike the no-argument current: this reads the frame list rather than a published snapshot, because the answer depends on the origin and is computed per event.

Three sets of frames take part, and only those:

  • the lineage of origin — itself and every frame it is nested in, following the parent links declared at push / update — so a screen named by the surface hosting the origin, or a mask raised by it, applies;

  • the subtree beneath origin, stopping at (and excluding) any frame pushed as a boundary together with everything under it. What the origin's own pipeline could not localize further — the TrackedScreens inside a composition — still applies, and what it could — a surface nested inside the origin, which the pipeline has established the event is not in — does not; and

  • every frame that is under no boundary at all: a root the app pushed by hand, a screen a native pipeline that claims no views pushes (iOS), a Compose declaration outside any claimed surface. Nothing localizes those, so they apply to every event, exactly as they do ambiently — as members: a hand-pushed root's scope is still subject to resolveScope's ambiguity rule unless it was pushed with pushGlobal, which is what makes it app-wide. A boundary-free stack therefore resolves with the same members with or without an origin — except what is nested under an inactive frame of another surface, see below — (and in the same order, unless a frame was reparented under one pushed after it — see the ranking below).

Everything else is out, and that is the point: a sibling surface's declaration, or its mask, never reaches an event that did not happen in it, whatever the two frames' insertion order. The survivors resolve in insertion order — with one correction: a frame ranks no later than the content nested in it. A surface adopted late pushes its frame after the composition it hosts (an Activity that predates the native capture's install is picked up at its next resume), and insertion order alone would let that late mask blank the TrackedScreen inside it; ranking the container where its earliest nested survivor ranks keeps the content winning, as it did before the surface was adopted, while a root the app pushed by hand still ranks where it was pushed. maskScreen and resolveScope then apply exactly as in current: a mask raised on the lineage clears the screen beneath it, content pushed after it inside the same surface still wins, and a subtree that branches is ambiguous just as it is ambiently. The active bit reaches down the lineage here too — a frame nested under an inactive frame is out — with one exemption: an inactive frame of the origin's own surface (on its lineage, or beneath it with no boundary between) drops its contribution and nothing else. The origin is the pipeline asserting the event happened in that surface, so what is declared inside it stays (a tap on a pager page the host reports as demoted still attributes to the page's own TrackedScreen, whether it lands on the Compose content or on a native button beside it — the composition's provider frame mirrors the same demotion), while a sibling surface's demotion silences that sibling's declarations for this event exactly as it does ambiently — on a shared, boundary-free stack (iOS; Android without the native capture) that is the only thing keeping a demoted neighbour page's screen off a tap on the current one. The ambient read is the lineage-less case of the same rule; see recompute.

A frame in the lineage that is no longer on the stack (its surface was torn down but a stale handle survived) contributes nothing, and the walk continues past it; an origin that is not on the stack at all therefore resolves to whatever of its lineage still is. Fail-closed both ways: a stale origin yields less context, never someone else's.