---
include/dbus.h | 1 +
src/stk.c | 585 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 585 insertions(+), 1 deletions(-)
diff --git a/include/dbus.h b/include/dbus.h
index d988760..6b03dd7 100644
--- a/include/dbus.h
+++ b/include/dbus.h
@@ -49,6 +49,7 @@ extern "C" {
#define OFONO_VOICECALL_MANAGER_INTERFACE "org.ofono.VoiceCallManager"
#define OFONO_DATA_CONNECTION_MANAGER_INTERFACE
"org.ofono.DataConnectionManager"
#define OFONO_DATA_CONTEXT_INTERFACE "org.ofono.PrimaryDataContext"
+#define OFONO_SIM_APP_INTERFACE OFONO_SERVICE ".SimApplication"
/* Essentially a{sv} */
#define OFONO_PROPERTIES_ARRAY_SIGNATURE DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \
diff --git a/src/stk.c b/src/stk.c
index e3f6253..3959dc7 100644
--- a/src/stk.c
+++ b/src/stk.c
@@ -34,6 +34,7 @@
#include "ofono.h"
+#include "common.h"
#include "smsutil.h"
#include "stkutil.h"
@@ -50,12 +51,29 @@ typedef gboolean (*command_handler_t)(const struct
stk_command *cmd,
struct stk_response *rsp,
struct ofono_stk *stk);
+struct stk_menu {
+ char *title;
+ GSList *items;
+ struct stk_items_next_action_indicator next_action;
+ int default_item;
+ struct stk_text_attribute title_attr;
+ struct stk_item_text_attribute_list item_attrs;
+ gboolean soft_key;
+ gboolean has_help;
+};
+
struct ofono_stk {
const struct ofono_stk_driver *driver;
void *driver_data;
struct ofono_atom *atom;
command_handler_t handlers[STK_COMMAND_TYPE_END_SESSION + 1];
struct stk_command *pending_cmd;
+ DBusMessage *pending;
+ gint cmd_timeout;
+
+ struct stk_menu *default_menu;
+ struct stk_menu *current_menu; /* For Select Item menus */
+ int timeout;
};
static void stk_respond(struct ofono_stk *stk,
@@ -148,7 +166,8 @@ static void stk_command_cb(const struct ofono_error *error,
stk->pending_cmd = NULL;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
- ofono_error("TERMINAL RESPONSE to a UICC command failed");
+ ofono_error("TERMINAL RESPONSE to a UICC command failed: %s",
+ telephony_error_to_str(error));
/* "The ME may retry to deliver the same Cell Broadcast
* page." */
return;
@@ -229,6 +248,469 @@ static gboolean stk_command_handler_register(struct
ofono_stk *stk,
return TRUE;
}
+static struct stk_menu *stk_menu_create(const char *title,
+ const struct stk_text_attribute *title_attr, GSList *items,
+ const struct stk_item_text_attribute_list *item_attrs,
+ const struct stk_items_next_action_indicator *next_action,
+ int default_id, gboolean soft_key, gboolean has_help)
+{
+ struct stk_menu *ret = g_new(struct stk_menu, 1);
+ GSList *l;
+ int i;
+
+ ret->title = g_strdup(title);
+ ret->items = g_slist_copy((GSList *) items);
+ ret->default_item = -1;
+ memcpy(&ret->next_action, next_action, sizeof(ret->next_action));
+ memcpy(&ret->title_attr, title_attr, sizeof(ret->title_attr));
+ memcpy(&ret->item_attrs, item_attrs, sizeof(ret->item_attrs));
+ ret->soft_key = soft_key;
+ ret->has_help = has_help;
+
+ for (l = ret->items, i = 0; l; l = l->next, i++) {
+ struct stk_item *j = g_memdup(l->data, sizeof(*j));
+
+ j->text = g_strdup(j->text);
+ l->data = j;
+
+ if (j->id == default_id)
+ ret->default_item = i;
+ }
+
+ return ret;
+}
+
+static void stk_menu_free(struct stk_menu *menu)
+{
+ GSList *l;
+
+ for (l = menu->items; l; l = l->next) {
+ struct stk_item *i = l->data;
+
+ g_free(i->text);
+ g_free(i);
+ }
+
+ g_slist_free(menu->items);
+ g_free(menu->title);
+ g_free(menu);
+}
+
+static char **stk_menu_items_strlist(struct stk_menu *menu)
+{
+ char **items = g_new0(char *, g_slist_length(menu->items) + 1);
+ int i;
+ GSList *l;
+
+ for (i = 0, l = menu->items; l; l = l->next, i++) {
+ struct stk_item *item = l->data;
+ items[i] = item->text;
+ }
+
+ return items;
+}
+
+static void emit_menu_changed(struct ofono_stk *stk)
+{
+ static struct stk_menu no_menu = {
+ .title = "",
+ .items = NULL,
+ .has_help = FALSE,
+ .default_item = -1,
+ };
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = __ofono_atom_get_path(stk->atom);
+ char **items;
+ struct stk_menu *menu = &no_menu;
+ dbus_uint16_t timeout = stk->timeout;
+ dbus_bool_t boolval;
+ uint8_t byteval;
+
+ if (stk->default_menu)
+ menu = stk->default_menu;
+ if (stk->current_menu)
+ menu = stk->current_menu;
+
+ if (stk->cmd_timeout && stk->current_menu)
+ ofono_dbus_signal_property_changed(conn, path,
+ OFONO_SIM_APP_INTERFACE,
+ "Timeout",
+ DBUS_TYPE_UINT16, &timeout);
+
+ items = stk_menu_items_strlist(menu);
+ ofono_dbus_signal_array_property_changed(conn, path,
+ OFONO_SIM_APP_INTERFACE,
+ "MenuItems",
+ DBUS_TYPE_STRING, &items);
+ g_free(items);
+
+ ofono_dbus_signal_property_changed(conn, path,
+ OFONO_SIM_APP_INTERFACE,
+ "MenuTitle",
+ DBUS_TYPE_STRING, &menu->title);
+
+ boolval = menu->has_help;
+ ofono_dbus_signal_property_changed(conn, path,
+ OFONO_SIM_APP_INTERFACE,
+ "HelpAvailable",
+ DBUS_TYPE_BOOLEAN, &boolval);
+
+ boolval = stk->current_menu ? FALSE : TRUE;
+ ofono_dbus_signal_property_changed(conn, path,
+ OFONO_SIM_APP_INTERFACE,
+ "MainMenu",
+ DBUS_TYPE_BOOLEAN, &boolval);
+
+ if (menu->default_item >= 0) {
+ byteval = menu->default_item;
+ ofono_dbus_signal_property_changed(conn, path,
+ OFONO_SIM_APP_INTERFACE,
+ "DefaultSelection",
+ DBUS_TYPE_BYTE, &byteval);
+ }
+}
+
+static gboolean select_item_timeout(gpointer user_data)
+{
+ struct ofono_stk *stk = user_data;
+ struct stk_response rsp;
+
+ stk->cmd_timeout = 0;
+
+ if (stk->pending)
+ /*
+ * A command is currently executing, do nothing. The
+ * command's callback will take care of freeing the
+ * menu.
+ */
+ return FALSE;
+
+ ofono_debug("Select Item command timed out");
+
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.result.type = STK_RESULT_TYPE_NO_RESPONSE;
+
+ stk_respond(stk, &rsp, stk_command_cb);
+
+ stk_menu_free(stk->current_menu);
+ stk->current_menu = NULL;
+
+ emit_menu_changed(stk);
+
+ return FALSE;
+}
+
+static DBusMessage *stk_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_stk *stk = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ char **items;
+ struct stk_menu *menu = stk->default_menu;
+ dbus_uint16_t timeout = stk->timeout;
+ dbus_bool_t boolval;
+ uint8_t byteval;
+
+ if (stk->current_menu)
+ menu = stk->current_menu;
+
+ 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);
+
+ if (!menu)
+ goto done;
+
+ if (stk->cmd_timeout && stk->current_menu)
+ ofono_dbus_dict_append(&dict, "Timeout",
+ DBUS_TYPE_UINT16, &timeout);
+
+ items = stk_menu_items_strlist(menu);
+ ofono_dbus_dict_append_array(&dict, "MenuItems",
+ DBUS_TYPE_STRING, &items);
+ g_free(items);
+
+ ofono_dbus_dict_append(&dict, "MenuTitle",
+ DBUS_TYPE_STRING, &menu->title);
+
+ boolval = menu->has_help;
+ ofono_dbus_dict_append(&dict, "HelpAvailable",
+ DBUS_TYPE_BOOLEAN, &boolval);
+
+ boolval = stk->current_menu ? FALSE : TRUE;
+ ofono_dbus_dict_append(&dict, "MainMenu",
+ DBUS_TYPE_BOOLEAN, &boolval);
+
+ if (menu->default_item >= 0) {
+ byteval = menu->default_item;
+ ofono_dbus_dict_append(&dict, "DefaultSelection",
+ DBUS_TYPE_BYTE, &byteval);
+ }
+
+done:
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *stk_set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_stk *stk = data;
+ const char *path = __ofono_atom_get_path(stk->atom);
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ const char *property;
+ dbus_uint16_t timeout;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return __ofono_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ if (!dbus_message_iter_next(&iter))
+ return __ofono_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &var);
+
+ if (stk->cmd_timeout && stk->current_menu &&
+ !strcmp(property, "Timeout")) {
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_UINT16)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&var, &timeout);
+ if (dbus_message_iter_next(&iter))
+ return __ofono_error_invalid_args(msg);
+
+ stk->timeout = timeout;
+
+ g_source_remove(stk->cmd_timeout);
+ stk->cmd_timeout = g_timeout_add_seconds(stk->timeout,
+ select_item_timeout,
+ stk);
+
+ ofono_dbus_signal_property_changed(conn, path,
+ OFONO_SIM_APP_INTERFACE,
+ "Timeout",
+ DBUS_TYPE_UINT16, &timeout);
+
+ return dbus_message_new_method_return(msg);
+ }
+
+ return __ofono_error_invalid_args(msg);
+}
+
+static void select_item_cb(const struct ofono_error *error,
+ struct ofono_stk *stk)
+{
+ DBusMessage *reply;
+
+ if (stk->cmd_timeout) {
+ /*
+ * If the command has not timed out yet, return error and
+ * the user may then retry the selection. Otherwise assume
+ * the menu is no longer valid.
+ */
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+ goto out;
+
+ g_source_remove(stk->cmd_timeout);
+ }
+
+ stk_command_cb(error, stk);
+
+ stk_menu_free(stk->current_menu);
+ stk->current_menu = NULL;
+
+ emit_menu_changed(stk);
+
+out:
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ reply = __ofono_error_failed(stk->pending);
+ else
+ reply = dbus_message_new_method_return(stk->pending);
+
+ __ofono_dbus_pending_reply(&stk->pending, reply);
+}
+
+static void select_item_respond(struct ofono_stk *stk, struct stk_item *item,
+ gboolean help)
+{
+ struct stk_response rsp;
+
+ memset(&rsp, 0, sizeof(rsp));
+
+ if (help)
+ rsp.result.type = STK_RESULT_TYPE_HELP_REQUESTED;
+
+ rsp.select_item.item_id = item->id;
+
+ stk_respond(stk, &rsp, select_item_cb);
+}
+
+static void menu_selection_cb(const struct ofono_error *error,
+ const unsigned char *data, int len,
+ struct ofono_stk *stk)
+{
+ DBusMessage *reply;
+
+ if (len)
+ ofono_error("Selecting item returned %i bytes of data", len);
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+ reply = __ofono_error_failed(stk->pending);
+ else
+ reply = dbus_message_new_method_return(stk->pending);
+
+ __ofono_dbus_pending_reply(&stk->pending, reply);
+}
+
+static void send_menu_selection(struct ofono_stk *stk, struct stk_item *item,
+ gboolean help)
+{
+ struct stk_envelope e;
+
+ memset(&e, 0, sizeof(e));
+
+ e.type = STK_ENVELOPE_TYPE_MENU_SELECTION;
+ e.src = STK_DEVICE_IDENTITY_TYPE_KEYPAD,
+ e.menu_selection.item_id = item->id;
+ e.menu_selection.help_request = help;
+
+ stk_send_envelope(stk, &e, menu_selection_cb);
+}
+
+static DBusMessage *stk_menu_select(DBusConnection *conn, DBusMessage *msg,
+ void *data, gboolean help)
+{
+ struct ofono_stk *stk = data;
+ DBusMessageIter iter;
+ uint8_t selection;
+ struct stk_menu *menu;
+ struct stk_item *item;
+
+ if (stk->pending)
+ return __ofono_error_busy(msg);
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return __ofono_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &selection);
+ if (dbus_message_iter_next(&iter))
+ return __ofono_error_invalid_args(msg);
+
+ menu = stk->default_menu;
+ if (stk->current_menu)
+ menu = stk->current_menu;
+
+ if (!menu)
+ return __ofono_error_failed(msg);
+
+ if (help == TRUE && !menu->has_help)
+ return __ofono_error_failed(msg);
+
+ item = g_slist_nth_data(menu->items, selection);
+ if (!item)
+ return __ofono_error_invalid_args(msg);
+
+ stk->pending = dbus_message_ref(msg);
+
+ if (stk->current_menu)
+ select_item_respond(stk, item, help);
+ else
+ send_menu_selection(stk, item, help);
+
+ return NULL;
+}
+
+static DBusMessage *stk_select(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return stk_menu_select(conn, msg, data, FALSE);
+}
+
+static DBusMessage *stk_get_help(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return stk_menu_select(conn, msg, data, TRUE);
+}
+
+static DBusMessage *stk_back_or_terminate(DBusConnection *conn,
+ DBusMessage *msg, void *data,
+ enum stk_result_type type)
+{
+ struct ofono_stk *stk = data;
+ DBusMessageIter iter;
+ struct stk_response rsp;
+
+ if (stk->pending)
+ return __ofono_error_busy(msg);
+
+ if (!stk->current_menu)
+ return __ofono_error_failed(msg);
+
+ if (dbus_message_iter_init(msg, &iter))
+ return __ofono_error_invalid_args(msg);
+
+ stk->pending = dbus_message_ref(msg);
+
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.result.type = type;
+
+ stk_respond(stk, &rsp, select_item_cb);
+
+ return NULL;
+}
+
+static DBusMessage *stk_go_back(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return stk_back_or_terminate(conn, msg, data,
+ STK_RESULT_TYPE_GO_BACK);
+}
+
+static DBusMessage *stk_end_session(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return stk_back_or_terminate(conn, msg, data,
+ STK_RESULT_TYPE_USER_TERMINATED);
+}
+
+static GDBusMethodTable stk_methods[] = {
+ { "GetProperties", "", "a{sv}", stk_get_properties },
+ { "SetProperty", "sv", "", stk_set_property },
+ { "Select", "y", "", stk_select,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "RequestHelp", "y", "", stk_get_help,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "GoBack", "", "", stk_go_back,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "EndSession", "", "", stk_end_session,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable stk_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
static gboolean handle_command_more_time(const struct stk_command *cmd,
struct stk_response *rsp,
struct ofono_stk *stk)
@@ -238,6 +720,59 @@ static gboolean handle_command_more_time(const struct
stk_command *cmd,
return TRUE;
}
+static gboolean handle_command_select_item(const struct stk_command *cmd,
+ struct stk_response *rsp,
+ struct ofono_stk *stk)
+{
+ gboolean soft_key = (cmd->qualifier & (1 << 2)) != 0;
+ gboolean has_help = (cmd->qualifier & (1 << 7)) != 0;
+
+ stk->current_menu = stk_menu_create(cmd->select_item.alpha_id,
+ &cmd->select_item.text_attr,
+ cmd->select_item.items,
+ &cmd->select_item.item_text_attr_list,
+ &cmd->select_item.next_act,
+ cmd->select_item.item_id,
+ soft_key, has_help);
+
+ emit_menu_changed(stk);
+
+ stk->cmd_timeout = g_timeout_add_seconds(stk->timeout,
+ select_item_timeout,
+ stk);
+
+ return FALSE;
+}
+
+static gboolean handle_command_set_up_menu(const struct stk_command *cmd,
+ struct stk_response *rsp,
+ struct ofono_stk *stk)
+{
+ gboolean soft_key = (cmd->qualifier & (1 << 0)) != 0;
+ gboolean has_help = (cmd->qualifier & (1 << 7)) != 0;
+
+ if (stk->default_menu) {
+ stk_menu_free(stk->default_menu);
+ stk->default_menu = NULL;
+ }
+
+ if (cmd->setup_menu.items == NULL)
+ goto out;
+
+ stk->default_menu = stk_menu_create(cmd->setup_menu.alpha_id,
+ &cmd->setup_menu.text_attr,
+ cmd->setup_menu.items,
+ &cmd->setup_menu.item_text_attr_list,
+ &cmd->setup_menu.next_act,
+ 0, soft_key, has_help);
+
+out:
+ if (stk->current_menu == NULL)
+ emit_menu_changed(stk);
+
+ return TRUE;
+}
+
int ofono_stk_driver_register(const struct ofono_stk_driver *d)
{
DBG("driver: %p, name: %s", d, d->name);
@@ -259,6 +794,33 @@ void ofono_stk_driver_unregister(const struct
ofono_stk_driver *d)
static void stk_unregister(struct ofono_atom *atom)
{
+ struct ofono_stk *stk = __ofono_atom_get_data(atom);
+ DBusConnection *conn = ofono_dbus_get_connection();
+ struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+ const char *path = __ofono_atom_get_path(atom);
+
+ if (stk->pending_cmd) {
+ stk_command_free(stk->pending_cmd);
+ stk->pending_cmd = NULL;
+
+ if (stk->cmd_timeout) {
+ g_source_remove(stk->cmd_timeout);
+ stk->cmd_timeout = 0;
+ }
+ }
+
+ if (stk->current_menu) {
+ stk_menu_free(stk->current_menu);
+ stk->current_menu = NULL;
+ }
+
+ if (stk->default_menu) {
+ stk_menu_free(stk->default_menu);
+ stk->default_menu = NULL;
+ }
+
+ ofono_modem_remove_interface(modem, OFONO_SIM_APP_INTERFACE);
+ g_dbus_unregister_interface(conn, path, OFONO_SIM_APP_INTERFACE);
}
static void stk_remove(struct ofono_atom *atom)
@@ -308,14 +870,35 @@ struct ofono_stk *ofono_stk_create(struct ofono_modem
*modem,
break;
}
+ stk->timeout = 20;
+
stk_command_handler_register(stk, STK_COMMAND_TYPE_MORE_TIME,
handle_command_more_time);
+ stk_command_handler_register(stk, STK_COMMAND_TYPE_SELECT_ITEM,
+ handle_command_select_item);
+ stk_command_handler_register(stk, STK_COMMAND_TYPE_SETUP_MENU,
+ handle_command_set_up_menu);
return stk;
}
void ofono_stk_register(struct ofono_stk *stk)
{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ struct ofono_modem *modem = __ofono_atom_get_modem(stk->atom);
+ const char *path = __ofono_atom_get_path(stk->atom);
+
+ if (!g_dbus_register_interface(conn, path, OFONO_SIM_APP_INTERFACE,
+ stk_methods, stk_signals, NULL,
+ stk, NULL)) {
+ ofono_error("Could not create %s interface",
+ OFONO_SIM_APP_INTERFACE);
+
+ return;
+ }
+
+ ofono_modem_add_interface(modem, OFONO_SIM_APP_INTERFACE);
+
__ofono_atom_register(stk->atom, stk_unregister);
}
--
1.7.1.86.g0e460.dirty
_______________________________________________
ofono mailing list
[email protected]
http://lists.ofono.org/listinfo/ofono