#include <string.h>
#include <chrono>
#include <future>
#include "HciAdapter.h"
#include "HciSocket.h"
#include "Utils.h"
#include "Mgmt.h"
#include "Logger.h"
namespace ggk {
std::thread HciAdapter::eventThread;
const char * const HciAdapter::kCommandCodeNames[kMaxCommandCode + 1] =
{
"Invalid Command",
"Read Version Information Command",
"Read Supported Commands Command",
"Read Controller Index List Command",
"Read Controller Information Command",
"Set Powered Command",
"Set Discoverable Command",
"Set Connectable Command",
"Set Fast Connectable Command",
"Set Bondable Command",
"Set Link Security Command",
"Set Secure Simple Pairing Command",
"Set High Speed Command",
"Set Low Energy Command",
"Set Device Class",
"Set Local Name Command",
"Add UUID Command",
"Remove UUID Command",
"Load Link Keys Command",
"Load Long Term Keys Command",
"Disconnect Command",
"Get Connections Command",
"PIN Code Reply Command",
"PIN Code Negative Reply Command",
"Set IO Capability Command",
"Pair Device Command",
"Cancel Pair Device Command",
"Unpair Device Command",
"User Confirmation Reply Command",
"User Confirmation Negative Reply Command",
"User Passkey Reply Command",
"User Passkey Negative Reply Command",
"Read Local Out Of Band Data Command",
"Add Remote Out Of Band Data Command",
"Remove Remote Out Of Band Data Command",
"Start Discovery Command",
"Stop Discovery Command",
"Confirm Name Command",
"Block Device Command",
"Unblock Device Command",
"Set Device ID Command",
"Set Advertising Command",
"Set BR/EDR Command",
"Set Static Address Command",
"Set Scan Parameters Command",
"Set Secure Connections Command",
"Set Debug Keys Command",
"Set Privacy Command",
"Load Identity Resolving Keys Command",
"Get Connection Information Command",
"Get Clock Information Command",
"Add Device Command",
"Remove Device Command",
"Load Connection Parameters Command",
"Read Unconfigured Controller Index List Command",
"Read Controller Configuration Information Command",
"Set External Configuration Command",
"Set Public Address Command",
"Start Service Discovery Command",
"Read Local Out Of Band Extended Data Command",
"Read Extended Controller Index List Command",
"Read Advertising Features Command",
"Add Advertising Command",
"Remove Advertising Command",
"Get Advertising Size Information Command",
"Start Limited Discovery Command",
"Read Extended Controller Information Command",
"Set Appearance Command"
};
const char * const HciAdapter::kEventTypeNames[kMaxEventType + 1] =
{
"Invalid Event",
"Command Complete Event",
"Command Status Event",
"Controller Error Event",
"Index Added Event",
"Index Removed Event",
"New Settings Event",
"Class Of Device Changed Event",
"Local Name Changed Event",
"New Link Key Event",
"New Long Term Key Event",
"Device Connected Event",
"Device Disconnected Event",
"Connect Failed Event",
"PIN Code Request Event",
"User Confirmation Request Event",
"User Passkey Request Event",
"Authentication Failed Event",
"Device Found Event",
"Discovering Event",
"Device Blocked Event",
"Device Unblocked Event",
"Device Unpaired Event",
"Passkey Notify Event",
"New Identity Resolving Key Event",
"New Signature Resolving Key Event",
"Device Added Event",
"Device Removed Event",
"New Connection Parameter Event",
"Unconfigured Index Added Event",
"Unconfigured Index Removed Event",
"New Configuration Options Event",
"Extended Index Added Event",
"Extended Index Removed Event",
"Local Out Of Band Extended Data Updated Event",
"Advertising Added Event",
"Advertising Removed Event",
"Extended Controller Information Changed Event"
};
const char * const HciAdapter::kStatusCodes[kMaxStatusCode + 1] =
{
"Success",
"Unknown Command",
"Not Connected",
"Failed",
"Connect Failed",
"Authentication Failed",
"Not Paired",
"No Resources",
"Timeout",
"Already Connected",
"Busy",
"Rejected",
"Not Supported",
"Invalid Parameters",
"Disconnected",
"Not Powered",
"Cancelled",
"Invalid Index",
"RFKilled",
"Already Paired",
"Permission Denied",
};
void runEventThread()
{
HciAdapter::getInstance().runEventThread();
}
void HciAdapter::runEventThread()
{
Logger::trace("Entering the HciAdapter event thread");
while (ggkGetServerRunState() <= ERunning && hciSocket.isConnected())
{
std::vector<uint8_t> responsePacket = std::vector<uint8_t>();
if (!hciSocket.read(responsePacket))
{
break;
}
if (responsePacket.size() < 2)
{
Logger::error(SSTR << "Invalid command response: too short");
continue;
}
uint16_t eventCode = Utils::endianToHost(*reinterpret_cast<uint16_t *>(responsePacket.data()));
if (eventCode < HciAdapter::kMinEventType || eventCode > HciAdapter::kMaxEventType)
{
Logger::error(SSTR << "Invalid command response: event code (" << eventCode << ") out of range");
continue;
}
switch(eventCode)
{
case Mgmt::ECommandCompleteEvent:
{
CommandCompleteEvent event(responsePacket);
uint8_t *data = responsePacket.data() + sizeof(CommandCompleteEvent);
size_t dataLen = responsePacket.size() - sizeof(CommandCompleteEvent);
switch(event.commandCode)
{
case Mgmt::EReadVersionInformationCommand:
{
if (dataLen != sizeof(VersionInformation))
{
Logger::error("Invalid data length");
return;
}
versionInformation = *reinterpret_cast<VersionInformation *>(data);
versionInformation.toHost();
Logger::debug(versionInformation.debugText());
break;
}
case Mgmt::EReadControllerInformationCommand:
{
if (dataLen != sizeof(ControllerInformation))
{
Logger::error("Invalid data length");
return;
}
controllerInformation = *reinterpret_cast<ControllerInformation *>(data);
controllerInformation.toHost();
Logger::debug(controllerInformation.debugText());
break;
}
case Mgmt::ESetLocalNameCommand:
{
if (dataLen != sizeof(LocalName))
{
Logger::error("Invalid data length");
return;
}
localName = *reinterpret_cast<LocalName *>(data);
Logger::info(localName.debugText());
break;
}
case Mgmt::ESetPoweredCommand:
case Mgmt::ESetBREDRCommand:
case Mgmt::ESetSecureConnectionsCommand:
case Mgmt::ESetBondableCommand:
case Mgmt::ESetConnectableCommand:
case Mgmt::ESetLowEnergyCommand:
case Mgmt::ESetAdvertisingCommand:
{
if (dataLen != sizeof(AdapterSettings))
{
Logger::error("Invalid data length");
return;
}
adapterSettings = *reinterpret_cast<AdapterSettings *>(data);
adapterSettings.toHost();
Logger::debug(adapterSettings.debugText());
break;
}
}
setCommandResponse(event.commandCode);
break;
}
case Mgmt::ECommandStatusEvent:
{
CommandStatusEvent event(responsePacket);
setCommandResponse(event.commandCode);
break;
}
case Mgmt::EDeviceConnectedEvent:
{
DeviceConnectedEvent event(responsePacket);
activeConnections += 1;
Logger::debug(SSTR << " > Connection count incremented to " << activeConnections);
break;
}
case Mgmt::EDeviceDisconnectedEvent:
{
DeviceDisconnectedEvent event(responsePacket);
if (activeConnections > 0)
{
activeConnections -= 1;
Logger::debug(SSTR << " > Connection count decremented to " << activeConnections);
}
else
{
Logger::debug(SSTR << " > Connection count already at zero, ignoring non-connected disconnect event");
}
break;
}
default:
{
if (eventCode >= kMinEventType && eventCode <= kMaxEventType)
{
Logger::error("Unsupported response event type: " + Utils::hex(eventCode) + " (" + kEventTypeNames[eventCode] + ")");
}
else
{
Logger::error("Invalid event type response: " + Utils::hex(eventCode));
}
}
}
}
hciSocket.disconnect();
Logger::trace("Leaving the HciAdapter event thread");
}
void HciAdapter::sync(uint16_t controllerIndex)
{
Logger::debug("Synchronizing version information");
HciAdapter::HciHeader request;
request.code = Mgmt::EReadVersionInformationCommand;
request.controllerId = HciAdapter::kNonController;
request.dataSize = 0;
if (!HciAdapter::getInstance().sendCommand(request))
{
Logger::error("Failed to get version information");
}
Logger::debug("Synchronizing controller information");
request.code = Mgmt::EReadControllerInformationCommand;
request.controllerId = controllerIndex;
request.dataSize = 0;
if (!HciAdapter::getInstance().sendCommand(request))
{
Logger::error("Failed to get current settings");
}
}
bool HciAdapter::start()
{
if (eventThread.joinable())
{
return false;
}
if (!hciSocket.isConnected())
{
if (!hciSocket.connect())
{
return false;
}
}
try
{
eventThread = std::thread(ggk::runEventThread);
}
catch(std::system_error &ex)
{
Logger::error(SSTR << "HciAdapter thread was unable to start (code " << ex.code() << "): " << ex.what());
return false;
}
return true;
}
void HciAdapter::stop()
{
Logger::trace("HciAdapter waiting for thread termination");
try
{
if (eventThread.joinable())
{
eventThread.join();
Logger::trace("Event thread has stopped");
}
else
{
Logger::trace(" > Event thread is not joinable");
}
}
catch(std::system_error &ex)
{
if (ex.code() == std::errc::invalid_argument)
{
Logger::warn(SSTR << "HciAdapter event thread was not joinable during HciAdapter::wait(): " << ex.what());
}
else if (ex.code() == std::errc::no_such_process)
{
Logger::warn(SSTR << "HciAdapter event was not valid during HciAdapter::wait(): " << ex.what());
}
else if (ex.code() == std::errc::resource_deadlock_would_occur)
{
Logger::warn(SSTR << "Deadlock avoided in call to HciAdapter::wait() (did the event thread try to stop itself?): " << ex.what());
}
else
{
Logger::warn(SSTR << "Unknown system_error code (" << ex.code() << ") during HciAdapter::wait(): " << ex.what());
}
}
}
bool HciAdapter::sendCommand(HciHeader &request)
{
if (!eventThread.joinable() && !start())
{
Logger::error("HciAdapter failed to start");
return false;
}
conditionalValue = -1;
std::future<bool> fut = std::async(std::launch::async,
[&]() mutable
{
return waitForCommandResponse(request.code, kMaxEventWaitTimeMS);
});
request.toNetwork();
uint8_t *pRequest = reinterpret_cast<uint8_t *>(&request);
std::vector<uint8_t> requestPacket = std::vector<uint8_t>(pRequest, pRequest + sizeof(request) + request.dataSize);
if (!hciSocket.write(requestPacket))
{
return false;
}
return fut.get();
}
bool HciAdapter::waitForCommandResponse(uint16_t commandCode, int timeoutMS)
{
Logger::debug(SSTR << " + Waiting on command code " << commandCode << " for up to " << timeoutMS << "ms");
bool success = cvCommandResponse.wait_for(commandResponseLock, std::chrono::milliseconds(timeoutMS),
[&]
{
return conditionalValue == commandCode;
}
);
if (!success)
{
Logger::warn(SSTR << " + Timed out waiting on command code " << Utils::hex(commandCode) << " (" << kCommandCodeNames[commandCode] << ")");
}
else
{
Logger::debug(SSTR << " + Recieved the command code we were waiting for: " << Utils::hex(commandCode) << " (" << kCommandCodeNames[commandCode] << ")");
}
return success;
}
void HciAdapter::setCommandResponse(uint16_t commandCode)
{
std::lock_guard<std::mutex> lk(commandResponseMutex);
conditionalValue = commandCode;
cvCommandResponse.notify_one();
}
};