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.
This commit is contained in:
Andrzej Zaborowski 2010-02-15 11:05:16 +01:00 committed by Denis Kenzior
parent 400d692e6e
commit 740f8e3f09
2 changed files with 216 additions and 10 deletions

64
doc/ussd-api.txt Normal file
View File

@ -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().

View File

@ -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 },
{ "Cancel", "", "", ussd_cancel,
{ "Respond", "s", "", ussd_respond,
G_DBUS_METHOD_FLAG_ASYNC },
{ "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" },
{ }
};