15 #define OFONO_PREFIX "org.ofono"
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"
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)
34 LoggerD("Failed to call 'GetModems'");
37 Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
38 OFONO_MANAGER_IFACE, "/",
39 "ModemAdded", OFono::handleSignal,
41 Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
42 OFONO_MANAGER_IFACE, "/",
43 "ModemRemoved", OFono::handleSignal,
46 // won't request calls here, since the service OFONO_VOICECALLMANAGER_IFACE may not be available yet
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);
59 removeModem(mModemPath);
62 gboolean OFono::checkForModemPowered(gpointer user_data) {
63 OFono *ctx = static_cast<OFono*>(user_data);
64 if(!ctx || !ctx->mModemPath)
65 return G_SOURCE_CONTINUE; // continuous timeout
67 if(!ctx->isModemPowered(ctx->mModemPath)) {
68 ctx->setModemPowered(ctx->mModemPath, true);
71 return G_SOURCE_CONTINUE; // continuous timeout
74 bool OFono::isModemPowered(const char *modem) {
79 GVariant *reply = NULL;
80 reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
87 G_DBUS_CALL_FLAGS_NONE,
99 GVariant *prop = NULL;
100 GVariantIter *iter = NULL;
101 g_variant_get(reply, "(a{sv})", &iter);
102 bool powered = false;
103 while((prop = g_variant_iter_next_value(iter))) {
104 const char *name = NULL;
105 GVariant *value = NULL;
106 g_variant_get(prop, "{sv}", &name, &value);
107 if(name && !strcmp(name, "Powered")) {
108 powered = g_variant_get_boolean(value);
112 g_variant_unref(reply);
117 // synchronous version of method for setting property
118 bool OFono::setProperty(const char* iface, const char* path, const char *property, int type, void *value) {
119 LoggerD("OFono::setProperty(): name= " << property << " on " << path);
122 sprintf(signature, "%c", type); // eg. "b" for boolean
125 g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
130 g_variant_new ("(sv)", // floating parameters are consumed, no cleanup/unref needed
132 g_variant_new(signature, value) // floating parameters are consumed, no cleanup/unref needed
135 G_DBUS_CALL_FLAGS_NONE,
141 LoggerE("Failed to call \"SetProperty\" DBUS method: " << err->message);
149 // asynchronous version of method for setting property
150 void OFono::setPropertyAsync(const char* iface, const char* path, const char *property, int type, void *value, GAsyncReadyCallback callback) {
151 LoggerD("Setting OFono property via asynchronous call: name= " << property << " on " << path);
154 sprintf(signature, "%c", type); // eg. "b" for boolean
156 g_dbus_connection_call( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
161 g_variant_new ("(sv)", // floating parameters are consumed, no cleanup/unref needed
163 g_variant_new(signature, value) // floating parameters are consumed, no cleanup/unref needed
166 G_DBUS_CALL_FLAGS_NONE,
173 // callback for async calls on 'g_dbus'
174 void OFono::GDbusAsyncReadyCallback(GObject *source, GAsyncResult *result, gpointer user_data) {
175 //process result here if needed
176 //LoggerD("Async method call finished, ie. callback received");
179 void OFono::setModemPowered(const char *modem, bool powered) {
180 LoggerD("Setting modem 'Powered': " << modem << " to " << powered);
181 setPropertyAsync(OFONO_MODEM_IFACE, modem, "Powered", DBUS_TYPE_BOOLEAN, &powered, OFono::asyncSetModemPoweredCallback);
184 void OFono::asyncSetModemPoweredCallback(GObject *source, GAsyncResult *result, gpointer user_data) {
186 g_dbus_connection_call_finish(g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL), result, &err);
188 LoggerE("Failed to set 'Powered' property on modem: " << err->message);
189 OFono *ctx = static_cast<OFono*>(user_data);
191 LoggerE("Invalid ctx");
194 // notify about the failure
195 if(!ctx->isModemPowered(ctx->mModemPath)) // Modem is not 'Powered', ie. the remote BT device is not 'Selected' for BT operations
196 ctx->setModemPoweredFailed(err?err->message:"Failed to set 'Powered' property on Modem.");
200 LoggerE("Property 'Powered' successfuly set on modem");
203 void OFono::asyncSelectModemCallback(GObject *source, GAsyncResult *result, gpointer user_data) {
204 CtxCbData *data = static_cast<CtxCbData*>(user_data);
205 if(!data || !data->ctx || !data->data1) {
206 LoggerE("Invalid callback data");
209 OFono *ctx = static_cast<OFono*>(data->ctx);
210 char *addr = static_cast<char*>(data->data1);
212 std::string btAddress = addr;
219 reply = g_dbus_connection_call_finish(g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL), result, &err);
222 LoggerE("Failed to 'GetModems': " << err->message);
226 LoggerE("Reply from calling 'GetModems' method is NULL");
231 // path of retrieved modems is in form: /hfp/AABBCCDDEEFF_FFEEDDCCBBAA
232 // where: AABBCCDDEEFF is MAC address of default local BT device
233 // and: FFEEDDCCBBAA is MAC address of selected remote BT device
234 // we expect given MAC address to be in the form: AA:BB:CC:DD:EE:FF
235 // remove all non-digit characters, eg. ":","-"
236 btAddress.erase(std::remove_if(btAddress.begin(), btAddress.end(), isnxdigit), btAddress.end());
238 GVariantIter *modems;
241 g_variant_get(reply, "(a(oa{sv}))", &modems);
242 while(g_variant_iter_next(modems, "(oa{sv})", &path, &props)) {
243 // check if the modem is from selected remote device
244 std::string pathString(path);
245 size_t idx = pathString.find( "_" ) + 1; // index of address of remote device
246 std::string modemRemoteBtAddress = pathString.substr (idx, pathString.length()-idx);
248 // remove additional underscores
249 modemRemoteBtAddress.erase(std::remove_if(modemRemoteBtAddress.begin(),modemRemoteBtAddress.end(),isnxdigit),modemRemoteBtAddress.end());
250 LoggerD("modem paths to be compared: %s, %s", modemRemoteBtAddress.c_str(),btAddress.c_str());
252 if(!modemRemoteBtAddress.compare(btAddress))
254 ctx->addModem(path, props);
255 // currently only one modem is supported - that's why the break is here
256 // take the first one from the list and make it 'default'
261 g_variant_unref(reply);
264 void OFono::selectModem(std::string &btAddress) {
265 LoggerD("Selecting modem: " << btAddress);
266 // Retrieving list of available modems to get the one that is for 'wanted' device
267 g_dbus_connection_call( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
274 G_DBUS_CALL_FLAGS_NONE,
277 OFono::asyncSelectModemCallback,
278 new CtxCbData(this, NULL, strdup(btAddress.c_str()), NULL));
281 void OFono::addModem(const char *path, GVariantIter *props) {
287 // shouldn't happen, but just in case free the memory here
291 mModemPath = strdup(path); // make a copy of path, 'cause it will be unref-ed
293 const char *key = NULL;
294 GVariant *value = NULL;
296 while(g_variant_iter_next(props, "{sv}", &key, &value)) {
297 if(!strcmp(key, "Powered")) {
298 //bool powered = g_variant_get_boolean(value);
300 else if(!strcmp(key, "Online")) {
301 online = g_variant_get_boolean(value);
305 Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
306 OFONO_VOICECALLMANAGER_IFACE, mModemPath,
307 "CallAdded", OFono::handleSignal,
310 Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
311 OFONO_VOICECALLMANAGER_IFACE, mModemPath,
312 "PropertyChanged", OFono::handleSignal,
315 Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
316 OFONO_VOICECALLMANAGER_IFACE, mModemPath,
317 "CallRemoved", OFono::handleSignal,
320 Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
321 OFONO_MODEM_IFACE, mModemPath,
322 "PropertyChanged", OFono::handleSignal,
327 setModemPowered(mModemPath, true);
331 // we can query calls from here, since the modem is already powered
333 // when the modem is already online, "PropertyChanged" for "Paired" is not emited
334 // to handle all functionality to modem being powered, call modemPowered from here as well
339 void OFono::unselectModem() {
340 removeModem(mModemPath);
343 void OFono::removeModem(const char *modemPath) {
344 LoggerD("Removing modem: " << (modemPath?modemPath:"INVALID DATA"));
346 // remove all subscriptions to DBUS signals from the modem
348 Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
349 OFONO_VOICECALLMANAGER_IFACE, modemPath,
351 Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
352 OFONO_VOICECALLMANAGER_IFACE, modemPath,
354 Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
355 OFONO_VOICECALLMANAGER_IFACE, modemPath,
357 Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
358 OFONO_MODEM_IFACE, modemPath,
362 setModemPowered(modemPath, false);
363 // at this point we will not get notification "PropertyChanged" on 'Powered' property,
364 // call 'modemPowered()' method directly from here
368 if(modemPath && mModemPath && !strcmp(modemPath, mModemPath)) {
375 void OFono::getCalls()
380 GVariant *reply = NULL;
381 reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
384 OFONO_VOICECALLMANAGER_IFACE,
388 G_DBUS_CALL_FLAGS_NONE,
396 LoggerE("error calling GetCalls method");
400 LoggerE("reply is NULL");
408 g_variant_get(reply, "(a(oa{sv}))", &calls);
409 while(g_variant_iter_next(calls, "(oa{sv})", &path, &props)) {
410 addCall(path, props);
413 g_variant_unref(reply);
418 void OFono::handleSignal(GDBusConnection *connection,
420 const gchar *object_path,
421 const gchar *interface_name,
422 const gchar *signal_name,
423 GVariant *parameters,
426 LoggerD("signal received: \"" << interface_name << "\" -> \"" << signal_name << "\"");
428 OFono* ctx = static_cast<OFono*>(user_data);
430 LoggerE("Failed to cast object");
434 if(!interface_name || !signal_name) {
435 LoggerE("Invalid callback data");
438 if(!strcmp(interface_name, OFONO_MANAGER_IFACE)) {
439 if(!strcmp(signal_name, "ModemAdded")) {
440 const char *modem = NULL;
441 GVariantIter *props = NULL;
442 g_variant_get(parameters, "(oa{sv})", &modem, &props);
444 std::string modemString(modem);
445 size_t idx = modemString.find( "_" ) + 1; // index of address of remote device
446 std::string modemRemoteBtAddress = modemString.substr (idx, modemString.length()-idx);
447 if(makeMACFromRawMAC(modemRemoteBtAddress)) {
448 ctx->modemAdded(modemRemoteBtAddress);
453 else if(!strcmp(interface_name, OFONO_MANAGER_IFACE)) {
454 if(!strcmp(signal_name, "ModemRemoved")) {
455 const char *modem = NULL;
456 g_variant_get(parameters, "(o)", &modem);
458 LoggerD("Modem removed: " << modem);
459 if(ctx->mModemPath && !strcmp(modem, ctx->mModemPath)) {
460 ctx->removeModem(ctx->mModemPath);
465 else if(!strcmp(interface_name, OFONO_MODEM_IFACE)) {
466 if(!strcmp(signal_name, "PropertyChanged")) {
467 const char *name = NULL;
468 GVariant *value = NULL;
469 g_variant_get(parameters, "(sv)", &name, &value);
470 if(!strcmp(name, "Powered")) {
471 bool powered = g_variant_get_boolean(value);
472 LoggerD("\t" << name << " = " << (powered?"TRUE":"FALSE"));
473 ctx->modemPowered(powered);
474 // !!! won't request calls here, since the service may not be available yet
475 // see "Interfaces" "PropertyChanged" on OFONO_MODEM_IFACE
479 else if(!strcmp(name, "Online")) {
480 bool online = g_variant_get_boolean(value);
481 LoggerD("\t" << name << " = " << (online?"TRUE":"FALSE"));
483 else if(!strcmp(name, "Interfaces")) {
486 GVariant *variant = g_variant_get_child_value(parameters,1); // signature: "v"
487 GVariant *interfaces = g_variant_get_child_value(variant,0); // signature: "as"
488 g_variant_iter_init(&iter, interfaces);
489 while((interface = g_variant_iter_next_value(&iter))) {
490 const char *iface = g_variant_get_string(interface, NULL);
491 if(!strcmp(iface, OFONO_VOICECALLMANAGER_IFACE)) {
492 //TODO: ??? check if the service is newly added ??? - not to request calls multiple times
493 // service is up, request active calls now
500 else if(!strcmp(interface_name, OFONO_VOICECALLMANAGER_IFACE)) {
501 if(!strcmp(signal_name, "CallAdded")) {
503 LoggerE("Invalid parameters");
507 const char *path = NULL;
508 GVariantIter *props = NULL;
509 g_variant_get(parameters, "(oa{sv})", &path, &props);
510 ctx->addCall(path, props);
512 else if(!strcmp(signal_name, "CallRemoved")) {
514 LoggerD("Invalid parameters");
517 // TODO: do the check for the call
518 // for now, remove just the active one
519 ctx->removeCall(&ctx->mActiveCall);
522 LoggerD("un-handled signal \"" << signal_name << "\" on " << interface_name);
525 else if(!strcmp(interface_name, OFONO_VOICECALL_IFACE)) {
526 if(!strcmp(signal_name, "PropertyChanged")) {
527 const char *key = NULL;
528 GVariant *value = NULL;
529 g_variant_get(parameters, "(sv)", &key, &value);
530 if(!strcmp(key, "State")) {
531 const char *state = NULL;
532 g_variant_get(value, "s", &state);
533 if(ctx->mActiveCall->state)
534 g_free(ctx->mActiveCall->state);
535 ctx->mActiveCall->state = strdup(state);
536 LoggerD("PROPERTY CHANGED: " << key << " = " << state);
539 LoggerD("PROPERTY CHANGED: " << key);
541 // TODO: check for the remaining properties
542 ctx->callChanged(ctx->mActiveCall->state, ctx->mActiveCall->line_id);
546 LoggerD("signal received for un-handled IFACE: " << interface_name);
550 void OFono::addCall(const char *path, GVariantIter *props) {
553 // only one call at a time is curently supported
555 LoggerD("Unable to add a call when another is on-going");
562 mActiveCall = new OFono::Call();
563 mActiveCall->path = strdup(path);
565 Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
566 OFONO_VOICECALL_IFACE, path,
567 "PropertyChanged", OFono::handleSignal,
570 const char *key = NULL;
571 GVariant *value = NULL;
572 while(g_variant_iter_next(props, "{sv}", &key, &value)) {
573 if(!strcmp(key, "State")) {
574 const char *state = NULL;
575 g_variant_get(value, "s", &state);
576 mActiveCall->state = state ? strdup(state) : NULL;
578 else if(!strcmp(key, "LineIdentification")) {
579 const char *line_id = NULL;
580 g_variant_get(value, "s", &line_id);
581 mActiveCall->line_id = line_id ? strdup(line_id) : NULL;
585 // notify listener about call state changed
586 if(!!mActiveCall->state && !!mActiveCall->line_id) {
587 callChanged(mActiveCall->state, mActiveCall->line_id);
591 void OFono::removeCall(OFono::Call **call) {
594 // unsubscribe and remove signal listeners for the call
595 Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
596 OFONO_VOICECALL_IFACE, (*call)->path,
605 bool OFono::invokeCall(const char* phoneNumber, char **error) {
606 if(!mModemPath) { // no selected modem to perform operation on
607 *error = strdup("No active modem set");
611 if(mActiveCall) { // active call
612 *error = strdup("Already active call");
618 reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
621 OFONO_VOICECALLMANAGER_IFACE,
623 g_variant_new ("(ss)", // floating parameters are consumed, no cleanup/unref needed
628 G_DBUS_CALL_FLAGS_NONE,
634 LoggerE("Failed to call 'Dial' DBUS method: " << err->message);
635 *error = strdup(err->message);
641 LoggerE("Reply from calling 'Dial' is NULL");
642 *error = strdup("Failed to get 'call' object from OFONO's 'Dial' reply");
646 // TODO: process reply here - ??? is it really needed 'cause we are handling CallAdded signal anyway ???
647 g_variant_unref(reply);
652 bool OFono::answerCall(char **error) {
653 if(!mActiveCall) { // no active call to hangup
654 *error = strdup("No active call");
659 g_dbus_connection_call_sync (g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
662 OFONO_VOICECALL_IFACE,
666 G_DBUS_CALL_FLAGS_NONE,
672 LoggerD("Failed to call 'Answer' DBUS method: " << err->message);
673 *error = strdup(err->message);
681 bool OFono::hangupCall(char **error) {
682 if(!mActiveCall) { // no active call to hangup
683 *error = strdup("No active call");
688 g_dbus_connection_call_sync (g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
691 OFONO_VOICECALL_IFACE,
695 G_DBUS_CALL_FLAGS_NONE,
701 LoggerD("Failed to call 'Hangup' DBUS method: " << err->message);
702 *error = strdup(err->message);
710 OFono::Call *OFono::activeCall() {
711 LoggerD("OFono::activeCall()");
712 // make a copy of object, since it may be destroyed meanwhile
713 return mActiveCall ? new OFono::Call(mActiveCall) : NULL;
716 bool OFono::muteCall(bool mute, char **error) {
717 if(!mActiveCall) { // no active call to hangup
718 *error = strdup("No active call");
722 bool success = setProperty(OFONO_CALLVOLUME_IFACE, mModemPath, "Muted", DBUS_TYPE_BOOLEAN, &mute);
724 *error = strdup("Failed to set 'Muted' property on the call");