Build
OpenPhone uses Android's normal repo workflow. This repository stays small
and contains only OpenPhone-owned code, manifests, scripts, and patches.
Prerequisites
- Linux Android build host dependencies installed for full device images.
- macOS can sync, patch, extract blobs, and build host/module validation targets, but this Android branch does not emit full phone images on Darwin.
repoavailable inPATH.git-lfsinstalled and initialized.- GNU coreutils on macOS.
- Java version required by the selected Android/Lineage branch.
- Large case-sensitive filesystem required.
- At least several hundred GB of free disk space.
Install repo into ~/.local/bin:
./scripts/install-repo.shInstall Git LFS on macOS:
brew install git-lfs
git lfs installInstall GNU coreutils on macOS:
brew install coreutilsOn macOS, create a case-sensitive APFS sparse image before syncing:
./scripts/create-macos-build-volume.sh
export OPENPHONE_ANDROID_DIR="$PWD/.worktree/OpenPhoneAndroid/android"For a flashable Pixel 9a build, use a Linux x86_64 host or VM with enough disk and RAM. Docker Desktop on Apple Silicon can run Linux/amd64 containers, but a full Android build through emulation is usually too slow and too memory-limited for practical ROM work.
Environment
Useful variables:
OPENPHONE_ANDROID_DIR=/path/to/android/tree
LINEAGE_BRANCH=lineage-23.2
LINEAGE_MANIFEST_URL=https://github.com/LineageOS/android.git
OPENPHONE_LUNCH_TARGET=openphone_arm64-userdebugDefaults:
OPENPHONE_ANDROID_DIR=.worktree/android
LINEAGE_BRANCH=lineage-23.2
LINEAGE_MANIFEST_URL=https://github.com/LineageOS/android.git
OPENPHONE_RELEASE=bp4aSync
./scripts/sync.shThis initializes or updates the Android checkout and installs the OpenPhone local manifest.
Apply OpenPhone Overlay and Patches
./scripts/apply-patches.shThis copies overlay/ into the Android tree and applies any patch files under
patches/.
Build
Generic OpenPhone ARM64 build:
./scripts/build.sh openphone_arm64The Pixel 9a target uses the bp4a release suffix:
OPENPHONE_RELEASE=bp4a ./scripts/build.sh openphone_teguDefault build goal:
OPENPHONE_BUILD_GOAL=droid
OPENPHONE_SKIP_SOONG_TESTS=trueBuild only the assistant module:
OPENPHONE_BUILD_GOAL=OpenPhoneAssistant ./scripts/build.sh openphone_arm64The assistant module target is OpenPhoneAssistant.
Build only the framework service validation target:
OPENPHONE_BUILD_GOAL=MODULES-IN-frameworks-base-services-core ./scripts/build.sh openphone_arm64This validates the hidden OpenPhoneAgentManager framework API,
OpenPhoneAgentManagerService, and SystemServer startup wiring without
building the full product graph.
Emulator Build
OpenPhone provides LineageOS SDK phone products for the first runnable OS test path:
openphone_sdk_phone_x86_64-bp4a-eng
openphone_sdk_phone_arm64-bp4a-engChoose the image architecture for the workstation that will run the emulator
UI: arm64 for Apple Silicon and x86_64 for Intel/x86_64 workstations. A
Linux Android build host, including EC2, can be used to produce the portable
image zip. The eng variant is the default because ADB is enabled without
first completing emulator-side developer settings.
Build the ARM64 emulator image:
./scripts/build-emulator.sh --arch arm64Build the x86_64 image instead when the local emulator workstation is x86_64:
./scripts/build-emulator.sh --arch x86_64By default this builds droid emu_img_zip. To only build the runnable Android
image from the same product:
OPENPHONE_BUILD_GOAL=droid ./scripts/build-emulator.sh --arch arm64After the image is built, run it from the same Android tree:
./scripts/run-emulator.sh --arch arm64This requires the Android SDK Emulator binary to be installed and available as
emulator in PATH. The portable artifact is
out/target/product/<device>/sdk-repo-linux-system-images.zip, which can be
copied to a workstation and installed into Android Studio/AVD.
The full local install, manual AVD fallback for custom android-36.1 images,
visible UI launch, scrcpy mirror, and post-boot verification commands are in
EMULATOR.md.
For a headless remote host, pass emulator options after --:
./scripts/run-emulator.sh --arch x86_64 -- -no-window -gpu swiftshader_indirectOn LineageOS 21 and newer, emu_img_zip writes
sdk-repo-linux-system-images.zip under out/target/product/<device>/ for
use with Android Studio/AVD system image installs.
EC2 Build Host
For remote builds, launch an Ubuntu x86_64 EC2 host with at least 64 GB RAM and roughly 700 GB of fast gp3 storage, then bootstrap Android build dependencies:
sudo ./scripts/bootstrap-android-build-host.shCopy the OpenPhone repo to the host and build the x86_64 emulator target:
OPENPHONE_SKIP_JAVA_CHECK=1 ./scripts/check.sh
./scripts/sync.sh -j16
./scripts/apply-patches.sh
./scripts/build-emulator.sh --arch x86_64The pre-sync scaffold check skips the standalone Java check because the full
Android build provides the authoritative compiler/toolchain validation.
The EC2 host is primarily a build host; it may not have the Android SDK
Emulator binary or /dev/kvm. Copy
.worktree/android/out/target/product/emu64x/sdk-repo-linux-system-images.zip
back to a workstation with Android Studio/SDK Emulator to create and boot the
AVD.
Current generic-target status:
OPENPHONE_BUILD_GOAL=droid ./scripts/build.sh openphone_arm64has been validated on the prepared macOS build host for host/module graph coverage.OPENPHONE_BUILD_GOAL=MODULES-IN-frameworks-base-services-core ./scripts/build.sh openphone_arm64has been validated for the first OpenPhone framework service patch.OPENPHONE_BUILD_GOAL=OpenPhoneAssistant ./scripts/build.sh openphone_arm64has been validated for the privileged assistant bridge to the framework service.- The generic target validates the OpenPhone product graph and packages, but it is not yet a device-flashable OpenPhone release.
- In this branch,
systemimageis not a valid build goal for the genericopenphone_arm64target. Device-specific image output starts after a real device target is added.
Device-specific OpenPhone Pixel 9a build on Linux:
OPENPHONE_ANDROID_DIR=/path/to/android/tree \
OPENPHONE_RELEASE=bp4a \
OPENPHONE_BUILD_GOAL="droid target-files-package otapackage" \
./scripts/build.sh openphone_teguThe Pixel 9a product is openphone_tegu-bp4a-userdebug. Native macOS builds
will only build host-side targets because build/make/core/main.mk restricts
Darwin to host modules.
For openphone_tegu and openphone_tegu_smoke, scripts/build.sh
automatically prepares the Pixel 9a DTB before target-files/OTA-producing build
goals and verifies the generated vendor_kernel_boot.img afterward. The helper
extracts tegu.dtb from the upstream prebuilt
device/google/tegu-kernels/6.1/vendor_kernel_boot.img and checks it against
the known-good hash documented in TEGU_BOOTCHAIN.md.
To run those steps manually:
OPENPHONE_ANDROID_DIR=/path/to/android/tree ./scripts/prepare-tegu-dtb.sh
OPENPHONE_ANDROID_DIR=/path/to/android/tree ./scripts/verify-tegu-bootchain.sh openphone_teguFast Assistant Iteration
Use the full OTA path only when a change must be baked into Android partitions: framework/base patches, privapp permissions, sysconfig, sepolicy, boot/recovery behavior, or first install on a device. For assistant UI, prompt, policy, trajectory, and model-adapter work, use the debug harness on an already-booted userdebug/eng OpenPhone device.
Build only the assistant module on the Linux Android build host:
OPENPHONE_BUILD_GOAL=OpenPhoneAssistant \
./scripts/build.sh openphone_tegu-bp4a-userdebugCopy the resulting APK back to the host, then push it into /system_ext without
recovery. Because org.openphone.assistant is a persistent privileged app,
Android rejects normal adb install -r; enable Developer Options -> Rooted
debugging once, then use:
scripts/push-assistant-apk.sh /path/to/OpenPhoneAssistant.apkThe device reboots after the push. PackageManager may still show stale persistent system-app metadata on some builds; verify the mounted APK hash if that happens. Use full OTA or factory reset only when the mounted APK bytes do not match, framework/system files changed, or PackageManager state itself is what you are testing.
Run a task with a direct development OpenAI key:
mkdir -p .worktree/secrets
printf '%s' "$OPENAI_API_KEY" > .worktree/secrets/openai_api_key
scripts/run-assistant-task.sh --goal "screen" --wait 30The key file path is ignored by git. You can also pass --api-key-file <path>,
or set OPENAI_API_KEY directly in the shell.
Run without a provider key for local-mode/debug checks:
scripts/run-assistant-task.sh --local --goal "screen" --wait 10After using Advanced -> Export Trace in the assistant, pull and validate the newest trajectory:
scripts/pull-latest-trajectory.sh \
--output-dir .worktree/evals/latest-assistant-runThe harness passes the goal as base64 and uses Android singleTop intent
delivery so repeated host-side runs update the existing assistant activity
instead of reusing stale text. Provider keys remain in the assistant's
in-memory development field; use the broker path for publishable evidence.
Flash
./scripts/flash.sh /path/to/image-or-ota.zipThe flash script currently refuses to guess destructive steps. Device-specific
flash procedures belong in docs/devices/<codename>.md.
Optional User-Supplied Google Services
OpenPhone does not redistribute Google Play Store, Google Play Services, or Google apps. A user may sideload a compatible package on their own device after installing OpenPhone.
For the host-side helper and policy boundary, see GMS.md:
scripts/download-mindthegapps.sh
scripts/sideload-user-gms.sh \
--package .worktree/downloads/gms/MindTheGapps-16.0.0-arm64-*.zipFor Pixel 9a, run the sideload command after installing the OpenPhone OTA and
entering recovery Apply update -> Apply from ADB for additional packages.
After sideload success, the helper waits for Android to boot and grants Google
Play Services the location permissions/app-ops needed by fused/network location
providers.