summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrzej Zaborowski <andrew.zaborowski@intel.com>2010-02-15 11:05:16 +0100
committerDenis Kenzior <denkenz@gmail.com>2010-02-16 10:17:40 -0600
commit740f8e3f0941e033b33c9394299e6bb5a05c72ba (patch)
treee6c14c10f8594e48f9c5cd269e6e6684de43c228
parent400d692e6ef459fa3a98f8c2dd1f4302f39cb020 (diff)
downloadofono-740f8e3f0941e033b33c9394299e6bb5a05c72ba.tar.bz2
Handle network-initiated ussd requests.
This adds the methods on the D-bus interface to allow the client to handle USSD requests from the network, according to 22.090. Unfortunately this document is not clear on every point and some details can't be implemented. This includes reporting unsupported request to the network, unsupported language, ME busy etc, because there isn't an AT command for that.
-rw-r--r--doc/ussd-api.txt64
-rw-r--r--src/ussd.c162
2 files changed, 216 insertions, 10 deletions
diff --git a/doc/ussd-api.txt b/doc/ussd-api.txt
new file mode 100644
index 00000000..83d3cee7
--- /dev/null
+++ b/doc/ussd-api.txt
@@ -0,0 +1,64 @@
+SupplementaryServices hierarchy
+==========================
+
+Service org.ofono
+Interface org.ofono.SupplementaryServices
+Object path [variable prefix]/{modem0,modem1,...}
+
+Methods string, variant Initiate(string command)
+
+ Sends a USSD command string to the network
+ initiating a session. When the request is handled
+ by the appropriate node of the network, the
+ method returns the response or an appropriate
+ error. The network may be awaiting further response
+ from the ME after returning from this method and no
+ new command can be initiated until this one is
+ cancelled or ended.
+
+ void Respond(string reply)
+
+ Send a response to the network either when
+ it is awaiting further input after Initiate()
+ was called or after a network-initiated request.
+
+ void Cancel()
+
+ Cancel an ongoing USSD session, mobile- or
+ network-initiated.
+
+ dict GetProperties()
+
+ Returns Supplementary Services related properties. See
+ the properties section for available properties.
+
+Signals NotificationReceived(string message)
+
+ Signal is emitted on a network-initiated USSD
+ request for which no response is needed.
+
+ RequestReceived(string message)
+
+ Signal is emitted on a network-initiated USSD
+ request for which a response must be sent using
+ the Respond method unless it is cancelled or
+ the request is not supported.
+
+ PropertyChanged(string property, variant value)
+
+ Signal is emitted whenever a property has changed.
+ The new value is passed as the signal argument.
+
+Properties string USSDState [readonly]
+
+ Reflects the state of current USSD session. The
+ values have the following meanings:
+
+ "idle" No active USSD session.
+ "active" A session is active between the
+ network and the ME, the ME is
+ waiting for a reply from the
+ network.
+ "awaiting-user-response" The network is waiting for the
+ user's response, client must
+ call Respond().
diff --git a/src/ussd.c b/src/ussd.c
index 470634a9..99335333 100644
--- a/src/ussd.c
+++ b/src/ussd.c
@@ -272,6 +272,37 @@ out:
return ret;
}
+static const char *ussd_get_state_string(struct ofono_ussd *ussd)
+{
+ switch (ussd->state) {
+ case USSD_STATE_IDLE:
+ return "idle";
+ case USSD_STATE_ACTIVE:
+ return "active";
+ case USSD_STATE_USER_ACTION:
+ return "awaiting-user-response";
+ }
+
+ return "";
+}
+
+static void ussd_change_state(struct ofono_ussd *ussd, int state)
+{
+ const char *value;
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = __ofono_atom_get_path(ussd->atom);
+
+ if (state == ussd->state)
+ return;
+
+ ussd->state = state;
+
+ value = ussd_get_state_string(ussd);
+ ofono_dbus_signal_property_changed(conn, path,
+ SUPPLEMENTARY_SERVICES_INTERFACE,
+ "USSDState", DBUS_TYPE_STRING, &value);
+}
+
void ofono_ussd_notify(struct ofono_ussd *ussd, int status, const char *str)
{
DBusConnection *conn = ofono_dbus_get_connection();
@@ -282,7 +313,7 @@ void ofono_ussd_notify(struct ofono_ussd *ussd, int status, const char *str)
DBusMessageIter variant;
if (status == OFONO_USSD_STATUS_NOT_SUPPORTED) {
- ussd->state = USSD_STATE_IDLE;
+ ussd_change_state(ussd, USSD_STATE_IDLE);
if (!ussd->pending)
return;
@@ -292,7 +323,7 @@ void ofono_ussd_notify(struct ofono_ussd *ussd, int status, const char *str)
}
if (status == OFONO_USSD_STATUS_TIMED_OUT) {
- ussd->state = USSD_STATE_IDLE;
+ ussd_change_state(ussd, USSD_STATE_IDLE);
if (!ussd->pending)
return;
@@ -323,12 +354,34 @@ void ofono_ussd_notify(struct ofono_ussd *ussd, int status, const char *str)
dbus_message_iter_close_container(&iter, &variant);
if (status == OFONO_USSD_STATUS_ACTION_REQUIRED)
- ussd->state = USSD_STATE_USER_ACTION;
+ ussd_change_state(ussd, USSD_STATE_USER_ACTION);
else
- ussd->state = USSD_STATE_IDLE;
+ ussd_change_state(ussd, USSD_STATE_IDLE);
+
+ } else if (ussd->state == USSD_STATE_IDLE) {
+ const char *signal_name;
+ const char *path = __ofono_atom_get_path(ussd->atom);
+ int new_state;
+
+ if (status == OFONO_USSD_STATUS_ACTION_REQUIRED) {
+ new_state = USSD_STATE_USER_ACTION;
+ signal_name = "RequestReceived";
+ } else {
+ new_state = USSD_STATE_IDLE;
+ signal_name = "NotificationReceived";
+ }
+
+ if (!str)
+ str = "";
+
+ g_dbus_emit_signal(conn, path,
+ SUPPLEMENTARY_SERVICES_INTERFACE, signal_name,
+ DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID);
+ ussd_change_state(ussd, new_state);
+ return;
} else {
- ofono_error("Received an unsolicited USSD, ignoring for now...");
+ ofono_error("Received an unsolicited USSD but can't handle.");
DBG("USSD is: status: %d, %s", status, str);
return;
@@ -351,7 +404,7 @@ static void ussd_callback(const struct ofono_error *error, void *data)
telephony_error_to_str(error));
if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
- ussd->state = USSD_STATE_ACTIVE;
+ ussd_change_state(ussd, USSD_STATE_ACTIVE);
return;
}
@@ -371,7 +424,7 @@ static DBusMessage *ussd_initiate(DBusConnection *conn, DBusMessage *msg,
if (ussd->pending)
return __ofono_error_busy(msg);
- if (ussd->state == USSD_STATE_ACTIVE)
+ if (ussd->state != USSD_STATE_IDLE)
return __ofono_error_busy(msg);
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
@@ -401,12 +454,67 @@ static DBusMessage *ussd_initiate(DBusConnection *conn, DBusMessage *msg,
return NULL;
}
+static void ussd_response_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_ussd *ussd = data;
+ DBusConnection *conn = ofono_dbus_get_connection();
+ DBusMessage *reply;
+
+ if (!ussd->pending)
+ return;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
+ ussd_change_state(ussd, USSD_STATE_IDLE);
+
+ reply = dbus_message_new_method_return(ussd->pending);
+ } else {
+ DBG("ussd response failed with error: %s",
+ telephony_error_to_str(error));
+
+ reply = __ofono_error_failed(ussd->pending);
+ }
+
+ g_dbus_send_message(conn, reply);
+
+ reply = __ofono_error_failed(ussd->pending);
+ __ofono_dbus_pending_reply(&ussd->pending, reply);
+}
+
+static DBusMessage *ussd_respond(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_ussd *ussd = data;
+ const char *str;
+
+ if (ussd->pending)
+ return __ofono_error_busy(msg);
+
+ if (ussd->state != USSD_STATE_USER_ACTION)
+ return __ofono_error_not_active(msg);
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
+ DBUS_TYPE_INVALID) == FALSE)
+ return __ofono_error_invalid_args(msg);
+
+ if (strlen(str) == 0)
+ return __ofono_error_invalid_format(msg);
+
+ if (!ussd->driver->request)
+ return __ofono_error_not_implemented(msg);
+
+ ussd->pending = dbus_message_ref(msg);
+
+ ussd->driver->request(ussd, str, ussd_response_callback, ussd);
+
+ return NULL;
+}
+
static void ussd_cancel_callback(const struct ofono_error *error, void *data)
{
struct ofono_ussd *ussd = data;
DBusMessage *reply;
- ussd->state = USSD_STATE_IDLE;
+ ussd_change_state(ussd, USSD_STATE_IDLE);
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
DBG("ussd cancel failed with error: %s",
@@ -444,15 +552,49 @@ static DBusMessage *ussd_cancel(DBusConnection *conn, DBusMessage *msg,
return NULL;
}
+static DBusMessage *ussd_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_ussd *ussd = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ const char *value;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ OFONO_PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ value = ussd_get_state_string(ussd);
+ ofono_dbus_dict_append(&dict, "USSDState", DBUS_TYPE_STRING, &value);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
static GDBusMethodTable ussd_methods[] = {
- { "Initiate", "s", "sv", ussd_initiate,
+ { "Initiate", "s", "sv", ussd_initiate,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Respond", "s", "", ussd_respond,
G_DBUS_METHOD_FLAG_ASYNC },
- { "Cancel", "", "", ussd_cancel,
+ { "Cancel", "", "", ussd_cancel,
G_DBUS_METHOD_FLAG_ASYNC },
+ { "GetProperties", "", "a{sv}", ussd_get_properties,
+ 0 },
{ }
};
static GDBusSignalTable ussd_signals[] = {
+ { "NotificationReceived", "s" },
+ { "RequestReceived", "s" },
+ { "PropertyChanged", "sv" },
{ }
};