Skip to content

Support 32-bit ARM Android (armeabi-v7a), and share files on Android 8 and 9 - #30

Draft
kevinaboos wants to merge 3 commits into
mainfrom
support_32bit_android
Draft

kevinaboos wants to merge 3 commits into
mainfrom
support_32bit_android

Conversation

@kevinaboos

@kevinaboos kevinaboos commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Lets the robius crates build for 32-bit ARM Android (armv7-linux-androideabi, ABI armeabi-v7a), which Robrix needs in order to ship an APK for devices with a 32-bit Android system (e.g., ones whose CPU reports armv8l). Many of those devices run Android 8 or 9, where robius-share couldn't share file attachments, so this also fixes that.

Changes

  • robius-location: remove the compile_error!("non-64-bit Android targets are not supported") in the Android callback. It guarded nothing: the callback's Arc<Shared> pointer goes to Java as a jlong and comes back the same way, which round-trips losslessly on 32-bit as well. This was the only thing stopping robius from building for armv7.
  • CI: a new Android workflow runs cargo check on every crate (libs, examples and tests) for both aarch64-linux-android and armv7-linux-androideabi, using the runner's Android SDK so the Java build steps run for real. It pins JDK 17 and the newest stable SDK platform (the runner also has preview platforms, like android-37.2-beta3, that the build scripts would otherwise pick). It fails if robius-speech quietly builds without its Android backend, which it does when it can't find the SDK.
  • robius-share: file attachments on Android 8 and 9 (API 26-28).
    • Before, add_file(path) could only share small text files there, as text. Any other file, such as a photo or PDF, failed the whole share, because Android 8/9 has no MediaStore Downloads collection to copy into.
    • Now, if the app declares a provider with authority <package name>.robius.share (the authority the README already recommended), robius-share copies the file to <cache dir>/robius-share/<uuid>/<name> and shares content://<package name>.robius.share/robius-share/<uuid>/<name>. robius-share can't ship that provider itself: providers must be declared in the app's manifest, and robius-share's Java is loaded at runtime.
    • It only uses the provider if it belongs to the app, and is allowed to grant access to that URI. Any app can claim an authority no installed app holds, so another app could otherwise receive the file and substitute its content. A provider that can't grant the URI would make the system refuse to start the share sheet.
    • Before sharing, it opens the copy through the provider, so a provider that maps the URI elsewhere falls back to the old behavior instead of failing in the receiving app.
    • Copies older than a day are deleted on a later share. Only the per-share directories (named by a lowercase UUID) are deleted, without following symbolic links.
    • Unchanged: Android 10+ still copies to MediaStore first (the provider is only a fallback there), and apps without the provider behave as before.
    • The README and crate docs explain how to declare the provider, with androidx's FileProvider or with cargo-makepad's new MakepadFileProvider (Fixes for 32-bit ARM Android (armeabi-v7a) kevinaboos/makepad#6, also offered upstream). The FileProvider path name in the README example is now robius-share, the URI path robius-share uses.

Audit of the rest of robius for 32-bit

I checked all the Android code (Rust, Java and build scripts) for anything else that would break on 32-bit:

  • Every JNI native method takes a jlong wherever its descriptor says J.
  • Every pointer passed through Java is a thin pointer cast to jlong and back.
  • The Java side only stores these in long fields and compares them with 0.
  • There are no other target_pointer_width/target_arch gates and no usize/u64 truncation.
  • The d8 --min-api settings don't depend on the ABI.

Testing

  • cargo check --workspace --lib --examples --tests passes for both Android targets. I couldn't run the Java build steps locally (no Android SDK), so they were stubbed there; the new CI workflow runs them for real.

  • Robrix's whole dependency tree, with this branch patched in, passes cargo check for both armv7-linux-androideabi and aarch64-linux-android.

  • The SDK-selection step was tested against a copy of the runner's SDK directory layout, and against a missing SDK.

  • ShareSheet.java compiles cleanly against the API 33 stubs, with -Xlint:deprecation.

  • The Android 8/9 sharing was tested with Robolectric on the Android 8.0 (API 26) and 8.1 (API 27) runtimes, together with MakepadFileProvider, in 17 tests. They cover:

    • binary and text files, including through file:// locations;
    • reading the shared URIs back through the provider, with names that have spaces and #;
    • the chooser intent's read grant and ClipData;
    • no provider, a provider that serves another directory, a provider of another app, a provider that can't grant, and <grant-uri-permission> patterns;
    • pruning, with app files and symbolic links in the directory.

    The tests fail on the code before this change, and on deliberately broken variants. They are outside this repo, since it has no Java test setup. Robolectric approximates Android, so this still needs a check on a real Android 8 or 9 device.

  • cargo test -p robius-share and cargo doc -p robius-share pass.

Not changed here

add_file(path) fails on every Android version if the path contains :, because Uri.parse takes the text before the colon as a scheme. This bug was already there before this PR. Robrix isn't affected: it removes : from file names. The fix would be to treat a location that starts with / as a path in fileFromLocation.

Related: project-robius/robrix#1208 adds the armv7 build and release, and declares the provider. Its Cargo.lock temporarily points at this branch; once this merges, it moves to main with cargo update -p robius-location.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Va5dxPCRyW8YN5b1Yt1Awy

The `compile_error!` that rejected non-64-bit Android targets guarded nothing: the callback's
`Arc<Shared>` pointer goes to Java as a `jlong` and comes back the same way, which round-trips
losslessly on 32-bit too. It was the only thing stopping robius from building for armv7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va5dxPCRyW8YN5b1Yt1Awy
Builds every crate for `aarch64-linux-android` and `armv7-linux-androideabi` with the runner's
Android SDK (and its Java build steps), so code that only works with 64-bit pointers is caught.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va5dxPCRyW8YN5b1Yt1Awy
Android 8 and 9 (API 26-28) have no MediaStore Downloads collection, so
`add_file(path)` could only share small text files there, as text. Any
other file, such as a photo or PDF, failed the whole share.

There, robius-share now copies the file to
`<cache dir>/robius-share/<uuid>/<name>` and shares it as
`content://<package>.robius.share/robius-share/<uuid>/<name>`, if the
app declares a provider with that authority. That's the authority the
README already told apps to use. robius-share can't ship the provider
itself: providers must be declared in the app's manifest, and its Java is
loaded at runtime.

It only uses that provider if it is the app's own. Any app can claim an
authority no installed app holds, so another app could otherwise receive
the file and substitute its content. The provider must also be allowed to
grant access to the URI, or the system would refuse to start the share
sheet. Before sharing, it opens the copy through the provider, so a
provider that maps the URI elsewhere falls back to the old behavior rather
than failing in the receiving app.

Copies older than a day are deleted on a later share. Only per-share
directories (named by a lowercase UUID) are deleted, and symbolic links
are not followed.

The README shows how to declare such a provider, with androidx's
FileProvider or with cargo-makepad's MakepadFileProvider.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va5dxPCRyW8YN5b1Yt1Awy
kevinaboos pushed a commit to project-robius/robrix that referenced this pull request Oct 26, 2026
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
@kevinaboos kevinaboos changed the title Support 32-bit ARM Android (armeabi-v7a) Support 32-bit ARM Android (armeabi-v7a), 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