123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- #include "IConnectionBluetooth.h"
-
- #if defined TARGET_OS_Windows
- #define WIN32_LEAN_AND_MEAN
- #define UNICODE
- #include <Windows.h>
- #include <CommCtrl.h>
- #include <codecvt>
- #include <WinSock2.h>
- #include <ws2bth.h>
- #include <bluetoothapis.h>
- #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
- #include <sys/time.h>
- #include <sys/types.h>
- #include <sys/select.h>
- #include <sys/stat.h>
- #include <sys/ioctl.h>
-
- #include <unistd.h>
- #include <fcntl.h>
- #include <termios.h>
- #else
- #error "Unsupported platform"
- #endif
-
- #include <assert.h>
- #include <vector>
- #include <string>
-
- 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<const char*>(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<char*>(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<const char*>(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<char*>(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<SOCKADDR*>(&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<std::codecvt_utf8<wchar_t>, 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<std::string> devicesNames;
- std::vector<uint64_t> 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<SOCKADDR_BTH*>(wsaQuerySetW->lpcsaBuffer->RemoteAddr.lpSockaddr)->btAddr);
-
- std::wstring_convert<std::codecvt_utf8<wchar_t>, 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
|