Skip to content

Add Godot Android service integration - #1138

Draft
linuxificator wants to merge 8 commits into
shorepine:mainfrom
linuxificator:upstream/godot-android
Draft

Add Godot Android service integration#1138
linuxificator wants to merge 8 commits into
shorepine:mainfrom
linuxificator:upstream/godot-android

Conversation

@linuxificator

@linuxificator linuxificator commented Aug 23, 2026

Copy link
Copy Markdown

Dependency: stacked on #1136

This PR depends on #1136 (Add Android Oboe backend and AMY wire socket transport) and should not be merged before #1136 is accepted.

The branch is built directly on the current #1136 head, a12c19bfdd9936dcb10ab8b8e39214041dd56cd9. The Godot contribution itself is exactly three commits on top of that head. Until #1136 is merged into main, GitHub will necessarily show the #1136 commits in this PR as well because a Shorepine PR cannot use a branch from the fork as its base.

For the incremental Godot review, the relevant stack is:

  1. 63f07daa — Add Android transport backend to Godot Amy API
  2. 0f100c27 — Add Godot Android hello-world and CI
  3. f77cf938 — Document Godot Android service integration

Summary

Adds Android support to the existing high-level Godot Amy.gd API without running or compiling a second AMY engine in the Godot process.

On Android the path is:

Godot game code
    |
    | Amy.send(Dictionary)
    v
Amy.gd
    |
    | shared Dictionary -> AMY wire encoder
    v
AmyClient (pure Java transport helper)
    |
    | LocalSocket / SOCK_SEQPACKET
    v
<filesDir>/amy.sock
    |
    v
independent Android :amy service -> AMY -> Oboe/AAudio

The same message(Dictionary) wire generator used by the existing Godot API remains authoritative. Android only changes the transport/backend after a wire message has been built.

Android client boundary

The Godot application:

  • does not start or stop AmyService;
  • does not load the AmySynth GDExtension on Android;
  • does not compile/package AMY C/C++ source in the Godot process;
  • does not use a JNI/native socket client library;
  • packages the prebuilt amy-service AAR in the same APK/UID and sends wire messages through its private socket.

AmyClient is a small pure-Java adapter around Android's public LocalSocket(SOCKET_SEQPACKET) API. It contains no synth/DSP code and owns no service lifecycle.

The existing desktop GDExtension and web/WASM backends remain unchanged in architecture.

Godot example and packaging

Adds godot/android-hello-world, which demonstrates:

  • the normal Amy.send(Dictionary) API rather than direct test-only socket calls;
  • an Android export plugin that packages the service AAR;
  • ARM64 device and x86_64 CI export presets;
  • deliberate exclusion of amy.gdextension, godot/bin, AMY source, and any second AMY native client library.

The source-tree prepare.sh builds/stages debug and release AARs for development/CI. Downstream Godot applications can package a prebuilt AAR instead; they do not need to rebuild AMY as part of every Godot project.

Validation

The cleaned three-commit stack was validated in a fork-local PR directly against the exact #1136 head, so the test diff contained only these Godot changes.

Validated code head before the documentation-only startup clarification: 2f1522b5cc772bb3093068f2e298a64627f5bbec. Current head: f77cf9389a800a69396170f502f8c6b380ca2329.

Successful validation runs:

  • Godot Android run 32659597021: all build/import/export/packaging checks passed; emulator connected Amy.gd to amy.sock, sent Dictionary-generated AMY wire, and the captured AMY render/Oboe audio analyzer passed.
  • Android AMY run 32659597044: socket transport, AAR/hello-world build, Android emulator end-to-end test, and AMY/Oboe audio analysis all passed.

The Godot emulator test is intentionally scoped to the AMY integration contract rather than Godot's renderer. The x86_64 preset omits the optional Canvas UI, and unrelated Godot/SwiftShader renderer failures are not authoritative once the AMY path is established. The test still requires:

  • the separate :amy service and Oboe backend to start;
  • Amy.gd to connect to the private socket;
  • a high-level Dictionary call to generate and send AMY wire (including a note-on);
  • no AMY socket/send exceptions;
  • non-empty captured AMY-render and Oboe WAVs;
  • the existing Android audio analyzer to accept the captured audio.

Documentation

Updates docs/godot.md, android/README.md, and the Android Godot example documentation to make the platform boundary explicit, including:

  • safe backend_ready/backend_error handling;
  • prebuilt-AAR packaging and same-UID private socket requirements;
  • Android-supported wire-oriented GDScript API;
  • desktop-only direct C-API helpers and pre-start engine configuration limitations;
  • explicit instruction not to package the desktop AmySynth GDExtension in Android exports;
  • the Android AMY startup sequence: AmyAutoStartProvider requests the separate :amy service, the native service publishes amy.sock only after Oboe has delivered its first realtime callback, and Amy.gd treats a missing socket as expected during startup and retries for approximately 10 seconds before emitting backend_error.

This PR is intentionally left as Draft while #1136 is reviewed/accepted.

Add a fixed-size AF_UNIX/SOCK_SEQPACKET transport for local AMY wire messages, including peer credential checks, bounded queueing, cleanup safeguards, standalone regression tests, and focused CI.
Add a generic Android AAR service that renders AMY through Oboe/AAudio and accepts ordinary AMY wire packets over the private Unix transport. Include the minimal C-scale hello-world app plus Android build and emulator integration CI.
Document the generic Android AAR, Oboe backend, private SOCK_SEQPACKET client contract, readiness semantics, build requirements, and hello-world example.
Measure the raw AMY render stream and exact signed-16-bit buffer handed to Oboe during the Android hello-world integration test. Retain both WAVs and level statistics in CI, require byte-for-byte AMY-to-Oboe identity, healthy peak level, and no full-scale clipping.

Use AMY V10.0 for the audible hello-world. AMY's V control is a 0..10 bus/master scale and the final mixer applies a 0.1 factor, so the previous V2.0 setting was only 20% linear gain (about -14 dB relative to V10.0).

Validated at -2.721 dBFS peak with zero clipping and zero AMY-to-Oboe sample differences in Android AMY run 32630088165.
Make the Java hello-world a transport-only client. MainActivity no longer imports or starts AmyService and no longer loads a JNI/native client library. It uses Android LocalSocket SOCK_SEQPACKET directly and sends only ordinary AMY wire packets to filesDir/amy.sock.

Move service startup to an AAR-owned ContentProvider lifecycle hook, remove the hello-world C++/CMake client wrapper, and move test-only audio capture arming into CI so the example remains free of service/test control logic.
Add a transport-only Java AmyClient helper to the Android service AAR and teach the shared Godot Amy.gd wrapper to use it on Android.

Android keeps the same high-level Dictionary-to-wire API as desktop/web, but sends each generated wire message over the app-private SOCK_SEQPACKET amy.sock transport. The Godot process does not load or compile the AmySynth GDExtension on Android and does not start or stop AmyService.
Add a small Godot Android example that uses only the shared Amy.gd Dictionary API. The example packages the Android service AAR through a Godot export plugin and deliberately excludes the AmySynth GDExtension and AMY source from Android exports.

Add Android CI that exports ARM64 and x86_64 APKs, enforces a single AMY native implementation in the APK, and proves the high-level Amy.gd -> private socket -> :amy service -> Oboe audio path on an emulator. The emulator acceptance test deliberately ignores unrelated Godot renderer/UI failures once the AMY transport is established; it still fails on socket transport errors or missing/non-matching audio.
Document the Android service/socket architecture, platform-specific API surface, prebuilt AAR packaging, safe backend readiness handling, and startup sequencing. Clarify that the private ContentProvider requests service startup, the native service publishes amy.sock only after Oboe's first realtime callback, and Amy.gd tolerates an absent socket for approximately 10 seconds while retrying connection.
@linuxificator
linuxificator force-pushed the upstream/godot-android branch from d163680 to f77cf93 Compare August 23, 2026 19:16
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.

1 participant