Fixed bug where ModemRemoved signal never handled
[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         else if(!strcmp(signal_name, "ModemRemoved")) {
519             const char *modem = NULL;
520             g_variant_get(parameters, "(o)", &modem);
521             if(modem) {
522                 LoggerD("Modem removed: " << modem);
523                 if(ctx->mModemPath && !strcmp(modem, ctx->mModemPath)) {
524                     ctx->removeModem(ctx->mModemPath);
525                 }
526             }
527         }
528     }
529     else if(!strcmp(interface_name, OFONO_MODEM_IFACE)) {
530         if(!strcmp(signal_name, "PropertyChanged")) {
531             const char *name = NULL;
532             GVariant *value = NULL;
533             g_variant_get(parameters, "(sv)", &name, &value);
534             if(!strcmp(name, "Powered")) {
535                 bool powered = g_variant_get_boolean(value);
536                 LoggerD("\t" << name << " = " << (powered?"TRUE":"FALSE"));
537                 ctx->modemPowered(powered);
538                 // !!! won't request calls here, since the service may not be available yet
539                 // see "Interfaces" "PropertyChanged" on OFONO_MODEM_IFACE
540                 //if(powered)
541                 //    ctx->getCalls();
542             }
543             else if(!strcmp(name, "Online")) {
544                 bool online = g_variant_get_boolean(value);
545                 LoggerD("\t" << name << " = " << (online?"TRUE":"FALSE"));
546             }
547             else if(!strcmp(name, "Interfaces")) {
548                 GVariantIter iter;
549                 GVariant *interface;
550                 GVariant *variant = g_variant_get_child_value(parameters,1); // signature: "v"
551                 GVariant *interfaces = g_variant_get_child_value(variant,0); // signature: "as"
552                 g_variant_iter_init(&iter, interfaces);
553                 while((interface = g_variant_iter_next_value(&iter))) {
554                     const char *iface = g_variant_get_string(interface, NULL);
555                     if(!strcmp(iface, OFONO_VOICECALLMANAGER_IFACE)) {
556                         //TODO: ??? check if the service is newly added ??? - not to request calls multiple times
557                         // service is up, request active calls now
558                         ctx->getCalls();
559                     }
560                 };
561             }
562         }
563     }
564     else if(!strcmp(interface_name, OFONO_VOICECALLMANAGER_IFACE)) {
565         if(!strcmp(signal_name, "CallAdded")) {
566             if(!parameters) {
567                 LoggerE("Invalid parameters");
568                 return;
569             }
570
571             const char *path = NULL;
572             GVariantIter *props = NULL;
573             g_variant_get(parameters, "(oa{sv})", &path, &props);
574             ctx->addCall(path, props);
575         }
576         else if(!strcmp(signal_name, "CallRemoved")) {
577             if(!parameters) {
578                 LoggerD("Invalid parameters");
579                 return;
580             }
581             // TODO: do the check for the call
582             // for now, remove just the active one
583             ctx->removeCall(&ctx->mActiveCall);
584         }
585         else {
586             LoggerD("un-handled signal \"" << signal_name << "\" on " << interface_name);
587         }
588     }
589     else if(!strcmp(interface_name, OFONO_VOICECALL_IFACE)) {
590         if(!strcmp(signal_name, "PropertyChanged")) {
591             const char *key = NULL;
592             GVariant *value = NULL;
593             g_variant_get(parameters, "(sv)", &key, &value);
594             if(!strcmp(key, "State")) {
595                 const char *state = NULL;
596                 g_variant_get(value, "s", &state);
597                 if(ctx->mActiveCall->state)
598                     g_free(ctx->mActiveCall->state);
599                 ctx->mActiveCall->state = strdup(state);
600                 LoggerD("PROPERTY CHANGED: " << key << " = " << state);
601             }
602             else {
603                 LoggerD("PROPERTY CHANGED: " << key);
604             }
605             // TODO: check for the remaining properties
606             ctx->callChanged(ctx->mActiveCall->state, ctx->mActiveCall->line_id);
607         }
608     }
609     else {
610         LoggerD("signal received for un-handled IFACE: " << interface_name);
611     }
612 }
613
614 void OFono::addCall(const char *path, GVariantIter *props) {
615     LoggerD("entered");
616
617     // only one call at a time is curently supported
618     if(mActiveCall) {
619         LoggerD("Unable to add a call when another is on-going");
620         return;
621     }
622
623     if(!path)
624         return;
625
626     mActiveCall = new OFono::Call();
627     mActiveCall->path = strdup(path);
628
629     Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
630                              OFONO_VOICECALL_IFACE, path,
631                              "PropertyChanged", OFono::handleSignal,
632                              this);
633
634     const char *key = NULL;
635     GVariant *value = NULL;
636     while(g_variant_iter_next(props, "{sv}", &key, &value)) {
637         if(!strcmp(key, "State")) {
638             const char *state = NULL;
639             g_variant_get(value, "s", &state);
640             mActiveCall->state = state ? strdup(state) : NULL;
641         }
642         else if(!strcmp(key, "LineIdentification")) {
643             const char *line_id = NULL;
644             g_variant_get(value, "s", &line_id);
645             mActiveCall->line_id = line_id ? strdup(line_id) : NULL;
646         }
647     }
648
649     // notify listener about call state changed
650     if(!!mActiveCall->state && !!mActiveCall->line_id) {
651         callChanged(mActiveCall->state, mActiveCall->line_id);
652     }
653 }
654
655 void OFono::removeCall(OFono::Call **call) {
656     LoggerD("entered");
657
658     // unsubscribe and remove signal listeners for the call
659     Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
660                                 OFONO_VOICECALL_IFACE, (*call)->path,
661                                 "PropertyChanged");
662
663     if(*call) {
664         delete *call;
665         *call = NULL;
666     }
667 }
668
669 bool OFono::invokeCall(const char* phoneNumber, char **error) {
670     if(!mModemPath) { // no selected modem to perform operation on
671         *error = strdup("No active modem set");
672         return false;
673     }
674
675     if(mActiveCall) { // active call
676         *error = strdup("Already active call");
677         return false;
678     }
679
680     GError *err = NULL;
681     GVariant *reply;
682     reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
683                                          OFONO_SERVICE,
684                                          mModemPath,
685                                          OFONO_VOICECALLMANAGER_IFACE,
686                                          "Dial",
687                                          g_variant_new ("(ss)", // floating parameters are consumed, no cleanup/unref needed
688                                              phoneNumber,
689                                              ""
690                                          ),
691                                          NULL,
692                                          G_DBUS_CALL_FLAGS_NONE,
693                                          -1,
694                                          NULL,
695                                          &err);
696
697     if(err) {
698         LoggerE("Failed to call 'Dial' DBUS method: " << err->message);
699         *error = strdup(err->message);
700         g_error_free(err);
701         return false;
702     }
703
704     if(!reply) {
705         LoggerE("Reply from calling 'Dial' is NULL");
706         *error = strdup("Failed to get 'call' object from OFONO's 'Dial' reply");
707         return false;
708     }
709
710     // TODO: process reply here - ??? is it really needed 'cause we are handling CallAdded signal anyway ???
711     g_variant_unref(reply);
712
713     return true;
714 }
715
716 bool OFono::answerCall(char **error) {
717     if(!mActiveCall) { // no active call to hangup
718         *error = strdup("No active call");
719         return false;
720     }
721
722     GError *err = NULL;
723     g_dbus_connection_call_sync (g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
724                     OFONO_SERVICE,
725                     mActiveCall->path,
726                     OFONO_VOICECALL_IFACE,
727                     "Answer",
728                     NULL,
729                     NULL,
730                     G_DBUS_CALL_FLAGS_NONE,
731                     -1,
732                     NULL,
733                     &err);
734
735     if(err) {
736         LoggerD("Failed to call 'Answer' DBUS method: " << err->message);
737         *error = strdup(err->message);
738         g_error_free(err);
739         return false;
740     }
741
742     return true;
743 }
744
745 bool OFono::hangupCall(char **error) {
746     if(!mActiveCall) { // no active call to hangup
747         *error = strdup("No active call");
748         return false;
749     }
750
751     GError *err = NULL;
752     g_dbus_connection_call_sync (g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
753                     OFONO_SERVICE,
754                     mActiveCall->path,
755                     OFONO_VOICECALL_IFACE,
756                     "Hangup",
757                     NULL,
758                     NULL,
759                     G_DBUS_CALL_FLAGS_NONE,
760                     -1,
761                     NULL,
762                     &err);
763
764     if(err) {
765         LoggerD("Failed to call 'Hangup' DBUS method: " << err->message);
766         *error = strdup(err->message);
767         g_error_free(err);
768         return false;
769     }
770
771     return true;
772 }
773
774 OFono::Call *OFono::activeCall() {
775     LoggerD("OFono::activeCall()");
776     // make a copy of object, since it may be destroyed meanwhile
777     return mActiveCall ? new OFono::Call(mActiveCall) : NULL;
778 }
779
780 bool OFono::muteCall(bool mute, char **error) {
781     if(!mActiveCall) { // no active call to hangup
782         *error = strdup("No active call");
783         return false;
784     }
785
786     bool success = setProperty(OFONO_CALLVOLUME_IFACE, mModemPath, "Muted", DBUS_TYPE_BOOLEAN, &mute);
787     if(!success) {
788         *error = strdup("Failed to set 'Muted' property on the call");
789         return false;
790     }
791
792     return true;
793 }
794
795 } // PhoneD
796