/*
 *
 *  oFono - Open Source Telephony
 *
 *  Copyright (C) 2008-2011  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 <config.h>
#endif

#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

#include <glib.h>
#include <gdbus.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>

#include "ofono.h"

#include "common.h"
#include "util.h"
#include "smsutil.h"
#include "simutil.h"
#include "storage.h"
#include "simfs.h"
#include "stkutil.h"

/*
 * A new session object will be created if a USim/ISim applications are
 * found during app discovery. Any concurrent file/logical access to
 * these applications will share the same session ID.
 */
enum session_state {
	SESSION_STATE_INACTIVE,
	SESSION_STATE_OPENING,
	SESSION_STATE_CLOSING,
	SESSION_STATE_OPEN
};

struct ofono_sim_aid_session {
	struct sim_app_record *record;
	int session_id;
	struct ofono_sim *sim;
	struct ofono_watchlist *watches;
	enum session_state state;
};

struct ofono_sim {
	/* Contents of the SIM file system, in rough initialization order */
	char *iccid;

	char **language_prefs;
	unsigned char *efli;
	unsigned char efli_length;

	enum ofono_sim_password_type pin_type;
	gboolean locked_pins[OFONO_SIM_PASSWORD_SIM_PUK]; /* Number of PINs */

	int pin_retries[OFONO_SIM_PASSWORD_INVALID];

	enum ofono_sim_phase phase;
	unsigned char mnc_length;
	enum ofono_sim_cphs_phase cphs_phase;
	unsigned char cphs_service_table[2];
	unsigned char *efust;
	unsigned char efust_length;
	unsigned char *efest;
	unsigned char efest_length;
	unsigned char *efsst;
	unsigned char efsst_length;

	char *imsi;
	char mcc[OFONO_MAX_MCC_LENGTH + 1];
	char mnc[OFONO_MAX_MNC_LENGTH + 1];

	GSList *own_numbers;
	GSList *new_numbers;
	unsigned char efmsisdn_length;
	unsigned char efmsisdn_records;

	GSList *service_numbers;

	unsigned char *efimg;
	unsigned short efimg_length;

	enum ofono_sim_state state;
	struct ofono_watchlist *state_watches;

	char *spn;
	char *spn_dc;
	struct ofono_watchlist *spn_watches;
	unsigned int ef_spn_watch;
	unsigned int cphs_spn_watch;
	unsigned int cphs_spn_short_watch;

	struct sim_fs *simfs;
	struct sim_fs *simfs_isim;
	struct ofono_sim_context *context;
	struct ofono_sim_context *early_context;
	struct ofono_sim_context *isim_context;

	unsigned char *iidf_image;
	unsigned int *iidf_watch_ids;

	DBusMessage *pending;
	const struct ofono_sim_driver *driver;
	void *driver_data;
	struct ofono_atom *atom;
	unsigned int hfp_watch;

	GSList *aid_sessions;
	GSList *aid_list;
	char *impi;
	bool reading_spn : 1;
	bool language_prefs_update : 1;
	bool fixed_dialing : 1;
	bool barred_dialing : 1;
	bool sdn_ready : 1;
	bool initialized : 1;
	bool wait_initialized : 1;
};

struct msisdn_set_request {
	struct ofono_sim *sim;
	int pending;
	int failed;
	DBusMessage *msg;
};

struct service_number {
	char *id;
	struct ofono_phone_number ph;
};

static const char *const passwd_name[] = {
	[OFONO_SIM_PASSWORD_NONE] = "none",
	[OFONO_SIM_PASSWORD_SIM_PIN] = "pin",
	[OFONO_SIM_PASSWORD_SIM_PUK] = "puk",
	[OFONO_SIM_PASSWORD_PHSIM_PIN] = "phone",
	[OFONO_SIM_PASSWORD_PHFSIM_PIN] = "firstphone",
	[OFONO_SIM_PASSWORD_PHFSIM_PUK] = "firstphonepuk",
	[OFONO_SIM_PASSWORD_SIM_PIN2] = "pin2",
	[OFONO_SIM_PASSWORD_SIM_PUK2] = "puk2",
	[OFONO_SIM_PASSWORD_PHNET_PIN] = "network",
	[OFONO_SIM_PASSWORD_PHNET_PUK] = "networkpuk",
	[OFONO_SIM_PASSWORD_PHNETSUB_PIN] = "netsub",
	[OFONO_SIM_PASSWORD_PHNETSUB_PUK] = "netsubpuk",
	[OFONO_SIM_PASSWORD_PHSP_PIN] = "service",
	[OFONO_SIM_PASSWORD_PHSP_PUK] = "servicepuk",
	[OFONO_SIM_PASSWORD_PHCORP_PIN] = "corp",
	[OFONO_SIM_PASSWORD_PHCORP_PUK] = "corppuk",
};

static void sim_own_numbers_update(struct ofono_sim *sim);

static GSList *g_drivers = NULL;

static const char *sim_passwd_name(enum ofono_sim_password_type type)
{
	return passwd_name[type];
}

static enum ofono_sim_password_type sim_string_to_passwd(const char *name)
{
	int len = sizeof(passwd_name) / sizeof(*passwd_name);
	int i;

	for (i = 0; i < len; i++)
		if (!strcmp(passwd_name[i], name))
			return i;

	return OFONO_SIM_PASSWORD_INVALID;
}

static gboolean password_is_pin(enum ofono_sim_password_type type)
{
	switch (type) {
	case OFONO_SIM_PASSWORD_SIM_PIN:
	case OFONO_SIM_PASSWORD_PHSIM_PIN:
	case OFONO_SIM_PASSWORD_PHFSIM_PIN:
	case OFONO_SIM_PASSWORD_SIM_PIN2:
	case OFONO_SIM_PASSWORD_PHNET_PIN:
	case OFONO_SIM_PASSWORD_PHNETSUB_PIN:
	case OFONO_SIM_PASSWORD_PHSP_PIN:
	case OFONO_SIM_PASSWORD_PHCORP_PIN:
		return TRUE;
	case OFONO_SIM_PASSWORD_SIM_PUK:
	case OFONO_SIM_PASSWORD_PHFSIM_PUK:
	case OFONO_SIM_PASSWORD_SIM_PUK2:
	case OFONO_SIM_PASSWORD_PHNET_PUK:
	case OFONO_SIM_PASSWORD_PHNETSUB_PUK:
	case OFONO_SIM_PASSWORD_PHSP_PUK:
	case OFONO_SIM_PASSWORD_PHCORP_PUK:
	case OFONO_SIM_PASSWORD_INVALID:
	case OFONO_SIM_PASSWORD_NONE:
		return FALSE;
	}

	return FALSE;
}

static enum ofono_sim_password_type puk2pin(enum ofono_sim_password_type type)
{
	switch (type) {
	case OFONO_SIM_PASSWORD_SIM_PUK:
		return OFONO_SIM_PASSWORD_SIM_PIN;
	case OFONO_SIM_PASSWORD_PHFSIM_PUK:
		return OFONO_SIM_PASSWORD_PHFSIM_PIN;
	case OFONO_SIM_PASSWORD_SIM_PUK2:
		return OFONO_SIM_PASSWORD_SIM_PIN2;
	case OFONO_SIM_PASSWORD_PHNET_PUK:
		return OFONO_SIM_PASSWORD_PHNET_PIN;
	case OFONO_SIM_PASSWORD_PHNETSUB_PUK:
		return OFONO_SIM_PASSWORD_PHNETSUB_PIN;
	case OFONO_SIM_PASSWORD_PHSP_PUK:
		return OFONO_SIM_PASSWORD_PHSP_PIN;
	case OFONO_SIM_PASSWORD_PHCORP_PUK:
		return OFONO_SIM_PASSWORD_PHCORP_PIN;
	default:
		return OFONO_SIM_PASSWORD_INVALID;
	}
}

static char **get_own_numbers(GSList *own_numbers)
{
	int nelem = 0;
	GSList *l;
	struct ofono_phone_number *num;
	char **ret;

	if (own_numbers)
		nelem = g_slist_length(own_numbers);

	ret = g_new0(char *, nelem + 1);

	nelem = 0;
	for (l = own_numbers; l; l = l->next) {
		num = l->data;

		ret[nelem++] = g_strdup(phone_number_to_string(num));
	}

	return ret;
}

static char **get_locked_pins(struct ofono_sim *sim)
{
	int i;
	int nelem = 0;
	char **ret;

	for (i = 1; i < OFONO_SIM_PASSWORD_SIM_PUK; i++) {
		if (sim->locked_pins[i] == FALSE)
			continue;

		nelem += 1;
	}

	ret = g_new0(char *, nelem + 1);

	nelem = 0;

	for (i = 1; i < OFONO_SIM_PASSWORD_SIM_PUK; i++) {
		if (sim->locked_pins[i] == FALSE)
			continue;

		ret[nelem] = g_strdup(sim_passwd_name(i));
		nelem += 1;
	}

	return ret;
}

static void get_pin_retries(struct ofono_sim *sim, void ***out_dict,
				unsigned char **out_retries)
{
	int i, nelem;
	void **dict;
	unsigned char *retries;

	for (i = 1, nelem = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) {
		if (sim->pin_retries[i] == -1)
			continue;

		nelem += 1;
	}

	dict = g_new0(void *, nelem * 2 + 1);
	retries = g_new0(unsigned char, nelem);

	for (i = 1, nelem = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) {
		if (sim->pin_retries[i] == -1)
			continue;

		retries[nelem] = sim->pin_retries[i];

		dict[nelem * 2] = (void *) sim_passwd_name(i);
		dict[nelem * 2 + 1] = &retries[nelem];
		nelem += 1;
	}

	*out_dict = dict;
	*out_retries = retries;
}

static char **get_service_numbers(GSList *service_numbers)
{
	int nelem;
	GSList *l;
	struct service_number *num;
	char **ret;

	nelem = g_slist_length(service_numbers) * 2;

	ret = g_new0(char *, nelem + 1);

	nelem = 0;
	for (l = service_numbers; l; l = l->next) {
		num = l->data;

		ret[nelem++] = g_strdup(num->id);
		ret[nelem++] = g_strdup(phone_number_to_string(&num->ph));
	}

	return ret;
}

static void service_number_free(gpointer pointer)
{
	struct service_number *num = pointer;
	g_free(num->id);
	g_free(num);
}

static void call_state_watches(struct ofono_sim *sim)
{
	GSList *l;
	ofono_sim_state_event_cb_t notify;

	for (l = sim->state_watches->items; l; l = l->next) {
		struct ofono_watchlist_item *item = l->data;
		notify = item->notify;

		notify(sim->state, item->notify_data);
	}
}

static DBusMessage *sim_get_properties(DBusConnection *conn,
					DBusMessage *msg, void *data)
{
	struct ofono_sim *sim = data;
	DBusMessage *reply;
	DBusMessageIter iter;
	DBusMessageIter dict;
	char **own_numbers;
	char **service_numbers;
	char **locked_pins;
	const char *pin_name;
	void **pin_retries_dict;
	unsigned char *dbus_retries;
	dbus_bool_t present = sim->state != OFONO_SIM_STATE_NOT_PRESENT;
	dbus_bool_t fdn;
	dbus_bool_t bdn;

	reply = dbus_message_new_method_return(msg);
	if (reply == NULL)
		return NULL;

	dbus_message_iter_init_append(reply, &iter);

	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
					OFONO_PROPERTIES_ARRAY_SIGNATURE,
					&dict);

	ofono_dbus_dict_append(&dict, "Present", DBUS_TYPE_BOOLEAN, &present);

	if (!present)
		goto done;

	if (sim->iccid)
		ofono_dbus_dict_append(&dict, "CardIdentifier",
					DBUS_TYPE_STRING, &sim->iccid);

	if (sim->imsi)
		ofono_dbus_dict_append(&dict, "SubscriberIdentity",
					DBUS_TYPE_STRING, &sim->imsi);

	if (sim->spn)
		ofono_dbus_dict_append(&dict, "ServiceProviderName",
					DBUS_TYPE_STRING, &sim->spn);

	if (sim->impi)
		ofono_dbus_dict_append(&dict, "ImsPrivateIdentity",
					DBUS_TYPE_STRING, &sim->impi);

	fdn = sim->fixed_dialing;
	ofono_dbus_dict_append(&dict, "FixedDialing", DBUS_TYPE_BOOLEAN, &fdn);

	bdn = sim->barred_dialing;
	ofono_dbus_dict_append(&dict, "BarredDialing", DBUS_TYPE_BOOLEAN, &bdn);

	if (sim->mcc[0] != '\0' && sim->mnc[0] != '\0') {
		const char *str;
		str = sim->mcc;
		ofono_dbus_dict_append(&dict, "MobileCountryCode",
					DBUS_TYPE_STRING, &str);

		str = sim->mnc;
		ofono_dbus_dict_append(&dict, "MobileNetworkCode",
					DBUS_TYPE_STRING, &str);
	}

	own_numbers = get_own_numbers(sim->own_numbers);

	ofono_dbus_dict_append_array(&dict, "SubscriberNumbers",
					DBUS_TYPE_STRING, &own_numbers);
	g_strfreev(own_numbers);

	locked_pins = get_locked_pins(sim);
	ofono_dbus_dict_append_array(&dict, "LockedPins",
					DBUS_TYPE_STRING, &locked_pins);
	g_strfreev(locked_pins);

	if (sim->service_numbers && sim->sdn_ready) {
		service_numbers = get_service_numbers(sim->service_numbers);

		ofono_dbus_dict_append_dict(&dict, "ServiceNumbers",
						DBUS_TYPE_STRING,
						&service_numbers);
		g_strfreev(service_numbers);
	}

	if (sim->language_prefs)
		ofono_dbus_dict_append_array(&dict, "PreferredLanguages",
						DBUS_TYPE_STRING,
						&sim->language_prefs);

	pin_name = sim_passwd_name(sim->pin_type);
	ofono_dbus_dict_append(&dict, "PinRequired",
				DBUS_TYPE_STRING,
				(void *) &pin_name);

	get_pin_retries(sim, &pin_retries_dict, &dbus_retries);
	ofono_dbus_dict_append_dict(&dict, "Retries", DBUS_TYPE_BYTE,
							&pin_retries_dict);
	g_free(pin_retries_dict);
	g_free(dbus_retries);

done:
	dbus_message_iter_close_container(&iter, &dict);

	return reply;
}

static void sim_pin_retries_query_cb(const struct ofono_error *error,
					int retries[OFONO_SIM_PASSWORD_INVALID],
					void *data)
{
	struct ofono_sim *sim = data;
	DBusConnection *conn = ofono_dbus_get_connection();
	const char *path = __ofono_atom_get_path(sim->atom);
	void **pin_retries_dict;
	unsigned char *dbus_retries;

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
		ofono_error("Querying remaining pin retries failed");
		return;
	}

	if (!memcmp(retries, sim->pin_retries, sizeof(sim->pin_retries)))
		return;

	memcpy(sim->pin_retries, retries, sizeof(sim->pin_retries));

	get_pin_retries(sim, &pin_retries_dict, &dbus_retries);
	ofono_dbus_signal_dict_property_changed(conn, path,
					OFONO_SIM_MANAGER_INTERFACE, "Retries",
					DBUS_TYPE_BYTE,	&pin_retries_dict);
	g_free(pin_retries_dict);
	g_free(dbus_retries);
}

static void sim_pin_retries_check(struct ofono_sim *sim)
{
	if (sim->driver->query_pin_retries == NULL)
		return;

	sim->driver->query_pin_retries(sim, sim_pin_retries_query_cb, sim);
}

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->sim);

	g_free(req);
}

static void msisdn_set_cb(int ok, void *data)
{
	struct msisdn_set_request *req = data;

	if (!ok)
		req->failed++;

	req->pending--;

	if (!req->pending)
		msisdn_set_done(req);
}

static gboolean set_own_numbers(struct ofono_sim *sim,
				GSList *new_numbers, DBusMessage *msg)
{
	struct msisdn_set_request *req;
	int record;
	unsigned char efmsisdn[255];
	struct ofono_phone_number *number;

	if (new_numbers && g_slist_length(new_numbers) > sim->efmsisdn_records)
		return FALSE;

	req = g_new0(struct msisdn_set_request, 1);

	req->sim = sim;
	req->msg = dbus_message_ref(msg);

	for (record = 1; record <= sim->efmsisdn_records; record++) {
		if (new_numbers) {
			number = new_numbers->data;
			sim_adn_build(efmsisdn, sim->efmsisdn_length,
					number, NULL);
			new_numbers = new_numbers->next;
		} else {
			memset(efmsisdn, 0xff, sim->efmsisdn_length);
			/* Set number length */
			efmsisdn[sim->efmsisdn_length - 14] = 1;
		}

		if (ofono_sim_write(req->sim->context, 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);

	return TRUE;
}

static DBusMessage *sim_set_property(DBusConnection *conn, DBusMessage *msg,
					void *data)
{
	struct ofono_sim *sim = data;
	DBusMessageIter iter;
	DBusMessageIter var;
	DBusMessageIter var_elem;
	const char *name, *value;

	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")) {
		gboolean set_ok = FALSE;
		struct ofono_phone_number *own;
		GSList *own_numbers = NULL;

		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);

		/* Empty lists are supported */
		while (dbus_message_iter_get_arg_type(&var_elem) !=
				DBUS_TYPE_INVALID) {
			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);

			dbus_message_iter_next(&var_elem);
		}

		own_numbers = g_slist_reverse(own_numbers);
		set_ok = set_own_numbers(sim, own_numbers, msg);

error:
		g_slist_free_full(own_numbers, g_free);

		if (set_ok)
			return NULL;
	}

	return __ofono_error_invalid_args(msg);
}

static void sim_locked_cb(struct ofono_sim *sim, gboolean locked)
{
	DBusConnection *conn = ofono_dbus_get_connection();
	const char *path = __ofono_atom_get_path(sim->atom);
	const char *typestr;
	const char *pin;
	char **locked_pins;
	enum ofono_sim_password_type type;
	DBusMessage *reply;

	reply = dbus_message_new_method_return(sim->pending);

	dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_STRING, &typestr,
					DBUS_TYPE_STRING, &pin,
					DBUS_TYPE_INVALID);

	type = sim_string_to_passwd(typestr);

	/* This is used by lock/unlock pin, no puks allowed */
	sim->locked_pins[type] = locked;
	__ofono_dbus_pending_reply(&sim->pending, reply);

	locked_pins = get_locked_pins(sim);
	ofono_dbus_signal_array_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"LockedPins", DBUS_TYPE_STRING,
						&locked_pins);
	g_strfreev(locked_pins);

	sim_pin_retries_check(sim);
}

static void sim_unlock_cb(const struct ofono_error *error, void *data)
{
	struct ofono_sim *sim = data;

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
		DBusMessage *reply = __ofono_error_failed(sim->pending);

		__ofono_dbus_pending_reply(&sim->pending, reply);
		__ofono_sim_recheck_pin(sim);

		return;
	}

	sim_locked_cb(sim, FALSE);
}

static void sim_lock_cb(const struct ofono_error *error, void *data)
{
	struct ofono_sim *sim = data;

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
		DBusMessage *reply = __ofono_error_failed(sim->pending);

		__ofono_dbus_pending_reply(&sim->pending, reply);
		__ofono_sim_recheck_pin(sim);

		return;
	}

	sim_locked_cb(sim, TRUE);
}

static DBusMessage *sim_lock_or_unlock(struct ofono_sim *sim, int lock,
					DBusConnection *conn, DBusMessage *msg)
{
	enum ofono_sim_password_type type;
	const char *typestr;
	const char *pin;

	if (sim->driver->lock == NULL)
		return __ofono_error_not_implemented(msg);

	if (sim->pending)
		return __ofono_error_busy(msg);

	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
					DBUS_TYPE_STRING, &pin,
					DBUS_TYPE_INVALID) == FALSE)
		return __ofono_error_invalid_args(msg);

	type = sim_string_to_passwd(typestr);

	/*
	 * SIM PIN2 cannot be locked / unlocked according to 27.007,
	 * however the PIN combination can be changed
	 */
	if (password_is_pin(type) == FALSE ||
			type == OFONO_SIM_PASSWORD_SIM_PIN2)
		return __ofono_error_invalid_format(msg);

	if (!__ofono_is_valid_sim_pin(pin, type))
		return __ofono_error_invalid_format(msg);

	sim->pending = dbus_message_ref(msg);

	sim->driver->lock(sim, type, lock, pin,
				lock ? sim_lock_cb : sim_unlock_cb, sim);

	return NULL;
}

static DBusMessage *sim_lock_pin(DBusConnection *conn, DBusMessage *msg,
					void *data)
{
	struct ofono_sim *sim = data;

	return sim_lock_or_unlock(sim, 1, conn, msg);
}

static DBusMessage *sim_unlock_pin(DBusConnection *conn, DBusMessage *msg,
					void *data)
{
	struct ofono_sim *sim = data;

	return sim_lock_or_unlock(sim, 0, conn, msg);
}

static void sim_change_pin_cb(const struct ofono_error *error, void *data)
{
	struct ofono_sim *sim = data;

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
		__ofono_dbus_pending_reply(&sim->pending,
				__ofono_error_failed(sim->pending));

		__ofono_sim_recheck_pin(sim);

		return;
	}

	__ofono_dbus_pending_reply(&sim->pending,
				dbus_message_new_method_return(sim->pending));

	sim_pin_retries_check(sim);
}

static DBusMessage *sim_change_pin(DBusConnection *conn, DBusMessage *msg,
					void *data)
{
	struct ofono_sim *sim = data;
	enum ofono_sim_password_type type;
	const char *typestr;
	const char *old;
	const char *new;

	if (sim->driver->change_passwd == NULL)
		return __ofono_error_not_implemented(msg);

	if (sim->pending)
		return __ofono_error_busy(msg);

	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
					DBUS_TYPE_STRING, &old,
					DBUS_TYPE_STRING, &new,
					DBUS_TYPE_INVALID) == FALSE)
		return __ofono_error_invalid_args(msg);

	type = sim_string_to_passwd(typestr);

	if (password_is_pin(type) == FALSE)
		return __ofono_error_invalid_format(msg);

	if (!__ofono_is_valid_sim_pin(old, type))
		return __ofono_error_invalid_format(msg);

	if (!__ofono_is_valid_sim_pin(new, type))
		return __ofono_error_invalid_format(msg);

	if (!strcmp(new, old))
		return dbus_message_new_method_return(msg);

	sim->pending = dbus_message_ref(msg);
	sim->driver->change_passwd(sim, type, old, new,
					sim_change_pin_cb, sim);

	return NULL;
}

static void sim_enter_pin_cb(const struct ofono_error *error, void *data)
{
	struct ofono_sim *sim = data;
	DBusMessage *reply;

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
		reply = __ofono_error_failed(sim->pending);
	else
		reply = dbus_message_new_method_return(sim->pending);

	__ofono_dbus_pending_reply(&sim->pending, reply);

	/* If PIN entry fails, then recheck the PIN type */
	if (sim->initialized || error->type != OFONO_ERROR_TYPE_NO_ERROR)
		goto recheck;

	if (sim->pin_type == OFONO_SIM_PASSWORD_SIM_PIN ||
			sim->pin_type == OFONO_SIM_PASSWORD_SIM_PUK) {
		sim->wait_initialized = true;
		DBG("Waiting for ofono_sim_initialized_notify");
		return;
	}

recheck:
	__ofono_sim_recheck_pin(sim);
}

static DBusMessage *sim_enter_pin(DBusConnection *conn, DBusMessage *msg,
					void *data)
{
	struct ofono_sim *sim = data;
	const char *typestr;
	enum ofono_sim_password_type type;
	const char *pin;

	if (sim->driver->send_passwd == NULL)
		return __ofono_error_not_implemented(msg);

	if (sim->pending)
		return __ofono_error_busy(msg);

	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
					DBUS_TYPE_STRING, &pin,
					DBUS_TYPE_INVALID) == FALSE)
		return __ofono_error_invalid_args(msg);

	type = sim_string_to_passwd(typestr);

	if (type == OFONO_SIM_PASSWORD_NONE || type != sim->pin_type)
		return __ofono_error_invalid_format(msg);

	if (password_is_pin(type) == FALSE)
		return __ofono_error_invalid_format(msg);

	if (!__ofono_is_valid_sim_pin(pin, type))
		return __ofono_error_invalid_format(msg);

	sim->pending = dbus_message_ref(msg);
	sim->driver->send_passwd(sim, pin, sim_enter_pin_cb, sim);

	return NULL;
}

static void sim_get_image_cb(struct ofono_sim *sim,
				unsigned char id, char *xpm, gboolean cache)
{
	DBusMessage *reply;
	DBusMessageIter iter, array;
	int xpm_len;

	if (xpm == NULL) {
		reply = __ofono_error_failed(sim->pending);
		__ofono_dbus_pending_reply(&sim->pending, reply);
		return;
	}

	xpm_len = strlen(xpm);

	reply = dbus_message_new_method_return(sim->pending);
	dbus_message_iter_init_append(reply, &iter);

	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
					DBUS_TYPE_BYTE_AS_STRING, &array);

	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
						&xpm, xpm_len);
	dbus_message_iter_close_container(&iter, &array);

	__ofono_dbus_pending_reply(&sim->pending, reply);

	if (cache)
		sim_fs_cache_image(sim->simfs, (const char *) xpm, id);

	g_free(xpm);
}

static void sim_iidf_read_clut_cb(int ok, int length, int record,
					const unsigned char *data,
					int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;
	unsigned char id;
	unsigned char *efimg;
	unsigned short iidf_len;
	unsigned short clut_len;
	char *xpm;

	DBG("ok: %d", ok);

	dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_BYTE, &id,
					DBUS_TYPE_INVALID);
	id -= 1;
	efimg = &sim->efimg[id * 9];

	if (!ok) {
		sim_get_image_cb(sim, id, NULL, FALSE);
		goto done;
	}

	iidf_len = efimg[7] << 8 | efimg[8];

	if (sim->iidf_image[3] == 0)
		clut_len = 256 * 3;
	else
		clut_len = sim->iidf_image[3] * 3;

	xpm = stk_image_to_xpm(sim->iidf_image, iidf_len, efimg[2],
					data, clut_len);
	sim_get_image_cb(sim, id, xpm, TRUE);

done:
	g_free(sim->iidf_image);
	sim->iidf_image = NULL;
}

static void sim_iidf_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;
	unsigned char id;
	unsigned char *efimg;
	unsigned short iidf_id;
	unsigned short offset;
	unsigned short clut_len;
	unsigned char path[6];
	unsigned int path_len;

	DBG("ok: %d", ok);

	dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_BYTE, &id,
					DBUS_TYPE_INVALID);
	id -= 1;
	efimg = &sim->efimg[id * 9];

	if (!ok) {
		sim_get_image_cb(sim, id, NULL, FALSE);
		return;
	}

	if (efimg[2] == STK_IMG_SCHEME_BASIC) {
		char *xpm = stk_image_to_xpm(data, length, efimg[2], NULL, 0);
		sim_get_image_cb(sim, id, xpm, TRUE);
		return;
	}

	offset = data[4] << 8 | data[5];

	if (data[3] == 0)
		clut_len = 256 * 3;
	else
		clut_len = data[3] * 3;

	iidf_id = efimg[3] << 8 | efimg[4];
	sim->iidf_image = g_memdup(data, length);

	/* The path it the same between 2G and 3G */
	path_len = sim_ef_db_get_path_3g(SIM_EFIMG_FILEID, path);

	/* read the clut data */
	ofono_sim_read_bytes(sim->context, iidf_id, offset, clut_len,
					path, path_len,
					sim_iidf_read_clut_cb, sim);
}

static void sim_image_data_changed(int id, void *userdata)
{
	/* TODO: notify D-bus clients */
}

static void sim_get_image(struct ofono_sim *sim, unsigned char id,
				gpointer user_data)
{
	unsigned char *efimg;
	char *image;
	unsigned short iidf_id;
	unsigned short iidf_offset;
	unsigned short iidf_len;

	if (sim->efimg_length <= id * 9) {
		sim_get_image_cb(sim, id, NULL, FALSE);
		return;
	}

	image = sim_fs_get_cached_image(sim->simfs, id);
	if (image != NULL)
		sim_get_image_cb(sim, id, image, FALSE);

	efimg = &sim->efimg[id * 9];

	iidf_id = efimg[3] << 8 | efimg[4];
	iidf_offset = efimg[5] << 8 | efimg[6];
	iidf_len = efimg[7] << 8 | efimg[8];

	/* read the image data */
	if (image == NULL) {
		unsigned char path[6];
		unsigned int path_len;

		/* The path it the same between 2G and 3G */
		path_len = sim_ef_db_get_path_3g(SIM_EFIMG_FILEID, path);
		ofono_sim_read_bytes(sim->context, iidf_id, iidf_offset,
					iidf_len, path, path_len,
					sim_iidf_read_cb, sim);
	}

	if (sim->iidf_watch_ids[id] > 0)
		return;

	sim->iidf_watch_ids[id] = ofono_sim_add_file_watch(sim->context,
					iidf_id, sim_image_data_changed,
					sim, NULL);
}

static DBusMessage *sim_get_icon(DBusConnection *conn,
					DBusMessage *msg, void *data)
{
	struct ofono_sim *sim = data;
	unsigned char id;

	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &id,
					DBUS_TYPE_INVALID) == FALSE)
		return __ofono_error_invalid_args(msg);

	/* zero means no icon */
	if (id == 0)
		return __ofono_error_invalid_args(msg);

	if (sim->pending)
		return __ofono_error_busy(msg);

	if (sim->efimg == NULL)
		return __ofono_error_not_implemented(msg);

	sim->pending = dbus_message_ref(msg);

	sim_get_image(sim, id - 1, sim);

	return NULL;
}

static DBusMessage *sim_reset_pin(DBusConnection *conn, DBusMessage *msg,
					void *data)
{
	struct ofono_sim *sim = data;
	const char *typestr;
	enum ofono_sim_password_type type;
	const char *puk;
	const char *pin;

	if (sim->driver->reset_passwd == NULL)
		return __ofono_error_not_implemented(msg);

	if (sim->pending)
		return __ofono_error_busy(msg);

	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
					DBUS_TYPE_STRING, &puk,
					DBUS_TYPE_STRING, &pin,
					DBUS_TYPE_INVALID) == FALSE)
		return __ofono_error_invalid_args(msg);

	type = sim_string_to_passwd(typestr);

	if (type == OFONO_SIM_PASSWORD_NONE || type != sim->pin_type)
		return __ofono_error_invalid_format(msg);

	if (!__ofono_is_valid_sim_pin(puk, type))
		return __ofono_error_invalid_format(msg);

	type = puk2pin(type);

	if (!__ofono_is_valid_sim_pin(pin, type))
		return __ofono_error_invalid_format(msg);

	sim->pending = dbus_message_ref(msg);
	sim->driver->reset_passwd(sim, puk, pin, sim_enter_pin_cb, sim);

	return NULL;
}

static const GDBusMethodTable sim_methods[] = {
	{ GDBUS_METHOD("GetProperties",
			NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
			sim_get_properties) },
	{ GDBUS_ASYNC_METHOD("SetProperty",
			GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
			NULL, sim_set_property) },
	{ GDBUS_ASYNC_METHOD("ChangePin",
			GDBUS_ARGS({ "type", "s" }, { "oldpin", "s" },
						{ "newpin", "s" }), NULL,
			sim_change_pin) },
	{ GDBUS_ASYNC_METHOD("EnterPin",
			GDBUS_ARGS({ "type", "s" }, { "pin", "s" }), NULL,
			sim_enter_pin) },
	{ GDBUS_ASYNC_METHOD("ResetPin",
			GDBUS_ARGS({ "type", "s" }, { "puk", "s" },
						{ "newpin", "s" }), NULL,
			sim_reset_pin) },
	{ GDBUS_ASYNC_METHOD("LockPin",
			GDBUS_ARGS({ "type", "s" }, { "pin", "s" }), NULL,
			sim_lock_pin) },
	{ GDBUS_ASYNC_METHOD("UnlockPin",
			GDBUS_ARGS({ "type", "s" }, { "pin", "s" }), NULL,
			sim_unlock_pin) },
	{ GDBUS_ASYNC_METHOD("GetIcon",
			GDBUS_ARGS({ "id", "y" }),
			GDBUS_ARGS({ "icon", "ay" }),
			sim_get_icon) },
	{ }
};

static const GDBusSignalTable sim_signals[] = {
	{ GDBUS_SIGNAL("PropertyChanged",
			GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
	{ }
};

static gboolean numbers_list_equal(GSList *a, GSList *b)
{
	struct ofono_phone_number *num_a, *num_b;

	while (a || b) {
		if (a == NULL || b == NULL)
			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(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;
	int total;
	struct ofono_phone_number ph;

	if (!ok)
		goto check;

	if (record_length < 14 || length < record_length) {
		ofono_error("EFmsidn shall at least contain 14 bytes");
		return;
	}

	total = length / record_length;

	sim->efmsisdn_length = record_length;
	sim->efmsisdn_records = total;

	if (sim_adn_parse(data, record_length, &ph, NULL) == TRUE) {
		struct ofono_phone_number *own;

		own = g_new(struct ofono_phone_number, 1);
		memcpy(own, &ph, sizeof(struct ofono_phone_number));
		sim->new_numbers = g_slist_prepend(sim->new_numbers, own);
	}

	if (record != total)
		return;

check:
	/* All records retrieved */
	if (sim->new_numbers)
		sim->new_numbers = g_slist_reverse(sim->new_numbers);

	if (!numbers_list_equal(sim->new_numbers, sim->own_numbers)) {
		const char *path = __ofono_atom_get_path(sim->atom);
		char **own_numbers;
		DBusConnection *conn = ofono_dbus_get_connection();

		g_slist_free_full(sim->own_numbers, g_free);
		sim->own_numbers = sim->new_numbers;

		own_numbers = get_own_numbers(sim->own_numbers);

		ofono_dbus_signal_array_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"SubscriberNumbers",
						DBUS_TYPE_STRING, &own_numbers);

		g_strfreev(own_numbers);
	} else {
		g_slist_free_full(sim->new_numbers, g_free);
	}

	sim->new_numbers = NULL;
}

static gint service_number_compare(gconstpointer a, gconstpointer b)
{
	const struct service_number *sdn = a;
	const char *id = b;

	return strcmp(sdn->id, id);
}

static void sim_sdn_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;
	DBusConnection *conn = ofono_dbus_get_connection();
	const char *path = __ofono_atom_get_path(sim->atom);
	int total;
	struct ofono_phone_number ph;
	char *alpha;
	struct service_number *sdn;

	if (!ok)
		goto check;

	if (record_length < 14 || length < record_length)
		return;

	total = length / record_length;

	if (sim_adn_parse(data, record_length, &ph, &alpha) == FALSE)
		goto out;


	/* Use phone number if Id is unavailable */
	if (alpha && alpha[0] == '\0') {
		g_free(alpha);
		alpha = NULL;
	}

	if (alpha == NULL)
		alpha = g_strdup(phone_number_to_string(&ph));

	if (sim->service_numbers &&
			g_slist_find_custom(sim->service_numbers,
				alpha, service_number_compare)) {
		ofono_error("Duplicate EFsdn entries for `%s'",
				alpha);
		g_free(alpha);

		goto out;
	}

	sdn = g_new(struct service_number, 1);
	sdn->id = alpha;
	memcpy(&sdn->ph, &ph, sizeof(struct ofono_phone_number));

	sim->service_numbers = g_slist_prepend(sim->service_numbers, sdn);

out:
	if (record != total)
		return;

check:
	/* All records retrieved */
	if (sim->service_numbers) {
		sim->service_numbers = g_slist_reverse(sim->service_numbers);
		sim->sdn_ready = true;
	}

	if (sim->sdn_ready) {
		char **service_numbers;

		service_numbers = get_service_numbers(sim->service_numbers);

		ofono_dbus_signal_dict_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"ServiceNumbers",
						DBUS_TYPE_STRING,
						&service_numbers);
		g_strfreev(service_numbers);
	}
}

static void sim_service_numbers_changed(int id, void *userdata)
{
	struct ofono_sim *sim = userdata;

	if (sim->service_numbers) {
		g_slist_free_full(sim->service_numbers, service_number_free);
		sim->service_numbers = NULL;
	}

	ofono_sim_read(sim->context, SIM_EFSDN_FILEID,
			OFONO_SIM_FILE_STRUCTURE_FIXED, sim_sdn_read_cb, sim);
}

static void sim_own_numbers_update(struct ofono_sim *sim)
{
	ofono_sim_read(sim->context, SIM_EFMSISDN_FILEID,
			OFONO_SIM_FILE_STRUCTURE_FIXED, sim_msisdn_read_cb,
			sim);
}

static void sim_own_numbers_changed(int id, void *userdata)
{
	struct ofono_sim *sim = userdata;

	sim_own_numbers_update(sim);
}

static void sim_efimg_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;
	unsigned char *efimg;
	int num_records;

	if (!ok)
		return;

	num_records = length / record_length;

	/*
	 * EFimg descriptors are 9 bytes long.
	 * Byte 1 of the record is the number of descriptors per record.
	 */
	if ((record_length < 10) ||
			((record_length % 9 != 2) && (record_length % 9 != 1)))
		return;

	if (sim->efimg == NULL) {
		sim->efimg = g_try_malloc0(num_records * 9);
		if (sim->efimg == NULL)
			return;

		sim->iidf_watch_ids = g_try_new0(unsigned int, num_records);
		if (sim->iidf_watch_ids == NULL) {
			g_free(sim->efimg);
			sim->efimg = NULL;
			return;
		}

		sim->efimg_length = num_records * 9;
	}

	/*
	 * TBD - if we have more than one descriptor per record,
	 * pick the nicest one.  For now we use the first one.
	 */

	/* copy descriptor into slot for this record */
	efimg = &sim->efimg[(record - 1) * 9];

	memcpy(efimg, &data[1], 9);
}

static void sim_efimg_changed(int id, void *userdata)
{
	struct ofono_sim *sim = userdata;
	int i, watch;

	if (sim->efimg != NULL) {
		for (i = sim->efimg_length / 9 - 1; i >= 0; i--) {
			watch = sim->iidf_watch_ids[i];
			if (watch == 0)
				continue;

			ofono_sim_remove_file_watch(sim->context, watch);
		}

		g_free(sim->efimg);
		sim->efimg = NULL;
		sim->efimg_length = 0;
		g_free(sim->iidf_watch_ids);
		sim->iidf_watch_ids = NULL;
	}

	ofono_sim_read(sim->context, SIM_EFIMG_FILEID,
			OFONO_SIM_FILE_STRUCTURE_FIXED, sim_efimg_read_cb, sim);

	/* TODO: notify D-bus clients */
}

static void sim_ready(enum ofono_sim_state new_state, void *user)
{
	struct ofono_sim *sim = user;

	if (new_state != OFONO_SIM_STATE_READY)
		return;

	sim_own_numbers_update(sim);
	ofono_sim_add_file_watch(sim->context, SIM_EFMSISDN_FILEID,
					sim_own_numbers_changed, sim, NULL);

	ofono_sim_read(sim->context, SIM_EFSDN_FILEID,
			OFONO_SIM_FILE_STRUCTURE_FIXED, sim_sdn_read_cb, sim);
	ofono_sim_add_file_watch(sim->context, SIM_EFSDN_FILEID,
					sim_service_numbers_changed, sim, NULL);

	ofono_sim_read(sim->context, SIM_EFIMG_FILEID,
			OFONO_SIM_FILE_STRUCTURE_FIXED, sim_efimg_read_cb, sim);
	ofono_sim_add_file_watch(sim->context, SIM_EFIMG_FILEID,
					sim_efimg_changed, sim, NULL);
}

static void sim_set_ready(struct ofono_sim *sim)
{
	if (sim == NULL)
		return;

	if (sim->state != OFONO_SIM_STATE_INSERTED &&
			sim->state != OFONO_SIM_STATE_LOCKED_OUT)
		return;

	sim->state = OFONO_SIM_STATE_READY;

	sim_fs_check_version(sim->simfs);

	call_state_watches(sim);
}

static void impi_read_cb(int ok, int total_length, int record,
		const unsigned char *data,
		int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;

	if (!ok) {
		DBG("error reading IMPI");
		return;
	}

	if (data[0] != 0x80) {
		DBG("invalid TLV tag 0x%02x", data[0]);
		return;
	}

	sim->impi = g_strndup((const char *)data + 2, data[1]);
}

static void discover_apps_cb(const struct ofono_error *error,
		const unsigned char *dataobj,
		int len, void *data)
{
	GSList *iter;
	struct ofono_sim *sim = data;

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
		return;

	sim->aid_list = sim_parse_app_template_entries(dataobj, len);

	iter = sim->aid_list;

	while (iter) {
		struct sim_app_record *app = iter->data;
		struct ofono_sim_aid_session *s =
					g_new0(struct ofono_sim_aid_session, 1);

		s->watches = __ofono_watchlist_new(g_free);
		s->record = app;
		s->sim = sim;
		s->session_id = -1;
		s->state = SESSION_STATE_INACTIVE;
		sim->aid_sessions = g_slist_prepend(sim->aid_sessions, s);

		if (app->type == SIM_APP_TYPE_ISIM) {
			/*
			 * If an ISIM application is found, we should init
			 * the FS structure so the ISIM EF's can be accessed.
			 */
			sim->simfs_isim = sim_fs_new(sim, sim->driver);
			sim->isim_context = ofono_sim_context_create_isim(
					sim);
			/* attempt to get the NAI from EFimpi */
			ofono_sim_read_bytes(sim->isim_context,
					SIM_ISIM_EFIMPI_FILEID, 0, 255, NULL,
					0, impi_read_cb, sim);
		}

		iter = g_slist_next(iter);
	}
}

static void sim_imsi_obtained(struct ofono_sim *sim, const char *imsi)
{
	DBusConnection *conn = ofono_dbus_get_connection();
	const char *path = __ofono_atom_get_path(sim->atom);

	sim->imsi = g_strdup(imsi);

	ofono_dbus_signal_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"SubscriberIdentity",
						DBUS_TYPE_STRING, &sim->imsi);

	if (sim->mnc_length) {
		const char *str;

		strncpy(sim->mcc, sim->imsi, OFONO_MAX_MCC_LENGTH);
		sim->mcc[OFONO_MAX_MCC_LENGTH] = '\0';
		strncpy(sim->mnc, sim->imsi + OFONO_MAX_MCC_LENGTH,
			sim->mnc_length);
		sim->mnc[sim->mnc_length] = '\0';

		str = sim->mcc;
		ofono_dbus_signal_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"MobileCountryCode",
						DBUS_TYPE_STRING, &str);

		str = sim->mnc;
		ofono_dbus_signal_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"MobileNetworkCode",
						DBUS_TYPE_STRING, &str);
	}

	sim_set_ready(sim);

}

static void sim_efimsi_cb(const struct ofono_error *error,
				const unsigned char *data, int len, void *user)
{
	struct ofono_sim *sim = user;
	char imsi[17]; /* IMSI max length is 15 + 1 for NULL + 1 waste */
	unsigned char imsi_len;
	unsigned char parity;

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
		goto error;

	if (len != 9)
		goto error;

	imsi_len = data[0];

	if (imsi_len == 0 || imsi_len > 8)
		goto error;

	/* The low 3 bits of the first byte should be set to binary 001 */
	if ((data[1] & 0x7) != 0x1)
		goto error;

	/* Save off the parity bit */
	parity = (data[1] >> 3) & 1;

	extract_bcd_number(data + 1, imsi_len, imsi);
	imsi[16] = '\0';

	if ((strlen(imsi + 1) % 2) != parity)
		goto error;

	sim_imsi_obtained(sim, imsi + 1);
	return;

error:
	ofono_error("Unable to read IMSI, emergency calls only");
}

static void sim_imsi_cb(const struct ofono_error *error, const char *imsi,
			void *data)
{
	struct ofono_sim *sim = data;

	if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
		sim_imsi_obtained(sim, imsi);
		return;
	}

	/* Driver function failed, try via EF reads if possible */
	if (sim->driver->read_file_transparent == NULL) {
		ofono_error("Unable to read IMSI, emergency calls only");
		return;
	}

	sim->driver->read_file_transparent(sim, SIM_EFIMSI_FILEID, 0, 9,
						NULL, 0, sim_efimsi_cb, sim);
}

static void sim_retrieve_imsi(struct ofono_sim *sim)
{
	if (sim->driver->read_imsi) {
		sim->driver->read_imsi(sim, sim_imsi_cb, sim);
		return;
	}

	if (sim->driver->read_file_transparent == NULL) {
		ofono_error("IMSI retrieval not implemented,"
			" only emergency calls will be available");
		return;
	}

	sim->driver->read_file_transparent(sim, SIM_EFIMSI_FILEID, 0, 9,
						NULL, 0, sim_efimsi_cb, sim);
}

static void sim_fdn_enabled(struct ofono_sim *sim)
{
	DBusConnection *conn = ofono_dbus_get_connection();
	const char *path = __ofono_atom_get_path(sim->atom);
	dbus_bool_t val;

	sim->fixed_dialing = true;

	val = sim->fixed_dialing;
	ofono_dbus_signal_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"FixedDialing",
						DBUS_TYPE_BOOLEAN, &val);
}

static void sim_bdn_enabled(struct ofono_sim *sim)
{
	DBusConnection *conn = ofono_dbus_get_connection();
	const char *path = __ofono_atom_get_path(sim->atom);
	dbus_bool_t val;

	sim->barred_dialing = TRUE;

	val = sim->barred_dialing;
	ofono_dbus_signal_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"BarredDialing",
						DBUS_TYPE_BOOLEAN, &val);
}

static void sim_efbdn_info_read_cb(int ok, unsigned char file_status,
					int total_length, int record_length,
					void *userdata)
{
	struct ofono_sim *sim = userdata;

	if (!ok)
		goto out;

	if (file_status & SIM_FILE_STATUS_VALID)
		sim_bdn_enabled(sim);

out:
	if (!sim->fixed_dialing && !sim->barred_dialing)
		sim_retrieve_imsi(sim);
}

static gboolean check_bdn_status(struct ofono_sim *sim)
{
	/*
	 * Check the status of Barred Dialing in the SIM-card
	 * (TS 11.11/TS 51.011, Section 11.5.1: BDN capability request).
	 * If BDN is allocated, activated in EFsst and EFbdn is validated,
	 * halt the SIM initialization.
	 */
	if (sim_sst_is_active(sim->efsst, sim->efsst_length,
			SIM_SST_SERVICE_BDN)) {
		sim_fs_read_info(sim->context, SIM_EFBDN_FILEID,
				OFONO_SIM_FILE_STRUCTURE_FIXED,
				sim_efbdn_info_read_cb, sim);
		return TRUE;
	}

	return FALSE;
}

static void sim_efadn_info_read_cb(int ok, unsigned char file_status,
					int total_length, int record_length,
					void *userdata)
{
	struct ofono_sim *sim = userdata;

	if (!ok)
		goto out;

	if (!(file_status & SIM_FILE_STATUS_VALID))
		sim_fdn_enabled(sim);

out:
	if (check_bdn_status(sim) != TRUE) {
		if (!sim->fixed_dialing && !sim->barred_dialing)
			sim_retrieve_imsi(sim);
	}
}

static void sim_efsst_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;

	if (!ok)
		goto out;

	if (length < 2) {
		ofono_error("EFsst shall contain at least two bytes");
		goto out;
	}

	sim->efsst = g_memdup(data, length);
	sim->efsst_length = length;

	/*
	 * Check if Fixed Dialing is enabled in the SIM-card
	 * (TS 11.11/TS 51.011, Section 11.5.1: FDN capability request).
	 * If FDN is activated and ADN is invalidated,
	 * don't continue initialization routine.
	 */
	if (sim_sst_is_active(sim->efsst, sim->efsst_length,
				SIM_SST_SERVICE_FDN)) {
		sim_fs_read_info(sim->context, SIM_EFADN_FILEID,
					OFONO_SIM_FILE_STRUCTURE_FIXED,
					sim_efadn_info_read_cb, sim);
		return;
	}

	if (check_bdn_status(sim) == TRUE)
		return;

out:
	sim_retrieve_imsi(sim);
}

static void sim_efest_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;
	gboolean available;

	if (!ok)
		goto out;

	if (length < 1) {
		ofono_error("EFest shall contain at least one byte");
		goto out;
	}

	sim->efest = g_memdup(data, length);
	sim->efest_length = length;

	/*
	 * Check if Fixed Dialing is enabled in the USIM-card
	 * (TS 31.102, Section 5.3.2: FDN capability request).
	 * If FDN is activated, don't continue initialization routine.
	 */
	available = sim_ust_is_available(sim->efust, sim->efust_length,
						SIM_UST_SERVICE_FDN);
	if (available && sim_est_is_active(sim->efest, sim->efest_length,
						SIM_EST_SERVICE_FDN))
		sim_fdn_enabled(sim);

	/*
	 * Check the status of Barred Dialing in the USIM-card
	 * (TS 31.102, Section 5.3.2: BDN capability request).
	 * If BDN service is enabled, halt the USIM initialization.
	 */
	available = sim_ust_is_available(sim->efust, sim->efust_length,
						SIM_UST_SERVICE_BDN);
	if (available && sim_est_is_active(sim->efest, sim->efest_length,
						SIM_EST_SERVICE_BDN))
		sim_bdn_enabled(sim);

out:
	if (!sim->fixed_dialing && !sim->barred_dialing)
		sim_retrieve_imsi(sim);
}

static void sim_efust_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;

	if (!ok)
		goto out;

	if (length < 1) {
		ofono_error("EFust shall contain at least one byte");
		goto out;
	}

	sim->efust = g_memdup(data, length);
	sim->efust_length = length;

	/*
	 * Check whether the SIM provides EFest file
	 * According to 3GPP TS 31.102 section 4.2.47, EFest file
	 * shall be present if FDN or BDN or EST is available
	 * Lets be paranoid and check for the special cases as well
	 * where EST is not available(FDN or BDN available), but EFest
	 * is present
	 */
	if (sim_ust_is_available(sim->efust, sim->efust_length,
				SIM_UST_SERVICE_ENABLED_SERVICE_TABLE) ||
			sim_ust_is_available(sim->efust, sim->efust_length,
				SIM_UST_SERVICE_FDN) ||
			sim_ust_is_available(sim->efust, sim->efust_length,
				SIM_UST_SERVICE_BDN)) {
		ofono_sim_read(sim->context, SIM_EFEST_FILEID,
				OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
				sim_efest_read_cb, sim);

		return;
	}

out:
	sim_retrieve_imsi(sim);
}

static void sim_cphs_information_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;

	sim->cphs_phase = OFONO_SIM_CPHS_PHASE_NONE;

	if (!ok || length < 3)
		return;

	if (data[0] == 0x01)
		sim->cphs_phase = OFONO_SIM_CPHS_PHASE_1G;
	else if (data[0] >= 0x02)
		sim->cphs_phase = OFONO_SIM_CPHS_PHASE_2G;

	memcpy(sim->cphs_service_table, data + 1, 2);
}

static void sim_ad_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;
	int new_mnc_length;

	if (!ok)
		return;

	if (length < 4) {
		ofono_error("EFad should contain at least four bytes");
		return;
	}

	new_mnc_length = data[3] & 0xf;

	/* sanity check for potential invalid values */
	if (new_mnc_length < 2 || new_mnc_length > 3)
		return;

	sim->mnc_length = new_mnc_length;
}

static void sim_efphase_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;

	if (!ok || length != 1) {
		sim->phase = OFONO_SIM_PHASE_3G;

		ofono_sim_read(sim->context, SIM_EFUST_FILEID,
				OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
				sim_efust_read_cb, sim);

		return;
	}

	switch (data[0]) {
	case 0:
		sim->phase = OFONO_SIM_PHASE_1G;
		break;
	case 2:
		sim->phase = OFONO_SIM_PHASE_2G;
		break;
	case 3:
		sim->phase = OFONO_SIM_PHASE_2G_PLUS;
		break;
	default:
		ofono_error("Unknown phase");
		return;
	}

	ofono_sim_read(sim->context, SIM_EFSST_FILEID,
			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
			sim_efsst_read_cb, sim);
}

static void sim_initialize_after_pin(struct ofono_sim *sim)
{
	sim->context = ofono_sim_context_create(sim);

	/*
	 * Discover applications on SIM
	 */
	if (sim->driver->list_apps)
		sim->driver->list_apps(sim, discover_apps_cb, sim);

	ofono_sim_read(sim->context, SIM_EFPHASE_FILEID,
			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
			sim_efphase_read_cb, sim);

	ofono_sim_read(sim->context, SIM_EFAD_FILEID,
			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
			sim_ad_read_cb, sim);

	/*
	 * Read CPHS-support bits, this is still part of the SIM
	 * initialisation but no order is specified for it.
	 */
	ofono_sim_read(sim->context, SIM_EF_CPHS_INFORMATION_FILEID,
			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
			sim_cphs_information_read_cb, sim);
}

static void sim_efli_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;

	if (!ok)
		return;

	sim->efli = g_memdup(data, length);
	sim->efli_length = length;
}

/* Detect whether the file is in EFli format, as opposed to 51.011 EFlp */
static gboolean sim_efli_format(const unsigned char *ef, int length)
{
	int i;

	if (length & 1)
		return FALSE;

	for (i = 0; i < length; i += 2) {
		if (ef[i] == 0xff && ef[i+1] == 0xff)
			continue;

		/*
		 * ISO 639 country codes are each two lower-case SMS 7-bit
		 * characters while CB DCS language codes are in ranges
		 * (0 - 15) or (32 - 47), so the ranges don't overlap
		 */
		if (g_ascii_isalpha(ef[i]) == 0)
			return FALSE;

		if (g_ascii_isalpha(ef[i+1]) == 0)
			return FALSE;
	}

	return TRUE;
}

static GSList *parse_language_list(const unsigned char *ef, int length)
{
	int i;
	GSList *ret = NULL;

	for (i = 0; i < length; i += 2) {
		if (ef[i] > 0x7f || ef[i+1] > 0x7f)
			continue;

		/*
		 * ISO 639 codes contain only characters that are coded
		 * identically in SMS 7 bit charset, ASCII or UTF8 so
		 * no conversion.
		 */
		ret = g_slist_prepend(ret, g_ascii_strdown((char *)ef + i, 2));
	}

	if (ret)
		ret = g_slist_reverse(ret);

	return ret;
}

static GSList *parse_eflp(const unsigned char *eflp, int length)
{
	int i;
	char code[3];
	GSList *ret = NULL;

	for (i = 0; i < length; i++) {
		if (iso639_2_from_language(eflp[i], code) == FALSE)
			continue;

		ret = g_slist_prepend(ret, g_strdup(code));
	}

	if (ret)
		ret = g_slist_reverse(ret);

	return ret;
}

static char **concat_lang_prefs(GSList *a, GSList *b)
{
	GSList *l, *k;
	char **ret;
	int i = 0;
	int total = g_slist_length(a) + g_slist_length(b);

	if (total == 0)
		return NULL;

	ret = g_new0(char *, total + 1);

	for (l = a; l; l = l->next)
		ret[i++] = g_strdup(l->data);

	for (l = b; l; l = l->next) {
		gboolean duplicate = FALSE;

		for (k = a; k; k = k->next)
			if (!strcmp(k->data, l->data))
				duplicate = TRUE;

		if (duplicate)
			continue;

		ret[i++] = g_strdup(l->data);
	}

	return ret;
}

static void sim_efpl_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;
	const char *path = __ofono_atom_get_path(sim->atom);
	DBusConnection *conn = ofono_dbus_get_connection();
	gboolean efli_format = TRUE;
	GSList *efli = NULL;
	GSList *efpl = NULL;

	if (!ok || length < 2)
		goto skip_efpl;

	efpl = parse_language_list(data, length);

skip_efpl:
	if (sim->efli && sim->efli_length > 0) {
		efli_format = sim_efli_format(sim->efli, sim->efli_length);

		if (efli_format)
			efli = parse_language_list(sim->efli, sim->efli_length);
		else
			efli = parse_eflp(sim->efli, sim->efli_length);
	}

	/*
	 * If efli_format is TRUE, make a list of languages in both files in
	 * order of preference following TS 31.102.
	 * Quoting 31.102 Section 5.1.1.2:
	 * The preferred language selection shall always use the EFLI in
	 * preference to the EFPL at the MF unless:
	 * - if the EFLI has the value 'FFFF' in its highest priority position,
	 *   then the preferred language selection shall be the language
	 *   preference in the EFPL at the MF level
	 * Otherwise in order of preference according to TS 51.011
	 */
	if (efli_format) {
		if (sim->efli_length >= 2 && sim->efli[0] == 0xff &&
				sim->efli[1] == 0xff)
			sim->language_prefs = concat_lang_prefs(NULL, efpl);
		else
			sim->language_prefs = concat_lang_prefs(efli, efpl);
	} else {
		sim->language_prefs = concat_lang_prefs(efpl, efli);
	}

	if (sim->efli) {
		g_free(sim->efli);
		sim->efli = NULL;
		sim->efli_length = 0;
	}

	if (efli) {
		g_slist_free_full(efli, g_free);
	}

	if (efpl) {
		g_slist_free_full(efpl, g_free);
	}

	if (sim->language_prefs != NULL)
		ofono_dbus_signal_array_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"PreferredLanguages",
						DBUS_TYPE_STRING,
						&sim->language_prefs);

	/* Proceed with sim initialization if we're not merely updating */
	if (!sim->language_prefs_update)
		__ofono_sim_recheck_pin(sim);

	sim->language_prefs_update = false;
}

static void sim_iccid_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *userdata)
{
	struct ofono_sim *sim = userdata;
	const char *path = __ofono_atom_get_path(sim->atom);
	DBusConnection *conn = ofono_dbus_get_connection();
	char iccid[21]; /* ICCID max length is 20 + 1 for NULL */

	if (!ok || length < 10)
		return;

	extract_bcd_number(data, length, iccid);
	iccid[20] = '\0';
	sim->iccid = g_strdup(iccid);

	ofono_dbus_signal_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"CardIdentifier",
						DBUS_TYPE_STRING,
						&sim->iccid);
}

static void sim_iccid_changed(int id, void *userdata)
{
	struct ofono_sim *sim = userdata;

	if (sim->iccid) {
		g_free(sim->iccid);
		sim->iccid = NULL;
	}

	ofono_sim_read(sim->early_context, SIM_EF_ICCID_FILEID,
			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
			sim_iccid_read_cb, sim);
}

static void sim_efli_efpl_changed(int id, void *userdata)
{
	struct ofono_sim *sim = userdata;

	if (sim->efli != NULL) /* This shouldn't happen */
		return;

	if (sim->language_prefs_update)
		return;

	if (sim->language_prefs) {
		g_strfreev(sim->language_prefs);
		sim->language_prefs = NULL;
	}

	sim->language_prefs_update = true;

	ofono_sim_read(sim->early_context, SIM_EFLI_FILEID,
			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
			sim_efli_read_cb, sim);

	ofono_sim_read(sim->early_context, SIM_EFPL_FILEID,
			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
			sim_efpl_read_cb, sim);
}

static void sim_initialize(struct ofono_sim *sim)
{
	/*
	 * Perform SIM initialization according to 3GPP 31.102 Section 5.1.1.2
	 * The assumption here is that if sim manager is being initialized,
	 * then sim commands are implemented, and the sim manager is then
	 * responsible for checking the PIN, reading the IMSI and signaling
	 * SIM ready condition.
	 *
	 * The procedure according to 31.102, 51.011, 11.11 and CPHS 4.2 is
	 * roughly:
	 *
	 * Read EFecc
	 * Read EFli and EFpl
	 * SIM Pin check
	 * Request SIM phase (only in 51.011)
	 * Administrative information request (read EFad)
	 * Request CPHS Information (only in CPHS 4.2)
	 * Read EFsst (only in 11.11 & 51.011)
	 * Read EFust (only in 31.102)
	 * Read EFest (only in 31.102)
	 * Read IMSI
	 *
	 * At this point we signal the SIM ready condition and allow
	 * arbitrary files to be written or read, assuming their presence
	 * in the EFust
	 */

	if (sim->early_context == NULL)
		sim->early_context = ofono_sim_context_create(sim);

	/* Grab the EFiccid which is always available */
	ofono_sim_read(sim->early_context, SIM_EF_ICCID_FILEID,
			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
			sim_iccid_read_cb, sim);
	ofono_sim_add_file_watch(sim->early_context, SIM_EF_ICCID_FILEID,
					sim_iccid_changed, sim, NULL);

	/* EFecc is read by the voicecall atom */

	/*
	 * According to 31.102 the EFli is read first and EFpl is then
	 * only read if none of the EFli languages are supported by user
	 * interface.  51.011 mandates the exact opposite, making EFpl/EFelp
	 * preferred over EFlp (same EFid as EFli, different format).
	 * However we don't depend on the user interface and so
	 * need to read both files now.
	 */
	ofono_sim_read(sim->early_context, SIM_EFLI_FILEID,
			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
			sim_efli_read_cb, sim);
	ofono_sim_add_file_watch(sim->early_context, SIM_EFLI_FILEID,
					sim_efli_efpl_changed, sim, NULL);

	ofono_sim_read(sim->early_context, SIM_EFPL_FILEID,
			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
			sim_efpl_read_cb, sim);
	ofono_sim_add_file_watch(sim->early_context, SIM_EFPL_FILEID,
					sim_efli_efpl_changed, sim, NULL);
}

struct ofono_sim_context *ofono_sim_context_create(struct ofono_sim *sim)
{
	if (sim == NULL || sim->simfs == NULL)
		return NULL;

	return sim_fs_context_new(sim->simfs);
}

struct ofono_sim_context *ofono_sim_context_create_isim(
		struct ofono_sim *sim)
{
	GSList *iter = sim->aid_sessions;

	if (sim == NULL || sim->simfs_isim == NULL)
		return NULL;

	/* Find the AID */
	while (iter) {
		struct ofono_sim_aid_session *session = iter->data;

		if (session->record->type == SIM_APP_TYPE_ISIM) {
			return sim_fs_context_new_with_aid(sim->simfs_isim,
					session->record->aid);
		}

		iter = g_slist_next(iter);
	}

	return NULL;
}

void ofono_sim_context_free(struct ofono_sim_context *context)
{
	return sim_fs_context_free(context);
}

int ofono_sim_read_bytes(struct ofono_sim_context *context, int id,
			unsigned short offset, unsigned short num_bytes,
			const unsigned char *path, unsigned int len,
			ofono_sim_file_read_cb_t cb, void *data)
{
	if (num_bytes == 0)
		return -1;

	return sim_fs_read(context, id, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
				offset, num_bytes, path, len, cb, data);
}

int ofono_sim_read(struct ofono_sim_context *context, int id,
			enum ofono_sim_file_structure expected_type,
			ofono_sim_file_read_cb_t cb, void *data)
{
	return sim_fs_read(context, id, expected_type, 0, 0, NULL, 0, cb, data);
}

int ofono_sim_write(struct ofono_sim_context *context, int id,
			ofono_sim_file_write_cb_t cb,
			enum ofono_sim_file_structure structure, int record,
			const unsigned char *data, int length, void *userdata)
{
	return sim_fs_write(context, id, cb, structure, record, data, length,
				userdata);
}

unsigned int ofono_sim_add_file_watch(struct ofono_sim_context *context,
					int id, ofono_sim_file_changed_cb_t cb,
					void *userdata,
					ofono_destroy_func destroy)
{
	return sim_fs_file_watch_add(context, id, cb, userdata, destroy);
}

void ofono_sim_remove_file_watch(struct ofono_sim_context *context,
					unsigned int id)
{
	sim_fs_file_watch_remove(context, id);
}

const char *ofono_sim_get_imsi(struct ofono_sim *sim)
{
	if (sim == NULL)
		return NULL;

	return sim->imsi;
}

const char *ofono_sim_get_mcc(struct ofono_sim *sim)
{
	if (sim == NULL)
		return NULL;

	return sim->mcc;
}

const char *ofono_sim_get_mnc(struct ofono_sim *sim)
{
	if (sim == NULL)
		return NULL;

	return sim->mnc;
}

const char *ofono_sim_get_spn(struct ofono_sim *sim)
{
	if (sim == NULL)
		return NULL;

	return sim->spn;
}

enum ofono_sim_phase ofono_sim_get_phase(struct ofono_sim *sim)
{
	if (sim == NULL)
		return OFONO_SIM_PHASE_UNKNOWN;

	return sim->phase;
}

enum ofono_sim_cphs_phase ofono_sim_get_cphs_phase(struct ofono_sim *sim)
{
	if (sim == NULL)
		return OFONO_SIM_CPHS_PHASE_NONE;

	return sim->cphs_phase;
}

enum ofono_sim_password_type ofono_sim_get_password_type(struct ofono_sim *sim)
{
	if (sim == NULL)
		return OFONO_SIM_PASSWORD_NONE;

	return sim->pin_type;
}

const unsigned char *ofono_sim_get_cphs_service_table(struct ofono_sim *sim)
{
	if (sim == NULL)
		return NULL;

	return sim->cphs_service_table;
}

ofono_bool_t __ofono_sim_ust_service_available(struct ofono_sim *sim,
						int ust_service)
{
	if (sim->efust)
		return sim_ust_is_available(sim->efust, sim->efust_length,
						ust_service);
	return FALSE;
}

ofono_bool_t __ofono_sim_service_available(struct ofono_sim *sim,
						int ust_service,
						int sst_service)
{
	if (sim->efust)
		return sim_ust_is_available(sim->efust, sim->efust_length,
						ust_service);

	if (sim->efsst)
		return sim_sst_is_active(sim->efsst, sim->efsst_length,
						sst_service);

	return FALSE;
}

ofono_bool_t __ofono_sim_cphs_service_available(struct ofono_sim *sim,
						int cphs_service)
{
	return sim_cphs_is_active(sim->cphs_service_table, cphs_service);
}

static void sim_inserted_update(struct ofono_sim *sim)
{
	DBusConnection *conn = ofono_dbus_get_connection();
	const char *path = __ofono_atom_get_path(sim->atom);
	dbus_bool_t present = sim->state != OFONO_SIM_STATE_NOT_PRESENT;

	ofono_dbus_signal_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"Present",
						DBUS_TYPE_BOOLEAN, &present);
}

static void sim_free_early_state(struct ofono_sim *sim)
{
	if (sim->iccid) {
		g_free(sim->iccid);
		sim->iccid = NULL;
	}

	if (sim->efli) {
		g_free(sim->efli);
		sim->efli = NULL;
		sim->efli_length = 0;
	}

	if (sim->language_prefs) {
		g_strfreev(sim->language_prefs);
		sim->language_prefs = NULL;
	}

	if (sim->early_context) {
		ofono_sim_context_free(sim->early_context);
		sim->early_context = NULL;
	}
}

static void sim_spn_close(struct ofono_sim *sim)
{
	/*
	 * We have not initialized SPN logic at all yet, either because
	 * no netreg / gprs atom has been needed or we have not reached the
	 * post_sim state
	 */
	if (sim->ef_spn_watch == 0)
		return;

	ofono_sim_remove_file_watch(sim->context, sim->ef_spn_watch);
	sim->ef_spn_watch = 0;

	ofono_sim_remove_file_watch(sim->context, sim->cphs_spn_watch);
	sim->cphs_spn_watch = 0;

	if (sim->cphs_spn_short_watch) {
		ofono_sim_remove_file_watch(sim->context,
						sim->cphs_spn_short_watch);
		sim->cphs_spn_short_watch = 0;
	}

	sim->reading_spn = false;

	g_free(sim->spn);
	sim->spn = NULL;

	g_free(sim->spn_dc);
	sim->spn_dc = NULL;
}

static void aid_session_free(gpointer data)
{
	struct ofono_sim_aid_session *session = data;

	__ofono_watchlist_free(session->watches);

	g_free(session);
}

static void sim_free_main_state(struct ofono_sim *sim)
{
	if (sim->imsi) {
		g_free(sim->imsi);
		sim->imsi = NULL;
	}

	sim->mcc[0] = '\0';
	sim->mnc[0] = '\0';

	if (sim->own_numbers) {
		g_slist_free_full(sim->own_numbers, g_free);
		sim->own_numbers = NULL;
	}

	if (sim->service_numbers) {
		g_slist_free_full(sim->service_numbers, service_number_free);
		sim->service_numbers = NULL;
		sim->sdn_ready = false;
	}

	if (sim->efust) {
		g_free(sim->efust);
		sim->efust = NULL;
		sim->efust_length = 0;
	}

	if (sim->efest) {
		g_free(sim->efest);
		sim->efest = NULL;
		sim->efest_length = 0;
	}

	if (sim->efsst) {
		g_free(sim->efsst);
		sim->efsst = NULL;
		sim->efsst_length = 0;
	}

	sim->phase = OFONO_SIM_PHASE_UNKNOWN;
	sim->cphs_phase = OFONO_SIM_CPHS_PHASE_NONE;
	sim->mnc_length = 0;
	memset(sim->cphs_service_table, 0, 2);

	if (sim->efimg) {
		g_free(sim->efimg);
		sim->efimg = NULL;
		sim->efimg_length = 0;
		g_free(sim->iidf_watch_ids);
		sim->iidf_watch_ids = NULL;
	}

	g_free(sim->iidf_image);
	sim->iidf_image = NULL;

	sim->fixed_dialing = false;
	sim->barred_dialing = false;

	sim_spn_close(sim);

	if (sim->context) {
		ofono_sim_context_free(sim->context);
		sim->context = NULL;
	}

	if (sim->isim_context) {
		ofono_sim_context_free(sim->isim_context);
		sim->isim_context = NULL;
	}

	if (sim->impi)
		g_free(sim->impi);

	if (sim->aid_list) {
		g_slist_free_full(sim->aid_list,
					(GDestroyNotify) sim_app_record_free);
		sim->aid_list = NULL;
	}

	if (sim->aid_sessions) {
		g_slist_free_full(sim->aid_sessions, aid_session_free);
		sim->aid_sessions = NULL;
	}

	sim->initialized = false;
	sim->wait_initialized = false;
}

static void sim_free_state(struct ofono_sim *sim)
{
	sim_free_early_state(sim);
	sim_free_main_state(sim);
}

static void sim_set_locked_pin(struct ofono_sim *sim,
			enum ofono_sim_password_type type, gboolean locked)
{
	char **locked_pins;

	if (sim->locked_pins[type] == locked)
		return;

	sim->locked_pins[type] = locked;
	locked_pins = get_locked_pins(sim);

	ofono_dbus_signal_array_property_changed(ofono_dbus_get_connection(),
				__ofono_atom_get_path(sim->atom),
				OFONO_SIM_MANAGER_INTERFACE, "LockedPins",
				DBUS_TYPE_STRING, &locked_pins);

	g_strfreev(locked_pins);
}

static void sim_query_fac_pinlock_cb(const struct ofono_error *error,
				ofono_bool_t status, void *data)
{
	struct ofono_sim *sim = data;

	if (sim->state == OFONO_SIM_STATE_NOT_PRESENT)
		return;

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
		goto done;

	sim_set_locked_pin(data, OFONO_SIM_PASSWORD_SIM_PIN, status);

done:
	sim_initialize(sim);
}

static void sim_query_fac_networklock_cb(const struct ofono_error *error,
				ofono_bool_t status, void *data)
{
	struct ofono_sim *sim = data;

	if (sim->state == OFONO_SIM_STATE_NOT_PRESENT)
		return;

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
		goto done;

	sim_set_locked_pin(data, OFONO_SIM_PASSWORD_PHNET_PIN, status);

done:
	sim->driver->query_facility_lock(sim,
					OFONO_SIM_PASSWORD_SIM_PIN,
					sim_query_fac_pinlock_cb, sim);
}

static void sim_query_fac_imsilock_cb(const struct ofono_error *error,
				ofono_bool_t status, void *data)
{
	struct ofono_sim *sim = data;

	if (sim->state == OFONO_SIM_STATE_NOT_PRESENT)
		return;

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
		goto done;

	sim_set_locked_pin(data, OFONO_SIM_PASSWORD_PHSIM_PIN, status);

done:
	sim->driver->query_facility_lock(sim,
					OFONO_SIM_PASSWORD_PHNET_PIN,
					sim_query_fac_networklock_cb, sim);
}

void ofono_sim_inserted_notify(struct ofono_sim *sim, ofono_bool_t inserted)
{
	if (sim->state == OFONO_SIM_STATE_RESETTING && inserted) {
		/*
		 * Start initialization procedure from after EFiccid,
		 * EFli and EFpl are retrieved.
		 */
		sim->state = OFONO_SIM_STATE_INSERTED;
		__ofono_sim_recheck_pin(sim);
		return;
	}

	if (inserted == TRUE && sim->state == OFONO_SIM_STATE_NOT_PRESENT)
		sim->state = OFONO_SIM_STATE_INSERTED;
	else if (inserted == FALSE && sim->state != OFONO_SIM_STATE_NOT_PRESENT)
		sim->state = OFONO_SIM_STATE_NOT_PRESENT;
	else
		return;

	if (!__ofono_atom_get_registered(sim->atom))
		return;

	sim_inserted_update(sim);
	call_state_watches(sim);

	if (inserted) {
		if (sim->driver->query_facility_lock) {
			sim->driver->query_facility_lock(sim,
					OFONO_SIM_PASSWORD_PHSIM_PIN,
					sim_query_fac_imsilock_cb, sim);

		} else {
			sim_initialize(sim);
		}
	} else {
		switch (sim->pin_type) {
		case OFONO_SIM_PASSWORD_SIM_PIN:
		case OFONO_SIM_PASSWORD_SIM_PUK:
		case OFONO_SIM_PASSWORD_SIM_PIN2:
		case OFONO_SIM_PASSWORD_SIM_PUK2:
			sim->pin_type = OFONO_SIM_PASSWORD_NONE;
			break;
		default:
			break;
		}

		sim->locked_pins[OFONO_SIM_PASSWORD_SIM_PIN] = FALSE;
		sim->locked_pins[OFONO_SIM_PASSWORD_SIM_PIN2] = FALSE;

		sim->pin_retries[OFONO_SIM_PASSWORD_SIM_PIN] = -1;
		sim->pin_retries[OFONO_SIM_PASSWORD_SIM_PUK] = -1;
		sim->pin_retries[OFONO_SIM_PASSWORD_SIM_PIN2] = -1;
		sim->pin_retries[OFONO_SIM_PASSWORD_SIM_PUK2] = -1;

		sim_free_state(sim);
	}
}

void ofono_sim_initialized_notify(struct ofono_sim *sim)
{
	if (sim->state != OFONO_SIM_STATE_INSERTED &&
				sim->state != OFONO_SIM_STATE_LOCKED_OUT)
		return;

	sim->initialized = true;

	if (!sim->wait_initialized)
		return;

	sim->wait_initialized = false;
	__ofono_sim_recheck_pin(sim);
}

unsigned int ofono_sim_add_state_watch(struct ofono_sim *sim,
					ofono_sim_state_event_cb_t notify,
					void *data, ofono_destroy_func destroy)
{
	struct ofono_watchlist_item *item;

	DBG("%p", sim);

	if (sim == NULL)
		return 0;

	if (notify == NULL)
		return 0;

	item = g_new0(struct ofono_watchlist_item, 1);

	item->notify = notify;
	item->destroy = destroy;
	item->notify_data = data;

	return __ofono_watchlist_add_item(sim->state_watches, item);
}

void ofono_sim_remove_state_watch(struct ofono_sim *sim, unsigned int id)
{
	__ofono_watchlist_remove_item(sim->state_watches, id);
}

enum ofono_sim_state ofono_sim_get_state(struct ofono_sim *sim)
{
	if (sim == NULL)
		return OFONO_SIM_STATE_NOT_PRESENT;

	return sim->state;
}

static void spn_watch_cb(gpointer data, gpointer user_data)
{
	struct ofono_watchlist_item *item = data;
	struct ofono_sim *sim = user_data;

	if (item->notify)
		((ofono_sim_spn_cb_t) item->notify)(sim->spn, sim->spn_dc,
							item->notify_data);
}

static inline void spn_watches_notify(struct ofono_sim *sim)
{
	if (sim->spn_watches->items)
		g_slist_foreach(sim->spn_watches->items, spn_watch_cb, sim);

	sim->reading_spn = false;
}

static void sim_spn_set(struct ofono_sim *sim, const void *data, int length,
						const unsigned char *dc)
{
	DBusConnection *conn = ofono_dbus_get_connection();
	const char *path = __ofono_atom_get_path(sim->atom);

	g_free(sim->spn);
	sim->spn = NULL;

	g_free(sim->spn_dc);
	sim->spn_dc = NULL;

	if (data == NULL)
		goto notify;

	/*
	 * TS 31.102 says:
	 *
	 * 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].
	 *
	 * 31.101 has no such annex though.  51.101 refers to Annex B of
	 * itself which is not there either.  11.11 contains the same
	 * paragraph as 51.101 and has an Annex B which we implement.
	 */
	sim->spn = sim_string_to_utf8(data, length);
	if (sim->spn == NULL) {
		ofono_error("EFspn read successfully, but couldn't parse");
		goto notify;
	}

	if (strlen(sim->spn) == 0) {
		g_free(sim->spn);
		sim->spn = NULL;
		goto notify;
	}

	if (dc)
		sim->spn_dc = g_memdup(dc, 1);

notify:
	if (sim->spn)
		ofono_dbus_signal_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"ServiceProviderName",
						DBUS_TYPE_STRING, &sim->spn);

	spn_watches_notify(sim);
}

static void sim_cphs_spn_short_read_cb(int ok, int length, int record,
					const unsigned char *data,
					int record_length, void *user_data)
{
	struct ofono_sim *sim = user_data;

	if (!ok) {
		sim_spn_set(sim, NULL, 0, NULL);
		return;
	}

	sim_spn_set(sim, data, length, NULL);
}

static void sim_cphs_spn_read_cb(int ok, int length, int record,
					const unsigned char *data,
					int record_length, void *user_data)
{
	struct ofono_sim *sim = user_data;

	if (!ok) {
		if (__ofono_sim_cphs_service_available(sim,
						SIM_CPHS_SERVICE_SHORT_SPN))
			ofono_sim_read(sim->context,
					SIM_EF_CPHS_SPN_SHORT_FILEID,
					OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
					sim_cphs_spn_short_read_cb, sim);
		else
			sim_spn_set(sim, NULL, 0, NULL);

		return;
	}

	sim_spn_set(sim, data, length, NULL);
}

static void sim_spn_read_cb(int ok, int length, int record,
				const unsigned char *data,
				int record_length, void *user_data)
{
	struct ofono_sim *sim = user_data;

	if (!ok) {
		ofono_sim_read(sim->context, SIM_EF_CPHS_SPN_FILEID,
				OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
				sim_cphs_spn_read_cb, sim);

		return;
	}

	sim_spn_set(sim, data + 1, length - 1, data);
}

static void sim_spn_changed(int id, void *userdata)
{
	struct ofono_sim *sim = userdata;

	if (sim->reading_spn)
		return;

	sim->reading_spn = true;
	ofono_sim_read(sim->context, SIM_EFSPN_FILEID,
			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
			sim_spn_read_cb, sim);
}

static void sim_spn_init(struct ofono_sim *sim)
{
	sim->ef_spn_watch = ofono_sim_add_file_watch(sim->context,
					SIM_EFSPN_FILEID, sim_spn_changed, sim,
					NULL);

	sim->cphs_spn_watch = ofono_sim_add_file_watch(sim->context,
					SIM_EF_CPHS_SPN_FILEID,
					sim_spn_changed, sim, NULL);

	if (__ofono_sim_cphs_service_available(sim,
						SIM_CPHS_SERVICE_SHORT_SPN))
		sim->cphs_spn_short_watch = ofono_sim_add_file_watch(
				sim->context, SIM_EF_CPHS_SPN_SHORT_FILEID,
				sim_spn_changed, sim, NULL);
}

ofono_bool_t ofono_sim_add_spn_watch(struct ofono_sim *sim, unsigned int *id,
					ofono_sim_spn_cb_t cb, void *data,
					ofono_destroy_func destroy)
{
	struct ofono_watchlist_item *item;
	unsigned int watch_id;

	DBG("%p", sim);

	if (sim == NULL)
		return 0;

	item = g_new0(struct ofono_watchlist_item, 1);

	item->notify = cb;
	item->destroy = destroy;
	item->notify_data = data;

	watch_id = __ofono_watchlist_add_item(sim->spn_watches, item);
	if (watch_id == 0)
		return FALSE;

	*id = watch_id;

	if (sim->ef_spn_watch == 0) {
		sim_spn_init(sim);
		sim_spn_changed(0, sim);
		return TRUE;
	}

	if (sim->reading_spn)
		return TRUE;

	((ofono_sim_spn_cb_t) item->notify)(sim->spn, sim->spn_dc,
							item->notify_data);
	return TRUE;
}

ofono_bool_t ofono_sim_remove_spn_watch(struct ofono_sim *sim, unsigned int *id)
{
	gboolean ret;

	DBG("%p", sim);

	if (sim == NULL)
		return FALSE;

	ret = __ofono_watchlist_remove_item(sim->spn_watches, *id);
	if (ret == TRUE)
		*id = 0;

	return ret;
}

static void sim_pin_query_cb(const struct ofono_error *error,
				enum ofono_sim_password_type pin_type,
				void *data)
{
	struct ofono_sim *sim = data;
	DBusConnection *conn = ofono_dbus_get_connection();
	const char *path = __ofono_atom_get_path(sim->atom);
	const char *pin_name;
	char **locked_pins;
	gboolean lock_changed;

	DBG("sim->pin_type: %d, pin_type: %d", sim->pin_type, pin_type);

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
		ofono_error("Querying PIN authentication state failed");
		return;
	}

	if (sim->pin_type != pin_type) {
		sim->pin_type = pin_type;
		pin_name = sim_passwd_name(pin_type);

		if (pin_type != OFONO_SIM_PASSWORD_NONE &&
				password_is_pin(pin_type) == FALSE)
			pin_type = puk2pin(pin_type);

		if (pin_type != OFONO_SIM_PASSWORD_INVALID
				&& pin_type != OFONO_SIM_PASSWORD_NONE) {
			lock_changed = !sim->locked_pins[pin_type];

			sim->locked_pins[pin_type] = TRUE;

			if (lock_changed) {
				locked_pins = get_locked_pins(sim);

				ofono_dbus_signal_array_property_changed(conn,
						path,
						OFONO_SIM_MANAGER_INTERFACE,
						"LockedPins", DBUS_TYPE_STRING,
						&locked_pins);

				g_strfreev(locked_pins);
			}
		}

		ofono_dbus_signal_property_changed(conn, path,
						OFONO_SIM_MANAGER_INTERFACE,
						"PinRequired", DBUS_TYPE_STRING,
						&pin_name);
	}

	switch (pin_type) {
	case OFONO_SIM_PASSWORD_NONE:
	case OFONO_SIM_PASSWORD_SIM_PIN2:
	case OFONO_SIM_PASSWORD_SIM_PUK2:
		break;
	default:
		if (sim->state == OFONO_SIM_STATE_READY) {
			/* Force the sim state out of READY */
			sim_free_main_state(sim);

			sim->state = OFONO_SIM_STATE_LOCKED_OUT;
			call_state_watches(sim);
		}
		break;
	}

	sim_pin_retries_check(sim);

	switch (pin_type) {
	case OFONO_SIM_PASSWORD_SIM_PIN2:
	case OFONO_SIM_PASSWORD_SIM_PUK2:
	case OFONO_SIM_PASSWORD_NONE:
		if (sim->state == OFONO_SIM_STATE_READY)
			break;

		sim_initialize_after_pin(sim);
		break;
	default:
		break;
	}
}

void __ofono_sim_recheck_pin(struct ofono_sim *sim)
{
	if (sim->driver->query_passwd_state == NULL) {
		sim_initialize_after_pin(sim);
		return;
	}

	sim->driver->query_passwd_state(sim, sim_pin_query_cb, sim);
}

int ofono_sim_driver_register(const struct ofono_sim_driver *d)
{
	DBG("driver: %p, name: %s", d, d->name);

	if (d->probe == NULL)
		return -EINVAL;

	g_drivers = g_slist_prepend(g_drivers, (void *) d);

	return 0;
}

void ofono_sim_driver_unregister(const struct ofono_sim_driver *d)
{
	DBG("driver: %p, name: %s", d, d->name);

	g_drivers = g_slist_remove(g_drivers, (void *) d);
}

static void emulator_remove_handler(struct ofono_atom *atom, void *data)
{
	struct ofono_emulator *em = __ofono_atom_get_data(atom);

	ofono_emulator_remove_handler(em, data);
}

static void sim_unregister(struct ofono_atom *atom)
{
	DBusConnection *conn = ofono_dbus_get_connection();
	struct ofono_modem *modem = __ofono_atom_get_modem(atom);
	const char *path = __ofono_atom_get_path(atom);
	struct ofono_sim *sim = __ofono_atom_get_data(atom);

	__ofono_modem_foreach_registered_atom(modem,
						OFONO_ATOM_TYPE_EMULATOR_HFP,
						emulator_remove_handler,
						"+CNUM");

	__ofono_modem_remove_atom_watch(modem, sim->hfp_watch);

	__ofono_watchlist_free(sim->state_watches);
	sim->state_watches = NULL;
	__ofono_watchlist_free(sim->spn_watches);
	sim->spn_watches = NULL;

	g_dbus_unregister_interface(conn, path, OFONO_SIM_MANAGER_INTERFACE);
	ofono_modem_remove_interface(modem, OFONO_SIM_MANAGER_INTERFACE);
}

static void sim_remove(struct ofono_atom *atom)
{
	struct ofono_sim *sim = __ofono_atom_get_data(atom);

	DBG("atom: %p", atom);

	if (sim == NULL)
		return;

	if (sim->driver != NULL && sim->driver->remove != NULL)
		sim->driver->remove(sim);

	sim_free_state(sim);

	sim_fs_free(sim->simfs);
	sim->simfs = NULL;
	sim_fs_free(sim->simfs_isim);
	sim->simfs_isim = NULL;

	g_free(sim);
}

struct ofono_sim *ofono_sim_create(struct ofono_modem *modem,
					unsigned int vendor,
					const char *driver,
					void *data)
{
	struct ofono_sim *sim;
	GSList *l;
	int i;

	if (driver == NULL)
		return NULL;

	sim = g_try_new0(struct ofono_sim, 1);

	if (sim == NULL)
		return NULL;

	sim->phase = OFONO_SIM_PHASE_UNKNOWN;
	sim->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_SIM,
						sim_remove, sim);

	for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
		sim->pin_retries[i] = -1;

	for (l = g_drivers; l; l = l->next) {
		const struct ofono_sim_driver *drv = l->data;

		if (g_strcmp0(drv->name, driver))
			continue;

		if (drv->probe(sim, vendor, data) < 0)
			continue;

		sim->driver = drv;
		break;
	}

	return sim;
}

static void emulator_cnum_cb(struct ofono_emulator *em,
			struct ofono_emulator_request *req, void *userdata)
{
	struct ofono_sim *sim = userdata;
	struct ofono_error result;
	GSList *l;
	const char *phone;
	/*
	 * '+CNUM: ,"+",,,4' + phone number + phone type on 3 digits max
	 * + terminating null
	 */
	char buf[OFONO_MAX_PHONE_NUMBER_LENGTH + 18 + 1];

	result.error = 0;

	switch (ofono_emulator_request_get_type(req)) {
	case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY:
		for (l = sim->own_numbers; l; l = l->next) {
			struct ofono_phone_number *ph = l->data;

			phone = phone_number_to_string(ph);
			sprintf(buf, "+CNUM: ,\"%s\",%d,,4", phone, ph->type);
			ofono_emulator_send_info(em, buf, l->next == NULL ?
							TRUE : FALSE);
		}

		result.type = OFONO_ERROR_TYPE_NO_ERROR;
		ofono_emulator_send_final(em, &result);
		break;

	default:
		result.type = OFONO_ERROR_TYPE_FAILURE;
		ofono_emulator_send_final(em, &result);
	};
}

static void emulator_hfp_watch(struct ofono_atom *atom,
				enum ofono_atom_watch_condition cond,
				void *data)
{
	struct ofono_emulator *em = __ofono_atom_get_data(atom);

	if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED)
		ofono_emulator_add_handler(em, "+CNUM", emulator_cnum_cb, data,
						NULL);
}

void ofono_sim_register(struct ofono_sim *sim)
{
	DBusConnection *conn = ofono_dbus_get_connection();
	struct ofono_modem *modem = __ofono_atom_get_modem(sim->atom);
	const char *path = __ofono_atom_get_path(sim->atom);

	if (!g_dbus_register_interface(conn, path,
					OFONO_SIM_MANAGER_INTERFACE,
					sim_methods, sim_signals, NULL,
					sim, NULL)) {
		ofono_error("Could not create %s interface",
				OFONO_SIM_MANAGER_INTERFACE);

		return;
	}

	ofono_modem_add_interface(modem, OFONO_SIM_MANAGER_INTERFACE);
	sim->state_watches = __ofono_watchlist_new(g_free);
	sim->spn_watches = __ofono_watchlist_new(g_free);
	sim->simfs = sim_fs_new(sim, sim->driver);

	ofono_sim_add_state_watch(sim, sim_ready, sim, NULL);

	if (sim->state > OFONO_SIM_STATE_NOT_PRESENT)
		sim_initialize(sim);

	sim->hfp_watch = __ofono_modem_add_atom_watch(modem,
					OFONO_ATOM_TYPE_EMULATOR_HFP,
					emulator_hfp_watch, sim, NULL);

	__ofono_atom_register(sim->atom, sim_unregister);
}

void ofono_sim_remove(struct ofono_sim *sim)
{
	__ofono_atom_free(sim->atom);
}

void ofono_sim_set_data(struct ofono_sim *sim, void *data)
{
	sim->driver_data = data;
}

void *ofono_sim_get_data(struct ofono_sim *sim)
{
	return sim->driver_data;
}

static ofono_bool_t is_valid_pin(const char *pin, unsigned int min,
					unsigned int max)
{
	unsigned int i;

	/* Pin must not be empty */
	if (pin == NULL || pin[0] == '\0')
		return FALSE;

	i = strlen(pin);
	if (i != strspn(pin, "0123456789"))
		return FALSE;

	if (min <= i && i <= max)
		return TRUE;

	return FALSE;
}

ofono_bool_t __ofono_is_valid_sim_pin(const char *pin,
					enum ofono_sim_password_type type)
{
	switch (type) {
	case OFONO_SIM_PASSWORD_SIM_PIN:
	case OFONO_SIM_PASSWORD_SIM_PIN2:
		/* 11.11 Section 9.3 ("CHV"): 4..8 IA-5 digits */
		return is_valid_pin(pin, 4, 8);
		break;
	case OFONO_SIM_PASSWORD_PHSIM_PIN:
	case OFONO_SIM_PASSWORD_PHFSIM_PIN:
	case OFONO_SIM_PASSWORD_PHNET_PIN:
	case OFONO_SIM_PASSWORD_PHNETSUB_PIN:
	case OFONO_SIM_PASSWORD_PHSP_PIN:
	case OFONO_SIM_PASSWORD_PHCORP_PIN:
		/* 22.022 Section 14 4..16 IA-5 digits */
		return is_valid_pin(pin, 4, 16);
		break;
	case OFONO_SIM_PASSWORD_SIM_PUK:
	case OFONO_SIM_PASSWORD_SIM_PUK2:
	case OFONO_SIM_PASSWORD_PHFSIM_PUK:
	case OFONO_SIM_PASSWORD_PHNET_PUK:
	case OFONO_SIM_PASSWORD_PHNETSUB_PUK:
	case OFONO_SIM_PASSWORD_PHSP_PUK:
	case OFONO_SIM_PASSWORD_PHCORP_PUK:
		/* 11.11 Section 9.3 ("UNBLOCK CHV"), 8 IA-5 digits */
		return is_valid_pin(pin, 8, 8);
		break;
	case OFONO_SIM_PASSWORD_NONE:
	case OFONO_SIM_PASSWORD_INVALID:
		break;
	}

	return FALSE;
}

ofono_bool_t __ofono_is_valid_net_pin(const char *pin)
{
	return is_valid_pin(pin, 4, 4);
}

static void sim_file_changed_flush(struct ofono_sim *sim, int id)
{
	int i, imgid;

	if (id == SIM_EFIMG_FILEID)
		/* All cached images become invalid */
		sim_fs_image_cache_flush(sim->simfs);
	else if (sim->efimg) {
		/*
		 * Data and CLUT for image instances stored in the changed
		 * file need to be re-read.
		 */
		for (i = sim->efimg_length / 9 - 1; i >= 0; i--) {
			imgid = (sim->efimg[i * 9 + 3] << 8) |
				sim->efimg[i * 9 + 4];

			if (imgid == id)
				sim_fs_image_cache_flush_file(sim->simfs, i);
		}
	}

	sim_fs_cache_flush_file(sim->simfs, id);
}

void __ofono_sim_refresh(struct ofono_sim *sim, GSList *file_list,
			ofono_bool_t full_file_change, ofono_bool_t naa_init)
{
	GSList *l;
	gboolean reinit_naa = naa_init || full_file_change;

	/*
	 * Check if any files used in SIM initialisation procedure
	 * are affected, except EFiccid, EFpl, EFli.
	 */
	for (l = file_list; l; l = l->next) {
		struct stk_file *file = l->data;
		uint32_t mf, df, ef;

		if (file->len != 6)
			continue;

		mf = (file->file[0] << 8) | (file->file[1] << 0);
		df = (file->file[2] << 8) | (file->file[3] << 0);
		ef = (file->file[4] << 8) | (file->file[5] << 0);

		if (mf != 0x3f00)
			continue;

		/*
		 * 8.18: "the path '3F007FFF' indicates the relevant
		 * NAA Application dedicated file;".
		 */
		if (df == 0x7fff)
			df = 0x7f20;

#define DFGSM (0x7f20 << 16)
#define DFTEL (0x7f10 << 16)

		switch ((df << 16) | ef) {
		case DFGSM | SIM_EFEST_FILEID:
		case DFGSM | SIM_EFUST_FILEID: /* aka. EFSST */
		case DFGSM | SIM_EFPHASE_FILEID:
		case DFGSM | SIM_EFAD_FILEID:
		case DFTEL | SIM_EFBDN_FILEID:
		case DFTEL | SIM_EFADN_FILEID:
		case DFGSM | SIM_EF_CPHS_INFORMATION_FILEID:
			reinit_naa = TRUE;
			break;
		}
	}

	/* Flush cached content for affected files */
	if (full_file_change)
		sim_fs_cache_flush(sim->simfs);
	else {
		for (l = file_list; l; l = l->next) {
			struct stk_file *file = l->data;
			int id = (file->file[file->len - 2] << 8) |
				(file->file[file->len - 1] << 0);

			sim_file_changed_flush(sim, id);
		}
	}

	if (reinit_naa) {
		sim->state = OFONO_SIM_STATE_RESETTING;
		__ofono_modem_sim_reset(__ofono_atom_get_modem(sim->atom));

		/* Force the sim state out of READY */
		sim_free_main_state(sim);
		call_state_watches(sim);
	}

	/*
	 * Notify the subscribers of files that have changed and who
	 * haven't unsubsribed during the SIM state change.
	 */
	if (full_file_change)
		sim_fs_notify_file_watches(sim->simfs, -1);
	else {
		for (l = file_list; l; l = l->next) {
			struct stk_file *file = l->data;
			int id = (file->file[file->len - 2] << 8) |
				(file->file[file->len - 1] << 0);

			sim_fs_notify_file_watches(sim->simfs, id);
		}
	}
}

const char *__ofono_sim_get_impi(struct ofono_sim *sim)
{
	return sim->impi;
}

static void open_channel_cb(const struct ofono_error *error, int session_id,
		void *data);

static void close_channel_cb(const struct ofono_error *error, void *data)
{
	struct ofono_sim_aid_session *session = data;

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
		DBG("session %d failed to close", session->session_id);

	if (g_slist_length(session->watches->items) > 0 &&
				session->state == SESSION_STATE_OPENING) {
		/*
		 * An atom requested to open during a close, we can re-open
		 * here.
		 */
		session->sim->driver->open_channel(session->sim,
				session->record->aid, open_channel_cb,
				session);
		return;
	}

	session->state = SESSION_STATE_INACTIVE;
}

static void open_channel_cb(const struct ofono_error *error, int session_id,
		void *data)
{
	struct ofono_sim_aid_session *session = data;
	GSList *iter = session->watches->items;
	ofono_bool_t active = TRUE;

	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
		session->state = SESSION_STATE_INACTIVE;
		session->session_id = 0;
		active = FALSE;
		goto end;
	}

	if (g_slist_length(iter) == 0) {
		/*
		 * All watchers stopped watching before the channel could open.
		 * Close the channel.
		 */
		session->state = SESSION_STATE_CLOSING;
		session->sim->driver->close_channel(session->sim,
				session->session_id, close_channel_cb, session);
		return;
	}

	session->session_id = session_id;
	session->state = SESSION_STATE_OPEN;
end:
	/*
	 * Notify any watchers, after this point, all future watchers will be
	 * immediately notified with the session ID.
	 */
	while (iter) {
		struct ofono_watchlist_item *item = iter->data;
		ofono_sim_session_event_cb_t notify = item->notify;

		notify(active, session->session_id, item->notify_data);

		iter = g_slist_next(iter);
	}
}

unsigned int __ofono_sim_add_session_watch(
		struct ofono_sim_aid_session *session,
		ofono_sim_session_event_cb_t notify, void *data,
		ofono_destroy_func destroy)
{
	struct ofono_watchlist_item *item;

	DBG("%p", session);

	if (session == NULL)
		return 0;

	if (notify == NULL)
		return 0;

	item = g_new0(struct ofono_watchlist_item, 1);

	item->notify = notify;
	item->destroy = destroy;
	item->notify_data = data;

	if (g_slist_length(session->watches->items) == 0 &&
			session->state == SESSION_STATE_INACTIVE) {
		/*
		 * If the session is inactive and there are no watchers, open
		 * a new session.
		 */
		session->state = SESSION_STATE_OPENING;
		session->sim->driver->open_channel(session->sim,
				session->record->aid, open_channel_cb,
				session);
	} else if (session->state == SESSION_STATE_OPEN) {
		/*
		 * Session is already open and available, just call the
		 * notify callback immediately.
		 */
		notify(TRUE, session->session_id, data);
	} else if (session->state == SESSION_STATE_CLOSING) {
		/*
		 * There is a pending close, the close callback will handle
		 * re-opening the session.
		 */
		session->state = SESSION_STATE_OPENING;
	}

	return __ofono_watchlist_add_item(session->watches, item);
}

void __ofono_sim_remove_session_watch(struct ofono_sim_aid_session *session,
		unsigned int id)
{
	__ofono_watchlist_remove_item(session->watches, id);

	if (g_slist_length(session->watches->items) == 0) {
		/* last watcher, close session */
		session->state = SESSION_STATE_CLOSING;
		session->sim->driver->close_channel(session->sim,
				session->session_id, close_channel_cb, session);
	}
}

struct ofono_sim_aid_session *__ofono_sim_get_session_by_aid(
		struct ofono_sim *sim, unsigned char *aid)
{
	GSList *iter = sim->aid_sessions;

	while (iter) {
		struct ofono_sim_aid_session *session = iter->data;

		if (!memcmp(session->record->aid, aid, 16))
			return session;

		iter = g_slist_next(iter);
	}

	return NULL;
}

struct ofono_sim_aid_session *__ofono_sim_get_session_by_type(
		struct ofono_sim *sim, enum sim_app_type type)
{
	GSList *iter = sim->aid_sessions;

	while (iter) {
		struct ofono_sim_aid_session *session = iter->data;

		if (session->record->type == type)
			return session;

		iter = g_slist_next(iter);
	}

	return NULL;
}

int __ofono_sim_session_get_id(struct ofono_sim_aid_session *session)
{
	return session->session_id;
}

enum sim_app_type __ofono_sim_session_get_type(
		struct ofono_sim_aid_session *session)
{
	return session->record->type;
}

unsigned char *__ofono_sim_session_get_aid(
		struct ofono_sim_aid_session *session)
{
	return session->record->aid;
}

GSList *__ofono_sim_get_aid_list(struct ofono_sim *sim)
{
	return sim->aid_list;
}

int ofono_sim_logical_access(struct ofono_sim *sim, int session_id,
		unsigned char *pdu, unsigned int len,
		ofono_sim_logical_access_cb_t cb, void *data)
{
	if (!sim->driver->logical_access)
		return -ENOTSUP;

	if (session_id <= 0)
		return -EINVAL;

	sim->driver->logical_access(sim, session_id, pdu, len, cb, data);

	return 0;
}
