summaryrefslogtreecommitdiff
path: root/android_icu4j
diff options
context:
space:
mode:
Diffstat (limited to 'android_icu4j')
-rw-r--r--android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TimeZoneDataFiles.java17
-rw-r--r--android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java14
-rw-r--r--android_icu4j/src/main/java/android/icu/impl/ICUBinary.java24
-rw-r--r--android_icu4j/src/main/java/android/icu/platform/AndroidDataFiles.java25
-rw-r--r--android_icu4j/testing/src/android/icu/extratest/platform/AndroidDataFilesTest.java24
-rw-r--r--android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneDataFilesTest.java14
-rw-r--r--android_icu4j/testing/src/com/android/i18n/test/timezone/TzDataSetVersionTest.java25
7 files changed, 108 insertions, 35 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..2ffdef015 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
@@ -39,14 +39,27 @@ public final class TimeZoneDataFiles {
* Returns time zone file paths for the specified file name in an array in the order they
* should be tried. See {@link AndroidDataFiles#generateIcuDataPath()} for ICU files instead.
* <ul>
- * <li>[0] - the location of the file from the time zone module under /apex (must exist).</li>
+ * <li>[0] - the location of the versioned file from the time zone module under /apex
+ * (must exist).</li>
+ * <li>[1] - old, unversioned location of the file from the time zone module under /apex. Will
+ * be removed once prebuilts are updated.</>
* </ul>
*/
// VisibleForTesting
public static String[] getTimeZoneFilePaths(String fileName) {
- return new String[] { getTimeZoneModuleTzFile(fileName) };
+ return new String[] {
+ // TODO(b/319103072) There should be only versioned path.
+ getTimeZoneModuleTzFile(fileName),
+ getVersionedTimeZoneModuleTzFile(fileName) };
}
+ // TODO(b/319103072) This should be removed once prebuilts are updated.
+ public static String getVersionedTimeZoneModuleTzFile(String fileName) {
+ return getTimeZoneModuleFile("tz/versioned/"
+ + TzDataSetVersion.currentFormatMajorVersion() + "/" + fileName);
+ }
+
+ // TODO(b/319103072) This method should read from versioned directory.
public static String getTimeZoneModuleTzFile(String fileName) {
return getTimeZoneModuleFile("tz/" + fileName);
}
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..e3dd27581 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,9 @@ 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. */
+ // @VisibleForTesting
+ public 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" */
@@ -287,7 +286,8 @@ public final class TzDataSetVersion {
return value;
}
- private static String toFormatVersionString(int majorFormatVersion, int minorFormatVersion) {
+ // @VisibleForTesting
+ public static String toFormatVersionString(int majorFormatVersion, int minorFormatVersion) {
return to3DigitVersionString(majorFormatVersion)
+ "." + to3DigitVersionString(minorFormatVersion);
}
diff --git a/android_icu4j/src/main/java/android/icu/impl/ICUBinary.java b/android_icu4j/src/main/java/android/icu/impl/ICUBinary.java
index 5e0c57f09..f5f0c6adc 100644
--- a/android_icu4j/src/main/java/android/icu/impl/ICUBinary.java
+++ b/android_icu4j/src/main/java/android/icu/impl/ICUBinary.java
@@ -21,7 +21,6 @@ import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.MissingResourceException;
import java.util.Set;
@@ -226,11 +225,15 @@ public final class ICUBinary {
abstract void addBaseNamesInFolder(String folder, String suffix, Set<String> names);
}
+ // BEGIN Android-changed: Map file only once during ICUBinary initialization. Attempt to fix
+ // some apps not seeing metazones.res file. See b/339899412.
private static final class SingleDataFile extends DataFile {
+ private final ByteBuffer bytes;
private final File path;
SingleDataFile(String item, File path) {
super(item);
+ this.bytes = mapFile(path);
this.path = path;
}
@Override
@@ -241,12 +244,13 @@ public final class ICUBinary {
@Override
ByteBuffer getData(String requestedPath) {
if (requestedPath.equals(itemPath)) {
- return mapFile(path);
+ return bytes.duplicate();
} else {
return null;
}
}
-
+ // END Android-changed: Map file only once during ICUBinary initialization. Attempt to fix
+ // some apps not seeing metazones.res files in b/339899412.
@Override
void addBaseNamesInFolder(String folder, String suffix, Set<String> names) {
if (itemPath.length() > folder.length() + suffix.length() &&
@@ -284,8 +288,7 @@ public final class ICUBinary {
}
}
- // Android-changed: make icuDataFiles immutable and assign value exactly once.
- private static final List<DataFile> icuDataFiles;
+ private static final List<DataFile> icuDataFiles = new ArrayList<>();
static {
// BEGIN Android-changed: Initialize ICU data file paths.
@@ -299,15 +302,9 @@ public final class ICUBinary {
dataPath = AndroidDataFiles.generateIcuDataPath();
}
// END Android-changed: Initialize ICU data file paths.
- // BEGIN Android-changed: make icuDataFiles immutable and assign value exactly once.
if (dataPath != null) {
- List<DataFile> resolvedFiles = new ArrayList<>();
- addDataFilesFromPath(dataPath, resolvedFiles);
- icuDataFiles = Collections.unmodifiableList(resolvedFiles);
- } else {
- icuDataFiles = Collections.emptyList();
+ addDataFilesFromPath(dataPath, icuDataFiles);
}
- // END Android-changed: make icuDataFiles immutable and assign value exactly once.
}
private static void addDataFilesFromPath(String dataPath, List<DataFile> files) {
@@ -330,8 +327,7 @@ public final class ICUBinary {
path = path.substring(0, path.length() - 1);
}
if (path.length() != 0) {
- // Android-changed: pass `files` argument and not icuDataFiles.
- addDataFilesFromFolder(new File(path), new StringBuilder(), files);
+ addDataFilesFromFolder(new File(path), new StringBuilder(), icuDataFiles);
}
if (sepIndex < 0) {
break;
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..f91fbcac1 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,21 @@ import java.util.stream.Stream;
public class AndroidDataFilesTest {
private static final Set<String> TZDATA_RES_FILES =
- Set.of(
+ 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 Set<String> TZDATA_RES_FILES_AT_OLD_LOCATION =
+ Stream.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");
+ "/apex/com.android.tzdata/etc/icu/timezoneTypes.res")
+ .collect(toSet());
private static final String ICU_DAT_PATH =
"/apex/com.android.i18n/etc/icu/icudt" + VersionInfo.ICU_VERSION.getMajor() + "l.dat";
@@ -80,12 +90,16 @@ public class AndroidDataFilesTest {
.map(f -> f.getPath())
.collect(toSet());
- for (String resFile : TZDATA_RES_FILES) {
- assertContains(icuFiles, resFile);
- }
+ assertTrue(containsAllResFiles(icuFiles));
+
assertContains(icuFiles, ICU_DAT_PATH);
}
+ private static boolean containsAllResFiles(Set<String> existingFiles) {
+ return existingFiles.containsAll(TZDATA_RES_FILES)
+ || existingFiles.containsAll(TZDATA_RES_FILES_AT_OLD_LOCATION);
+ }
+
private static boolean isIcuFile(File file) {
return file.getName().endsWith(".res") || file.getName().endsWith(".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..3b5aff459 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
@@ -41,7 +41,7 @@ public class TimeZoneDataFilesTest {
@Test
public void getTimeZoneFilePaths() {
String[] paths = TimeZoneDataFiles.getTimeZoneFilePaths("foo");
- assertEquals(1, paths.length);
+ assertEquals(2, paths.length);
assertTrue(paths[0].startsWith(System.getenv(ANDROID_TZDATA_ROOT_ENV)));
assertTrue(paths[0].endsWith("/foo"));
@@ -53,13 +53,19 @@ public class TimeZoneDataFilesTest {
String icuDataPath = AndroidDataFiles.generateIcuDataPath();
String[] paths = icuDataPath.split(":");
- assertEquals(2, paths.length);
+ assertEquals(3, paths.length);
+
+ 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[0];
+ 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..937ad5c12 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,24 @@
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 com.android.i18n.timezone.TzDataSetVersion.FORMAT_VERSION_STRING_LENGTH;
+import static com.android.i18n.timezone.TzDataSetVersion.toFormatVersionString;
+
+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 +112,17 @@ 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() {
+ String formatVersion = toFormatVersionString(CURRENT_FORMAT_MAJOR_VERSION,
+ CURRENT_FORMAT_MINOR_VERSION);
+ assertEquals(FORMAT_VERSION_STRING_LENGTH, formatVersion.length());
+ }
+
private TzDataSetVersion createTzDataSetVersion(int majorFormatVersion, int minorFormatVersion)
throws TzDataSetException {
return new TzDataSetVersion(majorFormatVersion, minorFormatVersion, VALID_RULES_VERSION, 3);