The interface is mostly unchanged since
http://lists.ofono.org/pipermail/ofono/2010-June/003040.html
but Timeout property is removed and set to 10 minutes and an agent
disconnect sends a User Ended Session response. If no agent is
registered when the card sends a command for the UI, a User Ended
Session is sent back immediately. This has made the code a little
shorter. The "proactive session interaction with terminal display"
(TS 102.223 Section 6.9) is also more correctly implemented now.
This patch adds a skeleton that command implementations use in the
subsequent patches.
org.ofono.SimApplicationAgent
Methods
(int item, bool help) or GoBack/Terminate Menu(string title,
array{string} items, bool has_help, bool
soft_key_preferred, int default_item, bool is_main_menu)
Handles both Set Up Menu type of menu and Select Item
since they're similar. The agent can return GoBack or
Terminate if is_main_menu is False. The agent may
decide to release the display when it receives a main
menu request following an event other than a GoBack
response. default_item may be -1 meaning no default is
given.
Ok/GoBack/Terminate DisplayText(string text, bool
confirmation, bool urgent, bool navigation)
The UI should show the message, optionally asking the
user for confirmation. If navigation is false, the
returned value will have no effect. If confirmation is
false it is optional whether the user can clear the
message through MMI action (if so, the agent should
send a reply on user MMI action). Frames are not
supported currently. A low priority message should
only be shown when the screen is only used for idle
text display at the moment. A high priority message
should be shown when the screen is not used for other
high priority message such as low battery or incoming
call information. (Should there also be a "Screen busy"
reply so we can pass this information to SIM app, as
mandated by the spec?)
string/Back/Terminate/Help GetKey(string message, string charset,
bool help_available, bool single_key)
charset is one of:
"yesno" - response needs to be "yes" or "no"
"digit" - one of 0-9, *, #, +
"gsm" - only characters from the GSM SMS charset
"any" - UCS2 characters
single_key indicates that only characters from the device's
key faces are allowed (e.g. no "+" allowed if device only
has a basic keypad), and that the key should be returned
without being shown on screen or waiting for any kind of
user confirmation.
string/Back/Terminate/Help GetText(string message, string default,
string charset, byte min, byte max, bool
help_available, bool password)
charset is one of:
"digit" - one of 0-9, *, #, +
"gsm" - only characters from the GSM SMS charset
"any" - UCS2 characters
The returned string must be between min and max characters long.
The default value should be used as the initial value of the
text field being edited by user. If password is True, individual
characters entered must not be revealed on the screen, but
indication of new characters can be given, for example by
showing them as '*'.
void Cancel(void)
Cancels a method call in progress, usually due to
user taking too long to respond (timeout defined
by the SIM application or manufacturer defined) or
session termination or other events. No reply to
the cancelled call is expected.
void Release(void)
Agent is being released, possibly because of ofono
terminating, SimToolkit interface torn down or modem off.
Agent is unregistered, no UnregisterAgent call is
expected.
org.ofono.SimToolkit
Methods
void RegisterAgent(object)
void UnregisterAgent(object)
---
src/stk.c | 346 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 346 insertions(+), 0 deletions(-)
diff --git a/src/stk.c b/src/stk.c
index 556dc68..28a1beb 100644
--- a/src/stk.c
+++ b/src/stk.c
@@ -46,6 +46,28 @@ struct stk_timer {
time_t start;
};
+enum stk_agent_result {
+ STK_AGENT_RESULT_OK,
+ STK_AGENT_RESULT_HELP,
+ STK_AGENT_RESULT_BACK,
+ STK_AGENT_RESULT_TERMINATE,
+ STK_AGENT_RESULT_TIMEOUT,
+ STK_AGENT_RESULT_CANCEL,
+};
+
+struct stk_app_agent {
+ char *path;
+ char *bus;
+ DBusMessage *msg;
+ DBusPendingCall *call;
+ guint watch;
+ guint send_menu_source;
+ guint cmd_timeout;
+ void (*cmd_send)(struct ofono_stk *stk, DBusMessage *call);
+ void (*cmd_cb)(struct ofono_stk *stk, enum stk_agent_result result,
+ DBusMessage *reply);
+};
+
struct ofono_stk {
const struct ofono_stk_driver *driver;
void *driver_data;
@@ -57,6 +79,8 @@ struct ofono_stk {
struct stk_timer timers[8];
guint timers_source;
+ int timeout;
+ struct stk_app_agent *app_agent;
struct sms_submit_req *sms_submit_req;
char *idle_mode_text;
};
@@ -76,6 +100,11 @@ struct sms_submit_req {
#define ENVELOPE_RETRIES_DEFAULT 5
+#define OFONO_NAVIGATION_PREFIX OFONO_SERVICE ".Navigation"
+#define OFONO_NAVIGATION_GOBACK OFONO_NAVIGATION_PREFIX ".Back"
+#define OFONO_NAVIGATION_TERMINATED OFONO_NAVIGATION_PREFIX ".Terminated"
+#define OFONO_NAVIGATION_HELP OFONO_NAVIGATION_PREFIX ".Help"
+
static void envelope_queue_run(struct ofono_stk *stk);
static void timers_update(struct ofono_stk *stk);
@@ -255,8 +284,319 @@ static DBusMessage *stk_get_properties(DBusConnection *conn,
return reply;
}
+static void app_agent_request_send_cancel(struct stk_app_agent *agent)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ DBusMessage *message;
+
+ message = dbus_message_new_method_call(agent->bus, agent->path,
+ OFONO_SIM_APP_INTERFACE,
+ "Cancel");
+ if (message == NULL)
+ return;
+
+ dbus_message_set_no_reply(message, TRUE);
+
+ g_dbus_send_message(conn, message);
+}
+
+static void app_agent_request_end(struct stk_app_agent *agent)
+{
+ agent->cmd_send = NULL;
+ agent->cmd_cb = NULL;
+
+ if (agent->cmd_timeout) {
+ g_source_remove(agent->cmd_timeout);
+ agent->cmd_timeout = 0;
+ }
+
+ if (agent->msg) {
+ dbus_message_unref(agent->msg);
+ agent->msg = NULL;
+ }
+
+ if (agent->call) {
+ dbus_pending_call_cancel(agent->call);
+ dbus_pending_call_unref(agent->call);
+ agent->call = NULL;
+ }
+}
+
+static void app_agent_request_cancel(struct ofono_stk *stk)
+{
+ struct stk_app_agent *agent = stk->app_agent;
+
+ if (!agent)
+ return;
+
+ agent->cmd_cb(stk, STK_AGENT_RESULT_CANCEL, NULL);
+
+ app_agent_request_end(agent);
+
+ app_agent_request_send_cancel(agent);
+}
+
+static gboolean app_agent_request_timeout(gpointer user_data)
+{
+ struct ofono_stk *stk = user_data;
+ struct stk_app_agent *agent = stk->app_agent;
+
+ agent->cmd_timeout = 0;
+
+ agent->cmd_cb(stk, STK_AGENT_RESULT_TIMEOUT, NULL);
+
+ app_agent_request_end(agent);
+
+ app_agent_request_send_cancel(agent);
+
+ return FALSE;
+}
+
+static void app_agent_request_reply_handle(DBusPendingCall *call, void *data)
+{
+ struct ofono_stk *stk = data;
+ DBusError err;
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ enum stk_agent_result result = STK_AGENT_RESULT_OK;
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ ofono_error("SimAppAgent %s replied with error %s, %s",
+ stk->app_agent ? stk->app_agent->path :
+ "(destroyed)", err.name, err.message);
+
+ if (g_str_has_prefix(err.name, OFONO_NAVIGATION_GOBACK))
+ result = STK_AGENT_RESULT_BACK;
+ else if (g_str_has_prefix(err.name, OFONO_NAVIGATION_HELP))
+ result = STK_AGENT_RESULT_HELP;
+ else
+ result = STK_AGENT_RESULT_TERMINATE;
+
+ dbus_error_free(&err);
+ }
+
+ if (stk->app_agent) {
+ stk->app_agent->cmd_cb(stk, result, reply);
+
+ app_agent_request_end(stk->app_agent);
+ }
+
+ dbus_message_unref(reply);
+}
+
+static gboolean app_agent_request_send(struct ofono_stk *stk,
+ struct stk_app_agent *agent)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+
+ /*
+ * The cmd_send callback needs to set the method name to
+ * something different than "Cancel".
+ */
+ agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+ OFONO_SIM_APP_INTERFACE,
+ "Cancel");
+ if (agent->msg == NULL) {
+ ofono_error("Couldn't make a DBusMessage");
+ return FALSE;
+ }
+
+ agent->cmd_send(stk, agent->msg);
+
+ if (dbus_connection_send_with_reply(conn,
+ agent->msg, &agent->call, INT_MAX) == FALSE ||
+ agent->call == NULL) {
+ dbus_message_unref(agent->msg);
+ agent->msg = NULL;
+
+ ofono_error("Couldn't send a method call");
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(agent->call,
+ app_agent_request_reply_handle,
+ stk, NULL);
+
+ return TRUE;
+}
+
+static void app_agent_request_start(struct ofono_stk *stk,
+ void (*send)(struct ofono_stk *stk,
+ DBusMessage *call),
+ void (*cb)(struct ofono_stk *stk,
+ enum stk_agent_result result,
+ DBusMessage *reply),
+ int timeout)
+{
+ struct stk_app_agent *agent = stk->app_agent;
+
+ if (agent == NULL) {
+ cb(stk, STK_AGENT_RESULT_TERMINATE, NULL);
+
+ return;
+ }
+
+ if (agent->cmd_cb)
+ app_agent_request_cancel(stk);
+
+ agent->cmd_send = send;
+ agent->cmd_cb = cb;
+
+ app_agent_request_send(stk, agent);
+
+ /* Use the timeout value specified in the command, if any. */
+ if (timeout == 0)
+ timeout = stk->timeout * 1000;
+
+ if (timeout < 0)
+ return;
+
+ agent->cmd_timeout = g_timeout_add(timeout, app_agent_request_timeout,
+ stk);
+}
+
+static gboolean app_agent_send_menu(gpointer user_data)
+{
+ struct ofono_stk *stk = user_data;
+
+ stk->app_agent->send_menu_source = 0;
+
+ /* TODO */
+
+ return FALSE;
+}
+
+static void app_agent_request_cancel_no_agent(struct ofono_stk *stk,
+ struct stk_app_agent *agent)
+{
+ agent->cmd_cb(stk, STK_AGENT_RESULT_TERMINATE, NULL);
+
+ app_agent_request_end(agent);
+}
+
+static void app_agent_remove(struct ofono_stk *stk, struct stk_app_agent *agent)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+
+ if (agent->watch) {
+ DBusMessage *message;
+
+ g_dbus_remove_watch(conn, agent->watch);
+ agent->watch = 0;
+
+ if (agent->cmd_cb) {
+ app_agent_request_cancel_no_agent(stk, agent);
+
+ app_agent_request_send_cancel(agent);
+ }
+
+ message = dbus_message_new_method_call(agent->bus, agent->path,
+ OFONO_SIM_APP_INTERFACE,
+ "Release");
+ if (message) {
+ dbus_message_set_no_reply(message, TRUE);
+
+ g_dbus_send_message(conn, message);
+ }
+ } else {
+ if (agent->cmd_cb)
+ app_agent_request_cancel_no_agent(stk, agent);
+ }
+
+ if (agent->send_menu_source)
+ g_source_remove(agent->send_menu_source);
+
+ g_free(agent->path);
+ g_free(agent->bus);
+ g_free(agent);
+}
+
+static void app_agent_disconnect_cb(DBusConnection *conn, void *user_data)
+{
+ struct ofono_stk *stk = user_data;
+
+ ofono_debug("Agent exited without calling Unregister");
+
+ stk->app_agent->watch = 0;
+
+ app_agent_remove(stk, stk->app_agent);
+ stk->app_agent = NULL;
+}
+
+static struct stk_app_agent *app_agent_create(struct ofono_stk *stk,
+ const char *path,
+ const char *sender)
+{
+ struct stk_app_agent *agent = g_try_new0(struct stk_app_agent, 1);
+ DBusConnection *conn = ofono_dbus_get_connection();
+
+ if (!agent)
+ return NULL;
+
+ agent->path = g_strdup(path);
+ agent->bus = g_strdup(sender);
+
+ agent->watch = g_dbus_add_disconnect_watch(conn, sender,
+ app_agent_disconnect_cb,
+ stk, NULL);
+
+ agent->send_menu_source = g_timeout_add(0, app_agent_send_menu, stk);
+
+ return agent;
+}
+
+static DBusMessage *stk_register_agent(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_stk *stk = data;
+ const char *agent_path;
+
+ if (stk->app_agent)
+ return __ofono_error_busy(msg);
+
+ if (dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return __ofono_error_invalid_args(msg);
+
+ stk->app_agent = app_agent_create(stk, agent_path,
+ dbus_message_get_sender(msg));
+ if (!stk->app_agent)
+ return __ofono_error_failed(msg);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *stk_unregister_agent(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_stk *stk = data;
+ const char *agent_path;
+ const char *agent_bus = dbus_message_get_sender(msg);
+
+ if (dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return __ofono_error_invalid_args(msg);
+
+ if (!stk->app_agent)
+ return __ofono_error_failed(msg);
+
+ if (strcmp(stk->app_agent->path, agent_path))
+ return __ofono_error_failed(msg);
+ if (strcmp(stk->app_agent->bus, agent_bus))
+ return __ofono_error_failed(msg);
+
+ app_agent_remove(stk, stk->app_agent);
+ stk->app_agent = NULL;
+
+ return dbus_message_new_method_return(msg);
+}
+
static GDBusMethodTable stk_methods[] = {
{ "GetProperties", "", "a{sv}",stk_get_properties },
+ { "RegisterSimAppAgent", "o", "", stk_register_agent },
+ { "UnregisterSimAppAgent", "o", "", stk_unregister_agent
},
{ }
};
@@ -687,6 +1027,11 @@ static void stk_unregister(struct ofono_atom *atom)
struct ofono_modem *modem = __ofono_atom_get_modem(atom);
const char *path = __ofono_atom_get_path(atom);
+ if (stk->app_agent) {
+ app_agent_remove(stk, stk->app_agent);
+ stk->app_agent = NULL;
+ }
+
if (stk->pending_cmd) {
stk_command_free(stk->pending_cmd);
stk->pending_cmd = NULL;
@@ -778,6 +1123,7 @@ void ofono_stk_register(struct ofono_stk *stk)
__ofono_atom_register(stk->atom, stk_unregister);
+ stk->timeout = 600; /* 10 minutes */
stk->envelope_q = g_queue_new();
}
--
1.7.1.86.g0e460.dirty