mobile_provider: Choose the primary provider in preference to non-primary providers
Look for a 'primary' attribute on a provider to know if it should be
used as the provider when there are multiple providers that match the
same PLMN.
Update unit tests for mobile_provider_lookup_best_match
BUG=chrome-os-partner:3647
TEST=Ensure that APN selection is done properly
(cherry picked from commit 2e44b115b52ace624ee6b687219bb78c3fec9705)
Change-Id: I7624a8333ae27203ead7f01683214a4cd6d79c0f
Reviewed-on: http://gerrit.chromium.org/gerrit/1611
Tested-by: Jason Glasgow <jglasgow@chromium.org>
Reviewed-by: Jason Glasgow <jglasgow@chromium.org>
diff --git a/plugins/mobile_provider.c b/plugins/mobile_provider.c
index 873c290..6bf269a 100644
--- a/plugins/mobile_provider.c
+++ b/plugins/mobile_provider.c
@@ -46,8 +46,8 @@
int apnindex;
};
-#define WARN(fmt, arg...) \
- syslog(LOG_WARNING, "%s line %d: " fmt, state->filename, \
+#define WARN(fmt, arg...) \
+ syslog(LOG_WARNING, "%s line %d: " fmt, state->filename, \
state->linenum, ## arg)
#ifdef DEBUG
#define DEBUG(fmt, arg...) printf(fmt, ## arg)
@@ -112,6 +112,13 @@
return g_strdup(text);
}
+static void free_network_providers(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ GSList *list = (GSList *)value;
+ g_slist_free(list);
+}
+
static void free_provider(gpointer key, gpointer value, gpointer user_data)
{
struct mobile_provider *provider = (struct mobile_provider *)value;
@@ -182,6 +189,7 @@
gchar **pfields;
int num_names;
int num_apns;
+ int primary;
struct mobile_provider *provider;
state->provider = NULL;
@@ -189,7 +197,7 @@
state->apnindex = 0;
state->nameindex = 0;
pfields = g_strsplit(text, ",", 0);
- if (g_strv_length(pfields) != 2) {
+ if (g_strv_length(pfields) != 3) {
WARN("Badly formed \"providers\" entry: \"%s\"", text);
g_strfreev(pfields);
return;
@@ -197,6 +205,7 @@
errno = 0;
num_names = strtol(pfields[0], NULL, 0);
num_apns = strtol(pfields[1], NULL, 0);
+ primary = strtol(pfields[2], NULL, 0);
if (errno != 0) {
WARN("Error parsing \"providers\" entry \"%s\"", text);
g_strfreev(pfields);
@@ -208,12 +217,24 @@
if (num_names != 0)
provider->names = g_new0(struct localized_name *, num_names);
provider->num_apns = num_apns;
+ provider->primary = primary != 0;
if (num_apns != 0)
provider->apns = g_new0(struct mobile_apn *, num_apns);
state->provider = provider;
g_strfreev(pfields);
}
+static void network_add_provider(GHashTable *network_table,
+ gchar *network_id,
+ struct mobile_provider *provider)
+{
+ GSList *list;
+
+ list = g_hash_table_lookup(network_table, network_id);
+ list = g_slist_prepend(list, provider);
+ g_hash_table_insert(network_table, network_id, list);
+}
+
static void handle_networks(struct parser_state *state, gchar *text)
{
int listlen;
@@ -229,9 +250,9 @@
normalize_list(provider->networks);
listlen = g_strv_length(provider->networks);
for (i = 0; i < listlen; i++) {
- g_hash_table_insert(state->network_table,
- provider->networks[i],
- provider);
+ network_add_provider(state->network_table,
+ provider->networks[i],
+ provider);
}
}
}
@@ -323,8 +344,8 @@
name->name, provider);
} else {
DEBUG("Chain %s (%s) to (%s)\n", name->name,
- provider->country,
- other_provider->country);
+ provider->country,
+ other_provider->country);
while (other_provider->next != NULL)
other_provider = other_provider->next;
other_provider->next = provider;
@@ -333,8 +354,8 @@
} else {
name->name = g_strdup(namefields[1]);
DEBUG("Additional name %s (%s) for %s\n",
- name->name, provider->country,
- provider->names[0]->name);
+ name->name, provider->country,
+ provider->names[0]->name);
if (g_hash_table_lookup(state->name_table,
name->name) == NULL) {
g_hash_table_insert(state->name_table,
@@ -435,20 +456,46 @@
if (db != NULL) {
g_hash_table_foreach(db->name2provider, free_provider, NULL);
g_hash_table_destroy(db->name2provider);
+ g_hash_table_foreach(db->network2provider, free_network_providers, NULL);
g_hash_table_destroy(db->network2provider);
g_free(db);
}
}
+static struct mobile_provider *network_find_provider(
+ const struct mobile_provider_db *db,
+ const gchar *network_id,
+ gboolean primary)
+{
+ GSList *list;
+
+ list = g_hash_table_lookup(db->network2provider, network_id);
+ for( ; list != NULL; list = list->next) {
+ struct mobile_provider *provider = list->data;
+ if (provider->primary == primary)
+ return provider;
+ }
+ return NULL;
+}
+
struct mobile_provider *mobile_provider_lookup_by_network(
const struct mobile_provider_db *db,
const gchar *network_id)
{
char netid[NORMAL_NETWORK_ID_LEN+1];
struct mobile_provider *provider;
+ int primary;
- if (db != NULL && network_id != NULL) {
+ if (db == NULL || network_id == NULL)
+ return NULL;
+
+ /*
+ * First try to find a match of a primary provider, if that
+ * fails, return the first matching non-primary provider.
+ */
+ for(primary = 1; primary >= 0; primary--) {
+
/*
* Try the lookup of the MCC/MNC pair with 6 digits
* and then 5 digits, to account for the vagaries of
@@ -456,11 +503,13 @@
* difficult to determine.
*/
normalize(network_id, netid, sizeof(netid));
- provider = g_hash_table_lookup(db->network2provider, netid);
+ provider = network_find_provider(db, netid, primary);
if (provider != NULL)
return provider;
normalize(network_id, netid, sizeof(netid) - 1);
- return g_hash_table_lookup(db->network2provider, netid);
+ provider = network_find_provider(db, netid, primary);
+ if (provider != NULL)
+ return provider;
}
return NULL;
}
diff --git a/plugins/mobile_provider.h b/plugins/mobile_provider.h
index ccb10c5..ad26774 100644
--- a/plugins/mobile_provider.h
+++ b/plugins/mobile_provider.h
@@ -55,6 +55,7 @@
int num_apns;
struct mobile_apn **apns;
int refcnt;
+ gboolean primary;
};
struct mobile_provider_db;
diff --git a/scripts/convert-serviceproviders.xsl b/scripts/convert-serviceproviders.xsl
index b2efa5d..0364ca6 100644
--- a/scripts/convert-serviceproviders.xsl
+++ b/scripts/convert-serviceproviders.xsl
@@ -27,7 +27,10 @@
<xsl:template match="provider">
<xsl:if test="count(gsm[*])!=0">
-provider:<xsl:value-of select="count(name)"/>,<xsl:value-of select="count(gsm/apn)"/><xsl:apply-templates select="name"/><xsl:apply-templates select="gsm"/>
+provider:<xsl:value-of select="count(name)"/>,<xsl:value-of select="count(gsm/apn)"/>,<xsl:choose>
+ <xsl:when test="@primary='true'">1</xsl:when><xsl:otherwise>0</xsl:otherwise>
+</xsl:choose>
+<xsl:apply-templates select="name"/><xsl:apply-templates select="gsm"/>
</xsl:if>
</xsl:template>
@@ -68,7 +71,7 @@
#
# Each provider block is of the form
#
-# provider:<# of names>,<# of APNs>
+# provider:<# of names>,<# of APNs>,<primary>
# name:<lang>,<name 1>
# name:<lang>,<name 2>
# . . .
diff --git a/tools/providerdb_test.c b/tools/providerdb_test.c
index 2ff872f..0dc87f4 100644
--- a/tools/providerdb_test.c
+++ b/tools/providerdb_test.c
@@ -38,14 +38,14 @@
static char *test_data =
"serviceproviders:2.0\n"
"country:at\n"
-"provider:1,2\n"
+"provider:1,2,0\n"
"name:,A1/Telekom Austria\n"
"networks:23201\n"
"apn:1,a1.net,ppp@a1plus.at,ppp\n"
"name:,A1 Breitband\n"
"apn:1,aon.data,,ppp\n"
"name:,aon (Flex, Breitband-Duo, BusinessFlex)\n"
-"provider:1,3\n"
+"provider:1,3,0\n"
"name:,T-Mobile\n"
"networks:23203\n"
"apn:1,gprswap,t-mobile,tm\n"
@@ -55,27 +55,31 @@
"apn:1,business.gprsinternet,t-mobile,tm\n"
"name:,Business Internet\n"
"country:ca\n"
-"provider:1,1\n"
+"provider:1,1,0\n"
"name:,Vidéotron\n"
"networks:302500,302510\n"
"apn:1,ihvm.videotron,,\n"
"name:,IHVM\n"
"country:gb\n"
-"provider:1,1\n"
+"provider:1,1,0\n"
"name:,T-Mobile (Great Britain)\n"
"networks:23430\n"
"apn:0,general.t-mobile.uk,User,mms\n"
+"provider:1,1,0\n"
+"name:,3\n"
+"networks:23499\n"
+"apn:0,three.uk,,\n"
"country:ir\n"
-"provider:1,1\n"
+"provider:1,1,0\n"
"name:,همراه اول\n"
"networks:43211\n"
"apn:0,mcinet,,\n"
-"provider:1,1\n"
+"provider:1,1,0\n"
"name:,ایرانسل\n"
"networks:43235\n"
"apn:0,mtnirancell,,\n"
"country:py\n"
-"provider:1,2\n"
+"provider:1,2,0\n"
"name:,Tigo\n"
"networks:74404\n"
"apn:1,internet.tigo.py,,\n"
@@ -84,13 +88,17 @@
"name:,Broadband\n"
"name:es,Banda Ancha Móvil\n"
"country:ru\n"
-"provider:2,1\n"
+"provider:2,1,0\n"
"name:,BaikalWestCom\n"
"name:ru,БайкалВестКом\n"
"networks:25012\n"
"apn:0,inet.bwc.ru,bwc,bwc\n"
"country:us\n"
-"provider:1,4\n"
+"provider:1,1,0\n"
+"name:,3\n"
+"networks:31099\n"
+"apn:0,three.com,,\n"
+"provider:1,4,0\n"
"name:,AT&T\n"
"networks:310038,310090,310150,310410,310560,310680\n"
"apn:0,Broadband,,\n"
@@ -100,7 +108,7 @@
"name:,Data Connect\n"
"apn:1,ISP.CINGULAR,ISPDA@CINGULARGPRS.COM,CINGULAR1\n"
"name:,Data Connect (Accelerated)\n"
-"provider:1,4\n"
+"provider:1,4,1\n"
"name:,T-Mobile\n"
"networks:310160,310200,310210,310220,310230,310240,310250,310260,310270,310310,310490,31058,310660,310800\n"
"apn:1,epc.tmobile.com,,\n"
@@ -111,6 +119,11 @@
"name:,Internet (old)\n"
"apn:1,internet3.voicestream.com,,\n"
"name:,Internet with VPN (old)\n"
+"provider:1,1,0\n"
+"name:,T-Mobile MVNO\n"
+"networks:310160,310200,310210,310220,310230,310240,310250,310260,310270,310310,310490,31058,310660,310800\n"
+"apn:1,mvno.tmobile.com,,\n"
+"name:,MVNO Internet/WebConnect\n"
;
void setup_data(const char *file)
@@ -154,12 +167,30 @@
g_assert_cmpstr(provider->apns[3]->password, ==, "CINGULAR1");
}
+void test_lookup_by_mvno(TestFixture *fixture, gconstpointer test_data)
+{
+ struct mobile_provider *provider;
+
+ provider = mobile_provider_lookup_by_name(fixture->provider_db, "T-Mobile MVNO");
+ g_assert(provider != NULL);
+ g_assert_cmpint(provider->primary, ==, 0);
+ g_assert_cmpint(provider->num_names, ==, 1);
+ g_assert_cmpstr(provider->names[0]->name, ==, "T-Mobile MVNO");
+ g_assert_cmpstr(provider->country, ==, "us");
+ g_assert_cmpint(provider->num_apns, ==, 1);
+ g_assert_cmpint(g_strv_length(provider->networks), ==, 14);
+ g_assert_cmpstr(provider->apns[0]->value, ==, "mvno.tmobile.com");
+ g_assert(provider->apns[0]->username == NULL);
+ g_assert(provider->apns[0]->password == NULL);
+}
+
void test_lookup_by_network(TestFixture *fixture, gconstpointer test_data)
{
struct mobile_provider *provider;
provider = mobile_provider_lookup_by_network(fixture->provider_db, "31026");
g_assert(provider != NULL);
+ g_assert_cmpint(provider->primary, ==, 1);
g_assert_cmpint(provider->num_names, ==, 1);
g_assert_cmpstr(provider->names[0]->name, ==, "T-Mobile");
g_assert_cmpstr(provider->country, ==, "us");
@@ -304,6 +335,29 @@
g_assert(provider == NULL);
}
+void test_lookup_best_match(TestFixture *fixture, gconstpointer test_data)
+{
+ struct mobile_provider *provider;
+
+ /* Make sure we find the one in the US */
+ provider = mobile_provider_lookup_best_match(fixture->provider_db, "3",
+ "31099");
+ g_assert_cmpint(provider->num_apns, ==, 1);
+ g_assert_cmpstr(provider->apns[0]->value, ==, "three.com");
+
+ /* Make sure we find the one in the UK */
+ provider = mobile_provider_lookup_best_match(fixture->provider_db, "3",
+ "23499");
+ g_assert_cmpint(provider->num_apns, ==, 1);
+ g_assert_cmpstr(provider->apns[0]->value, ==, "three.uk");
+
+ /* Make sure we find the one in the UK even with a different MNC */
+ provider = mobile_provider_lookup_best_match(fixture->provider_db, "3",
+ "234878");
+ g_assert_cmpint(provider->num_apns, ==, 1);
+ g_assert_cmpstr(provider->apns[0]->value, ==, "three.uk");
+}
+
int main(int argc, char *argv[])
{
int result;
@@ -316,6 +370,7 @@
ADD_TEST(lookup_by_name);
ADD_TEST(lookup_by_network);
+ ADD_TEST(lookup_by_mvno);
ADD_TEST(other_network_id);
ADD_TEST(normalize_network_id);
ADD_TEST(multi_country);
@@ -325,6 +380,7 @@
ADD_TEST(name_with_commas);
ADD_TEST(bad_provider_name);
ADD_TEST(bad_network_id);
+ ADD_TEST(lookup_best_match);
result = g_test_run();