The problem
Mobile automation at scale was bottlenecked on three things: setting up devices was manual, jobs couldn't reuse warm devices for similar workflows, and instrumentation logic (Frida hooks, network capture) had to be baked into worker images at build time. Iteration was painful and capacity was lumpy.
The platform replaces all three. Jobs go onto a queue. The scheduler finds a warm device when it can, falls back to a cloud Genymotion instance otherwise, and accepts Frida scripts as runtime input — no more redeploying workers to ship a new hook.
Architecture
- 1 ProducerInternal automation request lands as a job on the SQS queue.
- 2 Affinity checkScheduler asks Postgres: is a similar workflow already running on a device? If yes, route there for warm-start.
- 3 Pool selectIf no warm device, choose between the on-prem Pixel rack (priority workloads) or cloud Genymotion pool (elastic capacity).
- 4 Runtime executorFrida instrumentation engine, network recorder, automation runner (Appium / UIAutomator), and custom-script executor — all wired into one runtime.
- 5 CaptureExecution logs, captured network logs, and automation results land in storage + internal analytics.
Three Core Decisions
1. Hybrid pool — on-prem Pixels for priority, Genymotion in the cloud for elasticity
Pure cloud is elastic but slower to warm and more expensive per session for priority workloads. Pure on-prem is cheap but capped at the rack you bought. The hybrid pool gives priority workflows a reserved physical Pixel while letting bursty/elastic capacity ride on Genymotion images in AWS. The scheduler treats them as separate pools with different priority weights.
2. Affinity scheduling — route similar workflows to the same warm device
A cold Android session pays a meaningful setup cost — install / grant permissions / launch app / log in. If a similar automation is already running on a device, routing the new job to that same device skips the cold-start. The scheduler keeps a workflow signature in Postgres and matches incoming jobs against active sessions before allocating a new device.
3. Frida script injection at runtime
Hardcoding Frida hooks into worker images means every new instrumentation requirement is a worker rebuild + redeploy. Accepting Frida scripts as job input — one script per job, validated and sandboxed before injection — turned hours-long iteration into minutes. The cost is having to be careful about what scripts are allowed; that's a smaller surface to defend than redeploying the fleet.
State and config
- PostgreSQL (RDS) — job scheduling, device allocation, execution tracking, automation lifecycle
- MongoDB — device capabilities, application configs, automation profiles (config-driven execution)
- Internal dashboard — health checks and device availability surfaced to operators in real time
What it ran
- 10 concurrent active automation sessions sustained
- Infrastructure capable of managing 100+ connected devices across physical and virtual pools
- Frida instrumentation, network recording, and Appium / UIAutomator unified under one execution layer
- Captured execution logs, network traffic, and automation results piped to internal analytics
Stack
- Python + Go control plane on AWS ECS
- AWS SQS for job queue
- PostgreSQL (RDS) for orchestration state, MongoDB for configs / profiles
- Frida, Appium, UIAutomator for runtime instrumentation and automation
- Genymotion for cloud Android instances; on-prem Pixel device rack for priority workloads