blob: 1498fb0f1a0c5bacb0260bbebb60adf6d41a6509 [file]
/* -*- 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 - 2022 Aleksander Morgado <aleksander@aleksander.es>
* Copyright (C) 2011 - 2022 Google, Inc.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-iface-modem.h"
#include "mm-base-sim.h"
#include "mm-base-modem-at.h"
#include "mm-base-modem.h"
#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-error-helpers.h"
#include "mm-bind.h"
static void async_initable_iface_init (GAsyncInitableIface *iface);
static void log_object_iface_init (MMLogObjectInterface *iface);
static void bind_iface_init (MMBindInterface *iface);
G_DEFINE_TYPE_EXTENDED (MMBaseSim, mm_base_sim, MM_GDBUS_TYPE_SIM_SKELETON, 0,
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_BIND, bind_iface_init))
enum {
PROP_0,
PROP_PATH,
PROP_CONNECTION,
PROP_BIND_TO,
PROP_MODEM,
PROP_SLOT_NUMBER,
PROP_LAST
};
enum {
SIGNAL_PIN_LOCK_ENABLED,
SIGNAL_LAST
};
static GParamSpec *properties[PROP_LAST];
struct _MMBaseSimPrivate {
/* The connection to the system bus */
GDBusConnection *connection;
guint dbus_id;
/* The authorization provider */
MMAuthProvider *authp;
GCancellable *authp_cancellable;
/* The object this SIM is bound to */
GObject *bind_to;
/* The modem which owns this SIM */
MMBaseModem *modem;
/* The path where the SIM object is exported */
gchar *path;
/* The SIM slot number, which will be 0 always if the system
* doesn't support multiple SIMS. */
guint slot_number;
};
static guint signals[SIGNAL_LAST] = { 0 };
/*****************************************************************************/
/* SIM type helpers */
#define IS_PSIM(self) \
(mm_gdbus_sim_get_sim_type (MM_GDBUS_SIM (self)) == MM_SIM_TYPE_PHYSICAL)
#define IS_ESIM(self) \
(mm_gdbus_sim_get_sim_type (MM_GDBUS_SIM (self)) == MM_SIM_TYPE_ESIM)
#define IS_ESIM_WITHOUT_PROFILES(self) \
(IS_ESIM (self) && (mm_gdbus_sim_get_esim_status (MM_GDBUS_SIM (self)) == MM_SIM_ESIM_STATUS_NO_PROFILES))
gboolean
mm_base_sim_is_esim_without_profiles (MMBaseSim *self)
{
return IS_ESIM_WITHOUT_PROFILES (self);
}
/*****************************************************************************/
void
mm_base_sim_export (MMBaseSim *self)
{
gchar *path;
path = g_strdup_printf (MM_DBUS_SIM_PREFIX "/%d", self->priv->dbus_id);
g_object_set (self,
MM_BASE_SIM_PATH, path,
NULL);
g_free (path);
}
/*****************************************************************************/
/* Reprobe when a puk lock is discovered after pin1_retries are exhausted */
static void
reprobe_if_puk_discovered (MMBaseSim *self,
GError *error)
{
if (g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK)) {
mm_obj_dbg (self, "Discovered PUK lock, discarding old modem...");
mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self->priv->modem));
}
}
/*****************************************************************************/
/* CHANGE PIN (Generic implementation) */
static gboolean
change_pin_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
change_pin_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (modem, res, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
change_pin (MMBaseSim *self,
const gchar *old_pin,
const gchar *new_pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
gchar *command;
task = g_task_new (self, NULL, callback, user_data);
command = g_strdup_printf ("+CPWD=\"SC\",\"%s\",\"%s\"",
old_pin,
new_pin);
mm_base_modem_at_command (self->priv->modem,
command,
3,
FALSE,
(GAsyncReadyCallback)change_pin_ready,
task);
g_free (command);
}
/*****************************************************************************/
/* CHANGE PIN (DBus call handling) */
typedef struct {
MMBaseSim *self;
GDBusMethodInvocation *invocation;
gchar *old_pin;
gchar *new_pin;
GError *save_error;
} HandleChangePinContext;
static void
handle_change_pin_context_free (HandleChangePinContext *ctx)
{
g_assert (ctx->save_error == NULL);
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_update_lock_info_ready (MMIfaceModem *modem,
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_lock_info_finish (modem, res, NULL);
if (ctx->save_error) {
mm_dbus_method_invocation_return_gerror (ctx->invocation, ctx->save_error);
reprobe_if_puk_discovered (ctx->self, ctx->save_error);
g_clear_error (&ctx->save_error);
} else {
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 (MMBaseSim *self,
GAsyncResult *res,
HandleChangePinContext *ctx)
{
MMModemLock known_lock = MM_MODEM_LOCK_UNKNOWN;
if (!MM_BASE_SIM_GET_CLASS (self)->change_pin_finish (self, res, &ctx->save_error)) {
if (g_error_matches (ctx->save_error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK))
known_lock = MM_MODEM_LOCK_SIM_PUK;
}
mm_iface_modem_update_lock_info (
MM_IFACE_MODEM (self->priv->modem),
known_lock,
(GAsyncReadyCallback)after_change_update_lock_info_ready,
ctx);
}
static void
handle_change_pin_auth_ready (MMAuthProvider *authp,
GAsyncResult *res,
HandleChangePinContext *ctx)
{
GError *error = NULL;
if (!mm_auth_provider_authorize_finish (authp, res, &error)) {
mm_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_BASE_SIM_GET_CLASS (ctx->self)->change_pin ||
!MM_BASE_SIM_GET_CLASS (ctx->self)->change_pin_finish) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot change PIN: operation not supported");
handle_change_pin_context_free (ctx);
return;
}
if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot change PIN: SIM not currently active");
handle_change_pin_context_free (ctx);
return;
}
if (IS_ESIM_WITHOUT_PROFILES (ctx->self)) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot change PIN: eSIM without profiles");
handle_change_pin_context_free (ctx);
return;
}
mm_obj_info (ctx->self, "processing user request to change PIN...");
MM_BASE_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 (MMBaseSim *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_auth_provider_authorize (self->priv->authp,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
self->priv->authp_cancellable,
(GAsyncReadyCallback)handle_change_pin_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
/* ENABLE PIN (Generic implementation) */
static gboolean
enable_pin_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
enable_pin_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (modem, res, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
enable_pin (MMBaseSim *self,
const gchar *pin,
gboolean enabled,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
gchar *command;
task = g_task_new (self, NULL, callback, user_data);
command = g_strdup_printf ("+CLCK=\"SC\",%d,\"%s\"",
enabled ? 1 : 0,
pin);
mm_base_modem_at_command (self->priv->modem,
command,
3,
FALSE,
(GAsyncReadyCallback)enable_pin_ready,
task);
g_free (command);
}
/*****************************************************************************/
/* ENABLE PIN (DBus call handling) */
typedef struct {
MMBaseSim *self;
GDBusMethodInvocation *invocation;
gchar *pin;
gboolean enabled;
GError *save_error;
} HandleEnablePinContext;
static void
handle_enable_pin_context_free (HandleEnablePinContext *ctx)
{
g_assert (ctx->save_error == NULL);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_free (ctx->pin);
g_free (ctx);
}
static void
after_enable_update_lock_info_ready (MMIfaceModem *modem,
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_lock_info_finish (modem, res, NULL);
if (ctx->save_error) {
mm_dbus_method_invocation_return_gerror (ctx->invocation, ctx->save_error);
reprobe_if_puk_discovered (ctx->self, ctx->save_error);
g_clear_error (&ctx->save_error);
} else {
/* Signal about the new lock state */
g_signal_emit (ctx->self, signals[SIGNAL_PIN_LOCK_ENABLED], 0, ctx->enabled);
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 (MMBaseSim *self,
GAsyncResult *res,
HandleEnablePinContext *ctx)
{
MMModemLock known_lock = MM_MODEM_LOCK_UNKNOWN;
if (!MM_BASE_SIM_GET_CLASS (self)->enable_pin_finish (self, res, &ctx->save_error)) {
if (g_error_matches (ctx->save_error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK))
known_lock = MM_MODEM_LOCK_SIM_PUK;
}
mm_iface_modem_update_lock_info (
MM_IFACE_MODEM (self->priv->modem),
known_lock,
(GAsyncReadyCallback)after_enable_update_lock_info_ready,
ctx);
}
static void
handle_enable_pin_auth_ready (MMAuthProvider *authp,
GAsyncResult *res,
HandleEnablePinContext *ctx)
{
GError *error = NULL;
if (!mm_auth_provider_authorize_finish (authp, res, &error)) {
mm_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_BASE_SIM_GET_CLASS (ctx->self)->enable_pin ||
!MM_BASE_SIM_GET_CLASS (ctx->self)->enable_pin_finish) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot enable/disable PIN: operation not supported");
handle_enable_pin_context_free (ctx);
return;
}
if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot enable/disable PIN: SIM not currently active");
handle_enable_pin_context_free (ctx);
return;
}
if (IS_ESIM_WITHOUT_PROFILES (ctx->self)) {
mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot enable/disable PIN: eSIM without profiles");
handle_enable_pin_context_free (ctx);
return;
}
mm_obj_info (ctx->self, "processing user request to %s PIN...", ctx->enabled ? "enable" : "disable");
MM_BASE_SIM_GET_CLASS (ctx->self)->enable_pin (ctx->self,
ctx->pin,
ctx->enabled,
(GAsyncReadyCallback)handle_enable_pin_ready,
ctx);
}
static gboolean
handle_enable_pin (MMBaseSim *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_auth_provider_authorize (self->priv->authp,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
self->priv->authp_cancellable,
(GAsyncReadyCallback)handle_enable_pin_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
/* SEND PIN/PUK (Generic implementation) */
static gboolean
common_send_pin_puk_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
send_pin_puk_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (modem, res, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
common_send_pin_puk (MMBaseSim *self,
const gchar *pin,
const gchar *puk,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
gchar *command;
task = g_task_new (self, NULL, callback, user_data);
command = (puk ?
g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin) :
g_strdup_printf ("+CPIN=\"%s\"", pin));
mm_base_modem_at_command (self->priv->modem,
command,
3,
FALSE,
(GAsyncReadyCallback)send_pin_puk_ready,
task);
g_free (command);
}
static void
send_puk (MMBaseSim *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 (MMBaseSim *self,
const gchar *pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
common_send_pin_puk (self, pin, NULL, callback, user_data);
}
/*****************************************************************************/
/* SEND PIN/PUK (common logic) */
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_base_sim_send_pin_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
gboolean
mm_base_sim_send_puk_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
update_lock_info_ready (MMIfaceModem *modem,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
MMModemLock lock;
lock = mm_iface_modem_update_lock_info_finish (modem, res, &error);
/* Consider it only an error if SIM-PIN/PUK is locked or lock is unknown */
if (lock == MM_MODEM_LOCK_UNKNOWN ||
lock == MM_MODEM_LOCK_SIM_PIN ||
lock == MM_MODEM_LOCK_SIM_PUK) {
const GError *saved_error;
/* Device is locked. Now:
* - If we got an error during update_lock_info, report it. The sim might have been blocked.
* - If we got an error in the original send-pin action, report it.
* - Otherwise, build our own error from the lock code.
*/
if (!error) {
saved_error = g_task_get_task_data (task);
if (saved_error)
error = g_error_copy (saved_error);
else
error = error_for_unlock_check (lock);
}
g_task_return_error (task, error);
} else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
send_pin_ready (MMBaseSim *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
MMModemLock known_lock = MM_MODEM_LOCK_UNKNOWN;
if (!MM_BASE_SIM_GET_CLASS (self)->send_pin_finish (self, res, &error)) {
g_task_set_task_data (task, error, (GDestroyNotify)g_error_free);
if (g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK))
known_lock = MM_MODEM_LOCK_SIM_PUK;
}
/* Once pin/puk has been sent, recheck lock */
mm_iface_modem_update_lock_info (
MM_IFACE_MODEM (self->priv->modem),
known_lock,
(GAsyncReadyCallback)update_lock_info_ready,
task);
}
static void
send_puk_ready (MMBaseSim *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!MM_BASE_SIM_GET_CLASS (self)->send_puk_finish (self, res, &error))
g_task_set_task_data (task, error, (GDestroyNotify)g_error_free);
/* Once pin/puk has been sent, recheck lock */
mm_iface_modem_update_lock_info (MM_IFACE_MODEM (self->priv->modem),
MM_MODEM_LOCK_UNKNOWN, /* ask */
(GAsyncReadyCallback)update_lock_info_ready,
task);
}
void
mm_base_sim_send_pin (MMBaseSim *self,
const gchar *pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* If sending PIN is not implemented, report an error */
if (!MM_BASE_SIM_GET_CLASS (self)->send_pin ||
!MM_BASE_SIM_GET_CLASS (self)->send_pin_finish) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot send PIN: operation not supported");
g_object_unref (task);
return;
}
/* Only allow sending SIM-PIN if really SIM-PIN locked */
if (mm_iface_modem_get_unlock_required (MM_IFACE_MODEM (self->priv->modem)) != MM_MODEM_LOCK_SIM_PIN) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
"Cannot send PIN: device is not SIM-PIN locked");
g_object_unref (task);
return;
}
MM_BASE_SIM_GET_CLASS (self)->send_pin (self,
pin,
(GAsyncReadyCallback)send_pin_ready,
task);
}
void
mm_base_sim_send_puk (MMBaseSim *self,
const gchar *puk,
const gchar *new_pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* If sending PIN is not implemented, report an error */
if (!MM_BASE_SIM_GET_CLASS (self)->send_puk ||
!MM_BASE_SIM_GET_CLASS (self)->send_puk_finish) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot send PUK: operation not supported");
g_object_unref (task);
return;
}
/* Only allow sending SIM-PUK if really SIM-PUK locked */
if (mm_iface_modem_get_unlock_required (MM_IFACE_MODEM (self->priv->modem)) != MM_MODEM_LOCK_SIM_PUK) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
"Cannot send PUK: device is not SIM-PUK locked");
g_object_unref (task);
return;
}
MM_BASE_SIM_GET_CLASS (self)->send_puk (self,
puk,
new_pin,
(GAsyncReadyCallback)send_puk_ready,
task);
}
/*****************************************************************************/
/* LOAD SIM IDENTIFIER */
gchar *
mm_base_sim_load_sim_identifier_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_sim_identifier_ready (MMBaseSim *self,
GAsyncResult *res,
GTask *task)
{
gchar *simid;
GError *error = NULL;
simid = MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish (self, res, &error);
if (!simid)
g_task_return_error (task, error);
else
g_task_return_pointer (task, simid, g_free);
g_object_unref (task);
}
void
mm_base_sim_load_sim_identifier (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
if (!MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier ||
!MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"not implemented");
g_object_unref (task);
return;
}
if (IS_ESIM_WITHOUT_PROFILES (self)) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"no SIM identifier in eSIM without profiles");
g_object_unref (task);
return;
}
MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier (
self,
(GAsyncReadyCallback)load_sim_identifier_ready,
task);
}
/*****************************************************************************/
/* SEND PIN (DBus call handling) */
typedef struct {
MMBaseSim *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 (MMBaseSim *self,
GAsyncResult *res,
HandleSendPinContext *ctx)
{
GError *error = NULL;
if (!mm_base_sim_send_pin_finish (self, res, &error)) {
mm_dbus_method_invocation_return_gerror (ctx->invocation, error);
reprobe_if_puk_discovered (self, error);
g_clear_error (&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 (MMAuthProvider *authp,
GAsyncResult *res,
HandleSendPinContext *ctx)
{
GError *error = NULL;
if (!mm_auth_provider_authorize_finish (authp, res, &error)) {
mm_dbus_method_invocation_take_error (ctx->invocation, error);
handle_send_pin_context_free (ctx);
return;
}
if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot send PIN: SIM not currently active");
handle_send_pin_context_free (ctx);
return;
}
if (IS_ESIM_WITHOUT_PROFILES (ctx->self)) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot send PIN: eSIM without profiles");
handle_send_pin_context_free (ctx);
return;
}
mm_obj_info (ctx->self, "processing user request to send PIN...");
mm_base_sim_send_pin (ctx->self,
ctx->pin,
(GAsyncReadyCallback)handle_send_pin_ready,
ctx);
}
static gboolean
handle_send_pin (MMBaseSim *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_auth_provider_authorize (self->priv->authp,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
self->priv->authp_cancellable,
(GAsyncReadyCallback)handle_send_pin_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
/* SEND PUK (DBus call handling) */
typedef struct {
MMBaseSim *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 (MMBaseSim *self,
GAsyncResult *res,
HandleSendPukContext *ctx)
{
GError *error = NULL;
gboolean sim_error = FALSE;
if (!mm_base_sim_send_puk_finish (self, res, &error)) {
sim_error = g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) ||
g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG);
mm_dbus_method_invocation_take_error (ctx->invocation, error);
} else
mm_gdbus_sim_complete_send_puk (MM_GDBUS_SIM (self), ctx->invocation);
if (sim_error) {
mm_obj_msg (self, "received critical sim error: SIM might be permanently blocked, reprobing...");
mm_iface_modem_process_sim_event (MM_IFACE_MODEM (self->priv->modem));
}
handle_send_puk_context_free (ctx);
}
static void
handle_send_puk_auth_ready (MMAuthProvider *authp,
GAsyncResult *res,
HandleSendPukContext *ctx)
{
GError *error = NULL;
if (!mm_auth_provider_authorize_finish (authp, res, &error)) {
mm_dbus_method_invocation_take_error (ctx->invocation, error);
handle_send_puk_context_free (ctx);
return;
}
if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot send PUK: SIM not currently active");
handle_send_puk_context_free (ctx);
return;
}
if (IS_ESIM_WITHOUT_PROFILES (ctx->self)) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot send PUK: eSIM without profiles");
handle_send_puk_context_free (ctx);
return;
}
mm_obj_info (ctx->self, "processing user request to send PUK...");
mm_base_sim_send_puk (ctx->self,
ctx->puk,
ctx->new_pin,
(GAsyncReadyCallback)handle_send_puk_ready,
ctx);
}
static gboolean
handle_send_puk (MMBaseSim *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_auth_provider_authorize (self->priv->authp,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
self->priv->authp_cancellable,
(GAsyncReadyCallback)handle_send_puk_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
/* Check if preferred networks is supported.
*
* Modems like the Intel-based EM7345 fail very badly when CPOL? is run, even
* completely blocking the AT port after that. We need to avoid running any
* CPOL related command in these modules.
*/
static gboolean
check_preferred_networks_disabled (MMBaseSim *self)
{
MMPort *primary;
primary = MM_PORT (mm_base_modem_peek_port_primary (self->priv->modem));
return (primary ?
mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (primary),
"ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED") :
FALSE);
}
/*****************************************************************************/
/* SET PREFERRED NETWORKS (Generic implementation) */
/* Setting preferred network list with AT+CPOL is a complicated procedure with
* the following steps:
* 1. Using AT+CPOL=? to get SIM capacity; the capacity is checked to ensure
* that the list is not too large for the SIM card.
* 2. Reading existing preferred networks from SIM with AT+CPOL?.
* 3. Clearing existing networks with a series of AT+CPOL=<index> commands.
* 4. Setting the new list by invoking AT+CPOL for each network.
*
* There are some complications with AT+CPOL handling which makes the work more
* difficult for us. It seems that modems can only handle a certain exact number
* of access technology identifiers - and this cannot be certainly known in
* advance.
*
* If AT+CPOL? in step 2 returns anything, we can deduce the number of supported
* identifiers there. But if there were no networks configured earlier, we must
* start with a default based on modem capacity and work our way down from there
* if the AT+CPOL command fails.
*/
static void set_preferred_networks_set_next (MMBaseSim *self,
GTask *task);
static void set_preferred_networks_clear_next (MMBaseSim *self,
GTask *task);
typedef struct {
GList *set_list;
/* AT+CPOL indices that must be cleared before setting the networks. */
GArray *clear_index;
/* Number of access technology identifiers we will set. */
guint act_count;
/* If TRUE, we know that act_count is something the modem can handle */
gboolean act_count_verified;
/* Index of preferred network currently being set (0 = first) */
guint current_write_index;
/* Operation error code */
GError *error;
} SetPreferredNetworksContext;
static void
set_preferred_network_context_free (SetPreferredNetworksContext *ctx)
{
g_list_free_full (ctx->set_list, (GDestroyNotify) mm_sim_preferred_network_free);
g_clear_error (&ctx->error);
g_array_free (ctx->clear_index, TRUE);
g_slice_free (SetPreferredNetworksContext, ctx);
}
static gboolean
set_preferred_networks_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parse_old_preferred_networks (const gchar *response,
SetPreferredNetworksContext *ctx)
{
gchar **entries;
gchar **iter;
entries = g_strsplit_set (response, "\r\n", -1);
for (iter = entries; iter && *iter; iter++) {
guint index;
guint act_count = 0;
g_strstrip (*iter);
if (strlen (*iter) == 0)
continue;
if (mm_sim_parse_cpol_query_response (*iter,
&index,
NULL, NULL, NULL, NULL, NULL, NULL,
&act_count,
NULL) && index > 0) {
/* Remember how many access technologies the modem/SIM can take */
if (!ctx->act_count_verified || act_count > ctx->act_count) {
ctx->act_count = act_count;
ctx->act_count_verified = TRUE;
}
/* Store the index to be cleared */
g_array_append_val (ctx->clear_index, index);
}
}
g_strfreev (entries);
}
/* This function is called only in error case, after reloading the network list from SIM. */
static void
set_preferred_networks_reload_ready (MMBaseSim *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
GList *preferred_nets_list;
SetPreferredNetworksContext *ctx;
ctx = g_task_get_task_data (task);
preferred_nets_list = MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish (self, res, &error);
if (error)
mm_obj_dbg (self, "couldn't load list of preferred networks: %s", error->message);
mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self),
mm_sim_preferred_network_list_get_variant (preferred_nets_list));
g_list_free_full (preferred_nets_list, (GDestroyNotify) mm_sim_preferred_network_free);
/* Return the original error stored in our context */
g_task_return_error (task, g_steal_pointer (&ctx->error));
g_object_unref (task);
}
static gboolean
set_preferred_networks_retry_command (MMBaseSim *self,
SetPreferredNetworksContext *ctx)
{
/* If we haven't yet determined the number of access technology parameters supported by
* the modem, try reducing the count if possible and retry with the reduced count.
*/
if (!ctx->act_count_verified && ctx->act_count > 0) {
ctx->act_count--;
mm_obj_dbg (self, "retrying operation with %u access technologies", ctx->act_count);
return TRUE;
}
return FALSE;
}
static void
set_preferred_network_reload_and_return_error (MMBaseSim *self,
GTask *task,
GError *error)
{
SetPreferredNetworksContext *ctx;
ctx = g_task_get_task_data (task);
/* Reload the complete list from SIM card to ensure that the PreferredNetworks
* property matches with whatever is actually on the SIM.
*/
if (MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks &&
MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish) {
ctx->error = error;
MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks (
self,
(GAsyncReadyCallback)set_preferred_networks_reload_ready,
task);
} else {
g_task_return_error (task, error);
g_object_unref (task);
}
}
static void
set_preferred_networks_set_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMBaseSim *self;
SetPreferredNetworksContext *ctx;
GError *error = NULL;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
mm_base_modem_at_command_finish (modem, res, &error);
/* The command may fail; check if we can retry */
if (error) {
if (!set_preferred_networks_retry_command (self, ctx)) {
/* Retrying not possible, failing... */
mm_obj_warn (self, "failed to set preferred networks: '%s'", error->message);
set_preferred_network_reload_and_return_error (self, task, error);
return;
}
/* Retrying possible */
g_clear_error (&error);
} else {
/* Last set operation was successful, so we know for sure how many access technologies
* the modem can take.
*/
ctx->act_count_verified = TRUE;
ctx->current_write_index++;
}
set_preferred_networks_set_next (self, task);
}
static gboolean
set_preferred_networks_check_support (MMBaseSim *self,
SetPreferredNetworksContext *ctx,
MMSimPreferredNetwork *network,
GError **error)
{
MMModemAccessTechnology requested_act;
MMModemAccessTechnology supported_act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
requested_act = mm_sim_preferred_network_get_access_technology (network);
if (ctx->act_count >= 1)
supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM;
if (ctx->act_count >= 2)
supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT;
if (ctx->act_count >= 3)
supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
if (ctx->act_count >= 4)
supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE;
if (ctx->act_count >= 5)
supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR;
if (requested_act & ~supported_act) {
g_autofree gchar *act_string = NULL;
act_string = mm_modem_access_technology_build_string_from_mask (requested_act & ~supported_act);
mm_obj_warn (self, "cannot set preferred net '%s'; access technology '%s' not supported by modem/SIM",
mm_sim_preferred_network_get_operator_code (network), act_string);
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Access technology unsupported by modem or SIM");
return FALSE;
}
return TRUE;
}
/* Set next preferred network in queue */
static void
set_preferred_networks_set_next (MMBaseSim *self,
GTask *task)
{
SetPreferredNetworksContext *ctx;
g_autofree gchar *command = NULL;
MMSimPreferredNetwork *current_network;
const gchar *operator_code;
MMModemAccessTechnology act;
GError *error = NULL;
ctx = g_task_get_task_data (task);
current_network = (MMSimPreferredNetwork *) g_list_nth_data (ctx->set_list, ctx->current_write_index);
if (current_network == NULL) {
/* No more networks to set; we are done. */
mm_obj_dbg (self, "setting preferred networks completed.");
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
if (!set_preferred_networks_check_support (self, ctx, current_network, &error)) {
set_preferred_network_reload_and_return_error (self, task, error);
return;
}
operator_code = mm_sim_preferred_network_get_operator_code (current_network);
act = mm_sim_preferred_network_get_access_technology (current_network);
/* Assemble the command to set the network */
command = g_strdup_printf ("+CPOL=%u,2,\"%s\"%s%s%s%s%s", ctx->current_write_index + 1, operator_code,
ctx->act_count == 0 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_GSM) ? ",1" : ",0"),
ctx->act_count <= 1 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT) ? ",1" : ",0"),
ctx->act_count <= 2 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_UMTS) ? ",1" : ",0"),
ctx->act_count <= 3 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_LTE) ? ",1" : ",0"),
ctx->act_count <= 4 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_5GNR) ? ",1" : ",0"));
mm_base_modem_at_command (
self->priv->modem,
command,
20,
FALSE,
(GAsyncReadyCallback)set_preferred_networks_set_ready,
task);
}
static void
set_preferred_networks_clear_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMBaseSim *self;
GError *error = NULL;
self = g_task_get_source_object (task);
mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
mm_obj_warn (self, "couldn't clear preferred network entry: '%s'", error->message);
set_preferred_network_reload_and_return_error (self, task, error);
return;
}
set_preferred_networks_clear_next (self, task);
}
/* Clear one of the networks in the clear list */
static void
set_preferred_networks_clear_next (MMBaseSim *self,
GTask *task)
{
SetPreferredNetworksContext *ctx;
g_autofree gchar *command = NULL;
guint current_clear_index;
ctx = g_task_get_task_data (task);
/* Clear from last index to first, since some modems (e.g. u-blox) may shift up items
* following the cleared ones.
*/
if (ctx->clear_index->len > 0) {
current_clear_index = g_array_index (ctx->clear_index, guint, ctx->clear_index->len - 1);
g_array_remove_index (ctx->clear_index, ctx->clear_index->len - 1);
command = g_strdup_printf ("+CPOL=%u", current_clear_index);
mm_base_modem_at_command (
self->priv->modem,
command,
20,
FALSE,
(GAsyncReadyCallback)set_preferred_networks_clear_ready,
task);
return;
}
/* All clear operations done; start setting new networks */
set_preferred_networks_set_next (self, task);
}
static void
set_preferred_networks_load_existing_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMBaseSim *self;
GError *error = NULL;
SetPreferredNetworksContext *ctx;
const gchar *response;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
response = mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
mm_obj_warn (self, "couldn't load existing preferred network list: '%s'", error->message);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
parse_old_preferred_networks (response, ctx);
set_preferred_networks_clear_next (self, task);
}
static void
set_preferred_networks_query_sim_capacity_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMBaseSim *self;
GError *error = NULL;
SetPreferredNetworksContext *ctx;
const gchar *response;
guint max_index;
guint networks_count;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
response = mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
mm_obj_warn (self, "couldn't query preferred network list capacity: '%s'", error->message);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (!mm_sim_parse_cpol_test_response (response, NULL, &max_index, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Compare the number of networks to maximum index returned by AT+CPOL=? */
networks_count = g_list_length (ctx->set_list);
if (networks_count > max_index) {
mm_obj_warn (self, "can't set %u preferred networks; SIM capacity: %u", networks_count, max_index);
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_TOO_MANY,
"Too many networks; SIM capacity %u", max_index);
g_object_unref (task);
return;
}
mm_obj_dbg (self, "setting %u preferred networks, SIM capacity: %u", networks_count, max_index);
mm_base_modem_at_command (
self->priv->modem,
"+CPOL?",
20,
FALSE,
(GAsyncReadyCallback)set_preferred_networks_load_existing_ready,
task);
}
static void
set_preferred_networks (MMBaseSim *self,
GList *preferred_network_list,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
SetPreferredNetworksContext *ctx;
task = g_task_new (self, NULL, callback, user_data);
if (check_preferred_networks_disabled (self)) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"setting preferred networks is unsupported");
g_object_unref (task);
return;
}
mm_obj_dbg (self, "set preferred networks: loading existing networks...");
ctx = g_slice_new0 (SetPreferredNetworksContext);
ctx->set_list = mm_sim_preferred_network_list_copy (preferred_network_list);
ctx->clear_index = g_array_new (FALSE, TRUE, sizeof (guint));
if (mm_iface_modem_is_5g (MM_IFACE_MODEM (self->priv->modem)))
ctx->act_count = 5;
else if (mm_iface_modem_is_4g (MM_IFACE_MODEM (self->priv->modem)))
ctx->act_count = 4;
else if (mm_iface_modem_is_3g (MM_IFACE_MODEM (self->priv->modem)))
ctx->act_count = 3;
else if (mm_iface_modem_is_2g (MM_IFACE_MODEM (self->priv->modem)))
ctx->act_count = 2;
g_task_set_task_data (task, ctx, (GDestroyNotify) set_preferred_network_context_free);
/* Query SIM capacity first to find out how many preferred networks it can take */
mm_base_modem_at_command (
self->priv->modem,
"+CPOL=?",
20,
FALSE, /* Do not cache, the response depends on SIM card properties */
(GAsyncReadyCallback)set_preferred_networks_query_sim_capacity_ready,
task);
}
/*****************************************************************************/
/* SET PREFERRED NETWORKS (DBus call handling) */
typedef struct {
MMBaseSim *self;
GDBusMethodInvocation *invocation;
GVariant *networks;
} HandleSetPreferredNetworksContext;
static void
handle_set_preferred_networks_context_free (HandleSetPreferredNetworksContext *ctx)
{
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_variant_unref (ctx->networks);
g_free (ctx);
}
static void
handle_set_preferred_networks_ready (MMBaseSim *self,
GAsyncResult *res,
HandleSetPreferredNetworksContext *ctx)
{
GError *error = NULL;
MM_BASE_SIM_GET_CLASS (self)->set_preferred_networks_finish (self, res, &error);
if (error) {
mm_obj_warn (self, "couldn't set preferred networks: %s", error->message);
mm_dbus_method_invocation_take_error (ctx->invocation, g_steal_pointer (&error));
} else {
mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self), ctx->networks);
mm_gdbus_sim_complete_set_preferred_networks (MM_GDBUS_SIM (self), ctx->invocation);
}
handle_set_preferred_networks_context_free (ctx);
}
static void
handle_set_preferred_networks_auth_ready (MMAuthProvider *authp,
GAsyncResult *res,
HandleSetPreferredNetworksContext *ctx)
{
GError *error = NULL;
if (!mm_auth_provider_authorize_finish (authp, res, &error)) {
mm_dbus_method_invocation_take_error (ctx->invocation, error);
handle_set_preferred_networks_context_free (ctx);
return;
}
if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot set preferred networks: SIM not currently active");
handle_set_preferred_networks_context_free (ctx);
return;
}
if (IS_ESIM_WITHOUT_PROFILES (ctx->self)) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot set preferred networks: eSIM without profiles");
handle_set_preferred_networks_context_free (ctx);
return;
}
if (!MM_BASE_SIM_GET_CLASS (ctx->self)->set_preferred_networks ||
!MM_BASE_SIM_GET_CLASS (ctx->self)->set_preferred_networks_finish) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot set preferred networks: not implemented");
handle_set_preferred_networks_context_free (ctx);
return;
}
mm_obj_info (ctx->self, "processing user request to set preferred networks...");
MM_BASE_SIM_GET_CLASS (ctx->self)->set_preferred_networks (
ctx->self,
mm_sim_preferred_network_list_new_from_variant (ctx->networks),
(GAsyncReadyCallback)handle_set_preferred_networks_ready,
ctx);
}
static gboolean
handle_set_preferred_networks (MMBaseSim *self,
GDBusMethodInvocation *invocation,
GVariant *networks_variant)
{
HandleSetPreferredNetworksContext *ctx;
ctx = g_new0 (HandleSetPreferredNetworksContext, 1);
ctx->self = g_object_ref (self);
ctx->invocation = g_object_ref (invocation);
ctx->networks = g_variant_ref (networks_variant);
mm_auth_provider_authorize (self->priv->authp,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
self->priv->authp_cancellable,
(GAsyncReadyCallback)handle_set_preferred_networks_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
static void
sim_dbus_export (MMBaseSim *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);
g_signal_connect (self,
"handle-set-preferred-networks",
G_CALLBACK (handle_set_preferred_networks),
NULL);
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self),
self->priv->connection,
self->priv->path,
&error)) {
mm_obj_warn (self, "couldn't export SIM: %s", error->message);
g_error_free (error);
}
}
static void
sim_dbus_unexport (MMBaseSim *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_base_sim_get_path (MMBaseSim *self)
{
return self->priv->path;
}
guint
mm_base_sim_get_slot_number (MMBaseSim *self)
{
return self->priv->slot_number;
}
/*****************************************************************************/
gboolean
mm_base_sim_is_emergency_number (MMBaseSim *self,
const gchar *number)
{
const gchar *const *emergency_numbers;
guint i;
emergency_numbers = mm_gdbus_sim_get_emergency_numbers (MM_GDBUS_SIM (self));
if (!emergency_numbers)
return FALSE;
for (i = 0; emergency_numbers[i]; i++) {
if (g_strcmp0 (number, emergency_numbers[i]) == 0)
return TRUE;
}
return FALSE;
}
/*****************************************************************************/
#undef STR_REPLY_READY_FN
#define STR_REPLY_READY_FN(NAME) \
static void \
NAME##_command_ready (MMBaseModem *modem, \
GAsyncResult *res, \
GTask *task) \
{ \
GError *error = NULL; \
const gchar *response; \
\
response = mm_base_modem_at_command_finish (modem, res, &error); \
if (error) \
g_task_return_error (task, error); \
else \
g_task_return_pointer (task, g_strdup (response), g_free); \
\
g_object_unref (task); \
}
/*****************************************************************************/
/* Emergency numbers */
static GStrv
parse_emergency_numbers (const gchar *response,
GError **error)
{
guint sw1 = 0;
guint sw2 = 0;
gchar *hex = 0;
GStrv ret;
if (!mm_3gpp_parse_crsm_response (response, &sw1, &sw2, &hex, error))
return NULL;
if ((sw1 == 0x90 && sw2 == 0x00) ||
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
ret = mm_3gpp_parse_emergency_numbers (hex, error);
g_free (hex);
return ret;
}
g_free (hex);
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 GStrv
load_emergency_numbers_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
gchar *result;
GStrv emergency_numbers;
guint i;
result = g_task_propagate_pointer (G_TASK (res), error);
if (!result)
return NULL;
emergency_numbers = parse_emergency_numbers (result, error);
g_free (result);
if (!emergency_numbers)
return NULL;
for (i = 0; emergency_numbers[i]; i++)
mm_obj_dbg (self, "loaded emergency number: %s", emergency_numbers[i]);
return emergency_numbers;
}
STR_REPLY_READY_FN (load_emergency_numbers)
static void
load_emergency_numbers (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_obj_dbg (self, "loading emergency numbers...");
/* READ BINARY of EF_ECC (Emergency Call Codes) ETSI TS 51.011 section 10.3.27 */
mm_base_modem_at_command (
self->priv->modem,
"+CRSM=176,28599,0,0,15",
20,
FALSE,
(GAsyncReadyCallback)load_emergency_numbers_command_ready,
g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* Preferred networks */
static GList *
parse_preferred_networks (const gchar *response,
GError **error)
{
gchar **entries;
gchar **iter;
GList *result = NULL;
entries = g_strsplit_set (response, "\r\n", -1);
for (iter = entries; iter && *iter; iter++) {
gchar *operator_code = NULL;
gboolean gsm_act;
gboolean gsm_compact_act;
gboolean utran_act;
gboolean eutran_act;
gboolean ngran_act;
MMSimPreferredNetwork *preferred_network = NULL;
MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
g_strstrip (*iter);
if (strlen (*iter) == 0)
continue;
if (mm_sim_parse_cpol_query_response (*iter,
NULL,
&operator_code,
&gsm_act,
&gsm_compact_act,
&utran_act,
&eutran_act,
&ngran_act,
NULL,
error)) {
preferred_network = mm_sim_preferred_network_new ();
mm_sim_preferred_network_set_operator_code (preferred_network, operator_code);
if (gsm_act)
act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM;
if (gsm_compact_act)
act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT;
if (utran_act)
act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
if (eutran_act)
act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE;
if (ngran_act)
act |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR;
mm_sim_preferred_network_set_access_technology (preferred_network, act);
result = g_list_append (result, preferred_network);
} else
break;
g_free (operator_code);
}
g_strfreev (entries);
return result;
}
static GList *
load_preferred_networks_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
g_autofree gchar *result = NULL;
result = g_task_propagate_pointer (G_TASK (res), error);
return result ? parse_preferred_networks (result, error) : NULL;
}
STR_REPLY_READY_FN (load_preferred_networks)
static void
load_preferred_networks_set_format_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMBaseSim *self;
GError *error = NULL;
self = g_task_get_source_object (task);
/* Ignore error */
mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
mm_obj_dbg (self, "setting preferred network list format failed: '%s'", error->message);
g_error_free (error);
}
mm_obj_dbg (self, "loading preferred networks...");
mm_base_modem_at_command (
modem,
"+CPOL?",
20,
FALSE,
(GAsyncReadyCallback)load_preferred_networks_command_ready,
task);
}
static void
load_preferred_networks_cpls_command_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMBaseSim *self;
GError *error = NULL;
self = g_task_get_source_object (task);
/* AT+CPLS may not be supported so we ignore any error and proceed even if it fails */
mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
mm_obj_dbg (self, "selecting user-defined preferred network list failed: '%s'", error->message);
g_error_free (error);
}
mm_obj_dbg (self, "setting preferred networks format...");
/* Request numeric MCCMNC format */
mm_base_modem_at_command (
modem,
"+CPOL=,2",
20,
FALSE,
(GAsyncReadyCallback)load_preferred_networks_set_format_ready,
task);
}
static void
load_preferred_networks (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
if (check_preferred_networks_disabled (self)) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"setting preferred networks is unsupported");
g_object_unref (task);
return;
}
/* Invoke AT+CPLS=0 first to make sure the correct (user-defined) preferred network list is selected */
mm_obj_dbg (self, "selecting user-defined preferred network list...");
mm_base_modem_at_command (
self->priv->modem,
"+CPLS=0",
20,
FALSE,
(GAsyncReadyCallback)load_preferred_networks_cpls_command_ready,
task);
}
/*****************************************************************************/
/* ICCID */
static gchar *
parse_iccid (const gchar *response,
GError **error)
{
guint sw1 = 0;
guint sw2 = 0;
gchar *hex = 0;
gchar *ret;
if (!mm_3gpp_parse_crsm_response (response,
&sw1,
&sw2,
&hex,
error))
return NULL;
if ((sw1 == 0x90 && sw2 == 0x00) ||
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
ret = mm_3gpp_parse_iccid (hex, error);
g_free (hex);
return ret;
} else {
g_free (hex);
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 (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
g_autofree gchar *result = NULL;
result = g_task_propagate_pointer (G_TASK (res), error);
return result ? parse_iccid (result, error) : NULL;
}
STR_REPLY_READY_FN (load_sim_identifier)
static void
load_sim_identifier (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_obj_dbg (self, "loading SIM identifier...");
/* READ BINARY of EFiccid (ICC Identification) ETSI TS 102.221 section 13.2 */
mm_base_modem_at_command (
self->priv->modem,
"+CRSM=176,12258,0,0,10",
20,
FALSE,
(GAsyncReadyCallback)load_sim_identifier_command_ready,
g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* IMSI */
static gchar *
parse_imsi (const gchar *response,
GError **error)
{
const gchar *s;
gint len;
g_assert (response != NULL);
for (s = mm_strip_tag (response, "+CIMI"), len = 0;
*s;
++s, ++len) {
/* IMSI is a number with 15 or less decimal digits. */
if (!isdigit (*s) || len > 15) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Invalid +CIMI response '%s'", response ? response : "<null>");
return NULL;
}
}
return g_strdup (response);
}
static gchar *
load_imsi_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
g_autofree gchar *result = NULL;
result = g_task_propagate_pointer (G_TASK (res), error);
return result ? parse_imsi (result, error) : NULL;
}
STR_REPLY_READY_FN (load_imsi)
static void
load_imsi (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_obj_dbg (self, "loading IMSI...");
mm_base_modem_at_command (
self->priv->modem,
"+CIMI",
3,
FALSE,
(GAsyncReadyCallback)load_imsi_command_ready,
g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* Operator ID */
static guint
parse_mnc_length (const gchar *response,
GError **error)
{
guint sw1 = 0;
guint sw2 = 0;
g_autofree gchar *hex = NULL;
if (!mm_3gpp_parse_crsm_response (response,
&sw1,
&sw2,
&hex,
error))
return 0;
if ((sw1 == 0x90 && sw2 == 0x00) ||
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
gsize buflen = 0;
g_autofree guint8 *bin = NULL;
/* Convert hex string to binary */
bin = mm_utils_hexstr2bin (hex, -1, &buflen, error);
if (!bin) {
g_prefix_error (error, "SIM returned malformed response '%s': ", hex);
return 0;
}
return mm_sim_validate_mnc_length (bin, buflen, error);
}
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 (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
GError *inner_error = NULL;
const gchar *imsi;
gchar *result;
guint mnc_length;
result = g_task_propagate_pointer (G_TASK (res), error);
if (!result)
return NULL;
mnc_length = parse_mnc_length (result, &inner_error);
g_free (result);
if (inner_error) {
g_propagate_error (error, inner_error);
return NULL;
}
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;
}
/* Build Operator ID */
return g_strndup (imsi, 3 + mnc_length);
}
STR_REPLY_READY_FN (load_operator_identifier)
static void
load_operator_identifier (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_obj_dbg (self, "loading operator ID...");
/* READ BINARY of EFad (Administrative Data) ETSI 51.011 section 10.3.18
* SIMCOM A760xE-H modems can answer in 10s or more, so use rather big timeout
*/
mm_base_modem_at_command (
self->priv->modem,
"+CRSM=176,28589,0,0,4",
20,
FALSE,
(GAsyncReadyCallback)load_operator_identifier_command_ready,
g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* Operator Name (Service Provider Name) */
static gchar *
parse_spn (const gchar *response,
GError **error)
{
guint sw1 = 0;
guint sw2 = 0;
g_autofree gchar *hex = NULL;
if (!mm_3gpp_parse_crsm_response (response,
&sw1,
&sw2,
&hex,
error))
return NULL;
if ((sw1 == 0x90 && sw2 == 0x00) ||
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
g_autofree guint8 *bin = NULL;
gsize binlen = 0;
/* Convert hex string to binary */
bin = mm_utils_hexstr2bin (hex, -1, &binlen, error);
if (!bin) {
g_prefix_error (error, "SIM returned malformed response '%s': ", hex);
return NULL;
}
return mm_sim_convert_spn_to_utf8 (bin, binlen, error);
}
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 (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
gchar *result;
gchar *spn;
result = g_task_propagate_pointer (G_TASK (res), error);
if (!result)
return NULL;
spn = parse_spn (result, error);
g_free (result);
return spn;
}
STR_REPLY_READY_FN (load_operator_name)
static void
load_operator_name (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_obj_dbg (self, "loading operator name...");
/* READ BINARY of EFspn (Service Provider Name) ETSI 51.011 section 10.3.11 */
mm_base_modem_at_command (
self->priv->modem,
"+CRSM=176,28486,0,0,17",
10,
FALSE,
(GAsyncReadyCallback)load_operator_name_command_ready,
g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* GID1 and GID2 */
static GByteArray *
parse_gid (const gchar *response,
GError **error)
{
guint sw1 = 0;
guint sw2 = 0;
g_autofree gchar *hex = NULL;
if (!mm_3gpp_parse_crsm_response (response, &sw1, &sw2, &hex, error))
return NULL;
if ((sw1 == 0x90 && sw2 == 0x00) ||
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
guint8 *bin = NULL;
gsize binlen = 0;
/* Convert hex string to binary */
bin = mm_utils_hexstr2bin (hex, -1, &binlen, error);
if (!bin) {
g_prefix_error (error, "SIM returned malformed response '%s': ", hex);
return NULL;
}
/* return as bytearray */
return g_byte_array_new_take (bin, binlen);
}
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 GByteArray *
common_load_gid_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
g_autofree gchar *result = NULL;
result = g_task_propagate_pointer (G_TASK (res), error);
if (!result)
return NULL;
return parse_gid (result, error);
}
STR_REPLY_READY_FN (load_gid1)
STR_REPLY_READY_FN (load_gid2)
static void
load_gid1 (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
/* READ BINARY of EFgid1 */
mm_base_modem_at_command (
self->priv->modem,
"+CRSM=176,28478,0,0,0",
10,
FALSE,
(GAsyncReadyCallback)load_gid1_command_ready,
g_task_new (self, NULL, callback, user_data));
}
static void
load_gid2 (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
/* READ BINARY of EFgid2 */
mm_base_modem_at_command (
self->priv->modem,
"+CRSM=176,28479,0,0,0",
10,
FALSE,
(GAsyncReadyCallback)load_gid2_command_ready,
g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
MMBaseSim *
mm_base_sim_new_initialized (MMBaseModem *modem,
GObject *bind_to,
guint slot_number,
gboolean active,
const gchar *sim_identifier,
const gchar *imsi,
const gchar *eid,
const gchar *operator_identifier,
const gchar *operator_name,
const GStrv emergency_numbers)
{
MMBaseSim *sim;
sim = MM_BASE_SIM (g_object_new (MM_TYPE_BASE_SIM,
MM_BASE_SIM_MODEM, modem,
MM_BIND_TO, bind_to,
MM_BASE_SIM_SLOT_NUMBER, slot_number,
"active", active,
"sim-identifier", sim_identifier,
"imsi", imsi,
"eid", eid,
"operator-identifier", operator_identifier,
"operator-name", operator_name,
"emergency-numbers", emergency_numbers,
NULL));
mm_base_sim_export (sim);
return sim;
}
/*****************************************************************************/
typedef struct _InitAsyncContext InitAsyncContext;
static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
INITIALIZATION_STEP_WAIT_READY,
INITIALIZATION_STEP_SIM_TYPE,
INITIALIZATION_STEP_ESIM_STATUS,
INITIALIZATION_STEP_SIM_IDENTIFIER,
INITIALIZATION_STEP_IMSI,
INITIALIZATION_STEP_OPERATOR_ID,
INITIALIZATION_STEP_OPERATOR_NAME,
INITIALIZATION_STEP_EMERGENCY_NUMBERS,
INITIALIZATION_STEP_PREFERRED_NETWORKS,
INITIALIZATION_STEP_GID1,
INITIALIZATION_STEP_GID2,
INITIALIZATION_STEP_EID,
INITIALIZATION_STEP_REMOVABILITY,
INITIALIZATION_STEP_LAST
} InitializationStep;
struct _InitAsyncContext {
InitializationStep step;
guint sim_identifier_tries;
};
MMBaseSim *
mm_base_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_base_sim_export (MM_BASE_SIM (sim));
return MM_BASE_SIM (sim);
}
static gboolean
initable_init_finish (GAsyncInitable *initable,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_boolean (G_TASK (result), error);
}
#undef COMMON_STR_REPLY_READY_FN
#define COMMON_STR_REPLY_READY_FN(NAME,DISPLAY,VALUE_FORMAT) \
static void \
init_load_##NAME##_ready (MMBaseSim *self, \
GAsyncResult *res, \
GTask *task) \
{ \
InitAsyncContext *ctx; \
g_autoptr(GError) error = NULL; \
g_autofree gchar *val = NULL; \
\
val = MM_BASE_SIM_GET_CLASS (self)->load_##NAME##_finish (self, res, &error); \
mm_gdbus_sim_set_##NAME (MM_GDBUS_SIM (self), val); \
\
if (error) \
mm_obj_dbg (self, "couldn't load %s: %s", DISPLAY, error->message); \
else \
mm_obj_info (self, "loaded %s: %s", DISPLAY, VALUE_FORMAT (val)); \
\
/* Go on to next step */ \
ctx = g_task_get_task_data (task); \
ctx->step++; \
interface_initialization_step (task); \
}
#undef STR_REPLY_READY_FN
#define STR_REPLY_READY_FN(NAME,DISPLAY) COMMON_STR_REPLY_READY_FN (NAME, DISPLAY, (const gchar *))
#undef PERSONAL_STR_REPLY_READY_FN
#define PERSONAL_STR_REPLY_READY_FN(NAME,DISPLAY) COMMON_STR_REPLY_READY_FN (NAME, DISPLAY, mm_log_str_personal_info)
#undef ENUM_REPLY_READY_FN
#define ENUM_REPLY_READY_FN(NAME,DISPLAY,ENUM_TYPE,ENUM_GET_STRING) \
static void \
init_load_##NAME##_ready (MMBaseSim *self, \
GAsyncResult *res, \
GTask *task) \
{ \
InitAsyncContext *ctx; \
g_autoptr(GError) error = NULL; \
ENUM_TYPE val; \
\
val = MM_BASE_SIM_GET_CLASS (self)->load_##NAME##_finish (self, res, &error); \
mm_gdbus_sim_set_##NAME (MM_GDBUS_SIM (self), (guint) val); \
\
if (error) \
mm_obj_dbg (self, "couldn't load %s: %s", DISPLAY, error->message); \
else \
mm_obj_info (self, "loaded %s: %s", DISPLAY, ENUM_GET_STRING (val)); \
\
/* Go on to next step */ \
ctx = g_task_get_task_data (task); \
ctx->step++; \
interface_initialization_step (task); \
}
#undef BYTEARRAY_REPLY_READY_FN
#define BYTEARRAY_REPLY_READY_FN(NAME,DISPLAY) \
static void \
init_load_##NAME##_ready (MMBaseSim *self, \
GAsyncResult *res, \
GTask *task) \
{ \
InitAsyncContext *ctx; \
g_autoptr(GError) error = NULL; \
g_autoptr(GByteArray) bytearray = NULL; \
\
bytearray = MM_BASE_SIM_GET_CLASS (self)->load_##NAME##_finish (self, res, &error); \
mm_gdbus_sim_set_##NAME (MM_GDBUS_SIM (self), \
(bytearray ? \
g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, \
bytearray->data, \
bytearray->len, \
sizeof (guint8)) : \
NULL)); \
\
if (error) \
mm_obj_dbg (self, "couldn't load %s: %s", DISPLAY, error->message); \
else { \
g_autofree gchar *bytearray_str = NULL; \
\
bytearray_str = mm_utils_bin2hexstr (bytearray->data, bytearray->len); \
mm_obj_info (self, "loaded %s: %s", DISPLAY, bytearray_str); \
} \
\
/* Go on to next step */ \
ctx = g_task_get_task_data (task); \
ctx->step++; \
interface_initialization_step (task); \
}
ENUM_REPLY_READY_FN (removability, "removability", MMSimRemovability, mm_sim_removability_get_string)
PERSONAL_STR_REPLY_READY_FN (eid, "EID")
BYTEARRAY_REPLY_READY_FN (gid2, "GID2")
BYTEARRAY_REPLY_READY_FN (gid1, "GID1")
static void
init_load_preferred_networks_ready (MMBaseSim *self,
GAsyncResult *res,
GTask *task)
{
InitAsyncContext *ctx;
g_autoptr(GError) error = NULL;
GList *preferred_nets_list;
preferred_nets_list = MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish (self, res, &error);
if (error)
mm_obj_dbg (self, "couldn't load list of preferred networks: %s", error->message);
else {
g_autoptr(GString) str = NULL;
GList *l;
str = g_string_new ("");
for (l = preferred_nets_list; l; l = g_list_next (l)) {
MMSimPreferredNetwork *item;
g_autofree gchar *access_tech_str = NULL;
item = (MMSimPreferredNetwork *)(l->data);
access_tech_str = mm_modem_access_technology_build_string_from_mask (mm_sim_preferred_network_get_access_technology (item));
g_string_append_printf (str, "%s%s (%s)", str->len ? ", " : "", mm_sim_preferred_network_get_operator_code (item), access_tech_str);
}
mm_obj_info (self, "loaded list of preferred networks: %s", str->str);
}
mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self),
mm_sim_preferred_network_list_get_variant (preferred_nets_list));
g_list_free_full (preferred_nets_list, (GDestroyNotify) mm_sim_preferred_network_free);
/* Go on to next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_initialization_step (task);
}
static void
init_load_emergency_numbers_ready (MMBaseSim *self,
GAsyncResult *res,
GTask *task)
{
InitAsyncContext *ctx;
g_autoptr(GError) error = NULL;
g_auto(GStrv) str_list = NULL;
str_list = MM_BASE_SIM_GET_CLASS (self)->load_emergency_numbers_finish (self, res, &error);
if (error)
mm_obj_dbg (self, "couldn't load list of emergency numbers: %s", error->message);
else {
g_autoptr(GString) str = NULL;
guint i;
str = g_string_new ("");
for (i = 0; str_list && str_list[i]; i++)
g_string_append_printf (str, "%s%s", str->len ? ", " : "", str_list[i]);
mm_obj_info (self, "loaded list of emergency numbers: %s", str->str);
}
mm_gdbus_sim_set_emergency_numbers (MM_GDBUS_SIM (self), (const gchar *const *) str_list);
/* Go on to next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_initialization_step (task);
}
STR_REPLY_READY_FN (operator_name, "operator name")
STR_REPLY_READY_FN (operator_identifier, "operator identifier")
PERSONAL_STR_REPLY_READY_FN (imsi, "IMSI")
static void
init_load_sim_identifier_ready (MMBaseSim *self,
GAsyncResult *res,
GTask *task)
{
InitAsyncContext *ctx;
GError *error = NULL;
gchar *simid;
ctx = g_task_get_task_data (task);
simid = MM_BASE_SIM_GET_CLASS (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 (task);
return;
}
mm_obj_warn (self, "couldn't load SIM identifier: %s", error ? error->message : "unknown error");
g_clear_error (&error);
} else
mm_obj_info (self, "loaded SIM identifier: %s", mm_log_str_personal_info (simid));
mm_gdbus_sim_set_sim_identifier (MM_GDBUS_SIM (self), simid);
g_free (simid);
/* Go on to next step */
ctx->step++;
interface_initialization_step (task);
}
ENUM_REPLY_READY_FN (esim_status, "esim status", MMSimEsimStatus, mm_sim_esim_status_get_string)
ENUM_REPLY_READY_FN (sim_type, "sim type", MMSimType, mm_sim_type_get_string)
static void
init_wait_sim_ready (MMBaseSim *self,
GAsyncResult *res,
GTask *task)
{
InitAsyncContext *ctx;
g_autoptr(GError) error = NULL;
if (!MM_BASE_SIM_GET_CLASS (self)->wait_sim_ready_finish (self, res, &error))
mm_obj_dbg (self, "couldn't wait for SIM to be ready: %s", error->message);
/* Go on to next step */
ctx = g_task_get_task_data (task);
ctx->step++;
interface_initialization_step (task);
}
static void
interface_initialization_step (GTask *task)
{
MMBaseSim *self;
InitAsyncContext *ctx;
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_WAIT_READY:
if (MM_BASE_SIM_GET_CLASS (self)->wait_sim_ready &&
MM_BASE_SIM_GET_CLASS (self)->wait_sim_ready_finish) {
MM_BASE_SIM_GET_CLASS (self)->wait_sim_ready (
self,
(GAsyncReadyCallback)init_wait_sim_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_SIM_TYPE:
if (mm_gdbus_sim_get_sim_type (MM_GDBUS_SIM (self)) == MM_SIM_TYPE_UNKNOWN &&
MM_BASE_SIM_GET_CLASS (self)->load_sim_type &&
MM_BASE_SIM_GET_CLASS (self)->load_sim_type_finish) {
MM_BASE_SIM_GET_CLASS (self)->load_sim_type (
self,
(GAsyncReadyCallback)init_load_sim_type_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_ESIM_STATUS:
/* Don't load eSIM status if the SIM is known to be a physical SIM */
if (IS_PSIM (self))
mm_obj_dbg (self, "not loading eSIM status in physical SIM");
else if (mm_gdbus_sim_get_esim_status (MM_GDBUS_SIM (self)) == MM_SIM_ESIM_STATUS_UNKNOWN &&
MM_BASE_SIM_GET_CLASS (self)->load_esim_status &&
MM_BASE_SIM_GET_CLASS (self)->load_esim_status_finish) {
MM_BASE_SIM_GET_CLASS (self)->load_esim_status (
self,
(GAsyncReadyCallback)init_load_esim_status_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_SIM_IDENTIFIER:
/* Don't load SIM ICCID if the SIM is known to be an eSIM without
* profiles; otherwise (if physical SIM, or if eSIM with profile, or if
* SIM type unknown) try to load it. */
if (IS_ESIM_WITHOUT_PROFILES (self))
mm_obj_dbg (self, "not loading SIM identifier in eSIM without profiles");
else if (mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (self)) == NULL &&
MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier &&
MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish) {
MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier (
self,
(GAsyncReadyCallback)init_load_sim_identifier_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_IMSI:
/* Don't load SIM IMSI if the SIM is known to be an eSIM without
* profiles; otherwise (if physical SIM, or if eSIM with profile, or if
* SIM type unknown) try to load it. */
if (IS_ESIM_WITHOUT_PROFILES (self))
mm_obj_dbg (self, "not loading IMSI in eSIM without profiles");
else if (mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (self)) == NULL &&
MM_BASE_SIM_GET_CLASS (self)->load_imsi &&
MM_BASE_SIM_GET_CLASS (self)->load_imsi_finish) {
MM_BASE_SIM_GET_CLASS (self)->load_imsi (
self,
(GAsyncReadyCallback)init_load_imsi_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_OPERATOR_ID:
/* Don't load operator ID if the SIM is known to be an eSIM without
* profiles; otherwise (if physical SIM, or if eSIM with profile, or if
* SIM type unknown) try to load it. */
if (IS_ESIM_WITHOUT_PROFILES (self))
mm_obj_dbg (self, "not loading operator ID in eSIM without profiles");
else if (mm_gdbus_sim_get_operator_identifier (MM_GDBUS_SIM (self)) == NULL &&
MM_BASE_SIM_GET_CLASS (self)->load_operator_identifier &&
MM_BASE_SIM_GET_CLASS (self)->load_operator_identifier_finish) {
MM_BASE_SIM_GET_CLASS (self)->load_operator_identifier (
self,
(GAsyncReadyCallback)init_load_operator_identifier_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_OPERATOR_NAME:
/* Don't load operator name if the SIM is known to be an eSIM without
* profiles; otherwise (if physical SIM, or if eSIM with profile, or if
* SIM type unknown) try to load it. */
if (IS_ESIM_WITHOUT_PROFILES (self))
mm_obj_dbg (self, "not loading operator name in eSIM without profiles");
else if (mm_gdbus_sim_get_operator_name (MM_GDBUS_SIM (self)) == NULL &&
MM_BASE_SIM_GET_CLASS (self)->load_operator_name &&
MM_BASE_SIM_GET_CLASS (self)->load_operator_name_finish) {
MM_BASE_SIM_GET_CLASS (self)->load_operator_name (
self,
(GAsyncReadyCallback)init_load_operator_name_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_EMERGENCY_NUMBERS:
/* Don't load emergency numbers if the SIM is known to be an eSIM without
* profiles; otherwise (if physical SIM, or if eSIM with profile, or if
* SIM type unknown) try to load it. */
if (IS_ESIM_WITHOUT_PROFILES (self))
mm_obj_dbg (self, "not loading emergency numbers in eSIM without profiles");
else if (mm_gdbus_sim_get_emergency_numbers (MM_GDBUS_SIM (self)) == NULL &&
MM_BASE_SIM_GET_CLASS (self)->load_emergency_numbers &&
MM_BASE_SIM_GET_CLASS (self)->load_emergency_numbers_finish) {
MM_BASE_SIM_GET_CLASS (self)->load_emergency_numbers (
self,
(GAsyncReadyCallback)init_load_emergency_numbers_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_PREFERRED_NETWORKS:
/* Don't load preferred networks if the SIM is known to be an eSIM without
* profiles; otherwise (if physical SIM, or if eSIM with profile, or if
* SIM type unknown) try to load it. */
if (IS_ESIM_WITHOUT_PROFILES (self))
mm_obj_dbg (self, "not loading preferred networks in eSIM without profiles");
else if (mm_gdbus_sim_get_preferred_networks (MM_GDBUS_SIM (self)) == NULL &&
MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks &&
MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish) {
MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks (
self,
(GAsyncReadyCallback)init_load_preferred_networks_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_GID1:
/* Don't load GID1 if the SIM is known to be an eSIM without profiles;
* otherwise (if physical SIM, or if eSIM with profile, or if
* SIM type unknown) try to load it. */
if (IS_ESIM_WITHOUT_PROFILES (self))
mm_obj_dbg (self, "not loading GID1 in eSIM without profiles");
else if (mm_gdbus_sim_get_gid1 (MM_GDBUS_SIM (self)) == NULL &&
MM_BASE_SIM_GET_CLASS (self)->load_gid1 &&
MM_BASE_SIM_GET_CLASS (self)->load_gid1_finish) {
MM_BASE_SIM_GET_CLASS (self)->load_gid1 (
self,
(GAsyncReadyCallback)init_load_gid1_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_GID2:
/* Don't load GID2 if the SIM is known to be an eSIM without profiles;
* otherwise (if physical SIM, or if eSIM with profile, or if
* SIM type unknown) try to load it. */
if (IS_ESIM_WITHOUT_PROFILES (self))
mm_obj_dbg (self, "not loading GID2 in eSIM without profiles");
else if (mm_gdbus_sim_get_gid2 (MM_GDBUS_SIM (self)) == NULL &&
MM_BASE_SIM_GET_CLASS (self)->load_gid2 &&
MM_BASE_SIM_GET_CLASS (self)->load_gid2_finish) {
MM_BASE_SIM_GET_CLASS (self)->load_gid2 (
self,
(GAsyncReadyCallback)init_load_gid2_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_EID:
/* Don't load EID if the SIM is known to be a physical SIM; otherwise
* (if eSIM with or without profiles) try to load it. */
if (IS_PSIM (self))
mm_obj_dbg (self, "not loading EID in physical SIM");
else if (mm_gdbus_sim_get_eid (MM_GDBUS_SIM (self)) == NULL &&
MM_BASE_SIM_GET_CLASS (self)->load_eid &&
MM_BASE_SIM_GET_CLASS (self)->load_eid_finish) {
MM_BASE_SIM_GET_CLASS (self)->load_eid (
self,
(GAsyncReadyCallback)init_load_eid_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_REMOVABILITY:
/* Although not very common, there are removable eSIMs, so always try to
* load it, regardless of SIM type. */
if (mm_gdbus_sim_get_removability (MM_GDBUS_SIM (self)) == MM_SIM_REMOVABILITY_UNKNOWN &&
MM_BASE_SIM_GET_CLASS (self)->load_removability &&
MM_BASE_SIM_GET_CLASS (self)->load_removability_finish) {
MM_BASE_SIM_GET_CLASS (self)->load_removability (
self,
(GAsyncReadyCallback)init_load_removability_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
default:
break;
}
g_assert_not_reached ();
}
static void
common_init_async (GAsyncInitable *initable,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBaseSim *self;
InitAsyncContext *ctx;
GTask *task;
self = MM_BASE_SIM (initable);
ctx = g_new (InitAsyncContext, 1);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->sim_identifier_tries = 0;
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, g_free);
interface_initialization_step (task);
}
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_eid (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);
mm_gdbus_sim_set_gid1 (MM_GDBUS_SIM (initable), NULL);
mm_gdbus_sim_set_gid2 (MM_GDBUS_SIM (initable), NULL);
common_init_async (initable, cancellable, callback, user_data);
}
void
mm_base_sim_new (MMBaseModem *modem,
GObject *bind_to,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (MM_TYPE_BASE_SIM,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
MM_BIND_TO, bind_to,
"active", TRUE, /* by default always active */
NULL);
}
gboolean
mm_base_sim_initialize_finish (MMBaseSim *self,
GAsyncResult *result,
GError **error)
{
return initable_init_finish (G_ASYNC_INITABLE (self), result, error);
}
void
mm_base_sim_initialize (MMBaseSim *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
common_init_async (G_ASYNC_INITABLE (self),
cancellable,
callback,
user_data);
}
/*****************************************************************************/
static gchar *
log_object_build_id (MMLogObject *_self)
{
MMBaseSim *self;
self = MM_BASE_SIM (_self);
return g_strdup_printf ("sim%u", self->priv->dbus_id);
}
/*****************************************************************************/
static void
set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MMBaseSim *self = MM_BASE_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)
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)
sim_dbus_unexport (self);
else if (self->priv->path)
sim_dbus_export (self);
break;
case PROP_BIND_TO:
g_clear_object (&self->priv->bind_to);
self->priv->bind_to = g_value_dup_object (value);
mm_bind_to (MM_BIND (self), MM_BASE_SIM_CONNECTION, self->priv->bind_to);
break;
case PROP_MODEM:
g_clear_object (&self->priv->modem);
self->priv->modem = g_value_dup_object (value);
break;
case PROP_SLOT_NUMBER:
self->priv->slot_number = g_value_get_uint (value);
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)
{
MMBaseSim *self = MM_BASE_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_BIND_TO:
g_value_set_object (value, self->priv->bind_to);
break;
case PROP_MODEM:
g_value_set_object (value, self->priv->modem);
break;
case PROP_SLOT_NUMBER:
g_value_set_uint (value, self->priv->slot_number);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
mm_base_sim_init (MMBaseSim *self)
{
static guint id = 0;
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_SIM, MMBaseSimPrivate);
/* Each SIM is given a unique id to build its own DBus path */
self->priv->dbus_id = id++;
/* Setup authorization provider */
self->priv->authp = mm_auth_provider_get ();
self->priv->authp_cancellable = g_cancellable_new ();
}
static void
finalize (GObject *object)
{
MMBaseSim *self = MM_BASE_SIM (object);
g_free (self->priv->path);
G_OBJECT_CLASS (mm_base_sim_parent_class)->finalize (object);
}
static void
dispose (GObject *object)
{
MMBaseSim *self = MM_BASE_SIM (object);
if (self->priv->connection) {
/* If we arrived here with a valid connection, make sure we unexport
* the object */
sim_dbus_unexport (self);
g_clear_object (&self->priv->connection);
}
g_clear_object (&self->priv->modem);
g_clear_object (&self->priv->bind_to);
g_cancellable_cancel (self->priv->authp_cancellable);
g_clear_object (&self->priv->authp_cancellable);
G_OBJECT_CLASS (mm_base_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
log_object_iface_init (MMLogObjectInterface *iface)
{
iface->build_id = log_object_build_id;
}
static void
bind_iface_init (MMBindInterface *iface)
{
}
static void
mm_base_sim_class_init (MMBaseSimClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMBaseSimPrivate));
/* 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->load_emergency_numbers = load_emergency_numbers;
klass->load_emergency_numbers_finish = load_emergency_numbers_finish;
klass->load_preferred_networks = load_preferred_networks;
klass->load_preferred_networks_finish = load_preferred_networks_finish;
klass->load_gid1 = load_gid1;
klass->load_gid1_finish = common_load_gid_finish;
klass->load_gid2 = load_gid2;
klass->load_gid2_finish = common_load_gid_finish;
klass->set_preferred_networks = set_preferred_networks;
klass->set_preferred_networks_finish = set_preferred_networks_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_BASE_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_BASE_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_BASE_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]);
g_object_class_override_property (object_class, PROP_BIND_TO, MM_BIND_TO);
properties[PROP_SLOT_NUMBER] =
g_param_spec_uint (MM_BASE_SIM_SLOT_NUMBER,
"Slot number",
"The slot number where the SIM is inserted",
0, G_MAXUINT, 0,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_SLOT_NUMBER, properties[PROP_SLOT_NUMBER]);
/* Signals */
signals[SIGNAL_PIN_LOCK_ENABLED] =
g_signal_new (MM_BASE_SIM_PIN_LOCK_ENABLED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MMBaseSimClass, pin_lock_enabled),
NULL, NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
}