3 * oFono - Open Source Telephony
5 * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
40 #define MAX_USSD_LENGTH 160
42 static GSList *g_drivers = NULL;
46 USSD_STATE_ACTIVE = 1,
47 USSD_STATE_USER_ACTION = 2,
48 USSD_STATE_RESPONSE_SENT,
52 ofono_ussd_request_cb_t cb;
61 GSList *ss_control_list;
62 GSList *ss_passwd_list;
63 const struct ofono_ussd_driver *driver;
65 struct ofono_atom *atom;
66 struct ussd_request *req;
73 ofono_destroy_func destroy;
76 gboolean __ofono_ussd_is_busy(struct ofono_ussd *ussd)
81 if (ussd->pending || ussd->state != USSD_STATE_IDLE || ussd->req)
87 static struct ssc_entry *ssc_entry_create(const char *sc, void *cb, void *data,
88 ofono_destroy_func destroy)
92 r = g_try_new0(struct ssc_entry, 1);
97 r->service = g_strdup(sc);
100 r->destroy = destroy;
105 static void ssc_entry_destroy(struct ssc_entry *ca)
108 ca->destroy(ca->user);
114 static gint ssc_entry_find_by_service(gconstpointer a, gconstpointer b)
116 const struct ssc_entry *ca = a;
118 return strcmp(ca->service, b);
121 gboolean __ofono_ussd_ssc_register(struct ofono_ussd *ussd, const char *sc,
122 ofono_ussd_ssc_cb_t cb, void *data,
123 ofono_destroy_func destroy)
125 struct ssc_entry *entry;
130 entry = ssc_entry_create(sc, cb, data, destroy);
134 ussd->ss_control_list = g_slist_prepend(ussd->ss_control_list, entry);
139 void __ofono_ussd_ssc_unregister(struct ofono_ussd *ussd, const char *sc)
146 l = g_slist_find_custom(ussd->ss_control_list, sc,
147 ssc_entry_find_by_service);
152 ssc_entry_destroy(l->data);
153 ussd->ss_control_list = g_slist_remove(ussd->ss_control_list, l->data);
156 gboolean __ofono_ussd_passwd_register(struct ofono_ussd *ussd, const char *sc,
157 ofono_ussd_passwd_cb_t cb, void *data,
158 ofono_destroy_func destroy)
160 struct ssc_entry *entry;
165 entry = ssc_entry_create(sc, cb, data, destroy);
169 ussd->ss_passwd_list = g_slist_prepend(ussd->ss_passwd_list, entry);
174 void __ofono_ussd_passwd_unregister(struct ofono_ussd *ussd, const char *sc)
181 l = g_slist_find_custom(ussd->ss_passwd_list, sc,
182 ssc_entry_find_by_service);
187 ssc_entry_destroy(l->data);
188 ussd->ss_passwd_list = g_slist_remove(ussd->ss_passwd_list, l->data);
191 static gboolean recognized_passwd_change_string(struct ofono_ussd *ussd,
193 char *sia, char *sib,
194 char *sic, char *sid,
195 char *dn, DBusMessage *msg)
197 GSList *l = ussd->ss_passwd_list;
200 case SS_CONTROL_TYPE_ACTIVATION:
201 case SS_CONTROL_TYPE_REGISTRATION:
208 if (strcmp(sc, "03") || strlen(dn))
211 /* If SIC & SID don't match, then we just bail out here */
212 if (strcmp(sic, sid)) {
213 DBusConnection *conn = ofono_dbus_get_connection();
214 DBusMessage *reply = __ofono_error_invalid_format(msg);
215 g_dbus_send_message(conn, reply);
219 while ((l = g_slist_find_custom(l, sia,
220 ssc_entry_find_by_service)) != NULL) {
221 struct ssc_entry *entry = l->data;
222 ofono_ussd_passwd_cb_t cb = entry->cb;
224 if (cb(sia, sib, sic, msg, entry->user))
233 static gboolean recognized_control_string(struct ofono_ussd *ussd,
237 char *str = g_strdup(ss_str);
238 char *sc, *sia, *sib, *sic, *sid, *dn;
240 gboolean ret = FALSE;
242 DBG("parsing control string");
244 if (parse_ss_control_string(str, &type, &sc,
245 &sia, &sib, &sic, &sid, &dn)) {
246 GSList *l = ussd->ss_control_list;
248 DBG("Got parse result: %d, %s, %s, %s, %s, %s, %s",
249 type, sc, sia, sib, sic, sid, dn);
252 * A password change string needs to be treated separately
253 * because it uses a fourth SI and is thus not a valid
256 if (recognized_passwd_change_string(ussd, type, sc,
257 sia, sib, sic, sid, dn, msg)) {
265 while ((l = g_slist_find_custom(l, sc,
266 ssc_entry_find_by_service)) != NULL) {
267 struct ssc_entry *entry = l->data;
268 ofono_ussd_ssc_cb_t cb = entry->cb;
270 if (cb(type, sc, sia, sib, sic, dn, msg, entry->user)) {
280 /* TODO: Handle all strings that control voice calls */
282 /* TODO: Handle Multiple subscriber profile DN*59#SEND and *59#SEND */
285 * Note: SIM PIN/PIN2 change and unblock and IMEI presentation
286 * procedures are not handled by the daemon since they are not followed
287 * by SEND and are not valid USSD requests.
296 static const char *ussd_get_state_string(struct ofono_ussd *ussd)
298 switch (ussd->state) {
299 case USSD_STATE_IDLE:
301 case USSD_STATE_ACTIVE:
302 case USSD_STATE_RESPONSE_SENT:
304 case USSD_STATE_USER_ACTION:
305 return "user-response";
311 static void ussd_change_state(struct ofono_ussd *ussd, int state)
314 DBusConnection *conn = ofono_dbus_get_connection();
315 const char *path = __ofono_atom_get_path(ussd->atom);
317 if (state == ussd->state)
322 value = ussd_get_state_string(ussd);
323 ofono_dbus_signal_property_changed(conn, path,
324 OFONO_SUPPLEMENTARY_SERVICES_INTERFACE,
325 "State", DBUS_TYPE_STRING, &value);
328 static void ussd_request_finish(struct ofono_ussd *ussd, int error, int dcs,
329 const unsigned char *pdu, int len)
331 struct ussd_request *req = ussd->req;
334 req->cb(error, dcs, pdu, len, req->user_data);
340 static int ussd_status_to_failure_code(int status)
343 case OFONO_USSD_STATUS_TIMED_OUT:
345 case OFONO_USSD_STATUS_NOT_SUPPORTED:
352 static char const *ussd_status_name(int status)
355 case OFONO_USSD_STATUS_NOTIFY:
357 case OFONO_USSD_STATUS_ACTION_REQUIRED:
358 return "ACTION_REQUIRED";
359 case OFONO_USSD_STATUS_TERMINATED:
361 case OFONO_USSD_STATUS_LOCAL_CLIENT_RESPONDED:
362 return "LOCAL_CLIENT_RESPONDED";
363 case OFONO_USSD_STATUS_NOT_SUPPORTED:
364 return "NOT_SUPPORTED";
365 case OFONO_USSD_STATUS_TIMED_OUT:
372 static const char *ussd_state_name(enum ussd_state state)
375 case USSD_STATE_IDLE:
377 case USSD_STATE_ACTIVE:
379 case USSD_STATE_RESPONSE_SENT:
380 return "RESPONSE_SENT";
381 case USSD_STATE_USER_ACTION:
382 return "USER_ACTION";
389 void ofono_ussd_notify(struct ofono_ussd *ussd, int status, int dcs,
390 const unsigned char *data, int data_len)
392 DBusConnection *conn = ofono_dbus_get_connection();
393 const char *ussdstr = "USSD";
394 char *utf8_str = NULL;
396 const char sig[] = { DBUS_TYPE_STRING, 0 };
398 DBusMessageIter iter;
399 DBusMessageIter variant;
401 DBG("status: %d %s, state: %d %s",
402 status, ussd_status_name(status),
403 ussd->state, ussd_state_name(ussd->state));
406 (status == OFONO_USSD_STATUS_NOTIFY ||
407 status == OFONO_USSD_STATUS_TERMINATED ||
408 status == OFONO_USSD_STATUS_TIMED_OUT ||
409 status == OFONO_USSD_STATUS_NOT_SUPPORTED)) {
410 ussd_request_finish(ussd, ussd_status_to_failure_code(status),
411 dcs, data, data_len);
413 ussd_change_state(ussd, USSD_STATE_IDLE);
417 if (status == OFONO_USSD_STATUS_NOT_SUPPORTED) {
418 ussd_change_state(ussd, USSD_STATE_IDLE);
420 if (ussd->pending == NULL)
423 reply = __ofono_error_not_supported(ussd->pending);
427 if (status == OFONO_USSD_STATUS_TIMED_OUT) {
428 ussd_change_state(ussd, USSD_STATE_IDLE);
430 if (ussd->pending == NULL)
433 reply = __ofono_error_timed_out(ussd->pending);
437 if (data && data_len > 0)
438 utf8_str = ussd_decode(dcs, data_len, data);
442 /* TODO: Rework this in the Agent framework */
443 if (ussd->state == USSD_STATE_ACTIVE) {
445 reply = dbus_message_new_method_return(ussd->pending);
450 dbus_message_iter_init_append(reply, &iter);
452 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
455 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig,
458 dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING,
461 dbus_message_iter_close_container(&iter, &variant);
463 if (status == OFONO_USSD_STATUS_ACTION_REQUIRED)
464 ussd_change_state(ussd, USSD_STATE_USER_ACTION);
466 ussd_change_state(ussd, USSD_STATE_IDLE);
468 } else if (ussd->state == USSD_STATE_RESPONSE_SENT) {
469 reply = dbus_message_new_method_return(ussd->pending);
474 dbus_message_append_args(reply, DBUS_TYPE_STRING, &str,
477 if (status == OFONO_USSD_STATUS_ACTION_REQUIRED)
478 ussd_change_state(ussd, USSD_STATE_USER_ACTION);
480 ussd_change_state(ussd, USSD_STATE_IDLE);
481 } else if (ussd->state == USSD_STATE_IDLE) {
482 const char *signal_name;
483 const char *path = __ofono_atom_get_path(ussd->atom);
486 if (status == OFONO_USSD_STATUS_ACTION_REQUIRED) {
487 new_state = USSD_STATE_USER_ACTION;
488 signal_name = "RequestReceived";
490 new_state = USSD_STATE_IDLE;
491 signal_name = "NotificationReceived";
497 g_dbus_emit_signal(conn, path,
498 OFONO_SUPPLEMENTARY_SERVICES_INTERFACE, signal_name,
499 DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID);
501 ussd_change_state(ussd, new_state);
504 ofono_error("Received an unsolicited USSD but can't handle.");
505 DBG("USSD is: status: %d, %s", status, str);
511 g_dbus_send_message(conn, reply);
513 dbus_message_unref(ussd->pending);
514 ussd->pending = NULL;
520 static void ussd_callback(const struct ofono_error *error, void *data)
522 struct ofono_ussd *ussd = data;
525 if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
526 DBG("ussd request failed with error: %s",
527 telephony_error_to_str(error));
529 if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
530 ussd_change_state(ussd, USSD_STATE_ACTIVE);
534 if (ussd->pending == NULL)
537 reply = __ofono_error_failed(ussd->pending);
538 __ofono_dbus_pending_reply(&ussd->pending, reply);
541 static DBusMessage *ussd_initiate(DBusConnection *conn, DBusMessage *msg,
544 struct ofono_ussd *ussd = data;
545 struct ofono_modem *modem = __ofono_atom_get_modem(ussd->atom);
546 struct ofono_voicecall *vc;
547 gboolean call_in_progress;
550 unsigned char buf[160];
553 if (__ofono_ussd_is_busy(ussd))
554 return __ofono_error_busy(msg);
556 if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
557 DBUS_TYPE_INVALID) == FALSE)
558 return __ofono_error_invalid_args(msg);
560 if (strlen(str) == 0)
561 return __ofono_error_invalid_format(msg);
563 DBG("checking if this is a recognized control string");
564 if (recognized_control_string(ussd, str, msg))
567 vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, modem);
569 call_in_progress = __ofono_voicecall_is_busy(vc,
570 OFONO_VOICECALL_INTERACTION_NONE);
572 call_in_progress = FALSE;
574 DBG("No.., checking if this is a USSD string");
575 if (!valid_ussd_string(str, call_in_progress))
576 return __ofono_error_not_recognized(msg);
578 if (!ussd_encode(str, &num_packed, buf))
579 return __ofono_error_invalid_format(msg);
581 if (ussd->driver->request == NULL)
582 return __ofono_error_not_implemented(msg);
584 DBG("OK, running USSD request");
586 ussd->pending = dbus_message_ref(msg);
588 ussd->driver->request(ussd, dcs, buf, num_packed, ussd_callback, ussd);
593 static void ussd_response_callback(const struct ofono_error *error, void *data)
595 struct ofono_ussd *ussd = data;
598 if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
599 DBG("ussd response failed with error: %s",
600 telephony_error_to_str(error));
602 if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
603 ussd_change_state(ussd, USSD_STATE_RESPONSE_SENT);
607 if (ussd->pending == NULL)
610 reply = __ofono_error_failed(ussd->pending);
611 __ofono_dbus_pending_reply(&ussd->pending, reply);
614 static DBusMessage *ussd_respond(DBusConnection *conn, DBusMessage *msg,
617 struct ofono_ussd *ussd = data;
620 unsigned char buf[160];
624 return __ofono_error_busy(msg);
626 if (ussd->state != USSD_STATE_USER_ACTION)
627 return __ofono_error_not_active(msg);
629 if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
630 DBUS_TYPE_INVALID) == FALSE)
631 return __ofono_error_invalid_args(msg);
633 if (strlen(str) == 0)
634 return __ofono_error_invalid_format(msg);
636 if (!ussd_encode(str, &num_packed, buf))
637 return __ofono_error_invalid_format(msg);
639 if (ussd->driver->request == NULL)
640 return __ofono_error_not_implemented(msg);
642 ussd->pending = dbus_message_ref(msg);
644 ussd->driver->request(ussd, dcs, buf, num_packed,
645 ussd_response_callback, ussd);
650 static void ussd_cancel_callback(const struct ofono_error *error, void *data)
652 struct ofono_ussd *ussd = data;
655 if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
656 DBG("ussd cancel failed with error: %s",
657 telephony_error_to_str(error));
659 reply = __ofono_error_failed(ussd->cancel);
660 __ofono_dbus_pending_reply(&ussd->cancel, reply);
666 reply = __ofono_error_canceled(ussd->pending);
667 __ofono_dbus_pending_reply(&ussd->pending, reply);
670 reply = dbus_message_new_method_return(ussd->cancel);
671 __ofono_dbus_pending_reply(&ussd->cancel, reply);
674 ussd_request_finish(ussd, -ECANCELED, 0, NULL, 0);
676 ussd_change_state(ussd, USSD_STATE_IDLE);
679 static DBusMessage *ussd_cancel(DBusConnection *conn, DBusMessage *msg,
682 struct ofono_ussd *ussd = data;
684 if (ussd->state == USSD_STATE_IDLE)
685 return __ofono_error_not_active(msg);
687 /* We have called Respond() but not returned from its callback yet */
688 if (ussd->state == USSD_STATE_USER_ACTION && ussd->pending)
689 return __ofono_error_busy(msg);
692 return __ofono_error_busy(msg);
694 if (ussd->driver->cancel == NULL)
695 return __ofono_error_not_implemented(msg);
697 ussd->cancel = dbus_message_ref(msg);
699 ussd->driver->cancel(ussd, ussd_cancel_callback, ussd);
704 static DBusMessage *ussd_get_properties(DBusConnection *conn,
705 DBusMessage *msg, void *data)
707 struct ofono_ussd *ussd = data;
709 DBusMessageIter iter;
710 DBusMessageIter dict;
713 reply = dbus_message_new_method_return(msg);
717 dbus_message_iter_init_append(reply, &iter);
719 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
720 OFONO_PROPERTIES_ARRAY_SIGNATURE,
723 value = ussd_get_state_string(ussd);
724 ofono_dbus_dict_append(&dict, "State", DBUS_TYPE_STRING, &value);
726 dbus_message_iter_close_container(&iter, &dict);
731 static const GDBusMethodTable ussd_methods[] = {
732 { GDBUS_ASYNC_METHOD("Initiate",
733 GDBUS_ARGS({ "command", "s" }),
734 GDBUS_ARGS({ "result_name", "s" }, { "value", "v" }),
736 { GDBUS_ASYNC_METHOD("Respond",
737 GDBUS_ARGS({ "reply", "s" }),
738 GDBUS_ARGS({ "result", "s" }),
740 { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL, ussd_cancel) },
741 { GDBUS_METHOD("GetProperties",
742 NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
743 ussd_get_properties) },
747 static const GDBusSignalTable ussd_signals[] = {
748 { GDBUS_SIGNAL("NotificationReceived",
749 GDBUS_ARGS({ "message", "s" })) },
750 { GDBUS_SIGNAL("RequestReceived",
751 GDBUS_ARGS({ "message", "s" })) },
752 { GDBUS_SIGNAL("PropertyChanged",
753 GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
757 int ofono_ussd_driver_register(const struct ofono_ussd_driver *d)
759 DBG("driver: %p, name: %s", d, d->name);
761 if (d->probe == NULL)
764 g_drivers = g_slist_prepend(g_drivers, (void *) d);
769 void ofono_ussd_driver_unregister(const struct ofono_ussd_driver *d)
771 DBG("driver: %p, name: %s", d, d->name);
773 g_drivers = g_slist_remove(g_drivers, (void *) d);
776 static void ussd_unregister(struct ofono_atom *atom)
778 struct ofono_ussd *ussd = __ofono_atom_get_data(atom);
779 DBusConnection *conn = ofono_dbus_get_connection();
780 struct ofono_modem *modem = __ofono_atom_get_modem(atom);
781 const char *path = __ofono_atom_get_path(atom);
783 g_slist_foreach(ussd->ss_control_list, (GFunc) ssc_entry_destroy, NULL);
784 g_slist_free(ussd->ss_control_list);
785 ussd->ss_control_list = NULL;
787 g_slist_foreach(ussd->ss_passwd_list, (GFunc) ssc_entry_destroy, NULL);
788 g_slist_free(ussd->ss_passwd_list);
789 ussd->ss_passwd_list = NULL;
791 ofono_modem_remove_interface(modem,
792 OFONO_SUPPLEMENTARY_SERVICES_INTERFACE);
793 g_dbus_unregister_interface(conn, path,
794 OFONO_SUPPLEMENTARY_SERVICES_INTERFACE);
797 static void ussd_remove(struct ofono_atom *atom)
799 struct ofono_ussd *ussd = __ofono_atom_get_data(atom);
801 DBG("atom: %p", atom);
806 if (ussd->driver && ussd->driver->remove)
807 ussd->driver->remove(ussd);
812 struct ofono_ussd *ofono_ussd_create(struct ofono_modem *modem,
817 struct ofono_ussd *ussd;
823 ussd = g_try_new0(struct ofono_ussd, 1);
828 ussd->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_USSD,
831 for (l = g_drivers; l; l = l->next) {
832 const struct ofono_ussd_driver *drv = l->data;
834 if (g_strcmp0(drv->name, driver))
837 if (drv->probe(ussd, vendor, data) < 0)
847 void ofono_ussd_register(struct ofono_ussd *ussd)
849 DBusConnection *conn = ofono_dbus_get_connection();
850 struct ofono_modem *modem = __ofono_atom_get_modem(ussd->atom);
851 const char *path = __ofono_atom_get_path(ussd->atom);
853 if (!g_dbus_register_interface(conn, path,
854 OFONO_SUPPLEMENTARY_SERVICES_INTERFACE,
855 ussd_methods, ussd_signals, NULL,
857 ofono_error("Could not create %s interface",
858 OFONO_SUPPLEMENTARY_SERVICES_INTERFACE);
863 ofono_modem_add_interface(modem,
864 OFONO_SUPPLEMENTARY_SERVICES_INTERFACE);
866 __ofono_atom_register(ussd->atom, ussd_unregister);
869 void ofono_ussd_remove(struct ofono_ussd *ussd)
871 __ofono_atom_free(ussd->atom);
874 void ofono_ussd_set_data(struct ofono_ussd *ussd, void *data)
876 ussd->driver_data = data;
879 void *ofono_ussd_get_data(struct ofono_ussd *ussd)
881 return ussd->driver_data;
884 static void ussd_request_callback(const struct ofono_error *error, void *data)
886 struct ofono_ussd *ussd = data;
888 if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
889 ussd_request_finish(ussd, -EINVAL, 0, NULL, 0);
891 ussd_change_state(ussd, USSD_STATE_ACTIVE);
894 int __ofono_ussd_initiate(struct ofono_ussd *ussd, int dcs,
895 const unsigned char *pdu, int len,
896 ofono_ussd_request_cb_t cb, void *user_data)
898 struct ussd_request *req;
900 if (ussd->driver->request == NULL)
903 if (__ofono_ussd_is_busy(ussd))
906 req = g_try_new0(struct ussd_request, 1);
911 req->user_data = user_data;
915 ussd->driver->request(ussd, dcs, pdu, len, ussd_request_callback, ussd);
920 void __ofono_ussd_initiate_cancel(struct ofono_ussd *ussd)
922 if (ussd->req == NULL || ussd->req->cb == NULL)
925 ussd->req->cb = NULL;