diff options
author | Sven Gothel <[email protected]> | 2020-04-20 07:21:35 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2020-04-20 07:21:35 +0200 |
commit | 6fdcdb13db5e13f11c44f0f9bba088252d9ffa18 (patch) | |
tree | 8c9c321dd2064eb53438544f0faba91306629c8c | |
parent | 6505f49b63db200d817817d1d01a14005a3d732c (diff) |
Initial working Java binding for the direct_bt C++ module
Example ScannerTinyB01 demonstrates efficient scanning devices using the new BluetoothDeviceDiscoveryListener interface.
The C++ HCIObject extends JavaUplink, which handles the java object references
via 'std::shared_ptr<JavaAnonObj>', where JavaAnonObj relies on later polymorph specialization.
JavaAnonObj gets derived in the java/jni of direct_bt, where the JNI header + libraries are available.
+++
The java inplementing NativeDownlink implementations
store the nativeInstance to the C++ instances
as well as handle their java references within the native instances.
The C++ JavaUplink and Java NativeDownlink interfaces are complete
the cross referencing java <-> native.
+++
Native libraries are now split into pairs:
- tinyb + javatinyb
- direct_bt + javadirect_bt
TODO: BluetoothFactory must chose the proper bundle!
+++
The Java Adapter received a BluetoothDeviceDiscoveryListener hook,
matching C++ Adapter's HCIDeviceDiscoveryListener.
Since the Java Adapter implements its own discovery thread,
using the BluetoothDeviceDiscoveryListener is more efficient then polling
over a list of Devices fetched.
++++
TODO: Align Java and C++ class names, foremost in the C++ direct_bt space.
TODO: Bind the whole C++ GATT functionality
More testing.
75 files changed, 2286 insertions, 516 deletions
@@ -79,7 +79,7 @@ <option id="gnu.cpp.compiler.option.pthread.956658404" name="Support for pthread (-pthread)" superClass="gnu.cpp.compiler.option.pthread" useByScannerDiscovery="false" value="true" valueType="boolean"/> - <option id="gnu.cpp.compiler.option.warnings.extrawarn.602645664" superClass="gnu.cpp.compiler.option.warnings.extrawarn" useByScannerDiscovery="false" value="true" valueType="boolean"/> + <option id="gnu.cpp.compiler.option.warnings.extrawarn.602645664" name="Extra warnings (-Wextra)" superClass="gnu.cpp.compiler.option.warnings.extrawarn" useByScannerDiscovery="false" value="true" valueType="boolean"/> <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1946997217" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/> @@ -109,7 +109,7 @@ <option id="gnu.c.compiler.option.pthread.858610172" name="Support for pthread (-pthread)" superClass="gnu.c.compiler.option.pthread" useByScannerDiscovery="false" value="true" valueType="boolean"/> - <option id="gnu.c.compiler.option.warnings.extrawarn.1874778119" superClass="gnu.c.compiler.option.warnings.extrawarn" useByScannerDiscovery="false" value="true" valueType="boolean"/> + <option id="gnu.c.compiler.option.warnings.extrawarn.1874778119" name="Extra warnings (-Wextra)" superClass="gnu.c.compiler.option.warnings.extrawarn" useByScannerDiscovery="false" value="true" valueType="boolean"/> <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1736735651" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/> @@ -175,6 +175,8 @@ <entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="include/cppunit"/> + <entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="java/jni"/> + <entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="src/direct_bt"/> <entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="src/ieee11073"/> diff --git a/api/direct_bt/BasicTypes.hpp b/api/direct_bt/BasicTypes.hpp index 0da36a95..a626718d 100644 --- a/api/direct_bt/BasicTypes.hpp +++ b/api/direct_bt/BasicTypes.hpp @@ -59,6 +59,11 @@ namespace direct_bt { virtual ~RuntimeException() noexcept { } + RuntimeException(const RuntimeException &o) = default; + RuntimeException(RuntimeException &&o) = default; + RuntimeException& operator=(const RuntimeException &o) = default; + RuntimeException& operator=(RuntimeException &&o) = default; + virtual const char* what() const noexcept override; }; @@ -80,15 +85,9 @@ namespace direct_bt { : RuntimeException("IllegalArgumentException", m, file, line) {} }; - class IllegalStateException : public RuntimeException { - public: - IllegalStateException(std::string const m, const char* file, int line) noexcept - : RuntimeException("IllegalStateException", m, file, line) {} - }; - class InvalidStateException : public RuntimeException { public: - InvalidStateException(std::string const m, const char* file, int line) noexcept + InvalidStateException(std::string const m, const char* file, int line) noexcept : RuntimeException("InvalidStateException", m, file, line) {} }; @@ -104,6 +103,15 @@ namespace direct_bt { : RuntimeException("IndexOutOfBoundsException", "Index "+std::to_string(index)+", count "+std::to_string(count)+", data length "+std::to_string(length), file, line) {} }; + class BluetoothException : public RuntimeException { + public: + BluetoothException(std::string const m, const char* file, int line) noexcept + : RuntimeException("BluetoothException", m, file, line) {} + + BluetoothException(const char *m, const char* file, int line) noexcept + : RuntimeException("BluetoothException", m, file, line) {} + }; + /** // ************************************************* // ************************************************* @@ -355,6 +363,7 @@ namespace direct_bt { std::string uint8HexString(const uint8_t v, const bool leading0X); std::string uint16HexString(const uint16_t v, const bool leading0X); std::string uint32HexString(const uint32_t v, const bool leading0X); + std::string uint64HexString(const uint64_t v, const bool leading0X); /** * If lsbFirst is true, orders LSB left -> MSB right, usual for byte streams. diff --git a/api/direct_bt/HCITypes.hpp b/api/direct_bt/HCITypes.hpp index e7cb0384..17368aed 100644 --- a/api/direct_bt/HCITypes.hpp +++ b/api/direct_bt/HCITypes.hpp @@ -31,6 +31,7 @@ #include <memory> #include <cstdint> #include <vector> +#include <functional> #include <mutex> #include <atomic> @@ -40,6 +41,7 @@ #include "BTTypes.hpp" #include "HCIComm.hpp" #include "MgmtComm.hpp" +#include "JavaAccess.hpp" #define JAVA_MAIN_PACKAGE "org/tinyb" #define JAVA_HCI_PACKAGE "tinyb/hci" @@ -104,7 +106,7 @@ namespace direct_bt { // ************************************************* // ************************************************* - class HCIObject + class HCIObject : public JavaUplink { protected: std::mutex lk; @@ -137,8 +139,11 @@ namespace direct_bt { public: virtual void deviceAdded(HCIAdapter const &a, std::shared_ptr<HCIDevice> device) = 0; virtual void deviceUpdated(HCIAdapter const &a, std::shared_ptr<HCIDevice> device) = 0; + virtual void deviceRemoved(HCIAdapter const &a, std::shared_ptr<HCIDevice> device) = 0; virtual ~HCIDeviceDiscoveryListener() {} }; + /** Alternative method to DeviceDiscoveryListener to set a callback */ + typedef std::function<void(HCIAdapter const &a, std::shared_ptr<HCIDevice> device)> DeviceDiscoveryCallback; class HCIDevice : public HCIObject { @@ -169,6 +174,13 @@ namespace direct_bt { ~HCIDevice(); + std::string get_java_class() const override { + return java_class(); + } + static std::string java_class() { + return std::string(JAVA_DBT_PACKAGE "Device"); + } + /** Returns the managing adapter */ HCIAdapter const & getAdapter() const { return adapter; } @@ -246,7 +258,7 @@ namespace direct_bt { bool validateDevInfo(); friend bool HCISession::close(); - void sessionClosed(HCISession& s); + void sessionClosing(HCISession& s); friend std::shared_ptr<HCIDevice> HCIDevice::getSharedInstance() const; int findScannedDeviceIdx(EUI48 const & mac) const; @@ -277,6 +289,13 @@ namespace direct_bt { ~HCIAdapter(); + std::string get_java_class() const override { + return java_class(); + } + static std::string java_class() { + return std::string(JAVA_DBT_PACKAGE "Adapter"); + } + bool hasDevId() const { return 0 <= dev_id; } EUI48 const & getAddress() const { return adapterInfo->mac; } @@ -289,6 +308,11 @@ namespace direct_bt { */ std::shared_ptr<HCISession> open(); + /** + * Returns the {@link #open()} session or {@code nullptr} if closed. + */ + std::shared_ptr<HCISession> getOpenSession() { return session; } + // device discovery aka device scanning /** @@ -360,8 +384,8 @@ namespace direct_bt { /** Returns discovered devices from a discovery */ std::vector<std::shared_ptr<HCIDevice>> getDiscoveredDevices() { return discoveredDevices; } - /** Discards all discovered devices. */ - void removeDiscoveredDevices(); + /** Discards all discovered devices. Returns number of removed discovered devices. */ + int removeDiscoveredDevices(); /** Returns index >= 0 if found, otherwise -1 */ int findDiscoveredDeviceIdx(EUI48 const & mac) const; diff --git a/api/direct_bt/JavaAccess.hpp b/api/direct_bt/JavaAccess.hpp new file mode 100644 index 00000000..86c7251e --- /dev/null +++ b/api/direct_bt/JavaAccess.hpp @@ -0,0 +1,63 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef JAVA_ACCESS_HPP_ +#define JAVA_ACCESS_HPP_ + +#include <string> +#include <memory> + +namespace direct_bt { + + #define JAVA_DBT_PACKAGE "direct_bt/tinyb/" + + class JavaAnonObj { + public: + virtual ~JavaAnonObj() { } + virtual std::string toString() const { return "JavaAnonObj[???]"; } + }; + + class JavaUplink { + private: + std::shared_ptr<JavaAnonObj> javaObjectRef; + + public: + virtual std::string toString() const = 0; + virtual std::string get_java_class() const = 0; + + std::string javaObjectToString() const { return nullptr == javaObjectRef ? "JavaAnonObj[null]" : javaObjectRef->toString(); } + + std::shared_ptr<JavaAnonObj> getJavaObject() { return javaObjectRef; } + void setJavaObject(std::shared_ptr<JavaAnonObj> objRef) { javaObjectRef = objRef; } + + virtual ~JavaUplink() { + javaObjectRef = nullptr; + } + }; + +} /* namespace direct_bt */ + + +#endif /* JAVA_ACCESS_HPP_ */ diff --git a/api/direct_bt/MgmtComm.hpp b/api/direct_bt/MgmtComm.hpp index d7053273..a022a8d9 100644 --- a/api/direct_bt/MgmtComm.hpp +++ b/api/direct_bt/MgmtComm.hpp @@ -35,6 +35,7 @@ #include "BTIoctl.hpp" #include "OctetTypes.hpp" #include "HCIComm.hpp" +#include "JavaAccess.hpp" namespace direct_bt { @@ -494,7 +495,7 @@ namespace direct_bt { /** * A thread safe singleton handler of the Linux Kernel's BlueZ manager control channel. */ - class MgmtHandler { + class MgmtHandler : public JavaUplink { private: std::recursive_mutex mtx; const int ibuffer_size = 512; @@ -535,6 +536,13 @@ namespace direct_bt { } ~MgmtHandler() { close(); } + std::string get_java_class() const override { + return java_class(); + } + static std::string java_class() { + return std::string(JAVA_DBT_PACKAGE "Manager"); + } + /** Returns true if this mgmt instance is open and hence valid, otherwise false */ bool isOpen() const { return comm.isOpen(); @@ -551,6 +559,8 @@ namespace direct_bt { int getDefaultAdapterIdx() const { return adapters.size() > 0 ? 0 : -1; } int findAdapterIdx(const EUI48 &mac) const; std::shared_ptr<const AdapterInfo> getAdapter(const int idx) const; + + std::string toString() const override { return "MgmtHandler["+std::to_string(adapters.size())+" adapter, "+javaObjectToString()+"]"; } }; } // namespace direct_bt diff --git a/src/direct_bt/dbt_debug.hpp b/api/direct_bt/dbt_debug.hpp index f294b0ef..f294b0ef 100644 --- a/src/direct_bt/dbt_debug.hpp +++ b/api/direct_bt/dbt_debug.hpp diff --git a/examples/direct_bt_scanner/dbt_scanner.cpp b/examples/direct_bt_scanner/dbt_scanner.cpp index dc245064..055aa7e1 100644 --- a/examples/direct_bt_scanner/dbt_scanner.cpp +++ b/examples/direct_bt_scanner/dbt_scanner.cpp @@ -47,6 +47,11 @@ class DeviceDiscoveryListener : public direct_bt::HCIDeviceDiscoveryListener { fprintf(stderr, "Status HCIAdapter:\n"); fprintf(stderr, "%s\n", a.toString().c_str()); } + void deviceRemoved(direct_bt::HCIAdapter const &a, std::shared_ptr<direct_bt::HCIDevice> device) override { + fprintf(stderr, "****** REMOVED: %s\n", device->toString().c_str()); + fprintf(stderr, "Status HCIAdapter:\n"); + fprintf(stderr, "%s\n", a.toString().c_str()); + } }; static const uuid16_t _TEMPERATURE_MEASUREMENT(GattCharacteristicType::TEMPERATURE_MEASUREMENT); diff --git a/examples/java/CMakeLists.txt b/examples/java/CMakeLists.txt index a76b80b9..d8945755 100644 --- a/examples/java/CMakeLists.txt +++ b/examples/java/CMakeLists.txt @@ -22,9 +22,16 @@ add_custom_command(TARGET Notification COMMAND cp "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/Notification.dir/Notification.class" "${CMAKE_CURRENT_BINARY_DIR}" ) -add_jar(ScannerTinyB SOURCES ScannerTinyB.java INCLUDE_JARS "${CMAKE_CURRENT_BINARY_DIR}/../../java/tinyb2.jar" ENTRY_POINT Notification) +add_jar(ScannerTinyB00 SOURCES ScannerTinyB00.java INCLUDE_JARS "${CMAKE_CURRENT_BINARY_DIR}/../../java/tinyb2.jar" ENTRY_POINT Notification) -add_custom_command(TARGET ScannerTinyB +add_custom_command(TARGET ScannerTinyB00 POST_BUILD - COMMAND cp "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/ScannerTinyB.dir/ScannerTinyB.class" "${CMAKE_CURRENT_BINARY_DIR}" + COMMAND cp "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/ScannerTinyB00.dir/ScannerTinyB00.class" "${CMAKE_CURRENT_BINARY_DIR}" +) + +add_jar(ScannerTinyB01 SOURCES ScannerTinyB01.java INCLUDE_JARS "${CMAKE_CURRENT_BINARY_DIR}/../../java/tinyb2.jar" ENTRY_POINT Notification) + +add_custom_command(TARGET ScannerTinyB01 + POST_BUILD + COMMAND cp "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/ScannerTinyB01.dir/ScannerTinyB01.class" "${CMAKE_CURRENT_BINARY_DIR}" ) diff --git a/examples/java/ScannerTinyB.java b/examples/java/ScannerTinyB00.java index e3007d54..db9c2b38 100644 --- a/examples/java/ScannerTinyB.java +++ b/examples/java/ScannerTinyB00.java @@ -37,7 +37,7 @@ import org.tinyb.BluetoothGattService; import org.tinyb.BluetoothManager; import org.tinyb.BluetoothNotification; -public class ScannerTinyB { +public class ScannerTinyB00 { static { System.setProperty("org.tinyb.verbose", "true"); } diff --git a/examples/java/ScannerTinyB01.java b/examples/java/ScannerTinyB01.java new file mode 100644 index 00000000..449ab765 --- /dev/null +++ b/examples/java/ScannerTinyB01.java @@ -0,0 +1,325 @@ +/** + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import java.lang.reflect.InvocationTargetException; +import java.util.Iterator; +import java.util.List; + +import org.tinyb.BluetoothAdapter; +import org.tinyb.BluetoothDevice; +import org.tinyb.BluetoothDeviceDiscoveryListener; +import org.tinyb.BluetoothException; +import org.tinyb.BluetoothFactory; +import org.tinyb.BluetoothGattCharacteristic; +import org.tinyb.BluetoothGattDescriptor; +import org.tinyb.BluetoothGattService; +import org.tinyb.BluetoothManager; +import org.tinyb.BluetoothNotification; +import org.tinyb.BluetoothUtils; + +public class ScannerTinyB01 { + static { + System.setProperty("org.tinyb.verbose", "true"); + } + /** 60,000 milliseconds */ + static long TO_DISCOVER = 60000; + + public static void main(final String[] args) throws InterruptedException { + String factoryImplClassName = BluetoothFactory.DirectBTFactoryImplClassName; + int mode = 0; + boolean forever = false; + final String mac; + { + String _mac = null; + for(int i=0; i< args.length; i++) { + final String arg = args[i]; + + if( arg.equals("-mac") ) { + _mac = args[++i]; + } else if( arg.equals("-mode") ) { + mode = Integer.valueOf(args[++i]).intValue(); + } else if( arg.equals("-factory") ) { + factoryImplClassName = args[++i]; + } else if( arg.equals("-forever") ) { + forever = true; + } + } + + if ( null == _mac ) { + System.err.println("Run with '-mac <device_address> [-mode <mode>] [-factory <BluetoothManager-Factory-Implementation-Class>]'"); + System.exit(-1); + } + mac = _mac; + } + + final BluetoothManager manager; + { + BluetoothManager _manager = null; + try { + _manager = BluetoothFactory.getBluetoothManager(factoryImplClassName); + } catch (BluetoothException | NoSuchMethodException | SecurityException + | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | ClassNotFoundException e) { + System.err.println("Unable to instantiate BluetoothManager via factory "+factoryImplClassName); + e.printStackTrace(); + System.exit(-1); + } + manager = _manager; + } + final BluetoothAdapter adapter = manager.getDefaultAdapter(); + final BluetoothDevice[] matchingDiscoveredDeviceBucket = { null }; + + final BluetoothDeviceDiscoveryListener deviceDiscListener = new BluetoothDeviceDiscoveryListener() { + @Override + public void deviceAdded(final BluetoothAdapter adapter, final BluetoothDevice device) { + final boolean matches = device.getAddress().equals(mac); + System.err.println("****** ADDED__: "+device.toString()+" - match "+matches); + System.err.println("Status HCIAdapter:"); + System.err.println(adapter.toString()); + + if( matches ) { + synchronized(matchingDiscoveredDeviceBucket) { + matchingDiscoveredDeviceBucket[0] = device; + matchingDiscoveredDeviceBucket.notifyAll(); + } + } + } + + @Override + public void deviceUpdated(final BluetoothAdapter adapter, final BluetoothDevice device) { + final boolean matches = device.getAddress().equals(mac); + System.err.println("****** UPDATED: "+device.toString()+" - match "+matches); + System.err.println("Status HCIAdapter:"); + System.err.println(adapter.toString()); + } + + @Override + public void deviceRemoved(final BluetoothAdapter adapter, final BluetoothDevice device) { + final boolean matches = device.getAddress().equals(mac); + System.err.println("****** REMOVED: "+device.toString()+" - match "+matches); + System.err.println("Status HCIAdapter:"); + System.err.println(adapter.toString()); + } + }; + adapter.setDeviceDiscoveryListener(deviceDiscListener); + + + do { + final long t0 = BluetoothUtils.getCurrentMilliseconds(); + + adapter.removeDevices(); + final boolean discoveryStarted = adapter.startDiscovery(); + + System.err.println("The discovery started: " + (discoveryStarted ? "true" : "false") + " for mac "+mac+", mode "+mode); + if( !discoveryStarted ) { + break; + } + BluetoothDevice sensor = null; + + if( 0 == mode ) { + synchronized(matchingDiscoveredDeviceBucket) { + while( null == matchingDiscoveredDeviceBucket[0] ) { + matchingDiscoveredDeviceBucket.wait(TO_DISCOVER); + } + sensor = matchingDiscoveredDeviceBucket[0]; + } + } else if( 1 == mode ) { + sensor = adapter.find(null, mac, TO_DISCOVER); + } else { + boolean timeout = false; + while( null == sensor && !timeout ) { + final List<BluetoothDevice> devices = adapter.getDevices(); + for(final Iterator<BluetoothDevice> id = devices.iterator(); id.hasNext() && !timeout; ) { + final BluetoothDevice d = id.next(); + if(d.getAddress().equals(mac)) { + sensor = d; + break; + } + final long tn = BluetoothUtils.getCurrentMilliseconds(); + timeout = ( tn - t0 ) > TO_DISCOVER; + } + } + } + final long t1 = BluetoothUtils.getCurrentMilliseconds(); + if (sensor == null) { + System.err.println("No sensor found within "+(t1-t0)+" ms"); + System.exit(-1); + } + System.err.println("Found device in "+(t1-t0)+" ms: "); + printDevice(sensor); + System.err.println("ScannerTinyB01 01 stopDiscovery: "+adapter); + adapter.stopDiscovery(); + System.err.println("ScannerTinyB01 02 close: "+adapter); + adapter.close(); + System.err.println("ScannerTinyB01 03 ...: "+adapter); + + if(false) { + final BooleanNotification connectedNotification = new BooleanNotification("Connected", t1); + final BooleanNotification servicesResolvedNotification = new BooleanNotification("ServicesResolved", t1); + sensor.enableConnectedNotifications(connectedNotification); + sensor.enableServicesResolvedNotifications(servicesResolvedNotification); + + final long t2; + if ( sensor.connect() ) { + t2 = BluetoothUtils.getCurrentMilliseconds(); + System.err.println("Sensor connected in "+(t2-t1)+" ms"); + System.err.println("Sensor connectedNotification: "+connectedNotification.getValue()); + } else { + t2=0; + System.out.println("Could not connect device."); + System.exit(-1); + } + + synchronized( servicesResolvedNotification ) { + while( !servicesResolvedNotification.getValue() ) { + final long tn = BluetoothUtils.getCurrentMilliseconds(); + if( tn - t2 > 20000 ) { + break; // 20s TO + } + servicesResolvedNotification.wait(); + } + } + final long t3; + if ( servicesResolvedNotification.getValue() ) { + t3 = BluetoothUtils.getCurrentMilliseconds(); + System.err.println("Sensor servicesResolved in "+(t3-t2)+" ms, total "+(t3-t1)+" ms"); + } else { + t3=0; + System.out.println("Could not connect device."); + System.exit(-1); + } + // Will shut down everything .. ?? + //adapter.stopDiscovery(); + + final List<BluetoothGattService> allBluetoothServices = sensor.getServices(); + if (allBluetoothServices.isEmpty()) { + System.err.println("No BluetoothGattService found!"); + System.exit(1); + } + printAllServiceInfo(allBluetoothServices); + + sensor.disconnect(); + } + System.err.println("ScannerTinyB01 04 ...: "+adapter); + } while( forever ); + System.err.println("ScannerTinyB01 05"); + manager.shutdown(); + System.err.println("ScannerTinyB01 XX"); + } + private static void printDevice(final BluetoothDevice device) { + System.err.println("Address = " + device.getAddress()); + System.err.println(" Name = " + device.getName()); + System.err.println(" Connected = " + device.getConnected()); + System.err.println(); + } + private static void printAllServiceInfo(final List<BluetoothGattService> allBluetoothServices) { + try { + for (final BluetoothGattService service : allBluetoothServices) { + System.err.println("Service UUID: " + service.getUUID()); + final List<BluetoothGattCharacteristic> v = service.getCharacteristics(); + for (final BluetoothGattCharacteristic c : v) { + System.err.println(" Characteristic UUID: " + c.getUUID()); + + final List<BluetoothGattDescriptor> descriptors = c.getDescriptors(); + + for (final BluetoothGattDescriptor d : descriptors) { + System.err.println(" Descriptor UUID: " + d.getUUID()); + } + if (c.getUUID().contains("2a29-")) { + final byte[] tempRaw = c.readValue(); + System.err.println(" Manufacturer: " + new String(tempRaw)); + } + + if (c.getUUID().contains("2a28-")) { + final byte[] tempRaw = c.readValue(); + System.err.println(" Software: " + new String(tempRaw)); + } + + if (c.getUUID().contains("2a27-")) { + final byte[] tempRaw = c.readValue(); + System.err.println(" Hardware: " + new String(tempRaw)); + } + + if (c.getUUID().contains("2a26-")) { + final byte[] tempRaw = c.readValue(); + System.err.println(" Firmware: " + new String(tempRaw)); + } + + if (c.getUUID().contains("2a25-")) { + final byte[] tempRaw = c.readValue(); + System.err.println(" Serial: " + new String(tempRaw)); + } + + if (c.getUUID().contains("2a24-")) { + final byte[] tempRaw = c.readValue(); + System.err.println(" Model: " + new String(tempRaw)); + } + + if (c.getUUID().contains("2a23-")) { + final byte[] tempRaw = c.readValue(); + System.err.println(" System ID: " + bytesToHex(tempRaw)); + } + } + } + } catch (final RuntimeException e) { + } + } + private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + public static String bytesToHex(final byte[] bytes) { + final char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + final int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } + static class BooleanNotification implements BluetoothNotification<Boolean> { + private final long t0; + private final String name; + private boolean v; + + public BooleanNotification(final String name, final long t0) { + this.t0 = t0; + this.name = name; + this.v = false; + } + + @Override + public void run(final Boolean v) { + synchronized(this) { + final long t1 = BluetoothUtils.getCurrentMilliseconds(); + this.v = v.booleanValue(); + System.out.println("#### "+name+": "+v+" in td "+(t1-t0)+" ms!"); + this.notifyAll(); + } + } + public boolean getValue() { + synchronized(this) { + return v; + } + } + } +} diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt index 1f3b1771..ba7c017b 100644 --- a/java/CMakeLists.txt +++ b/java/CMakeLists.txt @@ -27,4 +27,5 @@ add_custom_command (TARGET tinybjar set(JNI_HEADER_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/tinybjar.dir/jni") install (FILES ${CMAKE_CURRENT_BINARY_DIR}/tinyb2.jar DESTINATION ${CMAKE_INSTALL_LIBDIR}/../lib/java) -add_subdirectory (jni) +add_subdirectory (jni/direct_bt) +add_subdirectory (jni/tinyb) diff --git a/java/direct_bt/tinyb/Adapter.java b/java/direct_bt/tinyb/Adapter.java new file mode 100644 index 00000000..b953c318 --- /dev/null +++ b/java/direct_bt/tinyb/Adapter.java @@ -0,0 +1,417 @@ +/** + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package direct_bt.tinyb; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +import org.tinyb.BluetoothAdapter; +import org.tinyb.BluetoothDevice; +import org.tinyb.BluetoothException; +import org.tinyb.BluetoothManager; +import org.tinyb.BluetoothNotification; +import org.tinyb.BluetoothType; +import org.tinyb.BluetoothDeviceDiscoveryListener; +import org.tinyb.TransportType; + +public class Adapter extends DBTObject implements BluetoothAdapter +{ + private final String address; + private final String name; + private final DiscoveryThread discoveryThread; + + /* pp */ Adapter(final long nativeInstance, final String address, final String name) + { + super(nativeInstance, compHash(address, name)); + this.address = address; + this.name = name; + this.discoveryThread = new DiscoveryThread(); + this.discoveryThread.start(null); + initImpl(this.discoveryThread.deviceDiscoveryListener); + } + + @Override + public synchronized void close() { + discoveryThread.stop(); + super.close(); + } + + @Override + public boolean equals(final Object obj) + { + if (obj == null || !(obj instanceof Device)) { + return false; + } + final Adapter other = (Adapter)obj; + return address.equals(other.address) && name.equals(other.name); + } + + @Override + public String getAddress() { return address; } + + @Override + public String getName() { return name; } + + public String getInterfaceName() { + throw new UnsupportedOperationException(); // FIXME + } + + @Override + public BluetoothType getBluetoothType() { return class_type(); } + + static BluetoothType class_type() { return BluetoothType.ADAPTER; } + + @Override + public final BluetoothAdapter clone() + { throw new UnsupportedOperationException(); } // FIXME + + @Override + public BluetoothDevice find(final String name, final String address, final long timeoutMS) { + final BluetoothManager manager = Manager.getBluetoothManager(); + return (BluetoothDevice) manager.find(BluetoothType.DEVICE, name, address, this, timeoutMS); + } + + @Override + public BluetoothDevice find(final String name, final String address) { + return find(name, address, 0); + } + + @Override + public String toString() { + final StringBuilder out = new StringBuilder(); + out.append("Adapter[").append(getAddress()).append(", '").append(getName()).append("', id=").append("]"); + synchronized(discoveryThread.discoveredDevicesLock) { + final int count = discoveryThread.discoveredDevices.size(); + if( count > 0 ) { + out.append("\n"); + for(final Iterator<BluetoothDevice> iter=discoveryThread.discoveredDevices.iterator(); iter.hasNext(); ) { + final BluetoothDevice device = iter.next(); + out.append(" ").append(device.toString()).append("\n"); + } + } + } + return out.toString(); + } + + /* property accessors: */ + + @Override + public String getAlias() { throw new UnsupportedOperationException(); } // FIXME + + @Override + public void setAlias(final String value) { throw new UnsupportedOperationException(); } // FIXME + + @Override + public long getBluetoothClass() { throw new UnsupportedOperationException(); } // FIXME + + @Override + public native boolean getPowered(); + + @Override + public native void enablePoweredNotifications(BluetoothNotification<Boolean> callback); + + @Override + public native void disablePoweredNotifications(); + + @Override + public native void setPowered(boolean value); + + @Override + public native boolean getDiscoverable(); + + @Override + public native void enableDiscoverableNotifications(BluetoothNotification<Boolean> callback); + + @Override + public native void disableDiscoverableNotifications(); + + @Override + public native void setDiscoverable(boolean value); + + @Override + public native long getDiscoverableTimeout(); + + @Override + public native void setDiscoverableTimout(long value); + + @Override + public native BluetoothDevice connectDevice(String address, String addressType); + + @Override + public native boolean getPairable(); + + @Override + public native void enablePairableNotifications(BluetoothNotification<Boolean> callback); + + @Override + public native void disablePairableNotifications(); + + @Override + public native void setPairable(boolean value); + + @Override + public native long getPairableTimeout(); + + @Override + public native void setPairableTimeout(long value); + + @Override + public native String[] getUUIDs(); + + @Override + public native String getModalias(); + + /* internal */ + + private native void initImpl(final BluetoothDeviceDiscoveryListener l); + + @Override + protected native void deleteImpl(); + + /* discovery */ + + @Override + public boolean startDiscovery() throws BluetoothException { + return discoveryThread.doDiscovery(true); + } + private native boolean startDiscoveryImpl() throws BluetoothException; + + @Override + public boolean stopDiscovery() throws BluetoothException { + return discoveryThread.doDiscovery(false); + } + private native boolean stopDiscoveryImpl() throws BluetoothException; + + @Override + public List<BluetoothDevice> getDevices() { + return discoveryThread.getDiscoveredDevices(); + } + + @Override + public int removeDevices() throws BluetoothException { + final int cj = discoveryThread.removeDiscoveredDevices(); + final int cn = removeDevicesImpl(); + if( cj != cn ) { + throw new InternalError("Inconsistent discovered device count: Native "+cn+", callback "+cj); + } + return cn; + } + private native int removeDevicesImpl() throws BluetoothException; + + @Override + public boolean getDiscovering() { return discoveryThread.running && discoveryThread.doDiscovery; } + + @Override + public void setDeviceDiscoveryListener(final BluetoothDeviceDiscoveryListener l) { + discoveryThread.setDeviceDiscoveryListener(l); + } + + @Override + public void enableDiscoveringNotifications(final BluetoothNotification<Boolean> callback) { + discoveryThread.setDiscoveringNotificationCallback(callback); + } + + @Override + public void disableDiscoveringNotifications() { + discoveryThread.setDiscoveringNotificationCallback(null); + } + + @Override + public void setDiscoveryFilter(final List<UUID> uuids, final int rssi, final int pathloss, final TransportType transportType) { + final List<String> uuidsFmt = new ArrayList<>(uuids.size()); + for (final UUID uuid : uuids) { + uuidsFmt.add(uuid.toString()); + } + setDiscoveryFilter(uuidsFmt, rssi, pathloss, transportType.ordinal()); + } + + public void setRssiDiscoveryFilter(final int rssi) { + setDiscoveryFilter(Collections.EMPTY_LIST, rssi, 0, TransportType.AUTO); + } + + private native void setDiscoveryFilter(List<String> uuids, int rssi, int pathloss, int transportType); + + private native int discoverAnyDeviceImpl(final int timeoutMS) throws BluetoothException; + // std::vector<std::shared_ptr<direct_bt::HCIDevice>> discoveredDevices = adapter.getDiscoveredDevices(); + private native List<BluetoothDevice> getDiscoveredDevicesImpl(); + + private class DiscoveryThread implements Runnable { + private int instanceID=-1; + + private volatile boolean running = false; + private volatile boolean doDiscovery = false; + private final Object stateLock = new Object(); + private volatile BluetoothDeviceDiscoveryListener userDeviceDiscoveryListener = null; + private volatile BluetoothNotification<Boolean> discoveringNotificationCB = null; + private List<BluetoothDevice> discoveredDevices = new ArrayList<BluetoothDevice>(); + private final Object discoveredDevicesLock = new Object(); + + private final BluetoothDeviceDiscoveryListener deviceDiscoveryListener = new BluetoothDeviceDiscoveryListener() { + @Override + public void deviceAdded(final BluetoothAdapter a, final BluetoothDevice device) { + final BluetoothDeviceDiscoveryListener l = userDeviceDiscoveryListener; + System.err.println("DBTAdapter.DeviceDiscoveryListener.added: "+device+" on "+a); + synchronized(discoveredDevicesLock) { + discoveredDevices.add(device); + } + if( null != l ) { + l.deviceAdded(a, device); + } + } + + @Override + public void deviceUpdated(final BluetoothAdapter a, final BluetoothDevice device) { + System.err.println("DBTAdapter.DeviceDiscoveryListener.updated: "+device+" on "+a); + // nop on discoveredDevices + userDeviceDiscoveryListener.deviceUpdated(a, device); + } + + @Override + public void deviceRemoved(final BluetoothAdapter a, final BluetoothDevice device) { + final BluetoothDeviceDiscoveryListener l = userDeviceDiscoveryListener; + System.err.println("DBTAdapter.DeviceDiscoveryListener.removed: "+device+" on "+a); + synchronized(discoveredDevicesLock) { + discoveredDevices.remove(device); + } + if( null != l ) { + l.deviceRemoved(a, device); + } + } + }; + + public void start(final ThreadGroup tg) { + synchronized( stateLock ) { + if ( !running ) { + instanceID = globThreadID.addAndGet(1); + final Thread t = new Thread(tg, this, "Adapter-"+instanceID); // Thread name aligned w/ 'Thread-#' + t.setDaemon(true); + t.start(); + while( !running ) { + try { + stateLock.wait(); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } + public void setDeviceDiscoveryListener(final BluetoothDeviceDiscoveryListener l) { + synchronized( stateLock ) { + userDeviceDiscoveryListener = l; + stateLock.notifyAll(); + } + } + public void setDiscoveringNotificationCallback(final BluetoothNotification<Boolean> cb) { + synchronized( stateLock ) { + discoveringNotificationCB = cb; + stateLock.notifyAll(); + } + } + public List<BluetoothDevice> getDiscoveredDevices() { + synchronized(discoveredDevicesLock) { + return new ArrayList<BluetoothDevice>(discoveredDevices); + } + } + public int removeDiscoveredDevices() { + synchronized(discoveredDevicesLock) { + final int n = discoveredDevices.size(); + discoveredDevices = new ArrayList<BluetoothDevice>(); + return n; + } + } + public boolean doDiscovery(final boolean v) { + synchronized( stateLock ) { + if( v == doDiscovery ) { + return v; + } + final BluetoothNotification<Boolean> cb = discoveringNotificationCB; + final boolean enable; + if( v ) { + enable = startDiscoveryImpl(); + } else { + enable = false; + stopDiscoveryImpl(); + } + doDiscovery = enable; + if( null != cb ) { + cb.run(enable); + } + stateLock.notifyAll(); + return enable; + } + } + public void stop() { + synchronized( stateLock ) { + stopDiscoveryImpl(); + running = false; + doDiscovery = false; + stateLock.notifyAll(); + } + } + + @Override + public void run() { + synchronized( stateLock ) { + running = true; + stateLock.notifyAll(); + } + while (running) { + synchronized( stateLock ) { + while( running && !doDiscovery ) { + try { + stateLock.wait(); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + } + if( !running ) { + break; // end loop and thread + } + + try { + if( doDiscovery ) { + // will trigger all discovery callbacks! + discoverAnyDeviceImpl(discoverTimeoutMS); + } + } catch (final Exception e) { + System.err.println(e.toString()); + e.printStackTrace(); + doDiscovery = false; + } + } // loop + + instanceID = -1; + } + } + private static AtomicInteger globThreadID = new AtomicInteger(0); + private static int discoverTimeoutMS = 100; +} diff --git a/java/tinyb/hci/HCIEvent.java b/java/direct_bt/tinyb/DBTEvent.java index 4dc30ec3..66317ea9 100644 --- a/java/tinyb/hci/HCIEvent.java +++ b/java/direct_bt/tinyb/DBTEvent.java @@ -23,13 +23,13 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package tinyb.hci; +package direct_bt.tinyb; import org.tinyb.BluetoothCallback; import org.tinyb.BluetoothEvent; import org.tinyb.BluetoothType; -public class HCIEvent implements BluetoothEvent +public class DBTEvent implements BluetoothEvent { private long nativeInstance; @@ -45,11 +45,11 @@ public class HCIEvent implements BluetoothEvent public native boolean hasCallback(); private native void init(BluetoothType type, String name, String identifier, - HCIObject parent, BluetoothCallback cb, Object data); + DBTObject parent, BluetoothCallback cb, Object data); private native void delete(); - public HCIEvent(final BluetoothType type, final String name, final String identifier, - final HCIObject parent, final BluetoothCallback cb, final Object data) + public DBTEvent(final BluetoothType type, final String name, final String identifier, + final DBTObject parent, final BluetoothCallback cb, final Object data) { init(type, name, identifier, parent, cb, data); } diff --git a/java/tinyb/hci/HCIObject.java b/java/direct_bt/tinyb/DBTObject.java index 85a903e5..93b10245 100644 --- a/java/tinyb/hci/HCIObject.java +++ b/java/direct_bt/tinyb/DBTObject.java @@ -23,28 +23,14 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package tinyb.hci; +package direct_bt.tinyb; -import org.tinyb.BluetoothFactory; import org.tinyb.BluetoothObject; import org.tinyb.BluetoothType; -public abstract class HCIObject implements BluetoothObject +public abstract class DBTObject extends NativeDownlink implements BluetoothObject { private final int hashValue; - private boolean isValid; - - static { - try { - System.loadLibrary(BluetoothFactory.JavaNativeLibBasename); - } catch (final Throwable e) { - System.err.println("Failed to load native library "+BluetoothFactory.JavaNativeLibBasename); - e.printStackTrace(); - throw e; // fwd exception - end here - } - } - - static BluetoothType class_type() { return BluetoothType.NONE; } /* pp */ static int compHash(final String a, final String b) { // 31 * x == (x << 5) - x @@ -52,12 +38,14 @@ public abstract class HCIObject implements BluetoothObject return ((hash << 5) - hash) + b.hashCode(); } - protected HCIObject(final int hashValue) + protected DBTObject(final long nativeInstance, final int hashValue) { + super(nativeInstance); this.hashValue = hashValue; - isValid = true; } + static BluetoothType class_type() { return BluetoothType.NONE; } + @Override public abstract boolean equals(final Object obj); @@ -74,18 +62,11 @@ public abstract class HCIObject implements BluetoothObject @Override public synchronized void close() { - if (!isValid) { - return; - } - isValid = false; delete(); } - @Override - public native BluetoothType getBluetoothType(); @Override public BluetoothObject clone() { throw new UnsupportedOperationException(); } // FIXME - private native void delete(); } diff --git a/java/tinyb/hci/HCIDevice.java b/java/direct_bt/tinyb/Device.java index b9a41a53..f3a90d6b 100644 --- a/java/tinyb/hci/HCIDevice.java +++ b/java/direct_bt/tinyb/Device.java @@ -23,7 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package tinyb.hci; +package direct_bt.tinyb; import java.util.List; import java.util.Map; @@ -34,30 +34,34 @@ import org.tinyb.BluetoothGattService; import org.tinyb.BluetoothManager; import org.tinyb.BluetoothNotification; import org.tinyb.BluetoothType; +import org.tinyb.BluetoothUtils; -import tinyb.dbus.DBusObject; - -public class HCIDevice extends HCIObject implements BluetoothDevice +public class Device extends DBTObject implements BluetoothDevice { - private final HCIAdapter adapter; + private final Adapter adapter; private final String address; private final String name; + private final long ts_creation; + long ts_update; - /* pp */ HCIDevice(final HCIAdapter adptr, final String address, final String name) + /* pp */ Device(final long nativeInstance, final Adapter adptr, final String address, final String name, final long ts_creation) { - super(compHash(address, name)); + super(nativeInstance, compHash(address, name)); this.adapter = adptr; this.address = address; this.name = name; + this.ts_creation = ts_creation; + ts_update = ts_creation; + initImpl(); } @Override public boolean equals(final Object obj) { - if (obj == null || !(obj instanceof HCIDevice)) { + if (obj == null || !(obj instanceof Device)) { return false; } - final HCIDevice other = (HCIDevice)obj; + final Device other = (Device)obj; return address.equals(other.address) && name.equals(other.name); } @@ -70,14 +74,15 @@ public class HCIDevice extends HCIObject implements BluetoothDevice @Override public BluetoothType getBluetoothType() { return class_type(); } - @Override - public native HCIDevice clone(); - static BluetoothType class_type() { return BluetoothType.DEVICE; } @Override + public final BluetoothDevice clone() + { throw new UnsupportedOperationException(); } // FIXME + + @Override public BluetoothGattService find(final String UUID, final long timeoutMS) { - final BluetoothManager manager = HCIManager.getBluetoothManager(); + final BluetoothManager manager = Manager.getBluetoothManager(); return (BluetoothGattService) manager.find(BluetoothType.GATT_SERVICE, null, UUID, this, timeoutMS); } @@ -87,6 +92,34 @@ public class HCIDevice extends HCIObject implements BluetoothDevice return find(UUID, 0); } + @Override + public String toString() { + final StringBuilder out = new StringBuilder(); + final long t0 = BluetoothUtils.getCurrentMilliseconds(); + // std::string msdstr = nullptr != msd ? msd->toString() : "MSD[null]"; + final String msdstr = "MSD[null]"; + out.append("Device[").append(getAddress()).append(", '").append(getName()) + .append("', age ").append(t0-ts_creation).append(" ms, lup ").append(t0-ts_update).append(" ms, rssi ").append(getRSSI()) + .append(", tx-power ").append(getTxPower()).append(", ").append(msdstr).append("]"); + /** + if(services.size() > 0 ) { + out.append("\n"); + final int i=0; + for(final auto it = services.begin(); it != services.end(); it++, i++) { + if( 0 < i ) { + out.append("\n"); + } + std::shared_ptr<uuid_t> p = *it; + out.append(" ").append(p->toUUID128String()).append(", ").append(std::to_string(static_cast<int>(p->getTypeSize()))).append(" bytes"); + } + } */ + return out.toString(); + } + + /* internal */ + + private native void initImpl(); + /* D-Bus method calls: */ @Override @@ -194,7 +227,7 @@ public class HCIDevice extends HCIObject implements BluetoothDevice public native String getModalias(); @Override - public native HCIAdapter getAdapter(); + public native Adapter getAdapter(); @Override public native Map<Short, byte[]> getManufacturerData(); @@ -227,5 +260,6 @@ public class HCIDevice extends HCIObject implements BluetoothDevice @Override public native void disableServicesResolvedNotifications(); - private native void delete(); + @Override + protected native void deleteImpl(); } diff --git a/java/tinyb/hci/HCIGattCharacteristic.java b/java/direct_bt/tinyb/GattCharacteristic.java index a2047e1e..801dac33 100644 --- a/java/tinyb/hci/HCIGattCharacteristic.java +++ b/java/direct_bt/tinyb/GattCharacteristic.java @@ -23,10 +23,11 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package tinyb.hci; +package direct_bt.tinyb; import java.util.List; +import org.tinyb.BluetoothDevice; import org.tinyb.BluetoothException; import org.tinyb.BluetoothGattCharacteristic; import org.tinyb.BluetoothGattDescriptor; @@ -35,23 +36,23 @@ import org.tinyb.BluetoothManager; import org.tinyb.BluetoothNotification; import org.tinyb.BluetoothType; -public class HCIGattCharacteristic extends HCIObject implements BluetoothGattCharacteristic +public class GattCharacteristic extends DBTObject implements BluetoothGattCharacteristic { private final String uuid; - /* pp */ HCIGattCharacteristic(final String uuid) + /* pp */ GattCharacteristic(final long nativeInstance, final String uuid) { - super(uuid.hashCode()); + super(nativeInstance, uuid.hashCode()); this.uuid = uuid; } @Override public boolean equals(final Object obj) { - if (obj == null || !(obj instanceof HCIGattCharacteristic)) { + if (obj == null || !(obj instanceof GattCharacteristic)) { return false; } - final HCIGattCharacteristic other = (HCIGattCharacteristic)obj; + final GattCharacteristic other = (GattCharacteristic)obj; return uuid.equals(other.uuid); } @@ -59,15 +60,17 @@ public class HCIGattCharacteristic extends HCIObject implements BluetoothGattCha public String getUUID() { return uuid; } @Override - public native BluetoothType getBluetoothType(); - @Override - public native HCIGattCharacteristic clone(); + public BluetoothType getBluetoothType() { return class_type(); } static BluetoothType class_type() { return BluetoothType.GATT_CHARACTERISTIC; } @Override + public final GattCharacteristic clone() + { throw new UnsupportedOperationException(); } // FIXME + + @Override public BluetoothGattDescriptor find(final String UUID, final long timeoutMS) { - final BluetoothManager manager = HCIManager.getBluetoothManager(); + final BluetoothManager manager = Manager.getBluetoothManager(); return (BluetoothGattDescriptor) manager.find(BluetoothType.GATT_DESCRIPTOR, null, UUID, this, timeoutMS); } @@ -108,8 +111,9 @@ public class HCIGattCharacteristic extends HCIObject implements BluetoothGattCha @Override public native List<BluetoothGattDescriptor> getDescriptors(); - private native void init(HCIGattCharacteristic obj); + private native void init(GattCharacteristic obj); - private native void delete(); + @Override + protected native void deleteImpl(); } diff --git a/java/tinyb/hci/HCIGattDescriptor.java b/java/direct_bt/tinyb/GattDescriptor.java index b04816a4..5b0d2898 100644 --- a/java/tinyb/hci/HCIGattDescriptor.java +++ b/java/direct_bt/tinyb/GattDescriptor.java @@ -23,30 +23,30 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package tinyb.hci; +package direct_bt.tinyb; import org.tinyb.BluetoothException; import org.tinyb.BluetoothGattDescriptor; import org.tinyb.BluetoothNotification; import org.tinyb.BluetoothType; -public class HCIGattDescriptor extends HCIObject implements BluetoothGattDescriptor +public class GattDescriptor extends DBTObject implements BluetoothGattDescriptor { private final String uuid; - /* pp */ HCIGattDescriptor(final String uuid) + /* pp */ GattDescriptor(final long nativeInstance, final String uuid) { - super(uuid.hashCode()); + super(nativeInstance, uuid.hashCode()); this.uuid = uuid; } @Override public boolean equals(final Object obj) { - if (obj == null || !(obj instanceof HCIGattDescriptor)) { + if (obj == null || !(obj instanceof GattDescriptor)) { return false; } - final HCIGattDescriptor other = (HCIGattDescriptor)obj; + final GattDescriptor other = (GattDescriptor)obj; return uuid.equals(other.uuid); } @@ -54,12 +54,14 @@ public class HCIGattDescriptor extends HCIObject implements BluetoothGattDescrip public String getUUID() { return uuid; } @Override - public native BluetoothType getBluetoothType(); - @Override - public native BluetoothGattDescriptor clone(); + public BluetoothType getBluetoothType() { return class_type(); } static BluetoothType class_type() { return BluetoothType.GATT_DESCRIPTOR; } + @Override + public final GattDescriptor clone() + { throw new UnsupportedOperationException(); } // FIXME + /* D-Bus method calls: */ @Override @@ -77,10 +79,11 @@ public class HCIGattDescriptor extends HCIObject implements BluetoothGattDescrip /* D-Bus property accessors: */ @Override - public native HCIGattCharacteristic getCharacteristic(); + public native GattCharacteristic getCharacteristic(); @Override public native byte[] getValue(); - private native void delete(); + @Override + protected native void deleteImpl(); } diff --git a/java/tinyb/hci/HCIGattService.java b/java/direct_bt/tinyb/GattService.java index 584f68b3..8377c806 100644 --- a/java/tinyb/hci/HCIGattService.java +++ b/java/direct_bt/tinyb/GattService.java @@ -23,7 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package tinyb.hci; +package direct_bt.tinyb; import java.util.List; @@ -32,23 +32,23 @@ import org.tinyb.BluetoothGattService; import org.tinyb.BluetoothManager; import org.tinyb.BluetoothType; -public class HCIGattService extends HCIObject implements BluetoothGattService +public class GattService extends DBTObject implements BluetoothGattService { private final String uuid; - /* pp */ HCIGattService(final String uuid) + /* pp */ GattService(final long nativeInstance, final String uuid) { - super(uuid.hashCode()); + super(nativeInstance, uuid.hashCode()); this.uuid = uuid; } @Override public boolean equals(final Object obj) { - if (obj == null || !(obj instanceof HCIGattService)) { + if (obj == null || !(obj instanceof GattService)) { return false; } - final HCIGattService other = (HCIGattService)obj; + final GattService other = (GattService)obj; return uuid.equals(other.uuid); } @@ -56,17 +56,18 @@ public class HCIGattService extends HCIObject implements BluetoothGattService public String getUUID() { return uuid; } @Override - public native BluetoothType getBluetoothType(); - - @Override - public native BluetoothGattService clone(); + public BluetoothType getBluetoothType() { return class_type(); } static BluetoothType class_type() { return BluetoothType.GATT_SERVICE; } @Override + public final GattService clone() + { throw new UnsupportedOperationException(); } // FIXME + + @Override public BluetoothGattCharacteristic find(final String UUID, final long timeoutMS) { - final BluetoothManager manager = HCIManager.getBluetoothManager(); - return (HCIGattCharacteristic) manager.find(BluetoothType.GATT_CHARACTERISTIC, + final BluetoothManager manager = Manager.getBluetoothManager(); + return (GattCharacteristic) manager.find(BluetoothType.GATT_CHARACTERISTIC, null, UUID, this, timeoutMS); } @@ -78,7 +79,7 @@ public class HCIGattService extends HCIObject implements BluetoothGattService /* D-Bus property accessors: */ @Override - public native HCIDevice getDevice(); + public native Device getDevice(); @Override public native boolean getPrimary(); @@ -86,5 +87,6 @@ public class HCIGattService extends HCIObject implements BluetoothGattService @Override public native List<BluetoothGattCharacteristic> getCharacteristics(); - private native void delete(); + @Override + protected native void deleteImpl(); } diff --git a/java/tinyb/hci/HCIManager.java b/java/direct_bt/tinyb/Manager.java index 7afba39c..99ea2cce 100644 --- a/java/tinyb/hci/HCIManager.java +++ b/java/direct_bt/tinyb/Manager.java @@ -23,7 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package tinyb.hci; +package direct_bt.tinyb; import java.util.ArrayList; import java.util.List; @@ -36,30 +36,31 @@ import org.tinyb.BluetoothObject; import org.tinyb.BluetoothManager; import org.tinyb.BluetoothType; -public class HCIManager implements BluetoothManager +public class Manager implements BluetoothManager { - private static HCIManager inst; + private long nativeInstance; + private static Manager inst; private final List<BluetoothAdapter> adapters = new ArrayList<BluetoothAdapter>(); - public native BluetoothType getBluetoothType(); + public BluetoothType getBluetoothType() { return BluetoothType.NONE; } - private HCIObject find(final int type, final String name, final String identifier, final BluetoothObject parent, final long milliseconds) + private DBTObject find(final int type, final String name, final String identifier, final BluetoothObject parent, final long milliseconds) { throw new UnsupportedOperationException(); } // FIXME @Override - public HCIObject find(final BluetoothType type, final String name, final String identifier, final BluetoothObject parent, final long timeoutMS) { + public DBTObject find(final BluetoothType type, final String name, final String identifier, final BluetoothObject parent, final long timeoutMS) { return find(type.ordinal(), name, identifier, parent, timeoutMS); } @Override - public HCIObject find(final BluetoothType type, final String name, final String identifier, final BluetoothObject parent) { + public DBTObject find(final BluetoothType type, final String name, final String identifier, final BluetoothObject parent) { return find(type, name, identifier, parent, 0); } @SuppressWarnings("unchecked") @Override public <T extends BluetoothObject> T find(final String name, final String identifier, final BluetoothObject parent, final long timeoutMS) { - return (T) find(HCIObject.class_type().ordinal(), name, identifier, parent, timeoutMS); + return (T) find(DBTObject.class_type().ordinal(), name, identifier, parent, timeoutMS); } @SuppressWarnings("unchecked") @@ -108,12 +109,22 @@ public class HCIManager implements BluetoothManager @Override public boolean getDiscovering() throws BluetoothException { return getDefaultAdapter().getDiscovering(); } - private native HCIAdapter init() throws BluetoothException; - private native void delete(); + /** + * Returns an opened default adapter instance! + * @throws BluetoothException in case adapter is invalid or could not have been opened. + */ + private native Adapter getDefaultAdapterImpl() throws BluetoothException; - private HCIManager() + private native void initImpl() throws BluetoothException; + private native void deleteImpl(); + private Manager() { - adapters.add(init()); + initImpl(); + try { + adapters.add(getDefaultAdapterImpl()); + } catch (final BluetoothException be) { + be.printStackTrace(); + } } /** Returns an instance of BluetoothManager, to be used instead of constructor. @@ -123,15 +134,20 @@ public class HCIManager implements BluetoothManager { if (inst == null) { - inst = new HCIManager(); + inst = new Manager(); } return inst; } @Override - protected void finalize() - { + protected void finalize() { + shutdown(); + } + + @Override + public void shutdown() { adapters.clear(); - delete(); + deleteImpl(); } + } diff --git a/java/direct_bt/tinyb/NativeDownlink.java b/java/direct_bt/tinyb/NativeDownlink.java new file mode 100644 index 00000000..72e4e137 --- /dev/null +++ b/java/direct_bt/tinyb/NativeDownlink.java @@ -0,0 +1,87 @@ +/** + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package direct_bt.tinyb; + +import org.tinyb.BluetoothFactory; + +public abstract class NativeDownlink +{ + protected long nativeInstance; + private boolean isValid; + + static { + try { + System.loadLibrary(BluetoothFactory.JavaNativeLibBasename); + } catch (final Throwable e) { + System.err.println("Failed to load native library "+BluetoothFactory.JavaNativeLibBasename); + e.printStackTrace(); + throw e; // fwd exception - end here + } + } + + protected NativeDownlink(final long nativeInstance) + { + this.nativeInstance = nativeInstance; + isValid = true; + initNativeJavaObject(nativeInstance); + } + + @Override + protected void finalize() + { + delete(); + } + + /** + * Deletes the native instance in the following order + * <ol> + * <li>Removes this java reference from the native instance</li> + * <li>Deletes the native instance via {@link #deleteImpl()}</li> + * <li>Sets the nativeInstance := 0</li> + * </ol> + */ + public synchronized void delete() { + if (!isValid) { + return; + } + isValid = false; + clearNativeJavaObject(nativeInstance); + deleteImpl(); + nativeInstance = 0; + } + + /** + * Deletes the native instance. + * <p> + * Called via {@link #delete()} and at this point this java reference + * has been removed from the native instance. + * </p> + */ + protected abstract void deleteImpl(); + + private native void initNativeJavaObject(final long nativeInstance); + private native void clearNativeJavaObject(final long nativeInstance); +} diff --git a/java/jni/BluetoothUtils.cxx b/java/jni/BluetoothUtils.cxx new file mode 100644 index 00000000..83e71dd0 --- /dev/null +++ b/java/jni/BluetoothUtils.cxx @@ -0,0 +1,53 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "org_tinyb_BluetoothUtils.h" + +#include <cstdint> +#include <cinttypes> + +#include <time.h> + +static const int64_t NanoPerMilli = 1000000L; +static const int64_t MilliPerOne = 1000L; + +/** + * See <http://man7.org/linux/man-pages/man2/clock_gettime.2.html> + * <p> + * Regarding avoiding kernel via VDSO, + * see <http://man7.org/linux/man-pages/man7/vdso.7.html>, + * clock_gettime seems to be well supported at least on kernel >= 4.4. + * Only bfin and sh are missing, while ia64 seems to be complicated. + */ +jlong Java_org_tinyb_BluetoothUtils_getCurrentMilliseconds(JNIEnv *env, jclass clazz) { + (void)env; + (void)clazz; + + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + int64_t res = t.tv_sec * MilliPerOne + t.tv_nsec / NanoPerMilli; + return (jlong)res; +} + diff --git a/java/jni/CMakeLists.txt b/java/jni/CMakeLists.txt deleted file mode 100644 index cd715a5a..00000000 --- a/java/jni/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -find_package(JNI REQUIRED) - -if (JNI_FOUND) - message (STATUS "JNI_INCLUDE_DIRS=${JNI_INCLUDE_DIRS}") - message (STATUS "JNI_LIBRARIES=${JNI_LIBRARIES}") -endif (JNI_FOUND) - -set (tinyb_LIB_INCLUDE_DIRS - ${PROJECT_SOURCE_DIR}/api - ${PROJECT_SOURCE_DIR}/api/tinyb - ${PROJECT_SOURCE_DIR}/api/direct_bt - ${PROJECT_SOURCE_DIR}/include -) - -include_directories( - ${JNI_INCLUDE_DIRS} - ${tinyb_LIB_INCLUDE_DIRS} - ${JNI_HEADER_PATH} -) - -file(GLOB JNI_SOURCES "*.cxx") - -set (CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed") - -add_library (javatinyb SHARED ${JNI_SOURCES}) -target_link_libraries(javatinyb ${JNI_LIBRARIES} tinyb) - -set_target_properties( - javatinyb - PROPERTIES - SOVERSION ${tinyb_VERSION_MAJOR} - VERSION ${tinyb_VERSION_STRING} - CXX_STANDARD 11 - COMPILE_FLAGS "-Wall -Wextra" -) - -install(TARGETS javatinyb LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) - diff --git a/java/jni/HCIManager.cxx b/java/jni/HCIManager.cxx deleted file mode 100644 index 57865644..00000000 --- a/java/jni/HCIManager.cxx +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Author: Sven Gothel <[email protected]> - * Copyright (c) 2020 Gothel Software e.K. - * Copyright (c) 2020 ZAFENA AB - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "direct_bt/HCITypes.hpp" - -#include "JNIMem.hpp" -#include "helper_base.hpp" - diff --git a/java/jni/HCIObject.cxx b/java/jni/HCIObject.cxx deleted file mode 100644 index 1f420eea..00000000 --- a/java/jni/HCIObject.cxx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Author: Sven Gothel <[email protected]> - * Copyright (c) 2020 Gothel Software e.K. - * Copyright (c) 2020 ZAFENA AB - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "direct_bt/HCITypes.hpp" - -#include "tinyb_hci_HCIObject.h" - -#include "JNIMem.hpp" -#include "helper_base.hpp" - -using namespace direct_bt; - diff --git a/java/jni/JNIMem.cxx b/java/jni/JNIMem.cxx index 9861995c..ecea39aa 100644 --- a/java/jni/JNIMem.cxx +++ b/java/jni/JNIMem.cxx @@ -2,6 +2,10 @@ * Author: Petre Eftime <[email protected]> * Copyright (c) 2016 Intel Corporation. * + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -60,14 +64,14 @@ void JNIEnvContainer::attach() { if( JNI_EDETACHED == envRes ) { envRes = vm->AttachCurrentThreadAsDaemon((void**) &newEnv, NULL); if( JNI_OK != envRes ) { - throw std::runtime_error("Attach to VM failed"); + throw direct_bt::RuntimeException("Attach to VM failed", E_FILE_LINE); } env = newEnv; } else if( JNI_OK != envRes ) { - throw std::runtime_error("GetEnv of VM failed"); + throw direct_bt::RuntimeException("GetEnv of VM failed", E_FILE_LINE); } if (env==NULL) { - throw std::runtime_error("GetEnv of VM is NULL"); + throw direct_bt::RuntimeException("GetEnv of VM is NULL", E_FILE_LINE); } needsDetach = NULL != newEnv; } @@ -84,13 +88,24 @@ void JNIEnvContainer::detach() { } JNIGlobalRef::JNIGlobalRef(jobject object) { + if( nullptr == object ) { + throw direct_bt::RuntimeException("JNIGlobalRef ctor null jobject", E_FILE_LINE); + } this->object = jni_env->NewGlobalRef(object); } JNIGlobalRef::~JNIGlobalRef() { - jni_env->DeleteGlobalRef(object); -} - -jobject JNIGlobalRef::operator*() { - return object; + try { + JNIEnv * env = *jni_env; + if( nullptr == env ) { + throw direct_bt::RuntimeException("JNIGlobalRef dtor null JNIEnv", E_FILE_LINE); + } + if( nullptr == object ) { + throw direct_bt::RuntimeException("JNIGlobalRef dtor null jobject", E_FILE_LINE); + } else { + env->DeleteGlobalRef(object); + } + } catch (std::exception &e) { + fprintf(stderr, "JNIGlobalRef dtor: Caught %s\n", e.what()); + } } diff --git a/java/jni/JNIMem.hpp b/java/jni/JNIMem.hpp index 389a9157..a133bd9a 100644 --- a/java/jni/JNIMem.hpp +++ b/java/jni/JNIMem.hpp @@ -2,6 +2,10 @@ * Author: Petre Eftime <[email protected]> * Copyright (c) 2016 Intel Corporation. * + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -22,10 +26,14 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#pragma once +#ifndef JNIMEM__HPP_ +#define JNIMEM__HPP_ + #include <jni.h> #include <stdexcept> +#include "BasicTypes.hpp" + extern JavaVM* vm; @@ -67,12 +75,26 @@ private: jobject object; public: + static inline void check(jobject object, const char* file, int line) { + if( nullptr == object ) { + throw direct_bt::RuntimeException("JNIGlobalRef::check: Null jobject", file, line); + } + } + /* Creates a GlobalRef from an object passed to it */ JNIGlobalRef(jobject object); + /* Deletes the stored GlobalRef */ ~JNIGlobalRef(); - /* Provides access to the stored GlobalRef */ - jobject operator*(); + /* Provides access to the stored GlobalRef as an jobject. */ + jobject operator*() { return object; } + + /* Provides access to the stored GlobalRef as an jobject. */ + jobject getObject() const { return object; } + /* Provides access to the stored GlobalRef as a jclass. */ + jclass getClass() const { return (jclass)object; } }; +#endif /* JNIMEM__HPP_ */ + diff --git a/java/jni/direct_bt/CMakeLists.txt b/java/jni/direct_bt/CMakeLists.txt new file mode 100644 index 00000000..7622569f --- /dev/null +++ b/java/jni/direct_bt/CMakeLists.txt @@ -0,0 +1,53 @@ +find_package(JNI REQUIRED) + +if (JNI_FOUND) + message (STATUS "JNI_INCLUDE_DIRS=${JNI_INCLUDE_DIRS}") + message (STATUS "JNI_LIBRARIES=${JNI_LIBRARIES}") +endif (JNI_FOUND) + +set (direct_bt_LIB_INCLUDE_DIRS + ${PROJECT_SOURCE_DIR}/api + ${PROJECT_SOURCE_DIR}/api/direct_bt + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/java/jni +) + +include_directories( + ${JNI_INCLUDE_DIRS} + ${direct_bt_LIB_INCLUDE_DIRS} + ${JNI_HEADER_PATH} +) + +set (direct_bt_JNI_SRCS + ${PROJECT_SOURCE_DIR}/java/jni/JNIMem.cxx + ${PROJECT_SOURCE_DIR}/java/jni/helper_base.cxx + ${PROJECT_SOURCE_DIR}/java/jni/BluetoothFactory.cxx + ${PROJECT_SOURCE_DIR}/java/jni/BluetoothUtils.cxx + ${PROJECT_SOURCE_DIR}/java/jni/direct_bt/helper_dbt.cxx + ${PROJECT_SOURCE_DIR}/java/jni/direct_bt/DBTNativeDownlink.cxx + ${PROJECT_SOURCE_DIR}/java/jni/direct_bt/DBTAdapter.cxx + ${PROJECT_SOURCE_DIR}/java/jni/direct_bt/DBTDevice.cxx + ${PROJECT_SOURCE_DIR}/java/jni/direct_bt/DBTEvent.cxx + ${PROJECT_SOURCE_DIR}/java/jni/direct_bt/DBTGattCharacteristic.cxx + ${PROJECT_SOURCE_DIR}/java/jni/direct_bt/DBTGattDescriptor.cxx + ${PROJECT_SOURCE_DIR}/java/jni/direct_bt/DBTGattService.cxx + ${PROJECT_SOURCE_DIR}/java/jni/direct_bt/DBTManager.cxx + ${PROJECT_SOURCE_DIR}/java/jni/direct_bt/DBTObject.cxx +) + +set (CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed") + +add_library (javadirect_bt SHARED ${direct_bt_JNI_SRCS}) +target_link_libraries(javadirect_bt ${JNI_LIBRARIES} direct_bt) + +set_target_properties( + javadirect_bt + PROPERTIES + SOVERSION ${tinyb_VERSION_MAJOR} + VERSION ${tinyb_VERSION_STRING} + CXX_STANDARD 11 + COMPILE_FLAGS "-Wall -Wextra" +) + +install(TARGETS javadirect_bt LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + diff --git a/java/jni/direct_bt/DBTAdapter.cxx b/java/jni/direct_bt/DBTAdapter.cxx new file mode 100644 index 00000000..3f634dc2 --- /dev/null +++ b/java/jni/direct_bt/DBTAdapter.cxx @@ -0,0 +1,259 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "direct_bt_tinyb_Adapter.h" + +// #define VERBOSE_ON 1 +#include <dbt_debug.hpp> + +#include "JNIMem.hpp" +#include "helper_base.hpp" +#include "helper_dbt.hpp" + +#include "direct_bt/HCITypes.hpp" + +using namespace direct_bt; + +static const std::string _deviceClazzCtorArgs("(JLdirect_bt/tinyb/Adapter;Ljava/lang/String;Ljava/lang/String;J)V"); +static const std::string _deviceDiscoveryMethodArgs("(Lorg/tinyb/BluetoothAdapter;Lorg/tinyb/BluetoothDevice;)V"); + +class DeviceDiscoveryCallbackListener : public HCIDeviceDiscoveryListener { + public: + /** + package org.tinyb; + + public interface BluetoothDeviceDiscoveryListener { + public void deviceAdded(final BluetoothAdapter adapter, final BluetoothDevice device); + public void deviceUpdated(final BluetoothAdapter adapter, final BluetoothDevice device); + public void deviceRemoved(final BluetoothAdapter adapter, final BluetoothDevice device); + }; + */ + std::shared_ptr<JavaAnonObj> adapterObjRef; + std::unique_ptr<JNIGlobalRef> deviceClazzRef; + jmethodID deviceClazzCtor; + jfieldID deviceClazzTSUpdateField; + std::unique_ptr<JNIGlobalRef> listenerObjRef; + std::unique_ptr<JNIGlobalRef> listenerClazzRef; + jmethodID mDeviceAdded = nullptr; + jmethodID mDeviceUpdated = nullptr; + jmethodID mDeviceRemoved = nullptr; + + DeviceDiscoveryCallbackListener(JNIEnv *env, HCIAdapter *adapter, jobject deviceDiscoveryListener) { + adapterObjRef = adapter->getJavaObject(); + JavaGlobalObj::check(adapterObjRef, E_FILE_LINE); + { + jclass deviceClazz = search_class(*jni_env, HCIDevice::java_class().c_str()); + if( nullptr == deviceClazz ) { + throw InternalError("HCIDevice::java_class not found: "+HCIDevice::java_class(), E_FILE_LINE); + } + deviceClazzRef = std::unique_ptr<JNIGlobalRef>(new JNIGlobalRef(deviceClazz)); + env->DeleteLocalRef(deviceClazz); + } + deviceClazzCtor = search_method(*jni_env, deviceClazzRef->getClass(), "<init>", _deviceClazzCtorArgs.c_str(), false); + if( nullptr == deviceClazzCtor ) { + throw InternalError("HCIDevice::java_class ctor not found: "+HCIDevice::java_class()+".<init>"+_deviceClazzCtorArgs, E_FILE_LINE); + } + deviceClazzTSUpdateField = jni_env->GetFieldID(deviceClazzRef->getClass(), "ts_update", "J"); + if( nullptr == deviceClazzTSUpdateField ) { + throw InternalError("HCIDevice::java_class field not found: "+HCIDevice::java_class()+".ts_update", E_FILE_LINE); + } + + listenerObjRef = std::unique_ptr<JNIGlobalRef>(new JNIGlobalRef(deviceDiscoveryListener)); + { + jclass listenerClazz = search_class(env, listenerObjRef->getObject()); + if( nullptr == listenerClazz ) { + throw InternalError("BluetoothDeviceDiscoveryListener not found", E_FILE_LINE); + } + listenerClazzRef = std::unique_ptr<JNIGlobalRef>(new JNIGlobalRef(listenerClazz)); + env->DeleteLocalRef(listenerClazz); + } + mDeviceAdded = search_method(env, listenerClazzRef->getClass(), "deviceAdded", _deviceDiscoveryMethodArgs.c_str(), false); + mDeviceUpdated = search_method(env, listenerClazzRef->getClass(), "deviceUpdated", _deviceDiscoveryMethodArgs.c_str(), false); + mDeviceRemoved = search_method(env, listenerClazzRef->getClass(), "deviceRemoved", _deviceDiscoveryMethodArgs.c_str(), false); + if( nullptr == mDeviceAdded ) { + throw InternalError("BluetoothDeviceDiscoveryListener has no deviceAdded"+_deviceDiscoveryMethodArgs+" method, for "+adapter->toString(), E_FILE_LINE); + } + if( nullptr == mDeviceUpdated ) { + throw InternalError("BluetoothDeviceDiscoveryListener has no deviceUpdated"+_deviceDiscoveryMethodArgs+" method, for "+adapter->toString(), E_FILE_LINE); + } + if( nullptr == mDeviceRemoved ) { + throw InternalError("BluetoothDeviceDiscoveryListener has no deviceRemoved"+_deviceDiscoveryMethodArgs+" method, for "+adapter->toString(), E_FILE_LINE); + } + exception_check_raise_and_throw(env, E_FILE_LINE); + } + + void deviceAdded(HCIAdapter const &a, std::shared_ptr<HCIDevice> device) override { + try { + #ifdef VERBOSE_ON + fprintf(stderr, "****** Native Adapter Device ADDED__: %s\n", device->toString().c_str()); + fprintf(stderr, "Status HCIAdapter:\n"); + fprintf(stderr, "%s\n", a.toString().c_str()); + #endif + + // Device(final long nativeInstance, final Adapter adptr, final String address, final String name) + const jstring addr = from_string_to_jstring(*jni_env, device->getAddressString()); + const jstring name = from_string_to_jstring(*jni_env, device->getName()); + jobject jDevice = jni_env->NewObject(deviceClazzRef->getClass(), deviceClazzCtor, + (jlong)device.get(), adapterObjRef, addr, name, (jlong)device->ts_creation); + exception_check_raise_and_throw(*jni_env, E_FILE_LINE); + JNIGlobalRef::check(jDevice, E_FILE_LINE); + std::shared_ptr<JavaAnonObj> jDeviceRef = device->getJavaObject(); + JavaGlobalObj::check(jDeviceRef, E_FILE_LINE); + + jni_env->CallVoidMethod(listenerObjRef->getObject(), mDeviceAdded, JavaGlobalObj::GetObject(adapterObjRef), JavaGlobalObj::GetObject(jDeviceRef)); + exception_check_raise_and_throw(*jni_env, E_FILE_LINE); + } CATCH_EXCEPTION_AND_RAISE_JAVA(*jni_env, e) + } + void deviceUpdated(HCIAdapter const &a, std::shared_ptr<HCIDevice> device) override { + try { + #ifdef VERBOSE_ON + fprintf(stderr, "****** Native Adapter Device UPDATED: %s\n", device->toString().c_str()); + fprintf(stderr, "Status HCIAdapter:\n"); + fprintf(stderr, "%s\n", a.toString().c_str()); + #endif + std::shared_ptr<JavaAnonObj> jDeviceRef = device->getJavaObject(); + JavaGlobalObj::check(jDeviceRef, E_FILE_LINE); + jni_env->SetLongField(JavaGlobalObj::GetObject(jDeviceRef), deviceClazzTSUpdateField, (jlong)device->getUpdateTimestamp()); + jni_env->CallVoidMethod(listenerObjRef->getObject(), mDeviceUpdated, JavaGlobalObj::GetObject(adapterObjRef), JavaGlobalObj::GetObject(jDeviceRef)); + exception_check_raise_and_throw(*jni_env, E_FILE_LINE); + } CATCH_EXCEPTION_AND_RAISE_JAVA(*jni_env, e) + } + void deviceRemoved(HCIAdapter const &a, std::shared_ptr<HCIDevice> device) override { + try { + #ifdef VERBOSE_ON + fprintf(stderr, "****** DBTAdapter Device REMOVED: %s\n", device->toString().c_str()); + fprintf(stderr, "Status HCIAdapter:\n"); + fprintf(stderr, "%s\n", a.toString().c_str()); + #endif + std::shared_ptr<JavaAnonObj> jDeviceRef = device->getJavaObject(); + JavaGlobalObj::check(jDeviceRef, E_FILE_LINE); + jni_env->SetLongField(JavaGlobalObj::GetObject(jDeviceRef), deviceClazzTSUpdateField, (jlong)device->getUpdateTimestamp()); + jni_env->CallVoidMethod(listenerObjRef->getObject(), mDeviceRemoved, JavaGlobalObj::GetObject(adapterObjRef), JavaGlobalObj::GetObject(jDeviceRef)); + exception_check_raise_and_throw(*jni_env, E_FILE_LINE); + } CATCH_EXCEPTION_AND_RAISE_JAVA(*jni_env, e) + } +}; + +void Java_direct_1bt_tinyb_Adapter_initImpl(JNIEnv *env, jobject obj, jobject deviceDiscoveryListener) +{ + // org.tinyb.BluetoothDeviceDiscoveryListener + try { + HCIAdapter *adapter = getInstance<HCIAdapter>(env, obj); + JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE); + + // set our callback discovery listener. + DeviceDiscoveryCallbackListener *l = new DeviceDiscoveryCallbackListener(env, adapter, deviceDiscoveryListener); + adapter->setDeviceDiscoveryListener(std::shared_ptr<HCIDeviceDiscoveryListener>(l)); + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) +} + +void Java_direct_1bt_tinyb_Adapter_deleteImpl(JNIEnv *env, jobject obj) +{ + try { + HCIAdapter *adapter = getInstance<HCIAdapter>(env, obj); + DBG_PRINT("Java_direct_1bt_tinyb_Adapter_deleteImpl %s", adapter->toString().c_str()); + delete adapter; + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) +} + +jboolean Java_direct_1bt_tinyb_Adapter_startDiscoveryImpl(JNIEnv *env, jobject obj) +{ + try { + HCIAdapter *adapter = getInstance<HCIAdapter>(env, obj); + std::shared_ptr<direct_bt::HCISession> session = adapter->getOpenSession(); + if( nullptr == session ) { + throw BluetoothException("No adapter session: "+adapter->toString(), E_FILE_LINE); + } + if( !session->isOpen() ) { + throw BluetoothException("No open adapter session: "+adapter->toString(), E_FILE_LINE); + } + return adapter->startDiscovery(*session); + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) + return JNI_FALSE; +} + +jboolean Java_direct_1bt_tinyb_Adapter_stopDiscoveryImpl(JNIEnv *env, jobject obj) +{ + try { + HCIAdapter *adapter = getInstance<HCIAdapter>(env, obj); + std::shared_ptr<direct_bt::HCISession> session = adapter->getOpenSession(); + if( nullptr == session ) { + throw BluetoothException("No adapter session: "+adapter->toString(), E_FILE_LINE); + } + if( !session->isOpen() ) { + throw BluetoothException("No open adapter session: "+adapter->toString(), E_FILE_LINE); + } + adapter->setDeviceDiscoveryListener(nullptr); + adapter->stopDiscovery(*session); + return JNI_TRUE; + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) + return JNI_FALSE; +} + +jint Java_direct_1bt_tinyb_Adapter_discoverAnyDeviceImpl(JNIEnv *env, jobject obj, jint timeoutMS) +{ + try { + HCIAdapter *adapter = getInstance<HCIAdapter>(env, obj); + std::shared_ptr<direct_bt::HCISession> session = adapter->getOpenSession(); + if( nullptr == session ) { + throw BluetoothException("No adapter session: "+adapter->toString(), E_FILE_LINE); + } + if( !session->isOpen() ) { + throw BluetoothException("No open adapter session: "+adapter->toString(), E_FILE_LINE); + } + return adapter->discoverDevices(*session, -1, EUI48_ANY_DEVICE, timeoutMS, static_cast<uint32_t>(EInfoReport::Element::NAME)); + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) + return 0; +} + +jobject Java_direct_1bt_tinyb_Adapter_getDiscoveredDevicesImpl(JNIEnv *env, jobject obj) +{ + try { + HCIAdapter *adapter = getInstance<HCIAdapter>(env, obj); + /** + std::function<jobject(JNIEnv*, jclass, jmethodID, HCIDevice*)> ctor_device = [&](JNIEnv *env, jclass clazz, jmethodID clazz_ctor, HCIDevice *elem) { + // Device(final long nativeInstance, final Adapter adptr, final String address, final String name) + jstring addr = from_string_to_jstring(env, elem->getAddressString()); + jstring name = from_string_to_jstring(env, elem->getName()); + jobject object = env->NewObject(clazz, clazz_ctor, (jlong)elem, obj, addr, name); + return object; + }; + return convert_vector_to_jobject<HCIDevice>(env, array, "(JLdirect_bt/tinyb/Adapter;Ljava/lang/String;Ljava/lang/String;)V", ctor_device); + */ + std::vector<std::shared_ptr<HCIDevice>> array = adapter->getDiscoveredDevices(); + return convert_vector_to_jobject(env, array); + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) + return nullptr; +} + +jint Java_direct_1bt_tinyb_Adapter_removeDevicesImpl(JNIEnv *env, jobject obj) +{ + try { + HCIAdapter *adapter = getInstance<HCIAdapter>(env, obj); + return adapter->removeDiscoveredDevices(); + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) + return 0; +} + diff --git a/java/jni/direct_bt/DBTDevice.cxx b/java/jni/direct_bt/DBTDevice.cxx new file mode 100644 index 00000000..b2887b55 --- /dev/null +++ b/java/jni/direct_bt/DBTDevice.cxx @@ -0,0 +1,78 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "direct_bt_tinyb_Device.h" + +#include "JNIMem.hpp" +#include "helper_base.hpp" +#include "helper_dbt.hpp" + +#include "direct_bt/HCITypes.hpp" + +using namespace direct_bt; + +void Java_direct_1bt_tinyb_Device_initImpl(JNIEnv *env, jobject obj) +{ + try { + HCIDevice *device = getInstance<HCIDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) +} + +void Java_direct_1bt_tinyb_Device_deleteImpl(JNIEnv *env, jobject obj) +{ + try { + HCIDevice *device = getInstance<HCIDevice>(env, obj); + delete device; + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) +} + +jshort Java_direct_1bt_tinyb_Device_getRSSI(JNIEnv *env, jobject obj) +{ + try { + HCIDevice *device = getInstance<HCIDevice>(env, obj); + return device->getRSSI(); + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) + return 0; +} + +jshort Java_direct_1bt_tinyb_Device_getTxPower(JNIEnv *env, jobject obj) +{ + try { + HCIDevice *device = getInstance<HCIDevice>(env, obj); + return device->getTxPower(); + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) + return 0; +} + +jboolean Java_direct_1bt_tinyb_Device_getConnected(JNIEnv *env, jobject obj) +{ + try { + HCIDevice *device = getInstance<HCIDevice>(env, obj); + (void) device; + return JNI_TRUE; // FIXME + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) + return JNI_FALSE; +} diff --git a/java/jni/HCIEvent.cxx b/java/jni/direct_bt/DBTEvent.cxx index b1bc89c5..e9203d48 100644 --- a/java/jni/HCIEvent.cxx +++ b/java/jni/direct_bt/DBTEvent.cxx @@ -23,9 +23,9 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "tinyb_hci_HCIEvent.h" +#include "direct_bt_tinyb_DBTEvent.h" -jobject Java_tinyb_hci_HCIEvent_getType(JNIEnv *env, jobject obj) +jobject Java_direct_1bt_tinyb_DBTEvent_getType(JNIEnv *env, jobject obj) { (void)env; (void)obj; @@ -33,7 +33,7 @@ jobject Java_tinyb_hci_HCIEvent_getType(JNIEnv *env, jobject obj) return NULL; } -jstring Java_tinyb_hci_HCIEvent_getName(JNIEnv *env, jobject obj) +jstring Java_direct_1bt_tinyb_DBTEvent_getName(JNIEnv *env, jobject obj) { (void)env; (void)obj; @@ -41,7 +41,7 @@ jstring Java_tinyb_hci_HCIEvent_getName(JNIEnv *env, jobject obj) return NULL; } -jstring Java_tinyb_hci_HCIEvent_getIdentifier(JNIEnv *env, jobject obj) +jstring Java_direct_1bt_tinyb_DBTEvent_getIdentifier(JNIEnv *env, jobject obj) { (void)env; (void)obj; @@ -49,7 +49,7 @@ jstring Java_tinyb_hci_HCIEvent_getIdentifier(JNIEnv *env, jobject obj) return NULL; } -jboolean Java_tinyb_hci_HCIEvent_executeCallback(JNIEnv *env, jobject obj) +jboolean Java_direct_1bt_tinyb_DBTEvent_executeCallback(JNIEnv *env, jobject obj) { (void)env; (void)obj; @@ -57,7 +57,7 @@ jboolean Java_tinyb_hci_HCIEvent_executeCallback(JNIEnv *env, jobject obj) return JNI_FALSE; } -jboolean Java_tinyb_hci_HCIEvent_hasCallback(JNIEnv *env, jobject obj) +jboolean Java_direct_1bt_tinyb_DBTEvent_hasCallback(JNIEnv *env, jobject obj) { (void)env; (void)obj; @@ -65,9 +65,9 @@ jboolean Java_tinyb_hci_HCIEvent_hasCallback(JNIEnv *env, jobject obj) return JNI_FALSE; } -void Java_tinyb_hci_HCIEvent_init(JNIEnv *env, jobject obj, jobject type, jstring name, - jstring identifier, jobject parent, jobject callback, - jobject arg_data) +void Java_direct_1bt_tinyb_DBTEvent_init(JNIEnv *env, jobject obj, jobject type, jstring name, + jstring identifier, jobject parent, jobject callback, + jobject arg_data) { (void)env; (void)obj; @@ -79,7 +79,7 @@ void Java_tinyb_hci_HCIEvent_init(JNIEnv *env, jobject obj, jobject type, jstrin (void)arg_data; } -void Java_tinyb_hci_HCIEvent_delete(JNIEnv *env, jobject obj) +void Java_direct_1bt_tinyb_DBTEvent_delete(JNIEnv *env, jobject obj) { (void)env; (void)obj; diff --git a/java/jni/HCIGattCharacteristic.cxx b/java/jni/direct_bt/DBTGattCharacteristic.cxx index 0a0c4c07..cae33960 100644 --- a/java/jni/HCIGattCharacteristic.cxx +++ b/java/jni/direct_bt/DBTGattCharacteristic.cxx @@ -23,12 +23,12 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "direct_bt/HCITypes.hpp" - -#include "tinyb_hci_HCIGattCharacteristic.h" +#include "direct_bt_tinyb_GattCharacteristic.h" #include "JNIMem.hpp" #include "helper_base.hpp" +#include "direct_bt/HCITypes.hpp" + using namespace direct_bt; diff --git a/java/jni/HCIAdapter.cxx b/java/jni/direct_bt/DBTGattDescriptor.cxx index afb420fc..59410d92 100644 --- a/java/jni/HCIAdapter.cxx +++ b/java/jni/direct_bt/DBTGattDescriptor.cxx @@ -23,12 +23,12 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "direct_bt/HCITypes.hpp" - -#include "tinyb_hci_HCIAdapter.h" +#include "direct_bt_tinyb_GattDescriptor.h" #include "JNIMem.hpp" #include "helper_base.hpp" +#include "direct_bt/HCITypes.hpp" + using namespace direct_bt; diff --git a/java/jni/HCIGattDescriptor.cxx b/java/jni/direct_bt/DBTGattService.cxx index cefc6b4d..6e0fe141 100644 --- a/java/jni/HCIGattDescriptor.cxx +++ b/java/jni/direct_bt/DBTGattService.cxx @@ -23,12 +23,12 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "direct_bt/HCITypes.hpp" - -#include "tinyb_hci_HCIGattDescriptor.h" +#include "direct_bt_tinyb_GattService.h" #include "JNIMem.hpp" #include "helper_base.hpp" +#include "direct_bt/HCITypes.hpp" + using namespace direct_bt; diff --git a/java/jni/direct_bt/DBTManager.cxx b/java/jni/direct_bt/DBTManager.cxx new file mode 100644 index 00000000..0c01272d --- /dev/null +++ b/java/jni/direct_bt/DBTManager.cxx @@ -0,0 +1,106 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "direct_bt_tinyb_Manager.h" + +// #define VERBOSE_ON 1 +#include <dbt_debug.hpp> + +#include "JNIMem.hpp" +#include "helper_base.hpp" +#include "helper_dbt.hpp" + +#include "direct_bt/HCITypes.hpp" + +using namespace direct_bt; + +void Java_direct_1bt_tinyb_Manager_initImpl(JNIEnv *env, jobject obj) +{ + try { + MgmtHandler *manager = &MgmtHandler::get(); // special: static singleton + setInstance<MgmtHandler>(env, obj, manager); + manager->setJavaObject( std::shared_ptr<JavaAnonObj>( new JavaGlobalObj(obj) ) ); + JavaGlobalObj::check(manager->getJavaObject(), E_FILE_LINE); + DBG_PRINT("Java_direct_1bt_tinyb_Manager_init: Manager %s", manager->toString().c_str()); + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) +} + +void Java_direct_1bt_tinyb_Manager_deleteImpl(JNIEnv *env, jobject obj) +{ + try { + MgmtHandler *manager = getInstance<MgmtHandler>(env, obj); // special: static singleton + manager->setJavaObject(nullptr); + // delete manager; + (void) manager; + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) +} + +static const std::string _adapterClazzCtorArgs("(JLjava/lang/String;Ljava/lang/String;)V"); + +jobject Java_direct_1bt_tinyb_Manager_getDefaultAdapterImpl(JNIEnv *env, jobject obj) +{ + try { + MgmtHandler *manager = getInstance<MgmtHandler>(env, obj); + DBG_PRINT("Java_direct_1bt_tinyb_Manager_getDefaultAdapterImpl: Manager %s", manager->toString().c_str()); + int defAdapterIdx = manager->getDefaultAdapterIdx(); + HCIAdapter * adapter = new HCIAdapter(defAdapterIdx); + if( !adapter->isValid() ) { + delete adapter; + throw BluetoothException("Invalid default adapter "+std::to_string(defAdapterIdx), E_FILE_LINE); + } + if( !adapter->hasDevId() ) { + delete adapter; + throw BluetoothException("Invalid default adapter dev-id "+std::to_string(defAdapterIdx), E_FILE_LINE); + } + std::shared_ptr<direct_bt::HCISession> session = adapter->open(); + if( nullptr == session ) { + delete adapter; + throw BluetoothException("Couldn't open default adapter "+std::to_string(defAdapterIdx), E_FILE_LINE); + } + + // prepare adapter ctor + const jstring addr = from_string_to_jstring(env, adapter->getAddressString()); + const jstring name = from_string_to_jstring(env, adapter->getName()); + const jclass clazz = search_class(env, *adapter); + if( nullptr == clazz ) { + throw InternalError("Adapter class not found: "+HCIAdapter::java_class(), E_FILE_LINE); + } + const jmethodID clazz_ctor = search_method(env, clazz, "<init>", _adapterClazzCtorArgs.c_str(), false); + if( nullptr == clazz_ctor ) { + throw InternalError("Adapter ctor not found: "+HCIAdapter::java_class()+".<init>"+_adapterClazzCtorArgs, E_FILE_LINE); + } + exception_check_raise_and_throw(env, E_FILE_LINE); + jobject jAdapter = env->NewObject(clazz, clazz_ctor, (jlong)adapter, addr, name); + exception_check_raise_and_throw(env, E_FILE_LINE); + JNIGlobalRef::check(jAdapter, E_FILE_LINE); + std::shared_ptr<JavaAnonObj> jAdapterRef = adapter->getJavaObject(); + JavaGlobalObj::check(jAdapterRef, E_FILE_LINE); + + DBG_PRINT("Java_direct_1bt_tinyb_Manager_getDefaultAdapterImpl: New Adapter %s", adapter->toString().c_str()); + return JavaGlobalObj::GetObject(jAdapterRef); + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) + return NULL; +} + diff --git a/java/jni/direct_bt/DBTNativeDownlink.cxx b/java/jni/direct_bt/DBTNativeDownlink.cxx new file mode 100644 index 00000000..201d0728 --- /dev/null +++ b/java/jni/direct_bt/DBTNativeDownlink.cxx @@ -0,0 +1,58 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "direct_bt_tinyb_NativeDownlink.h" + +// #define VERBOSE_ON 1 +#include <dbt_debug.hpp> + +#include "JNIMem.hpp" +#include "helper_base.hpp" +#include "helper_dbt.hpp" + +#include "direct_bt/HCITypes.hpp" + +using namespace direct_bt; + +void Java_direct_1bt_tinyb_NativeDownlink_initNativeJavaObject(JNIEnv *env, jobject obj, jlong nativeInstance) +{ + try { + JavaUplink *javaUplink = castInstance<JavaUplink>(nativeInstance); + std::shared_ptr<JavaGlobalObj> jobjRef( new JavaGlobalObj(obj) ); + javaUplink->setJavaObject( jobjRef ); + JavaGlobalObj::check(javaUplink->getJavaObject(), E_FILE_LINE); + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) +} + +void Java_direct_1bt_tinyb_NativeDownlink_clearNativeJavaObject(JNIEnv *env, jobject obj, jlong nativeInstance) +{ + (void)obj; + try { + JavaUplink *javaUplink = castInstance<JavaUplink>(nativeInstance); + DBG_PRINT("Java_direct_1bt_tinyb_NativeDownlink_clearNativeJavaObject %s", javaUplink->toString().c_str()); + javaUplink->setJavaObject(nullptr); + } CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) +} + diff --git a/java/jni/HCIGattService.cxx b/java/jni/direct_bt/DBTObject.cxx index 14dfcbe8..74f40e8a 100644 --- a/java/jni/HCIGattService.cxx +++ b/java/jni/direct_bt/DBTObject.cxx @@ -23,12 +23,12 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "direct_bt/HCITypes.hpp" - -#include "tinyb_hci_HCIGattService.h" +// #include "direct_bt_tinyb_DBTObject.h" #include "JNIMem.hpp" #include "helper_base.hpp" +#include "direct_bt/HCITypes.hpp" + using namespace direct_bt; diff --git a/java/jni/direct_bt/helper_dbt.cxx b/java/jni/direct_bt/helper_dbt.cxx new file mode 100644 index 00000000..b3431edc --- /dev/null +++ b/java/jni/direct_bt/helper_dbt.cxx @@ -0,0 +1,65 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <jni.h> +#include <memory> +#include <stdexcept> +#include <vector> + +#include "helper_dbt.hpp" + +using namespace direct_bt; + +jclass direct_bt::search_class(JNIEnv *env, JavaUplink &object) +{ + return search_class(env, object.get_java_class().c_str()); +} + +#if 0 + +jobject direct_bt::convert_vector_to_jobject(JNIEnv *env, std::vector<std::shared_ptr<JavaUplink>>& array) +{ + unsigned int array_size = array.size(); + + jmethodID arraylist_add; + jobject result = get_new_arraylist(env, array_size, &arraylist_add); + + if (0 == array_size) { + return result; + } + + for (unsigned int i = 0; i < array_size; ++i) { + std::shared_ptr<JavaUplink> elem = array.at(i); + std::shared_ptr<JNIGlobalRef> objref = elem->getJavaObject(); + if ( nullptr == objref ) { + throw InternalError("JavaUplink element of array has no valid java-object: "+elem->toString(), E_FILE_LINE); + } + env->CallBooleanMethod(result, arraylist_add, objref->get()); + } + return result; +} + +#endif + diff --git a/java/jni/direct_bt/helper_dbt.hpp b/java/jni/direct_bt/helper_dbt.hpp new file mode 100644 index 00000000..2f3d265f --- /dev/null +++ b/java/jni/direct_bt/helper_dbt.hpp @@ -0,0 +1,113 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef HELPER_DBT_HPP_ +#define HELPER_DBT_HPP_ + +#include "JNIMem.hpp" +#include "helper_base.hpp" + +#include "direct_bt/JavaAccess.hpp" +#include "direct_bt/BasicTypes.hpp" + +namespace direct_bt { + + class JavaGlobalObj : public JavaAnonObj { + private: + JNIGlobalRef javaObjectRef; + + public: + static inline void check(const std::shared_ptr<JavaAnonObj> & shref, const char* file, int line) { + if( nullptr == shref ) { + throw direct_bt::RuntimeException("JavaGlobalObj::check: Null shared-JavaAnonObj", file, line); + } + const jobject obj = static_cast<const JavaGlobalObj*>(shref.get())->getObject(); + if( nullptr == obj ) { + throw direct_bt::RuntimeException("JavaGlobalObj::check: Null object", file, line); + } + } + JavaGlobalObj(jobject obj) : javaObjectRef(obj) { } + ~JavaGlobalObj() override { } + + std::string toString() const override { + const uint64_t ref = (uint64_t)(void*)javaObjectRef.getObject(); + return "JavaGlobalObj["+uint64HexString(ref, true)+"]"; + } + + JNIGlobalRef & getJavaObject() { return javaObjectRef; } + + /* Provides access to the stored GlobalRef as an jobject. */ + jobject getObject() const { return javaObjectRef.getObject(); } + /* Provides access to the stored GlobalRef as a jclass. */ + jclass getClass() const { return javaObjectRef.getClass(); } + + /* Provides access to the stored GlobalRef as an jobject. */ + static jobject GetObject(const std::shared_ptr<JavaAnonObj> & shref) { + return static_cast<JavaGlobalObj*>(shref.get())->getObject(); + } + + /* Provides access to the stored GlobalRef as a jclass. */ + static jclass GetClass(const std::shared_ptr<JavaAnonObj> & shref) { + return static_cast<JavaGlobalObj*>(shref.get())->getClass(); + } + }; + + + jclass search_class(JNIEnv *env, JavaUplink &object); + +#if 0 + + jobject convert_vector_to_jobject(JNIEnv *env, std::vector<std::shared_ptr<JavaUplink>>& array); + +#else + + template <typename T> + jobject convert_vector_to_jobject(JNIEnv *env, std::vector<std::shared_ptr<T>>& array) + { + unsigned int array_size = array.size(); + + jmethodID arraylist_add; + jobject result = get_new_arraylist(env, array_size, &arraylist_add); + + if (0 == array_size) { + return result; + } + + for (unsigned int i = 0; i < array_size; ++i) { + std::shared_ptr<T> elem = array.at(i); + std::shared_ptr<JavaAnonObj> objref = elem->getJavaObject(); + if ( nullptr == objref ) { + throw InternalError("JavaUplink element of array has no valid java-object: "+elem->toString(), E_FILE_LINE); + } + env->CallBooleanMethod(result, arraylist_add, JavaGlobalObj::GetObject(objref)); + } + return result; + } + +#endif + +} // namespace direct_bt + +#endif /* HELPER_DBT_HPP_ */ diff --git a/java/jni/helper_base.cxx b/java/jni/helper_base.cxx index 2c0c23e6..dd3dcf54 100644 --- a/java/jni/helper_base.cxx +++ b/java/jni/helper_base.cxx @@ -31,7 +31,9 @@ #include <stdexcept> #include <vector> -#include "helper_tinyb.hpp" +#include "helper_base.hpp" + +#define JAVA_MAIN_PACKAGE "org/tinyb" jfieldID getInstanceField(JNIEnv *env, jobject obj) { @@ -144,6 +146,11 @@ std::string from_jstring_to_string(JNIEnv *env, jstring str) return string_to_write; } +jstring from_string_to_jstring(JNIEnv *env, const std::string & str) +{ + return env->NewStringUTF(str.c_str()); +} + jobject get_bluetooth_type(JNIEnv *env, const char *field_name) { jclass b_type_enum = search_class(env, JAVA_MAIN_PACKAGE "/BluetoothType"); @@ -182,6 +189,10 @@ void raise_java_runtime_exception(JNIEnv *env, std::runtime_error &e) env->ThrowNew(env->FindClass("java/lang/RuntimeException"), e.what()); } +void raise_java_runtime_exception(JNIEnv *env, direct_bt::RuntimeException &e) { + env->ThrowNew(env->FindClass("java/lang/RuntimeException"), e.what()); +} + void raise_java_oom_exception(JNIEnv *env, std::bad_alloc &e) { env->ThrowNew(env->FindClass("java/lang/OutOfMemoryException"), e.what()); @@ -192,4 +203,18 @@ void raise_java_invalid_arg_exception(JNIEnv *env, std::invalid_argument &e) env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"), e.what()); } +void raise_java_bluetooth_exception(JNIEnv *env, direct_bt::BluetoothException &e) +{ + env->ThrowNew(env->FindClass("org/tinyb/BluetoothException"), e.what()); +} +void exception_check_raise_and_throw(JNIEnv *env, const char* file, int line) +{ + if( env->ExceptionCheck() ) { + env->ExceptionDescribe(); + jthrowable e = env->ExceptionOccurred(); + env->ExceptionClear(); + env->Throw(e); + throw direct_bt::RuntimeException("Java exception occurred and forwarded.", file, line); + } +} diff --git a/java/jni/helper_base.hpp b/java/jni/helper_base.hpp index 153faa9b..4cbda995 100644 --- a/java/jni/helper_base.hpp +++ b/java/jni/helper_base.hpp @@ -26,10 +26,15 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#pragma once +#ifndef HELPER_BASE_HPP_ +#define HELPER_BASE_HPP_ #include <vector> #include <memory> +#include <functional> +#include <jni.h> + +#include "BasicTypes.hpp" jfieldID getInstanceField(JNIEnv *env, jobject obj); @@ -41,10 +46,20 @@ jfieldID search_field(JNIEnv *env, jclass clazz, const char *field_name, const char *type, bool is_static); bool from_jboolean_to_bool(jboolean val); std::string from_jstring_to_string(JNIEnv *env, jstring str); +jstring from_string_to_jstring(JNIEnv *env, const std::string & str); jobject get_bluetooth_type(JNIEnv *env, const char *field_name); jobject get_new_arraylist(JNIEnv *env, unsigned int size, jmethodID *add); template <typename T> +T *castInstance(jlong instance) +{ + T *t = reinterpret_cast<T *>(instance); + if (t == nullptr) + throw std::runtime_error("Trying to cast null object"); + return t; +} + +template <typename T> T *getInstance(JNIEnv *env, jobject obj) { jlong instance = env->GetLongField(obj, getInstanceField(env, obj)); @@ -111,7 +126,58 @@ jobject convert_vector_to_jobject(JNIEnv *env, std::vector<std::unique_ptr<T>>& return result; } +template <typename T> +jobject convert_vector_to_jobject(JNIEnv *env, std::vector<std::shared_ptr<T>>& array, + const char *ctor_prototype, std::function<jobject(JNIEnv*, jclass, jmethodID, T*)> ctor) +{ + unsigned int array_size = array.size(); + + jmethodID arraylist_add; + jobject result = get_new_arraylist(env, array_size, &arraylist_add); + + if (array_size == 0) + { + return result; + } + + jclass clazz = search_class(env, T::java_class().c_str()); + jmethodID clazz_ctor = search_method(env, clazz, "<init>", ctor_prototype, false); + + for (unsigned int i = 0; i < array_size; ++i) + { + T *elem = array.at(i).get(); + jobject object = ctor(env, clazz, clazz_ctor, elem); + if (!object) + { + throw std::runtime_error("cannot create instance of class\n"); + } + env->CallBooleanMethod(result, arraylist_add, object); + } + return result; +} + void raise_java_exception(JNIEnv *env, std::exception &e); void raise_java_runtime_exception(JNIEnv *env, std::runtime_error &e); +void raise_java_runtime_exception(JNIEnv *env, direct_bt::RuntimeException &e); void raise_java_oom_exception(JNIEnv *env, std::bad_alloc &e); void raise_java_invalid_arg_exception(JNIEnv *env, std::invalid_argument &e); +void raise_java_bluetooth_exception(JNIEnv *env, direct_bt::BluetoothException &e); + +void exception_check_raise_and_throw(JNIEnv *env, const char* file, int line); + +#define CATCH_EXCEPTION_AND_RAISE_JAVA(env, e) \ + catch (std::bad_alloc &e) { \ + raise_java_oom_exception(env, e); \ +} catch (direct_bt::BluetoothException &e) { \ + raise_java_bluetooth_exception(env, e); \ +} catch (direct_bt::RuntimeException &e) { \ + raise_java_runtime_exception(env, e); \ +} catch (std::runtime_error &e) { \ + raise_java_runtime_exception(env, e); \ +} catch (std::invalid_argument &e) { \ + raise_java_invalid_arg_exception(env, e); \ +} catch (std::exception &e) { \ + raise_java_exception(env, e); \ +} + +#endif /* HELPER_BASE_HPP_ */ diff --git a/java/jni/tinyb/CMakeLists.txt b/java/jni/tinyb/CMakeLists.txt new file mode 100644 index 00000000..a2bfb5a7 --- /dev/null +++ b/java/jni/tinyb/CMakeLists.txt @@ -0,0 +1,54 @@ +find_package(JNI REQUIRED) + +if (JNI_FOUND) + message (STATUS "JNI_INCLUDE_DIRS=${JNI_INCLUDE_DIRS}") + message (STATUS "JNI_LIBRARIES=${JNI_LIBRARIES}") +endif (JNI_FOUND) + +set (tinyb_LIB_INCLUDE_DIRS + ${PROJECT_SOURCE_DIR}/api + ${PROJECT_SOURCE_DIR}/api/direct_bt + ${PROJECT_SOURCE_DIR}/api/tinyb + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/java/jni +) + +include_directories( + ${JNI_INCLUDE_DIRS} + ${tinyb_LIB_INCLUDE_DIRS} + ${JNI_HEADER_PATH} +) + +set (tinyb_JNI_SRCS + ${PROJECT_SOURCE_DIR}/src/direct_bt/BasicTypes.cpp + ${PROJECT_SOURCE_DIR}/java/jni/JNIMem.cxx + ${PROJECT_SOURCE_DIR}/java/jni/helper_base.cxx + ${PROJECT_SOURCE_DIR}/java/jni/BluetoothFactory.cxx + ${PROJECT_SOURCE_DIR}/java/jni/BluetoothUtils.cxx + ${PROJECT_SOURCE_DIR}/java/jni/tinyb/helper_tinyb.cxx + ${PROJECT_SOURCE_DIR}/java/jni/tinyb/DBusAdapter.cxx + ${PROJECT_SOURCE_DIR}/java/jni/tinyb/DBusDevice.cxx + ${PROJECT_SOURCE_DIR}/java/jni/tinyb/DBusEvent.cxx + ${PROJECT_SOURCE_DIR}/java/jni/tinyb/DBusGattCharacteristic.cxx + ${PROJECT_SOURCE_DIR}/java/jni/tinyb/DBusGattDescriptor.cxx + ${PROJECT_SOURCE_DIR}/java/jni/tinyb/DBusGattService.cxx + ${PROJECT_SOURCE_DIR}/java/jni/tinyb/DBusManager.cxx + ${PROJECT_SOURCE_DIR}/java/jni/tinyb/DBusObject.cxx +) + +set (CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed") + +add_library (javatinyb SHARED ${tinyb_JNI_SRCS}) +target_link_libraries(javatinyb ${JNI_LIBRARIES} tinyb) + +set_target_properties( + javatinyb + PROPERTIES + SOVERSION ${tinyb_VERSION_MAJOR} + VERSION ${tinyb_VERSION_STRING} + CXX_STANDARD 11 + COMPILE_FLAGS "-Wall -Wextra" +) + +install(TARGETS javatinyb LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + diff --git a/java/jni/DBusAdapter.cxx b/java/jni/tinyb/DBusAdapter.cxx index 9f43c4b8..9f43c4b8 100644 --- a/java/jni/DBusAdapter.cxx +++ b/java/jni/tinyb/DBusAdapter.cxx diff --git a/java/jni/DBusDevice.cxx b/java/jni/tinyb/DBusDevice.cxx index 6a833356..6a833356 100644 --- a/java/jni/DBusDevice.cxx +++ b/java/jni/tinyb/DBusDevice.cxx diff --git a/java/jni/DBusEvent.cxx b/java/jni/tinyb/DBusEvent.cxx index 14b6431c..14b6431c 100644 --- a/java/jni/DBusEvent.cxx +++ b/java/jni/tinyb/DBusEvent.cxx diff --git a/java/jni/DBusGattCharacteristic.cxx b/java/jni/tinyb/DBusGattCharacteristic.cxx index 43ac754f..43ac754f 100644 --- a/java/jni/DBusGattCharacteristic.cxx +++ b/java/jni/tinyb/DBusGattCharacteristic.cxx diff --git a/java/jni/DBusGattDescriptor.cxx b/java/jni/tinyb/DBusGattDescriptor.cxx index 2118205c..2118205c 100644 --- a/java/jni/DBusGattDescriptor.cxx +++ b/java/jni/tinyb/DBusGattDescriptor.cxx diff --git a/java/jni/DBusGattService.cxx b/java/jni/tinyb/DBusGattService.cxx index 0ab089f2..0ab089f2 100644 --- a/java/jni/DBusGattService.cxx +++ b/java/jni/tinyb/DBusGattService.cxx diff --git a/java/jni/DBusManager.cxx b/java/jni/tinyb/DBusManager.cxx index 1680e866..1680e866 100644 --- a/java/jni/DBusManager.cxx +++ b/java/jni/tinyb/DBusManager.cxx diff --git a/java/jni/DBusObject.cxx b/java/jni/tinyb/DBusObject.cxx index 8e3b93a2..8e3b93a2 100644 --- a/java/jni/DBusObject.cxx +++ b/java/jni/tinyb/DBusObject.cxx diff --git a/java/jni/helper_tinyb.cxx b/java/jni/tinyb/helper_tinyb.cxx index 865d55e5..c20b5933 100644 --- a/java/jni/helper_tinyb.cxx +++ b/java/jni/tinyb/helper_tinyb.cxx @@ -33,76 +33,78 @@ #include "helper_tinyb.hpp" -jclass search_class(JNIEnv *env, tinyb::BluetoothObject &object) +using namespace tinyb; + +jclass search_class(JNIEnv *env, BluetoothObject &object) { return search_class(env, object.get_java_class().c_str()); } -tinyb::BluetoothType from_int_to_btype(int type) +BluetoothType from_int_to_btype(int type) { - tinyb::BluetoothType result = tinyb::BluetoothType::NONE; + BluetoothType result = BluetoothType::NONE; switch (type) { case 0: - result = tinyb::BluetoothType::NONE; + result = BluetoothType::NONE; break; case 1: - result = tinyb::BluetoothType::ADAPTER; + result = BluetoothType::ADAPTER; break; case 2: - result = tinyb::BluetoothType::DEVICE; + result = BluetoothType::DEVICE; break; case 3: - result = tinyb::BluetoothType::GATT_SERVICE; + result = BluetoothType::GATT_SERVICE; break; case 4: - result = tinyb::BluetoothType::GATT_CHARACTERISTIC; + result = BluetoothType::GATT_CHARACTERISTIC; break; case 5: - result = tinyb::BluetoothType::GATT_CHARACTERISTIC; + result = BluetoothType::GATT_CHARACTERISTIC; break; default: - result = tinyb::BluetoothType::NONE; + result = BluetoothType::NONE; break; } return result; } -tinyb::TransportType from_int_to_transport_type(int type) +TransportType from_int_to_transport_type(int type) { - tinyb::TransportType result = tinyb::TransportType::AUTO; + TransportType result = TransportType::AUTO; switch (type) { case 0: - result = tinyb::TransportType::AUTO; + result = TransportType::AUTO; break; case 1: - result = tinyb::TransportType::BREDR; + result = TransportType::BREDR; break; case 2: - result = tinyb::TransportType::LE; + result = TransportType::LE; break; default: - result = tinyb::TransportType::AUTO; + result = TransportType::AUTO; break; } return result; } -void raise_java_bluetooth_exception(JNIEnv *env, tinyb::BluetoothException &e) +void raise_java_bluetooth_exception(JNIEnv *env, BluetoothException &e) { env->ThrowNew(env->FindClass("org/tinyb/BluetoothException"), e.what()); } diff --git a/java/jni/helper_tinyb.hpp b/java/jni/tinyb/helper_tinyb.hpp index f1873a7f..d91051c4 100644 --- a/java/jni/helper_tinyb.hpp +++ b/java/jni/tinyb/helper_tinyb.hpp @@ -32,8 +32,12 @@ #include "tinyb/BluetoothObject.hpp" #include "tinyb/BluetoothException.hpp" -jclass search_class(JNIEnv *env, tinyb::BluetoothObject &object); -tinyb::BluetoothType from_int_to_btype(int type); -tinyb::TransportType from_int_to_transport_type(int type); +namespace tinyb { -void raise_java_bluetooth_exception(JNIEnv *env, tinyb::BluetoothException &e); + jclass search_class(JNIEnv *env, BluetoothObject &object); + BluetoothType from_int_to_btype(int type); + TransportType from_int_to_transport_type(int type); + + void raise_java_bluetooth_exception(JNIEnv *env, BluetoothException &e); + +} // namespace tinyb diff --git a/java/org/tinyb/BluetoothAdapter.java b/java/org/tinyb/BluetoothAdapter.java index 95d2e6d0..2b55cf75 100644 --- a/java/org/tinyb/BluetoothAdapter.java +++ b/java/org/tinyb/BluetoothAdapter.java @@ -242,6 +242,12 @@ public interface BluetoothAdapter extends BluetoothObject public boolean getDiscovering(); /** + * Sets the {@link BluetoothDeviceDiscoveryListener} for the respective device discovery events. + * @param listener A {@link BluetoothDeviceDiscoveryListener} instance, or {@code null} to disable notifications. + */ + public void setDeviceDiscoveryListener(final BluetoothDeviceDiscoveryListener listener); + + /** * Enables notifications for the discovering property and calls run function of the * BluetoothNotification object. * @param callback A BluetoothNotification<Boolean> object. Its run function will be called diff --git a/java/org/tinyb/BluetoothDeviceDiscoveryListener.java b/java/org/tinyb/BluetoothDeviceDiscoveryListener.java new file mode 100644 index 00000000..737f47b9 --- /dev/null +++ b/java/org/tinyb/BluetoothDeviceDiscoveryListener.java @@ -0,0 +1,42 @@ +/** + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.tinyb; + +/** + * {@link BluetoothDevice} listener for the respective {@link BluetoothDevice} discovery events: Added, updated and removed. + * <p> + * A listener instance may be attached to a {@link BluetoothAdapter} via + * {@link BluetoothAdapter#setDeviceDiscoveryListener(BluetoothDeviceDiscoveryListener)}. + * </p> + */ +public interface BluetoothDeviceDiscoveryListener { + /** A {@link BluetoothDevice} has been newly discovered. */ + public void deviceAdded(final BluetoothAdapter adapter, final BluetoothDevice device); + /** An already discovered {@link BluetoothDevice} has been updated. */ + public void deviceUpdated(final BluetoothAdapter adapter, final BluetoothDevice device); + /** An already discovered {@link BluetoothDevice} has been removed or lost. */ + public void deviceRemoved(final BluetoothAdapter adapter, final BluetoothDevice device); +}; diff --git a/java/org/tinyb/BluetoothFactory.java b/java/org/tinyb/BluetoothFactory.java index 8f01db15..38306a16 100644 --- a/java/org/tinyb/BluetoothFactory.java +++ b/java/org/tinyb/BluetoothFactory.java @@ -39,12 +39,12 @@ public class BluetoothFactory { /** * Name of the native implementation native library basename: {@value} */ - public static final String ImplNativeLibBasename = "tinyb"; + public static final String ImplNativeLibBasename = "direct_bt"; // "tinyb"; /** * Name of the Jave native library basename: {@value} */ - public static final String JavaNativeLibBasename = "javatinyb"; + public static final String JavaNativeLibBasename = "javadirect_bt"; // "javatinyb"; /** * Manifest's {@link Attributes.Name#SPECIFICATION_VERSION} or {@code null} if not available. @@ -138,12 +138,12 @@ public class BluetoothFactory { public static final String DBusFactoryImplClassName = "tinyb.dbus.DBusManager"; /** - * Fully qualified factory class name for native HCI implementation: {@value} + * Fully qualified factory class name for direct_bt implementation: {@value} * <p> * This value is exposed for convenience, user implementations are welcome. * </p> */ - public static final String HCIFactoryImplClassName = "tinyb.hci.HCIManager"; + public static final String DirectBTFactoryImplClassName = "direct_bt.tinyb.Manager"; /** * Returns an initialized BluetoothManager instance using the given {@code factoryImplClass}. @@ -187,7 +187,7 @@ public class BluetoothFactory { * @throws InvocationTargetException * @throws ClassNotFoundException * @see {@link #DBusFactoryImplClassName} - * @see {@link #HCIFactoryImplClassName} + * @see {@link #DirectBTFactoryImplClassName} */ public static synchronized BluetoothManager getBluetoothManager(final String factoryImplClassName) throws BluetoothException, NoSuchMethodException, SecurityException, diff --git a/java/org/tinyb/BluetoothManager.java b/java/org/tinyb/BluetoothManager.java index ec1de2d7..b7bec3cb 100644 --- a/java/org/tinyb/BluetoothManager.java +++ b/java/org/tinyb/BluetoothManager.java @@ -181,4 +181,13 @@ public interface BluetoothManager * @return TRUE if discovery is running */ public boolean getDiscovering() throws BluetoothException; + + /** + * Release the native memory associated with this object and all related Bluetooth resources. + * The object should not be used following a call to close + * <p> + * Shutdown method is intended to allow a clean Bluetooth state at program exist. + * </p> + */ + public void shutdown(); } diff --git a/java/jni/HCIDevice.cxx b/java/org/tinyb/BluetoothUtils.java index d143b85d..e7a0276b 100644 --- a/java/jni/HCIDevice.cxx +++ b/java/org/tinyb/BluetoothUtils.java @@ -1,4 +1,4 @@ -/* +/** * Author: Sven Gothel <[email protected]> * Copyright (c) 2020 Gothel Software e.K. * Copyright (c) 2020 ZAFENA AB @@ -22,13 +22,13 @@ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +package org.tinyb; -#include "direct_bt/HCITypes.hpp" +public class BluetoothUtils { -#include "tinyb_hci_HCIDevice.h" - -#include "JNIMem.hpp" -#include "helper_base.hpp" - -using namespace direct_bt; + /** + * Returns current monotonic time in milliseconds. + */ + public static native long getCurrentMilliseconds(); +} diff --git a/java/tinyb/dbus/DBusAdapter.java b/java/tinyb/dbus/DBusAdapter.java index 695677fe..973d3e8d 100644 --- a/java/tinyb/dbus/DBusAdapter.java +++ b/java/tinyb/dbus/DBusAdapter.java @@ -35,6 +35,7 @@ import java.util.UUID; import org.tinyb.BluetoothAdapter; import org.tinyb.BluetoothDevice; +import org.tinyb.BluetoothDeviceDiscoveryListener; import org.tinyb.BluetoothException; import org.tinyb.BluetoothManager; import org.tinyb.BluetoothNotification; @@ -148,6 +149,11 @@ public class DBusAdapter extends DBusObject implements BluetoothAdapter public native boolean getDiscovering(); @Override + public void setDeviceDiscoveryListener(final BluetoothDeviceDiscoveryListener l) { + throw new UnsupportedOperationException(); // FIXME + } + + @Override public native void enableDiscoveringNotifications(BluetoothNotification<Boolean> callback); @Override diff --git a/java/tinyb/dbus/DBusManager.java b/java/tinyb/dbus/DBusManager.java index 2b5efe65..bfcd5e4b 100644 --- a/java/tinyb/dbus/DBusManager.java +++ b/java/tinyb/dbus/DBusManager.java @@ -134,4 +134,9 @@ public class DBusManager implements BluetoothManager { delete(); } + + @Override + public void shutdown() { + delete(); + } } diff --git a/java/tinyb/hci/HCIAdapter.java b/java/tinyb/hci/HCIAdapter.java deleted file mode 100644 index 71c9d818..00000000 --- a/java/tinyb/hci/HCIAdapter.java +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Author: Sven Gothel <[email protected]> - * Copyright (c) 2020 Gothel Software e.K. - * Copyright (c) 2020 ZAFENA AB - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package tinyb.hci; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; - -import org.tinyb.BluetoothAdapter; -import org.tinyb.BluetoothDevice; -import org.tinyb.BluetoothException; -import org.tinyb.BluetoothManager; -import org.tinyb.BluetoothNotification; -import org.tinyb.BluetoothType; -import org.tinyb.TransportType; - -public class HCIAdapter extends HCIObject implements BluetoothAdapter -{ - private final String address; - private final String name; - - /* pp */ HCIAdapter(final String address, final String name) - { - super(compHash(address, name)); - this.address = address; - this.name = name; - } - - @Override - public boolean equals(final Object obj) - { - if (obj == null || !(obj instanceof HCIDevice)) { - return false; - } - final HCIAdapter other = (HCIAdapter)obj; - return address.equals(other.address) && name.equals(other.name); - } - - @Override - public String getAddress() { return address; } - - @Override - public String getName() { return name; } - - public String getInterfaceName() { - throw new UnsupportedOperationException(); // FIXME - } - - @Override - public native BluetoothType getBluetoothType(); - - @Override - public native BluetoothAdapter clone(); - - static BluetoothType class_type() { return BluetoothType.ADAPTER; } - - @Override - public BluetoothDevice find(final String name, final String address, final long timeoutMS) { - final BluetoothManager manager = HCIManager.getBluetoothManager(); - return (BluetoothDevice) manager.find(BluetoothType.DEVICE, name, address, this, timeoutMS); - } - - @Override - public BluetoothDevice find(final String name, final String address) { - return find(name, address, 0); - } - - /* D-Bus method calls: */ - - @Override - public native boolean startDiscovery() throws BluetoothException; - - @Override - public native boolean stopDiscovery() throws BluetoothException; - - @Override - public native List<BluetoothDevice> getDevices(); - - @Override - public native int removeDevices() throws BluetoothException; - - /* D-Bus property accessors: */ - - @Override - public native String getAlias(); - - @Override - public native void setAlias(String value); - - @Override - public native long getBluetoothClass(); - - @Override - public native boolean getPowered(); - - @Override - public native void enablePoweredNotifications(BluetoothNotification<Boolean> callback); - - @Override - public native void disablePoweredNotifications(); - - @Override - public native void setPowered(boolean value); - - @Override - public native boolean getDiscoverable(); - - @Override - public native void enableDiscoverableNotifications(BluetoothNotification<Boolean> callback); - - @Override - public native void disableDiscoverableNotifications(); - - @Override - public native void setDiscoverable(boolean value); - - @Override - public native long getDiscoverableTimeout(); - - @Override - public native void setDiscoverableTimout(long value); - - @Override - public native BluetoothDevice connectDevice(String address, String addressType); - - @Override - public native boolean getPairable(); - - @Override - public native void enablePairableNotifications(BluetoothNotification<Boolean> callback); - - @Override - public native void disablePairableNotifications(); - - @Override - public native void setPairable(boolean value); - - @Override - public native long getPairableTimeout(); - - @Override - public native void setPairableTimeout(long value); - - @Override - public native boolean getDiscovering(); - - @Override - public native void enableDiscoveringNotifications(BluetoothNotification<Boolean> callback); - - @Override - public native void disableDiscoveringNotifications(); - - @Override - public native String[] getUUIDs(); - - @Override - public native String getModalias(); - - @Override - public void setDiscoveryFilter(final List<UUID> uuids, final int rssi, final int pathloss, final TransportType transportType) { - final List<String> uuidsFmt = new ArrayList<>(uuids.size()); - for (final UUID uuid : uuids) { - uuidsFmt.add(uuid.toString()); - } - setDiscoveryFilter(uuidsFmt, rssi, pathloss, transportType.ordinal()); - } - - public void setRssiDiscoveryFilter(final int rssi) { - setDiscoveryFilter(Collections.EMPTY_LIST, rssi, 0, TransportType.AUTO); - } - - private native void delete(); - - private native void setDiscoveryFilter(List<String> uuids, int rssi, int pathloss, int transportType); -} diff --git a/scripts/run-dbt_scanner.sh b/scripts/run-dbt_scanner.sh index c69b4343..bbeb6968 100755 --- a/scripts/run-dbt_scanner.sh +++ b/scripts/run-dbt_scanner.sh @@ -1,5 +1,9 @@ #!/bin/sh +# +# ../scripts/run-dbt_scanner.sh -wait -mac C0:26:DA:01:DA:B1 2>&1 | tee ~/dbt_scanner.log +# + if [ ! -e bin/dbt_scanner -o ! -e lib/libtinyb.so -o ! -e lib/libdirect_bt.so ] ; then echo run from dist directory exit 1 diff --git a/scripts/run-java-scanner.sh b/scripts/run-java-scanner.sh deleted file mode 100755 index d4f40875..00000000 --- a/scripts/run-java-scanner.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -if [ ! -e lib/java/tinyb2.jar -o ! -e bin/java/ScannerTinyB.jar -o ! -e lib/libtinyb.so -o ! -e lib/libdirect_bt.so ] ; then - echo run from dist directory - exit 1 -fi -java -cp lib/java/tinyb2.jar:bin/java/ScannerTinyB.jar -Djava.library.path=`pwd`/lib ScannerTinyB $* diff --git a/scripts/run-java-scanner00.sh b/scripts/run-java-scanner00.sh new file mode 100755 index 00000000..509b5d79 --- /dev/null +++ b/scripts/run-java-scanner00.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +if [ ! -e lib/java/tinyb2.jar -o ! -e bin/java/ScannerTinyB00.jar -o ! -e lib/libtinyb.so -o ! -e lib/libdirect_bt.so ] ; then + echo run from dist directory + exit 1 +fi +java -cp lib/java/tinyb2.jar:bin/java/ScannerTinyB00.jar -Djava.library.path=`pwd`/lib ScannerTinyB00 $* diff --git a/scripts/run-java-scanner01.sh b/scripts/run-java-scanner01.sh new file mode 100755 index 00000000..fec669f2 --- /dev/null +++ b/scripts/run-java-scanner01.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# +# ../scripts/run-java-scanner01.sh -mac C0:26:DA:01:DA:B1 -mode 0 2>&1 | tee ~/dbt_scanner_java.log +# + +if [ ! -e lib/java/tinyb2.jar -o ! -e bin/java/ScannerTinyB01.jar -o ! -e lib/libtinyb.so -o ! -e lib/libdirect_bt.so ] ; then + echo run from dist directory + exit 1 +fi +java -cp lib/java/tinyb2.jar:bin/java/ScannerTinyB01.jar -Djava.library.path=`pwd`/lib ScannerTinyB01 $* diff --git a/src/direct_bt/ATTPDUTypes.cpp b/src/direct_bt/ATTPDUTypes.cpp index d32667a2..88747d25 100644 --- a/src/direct_bt/ATTPDUTypes.cpp +++ b/src/direct_bt/ATTPDUTypes.cpp @@ -23,6 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include <dbt_debug.hpp> #include <cstring> #include <string> #include <memory> @@ -34,7 +35,6 @@ #include "ATTPDUTypes.hpp" -#include "dbt_debug.hpp" using namespace direct_bt; diff --git a/src/direct_bt/BTTypes.cpp b/src/direct_bt/BTTypes.cpp index bfd955f6..bb79ad14 100644 --- a/src/direct_bt/BTTypes.cpp +++ b/src/direct_bt/BTTypes.cpp @@ -23,6 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include <dbt_debug.hpp> #include <cstring> #include <string> #include <memory> @@ -34,7 +35,6 @@ #include "BTTypes.hpp" -#include "dbt_debug.hpp" std::string EUI48::toString() const { const int length = 17; diff --git a/src/direct_bt/BasicTypes.cpp b/src/direct_bt/BasicTypes.cpp index aa848b7e..6bda705c 100644 --- a/src/direct_bt/BasicTypes.cpp +++ b/src/direct_bt/BasicTypes.cpp @@ -38,7 +38,7 @@ extern "C" { } #endif -#include "dbt_debug.hpp" +#include <dbt_debug.hpp> using namespace direct_bt; @@ -59,21 +59,23 @@ int64_t direct_bt::getCurrentMilliseconds() { return t.tv_sec * MilliPerOne + t.tv_nsec / NanoPerMilli; } -const char* RuntimeException::what() const noexcept { +const char* direct_bt::RuntimeException::what() const noexcept { #if _USE_BACKTRACE_ + // std::string out(std::runtime_error::what()); std::string out(msg); void *buffers[10]; size_t nptrs = backtrace(buffers, 10); char **symbols = backtrace_symbols(buffers, nptrs); if( NULL != symbols ) { out.append("\nBacktrace:\n"); - for(int i=0; i<nptrs; i++) { + for(size_t i=0; i<nptrs; i++) { out.append(symbols[i]).append("\n"); } free(symbols); } return out.c_str(); #else + // return std::runtime_error::what(); return msg.c_str(); #endif } @@ -195,6 +197,19 @@ std::string direct_bt::uint32HexString(const uint32_t v, const bool leading0X) { return str; } +std::string direct_bt::uint64HexString(const uint64_t v, const bool leading0X) { + const int length = leading0X ? 18 : 16; // ( '0x0000000000000000' | '0000000000000000' ) + std::string str; + str.reserve(length+1); // including EOS for snprintf + str.resize(length); + + const int count = snprintf(&str[0], str.capacity(), ( leading0X ? "0x%.16" PRIX64 : "%.16" PRIX64 ), v); + if( length != count ) { + throw InternalError("uint64_t string not of length "+std::to_string(length)+" but "+std::to_string(count), E_FILE_LINE); + } + return str; +} + static const char* HEX_ARRAY = "0123456789ABCDEF"; std::string direct_bt::bytesHexString(const uint8_t * bytes, const int offset, const int length, const bool lsbFirst, const bool leading0X) { diff --git a/src/direct_bt/GATTHandler.cpp b/src/direct_bt/GATTHandler.cpp index 75370a3e..27183b42 100644 --- a/src/direct_bt/GATTHandler.cpp +++ b/src/direct_bt/GATTHandler.cpp @@ -47,7 +47,7 @@ extern "C" { #include "GATTHandler.hpp" -#include "dbt_debug.hpp" +#include <dbt_debug.hpp> using namespace direct_bt; diff --git a/src/direct_bt/GATTNumbers.cpp b/src/direct_bt/GATTNumbers.cpp index 970d94e2..564e5427 100644 --- a/src/direct_bt/GATTNumbers.cpp +++ b/src/direct_bt/GATTNumbers.cpp @@ -23,6 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include <dbt_debug.hpp> #include <cstring> #include <string> #include <memory> @@ -34,7 +35,6 @@ #include "GATTNumbers.hpp" -#include "dbt_debug.hpp" using namespace direct_bt; diff --git a/src/direct_bt/GATTTypes.cpp b/src/direct_bt/GATTTypes.cpp index 14a5d3dd..bedc0dba 100644 --- a/src/direct_bt/GATTTypes.cpp +++ b/src/direct_bt/GATTTypes.cpp @@ -23,6 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include <dbt_debug.hpp> #include <cstring> #include <string> #include <memory> @@ -34,7 +35,6 @@ #include "GATTTypes.hpp" -#include "dbt_debug.hpp" using namespace direct_bt; diff --git a/src/direct_bt/HCIAdapter.cpp b/src/direct_bt/HCIAdapter.cpp index 2b3d7e51..da9db337 100644 --- a/src/direct_bt/HCIAdapter.cpp +++ b/src/direct_bt/HCIAdapter.cpp @@ -32,6 +32,9 @@ #include <algorithm> +// #define VERBOSE_ON 1 +#include <dbt_debug.hpp> + #include "BTIoctl.hpp" #include "HCIIoctl.hpp" @@ -44,8 +47,6 @@ extern "C" { #include <poll.h> } -#include "dbt_debug.hpp" - using namespace direct_bt; // ************************************************* @@ -58,6 +59,7 @@ void HCISession::disconnect(const uint8_t reason) { connectedDevice = nullptr; if( !hciComm.isLEConnected() ) { + DBG_PRINT("HCISession::disconnect: Not connected"); return; } if( !hciComm.le_disconnect(reason) ) { @@ -68,10 +70,12 @@ void HCISession::disconnect(const uint8_t reason) { bool HCISession::close() { if( !hciComm.isOpen() ) { + DBG_PRINT("HCISession::close: Not open"); return false; } + DBG_PRINT("HCISession::close: ..."); + adapter.sessionClosing(*this); hciComm.close(); - adapter.sessionClosed(*this); return true; } @@ -88,9 +92,9 @@ bool HCIAdapter::validateDevInfo() { return true; } -void HCIAdapter::sessionClosed(HCISession& s) +void HCIAdapter::sessionClosing(HCISession& s) { - (void)s; + stopDiscovery(s); session = nullptr; } @@ -113,6 +117,9 @@ HCIAdapter::HCIAdapter(const int dev_id) } HCIAdapter::~HCIAdapter() { + DBG_PRINT("HCIAdapter::dtor: %s", toString().c_str()); + deviceDiscoveryListener = nullptr; + scannedDevices.clear(); discoveredDevices.clear(); session = nullptr; @@ -156,8 +163,10 @@ bool HCIAdapter::startDiscovery(HCISession &session, uint8_t own_mac_type, void HCIAdapter::stopDiscovery(HCISession& session) { if( !session.isOpen() ) { + DBG_PRINT("HCIAdapter::stopDiscovery: Not open"); return; } + DBG_PRINT("HCIAdapter::stopDiscovery: ..."); session.hciComm.le_disable_scan(); } @@ -213,14 +222,16 @@ bool HCIAdapter::addDiscoveredDevice(std::shared_ptr<HCIDevice> const &device) { return false; } -void HCIAdapter::removeDiscoveredDevices() { +int HCIAdapter::removeDiscoveredDevices() { // also need to flush scannedDevices, old data scannedDevices.clear(); + int res = discoveredDevices.size(); discoveredDevices.clear(); + return res; } std::string HCIAdapter::toString() const { - std::string out("Adapter["+getAddressString()+", '"+getName()+"', id="+std::to_string(dev_id)+"]"); + std::string out("Adapter["+getAddressString()+", '"+getName()+"', id="+std::to_string(dev_id)+", "+javaObjectToString()+"]"); if(discoveredDevices.size() > 0 ) { out.append("\n"); for(auto it = discoveredDevices.begin(); it != discoveredDevices.end(); it++) { diff --git a/src/direct_bt/HCIComm.cpp b/src/direct_bt/HCIComm.cpp index 8768a0ed..a45bf781 100644 --- a/src/direct_bt/HCIComm.cpp +++ b/src/direct_bt/HCIComm.cpp @@ -33,6 +33,7 @@ #include <algorithm> // #define VERBOSE_ON 1 +#include <dbt_debug.hpp> #include "HCIComm.hpp" @@ -47,8 +48,6 @@ extern "C" { #include <poll.h> } -#include "dbt_debug.hpp" - namespace direct_bt { int HCIComm::hci_open_dev(const uint16_t dev_id, const uint16_t channel) diff --git a/src/direct_bt/HCIDevice.cpp b/src/direct_bt/HCIDevice.cpp index 6abd854a..4ef9354d 100644 --- a/src/direct_bt/HCIDevice.cpp +++ b/src/direct_bt/HCIDevice.cpp @@ -23,6 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include <dbt_debug.hpp> #include <cstring> #include <string> #include <memory> @@ -35,7 +36,6 @@ #include "HCIComm.hpp" #include "HCITypes.hpp" -#include "dbt_debug.hpp" using namespace direct_bt; @@ -93,7 +93,7 @@ std::string HCIDevice::toString() const { std::string msdstr = nullptr != msd ? msd->toString() : "MSD[null]"; std::string out("Device["+getAddressString()+", '"+getName()+ "', age "+std::to_string(t0-ts_creation)+" ms, lup "+std::to_string(t0-ts_update)+" ms, rssi "+std::to_string(getRSSI())+ - ", tx-power "+std::to_string(tx_power)+", "+msdstr+"]"); + ", tx-power "+std::to_string(tx_power)+", "+msdstr+", "+javaObjectToString()+"]"); if(services.size() > 0 ) { out.append("\n"); int i=0; diff --git a/src/direct_bt/L2CAPComm.cpp b/src/direct_bt/L2CAPComm.cpp index e23ffa74..dfa8778e 100644 --- a/src/direct_bt/L2CAPComm.cpp +++ b/src/direct_bt/L2CAPComm.cpp @@ -45,7 +45,7 @@ extern "C" { #include <poll.h> } -#include "dbt_debug.hpp" +#include <dbt_debug.hpp> using namespace direct_bt; diff --git a/src/direct_bt/MgmtComm.cpp b/src/direct_bt/MgmtComm.cpp index 6c1b9dbc..d4ac14ef 100644 --- a/src/direct_bt/MgmtComm.cpp +++ b/src/direct_bt/MgmtComm.cpp @@ -48,7 +48,7 @@ extern "C" { #include <poll.h> } -#include "dbt_debug.hpp" +#include <dbt_debug.hpp> using namespace direct_bt; diff --git a/src/direct_bt/UUID.cpp b/src/direct_bt/UUID.cpp index 4acfb2e2..83cec826 100644 --- a/src/direct_bt/UUID.cpp +++ b/src/direct_bt/UUID.cpp @@ -23,9 +23,9 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include <dbt_debug.hpp> #include "UUID.hpp" -#include "dbt_debug.hpp" using namespace direct_bt; |