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);
65 return G_SOURCE_CONTINUE; // continuous timeout
67 if(!ctx->mModemPath) {
68 LoggerD("No modem available, get all available modems");
72 if(!ctx->isModemPowered(ctx->mModemPath)) {
73 LoggerD("modem is not powered");
74 ctx->setModemPowered(ctx->mModemPath, true);
77 return G_SOURCE_CONTINUE; // continuous timeout
80 bool OFono::isModemPowered(const char *modem) {
85 GVariant *reply = NULL;
86 reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
93 G_DBUS_CALL_FLAGS_NONE,
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);
118 g_variant_unref(reply);
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);
128 sprintf(signature, "%c", type); // eg. "b" for boolean
131 g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
136 g_variant_new ("(sv)", // floating parameters are consumed, no cleanup/unref needed
138 g_variant_new(signature, value) // floating parameters are consumed, no cleanup/unref needed
141 G_DBUS_CALL_FLAGS_NONE,
147 LoggerE("Failed to call \"SetProperty\" DBUS method: " << err->message);
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);
160 sprintf(signature, "%c", type); // eg. "b" for boolean
162 g_dbus_connection_call( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
167 g_variant_new ("(sv)", // floating parameters are consumed, no cleanup/unref needed
169 g_variant_new(signature, value) // floating parameters are consumed, no cleanup/unref needed
172 G_DBUS_CALL_FLAGS_NONE,
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");
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);
190 void OFono::asyncSetModemPoweredCallback(GObject *source, GAsyncResult *result, gpointer user_data) {
192 g_dbus_connection_call_finish(g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL), result, &err);
194 LoggerE("Failed to set 'Powered' property on modem: " << err->message);
195 OFono *ctx = static_cast<OFono*>(user_data);
197 LoggerE("Invalid ctx");
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.");
206 LoggerE("Property 'Powered' successfuly set on modem");
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");
215 OFono *ctx = static_cast<OFono*>(data->ctx);
216 char *addr = static_cast<char*>(data->data1);
218 std::string btAddress = addr;
225 reply = g_dbus_connection_call_finish(g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL), result, &err);
228 LoggerE("Failed to 'GetModems': " << err->message);
232 LoggerE("Reply from calling 'GetModems' method is NULL");
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());
244 GVariantIter *modems;
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);
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());
258 if(!modemRemoteBtAddress.compare(btAddress))
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'
267 g_variant_unref(reply);
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),
280 G_DBUS_CALL_FLAGS_NONE,
283 OFono::asyncSelectModemCallback,
284 new CtxCbData(this, NULL, strdup(btAddress.c_str()), NULL));
287 void OFono::addModem(const char *path, GVariantIter *props) {
293 // shouldn't happen, but just in case free the memory here
297 mModemPath = strdup(path); // make a copy of path, 'cause it will be unref-ed
299 const char *key = NULL;
300 GVariant *value = NULL;
302 while(g_variant_iter_next(props, "{sv}", &key, &value)) {
303 if(!strcmp(key, "Powered")) {
304 //bool powered = g_variant_get_boolean(value);
306 else if(!strcmp(key, "Online")) {
307 online = g_variant_get_boolean(value);
311 Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
312 OFONO_VOICECALLMANAGER_IFACE, mModemPath,
313 "CallAdded", OFono::handleSignal,
316 Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
317 OFONO_VOICECALLMANAGER_IFACE, mModemPath,
318 "PropertyChanged", OFono::handleSignal,
321 Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
322 OFONO_VOICECALLMANAGER_IFACE, mModemPath,
323 "CallRemoved", OFono::handleSignal,
326 Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
327 OFONO_MODEM_IFACE, mModemPath,
328 "PropertyChanged", OFono::handleSignal,
333 setModemPowered(mModemPath, true);
337 // we can query calls from here, since the modem is already powered
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
345 void OFono::unselectModem() {
346 removeModem(mModemPath);
349 void OFono::removeModem(const char *modemPath) {
350 LoggerD("Removing modem: " << (modemPath?modemPath:"INVALID DATA"));
352 // remove all subscriptions to DBUS signals from the modem
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_VOICECALLMANAGER_IFACE, modemPath,
360 Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
361 OFONO_VOICECALLMANAGER_IFACE, modemPath,
363 Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
364 OFONO_MODEM_IFACE, modemPath,
368 setModemPowered(modemPath, false);
369 // at this point we will not get notification "PropertyChanged" on 'Powered' property,
370 // call 'modemPowered()' method directly from here
374 if(modemPath && mModemPath && !strcmp(modemPath, mModemPath)) {
381 void OFono::getModems()
386 GVariant *reply = NULL;
387 reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
394 G_DBUS_CALL_FLAGS_NONE,
401 LoggerE("error calling GetModems method");
405 LoggerE("reply is NULL");
410 GVariantIter *modems;
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);
420 // remove additional underscores
421 modemRemoteBtAddress.erase(std::remove_if(modemRemoteBtAddress.begin(),modemRemoteBtAddress.end(),isnxdigit),modemRemoteBtAddress.end());
423 // check if it is a valid bluetooth MAC address
424 if(makeMACFromRawMAC(modemRemoteBtAddress))
426 LoggerD("Modem found: " << modemRemoteBtAddress);
427 modemAdded(modemRemoteBtAddress);
430 g_variant_iter_free(props);
433 g_variant_iter_free(modems);
434 g_variant_unref(reply);
439 void OFono::getCalls()
444 GVariant *reply = NULL;
445 reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
448 OFONO_VOICECALLMANAGER_IFACE,
452 G_DBUS_CALL_FLAGS_NONE,
460 LoggerE("error calling GetCalls method");
464 LoggerE("reply is NULL");
472 g_variant_get(reply, "(a(oa{sv}))", &calls);
473 while(g_variant_iter_next(calls, "(oa{sv})", &path, &props)) {
474 addCall(path, props);
477 g_variant_unref(reply);
482 void OFono::handleSignal(GDBusConnection *connection,
484 const gchar *object_path,
485 const gchar *interface_name,
486 const gchar *signal_name,
487 GVariant *parameters,
490 LoggerD("signal received: \"" << interface_name << "\" -> \"" << signal_name << "\"");
492 OFono* ctx = static_cast<OFono*>(user_data);
494 LoggerE("Failed to cast object");
498 if(!interface_name || !signal_name) {
499 LoggerE("Invalid callback data");
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);
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);
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);
524 LoggerD("Modem removed: " << modem);
525 if(ctx->mModemPath && !strcmp(modem, ctx->mModemPath)) {
526 ctx->removeModem(ctx->mModemPath);
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
545 else if(!strcmp(name, "Online")) {
546 bool online = g_variant_get_boolean(value);
547 LoggerD("\t" << name << " = " << (online?"TRUE":"FALSE"));
549 else if(!strcmp(name, "Interfaces")) {
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
566 else if(!strcmp(interface_name, OFONO_VOICECALLMANAGER_IFACE)) {
567 if(!strcmp(signal_name, "CallAdded")) {
569 LoggerE("Invalid parameters");
573 const char *path = NULL;
574 GVariantIter *props = NULL;
575 g_variant_get(parameters, "(oa{sv})", &path, &props);
576 ctx->addCall(path, props);
578 else if(!strcmp(signal_name, "CallRemoved")) {
580 LoggerD("Invalid parameters");
583 // TODO: do the check for the call
584 // for now, remove just the active one
585 ctx->removeCall(&ctx->mActiveCall);
588 LoggerD("un-handled signal \"" << signal_name << "\" on " << interface_name);
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);
605 LoggerD("PROPERTY CHANGED: " << key);
607 // TODO: check for the remaining properties
608 ctx->callChanged(ctx->mActiveCall->state, ctx->mActiveCall->line_id);
612 LoggerD("signal received for un-handled IFACE: " << interface_name);
616 void OFono::addCall(const char *path, GVariantIter *props) {
619 // only one call at a time is curently supported
621 LoggerD("Unable to add a call when another is on-going");
628 mActiveCall = new OFono::Call();
629 mActiveCall->path = strdup(path);
631 Utils::setSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
632 OFONO_VOICECALL_IFACE, path,
633 "PropertyChanged", OFono::handleSignal,
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;
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;
651 // notify listener about call state changed
652 if(!!mActiveCall->state && !!mActiveCall->line_id) {
653 callChanged(mActiveCall->state, mActiveCall->line_id);
657 void OFono::removeCall(OFono::Call **call) {
660 // unsubscribe and remove signal listeners for the call
661 Utils::removeSignalListener(G_BUS_TYPE_SYSTEM, OFONO_SERVICE,
662 OFONO_VOICECALL_IFACE, (*call)->path,
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");
677 if(mActiveCall) { // active call
678 *error = strdup("Already active call");
684 reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
687 OFONO_VOICECALLMANAGER_IFACE,
689 g_variant_new ("(ss)", // floating parameters are consumed, no cleanup/unref needed
694 G_DBUS_CALL_FLAGS_NONE,
700 LoggerE("Failed to call 'Dial' DBUS method: " << err->message);
701 *error = strdup(err->message);
707 LoggerE("Reply from calling 'Dial' is NULL");
708 *error = strdup("Failed to get 'call' object from OFONO's 'Dial' reply");
712 // TODO: process reply here - ??? is it really needed 'cause we are handling CallAdded signal anyway ???
713 g_variant_unref(reply);
718 bool OFono::answerCall(char **error) {
719 if(!mActiveCall) { // no active call to hangup
720 *error = strdup("No active call");
725 g_dbus_connection_call_sync (g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
728 OFONO_VOICECALL_IFACE,
732 G_DBUS_CALL_FLAGS_NONE,
738 LoggerD("Failed to call 'Answer' DBUS method: " << err->message);
739 *error = strdup(err->message);
747 bool OFono::hangupCall(char **error) {
748 if(!mActiveCall) { // no active call to hangup
749 *error = strdup("No active call");
754 g_dbus_connection_call_sync (g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
757 OFONO_VOICECALL_IFACE,
761 G_DBUS_CALL_FLAGS_NONE,
767 LoggerD("Failed to call 'Hangup' DBUS method: " << err->message);
768 *error = strdup(err->message);
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;
782 bool OFono::muteCall(bool mute, char **error) {
783 if(!mActiveCall) { // no active call to hangup
784 *error = strdup("No active call");
788 bool success = setProperty(OFONO_CALLVOLUME_IFACE, mModemPath, "Muted", DBUS_TYPE_BOOLEAN, &mute);
790 *error = strdup("Failed to set 'Muted' property on the call");