Refactor DistroVersion, create TzDataSetVersion

DistroVersion was created for the time zone updates via APK
feature to handle data and format versioning.

This change refactors DistroVersion. It still defines the
format of the distro_version file inside of a time zone data
distro.zip.

A new class, TzDataSetVersion is being added to libcore to
define the format of the tz_version file used in the newer
time zone data module. The knowledge of the format version
used on the device now moves there. The TzDataSetVersion is
also being exposed in a host library for host-side tests.

Bug: 119026403
Test: CTS: run cts-dev -m CtsLibcoreTestCases
Change-Id: I9d9b999336bdd1185a5014a866a508371733d782
diff --git a/distro/core/Android.bp b/distro/core/Android.bp
index 96cdc42..67536da 100644
--- a/distro/core/Android.bp
+++ b/distro/core/Android.bp
@@ -18,17 +18,22 @@
 // This is distinct from time_zone_distro_unbundled. It should be used
 // for platform code as it avoids circular dependencies when stubs targets
 // need to build framework (as appears to be the case in aosp/master).
-// Also used for host-side tests.
-java_library_static {
+// Also used from host-side tests and tools.
+java_library {
     name: "time_zone_distro",
     host_supported: true,
 
     srcs: ["src/main/**/*.java"],
+    target: {
+        host: {
+            static_libs: ["timezone-host"],
+        },
+    },
 }
 
 // Library of classes for handling time zone distros. Used in unbundled
 // cases. Same as above, except dependent on system_current stubs.
-java_library_static {
+java_library {
     name: "time_zone_distro_unbundled",
 
     srcs: ["src/main/**/*.java"],
@@ -36,7 +41,7 @@
 }
 
 // Tests for time_zone_distro code.
-java_library_static {
+java_library {
     name: "time_zone_distro-tests",
 
     srcs: ["src/test/**/*.java"],
diff --git a/distro/core/src/main/com/android/timezone/distro/DistroVersion.java b/distro/core/src/main/com/android/timezone/distro/DistroVersion.java
index 9610d45..a3ead3f 100644
--- a/distro/core/src/main/com/android/timezone/distro/DistroVersion.java
+++ b/distro/core/src/main/com/android/timezone/distro/DistroVersion.java
@@ -26,27 +26,11 @@
  */
 public class DistroVersion {
 
-    /**
-     * The major distro format version supported by this device.
-     * Increment this for non-backwards compatible changes to the distro format. Reset the minor
-     * version to 1 when doing so.
-     * This constant must match the one in system/core/tzdatacheck/tzdatacheck.cpp.
-     */
-    public static final int CURRENT_FORMAT_MAJOR_VERSION = 3; // Android Q
-
-    /**
-     * The minor distro format version supported by this device. Increment this for
-     * backwards-compatible changes to the distro format.
-     * This constant must match the one in system/core/tzdatacheck/tzdatacheck.cpp.
-     */
-    public static final int CURRENT_FORMAT_MINOR_VERSION = 1;
-
-    /** The full major + minor distro format version for this device. */
-    private static final String FULL_CURRENT_FORMAT_VERSION_STRING =
-            toFormatVersionString(CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION);
+    /** An example major + minor distro format version string. */
+    private static final String SAMPLE_FORMAT_VERSION_STRING = toFormatVersionString(1, 1);
 
     private static final int FORMAT_VERSION_STRING_LENGTH =
-            FULL_CURRENT_FORMAT_VERSION_STRING.length();
+            SAMPLE_FORMAT_VERSION_STRING.length();
     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" */
@@ -124,11 +108,6 @@
                 .getBytes(StandardCharsets.US_ASCII);
     }
 
-    public static boolean isCompatibleWithThisDevice(DistroVersion distroVersion) {
-        return (CURRENT_FORMAT_MAJOR_VERSION == distroVersion.formatMajorVersion)
-                && (CURRENT_FORMAT_MINOR_VERSION <= distroVersion.formatMinorVersion);
-    }
-
     @Override
     public boolean equals(Object o) {
         if (this == o) {
diff --git a/distro/core/src/main/com/android/timezone/distro/TimeZoneDistro.java b/distro/core/src/main/com/android/timezone/distro/TimeZoneDistro.java
index a21b261..27dca1d 100644
--- a/distro/core/src/main/com/android/timezone/distro/TimeZoneDistro.java
+++ b/distro/core/src/main/com/android/timezone/distro/TimeZoneDistro.java
@@ -21,8 +21,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Arrays;
-import java.util.function.Supplier;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
diff --git a/distro/core/src/test/com/android/timezone/distro/DistroVersionTest.java b/distro/core/src/test/com/android/timezone/distro/DistroVersionTest.java
index a4d96c5..7f54e84 100644
--- a/distro/core/src/test/com/android/timezone/distro/DistroVersionTest.java
+++ b/distro/core/src/test/com/android/timezone/distro/DistroVersionTest.java
@@ -63,38 +63,6 @@
         assertEquals(distroVersion, DistroVersion.fromBytes(distroVersion.toBytes()));
     }
 
-    public void testIsCompatibleWithThisDevice() throws Exception {
-        DistroVersion exactMatch = createDistroVersion(
-                DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
-                DistroVersion.CURRENT_FORMAT_MINOR_VERSION);
-        assertTrue(DistroVersion.isCompatibleWithThisDevice(exactMatch));
-
-        DistroVersion newerMajor = createDistroVersion(
-                DistroVersion.CURRENT_FORMAT_MAJOR_VERSION + 1,
-                DistroVersion.CURRENT_FORMAT_MINOR_VERSION);
-        assertFalse(DistroVersion.isCompatibleWithThisDevice(newerMajor));
-
-        DistroVersion newerMinor = createDistroVersion(
-                DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
-                DistroVersion.CURRENT_FORMAT_MINOR_VERSION + 1);
-        assertTrue(DistroVersion.isCompatibleWithThisDevice(newerMinor));
-
-        // The constant versions should never be below 1. We allow 0 but want to start version
-        // numbers at 1 to allow testing of older version logic.
-        assertTrue(DistroVersion.CURRENT_FORMAT_MAJOR_VERSION >= 1);
-        assertTrue(DistroVersion.CURRENT_FORMAT_MINOR_VERSION >= 1);
-
-        DistroVersion olderMajor = createDistroVersion(
-                DistroVersion.CURRENT_FORMAT_MAJOR_VERSION - 1,
-                DistroVersion.CURRENT_FORMAT_MINOR_VERSION);
-        assertFalse(DistroVersion.isCompatibleWithThisDevice(olderMajor));
-
-        DistroVersion olderMinor = createDistroVersion(
-                DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
-                DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1);
-        assertFalse(DistroVersion.isCompatibleWithThisDevice(olderMinor));
-    }
-
     private DistroVersion createDistroVersion(int majorFormatVersion, int minorFormatVersion)
             throws DistroException {
         return new DistroVersion(majorFormatVersion, minorFormatVersion, VALID_RULES_VERSION, 3);
diff --git a/distro/core/src/test/com/android/timezone/distro/TimeZoneDistroTest.java b/distro/core/src/test/com/android/timezone/distro/TimeZoneDistroTest.java
index 0d5b474..ac6d825 100644
--- a/distro/core/src/test/com/android/timezone/distro/TimeZoneDistroTest.java
+++ b/distro/core/src/test/com/android/timezone/distro/TimeZoneDistroTest.java
@@ -18,6 +18,7 @@
 import junit.framework.TestCase;
 
 import libcore.testing.io.TestIoUtils;
+import libcore.timezone.TzDataSetVersion;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -50,8 +51,9 @@
     }
 
     public void testGetDistroVersion() throws Exception {
-        DistroVersion distroVersion = new DistroVersion(DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
-                DistroVersion.CURRENT_FORMAT_MINOR_VERSION, "2016c", 1);
+        DistroVersion distroVersion = new DistroVersion(
+                TzDataSetVersion.currentFormatMajorVersion(),
+                TzDataSetVersion.currentFormatMinorVersion(), "2016c", 1);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) {
             addZipEntry(zipOutputStream, TimeZoneDistro.DISTRO_VERSION_FILE_NAME,
@@ -63,8 +65,8 @@
     }
 
     public void testGetDistroVersion_closesStream() throws Exception {
-        DistroVersion distroVersion = new DistroVersion(DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
-                DistroVersion.CURRENT_FORMAT_MINOR_VERSION, "2016c", 1);
+        DistroVersion distroVersion = new DistroVersion(
+                3 /* majorVersion */, 2 /* minorVersion */, "2016c", 1);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) {
             addZipEntry(zipOutputStream, TimeZoneDistro.DISTRO_VERSION_FILE_NAME,
@@ -79,8 +81,8 @@
     }
 
     public void testExtractTo_closesStream() throws Exception {
-        DistroVersion distroVersion = new DistroVersion(DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
-                DistroVersion.CURRENT_FORMAT_MINOR_VERSION, "2016c", 1);
+        DistroVersion distroVersion = new DistroVersion(
+                3 /* majorVersion */, 2 /* minorVersion */, "2016c", 1);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) {
             addZipEntry(zipOutputStream, TimeZoneDistro.DISTRO_VERSION_FILE_NAME,
diff --git a/distro/installer/Android.bp b/distro/installer/Android.bp
index 8750f91..e954c8f 100644
--- a/distro/installer/Android.bp
+++ b/distro/installer/Android.bp
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // The classes needed to handle installation of time zone distros.
-java_library_static {
+java_library {
     name: "time_zone_distro_installer",
 
     srcs: ["src/main/**/*.java"],
@@ -21,7 +21,7 @@
 }
 
 // Tests for time_zone_distro_installer code
-java_library_static {
+java_library {
     name: "time_zone_distro_installer-tests",
 
     srcs: ["src/test/**/*.java"],
diff --git a/distro/installer/src/main/com/android/timezone/distro/installer/TimeZoneDistroInstaller.java b/distro/installer/src/main/com/android/timezone/distro/installer/TimeZoneDistroInstaller.java
index 68c9715..13cb560 100644
--- a/distro/installer/src/main/com/android/timezone/distro/installer/TimeZoneDistroInstaller.java
+++ b/distro/installer/src/main/com/android/timezone/distro/installer/TimeZoneDistroInstaller.java
@@ -29,6 +29,8 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import libcore.timezone.TzDataSetVersion;
+import libcore.timezone.TzDataSetVersion.TzDataSetException;
 import libcore.util.TimeZoneFinder;
 import libcore.util.ZoneInfoDB;
 
@@ -175,7 +177,16 @@
                 Slog.i(logTag, "Update not applied: Distro version could not be loaded");
                 return INSTALL_FAIL_BAD_DISTRO_STRUCTURE;
             }
-            if (!DistroVersion.isCompatibleWithThisDevice(distroVersion)) {
+            TzDataSetVersion distroTzDataSetVersion;
+            try {
+                distroTzDataSetVersion = new TzDataSetVersion(
+                        distroVersion.formatMajorVersion, distroVersion.formatMinorVersion,
+                        distroVersion.rulesVersion, distroVersion.revision);
+            } catch (TzDataSetException e) {
+                Slog.i(logTag, "Update not applied: Distro version could not be converted", e);
+                return INSTALL_FAIL_BAD_DISTRO_STRUCTURE;
+            }
+            if (!TzDataSetVersion.isCompatibleWithThisDevice(distroTzDataSetVersion)) {
                 Slog.i(logTag, "Update not applied: Distro format version check failed: "
                         + distroVersion);
                 return INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION;
@@ -186,7 +197,7 @@
                 return INSTALL_FAIL_BAD_DISTRO_STRUCTURE;
             }
 
-            if (!checkDistroRulesNewerThanSystem(systemTzDataFile, distroVersion)) {
+            if (!checkDistroRulesNewerThanSystem(systemTzDataFile, distroTzDataSetVersion)) {
                 Slog.i(logTag, "Update not applied: Distro rules version check failed");
                 return INSTALL_FAIL_RULES_TOO_OLD;
             }
@@ -387,7 +398,7 @@
      * Returns true if the the distro IANA rules version is >= system IANA rules version.
      */
     private boolean checkDistroRulesNewerThanSystem(
-            File systemTzDataFile, DistroVersion distroVersion) throws IOException {
+            File systemTzDataFile, TzDataSetVersion distroVersion) throws IOException {
 
         // We only check the /system tzdata file and assume that other data like ICU is in sync.
         // There is a CTS test that checks ICU and bionic/libcore are in sync.
diff --git a/distro/installer/src/test/com/android/timezone/distro/installer/TimeZoneDistroInstallerTest.java b/distro/installer/src/test/com/android/timezone/distro/installer/TimeZoneDistroInstallerTest.java
index ab8f512..da9f671 100644
--- a/distro/installer/src/test/com/android/timezone/distro/installer/TimeZoneDistroInstallerTest.java
+++ b/distro/installer/src/test/com/android/timezone/distro/installer/TimeZoneDistroInstallerTest.java
@@ -38,7 +38,8 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 import libcore.io.IoUtils;
-import libcore.tzdata.testing.ZoneInfoTestHelper;
+import libcore.timezone.TzDataSetVersion;
+import libcore.timezone.testing.ZoneInfoTestHelper;
 
 import static org.junit.Assert.assertArrayEquals;
 
@@ -333,8 +334,8 @@
         // Create a distro that will appear to be newer than the one currently supported.
         byte[] distroBytes = createValidTimeZoneDistroBuilder(NEW_RULES_VERSION, 1)
                 .replaceFormatVersionForTests(
-                        DistroVersion.CURRENT_FORMAT_MAJOR_VERSION + 1,
-                        DistroVersion.CURRENT_FORMAT_MINOR_VERSION)
+                        TzDataSetVersion.currentFormatMajorVersion() + 1,
+                        TzDataSetVersion.currentFormatMinorVersion())
                 .buildUnvalidatedBytes();
         assertEquals(
                 TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION,
@@ -597,8 +598,8 @@
                 + "  </countryzones>\n"
                 + "</timezones>\n";
         DistroVersion distroVersion = new DistroVersion(
-                DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
-                DistroVersion.CURRENT_FORMAT_MINOR_VERSION,
+                TzDataSetVersion.currentFormatMajorVersion(),
+                TzDataSetVersion.currentFormatMinorVersion(),
                 rulesVersion,
                 revision);
         return new TimeZoneDistroBuilder()
diff --git a/distro/tools/Android.bp b/distro/tools/Android.bp
index 8d9d9b1..ab87adb 100644
--- a/distro/tools/Android.bp
+++ b/distro/tools/Android.bp
@@ -14,7 +14,7 @@
 
 // Library of tools classes for creating / interpreting time zone distros.
 // Used when generating distros and in host-side tests.
-java_library_static {
+java_library {
     name: "time_zone_distro_tools",
     host_supported: true,
 
diff --git a/distro/tools/src/main/com/android/timezone/distro/tools/CreateTimeZoneDistro.java b/distro/tools/src/main/com/android/timezone/distro/tools/CreateTimeZoneDistro.java
index 1328ba2..b29fba7 100644
--- a/distro/tools/src/main/com/android/timezone/distro/tools/CreateTimeZoneDistro.java
+++ b/distro/tools/src/main/com/android/timezone/distro/tools/CreateTimeZoneDistro.java
@@ -15,6 +15,8 @@
  */
 package com.android.timezone.distro.tools;
 
+import libcore.timezone.TzDataSetVersion;
+
 import com.android.timezone.distro.DistroVersion;
 import com.android.timezone.distro.TimeZoneDistro;
 
@@ -28,7 +30,8 @@
 import java.util.Properties;
 
 /**
- * A command-line tool for creating a time zone update distro file.
+ * A command-line tool for creating a time zone update distro and associated
+ * files.
  *
  * <p>Args:
  * <dl>
@@ -79,13 +82,21 @@
         Properties properties = loadProperties(f);
         String ianaRulesVersion = getMandatoryProperty(properties, "rules.version");
         int revision = Integer.parseInt(getMandatoryProperty(properties, "revision"));
-        DistroVersion distroVersion = new DistroVersion(
-                DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
-                DistroVersion.CURRENT_FORMAT_MINOR_VERSION,
+
+        // Create an object to hold version metadata for the tz data.
+        TzDataSetVersion tzDataSetVersion = new TzDataSetVersion(
+                TzDataSetVersion.currentFormatMajorVersion(),
+                TzDataSetVersion.currentFormatMinorVersion(),
                 ianaRulesVersion,
                 revision);
-        byte[] distroVersionBytes = distroVersion.toBytes();
+        byte[] tzDataSetVersionBytes = tzDataSetVersion.toBytes();
 
+        // Create a DistroVersion from the TzDataSetVersion.
+        DistroVersion distroVersion = new DistroVersion(
+                tzDataSetVersion.formatMajorVersion,
+                tzDataSetVersion.formatMinorVersion,
+                tzDataSetVersion.rulesVersion,
+                tzDataSetVersion.revision);
         TimeZoneDistroBuilder builder = new TimeZoneDistroBuilder()
                 .setDistroVersion(distroVersion)
                 .setTzDataFile(getMandatoryPropertyFile(properties, "tzdata.file"))
@@ -96,9 +107,9 @@
         File outputDistroDir = getMandatoryPropertyFile(properties, "output.distro.dir");
         File outputVersionFile = new File(getMandatoryProperty(properties, "output.version.file"));
 
-        // Write the distro version file outside the zip for reference.
+        // Write the tz data set version file.
         try (OutputStream os = new FileOutputStream(outputVersionFile)) {
-            os.write(distroVersionBytes);
+            os.write(tzDataSetVersionBytes);
         }
         System.out.println("Wrote " + outputVersionFile);
 
diff --git a/testing/src/main/java/libcore/tzdata/testing/ZoneInfoTestHelper.java b/testing/src/main/java/libcore/timezone/testing/ZoneInfoTestHelper.java
similarity index 99%
rename from testing/src/main/java/libcore/tzdata/testing/ZoneInfoTestHelper.java
rename to testing/src/main/java/libcore/timezone/testing/ZoneInfoTestHelper.java
index 75ddc66..bdfd707 100644
--- a/testing/src/main/java/libcore/tzdata/testing/ZoneInfoTestHelper.java
+++ b/testing/src/main/java/libcore/timezone/testing/ZoneInfoTestHelper.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package libcore.tzdata.testing;
+package libcore.timezone.testing;
 
 import java.io.ByteArrayOutputStream;
 import java.nio.ByteBuffer;
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
index 3a88557..2a1defc 100644
--- a/tzdatacheck/tzdatacheck.cpp
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -55,14 +55,15 @@
 static const int DISTRO_VERSION_LENGTH = 13;
 
 // The major version of the distro format supported by this code as a null-terminated char[].
-// See also com.android.timezone.distro.TimeZoneDistro / com.android.timezone.distro.DistroVersion.
+// See also com.android.timezone.distro.TimeZoneDistro / libcore.timezone.TzDataSetVersion.
 static const char SUPPORTED_DISTRO_MAJOR_VERSION[] = "003";
 
 // The length of the distro format major version excluding the \0
 static const size_t SUPPORTED_DISTRO_MAJOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MAJOR_VERSION) - 1;
 
 // The minor version of the distro format supported by this code as a null-terminated char[].
-// See also com.android.timezone.distro.TimeZoneDistro / com.android.timezone.distro.DistroVersion.
+// See also com.android.timezone.distro.TimeZoneDistro /
+// libcore.timezone.TzDataSetVersion
 static const char SUPPORTED_DISTRO_MINOR_VERSION[] = "001";
 
 // The length of the distro format minor version excluding the \0