4de26fdf9968c8bbd3314106369b4a5e3d626419
[profile/ivi/automotive-message-broker.git] / plugins / dbus / 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
30 #include "dbussignaller.h"
31
32 static DBusSignaller* signaller = nullptr;
33
34 unordered_map<string, AbstractDBusInterface*> AbstractDBusInterface::objectMap;
35 PropertyList AbstractDBusInterface::mimplementedProperties;
36
37 const uint getPid(const char *owner)
38 {
39         GError* error = nullptr;
40         GDBusProxy* dbus = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL,
41                                                                                                          "org.freedesktop.DBus",
42                                                                                                          "/",
43                                                                                                          "org.freedesktop.DBus",
44                                                                                                          NULL,
45                                                                                                          &error);
46
47         if(error)
48         {
49                 throw std::runtime_error(error->message);
50         }
51
52         error = nullptr;
53
54         GVariant* pid = g_dbus_proxy_call_sync(dbus, "GetConnectionUnixProcessID", g_variant_new("(s)", owner), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
55
56         if(error)
57         {
58                 throw std::runtime_error(error->message);
59         }
60
61         uint thePid=0;
62
63         g_variant_get(pid,"(u)",&thePid);
64
65         return thePid;
66 }
67
68
69
70 static void handleMyMethodCall(GDBusConnection       *connection,
71                                                          const gchar           *sender,
72                                                          const gchar           *object_path,
73                                                          const gchar           *interface_name,
74                                                          const gchar           *method_name,
75                                                          GVariant              *parameters,
76                                                          GDBusMethodInvocation *invocation,
77                                                          gpointer               user_data)
78 {
79
80         std::string method = method_name;
81         AbstractDBusInterface* iface = static_cast<AbstractDBusInterface*>(user_data);
82
83         if(DebugOut::getDebugThreshhold() >= 6)
84         {
85                 DebugOut(6)<<"DBus method call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interface_name<<" method: "<<method<<endl;
86                 DebugOut(6)<<"DBus method call path: "<<object_path<<endl;
87         }
88
89         g_assert(iface);
90
91         if(method == "GetHistory")
92         {
93                 double beginTime = 0;
94                 double endTime = 0;
95
96                 g_variant_get(parameters, "(dd)", &beginTime, &endTime);
97
98                 auto propertyMap = iface->getProperties();
99
100                 PropertyList propertyList;
101
102                 for(auto itr = propertyMap.begin(); itr != propertyMap.end(); itr++)
103                 {
104                         AbstractProperty* prop = (*itr).second;
105
106                         if(!contains(propertyList, prop->ambPropertyName()))
107                                 propertyList.push_back(prop->ambPropertyName());
108                 }
109
110                 std::string ifaceName = iface->interfaceName();
111
112                 AsyncRangePropertyRequest request;
113
114                 request.properties = propertyList;
115                 request.timeBegin = beginTime;
116                 request.timeEnd = endTime;
117                 request.zone = iface->zone();
118                 //request.sourceUuid = iface->source();
119
120                 request.completed = [&invocation,&ifaceName](AsyncRangePropertyReply* r)
121                 {
122                         auto reply = amb::make_unique(r);
123                         if(!reply->success)
124                         {
125                                 stringstream str;
126                                 str<<"Error during request: "<<AsyncPropertyReply::errorToStr(reply->error);
127                                 ifaceName += ".Error";
128                                 g_dbus_method_invocation_return_dbus_error(invocation, ifaceName.c_str(), str.str().c_str());
129                                 return;
130                         }
131
132                         if(!reply->values.size())
133                         {
134                                 ifaceName += ".Error";
135                                 g_dbus_method_invocation_return_dbus_error(invocation, ifaceName.c_str(), "No results");
136                                 return;
137                         }
138
139                         GVariantBuilder builder;
140                         g_variant_builder_init(&builder, G_VARIANT_TYPE("a(svd)"));
141
142
143                         for(auto itr = reply->values.begin(); itr != reply->values.end(); itr++)
144                         {
145                                 AbstractPropertyType* value = *itr;
146
147                                 g_variant_builder_add(&builder, "(svd)", value->name.c_str(), g_variant_ref(value->toVariant()),value->timestamp);
148                         }
149
150                         g_dbus_method_invocation_return_value(invocation,g_variant_new("(a(svd))",&builder));
151                 };
152
153                 iface->re->getRangePropertyAsync(request);
154
155                 return;
156         }
157
158         else if(boost::algorithm::starts_with(method, "Get"))
159         {
160                 std::string propertyName = method.substr(3);
161                 auto propertyMap = iface->getProperties();
162                 if(propertyMap.find(propertyName) == propertyMap.end())
163                 {
164                         g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method.");
165                         return;
166                 }
167
168                 AbstractProperty* property = propertyMap[propertyName];
169
170                 GError *error = NULL;
171
172                 GVariant **params = g_new(GVariant*,4);
173                 GVariant *val = g_variant_ref(property->value()->toVariant());
174                 params[0] = g_variant_new("v", val);
175                 params[1] = g_variant_new("d",property->timestamp);
176                 params[2] = g_variant_new("i",property->value()->sequence);
177                 params[3] = g_variant_new("i",property->updateFrequency());
178
179                 GVariant *tuple_variant = g_variant_new_tuple(params,4);
180
181                 g_dbus_method_invocation_return_value(invocation, tuple_variant);
182
183                 g_free(params);
184                 g_variant_unref(val);
185
186                 if(error)
187                 {
188                         DebugOut(DebugOut::Error)<<error->message<<endl;
189                         g_error_free(error);
190                 }
191                 return;
192         }
193
194         g_dbus_method_invocation_return_error(invocation,G_DBUS_ERROR,G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method.");
195 }
196
197 AbstractDBusInterface::AbstractDBusInterface(string interfaceName, string objectName,
198                                                                                          GDBusConnection* connection)
199         : mInterfaceName(interfaceName), mConnection(connection), mPropertyName(objectName), supported(false), zoneFilter(Zone::None), mTime(0), regId(0)
200 {
201         startRegistration();
202
203         mObjectPath = "/" + objectName;
204 }
205
206 AbstractDBusInterface::~AbstractDBusInterface()
207 {
208         unregisterObject();
209
210         PropertyList impl = implementedProperties();
211
212         for(auto itr = impl.begin(); itr != impl.end(); itr++)
213         {
214                 if(properties.find(*itr) != properties.end())
215                 {
216                         // Deleted in ~DBusSink()
217                         //delete properties[*itr];
218                         properties.erase(*itr);
219                 }
220         }
221
222         objectMap.erase(mObjectPath);
223
224 }
225
226 void AbstractDBusInterface::addProperty(AbstractProperty* property)
227 {
228         string nameToLower = property->name;
229         boost::algorithm::to_lower<string>(nameToLower);
230
231         string access;
232
233         if(property->access() == AbstractProperty::Read)
234                 access = "read";
235         else if(property->access() == AbstractProperty::Write)
236                 access = "write";
237         else if(property->access() == AbstractProperty::ReadWrite)
238                 access = "readwrite";
239         else throw -1; //FIXME: don't throw
240
241         std::string pn = property->name;
242
243         ///see which properties are supported:
244         introspectionXml +=
245                         "<property type='"+ string(property->signature()) + "' name='"+ pn +"' access='"+access+"' />"
246                         "<method name='Get" + pn + "'>"
247                         "       <arg type='v' direction='out' name='value' />"
248                         "       <arg type='d' direction='out' name='timestamp' />"
249                         "       <arg type='i' direction='out' name='sequence' />"
250                         "   <arg type='i' direction='out' name='updateFrequency' />"
251                         "</method>"
252                         "<signal name='" + pn + "Changed' >"
253                         "       <arg type='v' name='" + nameToLower + "' direction='out' />"
254                         "       <arg type='d' name='imestamp' direction='out' />"
255                         "</signal>"
256                         "<property type='i' name='" + property->name + "Sequence' access='read' />";
257
258         properties[property->name] = property;
259
260         if(!contains(mimplementedProperties, property->ambPropertyName()))
261         {
262                 std::string pname = property->ambPropertyName();
263                 mimplementedProperties.push_back(pname);
264         }
265 }
266
267 void AbstractDBusInterface::registerObject()
268 {
269         if(!mConnection)
270         {
271                 throw std::runtime_error("forgot to call setDBusConnection on AbstractDBusInterface");
272         }
273
274         if(introspectionXml.empty())
275         {
276                 cerr<<"no interface to export: "<<mInterfaceName<<endl;
277                 throw -1;
278         }
279
280         if(!boost::algorithm::ends_with(introspectionXml,"</node>"))
281         {
282                 introspectionXml += "</interface>"
283                                 "</node>";
284         }
285
286         GError* error=NULL;
287
288         GDBusNodeInfo* introspection = g_dbus_node_info_new_for_xml(introspectionXml.c_str(), &error);
289
290         if(!introspection || error)
291         {
292
293                 DebugOut(DebugOut::Error)<<"Error in "<<__FILE__<<" - "<<__FUNCTION__<<":"<<__LINE__<<endl;
294                 DebugOut(DebugOut::Error)<<error->message<<endl;
295                 DebugOut(DebugOut::Error)<<"probably bad xml:"<<endl;
296                 DebugOut(DebugOut::Error)<<introspectionXml<<endl;
297
298                 g_error_free(error);
299
300                 return;
301         }
302
303         GDBusInterfaceInfo* mInterfaceInfo = g_dbus_node_info_lookup_interface(introspection, mInterfaceName.c_str());
304
305
306         const GDBusInterfaceVTable vtable = { handleMyMethodCall, AbstractDBusInterface::getProperty, AbstractDBusInterface::setProperty };
307
308         GError* error2=NULL;
309
310         DebugOut()<<"registering DBus path: "<<mObjectPath<<endl;
311
312         regId = g_dbus_connection_register_object(mConnection, mObjectPath.c_str(), mInterfaceInfo, &vtable, this, NULL, &error2);
313         g_dbus_node_info_unref(introspection);
314         if(error2)
315         {
316                 DebugOut(DebugOut::Error)<<error2->message<<endl;
317                 g_error_free(error2);
318         }
319
320         if(regId == 0)
321         {
322                 DebugOut(DebugOut::Error)<<"We failed to register on DBus"<<endl;
323         }
324 }
325
326 void AbstractDBusInterface::unregisterObject()
327 {
328         if(regId)
329         {
330                 DebugOut()<<__FUNCTION__<<endl;
331                 g_dbus_connection_unregister_object(mConnection, regId);
332         }
333
334         regId=0;
335 }
336
337 void AbstractDBusInterface::updateValue(AbstractProperty *property)
338 {
339         if(mConnection == nullptr)
340         {
341                 return;
342         }
343
344         if(isRegistered())
345                 signaller->fireSignal(mConnection, mObjectPath, mInterfaceName, "PropertiesChanged", property);
346 }
347
348 std::list<AbstractDBusInterface *> AbstractDBusInterface::getObjectsForProperty(string object)
349 {
350         std::list<AbstractDBusInterface *> l;
351         for(auto itr = objectMap.begin(); itr != objectMap.end(); itr++)
352         {
353                 AbstractDBusInterface * interface = (*itr).second;
354                 if(interface->objectName() == object)
355                         l.push_back(interface);
356         }
357
358         return l;
359 }
360 list<AbstractDBusInterface *> AbstractDBusInterface::interfaces()
361 {
362         std::list<AbstractDBusInterface*> ifaces;
363
364         for(auto itr = objectMap.begin(); itr != objectMap.end(); itr++)
365         {
366                 ifaces.push_back((*itr).second);
367         }
368
369         return ifaces;
370 }
371
372 std::vector<std::string> AbstractDBusInterface::supportedInterfaces()
373 {
374         std::vector<std::string> ifaces;
375
376         for(auto itr : objectMap)
377         {
378                 if(itr.second->isSupported())
379                         ifaces.push_back(itr.second->objectName());
380         }
381
382         return ifaces;
383 }
384
385 bool AbstractDBusInterface::implementsProperty(string property)
386 {
387         for(auto itr = properties.begin(); itr != properties.end(); itr++)
388         {
389                 if((*itr).first == property)
390                 {
391                         return true;
392                 }
393         }
394
395         return false;
396 }
397
398 void AbstractDBusInterface::startRegistration()
399 {
400         //unregisterObject();
401         introspectionXml ="<node>" ;
402         introspectionXml +=
403                         "<interface name='"+ mInterfaceName + "' >"
404                         "<property type='i' name='Zone' access='read' />"
405                         "<property type='d' name='Time' access='read' />"
406                         "<method name='GetHistory'>"
407                         "       <arg type='d' direction='in' name='beginTimestamp' />"
408                         "       <arg type='d' direction='in' name='endTimestamp' />"
409                         "   <arg type='a(svd)' direction='out' name='result' />"
410                         "</method>";
411 }
412
413 GVariant* AbstractDBusInterface::getProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName, const gchar* propertyName, GError** error, gpointer userData)
414 {
415         if(DebugOut::getDebugThreshhold() >= 6)
416         {
417                 DebugOut(6)<<"DBus GetProperty call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interfaceName<<" property: "<<propertyName<<endl;
418                 DebugOut(6)<<"DBus GetProperty call path: "<<objectPath<<endl;
419         }
420
421         std::string pn = propertyName;
422         if(pn == "Time")
423         {
424                 if(objectMap.find(objectPath) == objectMap.end())
425                 {
426                         DebugOut(DebugOut::Error)<<objectPath<<" is not a valid object path."<<endl;
427                         return nullptr;
428                 }
429                 double time = objectMap[objectPath]->time();
430
431                 GVariant* value = g_variant_new("d", time);
432                 return value;
433         }
434
435         if(boost::ends_with(pn, "Sequence"))
436         {
437                 AbstractDBusInterface* t = static_cast<AbstractDBusInterface*>(userData);
438
439                 int pos = pn.find("Sequence");
440
441                 std::string p = pn.substr(0,pos);
442
443                 AbstractProperty* theProperty = t->property(p);
444
445                 if(!theProperty)
446                 {
447                         DebugOut(DebugOut::Error)<<"Invalid Sequence property: "<<p<<endl;
448                         return nullptr;
449                 }
450
451                 int sequence = theProperty->sequence;
452
453                 GVariant* value = g_variant_new("i", sequence);
454                 return value;
455         }
456
457         if(pn == "Zone")
458         {
459                 if(objectMap.find(objectPath) == objectMap.end())
460                 {
461                         DebugOut(DebugOut::Error)<<objectPath<<" is not a valid object path."<<endl;
462                         return nullptr;
463                 }
464
465                 Zone::Type zone = objectMap[objectPath]->zone();
466
467                 GVariant* value = g_variant_new("i",(int)zone);
468                 return value;
469         }
470
471         if(objectMap.count(objectPath))
472         {
473                 GVariant* value = objectMap[objectPath]->getProperty(propertyName);
474                 return value;
475         }
476
477
478         DebugOut(DebugOut::Error)<<"No interface for" << interfaceName <<endl;
479         return nullptr;
480 }
481
482 gboolean AbstractDBusInterface::setProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName, const gchar* propertyName, GVariant* value, GError** error, gpointer userData)
483 {
484         if(DebugOut::getDebugThreshhold() >= 6)
485         {
486                 DebugOut(6)<<"DBus SetProperty call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interfaceName<<" property: "<<propertyName<<endl;
487                 DebugOut(6)<<"DBus SetProperty call path: "<<objectPath<<endl;
488         }
489
490         if(objectMap.count(objectPath))
491         {
492                 objectMap[objectPath]->setProperty(propertyName, value);
493                 return true;
494         }
495
496         return false;
497 }
498
499 void AbstractDBusInterface::setProperty(string propertyName, GVariant *value)
500 {
501         if(properties.count(propertyName))
502         {
503                 properties[propertyName]->fromVariant(value);
504         }
505         else
506         {
507                 throw -1;
508         }
509 }
510
511 GVariant *AbstractDBusInterface::getProperty(string propertyName)
512 {
513         if(properties.count(propertyName))
514                 return properties[propertyName]->toVariant();
515         else
516                 throw -1;
517 }
518
519 void AbstractDBusInterface::setTimeout(int timeout)
520 {
521         if(!signaller)
522                 signaller = DBusSignaller::factory(timeout);
523 }
524