From acc06bce355097b0df4e4a5f0955928dbb298bb7 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 8 Jun 2026 14:19:43 -0700 Subject: [PATCH] android: fixed crash adding joysticks before joysticks are initialized Fixes https://github.com/libsdl-org/SDL/issues/15777 --- .../org/libsdl/app/SDLControllerManager.java | 141 ++++++++++-------- src/joystick/android/SDL_sysjoystick.c | 12 +- 2 files changed, 82 insertions(+), 71 deletions(-) diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java b/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java index f61bab1d54..0e6c204945 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java @@ -275,82 +275,91 @@ class SDLJoystickHandler { * Handles adding and removing of input devices. */ synchronized void detectDevices() { - int[] deviceIds = InputDevice.getDeviceIds(); - for (int device_id : deviceIds) { - if (SDLControllerManager.isDeviceSDLJoystick(device_id)) { - deviceAdded(device_id); - } - } - } + int[] deviceIds = InputDevice.getDeviceIds(); + for (int device_id : deviceIds) { + if (SDLControllerManager.isDeviceSDLJoystick(device_id)) { + deviceAdded(device_id); + } + } + } void deviceAdded(int device_id) { + InputDevice joystickDevice = InputDevice.getDevice(device_id); + if (joystickDevice == null ) { + return; + } + SDLJoystick joystick = getJoystick(device_id); if (joystick == null) { - InputDevice joystickDevice = InputDevice.getDevice(device_id); joystick = new SDLJoystick(); joystick.device_id = device_id; - joystick.name = joystickDevice.getName(); - joystick.desc = getJoystickDescriptor(joystickDevice); - joystick.axes = new ArrayList(); - joystick.hats = new ArrayList(); - java.util.Set axisStrsSet = new java.util.HashSet(); - joystick.lights = new ArrayList(); + joystick.name = joystickDevice.getName(); + joystick.desc = getJoystickDescriptor(joystickDevice); + joystick.axes = new ArrayList(); + joystick.hats = new ArrayList(); + java.util.Set axisStrsSet = new java.util.HashSet(); + joystick.lights = new ArrayList(); - List ranges = joystickDevice.getMotionRanges(); - Collections.sort(ranges, new RangeComparator()); - for (InputDevice.MotionRange range : ranges) { - if (((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) && axisStrsSet.add(range.getAxis())) { - if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { - joystick.hats.add(range); - } else { - joystick.axes.add(range); - } - } + List ranges = joystickDevice.getMotionRanges(); + Collections.sort(ranges, new RangeComparator()); + for (InputDevice.MotionRange range : ranges) { + if (((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) && axisStrsSet.add(range.getAxis())) { + if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { + joystick.hats.add(range); + } else { + joystick.axes.add(range); } - - boolean can_rumble = false; - boolean has_rgb_led = false; - boolean has_accelerometer = false; - boolean has_gyroscope = false; - if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) { - VibratorManager vibratorManager = joystickDevice.getVibratorManager(); - int[] vibrators = vibratorManager.getVibratorIds(); - if (vibrators.length > 0) { - can_rumble = true; - } - LightsManager lightsManager = joystickDevice.getLightsManager(); - List lights = lightsManager.getLights(); - for (Light light : lights) { - if (light.hasRgbControl()) { - joystick.lights.add(light); - } - } - if (!joystick.lights.isEmpty()) { - joystick.lightsSession = lightsManager.openSession(); - has_rgb_led = true; - } - SensorManager sensorManager = joystickDevice.getSensorManager(); - if (sensorManager != null) { - joystick.sensorManager = sensorManager; - joystick.sensorListener = new SDLJoySensorListener(joystick.device_id); - joystick.accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - if (joystick.accelerometerSensor != null) { - has_accelerometer = true; - } - joystick.gyroscopeSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); - if (joystick.gyroscopeSensor != null) { - has_gyroscope = true; - } - } - } - - mJoysticks.add(joystick); - SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, - getVendorId(joystickDevice), getProductId(joystickDevice), - getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble, has_rgb_led, - has_accelerometer, has_gyroscope); } + } + if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) { + LightsManager lightsManager = joystickDevice.getLightsManager(); + List lights = lightsManager.getLights(); + for (Light light : lights) { + if (light.hasRgbControl()) { + joystick.lights.add(light); + } + } + if (!joystick.lights.isEmpty()) { + joystick.lightsSession = lightsManager.openSession(); + } + SensorManager sensorManager = joystickDevice.getSensorManager(); + if (sensorManager != null) { + joystick.sensorManager = sensorManager; + joystick.sensorListener = new SDLJoySensorListener(joystick.device_id); + joystick.accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + joystick.gyroscopeSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); + } + } + + mJoysticks.add(joystick); + } + + boolean can_rumble = false; + boolean has_rgb_led = false; + boolean has_accelerometer = false; + boolean has_gyroscope = false; + if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) { + VibratorManager vibratorManager = joystickDevice.getVibratorManager(); + int[] vibrators = vibratorManager.getVibratorIds(); + if (vibrators.length > 0) { + can_rumble = true; + } + if (!joystick.lights.isEmpty()) { + has_rgb_led = true; + } + if (joystick.accelerometerSensor != null) { + has_accelerometer = true; + } + if (joystick.gyroscopeSensor != null) { + has_gyroscope = true; + } + } + + SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, + getVendorId(joystickDevice), getProductId(joystickDevice), + getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble, has_rgb_led, + has_accelerometer, has_gyroscope); } void deviceRemoved(int device_id) { diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c index fc3368ed78..a3f105612f 100644 --- a/src/joystick/android/SDL_sysjoystick.c +++ b/src/joystick/android/SDL_sysjoystick.c @@ -389,6 +389,13 @@ void Android_AddJoystick(int device_id, const char *name, const char *desc, int SDL_GUID guid; int i; + // Java might notify us about joysticks being added before joysticks have + // been initialized. That's fine, we'll get called again with the full set + // in Android_JNI_DetectDevices() + if (!SDL_JoysticksInitialized()) { + return; + } + SDL_LockJoysticks(); if (!SDL_GetHintBoolean(SDL_HINT_TV_REMOTE_AS_JOYSTICK, true)) { @@ -726,10 +733,6 @@ static void ANDROID_JoystickClose(SDL_Joystick *joystick) static void ANDROID_JoystickQuit(void) { -/* We don't have any way to scan for joysticks at init, so don't wipe the list - * of joysticks here in case this is a reinit. - */ -#if 0 SDL_joylist_item *item = NULL; SDL_joylist_item *next = NULL; @@ -742,7 +745,6 @@ static void ANDROID_JoystickQuit(void) SDL_joylist = SDL_joylist_tail = NULL; numjoysticks = 0; -#endif // 0 } static bool ANDROID_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)