The MMBroadbandModemQmi will not create a MMSimQmi until the unlock status has been checked, and therefore this means that when the SIM object is being created we already know whether the modem supports DMS UIM commands or not, so avoid further fallbacks in the SIM object.
1184 lines
36 KiB
C
1184 lines
36 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) 2012 Google, Inc.
|
|
* Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
|
*/
|
|
|
|
#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-log.h"
|
|
#include "mm-sim-qmi.h"
|
|
|
|
G_DEFINE_TYPE (MMSimQmi, mm_sim_qmi, MM_TYPE_BASE_SIM)
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_DMS_UIM_DEPRECATED,
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *properties[PROP_LAST];
|
|
|
|
struct _MMSimQmiPrivate {
|
|
gboolean dms_uim_deprecated;
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
ensure_qmi_client (GTask *task,
|
|
MMSimQmi *self,
|
|
QmiService service,
|
|
QmiClient **o_client)
|
|
{
|
|
MMBaseModem *modem = NULL;
|
|
QmiClient *client;
|
|
MMPortQmi *port;
|
|
|
|
g_object_get (self,
|
|
MM_BASE_SIM_MODEM, &modem,
|
|
NULL);
|
|
g_assert (MM_IS_BASE_MODEM (modem));
|
|
|
|
port = mm_base_modem_peek_port_qmi (modem);
|
|
g_object_unref (modem);
|
|
|
|
if (!port) {
|
|
if (task) {
|
|
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Couldn't peek QMI port");
|
|
g_object_unref (task);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
client = mm_port_qmi_peek_client (port,
|
|
service,
|
|
MM_PORT_QMI_FLAG_DEFAULT);
|
|
if (!client) {
|
|
if (task) {
|
|
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Couldn't peek client for service '%s'",
|
|
qmi_service_get_string (service));
|
|
g_object_unref (task);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
*o_client = client;
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load SIM ID (ICCID) */
|
|
|
|
static gchar *
|
|
load_sim_identifier_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
dms_uim_get_iccid_ready (QmiClientDms *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimGetIccidOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_dms_uim_get_iccid_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_dms_uim_get_iccid_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't get UIM ICCID: ");
|
|
g_task_return_error (task, error);
|
|
} else {
|
|
const gchar *str = NULL;
|
|
|
|
qmi_message_dms_uim_get_iccid_output_get_iccid (output, &str, NULL);
|
|
g_task_return_pointer (task, g_strdup (str), (GDestroyNotify) g_free);
|
|
}
|
|
|
|
if (output)
|
|
qmi_message_dms_uim_get_iccid_output_unref (output);
|
|
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
load_sim_identifier (MMBaseSim *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
QmiClient *client = NULL;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
if (!ensure_qmi_client (task,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_DMS, &client))
|
|
return;
|
|
|
|
mm_dbg ("loading SIM identifier...");
|
|
qmi_client_dms_uim_get_iccid (QMI_CLIENT_DMS (client),
|
|
NULL,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)dms_uim_get_iccid_ready,
|
|
task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load IMSI */
|
|
|
|
static gchar *
|
|
load_imsi_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
dms_uim_get_imsi_ready (QmiClientDms *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimGetImsiOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_dms_uim_get_imsi_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_dms_uim_get_imsi_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't get UIM IMSI: ");
|
|
g_task_return_error (task, error);
|
|
} else {
|
|
const gchar *str = NULL;
|
|
|
|
qmi_message_dms_uim_get_imsi_output_get_imsi (output, &str, NULL);
|
|
g_task_return_pointer (task, g_strdup (str), (GDestroyNotify) g_free);
|
|
}
|
|
|
|
if (output)
|
|
qmi_message_dms_uim_get_imsi_output_unref (output);
|
|
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
load_imsi (MMBaseSim *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
QmiClient *client = NULL;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
if (!ensure_qmi_client (task,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_DMS, &client))
|
|
return;
|
|
|
|
mm_dbg ("loading IMSI...");
|
|
qmi_client_dms_uim_get_imsi (QMI_CLIENT_DMS (client),
|
|
NULL,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)dms_uim_get_imsi_ready,
|
|
task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load operator identifier */
|
|
|
|
static gboolean
|
|
get_home_network (QmiClientNas *client,
|
|
GAsyncResult *res,
|
|
guint16 *out_mcc,
|
|
guint16 *out_mnc,
|
|
gboolean *out_mnc_with_pcs,
|
|
gchar **out_operator_name,
|
|
GError **error)
|
|
{
|
|
QmiMessageNasGetHomeNetworkOutput *output = NULL;
|
|
gboolean success = FALSE;
|
|
|
|
output = qmi_client_nas_get_home_network_finish (client, res, error);
|
|
if (!output) {
|
|
g_prefix_error (error, "QMI operation failed: ");
|
|
} else if (!qmi_message_nas_get_home_network_output_get_result (output, error)) {
|
|
g_prefix_error (error, "Couldn't get home network: ");
|
|
} else {
|
|
const gchar *name = NULL;
|
|
|
|
qmi_message_nas_get_home_network_output_get_home_network (
|
|
output,
|
|
out_mcc,
|
|
out_mnc,
|
|
&name,
|
|
NULL);
|
|
if (out_operator_name)
|
|
*out_operator_name = g_strdup (name);
|
|
|
|
if (out_mnc_with_pcs) {
|
|
gboolean is_3gpp;
|
|
gboolean mnc_includes_pcs_digit;
|
|
|
|
if (qmi_message_nas_get_home_network_output_get_home_network_3gpp_mnc (
|
|
output,
|
|
&is_3gpp,
|
|
&mnc_includes_pcs_digit,
|
|
NULL) &&
|
|
is_3gpp &&
|
|
mnc_includes_pcs_digit) {
|
|
/* MNC should include PCS digit */
|
|
*out_mnc_with_pcs = TRUE;
|
|
} else {
|
|
/* We default to NO PCS digit, unless of course the MNC is already > 99 */
|
|
*out_mnc_with_pcs = FALSE;
|
|
}
|
|
}
|
|
|
|
success = TRUE;
|
|
}
|
|
|
|
if (output)
|
|
qmi_message_nas_get_home_network_output_unref (output);
|
|
|
|
return success;
|
|
}
|
|
|
|
static gchar *
|
|
load_operator_identifier_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
load_operator_identifier_ready (QmiClientNas *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
guint16 mcc, mnc;
|
|
gboolean mnc_with_pcs;
|
|
GError *error = NULL;
|
|
GString *aux;
|
|
|
|
if (!get_home_network (client, res, &mcc, &mnc, &mnc_with_pcs, NULL, &error)) {
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
aux = g_string_new ("");
|
|
/* MCC always 3 digits */
|
|
g_string_append_printf (aux, "%.3" G_GUINT16_FORMAT, mcc);
|
|
/* Guess about MNC, if < 100 assume it's 2 digits, no PCS info here */
|
|
if (mnc >= 100 || mnc_with_pcs)
|
|
g_string_append_printf (aux, "%.3" G_GUINT16_FORMAT, mnc);
|
|
else
|
|
g_string_append_printf (aux, "%.2" G_GUINT16_FORMAT, mnc);
|
|
g_task_return_pointer (task, g_string_free (aux, FALSE), (GDestroyNotify) g_free);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
load_operator_identifier (MMBaseSim *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
QmiClient *client = NULL;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
if (!ensure_qmi_client (task,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_NAS, &client))
|
|
return;
|
|
|
|
mm_dbg ("loading SIM operator identifier...");
|
|
qmi_client_nas_get_home_network (QMI_CLIENT_NAS (client),
|
|
NULL,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)load_operator_identifier_ready,
|
|
task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Load operator name */
|
|
|
|
static gchar *
|
|
load_operator_name_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
load_operator_name_ready (QmiClientNas *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
gchar *operator_name = NULL;
|
|
GError *error = NULL;
|
|
|
|
if (!get_home_network (client, res, NULL, NULL, NULL, &operator_name, &error))
|
|
g_task_return_error (task, error);
|
|
else
|
|
g_task_return_pointer (task, operator_name, (GDestroyNotify) g_free);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
load_operator_name (MMBaseSim *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
QmiClient *client = NULL;
|
|
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
if (!ensure_qmi_client (task,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_NAS, &client))
|
|
return;
|
|
|
|
mm_dbg ("loading SIM operator name...");
|
|
qmi_client_nas_get_home_network (QMI_CLIENT_NAS (client),
|
|
NULL,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)load_operator_name_ready,
|
|
task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Send PIN */
|
|
|
|
static GError *
|
|
pin_qmi_error_to_mobile_equipment_error (GError *qmi_error)
|
|
{
|
|
GError *me_error = NULL;
|
|
|
|
if (g_error_matches (qmi_error,
|
|
QMI_PROTOCOL_ERROR,
|
|
QMI_PROTOCOL_ERROR_INCORRECT_PIN)) {
|
|
me_error = g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD,
|
|
qmi_error->message);
|
|
} else if (g_error_matches (qmi_error,
|
|
QMI_PROTOCOL_ERROR,
|
|
QMI_PROTOCOL_ERROR_PIN_BLOCKED)) {
|
|
me_error = g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR,
|
|
MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK,
|
|
qmi_error->message);
|
|
}
|
|
|
|
if (me_error) {
|
|
g_error_free (qmi_error);
|
|
return me_error;
|
|
}
|
|
|
|
return qmi_error;
|
|
}
|
|
|
|
static gboolean
|
|
send_pin_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
uim_verify_pin_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimVerifyPinOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_uim_verify_pin_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_uim_verify_pin_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't verify PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_uim_verify_pin_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
uim_verify_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimVerifyPinInput *input;
|
|
QmiClient *client = NULL;
|
|
|
|
if (!ensure_qmi_client (task,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_UIM, &client))
|
|
return;
|
|
|
|
input = qmi_message_uim_verify_pin_input_new ();
|
|
qmi_message_uim_verify_pin_input_set_info (
|
|
input,
|
|
QMI_UIM_PIN_ID_PIN1,
|
|
g_task_get_task_data (task),
|
|
NULL);
|
|
qmi_message_uim_verify_pin_input_set_session_information (
|
|
input,
|
|
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
|
|
"", /* ignored */
|
|
NULL);
|
|
qmi_client_uim_verify_pin (QMI_CLIENT_UIM (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback) uim_verify_pin_ready,
|
|
task);
|
|
qmi_message_uim_verify_pin_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
dms_uim_verify_pin_ready (QmiClientDms *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimVerifyPinOutput *output = NULL;
|
|
GError *error = NULL;
|
|
MMSimQmi *self;
|
|
|
|
self = g_task_get_source_object (task);
|
|
|
|
output = qmi_client_dms_uim_verify_pin_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_dms_uim_verify_pin_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't verify PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_dms_uim_verify_pin_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
dms_uim_verify_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimVerifyPinInput *input;
|
|
QmiClient *client = NULL;
|
|
|
|
if (!ensure_qmi_client (NULL,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_DMS, &client)) {
|
|
/* Very unlikely that this will ever happen, but anyway, try with
|
|
* UIM service instead */
|
|
uim_verify_pin (self, task);
|
|
return;
|
|
}
|
|
|
|
mm_dbg ("Sending PIN...");
|
|
input = qmi_message_dms_uim_verify_pin_input_new ();
|
|
qmi_message_dms_uim_verify_pin_input_set_info (
|
|
input,
|
|
QMI_DMS_UIM_PIN_ID_PIN,
|
|
g_task_get_task_data (task),
|
|
NULL);
|
|
qmi_client_dms_uim_verify_pin (QMI_CLIENT_DMS (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback) dms_uim_verify_pin_ready,
|
|
task);
|
|
qmi_message_dms_uim_verify_pin_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
send_pin (MMBaseSim *_self,
|
|
const gchar *pin,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
MMSimQmi *self;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
g_task_set_task_data (task, g_strdup (pin), (GDestroyNotify) g_free);
|
|
|
|
mm_dbg ("Verifying PIN...");
|
|
if (!self->priv->dms_uim_deprecated)
|
|
dms_uim_verify_pin (self, task);
|
|
else
|
|
uim_verify_pin (self, task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Send PUK */
|
|
|
|
typedef struct {
|
|
gchar *puk;
|
|
gchar *new_pin;
|
|
} UnblockPinContext;
|
|
|
|
static void
|
|
unblock_pin_context_free (UnblockPinContext *ctx)
|
|
{
|
|
g_free (ctx->puk);
|
|
g_free (ctx->new_pin);
|
|
g_slice_free (UnblockPinContext, ctx);
|
|
}
|
|
|
|
static gboolean
|
|
send_puk_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
uim_unblock_pin_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimUnblockPinOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_uim_unblock_pin_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_uim_unblock_pin_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't unblock PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_uim_unblock_pin_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
uim_unblock_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimUnblockPinInput *input;
|
|
QmiClient *client = NULL;
|
|
UnblockPinContext *ctx;
|
|
|
|
if (!ensure_qmi_client (task,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_UIM, &client))
|
|
return;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
input = qmi_message_uim_unblock_pin_input_new ();
|
|
qmi_message_uim_unblock_pin_input_set_info (
|
|
input,
|
|
QMI_UIM_PIN_ID_PIN1,
|
|
ctx->puk,
|
|
ctx->new_pin,
|
|
NULL);
|
|
qmi_message_uim_unblock_pin_input_set_session_information (
|
|
input,
|
|
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
|
|
"", /* ignored */
|
|
NULL);
|
|
qmi_client_uim_unblock_pin (QMI_CLIENT_UIM (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback) uim_unblock_pin_ready,
|
|
task);
|
|
qmi_message_uim_unblock_pin_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
dms_uim_unblock_pin_ready (QmiClientDms *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimUnblockPinOutput *output = NULL;
|
|
GError *error = NULL;
|
|
MMSimQmi *self;
|
|
|
|
self = g_task_get_source_object (task);
|
|
|
|
output = qmi_client_dms_uim_unblock_pin_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_dms_uim_unblock_pin_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't unblock PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_dms_uim_unblock_pin_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
dms_uim_unblock_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimUnblockPinInput *input;
|
|
QmiClient *client = NULL;
|
|
UnblockPinContext *ctx;
|
|
|
|
if (!ensure_qmi_client (NULL,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_DMS, &client)) {
|
|
/* Very unlikely that this will ever happen, but anyway, try with
|
|
* UIM service instead */
|
|
uim_unblock_pin (self, task);
|
|
return;
|
|
}
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
input = qmi_message_dms_uim_unblock_pin_input_new ();
|
|
qmi_message_dms_uim_unblock_pin_input_set_info (
|
|
input,
|
|
QMI_DMS_UIM_PIN_ID_PIN,
|
|
ctx->puk,
|
|
ctx->new_pin,
|
|
NULL);
|
|
qmi_client_dms_uim_unblock_pin (QMI_CLIENT_DMS (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)dms_uim_unblock_pin_ready,
|
|
task);
|
|
qmi_message_dms_uim_unblock_pin_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
send_puk (MMBaseSim *_self,
|
|
const gchar *puk,
|
|
const gchar *new_pin,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
UnblockPinContext *ctx;
|
|
MMSimQmi *self;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
ctx = g_slice_new (UnblockPinContext);
|
|
ctx->puk = g_strdup (puk);
|
|
ctx->new_pin = g_strdup (new_pin);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify) unblock_pin_context_free);
|
|
|
|
mm_dbg ("Unblocking PIN...");
|
|
if (!self->priv->dms_uim_deprecated)
|
|
dms_uim_unblock_pin (self, task);
|
|
else
|
|
uim_unblock_pin (self, task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Change PIN */
|
|
|
|
typedef struct {
|
|
gchar *old_pin;
|
|
gchar *new_pin;
|
|
} ChangePinContext;
|
|
|
|
static void
|
|
change_pin_context_free (ChangePinContext *ctx)
|
|
{
|
|
g_free (ctx->old_pin);
|
|
g_free (ctx->new_pin);
|
|
g_slice_free (ChangePinContext, ctx);
|
|
}
|
|
|
|
static gboolean
|
|
change_pin_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
uim_change_pin_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimChangePinOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_uim_change_pin_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_uim_change_pin_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't change PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_uim_change_pin_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
uim_change_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimChangePinInput *input;
|
|
QmiClient *client = NULL;
|
|
ChangePinContext *ctx;
|
|
|
|
if (!ensure_qmi_client (task,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_UIM, &client))
|
|
return;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
input = qmi_message_uim_change_pin_input_new ();
|
|
qmi_message_uim_change_pin_input_set_info (
|
|
input,
|
|
QMI_UIM_PIN_ID_PIN1,
|
|
ctx->old_pin,
|
|
ctx->new_pin,
|
|
NULL);
|
|
qmi_message_uim_change_pin_input_set_session_information (
|
|
input,
|
|
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
|
|
"", /* ignored */
|
|
NULL);
|
|
qmi_client_uim_change_pin (QMI_CLIENT_UIM (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback) uim_change_pin_ready,
|
|
task);
|
|
qmi_message_uim_change_pin_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
dms_uim_change_pin_ready (QmiClientDms *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimChangePinOutput *output = NULL;
|
|
GError *error = NULL;
|
|
MMSimQmi *self;
|
|
|
|
self = g_task_get_source_object (task);
|
|
|
|
output = qmi_client_dms_uim_change_pin_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_dms_uim_change_pin_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't change PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_dms_uim_change_pin_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
dms_uim_change_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimChangePinInput *input;
|
|
QmiClient *client = NULL;
|
|
ChangePinContext *ctx;
|
|
|
|
if (!ensure_qmi_client (NULL,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_DMS, &client)) {
|
|
/* Very unlikely that this will ever happen, but anyway, try with
|
|
* UIM service instead */
|
|
uim_change_pin (self, task);
|
|
return;
|
|
}
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
input = qmi_message_dms_uim_change_pin_input_new ();
|
|
qmi_message_dms_uim_change_pin_input_set_info (
|
|
input,
|
|
QMI_DMS_UIM_PIN_ID_PIN,
|
|
ctx->old_pin,
|
|
ctx->new_pin,
|
|
NULL);
|
|
qmi_client_dms_uim_change_pin (QMI_CLIENT_DMS (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback) dms_uim_change_pin_ready,
|
|
task);
|
|
qmi_message_dms_uim_change_pin_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
change_pin (MMBaseSim *_self,
|
|
const gchar *old_pin,
|
|
const gchar *new_pin,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
ChangePinContext *ctx;
|
|
MMSimQmi *self;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
ctx = g_slice_new (ChangePinContext);
|
|
ctx->old_pin = g_strdup (old_pin);
|
|
ctx->new_pin = g_strdup (new_pin);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify) change_pin_context_free);
|
|
|
|
mm_dbg ("Changing PIN...");
|
|
if (!self->priv->dms_uim_deprecated)
|
|
dms_uim_change_pin (self, task);
|
|
else
|
|
uim_change_pin (self, task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Enable PIN */
|
|
|
|
typedef struct {
|
|
gchar *pin;
|
|
gboolean enabled;
|
|
} EnablePinContext;
|
|
|
|
static void
|
|
enable_pin_context_free (EnablePinContext *ctx)
|
|
{
|
|
g_free (ctx->pin);
|
|
g_slice_free (EnablePinContext, ctx);
|
|
}
|
|
|
|
static gboolean
|
|
enable_pin_finish (MMBaseSim *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void
|
|
uim_set_pin_protection_ready (QmiClientUim *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimSetPinProtectionOutput *output = NULL;
|
|
GError *error = NULL;
|
|
|
|
output = qmi_client_uim_set_pin_protection_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_uim_set_pin_protection_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't enable PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_uim_set_pin_protection_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
uim_enable_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageUimSetPinProtectionInput *input;
|
|
QmiClient *client = NULL;
|
|
EnablePinContext *ctx;
|
|
|
|
if (!ensure_qmi_client (task,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_UIM, &client))
|
|
return;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
input = qmi_message_uim_set_pin_protection_input_new ();
|
|
qmi_message_uim_set_pin_protection_input_set_info (
|
|
input,
|
|
QMI_UIM_PIN_ID_PIN1,
|
|
ctx->enabled,
|
|
ctx->pin,
|
|
NULL);
|
|
qmi_message_uim_set_pin_protection_input_set_session_information (
|
|
input,
|
|
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
|
|
"", /* ignored */
|
|
NULL);
|
|
qmi_client_uim_set_pin_protection (QMI_CLIENT_UIM (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)uim_set_pin_protection_ready,
|
|
task);
|
|
qmi_message_uim_set_pin_protection_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
dms_uim_set_pin_protection_ready (QmiClientDms *client,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimSetPinProtectionOutput *output = NULL;
|
|
GError *error = NULL;
|
|
MMSimQmi *self;
|
|
|
|
self = g_task_get_source_object (task);
|
|
|
|
output = qmi_client_dms_uim_set_pin_protection_finish (client, res, &error);
|
|
if (!output) {
|
|
g_prefix_error (&error, "QMI operation failed: ");
|
|
g_task_return_error (task, error);
|
|
} else if (!qmi_message_dms_uim_set_pin_protection_output_get_result (output, &error)) {
|
|
g_prefix_error (&error, "Couldn't enable PIN: ");
|
|
g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
|
|
} else
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
if (output)
|
|
qmi_message_dms_uim_set_pin_protection_output_unref (output);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
dms_uim_enable_pin (MMSimQmi *self,
|
|
GTask *task)
|
|
{
|
|
QmiMessageDmsUimSetPinProtectionInput *input;
|
|
QmiClient *client = NULL;
|
|
EnablePinContext *ctx;
|
|
|
|
if (!ensure_qmi_client (NULL,
|
|
MM_SIM_QMI (self),
|
|
QMI_SERVICE_DMS, &client)) {
|
|
/* Very unlikely that this will ever happen, but anyway, try with
|
|
* UIM service instead */
|
|
uim_enable_pin (self, task);
|
|
return;
|
|
}
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
input = qmi_message_dms_uim_set_pin_protection_input_new ();
|
|
qmi_message_dms_uim_set_pin_protection_input_set_info (
|
|
input,
|
|
QMI_DMS_UIM_PIN_ID_PIN,
|
|
ctx->enabled,
|
|
ctx->pin,
|
|
NULL);
|
|
qmi_client_dms_uim_set_pin_protection (QMI_CLIENT_DMS (client),
|
|
input,
|
|
5,
|
|
NULL,
|
|
(GAsyncReadyCallback)dms_uim_set_pin_protection_ready,
|
|
task);
|
|
qmi_message_dms_uim_set_pin_protection_input_unref (input);
|
|
}
|
|
|
|
static void
|
|
enable_pin (MMBaseSim *_self,
|
|
const gchar *pin,
|
|
gboolean enabled,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
EnablePinContext *ctx;
|
|
MMSimQmi *self;
|
|
|
|
self = MM_SIM_QMI (_self);
|
|
task = g_task_new (self, NULL, callback, user_data);
|
|
|
|
ctx = g_slice_new (EnablePinContext);
|
|
ctx->pin = g_strdup (pin);
|
|
ctx->enabled = enabled;
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify) enable_pin_context_free);
|
|
|
|
mm_dbg ("%s PIN...", enabled ? "Enabling" : "Disabling");
|
|
if (!self->priv->dms_uim_deprecated)
|
|
dms_uim_enable_pin (self, task);
|
|
else
|
|
uim_enable_pin (self, task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
MMBaseSim *
|
|
mm_sim_qmi_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);
|
|
}
|
|
|
|
void
|
|
mm_sim_qmi_new (MMBaseModem *modem,
|
|
gboolean dms_uim_deprecated,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
g_async_initable_new_async (MM_TYPE_SIM_QMI,
|
|
G_PRIORITY_DEFAULT,
|
|
cancellable,
|
|
callback,
|
|
user_data,
|
|
MM_BASE_SIM_MODEM, modem,
|
|
MM_SIM_QMI_DMS_UIM_DEPRECATED, dms_uim_deprecated,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
mm_sim_qmi_init (MMSimQmi *self)
|
|
{
|
|
/* Initialize private data */
|
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
|
MM_TYPE_SIM_QMI,
|
|
MMSimQmiPrivate);
|
|
}
|
|
|
|
static void
|
|
set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MMSimQmi *self = MM_SIM_QMI (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DMS_UIM_DEPRECATED:
|
|
self->priv->dms_uim_deprecated = g_value_get_boolean (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)
|
|
{
|
|
MMSimQmi *self = MM_SIM_QMI (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DMS_UIM_DEPRECATED:
|
|
g_value_set_boolean (value, self->priv->dms_uim_deprecated);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
mm_sim_qmi_class_init (MMSimQmiClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass);
|
|
|
|
g_type_class_add_private (object_class, sizeof (MMSimQmiPrivate));
|
|
|
|
object_class->get_property = get_property;
|
|
object_class->set_property = set_property;
|
|
|
|
base_sim_class->load_sim_identifier = load_sim_identifier;
|
|
base_sim_class->load_sim_identifier_finish = load_sim_identifier_finish;
|
|
base_sim_class->load_imsi = load_imsi;
|
|
base_sim_class->load_imsi_finish = load_imsi_finish;
|
|
base_sim_class->load_operator_identifier = load_operator_identifier;
|
|
base_sim_class->load_operator_identifier_finish = load_operator_identifier_finish;
|
|
base_sim_class->load_operator_name = load_operator_name;
|
|
base_sim_class->load_operator_name_finish = load_operator_name_finish;
|
|
base_sim_class->send_pin = send_pin;
|
|
base_sim_class->send_pin_finish = send_pin_finish;
|
|
base_sim_class->send_puk = send_puk;
|
|
base_sim_class->send_puk_finish = send_puk_finish;
|
|
base_sim_class->change_pin = change_pin;
|
|
base_sim_class->change_pin_finish = change_pin_finish;
|
|
base_sim_class->enable_pin = enable_pin;
|
|
base_sim_class->enable_pin_finish = enable_pin_finish;
|
|
|
|
properties[PROP_DMS_UIM_DEPRECATED] =
|
|
g_param_spec_boolean (MM_SIM_QMI_DMS_UIM_DEPRECATED,
|
|
"DMS UIM deprecated",
|
|
"Whether DMS UIM commands should be skipped",
|
|
FALSE,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
|
g_object_class_install_property (object_class, PROP_DMS_UIM_DEPRECATED, properties[PROP_DMS_UIM_DEPRECATED]);
|
|
}
|