diff options
author | Almaz Mingaleev <mingaleev@google.com> | 2024-06-04 15:59:41 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2024-06-04 15:59:41 +0000 |
commit | 832d041f4f488388532cc25b81f7fb1046075a80 (patch) | |
tree | 5b8a3e1f7b03e16f024f57172a9cea9f49716dd4 | |
parent | 7427fb0b87fdbaa692799d1e372ae93efe4d0a3b (diff) | |
parent | d76863ead29a6df7a6b0f7fcf50ab4d13f1df30b (diff) | |
download | icu-832d041f4f488388532cc25b81f7fb1046075a80.tar.gz |
Merge "Revert^2 "Read files from versioned tzdata paths."" into main
8 files changed, 117 insertions, 20 deletions
diff --git a/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TimeZoneDataFiles.java b/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TimeZoneDataFiles.java index 6f3b16eac..4bd971a3f 100644 --- a/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TimeZoneDataFiles.java +++ b/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TimeZoneDataFiles.java @@ -48,7 +48,8 @@ public final class TimeZoneDataFiles { } public static String getTimeZoneModuleTzFile(String fileName) { - return getTimeZoneModuleFile("tz/" + fileName); + return getTimeZoneModuleFile("tz/versioned/" + + TzDataSetVersion.currentFormatMajorVersion() + "/" + fileName); } // Remove from CorePlatformApi when all users in platform code are removed. http://b/123398797 diff --git a/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java b/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java index c91745cbd..907e780ae 100644 --- a/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java +++ b/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java @@ -61,7 +61,9 @@ public final class TzDataSetVersion { * version to 1 when doing so. */ // @VisibleForTesting : Keep this inline-able: it is used from CTS tests. + // LINT.IfChange public static final int CURRENT_FORMAT_MAJOR_VERSION = 8; // Android V + // LINT.ThenChange(external/icu/android_icu4j/src/main/java/android/icu/platform/AndroidDataFiles.java) /** * Returns the major tz data format version supported by this device. @@ -86,12 +88,8 @@ public final class TzDataSetVersion { return CURRENT_FORMAT_MINOR_VERSION; } - /** The full major + minor tz data format version for this device. */ - private static final String FULL_CURRENT_FORMAT_VERSION_STRING = - toFormatVersionString(CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION); - - private static final int FORMAT_VERSION_STRING_LENGTH = - FULL_CURRENT_FORMAT_VERSION_STRING.length(); + /** The full major + minor tz data format version's length for this device. */ + private static final int FORMAT_VERSION_STRING_LENGTH = 7; private static final Pattern FORMAT_VERSION_PATTERN = Pattern.compile("(\\d{3})\\.(\\d{3})"); /** A pattern that matches the IANA rules value of a rules update. e.g. "2016g" */ diff --git a/android_icu4j/src/main/java/android/icu/platform/AndroidDataFiles.java b/android_icu4j/src/main/java/android/icu/platform/AndroidDataFiles.java index 74ab82fc3..121197863 100644 --- a/android_icu4j/src/main/java/android/icu/platform/AndroidDataFiles.java +++ b/android_icu4j/src/main/java/android/icu/platform/AndroidDataFiles.java @@ -38,8 +38,19 @@ public class AndroidDataFiles { public static final String ANDROID_I18N_ROOT_ENV = "ANDROID_I18N_ROOT"; public static final String ANDROID_TZDATA_ROOT_ENV = "ANDROID_TZDATA_ROOT"; + /** + * This is identical to + * {@link com.android.i18n.timezone.TzDataSetVersion#CURRENT_FORMAT_MAJOR_VERSION}, but because + * dependency is in the opposite direction we can't refer to that field from this class. + * TzDataSetVersionTest ensures that their values are the same. + */ + // VisibleForTesting + // LINT.IfChange + public static final int CURRENT_MAJOR_VERSION = 8; + // LINT.ThenChange(external/icu/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java) + // VisibleForTesting - public static String getTimeZoneModuleIcuFile(String fileName) { + public static String getTimeZoneModuleIcuFileAtOldLocation(String fileName) { return getTimeZoneModuleFile("icu/" + fileName); } @@ -47,6 +58,10 @@ public class AndroidDataFiles { return System.getenv(ANDROID_TZDATA_ROOT_ENV) + "/etc/" + fileName; } + private static String getTimeZoneModuleIcuFile(String fileName) { + return getTimeZoneModuleFile("tz/versioned/" + CURRENT_MAJOR_VERSION + "/icu/" + fileName); + } + // VisibleForTesting public static String getI18nModuleIcuFile(String fileName) { return getI18nModuleFile("icu/" + fileName); @@ -64,7 +79,13 @@ public class AndroidDataFiles { // ICU should look for a mounted time zone module file in /apex. This is used for // (optional) time zone data that can be updated with an APEX file. - String timeZoneModuleIcuDataPath = getTimeZoneModuleIcuFile(""); + paths.add(getTimeZoneModuleIcuFile("")); + + // TODO (b/319103072): remove this path once prebuilts are updated. + // Starting from V content of the tzdata module is versioned so it can be used across + // multiple Android releases. timeZoneModuleIcuDataPath will be removed once all prebuilts + // are updated. Production tzdata6 won't have ICU files under etc/icu. + String timeZoneModuleIcuDataPath = getTimeZoneModuleIcuFileAtOldLocation(""); paths.add(timeZoneModuleIcuDataPath); // ICU should always look in the i18n module path as this is where most of the data diff --git a/android_icu4j/testing/src/android/icu/extratest/platform/AndroidDataFilesTest.java b/android_icu4j/testing/src/android/icu/extratest/platform/AndroidDataFilesTest.java index 190fb3d1b..20ef055a6 100644 --- a/android_icu4j/testing/src/android/icu/extratest/platform/AndroidDataFilesTest.java +++ b/android_icu4j/testing/src/android/icu/extratest/platform/AndroidDataFilesTest.java @@ -48,11 +48,13 @@ import java.util.stream.Stream; public class AndroidDataFilesTest { private static final Set<String> TZDATA_RES_FILES = - Set.of( - "/apex/com.android.tzdata/etc/icu/metaZones.res", - "/apex/com.android.tzdata/etc/icu/windowsZones.res", - "/apex/com.android.tzdata/etc/icu/zoneinfo64.res", - "/apex/com.android.tzdata/etc/icu/timezoneTypes.res"); + Stream.of( + "/apex/com.android.tzdata/etc/tz/versioned/%d/icu/metaZones.res", + "/apex/com.android.tzdata/etc/tz/versioned/%d/icu/windowsZones.res", + "/apex/com.android.tzdata/etc/tz/versioned/%d/icu/zoneinfo64.res", + "/apex/com.android.tzdata/etc/tz/versioned/%d/icu/timezoneTypes.res") + .map(path -> path.formatted(AndroidDataFiles.CURRENT_MAJOR_VERSION)) + .collect(toSet()); private static final String ICU_DAT_PATH = "/apex/com.android.i18n/etc/icu/icudt" + VersionInfo.ICU_VERSION.getMajor() + "l.dat"; diff --git a/android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneDataFilesTest.java b/android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneDataFilesTest.java index 064c7bbe0..403e90de0 100644 --- a/android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneDataFilesTest.java +++ b/android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneDataFilesTest.java @@ -53,13 +53,19 @@ public class TimeZoneDataFilesTest { String icuDataPath = AndroidDataFiles.generateIcuDataPath(); String[] paths = icuDataPath.split(":"); - assertEquals(2, paths.length); + assertEquals(3, paths.length); - String tzdataModulePath = paths[0]; + String versionedTzdataModulePath = paths[0]; + assertTrue(versionedTzdataModulePath + " invalid", + versionedTzdataModulePath.startsWith(System.getenv(ANDROID_TZDATA_ROOT_ENV))); + assertTrue(versionedTzdataModulePath + " should be versioned", + versionedTzdataModulePath.contains("versioned")); + + String tzdataModulePath = paths[1]; assertTrue(tzdataModulePath + " invalid", tzdataModulePath.startsWith(System.getenv(ANDROID_TZDATA_ROOT_ENV))); - String runtimeModulePath = paths[1]; + String runtimeModulePath = paths[2]; assertTrue(runtimeModulePath + " invalid", runtimeModulePath.startsWith(System.getenv(ANDROID_I18N_ROOT_ENV))); assertTrue(runtimeModulePath + " invalid", runtimeModulePath.contains("/etc/icu")); diff --git a/android_icu4j/testing/src/com/android/i18n/test/timezone/TzDataSetVersionTest.java b/android_icu4j/testing/src/com/android/i18n/test/timezone/TzDataSetVersionTest.java index fdf7294bf..ba0098797 100644 --- a/android_icu4j/testing/src/com/android/i18n/test/timezone/TzDataSetVersionTest.java +++ b/android_icu4j/testing/src/com/android/i18n/test/timezone/TzDataSetVersionTest.java @@ -16,12 +16,22 @@ package com.android.i18n.test.timezone; +import static com.android.i18n.timezone.TzDataSetVersion.CURRENT_FORMAT_MAJOR_VERSION; +import static com.android.i18n.timezone.TzDataSetVersion.CURRENT_FORMAT_MINOR_VERSION; + +import static java.lang.invoke.MethodType.methodType; + +import android.icu.platform.AndroidDataFiles; import android.icu.testsharding.MainTestShard; -import junit.framework.TestCase; import com.android.i18n.timezone.TzDataSetVersion; import com.android.i18n.timezone.TzDataSetVersion.TzDataSetException; +import junit.framework.TestCase; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + @MainTestShard public class TzDataSetVersionTest extends TestCase { @@ -100,6 +110,32 @@ public class TzDataSetVersionTest extends TestCase { assertFalse(TzDataSetVersion.isCompatibleWithThisDevice(olderMinor)); } + public void testConsistency() { + String msg = "Major versions in TzDataSetVersion and AndroidDataFiles differ"; + assertEquals(msg, CURRENT_FORMAT_MAJOR_VERSION, AndroidDataFiles.CURRENT_MAJOR_VERSION); + } + + public void testFormatLength() throws Throwable { + int versionLengthField = + (int) MethodHandles.privateLookupIn(TzDataSetVersion.class, MethodHandles.lookup()) + .findStaticGetter( + TzDataSetVersion.class, + "FORMAT_VERSION_STRING_LENGTH", + int.class) + .invoke(); + + // Using method handles to not increase API surface. + MethodHandle TO_FORMAT_VERSION_MH = + MethodHandles.privateLookupIn(TzDataSetVersion.class, MethodHandles.lookup()) + .findStatic( + TzDataSetVersion.class, + "toFormatVersionString", + methodType(String.class, int.class, int.class)); + String formatVersion = (String) TO_FORMAT_VERSION_MH.invoke(CURRENT_FORMAT_MAJOR_VERSION, + CURRENT_FORMAT_MINOR_VERSION); + assertEquals(versionLengthField, formatVersion.length()); + } + private TzDataSetVersion createTzDataSetVersion(int majorFormatVersion, int minorFormatVersion) throws TzDataSetException { return new TzDataSetVersion(majorFormatVersion, minorFormatVersion, VALID_RULES_VERSION, 3); diff --git a/libandroidicuinit/IcuRegistration.cpp b/libandroidicuinit/IcuRegistration.cpp index 63397fd20..752527418 100644 --- a/libandroidicuinit/IcuRegistration.cpp +++ b/libandroidicuinit/IcuRegistration.cpp @@ -216,8 +216,9 @@ IcuRegistration::IcuRegistration() { // If it does, map it so we use its data in preference to later ones. // However, I18N apex is not expected to have the time zone data resources. // http://b/171542040 - std::string tzModulePath = getTimeZoneModulePath(); + std::string tzModulePath = getPreVTimeZoneModulePath(); std::string tzIcuDataPath = tzModulePath + "icu_tzdata.dat"; + std::string versionedTzIcuDataPath = getTimeZoneModulePath(); if (pathExists(tzIcuDataPath)) { AICU_LOGD("Time zone APEX ICU file found: %s", tzIcuDataPath.c_str()); icu_datamap_from_tz_module_ = impl::IcuDataMap::Create(tzIcuDataPath); @@ -225,6 +226,15 @@ IcuRegistration::IcuRegistration() { AICU_LOGW("TZ module .dat file %s exists but could not be loaded. Skipping.", tzIcuDataPath.c_str()); } + } else if (pathExists(versionedTzIcuDataPath)) { + UErrorCode status = U_ZERO_ERROR; + u_setTimeZoneFilesDirectory(versionedTzIcuDataPath.c_str(), &status); + if (U_SUCCESS(status)) { + AICU_LOGD("u_setTimeZoneFilesDirectory(\"%s\") succeeded. ", versionedTzIcuDataPath.c_str()); + } else { + AICU_LOGE("u_setTimeZoneFilesDirectory(\"%s\") failed: %s", + versionedTzIcuDataPath.c_str(), u_errorName(status)); + } } else { UErrorCode status = U_ZERO_ERROR; u_setTimeZoneFilesDirectory(tzModulePath.c_str(), &status); @@ -261,7 +271,7 @@ bool IcuRegistration::pathExists(const std::string& path) { // Returns a string containing the expected path of the /apex tz // module ICU data directory -std::string IcuRegistration::getTimeZoneModulePath() { +std::string IcuRegistration::getPreVTimeZoneModulePath() { const char* tzdataModulePathPrefix = getenv("ANDROID_TZDATA_ROOT"); if (tzdataModulePathPrefix == NULL) { AICU_LOGE("ANDROID_TZDATA_ROOT environment variable not set"); @@ -274,6 +284,24 @@ std::string IcuRegistration::getTimeZoneModulePath() { return tzdataModulePath; } +// Identical to TzDataSetVersion#CURRENT_MAJOR_FORMAT_VERSION. +// LINT.IfChange +static const std::string CURRENT_MAJOR_FORMAT_VERSION = "8"; +// LINT.ThenChange(external/icu/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java) + +std::string IcuRegistration::getTimeZoneModulePath() { + const char* tzdataModulePathPrefix = getenv("ANDROID_TZDATA_ROOT"); + if (tzdataModulePathPrefix == NULL) { + AICU_LOGE("ANDROID_TZDATA_ROOT environment variable not set"); + abort(); + } + + std::string tzdataModulePath; + tzdataModulePath = tzdataModulePathPrefix; + tzdataModulePath += "/etc/tz/versioned/" + CURRENT_MAJOR_FORMAT_VERSION + "/icu"; + return tzdataModulePath; +} + std::string IcuRegistration::getI18nModulePath() { const char* i18nModulePathPrefix = getenv("ANDROID_I18N_ROOT"); if (i18nModulePathPrefix == NULL) { diff --git a/libandroidicuinit/IcuRegistration.h b/libandroidicuinit/IcuRegistration.h index e968dbdd6..b9065daed 100644 --- a/libandroidicuinit/IcuRegistration.h +++ b/libandroidicuinit/IcuRegistration.h @@ -127,6 +127,11 @@ class IcuRegistration final { IcuRegistration(); static bool pathExists(const std::string& path); + // TODO (b/319103072): remove this method once prebuilts are updated. + // Production tzdata6 will have versioned files only. Keeping this method while there are + // prebuilts which have ICU file(s) under /etc/icu and not /etc/tz/versioned/*/icu directory in + // the tzdata APEX. + static std::string getPreVTimeZoneModulePath(); static std::string getTimeZoneModulePath(); static std::string getI18nModulePath(); |