Files
ModemManager/src/mm-sim.c
T
Aleksander Morgado d27e40ae9a core: new `Initializing' state in the global modem state machine
We need to define a state to be used while the modem is being initialized, so
that we forbid any operation on the modem on already exported interfaces, while
there are interfaces pending to get exported.

This Initializing state will also cover the state between having the SIM
unlocked (which launches re-initialization) and being completely initialized.
2012-03-16 14:53:24 +01:00

1804 lines
58 KiB
C

/* -*- 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);
}
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);
}
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
send_pin_puk_context_complete_and_free (ctx);
return;
}
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);
}