Merge "Fix Os.getDefault() BlockGuard test."
diff --git a/luni/src/main/java/libcore/util/HexEncoding.java b/luni/src/main/java/libcore/util/HexEncoding.java
index eceec6b..f5eed4f 100644
--- a/luni/src/main/java/libcore/util/HexEncoding.java
+++ b/luni/src/main/java/libcore/util/HexEncoding.java
@@ -23,17 +23,43 @@
 @libcore.api.CorePlatformApi
 public class HexEncoding {
 
+    private static final char[] LOWER_CASE_DIGITS = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    private static final char[] UPPER_CASE_DIGITS = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+    };
+
     /** Hidden constructor to prevent instantiation. */
     private HexEncoding() {}
 
-    private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
+    /**
+     * Encodes the provided byte as a two-digit hexadecimal String value.
+     */
+    @libcore.api.CorePlatformApi
+    public static String encodeToString(byte b, boolean upperCase) {
+        char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS;
+        char[] buf = new char[2]; // We always want two digits.
+        buf[0] = digits[(b >> 4) & 0xf];
+        buf[1] = digits[b & 0xf];
+        return new String(buf, 0, 2);
+    }
 
     /**
      * Encodes the provided data as a sequence of hexadecimal characters.
      */
     @libcore.api.CorePlatformApi
     public static char[] encode(byte[] data) {
-        return encode(data, 0, data.length);
+        return encode(data, 0, data.length, true /* upperCase */);
+    }
+
+    /**
+     * Encodes the provided data as a sequence of hexadecimal characters.
+     */
+    @libcore.api.CorePlatformApi
+    public static char[] encode(byte[] data, boolean upperCase) {
+        return encode(data, 0, data.length, upperCase);
     }
 
     /**
@@ -41,12 +67,20 @@
      */
     @libcore.api.CorePlatformApi
     public static char[] encode(byte[] data, int offset, int len) {
+        return encode(data, offset, len, true /* upperCase */);
+    }
+
+    /**
+     * Encodes the provided data as a sequence of hexadecimal characters.
+     */
+    private static char[] encode(byte[] data, int offset, int len, boolean upperCase) {
+        char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS;
         char[] result = new char[len * 2];
         for (int i = 0; i < len; i++) {
             byte b = data[offset + i];
             int resultIndex = 2 * i;
-            result[resultIndex] = (HEX_DIGITS[(b >>> 4) & 0x0f]);
-            result[resultIndex + 1] = (HEX_DIGITS[b & 0x0f]);
+            result[resultIndex] = (digits[(b >> 4) & 0x0f]);
+            result[resultIndex + 1] = (digits[b & 0x0f]);
         }
 
         return result;
@@ -57,7 +91,15 @@
      */
     @libcore.api.CorePlatformApi
     public static String encodeToString(byte[] data) {
-        return new String(encode(data));
+        return encodeToString(data, true /* upperCase */);
+    }
+
+    /**
+     * Encodes the provided data as a sequence of hexadecimal characters.
+     */
+    @libcore.api.CorePlatformApi
+    public static String encodeToString(byte[] data, boolean upperCase) {
+        return new String(encode(data, upperCase));
     }
 
     /**
diff --git a/luni/src/main/java/libcore/util/ZoneInfo.java b/luni/src/main/java/libcore/util/ZoneInfo.java
index 65df611..abd98b1 100644
--- a/luni/src/main/java/libcore/util/ZoneInfo.java
+++ b/luni/src/main/java/libcore/util/ZoneInfo.java
@@ -37,8 +37,9 @@
  * Our concrete TimeZone implementation, backed by zoneinfo data.
  *
  * <p>This reads time zone information from a binary file stored on the platform. The binary file
- * is essentially a single file containing compacted versions of all the tzfile (see
- * {@code man 5 tzfile} for details of the source) and an index by long name, e.g. Europe/London.
+ * is essentially a single file containing compacted versions of all the tzfiles produced by the
+ * zone info compiler (zic) tool (see {@code man 5 tzfile} for details of the format and
+ * {@code man 8 zic}) and an index by long name, e.g. Europe/London.
  *
  * <p>The compacted form is created by {@code external/icu/tools/ZoneCompactor.java} and is used
  * by both this and Bionic. {@link ZoneInfoDB} is responsible for mapping the binary file, and
@@ -307,6 +308,8 @@
 
         // Use the latest non-daylight offset (if any) as the raw offset.
         if (mTransitions.length == 0) {
+            // This case is no longer expected to occur in the data used on Android after changes
+            // made in zic version 2014c. It is kept as a fallback.
             // If there are no transitions then use the first GMT offset.
             mRawOffset = gmtOffsets[0];
         } else {
diff --git a/luni/src/test/java/libcore/android/system/OsTest.java b/luni/src/test/java/libcore/android/system/OsTest.java
index 7536885..dd5e698 100644
--- a/luni/src/test/java/libcore/android/system/OsTest.java
+++ b/luni/src/test/java/libcore/android/system/OsTest.java
@@ -56,6 +56,7 @@
 import junit.framework.TestCase;
 
 import libcore.io.IoUtils;
+import libcore.testing.io.TestIoUtils;
 
 import static android.system.OsConstants.*;
 
@@ -81,7 +82,7 @@
             int flags = Os.fcntlVoid(fis.getFD(), F_GETFD);
             assertTrue((flags & FD_CLOEXEC) != 0);
         } finally {
-            IoUtils.closeQuietly(fis);
+            TestIoUtils.closeQuietly(fis);
             f.delete();
         }
     }
@@ -1133,7 +1134,7 @@
     }
 
     public void test_readlink() throws Exception {
-        File path = new File(IoUtils.createTemporaryDirectory("test_readlink"), "symlink");
+        File path = new File(TestIoUtils.createTemporaryDirectory("test_readlink"), "symlink");
 
         // ext2 and ext4 have PAGE_SIZE limits on symlink targets.
         // If file encryption is enabled, there's extra overhead to store the
@@ -1251,7 +1252,7 @@
                 android.system.Os.sendfile(outFd, inFd, offset, maxBytes);
                 assertEquals(expectedEndOffset, offset == null ? null : offset.value);
             }
-            return IoUtils.readFileAsString(out.getPath());
+            return TestIoUtils.readFileAsString(out.getPath());
         } finally {
             out.delete();
         }
@@ -1307,7 +1308,7 @@
             assertEquals(5, offOut.value);
         }
 
-        assertEquals("oobar", IoUtils.readFileAsString(out.getPath()));
+        assertEquals("oobar", TestIoUtils.readFileAsString(out.getPath()));
 
         Os.close(pipe[0]);
         Os.close(pipe[1]);
diff --git a/luni/src/test/java/libcore/java/net/InetAddressTest.java b/luni/src/test/java/libcore/java/net/InetAddressTest.java
index 307cd1d..2ce79fd 100644
--- a/luni/src/test/java/libcore/java/net/InetAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetAddressTest.java
@@ -36,6 +36,7 @@
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 import libcore.libcore.util.SerializationTester;
+import libcore.net.InetAddressUtils;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -312,8 +313,11 @@
     public void test_getByName_invalid(String invalid) throws Exception {
         try {
             InetAddress.getByName(invalid);
-            fail("Invalid IP address incorrectly recognized as valid: "
-                + invalid);
+            String failMessage = "Invalid IP address incorrectly recognized as valid: " + invalid;
+            if (InetAddressUtils.parseNumericAddressNoThrowStripOptionalBrackets(invalid) == null) {
+                failMessage += " (it was probably unexpectedly resolved by this network's DNS)";
+            }
+            fail(failMessage);
         } catch (UnknownHostException expected) {
         }
 
diff --git a/luni/src/test/java/libcore/java/util/DateTest.java b/luni/src/test/java/libcore/java/util/DateTest.java
index df86a38..1fd8193 100644
--- a/luni/src/test/java/libcore/java/util/DateTest.java
+++ b/luni/src/test/java/libcore/java/util/DateTest.java
@@ -50,10 +50,19 @@
         c.clear();
         c.set(Calendar.YEAR, 21);
         assertEquals("Wed Jan 01 00:00:00 PST 21", c.getTime().toString());
-        assertEquals("1 Jan 21 08:00:00 GMT", c.getTime().toGMTString());
+        String actual21GmtString = c.getTime().toGMTString();
+        // zic <= 2014b data gives -08:00:00, later ones gives -07:52:58 instead. http://b/73719425
+        assertTrue("Actual: " + actual21GmtString,
+                "1 Jan 21 07:52:58 GMT".equals(actual21GmtString)
+                        || "1 Jan 21 08:00:00 GMT".equals(actual21GmtString));
+
         c.set(Calendar.YEAR, 321);
         assertEquals("Sun Jan 01 00:00:00 PST 321", c.getTime().toString());
-        assertEquals("1 Jan 321 08:00:00 GMT", c.getTime().toGMTString());
+        String actual321GmtString = c.getTime().toGMTString();
+        // zic <= 2014b data gives -08:00:00, later ones gives -07:52:58 instead. http://b/73719425
+        assertTrue("Actual: " + actual321GmtString,
+                "1 Jan 321 07:52:58 GMT".equals(actual321GmtString)
+                        || "1 Jan 321 08:00:00 GMT".equals(actual321GmtString));
     }
 
     public void test_toGMTString_nonUs() throws Exception {
@@ -64,10 +73,19 @@
         c.clear();
         c.set(Calendar.YEAR, 21);
         assertEquals("Wed Jan 01 00:00:00 PST 21", c.getTime().toString());
-        assertEquals("1 Jan 21 08:00:00 GMT", c.getTime().toGMTString());
+        String actual21GmtString = c.getTime().toGMTString();
+        // zic <= 2014b data gives -08:00:00, later ones gives -07:52:58 instead. http://b/73719425
+        assertTrue("Actual: " + actual21GmtString,
+                "1 Jan 21 07:52:58 GMT".equals(actual21GmtString)
+                        || "1 Jan 21 08:00:00 GMT".equals(actual21GmtString));
+
         c.set(Calendar.YEAR, 321);
         assertEquals("Sun Jan 01 00:00:00 PST 321", c.getTime().toString());
-        assertEquals("1 Jan 321 08:00:00 GMT", c.getTime().toGMTString());
+        String actual321GmtString = c.getTime().toGMTString();
+        // zic <= 2014b data gives -08:00:00, later ones gives -07:52:58 instead. http://b/73719425
+        assertTrue("Actual: " + actual321GmtString,
+                "1 Jan 321 07:52:58 GMT".equals(actual321GmtString)
+                        || "1 Jan 321 08:00:00 GMT".equals(actual321GmtString));
     }
 
     public void test_parse_timezones() {
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 68b6a54..6297080 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -58,7 +58,30 @@
     }
 
     // http://code.google.com/p/android/issues/detail?id=14395
-    public void testPreHistoricInDaylightTime() throws Exception {
+    public void testPreHistoricInDaylightTime() {
+        // A replacement for testPreHistoricInDaylightTime_old() using a zone that still lacks an
+        // explicit transition at Integer.MIN_VALUE with zic 2019a and 2019a data.
+        TimeZone tz = TimeZone.getTimeZone("CET");
+
+        long firstTransitionTimeMillis = -1693706400000L; // Apr 30, 1916 22:00:00 GMT
+        assertEquals(7200000L, tz.getOffset(firstTransitionTimeMillis));
+        assertTrue(tz.inDaylightTime(new Date(firstTransitionTimeMillis)));
+
+        long beforeFirstTransitionTimeMillis = firstTransitionTimeMillis - 1;
+        assertEquals(3600000L, tz.getOffset(beforeFirstTransitionTimeMillis));
+        assertFalse(tz.inDaylightTime(new Date(beforeFirstTransitionTimeMillis)));
+    }
+
+    // http://code.google.com/p/android/issues/detail?id=14395
+    public void testPreHistoricInDaylightTime_old() throws Exception {
+        // Originally this test was intended to assert what happens when the first transition for a
+        // time zone was a "to DST" transition. i.e. that the (implicit) offset / DST state before
+        // the first was treated as a non-DST state. Since zic version 2014c some zones have an
+        // explicit non-DST transition at time -2^31 seconds so it is no longer possible to test
+        // this with America/Los_Angeles.
+        // This regression test has been kept in case that changes again in future and to prove the
+        // behavior has remained consistent.
+
         Locale.setDefault(Locale.US);
         TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
         TimeZone.setDefault(tz);
@@ -69,7 +92,7 @@
         assertFalse(tz.inDaylightTime(date));
         assertEquals("Fri Oct 31 08:00:00 PST 1902", date.toString());
         assertEquals("31 Oct 1902 16:00:00 GMT", date.toGMTString());
-        // Any time before we have transition data is considered non-daylight, even in summer.
+        // For zic versions <= 2014b, this would be before the first transition.
         date = sdf.parse("1902-06-01T00:00:00.000+0800");
         assertEquals(-28800000, tz.getOffset(date.getTime()));
         assertFalse(tz.inDaylightTime(date));
@@ -84,17 +107,20 @@
     public void testPreHistoricOffsets() throws Exception {
         // Note: This test changed after P to account for previously incorrect handling of
         // prehistoric offsets. http://b/118835133
-        // "Africa/Bissau" has just a few transitions:
-        // Date, Offset, IsDst
-        // 1901-12-13 20:45:52,-3740,0 (Integer.MIN_VALUE, implicit with zic <= 2014b)
-        // 1912-01-01 01:00:00,-3600,0
-        // 1975-01-01 01:00:00,0,0
+        // "Africa/Bissau" has just a few known transitions:
+        // Transition time             : Offset    : DST / non-DST
+        // <Integer.MIN_VALUE secs>[1] : -01:02:20 : non-DST
+        // 1912-01-01 01:00:00 GMT     : -01:00:00 : non-DST
+        // 1975-01-01 01:00:00 GMT     :  00:00:00 : non-DST
+        //
+        // [1] This transition can be implicit or explicit depending on the version of zic used to
+        // generate the data. When implicit, the first non-DST type defn should be used.
         TimeZone tz = TimeZone.getTimeZone("Africa/Bissau");
 
-        // Before Integer.MIN_VALUE.
+        // Times before Integer.MIN_VALUE should assume we're using the first non-DST type.
         assertNonDaylightOffset(-3740, parseIsoTime("1900-01-01T00:00:00.0+0000"), tz);
 
-        // Times before 1912-01-01 01:00:00
+        // Time before 1912-01-01 01:00:00 but after Integer.MIN_VALUE.
         assertNonDaylightOffset(-3740, parseIsoTime("1911-01-01T00:00:00.0+0000"), tz);
 
         // Times after 1912-01-01 01:00:00 should use that transition.
@@ -104,19 +130,22 @@
         assertNonDaylightOffset(0, parseIsoTime("1980-01-01T00:00:00.0+0000"), tz);
     }
 
-    private static void assertNonDaylightOffset(int expectedOffsetSeconds, long epochSeconds, TimeZone tz) {
-        assertEquals(expectedOffsetSeconds, tz.getOffset(epochSeconds * 1000) / 1000);
-        assertFalse(tz.inDaylightTime(new Date(epochSeconds * 1000)));
+    private static void assertNonDaylightOffset(
+            int expectedOffsetSeconds, long epochMillis, TimeZone tz) {
+        assertEquals(expectedOffsetSeconds, tz.getOffset(epochMillis) / 1000);
+        assertFalse(tz.inDaylightTime(new Date(epochMillis)));
     }
 
+    /** Returns the millis elapsed since the beginning of the Unix epoch. */
     private static long parseIsoTime(String isoTime) throws Exception {
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
         Date date = sdf.parse(isoTime);
-        return date.getTime() / 1000;
+        return date.getTime();
     }
 
-    public void testZeroTransitionZones() throws Exception {
-        // Zones with no transitions historical or future seem ideal for testing.
+    public void testMinimalTransitionZones() throws Exception {
+        // Zones with minimal transitions, historical or future, seem ideal for testing.
+        // UTC is also included, although it may be implemented differently from the others.
         String[] ids = new String[] { "Africa/Bujumbura", "Indian/Cocos", "Pacific/Wake", "UTC" };
         for (String id : ids) {
             TimeZone tz = TimeZone.getTimeZone(id);
@@ -338,8 +367,7 @@
         final long lowerTimeMillis = beforeInt32Seconds * 1000L;
         final long upperTimeMillis = afterInt32Seconds * 1000L;
 
-        // This timezone didn't have any daylight savings prior to 1917 and this
-        // date is in 1900.
+        // This timezone didn't have any daylight savings prior to 1917 and this date is in 1900.
         assertFalse(tz.inDaylightTime(new Date(lowerTimeMillis)));
 
         // http://b/118835133:
@@ -347,7 +375,7 @@
         // 07:00:00 GMT) the offset was -18000000.
         // zic > 2014b produces data that suggests before Integer.MIN_VALUE seconds the offset was
         // -17762000 and between Integer.MIN_VALUE and -1633280400 it was -18000000. Once Android
-        // moves to zic > 2014b the -18000000 can be removed.
+        // moves to zic > 2014b the -18000000 can be removed. http://b/73719425
         int actualOffset = tz.getOffset(lowerTimeMillis);
         assertTrue(-18000000 == actualOffset || -17762000 == actualOffset);
 
diff --git a/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java b/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java
index 4de1a01..800928f 100644
--- a/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java
+++ b/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java
@@ -18,28 +18,69 @@
 
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.Locale;
+
 import junit.framework.TestCase;
 import static libcore.util.HexEncoding.decode;
 import static libcore.util.HexEncoding.encode;
+import static libcore.util.HexEncoding.encodeToString;
 
 public class HexEncodingTest extends TestCase {
-  public void testEncode() {
-    final byte[] avocados = "avocados".getBytes(StandardCharsets.UTF_8);
 
-    assertArraysEqual("61766F6361646F73".toCharArray(), encode(avocados));
-    assertArraysEqual(avocados, decode(encode(avocados), false));
-    // Make sure we can handle lower case hex encodings as well.
-    assertArraysEqual(avocados, decode("61766f6361646f73".toCharArray(), false));
+  public void testEncodeByte() {
+    Object[][] testCases = new Object[][] {
+        { 0x01, "01" },
+        { 0x09, "09" },
+        { 0x0A, "0A" },
+        { 0x0F, "0F" },
+        { 0x10, "10" },
+        { 0x1F, "1F" },
+        { 0x20, "20" },
+        { 0x7F, "7F" },
+        { 0x80, "80" },
+        { 0xFF, "FF" },
+    };
+    for (Object[] testCase : testCases) {
+      Number toEncode = (Number) testCase[0];
+      String expected = (String) testCase[1];
+
+      String actualUpper = encodeToString(toEncode.byteValue(), true /* upperCase */);
+      assertEquals(upper(expected), actualUpper);
+
+      String actualLower = encodeToString(toEncode.byteValue(), false /* upperCase */);
+      assertEquals(lower(expected), actualLower);
+    }
+  }
+
+  public void testEncodeBytes() {
+    Object[][] testCases = new Object[][] {
+        { "avocados".getBytes(StandardCharsets.UTF_8), "61766F6361646F73" },
+    };
+
+    for (Object[] testCase : testCases) {
+      byte[] bytes = (byte[]) testCase[0];
+      String encodedLower = lower((String) testCase[1]);
+      String encodedUpper = upper((String) testCase[1]);
+
+      assertArraysEqual(encodedUpper.toCharArray(), encode(bytes));
+      assertArraysEqual(encodedUpper.toCharArray(), encode(bytes, true /* upperCase */));
+      assertArraysEqual(encodedLower.toCharArray(), encode(bytes, false /* upperCase */));
+
+      assertArraysEqual(bytes, decode(encode(bytes), false /* allowSingleChar */));
+
+      // Make sure we can handle lower case hex encodings as well.
+      assertArraysEqual(bytes, decode(encodedLower.toCharArray(), false /* allowSingleChar */));
+    }
   }
 
   public void testDecode_allow4Bit() {
     assertArraysEqual(new byte[] { 6 }, decode("6".toCharArray(), true));
-    assertArraysEqual(new byte[] { 6, 'v' }, decode("676".toCharArray(), true));
+    assertArraysEqual(new byte[] { 6, 0x76 }, decode("676".toCharArray(), true));
   }
 
   public void testDecode_disallow4Bit() {
     try {
-      decode("676".toCharArray(), false);
+      decode("676".toCharArray(), false /* allowSingleChar */);
       fail();
     } catch (IllegalArgumentException expected) {
     }
@@ -47,7 +88,7 @@
 
   public void testDecode_invalid() {
     try {
-      decode("DEADBARD".toCharArray(), false);
+      decode("DEADBARD".toCharArray(), false /* allowSingleChar */);
       fail();
     } catch (IllegalArgumentException expected) {
     }
@@ -56,13 +97,13 @@
     // commons uses Character.isDigit and would successfully decode a string with
     // arabic and devanagari characters.
     try {
-      decode("६१٧٥٥F6361646F73".toCharArray(), false);
+      decode("६१٧٥٥F6361646F73".toCharArray(), false /* allowSingleChar */);
       fail();
     } catch (IllegalArgumentException expected) {
     }
 
     try {
-      decode("#%6361646F73".toCharArray(), false);
+      decode("#%6361646F73".toCharArray(), false /* allowSingleChar */);
       fail();
     } catch (IllegalArgumentException expected) {
     }
@@ -75,4 +116,12 @@
   private static void assertArraysEqual(byte[] lhs, byte[] rhs) {
     assertEquals(Arrays.toString(lhs), Arrays.toString(rhs));
   }
+
+  private static String lower(String string) {
+    return string.toLowerCase(Locale.ROOT);
+  }
+
+  private static String upper(String string) {
+    return string.toUpperCase(Locale.ROOT);
+  }
 }
diff --git a/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java b/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
index a151ee4..cddf41b 100644
--- a/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
+++ b/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
@@ -322,6 +322,7 @@
    * <p>Newer versions of zic after 2014b introduce an explicit transition at the earliest
    * representable time, which is Integer.MIN_VALUE for TZif version 1 files. Previously the type
    * used was left implicit and readers were expected to use the first non-DST type in the file.
+   * This extra transition mostly went away again with zic 2018f.
    *
    * <p>Testing newer zic versions demonstrated that Android had been mishandling the lookup of
    * offset for times before the first transition. The logic has been corrected. This test would
@@ -357,8 +358,8 @@
             { offsetToSeconds(type2Offset), 0 },
     };
 
-    // Creates a simulation of zic version <= 2014b where there is usually no explicit transition at
-    // Integer.MIN_VALUE seconds in TZif version 1 data.
+    // Creates a simulation of zic version <= 2014b or zic version >= 2018f where there is often
+    // no explicit transition at Integer.MIN_VALUE seconds in TZif version 1 data.
     {
       int[][] transitions = {
               { timeToSeconds(firstRealTransitionTime), 2 /* type 2 */ },
@@ -374,8 +375,8 @@
       assertOffsetAt(oldZoneInfo, type2Offset, afterFirstRealTransitionTimes);
     }
 
-    // Creates a simulation of zic version > 2014b where there is usually an explicit transition at
-    // Integer.MIN_VALUE seconds for TZif version 1 data.
+    // Creates a simulation of zic version > 2014b and zic version < 2018f where there is usually an
+    // explicit transition at Integer.MIN_VALUE seconds for TZif version 1 data.
     {
       int[][] transitions = {
               { Integer.MIN_VALUE, 1 /* type 1 */ }, // The extra transition added by zic.
diff --git a/mmodules/core_platform_api/api/platform/current-api.txt b/mmodules/core_platform_api/api/platform/current-api.txt
index a304cf8..c0c2c74 100644
--- a/mmodules/core_platform_api/api/platform/current-api.txt
+++ b/mmodules/core_platform_api/api/platform/current-api.txt
@@ -1231,8 +1231,11 @@
     method public static byte[] decode(char[]) throws java.lang.IllegalArgumentException;
     method public static byte[] decode(char[], boolean) throws java.lang.IllegalArgumentException;
     method public static char[] encode(byte[]);
+    method public static char[] encode(byte[], boolean);
     method public static char[] encode(byte[], int, int);
+    method public static String encodeToString(byte, boolean);
     method public static String encodeToString(byte[]);
+    method public static String encodeToString(byte[], boolean);
   }
 
   public class NativeAllocationRegistry {
diff --git a/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java b/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java
index c569981..67507bc 100644
--- a/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java
+++ b/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java
@@ -1574,14 +1574,17 @@
             zoneOffsets = new int[2];
         }
         if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
-            // Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
+            // BEGIN Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
             // if (tz instanceof ZoneInfo) {
             //     zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
-            // } else {
+            if (tz instanceof libcore.util.ZoneInfo) {
+                zoneOffset = ((libcore.util.ZoneInfo)tz).getOffsetsByUtcTime(time, zoneOffsets);
+            // END Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
+            } else {
                 zoneOffset = tz.getOffset(time);
                 zoneOffsets[0] = tz.getRawOffset();
                 zoneOffsets[1] = zoneOffset - zoneOffsets[0];
-            // }
+            }
         }
         if (tzMask != 0) {
             if (isFieldSet(tzMask, ZONE_OFFSET)) {
diff --git a/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java b/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
index 143fa5e..b71d11a 100644
--- a/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
+++ b/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
@@ -188,7 +188,6 @@
      */
     private static class NoPreloadHolder {
         public static HostnameVerifier defaultHostnameVerifier;
-        public static final Class<? extends HostnameVerifier> originalDefaultHostnameVerifierClass;
         static {
             try {
                 /**
@@ -200,7 +199,6 @@
                 defaultHostnameVerifier = (HostnameVerifier)
                         Class.forName("com.android.okhttp.internal.tls.OkHostnameVerifier")
                         .getField("INSTANCE").get(null);
-                originalDefaultHostnameVerifierClass = defaultHostnameVerifier.getClass();
             } catch (Exception e) {
                 throw new AssertionError("Failed to obtain okhttp HostnameVerifier", e);
             }
@@ -210,7 +208,7 @@
     /**
      * The <code>hostnameVerifier</code> for this object.
      */
-    protected HostnameVerifier hostnameVerifier;
+    protected HostnameVerifier hostnameVerifier = NoPreloadHolder.defaultHostnameVerifier;
     // END Android-changed: Use holder class idiom for a lazily-created OkHttp hostname verifier.
 
     // Android-changed: Modified the documentation to explain side effects / discourage method use.
@@ -329,15 +327,6 @@
      * @see #setDefaultHostnameVerifier(HostnameVerifier)
      */
     public HostnameVerifier getHostnameVerifier() {
-        // Android-added: Use the default verifier if none is set.
-        // Note that this also has the side effect of *setting* (if unset)
-        // hostnameVerifier to be the default one. It's not clear why this
-        // was done (commit abd00f0eaa46f71f98e75a631c268c812d1ec7c1) but
-        // we're keeping this behavior for lack of a strong reason to do
-        // otherwise.
-        if (hostnameVerifier == null) {
-            hostnameVerifier = NoPreloadHolder.defaultHostnameVerifier;
-        }
         return hostnameVerifier;
     }
 
diff --git a/ojluni/src/main/java/sun/util/calendar/AbstractCalendar.java b/ojluni/src/main/java/sun/util/calendar/AbstractCalendar.java
index ead18de..28b10c8 100644
--- a/ojluni/src/main/java/sun/util/calendar/AbstractCalendar.java
+++ b/ojluni/src/main/java/sun/util/calendar/AbstractCalendar.java
@@ -126,12 +126,14 @@
             // BEGIN Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
             // if (zi instanceof ZoneInfo) {
             //    zoneOffset = ((ZoneInfo)zi).getOffsets(millis, offsets);
-            // } else {
+            if (zi instanceof libcore.util.ZoneInfo) {
+                zoneOffset = ((libcore.util.ZoneInfo) zi).getOffsetsByUtcTime(millis, offsets);
+            // END Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
+            } else {
                 zoneOffset = zi.getOffset(millis);
                 offsets[0] = zi.getRawOffset();
                 offsets[1] = zoneOffset - offsets[0];
-            // }
-            // END Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
+            }
 
             // We need to calculate the given millis and time zone
             // offset separately for java.util.GregorianCalendar
diff --git a/support/src/test/java/libcore/testing/io/TestIoUtils.java b/support/src/test/java/libcore/testing/io/TestIoUtils.java
index 8e241df..34a2cf7 100644
--- a/support/src/test/java/libcore/testing/io/TestIoUtils.java
+++ b/support/src/test/java/libcore/testing/io/TestIoUtils.java
@@ -17,6 +17,10 @@
 package libcore.testing.io;
 
 import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.Random;
 
 public class TestIoUtils {
@@ -25,6 +29,13 @@
     private TestIoUtils() {}
 
     /**
+     * Returns the contents of 'path' as a string. The contents are assumed to be UTF-8.
+     */
+    public static String readFileAsString(String absolutePath) throws IOException {
+        return new String(Files.readAllBytes(Paths.get(absolutePath)), StandardCharsets.UTF_8);
+    }
+
+    /**
      * Creates a unique new temporary directory under "java.io.tmpdir".
      */
     public static File createTemporaryDirectory(String prefix) {