From 47c8dcc968b29db7164e3ee5b5a8d8874ef7d26b Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 20 May 2026 14:57:36 -0700
Subject: [PATCH] android: handle sensor registration synchronized in one place
---
.../org/libsdl/app/SDLControllerManager.java | 16 +++-------
.../java/org/libsdl/app/SDLSensorManager.java | 32 +++++++++++++++++++
.../main/java/org/libsdl/app/SDLSurface.java | 6 ++--
3 files changed, 40 insertions(+), 14 deletions(-)
create mode 100644 android-project/app/src/main/java/org/libsdl/app/SDLSensorManager.java
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 bce15e9de228a..a41467c61d06d 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
@@ -91,13 +91,7 @@ static void joystickSetLED(int device_id, int red, int green, int blue) {
* This method is called by SDL using JNI.
*/
static void joystickSetSensorsEnabled(int device_id, boolean enabled) {
- // Run this on the UI thread so we don't race with enableSensor() in SDLSurface.java
- SDL.getContext().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mJoystickHandler.setSensorsEnabled(device_id, enabled);
- }
- });
+ mJoystickHandler.setSensorsEnabled(device_id, enabled);
}
/**
@@ -558,17 +552,17 @@ void setSensorsEnabled(int device_id, boolean enabled) {
}
if (enabled) {
if (joystick.accelerometerSensor != null) {
- joystick.sensorManager.registerListener(joystick.sensorListener, joystick.accelerometerSensor, SensorManager.SENSOR_DELAY_GAME, null);
+ SDLSensorManager.registerListener(joystick.sensorManager, joystick.sensorListener, joystick.accelerometerSensor, SensorManager.SENSOR_DELAY_GAME);
}
if (joystick.gyroscopeSensor != null) {
- joystick.sensorManager.registerListener(joystick.sensorListener, joystick.gyroscopeSensor, SensorManager.SENSOR_DELAY_GAME, null);
+ SDLSensorManager.registerListener(joystick.sensorManager, joystick.sensorListener, joystick.gyroscopeSensor, SensorManager.SENSOR_DELAY_GAME);
}
} else {
if (joystick.accelerometerSensor != null) {
- joystick.sensorManager.unregisterListener(joystick.sensorListener, joystick.accelerometerSensor);
+ SDLSensorManager.unregisterListener(joystick.sensorManager, joystick.sensorListener, joystick.accelerometerSensor);
}
if (joystick.gyroscopeSensor != null) {
- joystick.sensorManager.unregisterListener(joystick.sensorListener, joystick.gyroscopeSensor);
+ SDLSensorManager.unregisterListener(joystick.sensorManager, joystick.sensorListener, joystick.gyroscopeSensor);
}
}
}
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLSensorManager.java b/android-project/app/src/main/java/org/libsdl/app/SDLSensorManager.java
new file mode 100644
index 0000000000000..586e3fab6e8fd
--- /dev/null
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLSensorManager.java
@@ -0,0 +1,32 @@
+package org.libsdl.app;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+
+// This class coordinates synchronized access to sensor manager registration
+//
+// This prevents a java.util.ConcurrentModificationException exception on
+// Android 16, specifically on the Samsung Tab S9 Ultra.
+
+class SDLSensorManager
+{
+ static private SDLSensorManager mManager = new SDLSensorManager();
+
+ public static void registerListener(SensorManager manager, SensorEventListener listener, Sensor sensor, int samplingPeriodUs) {
+ mManager.RegisterListener(manager, listener, sensor, samplingPeriodUs);
+ }
+
+ public static void unregisterListener(SensorManager manager, SensorEventListener listener, Sensor sensor) {
+ mManager.UnregisterListener(manager, listener, sensor);
+ }
+
+ private synchronized void RegisterListener(SensorManager manager, SensorEventListener listener, Sensor sensor, int samplingPeriodUs) {
+ manager.registerListener(listener, sensor, samplingPeriodUs, null);
+ }
+
+ private synchronized void UnregisterListener(SensorManager manager, SensorEventListener listener, Sensor sensor) {
+ manager.unregisterListener(listener, sensor);
+ }
+}
+
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java b/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java
index dedc00b78a8c0..196cf04ec2fb5 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java
@@ -328,11 +328,11 @@ public boolean onTouch(View v, MotionEvent event) {
protected void enableSensor(int sensortype, boolean enabled) {
// TODO: This uses getDefaultSensor - what if we have >1 accels?
if (enabled) {
- mSensorManager.registerListener(this,
+ SDLSensorManager.registerListener(mSensorManager, this,
mSensorManager.getDefaultSensor(sensortype),
- SensorManager.SENSOR_DELAY_GAME, null);
+ SensorManager.SENSOR_DELAY_GAME);
} else {
- mSensorManager.unregisterListener(this,
+ SDLSensorManager.unregisterListener(mSensorManager, this,
mSensorManager.getDefaultSensor(sensortype));
}
}