lawnchair-build — PawletOS Lawnchair build bridge
Builds Lawnchair (16-dev) either the current known-good way (vendored
prebuilt stubs) or against PawletOS's actual AOSP android-16.0.0_r4
checkout, to eliminate the framework version-skew crash in
TouchInteractionService (NoSuchMethodError at startup →
OverviewProxyService never binds → no nav pill/gestures).
Kept separate from the lawnchair repo on purpose: the bridge mutates the
Lawnchair tree reversibly, and this way a failed experiment can't dirty the
launcher history.
Three-repo layout
| Repo | Role | Where |
|---|---|---|
PawletOS/prebuilt_lawnchair |
pre-built APK shipped in the ROM | vendor/Lawnchair/LawnchairLauncher (Soong build) |
PawletOS/Lawnchair (16-dev) |
launcher source | external/build-tools/lawnchair/source (in-tree) |
PawletOS/lawnchair-build |
this repo — build scripts | external/build-tools/lawnchair/build (in-tree) |
The in-tree paths are wired in android_local_manifest/common/pawletos.xml
and synced by environ.py --forceResync. Neither source nor build repo is
part of the Soong build — the output APK is committed to
prebuilt_lawnchair, which is what the ROM actually packages.
The scripts find the Lawnchair checkout as a sibling automatically, in either layout:
AOSP tree (build server) PawletOS working copy (Windows)
external/build-tools/lawnchair/ PawletOS/
source/ <- Lawnchair 16-dev lawnchair/ <- Lawnchair 16-dev
build/ <- this repo lawnchair-build/ <- this repo
In-tree, build.sh aosp also derives AOSP_ROOT from its own location
(4 levels up), so no env vars are needed on the server.
Hybrid mode (build on a machine without the AOSP tree)
aosp-bridge/fetch-aosp-cache.sh <LAWNCHAIR_ROOT> pulls the bridge's
inputs from the build server over ssh into .aosp-cache/ shaped like an
AOSP root (the vendored-AIDL counterparts + the compiled framework jar,
~48 MB), then:
AOSP_ROOT="$PWD/.aosp-cache" ./build.sh aosp
Requires m framework-minus-apex to have been run on the server first
(AIDL-only --skip-jars works without it).
Server Gradle toolchain
The build server has no system JDK/SDK. Gradle needs:
JAVA_HOME=~/PawletOS-Build/AOSP-Source/prebuilts/jdk/jdk21/linux-x86(AOSP's prebuilt JDK 21 — nothing installed)ANDROID_HOME=~/android-sdk— cmdline-tools +platforms;android-37.0build-tools;37.0.0(note: the package isandroid-37.0, notandroid-37— minor-versioned platform naming)
Usage
./build.sh prebuilt # plain Gradle, vendored stubs — works anywhere
./build.sh aosp # both swaps + Gradle — needs an AOSP env on THIS machine
./build.sh aosp --skip-jars # AIDL swap only (try this first)
./build.sh aosp --wmshell # additionally swap WindowManager-Shell-16.jar
./build.sh restore # undo all bridge changes in the lawnchair tree
./build.sh clone # clone Lawnchair next to this folder if missing
aosp mode auto-detects the AOSP tree ($AOSP_ROOT,
~/PawletOS-Build/AOSP-Source, /mnt/ssd/AOSP-Source) and errors out if
none exists — so it effectively only runs on ubuntu-server-01, while
prebuilt works on any machine with a JDK/Android SDK.
What the bridge actually swaps (verified against the real tree, 2026-07)
The original plan guessed at Lawnchair's layout; the real wiring is:
| Plan assumed | Reality |
|---|---|
quickstep/build.gradle.kts with its own aidl sourceSet |
One Groovy root build.gradle; quickstep is the withQuickstep sourceSet |
Hidden-API jar at quickstep/libs/, needs Gradle edit |
addFrameworkJar('framework-16.jar') resolves prebuilts/libs/framework-16.jar by name into ~25 modules — drop-in file replacement, zero Gradle edits |
| Symlink IPip/ISplitScreen/IRecentsAnimation* .aidl | Those files under wmshell/src are dead source — the compiled wm.shell stubs ship in prebuilts/libs/WindowManager-Shell-16.jar (an implementation dep, bundled into the APK). Swapping the jar (--wmshell) is the real lever |
assembleLawnWithQuickstepRelease |
Three flavor dimensions → assembleLawnWithQuickstepGithubRelease |
So the two swaps are:
- AIDL sources (
aosp-bridge/gen-aidl-sourceset.sh) — every.aidlundersystemUI/shared/srcthat has a same-path counterpart inframeworks/base/packages/SystemUI/shared/src(orcore/java) is replaced by the AOSP copy. Copies, not symlinks; originals kept as.orig+ manifest. - Prebuilt jars (
aosp-bridge/gen-framework-jars.sh) —prebuilts/libs/framework-16.jar←framework-minus-apex.jarfromout/soong/.intermediates.--wmshelladditionally swapsWindowManager-Shell-16.jar.
Known skew already found
Lawnchair vendors both IOverviewProxy.aidl and ILauncherProxy.aidl
in systemUI/shared/src/com/android/systemui/shared/recents/; the r4 tree
has only ILauncherProxy + ISystemUiProxy (IOverviewProxy is the
pre-rename QPR name). If Lawnchair's TouchInteractionService still hands
SystemUI an IOverviewProxy binder, that alone explains the bind failure —
the AIDL swap script flags this case but can't fix it; it needs a source
look in Lawnchair's quickstep code.
Suggested order of operations
- Fix the current AOSP build blocker first (last build died in Soong
generation: TWRP theme selection —
TW_THEMEunset, likely from the newrecovery_toolkitinandroid_vendor_pawlet), then at minimumm framework-minus-apex. DRY_RUN=1 ./aosp-bridge/gen-aidl-sourceset.sh <AOSP_ROOT> <LAWNCHAIR_ROOT>— read what differs before anything is touched../build.sh aosp --skip-jars— test whether the AIDL swap alone fixes the crash.- Full
./build.sh aosp, then--wmshellon top, escalating only as needed — each step pulls in more of the diverged class graph, and compile errors from the jar swaps are the skew surfacing at build time (fix in Lawnchair source, don't paper over). - If it balloons:
./build.sh prebuiltstill works (it auto-restores any bridge leftovers), and pinning to Lawnchair 15 stable remains the fallback plan.
Verifying the fix on device
adb connect 10.0.0.236:5555
adb logcat -c
# restart Lawnchair / reboot
adb logcat | grep -iE "TouchInteractionService|app.lawnchair|FATAL EXCEPTION|OverviewProxyService"
Success: no NoSuchMethodError/NoSuchFieldError, an OverviewProxy bind
log line, and the nav pill/gesture affordances actually rendering.