Updated comments and fixed tabbing
[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
133                         auto time = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, "Time", nullptr, iface);
134                         auto zone = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, "Zone", nullptr, iface);
135                         g_variant_builder_add(&builder, "{sv}", "Time", g_variant_new("v", time));
136                         g_variant_builder_add(&builder, "{sv}", "Zone", g_variant_new("v", zone));
137
138                         g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", &builder));
139                         return;
140                 }
141                 else if(method == "Set")
142                 {
143                         gchar* ifaceName = nullptr;
144                         gchar* propName = nullptr;
145                         GVariant* value;
146                         g_variant_get(parameters, "(ssv)", &ifaceName, &propName, &value);
147
148                         AbstractDBusInterface::setProperty(connection, sender, object_path, ifaceName, propName, value, nullptr, iface,
149                                                                                            [&invocation, &ifaceName](bool success, AsyncPropertyReply::Error error) {
150                                 if(success)
151                                 {
152                                         g_dbus_method_invocation_return_value(invocation, nullptr);
153                                 }
154                                 else
155                                 {
156                                         g_dbus_method_invocation_return_dbus_error(invocation, ifaceName, AsyncPropertyReply::errorToStr(error).c_str());
157                                 }
158                         });
159                         return;
160                 }
161         }
162         else if(method == "GetHistory")
163         {
164                 double beginTime = 0;
165                 double endTime = 0;
166
167                 g_variant_get(parameters, "(dd)", &beginTime, &endTime);
168
169                 auto propertyMap = iface->getProperties();
170
171                 PropertyList propertyList;
172
173                 for(auto itr = propertyMap.begin(); itr != propertyMap.end(); itr++)
174                 {
175                         VariantType* prop = (*itr).second;
176
177                         if(!contains(propertyList, prop->ambPropertyName()))
178                                 propertyList.push_back(prop->ambPropertyName());
179                 }
180
181                 std::string ifaceName = iface->interfaceName();
182
183                 AsyncRangePropertyRequest request;
184
185                 request.properties = propertyList;
186                 request.timeBegin = beginTime;
187                 request.timeEnd = endTime;
188                 request.zone = iface->zone();
189                 //request.sourceUuid = iface->source();
190
191                 request.completed = [&invocation,&ifaceName](AsyncRangePropertyReply* r)
192                 {
193                         auto reply = amb::make_unique(r);
194                         if(!reply->success)
195                         {
196                                 stringstream str;
197                                 str<<"Error during request: "<<AsyncPropertyReply::errorToStr(reply->error);
198                                 ifaceName += ".Error";
199                                 g_dbus_method_invocation_return_dbus_error(invocation, ifaceName.c_str(), str.str().c_str());
200                                 return;
201                         }
202
203                         if(!reply->values.size())
204                         {
205                                 ifaceName += ".Error";
206                                 g_dbus_method_invocation_return_dbus_error(invocation, ifaceName.c_str(), "No results");
207                                 return;
208                         }
209
210                         GVariantBuilder builder;
211                         g_variant_builder_init(&builder, G_VARIANT_TYPE("a{svd}"));
212
213
214                         for(auto itr = reply->values.begin(); itr != reply->values.end(); itr++)
215                         {
216                                 AbstractPropertyType* value = *itr;
217
218                                 g_variant_builder_add(&builder, "{svd}", value->name.c_str(), g_variant_ref(value->toVariant()),value->timestamp);
219                         }
220
221                         g_dbus_method_invocation_return_value(invocation,g_variant_new("(a{svd})",&builder));
222                 };
223
224                 iface->re->getRangePropertyAsync(request);
225
226                 return;
227         }
228
229         ///TODO: Deprecated in 0.15
230         else if(boost::algorithm::starts_with(method, "Get"))
231         {
232                 amb::deprecateMethod(method, "0.15");
233                 std::string propertyName = method.substr(3);
234                 auto propertyMap = iface->getProperties();
235                 if(propertyMap.find(propertyName) == propertyMap.end())
236                 {
237                         g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method.");
238                         return;
239                 }
240
241                 VariantType * property = propertyMap[propertyName];
242
243                 GError *error = NULL;
244
245                 GVariant **params = g_new(GVariant*,4);
246                 GVariant *val = g_variant_ref(property->value()->toVariant());
247                 params[0] = g_variant_new("v", val);
248                 params[1] = g_variant_new("d",property->timestamp());
249                 params[2] = g_variant_new("i",property->value()->sequence);
250                 params[3] = g_variant_new("i",property->updateFrequency());
251
252                 GVariant *tuple_variant = g_variant_new_tuple(params,4);
253
254                 g_dbus_method_invocation_return_value(invocation, tuple_variant);
255
256                 g_free(params);
257                 g_variant_unref(val);
258
259                 if(error)
260                 {
261                         DebugOut(DebugOut::Error)<<error->message<<endl;
262                         g_error_free(error);
263                 }
264                 return;
265         }
266
267         g_dbus_method_invocation_return_error(invocation,G_DBUS_ERROR,G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method.");
268 }
269
270 AbstractDBusInterface::AbstractDBusInterface(string interfaceName, string objectName,
271                                                                                          GDBusConnection* connection)
272         : mInterfaceName(interfaceName), mConnection(connection), mPropertyName(objectName), supported(false), zoneFilter(Zone::None), mTime(0), regId(0)
273 {
274         startRegistration();
275
276         mObjectPath = "/" + objectName;
277 }
278
279 AbstractDBusInterface::~AbstractDBusInterface()
280 {
281         unregisterObject();
282
283         PropertyList impl = implementedProperties();
284
285         for(auto itr = impl.begin(); itr != impl.end(); itr++)
286         {
287                 if(properties.find(*itr) != properties.end())
288                 {
289                         // Deleted in ~DBusSink()
290                         //delete properties[*itr];
291                         properties.erase(*itr);
292                 }
293         }
294
295         objectMap.erase(mObjectPath);
296 }
297
298 void AbstractDBusInterface::addProperty(VariantType * property)
299 {
300         string nameToLower = property->name();
301         boost::algorithm::to_lower<string>(nameToLower);
302
303         string access;
304
305         if(property->access() == VariantType::Read)
306                 access = "read";
307         else if(property->access() == VariantType::Write)
308                 access = "write";
309         else if(property->access() == VariantType::ReadWrite)
310                 access = "readwrite";
311         else throw -1; //FIXME: don't throw
312
313         std::string pn = property->name();
314
315         ///see which properties are supported:
316         introspectionXml +=
317                         "<property type='"+ string(property->signature()) + "' name='"+ pn +"' access='"+access+"' />"
318                         "<!-- 'GetFoo' is deprecated as of 0.14 -->"
319                         /// TODO: remove GetFoo in 0.15
320                         "<method name='Get" + pn + "'>"
321                         "       <arg type='v' direction='out' name='value' />"
322                         "       <arg type='d' direction='out' name='timestamp' />"
323                         "       <arg type='i' direction='out' name='sequence' />"
324                         "   <arg type='i' direction='out' name='updateFrequency' />"
325                         "</method>"
326                         "<property type='i' name='" + pn + "Sequence' access='read' />";
327
328         properties[pn] = property;
329
330         if(!contains(mimplementedProperties, property->ambPropertyName()))
331         {
332                 std::string pname = property->ambPropertyName();
333                 mimplementedProperties.push_back(pname);
334         }
335 }
336
337 void AbstractDBusInterface::registerObject()
338 {
339         if(!mConnection)
340         {
341                 throw std::runtime_error("forgot to call setDBusConnection on AbstractDBusInterface");
342         }
343
344         if(introspectionXml.empty())
345         {
346                 cerr<<"no interface to export: "<<mInterfaceName<<endl;
347                 throw -1;
348         }
349
350         if(!boost::algorithm::ends_with(introspectionXml,"</node>"))
351         {
352                 introspectionXml += "</interface>"
353                                 "</node>";
354         }
355
356         GError* error=NULL;
357
358         GDBusNodeInfo* introspection = g_dbus_node_info_new_for_xml(introspectionXml.c_str(), &error);
359
360         if(!introspection || error)
361         {
362
363                 DebugOut(DebugOut::Error)<<"Error in "<<__FILE__<<" - "<<__FUNCTION__<<":"<<__LINE__<<endl;
364                 DebugOut(DebugOut::Error)<<error->message<<endl;
365                 DebugOut(DebugOut::Error)<<"probably bad xml:"<<endl;
366                 DebugOut(DebugOut::Error)<<introspectionXml<<endl;
367
368                 g_error_free(error);
369
370                 return;
371         }
372
373         GDBusInterfaceInfo* mInterfaceInfo = g_dbus_node_info_lookup_interface(introspection, mInterfaceName.c_str());
374
375
376         const GDBusInterfaceVTable vtable = { handleMyMethodCall, nullptr, nullptr };
377
378         GError* error2=NULL;
379
380         DebugOut()<<"registering DBus path: "<<mObjectPath<<endl;
381
382         regId = g_dbus_connection_register_object(mConnection, mObjectPath.c_str(), mInterfaceInfo, &vtable, this, NULL, &error2);
383         g_dbus_node_info_unref(introspection);
384         if(error2)
385         {
386                 DebugOut(DebugOut::Error)<<error2->message<<endl;
387                 g_error_free(error2);
388         }
389
390         if(regId == 0)
391         {
392                 DebugOut(DebugOut::Error)<<"We failed to register on DBus"<<endl;
393         }
394 }
395
396 void AbstractDBusInterface::unregisterObject()
397 {
398         if(regId)
399         {
400                 DebugOut()<<__FUNCTION__<<endl;
401                 g_dbus_connection_unregister_object(mConnection, regId);
402         }
403
404         regId=0;
405 }
406
407 void AbstractDBusInterface::updateValue(VariantType *property)
408 {
409         if(mConnection == nullptr)
410         {
411                 return;
412         }
413
414         if(isRegistered())
415                 signaller->fireSignal(mConnection, mObjectPath, mInterfaceName, "PropertiesChanged", property);
416 }
417
418 std::list<AbstractDBusInterface *> AbstractDBusInterface::getObjectsForProperty(string object)
419 {
420         std::list<AbstractDBusInterface *> l;
421         for(auto itr = objectMap.begin(); itr != objectMap.end(); itr++)
422         {
423                 AbstractDBusInterface * interface = (*itr).second;
424                 if(interface->objectName() == object)
425                         l.push_back(interface);
426         }
427
428         return l;
429 }
430 list<AbstractDBusInterface *> AbstractDBusInterface::interfaces()
431 {
432         std::list<AbstractDBusInterface*> ifaces;
433
434         for(auto itr = objectMap.begin(); itr != objectMap.end(); itr++)
435         {
436                 ifaces.push_back((*itr).second);
437         }
438
439         return ifaces;
440 }
441
442 std::vector<std::string> AbstractDBusInterface::supportedInterfaces()
443 {
444         std::vector<std::string> ifaces;
445
446         for(auto itr : objectMap)
447         {
448                 if(itr.second->isSupported())
449                         ifaces.push_back(itr.second->objectName());
450         }
451
452         return ifaces;
453 }
454
455 bool AbstractDBusInterface::implementsProperty(string property)
456 {
457         for(auto itr = properties.begin(); itr != properties.end(); itr++)
458         {
459                 if((*itr).first == property)
460                 {
461                         return true;
462                 }
463         }
464
465         return false;
466 }
467
468 void AbstractDBusInterface::startRegistration()
469 {
470         //unregisterObject();
471         introspectionXml ="<node>" ;
472         introspectionXml +=
473                         "<interface name='org.freedesktop.DBus.Properties'>"
474                         "<method name='Get'>"
475                         "   <arg type='s' direction='in' name='interface' />"
476                         "   <arg type='s' direction='in' name='property' />"
477                         "   <arg type='v' direction='out' name='value' />"
478                         "</method>"
479                         "<method name='Set'>"
480                         "   <arg type='s' direction='in' name='interface' />"
481                         "   <arg type='s' direction='in' name='property' />"
482                         "   <arg type='v' direction='in' name='value' />"
483                         "</method>"
484                         "<method name='GetAll'>"
485                         "   <arg type='s' direction='in' name='interface' />"
486                         "   <arg type='a{sv}' direction='out' name='interface' />"
487                         "</method>"
488                         "</interface>";
489         introspectionXml +=
490                         "<interface name='"+ mInterfaceName + "' >"
491                         "<property type='i' name='Zone' access='read' />"
492                         "<property type='d' name='Time' access='read' />"
493                         "<method name='GetHistory'>"
494                         "       <arg type='d' direction='in' name='beginTimestamp' />"
495                         "       <arg type='d' direction='in' name='endTimestamp' />"
496                         "   <arg type='a(svd)' direction='out' name='result' />"
497                         "</method>";
498 }
499
500 GVariant* AbstractDBusInterface::getProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName,
501                                                                                          const gchar* propertyName, GError** error, gpointer userData)
502 {
503         if(DebugOut::getDebugThreshhold() >= 6)
504         {
505                 DebugOut(6)<<"DBus GetProperty call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interfaceName<<" property: "<<propertyName<<endl;
506                 DebugOut(6)<<"DBus GetProperty call path: "<<objectPath<<endl;
507         }
508
509         std::string pn = propertyName;
510         if(pn == "Time")
511         {
512                 if(objectMap.find(objectPath) == objectMap.end())
513                 {
514                         DebugOut(DebugOut::Error)<<objectPath<<" is not a valid object path."<<endl;
515                         return nullptr;
516                 }
517                 double time = objectMap[objectPath]->time();
518
519                 GVariant* value = g_variant_new("d", time);
520                 return value;
521         }
522
523         if(boost::ends_with(pn, "Sequence"))
524         {
525                 AbstractDBusInterface* t = static_cast<AbstractDBusInterface*>(userData);
526
527                 int pos = pn.find("Sequence");
528
529                 std::string p = pn.substr(0,pos);
530
531                 VariantType * theProperty = t->property(p);
532
533                 if(!theProperty)
534                 {
535                         DebugOut(DebugOut::Error)<<"Invalid Sequence property: "<<p<<endl;
536                         return nullptr;
537                 }
538
539                 int sequence = theProperty->sequence();
540
541                 GVariant* value = g_variant_new("i", sequence);
542                 return value;
543         }
544
545         if(pn == "Zone")
546         {
547                 if(objectMap.find(objectPath) == objectMap.end())
548                 {
549                         DebugOut(DebugOut::Error)<<objectPath<<" is not a valid object path."<<endl;
550                         return nullptr;
551                 }
552
553                 Zone::Type zone = objectMap[objectPath]->zone();
554
555                 GVariant* value = g_variant_new("i",(int)zone);
556                 return value;
557         }
558
559         if(objectMap.count(objectPath))
560         {
561                 GVariant* value = objectMap[objectPath]->getProperty(propertyName);
562
563                 DebugOut(6) << "Returning value for: " << propertyName << endl;
564                 return value;
565         }
566
567
568         DebugOut(DebugOut::Error)<<"No interface for" << interfaceName <<endl;
569         return nullptr;
570 }
571
572 gboolean AbstractDBusInterface::setProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName,
573                                                                                         const gchar* propertyName, GVariant* value, GError** error, gpointer userData,
574                                                                                         std::function<void (bool, AsyncPropertyReply::Error)> callback)
575 {
576         if(DebugOut::getDebugThreshhold() >= 6)
577         {
578                 DebugOut(6)<<"DBus SetProperty call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interfaceName<<" property: "<<propertyName<<endl;
579                 DebugOut(6)<<"DBus SetProperty call path: "<<objectPath<<endl;
580         }
581
582         if(objectMap.count(objectPath))
583         {
584                 objectMap[objectPath]->setProperty(propertyName, value, callback);
585                 return true;
586         }
587
588         return false;
589 }
590
591 void AbstractDBusInterface::setProperty(string propertyName, GVariant *value, std::function<void (bool, AsyncPropertyReply::Error)> callback)
592 {
593         if(properties.count(propertyName))
594         {
595                 properties[propertyName]->fromVariant(value, callback);
596         }
597         else if(callback)
598         {
599                 callback(false, AsyncPropertyReply::InvalidOperation);
600         }
601 }
602
603 GVariant *AbstractDBusInterface::getProperty(string propertyName)
604 {
605         if(properties.count(propertyName))
606                 return properties[propertyName]->toVariant();
607
608         return nullptr;
609 }
610
611 void AbstractDBusInterface::setTimeout(int timeout)
612 {
613         if(!signaller)
614                 signaller = DBusSignaller::factory(timeout);
615 }
616