Android: decouple JNI setup from unused subsystems#15895
Draft
HTRamsey wants to merge 1 commit into
Draft
Conversation
Query the natively compiled-in subsystems at runtime so the Java side only registers and initializes the managers that exist, fixing UnsatisfiedLinkError when SDL is built with a subsystem disabled (e.g. -DSDL_AUDIO_DISABLED). - SDL.setupJNI() calls nativeGetCompiledSubsystems() and skips SDLAudioManager/SDLControllerManager setup for absent subsystems. - Gate SDLControllerManager.initializeDeviceListener() and the joystick key-event path on the controller subsystem; gate the clipboard handler on video, mirroring the native guards. - Guard the SDLControllerManager JNI bindings with SDL_ANDROID_NEED_CONTROLLER_MANAGER (joystick or haptic enabled). - Reorganize SDL_android.h/.c into guarded per-subsystem groups with matching ordering.
slouken
reviewed
Jun 28, 2026
|
|
||
| if ((subsystems & SDL_INIT_AUDIO) != 0) { | ||
| int compiled = SDLActivity.nativeGetCompiledSubsystems(); | ||
| mInitializedSubsystems = subsystems & compiled; |
Collaborator
There was a problem hiding this comment.
Suggested change
| mInitializedSubsystems = subsystems & compiled; | |
| mInitializedSubsystems = (subsystems & compiled); |
slouken
reviewed
Jun 28, 2026
| // C functions we call | ||
| public static native String nativeGetVersion(); | ||
| public static native void nativeSetupJNI(); | ||
| public static native int nativeGetCompiledSubsystems(); |
Collaborator
There was a problem hiding this comment.
@HTRamsey, @AntTheAlchemist, do we need to do something with proguard here?
slouken
reviewed
Jun 28, 2026
| SDLActivity.getContentView().requestPointerCapture(); | ||
| } else { | ||
| SDLActivity.getContentView().releasePointerCapture(); | ||
| if (SDLActivity.getContentView() != null) { |
Collaborator
There was a problem hiding this comment.
Please put this into a temporary variable.
slouken
reviewed
Jun 28, 2026
| } | ||
|
|
||
| if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) { | ||
| if (mRelativeModeEnabled && !SDLActivity.isDeXMode() && SDLActivity.getContentView() != null) { |
Collaborator
There was a problem hiding this comment.
Again, please put this into a temporary variable.
slouken
reviewed
Jun 28, 2026
| return SDL_Unsupported(); | ||
| } | ||
|
|
||
| #ifndef SDL_HAPTIC_DISABLED |
Collaborator
There was a problem hiding this comment.
Please move this to the top of the function.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On Android, JNI setup previously assumed every subsystem was compiled in:
JNI_OnLoad/checkJNIReadywaited on the audio and controller manager classes unconditionally, andSDLActivityalways created a surface, clipboard handler, and controller listeners. As a result a build with subsystems disabled (e.g.-DSDL_VIDEO=OFF,-DSDL_AUDIO=OFF, joystick-only) would either fail to link, stall incheckJNIReady()becauseSDL_SetMainReady()never fired, or crash dereferencing a null surface.This decouples the Android JNI layer from individual subsystems so SDL builds and runs with any subsystem disabled, and supports a headless
SDLActivity(no rendering surface) whenSDL_VIDEO_DISABLEDis set.Changes
C (
src/core/android/)SDL_*_DISABLEDmacros. TheSDLControllerManagertable is gated on joystick (haptic requires joystick, enforced in CMake), audio onSDL_AUDIO_DISABLED, video/input and dialog rows likewise.JNI_OnLoadonly registers the manager classes that are compiled in;checkJNIReady()only waits on the classes that exist, soSDL_SetMainReady()fires in reduced builds.nativeGetCompiledSubsystems()exports the compiled-in subsystem mask to Java so the Java layer can match the native build.Java (
android-project/.../org/libsdl/app/)SDL.setupJNI()intersects the requested subsystems withnativeGetCompiledSubsystems()and only sets up the audio / controller managers whose bits survive — this is what prevents thecheckJNIReady()stall.SDLActivitygates surface, layout, clipboard, drop-file, orientation, and controller-listener setup on the active subsystems, and is null-safe on the lifecycle paths when there is no surface (headless).SDLControllerManageronly builds a haptic handler when haptic is active and null-guards the relative-mouse paths for the headless case.Testing
Built
arm64-v8a,Release, NDK r27,android-21,-DSDL_SHARED=ON, with each subsystem disabled individually plus an all-off "minimal" config. All 13 configurations configure, compile, and link cleanly. StrippedlibSDL3.sosizes (llvm-strip):libSDL3.so(stripped)SDL_POWER=OFFSDL_HAPTIC=OFFSDL_DIALOG=OFFSDL_SENSOR=OFFSDL_CAMERA=OFFSDL_AUDIO=OFFSDL_GPU=OFFSDL_JOYSTICK=OFF+SDL_HAPTIC=OFFSDL_RENDER=OFFSDL_HIDAPI=OFFSDL_VIDEO=OFF(headless)A minimal Android build is roughly 46% smaller than the full library.
Static/build verification is complete across all configs. On-device runtime validation of a headless activity and a controller-only build is still outstanding.