Package-level declarations
Client, connections, devices, elements, waits, the selector DSL, errors and the value types the server returns.
Types
One app on a device: its elements, its lifecycle and the waits on it. Obtain with Device.app, which performs no I/O.
An AUT lifecycle postcondition did not hold (process still alive, window never appeared...).
A process of an app as the host sees it: PID plus the /proc start token that tells reused PIDs apart.
Something the device produced that a test may keep: a Screenshot, a Hierarchy, a DeviceInfo or a DriverLog. Every artifact is plain data with its serialized bytes and mediaType, so it can go to a file (save), a report, an HTTP upload or an assertion without touching the device again.
The diagnostics of one device at one moment (Device.capture): a Screenshot, the Hierarchy, the DeviceInfo and the DriverLog. A part is null when it could not be produced; failures says why, by part name.
A driver command's outcome was error. code is the protocol error code; detail refines it (e.g. NOT_FOUND/END_REACHED); TRANSPORT_LOST/INDETERMINATE carry the transmission state in detail. Never retry a mutation on INDETERMINATE. requestId and generation identify the command in the driver and daemon logs.
Finds a running server; never starts one (see TapDaemonProcess).
A server address and the bearer token its RPCs carry (null: none).
One attached device backed by one live driver session, proxied by the host server. All calls are suspend. The client does not serialize calls on one device: issue them sequentially (the normal test style), and use coroutineScope + async for concurrency across devices (sibling failure cancels the other's in-flight RPC). Nothing here caches UI state: App.element and Screen.element return a lazy selector that every action resolves again, and mutations fail with AMBIGUOUS/NOT_FOUND before any input when the selector does not match exactly one node.
Another session — in this process or any other — holds the device, and DeviceOptions.waitForDevice was zero or ran out. The device is fine; try later or pick another serial.
One device ADB lists (TapClient.devices).
Facts about the device at the moment of Device.info: identity, display geometry and rotation, screen, keyguard and keyboard state, the package owning the focused window and the device conditions (Device.setAnimations and the rest). Serialized as JSON.
Device-attachment options. The driver is always the daemon's (bundled, or the APKs given to tap serve --driver-apk X --driver-test-apk Y); skipDriverInstall only skips reinstalling it.
The device's session journal keeps it out of service (a mutation whose outcome could not be proven, a corrupt journal, an uncertain ADB cleanup). It stays unusable until an explicit reset; retrying or waiting does not help.
A device's state in TapClient.devices. Only FREE and LEASED devices can be attached.
Gesture direction: where the finger moves for Element.swipe, the content edge moved towards for Element.scroll and Element.fling (DOWN reveals content below).
Clockwise display rotation relative to the device's natural orientation (portrait on phones, often landscape on tablets): Surface.ROTATION_0 / _90 / _180 / _270.
The state of one node at one instant (Element.snapshot). Text properties are null when the node has none. text is the raw accessibility text: on API 26+ an empty field reports its hint there and showingHint is true.
Wait builder returned by App.await / Screen.await / Element.await. visible, one and gone poll on the device in a single RPC; property waits poll snapshots from the host with delay-based polling, so test-root cancellation and sibling failure cancel them promptly.
Marks API that is not covered by the compatibility promise: it may change or go away in any release, including a patch. Today that is app synchronization (App.awaitIdle and the tap-sync-sdk contract behind it), which only works with apps the driver can see (see the app-lifecycle guide). Opt in with @OptIn(ExperimentalTapApi::class).
The server's structured reason for a failed call (ServerException.reason). UNSPECIFIED means the failure did not come from the daemon (a transport failure, a proxy, a cancelled call) or carried a reason this client version does not know.
The activity on top of the screen (Device.foregroundActivity): its app's packageName and its fully qualified className.
The accessibility hierarchy as XML (Device.dumpHierarchy). Diagnostic only: selectors never use it, so keep it out of assertions.
The encoding of a Screenshot's bytes.
The Precise / Approximate choice of the location permission dialog (Android 12+).
An active notification (Device.notifications, Device.awaitNotification): the packageName that posted it, its title and text (the android.title / android.text extras, null when it has none), the titles of its action buttons (actions, what Device.openNotification's action names), whether a swipe dismisses it (clearable: false for an ongoing one, such as a foreground service's), and when it was posted.
Display geometry: taller than wide, or wider than tall (Device.setOrientation, DeviceInfo.orientation).
A button of the runtime-permission dialog, by what it grants (Device.awaitPermissionPrompt, Device.choosePermission). Which ones a dialog offers depends on the permission, the Android version and the device's permission controller.
The runtime-permission dialog on screen: the packageName of its window (device.app(packageName) reaches its other elements), the choices it offers, in PermissionChoice order, and the location accuracies it lets the user pick (empty unless it asks for precise location).
A screen capture (Device.screenshot). bytes are the encoded image in format; width and height are its size in pixels.
The daemon's default timeouts (ServerInfo.defaults).
The host server rejected or failed a call (unknown connection or device, bad argument, driver start failure, ...). status is the gRPC status code name; reason is the server's structured failure reason, FailureReason.UNSPECIFIED when the failure did not come from the daemon.
What the server reports about itself (TapClient.info).
What App.awaitScreenStable watches for changes.
A standard accessibility action that takes no arguments, as a screen reader performs it (Element.performAction). Click and long click are not here: Element.tap and Element.longTap touch the screen.
A channel to a Tap host server. Discovery order: address; the tap.server system property / TAP_SERVER (host:port); a live daemon.json in the state dir (TAP_STATE_DIR, default ~/.tap). The client never starts the daemon: run tap start (or TapDaemonProcess.start) first. A started daemon stays up like the ADB server until tap stop.
This process's identity at the server: every Device it attaches belongs to it and is detached with it — explicitly by close, or by the server when the process goes away. One per process is the norm; see TapClient.connect.
Ownership marker for suspending device calls. Installed by tapScope (scripts) and by the JUnit 5 tapTest bridge (which uses the same element with a junit:<method> owner); every suspending Device/App/Element/ElementWait call requires it. TapClient, TapConnection, DaemonDiscovery and TapDaemonProcess never require it: they set up the channel, the connection and the daemon process outside any test scope.
Explicit daemon lifecycle from a test process: tap start and tap stop through the tap executable (DaemonDiscovery.findBinary). The executable is the only thing that spawns a server; it picks the port, detaches the process and waits for Info to answer.
Base of everything the client raises deliberately.
A suspending framework call was made outside its owning scope: tapTest { ... } without an active @TapTest binding, a nested tapTest/tapScope, or a Device/App/Element call from a coroutine that is not a child of that scope (for example GlobalScope). Scripts must wrap device use in tapScope { ... }; JUnit tests must use tapTest { ... }.
A toast Device.awaitToast saw: its text and the packageName that showed it (on Android 11+ a text toast is drawn by SystemUI but still reported under the app that posted it).
Why a device-side wait timed out (WaitTimeoutException.reason).
A wait ran out of time. Carries what was observed so the failure is diagnosable without a rerun: reason and matchCount for the waits the device runs (visible, one, gone, App.awaitVisible, App.awaitScreenStable), lastObservation and polls for the ones the client polls.
Properties
Largest file Device.pushFile, Device.pullFile and Device.addMedia move (512 MiB).
Functions
Takes a screenshot, the hierarchy, the device info and the driver log at once, each within timeout, and returns them as one Capture. Device failures never escape: a part that fails or runs out of time is null in the result, with its cause in Capture.failures. Only the caller's own cancellation propagates. Use it wherever a test wants evidence (after a step, in a catch); the JUnit extension and the pytest plugin call it for every failed test.
Requires an owning TapContext: tapScope { ... } in scripts, tapTest { ... } in JUnit. A coroutine that does not inherit the scope (for example GlobalScope.async) fails here instead of running uncancelled beside the test.
Resource id name in any package (<package>:id/name), or a Compose testTag name under testTagsAsResourceId. Under App.element only that app's nodes match; under Screen.element any visible package's. Use resId to name the id's package explicitly.
Any scrollable node; combine with the and* builders.
Runs block with a TapContext owner, for scripts and other non-JUnit callers. JUnit tests must use tapTest { ... } instead (same marker, plus the extension binding). Nesting (tapScope inside tapScope/tapTest) fails immediately.
Text containing value.
Text fully matching the RE2 regular expression re2 (linear time; no backreferences or lookaround).
Text starting with value.