#include <gio/gio.h>
#include <string>
#include <vector>
#include <atomic>
#include <chrono>
#include <thread>
#include "Server.h"
#include "Globals.h"
#include "Mgmt.h"
#include "HciAdapter.h"
#include "DBusObject.h"
#include "DBusInterface.h"
#include "GattCharacteristic.h"
#include "GattProperty.h"
#include "Logger.h"
#include "Init.h"
namespace ggk {
static const int kPeriodicTimerFrequencySeconds = 1;
static const int kRetryDelaySeconds = 2;
static const int kIdleFrequencyMS = 10;
static time_t retryTimeStart = 0;
GDBusConnection *pBusConnection = nullptr;
static guint ownedNameId = 0;
static guint periodicTimeoutId = 0;
static std::vector<guint> registeredObjectIds;
static std::atomic<GMainLoop *> pMainLoop(nullptr);
static GDBusObjectManager *pBluezObjectManager = nullptr;
static GDBusObject *pBluezAdapterObject = nullptr;
static GDBusObject *pBluezDeviceObject = nullptr;
static GDBusProxy *pBluezGattManagerProxy = nullptr;
static GDBusProxy *pBluezAdapterInterfaceProxy = nullptr;
static GDBusProxy *pBluezDeviceInterfaceProxy = nullptr;
static GDBusProxy *pBluezAdapterPropertiesInterfaceProxy = nullptr;
static bool bOwnedNameAcquired = false;
static bool bAdapterConfigured = false;
static bool bApplicationRegistered = false;
static std::string bluezGattManagerInterfaceName = "";
extern void setServerRunState(enum GGKServerRunState newState);
extern void setServerHealth(enum GGKServerHealth newState);
static void initializationStateProcessor();
bool idleFunc(void *pUserData)
{
if (ggkGetServerRunState() != ERunning)
{
return false;
}
const int kQueueEntryLen = 1024;
char queueEntry[kQueueEntryLen];
if (ggkPopUpdateQueue(queueEntry, kQueueEntryLen, 0) != 1)
{
return false;
}
std::string entryString = queueEntry;
auto token = entryString.find('|');
if (token == std::string::npos)
{
Logger::error("Queue entry was not formatted properly - could not find separating token");
return false;
}
DBusObjectPath objectPath = DBusObjectPath(entryString.substr(0, token));
std::string interfaceName = entryString.substr(token+1);
std::shared_ptr<const DBusInterface> pInterface = TheServer->findInterface(objectPath, interfaceName);
if (nullptr == pInterface)
{
Logger::warn(SSTR << "Unable to find interface for update: path[" << objectPath << "], name[" << interfaceName << "]");
}
else
{
if (std::shared_ptr<const GattCharacteristic> pCharacteristic = TRY_GET_CONST_INTERFACE_OF_TYPE(pInterface, GattCharacteristic))
{
Logger::debug(SSTR << "Processing updated value for interface '" << interfaceName << "' at path '" << objectPath << "'");
pCharacteristic->callOnUpdatedValue(pBusConnection, pUserData);
}
}
return false;
}
void uninit()
{
pMainLoop = nullptr;
if (nullptr != pBluezAdapterObject)
{
g_object_unref(pBluezAdapterObject);
pBluezAdapterObject = nullptr;
}
if (nullptr != pBluezDeviceObject)
{
g_object_unref(pBluezDeviceObject);
pBluezDeviceObject = nullptr;
}
if (nullptr != pBluezAdapterInterfaceProxy)
{
g_object_unref(pBluezAdapterInterfaceProxy);
pBluezAdapterInterfaceProxy = nullptr;
}
if (nullptr != pBluezDeviceInterfaceProxy)
{
g_object_unref(pBluezDeviceInterfaceProxy);
pBluezDeviceInterfaceProxy = nullptr;
}
if (nullptr != pBluezAdapterPropertiesInterfaceProxy)
{
g_object_unref(pBluezAdapterPropertiesInterfaceProxy);
pBluezAdapterPropertiesInterfaceProxy = nullptr;
}
if (nullptr != pBluezGattManagerProxy)
{
g_object_unref(pBluezGattManagerProxy);
pBluezGattManagerProxy = nullptr;
}
if (nullptr != pBluezObjectManager)
{
g_object_unref(pBluezObjectManager);
pBluezObjectManager = nullptr;
}
if (!registeredObjectIds.empty())
{
for (guint id : registeredObjectIds)
{
g_dbus_connection_unregister_object(pBusConnection, id);
}
registeredObjectIds.clear();
}
if (0 != periodicTimeoutId)
{
g_source_remove(periodicTimeoutId);
periodicTimeoutId = 0;
}
if (ownedNameId > 0)
{
g_bus_unown_name(ownedNameId);
}
if (nullptr != pBusConnection)
{
g_object_unref(pBusConnection);
pBusConnection = nullptr;
}
if (nullptr != pMainLoop)
{
g_main_loop_unref(pMainLoop);
pMainLoop = nullptr;
}
}
void shutdown()
{
if (ggkGetServerRunState() >= EStopping)
{
Logger::warn(SSTR << "shutdown() called while already shutting down");
}
setServerRunState(EStopping);
HciAdapter::getInstance().disconnect();
if (nullptr != pMainLoop)
{
g_main_loop_quit(pMainLoop);
}
}
gboolean onPeriodicTimer(gpointer pUserData)
{
if (0 != retryTimeStart)
{
Logger::debug(SSTR << "Ticking retry timer");
int secondsRemaining = time(nullptr) - retryTimeStart - kRetryDelaySeconds;
if (secondsRemaining >= 0)
{
retryTimeStart = 0;
initializationStateProcessor();
}
}
if (bApplicationRegistered)
{
for (const DBusObject &object : TheServer->getObjects())
{
if (object.isPublished())
{
object.tickEvents(pBusConnection, pUserData);
}
}
}
return TRUE;
}
void onMethodCall
(
GDBusConnection *pConnection,
const gchar *pSender,
const gchar *pObjectPath,
const gchar *pInterfaceName,
const gchar *pMethodName,
GVariant *pParameters,
GDBusMethodInvocation *pInvocation,
gpointer pUserData
)
{
DBusObjectPath objectPath(pObjectPath);
if (!TheServer->callMethod(objectPath, pInterfaceName, pMethodName, pConnection, pParameters, pInvocation, pUserData))
{
Logger::error(SSTR << " + Method not found: [" << pSender << "]:[" << objectPath << "]:[" << pInterfaceName << "]:[" << pMethodName << "]");
g_dbus_method_invocation_return_dbus_error(pInvocation, kErrorNotImplemented.c_str(), "This method is not implemented");
return;
}
return;
}
GVariant *onGetProperty
(
GDBusConnection *pConnection,
const gchar *pSender,
const gchar *pObjectPath,
const gchar *pInterfaceName,
const gchar *pPropertyName,
GError **ppError,
gpointer pUserData
)
{
DBusObjectPath objectPath(pObjectPath);
const GattProperty *pProperty = TheServer->findProperty(objectPath, pInterfaceName, pPropertyName);
std::string propertyPath = std::string("[") + pSender + "]:[" + objectPath.toString() + "]:[" + pInterfaceName + "]:[" + pPropertyName + "]";
if (!pProperty)
{
Logger::error(SSTR << "Property(get) not found: " << propertyPath);
g_set_error(ppError, G_IO_ERROR, G_IO_ERROR_FAILED, ("Property(get) not found: " + propertyPath).c_str(), pSender);
return nullptr;
}
if (!pProperty->getGetterFunc())
{
Logger::error(SSTR << "Property(get) func not found: " << propertyPath);
g_set_error(ppError, G_IO_ERROR, G_IO_ERROR_FAILED, ("Property(get) func not found: " + propertyPath).c_str(), pSender);
return nullptr;
}
Logger::info(SSTR << "Calling property getter: " << propertyPath);
GVariant *pResult = pProperty->getGetterFunc()(pConnection, pSender, objectPath.c_str(), pInterfaceName, pPropertyName, ppError, pUserData);
if (nullptr == pResult)
{
g_set_error(ppError, G_IO_ERROR, G_IO_ERROR_FAILED, ("Property(get) failed: " + propertyPath).c_str(), pSender);
return nullptr;
}
return pResult;
}
gboolean onSetProperty
(
GDBusConnection *pConnection,
const gchar *pSender,
const gchar *pObjectPath,
const gchar *pInterfaceName,
const gchar *pPropertyName,
GVariant *pValue,
GError **ppError,
gpointer pUserData
)
{
DBusObjectPath objectPath(pObjectPath);
const GattProperty *pProperty = TheServer->findProperty(objectPath, pInterfaceName, pPropertyName);
std::string propertyPath = std::string("[") + pSender + "]:[" + objectPath.toString() + "]:[" + pInterfaceName + "]:[" + pPropertyName + "]";
if (!pProperty)
{
Logger::error(SSTR << "Property(set) not found: " << propertyPath);
g_set_error(ppError, G_IO_ERROR, G_IO_ERROR_FAILED, ("Property(set) not found: " + propertyPath).c_str(), pSender);
return false;
}
if (!pProperty->getSetterFunc())
{
Logger::error(SSTR << "Property(set) func not found: " << propertyPath);
g_set_error(ppError, G_IO_ERROR, G_IO_ERROR_FAILED, ("Property(set) func not found: " + propertyPath).c_str(), pSender);
return false;
}
Logger::info(SSTR << "Calling property getter: " << propertyPath);
if (!pProperty->getSetterFunc()(pConnection, pSender, objectPath.c_str(), pInterfaceName, pPropertyName, pValue, ppError, pUserData))
{
g_set_error(ppError, G_IO_ERROR, G_IO_ERROR_FAILED, ("Property(set) failed: " + propertyPath).c_str(), pSender);
return false;
}
return true;
}
void setRetry()
{
retryTimeStart = time(nullptr);
}
void setRetryFailure()
{
setRetry();
Logger::warn(SSTR << " + Will retry the failed operation in about " << kRetryDelaySeconds << " seconds");
}
void doRegisterApplication()
{
g_auto(GVariantBuilder) builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
GVariant *pParams = g_variant_new("(oa{sv})", "/", &builder);
g_dbus_proxy_call
(
pBluezGattManagerProxy,
"RegisterApplication",
pParams,
G_DBUS_CALL_FLAGS_NONE,
-1,
nullptr,
[] (GObject * , GAsyncResult *pAsyncResult, gpointer )
{
GError *pError = nullptr;
GVariant *pVariant = g_dbus_proxy_call_finish(pBluezGattManagerProxy, pAsyncResult, &pError);
if (nullptr == pVariant)
{
Logger::error(SSTR << "Failed to register application: " << (nullptr == pError ? "Unknown" : pError->message));
setRetryFailure();
}
else
{
g_variant_unref(pVariant);
Logger::debug(SSTR << "GATT application registered with BlueZ");
bApplicationRegistered = true;
}
initializationStateProcessor();
},
nullptr
);
}
void registerNodeHierarchy(GDBusNodeInfo *pNode, const DBusObjectPath &basePath = DBusObjectPath(), int depth = 1)
{
std::string prefix;
prefix.insert(0, depth * 2, ' ');
static GDBusInterfaceVTable interfaceVtable;
interfaceVtable.method_call = onMethodCall;
interfaceVtable.get_property = onGetProperty;
interfaceVtable.set_property = onSetProperty;
GDBusInterfaceInfo **ppInterface = pNode->interfaces;
Logger::debug(SSTR << prefix << "+ " << pNode->path);
while(nullptr != *ppInterface)
{
GError *pError = nullptr;
Logger::debug(SSTR << prefix << " (iface: " << (*ppInterface)->name << ")");
guint registeredObjectId = g_dbus_connection_register_object
(
pBusConnection,
basePath.c_str(),
*ppInterface,
&interfaceVtable,
nullptr,
nullptr,
&pError
);
if (0 == registeredObjectId)
{
Logger::error(SSTR << "Failed to register object: " << (nullptr == pError ? "Unknown" : pError->message));
g_dbus_node_info_unref(pNode);
registeredObjectIds.clear();
setRetryFailure();
return;
}
registeredObjectIds.push_back(registeredObjectId);
++ppInterface;
}
GDBusNodeInfo **ppChild = pNode->nodes;
while(nullptr != *ppChild)
{
registerNodeHierarchy(*ppChild, basePath + (*ppChild)->path, depth + 1);
++ppChild;
}
}
void registerObjects()
{
for (const DBusObject &object : TheServer->getObjects())
{
GError *pError = nullptr;
std::string xmlString = object.generateIntrospectionXML();
GDBusNodeInfo *pNode = g_dbus_node_info_new_for_xml(xmlString.c_str(), &pError);
if (nullptr == pNode)
{
Logger::error(SSTR << "Failed to introspect XML: " << (nullptr == pError ? "Unknown" : pError->message));
setRetryFailure();
return;
}
Logger::debug(SSTR << "Registering object hierarchy with D-Bus hierarchy");
registerNodeHierarchy(pNode, DBusObjectPath(pNode->path));
g_dbus_node_info_unref(pNode);
}
initializationStateProcessor();
}
void configureAdapter()
{
Mgmt mgmt;
std::string advertisingName = Mgmt::truncateName(TheServer->getAdvertisingName());
std::string advertisingShortName = Mgmt::truncateShortName(TheServer->getAdvertisingShortName());
HciAdapter::ControllerInformation info = HciAdapter::getInstance().getControllerInformation();
bool pwFlag = info.currentSettings.isSet(HciAdapter::EHciPowered) == true;
bool leFlag = info.currentSettings.isSet(HciAdapter::EHciLowEnergy) == true;
bool brFlag = info.currentSettings.isSet(HciAdapter::EHciBasicRate_EnhancedDataRate) == TheServer->getEnableBREDR();
bool scFlag = info.currentSettings.isSet(HciAdapter::EHciSecureConnections) == TheServer->getEnableSecureConnection();
bool bnFlag = info.currentSettings.isSet(HciAdapter::EHciBondable) == TheServer->getEnableBondable();
bool cnFlag = info.currentSettings.isSet(HciAdapter::EHciConnectable) == TheServer->getEnableConnectable();
bool adFlag = info.currentSettings.isSet(HciAdapter::EHciAdvertising) == TheServer->getEnableAdvertising();
bool anFlag = (advertisingName.length() == 0 || advertisingName == info.name) && (advertisingShortName.length() == 0 || advertisingShortName == info.shortName);
if (pwFlag && leFlag && brFlag && scFlag && bnFlag && cnFlag && adFlag && anFlag)
{
Logger::info("The adapter is fully configured");
bAdapterConfigured = true;
initializationStateProcessor();
return;
}
if (pwFlag)
{
Logger::debug("Powering off");
mgmt.setPowered(false);
}
if (!leFlag)
{
Logger::debug("Enabling LE");
mgmt.setLE(true);
}
if (!brFlag)
{
Logger::debug(SSTR << (TheServer->getEnableBREDR() ? "Enabling":"Disabling") << " BR/EDR");
mgmt.setBredr(TheServer->getEnableBREDR());
}
if (!scFlag)
{
Logger::debug(SSTR << (TheServer->getEnableSecureConnection() ? "Enabling":"Disabling") << " Secure Connections");
mgmt.setSecureConnections(TheServer->getEnableSecureConnection() ? 1 : 0);
}
if (!bnFlag)
{
Logger::debug(SSTR << (TheServer->getEnableBondable() ? "Enabling":"Disabling") << " Bondable");
mgmt.setBondable(TheServer->getEnableBondable());
}
if (!cnFlag)
{
Logger::debug(SSTR << (TheServer->getEnableConnectable() ? "Enabling":"Disabling") << " Connectable");
mgmt.setConnectable(TheServer->getEnableConnectable());
}
if (!adFlag)
{
Logger::debug(SSTR << (TheServer->getEnableAdvertising() ? "Enabling":"Disabling") << " Advertising");
mgmt.setAdvertising(TheServer->getEnableAdvertising() ? 1 : 0);
}
if (!anFlag)
{
Logger::info(SSTR << "Setting advertising name to '" << advertisingName << "' (with short name: '" << advertisingShortName << "')");
mgmt.setName(advertisingName.c_str(), advertisingShortName.c_str());
}
Logger::debug("Powering on");
mgmt.setPowered(true);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
HciAdapter::getInstance().getControllerInformation();
setRetry();
}
void findAdapterInterface()
{
GList *pObjects = g_dbus_object_manager_get_objects(pBluezObjectManager);
if (nullptr == pObjects)
{
Logger::error(SSTR << "Unable to get ObjectManager objects");
setRetryFailure();
return;
}
for (guint i = 0; i < g_list_length(pObjects) && bluezGattManagerInterfaceName.empty(); ++i)
{
pBluezAdapterObject = static_cast<GDBusObject *>(g_list_nth_data(pObjects, i));
if (nullptr == pBluezAdapterObject) { continue; }
pBluezGattManagerProxy = reinterpret_cast<GDBusProxy *>(g_dbus_object_get_interface(pBluezAdapterObject, "org.bluez.GattManager1"));
if (nullptr == pBluezGattManagerProxy) { continue; }
pBluezAdapterInterfaceProxy = reinterpret_cast<GDBusProxy *>(g_dbus_object_get_interface(pBluezAdapterObject, "org.bluez.Adapter1"));
if (nullptr == pBluezAdapterInterfaceProxy)
{
Logger::warn(SSTR << "Failed to get adapter proxy for interface 'org.bluez.Adapter1'");
continue;
}
pBluezAdapterPropertiesInterfaceProxy = reinterpret_cast<GDBusProxy *>(g_dbus_object_get_interface(pBluezAdapterObject, "org.freedesktop.DBus.Properties"));
if (nullptr == pBluezAdapterPropertiesInterfaceProxy)
{
Logger::warn(SSTR << "Failed to get adapter properties proxy for interface 'org.freedesktop.DBus.Properties'");
continue;
}
bluezGattManagerInterfaceName = g_dbus_proxy_get_object_path(pBluezGattManagerProxy);
break;
}
pBluezAdapterObject = g_dbus_object_manager_get_object(pBluezObjectManager, g_dbus_object_get_object_path(pBluezAdapterObject));
pBluezDeviceObject = g_dbus_object_manager_get_object(pBluezObjectManager, g_dbus_object_get_object_path(pBluezAdapterObject));
for (guint i = 0; i < g_list_length(pObjects) && bluezGattManagerInterfaceName.empty(); ++i)
{
g_object_unref(g_list_nth_data(pObjects, i));
}
g_list_free(pObjects);
if (nullptr == pBluezAdapterObject || nullptr == pBluezDeviceObject)
{
Logger::warn(SSTR << "Unable to find BlueZ objects outside of object list");
bluezGattManagerInterfaceName.clear();
}
if (bluezGattManagerInterfaceName.empty())
{
Logger::error(SSTR << "Unable to find the adapter");
setRetryFailure();
return;
}
initializationStateProcessor();
}
void getBluezObjectManager()
{
g_dbus_object_manager_client_new
(
pBusConnection,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
"org.bluez",
"/",
nullptr,
nullptr,
nullptr,
nullptr,
[] (GObject * , GAsyncResult *pAsyncResult, gpointer )
{
GError *pError = nullptr;
pBluezObjectManager = g_dbus_object_manager_client_new_finish(pAsyncResult, &pError);
if (nullptr == pBluezObjectManager)
{
Logger::error(SSTR << "Failed to get an ObjectManager client: " << (nullptr == pError ? "Unknown" : pError->message));
setRetryFailure();
return;
}
initializationStateProcessor();
},
nullptr
);
}
void doOwnedNameAcquire()
{
bOwnedNameAcquired = false;
ownedNameId = g_bus_own_name_on_connection
(
pBusConnection,
TheServer->getOwnedName().c_str(),
G_BUS_NAME_OWNER_FLAGS_NONE,
[](GDBusConnection *, const gchar *, gpointer)
{
periodicTimeoutId = g_timeout_add_seconds(kPeriodicTimerFrequencySeconds, onPeriodicTimer, pBusConnection);
if (periodicTimeoutId <= 0)
{
Logger::fatal(SSTR << "Failed to add a periodic timer");
setServerHealth(EFailedInit);
shutdown();
}
bOwnedNameAcquired = true;
initializationStateProcessor();
},
[](GDBusConnection *, const gchar *, gpointer)
{
bOwnedNameAcquired = false;
if (0 == periodicTimeoutId)
{
Logger::fatal(SSTR << "Unable to acquire an owned name ('" << TheServer->getOwnedName() << "') on the bus");
setServerHealth(EFailedInit);
shutdown();
}
else
{
Logger::warn(SSTR << "Owned name ('" << TheServer->getOwnedName() << "') lost");
setRetryFailure();
return;
}
initializationStateProcessor();
},
nullptr,
nullptr
);
}
void doBusAcquire()
{
g_bus_get
(
G_BUS_TYPE_SYSTEM,
nullptr,
[] (GObject
{
GError *pError = nullptr;
pBusConnection = g_bus_get_finish(pAsyncResult, &pError);
if (nullptr == pBusConnection)
{
Logger::fatal(SSTR << "Failed to get bus connection: " << (nullptr == pError ? "Unknown" : pError->message));
setServerHealth(EFailedInit);
shutdown();
}
initializationStateProcessor();
},
nullptr
);
}
void initializationStateProcessor()
{
if (ggkGetServerRunState() > ERunning || 0 != retryTimeStart) { return; }
if (nullptr == pBusConnection)
{
Logger::debug(SSTR << "Acquiring bus connection");
doBusAcquire();
return;
}
if (!bOwnedNameAcquired)
{
Logger::debug(SSTR << "Acquiring owned name: '" << TheServer->getOwnedName() << "'");
doOwnedNameAcquire();
return;
}
if (nullptr == pBluezObjectManager)
{
Logger::debug(SSTR << "Getting BlueZ ObjectManager");
getBluezObjectManager();
return;
}
if (bluezGattManagerInterfaceName.empty())
{
Logger::debug(SSTR << "Finding BlueZ GattManager1 interface");
findAdapterInterface();
return;
}
if (!bAdapterConfigured)
{
Logger::debug(SSTR << "Configuring BlueZ adapter '" << bluezGattManagerInterfaceName << "'");
configureAdapter();
return;
}
if (registeredObjectIds.empty())
{
Logger::debug(SSTR << "Registering with D-Bus");
registerObjects();
return;
}
if (!bApplicationRegistered)
{
Logger::debug(SSTR << "Registering application with BlueZ GATT manager");
doRegisterApplication();
return;
}
if (ggkGetServerHealth() != EOk)
{
shutdown();
return;
}
setServerRunState(ERunning);
}
void runServerThread()
{
setServerRunState(EInitializing);
initializationStateProcessor();
Logger::debug(SSTR << "Starting GLib main loop");
pMainLoop = g_main_loop_new(NULL, FALSE);
guint res = g_idle_add
(
[](gpointer pUserData) -> gboolean
{
if (!idleFunc(pUserData))
{
std::this_thread::sleep_for(std::chrono::milliseconds(kIdleFrequencyMS));
}
return TRUE;
},
nullptr
);
if (res == 0)
{
Logger::error(SSTR << "Unable to add idle to main loop");
}
g_main_loop_run(pMainLoop);
setServerRunState(EStopped);
Logger::info("GGK server stopped");
uninit();
}
};