blob: 52446907736a71cefc8ac23f20cfc3b6e015b488 [file] [log] [blame]
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2011 Red Hat, Inc.
* Copyright (C) 2011 Google, Inc.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <ModemManager.h>
#include <libmm-common.h>
#include "mm-iface-modem.h"
#include "mm-sim.h"
#include "mm-base-modem-at.h"
#include "mm-base-modem.h"
#include "mm-utils.h"
#include "mm-log.h"
#include "mm-modem-helpers.h"
#include "mm-marshal.h"
static void async_initable_iface_init (GAsyncInitableIface *iface);
G_DEFINE_TYPE_EXTENDED (MMSim, mm_sim, MM_GDBUS_TYPE_SIM_SKELETON, 0,
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
async_initable_iface_init));
enum {
PROP_0,
PROP_PATH,
PROP_CONNECTION,
PROP_MODEM,
PROP_LAST
};
enum {
SIGNAL_PIN_LOCK_ENABLED,
SIGNAL_LAST
};
static GParamSpec *properties[PROP_LAST];
struct _MMSimPrivate {
/* The connection to the system bus */
GDBusConnection *connection;
/* The modem which owns this SIM */
MMBaseModem *modem;
/* The path where the SIM object is exported */
gchar *path;
};
static guint signals[SIGNAL_LAST] = { 0 };
/*****************************************************************************/
void
mm_sim_export (MMSim *self)
{
static guint id = 0;
gchar *path;
path = g_strdup_printf (MM_DBUS_SIM_PREFIX "/%d", id++);
g_object_set (self,
MM_SIM_PATH, path,
NULL);
g_free (path);
}
/*****************************************************************************/
/* CHANGE PIN (Generic implementation) */
static gboolean
change_pin_finish (MMSim *self,
GAsyncResult *res,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
static void
change_pin_ready (MMBaseModem *modem,
GAsyncResult *res,
GSimpleAsyncResult *simple)
{
GError *error = NULL;
mm_base_modem_at_command_finish (modem, res, &error);
if (error)
g_simple_async_result_take_error (simple, error);
else
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
g_simple_async_result_complete (simple);
g_object_unref (simple);
}
static void
change_pin (MMSim *self,
const gchar *old_pin,
const gchar *new_pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *result;
gchar *command;
result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
change_pin);
command = g_strdup_printf ("+CPWD=\"SC\",\"%s\",\"%s\"",
old_pin,
new_pin);
mm_base_modem_at_command (MM_BASE_MODEM (self->priv->modem),
command,
3,
FALSE,
(GAsyncReadyCallback)change_pin_ready,
result);
g_free (command);
}
/*****************************************************************************/
/* CHANGE PIN (DBus call handling) */
typedef struct {
MMSim *self;
GDBusMethodInvocation *invocation;
gchar *old_pin;
gchar *new_pin;
} HandleChangePinContext;
static void
handle_change_pin_context_free (HandleChangePinContext *ctx)
{
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_free (ctx->old_pin);
g_free (ctx->new_pin);
g_free (ctx);
}
static void
after_change_unlock_retries_ready (MMIfaceModem *self,
GAsyncResult *res,
HandleChangePinContext *ctx)
{
/* We just want to ensure that we tried to update the unlock
* retries, no big issue if it failed */
mm_iface_modem_update_unlock_retries_finish (self, res, NULL);
mm_gdbus_sim_complete_change_pin (MM_GDBUS_SIM (ctx->self), ctx->invocation);
handle_change_pin_context_free (ctx);
}
static void
handle_change_pin_ready (MMSim *self,
GAsyncResult *res,
HandleChangePinContext *ctx)
{
GError *error = NULL;
MM_SIM_GET_CLASS (self)->change_pin_finish (self, res, &error);
if (error) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_change_pin_context_free (ctx);
return;
}
/* Before returning success, ensure that we get the unlock retry
* counts updated properly. */
mm_iface_modem_update_unlock_retries (MM_IFACE_MODEM (self->priv->modem),
(GAsyncReadyCallback)after_change_unlock_retries_ready,
ctx);
}
static void
handle_change_pin_auth_ready (MMBaseModem *modem,
GAsyncResult *res,
HandleChangePinContext *ctx)
{
GError *error = NULL;
if (!mm_base_modem_authorize_finish (modem, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_change_pin_context_free (ctx);
return;
}
/* If changing PIN is not implemented, report an error */
if (!MM_SIM_GET_CLASS (ctx->self)->change_pin ||
!MM_SIM_GET_CLASS (ctx->self)->change_pin_finish) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot change PIN: "
"operation not supported");
handle_change_pin_context_free (ctx);
return;
}
MM_SIM_GET_CLASS (ctx->self)->change_pin (ctx->self,
ctx->old_pin,
ctx->new_pin,
(GAsyncReadyCallback)handle_change_pin_ready,
ctx);
}
static gboolean
handle_change_pin (MMSim *self,
GDBusMethodInvocation *invocation,
const gchar *old_pin,
const gchar *new_pin,
gboolean changed)
{
HandleChangePinContext *ctx;
ctx = g_new0 (HandleChangePinContext, 1);
ctx->self = g_object_ref (self);
ctx->invocation = g_object_ref (invocation);
ctx->old_pin = g_strdup (old_pin);
ctx->new_pin = g_strdup (new_pin);
mm_base_modem_authorize (self->priv->modem,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)handle_change_pin_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
/* ENABLE PIN (Generic implementation) */
static gboolean
enable_pin_finish (MMSim *self,
GAsyncResult *res,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
static void
enable_pin_ready (MMBaseModem *modem,
GAsyncResult *res,
GSimpleAsyncResult *simple)
{
GError *error = NULL;
mm_base_modem_at_command_finish (modem, res, &error);
if (error)
g_simple_async_result_take_error (simple, error);
else
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
g_simple_async_result_complete (simple);
g_object_unref (simple);
}
static void
enable_pin (MMSim *self,
const gchar *pin,
gboolean enabled,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *result;
gchar *command;
result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
enable_pin);
command = g_strdup_printf ("+CLCK=\"SC\",%d,\"%s\"",
enabled ? 1 : 0,
pin);
mm_base_modem_at_command (MM_BASE_MODEM (self->priv->modem),
command,
3,
FALSE,
(GAsyncReadyCallback)enable_pin_ready,
result);
g_free (command);
}
/*****************************************************************************/
/* ENABLE PIN (DBus call handling) */
typedef struct {
MMSim *self;
GDBusMethodInvocation *invocation;
gchar *pin;
gboolean enabled;
} HandleEnablePinContext;
static void
handle_enable_pin_context_free (HandleEnablePinContext *ctx)
{
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_free (ctx->pin);
g_free (ctx);
}
static void
after_enable_unlock_retries_ready (MMIfaceModem *self,
GAsyncResult *res,
HandleEnablePinContext *ctx)
{
/* We just want to ensure that we tried to update the unlock
* retries, no big issue if it failed */
mm_iface_modem_update_unlock_retries_finish (self, res, NULL);
mm_gdbus_sim_complete_enable_pin (MM_GDBUS_SIM (ctx->self), ctx->invocation);
handle_enable_pin_context_free (ctx);
}
static void
handle_enable_pin_ready (MMSim *self,
GAsyncResult *res,
HandleEnablePinContext *ctx)
{
GError *error = NULL;
MM_SIM_GET_CLASS (self)->enable_pin_finish (self, res, &error);
if (error) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_enable_pin_context_free (ctx);
return;
}
/* Signal about the new lock state */
g_signal_emit (self, signals[SIGNAL_PIN_LOCK_ENABLED], 0, ctx->enabled);
/* Before returning success, ensure that we get the unlock retry
* counts updated properly. */
mm_iface_modem_update_unlock_retries (MM_IFACE_MODEM (self->priv->modem),
(GAsyncReadyCallback)after_enable_unlock_retries_ready,
ctx);
}
static void
handle_enable_pin_auth_ready (MMBaseModem *modem,
GAsyncResult *res,
HandleEnablePinContext *ctx)
{
GError *error = NULL;
if (!mm_base_modem_authorize_finish (modem, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_enable_pin_context_free (ctx);
return;
}
/* If changing PIN is not implemented, report an error */
if (!MM_SIM_GET_CLASS (ctx->self)->enable_pin ||
!MM_SIM_GET_CLASS (ctx->self)->enable_pin_finish) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot enable/disable PIN: "
"operation not supported");
handle_enable_pin_context_free (ctx);
return;
}
MM_SIM_GET_CLASS (ctx->self)->enable_pin (ctx->self,
ctx->pin,
ctx->enabled,
(GAsyncReadyCallback)handle_enable_pin_ready,
ctx);
}
static gboolean
handle_enable_pin (MMSim *self,
GDBusMethodInvocation *invocation,
const gchar *pin,
gboolean enabled)
{
HandleEnablePinContext *ctx;
ctx = g_new0 (HandleEnablePinContext, 1);
ctx->self = g_object_ref (self);
ctx->invocation = g_object_ref (invocation);
ctx->pin = g_strdup (pin);
ctx->enabled = enabled;
mm_base_modem_authorize (self->priv->modem,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)handle_enable_pin_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
/* SEND PIN/PUK (Generic implementation) */
static gboolean
common_send_pin_puk_finish (MMSim *self,
GAsyncResult *res,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
static void
send_pin_puk_ready (MMBaseModem *modem,
GAsyncResult *res,
GSimpleAsyncResult *simple)
{
GError *error = NULL;
mm_base_modem_at_command_finish (modem, res, &error);
if (error)
g_simple_async_result_take_error (simple, error);
else
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
g_simple_async_result_complete (simple);
g_object_unref (simple);
}
static void
common_send_pin_puk (MMSim *self,
const gchar *pin,
const gchar *puk,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *result;
gchar *command;
result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
common_send_pin_puk);
command = (puk ?
g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin) :
g_strdup_printf ("+CPIN=\"%s\"", pin));
mm_base_modem_at_command (MM_BASE_MODEM (self->priv->modem),
command,
3,
FALSE,
(GAsyncReadyCallback)send_pin_puk_ready,
result);
g_free (command);
}
static void
send_puk (MMSim *self,
const gchar *puk,
const gchar *new_pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
common_send_pin_puk (self, new_pin, puk, callback, user_data);
}
static void
send_pin (MMSim *self,
const gchar *pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
common_send_pin_puk (self, pin, NULL, callback, user_data);
}
/*****************************************************************************/
/* SEND PIN/PUK (common logic) */
typedef struct {
MMSim *self;
GSimpleAsyncResult *result;
GError *save_error;
gulong wait_for_unlock_id;
} SendPinPukContext;
static void
send_pin_puk_context_complete_and_free (SendPinPukContext *ctx)
{
if (ctx->wait_for_unlock_id)
g_signal_handler_disconnect (ctx->self->priv->modem,
ctx->wait_for_unlock_id);
if (ctx->save_error)
g_error_free (ctx->save_error);
g_simple_async_result_complete (ctx->result);
g_object_unref (ctx->result);
g_object_unref (ctx->self);
g_free (ctx);
}
static GError *
error_for_unlock_check (MMModemLock lock)
{
static const MMMobileEquipmentError errors_for_locks [] = {
MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, /* MM_MODEM_LOCK_UNKNOWN */
MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, /* MM_MODEM_LOCK_NONE */
MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN, /* MM_MODEM_LOCK_SIM_PIN */
MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN2, /* MM_MODEM_LOCK_SIM_PIN2 */
MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK, /* MM_MODEM_LOCK_SIM_PUK */
MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK2, /* MM_MODEM_LOCK_SIM_PUK2 */
MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PIN, /* MM_MODEM_LOCK_PH_SP_PIN */
MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PUK, /* MM_MODEM_LOCK_PH_SP_PUK */
MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PIN, /* MM_MODEM_LOCK_PH_NET_PIN */
MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PUK, /* MM_MODEM_LOCK_PH_NET_PUK */
MM_MOBILE_EQUIPMENT_ERROR_PH_SIM_PIN, /* MM_MODEM_LOCK_PH_SIM_PIN */
MM_MOBILE_EQUIPMENT_ERROR_CORP_PIN, /* MM_MODEM_LOCK_PH_CORP_PIN */
MM_MOBILE_EQUIPMENT_ERROR_CORP_PUK, /* MM_MODEM_LOCK_PH_CORP_PUK */
MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PIN, /* MM_MODEM_LOCK_PH_FSIM_PIN */
MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PUK, /* MM_MODEM_LOCK_PH_FSIM_PUK */
MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PIN, /* MM_MODEM_LOCK_PH_NETSUB_PIN */
MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PUK, /* MM_MODEM_LOCK_PH_NETSUB_PUK */
};
g_assert (lock >= MM_MODEM_LOCK_UNKNOWN);
g_assert (lock <= MM_MODEM_LOCK_PH_NETSUB_PUK);
return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
errors_for_locks[lock],
"Device is locked: '%s'",
mm_modem_lock_get_string (lock));
}
gboolean
mm_sim_send_pin_finish (MMSim *self,
GAsyncResult *res,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
gboolean
mm_sim_send_puk_finish (MMSim *self,
GAsyncResult *res,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
static void
unlock_check_ready (MMIfaceModem *modem,
GAsyncResult *res,
SendPinPukContext *ctx)
{
GError *error = NULL;
MMModemLock lock;
lock = mm_iface_modem_unlock_check_finish (modem, res, &error);
if (lock != MM_MODEM_LOCK_NONE) {
/* Device is locked. Now:
* - If we got an error in the original send-pin action, report it.
* - If we got an error in the pin-check action, report it.
* - Otherwise, build our own error from the lock code.
*/
if (ctx->save_error) {
g_simple_async_result_take_error (ctx->result, ctx->save_error);
ctx->save_error = NULL;
g_clear_error (&error);
} else if (error)
g_simple_async_result_take_error (ctx->result, error);
else
g_simple_async_result_take_error (ctx->result,
error_for_unlock_check (lock));
send_pin_puk_context_complete_and_free (ctx);
return;
}
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
send_pin_puk_context_complete_and_free (ctx);
}
static void
send_pin_ready (MMSim *self,
GAsyncResult *res,
SendPinPukContext *ctx)
{
MM_SIM_GET_CLASS (self)->send_pin_finish (self, res, &ctx->save_error);
/* Once pin/puk has been sent, recheck lock */
mm_iface_modem_unlock_check (MM_IFACE_MODEM (self->priv->modem),
(GAsyncReadyCallback)unlock_check_ready,
ctx);
}
static void
send_puk_ready (MMSim *self,
GAsyncResult *res,
SendPinPukContext *ctx)
{
MM_SIM_GET_CLASS (self)->send_puk_finish (self, res, &ctx->save_error);
/* Once pin/puk has been sent, recheck lock */
mm_iface_modem_unlock_check (MM_IFACE_MODEM (self->priv->modem),
(GAsyncReadyCallback)unlock_check_ready,
ctx);
}
void
mm_sim_send_pin (MMSim *self,
const gchar *pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
SendPinPukContext *ctx;
/* If sending PIN is not implemented, report an error */
if (!MM_SIM_GET_CLASS (self)->send_pin ||
!MM_SIM_GET_CLASS (self)->send_pin_finish) {
g_simple_async_report_error_in_idle (G_OBJECT (self),
callback,
user_data,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot send PIN: "
"operation not supported");
return;
}
ctx = g_new0 (SendPinPukContext, 1);
ctx->self = g_object_ref (self);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
mm_sim_send_pin);
MM_SIM_GET_CLASS (self)->send_pin (self,
pin,
(GAsyncReadyCallback)send_pin_ready,
ctx);
}
void
mm_sim_send_puk (MMSim *self,
const gchar *puk,
const gchar *new_pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
SendPinPukContext *ctx;
/* If sending PIN is not implemented, report an error */
if (!MM_SIM_GET_CLASS (self)->send_puk ||
!MM_SIM_GET_CLASS (self)->send_puk_finish) {
g_simple_async_report_error_in_idle (G_OBJECT (self),
callback,
user_data,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot send PUK: "
"operation not supported");
return;
}
ctx = g_new0 (SendPinPukContext, 1);
ctx->self = g_object_ref (self);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
mm_sim_send_puk);
MM_SIM_GET_CLASS (self)->send_puk (self,
puk,
new_pin,
(GAsyncReadyCallback)send_puk_ready,
ctx);
}
/*****************************************************************************/
/* SEND PIN (DBus call handling) */
typedef struct {
MMSim *self;
GDBusMethodInvocation *invocation;
gchar *pin;
} HandleSendPinContext;
static void
handle_send_pin_context_free (HandleSendPinContext *ctx)
{
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_free (ctx->pin);
g_free (ctx);
}
static void
handle_send_pin_ready (MMSim *self,
GAsyncResult *res,
HandleSendPinContext *ctx)
{
GError *error = NULL;
if (!mm_sim_send_pin_finish (self, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else
mm_gdbus_sim_complete_send_pin (MM_GDBUS_SIM (self), ctx->invocation);
handle_send_pin_context_free (ctx);
}
static void
handle_send_pin_auth_ready (MMBaseModem *modem,
GAsyncResult *res,
HandleSendPinContext *ctx)
{
GError *error = NULL;
if (!mm_base_modem_authorize_finish (modem, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_send_pin_context_free (ctx);
return;
}
mm_sim_send_pin (ctx->self,
ctx->pin,
(GAsyncReadyCallback)handle_send_pin_ready,
ctx);
}
static gboolean
handle_send_pin (MMSim *self,
GDBusMethodInvocation *invocation,
const gchar *pin)
{
HandleSendPinContext *ctx;
ctx = g_new0 (HandleSendPinContext, 1);
ctx->self = g_object_ref (self);
ctx->invocation = g_object_ref (invocation);
ctx->pin = g_strdup (pin);
mm_base_modem_authorize (self->priv->modem,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)handle_send_pin_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
/* SEND PUK (DBus call handling) */
typedef struct {
MMSim *self;
GDBusMethodInvocation *invocation;
gchar *puk;
gchar *new_pin;
} HandleSendPukContext;
static void
handle_send_puk_context_free (HandleSendPukContext *ctx)
{
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_free (ctx->puk);
g_free (ctx->new_pin);
g_free (ctx);
}
static void
handle_send_puk_ready (MMSim *self,
GAsyncResult *res,
HandleSendPukContext *ctx)
{
GError *error = NULL;
if (!mm_sim_send_puk_finish (self, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else
mm_gdbus_sim_complete_send_puk (MM_GDBUS_SIM (self), ctx->invocation);
handle_send_puk_context_free (ctx);
}
static void
handle_send_puk_auth_ready (MMBaseModem *modem,
GAsyncResult *res,
HandleSendPukContext *ctx)
{
GError *error = NULL;
if (!mm_base_modem_authorize_finish (modem, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_send_puk_context_free (ctx);
return;
}
mm_sim_send_puk (ctx->self,
ctx->puk,
ctx->new_pin,
(GAsyncReadyCallback)handle_send_puk_ready,
ctx);
}
static gboolean
handle_send_puk (MMSim *self,
GDBusMethodInvocation *invocation,
const gchar *puk,
const gchar *new_pin)
{
HandleSendPukContext *ctx;
ctx = g_new0 (HandleSendPukContext, 1);
ctx->self = g_object_ref (self);
ctx->invocation = g_object_ref (invocation);
ctx->puk = g_strdup (puk);
ctx->new_pin = g_strdup (new_pin);
mm_base_modem_authorize (self->priv->modem,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)handle_send_puk_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
static void
mm_sim_dbus_export (MMSim *self)
{
GError *error = NULL;
/* Handle method invocations */
g_signal_connect (self,
"handle-change-pin",
G_CALLBACK (handle_change_pin),
NULL);
g_signal_connect (self,
"handle-enable-pin",
G_CALLBACK (handle_enable_pin),
NULL);
g_signal_connect (self,
"handle-send-pin",
G_CALLBACK (handle_send_pin),
NULL);
g_signal_connect (self,
"handle-send-puk",
G_CALLBACK (handle_send_puk),
NULL);
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self),
self->priv->connection,
self->priv->path,
&error)) {
mm_warn ("couldn't export SIM at '%s': '%s'",
self->priv->path,
error->message);
g_error_free (error);
}
}
static void
mm_sim_dbus_unexport (MMSim *self)
{
/* Only unexport if currently exported */
if (g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self)))
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self));
}
/*****************************************************************************/
const gchar *
mm_sim_get_path (MMSim *self)
{
return self->priv->path;
}
/*****************************************************************************/
#undef STR_REPLY_READY_FN
#define STR_REPLY_READY_FN(NAME) \
static void \
NAME##_command_ready (MMBaseModem *modem, \
GAsyncResult *res, \
GSimpleAsyncResult *operation_result) \
{ \
GError *error = NULL; \
const gchar *response; \
\
response = mm_base_modem_at_command_finish (modem, res, &error); \
if (error) \
g_simple_async_result_take_error (operation_result, error); \
else \
g_simple_async_result_set_op_res_gpointer (operation_result, \
(gpointer)response, \
NULL); \
\
g_simple_async_result_complete (operation_result); \
g_object_unref (operation_result); \
}
/*****************************************************************************/
/* SIM IDENTIFIER */
static gchar *
parse_iccid (const gchar *response,
GError **error)
{
gchar buf[21];
gchar swapped[21];
const gchar *str;
gint sw1;
gint sw2;
gboolean success = FALSE;
memset (buf, 0, sizeof (buf));
str = mm_strip_tag (response, "+CRSM:");
if (sscanf (str, "%d,%d,\"%20c\"", &sw1, &sw2, (char *) &buf) == 3)
success = TRUE;
else {
/* May not include quotes... */
if (sscanf (str, "%d,%d,%20c", &sw1, &sw2, (char *) &buf) == 3)
success = TRUE;
}
if (!success) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Could not parse the CRSM response");
return NULL;
}
if ((sw1 == 0x90 && sw2 == 0x00) ||
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
gsize len = 0;
gint f_pos = -1;
gint i;
/* Make sure the buffer is only digits or 'F' */
for (len = 0; len < sizeof (buf) && buf[len]; len++) {
if (isdigit (buf[len]))
continue;
if (buf[len] == 'F' || buf[len] == 'f') {
buf[len] = 'F'; /* canonicalize the F */
f_pos = len;
continue;
}
if (buf[len] == '\"') {
buf[len] = 0;
break;
}
/* Invalid character */
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"CRSM ICCID response contained invalid character '%c'",
buf[len]);
return NULL;
}
/* BCD encoded ICCIDs are 20 digits long */
if (len != 20) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Invalid +CRSM ICCID response size (was %zd, expected 20)",
len);
return NULL;
}
/* Ensure if there's an 'F' that it's second-to-last */
if ((f_pos >= 0) && (f_pos != len - 2)) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Invalid +CRSM ICCID length (unexpected F)");
return NULL;
}
/* Swap digits in the EFiccid response to get the actual ICCID, each
* group of 2 digits is reversed in the +CRSM response. i.e.:
*
* 21436587 -> 12345678
*/
memset (swapped, 0, sizeof (swapped));
for (i = 0; i < 10; i++) {
swapped[i * 2] = buf[(i * 2) + 1];
swapped[(i * 2) + 1] = buf[i * 2];
}
/* Zero out the F for 19 digit ICCIDs */
if (swapped[len - 1] == 'F')
swapped[len - 1] = 0;
return g_strdup (swapped);
} else {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"SIM failed to handle CRSM request (sw1 %d sw2 %d)",
sw1, sw2);
return NULL;
}
}
static gchar *
load_sim_identifier_finish (MMSim *self,
GAsyncResult *res,
GError **error)
{
const gchar *result;
gchar *sim_identifier;
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return NULL;
result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
sim_identifier = parse_iccid (result, error);
if (!sim_identifier)
return NULL;
mm_dbg ("loaded SIM identifier: %s", sim_identifier);
return sim_identifier;
}
STR_REPLY_READY_FN (load_sim_identifier)
static void
load_sim_identifier (MMSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_dbg ("loading SIM identifier...");
/* READ BINARY of EFiccid (ICC Identification) ETSI TS 102.221 section 13.2 */
mm_base_modem_at_command (
MM_BASE_MODEM (self->priv->modem),
"+CRSM=176,12258,0,0,10",
20,
FALSE,
(GAsyncReadyCallback)load_sim_identifier_command_ready,
g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
load_sim_identifier));
}
/*****************************************************************************/
/* IMSI */
static gchar *
load_imsi_finish (MMSim *self,
GAsyncResult *res,
GError **error)
{
gchar *imsi;
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return NULL;
imsi = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
mm_dbg ("loaded IMSI: %s", imsi);
return imsi;
}
STR_REPLY_READY_FN (load_imsi)
static void
load_imsi (MMSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_dbg ("loading IMSI...");
mm_base_modem_at_command (
MM_BASE_MODEM (self->priv->modem),
"+CIMI",
3,
TRUE,
(GAsyncReadyCallback)load_imsi_command_ready,
g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
load_imsi));
}
/*****************************************************************************/
/* Operator ID */
static guint
parse_mnc_length (const gchar *response,
GError **error)
{
gint sw1;
gint sw2;
gboolean success = FALSE;
gchar hex[51];
memset (hex, 0, sizeof (hex));
if (sscanf (response, "+CRSM:%d,%d,\"%50c\"", &sw1, &sw2, (char *) &hex) == 3)
success = TRUE;
else {
/* May not include quotes... */
if (sscanf (response, "+CRSM:%d,%d,%50c", &sw1, &sw2, (char *) &hex) == 3)
success = TRUE;
}
if (!success) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Could not parse the CRSM response");
return 0;
}
if ((sw1 == 0x90 && sw2 == 0x00) ||
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
gsize buflen = 0;
guint32 mnc_len;
gchar *bin;
/* Make sure the buffer is only hex characters */
while (buflen < sizeof (hex) && hex[buflen]) {
if (!isxdigit (hex[buflen])) {
hex[buflen] = 0x0;
break;
}
buflen++;
}
/* Convert hex string to binary */
bin = utils_hexstr2bin (hex, &buflen);
if (!bin || buflen < 4) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"SIM returned malformed response '%s'",
hex);
g_free (bin);
return 0;
}
/* MNC length is byte 4 of this SIM file */
mnc_len = bin[3] & 0xFF;
if (mnc_len == 2 || mnc_len == 3) {
g_free (bin);
return mnc_len;
}
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"SIM returned invalid MNC length %d (should be either 2 or 3)",
mnc_len);
g_free (bin);
return 0;
}
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"SIM failed to handle CRSM request (sw1 %d sw2 %d)",
sw1, sw2);
return 0;
}
static gchar *
load_operator_identifier_finish (MMSim *self,
GAsyncResult *res,
GError **error)
{
GError *inner_error = NULL;
const gchar *imsi;
const gchar *result;
guint mnc_length;
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return NULL;
result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
imsi = mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (self));
if (!imsi) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Cannot load Operator ID without IMSI");
return NULL;
}
mnc_length = parse_mnc_length (result, &inner_error);
if (inner_error) {
g_propagate_error (error, inner_error);
return NULL;
}
/* Build Operator ID */
return g_strndup (imsi, 3 + mnc_length);
}
STR_REPLY_READY_FN (load_operator_identifier)
static void
load_operator_identifier (MMSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_dbg ("loading Operator ID...");
/* READ BINARY of EFad (Administrative Data) ETSI 51.011 section 10.3.18 */
mm_base_modem_at_command (
MM_BASE_MODEM (self->priv->modem),
"+CRSM=176,28589,0,0,4",
10,
FALSE,
(GAsyncReadyCallback)load_operator_identifier_command_ready,
g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
load_operator_identifier));
}
/*****************************************************************************/
/* Operator Name (Service Provider Name) */
static gchar *
parse_spn (const gchar *response,
GError **error)
{
gint sw1;
gint sw2;
gboolean success = FALSE;
gchar hex[51];
memset (hex, 0, sizeof (hex));
if (sscanf (response, "+CRSM:%d,%d,\"%50c\"", &sw1, &sw2, (char *) &hex) == 3)
success = TRUE;
else {
/* May not include quotes... */
if (sscanf (response, "+CRSM:%d,%d,%50c", &sw1, &sw2, (char *) &hex) == 3)
success = TRUE;
}
if (!success) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Could not parse the CRSM response");
return NULL;
}
if ((sw1 == 0x90 && sw2 == 0x00) ||
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
gsize buflen = 0;
gchar *bin;
gchar *utf8;
/* Make sure the buffer is only hex characters */
while (buflen < sizeof (hex) && hex[buflen]) {
if (!isxdigit (hex[buflen])) {
hex[buflen] = 0x0;
break;
}
buflen++;
}
/* Convert hex string to binary */
bin = utils_hexstr2bin (hex, &buflen);
if (!bin) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"SIM returned malformed response '%s'",
hex);
return NULL;
}
/* Remove the FF filler at the end */
while (buflen > 1 && bin[buflen - 1] == (char)0xff)
buflen--;
/* First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
utf8 = (gchar *)mm_charset_gsm_unpacked_to_utf8 ((guint8 *)bin + 1, buflen - 1);
g_free (bin);
return utf8;
}
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"SIM failed to handle CRSM request (sw1 %d sw2 %d)",
sw1, sw2);
return NULL;
}
static gchar *
load_operator_name_finish (MMSim *self,
GAsyncResult *res,
GError **error)
{
const gchar *result;
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return NULL;
result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
return parse_spn (result, error);
}
STR_REPLY_READY_FN (load_operator_name)
static void
load_operator_name (MMSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_dbg ("loading Operator Name...");
/* READ BINARY of EFspn (Service Provider Name) ETSI 51.011 section 10.3.11 */
mm_base_modem_at_command (
MM_BASE_MODEM (self->priv->modem),
"+CRSM=176,28486,0,0,17",
10,
FALSE,
(GAsyncReadyCallback)load_operator_name_command_ready,
g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
load_operator_name));
}
/*****************************************************************************/
typedef struct _InitAsyncContext InitAsyncContext;
static void interface_initialization_step (InitAsyncContext *ctx);
typedef enum {
INITIALIZATION_STEP_FIRST,
INITIALIZATION_STEP_SIM_IDENTIFIER,
INITIALIZATION_STEP_IMSI,
INITIALIZATION_STEP_OPERATOR_ID,
INITIALIZATION_STEP_OPERATOR_NAME,
INITIALIZATION_STEP_LAST
} InitializationStep;
struct _InitAsyncContext {
GSimpleAsyncResult *result;
GCancellable *cancellable;
MMSim *self;
InitializationStep step;
guint sim_identifier_tries;
};
static void
init_async_context_free (InitAsyncContext *ctx)
{
g_object_unref (ctx->self);
g_object_unref (ctx->result);
if (ctx->cancellable)
g_object_unref (ctx->cancellable);
g_free (ctx);
}
MMSim *
mm_sim_new_finish (GAsyncResult *res,
GError **error)
{
GObject *source;
GObject *sim;
source = g_async_result_get_source_object (res);
sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
g_object_unref (source);
if (!sim)
return NULL;
/* Only export valid SIMs */
mm_sim_export (MM_SIM (sim));
return MM_SIM (sim);
}
static gboolean
initable_init_finish (GAsyncInitable *initable,
GAsyncResult *result,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
}
static void
load_sim_identifier_ready (MMSim *self,
GAsyncResult *res,
InitAsyncContext *ctx)
{
GError *error = NULL;
gchar *simid;
simid = MM_SIM_GET_CLASS (ctx->self)->load_sim_identifier_finish (self, res, &error);
if (!simid) {
/* TODO: make the retries gobi-specific? */
/* Try one more time... Gobi 1K cards may reply to the first
* request with '+CRSM: 106,134,""' which is bogus because
* subsequent requests work fine.
*/
if (++ctx->sim_identifier_tries < 2) {
g_clear_error (&error);
interface_initialization_step (ctx);
return;
}
mm_warn ("couldn't load SIM identifier: '%s'",
error ? error->message : "unknown error");
g_clear_error (&error);
}
mm_gdbus_sim_set_sim_identifier (MM_GDBUS_SIM (self), simid);
g_free (simid);
/* Go on to next step */
ctx->step++;
interface_initialization_step (ctx);
}
#undef STR_REPLY_READY_FN
#define STR_REPLY_READY_FN(NAME,DISPLAY) \
static void \
load_##NAME##_ready (MMSim *self, \
GAsyncResult *res, \
InitAsyncContext *ctx) \
{ \
GError *error = NULL; \
gchar *val; \
\
val = MM_SIM_GET_CLASS (ctx->self)->load_##NAME##_finish (self, res, &error); \
mm_gdbus_sim_set_##NAME (MM_GDBUS_SIM (self), val); \
g_free (val); \
\
if (error) { \
mm_warn ("couldn't load %s: '%s'", DISPLAY, error->message); \
g_error_free (error); \
} \
\
/* Go on to next step */ \
ctx->step++; \
interface_initialization_step (ctx); \
}
STR_REPLY_READY_FN (imsi, "IMSI")
STR_REPLY_READY_FN (operator_identifier, "Operator identifier")
STR_REPLY_READY_FN (operator_name, "Operator name")
static void
interface_initialization_step (InitAsyncContext *ctx)
{
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_SIM_IDENTIFIER:
/* SIM ID is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
if (mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (ctx->self)) == NULL &&
MM_SIM_GET_CLASS (ctx->self)->load_sim_identifier &&
MM_SIM_GET_CLASS (ctx->self)->load_sim_identifier_finish) {
MM_SIM_GET_CLASS (ctx->self)->load_sim_identifier (
ctx->self,
(GAsyncReadyCallback)load_sim_identifier_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_IMSI:
/* IMSI is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
if (mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (ctx->self)) == NULL &&
MM_SIM_GET_CLASS (ctx->self)->load_imsi &&
MM_SIM_GET_CLASS (ctx->self)->load_imsi_finish) {
MM_SIM_GET_CLASS (ctx->self)->load_imsi (
ctx->self,
(GAsyncReadyCallback)load_imsi_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_OPERATOR_ID:
/* Operator ID is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
if (mm_gdbus_sim_get_operator_identifier (MM_GDBUS_SIM (ctx->self)) == NULL &&
MM_SIM_GET_CLASS (ctx->self)->load_operator_identifier &&
MM_SIM_GET_CLASS (ctx->self)->load_operator_identifier_finish) {
MM_SIM_GET_CLASS (ctx->self)->load_operator_identifier (
ctx->self,
(GAsyncReadyCallback)load_operator_identifier_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_OPERATOR_NAME:
/* Operator Name is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
if (mm_gdbus_sim_get_operator_name (MM_GDBUS_SIM (ctx->self)) == NULL &&
MM_SIM_GET_CLASS (ctx->self)->load_operator_name &&
MM_SIM_GET_CLASS (ctx->self)->load_operator_name_finish) {
MM_SIM_GET_CLASS (ctx->self)->load_operator_name (
ctx->self,
(GAsyncReadyCallback)load_operator_name_ready,
ctx);
return;
}
/* Fall down to next step */
ctx->step++;
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
g_simple_async_result_complete_in_idle (ctx->result);
init_async_context_free (ctx);
return;
}
g_assert_not_reached ();
}
static void
common_init_async (GAsyncInitable *initable,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
InitAsyncContext *ctx;
ctx = g_new (InitAsyncContext, 1);
ctx->self = g_object_ref (initable);
ctx->result = g_simple_async_result_new (G_OBJECT (initable),
callback,
user_data,
common_init_async);
ctx->cancellable = (cancellable ?
g_object_ref (cancellable) :
NULL);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->sim_identifier_tries = 0;
interface_initialization_step (ctx);
}
static void
initable_init_async (GAsyncInitable *initable,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_gdbus_sim_set_sim_identifier (MM_GDBUS_SIM (initable), NULL);
mm_gdbus_sim_set_imsi (MM_GDBUS_SIM (initable), NULL);
mm_gdbus_sim_set_operator_identifier (MM_GDBUS_SIM (initable), NULL);
mm_gdbus_sim_set_operator_name (MM_GDBUS_SIM (initable), NULL);
common_init_async (initable, cancellable, callback, user_data);
}
void
mm_sim_new (MMBaseModem *modem,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (MM_TYPE_SIM,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_SIM_MODEM, modem,
NULL);
}
gboolean
mm_sim_initialize_finish (MMSim *self,
GAsyncResult *result,
GError **error)
{
return initable_init_finish (G_ASYNC_INITABLE (self), result, error);
}
void
mm_sim_initialize (MMSim *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
common_init_async (G_ASYNC_INITABLE (self),
cancellable,
callback,
user_data);
}
static void
set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MMSim *self = MM_SIM (object);
switch (prop_id) {
case PROP_PATH:
g_free (self->priv->path);
self->priv->path = g_value_dup_string (value);
/* Export when we get a DBus connection AND we have a path */
if (self->priv->path &&
self->priv->connection)
mm_sim_dbus_export (self);
break;
case PROP_CONNECTION:
g_clear_object (&self->priv->connection);
self->priv->connection = g_value_dup_object (value);
/* Export when we get a DBus connection AND we have a path */
if (!self->priv->connection)
mm_sim_dbus_unexport (self);
else if (self->priv->path)
mm_sim_dbus_export (self);
break;
case PROP_MODEM:
g_clear_object (&self->priv->modem);
self->priv->modem = g_value_dup_object (value);
if (self->priv->modem) {
/* Bind the modem's connection (which is set when it is exported,
* and unset when unexported) to the SIM's connection */
g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION,
self, MM_SIM_CONNECTION,
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MMSim *self = MM_SIM (object);
switch (prop_id) {
case PROP_PATH:
g_value_set_string (value, self->priv->path);
break;
case PROP_CONNECTION:
g_value_set_object (value, self->priv->connection);
break;
case PROP_MODEM:
g_value_set_object (value, self->priv->modem);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
mm_sim_init (MMSim *self)
{
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
MM_TYPE_SIM,
MMSimPrivate);
}
static void
finalize (GObject *object)
{
MMSim *self = MM_SIM (object);
g_free (self->priv->path);
G_OBJECT_CLASS (mm_sim_parent_class)->finalize (object);
}
static void
dispose (GObject *object)
{
MMSim *self = MM_SIM (object);
if (self->priv->connection) {
/* If we arrived here with a valid connection, make sure we unexport
* the object */
mm_sim_dbus_unexport (self);
g_clear_object (&self->priv->connection);
}
g_clear_object (&self->priv->modem);
G_OBJECT_CLASS (mm_sim_parent_class)->dispose (object);
}
static void
async_initable_iface_init (GAsyncInitableIface *iface)
{
iface->init_async = initable_init_async;
iface->init_finish = initable_init_finish;
}
static void
mm_sim_class_init (MMSimClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMSimPrivate));
/* Virtual methods */
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->finalize = finalize;
object_class->dispose = dispose;
klass->load_sim_identifier = load_sim_identifier;
klass->load_sim_identifier_finish = load_sim_identifier_finish;
klass->load_imsi = load_imsi;
klass->load_imsi_finish = load_imsi_finish;
klass->load_operator_identifier = load_operator_identifier;
klass->load_operator_identifier_finish = load_operator_identifier_finish;
klass->load_operator_name = load_operator_name;
klass->load_operator_name_finish = load_operator_name_finish;
klass->send_pin = send_pin;
klass->send_pin_finish = common_send_pin_puk_finish;
klass->send_puk = send_puk;
klass->send_puk_finish = common_send_pin_puk_finish;
klass->enable_pin = enable_pin;
klass->enable_pin_finish = enable_pin_finish;
klass->change_pin = change_pin;
klass->change_pin_finish = change_pin_finish;
properties[PROP_CONNECTION] =
g_param_spec_object (MM_SIM_CONNECTION,
"Connection",
"GDBus connection to the system bus.",
G_TYPE_DBUS_CONNECTION,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]);
properties[PROP_PATH] =
g_param_spec_string (MM_SIM_PATH,
"Path",
"DBus path of the SIM",
NULL,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_PATH, properties[PROP_PATH]);
properties[PROP_MODEM] =
g_param_spec_object (MM_SIM_MODEM,
"Modem",
"The Modem which owns this SIM",
MM_TYPE_BASE_MODEM,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]);
/* Signals */
signals[SIGNAL_PIN_LOCK_ENABLED] =
g_signal_new (MM_SIM_PIN_LOCK_ENABLED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MMSimClass, pin_lock_enabled),
NULL, NULL,
mm_marshal_VOID__BOOLEAN,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
}