Treat Latin American locales specially

Due to legacy reasons, Android translations of European Spanish were
kept under 'es', while Latin American Spanish translations were kept
under 'es-US'. The combination of this, and the new locale
preference rules in Nougat, resulted in 'es' winning over 'es-US' for
all Latin American locales, since 'es' was a direct ancestor, while
'es-US' was just a fallback.

The changes in Nougat had assumed that app developers would put Latin
American Spanish translations under 'es-419', but that could create a
backward-compatibility problem under older Android versions that did
not support three-digit region codes properly.

This CL keeps the Nougat logic and its locale parent tree, but
special-cases es-US and es-MX to be treated as equivalents of es-419
in cases where they are present and es-419 is not.

Bug: 31545805
Bug: 34126460
Test: unit tests are included
Change-Id: Iab26f41294587ee044685a5a6560520c7cbb06f7
diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 6e99815..86a627e 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -516,15 +516,80 @@
     EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
 
     fillIn("es", "AR", NULL, NULL, &request);
+    fillIn("es", "US", NULL, NULL, &config1);
+    fillIn("es", NULL, NULL, NULL, &config2);
+    // Special case for Latin American Spanish: es-MX and es-US are
+    // pseudo-parents of all Latin Ameircan Spanish locales.
+    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+    fillIn("es", "MX", NULL, NULL, &request);
+    fillIn("es", "US", NULL, NULL, &config1);
+    fillIn("es", NULL, NULL, NULL, &config2);
+    // Special case for Latin American Spanish: es-MX and es-US are
+    // pseudo-parents of all Latin Ameircan Spanish locales.
+    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+    fillIn("es", "AR", NULL, NULL, &request);
+    fillIn("es", "MX", NULL, NULL, &config1);
+    fillIn("es", NULL, NULL, NULL, &config2);
+    // Special case for Latin American Spanish: es-MX and es-US are
+    // pseudo-parents of all Latin Ameircan Spanish locales.
+    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+    fillIn("es", "US", NULL, NULL, &request);
+    fillIn("es", "MX", NULL, NULL, &config1);
+    fillIn("es", NULL, NULL, NULL, &config2);
+    // Special case for Latin American Spanish: es-MX and es-US are
+    // pseudo-parents of all Latin Ameircan Spanish locales.
+    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+    fillIn("es", "AR", NULL, NULL, &request);
+    fillIn("es", "419", NULL, NULL, &config1);
+    fillIn("es", "MX", NULL, NULL, &config2);
+    // Even though es-MX and es-US are pseudo-parents of all Latin Ameircan
+    // Spanish locales, es-419 is a closer parent.
+    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+    fillIn("es", "US", NULL, NULL, &request);
+    fillIn("es", "419", NULL, NULL, &config1);
+    fillIn("es", "MX", NULL, NULL, &config2);
+    // Even though es-MX and es-US are pseudo-parents of all Latin Ameircan
+    // Spanish locales, es-419 is a closer parent.
+    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+    fillIn("es", "MX", NULL, NULL, &request);
+    fillIn("es", "419", NULL, NULL, &config1);
+    fillIn("es", "US", NULL, NULL, &config2);
+    // Even though es-MX and es-US are pseudo-parents of all Latin Ameircan
+    // Spanish locales, es-419 is a closer parent.
+    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+    fillIn("es", "AR", NULL, NULL, &request);
     fillIn("es", "MX", NULL, NULL, &config1);
     fillIn("es", "BO", NULL, NULL, &config2);
-    // A representative locale is better if they are equidistant.
+    // Special case for Latin American Spanish: es-MX and es-US are
+    // pseudo-parents of all Latin Ameircan Spanish locales.
     EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
     EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
 
     fillIn("es", "AR", NULL, NULL, &request);
     fillIn("es", "US", NULL, NULL, &config1);
     fillIn("es", "BO", NULL, NULL, &config2);
+    // Special case for Latin American Spanish: es-MX and es-US are
+    // pseudo-parents of all Latin Ameircan Spanish locales.
+    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+    fillIn("es", "IC", NULL, NULL, &request);
+    fillIn("es", "ES", NULL, NULL, &config1);
+    fillIn("es", "GQ", NULL, NULL, &config2);
     // A representative locale is better if they are equidistant.
     EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
     EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));