You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

IConnectionBluetooth.cpp 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. #include "IConnectionBluetooth.h"
  2. #if defined TARGET_OS_Windows
  3. #define WIN32_LEAN_AND_MEAN
  4. #define UNICODE
  5. #include <Windows.h>
  6. #include <CommCtrl.h>
  7. #include <codecvt>
  8. #include <WinSock2.h>
  9. #include <ws2bth.h>
  10. #include <bluetoothapis.h>
  11. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  12. #include <sys/time.h>
  13. #include <sys/types.h>
  14. #include <sys/select.h>
  15. #include <sys/stat.h>
  16. #include <sys/ioctl.h>
  17. #include <unistd.h>
  18. #include <fcntl.h>
  19. #include <termios.h>
  20. #else
  21. #error "Unsupported platform"
  22. #endif
  23. #include <assert.h>
  24. #include <vector>
  25. #include <string>
  26. namespace Socket {
  27. class CConnectionBluetooth final : public IConnectionBluetooth
  28. {
  29. public:
  30. #if defined TARGET_OS_Windows
  31. CConnectionBluetooth() : m_Socket(INVALID_SOCKET) { }
  32. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  33. CConnectionBluetooth() : m_LastError() { }
  34. #endif
  35. #if defined TARGET_OS_Windows
  36. bool initialize()
  37. {
  38. WSADATA wsaData;
  39. // Ask for Winsock version.
  40. if (_WINSOCK2API_::WSAStartup(MAKEWORD(WIN_SOCKET_MAJOR_VERSION, WIN_SOCKET_MINOR_VERSION), &wsaData) != 0)
  41. {
  42. m_LastError = "Failed to start Winsock " + std::to_string(WIN_SOCKET_MAJOR_VERSION) + "." + std::to_string(WIN_SOCKET_MINOR_VERSION) + ": " + this->
  43. getLastErrorFormated();
  44. return false;
  45. }
  46. // Confirm that the WinSock DLL supports version requested.
  47. // Note that if the DLL supports versions greater than the version requested, in addition to the version requested, it will still return the version requested in wVersion.
  48. if (LOBYTE(wsaData.wVersion) != WIN_SOCKET_MAJOR_VERSION || HIBYTE(wsaData.wVersion) != WIN_SOCKET_MINOR_VERSION)
  49. {
  50. m_LastError = "Could not find a usable version of Winsock.dll.";
  51. _WINSOCK2API_::WSACleanup();
  52. return false;
  53. }
  54. return true;
  55. }
  56. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  57. bool initialize() { return false; }
  58. #endif
  59. bool open() override { return false; }
  60. bool close() override
  61. {
  62. if (!this->isConnected())
  63. {
  64. m_LastError = "Bluetooth device is not connected.";
  65. return false;
  66. }
  67. #if defined TARGET_OS_Windows
  68. bool isSuccess = true;
  69. if (m_Socket != INVALID_SOCKET)
  70. {
  71. // shutdown the connection since no more data will be sent or received
  72. if (_WINSOCK2API_::shutdown(m_Socket, SD_BOTH) == SOCKET_ERROR)
  73. {
  74. m_LastError = "Failed to shutdown the bluetooth socket:" + this->getLastErrorFormated();
  75. isSuccess = false;
  76. }
  77. if (_WINSOCK2API_::closesocket(m_Socket) == SOCKET_ERROR)
  78. {
  79. m_LastError = "Failed to close the bluetooth socket:" + this->getLastErrorFormated();
  80. isSuccess = false;
  81. }
  82. if (_WINSOCK2API_::WSACleanup() == SOCKET_ERROR)
  83. {
  84. m_LastError = "Failed to cleanup the bluetooth socket:" + this->getLastErrorFormated();
  85. isSuccess = false;
  86. }
  87. m_Socket = INVALID_SOCKET;
  88. }
  89. return isSuccess;
  90. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  91. return false;
  92. #endif
  93. }
  94. bool isReadyToSend(const size_t /*timeOut*/) const override { return this->isConnected(); }
  95. bool isReadyToReceive(const size_t /*timeOut*/) const override
  96. {
  97. if (!this->isConnected()) { return false; }
  98. #if defined TARGET_OS_Windows
  99. unsigned long nPendingBytes = 0;
  100. if (_WINSOCK2API_::ioctlsocket(m_Socket, FIONREAD, &nPendingBytes) == SOCKET_ERROR)
  101. {
  102. //m_LastError = "Failed to get the pending bytes count: " + this->getLastErrorFormated();
  103. return false;
  104. }
  105. return nPendingBytes != 0;
  106. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  107. return false;
  108. #endif
  109. }
  110. size_t getPendingByteCount() override
  111. {
  112. if (!this->isConnected())
  113. {
  114. m_LastError = "Bluetooth device not connected.";
  115. return 0;
  116. }
  117. #if defined TARGET_OS_Windows
  118. unsigned long nPendingBytes = 0;
  119. if (_WINSOCK2API_::ioctlsocket(m_Socket, FIONREAD, &nPendingBytes) == SOCKET_ERROR)
  120. {
  121. m_LastError = "Failed to get the pending bytes count: " + this->getLastErrorFormated();
  122. return 0;
  123. }
  124. return nPendingBytes;
  125. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  126. return 0;
  127. #endif
  128. }
  129. size_t sendBuffer(const void* buffer, const size_t size) override
  130. {
  131. if (!this->isConnected())
  132. {
  133. m_LastError = "Bluetooth device is not connected.";
  134. return 0;
  135. }
  136. #if defined TARGET_OS_Windows
  137. const int nBytesSent = _WINSOCK2API_::send(m_Socket, reinterpret_cast<const char*>(buffer), int(size), 0);
  138. if (nBytesSent == SOCKET_ERROR)
  139. {
  140. m_LastError = "Failed to write on the bluetooth port: " + getLastErrorFormated();
  141. this->close();
  142. return 0;
  143. }
  144. return size_t(nBytesSent);
  145. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  146. return 0;
  147. #endif
  148. }
  149. size_t receiveBuffer(void* buffer, const size_t size) override
  150. {
  151. if (!this->isConnected())
  152. {
  153. m_LastError = "Bluetooth device is not connected.";
  154. return 0;
  155. }
  156. #if defined TARGET_OS_Windows
  157. const int nBytesReceived = _WINSOCK2API_::recv(m_Socket, static_cast<char*>(buffer), int(size), 0);
  158. if (nBytesReceived == SOCKET_ERROR)
  159. {
  160. m_LastError = "Failed to receive data from bluetooth: " + getLastErrorFormated();
  161. this->close();
  162. return 0;
  163. }
  164. return size_t(nBytesReceived);
  165. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  166. return 0;
  167. #endif
  168. }
  169. bool sendBufferBlocking(const void* buffer, const size_t size) override
  170. {
  171. if (!this->isConnected())
  172. {
  173. m_LastError = "Bluetooth device is not connected.";
  174. return false;
  175. }
  176. const char* p = reinterpret_cast<const char*>(buffer);
  177. size_t bytesLeft = size;
  178. while (bytesLeft != 0 && this->isConnected())
  179. {
  180. bytesLeft -= this->sendBuffer(p + size - bytesLeft, bytesLeft);
  181. if (this->isErrorRaised()) { return false; }
  182. }
  183. return bytesLeft == 0;
  184. }
  185. bool receiveBufferBlocking(void* buffer, const size_t size) override
  186. {
  187. if (!this->isConnected())
  188. {
  189. m_LastError = "Bluetooth device is not connected.";
  190. return false;
  191. }
  192. char* p = reinterpret_cast<char*>(buffer);
  193. size_t bytesLeft = size;
  194. while (bytesLeft != 0 && this->isConnected())
  195. {
  196. bytesLeft -= this->receiveBuffer(p + size - bytesLeft, bytesLeft);
  197. if (this->isErrorRaised()) { return false; }
  198. }
  199. return bytesLeft == 0;
  200. }
  201. bool isConnected() const override
  202. {
  203. #if defined TARGET_OS_Windows
  204. return m_Socket != INVALID_SOCKET;
  205. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  206. return false;
  207. #endif
  208. }
  209. void release() override { delete this; }
  210. bool connect(const uint64_t u64BluetoothAddress) override
  211. {
  212. m_LastError.clear();
  213. if (this->isConnected())
  214. {
  215. m_LastError = "Bluetooth device is already connected";
  216. return false;
  217. }
  218. #if defined TARGET_OS_Windows
  219. if (!this->initialize()) { return false; }
  220. m_Socket = _WINSOCK2API_::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
  221. if (m_Socket == INVALID_SOCKET)
  222. {
  223. m_LastError = "Failed to create bluetooth socket: " + getLastErrorFormated();
  224. _WINSOCK2API_::WSACleanup();
  225. return false;
  226. }
  227. SOCKADDR_BTH sockAddressBlutoothServer;
  228. sockAddressBlutoothServer.btAddr = u64BluetoothAddress;
  229. sockAddressBlutoothServer.addressFamily = AF_BTH;
  230. sockAddressBlutoothServer.serviceClassId = RFCOMM_PROTOCOL_UUID;
  231. sockAddressBlutoothServer.port = BT_PORT_ANY;
  232. if (_WINSOCK2API_::connect(m_Socket, reinterpret_cast<SOCKADDR*>(&sockAddressBlutoothServer), sizeof(SOCKADDR_BTH)) == SOCKET_ERROR)
  233. {
  234. m_LastError = "Failed to connect the socket to the bluetooth address [" + std::to_string(sockAddressBlutoothServer.btAddr) + "]: " +
  235. getLastErrorFormated();
  236. _WINSOCK2API_::closesocket(m_Socket); // Returned code not checked.
  237. _WINSOCK2API_::WSACleanup(); // Returned code not checked.
  238. m_Socket = INVALID_SOCKET;
  239. return false;
  240. }
  241. return true;
  242. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  243. return false;
  244. #endif
  245. }
  246. bool isErrorRaised() const { return !m_LastError.empty(); }
  247. const char* getLastError() const override { return m_LastError.c_str(); }
  248. static std::string getLastErrorFormated()
  249. {
  250. #if defined TARGET_OS_Windows
  251. LPTSTR text;
  252. const DWORD errCode = GetLastError();
  253. const size_t size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | // use system message tables to retrieve error text
  254. FORMAT_MESSAGE_ALLOCATE_BUFFER | // allocate buffer on local heap for error text
  255. FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters
  256. nullptr, // unused with FORMAT_MESSAGE_FROM_SYSTEM
  257. errCode, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
  258. LPTSTR(&text), // output
  259. 0, // minimum size for output buffer
  260. nullptr);
  261. // Converts std::wstring to std::string and returns it.
  262. const std::wstring message(text, size);
  263. LocalFree(text);
  264. std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
  265. return converter.to_bytes(message);
  266. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  267. return "";
  268. #endif
  269. }
  270. void clearError() override { m_LastError.clear(); }
  271. bool listPairedBluetoothDevices(size_t* nPairedBluetoothDevices, char** names, uint64_t** addresses) override
  272. {
  273. std::vector<std::string> devicesNames;
  274. std::vector<uint64_t> devicesAddresses;
  275. #if defined TARGET_OS_Windows
  276. HANDLE handle;
  277. WSAQUERYSET wsaQuerySet;
  278. memset((void*)&wsaQuerySet, 0, sizeof(wsaQuerySet));
  279. wsaQuerySet.dwSize = sizeof(wsaQuerySet);
  280. wsaQuerySet.dwNameSpace = NS_BTH;
  281. wsaQuerySet.lpcsaBuffer = nullptr;
  282. if (_WINSOCK2API_::WSALookupServiceBegin(&wsaQuerySet, LUP_CONTAINERS | LUP_RETURN_NAME | LUP_RETURN_ADDR, &handle) == SOCKET_ERROR)
  283. {
  284. m_LastError = "Failed to start the Bluetooth lookup service: " + getLastErrorFormated();
  285. return false;
  286. }
  287. char buffer[5000];
  288. const LPWSAQUERYSET wsaQuerySetW = LPWSAQUERYSET(buffer);
  289. DWORD size = sizeof(buffer);
  290. memset((void*)wsaQuerySetW, 0, sizeof(WSAQUERYSET));
  291. wsaQuerySetW->dwSize = sizeof(WSAQUERYSET);
  292. wsaQuerySetW->dwNameSpace = NS_BTH;
  293. wsaQuerySetW->lpBlob = nullptr;
  294. bool lookup = true;
  295. while (lookup)
  296. {
  297. // Check next bluetooth device
  298. const int res = _WINSOCK2API_::WSALookupServiceNext(handle, LUP_RETURN_NAME | LUP_RETURN_ADDR, &size, wsaQuerySetW);
  299. if (res == SOCKET_ERROR)
  300. {
  301. // If it is a "real" error, we trace it and return false.
  302. if (_WINSOCK2API_::WSAGetLastError() != WSA_E_NO_MORE)
  303. {
  304. m_LastError = "Lookup service next operation failed: " + getLastErrorFormated();
  305. return false;
  306. }
  307. // Else, it is because there is no more Bluetooth devices available.
  308. lookup = false;
  309. break;
  310. }
  311. // Get bluetooth MAC address and name
  312. devicesAddresses.push_back(reinterpret_cast<SOCKADDR_BTH*>(wsaQuerySetW->lpcsaBuffer->RemoteAddr.lpSockaddr)->btAddr);
  313. std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converterX;
  314. devicesNames.push_back(converterX.to_bytes(wsaQuerySetW->lpszServiceInstanceName));
  315. }
  316. if (_WINSOCK2API_::WSALookupServiceEnd(handle) == SOCKET_ERROR)
  317. {
  318. m_LastError = "Failed to stop the Bluetooth lookup service: " + getLastErrorFormated();
  319. return false;
  320. }
  321. *nPairedBluetoothDevices = devicesAddresses.size();
  322. names = new char*[*nPairedBluetoothDevices];
  323. for (size_t i = 0; i < *nPairedBluetoothDevices; ++i)
  324. {
  325. names[i] = new char[devicesNames[i].size() + 1];
  326. std::strcpy(names[i], devicesNames[i].c_str());
  327. }
  328. addresses = new uint64_t*[*nPairedBluetoothDevices];
  329. return true;
  330. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  331. return false;
  332. #endif
  333. }
  334. std::string m_LastError;
  335. #if defined TARGET_OS_Windows
  336. SOCKET m_Socket;
  337. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  338. #endif
  339. };
  340. IConnectionBluetooth* createConnectionBluetooth() { return new CConnectionBluetooth(); }
  341. } // namespace Socket