Fix double initialisation of createIsoLanguageReplacementMap

In the absence of any lock `createIsoLanguageReplacementMap`
method was getting called twice due to race condition.
Used Suppliers.memoize() which is by default thread safe.

PiperOrigin-RevId: 524007754
This commit is contained in:
sheenachhabra 2023-04-13 16:28:04 +01:00 committed by Rohit Singh
parent 165f4f2fd4
commit a358ccb046

View File

@ -85,6 +85,9 @@ import androidx.media3.common.Player;
import androidx.media3.common.Player.Commands; import androidx.media3.common.Player.Commands;
import com.google.common.base.Ascii; import com.google.common.base.Ascii;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@ -108,6 +111,7 @@ import java.util.GregorianCalendar;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException; import java.util.MissingResourceException;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.TimeZone; import java.util.TimeZone;
@ -180,7 +184,8 @@ public final class Util {
private static final String ISM_DASH_FORMAT_EXTENSION = "format=mpd-time-csf"; private static final String ISM_DASH_FORMAT_EXTENSION = "format=mpd-time-csf";
// Replacement map of ISO language codes used for normalization. // Replacement map of ISO language codes used for normalization.
@Nullable private static HashMap<String, String> languageTagReplacementMap; private static final Supplier<ImmutableMap<String, String>> LANGUAGE_TAG_REPLACEMENT_MAP =
Suppliers.memoize(Util::createIsoLanguageReplacementMap);
private Util() {} private Util() {}
@ -818,10 +823,8 @@ public final class Util {
} }
normalizedTag = Ascii.toLowerCase(normalizedTag); normalizedTag = Ascii.toLowerCase(normalizedTag);
String mainLanguage = splitAtFirst(normalizedTag, "-")[0]; String mainLanguage = splitAtFirst(normalizedTag, "-")[0];
if (languageTagReplacementMap == null) {
languageTagReplacementMap = createIsoLanguageReplacementMap(); @Nullable String replacedLanguage = LANGUAGE_TAG_REPLACEMENT_MAP.get().get(mainLanguage);
}
@Nullable String replacedLanguage = languageTagReplacementMap.get(mainLanguage);
if (replacedLanguage != null) { if (replacedLanguage != null) {
normalizedTag = normalizedTag =
replacedLanguage + normalizedTag.substring(/* beginIndex= */ mainLanguage.length()); replacedLanguage + normalizedTag.substring(/* beginIndex= */ mainLanguage.length());
@ -2979,11 +2982,12 @@ public final class Util {
return locale.toLanguageTag(); return locale.toLanguageTag();
} }
private static HashMap<String, String> createIsoLanguageReplacementMap() { private static ImmutableMap<String, String> createIsoLanguageReplacementMap() {
String[] iso2Languages = Locale.getISOLanguages(); String[] iso2Languages = Locale.getISOLanguages();
HashMap<String, String> replacedLanguages = HashMap<String, String> replacedLanguages =
new HashMap<>( new HashMap<>(
/* initialCapacity= */ iso2Languages.length + additionalIsoLanguageReplacements.length); /* initialCapacity= */ iso2Languages.length
+ ADDITIONAL_ISO_LANGUAGE_REPLACEMENTS.size());
for (String iso2 : iso2Languages) { for (String iso2 : iso2Languages) {
try { try {
// This returns the ISO 639-2/T code for the language. // This returns the ISO 639-2/T code for the language.
@ -2996,11 +3000,9 @@ public final class Util {
} }
} }
// Add additional replacement mappings. // Add additional replacement mappings.
for (int i = 0; i < additionalIsoLanguageReplacements.length; i += 2) { replacedLanguages.putAll(ADDITIONAL_ISO_LANGUAGE_REPLACEMENTS);
replacedLanguages.put(
additionalIsoLanguageReplacements[i], additionalIsoLanguageReplacements[i + 1]); return ImmutableMap.copyOf(replacedLanguages);
}
return replacedLanguages;
} }
@RequiresApi(api = Build.VERSION_CODES.M) @RequiresApi(api = Build.VERSION_CODES.M)
@ -3022,84 +3024,84 @@ public final class Util {
} }
private static String maybeReplaceLegacyLanguageTags(String languageTag) { private static String maybeReplaceLegacyLanguageTags(String languageTag) {
for (int i = 0; i < isoLegacyTagReplacements.length; i += 2) { for (Map.Entry<String, String> legacyTagToModernTag : ISO_LEGACY_TAG_REPLACEMENTS.entrySet()) {
if (languageTag.startsWith(isoLegacyTagReplacements[i])) { if (languageTag.startsWith(legacyTagToModernTag.getKey())) {
return isoLegacyTagReplacements[i + 1] return legacyTagToModernTag.getValue()
+ languageTag.substring(/* beginIndex= */ isoLegacyTagReplacements[i].length()); + languageTag.substring(/* beginIndex= */ legacyTagToModernTag.getKey().length());
} }
} }
return languageTag; return languageTag;
} }
// Additional mapping from ISO3 to ISO2 language codes. // Additional mapping from ISO3 to ISO2 language codes.
private static final String[] additionalIsoLanguageReplacements = private static final ImmutableMap<String, String> ADDITIONAL_ISO_LANGUAGE_REPLACEMENTS =
new String[] { new ImmutableMap.Builder<String, String>()
// Bibliographical codes defined in ISO 639-2/B, replaced by terminological code defined in // Bibliographical codes defined in ISO 639-2/B, replaced by terminological code defined
// ISO 639-2/T. See https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes. // in ISO 639-2/T. See https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes.
"alb", "sq", .put("alb", "sq")
"arm", "hy", .put("arm", "hy")
"baq", "eu", .put("baq", "eu")
"bur", "my", .put("bur", "my")
"tib", "bo", .put("tib", "bo")
"chi", "zh", .put("chi", "zh")
"cze", "cs", .put("cze", "cs")
"dut", "nl", .put("dut", "nl")
"ger", "de", .put("ger", "de")
"gre", "el", .put("gre", "el")
"fre", "fr", .put("fre", "fr")
"geo", "ka", .put("geo", "ka")
"ice", "is", .put("ice", "is")
"mac", "mk", .put("mac", "mk")
"mao", "mi", .put("mao", "mi")
"may", "ms", .put("may", "ms")
"per", "fa", .put("per", "fa")
"rum", "ro", .put("rum", "ro")
"scc", "hbs-srp", .put("scc", "hbs-srp")
"slo", "sk", .put("slo", "sk")
"wel", "cy", .put("wel", "cy")
// Deprecated 2-letter codes, replaced by modern equivalent (including macrolanguage) // Deprecated 2-letter codes, replaced by modern equivalent (including macrolanguage)
// See https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes, "ISO 639:1988" // See https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes, "ISO 639:1988"
"id", "ms-ind", .put("id", "ms-ind")
"iw", "he", .put("iw", "he")
"heb", "he", .put("heb", "he")
"ji", "yi", .put("ji", "yi")
// Individual macrolanguage codes mapped back to full macrolanguage code. // Individual macrolanguage codes mapped back to full macrolanguage code.
// See https://en.wikipedia.org/wiki/ISO_639_macrolanguage // See https://en.wikipedia.org/wiki/ISO_639_macrolanguage
"arb", "ar-arb", .put("arb", "ar-arb")
"in", "ms-ind", .put("in", "ms-ind")
"ind", "ms-ind", .put("ind", "ms-ind")
"nb", "no-nob", .put("nb", "no-nob")
"nob", "no-nob", .put("nob", "no-nob")
"nn", "no-nno", .put("nn", "no-nno")
"nno", "no-nno", .put("nno", "no-nno")
"tw", "ak-twi", .put("tw", "ak-twi")
"twi", "ak-twi", .put("twi", "ak-twi")
"bs", "hbs-bos", .put("bs", "hbs-bos")
"bos", "hbs-bos", .put("bos", "hbs-bos")
"hr", "hbs-hrv", .put("hr", "hbs-hrv")
"hrv", "hbs-hrv", .put("hrv", "hbs-hrv")
"sr", "hbs-srp", .put("sr", "hbs-srp")
"srp", "hbs-srp", .put("srp", "hbs-srp")
"cmn", "zh-cmn", .put("cmn", "zh-cmn")
"hak", "zh-hak", .put("hak", "zh-hak")
"nan", "zh-nan", .put("nan", "zh-nan")
"hsn", "zh-hsn" .put("hsn", "zh-hsn")
}; .buildOrThrow();
// Legacy tags that have been replaced by modern equivalents (including macrolanguage) // Legacy tags that have been replaced by modern equivalents (including macrolanguage)
// See https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry. // See https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry.
private static final String[] isoLegacyTagReplacements = private static final ImmutableMap<String, String> ISO_LEGACY_TAG_REPLACEMENTS =
new String[] { ImmutableMap.of(
"i-lux", "lb", "i-lux", "lb",
"i-hak", "zh-hak", "i-hak", "zh-hak",
"i-navajo", "nv", "i-navajo", "nv",
"no-bok", "no-nob", "no-bok", "no-nob",
"no-nyn", "no-nno", "no-nyn", "no-nno",
"zh-guoyu", "zh-cmn", "zh-guoyu", "zh-cmn",
"zh-hakka", "zh-hak", "zh-hakka", "zh-hak",
"zh-min-nan", "zh-nan", "zh-min-nan", "zh-nan",
"zh-xiang", "zh-hsn" "zh-xiang", "zh-hsn");
};
/** /**
* Allows the CRC-32 calculation to be done byte by byte instead of bit per bit in the order "most * Allows the CRC-32 calculation to be done byte by byte instead of bit per bit in the order "most