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