#include <algorithm>
#include "Server.h"
#include "ServerUtils.h"
#include "Utils.h"
#include "Globals.h"
#include "DBusObject.h"
#include "DBusInterface.h"
#include "GattProperty.h"
#include "GattService.h"
#include "GattUuid.h"
#include "GattCharacteristic.h"
#include "GattDescriptor.h"
#include "Logger.h"
namespace ggk {
#if defined(__GNUC__) && defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#endif
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
std::shared_ptr<Server> TheServer = nullptr;
Server::Server(const std::string &serviceName, const std::string &advertisingName, const std::string &advertisingShortName,
GGKServerDataGetter getter, GGKServerDataSetter setter)
{
this->serviceName = serviceName;
std::transform(this->serviceName.begin(), this->serviceName.end(), this->serviceName.begin(), ::tolower);
this->advertisingName = advertisingName;
this->advertisingShortName = advertisingShortName;
dataGetter = getter;
dataSetter = setter;
enableBREDR = false;
enableSecureConnection = false;
enableConnectable = true;
enableAdvertising = true;
enableBondable = false;
objects.push_back(DBusObject(DBusObjectPath() + "com" + getServiceName()));
objects.back()
.gattServiceBegin("device", "180A")
.gattCharacteristicBegin("mfgr_name", "2A29", {"read"})
.onReadValue(CHARACTERISTIC_METHOD_CALLBACK_LAMBDA
{
self.methodReturnValue(pInvocation, "Acme Inc.", true);
})
.gattCharacteristicEnd()
.gattCharacteristicBegin("model_num", "2A24", {"read"})
.onReadValue(CHARACTERISTIC_METHOD_CALLBACK_LAMBDA
{
self.methodReturnValue(pInvocation, "Marvin-PA", true);
})
.gattCharacteristicEnd()
.gattServiceEnd()
.gattServiceBegin("battery", "180F")
.gattCharacteristicBegin("level", "2A19", {"read", "notify"})
.onReadValue(CHARACTERISTIC_METHOD_CALLBACK_LAMBDA
{
uint8_t batteryLevel = self.getDataValue<uint8_t>("battery/level", 0);
self.methodReturnValue(pInvocation, batteryLevel, true);
})
.onUpdatedValue(CHARACTERISTIC_UPDATED_VALUE_CALLBACK_LAMBDA
{
uint8_t batteryLevel = self.getDataValue<uint8_t>("battery/level", 0);
self.sendChangeNotificationValue(pConnection, batteryLevel);
return true;
})
.gattCharacteristicEnd()
.gattServiceEnd()
.gattServiceBegin("time", "1805")
.gattCharacteristicBegin("current", "2A2B", {"read", "notify"})
.onReadValue(CHARACTERISTIC_METHOD_CALLBACK_LAMBDA
{
self.methodReturnVariant(pInvocation, ServerUtils::gvariantCurrentTime(), true);
})
.onEvent(1, nullptr, CHARACTERISTIC_EVENT_CALLBACK_LAMBDA
{
self.sendChangeNotificationVariant(pConnection, ServerUtils::gvariantCurrentTime());
})
.gattCharacteristicEnd()
.gattCharacteristicBegin("local", "2A0F", {"read"})
.onReadValue(CHARACTERISTIC_METHOD_CALLBACK_LAMBDA
{
self.methodReturnVariant(pInvocation, ServerUtils::gvariantLocalTime(), true);
})
.gattCharacteristicEnd()
.gattServiceEnd()
.gattServiceBegin("text", "00000001-1E3C-FAD4-74E2-97A033F1BFAA")
.gattCharacteristicBegin("string", "00000002-1E3C-FAD4-74E2-97A033F1BFAA", {"read", "write", "notify"})
.onReadValue(CHARACTERISTIC_METHOD_CALLBACK_LAMBDA
{
const char *pTextString = self.getDataPointer<const char *>("text/string", "");
self.methodReturnValue(pInvocation, pTextString, true);
})
.onWriteValue(CHARACTERISTIC_METHOD_CALLBACK_LAMBDA
{
GVariant *pAyBuffer = g_variant_get_child_value(pParameters, 0);
self.setDataPointer("text/string", Utils::stringFromGVariantByteArray(pAyBuffer).c_str());
self.callOnUpdatedValue(pConnection, pUserData);
})
.onUpdatedValue(CHARACTERISTIC_UPDATED_VALUE_CALLBACK_LAMBDA
{
const char *pTextString = self.getDataPointer<const char *>("text/string", "");
self.sendChangeNotificationValue(pConnection, pTextString);
return true;
})
.gattDescriptorBegin("description", "2901", {"read"})
.onReadValue(DESCRIPTOR_METHOD_CALLBACK_LAMBDA
{
const char *pDescription = "A mutable text string used for testing. Read and write to me, it tickles!";
self.methodReturnValue(pInvocation, pDescription, true);
})
.gattDescriptorEnd()
.gattCharacteristicEnd()
.gattServiceEnd()
.gattServiceBegin("ascii_time", "00000001-1E3D-FAD4-74E2-97A033F1BFEE")
.gattCharacteristicBegin("string", "00000002-1E3D-FAD4-74E2-97A033F1BFEE", {"read"})
.onReadValue(CHARACTERISTIC_METHOD_CALLBACK_LAMBDA
{
time_t timeVal = time(nullptr);
struct tm *pTimeStruct = localtime(&timeVal);
std::string timeString = Utils::trim(asctime(pTimeStruct));
self.methodReturnValue(pInvocation, timeString, true);
})
.gattDescriptorBegin("description", "2901", {"read"})
.onReadValue(DESCRIPTOR_METHOD_CALLBACK_LAMBDA
{
const char *pDescription = "Returns the local time (as reported by POSIX asctime()) each time it is read";
self.methodReturnValue(pInvocation, pDescription, true);
})
.gattDescriptorEnd()
.gattCharacteristicEnd()
.gattServiceEnd()
.gattServiceBegin("cpu", "0000B001-1E3D-FAD4-74E2-97A033F1BFEE")
.gattCharacteristicBegin("count", "0000B002-1E3D-FAD4-74E2-97A033F1BFEE", {"read"})
.onReadValue(CHARACTERISTIC_METHOD_CALLBACK_LAMBDA
{
int16_t cpuCount = 0;
ServerUtils::getCpuInfo(cpuCount);
self.methodReturnValue(pInvocation, cpuCount, true);
})
.gattDescriptorBegin("description", "2901", {"read"})
.onReadValue(DESCRIPTOR_METHOD_CALLBACK_LAMBDA
{
const char *pDescription = "This might represent the number of CPUs in the system";
self.methodReturnValue(pInvocation, pDescription, true);
})
.gattDescriptorEnd()
.gattCharacteristicEnd()
.gattCharacteristicBegin("model", "0000B003-1E3D-FAD4-74E2-97A033F1BFEE", {"read"})
.onReadValue(CHARACTERISTIC_METHOD_CALLBACK_LAMBDA
{
int16_t cpuCount = 0;
self.methodReturnValue(pInvocation, ServerUtils::getCpuInfo(cpuCount), true);
})
.gattDescriptorBegin("description", "2901", {"read"})
.onReadValue(DESCRIPTOR_METHOD_CALLBACK_LAMBDA
{
const char *pDescription = "Possibly the model of the CPU in the system";
self.methodReturnValue(pInvocation, pDescription, true);
})
.gattDescriptorEnd()
.gattCharacteristicEnd()
.gattServiceEnd();
objects.push_back(DBusObject(DBusObjectPath(), false));
DBusObject &objectManager = objects.back();
auto omInterface = std::make_shared<DBusInterface>(objectManager, "org.freedesktop.DBus.ObjectManager");
objectManager.addInterface(omInterface);
const char *pInArgs[] = { nullptr };
const char *pOutArgs = "a{oa{sa{sv}}}";
omInterface->addMethod("GetManagedObjects", pInArgs, pOutArgs, INTERFACE_METHOD_CALLBACK_LAMBDA
{
ServerUtils::getManagedObjects(pInvocation);
});
}
std::shared_ptr<const DBusInterface> Server::findInterface(const DBusObjectPath &objectPath, const std::string &interfaceName) const
{
for (const DBusObject &object : objects)
{
std::shared_ptr<const DBusInterface> pInterface = object.findInterface(objectPath, interfaceName);
if (pInterface != nullptr)
{
return pInterface;
}
}
return nullptr;
}
bool Server::callMethod(const DBusObjectPath &objectPath, const std::string &interfaceName, const std::string &methodName, GDBusConnection *pConnection, GVariant *pParameters, GDBusMethodInvocation *pInvocation, gpointer pUserData) const
{
for (const DBusObject &object : objects)
{
if (object.callMethod(objectPath, interfaceName, methodName, pConnection, pParameters, pInvocation, pUserData))
{
return true;
}
}
return false;
}
const GattProperty *Server::findProperty(const DBusObjectPath &objectPath, const std::string &interfaceName, const std::string &propertyName) const
{
std::shared_ptr<const DBusInterface> pInterface = findInterface(objectPath, interfaceName);
if (std::shared_ptr<const GattInterface> pGattInterface = TRY_GET_CONST_INTERFACE_OF_TYPE(pInterface, GattInterface))
{
return pGattInterface->findProperty(propertyName);
}
else if (std::shared_ptr<const GattService> pGattInterface = TRY_GET_CONST_INTERFACE_OF_TYPE(pInterface, GattService))
{
return pGattInterface->findProperty(propertyName);
}
else if (std::shared_ptr<const GattCharacteristic> pGattInterface = TRY_GET_CONST_INTERFACE_OF_TYPE(pInterface, GattCharacteristic))
{
return pGattInterface->findProperty(propertyName);
}
return nullptr;
}
};