android

package
v0.2.0-beta.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// AgentAppPackage is the app APK: drivers/android/agent's applicationId.
	AgentAppPackage = "dev.larchwave.flowbaton"
	// AgentTestPackage is the instrumentation APK that hosts the gRPC server.
	AgentTestPackage = "dev.larchwave.flowbaton.test"
)

The two installable halves of the on-device agent and the instrumentation entrypoint between them are defined by specs/02-device-drivers.md §2.2.

View Source
const Platform = device.Platform("android")

Platform is the device platform this driver reports.

Variables

This section is empty.

Functions

func DeclaredCapabilities

func DeclaredCapabilities() device.Capabilities

DeclaredCapabilities is the non-mutating Android capability document used by both the driver and selected-platform flow preflight.

Types

type Adb

type Adb struct {
	// contains filtered or unexported fields
}

Adb is the typed surface over adb for one device.

func NewAdb

func NewAdb(serial string, runner CommandRunner) *Adb

NewAdb binds a serial to a runner. An empty serial is legal only for the commands that are not device-scoped, such as ListDevices.

func (*Adb) APILevel

func (adb *Adb) APILevel(ctx context.Context) (int, error)

APILevel reads Android's immutable SDK property without starting the agent or mutating device state. Runtime-gated operations use it before upload.

func (*Adb) AirplaneModeEnabled

func (adb *Adb) AirplaneModeEnabled(ctx context.Context) (bool, error)

AirplaneModeEnabled reads the state from the connectivity service's own command, which reports exactly "enabled" or "disabled".

func (*Adb) AllowMockLocation

func (adb *Adb) AllowMockLocation(ctx context.Context, packageName string) error

AllowMockLocation lets a package feed the platform a mock location. The agent is an ordinary app, and LocationManager throws MOCK_LOCATION at one that has not been allowed the app-op — which is what setLocation hit on a real emulator.

func (*Adb) Bugreport

func (adb *Adb) Bugreport(ctx context.Context, outputPath string) error

Bugreport writes the device's diagnostic bundle to outputPath. adb chooses zip vs flat based on the device's own bugreport support; the path is passed through unchanged.

func (*Adb) ClearPackageData

func (adb *Adb) ClearPackageData(ctx context.Context, packageName string) error

ClearPackageData is what clearState means on Android: pm clear wipes the app's data and cache without uninstalling it.

func (*Adb) CurrentOrientation

func (adb *Adb) CurrentOrientation(ctx context.Context) (device.Orientation, error)

CurrentOrientation reads the display service's current Surface rotation.

func (*Adb) ForceStop

func (adb *Adb) ForceStop(ctx context.Context, packageName string) error

func (*Adb) ForwardAbstract

func (adb *Adb) ForwardAbstract(ctx context.Context, hostPort int, socket string) error

ForwardAbstract publishes an abstract unix socket on a host TCP port. The Chrome DevTools endpoint of a debuggable WebView lives on one of these (spec 02:44), so a TCP-to-TCP forward cannot reach it.

func (*Adb) ForwardAdd

func (adb *Adb) ForwardAdd(ctx context.Context, hostPort, devicePort int) error

ForwardAdd publishes the on-device agent port on a host port. The forward is the transport the whole gRPC half rides on.

func (*Adb) ForwardRemove

func (adb *Adb) ForwardRemove(ctx context.Context, hostPort int) error

func (*Adb) GrantPermission

func (adb *Adb) GrantPermission(ctx context.Context, packageName, permission string) error

func (*Adb) Install

func (adb *Adb) Install(ctx context.Context, apkPath string) error

Install replaces an existing install (-r): the agent APKs are reinstalled per session, and a plain install fails on the second one.

func (*Adb) Instrument

func (adb *Adb) Instrument(ctx context.Context, devicePort int) ([]byte, error)

Instrument starts the agent's gRPC server and blocks until the instrumentation exits — for a healthy agent that is the rest of the session, so callers run this on its own goroutine. The shell line is specs/02-device-drivers.md §2.2's, with FlowBaton's packages: -w keeps the process attached so its output reports a refusal, and -m is unconditional because the agent's minSdk is 26, the exact API level the spec gates -m on.

func (*Adb) KeyboardShown

func (adb *Adb) KeyboardShown(ctx context.Context) (bool, error)

KeyboardShown reads the input-method service state; mInputShown is the flag the IME flips while the soft keyboard is on screen.

func (*Adb) Keyevent

func (adb *Adb) Keyevent(ctx context.Context, code string) error

Keyevent presses one key. The symbolic KEYCODE_* names are used rather than raw numbers so the argv reads as the operation it performs.

func (*Adb) Kill

func (adb *Adb) Kill(ctx context.Context, packageName string) error

Kill sends the system-initiated process death: the public docs pin killApp on Android to exactly `adb shell am kill <package>`, which the tool's own help describes as killing the app's background processes.

func (adb *Adb) OpenLink(ctx context.Context, link string, forceChrome bool) error

OpenLink fires the VIEW intent. am start may report resolution failures in its output while exiting successfully, so success requires reading both.

func (*Adb) ProcessID

func (adb *Adb) ProcessID(ctx context.Context, appID string) (string, error)

ProcessID returns one running process id for an application. The read-only pidof query scopes logcat without changing application state.

func (*Adb) PutSetting

func (adb *Adb) PutSetting(ctx context.Context, namespace, key, value string) error

PutSetting writes one settings row; specs/02-device-drivers.md line 46 pins the proxy to `settings put global http_proxy` and orientation to `settings put system user_rotation`.

func (*Adb) RevokePermission

func (adb *Adb) RevokePermission(ctx context.Context, packageName, permission string) error

func (*Adb) RuntimePermissions

func (adb *Adb) RuntimePermissions(ctx context.Context, packageName string) ([]string, error)

RuntimePermissions lists the app's own changeable permissions, read off `dumpsys package`. This is what `all` expands to: only what the app requested can be granted, and pm refuses everything else.

func (*Adb) SetAirplaneMode

func (adb *Adb) SetAirplaneMode(ctx context.Context, enabled bool) error

func (*Adb) Swipe

func (adb *Adb) Swipe(ctx context.Context, startX, startY, endX, endY int, durationMillis int64) error

Swipe drags between two points over a duration; the same verb with zero travel is how a long press is expressed. specs/02-device-drivers.md line 43 pins swipes to `adb shell input swipe`, not the gRPC agent.

func (*Adb) Uninstall

func (adb *Adb) Uninstall(ctx context.Context, packageName string) error

type AgentAPKs

type AgentAPKs struct {
	App  string
	Test string
}

AgentAPKs names the two installable halves of the on-device agent: the app APK and the instrumentation APK hosting the gRPC server. Handing them to NewDriver makes Open own the whole spec 02 §2.2 lifecycle — reinstall, `am instrument`, and the 100ms reachability poll; a nil AgentAPKs keeps the operator-started mode, where the agent must already be running.

type CommandRunner

type CommandRunner interface {
	Run(ctx context.Context, name string, args ...string) ([]byte, error)
}

CommandRunner executes one external command and returns its combined output.

type Device

type Device struct {
	Serial string
	State  string
	Model  string
}

Device is one entry of the adb inventory.

func ListDevices

func ListDevices(ctx context.Context, runner CommandRunner) ([]Device, error)

ListDevices returns every device adb can currently use. Entries in any other state (offline, unauthorized) are dropped: they cannot run a flow, so offering them as targets would only defer the failure.

type Driver

type Driver struct {
	// contains filtered or unexported fields
}

Driver drives one device or emulator.

func NewDriver

func NewDriver(serial string, hostPort int, runner CommandRunner, apks *AgentAPKs) *Driver

NewDriver binds a device serial and a host port to an adb runner. A nil runner executes adb for real; a nil apks means the agent is already running and Open only forwards and probes.

func (*Driver) AddMedia

func (driver *Driver) AddMedia(ctx context.Context, request device.AddMediaRequest) error

AddMedia streams each file to the agent over the service's one client-streaming rpc. Every frame carries the name and extension alongside its payload chunk — the metadata is idempotent, which keeps the agent free of first-frame special cases. One rpc moves one file.

func (*Driver) BackPress

func (driver *Driver) BackPress(ctx context.Context) error

func (*Driver) Capabilities

func (driver *Driver) Capabilities() device.Capabilities

Capabilities declares what this driver refuses. Every false here has a matching ErrUnsupported at call time; preflight reads this so a flow is refused before it runs rather than halfway through.

func (*Driver) ClearAppState

func (driver *Driver) ClearAppState(ctx context.Context, request device.AppRequest) error

ClearAppState is pm clear: data and cache wiped, app still installed — unlike iOS, Android has the per-app verb, so no uninstall is needed.

func (*Driver) ClearKeychain

func (driver *Driver) ClearKeychain(context.Context) error

ClearKeychain is a documented no-op: the public docs state the command "only applies to iOS and has no effect on Android or Web". There is no keychain here to clear, so the success is vacuous rather than a lie.

func (*Driver) Close

func (driver *Driver) Close(ctx context.Context) error

Close undoes what Open did: the connection, the instrumentation and the APKs when Open owned them, and the forward. An operator-started agent is left exactly as it was, for the same reason the iOS driver leaves the simulator running — the state after a failed run is something an operator inspects.

func (*Driver) CollectCrashArtifacts

func (driver *Driver) CollectCrashArtifacts(
	ctx context.Context,
	request device.ArtifactRequest,
) ([]device.Artifact, error)

func (*Driver) ContentDescriptor

func (driver *Driver) ContentDescriptor(
	ctx context.Context,
	request device.ContentDescriptorRequest,
) (device.TreeNode, error)

ContentDescriptor asks the agent for the spec 04 §2 XML and converts it. The app filter has nothing to carry -- the agent dumps every window -- but the keyboard exclusion does: only the agent can see which window is the input method, so the ask travels with the request.

func (*Driver) CurrentOrientation

func (driver *Driver) CurrentOrientation(ctx context.Context) (device.Orientation, error)

func (*Driver) DeviceInfo

func (driver *Driver) DeviceInfo(ctx context.Context) (device.DeviceInfo, error)

func (*Driver) EraseText

func (driver *Driver) EraseText(ctx context.Context, request device.EraseTextRequest) error

func (*Driver) HideKeyboard

func (driver *Driver) HideKeyboard(ctx context.Context) error

HideKeyboard is the back key: the public docs record that Android hides the keyboard with "a back button event, ... identical to the back command".

func (*Driver) InputText

func (driver *Driver) InputText(ctx context.Context, request device.InputTextRequest) error

InputText goes to the agent because shell `input text` mangles unicode and spaces. The agent commits text in-process to the focused field. AppIDs has nothing to carry: input always targets whatever holds focus.

func (*Driver) IsAirplaneModeEnabled

func (driver *Driver) IsAirplaneModeEnabled(ctx context.Context) (bool, error)

func (*Driver) IsKeyboardVisible

func (driver *Driver) IsKeyboardVisible(ctx context.Context, _ device.KeyboardRequest) (bool, error)

func (*Driver) IsShutdown

func (driver *Driver) IsShutdown(ctx context.Context) (bool, error)

IsShutdown asks the agent, not adb: a device whose agent has died is unusable for a flow even though adb still lists it.

func (*Driver) KillApp

func (driver *Driver) KillApp(ctx context.Context, request device.AppRequest) error

KillApp is the system-initiated process death, distinct from StopApp on this platform: the public docs pin it to `adb shell am kill`.

func (*Driver) LaunchApp

func (driver *Driver) LaunchApp(ctx context.Context, request device.LaunchAppRequest) error

LaunchApp goes through the agent, not adb: only the gRPC route carries the typed extras, and `am start` would need the launch activity resolved by hand. An unknown argument type is refused before anything crosses the wire.

func (*Driver) LongPress

func (driver *Driver) LongPress(ctx context.Context, request device.LongPressRequest) error

LongPress is a zero-travel `input swipe`: the frozen proto has no long press rpc, and holding one point for the duration is exactly what the swipe verb does when start and end coincide.

func (*Driver) Name

func (driver *Driver) Name() string

Name identifies the agent this driver talks to, port included, like the iOS driver's ios:UDID:PORT.

func (*Driver) Open

func (driver *Driver) Open(ctx context.Context) error

Open publishes the agent port on the host and confirms the agent answers. With APKs it owns the whole spec 02 §2.2 lifecycle: reinstall both halves, start the instrumentation, and poll for reachability. Without them the instrumentation must already be RUNNING for any gRPC call to answer; discovering an absent agent here, with the address in the error, is the difference between a setup failure and a flow failure.

func (driver *Driver) OpenLink(ctx context.Context, request device.OpenLinkRequest) error

OpenLink fires the VIEW intent. The only browser choice Android honors is the boolean chrome force (openBrowser); any other named browser is refused rather than silently opened in the default.

func (*Driver) PreflightRuntime

func (driver *Driver) PreflightRuntime(ctx context.Context, requirements device.RuntimeRequirements) error

PreflightRuntime resolves Android-version-dependent support without opening or installing the agent. adb getprop is read-only, so an API 26--28 device that cannot implement scoped MediaStore insertion is refused before Open can uninstall or install either driver APK.

func (*Driver) PressKey

func (driver *Driver) PressKey(ctx context.Context, request device.PressKeyRequest) error

func (*Driver) QueryOnDeviceElements

func (driver *Driver) QueryOnDeviceElements(
	context.Context,
	device.QueryRequest,
) ([]device.TreeNode, error)

QueryOnDeviceElements has no rpc in the frozen proto: the agent returns a whole hierarchy, it does not evaluate queries against it.

func (*Driver) ResetProxy

func (driver *Driver) ResetProxy(ctx context.Context) error

ResetProxy writes the sentinel ":0", the platform's own convention for "no proxy" — deleting the row does not reliably clear an applied proxy.

func (*Driver) ScrollVertical

func (driver *Driver) ScrollVertical(ctx context.Context, request device.ScrollVerticalRequest) error

ScrollVertical is a swipe across the middle of the screen, sized from the device's own geometry — the same rule as the iOS driver, on the pixel grid.

func (*Driver) SetAirplaneMode

func (driver *Driver) SetAirplaneMode(ctx context.Context, request device.AirplaneModeRequest) error

func (*Driver) SetAndroidChromeDevToolsEnabled

func (driver *Driver) SetAndroidChromeDevToolsEnabled(
	ctx context.Context, request device.ChromeDevToolsRequest,
) error

SetAndroidChromeDevToolsEnabled turns the WebView merge on or off.

Enabling publishes the device's abstract DevTools socket on a host port and attaches to it; disabling closes the connection and removes the forward. A forward left behind pins its host port for the life of the adb server, so the next shard that reserves that number gets a socket pointing at a dead page.

func (*Driver) SetLocation

func (driver *Driver) SetLocation(ctx context.Context, location device.Location) error

SetLocation enables the agent's mock providers first, then pushes the coordinate: setLocation lands on the mock Fused/LocationManager providers, which do not exist until the agent registers them.

func (*Driver) SetOrientation

func (driver *Driver) SetOrientation(ctx context.Context, orientation device.Orientation) error

func (*Driver) SetPermissions

func (driver *Driver) SetPermissions(ctx context.Context, request device.PermissionsRequest) error

SetPermissions applies one pm call per permission, in a stable order: pm takes a single permission per invocation, and map order must not decide what gets applied first.

`all` is applied before the named keys so an explicit key can override it — the docs' own example is `all: deny` plus `camera: allow`. It expands to the app's OWN runtime permissions (read off dumpsys), because pm refuses to grant anything the app never requested.

ponytail: a named permission the app did not request still fails with pm's own SecurityException naming it; intersecting named keys with the manifest too is the upgrade path if that ever bites.

func (*Driver) SetProxy

func (driver *Driver) SetProxy(ctx context.Context, proxy device.Proxy) error

SetProxy is the global http_proxy setting (specs/02 line 46).

func (*Driver) StartDeviceLogCapture

func (driver *Driver) StartDeviceLogCapture(
	ctx context.Context,
	request device.DeviceLogRequest,
) (device.CaptureID, error)

func (*Driver) StartScreenRecording

func (driver *Driver) StartScreenRecording(
	ctx context.Context,
	request device.ScreenRecordingRequest,
) (device.CaptureID, error)

StartScreenRecording spawns `adb shell screenrecord <devicePath>` as a long-lived child (specs/02-device-drivers.md line 9) and returns a CaptureID that StopScreenRecording later stops. screenrecord can only write to device storage, so it records to /sdcard/<name> and stop pulls the finished file to the host sink.

func (*Driver) StopApp

func (driver *Driver) StopApp(ctx context.Context, request device.AppRequest) error

func (*Driver) StopDeviceLogCapture

func (driver *Driver) StopDeviceLogCapture(
	ctx context.Context,
	id device.CaptureID,
) ([]device.Artifact, error)

func (*Driver) StopScreenRecording

func (driver *Driver) StopScreenRecording(
	ctx context.Context,
	id device.CaptureID,
) ([]device.Artifact, error)

StopScreenRecording ends a recording StartScreenRecording began, pulls the artifact to its sink, and returns it. Like the iOS counterpart it is NOT on the frozen Driver surface — v0 declares only the start half — so the recording controller completes the lifecycle by calling this directly.

func (*Driver) Swipe

func (driver *Driver) Swipe(ctx context.Context, request device.SwipeRequest) error

Swipe covers the three shapes specs/02-device-drivers.md §1 gives the driver, all through `input swipe` (specs/02 line 43): explicit points, a bare direction, and an element point plus a direction.

func (*Driver) TakeScreenshot

func (driver *Driver) TakeScreenshot(
	ctx context.Context,
	_ device.ScreenshotRequest,
) ([]byte, error)

TakeScreenshot returns the agent's PNG. The compressed flag is iOS's JPEG-vs-PNG switch; the Android agent produces PNG either way (spec 04 §1), so there is nothing here for the flag to change.

func (*Driver) Tap

func (driver *Driver) Tap(ctx context.Context, request device.TapRequest) error

func (*Driver) WaitForAppToSettle

func (driver *Driver) WaitForAppToSettle(
	ctx context.Context,
	request device.SettleRequest,
) (*device.ViewHierarchy, error)

WaitForAppToSettle returns nil while the window is still updating. Callers must not interpret nil as settled confirmation.

func (*Driver) WaitUntilScreenIsStatic

func (driver *Driver) WaitUntilScreenIsStatic(
	ctx context.Context,
	_ device.ScreenStaticRequest,
) (bool, error)

WaitUntilScreenIsStatic is the inverse of the agent's isWindowUpdating. The timeout is the caller's to enforce: the engine owns every wait budget.

type ExecRunner

type ExecRunner struct{}

ExecRunner runs commands for real.

func (ExecRunner) Run

func (ExecRunner) Run(ctx context.Context, name string, args ...string) ([]byte, error)

Directories

Path Synopsis
Package grpcwire is a minimal gRPC-over-h2c client for the Android agent.
Package grpcwire is a minimal gRPC-over-h2c client for the Android agent.
Package pbwire is a hand-rolled proto3 codec for exactly the messages of proto/flowbaton_android.proto.
Package pbwire is a hand-rolled proto3 codec for exactly the messages of proto/flowbaton_android.proto.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL