diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2021-04-04 10:36:04 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2021-04-29 10:13:22 +0000 |
commit | 62cbf7fa86a3f4773184186b7f937a7bee8b0e5a (patch) | |
tree | ea212942aa9c24578bc7662752b7ab73777db9ab | |
parent | dd7938b3e48bf9726146ab5c4b941c056b9cbce9 (diff) |
modem-helpers: new profile list helpers
The new helpers allow converting a PDP context list returned from the
+CGDCONT? parser to a list of 3GPP profile objects.
The new mm_3gpp_profile_list_find_best() method is equivalent to the
mm_3gpp_select_best_cid() one, but using profile objects as
input/output instead of specific settings.
The unit tests that were testing mm_3gpp_select_best_cid() are also
converted to use mm_3gpp_profile_list_find_best().
-rw-r--r-- | src/mm-modem-helpers.c | 197 | ||||
-rw-r--r-- | src/mm-modem-helpers.h | 25 | ||||
-rw-r--r-- | src/tests/test-modem-helpers.c | 123 |
3 files changed, 293 insertions, 52 deletions
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index 41573ede..9a389ec3 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -1613,6 +1613,203 @@ mm_3gpp_select_best_cid (const gchar *apn, /*************************************************************************/ +MM3gppProfile * +mm_3gpp_profile_new_from_pdp_context (MM3gppPdpContext *pdp_context) +{ + MM3gppProfile *profile; + + profile = mm_3gpp_profile_new (); + mm_3gpp_profile_set_profile_id (profile, pdp_context->cid); + mm_3gpp_profile_set_apn (profile, pdp_context->apn); + mm_3gpp_profile_set_ip_type (profile, pdp_context->pdp_type); + return profile; +} + +GList * +mm_3gpp_profile_list_new_from_pdp_context_list (GList *pdp_context_list) +{ + GList *profile_list = NULL; + GList *l; + + for (l = pdp_context_list; l; l = g_list_next (l)) { + MM3gppPdpContext *pdp_context; + MM3gppProfile *profile; + + pdp_context = (MM3gppPdpContext *)l->data; + profile = mm_3gpp_profile_new_from_pdp_context (pdp_context); + profile_list = g_list_append (profile_list, profile); + } + return profile_list; +} + +void +mm_3gpp_profile_list_free (GList *profile_list) +{ + g_list_free_full (profile_list, g_object_unref); +} + +MM3gppProfile * +mm_3gpp_profile_list_find_by_profile_id (GList *profile_list, + gint profile_id, + GError **error) +{ + GList *l; + + for (l = profile_list; l; l = g_list_next (l)) { + MM3gppProfile *iter_profile = l->data; + + if (mm_3gpp_profile_get_profile_id (iter_profile) == profile_id) + return g_object_ref (iter_profile); + } + + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, + "Profile '%d' not found", profile_id); + return NULL; +} + +gint +mm_3gpp_profile_list_find_empty (GList *profile_list, + gint min_profile_id, + gint max_profile_id, + GError **error) +{ + GList *l; + gint profile_id; + + profile_id = min_profile_id; + for (l = profile_list; l; l = g_list_next (l)) { + MM3gppProfile *iter_profile = l->data; + gint iter_profile_id; + + iter_profile_id = mm_3gpp_profile_get_profile_id (iter_profile); + if (iter_profile_id > profile_id) + break; + if (iter_profile_id == profile_id) + profile_id++; + } + + if (profile_id > max_profile_id) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, + "No empty profile available"); + return MM_3GPP_PROFILE_ID_UNKNOWN; + } + + return profile_id; +} + +gint +mm_3gpp_profile_list_find_best (GList *profile_list, + MM3gppProfile *requested, + GEqualFunc cmp_apn, + MM3gppProfileCmpFlags cmp_flags, + gint min_profile_id, + gint max_profile_id, + gpointer log_object, + MM3gppProfile **out_reused, + gboolean *out_overwritten) +{ + GList *l; + MMBearerIpFamily requested_ip_type; + gint prev_profile_id = 0; + gint unused_profile_id = 0; + gint max_found_profile_id = 0; + gint max_allowed_profile_id = 0; + gint blank_profile_id = 0; + + g_assert (out_reused); + g_assert (out_overwritten); + + requested_ip_type = mm_3gpp_profile_get_ip_type (requested); + + /* When looking for exact profile matches we should not compare + * the profile id, as the requested profile won't have one set */ + cmp_flags |= MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_ID; + + /* Look for the exact PDP context we want */ + for (l = profile_list; l; l = g_list_next (l)) { + MM3gppProfile *iter_profile = l->data; + MMBearerIpFamily iter_ip_type; + const gchar *iter_apn; + gint iter_profile_id; + + iter_profile_id = mm_3gpp_profile_get_profile_id (iter_profile); + + /* Always prefer an exact match; compare all supported fields except for profile id */ + if (mm_3gpp_profile_cmp (iter_profile, requested, cmp_apn, cmp_flags)) { + mm_obj_dbg (log_object, "found exact context at profile %d", iter_profile_id); + *out_reused = g_object_ref (iter_profile); + *out_overwritten = FALSE; + return iter_profile_id; + } + + /* Same PDP type but with no APN set? we may use that one if no exact match found */ + iter_ip_type = mm_3gpp_profile_get_ip_type (iter_profile); + iter_apn = mm_3gpp_profile_get_apn (iter_profile); + if ((iter_ip_type == requested_ip_type) && (!iter_apn || !iter_apn[0]) && !blank_profile_id) + blank_profile_id = iter_profile_id; + + /* If an unused CID was not found yet and the previous CID is not (CID - 1), + * this means that (previous CID + 1) is an unused CID that can be used. + * This logic will allow us using unused CIDs that are available in the gaps + * between already defined contexts. + */ + if (!unused_profile_id && prev_profile_id && ((prev_profile_id + 1) < iter_profile_id)) + unused_profile_id = prev_profile_id + 1; + + /* Update previous CID value to the current CID for use in the next loop, + * unless an unused CID was already found. + */ + if (!unused_profile_id) + prev_profile_id = iter_profile_id; + + /* Update max CID if we found a bigger one */ + if (max_found_profile_id < iter_profile_id) + max_found_profile_id = iter_profile_id; + } + + /* Try to use an unused CID detected in between the already defined contexts */ + if (unused_profile_id) { + mm_obj_dbg (log_object, "found unused profile %d", unused_profile_id); + *out_reused = NULL; + *out_overwritten = FALSE; + return unused_profile_id; + } + + /* If the max existing CID found during CGDCONT? is below the max allowed + * CID, then we can use the next available CID because it's an unused one. */ + max_allowed_profile_id = max_profile_id; + if (max_found_profile_id && (max_found_profile_id < max_allowed_profile_id)) { + mm_obj_dbg (log_object, "found unused profile %d (<%d)", max_found_profile_id + 1, max_allowed_profile_id); + *out_reused = NULL; + *out_overwritten = FALSE; + return (max_found_profile_id + 1); + } + + /* Rewrite a context defined with no APN, if any */ + if (blank_profile_id) { + mm_obj_dbg (log_object, "rewriting profile %d with empty APN", blank_profile_id); + *out_reused = NULL; + *out_overwritten = TRUE; + return blank_profile_id; + } + + /* Rewrite the last existing one found */ + if (max_found_profile_id) { + mm_obj_dbg (log_object, "rewriting last profile %d detected", max_found_profile_id); + *out_reused = NULL; + *out_overwritten = TRUE; + return max_found_profile_id; + } + + /* Otherwise, just fallback to min CID */ + mm_obj_dbg (log_object, "falling back to profile %d", min_profile_id); + *out_reused = NULL; + *out_overwritten = TRUE; + return min_profile_id; +} + +/*************************************************************************/ + static void mm_3gpp_pdp_context_format_free (MM3gppPdpContextFormat *format) { diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h index dfa71884..c16ddb64 100644 --- a/src/mm-modem-helpers.h +++ b/src/mm-modem-helpers.h @@ -470,6 +470,31 @@ gboolean mm_3gpp_rssnr_level_to_rssnr (gint rssnr_level, GStrv mm_3gpp_parse_emergency_numbers (const char *raw, GError **error); +/* PDP context -> profile */ +MM3gppProfile *mm_3gpp_profile_new_from_pdp_context (MM3gppPdpContext *pdp_context); + +/* Profile list operations */ +GList *mm_3gpp_profile_list_new_from_pdp_context_list (GList *pdp_context_list); +void mm_3gpp_profile_list_free (GList *profile_list); + +gint mm_3gpp_profile_list_find_empty (GList *profile_list, + gint min_profile_id, + gint max_profile_id, + GError **error); +gint mm_3gpp_profile_list_find_best (GList *profile_list, + MM3gppProfile *requested, + GEqualFunc cmp_apn, + MM3gppProfileCmpFlags cmp_flags, + gint min_profile_id, + gint max_profile_id, + gpointer log_object, + MM3gppProfile **out_reused, + gboolean *out_overwritten); + +MM3gppProfile *mm_3gpp_profile_list_find_by_profile_id (GList *profile_list, + gint profile_id, + GError **error); + /*****************************************************************************/ /* CDMA specific helpers and utilities */ /*****************************************************************************/ diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c index 9624c520..3a2f7b66 100644 --- a/src/tests/test-modem-helpers.c +++ b/src/tests/test-modem-helpers.c @@ -2758,12 +2758,12 @@ typedef struct { MMBearerIpFamily ip_family; const gchar *cgdcont_test; const gchar *cgdcont_query; - guint expected_cid; - gboolean expected_cid_reused; - gboolean expected_cid_overwritten; -} CidSelectionTest; + gint expected_profile_id; + gboolean expected_profile_id_reused; + gboolean expected_profile_id_overwritten; +} ProfileSelectionTest; -static const CidSelectionTest cid_selection_tests[] = { +static const ProfileSelectionTest profile_selection_tests[] = { /* Test: exact APN match */ { .apn = "ac.vodafone.es", @@ -2774,9 +2774,9 @@ static const CidSelectionTest cid_selection_tests[] = { .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 2,\"IP\",\"ac.vodafone.es\",\"\",0,0\r\n" "+CGDCONT: 3,\"IP\",\"inet.es\",\"\",0,0\r\n", - .expected_cid = 2, - .expected_cid_reused = TRUE, - .expected_cid_overwritten = FALSE + .expected_profile_id = 2, + .expected_profile_id_reused = TRUE, + .expected_profile_id_overwritten = FALSE }, /* Test: exact APN match reported as activated */ { @@ -2788,9 +2788,9 @@ static const CidSelectionTest cid_selection_tests[] = { .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 2,\"IP\",\"ac.vodafone.es.MNC001.MCC214.GPRS\",\"\",0,0\r\n" "+CGDCONT: 3,\"IP\",\"inet.es\",\"\",0,0\r\n", - .expected_cid = 2, - .expected_cid_reused = TRUE, - .expected_cid_overwritten = FALSE + .expected_profile_id = 2, + .expected_profile_id_reused = TRUE, + .expected_profile_id_overwritten = FALSE }, /* Test: first empty slot in between defined contexts */ { @@ -2801,9 +2801,9 @@ static const CidSelectionTest cid_selection_tests[] = { "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n", .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 10,\"IP\",\"inet.es\",\"\",0,0\r\n", - .expected_cid = 2, - .expected_cid_reused = FALSE, - .expected_cid_overwritten = FALSE + .expected_profile_id = 2, + .expected_profile_id_reused = FALSE, + .expected_profile_id_overwritten = FALSE }, /* Test: first empty slot in between defined contexts, different PDP types */ { @@ -2814,9 +2814,9 @@ static const CidSelectionTest cid_selection_tests[] = { "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n", .cgdcont_query = "+CGDCONT: 1,\"IPV6\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 10,\"IP\",\"inet.es\",\"\",0,0\r\n", - .expected_cid = 2, - .expected_cid_reused = FALSE, - .expected_cid_overwritten = FALSE + .expected_profile_id = 2, + .expected_profile_id_reused = FALSE, + .expected_profile_id_overwritten = FALSE }, /* Test: first empty slot after last context found */ { @@ -2827,9 +2827,9 @@ static const CidSelectionTest cid_selection_tests[] = { "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n", .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 2,\"IP\",\"inet.es\",\"\",0,0\r\n", - .expected_cid = 3, - .expected_cid_reused = FALSE, - .expected_cid_overwritten = FALSE + .expected_profile_id = 3, + .expected_profile_id_reused = FALSE, + .expected_profile_id_overwritten = FALSE }, /* Test: first empty slot after last context found, different PDP types */ { @@ -2840,9 +2840,9 @@ static const CidSelectionTest cid_selection_tests[] = { "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n", .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 2,\"IPV6\",\"inet.es\",\"\",0,0\r\n", - .expected_cid = 3, - .expected_cid_reused = FALSE, - .expected_cid_overwritten = FALSE + .expected_profile_id = 3, + .expected_profile_id_reused = FALSE, + .expected_profile_id_overwritten = FALSE }, /* Test: no empty slot, rewrite context with empty APN */ { @@ -2854,9 +2854,9 @@ static const CidSelectionTest cid_selection_tests[] = { .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 2,\"IP\",\"\",\"\",0,0\r\n" "+CGDCONT: 3,\"IP\",\"inet.es\",\"\",0,0\r\n", - .expected_cid = 2, - .expected_cid_reused = FALSE, - .expected_cid_overwritten = TRUE + .expected_profile_id = 2, + .expected_profile_id_reused = FALSE, + .expected_profile_id_overwritten = TRUE }, /* Test: no empty slot, rewrite last context found */ { @@ -2868,9 +2868,9 @@ static const CidSelectionTest cid_selection_tests[] = { .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n" "+CGDCONT: 2,\"IP\",\"vzwinternet\",\"\",0,0\r\n" "+CGDCONT: 3,\"IP\",\"inet.es\",\"\",0,0\r\n", - .expected_cid = 3, - .expected_cid_reused = FALSE, - .expected_cid_overwritten = TRUE + .expected_profile_id = 3, + .expected_profile_id_reused = FALSE, + .expected_profile_id_overwritten = TRUE }, /* Test: CGDCONT? and CGDCONT=? failures, fallback to CID=1 (a.g. some Android phones) */ { @@ -2878,39 +2878,58 @@ static const CidSelectionTest cid_selection_tests[] = { .ip_family = MM_BEARER_IP_FAMILY_IPV4, .cgdcont_test = NULL, .cgdcont_query = NULL, - .expected_cid = 1, - .expected_cid_reused = FALSE, - .expected_cid_overwritten = TRUE + .expected_profile_id = 1, + .expected_profile_id_reused = FALSE, + .expected_profile_id_overwritten = TRUE }, }; static void -test_cid_selection (void) +test_profile_selection (void) { guint i; - for (i = 0; i < G_N_ELEMENTS (cid_selection_tests); i++) { - const CidSelectionTest *test; - GList *context_list; - GList *context_format_list; - guint cid; - gboolean cid_reused; - gboolean cid_overwritten; + for (i = 0; i < G_N_ELEMENTS (profile_selection_tests); i++) { + const ProfileSelectionTest *test; + GList *context_format_list; + GList *context_list; + GList *profile_list; + gint profile_id; + guint min_allowed_cid = 1; + guint max_allowed_cid = G_MAXINT - 1; + g_autoptr(MM3gppProfile) requested = NULL; + g_autoptr(MM3gppProfile) reused = NULL; + gboolean profile_id_overwritten; - test = &cid_selection_tests[i]; + test = &profile_selection_tests[i]; - context_format_list = test->cgdcont_test ? mm_3gpp_parse_cgdcont_test_response (test->cgdcont_test, NULL, NULL) : NULL; - context_list = test->cgdcont_query ? mm_3gpp_parse_cgdcont_read_response (test->cgdcont_query, NULL) : NULL; - - cid = mm_3gpp_select_best_cid (test->apn, test->ip_family, - context_list, context_format_list, - NULL, - &cid_reused, &cid_overwritten); + requested = mm_3gpp_profile_new (); + mm_3gpp_profile_set_apn (requested, test->apn); + mm_3gpp_profile_set_ip_type (requested, test->ip_family); - g_assert_cmpuint (cid, ==, test->expected_cid); - g_assert_cmpuint (cid_reused, ==, test->expected_cid_reused); - g_assert_cmpuint (cid_overwritten, ==, test->expected_cid_overwritten); + context_format_list = test->cgdcont_test ? mm_3gpp_parse_cgdcont_test_response (test->cgdcont_test, NULL, NULL) : NULL; + mm_3gpp_pdp_context_format_list_find_range (context_format_list, test->ip_family, &min_allowed_cid, &max_allowed_cid); + context_list = test->cgdcont_query ? mm_3gpp_parse_cgdcont_read_response (test->cgdcont_query, NULL) : NULL; + profile_list = mm_3gpp_profile_list_new_from_pdp_context_list (context_list); + + profile_id = mm_3gpp_profile_list_find_best (profile_list, + requested, + (GEqualFunc)mm_3gpp_cmp_apn_name, + (MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_ID | + MM_3GPP_PROFILE_CMP_FLAGS_NO_AUTH | + MM_3GPP_PROFILE_CMP_FLAGS_NO_APN_TYPE), + min_allowed_cid, + max_allowed_cid, + NULL, /* log_object */ + &reused, + &profile_id_overwritten); + + g_assert_cmpuint (profile_id, ==, test->expected_profile_id); + g_assert_cmpuint (!!reused, ==, test->expected_profile_id_reused); + g_assert_cmpuint (profile_id_overwritten, ==, test->expected_profile_id_overwritten); + + mm_3gpp_profile_list_free (profile_list); mm_3gpp_pdp_context_format_list_free (context_format_list); mm_3gpp_pdp_context_list_free (context_list); } @@ -4646,7 +4665,7 @@ int main (int argc, char **argv) g_test_suite_add (suite, TESTCASE (test_cgdcont_read_response_nokia, NULL)); g_test_suite_add (suite, TESTCASE (test_cgdcont_read_response_samsung, NULL)); - g_test_suite_add (suite, TESTCASE (test_cid_selection, NULL)); + g_test_suite_add (suite, TESTCASE (test_profile_selection, NULL)); g_test_suite_add (suite, TESTCASE (test_cgact_read_response_none, NULL)); g_test_suite_add (suite, TESTCASE (test_cgact_read_response_single_inactive, NULL)); |