#include "IConnectionBluetooth.h" #if defined TARGET_OS_Windows #define WIN32_LEAN_AND_MEAN #define UNICODE #include #include #include #include #include #include #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS #include #include #include #include #include #include #include #include #else #error "Unsupported platform" #endif #include #include #include namespace Socket { class CConnectionBluetooth final : public IConnectionBluetooth { public: #if defined TARGET_OS_Windows CConnectionBluetooth() : m_Socket(INVALID_SOCKET) { } #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS CConnectionBluetooth() : m_LastError() { } #endif #if defined TARGET_OS_Windows bool initialize() { WSADATA wsaData; // Ask for Winsock version. if (_WINSOCK2API_::WSAStartup(MAKEWORD(WIN_SOCKET_MAJOR_VERSION, WIN_SOCKET_MINOR_VERSION), &wsaData) != 0) { m_LastError = "Failed to start Winsock " + std::to_string(WIN_SOCKET_MAJOR_VERSION) + "." + std::to_string(WIN_SOCKET_MINOR_VERSION) + ": " + this-> getLastErrorFormated(); return false; } // Confirm that the WinSock DLL supports version requested. // 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. if (LOBYTE(wsaData.wVersion) != WIN_SOCKET_MAJOR_VERSION || HIBYTE(wsaData.wVersion) != WIN_SOCKET_MINOR_VERSION) { m_LastError = "Could not find a usable version of Winsock.dll."; _WINSOCK2API_::WSACleanup(); return false; } return true; } #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS bool initialize() { return false; } #endif bool open() override { return false; } bool close() override { if (!this->isConnected()) { m_LastError = "Bluetooth device is not connected."; return false; } #if defined TARGET_OS_Windows bool isSuccess = true; if (m_Socket != INVALID_SOCKET) { // shutdown the connection since no more data will be sent or received if (_WINSOCK2API_::shutdown(m_Socket, SD_BOTH) == SOCKET_ERROR) { m_LastError = "Failed to shutdown the bluetooth socket:" + this->getLastErrorFormated(); isSuccess = false; } if (_WINSOCK2API_::closesocket(m_Socket) == SOCKET_ERROR) { m_LastError = "Failed to close the bluetooth socket:" + this->getLastErrorFormated(); isSuccess = false; } if (_WINSOCK2API_::WSACleanup() == SOCKET_ERROR) { m_LastError = "Failed to cleanup the bluetooth socket:" + this->getLastErrorFormated(); isSuccess = false; } m_Socket = INVALID_SOCKET; } return isSuccess; #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS return false; #endif } bool isReadyToSend(const size_t /*timeOut*/) const override { return this->isConnected(); } bool isReadyToReceive(const size_t /*timeOut*/) const override { if (!this->isConnected()) { return false; } #if defined TARGET_OS_Windows unsigned long nPendingBytes = 0; if (_WINSOCK2API_::ioctlsocket(m_Socket, FIONREAD, &nPendingBytes) == SOCKET_ERROR) { //m_LastError = "Failed to get the pending bytes count: " + this->getLastErrorFormated(); return false; } return nPendingBytes != 0; #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS return false; #endif } size_t getPendingByteCount() override { if (!this->isConnected()) { m_LastError = "Bluetooth device not connected."; return 0; } #if defined TARGET_OS_Windows unsigned long nPendingBytes = 0; if (_WINSOCK2API_::ioctlsocket(m_Socket, FIONREAD, &nPendingBytes) == SOCKET_ERROR) { m_LastError = "Failed to get the pending bytes count: " + this->getLastErrorFormated(); return 0; } return nPendingBytes; #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS return 0; #endif } size_t sendBuffer(const void* buffer, const size_t size) override { if (!this->isConnected()) { m_LastError = "Bluetooth device is not connected."; return 0; } #if defined TARGET_OS_Windows const int nBytesSent = _WINSOCK2API_::send(m_Socket, reinterpret_cast(buffer), int(size), 0); if (nBytesSent == SOCKET_ERROR) { m_LastError = "Failed to write on the bluetooth port: " + getLastErrorFormated(); this->close(); return 0; } return size_t(nBytesSent); #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS return 0; #endif } size_t receiveBuffer(void* buffer, const size_t size) override { if (!this->isConnected()) { m_LastError = "Bluetooth device is not connected."; return 0; } #if defined TARGET_OS_Windows const int nBytesReceived = _WINSOCK2API_::recv(m_Socket, static_cast(buffer), int(size), 0); if (nBytesReceived == SOCKET_ERROR) { m_LastError = "Failed to receive data from bluetooth: " + getLastErrorFormated(); this->close(); return 0; } return size_t(nBytesReceived); #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS return 0; #endif } bool sendBufferBlocking(const void* buffer, const size_t size) override { if (!this->isConnected()) { m_LastError = "Bluetooth device is not connected."; return false; } const char* p = reinterpret_cast(buffer); size_t bytesLeft = size; while (bytesLeft != 0 && this->isConnected()) { bytesLeft -= this->sendBuffer(p + size - bytesLeft, bytesLeft); if (this->isErrorRaised()) { return false; } } return bytesLeft == 0; } bool receiveBufferBlocking(void* buffer, const size_t size) override { if (!this->isConnected()) { m_LastError = "Bluetooth device is not connected."; return false; } char* p = reinterpret_cast(buffer); size_t bytesLeft = size; while (bytesLeft != 0 && this->isConnected()) { bytesLeft -= this->receiveBuffer(p + size - bytesLeft, bytesLeft); if (this->isErrorRaised()) { return false; } } return bytesLeft == 0; } bool isConnected() const override { #if defined TARGET_OS_Windows return m_Socket != INVALID_SOCKET; #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS return false; #endif } void release() override { delete this; } bool connect(const uint64_t u64BluetoothAddress) override { m_LastError.clear(); if (this->isConnected()) { m_LastError = "Bluetooth device is already connected"; return false; } #if defined TARGET_OS_Windows if (!this->initialize()) { return false; } m_Socket = _WINSOCK2API_::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); if (m_Socket == INVALID_SOCKET) { m_LastError = "Failed to create bluetooth socket: " + getLastErrorFormated(); _WINSOCK2API_::WSACleanup(); return false; } SOCKADDR_BTH sockAddressBlutoothServer; sockAddressBlutoothServer.btAddr = u64BluetoothAddress; sockAddressBlutoothServer.addressFamily = AF_BTH; sockAddressBlutoothServer.serviceClassId = RFCOMM_PROTOCOL_UUID; sockAddressBlutoothServer.port = BT_PORT_ANY; if (_WINSOCK2API_::connect(m_Socket, reinterpret_cast(&sockAddressBlutoothServer), sizeof(SOCKADDR_BTH)) == SOCKET_ERROR) { m_LastError = "Failed to connect the socket to the bluetooth address [" + std::to_string(sockAddressBlutoothServer.btAddr) + "]: " + getLastErrorFormated(); _WINSOCK2API_::closesocket(m_Socket); // Returned code not checked. _WINSOCK2API_::WSACleanup(); // Returned code not checked. m_Socket = INVALID_SOCKET; return false; } return true; #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS return false; #endif } bool isErrorRaised() const { return !m_LastError.empty(); } const char* getLastError() const override { return m_LastError.c_str(); } static std::string getLastErrorFormated() { #if defined TARGET_OS_Windows LPTSTR text; const DWORD errCode = GetLastError(); const size_t size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | // use system message tables to retrieve error text FORMAT_MESSAGE_ALLOCATE_BUFFER | // allocate buffer on local heap for error text FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters nullptr, // unused with FORMAT_MESSAGE_FROM_SYSTEM errCode, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), LPTSTR(&text), // output 0, // minimum size for output buffer nullptr); // Converts std::wstring to std::string and returns it. const std::wstring message(text, size); LocalFree(text); std::wstring_convert, wchar_t> converter; return converter.to_bytes(message); #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS return ""; #endif } void clearError() override { m_LastError.clear(); } bool listPairedBluetoothDevices(size_t* nPairedBluetoothDevices, char** names, uint64_t** addresses) override { std::vector devicesNames; std::vector devicesAddresses; #if defined TARGET_OS_Windows HANDLE handle; WSAQUERYSET wsaQuerySet; memset((void*)&wsaQuerySet, 0, sizeof(wsaQuerySet)); wsaQuerySet.dwSize = sizeof(wsaQuerySet); wsaQuerySet.dwNameSpace = NS_BTH; wsaQuerySet.lpcsaBuffer = nullptr; if (_WINSOCK2API_::WSALookupServiceBegin(&wsaQuerySet, LUP_CONTAINERS | LUP_RETURN_NAME | LUP_RETURN_ADDR, &handle) == SOCKET_ERROR) { m_LastError = "Failed to start the Bluetooth lookup service: " + getLastErrorFormated(); return false; } char buffer[5000]; const LPWSAQUERYSET wsaQuerySetW = LPWSAQUERYSET(buffer); DWORD size = sizeof(buffer); memset((void*)wsaQuerySetW, 0, sizeof(WSAQUERYSET)); wsaQuerySetW->dwSize = sizeof(WSAQUERYSET); wsaQuerySetW->dwNameSpace = NS_BTH; wsaQuerySetW->lpBlob = nullptr; bool lookup = true; while (lookup) { // Check next bluetooth device const int res = _WINSOCK2API_::WSALookupServiceNext(handle, LUP_RETURN_NAME | LUP_RETURN_ADDR, &size, wsaQuerySetW); if (res == SOCKET_ERROR) { // If it is a "real" error, we trace it and return false. if (_WINSOCK2API_::WSAGetLastError() != WSA_E_NO_MORE) { m_LastError = "Lookup service next operation failed: " + getLastErrorFormated(); return false; } // Else, it is because there is no more Bluetooth devices available. lookup = false; break; } // Get bluetooth MAC address and name devicesAddresses.push_back(reinterpret_cast(wsaQuerySetW->lpcsaBuffer->RemoteAddr.lpSockaddr)->btAddr); std::wstring_convert, wchar_t> converterX; devicesNames.push_back(converterX.to_bytes(wsaQuerySetW->lpszServiceInstanceName)); } if (_WINSOCK2API_::WSALookupServiceEnd(handle) == SOCKET_ERROR) { m_LastError = "Failed to stop the Bluetooth lookup service: " + getLastErrorFormated(); return false; } *nPairedBluetoothDevices = devicesAddresses.size(); names = new char*[*nPairedBluetoothDevices]; for (size_t i = 0; i < *nPairedBluetoothDevices; ++i) { names[i] = new char[devicesNames[i].size() + 1]; std::strcpy(names[i], devicesNames[i].c_str()); } addresses = new uint64_t*[*nPairedBluetoothDevices]; return true; #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS return false; #endif } std::string m_LastError; #if defined TARGET_OS_Windows SOCKET m_Socket; #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS #endif }; IConnectionBluetooth* createConnectionBluetooth() { return new CConnectionBluetooth(); } } // namespace Socket