// Copyright 2017 Paul Nettle.
//
// This file is part of Gobbledegook.
//
// Gobbledegook is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Gobbledegook is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Gobbledegook. If not, see .
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// >>
// >>> INSIDE THIS FILE
// >>
//
// This is an abstraction layer for a D-Bus interface, the base class for all interfaces.
//
// >>
// >>> DISCUSSION
// >>
//
// Not sure what a D-Bus Interface is? Well, chedk the Readme for resources, but here's the TL;DR:
//
// A D-Bus interface is a contract (similar to programming language interfaces.) An interface defines a set of methods and
// properties for others to use.
//
// Interfaces are identified by their name, such as "org.freedesktop.DBus.Properties". In fact, if an object on the bus is found
// to have that interface, then you know that it provides an interface to access its properties via the methods "Get", "GetAll" and
// "Set". To see the details for this interface (and all of the D-Bus defined interfaces), see:
//
// https://dbus.freedesktop.org/doc/dbus-specification.html
//
// We're also interested in working with BlueZ which has their own set of interfaces. One example is "org.bluez.GattManager1" which
// is the interface used to create and register GATT services with BlueZ.
//
// Remember, interfaces are not implementations; they're just contracts to provide an implementation. That means some interfaces
// are intended for us to implement. One example is "org.bluez.GattService1" which defines the interface that we must conform to
// so that others (likely BlueZ) can access our GATT service(s). For more information on these, have a look at:
//
// https://git.kernel.org/pub/scm/bluetooth/bluez.git/plain/doc/gatt-api.txt
//
// Our interfaces also store a collection of events. Here, an event is much like a timer in modern UIs, which repeatedly fires
// after a defined time. A practical example of an event would be a BLE server that provides a Battery service. By adding a timer
// to the interface for this service, the server could wake up every minute to check the battery level and if it has changed, send
// a notifications to clients over BLE with the new battery level. This saves a lot of additional code on the server's part.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include "DBusInterface.h"
#include "GattProperty.h"
#include "DBusObject.h"
#include "Logger.h"
//
// Construction
//
DBusInterface::DBusInterface(DBusObject &owner, const std::string &name)
: owner(owner), name(name)
{
}
DBusInterface::~DBusInterface()
{
}
//
// Interface name
//
// Returns the name of this interface (ex: "org.freedesktop.DBus.Properties")
const std::string &DBusInterface::getName() const
{
return name;
}
// Sets the name of the interface (ex: "org.freedesktop.DBus.Properties")
DBusInterface &DBusInterface::setName(const std::string &name)
{
this->name = name;
return *this;
}
//
// Owner information
//
// Returns the owner (DBusObject) of this interface
DBusObject &DBusInterface::getOwner() const
{
return owner;
}
// Returns the path node of this interface's owner
DBusObjectPath DBusInterface::getPathNode() const
{
return owner.getPathNode();
}
// Returns the full path of this interface's owner
DBusObjectPath DBusInterface::getPath() const
{
return owner.getPath();
}
//
// D-Bus interface methods
//
// Add a named method to this interface
//
// This method returns a reference to `this` in order to enable chaining inside the server description.
DBusInterface &DBusInterface::addMethod(const std::string &name, const char *pInArgs[], const char *pOutArgs, DBusMethod::Callback callback)
{
methods.push_back(DBusMethod(this, name, pInArgs, pOutArgs, callback));
return *this;
}
// Calls a named method on this interface
//
// This method returns false if the method could not be found, otherwise it returns true. Note that the return value is not related
// to the result of the method call itself (methods do not return values.)
//
// NOTE: Subclasses are encouraged to override this method in order to support different callback types that are specific to
// their subclass type.
bool DBusInterface::callMethod(const std::string &methodName, GDBusConnection *pConnection, GVariant *pParameters, GDBusMethodInvocation *pInvocation, gpointer pUserData) const
{
for (const DBusMethod &method : methods)
{
if (methodName == method.getName())
{
method.call(pConnection, getPath(), getName(), methodName, pParameters, pInvocation, pUserData);
return true;
}
}
return false;
}
// Add an event to this interface
//
// For details on events, see TickEvent.cpp.
//
// This method returns a reference to `this` in order to enable chaining inside the server description.
//
// NOTE: Subclasses are encouraged to overload this method in order to support different callback types that are specific to
// their subclass type. In addition, they should return their own type. This simplifies the server description by allowing
// calls to chain.
DBusInterface &DBusInterface::onEvent(int tickFrequency, void *pUserData, TickEvent::Callback callback)
{
events.push_back(TickEvent(this, tickFrequency, callback, pUserData));
return *this;
}
// Ticks each event within this interface
//
// For details on events, see TickEvent.cpp.
//
// NOTE: Subclasses are encouraged to override this method in order to support different callback types that are specific to
// their subclass type.
void DBusInterface::tickEvents(GDBusConnection *pConnection, void *pUserData) const
{
for (const TickEvent &event : events)
{
event.tick(getPath(), pConnection, pUserData);
}
}
// Internal method used to generate introspection XML used to describe our services on D-Bus
std::string DBusInterface::generateIntrospectionXML(int depth) const
{
std::string prefix;
prefix.insert(0, depth * 2, ' ');
std::string xml = std::string();
if (methods.empty())
{
xml += prefix + "\n";
}
else
{
xml += prefix + "\n";
// Describe our methods
for (const DBusMethod &method : methods)
{
xml += method.generateIntrospectionXML(depth + 1);
}
xml += prefix + "\n";
}
return xml;
}