aboutsummaryrefslogtreecommitdiffstats
path: root/java/direct_bt
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2020-05-12 06:39:36 +0200
committerSven Gothel <[email protected]>2020-05-12 06:39:36 +0200
commitd198176d2fbe5c88cf81ffb647abced753af52c2 (patch)
tree6d41698d308520adf7fd743e718724343e48292c /java/direct_bt
parent604e03053942521c1e104b0c473864ff4139b939 (diff)
Fixing multiple Java/C++ Lifecycle Issues (DBTDevice, add ShutdownHook, ..)
DBTDevice - don't native delete @ JNI deleteImpl, adapter holds share_ptr ownership - only create its java object 1st time @ deviceFound callback of JNI DBTAdapter DBTAdapter::mgmtEvDeviceFoundCB - in case of !discoveredDeviceList but sharedDeviceList, the device shall be updated first, then deviceFound callbacks issued, allowing listener to act and register, then deviceUpdate callbacks issued, allowing data update on existing actors DBTManager.java: - Add ShutdownHook calling custom hooks and shutdown() - shutdown() in depth shutdown: -- iterated through all adapter issueing adapter.close() -- Adapter.close() iterates through all discoveredDevices issuing close() DBTDevice.java: - adds adapter JNI proxy to removeStatusListener(..), same 'to be resolved' issue as with addStatusListener(..).
Diffstat (limited to 'java/direct_bt')
-rw-r--r--java/direct_bt/tinyb/DBTAdapter.java14
-rw-r--r--java/direct_bt/tinyb/DBTDevice.java4
-rw-r--r--java/direct_bt/tinyb/DBTManager.java88
3 files changed, 103 insertions, 3 deletions
diff --git a/java/direct_bt/tinyb/DBTAdapter.java b/java/direct_bt/tinyb/DBTAdapter.java
index 71ab8394..77404d36 100644
--- a/java/direct_bt/tinyb/DBTAdapter.java
+++ b/java/direct_bt/tinyb/DBTAdapter.java
@@ -65,7 +65,7 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter
private boolean isPairable = false;
private boolean isOpen = false;
- private List<BluetoothDevice> discoveredDevices = new ArrayList<BluetoothDevice>();
+ private final List<BluetoothDevice> discoveredDevices = new ArrayList<BluetoothDevice>();
/* pp */ DBTAdapter(final long nativeInstance, final String address, final String name)
{
@@ -78,11 +78,21 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter
@Override
public synchronized void close() {
stopDiscovery();
+
+ for(final Iterator<BluetoothDevice> id = discoveredDevices.iterator(); id.hasNext(); ) {
+ final BluetoothDevice d = id.next();
+ d.close();
+ }
+
removeStatusListener(this.statusListener);
disableDiscoverableNotifications();
disableDiscoveringNotifications();
disablePairableNotifications();
disablePoweredNotifications();
+
+ removeDevicesImpl();
+ discoveredDevices.clear();
+
isOpen = false;
super.close();
}
@@ -285,7 +295,7 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter
private int removeDiscoveredDevices() {
synchronized(discoveredDevicesLock) {
final int n = discoveredDevices.size();
- discoveredDevices = new ArrayList<BluetoothDevice>();
+ discoveredDevices.clear();
return n;
}
}
diff --git a/java/direct_bt/tinyb/DBTDevice.java b/java/direct_bt/tinyb/DBTDevice.java
index 9fc0a806..ac4e50e8 100644
--- a/java/direct_bt/tinyb/DBTDevice.java
+++ b/java/direct_bt/tinyb/DBTDevice.java
@@ -124,6 +124,7 @@ public class DBTDevice extends DBTObject implements BluetoothDevice
* </p>
*/
private native boolean addStatusListener(final AdapterStatusListener l);
+ private native boolean removeStatusListener(final AdapterStatusListener l);
@Override
public synchronized void close() {
@@ -139,7 +140,8 @@ public class DBTDevice extends DBTObject implements BluetoothDevice
disableServiceDataNotifications();
disableTrustedNotifications();
- this.adapter.removeStatusListener(statusListener);
+ // this.adapter.removeStatusListener(statusListener);
+ removeStatusListener(statusListener);
super.close();
}
diff --git a/java/direct_bt/tinyb/DBTManager.java b/java/direct_bt/tinyb/DBTManager.java
index 4438c66f..e458403d 100644
--- a/java/direct_bt/tinyb/DBTManager.java
+++ b/java/direct_bt/tinyb/DBTManager.java
@@ -25,7 +25,10 @@
package direct_bt.tinyb;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import org.tinyb.BluetoothAdapter;
@@ -38,6 +41,87 @@ import org.tinyb.BluetoothType;
public class DBTManager implements BluetoothManager
{
+ protected static final boolean DEBUG = true;
+
+ private static volatile boolean isJVMShuttingDown = false;
+ private static final List<Runnable> userShutdownHooks = new ArrayList<Runnable>();
+
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ Runtime.getRuntime().addShutdownHook(
+ new Thread(null, new Runnable() {
+ @Override
+ public void run() {
+ DBTManager.shutdownImpl(true);
+ } }, "DBTManager_ShutdownHook" ) ) ;
+ return null;
+ } } ) ;
+ }
+
+ private static synchronized void shutdownImpl(final boolean _isJVMShuttingDown) {
+ isJVMShuttingDown = _isJVMShuttingDown;
+ if(DEBUG) {
+ System.err.println("DBTManager.shutdown() START: JVM Shutdown "+isJVMShuttingDown+", on thread "+Thread.currentThread().getName());
+ }
+ synchronized(userShutdownHooks) {
+ final int cshCount = userShutdownHooks.size();
+ for(int i=0; i < cshCount; i++) {
+ try {
+ if( DEBUG ) {
+ System.err.println("DBTManager.shutdown - userShutdownHook #"+(i+1)+"/"+cshCount);
+ }
+ userShutdownHooks.get(i).run();
+ } catch(final Throwable t) {
+ System.err.println("DBTManager.shutdown: Caught "+t.getClass().getName()+" during userShutdownHook #"+(i+1)+"/"+cshCount);
+ if( DEBUG ) {
+ t.printStackTrace();
+ }
+ }
+ }
+ userShutdownHooks.clear();
+ }
+ if(DEBUG) {
+ System.err.println("DBTManager.shutdown(): Post userShutdownHook");
+ }
+
+ try {
+ final BluetoothManager mgmt = getBluetoothManager();
+ mgmt.shutdown();
+ } catch(final Throwable t) {
+ System.err.println("DBTManager.shutdown: Caught "+t.getClass().getName()+" during DBTManager.shutdown()");
+ if( DEBUG ) {
+ t.printStackTrace();
+ }
+ }
+
+ if(DEBUG) {
+ System.err.println(Thread.currentThread().getName()+" - DBTManager.shutdown() END JVM Shutdown "+isJVMShuttingDown);
+ }
+ }
+
+ /** Returns true if the JVM is shutting down, otherwise false. */
+ public static final boolean isJVMShuttingDown() { return isJVMShuttingDown; }
+
+ /**
+ * Add a shutdown hook to be performed at JVM shutdown before shutting down DBTManager instance.
+ *
+ * @param head if true add runnable at the start, otherwise at the end
+ * @param runnable runnable to be added.
+ */
+ public static void addShutdownHook(final boolean head, final Runnable runnable) {
+ synchronized( userShutdownHooks ) {
+ if( !userShutdownHooks.contains( runnable ) ) {
+ if( head ) {
+ userShutdownHooks.add(0, runnable);
+ } else {
+ userShutdownHooks.add( runnable );
+ }
+ }
+ }
+ }
+
private long nativeInstance;
private static DBTManager inst;
private final List<BluetoothAdapter> adapters = new ArrayList<BluetoothAdapter>();
@@ -147,6 +231,10 @@ public class DBTManager implements BluetoothManager
@Override
public void shutdown() {
+ for(final Iterator<BluetoothAdapter> ia= adapters.iterator(); ia.hasNext(); ) {
+ final BluetoothAdapter a = ia.next();
+ a.close();
+ }
adapters.clear();
deleteImpl();
}