7b89a5c27d0276f7ab802be3ac993f8d216d7b51
[profile/ivi/phoned.git] / src / ofono.cpp
1 #include "ofono.h"
2 #include "utils.h"
3
4 #include <gio/gio.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <algorithm>
8
9 #include <stdlib.h>
10
11 #include "Logger.h"
12
13 namespace PhoneD {
14
15 #define OFONO_PREFIX                     "org.ofono"
16
17 #define OFONO_SERVICE                    OFONO_PREFIX
18 #define OFONO_MANAGER_IFACE              OFONO_PREFIX ".Manager"
19 #define OFONO_MODEM_IFACE                OFONO_PREFIX ".Modem"
20 #define OFONO_VOICECALLMANAGER_IFACE     OFONO_PREFIX ".VoiceCallManager"
21 #define OFONO_VOICECALL_IFACE            OFONO_PREFIX ".VoiceCall"
22 #define OFONO_CALLVOLUME_IFACE           OFONO_PREFIX ".CallVolume"
23
24 #define CHECK_FOR_MODEM_POWERED_INTERVAL 60 // interval for check whether 'Selected' remote device is Powered, and power it on, if it is not (in seconds)
25
26 OFono::OFono() :
27     mModemPath( NULL ),
28     mActiveCall( NULL )
29 {
30     LoggerD("entered");
31
32     /*
33     if(getModems()) {
34         LoggerD("Failed to call 'GetModems'");
35     }
36     */
37     Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
38                              OFONO_MANAGER_IFACE, "/",
39                              "ModemAdded", OFono::handleSignal,
40                              this);
41     Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
42                              OFONO_MANAGER_IFACE, "/",
43                              "ModemRemoved", OFono::handleSignal,
44                              this);
45
46     // won't request calls here, since the service OFONO_VOICECALLMANAGER_IFACE may not be available yet
47     //get active calls
48     //if(mModemPath) {
49     //    getCalls();
50     //}
51
52     // periodic check whether modem for 'Selected' remote device is powered
53     // among other things, it handles also the case when the phone gets out of the range of HMI BT
54     g_timeout_add(CHECK_FOR_MODEM_POWERED_INTERVAL*1000, OFono::checkForModemPowered, this);
55 }
56
57 OFono::~OFono() {
58     LoggerD("entered");
59     removeModem(mModemPath);
60 }
61
62 gboolean OFono::checkForModemPowered(gpointer user_data) {
63     OFono *ctx = static_cast<OFono*>(user_data);
64     if(!ctx)
65         return G_SOURCE_CONTINUE; // continuous timeout
66
67     if(!ctx->mModemPath) {
68         LoggerD("No modem available, get all available modems");
69         ctx->getModems();
70     }
71
72     if(!ctx->isModemPowered(ctx->mModemPath)) {
73         LoggerD("modem is not powered");
74         ctx->setModemPowered(ctx->mModemPath, true);
75     }
76
77     return G_SOURCE_CONTINUE; // continuous timeout
78 }
79
80 bool OFono::isModemPowered(const char *modem) {
81     if(!modem)
82         return false;
83
84     GError *err = NULL;
85     GVariant *reply = NULL;
86     reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
87                                          OFONO_SERVICE,
88                                          modem,
89                                          OFONO_MODEM_IFACE,
90                                          "GetProperties",
91                                          NULL,
92                                          NULL,
93                                          G_DBUS_CALL_FLAGS_NONE,
94                                          -1,
95                                          NULL,
96                                          &err);
97
98
99     if(err || !reply) {
100         if(err)
101             g_error_free(err);
102         return false;
103     }
104
105     GVariant *prop = NULL;
106     GVariantIter *iter = NULL;
107     g_variant_get(reply, "(a{sv})", &iter);
108     bool powered = false;
109     while((prop = g_variant_iter_next_value(iter))) {
110         const char *name = NULL;
111         GVariant *value = NULL;
112         g_variant_get(prop, "{sv}", &name, &value);
113         if(name && !strcmp(name, "Powered")) {
114             powered = g_variant_get_boolean(value);
115         }
116     }
117
118     g_variant_unref(reply);
119
120     return powered;
121 }
122
123 // synchronous version of method for setting property
124 bool OFono::setProperty(const char* iface, const char* path, const char *property, int type, void *value) {
125     LoggerD("OFono::setProperty(): name= " << property << " on " << path);
126
127     char signature[2];
128     sprintf(signature, "%c", type); // eg. "b" for boolean
129
130     GError *err = NULL;
131     g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
132                                  OFONO_SERVICE,
133                                  path,
134                                  iface,
135                                  "SetProperty",
136                                  g_variant_new ("(sv)", // floating parameters are consumed, no cleanup/unref needed
137                                      property,
138                                      g_variant_new(signature, value) // floating parameters are consumed, no cleanup/unref needed
139                                  ),
140                                  NULL,
141                                  G_DBUS_CALL_FLAGS_NONE,
142                                  -1,
143                                  NULL,
144                                  &err);
145
146     if(err) {
147         LoggerE("Failed to call \"SetProperty\" DBUS method: " << err->message);
148         g_error_free(err);
149         return false;
150     }
151
152     return true;
153 }
154
155 // asynchronous version of method for setting property
156 void OFono::setPropertyAsync(const char* iface, const char* path, const char *property, int type, void *value, GAsyncReadyCallback callback) {
157     LoggerD("Setting OFono property via asynchronous call: name= " << property << " on " << path);
158
159     char signature[2];
160     sprintf(signature, "%c", type); // eg. "b" for boolean
161
162     g_dbus_connection_call( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
163                             OFONO_SERVICE,
164                             path,
165                             iface,
166                             "SetProperty",
167                             g_variant_new ("(sv)", // floating parameters are consumed, no cleanup/unref needed
168                                 property,
169                                 g_variant_new(signature, value) // floating parameters are consumed, no cleanup/unref needed
170                             ),
171                             NULL,
172                             G_DBUS_CALL_FLAGS_NONE,
173                             -1,
174                             NULL,
175                             callback,
176                             this);
177 }
178
179 // callback for async calls on 'g_dbus'
180 void OFono::GDbusAsyncReadyCallback(GObject *source, GAsyncResult *result, gpointer user_data) {
181     //process result here if needed
182     //LoggerD("Async method call finished, ie. callback received");
183 }
184
185 void OFono::setModemPowered(const char *modem, bool powered) {
186     LoggerD("Setting modem 'Powered': " << modem << " to " << powered);
187     setPropertyAsync(OFONO_MODEM_IFACE, modem, "Powered", DBUS_TYPE_BOOLEAN, &powered, OFono::asyncSetModemPoweredCallback);
188 }
189
190 void OFono::asyncSetModemPoweredCallback(GObject *source, GAsyncResult *result, gpointer user_data) {
191     GError *err = NULL;
192     g_dbus_connection_call_finish(g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL), result, &err);
193     if(err) {
194         LoggerE("Failed to set 'Powered' property on modem: " << err->message);
195         OFono *ctx = static_cast<OFono*>(user_data);
196         if(!ctx) {
197             LoggerE("Invalid ctx");
198             return;
199         }
200         // notify about the failure
201         if(!ctx->isModemPowered(ctx->mModemPath)) // Modem is not 'Powered', ie. the remote BT device is not 'Selected' for BT operations
202             ctx->setModemPoweredFailed(err?err->message:"Failed to set 'Powered' property on Modem.");
203         g_error_free(err);
204     }
205     else
206         LoggerE("Property 'Powered' successfuly set on modem");
207 }
208
209 void OFono::asyncSelectModemCallback(GObject *source, GAsyncResult *result, gpointer user_data) {
210     CtxCbData *data = static_cast<CtxCbData*>(user_data);
211     if(!data || !data->ctx || !data->data1) {
212         LoggerE("Invalid callback data");
213         return;
214     }
215     OFono *ctx = static_cast<OFono*>(data->ctx);
216     char *addr = static_cast<char*>(data->data1);
217
218     std::string btAddress = addr;
219
220     free(addr);
221     delete data;
222
223     GError *err = NULL;
224     GVariant *reply;
225     reply = g_dbus_connection_call_finish(g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL), result, &err);
226     if(err || !reply) {
227         if(err) {
228             LoggerE("Failed to 'GetModems': " << err->message);
229             g_error_free(err);
230         }
231         else if(!reply)
232             LoggerE("Reply from calling 'GetModems' method is NULL");
233
234         return;
235     }
236
237     // path of retrieved modems is in form: /hfp/AABBCCDDEEFF_FFEEDDCCBBAA
238     // where: AABBCCDDEEFF is MAC address of default local BT device
239     //   and: FFEEDDCCBBAA is MAC address of selected remote BT device
240     // we expect given MAC address to be in the form: AA:BB:CC:DD:EE:FF
241     // remove all non-digit characters, eg. ":","-"
242     btAddress.erase(std::remove_if(btAddress.begin(), btAddress.end(), isnxdigit), btAddress.end());
243
244     GVariantIter *modems;
245     GVariantIter *props;
246     const char *path;
247     g_variant_get(reply, "(a(oa{sv}))", &modems);
248     while(g_variant_iter_next(modems, "(oa{sv})", &path, &props)) {
249         // check if the modem is from selected remote device
250         std::string pathString(path);
251         size_t idx = pathString.find( "_" ) + 1; // index of address of remote device
252         std::string modemRemoteBtAddress = pathString.substr (idx, pathString.length()-idx);
253
254         // remove additional underscores
255         modemRemoteBtAddress.erase(std::remove_if(modemRemoteBtAddress.begin(),modemRemoteBtAddress.end(),isnxdigit),modemRemoteBtAddress.end());
256         LoggerD("modem paths to be compared: %s, %s", modemRemoteBtAddress.c_str(),btAddress.c_str());
257
258         if(!modemRemoteBtAddress.compare(btAddress))
259         {
260             ctx->addModem(path, props);
261             // currently only one modem is supported - that's why the break is here
262             // take the first one from the list and make it 'default'
263             break;
264         }
265     }
266
267     g_variant_unref(reply);
268 }
269
270 void OFono::selectModem(std::string &btAddress) {
271     LoggerD("Selecting modem: " << btAddress);
272     // Retrieving list of available modems to get the one that is for 'wanted' device
273     g_dbus_connection_call( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
274                             OFONO_SERVICE,
275                             "/",
276                             OFONO_MANAGER_IFACE,
277                             "GetModems",
278                             NULL,
279                             NULL,
280                             G_DBUS_CALL_FLAGS_NONE,
281                             -1,
282                             NULL,
283                             OFono::asyncSelectModemCallback,
284                             new CtxCbData(this, NULL, strdup(btAddress.c_str()), NULL));
285 }
286
287 void OFono::addModem(const char *path, GVariantIter *props) {
288     LoggerD("entered");
289
290     if(!path || !props)
291         return;
292
293     // shouldn't happen, but just in case free the memory here
294     if(mModemPath)
295         free(mModemPath);
296
297     mModemPath = strdup(path); // make a copy of path, 'cause it will be unref-ed
298
299     const char *key = NULL;
300     GVariant *value = NULL;
301     bool online = FALSE;
302     while(g_variant_iter_next(props, "{sv}", &key, &value)) {
303         if(!strcmp(key, "Powered")) {
304             //bool powered = g_variant_get_boolean(value);
305         }
306         else if(!strcmp(key, "Online")) {
307             online = g_variant_get_boolean(value);
308         }
309     }
310
311     Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
312                              OFONO_VOICECALLMANAGER_IFACE, mModemPath,
313                              "CallAdded", OFono::handleSignal,
314                              this);
315
316     Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
317                              OFONO_VOICECALLMANAGER_IFACE, mModemPath,
318                              "PropertyChanged", OFono::handleSignal,
319                              this);
320
321     Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
322                              OFONO_VOICECALLMANAGER_IFACE, mModemPath,
323                              "CallRemoved", OFono::handleSignal,
324                              this);
325
326     Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
327                              OFONO_MODEM_IFACE, mModemPath,
328                              "PropertyChanged", OFono::handleSignal,
329                              this);
330
331     if(!online) {
332         // power on modem
333         setModemPowered(mModemPath, true);
334     }
335     else
336     {
337         // we can query calls from here, since the modem is already powered
338         getCalls();
339         // when the modem is already online, "PropertyChanged" for "Paired" is not emited
340         // to handle all functionality to modem being powered, call modemPowered from here as well
341         modemPowered(true);
342     }
343 }
344
345 void OFono::unselectModem() {
346     removeModem(mModemPath);
347 }
348
349 void OFono::removeModem(const char *modemPath) {
350     LoggerD("Removing modem: " << (modemPath?modemPath:"INVALID DATA"));
351
352     // remove all subscriptions to DBUS signals from the modem
353     if(modemPath) {
354         Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
355                                     OFONO_VOICECALLMANAGER_IFACE, modemPath,
356                                     "CallAdded");
357         Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
358                                     OFONO_VOICECALLMANAGER_IFACE, modemPath,
359                                     "PropertyChanged");
360         Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
361                                     OFONO_VOICECALLMANAGER_IFACE, modemPath,
362                                     "CallRemoved");
363         Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
364                                     OFONO_MODEM_IFACE, modemPath,
365                                     "PropertyChanged");
366
367         // power off modem
368         setModemPowered(modemPath, false);
369         // at this point we will not get notification "PropertyChanged" on 'Powered' property,
370         // call 'modemPowered()' method directly from here
371         modemPowered(false);
372
373         // free the memory
374         if(modemPath && mModemPath && !strcmp(modemPath, mModemPath)) {
375             free(mModemPath);
376             mModemPath = NULL;
377         }
378     }
379 }
380
381 void OFono::getModems()
382 {
383     LoggerD("entered");
384
385     GError *err = NULL;
386     GVariant *reply = NULL;
387     reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
388                                          OFONO_SERVICE,
389                                          "/",
390                                          OFONO_MANAGER_IFACE,
391                                          "GetModems",
392                                          NULL,
393                                          NULL,
394                                          G_DBUS_CALL_FLAGS_NONE,
395                                          -1,
396                                          NULL,
397                                          &err);
398
399     if(err || !reply) {
400         if(err) {
401             LoggerE("error calling GetModems method");
402             g_error_free(err);
403         }
404         else if(!reply)
405             LoggerE("reply is NULL");
406
407         return;
408     }
409
410     GVariantIter *modems;
411     GVariantIter *props;
412     const char *path;
413     g_variant_get(reply, "(a(oa{sv}))", &modems);
414     while(g_variant_iter_next(modems, "(oa{sv})", &path, &props)) {
415         // check if the modem is from selected remote device
416         std::string pathString(path);
417         size_t idx = pathString.find( "_" ) + 1; // index of address of remote device
418         std::string modemRemoteBtAddress = pathString.substr (idx, pathString.length()-idx);
419
420         // remove additional underscores
421         modemRemoteBtAddress.erase(std::remove_if(modemRemoteBtAddress.begin(),modemRemoteBtAddress.end(),isnxdigit),modemRemoteBtAddress.end());
422
423         // check if it is a valid bluetooth MAC address
424         if(makeMACFromRawMAC(modemRemoteBtAddress))
425         {
426             LoggerD("Modem found: " << modemRemoteBtAddress);
427             modemAdded(modemRemoteBtAddress);
428         }
429
430         g_variant_iter_free(props);
431     }
432
433     g_variant_iter_free(modems);
434     g_variant_unref(reply);
435
436     return;
437 }
438
439 void OFono::getCalls()
440 {
441     LoggerD("entered");
442
443     GError *err = NULL;
444     GVariant *reply = NULL;
445     reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
446                                          OFONO_SERVICE,
447                                          mModemPath,
448                                          OFONO_VOICECALLMANAGER_IFACE,
449                                          "GetCalls",
450                                          NULL,
451                                          NULL,
452                                          G_DBUS_CALL_FLAGS_NONE,
453                                          -1,
454                                          NULL,
455                                          &err);
456
457
458     if(err || !reply) {
459         if(err) {
460             LoggerE("error calling GetCalls method");
461             g_error_free(err);
462         }
463         else if(!reply)
464             LoggerE("reply is NULL");
465
466         return;
467     }
468
469     GVariantIter *calls;
470     GVariantIter *props;
471     const char *path;
472     g_variant_get(reply, "(a(oa{sv}))", &calls);
473     while(g_variant_iter_next(calls, "(oa{sv})", &path, &props)) {
474         addCall(path, props);
475     }
476
477     g_variant_unref(reply);
478
479     return;
480 }
481
482 void OFono::handleSignal(GDBusConnection       *connection,
483                          const gchar           *sender,
484                          const gchar           *object_path,
485                          const gchar           *interface_name,
486                          const gchar           *signal_name,
487                          GVariant              *parameters,
488                          gpointer               user_data)
489 {
490     LoggerD("signal received: \"" << interface_name << "\" -> \"" << signal_name << "\"");
491
492     OFono* ctx = static_cast<OFono*>(user_data);
493     if(!ctx) {
494         LoggerE("Failed to cast object");
495         return;
496     }
497
498     if(!interface_name || !signal_name) {
499         LoggerE("Invalid callback data");
500         return;
501     }
502     if(!strcmp(interface_name, OFONO_MANAGER_IFACE)) {
503         if(!strcmp(signal_name, "ModemAdded")) {
504             const char *modem = NULL;
505             GVariantIter *props = NULL;
506             g_variant_get(parameters, "(oa{sv})", &modem, &props);
507             if(modem) {
508                 std::string modemString(modem);
509                 size_t idx = modemString.find( "_" ) + 1; // index of address of remote device
510                 std::string modemRemoteBtAddress = modemString.substr (idx, modemString.length()-idx);
511                 modemRemoteBtAddress.erase(std::remove_if(modemRemoteBtAddress.begin(),
512                     modemRemoteBtAddress.end(), isnxdigit), modemRemoteBtAddress.end());
513                 if(makeMACFromRawMAC(modemRemoteBtAddress)) {
514                     ctx->modemAdded(modemRemoteBtAddress);
515                 }
516             }
517         }
518     }
519     else if(!strcmp(interface_name, OFONO_MANAGER_IFACE)) {
520         if(!strcmp(signal_name, "ModemRemoved")) {
521             const char *modem = NULL;
522             g_variant_get(parameters, "(o)", &modem);
523             if(modem) {
524                 LoggerD("Modem removed: " << modem);
525                 if(ctx->mModemPath && !strcmp(modem, ctx->mModemPath)) {
526                     ctx->removeModem(ctx->mModemPath);
527                 }
528             }
529         }
530     }
531     else if(!strcmp(interface_name, OFONO_MODEM_IFACE)) {
532         if(!strcmp(signal_name, "PropertyChanged")) {
533             const char *name = NULL;
534             GVariant *value = NULL;
535             g_variant_get(parameters, "(sv)", &name, &value);
536             if(!strcmp(name, "Powered")) {
537                 bool powered = g_variant_get_boolean(value);
538                 LoggerD("\t" << name << " = " << (powered?"TRUE":"FALSE"));
539                 ctx->modemPowered(powered);
540                 // !!! won't request calls here, since the service may not be available yet
541                 // see "Interfaces" "PropertyChanged" on OFONO_MODEM_IFACE
542                 //if(powered)
543                 //    ctx->getCalls();
544             }
545             else if(!strcmp(name, "Online")) {
546                 bool online = g_variant_get_boolean(value);
547                 LoggerD("\t" << name << " = " << (online?"TRUE":"FALSE"));
548             }
549             else if(!strcmp(name, "Interfaces")) {
550                 GVariantIter iter;
551                 GVariant *interface;
552                 GVariant *variant = g_variant_get_child_value(parameters,1); // signature: "v"
553                 GVariant *interfaces = g_variant_get_child_value(variant,0); // signature: "as"
554                 g_variant_iter_init(&iter, interfaces);
555                 while((interface = g_variant_iter_next_value(&iter))) {
556                     const char *iface = g_variant_get_string(interface, NULL);
557                     if(!strcmp(iface, OFONO_VOICECALLMANAGER_IFACE)) {
558                         //TODO: ??? check if the service is newly added ??? - not to request calls multiple times
559                         // service is up, request active calls now
560                         ctx->getCalls();
561                     }
562                 };
563             }
564         }
565     }
566     else if(!strcmp(interface_name, OFONO_VOICECALLMANAGER_IFACE)) {
567         if(!strcmp(signal_name, "CallAdded")) {
568             if(!parameters) {
569                 LoggerE("Invalid parameters");
570                 return;
571             }
572
573             const char *path = NULL;
574             GVariantIter *props = NULL;
575             g_variant_get(parameters, "(oa{sv})", &path, &props);
576             ctx->addCall(path, props);
577         }
578         else if(!strcmp(signal_name, "CallRemoved")) {
579             if(!parameters) {
580                 LoggerD("Invalid parameters");
581                 return;
582             }
583             // TODO: do the check for the call
584             // for now, remove just the active one
585             ctx->removeCall(&ctx->mActiveCall);
586         }
587         else {
588             LoggerD("un-handled signal \"" << signal_name << "\" on " << interface_name);
589         }
590     }
591     else if(!strcmp(interface_name, OFONO_VOICECALL_IFACE)) {
592         if(!strcmp(signal_name, "PropertyChanged")) {
593             const char *key = NULL;
594             GVariant *value = NULL;
595             g_variant_get(parameters, "(sv)", &key, &value);
596             if(!strcmp(key, "State")) {
597                 const char *state = NULL;
598                 g_variant_get(value, "s", &state);
599                 if(ctx->mActiveCall->state)
600                     g_free(ctx->mActiveCall->state);
601                 ctx->mActiveCall->state = strdup(state);
602                 LoggerD("PROPERTY CHANGED: " << key << " = " << state);
603             }
604             else {
605                 LoggerD("PROPERTY CHANGED: " << key);
606             }
607             // TODO: check for the remaining properties
608             ctx->callChanged(ctx->mActiveCall->state, ctx->mActiveCall->line_id);
609         }
610     }
611     else {
612         LoggerD("signal received for un-handled IFACE: " << interface_name);
613     }
614 }
615
616 void OFono::addCall(const char *path, GVariantIter *props) {
617     LoggerD("entered");
618
619     // only one call at a time is curently supported
620     if(mActiveCall) {
621         LoggerD("Unable to add a call when another is on-going");
622         return;
623     }
624
625     if(!path)
626         return;
627
628     mActiveCall = new OFono::Call();
629     mActiveCall->path = strdup(path);
630
631     Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
632                              OFONO_VOICECALL_IFACE, path,
633                              "PropertyChanged", OFono::handleSignal,
634                              this);
635
636     const char *key = NULL;
637     GVariant *value = NULL;
638     while(g_variant_iter_next(props, "{sv}", &key, &value)) {
639         if(!strcmp(key, "State")) {
640             const char *state = NULL;
641             g_variant_get(value, "s", &state);
642             mActiveCall->state = state ? strdup(state) : NULL;
643         }
644         else if(!strcmp(key, "LineIdentification")) {
645             const char *line_id = NULL;
646             g_variant_get(value, "s", &line_id);
647             mActiveCall->line_id = line_id ? strdup(line_id) : NULL;
648         }
649     }
650
651     // notify listener about call state changed
652     if(!!mActiveCall->state && !!mActiveCall->line_id) {
653         callChanged(mActiveCall->state, mActiveCall->line_id);
654     }
655 }
656
657 void OFono::removeCall(OFono::Call **call) {
658     LoggerD("entered");
659
660     // unsubscribe and remove signal listeners for the call
661     Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
662                                 OFONO_VOICECALL_IFACE, (*call)->path,
663                                 "PropertyChanged");
664
665     if(*call) {
666         delete *call;
667         *call = NULL;
668     }
669 }
670
671 bool OFono::invokeCall(const char* phoneNumber, char **error) {
672     if(!mModemPath) { // no selected modem to perform operation on
673         *error = strdup("No active modem set");
674         return false;
675     }
676
677     if(mActiveCall) { // active call
678         *error = strdup("Already active call");
679         return false;
680     }
681
682     GError *err = NULL;
683     GVariant *reply;
684     reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
685                                          OFONO_SERVICE,
686                                          mModemPath,
687                                          OFONO_VOICECALLMANAGER_IFACE,
688                                          "Dial",
689                                          g_variant_new ("(ss)", // floating parameters are consumed, no cleanup/unref needed
690                                              phoneNumber,
691                                              ""
692                                          ),
693                                          NULL,
694                                          G_DBUS_CALL_FLAGS_NONE,
695                                          -1,
696                                          NULL,
697                                          &err);
698
699     if(err) {
700         LoggerE("Failed to call 'Dial' DBUS method: " << err->message);
701         *error = strdup(err->message);
702         g_error_free(err);
703         return false;
704     }
705
706     if(!reply) {
707         LoggerE("Reply from calling 'Dial' is NULL");
708         *error = strdup("Failed to get 'call' object from OFONO's 'Dial' reply");
709         return false;
710     }
711
712     // TODO: process reply here - ??? is it really needed 'cause we are handling CallAdded signal anyway ???
713     g_variant_unref(reply);
714
715     return true;
716 }
717
718 bool OFono::answerCall(char **error) {
719     if(!mActiveCall) { // no active call to hangup
720         *error = strdup("No active call");
721         return false;
722     }
723
724     GError *err = NULL;
725     g_dbus_connection_call_sync (g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
726                     OFONO_SERVICE,
727                     mActiveCall->path,
728                     OFONO_VOICECALL_IFACE,
729                     "Answer",
730                     NULL,
731                     NULL,
732                     G_DBUS_CALL_FLAGS_NONE,
733                     -1,
734                     NULL,
735                     &err);
736
737     if(err) {
738         LoggerD("Failed to call 'Answer' DBUS method: " << err->message);
739         *error = strdup(err->message);
740         g_error_free(err);
741         return false;
742     }
743
744     return true;
745 }
746
747 bool OFono::hangupCall(char **error) {
748     if(!mActiveCall) { // no active call to hangup
749         *error = strdup("No active call");
750         return false;
751     }
752
753     GError *err = NULL;
754     g_dbus_connection_call_sync (g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
755                     OFONO_SERVICE,
756                     mActiveCall->path,
757                     OFONO_VOICECALL_IFACE,
758                     "Hangup",
759                     NULL,
760                     NULL,
761                     G_DBUS_CALL_FLAGS_NONE,
762                     -1,
763                     NULL,
764                     &err);
765
766     if(err) {
767         LoggerD("Failed to call 'Hangup' DBUS method: " << err->message);
768         *error = strdup(err->message);
769         g_error_free(err);
770         return false;
771     }
772
773     return true;
774 }
775
776 OFono::Call *OFono::activeCall() {
777     LoggerD("OFono::activeCall()");
778     // make a copy of object, since it may be destroyed meanwhile
779     return mActiveCall ? new OFono::Call(mActiveCall) : NULL;
780 }
781
782 bool OFono::muteCall(bool mute, char **error) {
783     if(!mActiveCall) { // no active call to hangup
784         *error = strdup("No active call");
785         return false;
786     }
787
788     bool success = setProperty(OFONO_CALLVOLUME_IFACE, mModemPath, "Muted", DBUS_TYPE_BOOLEAN, &mute);
789     if(!success) {
790         *error = strdup("Failed to set 'Muted' property on the call");
791         return false;
792     }
793
794     return true;
795 }
796
797 } // PhoneD
798