diff options
author | Akash Aggarwal <quic_akasagga@quicinc.com> | 2022-11-21 16:34:13 +0530 |
---|---|---|
committer | Aleksander Morgado <aleksandermj@chromium.org> | 2022-11-28 12:31:00 +0000 |
commit | f4d867cd5977380b7a07da066ad08cd7079942da (patch) | |
tree | 8d9ce75c90fff324fcaf9ae2c184ba62b31e5cbc /src | |
parent | b5c9bae104e9397cb24e493954ef4c41ce0a89a3 (diff) |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-sim-qmi.c | 246 |
1 files changed, 135 insertions, 111 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 <aleksander@aleksander.es> + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #include <config.h> @@ -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; + + self = g_task_get_source_object (task); - if (!get_home_network (client, res, &mcc, &mnc, &mnc_with_pcs, NULL, &error)) { + 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 gchar * +parse_spn (const guint8 *bin, + gsize len, + GError **error) +{ + g_autoptr(GByteArray) bin_array = NULL; + gsize binlen; + + /* 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 -load_operator_name_ready (QmiClientNas *client, - GAsyncResult *res, - GTask *task) +uim_read_efspn_ready (QmiClientUim *client, + GAsyncResult *res, + GTask *task) { - gchar *operator_name = NULL; - GError *error = NULL; + GError *error = NULL; + g_autoptr(GArray) read_result = NULL; + gchar *spn; - if (!get_home_network (client, res, NULL, NULL, NULL, &operator_name, &error)) + 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); } /*****************************************************************************/ @@ -1923,6 +1936,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) { GObjectClass *object_class = G_OBJECT_CLASS (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; |