[libamb] - added value quality, removed deprecated GetFoo call, made updateFrequency...
[profile/ivi/automotive-message-broker.git] / plugins / common / abstractdbusinterface.cpp
1 /*
2 Copyright (C) 2012 Intel Corporation
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19 #include "abstractdbusinterface.h"
20
21 #include <abstractroutingengine.h>
22 #include <debugout.h>
23 #include <boost/algorithm/string.hpp>
24 #include <boost/algorithm/string/predicate.hpp>
25 #include <gio/gio.h>
26 #include <listplusplus.h>
27
28 #include "varianttype.h"
29 #include "dbussignaller.h"
30
31 static DBusSignaller* signaller = nullptr;
32
33 unordered_map<string, AbstractDBusInterface*> AbstractDBusInterface::objectMap;
34 PropertyList AbstractDBusInterface::mimplementedProperties;
35
36 const uint getPid(const char *owner)
37 {
38         GError* error = nullptr;
39         GDBusProxy* dbus = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL,
40                                                                                                          "org.freedesktop.DBus",
41                                                                                                          "/",
42                                                                                                          "org.freedesktop.DBus",
43                                                                                                          NULL,
44                                                                                                          &error);
45
46         if(error)
47         {
48                 throw std::runtime_error(error->message);
49         }
50
51         error = nullptr;
52
53         GVariant* pid = g_dbus_proxy_call_sync(dbus, "GetConnectionUnixProcessID", g_variant_new("(s)", owner), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
54
55         if(error)
56         {
57                 DebugOut(DebugOut::Error)<< error->message << endl;
58         }
59
60         uint thePid=0;
61
62         g_variant_get(pid,"(u)",&thePid);
63
64         return thePid;
65 }
66
67 void AbstractDBusInterface::handleMyMethodCall(GDBusConnection       *connection,
68                                                          const gchar           *sender,
69                                                          const gchar           *object_path,
70                                                          const gchar           *interface_name,
71                                                          const gchar           *method_name,
72                                                          GVariant              *parameters,
73                                                          GDBusMethodInvocation *invocation,
74                                                          gpointer               user_data)
75 {
76
77         std::string method = method_name;
78         AbstractDBusInterface* iface = static_cast<AbstractDBusInterface*>(user_data);
79
80         if(DebugOut::getDebugThreshhold() >= 6)
81         {
82                 DebugOut(6)<<"DBus method call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interface_name<<" method: "<<method<<endl;
83                 DebugOut(6)<<"DBus method call path: "<<object_path<<endl;
84         }
85
86         g_assert(iface);
87
88         if(std::string(interface_name) == "org.freedesktop.DBus.Properties")
89         {
90                 if(method == "Get")
91                 {
92                         gchar* propertyName = nullptr;
93                         gchar* ifaceName = nullptr;
94                         g_variant_get(parameters, "(ss)", &ifaceName, &propertyName);
95
96                         DebugOut(6) << "Parameter signature: " << g_variant_get_type_string(parameters) << endl;
97 //                      DebugOut(6) << "Get property " << propertyName << " for interface " << ifaceName << endl;
98
99                         GError* error = nullptr;
100                         auto value = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, propertyName, &error, iface);
101                         amb::make_super(error);
102
103                         if(!value)
104                         {
105                                 g_dbus_method_invocation_return_dbus_error(invocation, std::string(std::string(ifaceName)+".PropertyNotFound").c_str(), "Property not found in interface");
106                         }
107
108                         g_dbus_method_invocation_return_value(invocation, g_variant_new("(v)", value));
109                         return;
110                 }
111                 else if(method == "GetAll")
112                 {
113                         gchar* ifaceName = nullptr;
114                         g_variant_get(parameters, "(s)", &ifaceName);
115
116                         GVariantBuilder builder;
117                         g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
118
119                         auto propertyMap = iface->getProperties();
120
121                         for(auto itr : propertyMap)
122                         {
123                                 auto prop = itr.second;
124                                 GError* error = nullptr;
125                                 auto value = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, prop->name().c_str(), &error, iface);
126                                 amb::make_super(error);
127                                 g_variant_builder_add(&builder, "{sv}", prop->name().c_str(), g_variant_new("v", value));
128                                 //sequence
129                                 auto sequence = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, std::string(prop->name()+"Sequence").c_str(), &error, iface);
130                                 g_variant_builder_add(&builder, "{sv}", std::string(prop->name()+"Sequence").c_str(), g_variant_new("v", sequence));
131
132                                 auto quality = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, std::string(prop->name()+"ValueQuality").c_str(), &error, iface);
133                                 g_variant_builder_add(&builder, "{sv}", std::string(prop->name()+"ValueQuality").c_str(), g_variant_new("v", quality));
134
135                                 auto freq = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, std::string(prop->name()+"UpdateFrequency").c_str(), &error, iface);
136                                 g_variant_builder_add(&builder, "{sv}", std::string(prop->name()+"UpdateFrequency").c_str(), g_variant_new("v", freq));
137                         }
138
139                         auto time = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, "Time", nullptr, iface);
140                         auto zone = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, "Zone", nullptr, iface);
141                         g_variant_builder_add(&builder, "{sv}", "Time", g_variant_new("v", time));
142                         g_variant_builder_add(&builder, "{sv}", "Zone", g_variant_new("v", zone));
143
144                         g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", &builder));
145                         return;
146                 }
147                 else if(method == "Set")
148                 {
149                         gchar* ifaceName = nullptr;
150                         gchar* propName = nullptr;
151                         GVariant* value;
152                         g_variant_get(parameters, "(ssv)", &ifaceName, &propName, &value);
153
154                         AbstractDBusInterface::setProperty(connection, sender, object_path, ifaceName, propName, value, nullptr, iface,
155                                                                                            [&invocation, &ifaceName](bool success, AsyncPropertyReply::Error error) {
156                                 if(success)
157                                 {
158                                         g_dbus_method_invocation_return_value(invocation, nullptr);
159                                 }
160                                 else
161                                 {
162                                         g_dbus_method_invocation_return_dbus_error(invocation, ifaceName, AsyncPropertyReply::errorToStr(error).c_str());
163                                 }
164                         });
165                         return;
166                 }
167         }
168         else if(method == "GetHistory")
169         {
170                 double beginTime = 0;
171                 double endTime = 0;
172
173                 g_variant_get(parameters, "(dd)", &beginTime, &endTime);
174
175                 auto propertyMap = iface->getProperties();
176
177                 PropertyList propertyList;
178
179                 for(auto itr = propertyMap.begin(); itr != propertyMap.end(); itr++)
180                 {
181                         VariantType* prop = (*itr).second;
182
183                         if(!contains(propertyList, prop->ambPropertyName()))
184                                 propertyList.push_back(prop->ambPropertyName());
185                 }
186
187                 std::string ifaceName = iface->interfaceName();
188
189                 AsyncRangePropertyRequest request;
190
191                 request.properties = propertyList;
192                 request.timeBegin = beginTime;
193                 request.timeEnd = endTime;
194                 request.zone = iface->zone();
195                 //request.sourceUuid = iface->source();
196
197                 request.completed = [&invocation,&ifaceName](AsyncRangePropertyReply* r)
198                 {
199                         auto reply = amb::make_unique(r);
200                         if(!reply->success)
201                         {
202                                 stringstream str;
203                                 str<<"Error during request: "<<AsyncPropertyReply::errorToStr(reply->error);
204                                 ifaceName += ".Error";
205                                 g_dbus_method_invocation_return_dbus_error(invocation, ifaceName.c_str(), str.str().c_str());
206                                 return;
207                         }
208
209                         if(!reply->values.size())
210                         {
211                                 ifaceName += ".Error";
212                                 g_dbus_method_invocation_return_dbus_error(invocation, ifaceName.c_str(), "No results");
213                                 return;
214                         }
215
216                         GVariantBuilder builder;
217                         g_variant_builder_init(&builder, G_VARIANT_TYPE("a{svd}"));
218
219
220                         for(auto itr = reply->values.begin(); itr != reply->values.end(); itr++)
221                         {
222                                 AbstractPropertyType* value = *itr;
223
224                                 g_variant_builder_add(&builder, "{svd}", value->name.c_str(), g_variant_ref(value->toVariant()),value->timestamp);
225                         }
226
227                         g_dbus_method_invocation_return_value(invocation,g_variant_new("(a{svd})",&builder));
228                 };
229
230                 iface->re->getRangePropertyAsync(request);
231
232                 return;
233         }
234
235         g_dbus_method_invocation_return_error(invocation,G_DBUS_ERROR,G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method.");
236 }
237
238 AbstractDBusInterface::AbstractDBusInterface(string interfaceName, string objectName,
239                                                                                          GDBusConnection* connection)
240         : mInterfaceName(interfaceName), mConnection(connection), mPropertyName(objectName), supported(false), zoneFilter(Zone::None), mTime(0), regId(0)
241 {
242         startRegistration();
243
244         mObjectPath = "/" + objectName;
245 }
246
247 AbstractDBusInterface::~AbstractDBusInterface()
248 {
249         unregisterObject();
250
251         PropertyList impl = implementedProperties();
252
253         for(auto itr = impl.begin(); itr != impl.end(); itr++)
254         {
255                 if(properties.find(*itr) != properties.end())
256                 {
257                         // Deleted in ~DBusSink()
258                         //delete properties[*itr];
259                         properties.erase(*itr);
260                 }
261         }
262
263         objectMap.erase(mObjectPath);
264 }
265
266 void AbstractDBusInterface::addProperty(VariantType * property)
267 {
268         string nameToLower = property->name();
269         boost::algorithm::to_lower<string>(nameToLower);
270
271         string access;
272
273         if(property->access() == VariantType::Read)
274                 access = "read";
275         else if(property->access() == VariantType::Write)
276                 access = "write";
277         else if(property->access() == VariantType::ReadWrite)
278                 access = "readwrite";
279         else throw -1; //FIXME: don't throw
280
281         std::string pn = property->name();
282
283         ///see which properties are supported:
284         introspectionXml +=
285                         "<property type='"+ string(property->signature()) + "' name='"+ pn +"' access='"+access+"' />"
286                         "<property type='i' name='" + pn + "Sequence' access='read' />"
287                         "<property type='i' name='" + pn + "ValueQuality' access='read' />"
288                         "<property type='i' name='" + pn + "UpdateFrequency' access='read' />";
289
290         properties[pn] = property;
291
292         if(!contains(mimplementedProperties, property->ambPropertyName()))
293         {
294                 std::string pname = property->ambPropertyName();
295                 mimplementedProperties.push_back(pname);
296         }
297 }
298
299 void AbstractDBusInterface::registerObject()
300 {
301         if(!mConnection)
302         {
303                 throw std::runtime_error("forgot to call setDBusConnection on AbstractDBusInterface");
304         }
305
306         if(introspectionXml.empty())
307         {
308                 cerr<<"no interface to export: "<<mInterfaceName<<endl;
309                 throw -1;
310         }
311
312         if(!boost::algorithm::ends_with(introspectionXml,"</node>"))
313         {
314                 introspectionXml += "</interface>"
315                                 "</node>";
316         }
317
318         GError* error=NULL;
319
320         GDBusNodeInfo* introspection = g_dbus_node_info_new_for_xml(introspectionXml.c_str(), &error);
321
322         if(!introspection || error)
323         {
324
325                 DebugOut(DebugOut::Error)<<"Error in "<<__FILE__<<" - "<<__FUNCTION__<<":"<<__LINE__<<endl;
326                 DebugOut(DebugOut::Error)<<error->message<<endl;
327                 DebugOut(DebugOut::Error)<<"probably bad xml:"<<endl;
328                 DebugOut(DebugOut::Error)<<introspectionXml<<endl;
329
330                 g_error_free(error);
331
332                 return;
333         }
334
335         GDBusInterfaceInfo* mInterfaceInfo = g_dbus_node_info_lookup_interface(introspection, mInterfaceName.c_str());
336
337
338         const GDBusInterfaceVTable vtable = { handleMyMethodCall, nullptr, nullptr };
339
340         GError* error2=NULL;
341
342         DebugOut()<<"registering DBus path: "<<mObjectPath<<endl;
343
344         regId = g_dbus_connection_register_object(mConnection, mObjectPath.c_str(), mInterfaceInfo, &vtable, this, NULL, &error2);
345         g_dbus_node_info_unref(introspection);
346         if(error2)
347         {
348                 DebugOut(DebugOut::Error)<<error2->message<<endl;
349                 g_error_free(error2);
350         }
351
352         if(regId == 0)
353         {
354                 DebugOut(DebugOut::Error)<<"We failed to register on DBus"<<endl;
355         }
356 }
357
358 void AbstractDBusInterface::unregisterObject()
359 {
360         if(regId)
361         {
362                 DebugOut()<<__FUNCTION__<<endl;
363                 g_dbus_connection_unregister_object(mConnection, regId);
364         }
365
366         regId=0;
367 }
368
369 void AbstractDBusInterface::updateValue(VariantType *property)
370 {
371         if(mConnection == nullptr)
372         {
373                 return;
374         }
375
376         if(isRegistered())
377                 signaller->fireSignal(mConnection, mObjectPath, mInterfaceName, "PropertiesChanged", property);
378 }
379
380 std::list<AbstractDBusInterface *> AbstractDBusInterface::getObjectsForProperty(string object)
381 {
382         std::list<AbstractDBusInterface *> l;
383         for(auto itr = objectMap.begin(); itr != objectMap.end(); itr++)
384         {
385                 AbstractDBusInterface * interface = (*itr).second;
386                 if(interface->objectName() == object)
387                         l.push_back(interface);
388         }
389
390         return l;
391 }
392 list<AbstractDBusInterface *> AbstractDBusInterface::interfaces()
393 {
394         std::list<AbstractDBusInterface*> ifaces;
395
396         for(auto itr = objectMap.begin(); itr != objectMap.end(); itr++)
397         {
398                 ifaces.push_back((*itr).second);
399         }
400
401         return ifaces;
402 }
403
404 std::vector<std::string> AbstractDBusInterface::supportedInterfaces()
405 {
406         std::vector<std::string> ifaces;
407
408         for(auto itr : objectMap)
409         {
410                 if(itr.second->isSupported())
411                         ifaces.push_back(itr.second->objectName());
412         }
413
414         return ifaces;
415 }
416
417 bool AbstractDBusInterface::implementsProperty(string property)
418 {
419         for(auto itr = properties.begin(); itr != properties.end(); itr++)
420         {
421                 if((*itr).first == property)
422                 {
423                         return true;
424                 }
425         }
426
427         return false;
428 }
429
430 void AbstractDBusInterface::startRegistration()
431 {
432         //unregisterObject();
433         introspectionXml ="<node>" ;
434         introspectionXml +=
435                         "<interface name='org.freedesktop.DBus.Properties'>"
436                         "<method name='Get'>"
437                         "   <arg type='s' direction='in' name='interface' />"
438                         "   <arg type='s' direction='in' name='property' />"
439                         "   <arg type='v' direction='out' name='value' />"
440                         "</method>"
441                         "<method name='Set'>"
442                         "   <arg type='s' direction='in' name='interface' />"
443                         "   <arg type='s' direction='in' name='property' />"
444                         "   <arg type='v' direction='in' name='value' />"
445                         "</method>"
446                         "<method name='GetAll'>"
447                         "   <arg type='s' direction='in' name='interface' />"
448                         "   <arg type='a{sv}' direction='out' name='interface' />"
449                         "</method>"
450                         "</interface>";
451         introspectionXml +=
452                         "<interface name='"+ mInterfaceName + "' >"
453                         "<property type='i' name='Zone' access='read' />"
454                         "<property type='d' name='Time' access='read' />"
455                         "<method name='GetHistory'>"
456                         "       <arg type='d' direction='in' name='beginTimestamp' />"
457                         "       <arg type='d' direction='in' name='endTimestamp' />"
458                         "   <arg type='a(svd)' direction='out' name='result' />"
459                         "</method>";
460 }
461
462 GVariant* AbstractDBusInterface::getProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName,
463                                                                                          const gchar* propertyName, GError** error, gpointer userData)
464 {
465         if(DebugOut::getDebugThreshhold() >= 6)
466         {
467                 DebugOut(6)<<"DBus GetProperty call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interfaceName<<" property: "<<propertyName<<endl;
468                 DebugOut(6)<<"DBus GetProperty call path: "<<objectPath<<endl;
469         }
470
471         std::string pn = propertyName;
472         if(pn == "Time")
473         {
474                 if(objectMap.find(objectPath) == objectMap.end())
475                 {
476                         DebugOut(DebugOut::Error)<<objectPath<<" is not a valid object path."<<endl;
477                         return nullptr;
478                 }
479                 double time = objectMap[objectPath]->time();
480
481                 GVariant* value = g_variant_new("d", time);
482                 return value;
483         }
484
485         else if(boost::ends_with(pn, "Sequence"))
486         {
487                 AbstractDBusInterface* t = static_cast<AbstractDBusInterface*>(userData);
488
489                 int pos = pn.find("Sequence");
490
491                 std::string p = pn.substr(0,pos);
492
493                 VariantType * theProperty = t->property(p);
494
495                 if(!theProperty)
496                 {
497                         DebugOut(DebugOut::Error)<<"Invalid Sequence property: "<<p<<endl;
498                         return nullptr;
499                 }
500
501                 int sequence = theProperty->sequence();
502
503                 GVariant* value = g_variant_new("i", sequence);
504                 return value;
505         }
506
507         else if(boost::ends_with(pn, "ValueQuality"))
508         {
509                 AbstractDBusInterface* t = static_cast<AbstractDBusInterface*>(userData);
510
511                 int pos = pn.find("ValueQuality");
512
513                 std::string p = pn.substr(0, pos);
514
515                 VariantType * theProperty = t->property(p);
516
517                 if(!theProperty)
518                 {
519                         DebugOut(DebugOut::Error)<<"Invalid ValueQuality property: "<<p<<endl;
520                         return nullptr;
521                 }
522
523                 int quality = theProperty->value()->valueQuality;
524
525                 GVariant* value = g_variant_new("i", quality);
526                 return value;
527         }
528
529         else if(boost::ends_with(pn, "UpdateFrequency"))
530         {
531                 AbstractDBusInterface* t = static_cast<AbstractDBusInterface*>(userData);
532
533                 int pos = pn.find("UpdateFrequency");
534
535                 std::string p = pn.substr(0, pos);
536
537                 VariantType * theProperty = t->property(p);
538
539                 if(!theProperty)
540                 {
541                         DebugOut(DebugOut::Error)<<"Invalid UpdateFrequency property: "<<p<<endl;
542                         return nullptr;
543                 }
544
545                 int freq = theProperty->updateFrequency();
546
547                 GVariant* value = g_variant_new("i", freq);
548                 return value;
549         }
550
551         else if(pn == "Zone")
552         {
553                 if(objectMap.find(objectPath) == objectMap.end())
554                 {
555                         DebugOut(DebugOut::Error)<<objectPath<<" is not a valid object path."<<endl;
556                         return nullptr;
557                 }
558
559                 Zone::Type zone = objectMap[objectPath]->zone();
560
561                 GVariant* value = g_variant_new("i",(int)zone);
562                 return value;
563         }
564
565         if(objectMap.count(objectPath))
566         {
567                 GVariant* value = objectMap[objectPath]->getProperty(propertyName);
568
569                 DebugOut(6) << "Returning value for: " << propertyName << endl;
570                 return value;
571         }
572
573
574         DebugOut(DebugOut::Error)<<"No interface for" << interfaceName <<endl;
575         return nullptr;
576 }
577
578 gboolean AbstractDBusInterface::setProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName,
579                                                                                         const gchar* propertyName, GVariant* value, GError** error, gpointer userData,
580                                                                                         std::function<void (bool, AsyncPropertyReply::Error)> callback)
581 {
582         if(DebugOut::getDebugThreshhold() >= 6)
583         {
584                 DebugOut(6)<<"DBus SetProperty call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interfaceName<<" property: "<<propertyName<<endl;
585                 DebugOut(6)<<"DBus SetProperty call path: "<<objectPath<<endl;
586         }
587
588         if(objectMap.count(objectPath))
589         {
590                 objectMap[objectPath]->setProperty(propertyName, value, callback);
591                 return true;
592         }
593
594         return false;
595 }
596
597 void AbstractDBusInterface::setProperty(string propertyName, GVariant *value, std::function<void (bool, AsyncPropertyReply::Error)> callback)
598 {
599         if(properties.count(propertyName))
600         {
601                 properties[propertyName]->fromVariant(value, callback);
602         }
603         else if(callback)
604         {
605                 callback(false, AsyncPropertyReply::InvalidOperation);
606         }
607 }
608
609 GVariant *AbstractDBusInterface::getProperty(string propertyName)
610 {
611         if(properties.count(propertyName))
612                 return properties[propertyName]->toVariant();
613
614         return nullptr;
615 }
616
617 void AbstractDBusInterface::setTimeout(int timeout)
618 {
619         if(!signaller)
620                 signaller = DBusSignaller::factory(timeout);
621 }
622