diff options
-rw-r--r-- | luni/src/main/java/libcore/util/ZoneInfoDB.java | 54 | ||||
-rw-r--r-- | luni/src/test/java/libcore/util/ZoneInfoDBTest.java | 32 |
2 files changed, 62 insertions, 24 deletions
diff --git a/luni/src/main/java/libcore/util/ZoneInfoDB.java b/luni/src/main/java/libcore/util/ZoneInfoDB.java index c53db120a8..e6559b5b4d 100644 --- a/luni/src/main/java/libcore/util/ZoneInfoDB.java +++ b/luni/src/main/java/libcore/util/ZoneInfoDB.java @@ -37,8 +37,8 @@ import libcore.io.MemoryMappedFile; * @hide - used to implement TimeZone */ public final class ZoneInfoDB { - private static final TzData DATA = - new TzData(System.getenv("ANDROID_DATA") + "/misc/zoneinfo/current/tzdata", + private static final TzData DATA = TzData.loadTzDataWithFallback( + System.getenv("ANDROID_DATA") + "/misc/zoneinfo/current/tzdata", System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo/tzdata"); public static class TzData { @@ -83,10 +83,16 @@ public final class ZoneInfoDB { } }; - public TzData(String... paths) { + /** + * Loads the data at the specified paths in order, returning the first valid one as a + * {@link TzData} object. If there is no valid one found a basic fallback instance is created + * containing just GMT. + */ + public static TzData loadTzDataWithFallback(String... paths) { for (String path : paths) { - if (loadData(path)) { - return; + TzData tzData = new TzData(); + if (tzData.loadData(path)) { + return tzData; } } @@ -94,10 +100,28 @@ public final class ZoneInfoDB { // This is actually implemented in TimeZone itself, so if this is the only time zone // we report, we won't be asked any more questions. System.logE("Couldn't find any tzdata!"); - version = "missing"; - zoneTab = "# Emergency fallback data.\n"; - ids = new String[] { "GMT" }; - byteOffsets = rawUtcOffsetsCache = new int[1]; + return TzData.createFallback(); + } + + /** + * Loads the data at the specified path and returns the {@link TzData} object if it is valid, + * otherwise {@code null}. + */ + public static TzData loadTzData(String path) { + TzData tzData = new TzData(); + if (tzData.loadData(path)) { + return tzData; + } + return null; + } + + private static TzData createFallback() { + TzData tzData = new TzData(); + tzData.populateFallback(); + return tzData; + } + + private TzData() { } /** @@ -115,6 +139,13 @@ public final class ZoneInfoDB { return it; } + private void populateFallback() { + version = "missing"; + zoneTab = "# Emergency fallback data.\n"; + ids = new String[] { "GMT" }; + byteOffsets = rawUtcOffsetsCache = new int[1]; + } + private boolean loadData(String path) { try { mappedFile = MemoryMappedFile.mmapRO(path); @@ -125,6 +156,11 @@ public final class ZoneInfoDB { readHeader(); return true; } catch (Exception ex) { + try { + mappedFile.close(); + } catch (ErrnoException ignored) { + } + // Something's wrong with the file. // Log the problem and return false so we try the next choice. System.logE("tzdata file \"" + path + "\" was present but invalid!", ex); diff --git a/luni/src/test/java/libcore/util/ZoneInfoDBTest.java b/luni/src/test/java/libcore/util/ZoneInfoDBTest.java index 12ad0d72af..7fa46d52c3 100644 --- a/luni/src/test/java/libcore/util/ZoneInfoDBTest.java +++ b/luni/src/test/java/libcore/util/ZoneInfoDBTest.java @@ -29,36 +29,38 @@ public class ZoneInfoDBTest extends junit.framework.TestCase { System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo/tzdata"; // An empty override file should fall back to the default file. - public void testEmptyOverrideFile() throws Exception { - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); + public void testLoadTzDataWithFallback_emptyOverrideFile() throws Exception { + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(TZDATA_IN_ROOT); String emptyFilePath = makeEmptyFile().getPath(); + ZoneInfoDB.TzData dataWithEmptyOverride = - new ZoneInfoDB.TzData(emptyFilePath, TZDATA_IN_ROOT); + ZoneInfoDB.TzData.loadTzDataWithFallback(emptyFilePath, TZDATA_IN_ROOT); assertEquals(data.getVersion(), dataWithEmptyOverride.getVersion()); assertEquals(data.getAvailableIDs().length, dataWithEmptyOverride.getAvailableIDs().length); } // A corrupt override file should fall back to the default file. - public void testCorruptOverrideFile() throws Exception { - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); + public void testLoadTzDataWithFallback_corruptOverrideFile() throws Exception { + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(TZDATA_IN_ROOT); String corruptFilePath = makeCorruptFile().getPath(); + ZoneInfoDB.TzData dataWithCorruptOverride = - new ZoneInfoDB.TzData(corruptFilePath, TZDATA_IN_ROOT); + ZoneInfoDB.TzData.loadTzDataWithFallback(corruptFilePath, TZDATA_IN_ROOT); assertEquals(data.getVersion(), dataWithCorruptOverride.getVersion()); assertEquals(data.getAvailableIDs().length, dataWithCorruptOverride.getAvailableIDs().length); } // Given no tzdata files we can use, we should fall back to built-in "GMT". - public void testNoGoodFile() throws Exception { + public void testLoadTzDataWithFallback_noGoodFile() throws Exception { String emptyFilePath = makeEmptyFile().getPath(); - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(emptyFilePath); + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzDataWithFallback(emptyFilePath); assertEquals("missing", data.getVersion()); assertEquals(1, data.getAvailableIDs().length); assertEquals("GMT", data.getAvailableIDs()[0]); } // Given a valid override file, we should find ourselves using that. - public void testGoodOverrideFile() throws Exception { + public void testLoadTzDataWithFallback_goodOverrideFile() throws Exception { RandomAccessFile in = new RandomAccessFile(TZDATA_IN_ROOT, "r"); byte[] content = new byte[(int) in.length()]; in.readFully(content); @@ -70,11 +72,11 @@ public class ZoneInfoDBTest extends junit.framework.TestCase { content[10] = 'z'; in.close(); - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(TZDATA_IN_ROOT); File goodFile = makeTemporaryFile(content); try { ZoneInfoDB.TzData dataWithOverride = - new ZoneInfoDB.TzData(goodFile.getPath(), TZDATA_IN_ROOT); + ZoneInfoDB.TzData.loadTzDataWithFallback(goodFile.getPath(), TZDATA_IN_ROOT); assertEquals("9999z", dataWithOverride.getVersion()); assertEquals(data.getAvailableIDs().length, dataWithOverride.getAvailableIDs().length); } finally { @@ -84,7 +86,7 @@ public class ZoneInfoDBTest extends junit.framework.TestCase { // Confirms any caching that exists correctly handles TimeZone mutability. public void testMakeTimeZone_timeZoneMutability() throws Exception { - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(TZDATA_IN_ROOT); String tzId = "Europe/London"; ZoneInfo first = data.makeTimeZone(tzId); ZoneInfo second = data.makeTimeZone(tzId); @@ -101,19 +103,19 @@ public class ZoneInfoDBTest extends junit.framework.TestCase { } public void testMakeTimeZone_notFound() throws Exception { - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(TZDATA_IN_ROOT); assertNull(data.makeTimeZone("THIS_TZ_DOES_NOT_EXIST")); assertFalse(data.hasTimeZone("THIS_TZ_DOES_NOT_EXIST")); } public void testMakeTimeZone_found() throws Exception { - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(TZDATA_IN_ROOT); assertNotNull(data.makeTimeZone("Europe/London")); assertTrue(data.hasTimeZone("Europe/London")); } public void testGetRulesVersion() throws Exception { - ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT); + ZoneInfoDB.TzData data = ZoneInfoDB.TzData.loadTzData(TZDATA_IN_ROOT); String rulesVersion = ZoneInfoDB.TzData.getRulesVersion(new File(TZDATA_IN_ROOT)); assertEquals(data.getVersion(), rulesVersion); |