summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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" },
{ }
};