Skip to content

Build and release a 32-bit ARM Android (armeabi-v7a) APK, and share files on Android 8 and 9 - #1208

Open
kevinaboos wants to merge 8 commits into
mainfrom
claude/robrix-armv8l-32bit-arm-kzu001
Open

kevinaboos wants to merge 8 commits into
mainfrom
claude/robrix-armv8l-32bit-arm-kzu001

Conversation

@kevinaboos

@kevinaboos kevinaboos commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Adds a 32-bit ARM Android build of Robrix (Rust target armv7-linux-androideabi, Android ABI armeabi-v7a) for devices with a 32-bit Android system, such as those whose CPU reports armv8l. Those devices can't install the arm64 APK at all. Many of them run Android 8 or 9, where Robrix couldn't share attachments, so this PR also fixes that.

Depends on

  • Support 32-bit ARM Android (armeabi-v7a), and share files on Android 8 and 9 robius#30:
    • removes robius-location's compile_error! on non-64-bit Android, the only thing in robius that blocks the armv7 build;
    • lets robius-share share files on Android 8 and 9 through the app's own provider.
  • Fixes for 32-bit ARM Android (armeabi-v7a) kevinaboos/makepad#6: makepad fixes for 32-bit ARM, merged into dock_current_tab_robrix.
    • The important one fixes DrawVars padding. Without it, every draw call's instance data is off by one f32 lane on 32-bit, so the whole UI draws garbled.
    • It also includes cargo-makepad's armv7 NDK-sysroot fix, and the MakepadFileProvider class that this PR's manifest declares.
  • Once both are merged, update this PR's Cargo.lock to them (cargo update -p robius-location -p makepad-widgets).

For now, two commits marked TEMPORARY point Cargo.lock at the heads of those two PR branches. Only the git revisions change. This lets CI test the whole stack, with the real NDK, before anything is merged. Before merging this PR, run the cargo update above, so the lock doesn't point at unmerged branch commits.

The makepad revision has to include MakepadFileProvider. The manifest declares that provider, and an APK built with a cargo-makepad that lacks the class crashes at launch. CI and release builds install cargo-makepad from the makepad revision in Cargo.lock.

Changes

  • Cargo.lock: bump aws-lc-rs 1.14.1 → 1.18.2 (aws-lc-sys 0.32.0 → 0.46.0).
    • aws-lc-sys 0.32 has no pre-generated bindings for armv7-linux-androideabi, so its build fails unless bindgen-cli is installed.
    • Since 0.33 it ships target-independent bindings, so bindgen and clang-sys also drop out of the tree.
    • The bump also moves cc to 1.10.0 and shlex to 2.0.1.
  • builds.yml: a new Build Android armv7 (Linux Host) PR job. --abi=armv7 has to come before both install-toolchain and build, or cargo-makepad only installs and builds aarch64.
  • release.yml: the Android release job is now a matrix (aarch64 + armv7), driven by determine_matrices like the desktop jobs.
    • A tag push builds both ABIs. A manual run has a new build_android_armv7 input next to build_android_aarch64.
    • Each leg sets MAKEPAD_ANDROID_ABI, because makepad-packaging-action picks the ABI from that alone. It ignores --target, so an armv7 leg without it would rebuild aarch64 and overwrite that asset.
    • The aarch64 leg's job name, env, args and asset name are unchanged. The new asset is Robrix-<version>-android-armv7.apk.
    • Both APKs of a release now share one versionCode, computed once in determine_matrices. Before, version_code = "auto" was computed per job, so two jobs straddling an hour would get different codes, and switching between the APKs would then hit a version-downgrade error.
  • Upload progress fix: upload percentages overflowed on 32-bit. current_bytes * 100 overflows a 32-bit usize once an upload passes about 43 MB, which made the shown percentage fall back toward 0% (and panic in debug builds). All three call sites now use one widening helper, with a test.
  • Sharing attachments on Android 8 and 9: the Android manifest declares cargo-makepad's MakepadFileProvider with authority {package_id}.robius.share, serving the robius-share directory of the app's cache.
    • On Android 8 and 9 there is no MediaStore Downloads collection, so robius-share can only share a file through a provider of the app's own. With this provider, it copies the file there and shares a content:// URI.
    • Without it, sharing any attachment other than a small text file failed on those versions.
    • Android 10+ is unchanged: there robius-share still copies to MediaStore.
  • README:
    • which APK to download, and how to build for armv7;
    • the minimum is Android 8.0 (API 26), not Android 13. cargo-makepad's default minSdk is 26, and Robrix doesn't override it;
    • install cargo-makepad from the Makepad fork and branch Robrix depends on, not upstream dev, whose cargo-makepad lacks the provider class.

How this was checked

Before writing any code, a multi-agent audit (with independent verification of each finding) looked at:

  • cargo-makepad's Android packaging
  • makepad's Android platform code (FFI/JNI/NDK bindings at 32-bit sizes)
  • the other makepad crates Robrix uses
  • robius, including the Java side
  • Robrix itself
  • the third-party dependency tree, with aws-lc's CMake/asm build and text relocations for armv7
  • the CI/release wiring

Everything it confirmed is fixed in this PR or in the two above. The Android 8/9 sharing change got its own multi-agent review, and every finding it confirmed is fixed.

  • CI: every job passes on this branch, with the lock pointing at both fix branches.
    • The new Build Android armv7 (Linux Host) job builds and links the armeabi-v7a APK with the real NDK. It installs cargo-makepad from the pinned makepad revision, so it also compiles MakepadFileProvider into the APK, next to the manifest that declares it.
    • The other jobs also pass: the three arm64 Android hosts, iOS, macOS, Windows, Linux, clippy and typos. Together they cover the aws-lc bump.
  • Local:
    • The new upload_percent test passes, and the old code fails it.
    • actionlint passes on both workflows.
    • I ran the determine_matrices script under node for a tag push and for several dispatch combinations.
    • I ran the version-code pin step against a copy of Cargo.toml. Its output parses as an integer version_code, which cargo-makepad accepts.
    • cargo metadata --locked resolves the moved pins, and CI's resolve-makepad-rev.sh picks a makepad revision that contains MakepadFileProvider.
  • Sharing on Android 8/9: robius-share's new code and MakepadFileProvider were tested together with Robolectric on the Android 8.0 and 8.1 runtimes (17 tests; see robius#30). That also covers reading the shared file back through the provider, and the chooser's read grant.

Not verified: running the app on an armv7 device, and sharing on a real Android 8 or 9 device.

The same makepad fixes, minus DrawVars which upstream already has, are also ported onto upstream makepad dev in kevinaboos/makepad:android_armv7_fixes, for an upstream PR. That port, which now includes MakepadFileProvider, doesn't change what Robrix depends on.

Known and not changed here

  • Attachments are held in memory: uploads and downloads are buffered whole. On 32-bit, files of a few hundred MB could exhaust the address space. This only matters on servers with upload limits far above Synapse's 50M default.
  • glEsVersion: the Android manifest declares glEsVersion 0x00020000, but makepad's Android EGL code needs ES3. Some cheap 32-bit devices (Mali-400/450 class GPUs) only have ES2, so the app would install there and then fail to render.
  • iOS bindgen step: builds.yml still installs bindgen-cli for the iOS build. aws-lc-sys 0.46 no longer needs it, so that step could go in a follow-up.
  • No NEON: the armv7 Rust target builds without NEON, which only affects performance.
  • Unreachable overflow: matrix-sdk-sqlite's get_db_size multiplies in usize and could overflow past 4 GiB on 32-bit, but Robrix never calls it.
  • Keyboard on Android 10 and older: the on-screen keyboard may cover the message input on Android versions before 11 (API 30). That needs its own change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Va5dxPCRyW8YN5b1Yt1Awy

aws-lc-sys 0.32.0 has no pre-generated bindings for `armv7-linux-androideabi`, so its build script
fails there unless `bindgen-cli` is installed. Since 0.33 it ships target-independent ("universal")
bindings, which also means `bindgen` and `clang-sys` drop out of the dependency tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va5dxPCRyW8YN5b1Yt1Awy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va5dxPCRyW8YN5b1Yt1Awy
…64 one

The Android release job is now a matrix, like the desktop jobs, built by `determine_matrices`.
A tag push builds both ABIs; a manual run builds whichever of `build_android_aarch64` and the new
`build_android_armv7` are checked. The armv7 asset is `Robrix-<version>-android-armv7.apk`.

makepad-packaging-action picks the ABI from `MAKEPAD_ANDROID_ABI` alone (it ignores `--target`),
so each leg sets it explicitly. The aarch64 leg builds and names its APK exactly as before.

Both APKs of a release now get the same versionCode, computed once in `determine_matrices`.
With `version_code = "auto"`, each job computed its own from the current hour, so jobs that
straddled an hour would have given one release two codes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va5dxPCRyW8YN5b1Yt1Awy
`current_bytes * 100` overflows a 32-bit `usize` (e.g., on armv7 Android) once an upload passes
~43 MB, which made the shown percentage jump back toward 0% (and panic with overflow checks on).
All three places that compute it now use one helper that widens first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va5dxPCRyW8YN5b1Yt1Awy
Points Cargo.lock at the heads of project-robius/robius#30 (`support_32bit_android`) and
kevinaboos/makepad#6 (`android_armv7_robrix`), so this PR's armv7 build can be checked before
those merge. Only the two git revisions change. Replace this commit before merging, once both
are merged: `cargo update -p robius-location -p makepad-widgets`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va5dxPCRyW8YN5b1Yt1Awy
Both branches now also carry the Android 8/9 file-sharing fix:
cargo-makepad's MakepadFileProvider (kevinaboos/makepad#6) and
robius-share's use of the app's provider (project-robius/robius#30). The
next commit declares that provider in Robrix's manifest. An APK built with
a cargo-makepad that lacks the class would crash at launch, so this pin
has to land first. Only the two git revisions change.

Like the previous TEMPORARY commit, replace this before merging, with
`cargo update -p robius-location -p makepad-widgets` once those PRs merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va5dxPCRyW8YN5b1Yt1Awy
On Android 8 and 9 (API 26-28), robius-share can only share a file through
the app's own `<package>.robius.share` provider, because there is no
MediaStore Downloads collection to copy it into. Without one, sharing
any attachment but a small text file fails there. Declare cargo-makepad's
MakepadFileProvider with that authority, serving robius-share's copies in
the cache.

README:
- Android 8.0 (API 26) is the minimum, not Android 13. cargo-makepad's
  default minSdk is 26, and Robrix doesn't override it.
- Install cargo-makepad from the Makepad fork and branch Robrix depends
  on, not upstream `dev`. The provider class comes with cargo-makepad,
  and an APK built without it crashes at launch. CI and release builds
  already install cargo-makepad from the makepad rev in Cargo.lock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va5dxPCRyW8YN5b1Yt1Awy
@kevinaboos kevinaboos changed the title Build and release a 32-bit ARM Android (armeabi-v7a) APK Build and release a 32-bit ARM Android (armeabi-v7a) APK, and share files on Android 8 and 9 Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants