diff --git a/src/Makefile.am b/src/Makefile.am index 38c97bd7..28dcc774 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,7 +12,7 @@ ofonod_SOURCES = main.c ofono.h log.c plugin.c \ manager.c dbus-gsm.h dbus-gsm.c util.h util.c \ network.c voicecall.c ussd.h ussd.c \ call-settings.c call-waiting.c call-forwarding.c call-meter.c \ - smsutil.h smsutil.c cssn.h cssn.c call-barring.c + smsutil.h smsutil.c cssn.h cssn.c call-barring.c sim.h sim.c ofonod_LDADD = $(top_builddir)/plugins/libbuiltin.la \ $(top_builddir)/drivers/libbuiltin.la \ diff --git a/src/driver.h b/src/driver.h index c329f563..3bc36bc7 100644 --- a/src/driver.h +++ b/src/driver.h @@ -94,6 +94,11 @@ struct ofono_cw_condition { int cls; }; +struct ofono_own_number { + char phone_number[OFONO_MAX_PHONE_NUMBER_LENGTH + 1]; + int number_type; +}; + /* Notification functions, the integer values here should map to * values obtained from the modem. The enumerations are the same * as the values for the fields found in 3GPP TS 27.007 @@ -153,6 +158,19 @@ typedef void (*ofono_call_meter_puct_query_cb_t)(const struct ofono_error *error typedef void (*ofono_call_barring_cb_t)(const struct ofono_error *error, int status, void *data); +typedef void (*ofono_sim_file_len_cb_t)(const struct ofono_error *error, + int length, void *data); + +typedef void (*ofono_sim_read_cb_t)(const struct ofono_error *error, + const unsigned char *sdata, int length, + void *data); + +typedef void (*ofono_imsi_cb_t)(const struct ofono_error *error, + const char *imsi, void *data); + +typedef void (*ofono_numbers_cb_t)(const struct ofono_error *error, + GSList *numbers, void *data); + struct ofono_modem_attribute_ops { void (*query_manufacturer)(struct ofono_modem *modem, ofono_modem_attribute_query_cb_t cb, void *data); @@ -347,3 +365,18 @@ struct ofono_call_barring_ops { int ofono_call_barring_register(struct ofono_modem *modem, struct ofono_call_barring_ops *ops); void ofono_call_barring_unregister(struct ofono_modem *modem); + +struct ofono_sim_ops { + void (*read_file_len)(struct ofono_modem *modem, int fileid, + ofono_sim_file_len_cb_t cb, void *data); + void (*read_file)(struct ofono_modem *modem, int fileid, int start, + int length, ofono_sim_read_cb_t cb, void *data); + void (*read_imsi)(struct ofono_modem *modem, + ofono_imsi_cb_t cb, void *data); + void (*read_own_numbers)(struct ofono_modem *modem, + ofono_numbers_cb_t cb, void *data); +}; + +int ofono_sim_manager_register(struct ofono_modem *modem, + struct ofono_sim_ops *ops); +void ofono_sim_manager_unregister(struct ofono_modem *modem); diff --git a/src/modem.c b/src/modem.c index c6946393..a1233ca7 100644 --- a/src/modem.c +++ b/src/modem.c @@ -36,6 +36,7 @@ #include "modem.h" #include "driver.h" #include "cssn.h" +#include "sim.h" #define MODEM_INTERFACE "org.ofono.Modem" @@ -412,6 +413,7 @@ struct ofono_modem *modem_create(int id, struct ofono_modem_attribute_ops *ops) return NULL; } + ofono_sim_manager_init(modem); ofono_cssn_init(modem); modem->modem_info->flags |= MODEM_FLAG_INITIALIZING_ATTRS; @@ -429,6 +431,7 @@ void modem_remove(struct ofono_modem *modem) ofono_debug("Removing modem: %s", modem->path); ofono_cssn_exit(modem); + ofono_sim_manager_exit(modem); g_dbus_unregister_interface(conn, path, MODEM_INTERFACE); diff --git a/src/modem.h b/src/modem.h index 9792e5ec..255bb722 100644 --- a/src/modem.h +++ b/src/modem.h @@ -39,6 +39,7 @@ struct ofono_modem { struct call_meter_data *call_meter; struct call_barring_data *call_barring; struct cssn_data *cssn; + struct sim_manager_data *sim_manager; }; struct ofono_modem *modem_create(int id, struct ofono_modem_attribute_ops *ops); diff --git a/src/network.c b/src/network.c index 2ace4942..91d3681b 100644 --- a/src/network.c +++ b/src/network.c @@ -36,12 +36,15 @@ #include "modem.h" #include "driver.h" #include "common.h" +#include "sim.h" #define NETWORK_REGISTRATION_INTERFACE "org.ofono.NetworkRegistration" #define NETWORK_OPERATOR_INTERFACE "org.ofono.NetworkOperator" #define NETWORK_REGISTRATION_FLAG_REQUESTING_OPLIST 0x1 #define NETWORK_REGISTRATION_FLAG_PENDING 0x2 +#define NETWORK_REGISTRATION_FLAG_HOME_SHOW_PLMN 0x4 +#define NETWORK_REGISTRATION_FLAG_ROAMING_SHOW_SPN 0x8 #define AUTO_REGISTER 1 @@ -59,6 +62,8 @@ struct network_registration_data { int flags; DBusMessage *pending; int signal_strength; + char display_name[OFONO_MAX_OPERATOR_NAME_LENGTH]; + char *spname; }; static void operator_list_callback(const struct ofono_error *error, int total, @@ -322,6 +327,45 @@ static void set_network_operator_technology(struct ofono_modem *modem, &tech_str); } +static char *get_operator_display_name(struct ofono_modem *modem) +{ + struct network_registration_data *netreg = modem->network_registration; + const char *plmn; + char *name = netreg->display_name; + int len = sizeof(netreg->display_name); + + /* The name displayed to user depends on whether we're in a home + * PLMN or roaming and on configuration bits from the SIM, all + * together there are four cases to consider. */ + + if (!netreg->current_operator) { + g_strlcpy(name, "", len); + return name; + } + + if (!netreg->spname) + g_strlcpy(name, netreg->current_operator->name, len); + + plmn = netreg->current_operator->name; + + if (netreg->status == NETWORK_REGISTRATION_STATUS_REGISTERED) + if (netreg->flags & NETWORK_REGISTRATION_FLAG_HOME_SHOW_PLMN) + /* Case 1 */ + snprintf(name, len, "%s (%s)", netreg->spname, plmn); + else + /* Case 2 */ + snprintf(name, len, "%s", netreg->spname); + else + if (netreg->flags & NETWORK_REGISTRATION_FLAG_ROAMING_SHOW_SPN) + /* Case 3 */ + snprintf(name, len, "%s (%s)", netreg->spname, plmn); + else + /* Case 4 */ + snprintf(name, len, "%s", plmn); + + return name; +} + static void set_network_operator_name(struct ofono_modem *modem, struct ofono_network_operator *op, const char *name) @@ -329,6 +373,7 @@ static void set_network_operator_name(struct ofono_modem *modem, struct network_registration_data *netreg = modem->network_registration; DBusConnection *conn = dbus_gsm_connection(); const char *path; + const char *operator; if (!strncmp(op->name, name, OFONO_MAX_OPERATOR_NAME_LENGTH)) return; @@ -343,11 +388,13 @@ static void set_network_operator_name(struct ofono_modem *modem, "Name", DBUS_TYPE_STRING, &name); - if (op == netreg->current_operator) + if (op == netreg->current_operator) { + operator = get_operator_display_name(modem); dbus_gsm_signal_property_changed(conn, modem->path, NETWORK_REGISTRATION_INTERFACE, "Operator", DBUS_TYPE_STRING, - &name); + &operator); + } } static DBusMessage *network_operator_get_properties(DBusConnection *conn, @@ -506,6 +553,9 @@ static void network_registration_destroy(gpointer userdata) g_slist_free(data->operator_list); + if (data->spname) + g_free(data->spname); + g_free(data); modem->network_registration = 0; @@ -521,8 +571,7 @@ static DBusMessage *network_get_properties(DBusConnection *conn, DBusMessageIter dict; const char *status = registration_status_to_string(netreg->status); - const char *operator = - netreg->current_operator ? netreg->current_operator->name : ""; + const char *operator; char **network_operators; @@ -558,6 +607,7 @@ static DBusMessage *network_get_properties(DBusConnection *conn, &technology); } + operator = get_operator_display_name(modem); dbus_gsm_dict_append(&dict, "Operator", DBUS_TYPE_STRING, &operator); network_operator_populate_registered(modem, &network_operators); @@ -934,9 +984,7 @@ static void current_operator_callback(const struct ofono_error *error, netreg->current_operator = NULL; } - operator = - netreg->current_operator ? netreg->current_operator->name : ""; - + operator = get_operator_display_name(modem); dbus_gsm_signal_property_changed(conn, modem->path, NETWORK_REGISTRATION_INTERFACE, "Operator", DBUS_TYPE_STRING, @@ -1024,6 +1072,34 @@ static void signal_strength_callback(const struct ofono_error *error, ofono_signal_strength_notify(modem, strength); } +static void ofono_update_spn(struct ofono_modem *modem, const char *spn, + int home_plmn_dpy, int roaming_spn_dpy) +{ + struct network_registration_data *netreg = modem->network_registration; + DBusConnection *conn = dbus_gsm_connection(); + const char *operator; + + if (!netreg) + return; + if (!strlen(spn)) + return; + + if (netreg->spname) + g_free(netreg->spname); + netreg->spname = g_strdup(spn); + + if (home_plmn_dpy) + netreg->flags |= NETWORK_REGISTRATION_FLAG_HOME_SHOW_PLMN; + if (roaming_spn_dpy) + netreg->flags |= NETWORK_REGISTRATION_FLAG_ROAMING_SHOW_SPN; + + operator = get_operator_display_name(modem); + dbus_gsm_signal_property_changed(conn, modem->path, + NETWORK_REGISTRATION_INTERFACE, + "Operator", DBUS_TYPE_STRING, + &operator); +} + int ofono_network_registration_register(struct ofono_modem *modem, struct ofono_network_registration_ops *ops) { @@ -1041,6 +1117,8 @@ int ofono_network_registration_register(struct ofono_modem *modem, initialize_network_registration(modem); + ofono_spn_update_notify_register(modem, ofono_update_spn); + if (ops->registration_status) ops->registration_status(modem, init_registration_status, modem); @@ -1052,6 +1130,8 @@ void ofono_network_registration_unregister(struct ofono_modem *modem) { DBusConnection *conn = dbus_gsm_connection(); + ofono_spn_update_notify_unregister(modem, ofono_update_spn); + g_dbus_unregister_interface(conn, modem->path, NETWORK_REGISTRATION_INTERFACE); modem_remove_interface(modem, NETWORK_REGISTRATION_INTERFACE); diff --git a/src/sim.c b/src/sim.c new file mode 100644 index 00000000..0fa13143 --- /dev/null +++ b/src/sim.c @@ -0,0 +1,353 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2008-2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#include + +#include "ofono.h" + +#include "dbus-gsm.h" +#include "modem.h" +#include "driver.h" +#include "common.h" +#include "util.h" +#include "sim.h" + +#define SIM_MANAGER_INTERFACE "org.ofono.SimManager" + +#define SIM_FLAG_PENDING 0x1 + +struct sim_manager_data { + struct ofono_sim_ops *ops; + int flags; + DBusMessage *pending; + char *imsi; + char **numbers; + char *spn; + int dcbyte; + + GSList *update_spn_notify; +}; + +static struct sim_manager_data *sim_manager_create() +{ + return g_try_new0(struct sim_manager_data, 1); +} + +static void sim_manager_destroy(gpointer userdata) +{ + struct ofono_modem *modem = userdata; + struct sim_manager_data *data = modem->sim_manager; + + if (data->imsi) { + g_free(data->imsi); + data->imsi = NULL; + } + if (data->numbers) { + dbus_gsm_free_string_array(data->numbers); + data->numbers = NULL; + } + if (data->spn) { + g_free(data->spn); + data->spn = NULL; + } +} + +static DBusMessage *sim_get_properties(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct ofono_modem *modem = data; + struct sim_manager_data *sim = modem->sim_manager; + DBusMessage *reply; + DBusMessageIter iter; + DBusMessageIter dict; + + 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, + PROPERTIES_ARRAY_SIGNATURE, + &dict); + + dbus_gsm_dict_append(&dict, "IMSI", + DBUS_TYPE_STRING, &sim->imsi); + dbus_gsm_dict_append(&dict, "ServiceProvider", + DBUS_TYPE_STRING, &sim->spn); + dbus_gsm_dict_append_array(&dict, "OwnNumbers", + DBUS_TYPE_STRING, &sim->numbers); + + dbus_message_iter_close_container(&iter, &dict); + + return reply; +} + +static GDBusMethodTable sim_manager_methods[] = { + { "GetProperties", "", "a{sv}", sim_get_properties }, + { } +}; + +static GDBusSignalTable sim_manager_signals[] = { { } }; + +enum sim_fileids { + SIM_EFSPN_FILEID = 0x6f46, +}; + +#define SIM_EFSPN_DC_HOME_PLMN_BIT 0x1 +#define SIM_EFSPN_DC_ROAMING_SPN_BIT 0x2 + +static void sim_spn_notify(struct ofono_modem *modem, update_spn_cb cb) +{ + struct sim_manager_data *sim = modem->sim_manager; + + cb(modem, sim->spn, + sim->dcbyte & SIM_EFSPN_DC_HOME_PLMN_BIT, + !(sim->dcbyte & SIM_EFSPN_DC_ROAMING_SPN_BIT)); +} + +static void sim_spn_read_cb(const struct ofono_error *error, + const unsigned char *sdata, int length, void *data) +{ + struct ofono_modem *modem = data; + struct sim_manager_data *sim = modem->sim_manager; + unsigned char *endp; + GSList *l; + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR || length <= 1) + return; + + sim->dcbyte = sdata[0]; + sdata++; + length--; + + /* Successfully read the SPN from the SIM DB */ + endp = memchr(sdata, 0xff, length); + if (endp) + length = endp - sdata; + + /* + * The specification has this to say about the encoding of SPN: + * Coding: + * + * the string shall use: + * + * - either the SMS default 7-bit coded alphabet as defined in + * TS 23.038 [5] with bit 8 set to 0. The string shall be left + * justified. Unused bytes shall be set to 'FF'. + * + * - or one of the UCS2 code options defined in the annex of TS + * 31.101 [11]. + * + * Assume the first option. + */ + sim->spn = convert_gsm_to_utf8(sdata, length, NULL, NULL, 0xff); + + for (l = sim->update_spn_notify; l; l = l->next) + sim_spn_notify(modem, l->data); +} + +static void sim_spn_len_cb(const struct ofono_error *error, + int length, void *data) +{ + struct ofono_modem *modem = data; + struct sim_manager_data *sim = modem->sim_manager; + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR || length <= 1) + return; + + sim->ops->read_file(modem, SIM_EFSPN_FILEID, 0, length, + sim_spn_read_cb, modem); +} + +static gboolean sim_retrieve_spn(void *user_data) +{ + struct ofono_modem *modem = user_data; + struct sim_manager_data *sim = modem->sim_manager; + + sim->ops->read_file_len(modem, SIM_EFSPN_FILEID, + sim_spn_len_cb, modem); + + return FALSE; +} + +static void sim_imsi_cb(const struct ofono_error *error, const char *imsi, + void *data) +{ + struct ofono_modem *modem = data; + struct sim_manager_data *sim = modem->sim_manager; + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR) + return; + + sim->imsi = g_strdup(imsi); +} + +static gboolean sim_retrieve_imsi(void *user_data) +{ + struct ofono_modem *modem = user_data; + struct sim_manager_data *sim = modem->sim_manager; + + sim->ops->read_imsi(modem, sim_imsi_cb, modem); + + return FALSE; +} + +static void sim_own_number_cb(const struct ofono_error *error, GSList *numbers, + void *data) +{ + struct ofono_modem *modem = data; + struct sim_manager_data *sim = modem->sim_manager; + GSList *l; + struct ofono_own_number *msisdn; + char **number_str; + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR) + return; + + sim->numbers = g_try_new0(char *, g_slist_length(numbers) + 1); + number_str = sim->numbers; + + for (l = numbers; l; l = l->next) { + msisdn = l->data; + *number_str++ = g_strdup(phone_number_to_string( + msisdn->phone_number, + msisdn->number_type)); + } +} + +static gboolean sim_retrieve_own_number(void *user_data) +{ + struct ofono_modem *modem = user_data; + struct sim_manager_data *sim = modem->sim_manager; + + sim->ops->read_own_numbers(modem, sim_own_number_cb, modem); + + return FALSE; +} + +static void initialize_sim_manager(struct ofono_modem *modem) +{ + DBusConnection *conn = dbus_gsm_connection(); + + if (!g_dbus_register_interface(conn, modem->path, + SIM_MANAGER_INTERFACE, + sim_manager_methods, + sim_manager_signals, + NULL, modem, + sim_manager_destroy)) { + ofono_error("Could not register SIMManager interface"); + sim_manager_destroy(modem); + + return; + } + + ofono_debug("SIMManager interface for modem: %s created", + modem->path); + + modem_add_interface(modem, SIM_MANAGER_INTERFACE); + + if (modem->sim_manager->ops->read_file) + g_timeout_add(0, sim_retrieve_spn, modem); + + if (modem->sim_manager->ops->read_imsi) + g_timeout_add(0, sim_retrieve_imsi, modem); + + if (modem->sim_manager->ops->read_own_numbers) + g_timeout_add(0, sim_retrieve_own_number, modem); +} + +int ofono_sim_manager_register(struct ofono_modem *modem, + struct ofono_sim_ops *ops) +{ + if (modem == NULL) + return -1; + if (modem->sim_manager == NULL) + return -1; + + if (ops == NULL) + return -1; + + modem->sim_manager->ops = ops; + + initialize_sim_manager(modem); + + return 0; +} + +void ofono_sim_manager_unregister(struct ofono_modem *modem) +{ + DBusConnection *conn = dbus_gsm_connection(); + + g_dbus_unregister_interface(conn, modem->path, + SIM_MANAGER_INTERFACE); + modem_remove_interface(modem, SIM_MANAGER_INTERFACE); +} + +void ofono_sim_manager_init(struct ofono_modem *modem) +{ + modem->sim_manager = sim_manager_create(); +} + +void ofono_sim_manager_exit(struct ofono_modem *modem) +{ + if (modem->sim_manager == NULL) + return; + + g_free(modem->sim_manager); + + modem->sim_manager = 0; +} + +int ofono_spn_update_notify_register(struct ofono_modem *modem, + update_spn_cb cb) +{ + if (modem->sim_manager == NULL) + return -1; + + modem->sim_manager->update_spn_notify = + g_slist_append(modem->sim_manager->update_spn_notify, cb); + + if (modem->sim_manager->spn) + sim_spn_notify(modem, cb); + + return 0; +} + +int ofono_spn_update_notify_unregister(struct ofono_modem *modem, + update_spn_cb cb) +{ + if (modem->sim_manager == NULL) + return -1; + + modem->sim_manager->update_spn_notify = + g_slist_remove(modem->sim_manager->update_spn_notify, cb); + return 0; +} diff --git a/src/sim.h b/src/sim.h new file mode 100644 index 00000000..bf44681e --- /dev/null +++ b/src/sim.h @@ -0,0 +1,35 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2008-2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __SIM_H__ +#define __SIM_H__ + +typedef void (*update_spn_cb)(struct ofono_modem *modem, const char *spn, + int home_plmn_dpy, int roaming_spn_dpy); + +void ofono_sim_manager_init(struct ofono_modem *modem); +void ofono_sim_manager_exit(struct ofono_modem *modem); +int ofono_spn_update_notify_register(struct ofono_modem *modem, + update_spn_cb cb); +int ofono_spn_update_notify_unregister(struct ofono_modem *modem, + update_spn_cb cb); + +#endif