Commit 337cea44110d1223b12a7509613f03caa56d3045

Sam Lantinga 2018-10-08T12:49:30

Fixed life-cycle issues with two activities sharing HIDDeviceManager

diff --git a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
index ba30d0a..761ecca 100644
--- a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
+++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
@@ -24,7 +24,28 @@ public class HIDDeviceManager {
     private static final String TAG = "hidapi";
     private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
 
-    protected Context mContext;
+    private static HIDDeviceManager sManager;
+    private static int sManagerRefCount = 0;
+
+    public static HIDDeviceManager acquire(Context context) {
+        if (sManagerRefCount == 0) {
+            sManager = new HIDDeviceManager(context);
+        }
+        ++sManagerRefCount;
+        return sManager;
+    }
+
+    public static void release(HIDDeviceManager manager) {
+        if (manager == sManager) {
+            --sManagerRefCount;
+            if (sManagerRefCount == 0) {
+                sManager.close();
+                sManager = null;
+            }
+        }
+    }
+
+    private Context mContext;
     private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
     private HashMap<UsbDevice, HIDDeviceUSB> mUSBDevices = new HashMap<UsbDevice, HIDDeviceUSB>();
     private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
@@ -77,7 +98,7 @@ public class HIDDeviceManager {
         }
     };
 
-    public HIDDeviceManager(Context context) {
+    private HIDDeviceManager(Context context) {
         mContext = context;
 
         // Make sure we have the HIDAPI library loaded with the native functions
@@ -88,7 +109,7 @@ public class HIDDeviceManager {
             return;
         }
 
-        HIDDeviceRegisterCallback(this);
+        HIDDeviceRegisterCallback();
 
         mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
         mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
@@ -125,7 +146,7 @@ public class HIDDeviceManager {
         return result;
     }
 
-    protected void initializeUSB() {
+    private void initializeUSB() {
         mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
 
         /*
@@ -187,7 +208,7 @@ public class HIDDeviceManager {
         return mUsbManager;
     }
 
-    protected void shutdownUSB() {
+    private void shutdownUSB() {
         try {
             mContext.unregisterReceiver(mUsbBroadcast);
         } catch (Exception e) {
@@ -195,7 +216,7 @@ public class HIDDeviceManager {
         }
     }
 
-    protected boolean isHIDDeviceUSB(UsbDevice usbDevice) {
+    private boolean isHIDDeviceUSB(UsbDevice usbDevice) {
         for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); ++interface_number) {
             if (isHIDDeviceInterface(usbDevice, interface_number)) {
                 return true;
@@ -204,7 +225,7 @@ public class HIDDeviceManager {
         return false;
     }
 
-    protected boolean isHIDDeviceInterface(UsbDevice usbDevice, int interface_number) {
+    private boolean isHIDDeviceInterface(UsbDevice usbDevice, int interface_number) {
         UsbInterface usbInterface = usbDevice.getInterface(interface_number);
         if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
             return true;
@@ -217,7 +238,7 @@ public class HIDDeviceManager {
         return false;
     }
 
-    protected boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
+    private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
         final int XB360_IFACE_SUBCLASS = 93;
         final int XB360_IFACE_PROTOCOL = 1; // Wired only
         final int[] SUPPORTED_VENDORS = {
@@ -256,7 +277,7 @@ public class HIDDeviceManager {
         return false;
     }
 
-    protected boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
+    private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
         final int XB1_IFACE_SUBCLASS = 71;
         final int XB1_IFACE_PROTOCOL = 208;
         final int[] SUPPORTED_VENDORS = {
@@ -281,13 +302,13 @@ public class HIDDeviceManager {
         return false;
     }
 
-    protected void handleUsbDeviceAttached(UsbDevice usbDevice) {
+    private void handleUsbDeviceAttached(UsbDevice usbDevice) {
         if (isHIDDeviceUSB(usbDevice)) {
             connectHIDDeviceUSB(usbDevice);
         }
     }
 
-    protected void handleUsbDeviceDetached(UsbDevice usbDevice) {
+    private void handleUsbDeviceDetached(UsbDevice usbDevice) {
         HIDDeviceUSB device = mUSBDevices.get(usbDevice);
         if (device == null)
             return;
@@ -299,7 +320,7 @@ public class HIDDeviceManager {
         HIDDeviceDisconnected(id);
     }
 
-    protected void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
+    private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
         HIDDeviceUSB device = mUSBDevices.get(usbDevice);
         if (device == null)
             return;
@@ -311,7 +332,7 @@ public class HIDDeviceManager {
         HIDDeviceOpenResult(device.getId(), opened);
     }
 
-    protected void connectHIDDeviceUSB(UsbDevice usbDevice) {
+    private void connectHIDDeviceUSB(UsbDevice usbDevice) {
         synchronized (this) {
             for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); interface_number++) {
                 if (isHIDDeviceInterface(usbDevice, interface_number)) {
@@ -326,7 +347,7 @@ public class HIDDeviceManager {
         }
     }
 
-    protected void initializeBluetooth() {
+    private void initializeBluetooth() {
         Log.d(TAG, "Initializing Bluetooth");
 
         if (mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
@@ -377,7 +398,7 @@ public class HIDDeviceManager {
         }
     }
 
-    protected void shutdownBluetooth() {
+    private void shutdownBluetooth() {
         try {
             mContext.unregisterReceiver(mBluetoothBroadcast);
         } catch (Exception e) {
@@ -476,7 +497,7 @@ public class HIDDeviceManager {
         return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
     }
 
-    public void close() {
+    private void close() {
         shutdownUSB();
         shutdownBluetooth();
         synchronized (this) {
@@ -623,7 +644,7 @@ public class HIDDeviceManager {
     /////////////// Native methods
     //////////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private native void HIDDeviceRegisterCallback(Object callbackHandler);
+    private native void HIDDeviceRegisterCallback();
     private native void HIDDeviceReleaseCallback();
 
     native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number);
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
index 67cb3a3..c5901ab 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -250,7 +250,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
             mClipboardHandler = new SDLClipboardHandler_Old();
         }
 
-        mHIDDeviceManager = new HIDDeviceManager(this);
+        mHIDDeviceManager = HIDDeviceManager.acquire(this);
 
         // Set up the surface
         mSurface = new SDLSurface(getApplication());
@@ -380,7 +380,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
         Log.v(TAG, "onDestroy()");
 
         if (mHIDDeviceManager != null) {
-            mHIDDeviceManager.close();
+            HIDDeviceManager.release(mHIDDeviceManager);
             mHIDDeviceManager = null;
         }
 
diff --git a/src/hidapi/android/hid.cpp b/src/hidapi/android/hid.cpp
index b9d95e9..403a77d 100644
--- a/src/hidapi/android/hid.cpp
+++ b/src/hidapi/android/hid.cpp
@@ -696,7 +696,7 @@ static void ThreadDestroyed(void* value)
 
 
 extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz, jobject callbackHandler);
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz);
 
 extern "C"
 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz);
@@ -721,7 +721,7 @@ JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)
 
 
 extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz, jobject callbackHandler)
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz )
 {
 	LOGV( "HIDDeviceRegisterCallback()");
 
@@ -735,11 +735,19 @@ JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallba
 		__android_log_print(ANDROID_LOG_ERROR, TAG, "Error initializing pthread key");
 	}
 
-	g_HIDDeviceManagerCallbackHandler = env->NewGlobalRef( callbackHandler );
-	jclass objClass = env->GetObjectClass( callbackHandler );
+	if ( g_HIDDeviceManagerCallbackHandler != NULL )
+	{
+		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
+		g_HIDDeviceManagerCallbackClass = NULL;
+		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
+		g_HIDDeviceManagerCallbackHandler = NULL;
+	}
+
+	g_HIDDeviceManagerCallbackHandler = env->NewGlobalRef( thiz );
+	jclass objClass = env->GetObjectClass( thiz );
 	if ( objClass )
 	{
-		g_HIDDeviceManagerCallbackClass = reinterpret_cast< jclass >( env->NewGlobalRef(objClass) );
+		g_HIDDeviceManagerCallbackClass = reinterpret_cast< jclass >( env->NewGlobalRef( objClass ) );
 		g_midHIDDeviceManagerOpen = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "openDevice", "(I)Z" );
 		if ( !g_midHIDDeviceManagerOpen )
 		{
@@ -773,8 +781,13 @@ extern "C"
 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz)
 {
 	LOGV("HIDDeviceReleaseCallback");
-	env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
-	env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
+	if ( env->IsSameObject( thiz, g_HIDDeviceManagerCallbackHandler ) )
+	{
+		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
+		g_HIDDeviceManagerCallbackClass = NULL;
+		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
+		g_HIDDeviceManagerCallbackHandler = NULL;
+	}
 }
 
 extern "C"