diff options
author | Sven Gothel <[email protected]> | 2020-05-12 06:39:36 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2020-05-12 06:39:36 +0200 |
commit | d198176d2fbe5c88cf81ffb647abced753af52c2 (patch) | |
tree | 6d41698d308520adf7fd743e718724343e48292c /java/direct_bt | |
parent | 604e03053942521c1e104b0c473864ff4139b939 (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.java | 14 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTDevice.java | 4 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTManager.java | 88 |
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(); } |