Loading src/java/com/android/internal/telephony/MccTable.java +51 −25 Original line number Diff line number Diff line Loading @@ -28,6 +28,12 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Slog; import com.android.internal.app.LocaleStore; import com.android.internal.app.LocaleStore.LocaleInfo; import libcore.icu.ICU; import libcore.icu.TimeZoneNames; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; Loading @@ -36,9 +42,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import libcore.icu.ICU; import libcore.icu.TimeZoneNames; /** * Mobile Country Code * Loading Loading @@ -235,21 +238,21 @@ public final class MccTable { static { // If we have English (without a country) explicitly prioritize en_US. http://b/28998094 FALLBACKS.put(Locale.ENGLISH, Locale.US); FALLBACKS.put(Locale.CANADA, Locale.US); } /** * Find the best match we actually have a localization for. This function assumes we * couldn't find an exact match. * Finds a suitable locale among {@code candidates} to use as the fallback locale for * {@code target}. This looks through the list of {@link #FALLBACKS}, and follows the chain * until a locale in {@code candidates} is found. * This function assumes that {@code target} is not in {@code candidates}. * * TODO: This should really follow the CLDR chain of parent locales! That might be a bit * of a problem because we don't really have an en-001 locale on android. * * @return The fallback locale or {@code null} if there is no suitable fallback defined in the * lookup. */ private static Locale chooseBestFallback(Locale target, List<Locale> candidates) { if (candidates.isEmpty()) { return null; } private static Locale lookupFallback(Locale target, List<Locale> candidates) { Locale fallback = target; while ((fallback = FALLBACKS.get(fallback)) != null) { if (candidates.contains(fallback)) { Loading @@ -257,11 +260,7 @@ public final class MccTable { } } // Somewhat arbitrarily take the first locale for the language, // unless we get a perfect match later. Note that these come back in no // particular order, so there's no reason to think the first match is // a particularly good match. return candidates.get(0); return null; } /** Loading Loading @@ -314,14 +313,40 @@ public final class MccTable { } } Locale bestMatch = chooseBestFallback(target, languageMatches); if (languageMatches.isEmpty()) { Slog.d(LOG_TAG, "getLocaleForLanguageCountry: no locales for language " + language); return null; } Locale bestMatch = lookupFallback(target, languageMatches); if (bestMatch != null) { Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got a language-only match: " + bestMatch.toLanguageTag()); Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got a fallback match: " + bestMatch.toLanguageTag()); return bestMatch; } else { Slog.d(LOG_TAG, "getLocaleForLanguageCountry: no locales for language " + language); // Ask {@link LocaleStore} whether this locale is considered "translated". // LocaleStore has a broader definition of translated than just the asset locales // above: a locale is "translated" if it has translation assets, or another locale // with the same language and script has translation assets. // If a locale is "translated", it is selectable in setup wizard, and can therefore // be considerd a valid result for this method. if (!TextUtils.isEmpty(target.getCountry())) { LocaleStore.fillCache(context); LocaleInfo targetInfo = LocaleStore.getLocaleInfo(target); if (targetInfo.isTranslated()) { Slog.d(LOG_TAG, "getLocaleForLanguageCountry: " + "target locale is translated: " + target); return target; } } // Somewhat arbitrarily take the first locale for the language, // unless we get a perfect match later. Note that these come back in no // particular order, so there's no reason to think the first match is // a particularly good match. Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got language-only match: " + language); return languageMatches.get(0); } } catch (Exception e) { Slog.d(LOG_TAG, "getLocaleForLanguageCountry: exception", e); Loading Loading @@ -359,7 +384,8 @@ public final class MccTable { * @return locale for the mcc or null if none */ public static Locale getLocaleFromMcc(Context context, int mcc, String simLanguage) { String language = (simLanguage == null) ? MccTable.defaultLanguageForMcc(mcc) : simLanguage; boolean hasSimLanguage = !TextUtils.isEmpty(simLanguage); String language = hasSimLanguage ? simLanguage : MccTable.defaultLanguageForMcc(mcc); String country = MccTable.countryCodeForMcc(mcc); Slog.d(LOG_TAG, "getLocaleFromMcc(" + language + ", " + country + ", " + mcc); Loading @@ -367,10 +393,10 @@ public final class MccTable { // If we couldn't find a locale that matches the SIM language, give it a go again // with the "likely" language for the given country. if (locale == null && simLanguage != null) { if (locale == null && hasSimLanguage) { language = MccTable.defaultLanguageForMcc(mcc); Slog.d(LOG_TAG, "[retry ] getLocaleFromMcc(" + language + ", " + country + ", " + mcc); return getLocaleForLanguageCountry(context, null, country); return getLocaleForLanguageCountry(context, language, country); } return locale; Loading tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java +51 −37 Original line number Diff line number Diff line Loading @@ -16,67 +16,81 @@ package com.android.internal.telephony; import com.android.internal.telephony.MccTable; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.telephony.Rlog; import java.util.Locale; public class MccTableTest extends AndroidTestCase { private final static String LOG_TAG = "GSM"; @SmallTest public void testTimeZone() throws Exception { assertEquals(MccTable.defaultTimeZoneForMcc(208), "Europe/Paris"); assertEquals(MccTable.defaultTimeZoneForMcc(232), "Europe/Vienna"); assertEquals(MccTable.defaultTimeZoneForMcc(655), "Africa/Johannesburg"); assertEquals(MccTable.defaultTimeZoneForMcc(440), "Asia/Tokyo"); assertEquals(MccTable.defaultTimeZoneForMcc(441), "Asia/Tokyo"); assertEquals(MccTable.defaultTimeZoneForMcc(525), "Asia/Singapore"); assertEquals(MccTable.defaultTimeZoneForMcc(240), "Europe/Stockholm"); assertEquals(MccTable.defaultTimeZoneForMcc(0), null); // mcc not defined, hence default assertEquals(MccTable.defaultTimeZoneForMcc(2000), null); // mcc not defined, hence default assertEquals("Europe/Paris", MccTable.defaultTimeZoneForMcc(208)); assertEquals("Europe/Vienna", MccTable.defaultTimeZoneForMcc(232)); assertEquals("Africa/Johannesburg", MccTable.defaultTimeZoneForMcc(655)); assertEquals("Asia/Tokyo", MccTable.defaultTimeZoneForMcc(440)); assertEquals("Asia/Tokyo", MccTable.defaultTimeZoneForMcc(441)); assertEquals("Asia/Singapore", MccTable.defaultTimeZoneForMcc(525)); assertEquals("Europe/Stockholm", MccTable.defaultTimeZoneForMcc(240)); assertEquals(null, MccTable.defaultTimeZoneForMcc(0)); // mcc not defined, hence default assertEquals(null, MccTable.defaultTimeZoneForMcc(2000)); // mcc not defined, hence default } @SmallTest public void testCountryCode() throws Exception { assertEquals(MccTable.countryCodeForMcc(270), "lu"); assertEquals(MccTable.countryCodeForMcc(202), "gr"); assertEquals(MccTable.countryCodeForMcc(750), "fk"); assertEquals(MccTable.countryCodeForMcc(646), "mg"); assertEquals(MccTable.countryCodeForMcc(314), "us"); assertEquals(MccTable.countryCodeForMcc(300), ""); // mcc not defined, hence default assertEquals(MccTable.countryCodeForMcc(0), ""); // mcc not defined, hence default assertEquals(MccTable.countryCodeForMcc(2000), ""); // mcc not defined, hence default assertEquals("lu", MccTable.countryCodeForMcc(270)); assertEquals("gr", MccTable.countryCodeForMcc(202)); assertEquals("fk", MccTable.countryCodeForMcc(750)); assertEquals("mg", MccTable.countryCodeForMcc(646)); assertEquals("us", MccTable.countryCodeForMcc(314)); assertEquals("", MccTable.countryCodeForMcc(300)); // mcc not defined, hence default assertEquals("", MccTable.countryCodeForMcc(0)); // mcc not defined, hence default assertEquals("", MccTable.countryCodeForMcc(2000)); // mcc not defined, hence default } @SmallTest public void testLang() throws Exception { assertEquals(MccTable.defaultLanguageForMcc(311), "en"); assertEquals(MccTable.defaultLanguageForMcc(232), "de"); assertEquals(MccTable.defaultLanguageForMcc(230), "cs"); assertEquals(MccTable.defaultLanguageForMcc(204), "nl"); assertEquals(MccTable.defaultLanguageForMcc(274), "is"); assertEquals(MccTable.defaultLanguageForMcc(0), null); // mcc not defined, hence default assertEquals(MccTable.defaultLanguageForMcc(2000), null); // mcc not defined, hence default assertEquals("en", MccTable.defaultLanguageForMcc(311)); assertEquals("de", MccTable.defaultLanguageForMcc(232)); assertEquals("cs", MccTable.defaultLanguageForMcc(230)); assertEquals("nl", MccTable.defaultLanguageForMcc(204)); assertEquals("is", MccTable.defaultLanguageForMcc(274)); assertEquals(null, MccTable.defaultLanguageForMcc(0)); // mcc not defined, hence default assertEquals(null, MccTable.defaultLanguageForMcc(2000)); // mcc not defined, hence default } @SmallTest public void testLang_India() throws Exception { assertEquals(MccTable.defaultLanguageForMcc(404), "en"); assertEquals(MccTable.defaultLanguageForMcc(405), "en"); assertEquals(MccTable.defaultLanguageForMcc(406), "en"); assertEquals("en", MccTable.defaultLanguageForMcc(404)); assertEquals("en", MccTable.defaultLanguageForMcc(405)); assertEquals("en", MccTable.defaultLanguageForMcc(406)); } @SmallTest public void testLocale() throws Exception { assertEquals(Locale.forLanguageTag("en-CA"), MccTable.getLocaleFromMcc(getContext(), 302, null)); assertEquals(Locale.forLanguageTag("en-GB"), MccTable.getLocaleFromMcc(getContext(), 234, null)); assertEquals(Locale.forLanguageTag("en-US"), MccTable.getLocaleFromMcc(getContext(), 0, "en")); assertEquals(Locale.forLanguageTag("zh-HK"), MccTable.getLocaleFromMcc(getContext(), 454, null)); assertEquals(Locale.forLanguageTag("en-HK"), MccTable.getLocaleFromMcc(getContext(), 454, "en")); assertEquals(Locale.forLanguageTag("zh-TW"), MccTable.getLocaleFromMcc(getContext(), 466, null)); } @SmallTest public void testSmDigits() throws Exception { assertEquals(MccTable.smallestDigitsMccForMnc(312), 3); assertEquals(MccTable.smallestDigitsMccForMnc(430), 2); assertEquals(MccTable.smallestDigitsMccForMnc(365), 3); assertEquals(MccTable.smallestDigitsMccForMnc(536), 2); assertEquals(MccTable.smallestDigitsMccForMnc(352), 2); // sd not defined, hence default assertEquals(MccTable.smallestDigitsMccForMnc(0), 2); // mcc not defined, hence default assertEquals(MccTable.smallestDigitsMccForMnc(2000), 2); // mcc not defined, hence default assertEquals(3, MccTable.smallestDigitsMccForMnc(312)); assertEquals(2, MccTable.smallestDigitsMccForMnc(430)); assertEquals(3, MccTable.smallestDigitsMccForMnc(365)); assertEquals(2, MccTable.smallestDigitsMccForMnc(536)); assertEquals(2, MccTable.smallestDigitsMccForMnc(352)); // sd not defined, hence default assertEquals(2, MccTable.smallestDigitsMccForMnc(0)); // mcc not defined, hence default assertEquals(2, MccTable.smallestDigitsMccForMnc(2000)); // mcc not defined, hence default } } Loading
src/java/com/android/internal/telephony/MccTable.java +51 −25 Original line number Diff line number Diff line Loading @@ -28,6 +28,12 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Slog; import com.android.internal.app.LocaleStore; import com.android.internal.app.LocaleStore.LocaleInfo; import libcore.icu.ICU; import libcore.icu.TimeZoneNames; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; Loading @@ -36,9 +42,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import libcore.icu.ICU; import libcore.icu.TimeZoneNames; /** * Mobile Country Code * Loading Loading @@ -235,21 +238,21 @@ public final class MccTable { static { // If we have English (without a country) explicitly prioritize en_US. http://b/28998094 FALLBACKS.put(Locale.ENGLISH, Locale.US); FALLBACKS.put(Locale.CANADA, Locale.US); } /** * Find the best match we actually have a localization for. This function assumes we * couldn't find an exact match. * Finds a suitable locale among {@code candidates} to use as the fallback locale for * {@code target}. This looks through the list of {@link #FALLBACKS}, and follows the chain * until a locale in {@code candidates} is found. * This function assumes that {@code target} is not in {@code candidates}. * * TODO: This should really follow the CLDR chain of parent locales! That might be a bit * of a problem because we don't really have an en-001 locale on android. * * @return The fallback locale or {@code null} if there is no suitable fallback defined in the * lookup. */ private static Locale chooseBestFallback(Locale target, List<Locale> candidates) { if (candidates.isEmpty()) { return null; } private static Locale lookupFallback(Locale target, List<Locale> candidates) { Locale fallback = target; while ((fallback = FALLBACKS.get(fallback)) != null) { if (candidates.contains(fallback)) { Loading @@ -257,11 +260,7 @@ public final class MccTable { } } // Somewhat arbitrarily take the first locale for the language, // unless we get a perfect match later. Note that these come back in no // particular order, so there's no reason to think the first match is // a particularly good match. return candidates.get(0); return null; } /** Loading Loading @@ -314,14 +313,40 @@ public final class MccTable { } } Locale bestMatch = chooseBestFallback(target, languageMatches); if (languageMatches.isEmpty()) { Slog.d(LOG_TAG, "getLocaleForLanguageCountry: no locales for language " + language); return null; } Locale bestMatch = lookupFallback(target, languageMatches); if (bestMatch != null) { Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got a language-only match: " + bestMatch.toLanguageTag()); Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got a fallback match: " + bestMatch.toLanguageTag()); return bestMatch; } else { Slog.d(LOG_TAG, "getLocaleForLanguageCountry: no locales for language " + language); // Ask {@link LocaleStore} whether this locale is considered "translated". // LocaleStore has a broader definition of translated than just the asset locales // above: a locale is "translated" if it has translation assets, or another locale // with the same language and script has translation assets. // If a locale is "translated", it is selectable in setup wizard, and can therefore // be considerd a valid result for this method. if (!TextUtils.isEmpty(target.getCountry())) { LocaleStore.fillCache(context); LocaleInfo targetInfo = LocaleStore.getLocaleInfo(target); if (targetInfo.isTranslated()) { Slog.d(LOG_TAG, "getLocaleForLanguageCountry: " + "target locale is translated: " + target); return target; } } // Somewhat arbitrarily take the first locale for the language, // unless we get a perfect match later. Note that these come back in no // particular order, so there's no reason to think the first match is // a particularly good match. Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got language-only match: " + language); return languageMatches.get(0); } } catch (Exception e) { Slog.d(LOG_TAG, "getLocaleForLanguageCountry: exception", e); Loading Loading @@ -359,7 +384,8 @@ public final class MccTable { * @return locale for the mcc or null if none */ public static Locale getLocaleFromMcc(Context context, int mcc, String simLanguage) { String language = (simLanguage == null) ? MccTable.defaultLanguageForMcc(mcc) : simLanguage; boolean hasSimLanguage = !TextUtils.isEmpty(simLanguage); String language = hasSimLanguage ? simLanguage : MccTable.defaultLanguageForMcc(mcc); String country = MccTable.countryCodeForMcc(mcc); Slog.d(LOG_TAG, "getLocaleFromMcc(" + language + ", " + country + ", " + mcc); Loading @@ -367,10 +393,10 @@ public final class MccTable { // If we couldn't find a locale that matches the SIM language, give it a go again // with the "likely" language for the given country. if (locale == null && simLanguage != null) { if (locale == null && hasSimLanguage) { language = MccTable.defaultLanguageForMcc(mcc); Slog.d(LOG_TAG, "[retry ] getLocaleFromMcc(" + language + ", " + country + ", " + mcc); return getLocaleForLanguageCountry(context, null, country); return getLocaleForLanguageCountry(context, language, country); } return locale; Loading
tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java +51 −37 Original line number Diff line number Diff line Loading @@ -16,67 +16,81 @@ package com.android.internal.telephony; import com.android.internal.telephony.MccTable; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.telephony.Rlog; import java.util.Locale; public class MccTableTest extends AndroidTestCase { private final static String LOG_TAG = "GSM"; @SmallTest public void testTimeZone() throws Exception { assertEquals(MccTable.defaultTimeZoneForMcc(208), "Europe/Paris"); assertEquals(MccTable.defaultTimeZoneForMcc(232), "Europe/Vienna"); assertEquals(MccTable.defaultTimeZoneForMcc(655), "Africa/Johannesburg"); assertEquals(MccTable.defaultTimeZoneForMcc(440), "Asia/Tokyo"); assertEquals(MccTable.defaultTimeZoneForMcc(441), "Asia/Tokyo"); assertEquals(MccTable.defaultTimeZoneForMcc(525), "Asia/Singapore"); assertEquals(MccTable.defaultTimeZoneForMcc(240), "Europe/Stockholm"); assertEquals(MccTable.defaultTimeZoneForMcc(0), null); // mcc not defined, hence default assertEquals(MccTable.defaultTimeZoneForMcc(2000), null); // mcc not defined, hence default assertEquals("Europe/Paris", MccTable.defaultTimeZoneForMcc(208)); assertEquals("Europe/Vienna", MccTable.defaultTimeZoneForMcc(232)); assertEquals("Africa/Johannesburg", MccTable.defaultTimeZoneForMcc(655)); assertEquals("Asia/Tokyo", MccTable.defaultTimeZoneForMcc(440)); assertEquals("Asia/Tokyo", MccTable.defaultTimeZoneForMcc(441)); assertEquals("Asia/Singapore", MccTable.defaultTimeZoneForMcc(525)); assertEquals("Europe/Stockholm", MccTable.defaultTimeZoneForMcc(240)); assertEquals(null, MccTable.defaultTimeZoneForMcc(0)); // mcc not defined, hence default assertEquals(null, MccTable.defaultTimeZoneForMcc(2000)); // mcc not defined, hence default } @SmallTest public void testCountryCode() throws Exception { assertEquals(MccTable.countryCodeForMcc(270), "lu"); assertEquals(MccTable.countryCodeForMcc(202), "gr"); assertEquals(MccTable.countryCodeForMcc(750), "fk"); assertEquals(MccTable.countryCodeForMcc(646), "mg"); assertEquals(MccTable.countryCodeForMcc(314), "us"); assertEquals(MccTable.countryCodeForMcc(300), ""); // mcc not defined, hence default assertEquals(MccTable.countryCodeForMcc(0), ""); // mcc not defined, hence default assertEquals(MccTable.countryCodeForMcc(2000), ""); // mcc not defined, hence default assertEquals("lu", MccTable.countryCodeForMcc(270)); assertEquals("gr", MccTable.countryCodeForMcc(202)); assertEquals("fk", MccTable.countryCodeForMcc(750)); assertEquals("mg", MccTable.countryCodeForMcc(646)); assertEquals("us", MccTable.countryCodeForMcc(314)); assertEquals("", MccTable.countryCodeForMcc(300)); // mcc not defined, hence default assertEquals("", MccTable.countryCodeForMcc(0)); // mcc not defined, hence default assertEquals("", MccTable.countryCodeForMcc(2000)); // mcc not defined, hence default } @SmallTest public void testLang() throws Exception { assertEquals(MccTable.defaultLanguageForMcc(311), "en"); assertEquals(MccTable.defaultLanguageForMcc(232), "de"); assertEquals(MccTable.defaultLanguageForMcc(230), "cs"); assertEquals(MccTable.defaultLanguageForMcc(204), "nl"); assertEquals(MccTable.defaultLanguageForMcc(274), "is"); assertEquals(MccTable.defaultLanguageForMcc(0), null); // mcc not defined, hence default assertEquals(MccTable.defaultLanguageForMcc(2000), null); // mcc not defined, hence default assertEquals("en", MccTable.defaultLanguageForMcc(311)); assertEquals("de", MccTable.defaultLanguageForMcc(232)); assertEquals("cs", MccTable.defaultLanguageForMcc(230)); assertEquals("nl", MccTable.defaultLanguageForMcc(204)); assertEquals("is", MccTable.defaultLanguageForMcc(274)); assertEquals(null, MccTable.defaultLanguageForMcc(0)); // mcc not defined, hence default assertEquals(null, MccTable.defaultLanguageForMcc(2000)); // mcc not defined, hence default } @SmallTest public void testLang_India() throws Exception { assertEquals(MccTable.defaultLanguageForMcc(404), "en"); assertEquals(MccTable.defaultLanguageForMcc(405), "en"); assertEquals(MccTable.defaultLanguageForMcc(406), "en"); assertEquals("en", MccTable.defaultLanguageForMcc(404)); assertEquals("en", MccTable.defaultLanguageForMcc(405)); assertEquals("en", MccTable.defaultLanguageForMcc(406)); } @SmallTest public void testLocale() throws Exception { assertEquals(Locale.forLanguageTag("en-CA"), MccTable.getLocaleFromMcc(getContext(), 302, null)); assertEquals(Locale.forLanguageTag("en-GB"), MccTable.getLocaleFromMcc(getContext(), 234, null)); assertEquals(Locale.forLanguageTag("en-US"), MccTable.getLocaleFromMcc(getContext(), 0, "en")); assertEquals(Locale.forLanguageTag("zh-HK"), MccTable.getLocaleFromMcc(getContext(), 454, null)); assertEquals(Locale.forLanguageTag("en-HK"), MccTable.getLocaleFromMcc(getContext(), 454, "en")); assertEquals(Locale.forLanguageTag("zh-TW"), MccTable.getLocaleFromMcc(getContext(), 466, null)); } @SmallTest public void testSmDigits() throws Exception { assertEquals(MccTable.smallestDigitsMccForMnc(312), 3); assertEquals(MccTable.smallestDigitsMccForMnc(430), 2); assertEquals(MccTable.smallestDigitsMccForMnc(365), 3); assertEquals(MccTable.smallestDigitsMccForMnc(536), 2); assertEquals(MccTable.smallestDigitsMccForMnc(352), 2); // sd not defined, hence default assertEquals(MccTable.smallestDigitsMccForMnc(0), 2); // mcc not defined, hence default assertEquals(MccTable.smallestDigitsMccForMnc(2000), 2); // mcc not defined, hence default assertEquals(3, MccTable.smallestDigitsMccForMnc(312)); assertEquals(2, MccTable.smallestDigitsMccForMnc(430)); assertEquals(3, MccTable.smallestDigitsMccForMnc(365)); assertEquals(2, MccTable.smallestDigitsMccForMnc(536)); assertEquals(2, MccTable.smallestDigitsMccForMnc(352)); // sd not defined, hence default assertEquals(2, MccTable.smallestDigitsMccForMnc(0)); // mcc not defined, hence default assertEquals(2, MccTable.smallestDigitsMccForMnc(2000)); // mcc not defined, hence default } }