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