From f4d867cd5977380b7a07da066ad08cd7079942da Mon Sep 17 00:00:00 2001 From: Akash Aggarwal Date: Mon, 21 Nov 2022 16:34:13 +0530 Subject: [PATCH] mm-sim-qmi: use UIM service for loading operator identifier and name Output of NAS Get Home Network depends on network state, and we may fail to read SIM card info if device is not attached. Load card operator name and identifier using UIM Read Transparent. --- src/mm-sim-qmi.c | 248 ++++++++++++++++++++++++++--------------------- 1 file changed, 136 insertions(+), 112 deletions(-) diff --git a/src/mm-sim-qmi.c b/src/mm-sim-qmi.c index 7707fc1d..03a44240 100644 --- a/src/mm-sim-qmi.c +++ b/src/mm-sim-qmi.c @@ -12,6 +12,7 @@ * * Copyright (C) 2012 Google, Inc. * Copyright (C) 2016 Aleksander Morgado + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #include @@ -42,7 +43,8 @@ enum { static GParamSpec *properties[PROP_LAST]; struct _MMSimQmiPrivate { - gboolean dms_uim_deprecated; + gboolean dms_uim_deprecated; + gchar *imsi; }; static const guint16 mf_file_path[] = { 0x3F00 }; @@ -444,10 +446,13 @@ uim_get_imsi_ready (QmiClientUim *client, GAsyncResult *res, GTask *task) { + MMSimQmi *self; GError *error = NULL; g_autoptr(GArray) read_result = NULL; g_autofree gchar *imsi = NULL; + self = g_task_get_source_object (task); + read_result = uim_read_finish (client, res, &error); if (!read_result) { g_task_return_error (task, error); @@ -463,12 +468,18 @@ uim_get_imsi_ready (QmiClientUim *client, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "IMSI is malformed"); - else + else { /* EFimsi contains a length byte, follwed by a nibble for parity, * and then followed by the actual IMSI in BCD. After converting * the BCD into a decimal string, we simply skip the first 3 * decimal digits to obtain the IMSI. */ + + /* Cache IMSI */ + g_free (self->priv->imsi); + self->priv->imsi = g_strdup (imsi + 3); + g_task_return_pointer (task, g_strdup (imsi + 3), g_free); + } g_object_unref (task); } @@ -489,8 +500,11 @@ dms_uim_get_imsi_ready (QmiClientDms *client, GAsyncResult *res, GTask *task) { + MMSimQmi *self; QmiMessageDmsUimGetImsiOutput *output = NULL; - GError *error = NULL; + GError *error = NULL; + + self = g_task_get_source_object (task); output = qmi_client_dms_uim_get_imsi_finish (client, res, &error); if (!output) { @@ -503,6 +517,11 @@ dms_uim_get_imsi_ready (QmiClientDms *client, const gchar *str = NULL; qmi_message_dms_uim_get_imsi_output_get_imsi (output, &str, NULL); + + /* Cache IMSI */ + g_free (self->priv->imsi); + self->priv->imsi = g_strdup (str); + g_task_return_pointer (task, g_strdup (str), g_free); } @@ -633,63 +652,6 @@ load_gid2 (MMBaseSim *self, /*****************************************************************************/ /* 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, @@ -699,54 +661,70 @@ load_operator_identifier_finish (MMBaseSim *self, } static void -load_operator_identifier_ready (QmiClientNas *client, - GAsyncResult *res, - GTask *task) +uim_read_efad_ready (QmiClientUim *client, + GAsyncResult *res, + GTask *task) { - guint16 mcc, mnc; - gboolean mnc_with_pcs; - GError *error = NULL; - GString *aux; + MMSimQmi *self; + GError *error = NULL; + g_autoptr(GArray) read_result = NULL; + guint mnc_length; - if (!get_home_network (client, res, &mcc, &mnc, &mnc_with_pcs, NULL, &error)) { + self = g_task_get_source_object (task); + + read_result = uim_read_finish (client, res, &error); + if (!read_result) { 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), g_free); + if (read_result->len < 4) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Unexpected response length reading EFad: %u", read_result->len); + g_object_unref (task); + return; + } + + /* MNC length is byte 4 of this SIM file */ + mnc_length = read_result->data[3]; + if (mnc_length == 2 || mnc_length == 3) { + g_task_return_pointer (task, g_strndup (self->priv->imsi, 3 + mnc_length), g_free); + } else { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "SIM returned invalid MNC length %d (should be either 2 or 3)", mnc_length); + } g_object_unref (task); } static void -load_operator_identifier (MMBaseSim *self, +load_operator_identifier (MMBaseSim *_self, GAsyncReadyCallback callback, gpointer user_data) { - GTask *task; - QmiClient *client = NULL; + MMSimQmi *self; + GTask *task; + self = MM_SIM_QMI (_self); task = g_task_new (self, NULL, callback, user_data); - if (!ensure_qmi_client (task, - MM_SIM_QMI (self), - QMI_SERVICE_NAS, &client)) - return; mm_obj_dbg (self, "loading SIM operator identifier..."); - qmi_client_nas_get_home_network (QMI_CLIENT_NAS (client), - NULL, - 5, - NULL, - (GAsyncReadyCallback)load_operator_identifier_ready, - task); + + if (!self->priv->imsi) { + g_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't load SIM operator identifier without IMSI"); + g_object_unref (task); + return; + } + + uim_read (self, + 0x6FAD, + adf_file_path, + G_N_ELEMENTS (adf_file_path), + (GAsyncReadyCallback)uim_read_efad_ready, + task); } /*****************************************************************************/ @@ -760,42 +738,77 @@ load_operator_name_finish (MMBaseSim *self, return g_task_propagate_pointer (G_TASK (res), error); } -static void -load_operator_name_ready (QmiClientNas *client, - GAsyncResult *res, - GTask *task) +static gchar * +parse_spn (const guint8 *bin, + gsize len, + GError **error) { - gchar *operator_name = NULL; - GError *error = NULL; + g_autoptr(GByteArray) bin_array = NULL; + gsize binlen; - if (!get_home_network (client, res, NULL, NULL, NULL, &operator_name, &error)) + /* Remove the FF filler at the end */ + binlen = len; + while (binlen > 1 && bin[binlen - 1] == 0xff) + binlen--; + if (binlen <= 1) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM returned empty spn"); + return NULL; + } + + /* Setup as bytearray. + * First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */ + bin_array = g_byte_array_sized_new (binlen - 1); + g_byte_array_append (bin_array, bin + 1, binlen - 1); + + return mm_modem_charset_bytearray_to_utf8 (bin_array, MM_MODEM_CHARSET_GSM, FALSE, error); +} + +static void +uim_read_efspn_ready (QmiClientUim *client, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + g_autoptr(GArray) read_result = NULL; + gchar *spn; + + read_result = uim_read_finish (client, res, &error); + if (!read_result) { g_task_return_error (task, error); - else - g_task_return_pointer (task, operator_name, g_free); + g_object_unref (task); + return; + } + + spn = parse_spn ((const guint8 *) read_result->data, read_result->len, &error); + + if (!spn) { + g_task_return_error (task, error); + } else { + g_task_return_pointer (task, spn, g_free); + } + g_object_unref (task); } static void -load_operator_name (MMBaseSim *self, +load_operator_name (MMBaseSim *_self, GAsyncReadyCallback callback, gpointer user_data) { - GTask *task; - QmiClient *client = NULL; + MMSimQmi *self; + GTask *task; + self = MM_SIM_QMI (_self); task = g_task_new (self, NULL, callback, user_data); - if (!ensure_qmi_client (task, - MM_SIM_QMI (self), - QMI_SERVICE_NAS, &client)) - return; mm_obj_dbg (self, "loading SIM operator name..."); - qmi_client_nas_get_home_network (QMI_CLIENT_NAS (client), - NULL, - 5, - NULL, - (GAsyncReadyCallback)load_operator_name_ready, - task); + + uim_read (self, + 0x6F46, + adf_file_path, + G_N_ELEMENTS (adf_file_path), + (GAsyncReadyCallback)uim_read_efspn_ready, + task); } /*****************************************************************************/ @@ -1922,6 +1935,16 @@ get_property (GObject *object, } } +static void +finalize (GObject *object) +{ + MMSimQmi *self = MM_SIM_QMI (object); + + g_free (self->priv->imsi); + + G_OBJECT_CLASS (mm_sim_qmi_parent_class)->finalize (object); +} + static void mm_sim_qmi_class_init (MMSimQmiClass *klass) { @@ -1932,6 +1955,7 @@ mm_sim_qmi_class_init (MMSimQmiClass *klass) object_class->get_property = get_property; object_class->set_property = set_property; + object_class->finalize = finalize; base_sim_class->wait_sim_ready = wait_sim_ready; base_sim_class->wait_sim_ready_finish = wait_sim_ready_finish;