Tweak tzdata format

Add a new placeholder pointer that signifies the end of the zone.tab
section of the file. This will enable us to easily add sections in the
future if we want to. It also lays the groundwork for removal of
zone.tab from the file.

Test: atest CtsIcuTestCases
Bug: 161699306
Change-Id: I01bb0fb4b7357ada7763848cfeb728c1267edd84
diff --git a/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/ZoneInfoDb.java b/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/ZoneInfoDb.java
index 30eee3a..cd219ef 100644
--- a/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/ZoneInfoDb.java
+++ b/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/ZoneInfoDb.java
@@ -208,6 +208,7 @@
     // int index_offset
     // int data_offset
     // int zonetab_offset
+    // int final_offset
     BufferIterator it = mappedFile.bigEndianIterator();
 
     try {
@@ -226,15 +227,19 @@
       validateOffset(data_offset, fileSize);
       int zonetab_offset = it.readInt();
       validateOffset(zonetab_offset, fileSize);
+      int final_offset = it.readInt();
 
-      if (index_offset >= data_offset || data_offset >= zonetab_offset) {
+      if (index_offset >= data_offset
+              || data_offset >= zonetab_offset
+              || zonetab_offset >= final_offset
+              || final_offset > fileSize) {
         throw new IOException("Invalid offset: index_offset=" + index_offset
                 + ", data_offset=" + data_offset + ", zonetab_offset=" + zonetab_offset
-                + ", fileSize=" + fileSize);
+                + ", final_offset=" + final_offset + ", fileSize=" + fileSize);
       }
 
       readIndex(it, index_offset, data_offset);
-      readZoneTab(it, zonetab_offset, fileSize - zonetab_offset);
+      readZoneTab(it, zonetab_offset, final_offset - zonetab_offset);
     } catch (IndexOutOfBoundsException e) {
       throw new IOException("Invalid read from data file", e);
     }
diff --git a/android_icu4j/testing/src/com/android/i18n/test/timezone/ZoneInfoDbTest.java b/android_icu4j/testing/src/com/android/i18n/test/timezone/ZoneInfoDbTest.java
index a02ad84..8657dcf 100644
--- a/android_icu4j/testing/src/com/android/i18n/test/timezone/ZoneInfoDbTest.java
+++ b/android_icu4j/testing/src/com/android/i18n/test/timezone/ZoneInfoDbTest.java
@@ -128,14 +128,11 @@
     checkInvalidDataDetected(data);
   }
 
-  public void testLoadTzData_zoneTabOutsideFile() throws Exception {
+  public void testLoadTzData_zoneTabOffsetOutsideFile() throws Exception {
     ZoneInfoTestHelper.TzDataBuilder builder =
             new ZoneInfoTestHelper.TzDataBuilder()
                     .initializeToValid();
 
-    // Sections must be in the correct order: section sizing is calculated using them.
-    builder.setIndexOffsetOverride(10);
-    builder.setDataOffsetOverride(10 + SIZEOF_INDEX_ENTRY);
     builder.setZoneTabOffsetOverride(3000); // This is invalid if it is outside of the file.
 
     byte[] data = builder.build();
@@ -144,6 +141,19 @@
     checkInvalidDataDetected(data);
   }
 
+  public void testLoadTzData_finalOffsetOutsideFile() throws Exception {
+    ZoneInfoTestHelper.TzDataBuilder builder =
+            new ZoneInfoTestHelper.TzDataBuilder()
+                    .initializeToValid();
+
+    builder.setFinalOffsetOverride(3000); // This is invalid if it is outside of the file.
+
+    byte[] data = builder.build();
+    // The zoneTab offset must be outside of the file for this test to be valid.
+    assertTrue(3000 > data.length);
+    checkInvalidDataDetected(data);
+  }
+
   public void testLoadTzData_nonDivisibleIndex() throws Exception {
     ZoneInfoTestHelper.TzDataBuilder builder =
             new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
@@ -244,6 +254,20 @@
     }
   }
 
+  public void testGetZoneTab() throws Exception {
+    String zoneTab = "This is my zone.tab";
+    ZoneInfoTestHelper.TzDataBuilder builder =
+            new ZoneInfoTestHelper.TzDataBuilder()
+                    .initializeToValid()
+                    .setZoneTab(zoneTab);
+
+    byte[] data = builder.build();
+    File testFile = makeTemporaryFile(data);
+
+    ZoneInfoDb zoneInfoDb = ZoneInfoDb.loadTzData(testFile.getPath());
+    assertEquals(zoneTab, zoneInfoDb.getZoneTab());
+  }
+
   private static File makeCorruptFile() throws Exception {
     return makeTemporaryFile("invalid content".getBytes());
   }