Make SubscriberNumbers writable.

This implements writing SubscriberNumbers if the SIM allows this
(optional to the SIM provider).  Because the property is an array
of phone numbers, setting the property needs to write multiple
records on the SIM so it's not atomic and potentially can fail
partially.  In this case we have to report a write error to DBus
client but at the same time file may be changed and we send a
PropertyChanged.
This commit is contained in:
Andrzej Zaborowski 2009-08-07 11:24:27 +02:00 committed by Denis Kenzior
parent d6aae60983
commit 988f0ffbed
1 changed files with 203 additions and 5 deletions

208
src/sim.c
View File

@ -59,6 +59,7 @@
static gboolean sim_op_next(gpointer user_data);
static gboolean sim_op_retrieve_next(gpointer user);
static void sim_own_numbers_update(struct ofono_modem *modem);
struct sim_file_op {
int id;
@ -80,6 +81,15 @@ struct sim_manager_data {
GSList *ready_notify;
gboolean ready;
GQueue *simop_q;
int efmsisdn_length;
};
struct msisdn_set_request {
struct ofono_modem *modem;
int pending;
int failed;
DBusMessage *msg;
};
static char **get_own_numbers(GSList *own_numbers)
@ -172,8 +182,151 @@ static DBusMessage *sim_get_properties(DBusConnection *conn,
return reply;
}
static void msisdn_set_done(struct msisdn_set_request *req)
{
DBusMessage *reply;
if (req->failed)
reply = __ofono_error_failed(req->msg);
else
reply = dbus_message_new_method_return(req->msg);
__ofono_dbus_pending_reply(&req->msg, reply);
/* Re-read the numbers and emit signal if needed */
sim_own_numbers_update(req->modem);
g_free(req);
}
static void msisdn_set_cb(struct ofono_modem *modem, int ok, void *data)
{
struct msisdn_set_request *req = data;
if (!ok)
req->failed++;
req->pending--;
if (!req->pending)
msisdn_set_done(req);
}
static void set_own_numbers(struct ofono_modem *modem,
GSList *new_numbers, DBusMessage *msg)
{
struct sim_manager_data *sim = modem->sim_manager;
struct msisdn_set_request *req;
int record;
unsigned char efmsisdn[255];
struct ofono_phone_number *number;
if (!sim->own_numbers || g_slist_length(new_numbers) !=
g_slist_length(sim->own_numbers)) {
g_slist_foreach(new_numbers, (GFunc) g_free, 0);
g_slist_free(new_numbers);
msg = dbus_message_ref(msg);
__ofono_dbus_pending_reply(&msg,
__ofono_error_invalid_args(msg));
return;
}
req = g_new0(struct msisdn_set_request, 1);
req->modem = modem;
req->msg = dbus_message_ref(msg);
for (record = 1; new_numbers; record++) {
number = new_numbers->data;
new_numbers = g_slist_delete_link(new_numbers, new_numbers);
sim_adn_build(efmsisdn, sim->efmsisdn_length, number);
g_free(number);
if (ofono_sim_write(req->modem, SIM_EFMSISDN_FILEID,
msisdn_set_cb, OFONO_SIM_FILE_STRUCTURE_FIXED,
record++, efmsisdn,
sim->efmsisdn_length, req) == 0)
req->pending++;
else
req->failed++;
}
if (!req->pending)
msisdn_set_done(req);
}
static DBusMessage *sim_set_property(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct ofono_modem *modem = data;
struct sim_manager_data *sim = modem->sim_manager;
DBusMessageIter iter;
DBusMessageIter var;
DBusMessageIter var_elem;
const char *name, *value;
struct ofono_phone_number *own;
GSList *own_numbers = NULL;
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, &name);
if (!strcmp(name, "SubscriberNumbers")) {
if (sim->efmsisdn_length == 0)
return __ofono_error_busy(msg);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
return __ofono_error_invalid_args(msg);
dbus_message_iter_recurse(&iter, &var);
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&var) !=
DBUS_TYPE_STRING)
return __ofono_error_invalid_args(msg);
dbus_message_iter_recurse(&var, &var_elem);
do {
if (dbus_message_iter_get_arg_type(&var_elem) !=
DBUS_TYPE_STRING)
goto error;
dbus_message_iter_get_basic(&var_elem, &value);
if (!valid_phone_number_format(value))
goto error;
own = g_new0(struct ofono_phone_number, 1);
string_to_phone_number(value, own);
own_numbers = g_slist_prepend(own_numbers, own);
} while (dbus_message_iter_next(&var_elem));
set_own_numbers(modem, g_slist_reverse(own_numbers), msg);
return NULL;
}
error:
g_slist_foreach(own_numbers, (GFunc) g_free, 0);
g_slist_free(own_numbers);
return __ofono_error_invalid_args(msg);
}
static GDBusMethodTable sim_manager_methods[] = {
{ "GetProperties", "", "a{sv}", sim_get_properties },
{ "SetProperty", "sv", "", sim_set_property,
G_DBUS_METHOD_FLAG_ASYNC },
{ }
};
@ -182,6 +335,27 @@ static GDBusSignalTable sim_manager_signals[] = {
{ }
};
static gboolean numbers_list_equal(GSList *a, GSList *b)
{
struct ofono_phone_number *num_a, *num_b;
while (a || b) {
if (!a || !b)
return FALSE;
num_a = a->data;
num_b = b->data;
if (!g_str_equal(num_a->number, num_b->number) ||
num_a->type != num_b->type)
return FALSE;
a = a->next;
b = b->next;
}
return TRUE;
}
static void sim_msisdn_read_cb(struct ofono_modem *modem, int ok,
enum ofono_sim_file_structure structure,
@ -192,10 +366,13 @@ static void sim_msisdn_read_cb(struct ofono_modem *modem, int ok,
struct sim_manager_data *sim = modem->sim_manager;
int total;
struct ofono_phone_number ph;
GSList **new_own_numbers = userdata;
if (!ok)
goto check;
sim->efmsisdn_length = record_length;
if (structure != OFONO_SIM_FILE_STRUCTURE_FIXED)
return;
@ -209,19 +386,24 @@ static void sim_msisdn_read_cb(struct ofono_modem *modem, int ok,
own = g_new(struct ofono_phone_number, 1);
memcpy(own, &ph, sizeof(struct ofono_phone_number));
sim->own_numbers = g_slist_prepend(sim->own_numbers, own);
*new_own_numbers = g_slist_prepend(*new_own_numbers, own);
}
if (record != total)
return;
check:
if (sim->own_numbers) {
/* All records retrieved */
if (*new_own_numbers)
*new_own_numbers = g_slist_reverse(*new_own_numbers);
if (!numbers_list_equal(*new_own_numbers, sim->own_numbers)) {
char **own_numbers;
DBusConnection *conn = ofono_dbus_get_connection();
/* All records retrieved */
sim->own_numbers = g_slist_reverse(sim->own_numbers);
g_slist_foreach(sim->own_numbers, (GFunc) g_free, NULL);
g_slist_free(sim->own_numbers);
sim->own_numbers = *new_own_numbers;
own_numbers = get_own_numbers(sim->own_numbers);
@ -231,12 +413,28 @@ check:
DBUS_TYPE_STRING,
&own_numbers);
g_strfreev(own_numbers);
} else {
g_slist_foreach(*new_own_numbers, (GFunc) g_free, NULL);
g_slist_free(*new_own_numbers);
}
g_free(new_own_numbers);
}
static void sim_own_numbers_update(struct ofono_modem *modem)
{
GSList **new_own_numbers = g_new0(GSList *, 1);
if (!new_own_numbers)
return;
ofono_sim_read(modem, SIM_EFMSISDN_FILEID,
sim_msisdn_read_cb, new_own_numbers);
}
static void sim_ready(struct ofono_modem *modem)
{
ofono_sim_read(modem, SIM_EFMSISDN_FILEID, sim_msisdn_read_cb, NULL);
sim_own_numbers_update(modem);
}
static void sim_imsi_cb(const struct ofono_error *error, const char *imsi,