Android
This page covers only the Android-specific integration delta. The general attach / detach / release
contract (generation semantics, error codes, MigoSurfaceDescriptor field rules) is described in
Surface Interface; for Session lifecycle see
Lifecycle Concepts.
Availability: Released in 0.9 for arm64-v8a and x86_64 (emulator). Java/Kotlin hosts
operate through the AAR (com.migo.runtime); pure C hosts use the C ABI described below directly.
Descriptor
Section titled “Descriptor”#include <migo/platform/android.h>#include <migo/capabilities.h>typedef struct MigoAndroidNativeWindowDescriptor { uint32_t struct_size; uint32_t abi_version; MigoPlatformKind platform_kind; /* MIGO_PLATFORM_ANDROID_NATIVE_WINDOW */ MigoPlatformDescriptorFlags flags; /* must be 0 */ void *native_window; /* ANativeWindow* */} MigoAndroidNativeWindowDescriptor;native_window is an ANativeWindow*. The engine acquires its own strong reference before
attach returns successfully, and releases that reference before the release observer reaches
MIGO_SURFACE_RELEASE_RELEASED. The host retains its own reference; no additional refcount
management is needed after a successful attach.
When passing a MigoSurfaceDescriptor, point platform_descriptor at this struct, set
platform_descriptor_size to sizeof, and ensure platform_kind is consistent in both the
outer descriptor and the inner platform descriptor.
Pure-C Host: migo_android_init_context
Section titled “Pure-C Host: migo_android_init_context”MigoResult migo_android_init_context(void *vm, void *activity);Call this only when both of the following conditions hold:
- The host is a pure C / NDK host (no Java layer, not using the
com.migo.runtimeAAR). - The Session will play audio.
vm and activity correspond to the vm and clazz fields of ANativeActivity. Repeated calls
after the first are no-ops; calling again after an Activity recreation is safe without checking.
Passing NULL for vm returns MIGO_ERROR_INVALID_ARGUMENT.
Omitting this call does not cause attach to fail, but the first audio playback will terminate
the process instead of returning an error — call this before creating any Session that will
play audio.
The generation Counter (Android-Specific Pitfall)
Section titled “The generation Counter (Android-Specific Pitfall)”Android destroys and recreates ANativeWindow every time the app moves to the background and
returns to the foreground. Each surfaceCreated / surfaceChanged callback delivers a new window;
generation must strictly increment and must never be hard-coded to the constant 1.
/* host-owned counter, incremented across surfaceCreated/surfaceChanged */static uint64_t g_surface_gen = 0;A duplicate generation value returns MIGO_ERROR_STALE_SURFACE; passing 0 on the first call
returns MIGO_ERROR_INVALID_ARGUMENT (generation must be ≥ 1).
Attach
Section titled “Attach”static MigoSurfaceAttachment *s_attachment = NULL;
void on_surface_created(void *window, int32_t w, int32_t h, float density) { /* availability check (optional, but recommended once before the first attach) */ MigoCapabilities caps; caps.struct_size = (uint32_t)sizeof caps; caps.abi_version = MIGO_ABI_VERSION_CURRENT; if (migo_query_capabilities(&caps) != MIGO_OK || (caps.platform_kinds & (1ULL << MIGO_PLATFORM_ANDROID_NATIVE_WINDOW)) == 0) return; /* this build cannot attach an Android surface */
MigoAndroidNativeWindowDescriptor plat; memset(&plat, 0, sizeof plat); plat.struct_size = (uint32_t)sizeof plat; plat.abi_version = MIGO_ABI_VERSION_CURRENT; plat.platform_kind = MIGO_PLATFORM_ANDROID_NATIVE_WINDOW; plat.native_window = window;
MigoSurfaceDescriptor desc; memset(&desc, 0, sizeof desc); desc.struct_size = (uint32_t)sizeof desc; desc.abi_version = MIGO_ABI_VERSION_CURRENT; desc.generation = ++g_surface_gen; /* incremented; never a constant */ desc.platform_kind = MIGO_PLATFORM_ANDROID_NATIVE_WINDOW; desc.width_pixels = (uint32_t)w; desc.height_pixels = (uint32_t)h; desc.scale_factor = density; desc.color_space = MIGO_COLOR_SPACE_SRGB; desc.alpha_mode = MIGO_ALPHA_MODE_OPAQUE; desc.preferred_presentation_mode = MIGO_PRESENTATION_MODE_DEFAULT; desc.capability_flags = MIGO_SURFACE_CAPABILITY_NONE; /* must be 0 in 0.9 */ desc.platform_descriptor_size = (uint32_t)sizeof plat; desc.platform_descriptor = &plat;
migo_session_attach_surface(session, &desc, &s_attachment);}Detach and Release
Section titled “Detach and Release”Do not destroy ANativeWindow immediately when surfaceDestroyed fires — the GPU may still
hold a reference to it.
void on_surface_destroyed(void) { MigoSurfaceRelease *rel = NULL; migo_surface_begin_detach(s_attachment, &rel); s_attachment = NULL;
/* poll until RELEASED; a real host can use the on_surface_released callback instead */ MigoSurfaceReleaseStatus status; do { status.struct_size = (uint32_t)sizeof status; status.abi_version = MIGO_ABI_VERSION_CURRENT; migo_surface_release_query(rel, &status); } while (status.state != MIGO_SURFACE_RELEASE_RELEASED);
migo_surface_release_destroy(rel); /* only now is it safe to destroy the ANativeWindow */}Once migo_surface_begin_detach returns MIGO_OK, the attachment pointer is consumed and must
not be used again.