Merge "Thread: Improve exception message for bad priorities."
diff --git a/JavaLibrary.mk b/JavaLibrary.mk
index 758b6ce..5955f27 100644
--- a/JavaLibrary.mk
+++ b/JavaLibrary.mk
@@ -91,7 +91,7 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_MODULE := core-all
-LOCAL_REQUIRED_MODULES := tzdata
+LOCAL_REQUIRED_MODULES := tzdata tzlookup.xml
 LOCAL_CORE_LIBRARY := true
 LOCAL_UNINSTALLABLE_MODULE := true
 include $(BUILD_JAVA_LIBRARY)
@@ -101,16 +101,13 @@
 LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
-# TODO(oth): Remove --min-sdk-version=26 when the O SDK version is determined.
-# For now it represents the minimum sdk version required for invoke-polymorphic.
-# This is only needed when ANDROID_COMPILE_WITH_JACK=false (b/36118520).
-LOCAL_DX_FLAGS := --core-library --min-sdk-version=26
+LOCAL_DX_FLAGS := --core-library
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_MODULE := core-oj
 LOCAL_JAVA_LIBRARIES := core-all
 LOCAL_NOTICE_FILE := $(LOCAL_PATH)/ojluni/NOTICE
-LOCAL_REQUIRED_MODULES := tzdata
+LOCAL_REQUIRED_MODULES := tzdata tzlookup.xml
 LOCAL_CORE_LIBRARY := true
 include $(BUILD_JAVA_LIBRARY)
 
@@ -132,7 +129,7 @@
 endif # EMMA_INSTRUMENT_STATIC
 endif # EMMA_INSTRUMENT
 LOCAL_CORE_LIBRARY := true
-LOCAL_REQUIRED_MODULES := tzdata
+LOCAL_REQUIRED_MODULES := tzdata tzlookup.xml
 include $(BUILD_JAVA_LIBRARY)
 
 # A library that exists to satisfy javac when
@@ -167,7 +164,7 @@
 LOCAL_MODULE := core-oj-testdex
 LOCAL_JAVA_LIBRARIES := core-all
 LOCAL_NOTICE_FILE := $(LOCAL_PATH)/ojluni/NOTICE
-LOCAL_REQUIRED_MODULES := tzdata
+LOCAL_REQUIRED_MODULES := tzdata tzlookup.xml
 LOCAL_CORE_LIBRARY := true
 include $(BUILD_JAVA_LIBRARY)
 
@@ -201,7 +198,7 @@
 LOCAL_MODULE := core-libart-testdex
 LOCAL_JAVA_LIBRARIES := core-all
 LOCAL_CORE_LIBRARY := true
-LOCAL_REQUIRED_MODULES := tzdata
+LOCAL_REQUIRED_MODULES := tzdata tzlookup.xml
 include $(BUILD_JAVA_LIBRARY)
 endif
 
@@ -330,7 +327,7 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_MODULE := core-all-hostdex
-LOCAL_REQUIRED_MODULES := tzdata-host
+LOCAL_REQUIRED_MODULES := tzdata-host tzlookup.xml-host
 LOCAL_CORE_LIBRARY := true
 LOCAL_UNINSTALLABLE_MODULE := true
 include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
@@ -346,7 +343,7 @@
 LOCAL_MODULE := core-oj-hostdex
 LOCAL_NOTICE_FILE := $(LOCAL_PATH)/ojluni/NOTICE
 LOCAL_JAVA_LIBRARIES := core-all-hostdex
-LOCAL_REQUIRED_MODULES := tzdata-host
+LOCAL_REQUIRED_MODULES := tzdata-host tzlookup.xml-host
 LOCAL_CORE_LIBRARY := true
 include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
 
@@ -361,7 +358,7 @@
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_MODULE := core-libart-hostdex
 LOCAL_JAVA_LIBRARIES := core-oj-hostdex
-LOCAL_REQUIRED_MODULES := tzdata-host
+LOCAL_REQUIRED_MODULES := tzdata-host tzlookup.xml-host
 include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
 
 # A library that exists to satisfy javac when
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..72d7fb4
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+nfuller@google.com
+pszczepaniak@google.com
+android-libcore-team+review@google.com
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index e9537fb..58a4337 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1529,5 +1529,13 @@
     "com.google.security.wycheproof.EcdhTest#testModifiedPublic",
     "com.google.security.wycheproof.EcdhTest#testModifiedPublicSpec"
   ]
+},
+{
+  description: "Bullhead kernel does not block send when buffer is supposed to have saturated",
+  bug: 36691333,
+  result: EXEC_FAILED,
+  names: [
+    "libcore.java.net.SocketTimeoutTest#testSocketWriteNeverTimeouts"
+  ]
 }
 ]
diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
index adad301..20a4a47 100644
--- a/luni/src/main/java/android/system/OsConstants.java
+++ b/luni/src/main/java/android/system/OsConstants.java
@@ -521,6 +521,9 @@
     public static final int TCP_NODELAY = placeholder();
     public static final int TCP_USER_TIMEOUT = placeholder();
     /** @hide */ public static final int TIOCOUTQ = placeholder();
+    /** @hide */ public static final int UDP_ENCAP = placeholder();
+    /** @hide */ public static final int UDP_ENCAP_ESPINUDP_NON_IKE = placeholder();
+    /** @hide */ public static final int UDP_ENCAP_ESPINUDP = placeholder();
     /** @hide */ public static final int UNIX_PATH_MAX = placeholder();
     public static final int WCONTINUED = placeholder();
     public static final int WEXITED = placeholder();
diff --git a/luni/src/main/java/libcore/icu/TimeZoneNames.java b/luni/src/main/java/libcore/icu/TimeZoneNames.java
index 917d9ce..68e6e30 100644
--- a/luni/src/main/java/libcore/icu/TimeZoneNames.java
+++ b/luni/src/main/java/libcore/icu/TimeZoneNames.java
@@ -43,15 +43,6 @@
     public static final int NAME_COUNT = 5;
 
     private static final ZoneStringsCache cachedZoneStrings = new ZoneStringsCache();
-    static {
-        // Ensure that we pull in the zone strings for the root locale, en_US, and the
-        // user's default locale. (All devices must support the root locale and en_US,
-        // and they're used for various system things like HTTP headers.) Pre-populating
-        // the cache is especially useful on Android because we'll share this via the Zygote.
-        cachedZoneStrings.get(Locale.ROOT);
-        cachedZoneStrings.get(Locale.US);
-        cachedZoneStrings.get(Locale.getDefault());
-    }
 
     public static class ZoneStringsCache extends BasicLruCache<Locale, String[][]> {
         public ZoneStringsCache() {
@@ -71,6 +62,7 @@
             fillZoneStrings(locale.toLanguageTag(), result);
             long nativeEnd = System.nanoTime();
 
+            addOffsetStrings(result);
             internStrings(result);
             // Ending up in this method too often is an easy way to make your app slow, so we ensure
             // it's easy to tell from the log (a) what we were doing, (b) how long it took, and
@@ -83,8 +75,33 @@
             return result;
         }
 
+        /**
+         * Generate offset strings for cases where we don't have a name. Note that this is a
+         * potentially slow operation, as we need to load the timezone data for all affected
+         * time zones.
+         */
+        private void addOffsetStrings(String[][] result) {
+            for (int i = 0; i < result.length; ++i) {
+                TimeZone tz = null;
+                for (int j = 1; j < NAME_COUNT; ++j) {
+                    if (result[i][j] != null) {
+                        continue;
+                    }
+                    if (tz == null) {
+                        tz = TimeZone.getTimeZone(result[i][0]);
+                    }
+                    int offsetMillis = tz.getRawOffset();
+                    if (j == LONG_NAME_DST || j == SHORT_NAME_DST) {
+                        offsetMillis += tz.getDSTSavings();
+                    }
+                    result[i][j] = TimeZone.createGmtOffsetString(
+                            /* includeGmt */ true, /*includeMinuteSeparator */true, offsetMillis);
+                }
+            }
+        }
+
         // De-duplicate the strings (http://b/2672057).
-        private synchronized void internStrings(String[][] result) {
+        private void internStrings(String[][] result) {
             HashMap<String, String> internTable = new HashMap<String, String>();
             for (int i = 0; i < result.length; ++i) {
                 for (int j = 1; j < NAME_COUNT; ++j) {
diff --git a/luni/src/main/java/libcore/net/MimeUtils.java b/luni/src/main/java/libcore/net/MimeUtils.java
index b746273..693b6da 100644
--- a/luni/src/main/java/libcore/net/MimeUtils.java
+++ b/luni/src/main/java/libcore/net/MimeUtils.java
@@ -51,8 +51,7 @@
         add("application/mathematica", "nb");
         add("application/msaccess", "mdb");
         add("application/oda", "oda");
-        add("application/ogg", "ogg");
-        add("application/ogg", "oga");
+        add("application/ogg", "ogx");
         add("application/pdf", "pdf");
         add("application/pgp-keys", "key");
         add("application/pgp-signature", "pgp");
@@ -240,6 +239,9 @@
         add("audio/mpeg", "mp2");
         add("audio/mpeg", "m4a");
         add("audio/mpegurl", "m3u");
+        add("audio/ogg", "oga");
+        add("audio/ogg", "ogg");
+        add("audio/ogg", "spx");
         add("audio/prs.sid", "sid");
         add("audio/x-aiff", "aif");
         add("audio/x-aiff", "aiff");
@@ -372,6 +374,7 @@
         add("video/mpeg", "mpe");
         add("video/mp4", "mp4");
         add("video/mpeg", "VOB");
+        add("video/ogg", "ogv");
         add("video/quicktime", "qt");
         add("video/quicktime", "mov");
         add("video/vnd.mpegurl", "mxu");
diff --git a/luni/src/main/java/libcore/util/TimeZoneDataFiles.java b/luni/src/main/java/libcore/util/TimeZoneDataFiles.java
index 8361339..c792665 100644
--- a/luni/src/main/java/libcore/util/TimeZoneDataFiles.java
+++ b/luni/src/main/java/libcore/util/TimeZoneDataFiles.java
@@ -26,6 +26,14 @@
 
     private TimeZoneDataFiles() {}
 
+    /**
+     * Returns two time zone file paths for the specified file name in an array in the order they
+     * should be tried. See {@link #generateIcuDataPath()} for ICU files instead.
+     * <ul>
+     * <li>[0] - the location of the file in the /data partition (may not exist).</li>
+     * <li>[1] - the location of the file in the /system partition (should exist).</li>
+     * </ul>
+     */
     // VisibleForTesting
     public static String[] getTimeZoneFilePaths(String fileName) {
         return new String[] {
diff --git a/luni/src/main/java/libcore/util/TimeZoneFinder.java b/luni/src/main/java/libcore/util/TimeZoneFinder.java
new file mode 100644
index 0000000..4e47df4
--- /dev/null
+++ b/luni/src/main/java/libcore/util/TimeZoneFinder.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.util;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import android.icu.util.TimeZone;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A structure that can find matching time zones.
+ */
+public class TimeZoneFinder {
+
+    private static final String TZLOOKUP_FILE_NAME = "tzlookup.xml";
+    private static final String TIMEZONES_ELEMENT = "timezones";
+    private static final String COUNTRY_ZONES_ELEMENT = "countryzones";
+    private static final String COUNTRY_ELEMENT = "country";
+    private static final String COUNTRY_CODE_ATTRIBUTE = "code";
+    private static final String ID_ELEMENT = "id";
+
+    private static TimeZoneFinder instance;
+
+    private final ReaderSupplier xmlSource;
+
+    // Cached fields for the last country looked up.
+    private String lastCountryIso;
+    private List<TimeZone> lastCountryTimeZones;
+
+    private TimeZoneFinder(ReaderSupplier xmlSource) {
+        this.xmlSource = xmlSource;
+    }
+
+    /**
+     * Obtains an instance for use when resolving time zones. This method handles using the correct
+     * file when there are several to choose from. This method never returns {@code null}. No
+     * in-depth validation is performed on the file content, see {@link #validate()}.
+     */
+    public static TimeZoneFinder getInstance() {
+        synchronized(TimeZoneFinder.class) {
+            if (instance == null) {
+                String[] tzLookupFilePaths =
+                        TimeZoneDataFiles.getTimeZoneFilePaths(TZLOOKUP_FILE_NAME);
+                instance = createInstanceWithFallback(tzLookupFilePaths[0], tzLookupFilePaths[1]);
+            }
+        }
+        return instance;
+    }
+
+    // VisibleForTesting
+    public static TimeZoneFinder createInstanceWithFallback(String... tzLookupFilePaths) {
+        for (String tzLookupFilePath : tzLookupFilePaths) {
+            try {
+                // We assume that any file in /data was validated before install, and the system
+                // file was validated before the device shipped. Therefore, we do not pay the
+                // validation cost here.
+                return createInstance(tzLookupFilePath);
+            } catch (IOException e) {
+                System.logE("Unable to process file: " + tzLookupFilePath + " Trying next one.", e);
+            }
+        }
+
+        System.logE("No valid file found in set: " + Arrays.toString(tzLookupFilePaths)
+                + " Falling back to empty map.");
+        return createInstanceForTests("<timezones><countryzones /></timezones>");
+    }
+
+    /**
+     * Obtains an instance using a specific data file, throwing an IOException if the file does not
+     * exist or is not readable. This method never returns {@code null}. No in-depth validation is
+     * performed on the file content, see {@link #validate()}.
+     */
+    public static TimeZoneFinder createInstance(String path) throws IOException {
+        ReaderSupplier xmlSupplier = ReaderSupplier.forFile(path, StandardCharsets.UTF_8);
+        return new TimeZoneFinder(xmlSupplier);
+    }
+
+    /** Used to create an instance using an in-memory XML String instead of a file. */
+    // VisibleForTesting
+    public static TimeZoneFinder createInstanceForTests(String xml) {
+        return new TimeZoneFinder(ReaderSupplier.forString(xml));
+    }
+
+    /**
+     * Parses the data file, throws an exception if it is invalid or cannot be read.
+     */
+    public void validate() throws IOException {
+        try {
+            processXml(new CountryZonesValidator());
+        } catch (XmlPullParserException e) {
+            throw new IOException("Parsing error", e);
+        }
+    }
+
+    /**
+     * Return a time zone that has / would have had the specified offset and DST value at the
+     * specified moment in the specified country.
+     *
+     * <p>In order to be considered a configured zone must match the supplied offset information.
+     *
+     * <p>Matches are considered in a well-defined order. If multiple zones match and one of them
+     * also matches the (optional) bias parameter then the bias time zone will be returned.
+     * Otherwise the first match found is returned.
+     */
+    public TimeZone lookupTimeZoneByCountryAndOffset(
+            String countryIso, int offsetSeconds, boolean isDst, long whenMillis, TimeZone bias) {
+
+        List<TimeZone> candidates = lookupTimeZonesByCountry(countryIso);
+        if (candidates == null || candidates.isEmpty()) {
+            return null;
+        }
+
+        TimeZone firstMatch = null;
+        for (int i = 0; i < candidates.size(); i++) {
+            TimeZone match = candidates.get(i);
+            if (!offsetMatchesAtTime(match, offsetSeconds, isDst, whenMillis)) {
+                continue;
+            }
+
+            if (firstMatch == null) {
+                if (bias == null) {
+                    // No bias, so we can stop at the first match.
+                    return match;
+                }
+                // We have to carry on checking in case the bias matches. We want to return the
+                // first if it doesn't, though.
+                firstMatch = match;
+            }
+
+            // Check if match is also the bias. There must be a bias otherwise we'd have terminated
+            // already.
+            if (match.getID().equals(bias.getID())) {
+                return match;
+            }
+        }
+        // Return firstMatch, which can be null if there was no match.
+        return firstMatch;
+    }
+
+    /**
+     * Returns {@code true} if the specified offset, DST state and time would be valid in the
+     * timeZone.
+     */
+    private static boolean offsetMatchesAtTime(TimeZone timeZone, int offsetMillis, boolean isDst,
+            long whenMillis) {
+        int[] offsets = new int[2];
+        timeZone.getOffset(whenMillis, false /* local */, offsets);
+
+        // offsets[1] == 0 when the zone is not in DST.
+        boolean zoneIsDst = offsets[1] != 0;
+        if (isDst != zoneIsDst) {
+            return false;
+        }
+        return offsetMillis == (offsets[0] + offsets[1]);
+    }
+
+    /**
+     * Returns a list of time zones known to be used in the specified country. If the country code
+     * is not recognized or there is an error during lookup this can return null. The TimeZones
+     * returned will never contain {@link TimeZone#UNKNOWN_ZONE}. This method can return an empty
+     * list in a case when the underlying configuration references only unknown zone IDs.
+     */
+    public List<TimeZone> lookupTimeZonesByCountry(String countryIso) {
+        synchronized(this) {
+            if (countryIso.equals(lastCountryIso)) {
+                return lastCountryTimeZones;
+            }
+        }
+
+        CountryZonesExtractor extractor = new CountryZonesExtractor(countryIso);
+        List<TimeZone> countryTimeZones = null;
+        try {
+            processXml(extractor);
+            countryTimeZones = extractor.getMatchedZones();
+        } catch (IOException e) {
+            System.logW("Error reading country zones ", e);
+
+            // Clear the cached code so we will try again next time.
+            countryIso = null;
+        } catch (XmlPullParserException e) {
+            System.logW("Error reading country zones ", e);
+            // We want to cache the null. This won't get better over time.
+        }
+
+        synchronized(this) {
+            lastCountryIso = countryIso;
+            lastCountryTimeZones = countryTimeZones;
+        }
+        return countryTimeZones;
+    }
+
+    /**
+     * Processes the XML, applying the {@link CountryZonesProcessor} to the &lt;countryzones&gt;
+     * element. Processing can terminate early if the
+     * {@link CountryZonesProcessor#process(String, List, String)} returns
+     * {@link CountryZonesProcessor#HALT} or it throws an exception.
+     */
+    private void processXml(CountryZonesProcessor processor)
+            throws XmlPullParserException, IOException {
+        try (Reader reader = xmlSource.get()) {
+            XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory.newInstance();
+            xmlPullParserFactory.setNamespaceAware(false);
+
+            XmlPullParser parser = xmlPullParserFactory.newPullParser();
+            parser.setInput(reader);
+
+            /*
+             * The expected XML structure is:
+             * <timezones>
+             *   <countryzones>
+             *     <country code="us">
+             *       <id>America/New_York"</id>
+             *       ...
+             *       <id>America/Los_Angeles</id>
+             *     </country>
+             *     <country code="gb">
+             *       <id>Europe/London</id>
+             *     </country>
+             *   </countryzones>
+             * </timezones>
+             */
+
+            findRequiredStartTag(parser, TIMEZONES_ELEMENT);
+
+            // There is only one expected sub-element <countryzones> in the format currently, skip
+            // over anything before it.
+            findRequiredStartTag(parser, COUNTRY_ZONES_ELEMENT);
+
+            if (processCountryZones(parser, processor) == CountryZonesProcessor.HALT) {
+                return;
+            }
+
+            // Make sure we are on the </countryzones> tag.
+            checkOnEndTag(parser, COUNTRY_ZONES_ELEMENT);
+
+            // Advance to the next tag.
+            parser.next();
+
+            // Skip anything until </timezones>, and make sure the file is not truncated and we can
+            // find the end.
+            consumeUntilEndTag(parser, TIMEZONES_ELEMENT);
+
+            // Make sure we are on the </timezones> tag.
+            checkOnEndTag(parser, TIMEZONES_ELEMENT);
+        }
+    }
+
+    private static boolean processCountryZones(XmlPullParser parser,
+            CountryZonesProcessor processor) throws IOException, XmlPullParserException {
+
+        // Skip over any unexpected elements and process <country> elements.
+        while (findOptionalStartTag(parser, COUNTRY_ELEMENT)) {
+            if (processor == null) {
+                consumeUntilEndTag(parser, COUNTRY_ELEMENT);
+            } else {
+                String code = parser.getAttributeValue(
+                        null /* namespace */, COUNTRY_CODE_ATTRIBUTE);
+                if (code == null || code.isEmpty()) {
+                    throw new XmlPullParserException(
+                            "Unable to find country code: " + parser.getPositionDescription());
+                }
+
+                String debugInfo = parser.getPositionDescription();
+                List<String> timeZoneIds = parseZoneIds(parser);
+                if (processor.process(code, timeZoneIds, debugInfo)
+                        == CountryZonesProcessor.HALT) {
+                    return CountryZonesProcessor.HALT;
+                }
+            }
+
+            // Make sure we are on the </country> element.
+            checkOnEndTag(parser, COUNTRY_ELEMENT);
+        }
+
+        return CountryZonesExtractor.CONTINUE;
+    }
+
+    private static List<String> parseZoneIds(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        List<String> timeZones = new ArrayList<>();
+
+        // Skip over any unexpected elements and process <id> elements.
+        while (findOptionalStartTag(parser, ID_ELEMENT)) {
+            String zoneIdString = consumeText(parser);
+
+            // Make sure we are on the </id> element.
+            checkOnEndTag(parser, ID_ELEMENT);
+
+            // Process the zone ID.
+            timeZones.add(zoneIdString);
+        }
+
+        // The list is made unmodifiable to avoid callers changing it.
+        return Collections.unmodifiableList(timeZones);
+    }
+
+    private static void findRequiredStartTag(XmlPullParser parser, String elementName)
+            throws IOException, XmlPullParserException {
+        findStartTag(parser, elementName, true /* elementRequired */);
+    }
+
+    /** Called when on a START_TAG. When returning false, it leaves the parser on the END_TAG. */
+    private static boolean findOptionalStartTag(XmlPullParser parser, String elementName)
+            throws IOException, XmlPullParserException {
+        return findStartTag(parser, elementName, false /* elementRequired */);
+    }
+
+    /**
+     * Find a START_TAG with the specified name without decreasing the depth, or increasing the
+     * depth by more than one. More deeply nested elements and text are skipped, even START_TAGs
+     * with matching names. Returns when the START_TAG is found or the next (non-nested) END_TAG is
+     * encountered. The return can take the form of an exception or a false if the START_TAG is not
+     * found. True is returned when it is.
+     */
+    private static boolean findStartTag(
+            XmlPullParser parser, String elementName, boolean elementRequired)
+            throws IOException, XmlPullParserException {
+
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            switch (type) {
+                case XmlPullParser.START_TAG:
+                    String currentElementName = parser.getName();
+                    if (elementName.equals(currentElementName)) {
+                        return true;
+                    }
+
+                    // It was not the START_TAG we were looking for. Consume until the end.
+                    parser.next();
+                    consumeUntilEndTag(parser, currentElementName);
+                    break;
+                case XmlPullParser.END_TAG:
+                    if (elementRequired) {
+                        throw new XmlPullParserException(
+                                "No child element found with name " + elementName);
+                    }
+                    return false;
+                default:
+                    // Ignore.
+                    break;
+            }
+        }
+        throw new XmlPullParserException("Unexpected end of document while looking for "
+                + elementName);
+    }
+
+    /**
+     * Consume the remaining contents of an element and move to the END_TAG. Used when processing
+     * within an element can stop. The parser must be pointing at either the END_TAG we are looking
+     * for, a TEXT, or a START_TAG nested within the element to be consumed.
+     */
+    private static void consumeUntilEndTag(XmlPullParser parser, String elementName)
+            throws IOException, XmlPullParserException {
+
+        if (parser.getEventType() == XmlPullParser.END_TAG
+                && elementName.equals(parser.getName())) {
+            // Early return - we are already there.
+            return;
+        }
+
+        // Keep track of the required depth in case there are nested elements to be consumed.
+        // Both the name and the depth must match our expectation to complete.
+
+        int requiredDepth = parser.getDepth();
+        // A TEXT tag would be at the same depth as the END_TAG we are looking for.
+        if (parser.getEventType() == XmlPullParser.START_TAG) {
+            // A START_TAG would have incremented the depth, so we're looking for an END_TAG one
+            // higher than the current tag.
+            requiredDepth--;
+        }
+
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            int type = parser.next();
+
+            int currentDepth = parser.getDepth();
+            if (currentDepth < requiredDepth) {
+                throw new XmlPullParserException(
+                        "Unexpected depth while looking for end tag: "
+                                + parser.getPositionDescription());
+            } else if (currentDepth == requiredDepth) {
+                if (type == XmlPullParser.END_TAG) {
+                    if (elementName.equals(parser.getName())) {
+                        return;
+                    }
+                    throw new XmlPullParserException(
+                            "Unexpected eng tag: " + parser.getPositionDescription());
+                }
+            }
+            // Everything else is either a type we are not interested in or is too deep and so is
+            // ignored.
+        }
+        throw new XmlPullParserException("Unexpected end of document");
+    }
+
+    /**
+     * Reads the text inside the current element. Should be called when the parser is currently
+     * on the START_TAG before the TEXT. The parser will be positioned on the END_TAG after this
+     * call when it completes successfully.
+     */
+    private static String consumeText(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+
+        int type = parser.next();
+        String text;
+        if (type == XmlPullParser.TEXT) {
+            text = parser.getText();
+        } else {
+            throw new XmlPullParserException("Text not found. Found type=" + type
+                    + " at " + parser.getPositionDescription());
+        }
+
+        type = parser.next();
+        if (type != XmlPullParser.END_TAG) {
+            throw new XmlPullParserException(
+                    "Unexpected nested tag or end of document when expecting text: type=" + type
+                            + " at " + parser.getPositionDescription());
+        }
+        return text;
+    }
+
+    private static void checkOnEndTag(XmlPullParser parser, String elementName)
+            throws XmlPullParserException {
+        if (!(parser.getEventType() == XmlPullParser.END_TAG
+                && parser.getName().equals(elementName))) {
+            throw new XmlPullParserException(
+                    "Unexpected tag encountered: " + parser.getPositionDescription());
+        }
+    }
+
+    /**
+     * Processes &lt;countryzones&gt; data.
+     */
+    private interface CountryZonesProcessor {
+
+        boolean CONTINUE = true;
+        boolean HALT = false;
+
+        /**
+         * Returns {@code #CONTINUE} if processing of the XML should continue, {@code HALT} if it
+         * should stop (but without considering this an error). Problems with parser are reported as
+         * an exception.
+         */
+        boolean process(String countryCode, List<String> timeZoneIds, String debugInfo)
+                throws XmlPullParserException;
+    }
+
+    /**
+     * Validates &lt;countryzones&gt; elements. To be valid the country ISO code must be unique
+     * and it must not be empty.
+     */
+    private static class CountryZonesValidator implements CountryZonesProcessor {
+
+        private final Set<String> knownCountryCodes = new HashSet<>();
+
+        @Override
+        public boolean process(String countryCode, List<String> timeZoneIds, String debugInfo)
+                throws XmlPullParserException {
+            if (knownCountryCodes.contains(countryCode)) {
+                throw new XmlPullParserException("Second entry for country code: " + countryCode
+                        + " at " + debugInfo);
+            }
+            if (timeZoneIds.isEmpty()) {
+                throw new XmlPullParserException("No time zone IDs for country code: " + countryCode
+                        + " at " + debugInfo);
+            }
+
+            // We don't validate the zone IDs - they may be new and we can't easily check them
+            // against other timezone data that may be associated with this file.
+
+            knownCountryCodes.add(countryCode);
+
+            return CONTINUE;
+        }
+    }
+
+    /**
+     * Extracts the zones associated with a country code, halting when the country code is matched
+     * and making them available via {@link #getMatchedZones()}.
+     */
+    private static class CountryZonesExtractor implements CountryZonesProcessor {
+
+        private final String countryCodeToMatch;
+        private List<TimeZone> matchedZones;
+
+        private CountryZonesExtractor(String countryCodeToMatch) {
+            this.countryCodeToMatch = countryCodeToMatch;
+        }
+
+        @Override
+        public boolean process(String countryCode, List<String> timeZoneIds, String debugInfo) {
+            if (!countryCodeToMatch.equals(countryCode)) {
+                return CONTINUE;
+            }
+
+            List<TimeZone> timeZones = new ArrayList<>();
+            for (String zoneIdString : timeZoneIds) {
+                TimeZone tz = TimeZone.getTimeZone(zoneIdString);
+                if (tz.getID().equals(TimeZone.UNKNOWN_ZONE_ID)) {
+                    System.logW("Skipping invalid zone: " + zoneIdString + " at " + debugInfo);
+                } else {
+                    // The zone is frozen to prevent mutation by callers.
+                    timeZones.add(tz.freeze());
+                }
+            }
+            matchedZones = Collections.unmodifiableList(timeZones);
+            return HALT;
+        }
+
+        /**
+         * Returns the matched zones, or {@code null} if there were no matches. Unknown zone IDs are
+         * ignored so the list can be empty if there were no zones or the zone IDs were not
+         * recognized.
+         */
+        List<TimeZone> getMatchedZones() {
+            return matchedZones;
+        }
+    }
+
+    /**
+     * A source of Readers that can be used repeatedly.
+     */
+    private interface ReaderSupplier {
+        /** Returns a Reader. Throws an IOException if the Reader cannot be created. */
+        Reader get() throws IOException;
+
+        static ReaderSupplier forFile(String fileName, Charset charSet) throws IOException {
+            Path file = Paths.get(fileName);
+            if (!Files.exists(file)) {
+                throw new FileNotFoundException(fileName + " does not exist");
+            }
+            if (!Files.isRegularFile(file) && Files.isReadable(file)) {
+                throw new IOException(fileName + " must be a regular readable file.");
+            }
+            return () -> Files.newBufferedReader(file, charSet);
+        }
+
+        static ReaderSupplier forString(String xml) {
+            return () -> new StringReader(xml);
+        }
+    }
+}
diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
index 3ae4af6..a517475 100644
--- a/luni/src/main/native/android_system_OsConstants.cpp
+++ b/luni/src/main/native/android_system_OsConstants.cpp
@@ -47,6 +47,9 @@
 #include <linux/if_addr.h>
 #include <linux/rtnetlink.h>
 
+// Include linux socket constants for setting sockopts
+#include <linux/udp.h>
+
 #include <net/if.h> // After <sys/socket.h> to work around a Mac header file bug.
 
 #if defined(__BIONIC__)
@@ -577,6 +580,9 @@
     initConstant(env, c, "TCP_USER_TIMEOUT", TCP_USER_TIMEOUT);
 #endif
     initConstant(env, c, "TIOCOUTQ", TIOCOUTQ);
+    initConstant(env, c, "UDP_ENCAP", UDP_ENCAP);
+    initConstant(env, c, "UDP_ENCAP_ESPINUDP_NON_IKE", UDP_ENCAP_ESPINUDP_NON_IKE);
+    initConstant(env, c, "UDP_ENCAP_ESPINUDP", UDP_ENCAP_ESPINUDP);
     // UNIX_PATH_MAX is mentioned in some versions of unix(7), but not actually declared.
     initConstant(env, c, "UNIX_PATH_MAX", sizeof(sockaddr_un::sun_path));
     initConstant(env, c, "WCONTINUED", WCONTINUED);
diff --git a/luni/src/main/native/libcore_icu_TimeZoneNames.cpp b/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
index d30e7a3..c4f25e0 100644
--- a/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
+++ b/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
@@ -46,10 +46,7 @@
 }
 
 static bool setStringArrayElement(JNIEnv* env, jobjectArray array, int i, const icu::UnicodeString& s) {
-  // Fill in whatever we got. We don't use the display names if they're "GMT[+-]xx:xx"
-  // because icu4c doesn't use the up-to-date time zone transition data, so it gets these
-  // wrong. TimeZone.getDisplayName creates accurate names on demand.
-  // TODO: investigate whether it's worth doing that work once in the Java wrapper instead of on-demand.
+  // Don't use "GMT" string, for backwards compatibility.
   static const icu::UnicodeString kGmt("GMT", 3, US_INV);
   if (!s.isBogus() && !s.startsWith(kGmt)) {
     ScopedLocalRef<jstring> javaString(env, env->NewString(s.getBuffer(), s.length()));
diff --git a/luni/src/test/java/libcore/java/lang/LambdaImplementationTest.java b/luni/src/test/java/libcore/java/lang/LambdaImplementationTest.java
index d32fa20..0161c6a 100644
--- a/luni/src/test/java/libcore/java/lang/LambdaImplementationTest.java
+++ b/luni/src/test/java/libcore/java/lang/LambdaImplementationTest.java
@@ -27,6 +27,7 @@
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -35,18 +36,22 @@
 
 public class LambdaImplementationTest extends TestCase {
 
-    private static final String MSG = "Hello World";
+    private static final String STATIC_METHOD_RESPONSE = "StaticMethodResponse";
 
     public void testNonCapturingLambda() throws Exception {
-        Callable<String> r1 = () -> MSG;
+        Callable<String> r1 = () -> "Hello World";
         assertGeneralLambdaClassCharacteristics(r1);
         assertLambdaImplementsInterfaces(r1, Callable.class);
         assertLambdaMethodCharacteristics(r1, Callable.class);
         assertNonSerializableLambdaCharacteristics(r1);
-        assertCallableBehavior(r1, MSG);
+        assertCallableBehavior(r1, "Hello World");
 
-        Callable<String> r2 = () -> MSG;
-        assertMultipleInstanceCharacteristics(r1, r2);
+        List<Callable<String>> callables = new ArrayList<>();
+        for (int i = 0; i < 2; i++) {
+            callables.add(() -> "Hello World");
+        }
+        assertMultipleDefinitionCharacteristics(r1, callables.get(0));
+        assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1));
     }
 
     interface Condition<T> {
@@ -83,65 +88,96 @@
         assertLambdaMethodCharacteristics(r1, Callable.class);
         assertNonSerializableLambdaCharacteristics(r1);
 
-        assertCallableBehavior(r1, MSG);
+        assertCallableBehavior(r1, STATIC_METHOD_RESPONSE);
 
-        Callable<String> r2 = LambdaImplementationTest::staticMethod;
-        assertMultipleInstanceCharacteristics(r1, r2);
+        List<Callable<String>> callables = new ArrayList<>();
+        for (int i = 0; i < 2; i++) {
+            callables.add(LambdaImplementationTest::staticMethod);
+        }
+        assertMultipleDefinitionCharacteristics(r1, callables.get(0));
+        assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1));
     }
 
     public void testObjectMethodReferenceLambda() throws Exception {
-        StringBuilder o = new StringBuilder(MSG);
+        String msg = "Hello";
+        StringBuilder o = new StringBuilder(msg);
         Callable<String> r1 = o::toString;
         assertGeneralLambdaClassCharacteristics(r1);
         assertLambdaImplementsInterfaces(r1, Callable.class);
         assertLambdaMethodCharacteristics(r1, Callable.class);
         assertNonSerializableLambdaCharacteristics(r1);
 
-        assertCallableBehavior(r1, MSG);
+        assertCallableBehavior(r1, msg);
 
-        Callable<String> r2 = o::toString;
-        assertMultipleInstanceCharacteristics(r1, r2);
+        List<Callable<String>> callables = new ArrayList<>();
+        for (int i = 0; i < 2; i++) {
+            callables.add(o::toString);
+        }
+        assertMultipleDefinitionCharacteristics(r1, callables.get(0));
+        assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1));
     }
 
     public void testArgumentCapturingLambda() throws Exception {
-        String msg = MSG;
+        checkArgumentCapturingLambda("Argument");
+    }
+
+    private void checkArgumentCapturingLambda(String msg) throws Exception {
         Callable<String> r1 = () -> msg;
         assertGeneralLambdaClassCharacteristics(r1);
         assertLambdaImplementsInterfaces(r1, Callable.class);
         assertLambdaMethodCharacteristics(r1, Callable.class);
         assertNonSerializableLambdaCharacteristics(r1);
 
-        assertCallableBehavior(r1, MSG);
+        assertCallableBehavior(r1, msg);
 
-        Callable<String> r2 = () -> msg;
-        assertMultipleInstanceCharacteristics(r1, r2);
+        List<Callable<String>> callables = new ArrayList<>();
+        for (int i = 0; i < 2; i++) {
+            callables.add(() -> msg);
+        }
+        assertMultipleDefinitionCharacteristics(r1, callables.get(0));
+        assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1));
     }
 
     public void testSerializableLambda_withoutState() throws Exception {
-        Callable<String> r1 = (Callable<String> & Serializable) () -> MSG;
+        Callable<String> r1 = (Callable<String> & Serializable) () -> "No State";
         assertGeneralLambdaClassCharacteristics(r1);
         assertLambdaImplementsInterfaces(r1, Callable.class, Serializable.class);
         assertLambdaMethodCharacteristics(r1, Callable.class);
         assertSerializableLambdaCharacteristics(r1);
 
-        assertCallableBehavior(r1, MSG);
+        assertCallableBehavior(r1, "No State");
 
-        Callable<String> r2 = (Callable<String> & Serializable) () -> MSG;
-        assertMultipleInstanceCharacteristics(r1, r2);
+        List<Callable<String>> callables = new ArrayList<>();
+        for (int i = 0; i < 2; i++) {
+            Callable<String> callable = (Callable<String> & Serializable) () -> "No State";
+            assertLambdaImplementsInterfaces(callable, Callable.class, Serializable.class);
+            callables.add(callable);
+        }
+        assertMultipleDefinitionCharacteristics(r1, callables.get(0));
+        assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1));
     }
 
     public void testSerializableLambda_withState() throws Exception {
-        final int state = 123;
-        Callable<String> r1 = (Callable<String> & Serializable) () -> MSG + state;
+        final long state = System.currentTimeMillis();
+        Callable<String> r1 = (Callable<String> & Serializable) () -> "State:" + state;
         assertGeneralLambdaClassCharacteristics(r1);
         assertLambdaImplementsInterfaces(r1, Callable.class, Serializable.class);
         assertLambdaMethodCharacteristics(r1, Callable.class);
         assertSerializableLambdaCharacteristics(r1);
 
-        assertCallableBehavior(r1, MSG + state);
+        assertCallableBehavior(r1, "State:" + state);
 
         Callable<String> deserializedR1 = roundtripSerialization(r1);
         assertEquals(r1.call(), deserializedR1.call());
+
+        List<Callable<String>> callables = new ArrayList<>();
+        for (int i = 0; i < 2; i++) {
+            Callable<String> callable = (Callable<String> & Serializable) () -> "State:" + state;
+            assertLambdaImplementsInterfaces(callable, Callable.class, Serializable.class);
+            callables.add(callable);
+        }
+        assertMultipleDefinitionCharacteristics(r1, callables.get(0));
+        assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1));
     }
 
     public void testBadSerializableLambda() throws Exception {
@@ -159,14 +195,25 @@
     }
 
     public void testMultipleInterfaceLambda() throws Exception {
-        Callable<String> r1 = (Callable<String> & MarkerInterface) () -> MSG;
+        Callable<String> r1 = (Callable<String> & MarkerInterface) () -> "MultipleInterfaces";
         assertTrue(r1 instanceof MarkerInterface);
         assertGeneralLambdaClassCharacteristics(r1);
         assertLambdaMethodCharacteristics(r1, Callable.class);
         assertLambdaImplementsInterfaces(r1, Callable.class, MarkerInterface.class);
         assertNonSerializableLambdaCharacteristics(r1);
 
-        assertCallableBehavior(r1, MSG);
+        assertCallableBehavior(r1, "MultipleInterfaces");
+
+        List<Callable<String>> callables = new ArrayList<>();
+        for (int i = 0; i < 2; i++) {
+            Callable<String> callable =
+                    (Callable<String> & MarkerInterface) () -> "MultipleInterfaces";
+            assertLambdaImplementsInterfaces(callable, Callable.class, MarkerInterface.class);
+            callables.add(callable);
+        }
+        assertLambdaImplementsInterfaces(r1, Callable.class, MarkerInterface.class);
+        assertMultipleDefinitionCharacteristics(r1, callables.get(0));
+        assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1));
     }
 
     private static void assertSerializableLambdaCharacteristics(Object r1) throws Exception {
@@ -248,8 +295,15 @@
         }
     }
 
-    private static void assertMultipleInstanceCharacteristics(Object r1, Object r2)
-            throws Exception {
+    /**
+     * Asserts that necessary conditions hold when there are two lambdas with separate but identical
+     * definitions.
+     */
+    private static void assertMultipleDefinitionCharacteristics(
+            Callable<String> r1, Callable<String> r2) throws Exception {
+
+        // Sanity check that the lambdas do the same thing.
+        assertEquals(r1.call(), r2.call());
 
         // Unclear if any of this is *guaranteed* to be true.
 
@@ -258,10 +312,23 @@
         assertNotSame(r1, r2);
         assertTrue(!r1.equals(r2));
 
-        // Confirm the classes differ.
-        Class<?> lambda1Class = r1.getClass();
-        Class<?> lambda2Class = r2.getClass();
-        assertNotSame(lambda1Class, lambda2Class);
+        // Two lambdas from different definitions can share the same class or may not.
+        // See JLS 15.27.4.
+    }
+
+    /**
+     * Asserts that necessary conditions hold when there are two lambdas created from the same
+     * definition.
+     */
+    private static void assertMultipleInstanceCharacteristics(
+            Callable<String> r1, Callable<String> r2) throws Exception {
+
+        // Sanity check that the lambdas do the same thing.
+        assertEquals(r1.call(), r2.call());
+
+        // There doesn't appear to be anything else that is safe to assert here. Two lambdas
+        // created from the same definition can be the same, as can their class, but they can also
+        // be different. See JLS 15.27.4.
     }
 
     private static void assertGeneralLambdaClassCharacteristics(Object r1) throws Exception {
@@ -333,7 +400,7 @@
     }
 
     private static String staticMethod() {
-        return MSG;
+        return STATIC_METHOD_RESPONSE;
     }
 
     private interface MarkerInterface {
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
index 9bf02ef..5866ae4 100644
--- a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
+++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
@@ -1924,4 +1924,417 @@
             MethodType.methodType(void.class, MethodHandle.class, String.class));
         invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, adapter, "a: c+d ,b:c ,c:d ,d:e"));
     }
+
+    private static void checkBooleanCast_delegate(boolean expected, boolean z,  boolean b,
+                                                  boolean c, boolean s, boolean i, boolean j,
+                                                  boolean f, boolean d, boolean l) {
+        assertEquals(expected, z);
+        assertEquals(expected, b);
+        assertEquals(expected, c);
+        assertEquals(expected, s);
+        assertEquals(expected, i);
+        assertEquals(expected, j);
+        assertEquals(expected, f);
+        assertEquals(expected, d);
+        assertEquals(expected, l);
+    }
+
+    private static void checkByteCast_delegate(byte expected, byte z, byte b, byte c, byte s,
+                                               byte i, byte j, byte f, byte d, byte l) {
+        int mask = 0xff;
+        assertEquals(expected & 1, z);
+        assertEquals(expected, b);
+        assertEquals(expected, c & mask);
+        assertEquals(expected, s & mask);
+        assertEquals(expected, i & mask);
+        assertEquals(expected, j & mask);
+        assertEquals(expected, f & mask);
+        assertEquals(expected, d & mask);
+        assertEquals(expected, l);
+    }
+
+    private static void checkCharCast_delegate(char expected, char z, char b, char c, char s,
+                                               char i, char j, char f, char d, char l) {
+        int mask = 0xffff;
+        assertEquals(expected & 1, z);
+        assertEquals(expected & 0xff, b);
+        assertEquals(expected, c);
+        assertEquals(expected, s & mask);
+        assertEquals(expected, i & mask);
+        assertEquals(expected, j & mask);
+        assertEquals(expected, f & mask);
+        assertEquals(expected, d & mask);
+        assertEquals(expected, l);
+    }
+
+    private static void checkShortCast_delegate(short expected, short z, short b, short c, short s,
+                                                short i, short j, short f, short d, short l) {
+        int mask = 0xffff;
+        assertEquals(expected & 1, z);
+        assertEquals(expected & 0xff, b);
+        assertEquals(expected, c & mask);
+        assertEquals(expected, s);
+        assertEquals(expected, i & mask);
+        assertEquals(expected, j & mask);
+        assertEquals(expected, f & mask);
+        assertEquals(expected, d & mask);
+        assertEquals(expected, l);
+    }
+
+    private static void checkIntCast_delegate(int expected, int z, int b, int c, int s, int i,
+                                              int j, int f, int d, int l) {
+        int mask = 0xffffffff;
+        assertEquals(expected & 1, z);
+        assertEquals(expected & 0xff, b);
+        assertEquals(expected & 0xffff, c);
+        assertEquals(expected & 0xffff, s);
+        assertEquals(expected, i & mask);
+        assertEquals(expected, j & mask);
+        assertEquals(expected, f & mask);
+        assertEquals(expected, d & mask);
+        assertEquals(expected, l);
+    }
+
+    private static void checkLongCast_delegate(long expected, long z, long b, long c, long s,
+                                               long i, long j, long f, long d, long l) {
+        long mask = 0xffffffffl;
+        assertEquals(expected & 1, z);
+        assertEquals(expected & 0xff, b);
+        assertEquals(expected & 0xffff, c);
+        assertEquals(expected & 0xffff, s);
+        assertEquals(expected & mask, i & mask);
+        assertEquals(expected, j);
+        assertEquals(expected & mask, f & mask);
+        assertEquals(expected, d);
+        assertEquals(expected, l);
+    }
+
+    private static void checkFloatCast_delegate(float expected, float z, float b, float c, float s,
+                                                float i, float j, float f, float d, float l) {
+        assertEquals((byte) expected & 1, (int) z);
+        assertEquals((byte) expected, (int) b & 0xff);
+        assertEquals((char) expected & 0xffff, (int) c& 0xffff);
+        assertEquals((short) expected & 0xffff, (int) s & 0xffff);
+        assertEquals((int) expected, (int) i);
+        assertEquals((long) expected, (long) j);
+        assertEquals(expected, f);
+        assertEquals(expected, d);
+        assertEquals(expected, l);
+    }
+
+    private static void checkDoubleCast_delegate(double expected, double z, double b, double c,
+                                                 double s, double i, double j, double f, double d,
+                                                 double l) {
+        assertEquals((byte) expected & 1, (int) z);
+        assertEquals((byte) expected & 0xff, (int) b & 0xff);
+        assertEquals((int) expected & 0xffff, (int) c & 0xffff);
+        assertEquals((int) expected & 0xffff, (int) s & 0xffff);
+        assertEquals((int) expected, (int) i);
+        assertEquals((long) expected, (long) j);
+        assertEquals((float) expected, (float) f);
+        assertEquals(expected, d);
+        assertEquals(expected, l);
+    }
+
+    private static void checkBoxingCasts_delegate(boolean expected, Boolean z, Byte b, Character c,
+                                                  Short s, Integer i, Long j, Float f, Double d) {
+        int v = expected ? 1 : 0;
+        assertEquals(Boolean.valueOf(expected ? true : false), z);
+        assertEquals(Byte.valueOf((byte) v), b);
+        assertEquals(Character.valueOf((char) v), c);
+        assertEquals(Short.valueOf((short) v), s);
+        assertEquals(Integer.valueOf(v), i);
+        assertEquals(Long.valueOf(v), j);
+        assertEquals(Float.valueOf(v), f);
+        assertEquals(Double.valueOf(v), d);
+    }
+
+    public static void testExplicitCastArguments() throws Throwable {
+        MethodHandle target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkBooleanCast_delegate",
+            MethodType.methodType(void.class, boolean.class, boolean.class, boolean.class,
+                                  boolean.class, boolean.class, boolean.class, boolean.class,
+                                  boolean.class, boolean.class, boolean.class));
+        MethodHandle mh = MethodHandles.explicitCastArguments(
+            target, MethodType.methodType(void.class, boolean.class, boolean.class, byte.class,
+                                          char.class, short.class, int.class, long.class,
+                                          float.class, double.class, Boolean.class));
+        mh.invokeExact(false, false, (byte) 0, (char) 0, (short) 0, 0, 0l, 0.0f, 0.0,
+                       Boolean.valueOf(false));
+        mh.invokeExact(false, false, (byte) 2, (char) 2, (short) 2, 2, 2l, 2.2f, 2.2,
+                       Boolean.valueOf(false));
+        mh.invokeExact(true, true, (byte) 1, (char) 1, (short) 1, 1, 1l, 1.0f, 1.0,
+                       Boolean.valueOf(true));
+        mh.invokeExact(true, true, (byte) 51, (char) 51, (short) 51, 51, 51l, 51.0f, 51.0,
+                       Boolean.valueOf(true));
+        MethodHandles.explicitCastArguments(
+            target, MethodType.methodType(void.class, boolean.class, boolean.class, byte.class,
+                                          char.class, short.class, int.class, long.class,
+                                          float.class, double.class, String.class));
+        try {
+            mh.invoke(true, true, (byte) 51, (char) 51, (short) 51, 51, 51l, 51.0f, 51.0,
+                      "ClassCastException here!");
+            fail();
+        } catch (ClassCastException e) {
+        }
+
+        target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkByteCast_delegate",
+            MethodType.methodType(void.class, byte.class, byte.class, byte.class,
+                                  byte.class, byte.class, byte.class, byte.class,
+                                  byte.class, byte.class, byte.class));
+        mh = MethodHandles.explicitCastArguments(
+            target, MethodType.methodType(void.class, byte.class, boolean.class, byte.class,
+                                          char.class, short.class, int.class, long.class,
+                                          float.class, double.class, Byte.class));
+        mh.invokeExact((byte) 0x5a, false, (byte) 0x5a, (char) 0x5a5a, (short) 0x5a5a, (int) 0x5a5a,
+                       (long) 0x5a5a, (float) 0x5a5a, (double) 0x5a5a, Byte.valueOf((byte) 0x5a));
+        try {
+            mh.invoke((byte) 0x5a, false, (byte) 0x5a, (char) 0x5a5a, (short) 0x5a5a, (int) 0x5a5a,
+                      (long) 0x5a5a, (float) 0x5a5a, (double) 0x5a5a,
+                      Short.valueOf((short) 0x5a5a));
+            fail();
+        } catch (ClassCastException e) {
+        }
+
+        target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkCharCast_delegate",
+            MethodType.methodType(void.class, char.class, char.class, char.class,
+                                  char.class, char.class, char.class, char.class,
+                                  char.class, char.class, char.class));
+        mh = MethodHandles.explicitCastArguments(
+            target, MethodType.methodType(void.class, char.class, boolean.class, byte.class,
+                                          char.class, short.class, int.class, long.class,
+                                          float.class, double.class, Character.class));
+        mh.invokeExact((char) 0x5555, true, (byte) 0x5555, (char) 0x5555, (short) 0x5555,
+                       (int) 0x5555, (long) 0x5555, (float) 0x5555, (double) 0x5555,
+                       Character.valueOf((char) 0x5555));
+        try {
+            mh.invoke((char) 0x5555, false, (byte) 0x5555, (char) 0x5555, (short) 0x5555,
+                      (int) 0x5555, (long) 0x5555, (float) 0x5555, (double) 0x5555,
+                      Integer.valueOf((int) 0x5555));
+            fail();
+        } catch (ClassCastException e) {
+        }
+
+        target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkShortCast_delegate",
+            MethodType.methodType(void.class, short.class, short.class, short.class,
+                                  short.class, short.class, short.class, short.class,
+                                  short.class, short.class, short.class));
+        mh = MethodHandles.explicitCastArguments(
+            target, MethodType.methodType(void.class, short.class, boolean.class, byte.class,
+                                          char.class, short.class, int.class, long.class,
+                                          float.class, double.class, Short.class));
+        mh.invokeExact((short) 0x3773, true, (byte) 0x3773, (char) 0x3773, (short) 0x3773,
+                       (int) 0x3773, (long) 0x3773, (float) 0x3773, (double) 0x3773,
+                       Short.valueOf((short) 0x3773));
+        try {
+            mh.invoke((short) 0x3773, true, (byte) 0x3773, (char) 0x3773, (short) 0x3773,
+                      (int) 0x3773, (long) 0x3773, (float) 0x3773, (double) 0x3773,
+                      Long.valueOf((long) 0x3773));
+            fail();
+        } catch (ClassCastException e) {
+        }
+
+        target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkIntCast_delegate",
+            MethodType.methodType(void.class, int.class, int.class, int.class,
+                                  int.class, int.class, int.class, int.class,
+                                  int.class, int.class, int.class));
+        mh = MethodHandles.explicitCastArguments(
+            target, MethodType.methodType(void.class, int.class, boolean.class, byte.class,
+                                          char.class, short.class, int.class, long.class,
+                                          float.class, double.class, Integer.class));
+        mh.invokeExact((int) 0x3773470, false, (byte) 0x3773470, (char) 0x3773470,
+                       (short) 0x3773470, (int) 0x3773470, (long) 0x3773470, (float) 0x3773470,
+                       (double) 0x3773470, Integer.valueOf(0x3773470));
+        try {
+            mh.invoke((int) 0x3773470, false, (byte) 0x3773470, (char) 0x3773470,
+                      (short) 0x3773470, (int) 0x3773470, (long) 0x3773470, (float) 0x3773470,
+                      (double) 0x3773470, Long.valueOf((long) 0x3773470));
+            fail();
+        } catch (ClassCastException e) {
+        }
+
+        target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkLongCast_delegate",
+            MethodType.methodType(void.class, long.class, long.class, long.class,
+                                  long.class, long.class, long.class, long.class,
+                                  long.class, long.class, long.class));
+        mh = MethodHandles.explicitCastArguments(
+            target, MethodType.methodType(void.class, long.class, boolean.class, byte.class,
+                                          char.class, short.class, int.class, long.class,
+                                          float.class, double.class, Long.class));
+        long longValue = 0x770000000l;
+        mh.invokeExact((long) longValue, false, (byte) longValue, (char) longValue,
+                       (short) longValue, (int) longValue, (long) longValue, (float) longValue,
+                       (double) longValue, Long.valueOf(longValue));
+        try {
+            mh.invoke((long) longValue, false, (byte) longValue, (char) longValue,
+                      (short) longValue, (int) longValue, (long) longValue, (float) longValue,
+                      (double) longValue, Integer.valueOf(3));
+            fail();
+        } catch (ClassCastException e) {
+        }
+
+        target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkFloatCast_delegate",
+            MethodType.methodType(void.class, float.class, float.class, float.class,
+                                  float.class, float.class, float.class, float.class,
+                                  float.class, float.class, float.class));
+        mh = MethodHandles.explicitCastArguments(
+            target, MethodType.methodType(void.class, float.class, boolean.class, byte.class,
+                                          char.class, short.class, int.class, long.class,
+                                          float.class, double.class, Float.class));
+        float floatValue = 33333.141f;
+        mh.invokeExact(floatValue, true, (byte) floatValue, (char) floatValue, (short) floatValue,
+                       (int) floatValue, (long) floatValue, floatValue, (double) floatValue,
+                       Float.valueOf(floatValue));
+        try {
+            mh.invoke(floatValue, true, (byte) floatValue, (char) floatValue,
+                      (short) floatValue, (int) floatValue, (long) floatValue, floatValue,
+                      (double) floatValue, Integer.valueOf((int) floatValue));
+            fail();
+        } catch (ClassCastException e) {
+        }
+
+        target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkDoubleCast_delegate",
+            MethodType.methodType(void.class, double.class, double.class, double.class,
+                                  double.class, double.class, double.class, double.class,
+                                  double.class, double.class, double.class));
+        mh = MethodHandles.explicitCastArguments(
+            target, MethodType.methodType(void.class, double.class, boolean.class, byte.class,
+                                          char.class, short.class, int.class, long.class,
+                                          float.class, double.class, Double.class));
+        double doubleValue = 33333333333.141;
+        mh.invokeExact(doubleValue, true, (byte) doubleValue, (char) doubleValue,
+                       (short) doubleValue, (int) doubleValue, (long) doubleValue,
+                       (float) doubleValue, doubleValue, Double.valueOf(doubleValue));
+        try {
+            mh.invoke(doubleValue, true, (byte) doubleValue, (char) doubleValue,
+                      (short) doubleValue, (int) doubleValue, (long) doubleValue,
+                      (float) doubleValue, (double) doubleValue,
+                      Integer.valueOf((int) doubleValue));
+            fail();
+        } catch (ClassCastException e) {
+        }
+
+        target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkBoxingCasts_delegate",
+            MethodType.methodType(void.class, boolean.class, Boolean.class, Byte.class,
+                                  Character.class, Short.class, Integer.class, Long.class,
+                                  Float.class, Double.class));
+        mh = MethodHandles.explicitCastArguments(
+            target, MethodType.methodType(void.class, boolean.class, boolean.class, byte.class,
+                                          char.class, short.class, int.class, long.class,
+                                          float.class, double.class));
+        mh.invokeExact(false, false, (byte) 0, (char) 0, (short) 0, 0, 0l, 0.0f, 0.0);
+        mh.invokeExact(true, true, (byte) 1, (char) 1, (short) 1, 1, 1l, 1.0f, 1.0);
+        mh.invoke(Boolean.valueOf(false), Boolean.valueOf(false), Byte.valueOf((byte) 0),
+                  Character.valueOf((char) 0), Short.valueOf((short) 0), Integer.valueOf(0),
+                  Long.valueOf(0l), Float.valueOf(0.0f), Double.valueOf(0.0));
+        mh.invoke(Boolean.valueOf(true), Boolean.valueOf(true), Byte.valueOf((byte) 1),
+                  Character.valueOf((char) 1), Short.valueOf((short) 1), Integer.valueOf(1),
+                  Long.valueOf(1l), Float.valueOf(1.0f), Double.valueOf(1.0));
+        mh = MethodHandles.explicitCastArguments(
+            target, MethodType.methodType(void.class, double.class, boolean.class, byte.class,
+                                          char.class, short.class, int.class, long.class,
+                                          float.class, double.class));
+        mh.invokeExact(0.0, false, (byte) 0, (char) 0, (short) 0, 0, 0l, 0.0f, 0.0);
+    }
+
+    static void returnVoid() {}
+
+    static boolean returnBoolean(boolean b) { return b; }
+
+    static Boolean returnBooleanObject(boolean b) { return b; }
+
+    public static void testExplicitCastReturnValues() throws Throwable {
+        MethodHandle target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "returnVoid", MethodType.methodType(void.class));
+        assertEquals(false,
+                     MethodHandles
+                     .explicitCastArguments(target, MethodType.methodType(boolean.class))
+                     .invoke());
+        assertEquals(null,
+                     MethodHandles
+                     .explicitCastArguments(target, MethodType.methodType(Boolean.class))
+                     .invoke());
+        assertEquals(0l,
+                     MethodHandles
+                     .explicitCastArguments(target, MethodType.methodType(long.class))
+                     .invoke());
+        assertEquals(null,
+                     MethodHandles
+                     .explicitCastArguments(target, MethodType.methodType(Long.class))
+                     .invoke());
+
+        target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "returnBoolean",
+            MethodType.methodType(boolean.class, boolean.class));
+        assertEquals(false,
+                     MethodHandles
+                     .explicitCastArguments(target,
+                                            MethodType.methodType(boolean.class, boolean.class))
+                     .invoke(false));
+        assertEquals(true,
+                     MethodHandles
+                     .explicitCastArguments(target,
+                                            MethodType.methodType(boolean.class, boolean.class))
+                     .invoke(true));
+        assertEquals(Boolean.valueOf(false),
+                     MethodHandles
+                     .explicitCastArguments(target,
+                                            MethodType.methodType(Boolean.class, boolean.class))
+                     .invoke(false));
+        assertEquals(Boolean.valueOf(true),
+                     MethodHandles
+                     .explicitCastArguments(target,
+                                            MethodType.methodType(Boolean.class, boolean.class))
+                     .invoke(true));
+        assertEquals((byte) 0,
+                     MethodHandles
+                     .explicitCastArguments(target,
+                                            MethodType.methodType(byte.class, boolean.class))
+                     .invoke(false));
+        assertEquals((byte) 1,
+                     MethodHandles
+                     .explicitCastArguments(target,
+                                            MethodType.methodType(byte.class, boolean.class))
+                     .invoke(true));
+        try {
+            assertEquals(Byte.valueOf((byte) 0),
+                         MethodHandles
+                         .explicitCastArguments(target,
+                                                MethodType.methodType(Byte.class, boolean.class))
+                         .invoke(false));
+            fail();
+        } catch (ClassCastException e) {
+        }
+
+        try {
+            assertEquals(Byte.valueOf((byte) 1),
+                         MethodHandles
+                         .explicitCastArguments(target,
+                                                MethodType.methodType(Byte.class, boolean.class))
+                         .invoke(true));
+        } catch (ClassCastException e) {
+        }
+
+        target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "returnBooleanObject",
+            MethodType.methodType(Boolean.class, boolean.class));
+        assertEquals(false,
+                     (boolean) MethodHandles
+                     .explicitCastArguments(target,
+                                            MethodType.methodType(boolean.class, boolean.class))
+                     .invokeExact(false));
+        assertEquals(true,
+                     (boolean) MethodHandles
+                     .explicitCastArguments(target,
+                                            MethodType.methodType(boolean.class, boolean.class))
+                     .invokeExact(true));
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementParameterTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementParameterTest.java
index aa14bd3..cb49aef 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementParameterTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementParameterTest.java
@@ -114,6 +114,8 @@
 
         void singleAnnotation(@Repeated(1) String p0) {}
 
+        static void staticSingleAnnotation(@Repeated(1) String p0) {}
+
         static Method getMethodWithoutAnnotations() throws Exception {
             return AnnotatedMethodClass.class.getDeclaredMethod("noAnnotation", String.class);
         }
@@ -135,6 +137,20 @@
         static Method getMethodSingleAnnotation() throws Exception {
             return AnnotatedMethodClass.class.getDeclaredMethod("singleAnnotation", String.class);
         }
+
+        static Method getMethodStaticSingleAnnotation() throws Exception {
+            return AnnotatedMethodClass.class.getDeclaredMethod("staticSingleAnnotation",
+                    String.class);
+        }
+    }
+
+    private static abstract class AnnotatedMethodAbstractClass {
+        abstract void abstractSingleAnnotation(@Repeated(1) String p0);
+
+        static Method getMethodAbstractSingleAnnotation() throws Exception {
+            return AnnotatedMethodAbstractClass.class.getDeclaredMethod(
+                    "abstractSingleAnnotation", String.class);
+        }
     }
 
     // Tests for isAnnotationPresent and getDeclaredAnnotation.
@@ -155,6 +171,12 @@
         checkParameter0DeclaredAnnotation(
                 AnnotatedMethodClass.getMethodSingleAnnotation(),
                 repeated, "@Repeated(1)");
+        checkParameter0DeclaredAnnotation(
+                AnnotatedMethodClass.getMethodStaticSingleAnnotation(),
+                repeated, "@Repeated(1)");
+        checkParameter0DeclaredAnnotation(
+                AnnotatedMethodAbstractClass.getMethodAbstractSingleAnnotation(),
+                repeated, "@Repeated(1)");
 
         Class<? extends Annotation> container = Container.class;
         checkParameter0DeclaredAnnotation(
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/ExecutableParameterTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/ExecutableParameterTest.java
index a07f2b3..38d9899 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/annotations/ExecutableParameterTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/ExecutableParameterTest.java
@@ -108,6 +108,8 @@
 
         void singleAnnotation(@Repeated(1) String p0) {}
 
+        static void staticSingleAnnotation(@Repeated(1) String p0) {}
+
         static Method getMethodWithoutAnnotations() throws Exception {
             return AnnotatedMethodClass.class.getDeclaredMethod("noAnnotation", String.class);
         }
@@ -129,8 +131,23 @@
         static Method getMethodSingleAnnotation() throws Exception {
             return AnnotatedMethodClass.class.getDeclaredMethod("singleAnnotation", String.class);
         }
+
+        static Method getMethodStaticSingleAnnotation() throws Exception {
+            return AnnotatedMethodClass.class.getDeclaredMethod("staticSingleAnnotation",
+                    String.class);
+        }
     }
 
+    private static abstract class AnnotatedMethodAbstractClass {
+        abstract void abstractSingleAnnotation(@Repeated(1) String p0);
+
+        static Method getMethodAbstractSingleAnnotation() throws Exception {
+            return AnnotatedMethodAbstractClass.class.getDeclaredMethod(
+                    "abstractSingleAnnotation", String.class);
+        }
+    }
+
+
     public void testMethodGetParameterAnnotations_repeated() throws Exception {
         assertParameter0Annotations(
                 AnnotatedMethodClass.getMethodWithoutAnnotations(), EXPECT_EMPTY);
@@ -146,6 +163,13 @@
         assertParameter0Annotations(
                 AnnotatedMethodClass.getMethodSingleAnnotation(),
                 "@Repeated(1)");
+        assertParameter0Annotations(
+                AnnotatedMethodClass.getMethodStaticSingleAnnotation(),
+                "@Repeated(1)");
+        assertParameter0Annotations(
+                AnnotatedMethodAbstractClass.getMethodAbstractSingleAnnotation(),
+                "@Repeated(1)");
+
     }
 
     private static class AnnotatedConstructorClass {
diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java
index 50d8ce6..0bfd241 100644
--- a/luni/src/test/java/libcore/java/net/SocketTest.java
+++ b/luni/src/test/java/libcore/java/net/SocketTest.java
@@ -262,8 +262,11 @@
                 s.setTrafficClass(i);
 
                 // b/30909505
-                // Linux does not set ECN bits for STREAM sockets, so these bits should be zero.
-                assertEquals(i & ~INET_ECN_MASK, s.getTrafficClass());
+                // Linux does not set ECN bits for IP_TOS, but sets for IPV6_TCLASS. We should
+                // accept either output.
+                int actual = s.getTrafficClass();
+                assertTrue(i == actual || // IPV6_TCLASS
+                        (actual == (i & ~INET_ECN_MASK))); // IP_TOS: ECN bits should be 0
             }
         }
     }
diff --git a/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProvider2Test.java b/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProvider2Test.java
index dbbfeae..2916b03 100644
--- a/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProvider2Test.java
+++ b/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProvider2Test.java
@@ -263,24 +263,35 @@
 
     @Test
     public void test_getFileStore() throws IOException {
-        FileStore fileStore = provider.getFileStore(filesSetup.getDataFilePath());
-        assertNotNull(fileStore);
-    }
+        try {
+            provider.getFileStore(filesSetup.getDataFilePath());
+            fail();
+        } catch (SecurityException expected) {
+        }
 
-    @Test
-    public void test_getFileStore_NPE() throws IOException {
         try {
             provider.getFileStore(null);
             fail();
-        } catch(NullPointerException expected) {}
+        } catch (SecurityException expected) {
+        }
     }
 
     @Test
     public void test_isHidden() throws IOException {
         assertFalse(provider.isHidden(filesSetup.getDataFilePath()));
-        Files.setAttribute(filesSetup.getDataFilePath(), "dos:hidden", true);
 
-        // Files can't be hid.
+        // Files can't be hidden using the "dos" view, which is unsupported since it relies
+        // on a custom xattr, which may or may not be available on all FSs.
+        //
+        // Note that this weirdly asymmetric : setting the hidden attribute uses xattrs to
+        // emulate dos attributes whereas isHidden checks whether the the file name begins with a
+        // leading period. <shrug>
+        try {
+            Files.setAttribute(filesSetup.getDataFilePath(), "dos:hidden", true);
+            fail();
+        } catch (UnsupportedOperationException expected) {
+        }
+
         assertFalse(provider.isHidden(filesSetup.getDataFilePath()));
     }
 
diff --git a/luni/src/test/java/libcore/java/nio/file/Files2Test.java b/luni/src/test/java/libcore/java/nio/file/Files2Test.java
index 394643a..436d3e3 100644
--- a/luni/src/test/java/libcore/java/nio/file/Files2Test.java
+++ b/luni/src/test/java/libcore/java/nio/file/Files2Test.java
@@ -138,9 +138,12 @@
 
     @Test
     public void test_getFileStore() throws IOException {
-        FileStore mockFileStore = mock(FileStore.class);
-        when(mockFileSystemProvider.getFileStore(mockPath)).thenReturn(mockFileStore);
-        assertEquals(mockFileStore, Files.getFileStore(mockPath));
+        when(mockFileSystemProvider.getFileStore(mockPath)).thenThrow(new SecurityException());
+        try {
+            Files.getFileStore(mockPath);
+            fail();
+        } catch (SecurityException expected) {
+        }
     }
 
     @Test
diff --git a/luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java b/luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java
index 26afa1d..34c6aab 100644
--- a/luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java
+++ b/luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java
@@ -124,7 +124,7 @@
         // It is expected that the symbols may change with future CLDR updates.
 
         dfs = new DecimalFormatSymbols(Locale.forLanguageTag("ar"));
-        assertEquals('%', dfs.getPercent());
+        assertEquals('Ùª', dfs.getPercent());
         assertEquals('-', dfs.getMinusSign());
 
         dfs = new DecimalFormatSymbols(Locale.forLanguageTag("fa"));
@@ -186,4 +186,26 @@
         compareDfs(dfs, icuSymb);
     }
 
+    // http://b/36562145
+    public void testMaybeStripMarkers() {
+        final char ltr = '\u200E';
+        final char rtl = '\u200F';
+        final char alm = '\u061C';
+        final char fallback = 'F';
+        assertEquals(fallback, DecimalFormatSymbols.maybeStripMarkers("", fallback));
+        assertEquals(fallback, DecimalFormatSymbols.maybeStripMarkers("XY", fallback));
+        assertEquals(fallback, DecimalFormatSymbols.maybeStripMarkers("" + ltr, fallback));
+        assertEquals(fallback, DecimalFormatSymbols.maybeStripMarkers("" + rtl, fallback));
+        assertEquals(fallback, DecimalFormatSymbols.maybeStripMarkers("" + alm, fallback));
+        assertEquals(fallback,
+                DecimalFormatSymbols.maybeStripMarkers("X" + ltr + rtl + alm + "Y", fallback));
+        assertEquals(fallback,
+                DecimalFormatSymbols.maybeStripMarkers("" + ltr + rtl + alm, fallback));
+        assertEquals(fallback, DecimalFormatSymbols.maybeStripMarkers(alm + "XY" + rtl, fallback));
+        assertEquals('X', DecimalFormatSymbols.maybeStripMarkers("X", fallback));
+        assertEquals('X', DecimalFormatSymbols.maybeStripMarkers("X" + ltr, fallback));
+        assertEquals('X', DecimalFormatSymbols.maybeStripMarkers("X" + rtl, fallback));
+        assertEquals('X', DecimalFormatSymbols.maybeStripMarkers(alm + "X", fallback));
+        assertEquals('X', DecimalFormatSymbols.maybeStripMarkers(alm + "X" + rtl, fallback));
+    }
 }
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 375eb36..9f7267c 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -384,6 +384,18 @@
         }
     }
 
+    // http://b/33197219
+    public void testDisplayNameForNonCanonicalTimezones() {
+        TimeZone canonical = TimeZone.getTimeZone("Europe/London");
+        TimeZone nonCanonical = TimeZone.getTimeZone("GB");
+
+        // verify that GB is actually an alias for Europe/London
+        assertTrue(canonical.hasSameRules(nonCanonical));
+
+        assertEquals(canonical.getDisplayName(true, TimeZone.LONG, Locale.ENGLISH),
+                nonCanonical.getDisplayName(true, TimeZone.LONG, Locale.ENGLISH));
+    }
+
     // http://b/30937209
     public void testSetDefaultDeadlock() throws InterruptedException, BrokenBarrierException {
         // Since this tests a deadlock, the test has two fundamental problems:
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
index 1ddeacb..ebf72f0 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
@@ -20,6 +20,7 @@
 import java.lang.reflect.Method;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
+import java.io.InputStream;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.SocketException;
@@ -249,4 +250,15 @@
         assertNotNull(ssl);
         assertTrue(SSLSocket.class.isAssignableFrom(ssl.getClass()));
     }
+
+
+    public void test_SSLSocketFactory_createSocket_withConsumedInputStream()
+            throws Exception {
+        try {
+            SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+            Socket sslSocket = sf.createSocket(null, (InputStream) null, false);
+            fail();
+        } catch (UnsupportedOperationException expected) {
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/net/MimeUtilsTest.java b/luni/src/test/java/libcore/net/MimeUtilsTest.java
index ac0c017..af8f479 100644
--- a/luni/src/test/java/libcore/net/MimeUtilsTest.java
+++ b/luni/src/test/java/libcore/net/MimeUtilsTest.java
@@ -76,4 +76,13 @@
     assertEquals("video/3gpp2", MimeUtils.guessMimeTypeFromExtension("3gpp2"));
     assertEquals("video/3gpp2", MimeUtils.guessMimeTypeFromExtension("3g2"));
   }
+
+  public void test_37167977() {
+    // https://tools.ietf.org/html/rfc5334#section-10.1
+    assertEquals("audio/ogg", MimeUtils.guessMimeTypeFromExtension("ogg"));
+    assertEquals("audio/ogg", MimeUtils.guessMimeTypeFromExtension("oga"));
+    assertEquals("audio/ogg", MimeUtils.guessMimeTypeFromExtension("spx"));
+    assertEquals("video/ogg", MimeUtils.guessMimeTypeFromExtension("ogv"));
+  }
+
 }
diff --git a/luni/src/test/java/libcore/util/TimeZoneFinderTest.java b/luni/src/test/java/libcore/util/TimeZoneFinderTest.java
new file mode 100644
index 0000000..0b31c9a
--- /dev/null
+++ b/luni/src/test/java/libcore/util/TimeZoneFinderTest.java
@@ -0,0 +1,729 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.util;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import android.icu.util.TimeZone;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+public class TimeZoneFinderTest {
+
+    private static final int HOUR_MILLIS = 60 * 60 * 1000;
+
+    // Zones used in the tests. NEW_YORK_TZ and LONDON_TZ chosen because they never overlap but both
+    // have DST.
+    private static final TimeZone NEW_YORK_TZ = TimeZone.getTimeZone("America/New_York");
+    private static final TimeZone LONDON_TZ = TimeZone.getTimeZone("Europe/London");
+    // A zone that matches LONDON_TZ for WHEN_NO_DST. It does not have DST so differs for WHEN_DST.
+    private static final TimeZone REYKJAVIK_TZ = TimeZone.getTimeZone("Atlantic/Reykjavik");
+    // Another zone that matches LONDON_TZ for WHEN_NO_DST. It does not have DST so differs for
+    // WHEN_DST.
+    private static final TimeZone UTC_TZ = TimeZone.getTimeZone("Etc/UTC");
+
+    // 22nd July 2017, 13:14:15 UTC (DST time in all the timezones used in these tests that observe
+    // DST).
+    private static final long WHEN_DST = 1500729255000L;
+    // 22nd January 2018, 13:14:15 UTC (non-DST time in all timezones used in these tests).
+    private static final long WHEN_NO_DST = 1516626855000L;
+
+    private static final int LONDON_DST_OFFSET_MILLIS = HOUR_MILLIS;
+    private static final int LONDON_NO_DST_OFFSET_MILLIS = 0;
+
+    private static final int NEW_YORK_DST_OFFSET_MILLIS = -4 * HOUR_MILLIS;
+    private static final int NEW_YORK_NO_DST_OFFSET_MILLIS = -5 * HOUR_MILLIS;
+
+    private Path testDir;
+
+    @Before
+    public void setUp() throws Exception {
+        testDir = Files.createTempDirectory("TimeZoneFinderTest");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Delete the testDir and all contents.
+        Files.walkFileTree(testDir, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+                    throws IOException {
+                Files.delete(file);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc)
+                    throws IOException {
+                Files.delete(dir);
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    @Test
+    public void createInstanceWithFallback() throws Exception {
+        String validXml1 = "<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n";
+        String validXml2 = "<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/Paris</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n";
+
+        String invalidXml = "<foo></foo>\n";
+        checkValidateThrowsParserException(invalidXml);
+
+        String validFile1 = createFile(validXml1);
+        String validFile2 = createFile(validXml2);
+        String invalidFile = createFile(invalidXml);
+        String missingFile = createMissingFile();
+
+        TimeZoneFinder file1ThenFile2 =
+                TimeZoneFinder.createInstanceWithFallback(validFile1, validFile2);
+        assertZonesEqual(zones("Europe/London"), file1ThenFile2.lookupTimeZonesByCountry("gb"));
+
+        TimeZoneFinder missingFileThenFile1 =
+                TimeZoneFinder.createInstanceWithFallback(missingFile, validFile1);
+        assertZonesEqual(zones("Europe/London"),
+                missingFileThenFile1.lookupTimeZonesByCountry("gb"));
+
+        TimeZoneFinder file2ThenFile1 =
+                TimeZoneFinder.createInstanceWithFallback(validFile2, validFile1);
+        assertZonesEqual(zones("Europe/Paris"), file2ThenFile1.lookupTimeZonesByCountry("gb"));
+
+        // We assume the file has been validated so an invalid file is not checked ahead of time.
+        // We will find out when we look something up.
+        TimeZoneFinder invalidThenValid =
+                TimeZoneFinder.createInstanceWithFallback(invalidFile, validFile1);
+        assertNull(invalidThenValid.lookupTimeZonesByCountry("gb"));
+
+        // This is not a normal case: It would imply a define shipped without a file in /system!
+        TimeZoneFinder missingFiles =
+                TimeZoneFinder.createInstanceWithFallback(missingFile, missingFile);
+        assertNull(missingFiles.lookupTimeZonesByCountry("gb"));
+    }
+
+    @Test
+    public void xmlParsing_emptyFile() throws Exception {
+        checkValidateThrowsParserException("");
+    }
+
+    @Test
+    public void xmlParsing_unexpectedRootElement() throws Exception {
+        checkValidateThrowsParserException("<foo></foo>\n");
+    }
+
+    @Test
+    public void xmlParsing_missingCountryZones() throws Exception {
+        checkValidateThrowsParserException("<timezones></timezones>\n");
+    }
+
+    @Test
+    public void xmlParsing_noCountriesOk() throws Exception {
+        validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+    }
+
+    @Test
+    public void xmlParsing_unexpectedComments() throws Exception {
+        TimeZoneFinder finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <!-- This is a comment -->"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb"));
+
+        // This is a crazy comment, but also helps prove that TEXT nodes are coalesced by the
+        // parser.
+        finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/<!-- Don't freak out! -->London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb"));
+    }
+
+    @Test
+    public void xmlParsing_unexpectedElementsIgnored() throws Exception {
+        String unexpectedElement = "<unexpected-element>\n<a /></unexpected-element>\n";
+        TimeZoneFinder finder = validate("<timezones>\n"
+                + "  " + unexpectedElement
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb"));
+
+        finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    " + unexpectedElement
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb"));
+
+        finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      " + unexpectedElement
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb"));
+
+        finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "      " + unexpectedElement
+                + "      <id>Europe/Paris</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London", "Europe/Paris"),
+                finder.lookupTimeZonesByCountry("gb"));
+
+        finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "    " + unexpectedElement
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb"));
+
+        // This test is important because it ensures we can extend the format in future with
+        // more information.
+        finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "  " + unexpectedElement
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb"));
+    }
+
+    @Test
+    public void xmlParsing_unexpectedTextIgnored() throws Exception {
+        String unexpectedText = "unexpected-text";
+        TimeZoneFinder finder = validate("<timezones>\n"
+                + "  " + unexpectedText
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb"));
+
+        finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    " + unexpectedText
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb"));
+
+        finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      " + unexpectedText
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb"));
+
+        finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "      " + unexpectedText
+                + "      <id>Europe/Paris</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London", "Europe/Paris"),
+                finder.lookupTimeZonesByCountry("gb"));
+    }
+
+    @Test
+    public void xmlParsing_truncatedInput() throws Exception {
+        checkValidateThrowsParserException("<timezones>\n");
+
+        checkValidateThrowsParserException("<timezones>\n"
+                + "  <countryzones>\n");
+
+        checkValidateThrowsParserException("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n");
+
+        checkValidateThrowsParserException("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n");
+
+        checkValidateThrowsParserException("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n");
+
+        checkValidateThrowsParserException("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n");
+    }
+
+    @Test
+    public void xmlParsing_unexpectedChildInTimeZoneIdThrows() throws Exception {
+        checkValidateThrowsParserException("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id><unexpected-element /></id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+    }
+
+    @Test
+    public void xmlParsing_unknownTimeZoneIdIgnored() throws Exception {
+        TimeZoneFinder finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Unknown_Id</id>\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb"));
+    }
+
+    @Test
+    public void xmlParsing_missingCountryCode() throws Exception {
+        checkValidateThrowsParserException("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country>\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+    }
+
+    @Test
+    public void xmlParsing_unknownCountryReturnsNull() throws Exception {
+        TimeZoneFinder finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+        assertNull(finder.lookupTimeZonesByCountry("gb"));
+    }
+
+    @Test
+    public void lookupTimeZonesByCountry_structuresAreImmutable() throws Exception {
+        TimeZoneFinder finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+
+        List<TimeZone> gbList = finder.lookupTimeZonesByCountry("gb");
+        assertEquals(1, gbList.size());
+        assertImmutableList(gbList);
+        assertImmutableTimeZone(gbList.get(0));
+
+        assertNull(finder.lookupTimeZonesByCountry("unknown"));
+    }
+
+    @Test
+    public void lookupTimeZoneByCountryAndOffset_unknownCountry() throws Exception {
+        TimeZoneFinder finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"xx\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+
+        // Demonstrate the arguments work for a known country.
+        assertZoneEquals(LONDON_TZ,
+                finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS,
+                        true /* isDst */, WHEN_DST, null /* bias */));
+
+        // Test with an unknown country.
+        String unknownCountryCode = "yy";
+        assertNull(finder.lookupTimeZoneByCountryAndOffset(unknownCountryCode,
+                LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */));
+
+        assertNull(finder.lookupTimeZoneByCountryAndOffset(unknownCountryCode,
+                LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, LONDON_TZ /* bias */));
+    }
+
+    @Test
+    public void lookupTimeZoneByCountryAndOffset_oneCandidate() throws Exception {
+        TimeZoneFinder finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"xx\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+
+        // The three parameters match the configured zone: offset, isDst and when.
+        assertZoneEquals(LONDON_TZ,
+                finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS,
+                        true /* isDst */, WHEN_DST, null /* bias */));
+        assertZoneEquals(LONDON_TZ,
+                finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_NO_DST_OFFSET_MILLIS,
+                        false /* isDst */, WHEN_NO_DST, null /* bias */));
+
+        // Some lookup failure cases where the offset, isDst and when do not match the configured
+        // zone.
+        TimeZone noDstMatch1 = finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, null /* bias */);
+        assertNull(noDstMatch1);
+
+        TimeZone noDstMatch2 = finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, null /* bias */);
+        assertNull(noDstMatch2);
+
+        TimeZone noDstMatch3 = finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_NO_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */);
+        assertNull(noDstMatch3);
+
+        TimeZone noDstMatch4 = finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_NO_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, null /* bias */);
+        assertNull(noDstMatch4);
+
+        TimeZone noDstMatch5 = finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_DST_OFFSET_MILLIS, false /* isDst */, WHEN_DST, null /* bias */);
+        assertNull(noDstMatch5);
+
+        TimeZone noDstMatch6 = finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_DST, null /* bias */);
+        assertNull(noDstMatch6);
+
+        // Some bias cases below.
+
+        // The bias is irrelevant here: it matches what would be returned anyway.
+        assertZoneEquals(LONDON_TZ,
+                finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS,
+                        true /* isDst */, WHEN_DST, LONDON_TZ /* bias */));
+        assertZoneEquals(LONDON_TZ,
+                finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_NO_DST_OFFSET_MILLIS,
+                        false /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */));
+        // A sample of a non-matching case with bias.
+        assertNull(finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS,
+                true /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */));
+
+        // The bias should be ignored: it doesn't match any of the country's zones.
+        assertZoneEquals(LONDON_TZ,
+                finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS,
+                        true /* isDst */, WHEN_DST, NEW_YORK_TZ /* bias */));
+
+        // The bias should still be ignored even though it matches the offset information given:
+        // it doesn't match any of the country's configured zones.
+        assertNull(finder.lookupTimeZoneByCountryAndOffset("xx", NEW_YORK_DST_OFFSET_MILLIS,
+                true /* isDst */, WHEN_DST, NEW_YORK_TZ /* bias */));
+    }
+
+    @Test
+    public void lookupTimeZoneByCountryAndOffset_multipleNonOverlappingCandidates()
+            throws Exception {
+        TimeZoneFinder finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"xx\">\n"
+                + "      <id>America/New_York</id>\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+
+        // The three parameters match the configured zone: offset, isDst and when.
+        assertZoneEquals(LONDON_TZ, finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */));
+        assertZoneEquals(LONDON_TZ, finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, null /* bias */));
+        assertZoneEquals(NEW_YORK_TZ, finder.lookupTimeZoneByCountryAndOffset("xx",
+                NEW_YORK_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */));
+        assertZoneEquals(NEW_YORK_TZ, finder.lookupTimeZoneByCountryAndOffset("xx",
+                NEW_YORK_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, null /* bias */));
+
+        // Some lookup failure cases where the offset, isDst and when do not match the configured
+        // zone. This is a sample, not complete.
+        TimeZone noDstMatch1 = finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, null /* bias */);
+        assertNull(noDstMatch1);
+
+        TimeZone noDstMatch2 = finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, null /* bias */);
+        assertNull(noDstMatch2);
+
+        TimeZone noDstMatch3 = finder.lookupTimeZoneByCountryAndOffset("xx",
+                NEW_YORK_NO_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */);
+        assertNull(noDstMatch3);
+
+        TimeZone noDstMatch4 = finder.lookupTimeZoneByCountryAndOffset("xx",
+                NEW_YORK_NO_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, null /* bias */);
+        assertNull(noDstMatch4);
+
+        TimeZone noDstMatch5 = finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_DST_OFFSET_MILLIS, false /* isDst */, WHEN_DST, null /* bias */);
+        assertNull(noDstMatch5);
+
+        TimeZone noDstMatch6 = finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_DST, null /* bias */);
+        assertNull(noDstMatch6);
+
+        // Some bias cases below.
+
+        // The bias is irrelevant here: it matches what would be returned anyway.
+        assertZoneEquals(LONDON_TZ,
+                finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS,
+                        true /* isDst */, WHEN_DST, LONDON_TZ /* bias */));
+        assertZoneEquals(LONDON_TZ,
+                finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_NO_DST_OFFSET_MILLIS,
+                        false /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */));
+        // A sample of a non-matching case with bias.
+        assertNull(finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS,
+                true /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */));
+
+        // The bias should be ignored: it matches a configured zone, but the offset is wrong so
+        // should not be considered a match.
+        assertZoneEquals(LONDON_TZ,
+                finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS,
+                        true /* isDst */, WHEN_DST, NEW_YORK_TZ /* bias */));
+    }
+
+    // This is an artificial case very similar to America/Denver and America/Phoenix in the US: both
+    // have the same offset for 6 months of the year but diverge. Australia/Lord_Howe too.
+    @Test
+    public void lookupTimeZoneByCountryAndOffset_multipleOverlappingCandidates() throws Exception {
+        // Three zones that have the same offset for some of the year. Europe/London changes
+        // offset WHEN_DST, the others do not.
+        TimeZoneFinder finder = validate("<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"xx\">\n"
+                + "      <id>Atlantic/Reykjavik</id>\n"
+                + "      <id>Europe/London</id>\n"
+                + "      <id>Etc/UTC</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n");
+
+        // This is the no-DST offset for LONDON_TZ, REYKJAVIK_TZ. UTC_TZ.
+        final int noDstOffset = LONDON_NO_DST_OFFSET_MILLIS;
+        // This is the DST offset for LONDON_TZ.
+        final int dstOffset = LONDON_DST_OFFSET_MILLIS;
+
+        // The three parameters match the configured zone: offset, isDst and when.
+        assertZoneEquals(LONDON_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", dstOffset,
+                true /* isDst */, WHEN_DST, null /* bias */));
+        assertZoneEquals(REYKJAVIK_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset,
+                false /* isDst */, WHEN_NO_DST, null /* bias */));
+        assertZoneEquals(LONDON_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", dstOffset,
+                true /* isDst */, WHEN_DST, null /* bias */));
+        assertZoneEquals(REYKJAVIK_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset,
+                false /* isDst */, WHEN_NO_DST, null /* bias */));
+        assertZoneEquals(REYKJAVIK_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset,
+                false /* isDst */, WHEN_DST, null /* bias */));
+
+        // Some lookup failure cases where the offset, isDst and when do not match the configured
+        // zones.
+        TimeZone noDstMatch1 = finder.lookupTimeZoneByCountryAndOffset("xx", dstOffset,
+                true /* isDst */, WHEN_NO_DST, null /* bias */);
+        assertNull(noDstMatch1);
+
+        TimeZone noDstMatch2 = finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset,
+                true /* isDst */, WHEN_DST, null /* bias */);
+        assertNull(noDstMatch2);
+
+        TimeZone noDstMatch3 = finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset,
+                true /* isDst */, WHEN_NO_DST, null /* bias */);
+        assertNull(noDstMatch3);
+
+        TimeZone noDstMatch4 = finder.lookupTimeZoneByCountryAndOffset("xx", dstOffset,
+                false /* isDst */, WHEN_DST, null /* bias */);
+        assertNull(noDstMatch4);
+
+
+        // Some bias cases below.
+
+        // The bias is relevant here: it overrides what would be returned naturally.
+        assertZoneEquals(REYKJAVIK_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset,
+                false /* isDst */, WHEN_NO_DST, null /* bias */));
+        assertZoneEquals(LONDON_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset,
+                false /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */));
+        assertZoneEquals(UTC_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset,
+                false /* isDst */, WHEN_NO_DST, UTC_TZ /* bias */));
+
+        // The bias should be ignored: it matches a configured zone, but the offset is wrong so
+        // should not be considered a match.
+        assertZoneEquals(LONDON_TZ, finder.lookupTimeZoneByCountryAndOffset("xx",
+                LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, REYKJAVIK_TZ /* bias */));
+    }
+
+    @Test
+    public void consistencyTest() throws Exception {
+        // Confirm that no new zones have been added to zones.tab without also adding them to the
+        // configuration used to drive TimeZoneFinder.
+
+        // zone.tab is a tab separated ASCII file provided by IANA and included in Android's tzdata
+        // file. Each line contains a mapping from country code -> zone ID. The ordering used by
+        // TimeZoneFinder is Android-specific, but we can use zone.tab to make sure we know about
+        // all country zones. Any update to tzdata that adds, renames, or removes zones should be
+        // reflected in the file used by TimeZoneFinder.
+        Map<String, Set<String>> zoneTabMappings = new HashMap<>();
+        for (String line : ZoneInfoDB.getInstance().getZoneTab().split("\n")) {
+            int countryCodeEnd = line.indexOf('\t', 1);
+            int olsonIdStart = line.indexOf('\t', 4) + 1;
+            int olsonIdEnd = line.indexOf('\t', olsonIdStart);
+            if (olsonIdEnd == -1) {
+                olsonIdEnd = line.length(); // Not all zone.tab lines have a comment.
+            }
+            String countryCode = line.substring(0, countryCodeEnd);
+            String olsonId = line.substring(olsonIdStart, olsonIdEnd);
+            Set<String> zoneIds = zoneTabMappings.get(countryCode);
+            if (zoneIds == null) {
+                zoneIds = new HashSet<>();
+                zoneTabMappings.put(countryCode, zoneIds);
+            }
+            zoneIds.add(olsonId);
+        }
+
+        TimeZoneFinder timeZoneFinder = TimeZoneFinder.getInstance();
+        for (Map.Entry<String, Set<String>> countryEntry : zoneTabMappings.entrySet()) {
+            String countryCode = countryEntry.getKey();
+            // Android uses lower case, IANA uses upper.
+            countryCode = countryCode.toLowerCase();
+
+            List<String> ianaZoneIds = countryEntry.getValue().stream().sorted()
+                    .collect(Collectors.toList());
+            List<TimeZone> androidZones = timeZoneFinder.lookupTimeZonesByCountry(countryCode);
+            List<String> androidZoneIds =
+                    androidZones.stream().map(TimeZone::getID).sorted()
+                            .collect(Collectors.toList());
+
+            assertEquals("Android zones for " + countryCode + " do not match IANA data",
+                    ianaZoneIds, androidZoneIds);
+        }
+    }
+
+    private void assertImmutableTimeZone(TimeZone timeZone) {
+        try {
+            timeZone.setRawOffset(1000);
+            fail();
+        } catch (UnsupportedOperationException expected) {
+        }
+    }
+
+    private static void assertImmutableList(List<TimeZone> timeZones) {
+        try {
+            timeZones.add(null);
+            fail();
+        } catch (UnsupportedOperationException expected) {
+        }
+    }
+
+    private static void assertZoneEquals(TimeZone expected, TimeZone actual) {
+        // TimeZone.equals() only checks the ID, but that's ok for these tests.
+        assertEquals(expected, actual);
+    }
+
+    private static void assertZonesEqual(List<TimeZone> expected, List<TimeZone> actual) {
+        // TimeZone.equals() only checks the ID, but that's ok for these tests.
+        assertEquals(expected, actual);
+    }
+
+    private static void checkValidateThrowsParserException(String xml) throws Exception {
+        try {
+            validate(xml);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    private static TimeZoneFinder validate(String xml) throws IOException {
+        TimeZoneFinder timeZoneFinder = TimeZoneFinder.createInstanceForTests(xml);
+        timeZoneFinder.validate();
+        return timeZoneFinder;
+    }
+
+    private static List<TimeZone> zones(String... ids) {
+        return Arrays.stream(ids).map(TimeZone::getTimeZone).collect(Collectors.toList());
+    }
+
+    private String createFile(String fileContent) throws IOException {
+        Path filePath = Files.createTempFile(testDir, null, null);
+        Files.write(filePath, fileContent.getBytes(StandardCharsets.UTF_8));
+        return filePath.toString();
+    }
+
+    private String createMissingFile() throws IOException {
+        Path filePath = Files.createTempFile(testDir, null, null);
+        Files.delete(filePath);
+        return filePath.toString();
+    }
+}
diff --git a/non_openjdk_java_files.mk b/non_openjdk_java_files.mk
index 31ee139..9d33f46 100644
--- a/non_openjdk_java_files.mk
+++ b/non_openjdk_java_files.mk
@@ -308,6 +308,7 @@
   luni/src/main/java/libcore/util/RecoverySystem.java \
   luni/src/main/java/libcore/util/SneakyThrow.java \
   luni/src/main/java/libcore/util/TimeZoneDataFiles.java \
+  luni/src/main/java/libcore/util/TimeZoneFinder.java \
   luni/src/main/java/libcore/util/ZoneInfo.java \
   luni/src/main/java/libcore/util/ZoneInfoDB.java \
   luni/src/main/java/libcore/util/HexEncoding.java \
diff --git a/ojluni/src/main/java/java/lang/System.java b/ojluni/src/main/java/java/lang/System.java
index 6e8f74f..ef08be0 100644
--- a/ojluni/src/main/java/java/lang/System.java
+++ b/ojluni/src/main/java/java/lang/System.java
@@ -989,7 +989,7 @@
         }
         p.put("os.version", info.release);
 
-        // Undocumented Android-only properties.
+        // Android-added: Undocumented properties that exist only on Android.
         p.put("android.icu.library.version", ICU.getIcuVersion());
         p.put("android.icu.unicode.version", ICU.getUnicodeVersion());
         p.put("android.icu.cldr.version", ICU.getCldrVersion());
diff --git a/ojluni/src/main/java/java/lang/invoke/CallSite.java b/ojluni/src/main/java/java/lang/invoke/CallSite.java
index b2226bf..85b4bb9 100644
--- a/ojluni/src/main/java/java/lang/invoke/CallSite.java
+++ b/ojluni/src/main/java/java/lang/invoke/CallSite.java
@@ -260,7 +260,7 @@
         }
     }
 
-    /* Android-changed: not used. */
+    // Android-changed: not used.
     // /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */
     // /*package-private*/
     // static Empty uninitializedCallSite() {
@@ -292,7 +292,7 @@
         UNSAFE.putObjectVolatile(this, TARGET_OFFSET, newTarget);
     }
 
-    /* Android-changed: not used. */
+    // Android-changed: not used.
     // this implements the upcall from the JVM, MethodHandleNatives.makeDynamicCallSite:
     // static CallSite makeSite(MethodHandle bootstrapMethod,
     //                          // Callee information:
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
index 6e414d0..94c9917 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
@@ -1978,22 +1978,18 @@
      * @throws NullPointerException if either argument is null
      * @throws WrongMethodTypeException if the conversion cannot be made
      * @see MethodHandle#asType
-     *
-     * @hide
      */
     public static
     MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
         explicitCastArgumentsChecks(target, newType);
         // use the asTypeCache when possible:
         MethodType oldType = target.type();
-        if (oldType == newType)  return target;
+        if (oldType == newType) return target;
         if (oldType.explicitCastEquivalentToAsType(newType)) {
             return target.asFixedArity().asType(newType);
         }
 
-        // return MethodHandleImpl.makePairwiseConvert(target, newType, false);
-        // TODO(narayan): Implement explicitCastArguments, remove @hide.
-        throw new UnsupportedOperationException("MethodHandles.explicitCastArguments is not implemented");
+        return new Transformers.ExplicitCastArguments(target, newType);
     }
 
     private static void explicitCastArgumentsChecks(MethodHandle target, MethodType newType) {
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodType.java b/ojluni/src/main/java/java/lang/invoke/MethodType.java
index 61e0675..bfa7ccd 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodType.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodType.java
@@ -870,18 +870,20 @@
      *  MHs.eCA has the following "upgrades" to MH.asType:
      *  1. interfaces are unchecked (that is, treated as if aliased to Object)
      *     Therefore, {@code Object->CharSequence} is possible in both cases but has different semantics
-     *  2. the full matrix of primitive-to-primitive conversions is supported
-     *     Narrowing like {@code long->byte} and basic-typing like {@code boolean->int}
-     *     are not supported by asType, but anything supported by asType is equivalent
-     *     with MHs.eCE.
+     *  2a. the full matrix of primitive-to-primitive conversions is supported
+     *      Narrowing like {@code long->byte} and basic-typing like {@code boolean->int}
+     *      are not supported by asType, but anything supported by asType is equivalent
+     *      with MHs.eCE.
+     *  2b. conversion of void->primitive means explicit cast has to insert zero/false/null.
      *  3a. unboxing conversions can be followed by the full matrix of primitive conversions
      *  3b. unboxing of null is permitted (creates a zero primitive value)
      * Other than interfaces, reference-to-reference conversions are the same.
      * Boxing primitives to references is the same for both operators.
      */
     private static boolean explicitCastEquivalentToAsType(Class<?> src, Class<?> dst) {
-        if (src == dst || dst == Object.class || dst == void.class)  return true;
-        if (src.isPrimitive()) {
+        if (src == dst || dst == Object.class || dst == void.class) {
+            return true;
+        } else if (src.isPrimitive() && src != void.class) {
             // Could be a prim/prim conversion, where casting is a strict superset.
             // Or a boxing conversion, which is always to an exact wrapper class.
             return canConvert(src, dst);
diff --git a/ojluni/src/main/java/java/lang/invoke/Transformers.java b/ojluni/src/main/java/java/lang/invoke/Transformers.java
index 82cc797..75ee192 100644
--- a/ojluni/src/main/java/java/lang/invoke/Transformers.java
+++ b/ojluni/src/main/java/java/lang/invoke/Transformers.java
@@ -22,6 +22,7 @@
 
 import dalvik.system.EmulatedStackFrame;
 import dalvik.system.EmulatedStackFrame.Range;
+import dalvik.system.EmulatedStackFrame.StackFrameAccessor;
 import dalvik.system.EmulatedStackFrame.StackFrameReader;
 import dalvik.system.EmulatedStackFrame.StackFrameWriter;
 import java.lang.reflect.Array;
@@ -742,11 +743,7 @@
                     case 'F': { o = reader.nextFloat(); break; }
                     case 'D': { o = reader.nextDouble(); break; }
                 }
-                if (o != null && !elementType.isAssignableFrom(o.getClass())) {
-                    throw new ClassCastException(
-                        o.getClass() + " not assignable to " + elementType);
-                }
-                Array.set(arityArray, i, o);
+                Array.set(arityArray, i, elementType.cast(o));
             }
             return arityArray;
         }
@@ -1765,4 +1762,409 @@
             calleeFrame.copyReturnValueTo(stackFrame);
         }
     }
+
+
+    /**
+     * Implements {@link java.lang.invokeMethodHandles#explicitCastArguments()}.
+     */
+    public static class ExplicitCastArguments extends Transformer {
+        private final MethodHandle target;
+
+        public ExplicitCastArguments(MethodHandle target, MethodType type) {
+            super(type);
+            this.target = target;
+        }
+
+        @Override
+        public void transform(EmulatedStackFrame callerFrame) throws Throwable {
+            // Create a new stack frame for the target.
+            EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
+
+            explicitCastArguments(callerFrame, targetFrame);
+            target.invoke(targetFrame);
+            explicitCastReturnValue(callerFrame, targetFrame);
+        }
+
+        private void explicitCastArguments(final EmulatedStackFrame callerFrame,
+                                           final EmulatedStackFrame targetFrame) {
+            final StackFrameReader reader = new StackFrameReader();
+            reader.attach(callerFrame);
+            final StackFrameWriter writer = new StackFrameWriter();
+            writer.attach(targetFrame);
+
+            final Class<?>[] fromTypes = type().ptypes();
+            final Class<?>[] toTypes = target.type().ptypes();
+            for (int i = 0; i < fromTypes.length; ++i) {
+                explicitCast(reader, fromTypes[i], writer, toTypes[i]);
+            }
+        }
+
+        private void explicitCastReturnValue(final EmulatedStackFrame callerFrame,
+                                             final EmulatedStackFrame targetFrame) {
+            Class<?> from = target.type().rtype();
+            Class<?> to = type().rtype();
+            if (to != void.class) {
+                final StackFrameWriter writer = new StackFrameWriter();
+                writer.attach(callerFrame);
+                writer.makeReturnValueAccessor();
+                if (from == void.class) {
+                    if (to.isPrimitive()) {
+                        unboxNull(writer, to);
+                    } else {
+                        writer.putNextReference(null, to);
+                    }
+                } else {
+                    final StackFrameReader reader = new StackFrameReader();
+                    reader.attach(targetFrame);
+                    reader.makeReturnValueAccessor();
+                    explicitCast(reader, target.type().rtype(), writer, type().rtype());
+                }
+            }
+        }
+
+        private static void throwUnexpectedType(final Class<?> unexpectedType) {
+            throw new InternalError("Unexpected type: " + unexpectedType);
+        }
+
+        private static void explicitCastFromBoolean(boolean fromValue,
+                                                    final StackFrameWriter writer,
+                                                    final Class<?> to) {
+            int value = fromValue ? 1 : 0;
+            if (to == byte.class) {
+                writer.putNextByte((byte) value);
+            } else if (to == char.class) {
+                writer.putNextChar((char) value);
+            } else if (to == short.class) {
+                writer.putNextShort((short) value);
+            } else if (to == int.class) {
+                writer.putNextInt(value);
+            } else if (to == long.class) {
+                writer.putNextLong(value);
+            } else if (to == float.class) {
+                writer.putNextFloat(value);
+            } else if (to == double.class) {
+                writer.putNextDouble(value);
+            } else {
+                throwUnexpectedType(to);
+            }
+        }
+
+        /**
+         * Converts byte value to boolean according to
+         * {@link java.lang.invoke.MethodHandles#explicitCast()}
+         */
+        private static boolean toBoolean(byte value) {
+            return (value & 1) == 1;
+        }
+
+        private static byte readPrimitiveAsByte(final StackFrameReader reader,
+                                                final Class<?> from) {
+            if (from == byte.class) {
+                return (byte) reader.nextByte();
+            } else if (from == char.class) {
+                return (byte) reader.nextChar();
+            } else if (from == short.class) {
+                return (byte) reader.nextShort();
+            } else if (from == int.class) {
+                return (byte) reader.nextInt();
+            } else if (from == long.class) {
+                return (byte) reader.nextLong();
+            } else if (from == float.class) {
+                return (byte) reader.nextFloat();
+            } else if (from == double.class) {
+                return (byte) reader.nextDouble();
+            } else {
+                throwUnexpectedType(from);
+                return 0;
+            }
+        }
+
+        private static char readPrimitiveAsChar(final StackFrameReader reader,
+                                                final Class<?> from) {
+            if (from == byte.class) {
+                return (char) reader.nextByte();
+            } else if (from == char.class) {
+                return (char) reader.nextChar();
+            } else if (from == short.class) {
+                return (char) reader.nextShort();
+            } else if (from == int.class) {
+                return (char) reader.nextInt();
+            } else if (from == long.class) {
+                return (char) reader.nextLong();
+            } else if (from == float.class) {
+                return (char) reader.nextFloat();
+            } else if (from == double.class) {
+                return (char) reader.nextDouble();
+            } else {
+                throwUnexpectedType(from);
+                return 0;
+            }
+        }
+
+        private static short readPrimitiveAsShort(final StackFrameReader reader,
+                                                  final Class<?> from) {
+            if (from == byte.class) {
+                return (short) reader.nextByte();
+            } else if (from == char.class) {
+                return (short) reader.nextChar();
+            } else if (from == short.class) {
+                return (short) reader.nextShort();
+            } else if (from == int.class) {
+                return (short) reader.nextInt();
+            } else if (from == long.class) {
+                return (short) reader.nextLong();
+            } else if (from == float.class) {
+                return (short) reader.nextFloat();
+            } else if (from == double.class) {
+                return (short) reader.nextDouble();
+            } else {
+                throwUnexpectedType(from);
+                return 0;
+            }
+        }
+
+        private static int readPrimitiveAsInt(final StackFrameReader reader,
+                                              final Class<?> from) {
+            if (from == byte.class) {
+                return (int) reader.nextByte();
+            } else if (from == char.class) {
+                return (int) reader.nextChar();
+            } else if (from == short.class) {
+                return (int) reader.nextShort();
+            } else if (from == int.class) {
+                return (int) reader.nextInt();
+            } else if (from == long.class) {
+                return (int) reader.nextLong();
+            } else if (from == float.class) {
+                return (int) reader.nextFloat();
+            } else if (from == double.class) {
+                return (int) reader.nextDouble();
+            } else {
+                throwUnexpectedType(from);
+                return 0;
+            }
+        }
+
+        private static long readPrimitiveAsLong(final StackFrameReader reader,
+                                                final Class<?> from) {
+            if (from == byte.class) {
+                return (long) reader.nextByte();
+            } else if (from == char.class) {
+                return (long) reader.nextChar();
+            } else if (from == short.class) {
+                return (long) reader.nextShort();
+            } else if (from == int.class) {
+                return (long) reader.nextInt();
+            } else if (from == long.class) {
+                return (long) reader.nextLong();
+            } else if (from == float.class) {
+                return (long) reader.nextFloat();
+            } else if (from == double.class) {
+                return (long) reader.nextDouble();
+            } else {
+                throwUnexpectedType(from);
+                return 0;
+            }
+        }
+
+        private static float readPrimitiveAsFloat(final StackFrameReader reader,
+                                                  final Class<?> from) {
+            if (from == byte.class) {
+                return (float) reader.nextByte();
+            } else if (from == char.class) {
+                return (float) reader.nextChar();
+            } else if (from == short.class) {
+                return (float) reader.nextShort();
+            } else if (from == int.class) {
+                return (float) reader.nextInt();
+            } else if (from == long.class) {
+                return (float) reader.nextLong();
+            } else if (from == float.class) {
+                return (float) reader.nextFloat();
+            } else if (from == double.class) {
+                return (float) reader.nextDouble();
+            } else {
+                throwUnexpectedType(from);
+                return 0;
+            }
+        }
+
+        private static double readPrimitiveAsDouble(final StackFrameReader reader,
+                                                    final Class<?> from) {
+            if (from == byte.class) {
+                return (double) reader.nextByte();
+            } else if (from == char.class) {
+                return (double) reader.nextChar();
+            } else if (from == short.class) {
+                return (double) reader.nextShort();
+            } else if (from == int.class) {
+                return (double) reader.nextInt();
+            } else if (from == long.class) {
+                return (double) reader.nextLong();
+            } else if (from == float.class) {
+                return (double) reader.nextFloat();
+            } else if (from == double.class) {
+                return (double) reader.nextDouble();
+            } else {
+                throwUnexpectedType(from);
+                return 0;
+            }
+        }
+
+        private static void explicitCastToBoolean(final StackFrameReader reader,
+                                                  final Class<?> from,
+                                                  final StackFrameWriter writer) {
+            byte byteValue = readPrimitiveAsByte(reader, from);
+            writer.putNextBoolean(toBoolean(byteValue));
+        }
+
+        private static void explicitCastPrimitives(final StackFrameReader reader,
+                                                   final Class<?> from,
+                                                   final StackFrameWriter writer,
+                                                   final Class<?> to) {
+            if (to == byte.class) {
+                byte value = readPrimitiveAsByte(reader, from);
+                writer.putNextByte(value);
+            } else if (to == char.class) {
+                char value = readPrimitiveAsChar(reader, from);
+                writer.putNextChar(value);
+            } else if (to == short.class) {
+                short value = readPrimitiveAsShort(reader, from);
+                writer.putNextShort(value);
+            } else if (to == int.class) {
+                int value = readPrimitiveAsInt(reader, from);
+                writer.putNextInt(value);
+            } else if (to == long.class) {
+                long value = readPrimitiveAsLong(reader, from);
+                writer.putNextLong(value);
+            } else if (to == float.class) {
+                float value = readPrimitiveAsFloat(reader, from);
+                writer.putNextFloat(value);
+            } else if (to == double.class) {
+                double value = readPrimitiveAsDouble(reader, from);
+                writer.putNextDouble(value);
+            } else {
+                throwUnexpectedType(to);
+            }
+        }
+
+        private static void unboxNull(final StackFrameWriter writer, final Class<?> to) {
+            if (to == boolean.class) {
+                writer.putNextBoolean(false);
+            } else if (to == byte.class) {
+                writer.putNextByte((byte) 0);
+            } else if (to == char.class) {
+                writer.putNextChar((char) 0);
+            } else if (to == short.class) {
+                writer.putNextShort((short) 0);
+            } else if (to == int.class) {
+                writer.putNextInt((int) 0);
+            } else if (to == long.class) {
+                writer.putNextLong((long) 0);
+            } else if (to == float.class) {
+                writer.putNextFloat((float) 0);
+            } else if (to == double.class) {
+                writer.putNextDouble((double) 0);
+            } else {
+                throwUnexpectedType(to);
+            }
+        }
+
+        private static void unboxNonNull(final Object ref, final Class<?> from,
+                                         final StackFrameWriter writer, final Class<?> to) {
+            if (to == boolean.class) {
+                if (from == Boolean.class) {
+                    writer.putNextBoolean((boolean) ref);
+                } else if (from == Float.class || from == Double.class) {
+                    byte b = (byte) ((double) ref);
+                    writer.putNextBoolean(toBoolean(b));
+                } else {
+                    byte b = (byte) ((long) ref);
+                    writer.putNextBoolean(toBoolean(b));
+                }
+            } else if (to == byte.class) {
+                writer.putNextByte((byte) ref);
+            } else if (to == char.class) {
+                writer.putNextChar((char) ref);
+            } else if (to == short.class) {
+                writer.putNextShort((short) ref);
+            } else if (to == int.class) {
+                writer.putNextInt((int) ref);
+            } else if (to == long.class) {
+                writer.putNextLong((long) ref);
+            } else if (to == float.class) {
+                writer.putNextFloat((float) ref);
+            } else if (to == double.class) {
+                writer.putNextDouble((double) ref);
+            } else {
+                throwUnexpectedType(to);
+            }
+        }
+
+        private static void unbox(final Object ref, final Class<?> from,
+                                  final StackFrameWriter writer, final Class<?> to) {
+            if (ref == null) {
+                unboxNull(writer, to);
+            } else {
+                unboxNonNull(ref, from, writer, to);
+            }
+        }
+
+        private static void box(final StackFrameReader reader, final Class<?> from,
+                                final StackFrameWriter writer, final Class<?> to) {
+            Object boxed = null;
+            if (from == boolean.class) {
+                boxed = Boolean.valueOf(reader.nextBoolean());
+            } else if (from == byte.class) {
+                boxed = Byte.valueOf(reader.nextByte());
+            } else if (from == char.class) {
+                boxed = Character.valueOf(reader.nextChar());
+            } else if (from == short.class) {
+                boxed = Short.valueOf(reader.nextShort());
+            } else if (from == int.class) {
+                boxed = Integer.valueOf(reader.nextInt());
+            } else if (from == long.class) {
+                boxed = Long.valueOf(reader.nextLong());
+            } else if (from == float.class) {
+                boxed = Float.valueOf(reader.nextFloat());
+            } else if (from == double.class) {
+                boxed = Double.valueOf(reader.nextDouble());
+            } else {
+                throwUnexpectedType(from);
+            }
+            writer.putNextReference(to.cast(boxed), to);
+        }
+
+        private static void explicitCast(final StackFrameReader reader, final Class<?> from,
+                                         final StackFrameWriter writer, final Class<?> to) {
+            if (from.equals(to)) {
+                StackFrameAccessor.copyNext(reader, writer, from);
+            } else if (!from.isPrimitive()) {
+                Object ref = reader.nextReference(from);
+                if (to.isInterface()) {
+                    // Pass from without a cast according to description for
+                    // {@link java.lang.invoke.MethodHandles#explicitCastArguments()}.
+                    writer.putNextReference(ref, to);
+                } else if (!to.isPrimitive()) {
+                    // |to| is a reference type, perform class cast check.
+                    writer.putNextReference(to.cast(ref), to);
+                } else {
+                    // |from| is a reference type, |to| is a primitive type,
+                    unbox(ref, from, writer, to);
+                }
+            } else if (to.isPrimitive()) {
+                // |from| and |to| are primitive types.
+                if (from == boolean.class) {
+                    explicitCastFromBoolean(reader.nextBoolean(), writer, to);
+                } else if (to == boolean.class) {
+                    explicitCastToBoolean(reader, from, writer);
+                } else {
+                    explicitCastPrimitives(reader, from, writer, to);
+                }
+            } else {
+                // |from| is a primitive type, |to| is a reference type.
+                box(reader, from, writer, to);
+            }
+        }
+    }
 }
diff --git a/ojluni/src/main/java/java/net/AbstractPlainSocketImpl.java b/ojluni/src/main/java/java/net/AbstractPlainSocketImpl.java
index 8c687ec..16cf18c 100644
--- a/ojluni/src/main/java/java/net/AbstractPlainSocketImpl.java
+++ b/ojluni/src/main/java/java/net/AbstractPlainSocketImpl.java
@@ -49,8 +49,8 @@
 {
     /* instance variable for SO_TIMEOUT */
     int timeout;   // timeout in millisec
-    // traffic class
-    private int trafficClass;
+    // Android-removed: traffic class is set through socket
+    // private int trafficClass;
 
     private boolean shut_rd = false;
     private boolean shut_wr = false;
@@ -205,17 +205,19 @@
         if (isClosedOrPending()) {
             throw new SocketException("Socket Closed");
         }
+        // Android-removed: Logic dealing with value type moved to socketSetOption.
+        /*
         boolean on = true;
         switch (opt) {
             /* check type safety b4 going native.  These should never
              * fail, since only java.Socket* has access to
              * PlainSocketImpl.setOption().
-             */
+             *
         case SO_LINGER:
             if (val == null || (!(val instanceof Integer) && !(val instanceof Boolean)))
                 throw new SocketException("Bad parameter for option");
             if (val instanceof Boolean) {
-                /* true only if disabling - enabling should be Integer */
+                /* true only if disabling - enabling should be Integer *
                 on = false;
             }
             break;
@@ -267,6 +269,11 @@
             throw new SocketException("unrecognized TCP option: " + opt);
         }
         socketSetOption(opt, on, val);
+        */
+        if (opt == SO_TIMEOUT) {
+            timeout = (Integer) val;
+        }
+        socketSetOption(opt, val);
     }
     public Object getOption(int opt) throws SocketException {
         if (isClosedOrPending()) {
@@ -275,6 +282,8 @@
         if (opt == SO_TIMEOUT) {
             return new Integer(timeout);
         }
+        // Android-removed: Logic dealing with value type moved to socketGetOption.
+        /*
         int ret = 0;
         /*
          * The native socketGetOption() knows about 3 options.
@@ -282,7 +291,7 @@
          * to what we're asking.  A return of -1 means it understands
          * the option but its turned off.  It will raise a SocketException
          * if "opt" isn't one it understands.
-         */
+         *
 
         switch (opt) {
         case TCP_NODELAY:
@@ -324,6 +333,8 @@
         default:
             return null;
         }
+        */
+        return socketGetOption(opt);
     }
 
     /**
@@ -725,9 +736,12 @@
         throws IOException;
     abstract void socketShutdown(int howto)
         throws IOException;
-    abstract void socketSetOption(int cmd, boolean on, Object value)
-        throws SocketException;
-    abstract int socketGetOption(int opt, Object iaContainerObj) throws SocketException;
+
+    // Android-changed: Method signature changed, socket{Get,Set}Option work directly with Object
+    // values.
+    abstract void socketSetOption(int cmd, Object value) throws SocketException;
+    abstract Object socketGetOption(int opt) throws SocketException;
+
     abstract void socketSendUrgentData(int data)
         throws IOException;
 
diff --git a/ojluni/src/main/java/java/net/DatagramSocket.java b/ojluni/src/main/java/java/net/DatagramSocket.java
index 34f3a2d..31a4db4 100755
--- a/ojluni/src/main/java/java/net/DatagramSocket.java
+++ b/ojluni/src/main/java/java/net/DatagramSocket.java
@@ -1347,17 +1347,16 @@
         factory = fac;
     }
 
+    // Android-added: for testing and internal use.
     /**
-     * Android-added: for testing and internal use.
-     *
      * @hide internal use only
      */
     public FileDescriptor getFileDescriptor$() {
         return impl.fd;
     }
 
+    // Android-added: setNetworkInterface() to set the network interface used by this socket.
     /**
-     * Android-added:
      * Sets the network interface used by this socket.  Any packets sent
      * via this socket are transmitted via the specified interface.  Any
      * packets received by this socket will come from the specified
diff --git a/ojluni/src/main/java/java/net/Inet6Address.java b/ojluni/src/main/java/java/net/Inet6Address.java
index 51b0927..ee946ee 100644
--- a/ojluni/src/main/java/java/net/Inet6Address.java
+++ b/ojluni/src/main/java/java/net/Inet6Address.java
@@ -262,7 +262,8 @@
             }
         }
 
-        /* ----- Android-removed -----
+        // Android-removed: getnameinfo returns smarter representations than getHostAddress()
+        /*
         String getHostAddress() {
             String s = numericToTextFormat(ipaddress);
             if (scope_ifname != null) { // must check this first
@@ -271,7 +272,8 @@
                 s = s + "%" + scope_id;
             }
             return s;
-        } */
+        }
+        */
 
         public boolean equals(Object o) {
             if (! (o instanceof Inet6AddressHolder)) {
@@ -870,7 +872,7 @@
      */
     @Override
     public String getHostAddress() {
-        // Android-changed: getnameinfo returns smarter representations
+        // Android-changed: getnameinfo returns smarter representations than getHostAddress()
         // return holder6.getHostAddress();
         return Libcore.os.getnameinfo(this, NI_NUMERICHOST); // Can't throw.
     }
diff --git a/ojluni/src/main/java/java/net/PlainSocketImpl.java b/ojluni/src/main/java/java/net/PlainSocketImpl.java
index f02006d..656defc 100644
--- a/ojluni/src/main/java/java/net/PlainSocketImpl.java
+++ b/ojluni/src/main/java/java/net/PlainSocketImpl.java
@@ -101,9 +101,9 @@
         return (T)flow;
     }
 
-    protected void socketSetOption(int opt, boolean b, Object val) throws SocketException {
+    protected void socketSetOption(int opt, Object val) throws SocketException {
         try {
-            socketSetOption0(opt, b, val);
+            socketSetOption0(opt, val);
         } catch (SocketException se) {
             if (socket == null || !socket.isConnected())
                 throw se;
@@ -268,10 +268,18 @@
         }
     }
 
-    native void socketSetOption0(int cmd, boolean on, Object value)
-        throws SocketException;
+    void socketSetOption0(int cmd, Object value) throws SocketException {
+        // OpenJDK does not set SO_TIMEOUT on Linux.
+        if (cmd == SO_TIMEOUT) {
+            return;
+        }
 
-    native int socketGetOption(int opt, Object iaContainerObj) throws SocketException;
+        IoBridge.setSocketOption(fd, cmd, value);
+    }
+
+    Object socketGetOption(int opt) throws SocketException {
+        return IoBridge.getSocketOption(fd, opt);
+    }
 
     void socketSendUrgentData(int data) throws IOException {
         if (fd == null || !fd.valid()) {
diff --git a/ojluni/src/main/java/java/net/ServerSocket.java b/ojluni/src/main/java/java/net/ServerSocket.java
index 670acf2..20ae95a 100644
--- a/ojluni/src/main/java/java/net/ServerSocket.java
+++ b/ojluni/src/main/java/java/net/ServerSocket.java
@@ -922,9 +922,8 @@
         /* Not implemented yet */
     }
 
+    // Android-added: for testing and internal use.
     /**
-     * Android-added: for testing and internal use.
-     *
      * @hide internal use only
      */
     public FileDescriptor getFileDescriptor$() {
diff --git a/ojluni/src/main/java/java/net/Socket.java b/ojluni/src/main/java/java/net/Socket.java
index 9d566b1..03e2b71 100644
--- a/ojluni/src/main/java/java/net/Socket.java
+++ b/ojluni/src/main/java/java/net/Socket.java
@@ -1768,9 +1768,8 @@
         /* Not implemented yet */
     }
 
+    // Android-added: for testing and internal use.
     /**
-     * Android-added: for testing and internal use.
-     *
      * @hide internal use only
      */
     public FileDescriptor getFileDescriptor$() {
diff --git a/ojluni/src/main/java/java/net/SocksSocketImpl.java b/ojluni/src/main/java/java/net/SocksSocketImpl.java
index f1b29b0..a81e219 100644
--- a/ojluni/src/main/java/java/net/SocksSocketImpl.java
+++ b/ojluni/src/main/java/java/net/SocksSocketImpl.java
@@ -347,8 +347,9 @@
                                       epoint.getPort());
         }
         if (server == null) {
+            // Android-removed: Logic to establish proxy connection based on default ProxySelector
             /*
-             * Android-changed: Removed code that tried to establish proxy connection if
+             * Removed code that tried to establish proxy connection if
              * ProxySelector#getDefault() is not null.
              * This was never the case in previous android releases, was causing
              * issues and therefore was removed.
diff --git a/ojluni/src/main/java/java/security/KeyPairGenerator.java b/ojluni/src/main/java/java/security/KeyPairGenerator.java
index 1e46cee..68ab5e9 100644
--- a/ojluni/src/main/java/java/security/KeyPairGenerator.java
+++ b/ojluni/src/main/java/java/security/KeyPairGenerator.java
@@ -34,11 +34,6 @@
 import sun.security.jca.*;
 import sun.security.jca.GetInstance.Instance;
 
-/*
-Android-removed: this debugging mechanism is not supported in Android.
-import sun.security.util.Debug;
-*/
-
 /**
  * The KeyPairGenerator class is used to generate pairs of
  * public and private keys. Key pair generators are constructed using the
@@ -149,8 +144,8 @@
 
 public abstract class KeyPairGenerator extends KeyPairGeneratorSpi {
 
+    // Android-removed: this debugging mechanism is not used in Android.
     /*
-    Android-removed: this debugging mechanism is not supported in Android.
     private static final Debug pdebug =
                         Debug.getInstance("provider", "Provider");
     private static final boolean skipDebug =
@@ -199,8 +194,8 @@
         }
         kpg.provider = instance.provider;
 
+        // Android-removed: this debugging mechanism is not used in Android.
         /*
-        Android-removed: this debugging mechanism is not supported in Android.
         if (!skipDebug && pdebug != null) {
             pdebug.println("KeyPairGenerator." + algorithm +
                 " algorithm from: " + kpg.provider.getName());
@@ -598,8 +593,8 @@
             this.serviceIterator = serviceIterator;
             initType = I_NONE;
 
+            // Android-removed: this debugging mechanism is not used in Android.
             /*
-            Android-removed: this debugging mechanism is not supported in Android.
             if (!skipDebug && pdebug != null) {
                 pdebug.println("KeyPairGenerator." + algorithm +
                     " algorithm from: " + provider.getName());
diff --git a/ojluni/src/main/java/java/security/MessageDigest.java b/ojluni/src/main/java/java/security/MessageDigest.java
index df6c456..5f586ac 100644
--- a/ojluni/src/main/java/java/security/MessageDigest.java
+++ b/ojluni/src/main/java/java/security/MessageDigest.java
@@ -35,10 +35,6 @@
 
 import java.nio.ByteBuffer;
 
-/*
-Android-removed: this debugging mechanism is not available in Android.
-import sun.security.util.Debug;
-*/
 /**
  * This MessageDigest class provides applications the functionality of a
  * message digest algorithm, such as SHA-1 or SHA-256.
@@ -134,8 +130,8 @@
 
 public abstract class MessageDigest extends MessageDigestSpi {
 
+    // Android-removed: this debugging mechanism is not used in Android.
     /*
-    Android-removed: this debugging mechanism is not available in Android.
     private static final Debug pdebug =
                         Debug.getInstance("provider", "Provider");
     private static final boolean skipDebug =
@@ -205,8 +201,8 @@
             }
             md.provider = (Provider)objs[1];
 
+            // Android-removed: this debugging mechanism is not used in Android.
             /*
-            Android-removed: this debugging mechanism is not available in Android.
             if (!skipDebug && pdebug != null) {
                 pdebug.println("MessageDigest." + algorithm +
                     " algorithm from: " + md.provider.getName());
diff --git a/ojluni/src/main/java/java/security/Provider.java b/ojluni/src/main/java/java/security/Provider.java
index d38882a..d1fbd29 100644
--- a/ojluni/src/main/java/java/security/Provider.java
+++ b/ojluni/src/main/java/java/security/Provider.java
@@ -977,7 +977,7 @@
             if (typeAndAlg == null) {
                 return;
             }
-            String type = typeAndAlg[0];
+            String type = getEngineName(typeAndAlg[0]);
             String aliasAlg = typeAndAlg[1].intern();
             ServiceKey key = new ServiceKey(type, stdAlg, true);
             Service s = legacyMap.get(key);
@@ -997,7 +997,7 @@
             int i = typeAndAlg[1].indexOf(' ');
             if (i == -1) {
                 // e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA");
-                String type = typeAndAlg[0];
+                String type = getEngineName(typeAndAlg[0]);
                 String stdAlg = typeAndAlg[1].intern();
                 String className = value;
                 ServiceKey key = new ServiceKey(type, stdAlg, true);
@@ -1012,7 +1012,7 @@
             } else { // attribute
                 // e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software");
                 String attributeValue = value;
-                String type = typeAndAlg[0];
+                String type = getEngineName(typeAndAlg[0]);
                 String attributeString = typeAndAlg[1];
                 String stdAlg = attributeString.substring(0, i).intern();
                 String attributeName = attributeString.substring(i + 1);
@@ -1324,10 +1324,8 @@
 
     private static void addEngine(String name, boolean sp, String paramName) {
         EngineDescription ed = new EngineDescription(name, sp, paramName);
-        // NOTE: The original OpenJDK code supported case-insensitive lookups on the list
-        // of known engines.
-        //
-        // knownEngines.put(name.toLowerCase(ENGLISH), ed);
+        // also index by canonical name to avoid toLowerCase() for some lookups
+        knownEngines.put(name.toLowerCase(ENGLISH), ed);
         knownEngines.put(name, ed);
     }
 
@@ -1378,6 +1376,17 @@
                             "java.lang.Object");
     }
 
+    // get the "standard" (mixed-case) engine name for arbitary case engine name
+    // if there is no known engine by that name, return s
+    private static String getEngineName(String s) {
+        // try original case first, usually correct
+        EngineDescription e = knownEngines.get(s);
+        if (e == null) {
+            e = knownEngines.get(s.toLowerCase(ENGLISH));
+        }
+        return (e == null) ? s : e.name;
+    }
+
     /**
      * The description of a security service. It encapsulates the properties
      * of a service and contains a factory method to obtain new implementation
@@ -1481,8 +1490,7 @@
                 throw new NullPointerException();
             }
             this.provider = provider;
-            // Android-changed.
-            this.type = type;
+            this.type = getEngineName(type);
             this.algorithm = algorithm;
             this.className = className;
             if (aliases == null) {
diff --git a/ojluni/src/main/java/java/security/SecureRandom.java b/ojluni/src/main/java/java/security/SecureRandom.java
index 78eac68..f512264 100644
--- a/ojluni/src/main/java/java/security/SecureRandom.java
+++ b/ojluni/src/main/java/java/security/SecureRandom.java
@@ -99,7 +99,7 @@
 
 public class SecureRandom extends java.util.Random {
 
-    // Android-removed: this debugging mechanism is not supported in Android.
+    // Android-removed: this debugging mechanism is not used in Android.
     /*
     private static final Debug pdebug =
                         Debug.getInstance("provider", "Provider");
diff --git a/ojluni/src/main/java/java/text/DateFormatSymbols.java b/ojluni/src/main/java/java/text/DateFormatSymbols.java
index aa57aad..96a966c 100644
--- a/ojluni/src/main/java/java/text/DateFormatSymbols.java
+++ b/ojluni/src/main/java/java/text/DateFormatSymbols.java
@@ -865,35 +865,17 @@
         }
     }
 
+    // BEGIN Android-changed: extract initialization of zoneStrings to separate method.
     private final synchronized String[][] internalZoneStrings() {
         if (zoneStrings == null) {
             zoneStrings = TimeZoneNames.getZoneStrings(locale);
-            // If icu4c doesn't have a name, our array contains a null. TimeZone.getDisplayName
-            // knows how to format GMT offsets (and, unlike icu4c, has accurate data). http://b/8128460.
-            for (String[] zone : zoneStrings) {
-                String id = zone[0];
-                if (zone[1] == null) {
-                    zone[1] =
-                        TimeZone.getTimeZone(id).getDisplayName(false, TimeZone.LONG, locale);
-                }
-                if (zone[2] == null) {
-                    zone[2] =
-                        TimeZone.getTimeZone(id).getDisplayName(false, TimeZone.SHORT, locale);
-                }
-                if (zone[3] == null) {
-                    zone[3] = TimeZone.getTimeZone(id).getDisplayName(true, TimeZone.LONG, locale);
-                }
-                if (zone[4] == null) {
-                    zone[4] =
-                        TimeZone.getTimeZone(id).getDisplayName(true, TimeZone.SHORT, locale);
-                }
-            }
         }
         return zoneStrings;
     }
 
     private final String[][] getZoneStringsImpl(boolean needsCopy) {
         String[][] zoneStrings = internalZoneStrings();
+        // END Android-changed: extract initialization of zoneStrings to separate method.
 
         if (!needsCopy) {
             return zoneStrings;
@@ -964,6 +946,7 @@
      * @since 1.6
      */
     private void writeObject(ObjectOutputStream stream) throws IOException {
+        // Android-changed: extract initialization of zoneStrings to separate method.
         internalZoneStrings();
         stream.defaultWriteObject();
     }
diff --git a/ojluni/src/main/java/java/text/DecimalFormatSymbols.java b/ojluni/src/main/java/java/text/DecimalFormatSymbols.java
index 833b6b6..9e82738 100644
--- a/ojluni/src/main/java/java/text/DecimalFormatSymbols.java
+++ b/ojluni/src/main/java/java/text/DecimalFormatSymbols.java
@@ -712,24 +712,34 @@
 
     // Android-changed: maybeStripMarkers added in b/26207216, fixed in b/32465689.
     /**
-     * Attempts to strip RTL, LTR and Arabic letter markers from {@code symbol}. If the symbol's
-     * length is 1, then the first char of the symbol is returned. If the symbol's length is more
-     * than 1 and the first char is a marker, then this marker is ignored. In all other cases,
-     * {@code fallback} is returned.
+     * Attempts to strip RTL, LTR and Arabic letter markers from {@code symbol}.
+     * If the string contains a single non-marker character (and any number of marker characters),
+     * then that character is returned, otherwise {@code fallback} is returned.
+     *
+     * @hide
      */
-    private static char maybeStripMarkers(String symbol, char fallback) {
+    // VisibleForTesting
+    public static char maybeStripMarkers(String symbol, char fallback) {
         final int length = symbol.length();
-        if (length == 1) {
-            return symbol.charAt(0);
-        }
-
-        if (length > 1) {
-            char first = symbol.charAt(0);
-            if (first =='\u200E' || first =='\u200F' || first =='\u061C') {
-                return symbol.charAt(1);
+        if (length >= 1) {
+            boolean sawNonMarker = false;
+            char nonMarker = 0;
+            for (int i = 0; i < length; i++) {
+                final char c = symbol.charAt(i);
+                if (c == '\u200E' || c == '\u200F' || c == '\u061C') {
+                    continue;
+                }
+                if (sawNonMarker) {
+                    // More than one non-marker character.
+                    return fallback;
+                }
+                sawNonMarker = true;
+                nonMarker = c;
+            }
+            if (sawNonMarker) {
+                return nonMarker;
             }
         }
-
         return fallback;
     }
 
diff --git a/ojluni/src/main/java/java/util/ArrayList.java b/ojluni/src/main/java/java/util/ArrayList.java
index dd465c9..d65bf1f 100644
--- a/ojluni/src/main/java/java/util/ArrayList.java
+++ b/ojluni/src/main/java/java/util/ArrayList.java
@@ -103,8 +103,8 @@
  * @see     Vector
  * @since   1.2
  */
+// Android-changed: Inlined methods; CME in iterators; throw AIOOBE when toIndex < fromIndex.
 /*
- * Android-changed:
  * - AOSP commit 3be987f0f18648b3c532c8b89d09505e18594241
  *   Inline for improved performance:
  *   - checkForComodification
diff --git a/ojluni/src/main/java/java/util/Arrays.java b/ojluni/src/main/java/java/util/Arrays.java
index b4f52a4..72c5f93 100644
--- a/ojluni/src/main/java/java/util/Arrays.java
+++ b/ojluni/src/main/java/java/util/Arrays.java
@@ -124,11 +124,10 @@
         }
     }
 
+    // BEGIN Android-added: checkOffsetAndCount() helper method for AIOOBE enforcement.
     /**
      * Checks that the range described by {@code offset} and {@code count} doesn't exceed
      * {@code arrayLength}.
-     *
-     * Android-changed.
      * @hide
      */
     public static void checkOffsetAndCount(int arrayLength, int offset, int count) {
@@ -137,6 +136,7 @@
                     count);
         }
     }
+    // END Android-added: checkOffsetAndCount() helper method for AIOOBE enforcement.
 
     /*
      * Sorting methods. Note that all public "sort" methods take the
diff --git a/ojluni/src/main/java/java/util/Collections.java b/ojluni/src/main/java/java/util/Collections.java
index 4818ee6..5d0325f 100644
--- a/ojluni/src/main/java/java/util/Collections.java
+++ b/ojluni/src/main/java/java/util/Collections.java
@@ -151,7 +151,7 @@
     @SuppressWarnings("unchecked")
     public static <T extends Comparable<? super T>> void sort(List<T> list) {
         // Android-changed: Call sort(list, null) here to be consistent
-        // with that method's (Android-changed) behavior.
+        // with that method's (Android changed) behavior.
         // list.sort(null);
         sort(list, null);
     }
diff --git a/ojluni/src/main/java/java/util/LinkedHashMap.java b/ojluni/src/main/java/java/util/LinkedHashMap.java
index 3b5c3af..aec40bc 100644
--- a/ojluni/src/main/java/java/util/LinkedHashMap.java
+++ b/ojluni/src/main/java/java/util/LinkedHashMap.java
@@ -190,7 +190,7 @@
      * LinkedHashMap.Entry is now treated as intermediary node class
      * that can also be converted to tree form.
      *
-     * BEGIN Android-changed
+     // BEGIN Android-changed
      * LinkedHashMapEntry should not be renamed. Specifically, for
      * source compatibility with earlier versions of Android, this
      * nested class must not be named "Entry". Otherwise, it would
@@ -201,7 +201,7 @@
      * To compile, that code snippet's "LinkedHashMap.Entry" must
      * mean java.util.Map.Entry which is the compile time type of
      * entrySet()'s elements.
-     * END Android-changed
+     // END Android-changed
      *
      * The changes in node classes also require using two fields
      * (head, tail) rather than a pointer to a header node to maintain
diff --git a/ojluni/src/main/java/java/util/Optional.java b/ojluni/src/main/java/java/util/Optional.java
index 10b7923..bce8ff9 100644
--- a/ojluni/src/main/java/java/util/Optional.java
+++ b/ojluni/src/main/java/java/util/Optional.java
@@ -29,6 +29,7 @@
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 
+// Android-changed: removed ValueBased paragraph.
 /**
  * A container object which may or may not contain a non-null value.
  * If a value is present, {@code isPresent()} will return {@code true} and
diff --git a/ojluni/src/main/java/java/util/OptionalDouble.java b/ojluni/src/main/java/java/util/OptionalDouble.java
index ead3c91..7112df1 100644
--- a/ojluni/src/main/java/java/util/OptionalDouble.java
+++ b/ojluni/src/main/java/java/util/OptionalDouble.java
@@ -28,6 +28,7 @@
 import java.util.function.DoubleSupplier;
 import java.util.function.Supplier;
 
+// Android-changed: removed ValueBased paragraph.
 /**
  * A container object which may or may not contain a {@code double} value.
  * If a value is present, {@code isPresent()} will return {@code true} and
diff --git a/ojluni/src/main/java/java/util/OptionalInt.java b/ojluni/src/main/java/java/util/OptionalInt.java
index 23c8b8c..7907328 100644
--- a/ojluni/src/main/java/java/util/OptionalInt.java
+++ b/ojluni/src/main/java/java/util/OptionalInt.java
@@ -28,6 +28,7 @@
 import java.util.function.IntSupplier;
 import java.util.function.Supplier;
 
+// Android-changed: removed ValueBased paragraph.
 /**
  * A container object which may or may not contain a {@code int} value.
  * If a value is present, {@code isPresent()} will return {@code true} and
diff --git a/ojluni/src/main/java/java/util/OptionalLong.java b/ojluni/src/main/java/java/util/OptionalLong.java
index 21aa70e..b337d83 100644
--- a/ojluni/src/main/java/java/util/OptionalLong.java
+++ b/ojluni/src/main/java/java/util/OptionalLong.java
@@ -28,6 +28,7 @@
 import java.util.function.LongSupplier;
 import java.util.function.Supplier;
 
+// Android-changed: removed ValueBased paragraph.
 /**
  * A container object which may or may not contain a {@code long} value.
  * If a value is present, {@code isPresent()} will return {@code true} and
diff --git a/ojluni/src/main/java/java/util/ResourceBundle.java b/ojluni/src/main/java/java/util/ResourceBundle.java
index 886ffc1..6fff252 100644
--- a/ojluni/src/main/java/java/util/ResourceBundle.java
+++ b/ojluni/src/main/java/java/util/ResourceBundle.java
@@ -360,7 +360,8 @@
      */
     private volatile Set<String> keySet;
 
-    /* Android-changed: Removed used of ResourceBundleControlProvider.
+    // Android-changed: Removed use of ResourceBundleControlProvider.
+    /*
     private static final List<ResourceBundleControlProvider> providers;
 
     static {
diff --git a/ojluni/src/main/java/java/util/TimeZone.java b/ojluni/src/main/java/java/util/TimeZone.java
index c745291..6ad180b 100644
--- a/ojluni/src/main/java/java/util/TimeZone.java
+++ b/ojluni/src/main/java/java/util/TimeZone.java
@@ -39,20 +39,15 @@
 
 package java.util;
 
+import org.apache.harmony.luni.internal.util.TimezoneGetter;
+import android.icu.text.TimeZoneNames;
 import java.io.IOException;
 import java.io.Serializable;
 import java.time.ZoneId;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.lang.ref.SoftReference;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.concurrent.ConcurrentHashMap;
-import libcore.icu.TimeZoneNames;
 import libcore.io.IoUtils;
 import libcore.util.ZoneInfoDB;
-import sun.security.action.GetPropertyAction;
-import org.apache.harmony.luni.internal.util.TimezoneGetter;
 
 /**
  * <code>TimeZone</code> represents a time zone offset, and also figures out daylight
@@ -387,35 +382,44 @@
      * @param locale the display locale.
      */
     public String getDisplayName(boolean daylightTime, int style, Locale locale) {
-        if (style != SHORT && style != LONG) {
-            throw new IllegalArgumentException("Illegal style: " + style);
+        // BEGIN Android-changed: implement using android.icu.text.TimeZoneNames
+        TimeZoneNames.NameType nameType;
+        switch (style) {
+            case SHORT:
+                nameType = daylightTime
+                        ? TimeZoneNames.NameType.SHORT_DAYLIGHT
+                        : TimeZoneNames.NameType.SHORT_STANDARD;
+                break;
+            case LONG:
+                nameType = daylightTime
+                        ? TimeZoneNames.NameType.LONG_DAYLIGHT
+                        : TimeZoneNames.NameType.LONG_STANDARD;
+                break;
+            default:
+                throw new IllegalArgumentException("Illegal style: " + style);
+        }
+        String canonicalID = android.icu.util.TimeZone.getCanonicalID(getID());
+        if (canonicalID != null) {
+            TimeZoneNames names = TimeZoneNames.getInstance(locale);
+            long now = System.currentTimeMillis();
+            String displayName = names.getDisplayName(canonicalID, nameType, now);
+            if (displayName != null) {
+                return displayName;
+            }
         }
 
-        // Android-changed: implement using libcore.icu.TimeZoneNames
-        String[][] zoneStrings = TimeZoneNames.getZoneStrings(locale);
-        String result = TimeZoneNames.getDisplayName(zoneStrings, getID(), daylightTime, style);
-        if (result != null) {
-            return result;
-        }
-
-        // If we get here, it's because icu4c has nothing for us. Most commonly, this is in the
-        // case of short names. For Pacific/Fiji, for example, icu4c has nothing better to offer
-        // than "GMT+12:00". Why do we re-do this work ourselves? Because we have up-to-date
-        // time zone transition data, which icu4c _doesn't_ use --- it uses its own baked-in copy,
-        // which only gets updated when we update icu4c. http://b/7955614 and http://b/8026776.
-
-        // TODO: should we generate these once, in TimeZoneNames.getDisplayName? Revisit when we
-        // upgrade to icu4c 50 and rewrite the underlying native code. See also the
-        // "element[j] != null" check in SimpleDateFormat.parseTimeZone, and the extra work in
-        // DateFormatSymbols.getZoneStrings.
+        // We get here if this is a custom timezone or ICU doesn't have name data for the specific
+        // style and locale.
         int offsetMillis = getRawOffset();
         if (daylightTime) {
             offsetMillis += getDSTSavings();
         }
         return createGmtOffsetString(true /* includeGmt */, true /* includeMinuteSeparator */,
                 offsetMillis);
+        // END Android-changed: implement using android.icu.text.TimeZoneNames
     }
 
+    // BEGIN Android-added: utility method to format an offset as a GMT offset string.
     /**
      * Returns a string representation of an offset from UTC.
      *
@@ -456,6 +460,7 @@
         }
         builder.append(string);
     }
+    // END Android-added: utility method to format an offset as a GMT offset string.
 
     /**
      * Returns the amount of time to be added to local standard time
diff --git a/ojluni/src/main/java/java/util/XMLUtils.java b/ojluni/src/main/java/java/util/XMLUtils.java
index b7133dc..8f061e5 100644
--- a/ojluni/src/main/java/java/util/XMLUtils.java
+++ b/ojluni/src/main/java/java/util/XMLUtils.java
@@ -92,7 +92,7 @@
     {
         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
         dbf.setIgnoringElementContentWhitespace(true);
-        // Android-chanaged: We don't currently have a validating document builder.
+        // Android-changed: We don't currently have a validating document builder.
         // Revert this if the situation changes.
         //
         // dbf.setValidating(true);
diff --git a/ojluni/src/main/java/java/util/logging/Level.java b/ojluni/src/main/java/java/util/logging/Level.java
index afc7035..c03b171 100644
--- a/ojluni/src/main/java/java/util/logging/Level.java
+++ b/ojluni/src/main/java/java/util/logging/Level.java
@@ -262,7 +262,7 @@
     }
 
     private String computeLocalizedLevelName(Locale newLocale) {
-        // Android-change: Use Thread.currentThread().getContextClassLoader(),
+        // Android-changed: Use Thread.currentThread().getContextClassLoader(),
         // otherwise we might get a BootClassLoader.
         ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName, newLocale,
                                                      Thread.currentThread().getContextClassLoader());
diff --git a/ojluni/src/main/java/javax/crypto/Cipher.java b/ojluni/src/main/java/javax/crypto/Cipher.java
index c2a2183..c707899 100644
--- a/ojluni/src/main/java/javax/crypto/Cipher.java
+++ b/ojluni/src/main/java/javax/crypto/Cipher.java
@@ -44,9 +44,6 @@
 
 import java.nio.ByteBuffer;
 import java.nio.ReadOnlyBufferException;
-/* Android-removed: this debugging mechanism is not used in Android
-import sun.security.util.Debug;
-*/
 import sun.security.jca.*;
 
 /**
@@ -230,7 +227,8 @@
 
 public class Cipher {
 
-    /* Android-removed: this debugging mechanism is not used in Android
+    // Android-removed: this debugging mechanism is not used in Android.
+    /*
     private static final Debug pdebug =
                         Debug.getInstance("provider", "Provider");
     private static final boolean skipDebug =
@@ -835,7 +833,8 @@
 
         initialized = true;
         this.opmode = opmode;
-        /* Android-removed: this debugging mechanism is not used in Android
+        // Android-removed: this debugging mechanism is not used in Android.
+        /*
         if (!skipDebug && pdebug != null) {
             pdebug.println("Cipher." + transformation + " " +
                 getOpmodeString(opmode) + " algorithm from: " +
@@ -975,7 +974,8 @@
         initialized = true;
         this.opmode = opmode;
 
-        /* Android-removed: this debugging mechanism is not used in Android
+        // Android-removed: this debugging mechanism is not used in Android.
+        /*
         if (!skipDebug && pdebug != null) {
             pdebug.println("Cipher." + transformation + " " +
                 getOpmodeString(opmode) + " algorithm from: " +
@@ -1115,7 +1115,8 @@
         initialized = true;
         this.opmode = opmode;
 
-        /* Android-removed: this debugging mechanism is not used in Android
+        // Android-removed: this debugging mechanism is not used in Android.
+        /*
         if (!skipDebug && pdebug != null) {
             pdebug.println("Cipher." + transformation + " " +
                 getOpmodeString(opmode) + " algorithm from: " +
@@ -1301,7 +1302,8 @@
         initialized = true;
         this.opmode = opmode;
 
-        /* Android-removed: this debugging mechanism is not used in Android
+        // Android-removed: this debugging mechanism is not used in Android.
+        /*
         if (!skipDebug && pdebug != null) {
             pdebug.println("Cipher." + transformation + " " +
                 getOpmodeString(opmode) + " algorithm from: " +
@@ -2171,8 +2173,8 @@
      */
     public static final int getMaxAllowedKeyLength(String transformation)
             throws NoSuchAlgorithmException {
-        // Android-changed: Remove references to CryptoPermission and throw early
-        // if transformation == null or isn't valid.
+        // Android-changed: Remove references to CryptoPermission.
+        // Throw early if transformation == null or isn't valid.
         //
         // CryptoPermission cp = getConfiguredPermission(transformation);
         // return cp.getMaxAllowedKeyLength();
@@ -2204,8 +2206,8 @@
      */
     public static final AlgorithmParameterSpec getMaxAllowedParameterSpec(
             String transformation) throws NoSuchAlgorithmException {
-        // Android-changed: Remove references to CryptoPermission and throw early
-        // if transformation == null or isn't valid.
+        // Android-changed: Remove references to CryptoPermission.
+        // Throw early if transformation == null or isn't valid.
         //
         // CryptoPermission cp = getConfiguredPermission(transformation);
         // return cp.getAlgorithmParameterSpec();
diff --git a/ojluni/src/main/java/javax/crypto/KeyAgreement.java b/ojluni/src/main/java/javax/crypto/KeyAgreement.java
index 8a0b1c1..7c29102 100644
--- a/ojluni/src/main/java/javax/crypto/KeyAgreement.java
+++ b/ojluni/src/main/java/javax/crypto/KeyAgreement.java
@@ -32,9 +32,6 @@
 import java.security.Provider.Service;
 import java.security.spec.*;
 
-/* Android-removed: this debugging mechanism is not used in Android.
-import sun.security.util.Debug;
-*/
 import sun.security.jca.*;
 import sun.security.jca.GetInstance.Instance;
 
@@ -91,7 +88,8 @@
 
 public class KeyAgreement {
 
-    /* Android-removed: this debugging mechanism is not used in Android.
+    // Android-removed: this debugging mechanism is not used in Android.
+    /*
     private static final Debug pdebug =
                         Debug.getInstance("provider", "Provider");
     private static final boolean skipDebug =
@@ -293,7 +291,8 @@
             if (spi != null) {
                 return;
             }
-            /* Android-removed: this debugging mechanism is not used in Android.
+            // Android-removed: this debugging mechanism is not used in Android.
+            /*
             if (debug != null) {
                 int w = --warnCount;
                 if (w >= 0) {
@@ -463,7 +462,8 @@
             }
         }
 
-        /* Android-removed: this debugging mechanism is not used in Android.
+        // Android-removed: this debugging mechanism is not used in Android.
+        /*
         if (!skipDebug && pdebug != null) {
             pdebug.println("KeyAgreement." + algorithm + " algorithm from: " +
                 this.provider.getName());
@@ -526,7 +526,8 @@
             chooseProvider(I_PARAMS, key, params, random);
         }
 
-        /* Android-removed: this debugging mechanism is not used in Android.
+        // Android-removed: this debugging mechanism is not used in Android.
+        /*
         if (!skipDebug && pdebug != null) {
             pdebug.println("KeyAgreement." + algorithm + " algorithm from: " +
                 this.provider.getName());
diff --git a/ojluni/src/main/java/javax/crypto/KeyGenerator.java b/ojluni/src/main/java/javax/crypto/KeyGenerator.java
index 8a54e5c..5dfde97 100644
--- a/ojluni/src/main/java/javax/crypto/KeyGenerator.java
+++ b/ojluni/src/main/java/javax/crypto/KeyGenerator.java
@@ -33,9 +33,6 @@
 
 import sun.security.jca.*;
 import sun.security.jca.GetInstance.Instance;
-/* Android-removed: this debugging mechanism is not used in Android.
-import sun.security.util.Debug;
-*/
 
 /**
  * This class provides the functionality of a secret (symmetric) key generator.
@@ -167,7 +164,8 @@
 
 public class KeyGenerator {
 
-    /* Android-removed: this debugging mechanism is not used in Android.
+    // Android-removed: this debugging mechanism is not used in Android.
+    /*
     private static final Debug pdebug =
                         Debug.getInstance("provider", "Provider");
     private static final boolean skipDebug =
@@ -212,7 +210,8 @@
         this.provider = provider;
         this.algorithm = algorithm;
 
-        /* Android-removed: this debugging mechanism is not used in Android.
+        // Android-removed: this debugging mechanism is not used in Android.
+        /*
         if (!skipDebug && pdebug != null) {
             pdebug.println("KeyGenerator." + algorithm + " algorithm from: " +
                 this.provider.getName());
@@ -232,7 +231,8 @@
                 (algorithm + " KeyGenerator not available");
         }
 
-        /* Android-removed: this debugging mechanism is not used in Android.
+        // Android-removed: this debugging mechanism is not used in Android.
+        /*
         if (!skipDebug && pdebug != null) {
             pdebug.println("KeyGenerator." + algorithm + " algorithm from: " +
                 this.provider.getName());
diff --git a/ojluni/src/main/java/javax/crypto/Mac.java b/ojluni/src/main/java/javax/crypto/Mac.java
index 2315db7..cdf6105 100644
--- a/ojluni/src/main/java/javax/crypto/Mac.java
+++ b/ojluni/src/main/java/javax/crypto/Mac.java
@@ -34,9 +34,6 @@
 
 import java.nio.ByteBuffer;
 
-/* Android-removed: this debugging mechanism is not used in Android.
-import sun.security.util.Debug;
-*/
 import sun.security.jca.*;
 import sun.security.jca.GetInstance.Instance;
 
@@ -156,7 +153,8 @@
 
 public class Mac implements Cloneable {
 
-    /* Android-removed: this debugging mechanism is not used in Android.
+    // Android-removed: this debugging mechanism is not used in Android.
+    /*
     private static final Debug pdebug =
                         Debug.getInstance("provider", "Provider");
     private static final boolean skipDebug =
@@ -344,7 +342,8 @@
             if (spi != null) {
                 return;
             }
-            /* Android-removed: this debugging mechanism is not used in Android.
+            // Android-removed: this debugging mechanism is not used in Android.
+            /*
             if (debug != null) {
                 int w = --warnCount;
                 if (w >= 0) {
@@ -472,7 +471,8 @@
         }
         initialized = true;
 
-        /* Android-removed: this debugging mechanism is not used in Android.
+        // Android-removed: this debugging mechanism is not used in Android.
+        /*
         if (!skipDebug && pdebug != null) {
             pdebug.println("Mac." + algorithm + " algorithm from: " +
                 this.provider.getName());
@@ -501,7 +501,8 @@
         }
         initialized = true;
 
-        /* Android-removed: this debugging mechanism is not used in Android.
+        // Android-removed: this debugging mechanism is not used in Android.
+        /*
         if (!skipDebug && pdebug != null) {
             pdebug.println("Mac." + algorithm + " algorithm from: " +
                 this.provider.getName());
diff --git a/ojluni/src/main/java/javax/net/ssl/SSLSocketFactory.java b/ojluni/src/main/java/javax/net/ssl/SSLSocketFactory.java
index f1d7057..a543ac2 100644
--- a/ojluni/src/main/java/javax/net/ssl/SSLSocketFactory.java
+++ b/ojluni/src/main/java/javax/net/ssl/SSLSocketFactory.java
@@ -30,6 +30,7 @@
 import java.net.*;
 import javax.net.SocketFactory;
 import java.io.IOException;
+import java.io.InputStream;
 import java.security.*;
 import java.util.Locale;
 
@@ -207,6 +208,54 @@
      */
     public abstract Socket createSocket(Socket s, String host,
             int port, boolean autoClose) throws IOException;
+
+    /**
+     * Creates a server mode {@link Socket} layered over an
+     * existing connected socket, and is able to read data which has
+     * already been consumed/removed from the {@link Socket}'s
+     * underlying {@link InputStream}.
+     * <p>
+     * This method can be used by a server application that needs to
+     * observe the inbound data but still create valid SSL/TLS
+     * connections: for example, inspection of Server Name Indication
+     * (SNI) extensions (See section 3 of <A
+     * HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions
+     * (RFC6066)</A>).  Data that has been already removed from the
+     * underlying {@link InputStream} should be loaded into the
+     * {@code consumed} stream before this method is called, perhaps
+     * using a {@link java.io.ByteArrayInputStream}.  When this
+     * {@link Socket} begins handshaking, it will read all of the data in
+     * {@code consumed} until it reaches {@code EOF}, then all further
+     * data is read from the underlying {@link InputStream} as
+     * usual.
+     * <p>
+     * The returned socket is configured using the socket options
+     * established for this factory, and is set to use server mode when
+     * handshaking (see {@link SSLSocket#setUseClientMode(boolean)}).
+     *
+     * @param  s
+     *         the existing socket
+     * @param  consumed
+     *         the consumed inbound network data that has already been
+     *         removed from the existing {@link Socket}
+     *         {@link InputStream}.  This parameter may be
+     *         {@code null} if no data has been removed.
+     * @param  autoClose close the underlying socket when this socket is closed.
+     *
+     * @return the {@link Socket} compliant with the socket options
+     *         established for this factory
+     *
+     * @throws IOException if an I/O error occurs when creating the socket
+     * @throws UnsupportedOperationException if the underlying provider
+     *         does not implement the operation
+     * @throws NullPointerException if {@code s} is {@code null}
+     *
+     * @since 1.8
+     */
+    public Socket createSocket(Socket s, InputStream consumed,
+            boolean autoClose) throws IOException {
+        throw new UnsupportedOperationException();
+    }
 }
 
 
diff --git a/ojluni/src/main/java/jdk/net/package-info.java b/ojluni/src/main/java/jdk/net/package-info.java
deleted file mode 100644
index b05d543..0000000
--- a/ojluni/src/main/java/jdk/net/package-info.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/**
- * Platform specific socket options for the {@code java.net} and {@code java.nio.channels}
- * socket classes.
- */
-
-@jdk.Exported
-package jdk.net;
diff --git a/ojluni/src/main/java/sun/misc/FDBigInt.java b/ojluni/src/main/java/sun/misc/FDBigInt.java
deleted file mode 100644
index 85b5b35..0000000
--- a/ojluni/src/main/java/sun/misc/FDBigInt.java
+++ /dev/null
@@ -1,493 +0,0 @@
-/*
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.misc;
-
-/*
- * A really, really simple bigint package
- * tailored to the needs of floating base conversion.
- */
-public class FDBigInt {
-    int nWords; // number of words used
-    int data[]; // value: data[0] is least significant
-
-
-    public FDBigInt( int v ){
-        nWords = 1;
-        data = new int[1];
-        data[0] = v;
-    }
-
-    public FDBigInt( long v ){
-        data = new int[2];
-        data[0] = (int)v;
-        data[1] = (int)(v>>>32);
-        nWords = (data[1]==0) ? 1 : 2;
-    }
-
-    public FDBigInt( FDBigInt other ){
-        data = new int[nWords = other.nWords];
-        System.arraycopy( other.data, 0, data, 0, nWords );
-    }
-
-    private FDBigInt( int [] d, int n ){
-        data = d;
-        nWords = n;
-    }
-
-    public FDBigInt( long seed, char digit[], int nd0, int nd ){
-        int n= (nd+8)/9;        // estimate size needed.
-        if ( n < 2 ) n = 2;
-        data = new int[n];      // allocate enough space
-        data[0] = (int)seed;    // starting value
-        data[1] = (int)(seed>>>32);
-        nWords = (data[1]==0) ? 1 : 2;
-        int i = nd0;
-        int limit = nd-5;       // slurp digits 5 at a time.
-        int v;
-        while ( i < limit ){
-            int ilim = i+5;
-            v = (int)digit[i++]-(int)'0';
-            while( i <ilim ){
-                v = 10*v + (int)digit[i++]-(int)'0';
-            }
-            multaddMe( 100000, v); // ... where 100000 is 10^5.
-        }
-        int factor = 1;
-        v = 0;
-        while ( i < nd ){
-            v = 10*v + (int)digit[i++]-(int)'0';
-            factor *= 10;
-        }
-        if ( factor != 1 ){
-            multaddMe( factor, v );
-        }
-    }
-
-    /*
-     * Left shift by c bits.
-     * Shifts this in place.
-     */
-    public void
-    lshiftMe( int c )throws IllegalArgumentException {
-        if ( c <= 0 ){
-            if ( c == 0 )
-                return; // silly.
-            else
-                throw new IllegalArgumentException("negative shift count");
-        }
-        int wordcount = c>>5;
-        int bitcount  = c & 0x1f;
-        int anticount = 32-bitcount;
-        int t[] = data;
-        int s[] = data;
-        if ( nWords+wordcount+1 > t.length ){
-            // reallocate.
-            t = new int[ nWords+wordcount+1 ];
-        }
-        int target = nWords+wordcount;
-        int src    = nWords-1;
-        if ( bitcount == 0 ){
-            // special hack, since an anticount of 32 won't go!
-            System.arraycopy( s, 0, t, wordcount, nWords );
-            target = wordcount-1;
-        } else {
-            t[target--] = s[src]>>>anticount;
-            while ( src >= 1 ){
-                t[target--] = (s[src]<<bitcount) | (s[--src]>>>anticount);
-            }
-            t[target--] = s[src]<<bitcount;
-        }
-        while( target >= 0 ){
-            t[target--] = 0;
-        }
-        data = t;
-        nWords += wordcount + 1;
-        // may have constructed high-order word of 0.
-        // if so, trim it
-        while ( nWords > 1 && data[nWords-1] == 0 )
-            nWords--;
-    }
-
-    /*
-     * normalize this number by shifting until
-     * the MSB of the number is at 0x08000000.
-     * This is in preparation for quoRemIteration, below.
-     * The idea is that, to make division easier, we want the
-     * divisor to be "normalized" -- usually this means shifting
-     * the MSB into the high words sign bit. But because we know that
-     * the quotient will be 0 < q < 10, we would like to arrange that
-     * the dividend not span up into another word of precision.
-     * (This needs to be explained more clearly!)
-     */
-    public int
-    normalizeMe() throws IllegalArgumentException {
-        int src;
-        int wordcount = 0;
-        int bitcount  = 0;
-        int v = 0;
-        for ( src= nWords-1 ; src >= 0 && (v=data[src]) == 0 ; src--){
-            wordcount += 1;
-        }
-        if ( src < 0 ){
-            // oops. Value is zero. Cannot normalize it!
-            throw new IllegalArgumentException("zero value");
-        }
-        /*
-         * In most cases, we assume that wordcount is zero. This only
-         * makes sense, as we try not to maintain any high-order
-         * words full of zeros. In fact, if there are zeros, we will
-         * simply SHORTEN our number at this point. Watch closely...
-         */
-        nWords -= wordcount;
-        /*
-         * Compute how far left we have to shift v s.t. its highest-
-         * order bit is in the right place. Then call lshiftMe to
-         * do the work.
-         */
-        if ( (v & 0xf0000000) != 0 ){
-            // will have to shift up into the next word.
-            // too bad.
-            for( bitcount = 32 ; (v & 0xf0000000) != 0 ; bitcount-- )
-                v >>>= 1;
-        } else {
-            while ( v <= 0x000fffff ){
-                // hack: byte-at-a-time shifting
-                v <<= 8;
-                bitcount += 8;
-            }
-            while ( v <= 0x07ffffff ){
-                v <<= 1;
-                bitcount += 1;
-            }
-        }
-        if ( bitcount != 0 )
-            lshiftMe( bitcount );
-        return bitcount;
-    }
-
-    /*
-     * Multiply a FDBigInt by an int.
-     * Result is a new FDBigInt.
-     */
-    public FDBigInt
-    mult( int iv ) {
-        long v = iv;
-        int r[];
-        long p;
-
-        // guess adequate size of r.
-        r = new int[ ( v * ((long)data[nWords-1]&0xffffffffL) > 0xfffffffL ) ? nWords+1 : nWords ];
-        p = 0L;
-        for( int i=0; i < nWords; i++ ) {
-            p += v * ((long)data[i]&0xffffffffL);
-            r[i] = (int)p;
-            p >>>= 32;
-        }
-        if ( p == 0L){
-            return new FDBigInt( r, nWords );
-        } else {
-            r[nWords] = (int)p;
-            return new FDBigInt( r, nWords+1 );
-        }
-    }
-
-    /*
-     * Multiply a FDBigInt by an int and add another int.
-     * Result is computed in place.
-     * Hope it fits!
-     */
-    public void
-    multaddMe( int iv, int addend ) {
-        long v = iv;
-        long p;
-
-        // unroll 0th iteration, doing addition.
-        p = v * ((long)data[0]&0xffffffffL) + ((long)addend&0xffffffffL);
-        data[0] = (int)p;
-        p >>>= 32;
-        for( int i=1; i < nWords; i++ ) {
-            p += v * ((long)data[i]&0xffffffffL);
-            data[i] = (int)p;
-            p >>>= 32;
-        }
-        if ( p != 0L){
-            data[nWords] = (int)p; // will fail noisily if illegal!
-            nWords++;
-        }
-    }
-
-    /*
-     * Multiply a FDBigInt by another FDBigInt.
-     * Result is a new FDBigInt.
-     */
-    public FDBigInt
-    mult( FDBigInt other ){
-        // crudely guess adequate size for r
-        int r[] = new int[ nWords + other.nWords ];
-        int i;
-        // I think I am promised zeros...
-
-        for( i = 0; i < this.nWords; i++ ){
-            long v = (long)this.data[i] & 0xffffffffL; // UNSIGNED CONVERSION
-            long p = 0L;
-            int j;
-            for( j = 0; j < other.nWords; j++ ){
-                p += ((long)r[i+j]&0xffffffffL) + v*((long)other.data[j]&0xffffffffL); // UNSIGNED CONVERSIONS ALL 'ROUND.
-                r[i+j] = (int)p;
-                p >>>= 32;
-            }
-            r[i+j] = (int)p;
-        }
-        // compute how much of r we actually needed for all that.
-        for ( i = r.length-1; i> 0; i--)
-            if ( r[i] != 0 )
-                break;
-        return new FDBigInt( r, i+1 );
-    }
-
-    /*
-     * Add one FDBigInt to another. Return a FDBigInt
-     */
-    public FDBigInt
-    add( FDBigInt other ){
-        int i;
-        int a[], b[];
-        int n, m;
-        long c = 0L;
-        // arrange such that a.nWords >= b.nWords;
-        // n = a.nWords, m = b.nWords
-        if ( this.nWords >= other.nWords ){
-            a = this.data;
-            n = this.nWords;
-            b = other.data;
-            m = other.nWords;
-        } else {
-            a = other.data;
-            n = other.nWords;
-            b = this.data;
-            m = this.nWords;
-        }
-        int r[] = new int[ n ];
-        for ( i = 0; i < n; i++ ){
-            c += (long)a[i] & 0xffffffffL;
-            if ( i < m ){
-                c += (long)b[i] & 0xffffffffL;
-            }
-            r[i] = (int) c;
-            c >>= 32; // signed shift.
-        }
-        if ( c != 0L ){
-            // oops -- carry out -- need longer result.
-            int s[] = new int[ r.length+1 ];
-            System.arraycopy( r, 0, s, 0, r.length );
-            s[i++] = (int)c;
-            return new FDBigInt( s, i );
-        }
-        return new FDBigInt( r, i );
-    }
-
-    /*
-     * Subtract one FDBigInt from another. Return a FDBigInt
-     * Assert that the result is positive.
-     */
-    public FDBigInt
-    sub( FDBigInt other ){
-        int r[] = new int[ this.nWords ];
-        int i;
-        int n = this.nWords;
-        int m = other.nWords;
-        int nzeros = 0;
-        long c = 0L;
-        for ( i = 0; i < n; i++ ){
-            c += (long)this.data[i] & 0xffffffffL;
-            if ( i < m ){
-                c -= (long)other.data[i] & 0xffffffffL;
-            }
-            if ( ( r[i] = (int) c ) == 0 )
-                nzeros++;
-            else
-                nzeros = 0;
-            c >>= 32; // signed shift
-        }
-        assert c == 0L : c; // borrow out of subtract
-        assert dataInRangeIsZero(i, m, other); // negative result of subtract
-        return new FDBigInt( r, n-nzeros );
-    }
-
-    private static boolean dataInRangeIsZero(int i, int m, FDBigInt other) {
-        while ( i < m )
-            if (other.data[i++] != 0)
-                return false;
-        return true;
-    }
-
-    /*
-     * Compare FDBigInt with another FDBigInt. Return an integer
-     * >0: this > other
-     *  0: this == other
-     * <0: this < other
-     */
-    public int
-    cmp( FDBigInt other ){
-        int i;
-        if ( this.nWords > other.nWords ){
-            // if any of my high-order words is non-zero,
-            // then the answer is evident
-            int j = other.nWords-1;
-            for ( i = this.nWords-1; i > j ; i-- )
-                if ( this.data[i] != 0 ) return 1;
-        }else if ( this.nWords < other.nWords ){
-            // if any of other's high-order words is non-zero,
-            // then the answer is evident
-            int j = this.nWords-1;
-            for ( i = other.nWords-1; i > j ; i-- )
-                if ( other.data[i] != 0 ) return -1;
-        } else{
-            i = this.nWords-1;
-        }
-        for ( ; i > 0 ; i-- )
-            if ( this.data[i] != other.data[i] )
-                break;
-        // careful! want unsigned compare!
-        // use brute force here.
-        int a = this.data[i];
-        int b = other.data[i];
-        if ( a < 0 ){
-            // a is really big, unsigned
-            if ( b < 0 ){
-                return a-b; // both big, negative
-            } else {
-                return 1; // b not big, answer is obvious;
-            }
-        } else {
-            // a is not really big
-            if ( b < 0 ) {
-                // but b is really big
-                return -1;
-            } else {
-                return a - b;
-            }
-        }
-    }
-
-    /*
-     * Compute
-     * q = (int)( this / S )
-     * this = 10 * ( this mod S )
-     * Return q.
-     * This is the iteration step of digit development for output.
-     * We assume that S has been normalized, as above, and that
-     * "this" has been lshift'ed accordingly.
-     * Also assume, of course, that the result, q, can be expressed
-     * as an integer, 0 <= q < 10.
-     */
-    public int
-    quoRemIteration( FDBigInt S )throws IllegalArgumentException {
-        // ensure that this and S have the same number of
-        // digits. If S is properly normalized and q < 10 then
-        // this must be so.
-        if ( nWords != S.nWords ){
-            throw new IllegalArgumentException("disparate values");
-        }
-        // estimate q the obvious way. We will usually be
-        // right. If not, then we're only off by a little and
-        // will re-add.
-        int n = nWords-1;
-        long q = ((long)data[n]&0xffffffffL) / (long)S.data[n];
-        long diff = 0L;
-        for ( int i = 0; i <= n ; i++ ){
-            diff += ((long)data[i]&0xffffffffL) -  q*((long)S.data[i]&0xffffffffL);
-            data[i] = (int)diff;
-            diff >>= 32; // N.B. SIGNED shift.
-        }
-        if ( diff != 0L ) {
-            // damn, damn, damn. q is too big.
-            // add S back in until this turns +. This should
-            // not be very many times!
-            long sum = 0L;
-            while ( sum ==  0L ){
-                sum = 0L;
-                for ( int i = 0; i <= n; i++ ){
-                    sum += ((long)data[i]&0xffffffffL) +  ((long)S.data[i]&0xffffffffL);
-                    data[i] = (int) sum;
-                    sum >>= 32; // Signed or unsigned, answer is 0 or 1
-                }
-                /*
-                 * Originally the following line read
-                 * "if ( sum !=0 && sum != -1 )"
-                 * but that would be wrong, because of the
-                 * treatment of the two values as entirely unsigned,
-                 * it would be impossible for a carry-out to be interpreted
-                 * as -1 -- it would have to be a single-bit carry-out, or
-                 * +1.
-                 */
-                assert sum == 0 || sum == 1 : sum; // carry out of division correction
-                q -= 1;
-            }
-        }
-        // finally, we can multiply this by 10.
-        // it cannot overflow, right, as the high-order word has
-        // at least 4 high-order zeros!
-        long p = 0L;
-        for ( int i = 0; i <= n; i++ ){
-            p += 10*((long)data[i]&0xffffffffL);
-            data[i] = (int)p;
-            p >>= 32; // SIGNED shift.
-        }
-        assert p == 0L : p; // Carry out of *10
-        return (int)q;
-    }
-
-    public long
-    longValue(){
-        // if this can be represented as a long, return the value
-        assert this.nWords > 0 : this.nWords; // longValue confused
-
-        if (this.nWords == 1)
-            return ((long)data[0]&0xffffffffL);
-
-        assert dataInRangeIsZero(2, this.nWords, this); // value too big
-        assert data[1] >= 0;  // value too big
-        return ((long)(data[1]) << 32) | ((long)data[0]&0xffffffffL);
-    }
-
-    public String
-    toString() {
-        StringBuffer r = new StringBuffer(30);
-        r.append('[');
-        int i = Math.min( nWords-1, data.length-1) ;
-        if ( nWords > data.length ){
-            r.append( "("+data.length+"<"+nWords+"!)" );
-        }
-        for( ; i> 0 ; i-- ){
-            r.append( Integer.toHexString( data[i] ) );
-            r.append(' ');
-        }
-        r.append( Integer.toHexString( data[0] ) );
-        r.append(']');
-        return new String( r );
-    }
-}
diff --git a/ojluni/src/main/java/sun/nio/ch/DatagramSocketAdaptor.java b/ojluni/src/main/java/sun/nio/ch/DatagramSocketAdaptor.java
index 762df01..3923675 100644
--- a/ojluni/src/main/java/sun/nio/ch/DatagramSocketAdaptor.java
+++ b/ojluni/src/main/java/sun/nio/ch/DatagramSocketAdaptor.java
@@ -361,9 +361,7 @@
         return dc;
     }
 
-    /*
-     * Android-added: for testing and internal use.
-     */
+    // Android-added: for testing and internal use.
     @Override
     public final FileDescriptor getFileDescriptor$() {
         return dc.fd;
diff --git a/ojluni/src/main/java/sun/nio/ch/SocketAdaptor.java b/ojluni/src/main/java/sun/nio/ch/SocketAdaptor.java
index aa6d834..a2aba2d 100644
--- a/ojluni/src/main/java/sun/nio/ch/SocketAdaptor.java
+++ b/ojluni/src/main/java/sun/nio/ch/SocketAdaptor.java
@@ -453,9 +453,7 @@
         return !sc.isOutputOpen();
     }
 
-    /*
-     * Android-added: for testing and internal use.
-     */
+    // Android-added: for testing and internal use.
     @Override
     public FileDescriptor getFileDescriptor$() {
         return sc.getFD();
diff --git a/ojluni/src/main/java/sun/nio/fs/LinuxFileSystemProvider.java b/ojluni/src/main/java/sun/nio/fs/LinuxFileSystemProvider.java
index 8f1fa47..e6468d1 100644
--- a/ojluni/src/main/java/sun/nio/fs/LinuxFileSystemProvider.java
+++ b/ojluni/src/main/java/sun/nio/fs/LinuxFileSystemProvider.java
@@ -46,7 +46,11 @@
 
     @Override
     LinuxFileStore getFileStore(UnixPath path) throws IOException {
-        return new LinuxFileStore(path);
+        // Android-changed: Complete information about file systems is neither available to regular
+        // apps nor the system server due to SELinux policies.
+        // return new LinuxFileStore(path);
+
+        throw new SecurityException("getFileStore");
     }
 
     @Override
@@ -55,14 +59,18 @@
                                                                 Class<V> type,
                                                                 LinkOption... options)
     {
-        if (type == DosFileAttributeView.class) {
-            return (V) new LinuxDosFileAttributeView(UnixPath.toUnixPath(obj),
-                                                     Util.followLinks(options));
-        }
-        if (type == UserDefinedFileAttributeView.class) {
-            return (V) new LinuxUserDefinedFileAttributeView(UnixPath.toUnixPath(obj),
-                                                             Util.followLinks(options));
-        }
+        // Android-changed: Delegate to UnixFileSystemProvider, remove support for "dos" and
+        // "user" file attribute views which are not supported.
+        //
+        // if (type == DosFileAttributeView.class) {
+        //     return (V) new LinuxDosFileAttributeView(UnixPath.toUnixPath(obj),
+        //                                              Util.followLinks(options));
+        // }
+        // if (type == UserDefinedFileAttributeView.class) {
+        //     return (V) new LinuxUserDefinedFileAttributeView(UnixPath.toUnixPath(obj),
+        //                                                      Util.followLinks(options));
+        // }
+
         return super.getFileAttributeView(obj, type, options);
     }
 
@@ -71,14 +79,18 @@
                                                          String name,
                                                          LinkOption... options)
     {
-        if (name.equals("dos")) {
-            return new LinuxDosFileAttributeView(UnixPath.toUnixPath(obj),
-                                                 Util.followLinks(options));
-        }
-        if (name.equals("user")) {
-            return new LinuxUserDefinedFileAttributeView(UnixPath.toUnixPath(obj),
-                                                         Util.followLinks(options));
-        }
+        // Android-changed: Delegate to UnixFileSystemProvider, remove support for "dos" and
+        // "user" file attribute views which are not supported.
+        //
+        // if (name.equals("dos")) {
+        //     return new LinuxDosFileAttributeView(UnixPath.toUnixPath(obj),
+        //                                          Util.followLinks(options));
+        // }
+        // if (name.equals("user")) {
+        //     return new LinuxUserDefinedFileAttributeView(UnixPath.toUnixPath(obj),
+        //                                                  Util.followLinks(options));
+        // }
+
         return super.getFileAttributeView(obj, name, options);
     }
 
@@ -89,13 +101,17 @@
                                                             LinkOption... options)
         throws IOException
     {
-        if (type == DosFileAttributes.class) {
-            DosFileAttributeView view =
-                getFileAttributeView(file, DosFileAttributeView.class, options);
-            return (A) view.readAttributes();
-        } else {
-            return super.readAttributes(file, type, options);
-        }
+        // Android-changed: Delegate to UnixFileSystemProvider, remove support for "dos" and
+        // "user" file attribute views which are not supported.
+        //
+        // if (type == DosFileAttributes.class) {
+        //     DosFileAttributeView view =
+        //         getFileAttributeView(file, DosFileAttributeView.class, options);
+        //     return (A) view.readAttributes();
+        // } else {
+        //     return super.readAttributes(file, type, options);
+        // }
+        return super.readAttributes(file, type, options);
     }
 
     @Override
diff --git a/ojluni/src/main/java/sun/nio/fs/UnixFileSystemProvider.java b/ojluni/src/main/java/sun/nio/fs/UnixFileSystemProvider.java
index 182a57b..3724956 100644
--- a/ojluni/src/main/java/sun/nio/fs/UnixFileSystemProvider.java
+++ b/ojluni/src/main/java/sun/nio/fs/UnixFileSystemProvider.java
@@ -359,13 +359,17 @@
 
     @Override
     public FileStore getFileStore(Path obj) throws IOException {
-        UnixPath file = UnixPath.toUnixPath(obj);
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-            sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
-            file.checkRead();
-        }
-        return getFileStore(file);
+        // Android-changed: Complete information about file systems is neither available to regular
+        // apps nor the system server due to SELinux policies.
+        //
+        // UnixPath file = UnixPath.toUnixPath(obj);
+        // SecurityManager sm = System.getSecurityManager();
+        // if (sm != null) {
+        //     sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
+        //     file.checkRead();
+        // }
+        // return getFileStore(file);
+        throw new SecurityException("getFileStore");
     }
 
     @Override
diff --git a/ojluni/src/main/java/sun/security/pkcs/PKCS7.java b/ojluni/src/main/java/sun/security/pkcs/PKCS7.java
index 49d898a..4fa4711 100644
--- a/ojluni/src/main/java/sun/security/pkcs/PKCS7.java
+++ b/ojluni/src/main/java/sun/security/pkcs/PKCS7.java
@@ -1172,5 +1172,6 @@
         }
         return tsReply.getEncodedToken();
     }
-    END Android-removed */
+    */
+    // END Android-removed: unused in Android
 }
diff --git a/ojluni/src/main/java/sun/security/pkcs/SignerInfo.java b/ojluni/src/main/java/sun/security/pkcs/SignerInfo.java
index b78fdf9..e2f8e28 100644
--- a/ojluni/src/main/java/sun/security/pkcs/SignerInfo.java
+++ b/ojluni/src/main/java/sun/security/pkcs/SignerInfo.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,21 +33,38 @@
 import java.io.OutputStream;
 import java.io.IOException;
 import java.math.BigInteger;
+import java.security.CryptoPrimitive;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.Timestamp;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.CertPath;
 import java.security.cert.X509Certificate;
-import java.security.*;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
 
+import sun.misc.HexDumpEncoder;
 import sun.security.timestamp.TimestampToken;
-import sun.security.util.*;
+import sun.security.util.Debug;
+import sun.security.util.DerEncoder;
+import sun.security.util.DerInputStream;
+import sun.security.util.DerOutputStream;
+import sun.security.util.DerValue;
+import sun.security.util.DisabledAlgorithmConstraints;
+import sun.security.util.ObjectIdentifier;
 import sun.security.x509.AlgorithmId;
 import sun.security.x509.X500Name;
 import sun.security.x509.KeyUsageExtension;
 import sun.security.x509.PKIXExtensions;
-import sun.misc.HexDumpEncoder;
 
 /**
  * A SignerInfo, as defined in PKCS#7's signedData type.
@@ -56,6 +73,17 @@
  */
 public class SignerInfo implements DerEncoder {
 
+    // Digest and Signature restrictions
+    private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET =
+            Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST));
+
+    private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET =
+            Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
+
+    private static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK =
+            new DisabledAlgorithmConstraints(
+                    DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);
+
     BigInteger version;
     X500Name issuerName;
     BigInteger certificateSerialNumber;
@@ -337,6 +365,13 @@
                 if (messageDigest == null) // fail if there is no message digest
                     return null;
 
+                // check that algorithm is not restricted
+                if (!JAR_DISABLED_CHECK.permits(DIGEST_PRIMITIVE_SET,
+                        digestAlgname, null)) {
+                    throw new SignatureException("Digest check failed. " +
+                            "Disabled algorithm used: " + digestAlgname);
+                }
+
                 MessageDigest md = MessageDigest.getInstance(digestAlgname);
 
                 byte[] buffer = new byte[4096];
@@ -374,12 +409,24 @@
             String algname = AlgorithmId.makeSigAlg(
                     digestAlgname, encryptionAlgname);
 
-            Signature sig = Signature.getInstance(algname);
-            X509Certificate cert = getCertificate(block);
+            // check that algorithm is not restricted
+            if (!JAR_DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, algname, null)) {
+                throw new SignatureException("Signature check failed. " +
+                        "Disabled algorithm used: " + algname);
+            }
 
+            X509Certificate cert = getCertificate(block);
+            PublicKey key = cert.getPublicKey();
             if (cert == null) {
                 return null;
             }
+
+            // check if the public key is restricted
+            if (!JAR_DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
+                throw new SignatureException("Public key check failed. " +
+                        "Disabled algorithm used: " + key.getAlgorithm());
+            }
+
             if (cert.hasUnsupportedCriticalExtension()) {
                 throw new SignatureException("Certificate has unsupported "
                                              + "critical extension(s)");
@@ -416,7 +463,7 @@
                 }
             }
 
-            PublicKey key = cert.getPublicKey();
+            Signature sig = Signature.getInstance(algname);
             sig.initVerify(key);
 
             byte[] buffer = new byte[4096];
@@ -549,9 +596,16 @@
      */
     private void verifyTimestamp(TimestampToken token)
         throws NoSuchAlgorithmException, SignatureException {
+        String digestAlgname = token.getHashAlgorithm().getName();
+        // check that algorithm is not restricted
+        if (!JAR_DISABLED_CHECK.permits(DIGEST_PRIMITIVE_SET, digestAlgname,
+                null)) {
+            throw new SignatureException("Timestamp token digest check failed. " +
+                    "Disabled algorithm used: " + digestAlgname);
+        }
 
         MessageDigest md =
-            MessageDigest.getInstance(token.getHashAlgorithm().getName());
+            MessageDigest.getInstance(digestAlgname);
 
         if (!Arrays.equals(token.getHashedMessage(),
             md.digest(encryptedDigest))) {
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/AlgorithmChecker.java b/ojluni/src/main/java/sun/security/provider/certpath/AlgorithmChecker.java
index e92590a..f727a3a 100644
--- a/ojluni/src/main/java/sun/security/provider/certpath/AlgorithmChecker.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/AlgorithmChecker.java
@@ -255,16 +255,17 @@
 
         PublicKey currPubKey = cert.getPublicKey();
 
-        // Check against DisabledAlgorithmConstraints certpath constraints.
-        // permits() will throw exception on failure.
-        certPathDefaultConstraints.permits(primitives,
+        if (constraints instanceof DisabledAlgorithmConstraints) {
+            // Check against DisabledAlgorithmConstraints certpath constraints.
+            // permits() will throw exception on failure.
+            ((DisabledAlgorithmConstraints)constraints).permits(primitives,
                 new CertConstraintParameters((X509Certificate)cert,
                         trustedMatch));
-                // new CertConstraintParameters(x509Cert, trustedMatch));
-        // If there is no previous key, set one and exit
-        if (prevPubKey == null) {
-            prevPubKey = currPubKey;
-            return;
+            // If there is no previous key, set one and exit
+            if (prevPubKey == null) {
+                prevPubKey = currPubKey;
+                return;
+            }
         }
 
         X509CertImpl x509Cert;
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/Builder.java b/ojluni/src/main/java/sun/security/provider/certpath/Builder.java
index 84cbe66..e6b5fc0 100644
--- a/ojluni/src/main/java/sun/security/provider/certpath/Builder.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/Builder.java
@@ -102,8 +102,8 @@
 
     /**
      * Verifies whether the input certificate completes the path.
-     * When building forward, a trust anchor will complete the path.
-     * When building reverse, the target certificate will complete the path.
+     * When building in the forward direction, a trust anchor will
+     * complete the path.
      *
      * @param cert the certificate to test
      * @return a boolean value indicating whether the cert completes the path.
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/ForwardBuilder.java b/ojluni/src/main/java/sun/security/provider/certpath/ForwardBuilder.java
index 57e819c..27bb25f 100644
--- a/ojluni/src/main/java/sun/security/provider/certpath/ForwardBuilder.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/ForwardBuilder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -824,18 +824,19 @@
 
     /**
      * Verifies whether the input certificate completes the path.
-     * Checks the cert against each trust anchor that was specified, in order,
-     * and returns true as soon as it finds a valid anchor.
-     * Returns true if the cert matches a trust anchor specified as a
-     * certificate or if the cert verifies with a trust anchor that
-     * was specified as a trusted {pubkey, caname} pair. Returns false if none
-     * of the trust anchors are valid for this cert.
-     *
-     * @param cert the certificate to test
-     * @return a boolean value indicating whether the cert completes the path.
+     * First checks the cert against each trust anchor that was specified,
+     * in order, and returns true if the cert matches the trust anchor
+     * specified as a certificate or has the same key and subject of an anchor
+     * specified as a trusted {pubkey, caname} pair.
+     * If no match has been found, does a second check of the cert against
+     * anchors specified as a trusted {pubkey, caname} pair to see if the cert
+     * was issued by that anchor.
+     * Returns false if none of the trust anchors are valid for this cert.
      */
     @Override
     boolean isPathCompleted(X509Certificate cert) {
+        List<TrustAnchor> otherAnchors = new ArrayList<>();
+        // first, check if cert is already trusted
         for (TrustAnchor anchor : trustAnchors) {
             if (anchor.getTrustedCert() != null) {
                 if (cert.equals(anchor.getTrustedCert())) {
@@ -857,7 +858,12 @@
                 }
                 // else, it is a self-issued certificate of the anchor
             }
-
+            otherAnchors.add(anchor);
+        }
+        // next, check if cert is issued by anchor specified by key/name
+        for (TrustAnchor anchor : otherAnchors) {
+            X500Principal principal = anchor.getCA();
+            PublicKey publicKey = anchor.getCAPublicKey();
             // Check subject/issuer name chaining
             if (principal == null ||
                     !principal.equals(cert.getIssuerX500Principal())) {
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/OCSPResponse.java b/ojluni/src/main/java/sun/security/provider/certpath/OCSPResponse.java
index 8075d73..c8a75ea 100644
--- a/ojluni/src/main/java/sun/security/provider/certpath/OCSPResponse.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/OCSPResponse.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -151,8 +151,8 @@
     private static final int DEFAULT_MAX_CLOCK_SKEW = 900000;
 
     /**
-     * Integer value indicating the maximum allowable clock skew, in seconds,
-     * to be used for the OCSP check.
+     * Integer value indicating the maximum allowable clock skew,
+     * in milliseconds, to be used for the OCSP check.
      */
     private static final int MAX_CLOCK_SKEW = initializeClockSkew();
 
@@ -586,13 +586,14 @@
                 "Unable to verify OCSP Response's signature");
         }
 
-        // Check freshness of OCSPResponse
         if (nonce != null) {
             if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) {
                 throw new CertPathValidatorException("Nonces don't match");
             }
         }
 
+        // Check freshness of OCSPResponse
+
         long now = (date == null) ? System.currentTimeMillis() : date.getTime();
         Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
         Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
@@ -602,13 +603,18 @@
                 if (sr.nextUpdate != null) {
                     until = " until " + sr.nextUpdate;
                 }
-                debug.println("Response's validity interval is from " +
-                              sr.thisUpdate + until);
+                debug.println("OCSP response validity interval is from " +
+                               sr.thisUpdate + until);
+                debug.println("Checking validity of OCSP response on: " +
+                    new Date(now));
             }
 
-            // Check that the test date is within the validity interval
-            if ((sr.thisUpdate != null && nowPlusSkew.before(sr.thisUpdate)) ||
-                (sr.nextUpdate != null && nowMinusSkew.after(sr.nextUpdate)))
+            // Check that the test date is within the validity interval:
+            //   [ thisUpdate - MAX_CLOCK_SKEW,
+            //     MAX(thisUpdate, nextUpdate) + MAX_CLOCK_SKEW ]
+            if (nowPlusSkew.before(sr.thisUpdate) ||
+                nowMinusSkew.after(
+                    sr.nextUpdate != null ? sr.nextUpdate : sr.thisUpdate))
             {
                 throw new CertPathValidatorException(
                                       "Response is unreliable: its validity " +
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/PKIX.java b/ojluni/src/main/java/sun/security/provider/certpath/PKIX.java
index 98c8834..e33d4a2 100644
--- a/ojluni/src/main/java/sun/security/provider/certpath/PKIX.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/PKIX.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,6 @@
 package sun.security.provider.certpath;
 
 import java.security.InvalidAlgorithmParameterException;
-import java.security.KeyStore;
 import java.security.PublicKey;
 import java.security.cert.*;
 import java.security.interfaces.DSAPublicKey;
@@ -194,7 +193,6 @@
 
     static class BuilderParams extends ValidatorParams {
         private PKIXBuilderParameters params;
-        private boolean buildForward = true;
         private List<CertStore> stores;
         private X500Principal targetSubject;
 
@@ -213,10 +211,6 @@
                     + "targetCertConstraints parameter must be an "
                     + "X509CertSelector");
             }
-            if (params instanceof SunCertPathBuilderParameters) {
-                buildForward =
-                    ((SunCertPathBuilderParameters)params).getBuildForward();
-            }
             this.params = params;
             this.targetSubject = getTargetSubject(
                 certStores(), (X509CertSelector)targetCertConstraints());
@@ -230,7 +224,6 @@
             return stores;
         }
         int maxPathLength() { return params.getMaxPathLength(); }
-        boolean buildForward() { return buildForward; }
         PKIXBuilderParameters params() { return params; }
         X500Principal targetSubject() { return targetSubject; }
 
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/PKIXCertPathValidator.java b/ojluni/src/main/java/sun/security/provider/certpath/PKIXCertPathValidator.java
index 492e851..d2d9e36 100644
--- a/ojluni/src/main/java/sun/security/provider/certpath/PKIXCertPathValidator.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/PKIXCertPathValidator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -94,9 +94,6 @@
             X509Certificate firstCert = certList.get(0);
             // check trusted certificate's subject
             selector.setSubject(firstCert.getIssuerX500Principal());
-            // check the validity period
-            selector.setValidityPeriod(firstCert.getNotBefore(),
-                                       firstCert.getNotAfter());
             /*
              * Facilitate certification path construction with authority
              * key identifier and subject key identifier.
@@ -162,13 +159,22 @@
                                                         ValidatorParams params)
         throws CertPathValidatorException
     {
+        // add standard checkers that we will be using
+        // Android-removed: Android doesn't use this mechanism for checking untrusted certificates.
+        // check if anchor is untrusted
+        //UntrustedChecker untrustedChecker = new UntrustedChecker();
+        //X509Certificate anchorCert = anchor.getTrustedCert();
+        //if (anchorCert != null) {
+        //    untrustedChecker.check(anchorCert);
+        //}
+
         int certPathLen = params.certificates().size();
 
         // create PKIXCertPathCheckers
         List<PKIXCertPathChecker> certPathCheckers = new ArrayList<>();
         // add standard checkers that we will be using
         // Android-removed: Android doesn't use this mechanism for checking untrusted certificates.
-        // certPathCheckers.add(new UntrustedChecker());
+        // certPathCheckers.add(untrustedChecker);
         certPathCheckers.add(new AlgorithmChecker(anchor));
         certPathCheckers.add(new KeyChecker(certPathLen,
                                             params.targetCertConstraints()));
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/ReverseBuilder.java b/ojluni/src/main/java/sun/security/provider/certpath/ReverseBuilder.java
deleted file mode 100644
index 1815a22..0000000
--- a/ojluni/src/main/java/sun/security/provider/certpath/ReverseBuilder.java
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.provider.certpath;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.Principal;
-import java.security.cert.CertificateException;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertStore;
-import java.security.cert.CertStoreException;
-import java.security.cert.PKIXBuilderParameters;
-import java.security.cert.PKIXCertPathChecker;
-import java.security.cert.PKIXParameters;
-import java.security.cert.PKIXReason;
-import java.security.cert.TrustAnchor;
-import java.security.cert.X509Certificate;
-import java.security.cert.X509CertSelector;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.LinkedList;
-import java.util.Set;
-
-import javax.security.auth.x500.X500Principal;
-
-import sun.security.provider.certpath.PKIX.BuilderParams;
-import sun.security.util.Debug;
-import sun.security.x509.Extension;
-import static sun.security.x509.PKIXExtensions.*;
-import sun.security.x509.X500Name;
-import sun.security.x509.X509CertImpl;
-import sun.security.x509.PolicyMappingsExtension;
-
-/**
- * This class represents a reverse builder, which is able to retrieve
- * matching certificates from CertStores and verify a particular certificate
- * against a ReverseState.
- *
- * @since       1.4
- * @author      Sean Mullan
- * @author      Yassir Elley
- */
-
-class ReverseBuilder extends Builder {
-
-    private Debug debug = Debug.getInstance("certpath");
-
-    private final Set<String> initPolicies;
-
-    /**
-     * Initialize the builder with the input parameters.
-     *
-     * @param params the parameter set used to build a certification path
-     */
-    ReverseBuilder(BuilderParams buildParams) {
-        super(buildParams);
-
-        Set<String> initialPolicies = buildParams.initialPolicies();
-        initPolicies = new HashSet<String>();
-        if (initialPolicies.isEmpty()) {
-            // if no initialPolicies are specified by user, set
-            // initPolicies to be anyPolicy by default
-            initPolicies.add(PolicyChecker.ANY_POLICY);
-        } else {
-            initPolicies.addAll(initialPolicies);
-        }
-    }
-
-    /**
-     * Retrieves all certs from the specified CertStores that satisfy the
-     * requirements specified in the parameters and the current
-     * PKIX state (name constraints, policy constraints, etc).
-     *
-     * @param currentState the current state.
-     *        Must be an instance of <code>ReverseState</code>
-     * @param certStores list of CertStores
-     */
-    @Override
-    Collection<X509Certificate> getMatchingCerts
-        (State currState, List<CertStore> certStores)
-        throws CertStoreException, CertificateException, IOException
-    {
-        ReverseState currentState = (ReverseState) currState;
-
-        if (debug != null)
-            debug.println("In ReverseBuilder.getMatchingCerts.");
-
-        /*
-         * The last certificate could be an EE or a CA certificate
-         * (we may be building a partial certification path or
-         * establishing trust in a CA).
-         *
-         * Try the EE certs before the CA certs. It will be more
-         * common to build a path to an end entity.
-         */
-        Collection<X509Certificate> certs =
-            getMatchingEECerts(currentState, certStores);
-        certs.addAll(getMatchingCACerts(currentState, certStores));
-
-        return certs;
-    }
-
-    /*
-     * Retrieves all end-entity certificates which satisfy constraints
-     * and requirements specified in the parameters and PKIX state.
-     */
-    private Collection<X509Certificate> getMatchingEECerts
-        (ReverseState currentState, List<CertStore> certStores)
-        throws CertStoreException, CertificateException, IOException {
-
-        /*
-         * Compose a CertSelector to filter out
-         * certs which do not satisfy requirements.
-         *
-         * First, retrieve clone of current target cert constraints, and
-         * then add more selection criteria based on current validation state.
-         */
-        X509CertSelector sel = (X509CertSelector) targetCertConstraints.clone();
-
-        /*
-         * Match on issuer (subject of previous cert)
-         */
-        sel.setIssuer(currentState.subjectDN);
-
-        /*
-         * Match on certificate validity date.
-         */
-        sel.setCertificateValid(buildParams.date());
-
-        /*
-         * Policy processing optimizations
-         */
-        if (currentState.explicitPolicy == 0)
-            sel.setPolicy(getMatchingPolicies());
-
-        /*
-         * If previous cert has a subject key identifier extension,
-         * use it to match on authority key identifier extension.
-         */
-        /*if (currentState.subjKeyId != null) {
-          AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension(
-                (KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID),
-                null, null);
-        sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue());
-        }*/
-
-        /*
-         * Require EE certs
-         */
-        sel.setBasicConstraints(-2);
-
-        /* Retrieve matching certs from CertStores */
-        HashSet<X509Certificate> eeCerts = new HashSet<>();
-        addMatchingCerts(sel, certStores, eeCerts, true);
-
-        if (debug != null) {
-            debug.println("ReverseBuilder.getMatchingEECerts got "
-                          + eeCerts.size() + " certs.");
-        }
-        return eeCerts;
-    }
-
-    /*
-     * Retrieves all CA certificates which satisfy constraints
-     * and requirements specified in the parameters and PKIX state.
-     */
-    private Collection<X509Certificate> getMatchingCACerts
-        (ReverseState currentState, List<CertStore> certStores)
-        throws CertificateException, CertStoreException, IOException {
-
-        /*
-         * Compose a CertSelector to filter out
-         * certs which do not satisfy requirements.
-         */
-        X509CertSelector sel = new X509CertSelector();
-
-        /*
-         * Match on issuer (subject of previous cert)
-         */
-        sel.setIssuer(currentState.subjectDN);
-
-        /*
-         * Match on certificate validity date.
-         */
-        sel.setCertificateValid(buildParams.date());
-
-        /*
-         * Match on target subject name (checks that current cert's
-         * name constraints permit it to certify target).
-         * (4 is the integer type for DIRECTORY name).
-         */
-        byte[] subject = targetCertConstraints.getSubjectAsBytes();
-        if (subject != null) {
-            sel.addPathToName(4, subject);
-        } else {
-            X509Certificate cert = targetCertConstraints.getCertificate();
-            if (cert != null) {
-                sel.addPathToName(4,
-                                  cert.getSubjectX500Principal().getEncoded());
-            }
-        }
-
-        /*
-         * Policy processing optimizations
-         */
-        if (currentState.explicitPolicy == 0)
-            sel.setPolicy(getMatchingPolicies());
-
-        /*
-         * If previous cert has a subject key identifier extension,
-         * use it to match on authority key identifier extension.
-         */
-        /*if (currentState.subjKeyId != null) {
-          AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension(
-                (KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID),
-                                null, null);
-          sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue());
-        }*/
-
-        /*
-         * Require CA certs
-         */
-        sel.setBasicConstraints(0);
-
-        /* Retrieve matching certs from CertStores */
-        ArrayList<X509Certificate> reverseCerts = new ArrayList<>();
-        addMatchingCerts(sel, certStores, reverseCerts, true);
-
-        /* Sort remaining certs using name constraints */
-        Collections.sort(reverseCerts, new PKIXCertComparator());
-
-        if (debug != null)
-            debug.println("ReverseBuilder.getMatchingCACerts got " +
-                          reverseCerts.size() + " certs.");
-        return reverseCerts;
-    }
-
-    /*
-     * This inner class compares 2 PKIX certificates according to which
-     * should be tried first when building a path to the target. For
-     * now, the algorithm is to look at name constraints in each cert and those
-     * which constrain the path closer to the target should be
-     * ranked higher. Later, we may want to consider other components,
-     * such as key identifiers.
-     */
-    class PKIXCertComparator implements Comparator<X509Certificate> {
-
-        private Debug debug = Debug.getInstance("certpath");
-
-        @Override
-        public int compare(X509Certificate cert1, X509Certificate cert2) {
-
-            /*
-             * if either cert certifies the target, always
-             * put at head of list.
-             */
-            X500Principal targetSubject = buildParams.targetSubject();
-            if (cert1.getSubjectX500Principal().equals(targetSubject)) {
-                return -1;
-            }
-            if (cert2.getSubjectX500Principal().equals(targetSubject)) {
-                return 1;
-            }
-
-            int targetDist1;
-            int targetDist2;
-            try {
-                X500Name targetSubjectName = X500Name.asX500Name(targetSubject);
-                targetDist1 = Builder.targetDistance(
-                    null, cert1, targetSubjectName);
-                targetDist2 = Builder.targetDistance(
-                    null, cert2, targetSubjectName);
-            } catch (IOException e) {
-                if (debug != null) {
-                    debug.println("IOException in call to Builder.targetDistance");
-                    e.printStackTrace();
-                }
-                throw new ClassCastException
-                    ("Invalid target subject distinguished name");
-            }
-
-            if (targetDist1 == targetDist2)
-                return 0;
-
-            if (targetDist1 == -1)
-                return 1;
-
-            if (targetDist1 < targetDist2)
-                return -1;
-
-            return 1;
-        }
-    }
-
-    /**
-     * Verifies a matching certificate.
-     *
-     * This method executes any of the validation steps in the PKIX path validation
-     * algorithm which were not satisfied via filtering out non-compliant
-     * certificates with certificate matching rules.
-     *
-     * If the last certificate is being verified (the one whose subject
-     * matches the target subject, then the steps in Section 6.1.4 of the
-     * Certification Path Validation algorithm are NOT executed,
-     * regardless of whether or not the last cert is an end-entity
-     * cert or not. This allows callers to certify CA certs as
-     * well as EE certs.
-     *
-     * @param cert the certificate to be verified
-     * @param currentState the current state against which the cert is verified
-     * @param certPathList the certPathList generated thus far
-     */
-    @Override
-    void verifyCert(X509Certificate cert, State currState,
-        List<X509Certificate> certPathList)
-        throws GeneralSecurityException
-    {
-        if (debug != null) {
-            debug.println("ReverseBuilder.verifyCert(SN: "
-                + Debug.toHexString(cert.getSerialNumber())
-                + "\n  Subject: " + cert.getSubjectX500Principal() + ")");
-        }
-
-        ReverseState currentState = (ReverseState) currState;
-
-        /* we don't perform any validation of the trusted cert */
-        if (currentState.isInitial()) {
-            return;
-        }
-
-        // BEGIN Android-removed: Android doesn't use this mechanism for checking untrusted certificates.
-        // // Don't bother to verify untrusted certificate more.
-        // currentState.untrustedChecker.check(cert,
-        //                             Collections.<String>emptySet());
-        // END Android-removed: Android doesn't use this mechanism for checking untrusted certificates.
-
-        /*
-         * check for looping - abort a loop if
-         * ((we encounter the same certificate twice) AND
-         * ((policyMappingInhibited = true) OR (no policy mapping
-         * extensions can be found between the occurrences of the same
-         * certificate)))
-         * in order to facilitate the check to see if there are
-         * any policy mapping extensions found between the occurrences
-         * of the same certificate, we reverse the certpathlist first
-         */
-        if ((certPathList != null) && (!certPathList.isEmpty())) {
-            List<X509Certificate> reverseCertList = new ArrayList<>();
-            for (X509Certificate c : certPathList) {
-                reverseCertList.add(0, c);
-            }
-
-            boolean policyMappingFound = false;
-            for (X509Certificate cpListCert : reverseCertList) {
-                X509CertImpl cpListCertImpl = X509CertImpl.toImpl(cpListCert);
-                PolicyMappingsExtension policyMappingsExt =
-                        cpListCertImpl.getPolicyMappingsExtension();
-                if (policyMappingsExt != null) {
-                    policyMappingFound = true;
-                }
-                if (debug != null)
-                    debug.println("policyMappingFound = " + policyMappingFound);
-                if (cert.equals(cpListCert)) {
-                    if ((buildParams.policyMappingInhibited()) ||
-                        (!policyMappingFound)){
-                        if (debug != null)
-                            debug.println("loop detected!!");
-                        throw new CertPathValidatorException("loop detected");
-                    }
-                }
-            }
-        }
-
-        /* check if target cert */
-        boolean finalCert = cert.getSubjectX500Principal().equals(buildParams.targetSubject());
-
-        /* check if CA cert */
-        boolean caCert = (cert.getBasicConstraints() != -1 ? true : false);
-
-        /* if there are more certs to follow, verify certain constraints */
-        if (!finalCert) {
-
-            /* check if CA cert */
-            if (!caCert)
-                throw new CertPathValidatorException("cert is NOT a CA cert");
-
-            /* If the certificate was not self-issued, verify that
-             * remainingCerts is greater than zero
-             */
-            if ((currentState.remainingCACerts <= 0) && !X509CertImpl.isSelfIssued(cert)) {
-                    throw new CertPathValidatorException
-                        ("pathLenConstraint violated, path too long", null,
-                         null, -1, PKIXReason.PATH_TOO_LONG);
-            }
-
-            /*
-             * Check keyUsage extension (only if CA cert and not final cert)
-             */
-            KeyChecker.verifyCAKeyUsage(cert);
-
-        } else {
-
-            /*
-             * If final cert, check that it satisfies specified target
-             * constraints
-             */
-            if (targetCertConstraints.match(cert) == false) {
-                throw new CertPathValidatorException("target certificate " +
-                    "constraints check failed");
-            }
-        }
-
-        /*
-         * Check revocation.
-         */
-        if (buildParams.revocationEnabled() && currentState.revChecker != null) {
-            currentState.revChecker.check(cert, Collections.<String>emptySet());
-        }
-
-        /* Check name constraints if this is not a self-issued cert */
-        if (finalCert || !X509CertImpl.isSelfIssued(cert)){
-            if (currentState.nc != null) {
-                try {
-                    if (!currentState.nc.verify(cert)){
-                        throw new CertPathValidatorException
-                            ("name constraints check failed", null, null, -1,
-                             PKIXReason.INVALID_NAME);
-                    }
-                } catch (IOException ioe) {
-                    throw new CertPathValidatorException(ioe);
-                }
-            }
-        }
-
-        /*
-         * Check policy
-         */
-        X509CertImpl certImpl = X509CertImpl.toImpl(cert);
-        currentState.rootNode = PolicyChecker.processPolicies
-            (currentState.certIndex, initPolicies,
-            currentState.explicitPolicy, currentState.policyMapping,
-            currentState.inhibitAnyPolicy,
-            buildParams.policyQualifiersRejected(), currentState.rootNode,
-            certImpl, finalCert);
-
-        /*
-         * Check CRITICAL private extensions
-         */
-        Set<String> unresolvedCritExts = cert.getCriticalExtensionOIDs();
-        if (unresolvedCritExts == null) {
-            unresolvedCritExts = Collections.<String>emptySet();
-        }
-
-        /*
-         * Check that the signature algorithm is not disabled.
-         */
-        currentState.algorithmChecker.check(cert, unresolvedCritExts);
-
-        for (PKIXCertPathChecker checker : currentState.userCheckers) {
-            checker.check(cert, unresolvedCritExts);
-        }
-
-        /*
-         * Look at the remaining extensions and remove any ones we have
-         * already checked. If there are any left, throw an exception!
-         */
-        if (!unresolvedCritExts.isEmpty()) {
-            unresolvedCritExts.remove(BasicConstraints_Id.toString());
-            unresolvedCritExts.remove(NameConstraints_Id.toString());
-            unresolvedCritExts.remove(CertificatePolicies_Id.toString());
-            unresolvedCritExts.remove(PolicyMappings_Id.toString());
-            unresolvedCritExts.remove(PolicyConstraints_Id.toString());
-            unresolvedCritExts.remove(InhibitAnyPolicy_Id.toString());
-            unresolvedCritExts.remove(SubjectAlternativeName_Id.toString());
-            unresolvedCritExts.remove(KeyUsage_Id.toString());
-            unresolvedCritExts.remove(ExtendedKeyUsage_Id.toString());
-
-            if (!unresolvedCritExts.isEmpty())
-                throw new CertPathValidatorException
-                    ("Unrecognized critical extension(s)", null, null, -1,
-                     PKIXReason.UNRECOGNIZED_CRIT_EXT);
-        }
-
-        /*
-         * Check signature.
-         */
-        if (buildParams.sigProvider() != null) {
-            cert.verify(currentState.pubKey, buildParams.sigProvider());
-        } else {
-            cert.verify(currentState.pubKey);
-        }
-    }
-
-    /**
-     * Verifies whether the input certificate completes the path.
-     * This checks whether the cert is the target certificate.
-     *
-     * @param cert the certificate to test
-     * @return a boolean value indicating whether the cert completes the path.
-     */
-    @Override
-    boolean isPathCompleted(X509Certificate cert) {
-        return cert.getSubjectX500Principal().equals(buildParams.targetSubject());
-    }
-
-    /** Adds the certificate to the certPathList
-     *
-     * @param cert the certificate to be added
-     * @param certPathList the certification path list
-     */
-    @Override
-    void addCertToPath(X509Certificate cert,
-        LinkedList<X509Certificate> certPathList) {
-        certPathList.addLast(cert);
-    }
-
-    /** Removes final certificate from the certPathList
-     *
-     * @param certPathList the certification path list
-     */
-    @Override
-    void removeFinalCertFromPath(LinkedList<X509Certificate> certPathList) {
-        certPathList.removeLast();
-    }
-}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/ReverseState.java b/ojluni/src/main/java/sun/security/provider/certpath/ReverseState.java
deleted file mode 100644
index a9944af..0000000
--- a/ojluni/src/main/java/sun/security/provider/certpath/ReverseState.java
+++ /dev/null
@@ -1,407 +0,0 @@
-/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.provider.certpath;
-
-import java.io.IOException;
-import java.security.PublicKey;
-import java.security.cert.CertificateException;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.PKIXCertPathChecker;
-import java.security.cert.PKIXRevocationChecker;
-import java.security.cert.TrustAnchor;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Set;
-import javax.security.auth.x500.X500Principal;
-
-import sun.security.provider.certpath.PKIX.BuilderParams;
-import sun.security.util.Debug;
-import sun.security.x509.NameConstraintsExtension;
-import sun.security.x509.SubjectKeyIdentifierExtension;
-import sun.security.x509.X509CertImpl;
-
-/**
- * A specification of a reverse PKIX validation state
- * which is initialized by each build and updated each time a
- * certificate is added to the current path.
- * @since       1.4
- * @author      Sean Mullan
- * @author      Yassir Elley
- */
-
-class ReverseState implements State {
-
-    private static final Debug debug = Debug.getInstance("certpath");
-
-    /* The subject DN of the last cert in the path */
-    X500Principal subjectDN;
-
-    /* The subject public key of the last cert */
-    PublicKey pubKey;
-
-    /* The subject key identifier extension (if any) of the last cert */
-    SubjectKeyIdentifierExtension subjKeyId;
-
-    /* The PKIX constrained/excluded subtrees state variable */
-    NameConstraintsExtension nc;
-
-    /* The PKIX explicit policy, policy mapping, and inhibit_any-policy
-       state variables */
-    int explicitPolicy;
-    int policyMapping;
-    int inhibitAnyPolicy;
-    int certIndex;
-    PolicyNodeImpl rootNode;
-
-    /* The number of remaining CA certs which may follow in the path.
-     * -1: previous cert was an EE cert
-     * 0: only EE certs may follow.
-     * >0 and <Integer.MAX_VALUE:no more than this number of CA certs may follow
-     * Integer.MAX_VALUE: unlimited
-     */
-    int remainingCACerts;
-
-    /* The list of user-defined checkers retrieved from the PKIXParameters
-     * instance */
-    ArrayList<PKIXCertPathChecker> userCheckers;
-
-    /* Flag indicating if state is initial (path is just starting) */
-    private boolean init = true;
-
-    /* the checker used for revocation status */
-    RevocationChecker revChecker;
-
-    /* the algorithm checker */
-    AlgorithmChecker algorithmChecker;
-
-    /* the untrusted certificates checker */
-    // Android-removed: Android doesn't use this mechanism for checking untrusted certificates.
-    // UntrustedChecker untrustedChecker;
-
-    /* the trust anchor used to validate the path */
-    TrustAnchor trustAnchor;
-
-    /* Flag indicating if current cert can vouch for the CRL for
-     * the next cert
-     */
-    boolean crlSign = true;
-
-    /**
-     * Returns a boolean flag indicating if the state is initial
-     * (just starting)
-     *
-     * @return boolean flag indicating if the state is initial (just starting)
-     */
-    @Override
-    public boolean isInitial() {
-        return init;
-    }
-
-    /**
-     * Display state for debugging purposes
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("State [");
-        sb.append("\n  subjectDN of last cert: ").append(subjectDN);
-        sb.append("\n  subjectKeyIdentifier: ").append
-                 (String.valueOf(subjKeyId));
-        sb.append("\n  nameConstraints: ").append(String.valueOf(nc));
-        sb.append("\n  certIndex: ").append(certIndex);
-        sb.append("\n  explicitPolicy: ").append(explicitPolicy);
-        sb.append("\n  policyMapping:  ").append(policyMapping);
-        sb.append("\n  inhibitAnyPolicy:  ").append(inhibitAnyPolicy);
-        sb.append("\n  rootNode: ").append(rootNode);
-        sb.append("\n  remainingCACerts: ").append(remainingCACerts);
-        sb.append("\n  crlSign: ").append(crlSign);
-        sb.append("\n  init: ").append(init);
-        sb.append("\n]\n");
-        return sb.toString();
-    }
-
-    /**
-     * Initialize the state.
-     *
-     * @param buildParams builder parameters
-     */
-    public void initState(BuilderParams buildParams)
-        throws CertPathValidatorException
-    {
-        /*
-         * Initialize number of remainingCACerts.
-         * Note that -1 maxPathLen implies unlimited.
-         * 0 implies only an EE cert is acceptable.
-         */
-        int maxPathLen = buildParams.maxPathLength();
-        remainingCACerts = (maxPathLen == -1) ? Integer.MAX_VALUE
-                                              : maxPathLen;
-
-        /* Initialize explicit policy state variable */
-        if (buildParams.explicitPolicyRequired()) {
-            explicitPolicy = 0;
-        } else {
-            // unconstrained if maxPathLen is -1,
-            // otherwise, we want to initialize this to the value of the
-            // longest possible path + 1 (i.e. maxpathlen + finalcert + 1)
-            explicitPolicy = (maxPathLen == -1) ? maxPathLen : maxPathLen + 2;
-        }
-
-        /* Initialize policy mapping state variable */
-        if (buildParams.policyMappingInhibited()) {
-            policyMapping = 0;
-        } else {
-            policyMapping = (maxPathLen == -1) ? maxPathLen : maxPathLen + 2;
-        }
-
-        /* Initialize inhibit any policy state variable */
-        if (buildParams.anyPolicyInhibited()) {
-            inhibitAnyPolicy = 0;
-        } else {
-            inhibitAnyPolicy = (maxPathLen == -1) ? maxPathLen : maxPathLen + 2;
-        }
-
-        /* Initialize certIndex */
-        certIndex = 1;
-
-        /* Initialize policy tree */
-        Set<String> initExpPolSet = new HashSet<>(1);
-        initExpPolSet.add(PolicyChecker.ANY_POLICY);
-
-        rootNode = new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null,
-                                      false, initExpPolSet, false);
-
-        /*
-         * Initialize each user-defined checker
-         * Shallow copy the checkers
-         */
-        userCheckers = new ArrayList<>(buildParams.certPathCheckers());
-        /* initialize each checker (just in case) */
-        for (PKIXCertPathChecker checker : userCheckers) {
-            checker.init(false);
-        }
-
-        /* Start by trusting the cert to sign CRLs */
-        crlSign = true;
-
-        init = true;
-    }
-
-    /**
-     * Update the state with the specified trust anchor.
-     *
-     * @param anchor the most-trusted CA
-     * @param buildParams builder parameters
-     */
-    public void updateState(TrustAnchor anchor, BuilderParams buildParams)
-        throws CertificateException, IOException, CertPathValidatorException
-    {
-        trustAnchor = anchor;
-        X509Certificate trustedCert = anchor.getTrustedCert();
-        if (trustedCert != null) {
-            updateState(trustedCert);
-        } else {
-            X500Principal caName = anchor.getCA();
-            updateState(anchor.getCAPublicKey(), caName);
-        }
-
-        // The user specified AlgorithmChecker and RevocationChecker may not be
-        // able to set the trust anchor until now.
-        boolean revCheckerAdded = false;
-        for (PKIXCertPathChecker checker : userCheckers) {
-            if (checker instanceof AlgorithmChecker) {
-                ((AlgorithmChecker)checker).trySetTrustAnchor(anchor);
-            } else if (checker instanceof PKIXRevocationChecker) {
-                if (revCheckerAdded) {
-                    throw new CertPathValidatorException(
-                        "Only one PKIXRevocationChecker can be specified");
-                }
-                // if it's our own, initialize it
-                if (checker instanceof RevocationChecker) {
-                    ((RevocationChecker)checker).init(anchor, buildParams);
-                }
-                ((PKIXRevocationChecker)checker).init(false);
-                revCheckerAdded = true;
-            }
-        }
-
-        // only create a RevocationChecker if revocation is enabled and
-        // a PKIXRevocationChecker has not already been added
-        if (buildParams.revocationEnabled() && !revCheckerAdded) {
-            revChecker = new RevocationChecker(anchor, buildParams);
-            revChecker.init(false);
-        }
-
-        init = false;
-    }
-
-    /**
-     * Update the state. This method is used when the most-trusted CA is
-     * a trusted public-key and caName, instead of a trusted cert.
-     *
-     * @param pubKey the public key of the trusted CA
-     * @param subjectDN the subject distinguished name of the trusted CA
-     */
-    private void updateState(PublicKey pubKey, X500Principal subjectDN) {
-
-        /* update subject DN */
-        this.subjectDN = subjectDN;
-
-        /* update subject public key */
-        this.pubKey = pubKey;
-    }
-
-    /**
-     * Update the state with the next certificate added to the path.
-     *
-     * @param cert the certificate which is used to update the state
-     */
-    public void updateState(X509Certificate cert)
-        throws CertificateException, IOException, CertPathValidatorException {
-
-        if (cert == null) {
-            return;
-        }
-
-        /* update subject DN */
-        subjectDN = cert.getSubjectX500Principal();
-
-        /* check for key needing to inherit alg parameters */
-        X509CertImpl icert = X509CertImpl.toImpl(cert);
-        PublicKey newKey = cert.getPublicKey();
-        if (PKIX.isDSAPublicKeyWithoutParams(newKey)) {
-            newKey = BasicChecker.makeInheritedParamsKey(newKey, pubKey);
-        }
-
-        /* update subject public key */
-        pubKey = newKey;
-
-        /*
-         * if this is a trusted cert (init == true), then we
-         * don't update any of the remaining fields
-         */
-        if (init) {
-            init = false;
-            return;
-        }
-
-        /* update subject key identifier */
-        subjKeyId = icert.getSubjectKeyIdentifierExtension();
-
-        /* update crlSign */
-        crlSign = RevocationChecker.certCanSignCrl(cert);
-
-        /* update current name constraints */
-        if (nc != null) {
-            nc.merge(icert.getNameConstraintsExtension());
-        } else {
-            nc = icert.getNameConstraintsExtension();
-            if (nc != null) {
-                // Make sure we do a clone here, because we're probably
-                // going to modify this object later and we don't want to
-                // be sharing it with a Certificate object!
-                nc = (NameConstraintsExtension) nc.clone();
-            }
-        }
-
-        /* update policy state variables */
-        explicitPolicy =
-            PolicyChecker.mergeExplicitPolicy(explicitPolicy, icert, false);
-        policyMapping =
-            PolicyChecker.mergePolicyMapping(policyMapping, icert);
-        inhibitAnyPolicy =
-            PolicyChecker.mergeInhibitAnyPolicy(inhibitAnyPolicy, icert);
-        certIndex++;
-
-        /*
-         * Update remaining CA certs
-         */
-        remainingCACerts =
-            ConstraintsChecker.mergeBasicConstraints(cert, remainingCACerts);
-
-        init = false;
-    }
-
-    /**
-     * Returns a boolean flag indicating if a key lacking necessary key
-     * algorithm parameters has been encountered.
-     *
-     * @return boolean flag indicating if key lacking parameters encountered.
-     */
-    @Override
-    public boolean keyParamsNeeded() {
-        /* when building in reverse, we immediately get parameters needed
-         * or else throw an exception
-         */
-        return false;
-    }
-
-    /*
-     * Clone current state. The state is cloned as each cert is
-     * added to the path. This is necessary if backtracking occurs,
-     * and a prior state needs to be restored.
-     *
-     * Note that this is a SMART clone. Not all fields are fully copied,
-     * because some of them (e.g., subjKeyId) will
-     * not have their contents modified by subsequent calls to updateState.
-     */
-    @Override
-    @SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly
-    public Object clone() {
-        try {
-            ReverseState clonedState = (ReverseState) super.clone();
-
-            /* clone checkers, if cloneable */
-            clonedState.userCheckers =
-                        (ArrayList<PKIXCertPathChecker>)userCheckers.clone();
-            ListIterator<PKIXCertPathChecker> li =
-                        clonedState.userCheckers.listIterator();
-            while (li.hasNext()) {
-                PKIXCertPathChecker checker = li.next();
-                if (checker instanceof Cloneable) {
-                    li.set((PKIXCertPathChecker)checker.clone());
-                }
-            }
-
-            /* make copy of name constraints */
-            if (nc != null) {
-                clonedState.nc = (NameConstraintsExtension) nc.clone();
-            }
-
-            /* make copy of policy tree */
-            if (rootNode != null) {
-                clonedState.rootNode = rootNode.copyTree();
-            }
-
-            return clonedState;
-        } catch (CloneNotSupportedException e) {
-            throw new InternalError(e.toString(), e);
-        }
-    }
-}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/RevocationChecker.java b/ojluni/src/main/java/sun/security/provider/certpath/RevocationChecker.java
index 61d5fd7..7445ade 100644
--- a/ojluni/src/main/java/sun/security/provider/certpath/RevocationChecker.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/RevocationChecker.java
@@ -1041,20 +1041,17 @@
                 boolean signFlag = true;
                 List<? extends Certificate> cpList =
                     cpbr.getCertPath().getCertificates();
-                if (cpList.isEmpty()) {
-                    return;
-                }
                 try {
-                    for (int i = cpList.size()-1; i >= 0; i-- ) {
-                        X509Certificate cert = (X509Certificate)cpList.get(i);
+                    for (int i = cpList.size() - 1; i >= 0; i--) {
+                        X509Certificate cert = (X509Certificate) cpList.get(i);
 
                         if (debug != null) {
                             debug.println("RevocationChecker.buildToNewKey()"
-                                          + " index " + i + " checking "
-                                          + cert);
+                                    + " index " + i + " checking "
+                                    + cert);
                         }
                         checkCRLs(cert, prevKey2, null, signFlag, true,
-                                  stackedCerts, newAnchors);
+                                stackedCerts, newAnchors);
                         signFlag = certCanSignCrl(cert);
                         prevKey2 = cert.getPublicKey();
                     }
@@ -1073,8 +1070,10 @@
                 // If it doesn't check out, try to find a different key.
                 // And if we can't find a key, then return false.
                 PublicKey newKey = cpbr.getPublicKey();
+                X509Certificate newCert = cpList.isEmpty() ?
+                    null : (X509Certificate) cpList.get(0);
                 try {
-                    checkCRLs(currCert, newKey, (X509Certificate) cpList.get(0),
+                    checkCRLs(currCert, newKey, newCert,
                               true, false, null, params.trustAnchors());
                     // If that passed, the cert is OK!
                     return;
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilder.java b/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilder.java
index 9b21a99..7ff8d6c 100644
--- a/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilder.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilder.java
@@ -35,8 +35,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.LinkedList;
 import java.util.Set;
@@ -47,8 +45,7 @@
 import sun.security.util.Debug;
 
 /**
- * This class is able to build certification paths in either the forward
- * or reverse directions.
+ * This class builds certification paths in the forward direction.
  *
  * <p> If successful, it returns a certification path which has successfully
  * satisfied all the constraints and requirements specified in the
@@ -102,10 +99,8 @@
     /**
      * Attempts to build a certification path using the Sun build
      * algorithm from a trusted anchor(s) to a target subject, which must both
-     * be specified in the input parameter set. By default, this method will
-     * attempt to build in the forward direction. In order to build in the
-     * reverse direction, the caller needs to pass in an instance of
-     * SunCertPathBuilderParameters with the buildForward flag set to false.
+     * be specified in the input parameter set. This method will
+     * attempt to build in the forward direction: from the target to the CA.
      *
      * <p>The certification path that is constructed is validated
      * according to the PKIX specification.
@@ -162,11 +157,7 @@
         policyTreeResult = null;
         LinkedList<X509Certificate> certPathList = new LinkedList<>();
         try {
-            if (buildParams.buildForward()) {
-                buildForward(adjList, certPathList, searchAllCertStores);
-            } else {
-                buildReverse(adjList, certPathList);
-            }
+            buildForward(adjList, certPathList, searchAllCertStores);
         } catch (GeneralSecurityException | IOException e) {
             if (debug != null) {
                 debug.println("SunCertPathBuilder.engineBuild() exception in "
@@ -210,82 +201,6 @@
     }
 
     /*
-     * Private build reverse method.
-     */
-    private void buildReverse(List<List<Vertex>> adjacencyList,
-                              LinkedList<X509Certificate> certPathList)
-        throws GeneralSecurityException, IOException
-    {
-        if (debug != null) {
-            debug.println("SunCertPathBuilder.buildReverse()...");
-            debug.println("SunCertPathBuilder.buildReverse() InitialPolicies: "
-                + buildParams.initialPolicies());
-        }
-
-        ReverseState currentState = new ReverseState();
-        /* Initialize adjacency list */
-        adjacencyList.clear();
-        adjacencyList.add(new LinkedList<Vertex>());
-
-        /*
-         * Perform a search using each trust anchor, until a valid
-         * path is found
-         */
-        Iterator<TrustAnchor> iter = buildParams.trustAnchors().iterator();
-        while (iter.hasNext()) {
-            TrustAnchor anchor = iter.next();
-
-            /* check if anchor satisfies target constraints */
-            if (anchorIsTarget(anchor, buildParams.targetCertConstraints())) {
-                this.trustAnchor = anchor;
-                this.pathCompleted = true;
-                this.finalPublicKey = anchor.getTrustedCert().getPublicKey();
-                break;
-            }
-
-            // skip anchor if it contains a DSA key with no DSA params
-            X509Certificate trustedCert = anchor.getTrustedCert();
-            PublicKey pubKey = trustedCert != null ? trustedCert.getPublicKey()
-                                                   : anchor.getCAPublicKey();
-
-            if (PKIX.isDSAPublicKeyWithoutParams(pubKey)) {
-                continue;
-            }
-
-            /* Initialize current state */
-            currentState.initState(buildParams);
-            currentState.updateState(anchor, buildParams);
-
-            currentState.algorithmChecker = new AlgorithmChecker(anchor);
-            // Android-removed: Android doesn't use this mechanism for checking untrusted certificates
-            // currentState.untrustedChecker = new UntrustedChecker();
-            try {
-                depthFirstSearchReverse(null, currentState,
-                                        new ReverseBuilder(buildParams),
-                                        adjacencyList, certPathList);
-            } catch (GeneralSecurityException | IOException e) {
-                // continue on error if more anchors to try
-                if (iter.hasNext())
-                    continue;
-                else
-                    throw e;
-            }
-
-            // break out of loop if search is successful
-            if (pathCompleted) {
-                break;
-            }
-        }
-
-        if (debug != null) {
-            debug.println("SunCertPathBuilder.buildReverse() returned from "
-                + "depthFirstSearchReverse()");
-            debug.println("SunCertPathBuilder.buildReverse() "
-                + "certPathList.size: " + certPathList.size());
-        }
-    }
-
-    /*
      * Private build forward method.
      */
     private void buildForward(List<List<Vertex>> adjacencyList,
@@ -634,147 +549,6 @@
     }
 
     /*
-     * This method performs a depth first search for a certification
-     * path while building reverse which meets the requirements set in
-     * the parameters object.
-     * It uses an adjacency list to store all certificates which were
-     * tried (i.e. at one time added to the path - they may not end up in
-     * the final path if backtracking occurs). This information can
-     * be used later to debug or demo the build.
-     *
-     * See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman"
-     * for an explanation of the DFS algorithm.
-     *
-     * @param dN the distinguished name being currently searched for certs
-     * @param currentState the current PKIX validation state
-     */
-    private void depthFirstSearchReverse(X500Principal dN,
-                                         ReverseState currentState,
-                                         ReverseBuilder builder,
-                                         List<List<Vertex>> adjList,
-                                         LinkedList<X509Certificate> cpList)
-        throws GeneralSecurityException, IOException
-    {
-        if (debug != null)
-            debug.println("SunCertPathBuilder.depthFirstSearchReverse(" + dN
-                + ", " + currentState.toString() + ")");
-
-        /*
-         * Find all the certificates issued by dN which
-         * satisfy the PKIX certification path constraints.
-         */
-        Collection<X509Certificate> certs =
-            builder.getMatchingCerts(currentState, buildParams.certStores());
-        List<Vertex> vertices = addVertices(certs, adjList);
-        if (debug != null)
-            debug.println("SunCertPathBuilder.depthFirstSearchReverse(): "
-                + "certs.size=" + vertices.size());
-
-        /*
-         * For each cert in the collection, verify anything
-         * that hasn't been checked yet (signature, revocation, etc)
-         * and check for loops. Call depthFirstSearchReverse()
-         * recursively for each good cert.
-         */
-        for (Vertex vertex : vertices) {
-            /**
-             * Restore state to currentState each time through the loop.
-             * This is important because some of the user-defined
-             * checkers modify the state, which MUST be restored if
-             * the cert eventually fails to lead to the target and
-             * the next matching cert is tried.
-             */
-            ReverseState nextState = (ReverseState) currentState.clone();
-            X509Certificate cert = vertex.getCertificate();
-            try {
-                builder.verifyCert(cert, nextState, cpList);
-            } catch (GeneralSecurityException gse) {
-                if (debug != null)
-                    debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
-                        + ": validation failed: " + gse);
-                vertex.setThrowable(gse);
-                continue;
-            }
-
-            /*
-             * Certificate is good, add it to the path (if it isn't a
-             * self-signed cert) and update state
-             */
-            if (!currentState.isInitial())
-                builder.addCertToPath(cert, cpList);
-            // save trust anchor
-            this.trustAnchor = currentState.trustAnchor;
-
-            /*
-             * Check if path is completed, return ASAP if so.
-             */
-            if (builder.isPathCompleted(cert)) {
-                if (debug != null)
-                    debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
-                        + ": path completed!");
-                pathCompleted = true;
-
-                PolicyNodeImpl rootNode = nextState.rootNode;
-
-                if (rootNode == null)
-                    policyTreeResult = null;
-                else {
-                    policyTreeResult = rootNode.copyTree();
-                    ((PolicyNodeImpl)policyTreeResult).setImmutable();
-                }
-
-                /*
-                 * Extract and save the final target public key
-                 */
-                finalPublicKey = cert.getPublicKey();
-                if (PKIX.isDSAPublicKeyWithoutParams(finalPublicKey)) {
-                    finalPublicKey =
-                        BasicChecker.makeInheritedParamsKey
-                            (finalPublicKey, currentState.pubKey);
-                }
-
-                return;
-            }
-
-            /* Update the PKIX state */
-            nextState.updateState(cert);
-
-            /*
-             * Append an entry for cert in adjacency list and
-             * set index for current vertex.
-             */
-            adjList.add(new LinkedList<Vertex>());
-            vertex.setIndex(adjList.size() - 1);
-
-            /* recursively search for matching certs at next dN */
-            depthFirstSearchReverse(cert.getSubjectX500Principal(), nextState,
-                                    builder, adjList, cpList);
-
-            /*
-             * If path has been completed, return ASAP!
-             */
-            if (pathCompleted) {
-                return;
-            } else {
-                /*
-                 * If we get here, it means we have searched all possible
-                 * certs issued by the dN w/o finding any matching certs. This
-                 * means we have to backtrack to the previous cert in the path
-                 * and try some other paths.
-                 */
-                if (debug != null)
-                    debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
-                        + ": backtracking");
-                if (!currentState.isInitial())
-                    builder.removeFinalCertFromPath(cpList);
-            }
-        }
-        if (debug != null)
-            debug.println("SunCertPathBuilder.depthFirstSearchReverse() all "
-                + "certs in this adjacency list checked");
-    }
-
-    /*
      * Adds a collection of matching certificates to the
      * adjacency list.
      */
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderParameters.java b/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderParameters.java
deleted file mode 100644
index 186e252..0000000
--- a/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderParameters.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.provider.certpath;
-
-import java.security.InvalidAlgorithmParameterException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.cert.*;
-import java.util.Set;
-
-/**
- * This class specifies the set of parameters used as input for the Sun
- * certification path build algorithm. It is identical to PKIXBuilderParameters
- * with the addition of a <code>buildForward</code> parameter which allows
- * the caller to specify whether or not the path should be constructed in
- * the forward direction.
- *
- * The default for the <code>buildForward</code> parameter is
- * true, which means that the build algorithm should construct paths
- * from the target subject back to the trusted anchor.
- *
- * @since       1.4
- * @author      Sean Mullan
- * @author      Yassir Elley
- */
-public class SunCertPathBuilderParameters extends PKIXBuilderParameters {
-
-    private boolean buildForward = true;
-
-    /**
-     * Creates an instance of <code>SunCertPathBuilderParameters</code> with the
-     * specified parameter values.
-     *
-     * @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s
-     * @param targetConstraints a <code>CertSelector</code> specifying the
-     * constraints on the target certificate
-     * @throws InvalidAlgorithmParameterException if the specified
-     * <code>Set</code> is empty <code>(trustAnchors.isEmpty() == true)</code>
-     * @throws NullPointerException if the specified <code>Set</code> is
-     * <code>null</code>
-     * @throws ClassCastException if any of the elements in the <code>Set</code>
-     * are not of type <code>java.security.cert.TrustAnchor</code>
-     */
-    public SunCertPathBuilderParameters(Set<TrustAnchor> trustAnchors,
-        CertSelector targetConstraints) throws InvalidAlgorithmParameterException
-    {
-        super(trustAnchors, targetConstraints);
-        setBuildForward(true);
-    }
-
-    /**
-     * Creates an instance of <code>SunCertPathBuilderParameters</code> that
-     * uses the specified <code>KeyStore</code> to populate the set
-     * of most-trusted CA certificates.
-     *
-     * @param keystore A keystore from which the set of most-trusted
-     * CA certificates will be populated.
-     * @param targetConstraints a <code>CertSelector</code> specifying the
-     * constraints on the target certificate
-     * @throws KeyStoreException if the keystore has not been initialized.
-     * @throws InvalidAlgorithmParameterException if the keystore does
-     * not contain at least one trusted certificate entry
-     * @throws NullPointerException if the keystore is <code>null</code>
-     */
-    public SunCertPathBuilderParameters(KeyStore keystore,
-        CertSelector targetConstraints)
-        throws KeyStoreException, InvalidAlgorithmParameterException
-    {
-        super(keystore, targetConstraints);
-        setBuildForward(true);
-    }
-
-    /**
-     * Returns the value of the buildForward flag.
-     *
-     * @return the value of the buildForward flag
-     */
-    public boolean getBuildForward() {
-        return this.buildForward;
-    }
-
-    /**
-     * Sets the value of the buildForward flag. If true, paths
-     * are built from the target subject to the trusted anchor.
-     * If false, paths are built from the trusted anchor to the
-     * target subject. The default value if not specified is true.
-     *
-     * @param buildForward the value of the buildForward flag
-     */
-    public void setBuildForward(boolean buildForward) {
-        this.buildForward = buildForward;
-    }
-
-    /**
-     * Returns a formatted string describing the parameters.
-     *
-     * @return a formatted string describing the parameters.
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("[\n");
-        sb.append(super.toString());
-        sb.append("  Build Forward Flag: " + String.valueOf(buildForward) + "\n");
-        sb.append("]\n");
-        return sb.toString();
-    }
-}
diff --git a/ojluni/src/main/java/sun/security/util/AbstractAlgorithmConstraints.java b/ojluni/src/main/java/sun/security/util/AbstractAlgorithmConstraints.java
index 1b64d9d..2e851e2 100644
--- a/ojluni/src/main/java/sun/security/util/AbstractAlgorithmConstraints.java
+++ b/ojluni/src/main/java/sun/security/util/AbstractAlgorithmConstraints.java
@@ -46,19 +46,23 @@
     // Get algorithm constraints from the specified security property.
     static String[] getAlgorithms(String propertyName) {
         String property = AccessController.doPrivileged(
-                (PrivilegedAction<String>) () -> Security.getProperty(
-                        propertyName));
+                new PrivilegedAction<String>() {
+                    @Override
+                    public String run() {
+                        return Security.getProperty(propertyName);
+                    }
+                });
+
 
         String[] algorithmsInProperty = null;
         if (property != null && !property.isEmpty()) {
             // remove double quote marks from beginning/end of the property
-            if (property.charAt(0) == '"'
-                    && property.charAt(property.length() - 1) == '"') {
+            if (property.length() >= 2 && property.charAt(0) == '"' &&
+                    property.charAt(property.length() - 1) == '"') {
                 property = property.substring(1, property.length() - 1);
             }
             algorithmsInProperty = property.split(",");
-            for (int i = 0; i < algorithmsInProperty.length;
-                    i++) {
+            for (int i = 0; i < algorithmsInProperty.length; i++) {
                 algorithmsInProperty[i] = algorithmsInProperty[i].trim();
             }
         }
diff --git a/ojluni/src/main/java/sun/security/util/AnchorCertificates.java b/ojluni/src/main/java/sun/security/util/AnchorCertificates.java
index 6bc0030..8498342 100644
--- a/ojluni/src/main/java/sun/security/util/AnchorCertificates.java
+++ b/ojluni/src/main/java/sun/security/util/AnchorCertificates.java
@@ -56,7 +56,7 @@
                 try {
                     cacerts = KeyStore.getInstance("JKS");
                     try (FileInputStream fis = new FileInputStream(f)) {
-                        cacerts.load(fis, "changeit".toCharArray());
+                        cacerts.load(fis, null);
                         certs = new HashSet<>();
                         Enumeration<String> list = cacerts.aliases();
                         String alias;
diff --git a/ojluni/src/main/java/sun/security/util/DerInputBuffer.java b/ojluni/src/main/java/sun/security/util/DerInputBuffer.java
index b375c60..01f7df5 100644
--- a/ojluni/src/main/java/sun/security/util/DerInputBuffer.java
+++ b/ojluni/src/main/java/sun/security/util/DerInputBuffer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -71,6 +71,7 @@
         return retval;
     }
 
+    // BEGIN Android-added: Added getPos & getSlice, needed for APK parsing
     int getPos() {
         return pos;
     }
@@ -80,6 +81,7 @@
         System.arraycopy(buf, startPos, result, 0, size);
         return result;
     }
+    // END Android-added: Added getPos & getSlice, needed for APK parsing
 
     int peek() throws IOException {
         if (pos >= count)
@@ -157,6 +159,11 @@
         System.arraycopy(buf, pos, bytes, 0, len);
         skip(len);
 
+        // check to make sure no extra leading 0s for DER
+        if (len >= 2 && (bytes[0] == 0) && (bytes[1] >= 0)) {
+            throw new IOException("Invalid encoding: redundant leading 0s");
+        }
+
         if (makePositive) {
             return new BigInteger(1, bytes);
         } else {
diff --git a/ojluni/src/main/java/sun/security/util/DerInputStream.java b/ojluni/src/main/java/sun/security/util/DerInputStream.java
index dae8afd..b182d4e 100644
--- a/ojluni/src/main/java/sun/security/util/DerInputStream.java
+++ b/ojluni/src/main/java/sun/security/util/DerInputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -77,7 +77,7 @@
      * @param data the buffer from which to create the string (CONSUMED)
      */
     public DerInputStream(byte[] data) throws IOException {
-        init(data, 0, data.length);
+        init(data, 0, data.length, true);
     }
 
     /**
@@ -92,23 +92,48 @@
      *          starting at "offset"
      */
     public DerInputStream(byte[] data, int offset, int len) throws IOException {
-        init(data, offset, len);
+        init(data, offset, len, true);
+    }
+
+    /**
+     * Create a DER input stream from part of a data buffer with
+     * additional arg to indicate whether to allow constructed
+     * indefinite-length encoding.
+     * The buffer is not copied, it is shared.  Accordingly, the
+     * buffer should be treated as read-only.
+     *
+     * @param data the buffer from which to create the string (CONSUMED)
+     * @param offset the first index of <em>data</em> which will
+     *          be read as DER input in the new stream
+     * @param len how long a chunk of the buffer to use,
+     *          starting at "offset"
+     * @param allowIndefiniteLength whether to allow constructed
+     *          indefinite-length encoding
+     */
+    public DerInputStream(byte[] data, int offset, int len,
+        boolean allowIndefiniteLength) throws IOException {
+        init(data, offset, len, allowIndefiniteLength);
     }
 
     /*
      * private helper routine
      */
-    private void init(byte[] data, int offset, int len) throws IOException {
+    private void init(byte[] data, int offset, int len,
+        boolean allowIndefiniteLength) throws IOException {
         if ((offset+2 > data.length) || (offset+len > data.length)) {
             throw new IOException("Encoding bytes too short");
         }
         // check for indefinite length encoding
         if (DerIndefLenConverter.isIndefinite(data[offset+1])) {
-            byte[] inData = new byte[len];
-            System.arraycopy(data, offset, inData, 0, len);
+            if (!allowIndefiniteLength) {
+                throw new IOException("Indefinite length BER encoding found");
+            } else {
+                byte[] inData = new byte[len];
+                System.arraycopy(data, offset, inData, 0, len);
 
-            DerIndefLenConverter derIn = new DerIndefLenConverter();
-            buffer = new DerInputBuffer(derIn.convert(inData));
+                DerIndefLenConverter derIn = new DerIndefLenConverter();
+                buffer = new DerInputBuffer(derIn.convert(inData));
+            }
         } else
             buffer = new DerInputBuffer(data, offset, len);
         buffer.mark(Integer.MAX_VALUE);
@@ -233,12 +258,21 @@
          * First byte = number of excess bits in the last octet of the
          * representation.
          */
-        int validBits = length*8 - buffer.read();
+        int excessBits = buffer.read();
+        if (excessBits < 0) {
+            throw new IOException("Unused bits of bit string invalid");
+        }
+        int validBits = length*8 - excessBits;
+        if (validBits < 0) {
+            throw new IOException("Valid bits of bit string invalid");
+        }
 
         byte[] repn = new byte[length];
 
-        if ((length != 0) && (buffer.read(repn) != length))
-            throw new IOException("short read of DER bit string");
+        if ((length != 0) && (buffer.read(repn) != length)) {
+            throw new IOException("Short read of DER bit string");
+        }
+
         return new BitArray(validBits, repn);
     }
 
@@ -252,7 +286,7 @@
         int length = getLength(buffer);
         byte[] retval = new byte[length];
         if ((length != 0) && (buffer.read(retval) != length))
-            throw new IOException("short read of DER octet string");
+            throw new IOException("Short read of DER octet string");
 
         return retval;
     }
@@ -262,7 +296,7 @@
      */
     public void getBytes(byte[] val) throws IOException {
         if ((val.length != 0) && (buffer.read(val) != val.length)) {
-            throw new IOException("short read of DER octet string");
+            throw new IOException("Short read of DER octet string");
         }
     }
 
@@ -384,7 +418,7 @@
         DerInputStream  newstr;
 
         byte lenByte = (byte)buffer.read();
-        int len = getLength((lenByte & 0xff), buffer);
+        int len = getLength(lenByte, buffer);
 
         if (len == -1) {
            // indefinite length encoding found
@@ -430,7 +464,7 @@
         } while (newstr.available() > 0);
 
         if (newstr.available() != 0)
-            throw new IOException("extra data at end of vector");
+            throw new IOException("Extra data at end of vector");
 
         /*
          * Now stick them into the array we're returning.
@@ -521,7 +555,7 @@
         int length = getLength(buffer);
         byte[] retval = new byte[length];
         if ((length != 0) && (buffer.read(retval) != length))
-            throw new IOException("short read of DER " +
+            throw new IOException("Short read of DER " +
                                   stringName + " string");
 
         return new String(retval, enc);
@@ -582,7 +616,11 @@
      */
     static int getLength(int lenByte, InputStream in) throws IOException {
         int value, tmp;
+        if (lenByte == -1) {
+            throw new IOException("Short read of DER length");
+        }
 
+        String mdName = "DerInputStream.getLength(): ";
         tmp = lenByte;
         if ((tmp & 0x080) == 0x00) { // short form, 1 byte datum
             value = tmp;
@@ -596,17 +634,23 @@
             if (tmp == 0)
                 return -1;
             if (tmp < 0 || tmp > 4)
-                throw new IOException("DerInputStream.getLength(): lengthTag="
-                    + tmp + ", "
+                throw new IOException(mdName + "lengthTag=" + tmp + ", "
                     + ((tmp < 0) ? "incorrect DER encoding." : "too big."));
 
-            for (value = 0; tmp > 0; tmp --) {
+            value = 0x0ff & in.read();
+            tmp--;
+            if (value == 0) {
+                // DER requires length value be encoded in minimum number of bytes
+                throw new IOException(mdName + "Redundant length bytes found");
+            }
+            while (tmp-- > 0) {
                 value <<= 8;
                 value += 0x0ff & in.read();
             }
             if (value < 0) {
-                throw new IOException("DerInputStream.getLength(): "
-                        + "Invalid length bytes");
+                throw new IOException(mdName + "Invalid length bytes");
+            } else if (value <= 127) {
+                throw new IOException(mdName + "Should use short form for length");
             }
         }
         return value;
diff --git a/ojluni/src/main/java/sun/security/util/DerValue.java b/ojluni/src/main/java/sun/security/util/DerValue.java
index 6bca1f7..21116dc 100644
--- a/ojluni/src/main/java/sun/security/util/DerValue.java
+++ b/ojluni/src/main/java/sun/security/util/DerValue.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -257,7 +257,7 @@
         int startPosInInput = in.getPos();
         tag = (byte)in.read();
         byte lenByte = (byte)in.read();
-        length = DerInputStream.getLength((lenByte & 0xff), in);
+        length = DerInputStream.getLength(lenByte, in);
         if (length == -1) {  // indefinite length encoding found
             DerInputBuffer inbuf = in.dup();
             int readLen = inbuf.available();
@@ -375,7 +375,7 @@
 
         tag = (byte)in.read();
         byte lenByte = (byte)in.read();
-        length = DerInputStream.getLength((lenByte & 0xff), in);
+        length = DerInputStream.getLength(lenByte, in);
         if (length == -1) { // indefinite length encoding found
             int readLen = in.available();
             int offset = 2;     // for tag and length bytes
diff --git a/ojluni/src/main/java/sun/security/util/DisabledAlgorithmConstraints.java b/ojluni/src/main/java/sun/security/util/DisabledAlgorithmConstraints.java
index d8c1462..aecb341 100644
--- a/ojluni/src/main/java/sun/security/util/DisabledAlgorithmConstraints.java
+++ b/ojluni/src/main/java/sun/security/util/DisabledAlgorithmConstraints.java
@@ -56,6 +56,9 @@
     public final static String PROPERTY_TLS_DISABLED_ALGS =
             "jdk.tls.disabledAlgorithms";
 
+    // the known security property, jdk.jar.disabledAlgorithms
+    public static final String PROPERTY_JAR_DISABLED_ALGS =
+            "jdk.jar.disabledAlgorithms";
 
     private final String[] disabledAlgorithms;
     private final Constraints algorithmConstraints;
@@ -257,14 +260,15 @@
                                     toUpperCase(Locale.ENGLISH));
                     policy = constraintEntry.substring(space + 1);
                 } else {
-                    constraintsMap.computeIfAbsent(
+                    constraintsMap.putIfAbsent(
                             constraintEntry.toUpperCase(Locale.ENGLISH),
-                            k -> new HashSet<>());
+                            new HashSet<>());
                     continue;
                 }
 
                 // Convert constraint conditions into Constraint classes
-                Constraint c, lastConstraint = null;
+                Constraint c = null;
+                Constraint lastConstraint = null;
                 // Allow only one jdkCA entry per constraint entry
                 boolean jdkCALimit = false;
 
@@ -292,11 +296,7 @@
                         }
                         c = new jdkCAConstraint(algorithm);
                         jdkCALimit = true;
-                    } else {
-                        throw new IllegalArgumentException("Error in security" +
-                                " property. Constraint unknown: " + entry);
                     }
-
                     // Link multiple conditions for a single constraint
                     // into a linked list.
                     if (lastConstraint == null) {
@@ -304,7 +304,9 @@
                             constraintsMap.putIfAbsent(algorithm,
                                     new HashSet<>());
                         }
-                        constraintsMap.get(algorithm).add(c);
+                        if (c != null) {
+                            constraintsMap.get(algorithm).add(c);
+                        }
                     } else {
                         lastConstraint.nextConstraint = c;
                     }
diff --git a/ojluni/src/main/java/sun/security/util/SignatureFileVerifier.java b/ojluni/src/main/java/sun/security/util/SignatureFileVerifier.java
index fa0f530..ed563dc 100644
--- a/ojluni/src/main/java/sun/security/util/SignatureFileVerifier.java
+++ b/ojluni/src/main/java/sun/security/util/SignatureFileVerifier.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,26 +25,49 @@
 
 package sun.security.util;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.CodeSigner;
+import java.security.CryptoPrimitive;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
 import java.security.cert.CertPath;
 import java.security.cert.X509Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
-import java.security.*;
-import java.io.*;
-import java.util.*;
-import java.util.jar.*;
-
-import sun.security.pkcs.*;
+import java.util.ArrayList;
 import java.util.Base64;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarException;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
 
 import sun.security.jca.Providers;
+import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.SignerInfo;
 
 public class SignatureFileVerifier {
 
     /* Are we debugging ? */
     private static final Debug debug = Debug.getInstance("jar");
 
-    /* cache of CodeSigner objects */
+    private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET =
+            Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST));
+
+    private static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK =
+            new DisabledAlgorithmConstraints(
+                    DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);
+
     private ArrayList<CodeSigner[]> signerCache;
 
     private static final String ATTR_DIGEST =
@@ -200,8 +223,15 @@
 
     /** get digest from cache */
 
-    private MessageDigest getDigest(String algorithm)
-    {
+    private MessageDigest getDigest(String algorithm) throws SignatureException {
+        // check that algorithm is not restricted
+        if (!JAR_DISABLED_CHECK.permits(DIGEST_PRIMITIVE_SET, algorithm, null)) {
+            SignatureException e =
+                    new SignatureException("SignatureFile check failed. " +
+                            "Disabled algorithm used: " + algorithm);
+            throw e;
+        }
+
         if (createdDigests == null)
             createdDigests = new HashMap<String, MessageDigest>();
 
@@ -321,7 +351,7 @@
     private boolean verifyManifestHash(Manifest sf,
                                        ManifestDigester md,
                                        List<Object> manifestDigests)
-         throws IOException
+         throws IOException, SignatureException
     {
         Attributes mattr = sf.getMainAttributes();
         boolean manifestSigned = false;
@@ -365,7 +395,7 @@
 
     private boolean verifyManifestMainAttrs(Manifest sf,
                                         ManifestDigester md)
-         throws IOException
+         throws IOException, SignatureException
     {
         Attributes mattr = sf.getMainAttributes();
         boolean attrsVerified = true;
@@ -431,14 +461,14 @@
     private boolean verifySection(Attributes sfAttr,
                                   String name,
                                   ManifestDigester md)
-         throws IOException
+         throws IOException, SignatureException
     {
         boolean oneDigestVerified = false;
         ManifestDigester.Entry mde = md.get(name,block.isOldStyle());
 
         if (mde == null) {
             throw new SecurityException(
-                  "no manifiest section for signature file entry "+name);
+                  "no manifest section for signature file entry "+name);
         }
 
         if (sfAttr != null) {
diff --git a/ojluni/src/main/java/sun/util/calendar/CalendarSystem.java b/ojluni/src/main/java/sun/util/calendar/CalendarSystem.java
index 58707fb..17ad0e8 100644
--- a/ojluni/src/main/java/sun/util/calendar/CalendarSystem.java
+++ b/ojluni/src/main/java/sun/util/calendar/CalendarSystem.java
@@ -122,7 +122,7 @@
             return GREGORIAN_INSTANCE;
         }
 
-        //Android-changed: remove lazy initialization, use classes instead of class names.
+        // Android-changed: remove lazy initialization, use classes instead of class names.
 
         CalendarSystem cal = calendars.get(calendarName);
         if (cal != null) {
diff --git a/ojluni/src/main/native/PlainSocketImpl.c b/ojluni/src/main/native/PlainSocketImpl.c
deleted file mode 100644
index c4b5be7..0000000
--- a/ojluni/src/main/native/PlainSocketImpl.c
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-#include <errno.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#if defined(__linux__) && !defined(USE_SELECT)
-#include <sys/poll.h>
-#endif
-#include <netinet/tcp.h>        /* Defines TCP_NODELAY, needed for 2.6 */
-#include <netinet/in.h>
-#ifdef __linux__
-#include <netinet/ip.h>
-#endif
-#include <netdb.h>
-#include <stdlib.h>
-
-#ifdef __solaris__
-#include <fcntl.h>
-#endif
-#ifdef __linux__
-#include <unistd.h>
-//#include <sys/sysctl.h>
-#endif
-
-#include "jvm.h"
-#include "jni_util.h"
-#include "net_util.h"
-
-#include "java_net_SocketOptions.h"
-#include "java_net_PlainSocketImpl.h"
-#include "JNIHelp.h"
-
-#define NATIVE_METHOD(className, functionName, signature) \
-{ #functionName, signature, (void*)(className ## _ ## functionName) }
-
-/************************************************************************
- * PlainSocketImpl
- */
-
-static jfieldID IO_fd_fdID;
-
-jfieldID psi_fdID;
-jfieldID psi_addressID;
-jfieldID psi_ipaddressID;
-jfieldID psi_portID;
-jfieldID psi_localportID;
-jfieldID psi_timeoutID;
-jfieldID psi_trafficClassID;
-jfieldID psi_serverSocketID;
-jfieldID psi_fdLockID;
-jfieldID psi_closePendingID;
-
-extern void setDefaultScopeID(JNIEnv *env, struct sockaddr *him);
-
-
-#define SET_NONBLOCKING(fd) {           \
-        int flags = fcntl(fd, F_GETFL); \
-        flags |= O_NONBLOCK;            \
-        fcntl(fd, F_SETFL, flags);      \
-}
-
-#define SET_BLOCKING(fd) {              \
-        int flags = fcntl(fd, F_GETFL); \
-        flags &= ~O_NONBLOCK;           \
-        fcntl(fd, F_SETFL, flags);      \
-}
-
-/*
- * Return the file descriptor given a PlainSocketImpl
- */
-static int getFD(JNIEnv *env, jobject this) {
-    jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
-    CHECK_NULL_RETURN(fdObj, -1);
-    return (*env)->GetIntField(env, fdObj, IO_fd_fdID);
-}
-
-static void PlainSocketImpl_initProto(JNIEnv *env) {
-    jclass cls = (*env)->FindClass(env, "java/net/PlainSocketImpl");
-    psi_fdID = (*env)->GetFieldID(env, cls , "fd",
-                                  "Ljava/io/FileDescriptor;");
-    CHECK_NULL(psi_fdID);
-    psi_addressID = (*env)->GetFieldID(env, cls, "address",
-                                          "Ljava/net/InetAddress;");
-    CHECK_NULL(psi_addressID);
-    psi_portID = (*env)->GetFieldID(env, cls, "port", "I");
-    CHECK_NULL(psi_portID);
-    psi_localportID = (*env)->GetFieldID(env, cls, "localport", "I");
-    CHECK_NULL(psi_localportID);
-    psi_timeoutID = (*env)->GetFieldID(env, cls, "timeout", "I");
-    CHECK_NULL(psi_timeoutID);
-    psi_trafficClassID = (*env)->GetFieldID(env, cls, "trafficClass", "I");
-    CHECK_NULL(psi_trafficClassID);
-    psi_serverSocketID = (*env)->GetFieldID(env, cls, "serverSocket",
-                        "Ljava/net/ServerSocket;");
-    CHECK_NULL(psi_serverSocketID);
-    psi_fdLockID = (*env)->GetFieldID(env, cls, "fdLock",
-                                      "Ljava/lang/Object;");
-    CHECK_NULL(psi_fdLockID);
-    psi_closePendingID = (*env)->GetFieldID(env, cls, "closePending", "Z");
-    CHECK_NULL(psi_closePendingID);
-    IO_fd_fdID = NET_GetFileDescriptorID(env);
-    CHECK_NULL(IO_fd_fdID);
-}
-
-/* a global reference to the java.net.SocketException class. In
- * socketCreate, we ensure that this is initialized. This is to
- * prevent the problem where socketCreate runs out of file
- * descriptors, and is then unable to load the exception class.
- */
-static jclass socketExceptionCls;
-
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketSetOption0
- * Signature: (IZLjava/lang/Object;)V
- */
-JNIEXPORT void JNICALL
-PlainSocketImpl_socketSetOption0(JNIEnv *env, jobject this,
-                                              jint cmd, jboolean on,
-                                              jobject value) {
-    int fd;
-    int level, optname, optlen;
-    union {
-        int i;
-        struct linger ling;
-    } optval;
-
-    /*
-     * Check that socket hasn't been closed
-     */
-    fd = getFD(env, this);
-    if (fd < 0) {
-        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
-                        "Socket closed");
-        return;
-    }
-
-    /*
-     * SO_TIMEOUT is a NOOP on Solaris/Linux
-     */
-    if (cmd == java_net_SocketOptions_SO_TIMEOUT) {
-        return;
-    }
-
-    /*
-     * Map the Java level socket option to the platform specific
-     * level and option name.
-     */
-    if (NET_MapSocketOption(cmd, &level, &optname)) {
-        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
-        return;
-    }
-
-    switch (cmd) {
-        case java_net_SocketOptions_SO_SNDBUF :
-        case java_net_SocketOptions_SO_RCVBUF :
-        case java_net_SocketOptions_SO_LINGER :
-        case java_net_SocketOptions_IP_TOS :
-            {
-                jclass cls;
-                jfieldID fid;
-
-                cls = (*env)->FindClass(env, "java/lang/Integer");
-                CHECK_NULL(cls);
-                fid = (*env)->GetFieldID(env, cls, "value", "I");
-                CHECK_NULL(fid);
-
-                if (cmd == java_net_SocketOptions_SO_LINGER) {
-                    if (on) {
-                        optval.ling.l_onoff = 1;
-                        optval.ling.l_linger = (*env)->GetIntField(env, value, fid);
-                    } else {
-                        optval.ling.l_onoff = 0;
-                        optval.ling.l_linger = 0;
-                    }
-                    optlen = sizeof(optval.ling);
-                } else {
-                    optval.i = (*env)->GetIntField(env, value, fid);
-                    optlen = sizeof(optval.i);
-                }
-
-                break;
-            }
-
-        /* Boolean -> int */
-        default :
-            optval.i = (on ? 1 : 0);
-            optlen = sizeof(optval.i);
-
-    }
-
-    if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) {
-#ifdef __solaris__
-        if (errno == EINVAL) {
-            // On Solaris setsockopt will set errno to EINVAL if the socket
-            // is closed. The default error message is then confusing
-            char fullMsg[128];
-            jio_snprintf(fullMsg, sizeof(fullMsg), "Invalid option or socket reset by remote peer");
-            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", fullMsg);
-            return;
-        }
-#endif /* __solaris__ */
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
-                                      "Error setting socket option");
-    }
-}
-
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketGetOption
- * Signature: (I)I
- */
-JNIEXPORT jint JNICALL
-PlainSocketImpl_socketGetOption(JNIEnv *env, jobject this,
-                                              jint cmd, jobject iaContainerObj) {
-
-    int fd;
-    int level, optname, optlen;
-    union {
-        int i;
-        struct linger ling;
-    } optval;
-
-    /*
-     * Check that socket hasn't been closed
-     */
-    fd = getFD(env, this);
-    if (fd < 0) {
-        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
-                        "Socket closed");
-        return -1;
-    }
-
-    /*
-     * SO_BINDADDR isn't a socket option
-     */
-    if (cmd == java_net_SocketOptions_SO_BINDADDR) {
-        SOCKADDR him;
-        socklen_t len = 0;
-        int port;
-        jobject iaObj;
-        jclass iaCntrClass;
-        jfieldID iaFieldID;
-
-        len = SOCKADDR_LEN;
-
-        if (getsockname(fd, (struct sockaddr *)&him, &len) < 0) {
-            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
-                             "Error getting socket name");
-            return -1;
-        }
-        iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port);
-        CHECK_NULL_RETURN(iaObj, -1);
-
-        iaCntrClass = (*env)->GetObjectClass(env, iaContainerObj);
-        iaFieldID = (*env)->GetFieldID(env, iaCntrClass, "addr", "Ljava/net/InetAddress;");
-        CHECK_NULL_RETURN(iaFieldID, -1);
-        (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj);
-        return 0; /* notice change from before */
-    }
-
-    /*
-     * Map the Java level socket option to the platform specific
-     * level and option name.
-     */
-    if (NET_MapSocketOption(cmd, &level, &optname)) {
-        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
-        return -1;
-    }
-
-    /*
-     * Args are int except for SO_LINGER
-     */
-    if (cmd == java_net_SocketOptions_SO_LINGER) {
-        optlen = sizeof(optval.ling);
-    } else {
-        optlen = sizeof(optval.i);
-    }
-
-    if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
-                                      "Error getting socket option");
-        return -1;
-    }
-
-    switch (cmd) {
-        case java_net_SocketOptions_SO_LINGER:
-            return (optval.ling.l_onoff ? optval.ling.l_linger: -1);
-
-        case java_net_SocketOptions_SO_SNDBUF:
-        case java_net_SocketOptions_SO_RCVBUF:
-        case java_net_SocketOptions_IP_TOS:
-            return optval.i;
-
-        default :
-            return (optval.i == 0) ? -1 : 1;
-    }
-}
-
-
-static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(PlainSocketImpl, socketGetOption, "(ILjava/lang/Object;)I"),
-  NATIVE_METHOD(PlainSocketImpl, socketSetOption0, "(IZLjava/lang/Object;)V"),
-};
-
-void register_java_net_PlainSocketImpl(JNIEnv* env) {
-  jniRegisterNativeMethods(env, "java/net/PlainSocketImpl", gMethods, NELEM(gMethods));
-  PlainSocketImpl_initProto(env);
-}
diff --git a/ojluni/src/main/native/Register.cpp b/ojluni/src/main/native/Register.cpp
index 40c034c..588eedb 100644
--- a/ojluni/src/main/native/Register.cpp
+++ b/ojluni/src/main/native/Register.cpp
@@ -56,7 +56,6 @@
 extern void register_java_net_Inet6Address(JNIEnv*);
 extern void register_java_net_InetAddress(JNIEnv*);
 extern void register_java_net_PlainDatagramSocketImpl(JNIEnv*);
-extern void register_java_net_PlainSocketImpl(JNIEnv*);
 extern void register_java_net_SocketInputStream(JNIEnv*);
 extern void register_java_net_SocketOutputStream(JNIEnv*);
 extern void register_java_nio_Bits(JNIEnv* env);
@@ -125,7 +124,6 @@
     register_java_net_InetAddress(env);
     register_java_net_Inet4Address(env);
     register_java_net_Inet6Address(env);
-    register_java_net_PlainSocketImpl(env);
     register_java_net_SocketInputStream(env);
     register_java_net_SocketOutputStream(env);
     register_java_nio_Bits(env);
diff --git a/ojluni/src/main/native/System.c b/ojluni/src/main/native/System.c
index e75e01c..22c4d52 100644
--- a/ojluni/src/main/native/System.c
+++ b/ojluni/src/main/native/System.c
@@ -244,10 +244,6 @@
     }
 
     WITH_PLATFORM_STRING(env, javaMessage, message) {
-      if (message == NULL) {
-          // Since this function is used for last-gasp debugging output, be noisy on failure.
-          return;
-      }
       LOG_PRI(priority, "System", "%s", message);
     } END_PLATFORM_STRING(env, message);
 
diff --git a/ojluni/src/main/native/java_net_SocketOptions.h b/ojluni/src/main/native/java_net_SocketOptions.h
deleted file mode 100644
index e428bb5..0000000
--- a/ojluni/src/main/native/java_net_SocketOptions.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* This file was generated from java/net/SocketOptions.java and is licensed
- * under the same terms. The copyright and license information for
- * java/net/SocketOptions.java follows.
- *
- * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class java_net_SocketOptions */
-
-#ifndef _Included_java_net_SocketOptions
-#define _Included_java_net_SocketOptions
-#ifdef __cplusplus
-extern "C" {
-#endif
-#undef java_net_SocketOptions_TCP_NODELAY
-#define java_net_SocketOptions_TCP_NODELAY 1L
-#undef java_net_SocketOptions_SO_BINDADDR
-#define java_net_SocketOptions_SO_BINDADDR 15L
-#undef java_net_SocketOptions_SO_REUSEADDR
-#define java_net_SocketOptions_SO_REUSEADDR 4L
-#undef java_net_SocketOptions_SO_BROADCAST
-#define java_net_SocketOptions_SO_BROADCAST 32L
-#undef java_net_SocketOptions_IP_MULTICAST_IF
-#define java_net_SocketOptions_IP_MULTICAST_IF 16L
-#undef java_net_SocketOptions_IP_MULTICAST_IF2
-#define java_net_SocketOptions_IP_MULTICAST_IF2 31L
-#undef java_net_SocketOptions_IP_MULTICAST_LOOP
-#define java_net_SocketOptions_IP_MULTICAST_LOOP 18L
-#undef java_net_SocketOptions_IP_TOS
-#define java_net_SocketOptions_IP_TOS 3L
-#undef java_net_SocketOptions_SO_LINGER
-#define java_net_SocketOptions_SO_LINGER 128L
-#undef java_net_SocketOptions_SO_TIMEOUT
-#define java_net_SocketOptions_SO_TIMEOUT 4102L
-#undef java_net_SocketOptions_SO_SNDBUF
-#define java_net_SocketOptions_SO_SNDBUF 4097L
-#undef java_net_SocketOptions_SO_RCVBUF
-#define java_net_SocketOptions_SO_RCVBUF 4098L
-#undef java_net_SocketOptions_SO_KEEPALIVE
-#define java_net_SocketOptions_SO_KEEPALIVE 8L
-#undef java_net_SocketOptions_SO_OOBINLINE
-#define java_net_SocketOptions_SO_OOBINLINE 4099L
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/ojluni/src/main/native/net_util.h b/ojluni/src/main/native/net_util.h
index 839e947..afd94ed 100644
--- a/ojluni/src/main/native/net_util.h
+++ b/ojluni/src/main/native/net_util.h
@@ -78,6 +78,8 @@
 extern jclass ia4_class;
 extern jmethodID ia4_ctrID;
 
+/* Android-removed: NetworkInterface moved away fro JNI */
+#if 0
 /* NetworkInterface fields */
 extern jclass ni_class;
 extern jfieldID ni_nameID;
@@ -85,13 +87,17 @@
 extern jfieldID ni_addrsID;
 extern jfieldID ni_descID;
 extern jmethodID ni_ctrID;
+#endif
 
+/* Android-removed: PlainSocketImpl moved away fro JNI */
+#if 0
 /* PlainSocketImpl fields */
 extern jfieldID psi_timeoutID;
 extern jfieldID psi_fdID;
 extern jfieldID psi_addressID;
 extern jfieldID psi_portID;
 extern jfieldID psi_localportID;
+#endif
 
 /* Android-removed: DatagramSocket moved away from JNI */
 #if 0
@@ -138,8 +144,11 @@
 void initLocalAddrTable ();
 void parseExclusiveBindProperty(JNIEnv *env);
 
+// Android-removed: unused
+#if 0
 void
 NET_SetTrafficClass(struct sockaddr *him, int trafficClass);
+#endif
 
 JNIEXPORT jint JNICALL
 NET_GetPortFromSockaddr(struct sockaddr *him);
@@ -156,8 +165,11 @@
 int
 NET_IsEqual(jbyte* caddr1, jbyte* caddr2);
 
+// Android-removed: unused
+#if 0
 int
 NET_IsZeroAddr(jbyte* caddr);
+#endif
 
 /* Socket operations
  *
@@ -174,11 +186,14 @@
 JNIEXPORT int JNICALL
 NET_Bind(int fd, struct sockaddr *him, int len);
 
+// Android-removed: unused
+#if 0
 JNIEXPORT int JNICALL
 NET_MapSocketOption(jint cmd, int *level, int *optname);
 
 JNIEXPORT int JNICALL
 NET_MapSocketOptionV6(jint cmd, int *level, int *optname);
+#endif
 
 int getScopeID (struct sockaddr *);
 
diff --git a/ojluni/src/main/native/net_util_md.c b/ojluni/src/main/native/net_util_md.c
index 49c9779..d15e29d 100644
--- a/ojluni/src/main/native/net_util_md.c
+++ b/ojluni/src/main/native/net_util_md.c
@@ -64,7 +64,11 @@
 #include "jvm.h"
 #include "net_util.h"
 
+// Android-removed: unused
+#if 0
 #include "java_net_SocketOptions.h"
+#endif
+
 
 /* needed from libsocket on Solaris 8 */
 
@@ -83,6 +87,8 @@
 #define UDP_EXCLBIND            0x0101
 #endif
 
+// Android-removed: unused
+#if 0
 void setDefaultScopeID(JNIEnv *env, struct sockaddr *him)
 {
 #ifdef MACOSX
@@ -124,6 +130,7 @@
                                                  ni_defaultIndexID);
     return defaultIndex;
 }
+#endif
 
 #ifdef __solaris__
 static int init_tcp_max_buf, init_udp_max_buf;
@@ -817,6 +824,8 @@
     return 0;
 }
 
+// Android-removed: unused
+#if 0
 void
 NET_SetTrafficClass(struct sockaddr *him, int trafficClass) {
 #ifdef AF_INET6
@@ -826,6 +835,7 @@
     }
 #endif /* AF_INET6 */
 }
+#endif
 
 JNIEXPORT jint JNICALL
 NET_GetPortFromSockaddr(struct sockaddr *him) {
@@ -876,6 +886,8 @@
     return (jboolean)(getaddrinfo_ptr != NULL);
 }
 
+// Android-removed: unused
+#if 0
 int NET_IsZeroAddr(jbyte* caddr) {
     int i;
     for (i = 0; i < 16; i++) {
@@ -885,7 +897,10 @@
     }
     return 1;
 }
+#endif
 
+// Android-removed: unused
+#if 0
 /*
  * Map the Java level socket option to the platform specific
  * level and option name.
@@ -952,6 +967,7 @@
     /* not found */
     return -1;
 }
+#endif
 
 /*
  * Wrapper for getsockopt system routine - does any necessary
@@ -1321,6 +1337,8 @@
     return rv;
 }
 
+// Android-removed: unused
+#if 0
 /**
  * Wrapper for select/poll with timeout on a single file descriptor.
  *
@@ -1399,6 +1417,7 @@
 
     return timeout;
 }
+#endif
 
 #if 0
 // Stripped out unused code.
diff --git a/ojluni/src/main/native/net_util_md.h b/ojluni/src/main/native/net_util_md.h
index 46fa8dd..5c6dd42 100644
--- a/ojluni/src/main/native/net_util_md.h
+++ b/ojluni/src/main/native/net_util_md.h
@@ -112,7 +112,10 @@
 #define NET_WAIT_WRITE  0x02
 #define NET_WAIT_CONNECT        0x04
 
+// Android-removed: unused
+#if 0
 extern jint NET_Wait(JNIEnv *env, jint fd, jint flags, jint timeout);
+#endif
 
 /************************************************************************
  * Macros and constants
@@ -149,10 +152,13 @@
 /************************************************************************
  *  Utilities
  */
+// Android-removed: unused
+#if 0
 #ifdef __linux__
 extern int kernelIsV22();
 extern int kernelIsV24();
 #endif
+#endif
 
 void NET_ThrowByNameWithLastError(JNIEnv *env, const char *name,
                    const char *defaultDetail);
diff --git a/ojluni/src/main/native/openjdksub.mk b/ojluni/src/main/native/openjdksub.mk
index 3d37d00..d60bc09 100644
--- a/ojluni/src/main/native/openjdksub.mk
+++ b/ojluni/src/main/native/openjdksub.mk
@@ -50,7 +50,6 @@
     Inet6Address.c \
     Inet4Address.c \
     linux_close.cpp \
-    PlainSocketImpl.c \
     ServerSocketChannelImpl.c \
     SocketInputStream.c \
     SocketOutputStream.c \
diff --git a/ojluni/src/test/java/nio/file/attribute/AclFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/AclFileAttributeViewTest.java
deleted file mode 100644
index 2d9e32c..0000000
--- a/ojluni/src/test/java/nio/file/attribute/AclFileAttributeViewTest.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/* @test
- * @bug 4313887 6838333 6891404
- * @summary Unit test for java.nio.file.attribute.AclFileAttribueView
- * @library ../..
- */
-// Android-changed: Adapted from
-// jdk/test/java/nio/file/attribute/AclFileAttributeView/Basic.java
-// Android-changed: Added package & Test import
-package test.java.nio.file.attribute;
-import org.testng.annotations.Test;
-import test.java.nio.file.TestUtil;
-
-import java.nio.file.*;
-import java.nio.file.attribute.*;
-import java.io.IOException;
-import java.util.*;
-
-import static java.nio.file.attribute.AclEntryType.*;
-import static java.nio.file.attribute.AclEntryPermission.*;
-import static java.nio.file.attribute.AclEntryFlag.*;
-
-// Android-changed: Renamed from "Basic"
-public class AclFileAttributeViewTest {
-
-    static void printAcl(List<AclEntry> acl) {
-        for (AclEntry entry: acl) {
-            System.out.format("  %s%n", entry);
-        }
-    }
-
-    // sanity check read and writing ACL
-    static void testReadWrite(Path dir) throws IOException {
-        Path file = dir.resolve("foo");
-        if (Files.notExists(file))
-            Files.createFile(file);
-
-        AclFileAttributeView view =
-            Files.getFileAttributeView(file, AclFileAttributeView.class);
-
-        // print existing ACL
-        List<AclEntry> acl = view.getAcl();
-        System.out.println(" -- current ACL --");
-        printAcl(acl);
-
-        // insert entry to grant owner read access
-        UserPrincipal owner = view.getOwner();
-        AclEntry entry = AclEntry.newBuilder()
-            .setType(ALLOW)
-            .setPrincipal(owner)
-            .setPermissions(READ_DATA, READ_ATTRIBUTES)
-            .build();
-        System.out.println(" -- insert (entry 0) --");
-        System.out.format("  %s%n", entry);
-        acl.add(0, entry);
-        view.setAcl(acl);
-
-        // re-ACL and check entry
-        List<AclEntry> newacl = view.getAcl();
-        System.out.println(" -- current ACL --");
-        printAcl(acl);
-        if (!newacl.get(0).equals(entry)) {
-            throw new RuntimeException("Entry 0 is not expected");
-        }
-
-        // if PosixFileAttributeView then repeat test with OWNER@
-        if (Files.getFileStore(file).supportsFileAttributeView("posix")) {
-            owner = file.getFileSystem().getUserPrincipalLookupService()
-                .lookupPrincipalByName("OWNER@");
-            entry = AclEntry.newBuilder(entry).setPrincipal(owner).build();
-
-            System.out.println(" -- replace (entry 0) --");
-            System.out.format("  %s%n", entry);
-
-            acl.set(0, entry);
-            view.setAcl(acl);
-            newacl = view.getAcl();
-            System.out.println(" -- current ACL --");
-            printAcl(acl);
-            if (!newacl.get(0).equals(entry)) {
-                throw new RuntimeException("Entry 0 is not expected");
-            }
-        }
-    }
-
-    static FileAttribute<List<AclEntry>> asAclAttribute(final List<AclEntry> acl) {
-        return new FileAttribute<List<AclEntry>>() {
-            public String name() { return "acl:acl"; }
-            public List<AclEntry> value() { return acl; }
-        };
-    }
-
-    static void assertEquals(List<AclEntry> actual, List<AclEntry> expected) {
-        if (!actual.equals(expected)) {
-            System.err.format("Actual: %s\n", actual);
-            System.err.format("Expected: %s\n", expected);
-            throw new RuntimeException("ACL not expected");
-        }
-    }
-
-    // sanity check create a file or directory with initial ACL
-    static void testCreateFile(Path dir) throws IOException {
-        UserPrincipal user = Files.getOwner(dir);
-        AclFileAttributeView view;
-
-        // create file with initial ACL
-        System.out.println("-- create file with initial ACL --");
-        Path file = dir.resolve("gus");
-        List<AclEntry> fileAcl = Arrays.asList(
-            AclEntry.newBuilder()
-                .setType(AclEntryType.ALLOW)
-                .setPrincipal(user)
-                .setPermissions(SYNCHRONIZE, READ_DATA, WRITE_DATA,
-                    READ_ATTRIBUTES, READ_ACL, WRITE_ATTRIBUTES, DELETE)
-                .build());
-        Files.createFile(file, asAclAttribute(fileAcl));
-        view = Files.getFileAttributeView(file, AclFileAttributeView.class);
-        assertEquals(view.getAcl(), fileAcl);
-
-        // create directory with initial ACL
-        System.out.println("-- create directory with initial ACL --");
-        Path subdir = dir.resolve("stuff");
-        List<AclEntry> dirAcl = Arrays.asList(
-            AclEntry.newBuilder()
-                .setType(AclEntryType.ALLOW)
-                .setPrincipal(user)
-                .setPermissions(SYNCHRONIZE, ADD_FILE, DELETE)
-                .build(),
-            AclEntry.newBuilder(fileAcl.get(0))
-                .setFlags(FILE_INHERIT)
-                .build());
-        Files.createDirectory(subdir, asAclAttribute(dirAcl));
-        view = Files.getFileAttributeView(subdir, AclFileAttributeView.class);
-        assertEquals(view.getAcl(), dirAcl);
-    }
-
-    // Android-changed: Removed args & added @Test
-    @Test
-    public static void main() throws IOException {
-        // use work directory rather than system temporary directory to
-        // improve chances that ACLs are supported
-        // Android-changed: Switched to temp dir due to permissions
-        // Path dir = Paths.get("./work" + new Random().nextInt());
-        // Files.createTempDirectory(dir);
-        Path dir = Files.createTempDirectory("acl");
-        try {
-            if (!Files.getFileStore(dir).supportsFileAttributeView("acl")) {
-                System.out.println("ACLs not supported - test skipped!");
-                return;
-            }
-            testReadWrite(dir);
-
-            // only currently feasible on Windows
-            if (System.getProperty("os.name").startsWith("Windows"))
-                testCreateFile(dir);
-
-        } finally {
-            TestUtil.removeAll(dir);
-        }
-    }
-}
diff --git a/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java
index b393981..c2a30bb 100644
--- a/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java
+++ b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java
@@ -80,15 +80,17 @@
         boolean supportsCreationTimeRead = false;
         boolean supportsCreationTimeWrite = false;
         String os = System.getProperty("os.name");
-        if (os.contains("OS X") && Files.getFileStore(file).type().equals("hfs")) {
-            supportsCreationTimeRead = true;
-        } else if (os.startsWith("Windows")) {
-            String type = Files.getFileStore(file).type();
-            if (type.equals("NTFS") || type.equals("FAT")) {
-                supportsCreationTimeRead = true;
-                supportsCreationTimeWrite = true;
-            }
-        }
+        // Android-changed: This test is never run on Mac OS or windows hosts.
+        //
+        // if (os.contains("OS X") && Files.getFileStore(file).type().equals("hfs")) {
+        //     supportsCreationTimeRead = true;
+        // } else if (os.startsWith("Windows")) {
+        //     String type = Files.getFileStore(file).type();
+        //     if (type.equals("NTFS") || type.equals("FAT")) {
+        //         supportsCreationTimeRead = true;
+        //         supportsCreationTimeWrite = true;
+        //     }
+        // }
 
         /**
          * If the creation-time attribute is supported then change the file's
diff --git a/ojluni/src/test/java/nio/file/attribute/DosFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/DosFileAttributeViewTest.java
deleted file mode 100644
index 8386a31..0000000
--- a/ojluni/src/test/java/nio/file/attribute/DosFileAttributeViewTest.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/* @test
- * @bug 4313887 6838333
- * @summary Unit test for java.nio.file.attribute.DosFileAttributeView
- * @library ../..
- */
-// Android-changed: Adapted from
-// jdk/test/java/nio/file/attribute/DosFileAttributeView/Basic.java
-// Android-changed: Added package & Test import
-package test.java.nio.file.attribute;
-import org.testng.annotations.Test;
-import test.java.nio.file.TestUtil;
-
-import java.nio.file.*;
-import static java.nio.file.LinkOption.*;
-import java.nio.file.attribute.*;
-import java.util.*;
-import java.io.IOException;
-
-// Android-changed: Renamed from "Basic"
-public class DosFileAttributeViewTest {
-
-    static void check(boolean okay) {
-        if (!okay)
-            throw new RuntimeException("Test failed");
-    }
-
-    // exercise each setter/getter method, leaving all attributes unset
-    static void testAttributes(DosFileAttributeView view) throws IOException {
-        view.setReadOnly(true);
-        check(view.readAttributes().isReadOnly());
-        view.setReadOnly(false);
-        check(!view.readAttributes().isReadOnly());
-        view.setHidden(true);
-        check(view.readAttributes().isHidden());
-        view.setHidden(false);
-        check(!view.readAttributes().isHidden());
-        view.setArchive(true);
-        check(view.readAttributes().isArchive());
-        view.setArchive(false);
-        check(!view.readAttributes().isArchive());
-        view.setSystem(true);
-        check(view.readAttributes().isSystem());
-        view.setSystem(false);
-        check(!view.readAttributes().isSystem());
-    }
-
-    // set the value of all attributes
-    static void setAll(DosFileAttributeView view, boolean value)
-        throws IOException
-    {
-        view.setReadOnly(value);
-        view.setHidden(value);
-        view.setArchive(value);
-        view.setSystem(value);
-    }
-
-    // read and write FAT attributes
-    static void readWriteTests(Path dir) throws IOException {
-
-        // create "foo" and test that we can read/write each FAT attribute
-        Path file = Files.createFile(dir.resolve("foo"));
-        try {
-            testAttributes(Files.getFileAttributeView(file, DosFileAttributeView.class));
-
-            // Following tests use a symbolic link so skip if not supported
-            if (!TestUtil.supportsLinks(dir))
-                return;
-
-            Path link = dir.resolve("link");
-            Files.createSymbolicLink(link, file);
-
-            // test following links
-            testAttributes(Files.getFileAttributeView(link, DosFileAttributeView.class));
-
-            // test not following links
-            try {
-                try {
-                    testAttributes(Files
-                        .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS));
-                } catch (IOException x) {
-                    // access to link attributes not supported
-                    return;
-                }
-
-                // set all attributes on link
-                // run test on target of link (which leaves them all un-set)
-                // check that attributes of link remain all set
-                setAll(Files
-                    .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS), true);
-                testAttributes(Files
-                    .getFileAttributeView(link, DosFileAttributeView.class));
-                DosFileAttributes attrs =
-                    Files.getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS)
-                         .readAttributes();
-                check(attrs.isReadOnly());
-                check(attrs.isHidden());
-                check(attrs.isArchive());
-                check(attrs.isSystem());
-                setAll(Files
-                    .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS), false);
-
-                // set all attributes on target
-                // run test on link (which leaves them all un-set)
-                // check that attributes of target remain all set
-                setAll(Files.getFileAttributeView(link, DosFileAttributeView.class), true);
-                testAttributes(Files
-                    .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS));
-                attrs = Files.getFileAttributeView(link, DosFileAttributeView.class).readAttributes();
-                check(attrs.isReadOnly());
-                check(attrs.isHidden());
-                check(attrs.isArchive());
-                check(attrs.isSystem());
-                setAll(Files.getFileAttributeView(link, DosFileAttributeView.class), false);
-            } finally {
-                TestUtil.deleteUnchecked(link);
-            }
-        } finally {
-            TestUtil.deleteUnchecked(file);
-        }
-    }
-
-    // Android-changed: Removed args & added @Test
-    @Test
-    public static void main() throws IOException {
-        // create temporary directory to run tests
-        Path dir = TestUtil.createTemporaryDirectory();
-
-        try {
-            // skip test if DOS file attributes not supported
-            if (!Files.getFileStore(dir).supportsFileAttributeView("dos")) {
-                System.out.println("DOS file attribute not supported.");
-                return;
-            }
-            readWriteTests(dir);
-        } finally {
-            TestUtil.removeAll(dir);
-        }
-    }
-}
diff --git a/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java
index 7825837..4355b31 100644
--- a/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java
+++ b/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java
@@ -388,10 +388,13 @@
     public static void main() throws IOException {
         Path dir = TestUtil.createTemporaryDirectory();
         try {
-            if (!Files.getFileStore(dir).supportsFileAttributeView("posix")) {
-                System.out.println("PosixFileAttributeView not supported");
-                return;
-            }
+            // Android-changed: PosixFileAttributeViews are unconditionally supported in
+            // all writeable partitions.
+            //
+            // if (!Files.getFileStore(dir).supportsFileAttributeView("posix")) {
+            //     System.out.println("PosixFileAttributeView not supported");
+            //     return;
+            // }
 
             permissionTests(dir);
             createTests(dir);
diff --git a/ojluni/src/test/java/nio/file/attribute/UserDefinedFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/UserDefinedFileAttributeViewTest.java
deleted file mode 100644
index 6fb3c78..0000000
--- a/ojluni/src/test/java/nio/file/attribute/UserDefinedFileAttributeViewTest.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/* @test
- * @bug 4313887 6838333
- * @summary Unit test for java.nio.file.attribute.UserDefinedFileAttributeView
- * @library ../..
- */
-// Android-changed: Adapted from
-// jdk/test/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java
-// Android-changed: Added package & Test import
-package test.java.nio.file.attribute;
-import org.testng.annotations.Test;
-import test.java.nio.file.TestUtil;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.nio.file.*;
-import static java.nio.file.LinkOption.*;
-import java.nio.file.attribute.*;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Random;
-import java.io.IOException;
-
-// Android-changed: Renamed from "Basic"
-public class UserDefinedFileAttributeViewTest {
-
-    private static Random rand = new Random();
-
-    private static final String ATTR_NAME = "mime_type";
-    private static final String ATTR_VALUE = "text/plain";
-    private static final String ATTR_VALUE2 = "text/html";
-
-    static interface Task {
-        void run() throws Exception;
-    }
-
-    static void tryCatch(Class<? extends Throwable> ex, Task task) {
-        boolean caught = false;
-        try {
-            task.run();
-        } catch (Throwable x) {
-            if (ex.isAssignableFrom(x.getClass())) {
-                caught = true;
-            } else {
-                throw new RuntimeException(x);
-            }
-        }
-        if (!caught)
-            throw new RuntimeException(ex.getName() + " expected");
-    }
-
-    static void expectNullPointerException(Task task) {
-        tryCatch(NullPointerException.class, task);
-    }
-
-    static boolean hasAttribute(UserDefinedFileAttributeView view, String attr)
-        throws IOException
-    {
-        for (String name: view.list()) {
-            if (name.equals(ATTR_NAME))
-                return true;
-        }
-        return false;
-    }
-
-    static void test(Path file, LinkOption... options) throws IOException {
-        final UserDefinedFileAttributeView view =
-            Files.getFileAttributeView(file, UserDefinedFileAttributeView.class, options);
-        ByteBuffer buf = rand.nextBoolean() ?
-            ByteBuffer.allocate(100) : ByteBuffer.allocateDirect(100);
-
-        // Test: write
-        buf.put(ATTR_VALUE.getBytes()).flip();
-        int size = buf.remaining();
-        int nwrote = view.write(ATTR_NAME, buf);
-        if (nwrote != size)
-            throw new RuntimeException("Unexpected number of bytes written");
-
-        // Test: size
-        if (view.size(ATTR_NAME) != size)
-            throw new RuntimeException("Unexpected size");
-
-        // Test: read
-        buf.clear();
-        int nread = view.read(ATTR_NAME, buf);
-        if (nread != size)
-            throw new RuntimeException("Unexpected number of bytes read");
-        buf.flip();
-        String value = Charset.defaultCharset().decode(buf).toString();
-        if (!value.equals(ATTR_VALUE))
-            throw new RuntimeException("Unexpected attribute value");
-
-        // Test: read with insufficient space
-        tryCatch(IOException.class, new Task() {
-            public void run() throws IOException {
-                view.read(ATTR_NAME, ByteBuffer.allocateDirect(1));
-            }});
-
-        // Test: replace value
-        buf.clear();
-        buf.put(ATTR_VALUE2.getBytes()).flip();
-        size = buf.remaining();
-        view.write(ATTR_NAME, buf);
-        if (view.size(ATTR_NAME) != size)
-            throw new RuntimeException("Unexpected size");
-
-        // Test: list
-        if (!hasAttribute(view, ATTR_NAME))
-            throw new RuntimeException("Attribute name not in list");
-
-        // Test: delete
-        view.delete(ATTR_NAME);
-        if (hasAttribute(view, ATTR_NAME))
-            throw new RuntimeException("Attribute name in list");
-
-        // Test: dynamic access
-        String name = "user:" + ATTR_NAME;
-        byte[] valueAsBytes = ATTR_VALUE.getBytes();
-        Files.setAttribute(file, name, valueAsBytes);
-        byte[] actualAsBytes = (byte[])Files.getAttribute(file, name);
-        if (!Arrays.equals(valueAsBytes, actualAsBytes))
-            throw new RuntimeException("Unexpected attribute value");
-        Map<String,?> map = Files.readAttributes(file, name);
-        if (!Arrays.equals(valueAsBytes, (byte[])map.get(ATTR_NAME)))
-            throw new RuntimeException("Unexpected attribute value");
-        map = Files.readAttributes(file, "user:*");
-        if (!Arrays.equals(valueAsBytes, (byte[])map.get(ATTR_NAME)))
-            throw new RuntimeException("Unexpected attribute value");
-    }
-
-    static void miscTests(final Path file) throws IOException {
-        final UserDefinedFileAttributeView view =
-            Files.getFileAttributeView(file, UserDefinedFileAttributeView.class);
-        view.write(ATTR_NAME, ByteBuffer.wrap(ATTR_VALUE.getBytes()));
-
-        // NullPointerException
-        final ByteBuffer buf = ByteBuffer.allocate(100);
-
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-                view.read(null, buf);
-            }});
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-                view.read(ATTR_NAME, null);
-            }});
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-                view.write(null, buf);
-            }});
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-               view.write(ATTR_NAME, null);
-            }});
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-                view.size(null);
-            }});
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-                view.delete(null);
-            }});
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-                Files.getAttribute(file, null);
-            }});
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-                Files.getAttribute(file, "user:" + ATTR_NAME, (LinkOption[])null);
-            }});
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-                Files.setAttribute(file, "user:" + ATTR_NAME, null);
-            }});
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-                Files.setAttribute(file, null, new byte[0]);
-            }});
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-                Files.setAttribute(file, "user: " + ATTR_NAME, new byte[0], (LinkOption[])null);
-            }});
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-                Files.readAttributes(file, (String)null);
-            }});
-        expectNullPointerException(new Task() {
-            public void run() throws IOException {
-                Files.readAttributes(file, "*", (LinkOption[])null);
-            }});
-
-        // Read-only buffer
-        tryCatch(IllegalArgumentException.class, new Task() {
-            public void run() throws IOException {
-                ByteBuffer buf = ByteBuffer.wrap(ATTR_VALUE.getBytes()).asReadOnlyBuffer();
-                view.write(ATTR_NAME, buf);
-                buf.flip();
-                view.read(ATTR_NAME, buf);
-            }});
-
-        // Zero bytes remaining
-        tryCatch(IOException.class, new Task() {
-            public void run() throws IOException {
-                ByteBuffer buf = buf = ByteBuffer.allocateDirect(100);
-                buf.position(buf.capacity());
-                view.read(ATTR_NAME, buf);
-            }});
-    }
-
-    // Android-changed: Removed args & added @Test
-    @Test
-    public static void main() throws IOException {
-        // create temporary directory to run tests
-        Path dir = TestUtil.createTemporaryDirectory();
-        try {
-            if (!Files.getFileStore(dir).supportsFileAttributeView("user")) {
-                System.out.println("UserDefinedFileAttributeView not supported - skip test");
-                return;
-            }
-
-            // test access to user defined attributes of regular file
-            Path file = dir.resolve("foo.html");
-            Files.createFile(file);
-            try {
-                test(file);
-            } finally {
-                Files.delete(file);
-            }
-
-            // test access to user defined attributes of directory
-            Path subdir = dir.resolve("foo");
-            Files.createDirectory(subdir);
-            try {
-                test(subdir);
-            } finally {
-                Files.delete(subdir);
-            }
-
-            // test access to user defined attributes of sym link
-            if (TestUtil.supportsLinks(dir)) {
-                Path target = dir.resolve("doesnotexist");
-                Path link = dir.resolve("link");
-                Files.createSymbolicLink(link, target);
-                try {
-                    test(link, NOFOLLOW_LINKS);
-                } catch (IOException x) {
-                    // access to attributes of sym link may not be supported
-                } finally {
-                    Files.delete(link);
-                }
-            }
-
-            // misc. tests
-            try {
-                file = dir.resolve("foo.txt");
-                Files.createFile(file);
-                miscTests(dir);
-            } finally {
-                Files.delete(file);
-            }
-
-        } finally {
-            TestUtil.removeAll(dir);
-        }
-    }
- }
diff --git a/ojluni/src/test/java/security/cert/PKIXCertPathValidatorValidity.java b/ojluni/src/test/java/security/cert/PKIXCertPathValidatorValidity.java
new file mode 100644
index 0000000..fd68b44
--- /dev/null
+++ b/ojluni/src/test/java/security/cert/PKIXCertPathValidatorValidity.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+/**
+ * @test
+ * @bug 8021804
+ * @summary CertPath should validate even if the validity period of the
+ *          root cert does not include the validity period of a subordinate
+ *          cert.
+ */
+// Android-changed: Adapted from
+// jdk/test/sun/security/provider/certpath/PKIXCertPathValidator/Validity.java
+// Android-changed: Added package & Test import
+package test.java.security.cert;
+import org.testng.annotations.Test;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.*;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+// Android-changed: s/Validity/PKIXCertPathValidatorValidity
+public class PKIXCertPathValidatorValidity {
+
+    /*
+     * Subject: OU=TestOrg, CN=TestCA
+     * Issuer: OU=TestOrg, CN=TestCA
+     * Validity
+     *     Not Before: Feb 26 21:33:55 2014 GMT
+           Not After : Feb 26 21:33:55 2024 GMT
+     * Version 1
+     */
+    static String CACertStr =
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIIBvTCCASYCCQCQRiTo4lBCFjANBgkqhkiG9w0BAQUFADAjMRAwDgYDVQQLDAdU\n" +
+        "ZXN0T3JnMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMTQwMjI2MjEzMzU1WhcNMjQwMjI2\n" +
+        "MjEzMzU1WjAjMRAwDgYDVQQLDAdUZXN0T3JnMQ8wDQYDVQQDDAZUZXN0Q0EwgZ8w\n" +
+        "DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOtKS4ZrsM3ansd61ZxitcrN0w184I+A\n" +
+        "z0kyrSP1eMtlam+cC2U91NpTz11FYV4XUfBhqqxaXW043AWTUer8pS90Pt4sCrUX\n" +
+        "COx1+QA1M3ZhbZ4sTM7XQ90JbGaBJ/sEza9mlQP7hQ2yQO/hATKbP6J5qvgG2sT2\n" +
+        "S2WYjEgwNwmFAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQ/CXEpnx2WY4LJtv4jwE\n" +
+        "4jIVirur3pdzV5oBhPyqqHMsyhQBkukCfX7uD7L5wN1+xuM81DfANpIxlnUfybp5\n" +
+        "CpjcmktLpmyK4kJ6XnSd2blbLOIpsr9x6FqxPxpVDlyw/ySHYrIG/GZdsLHgmzGn\n" +
+        "B06jeYzH8OLf879VxAxSsPc=\n" +
+        "-----END CERTIFICATE-----";
+
+    /*
+     * Subject: OU=TestOrg, CN=TestEE0
+     * Issuer: OU=TestOrg, CN=TestCA
+     * Validity
+     *     Not Before: Feb 26 22:55:12 2014 GMT
+     *     Not After : Feb 25 22:55:12 2025 GMT
+     * Version 1
+     */
+    static String EECertStr =
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIIBtjCCAR8CAQQwDQYJKoZIhvcNAQEFBQAwIzEQMA4GA1UECwwHVGVzdE9yZzEP\n" +
+        "MA0GA1UEAwwGVGVzdENBMB4XDTE0MDIyNjIyNTUxMloXDTI1MDIyNTIyNTUxMlow\n" +
+        "JDEQMA4GA1UECwwHVGVzdE9yZzEQMA4GA1UEAwwHVGVzdEVFMDCBnzANBgkqhkiG\n" +
+        "9w0BAQEFAAOBjQAwgYkCgYEAt8xz9W3ruCTHjSOtTX6cxsUZ0nRP6EavEfzgcOYh\n" +
+        "CXGA0gr+viSHq3c2vQBxiRny2hm5rLcqpPo+2OxZtw/ajxfyrV6d/r8YyQLBvyl3\n" +
+        "xdCZdOkG1DCM1oFAQDaSRt9wN5Zm5kyg7uMig5Y4L45fP9Yee4x6Xyh36qYbsR89\n" +
+        "rFMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQDZrPqSo08va1m9TOWOztTuWilGdjK/\n" +
+        "2Ed2WXg8utIpy6uAV+NaOYtHQ7ULQBVRNmwg9nKghbVbh+E/xpoihjl1x7OXass4\n" +
+        "TbwXA5GKFIFpNtDvATQ/QQZoCuCzw1FW/mH0Q7UEQ/9/iJdDad6ebkapeMwtj/8B\n" +
+        "s2IZV7s85CEOXw==\n" +
+        "-----END CERTIFICATE-----";
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() throws Exception {
+
+        String[] certStrs = {EECertStr};
+        String[] trustedCertStrs = {CACertStr};
+        runTest(certStrs, trustedCertStrs);
+
+        System.out.println("Test passed.");
+    }
+
+    private static void runTest(String[] certStrs,
+                                String[] trustedCertStrs)
+            throws Exception {
+
+        CertificateFactory cf = CertificateFactory.getInstance("X509");
+
+        // Generate the CertPath from the certs named in certStrs
+        ArrayList<X509Certificate> certs = new ArrayList<>();
+        for (String certStr : certStrs) {
+            certs.add(generateCert(certStr, cf));
+        }
+        CertPath cp = cf.generateCertPath(certs);
+
+        // Generate the set of Trust Anchors from the certs named in
+        // trustedCertStrs
+        Set<TrustAnchor> trustAnchors = new HashSet<>();
+        for (String trustedCertStr : trustedCertStrs) {
+            TrustAnchor ta = new TrustAnchor(generateCert(trustedCertStr, cf),
+                                             null);
+            trustAnchors.add(ta);
+        }
+        PKIXParameters params = new PKIXParameters(trustAnchors);
+        params.setDate(new Date(114, 3, 1));   // 2014-03-01
+        params.setRevocationEnabled(false);
+
+        // Attempt to validate the CertPath. If no exception thrown, successful.
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
+        cpv.validate(cp, params);
+        System.out.println("CertPath validation successful.");
+    }
+
+    private static X509Certificate generateCert(String certStr,
+                                                CertificateFactory cf)
+            throws Exception {
+        ByteArrayInputStream stream
+                = new ByteArrayInputStream(certStr.getBytes());
+        return (X509Certificate) cf.generateCertificate(stream);
+
+    }
+}
diff --git a/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/DoubleStreamTestDataProvider.java b/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/DoubleStreamTestDataProvider.java
index 700d9c9..614cafa 100644
--- a/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/DoubleStreamTestDataProvider.java
+++ b/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/DoubleStreamTestDataProvider.java
@@ -35,7 +35,8 @@
     private static final double[] to1 = new double[1];
     private static final double[] to10 = new double[10];
     private static final double[] to100 = new double[100];
-    private static final double[] to1000 = new double[1000];
+    // Android-changed: remove 0..1000 test data from data providers.
+    // private static final double[] to1000 = new double[1000];
     private static final double[] reversed = new double[100];
     private static final double[] ones = new double[100];
     private static final double[] twice = new double[200];
@@ -45,7 +46,9 @@
     private static final Object[][] spliteratorTestData;
 
     static {
-        double[][] arrays = {to0, to1, to10, to100, to1000};
+        // Android-changed: remove 0..1000 test data from data providers.
+        // double[][] arrays = {to0, to1, to10, to100, to1000};
+        double[][] arrays = {to0, to1, to10, to100};
         for (double[] arr : arrays) {
             for (int i = 0; i < arr.length; i++) {
                 arr[i] = i;
@@ -70,7 +73,8 @@
             {"0..1", to1},
             {"0..10", to10},
             {"0..100", to100},
-            {"0..1000", to1000},
+            // Android-changed: remove 0..1000 test data from data providers.
+            // {"0..1000", to1000},
             {"100x[1]", ones},
             {"2x[0..100]", twice},
             {"reverse 0..100", reversed},
diff --git a/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/IntStreamTestDataProvider.java b/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/IntStreamTestDataProvider.java
index 972cf31..f80ba4a 100644
--- a/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/IntStreamTestDataProvider.java
+++ b/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/IntStreamTestDataProvider.java
@@ -35,7 +35,8 @@
     private static final int[] to1 = new int[1];
     private static final int[] to10 = new int[10];
     private static final int[] to100 = new int[100];
-    private static final int[] to1000 = new int[1000];
+    // Android-changed: remove 0..1000 test data from data providers.
+    // private static final int[] to1000 = new int[1000];
     private static final int[] reversed = new int[100];
     private static final int[] ones = new int[100];
     private static final int[] twice = new int[200];
@@ -45,7 +46,8 @@
     private static final Object[][] spliteratorTestData;
 
     static {
-        int[][] arrays = {to0, to1, to10, to100, to1000};
+        // Android-changed: remove 0..1000 test data from data providers.
+        int[][] arrays = {to0, to1, to10, to100};
         for (int[] arr : arrays) {
             for (int i = 0; i < arr.length; i++) {
                 arr[i] = i;
@@ -70,7 +72,8 @@
             {"0..1", to1},
             {"0..10", to10},
             {"0..100", to100},
-            {"0..1000", to1000},
+            // Android-changed: remove 0..1000 test data from data providers.
+            // {"0..1000", to1000},
             {"100x[1]", ones},
             {"2x[0..100]", twice},
             {"reverse 0..100", reversed},
diff --git a/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/LongStreamTestDataProvider.java b/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/LongStreamTestDataProvider.java
index 63a39e6..abee8fd 100644
--- a/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/LongStreamTestDataProvider.java
+++ b/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/LongStreamTestDataProvider.java
@@ -35,7 +35,8 @@
     private static final long[] to1 = new long[1];
     private static final long[] to10 = new long[10];
     private static final long[] to100 = new long[100];
-    private static final long[] to1000 = new long[1000];
+    // Android-changed: remove 0..1000 test data from data providers.
+    // private static final long[] to1000 = new long[1000];
     private static final long[] reversed = new long[100];
     private static final long[] ones = new long[100];
     private static final long[] twice = new long[200];
@@ -45,7 +46,8 @@
     private static final Object[][] spliteratorTestData;
 
     static {
-        long[][] arrays = {to0, to1, to10, to100, to1000};
+        // Android-changed: remove 0..1000 test data from data providers.
+        long[][] arrays = {to0, to1, to10, to100};
         for (long[] arr : arrays) {
             for (int i = 0; i < arr.length; i++) {
                 arr[i] = i;
@@ -70,7 +72,8 @@
             {"0..1", to1},
             {"0..10", to10},
             {"0..100", to100},
-            {"0..1000", to1000},
+            // Android-changed: remove 0..1000 test data from data providers.
+            // {"0..1000", to1000},
             {"100x[1]", ones},
             {"2x[0..100]", twice},
             {"reverse 0..100", reversed},
diff --git a/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/StreamTestDataProvider.java b/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/StreamTestDataProvider.java
index f933d4c..0c741bf 100644
--- a/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/StreamTestDataProvider.java
+++ b/ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/StreamTestDataProvider.java
@@ -42,7 +42,8 @@
     private static final Integer[] to1 = new Integer[1];
     private static final Integer[] to10 = new Integer[10];
     private static final Integer[] to100 = new Integer[100];
-    private static final Integer[] to1000 = new Integer[1000];
+    // Android-changed: remove 0..1000 test data from data providers.
+    // private static final Integer[] to1000 = new Integer[1000];
     private static final Integer[] reversed = new Integer[100];
     private static final Integer[] ones = new Integer[100];
     private static final Integer[] twice = new Integer[200];
@@ -53,7 +54,8 @@
     private static final Object[][] spliteratorTestData;
 
     static {
-        Integer[][] arrays = {to0, to1, to10, to100, to1000};
+        // Android-changed: remove 0..1000 test data from data providers.
+        Integer[][] arrays = {to0, to1, to10, to100};
         for (Integer[] arr : arrays) {
             for (int i = 0; i < arr.length; i++) {
                 arr[i] = i;
@@ -78,7 +80,8 @@
             {"0..1", to1},
             {"0..10", to10},
             {"0..100", to100},
-            {"0..1000", to1000},
+            // Android-changed: remove 0..1000 test data from data providers.
+            // {"0..1000", to1000},
             {"100x[1]", ones},
             {"2x[0..100]", twice},
             {"reverse 0..100", reversed},
diff --git a/openjdk_java_files.mk b/openjdk_java_files.mk
index 3b38617..7c13ca6 100644
--- a/openjdk_java_files.mk
+++ b/openjdk_java_files.mk
@@ -1342,6 +1342,26 @@
     ojluni/src/main/java/javax/sql/StatementEventListener.java \
     ojluni/src/main/java/sun/reflect/CallerSensitive.java \
 
+# Stubs needed to satisfy javac's dependencies when compiling lambda code. These are
+# not used on Android devices or required by the Jack compiler.
+#
+# On aosp/master:
+# openjdk_lambda_stub_files : These are included in core-oj as stubs
+# openjdk_lambda_duplicate_stub_files : These contain complete implementations in core-oj.
+#
+# On older platforms : Both sets of stub files are used and core-oj does not contain
+# any of these classes.
+openjdk_lambda_stub_files := \
+    ojluni/src/lambda/java/java/lang/invoke/LambdaMetafactory.java \
+    ojluni/src/lambda/java/java/lang/invoke/SerializedLambda.java
+openjdk_lambda_duplicate_stub_files := \
+    ojluni/src/lambda/java/java/lang/invoke/CallSite.java \
+    ojluni/src/lambda/java/java/lang/invoke/MethodHandles.java \
+    ojluni/src/lambda/java/java/lang/invoke/LambdaConversionException.java \
+    ojluni/src/lambda/java/java/lang/invoke/MethodHandle.java \
+    ojluni/src/lambda/java/java/lang/invoke/MethodType.java \
+
+
 # NOTE: Files in java/lang/invoke are listed here because they're not being made public
 # until the entire package is available for use.
 openjdk_java_files := \
@@ -1596,13 +1616,10 @@
     ojluni/src/main/java/sun/security/provider/certpath/PKIXMasterCertPathValidator.java \
     ojluni/src/main/java/sun/security/provider/certpath/PolicyChecker.java \
     ojluni/src/main/java/sun/security/provider/certpath/PolicyNodeImpl.java \
-    ojluni/src/main/java/sun/security/provider/certpath/ReverseBuilder.java \
-    ojluni/src/main/java/sun/security/provider/certpath/ReverseState.java \
     ojluni/src/main/java/sun/security/provider/certpath/RevocationChecker.java \
     ojluni/src/main/java/sun/security/provider/certpath/State.java \
     ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilder.java \
     ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderException.java \
-    ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderParameters.java \
     ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderResult.java \
     ojluni/src/main/java/sun/security/provider/certpath/URICertStore.java \
     ojluni/src/main/java/sun/security/provider/certpath/Vertex.java \
@@ -1741,21 +1758,3 @@
     $(openjdk_javadoc_files) \
     $(openjdk_lambda_stub_files)
 
-# Stubs needed to satisfy javac's dependencies when compiling lambda code. These are
-# not used on Android devices or required by the Jack compiler.
-#
-# On aosp/master:
-# openjdk_lambda_stub_files : These are included in core-oj as stubs
-# openjdk_lambda_duplicate_stub_files : These contain complete implementations in core-oj.
-#
-# On older platforms : Both sets of stub files are used and core-oj does not contain
-# any of these classes.
-openjdk_lambda_stub_files := \
-    ojluni/src/lambda/java/java/lang/invoke/LambdaMetafactory.java \
-    ojluni/src/lambda/java/java/lang/invoke/SerializedLambda.java
-openjdk_lambda_duplicate_stub_files := \
-    ojluni/src/lambda/java/java/lang/invoke/CallSite.java \
-    ojluni/src/lambda/java/java/lang/invoke/MethodHandles.java \
-    ojluni/src/lambda/java/java/lang/invoke/LambdaConversionException.java \
-    ojluni/src/lambda/java/java/lang/invoke/MethodHandle.java \
-    ojluni/src/lambda/java/java/lang/invoke/MethodType.java \
diff --git a/tools/upstream/oj_upstream_comparison.py b/tools/upstream/oj_upstream_comparison.py
index deab5fb..76d63aa 100755
--- a/tools/upstream/oj_upstream_comparison.py
+++ b/tools/upstream/oj_upstream_comparison.py
@@ -85,43 +85,34 @@
             return result
     return None
 
-
-# For files with N and M lines, respectively, this runs in time
-# O(N+M) if the files are identical or O(N*M) if not. This could
-# be improved to O(D*(N+M)) for files with at most D lines
-# difference by only considering array elements within D cells
-# from the diagonal.
-def edit_distance_lines(file_a, file_b):
+# For lists of length N and M, respectively, this runs in time O(N*M).
+# This could be improved to O(D*(N+M)) for lists with distance <= D by
+# only considering array elements within D cells of the diagonal.
+def edit_distance(a, b):
     """
-    Computes the line-based edit distance between two text files, i.e.
-    the smallest number of line deletions, additions or replacements
-    that would transform the content of one file into that of the other.
+    Computes the line-based edit distance between two lists, i.e.
+    the smallest number of list items to delete, insert or replace
+    that would transform the content of one list into the other.
     """
-    if filecmp.cmp(file_a, file_b, shallow=False):
-        return 0 # files identical
-    with open(file_a) as f:
-        lines_a = f.readlines()
-    with open(file_b) as f:
-        lines_b = f.readlines()
-    prev_cost = range(0, len(lines_b) + 1)
-    for end_a in range(1, len(lines_a) + 1):
+    prev_cost = range(0, len(b) + 1)
+    for end_a in range(1, len(a) + 1):
         # For each valid index i, prev_cost[i] is the edit distance between
-        # lines_a[:end_a-1] and lines_b[:i].
+        # a[:end_a-1] and b[:i].
         # We now calculate cur_cost[end_b] as the edit distance between
-        # line_a[:end_a] and lines_b[:end_b]
+        # a[:end_a] and b[:end_b]
         cur_cost = [end_a]
-        for end_b in range(1, len(lines_b) + 1):
+        for end_b in range(1, len(b) + 1):
             c = min(
-                cur_cost[-1] + 1, # append line from b
-                prev_cost[end_b] + 1, # append line from a
-                # match or replace line
-                prev_cost[end_b - 1] + (0 if lines_a[end_a - 1] == lines_b[end_b - 1] else 1)
+                cur_cost[-1] + 1, # append item from b
+                prev_cost[end_b] + 1, # append item from a
+                # match or replace item
+                prev_cost[end_b - 1] + (0 if a[end_a - 1] == b[end_b - 1] else 1)
                 )
             cur_cost.append(c)
         prev_cost = cur_cost
     return prev_cost[-1]
 
-def compare_to_upstreams_and_save(out_file, build_top, upstream_root, upstreams, rel_paths, best_only=False):
+def compare_to_upstreams_and_save(out_file, build_top, upstream_root, upstreams, rel_paths):
     """
     Prints tab-separated values comparing ojluni files vs. each
     upstream, for each of the rel_paths, suitable for human
@@ -144,13 +135,21 @@
             if upstream_file is None:
                 upstream_comparison = "missing"
             else:
-                edit_distance = edit_distance_lines(upstream_file, ojluni_file)
-                if edit_distance == 0:
+                if filecmp.cmp(upstream_file, ojluni_file, shallow=False):
+                    distance = 0
                     upstream_comparison = "identical"
                 else:
-                    upstream_comparison = "different (%d lines)" % (edit_distance)
-                if edit_distance < best_distance:
-                    best_distance = edit_distance
+                    with open(upstream_file) as f:
+                        lines_a = f.readlines()
+                    with open(ojluni_file) as f:
+                        lines_b = f.readlines()
+                    distance = edit_distance(lines_a, lines_b)
+                    # 0% for identical files
+                    # 100% for totally different files or where one file is empty
+                    percent_different = 100.0 * distance / max(len(lines_a), len(lines_b))
+                    upstream_comparison = "%.1f%% different (%d lines)" % (percent_different, distance)
+                if distance < best_distance:
+                    best_distance = distance
                     guessed_upstream = upstream
             upstream_comparisons.append(upstream_comparison)
         writer.writerow([rel_path, guessed_upstream ] + upstream_comparisons)
diff --git a/tzdata/shared2/src/main/libcore/tzdata/shared2/TimeZoneDistro.java b/tzdata/shared2/src/main/libcore/tzdata/shared2/TimeZoneDistro.java
index dd01fb0..9358c70 100644
--- a/tzdata/shared2/src/main/libcore/tzdata/shared2/TimeZoneDistro.java
+++ b/tzdata/shared2/src/main/libcore/tzdata/shared2/TimeZoneDistro.java
@@ -37,6 +37,9 @@
     /** The name of the file inside the distro containing ICU TZ data. */
     public static final String ICU_DATA_FILE_NAME = "icu/icu_tzdata.dat";
 
+    /** The name of the file inside the distro containing time zone lookup data. */
+    public static final String TZLOOKUP_FILE_NAME = "tzlookup.xml";
+
     /**
      * The name of the file inside the distro containing the distro version information.
      * The content is ASCII bytes representing a set of version numbers. See {@link DistroVersion}.
diff --git a/tzdata/tools2/src/main/libcore/tzdata/update2/tools/CreateTimeZoneDistro.java b/tzdata/tools2/src/main/libcore/tzdata/update2/tools/CreateTimeZoneDistro.java
index 4b70152..7fabf7a 100644
--- a/tzdata/tools2/src/main/libcore/tzdata/update2/tools/CreateTimeZoneDistro.java
+++ b/tzdata/tools2/src/main/libcore/tzdata/update2/tools/CreateTimeZoneDistro.java
@@ -30,7 +30,7 @@
  * A command-line tool for creating a timezone update distro.
  *
  * Args:
- * tzdata.properties file - the file describing the distro (see template file in tzdata/tools)
+ * tzdata.properties file - the file describing the distro (see template file in tzdata/tools2)
  * output file - the name of the file to be generated
  */
 public class CreateTimeZoneDistro {
@@ -56,8 +56,9 @@
                 Integer.parseInt(getMandatoryProperty(p, "revision")));
         TimeZoneDistroBuilder builder = new TimeZoneDistroBuilder()
                 .setDistroVersion(distroVersion)
-                .setTzData(getMandatoryPropertyFile(p, "bionic.file"))
-                .setIcuData(getMandatoryPropertyFile(p, "icu.file"));
+                .setTzDataFile(getMandatoryPropertyFile(p, "bionic.file"))
+                .setIcuDataFile(getMandatoryPropertyFile(p, "icu.file"))
+                .setTzLookupFile(getMandatoryPropertyFile(p, "tzlookup.file"));
 
         TimeZoneDistro distro = builder.build();
         File outputFile = new File(args[1]);
diff --git a/tzdata/tools2/src/main/libcore/tzdata/update2/tools/TimeZoneDistroBuilder.java b/tzdata/tools2/src/main/libcore/tzdata/update2/tools/TimeZoneDistroBuilder.java
index 4c12e96..9860489 100644
--- a/tzdata/tools2/src/main/libcore/tzdata/update2/tools/TimeZoneDistroBuilder.java
+++ b/tzdata/tools2/src/main/libcore/tzdata/update2/tools/TimeZoneDistroBuilder.java
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 import libcore.tzdata.shared2.DistroException;
@@ -34,6 +35,7 @@
     private DistroVersion distroVersion;
     private byte[] tzData;
     private byte[] icuData;
+    private String tzLookupXml;
 
     public TimeZoneDistroBuilder setDistroVersion(DistroVersion distroVersion) {
         this.distroVersion = distroVersion;
@@ -56,11 +58,11 @@
         return this;
     }
 
-    public TimeZoneDistroBuilder setTzData(File tzDataFile) throws IOException {
-        return setTzData(readFileAsByteArray(tzDataFile));
+    public TimeZoneDistroBuilder setTzDataFile(File tzDataFile) throws IOException {
+        return setTzDataFile(readFileAsByteArray(tzDataFile));
     }
 
-    public TimeZoneDistroBuilder setTzData(byte[] tzData) {
+    public TimeZoneDistroBuilder setTzDataFile(byte[] tzData) {
         this.tzData = tzData;
         return this;
     }
@@ -71,15 +73,24 @@
         return this;
     }
 
-    public TimeZoneDistroBuilder setIcuData(File icuDataFile) throws IOException {
-        return setIcuData(readFileAsByteArray(icuDataFile));
+    public TimeZoneDistroBuilder setIcuDataFile(File icuDataFile) throws IOException {
+        return setIcuDataFile(readFileAsByteArray(icuDataFile));
     }
 
-    public TimeZoneDistroBuilder setIcuData(byte[] icuData) {
+    public TimeZoneDistroBuilder setIcuDataFile(byte[] icuData) {
         this.icuData = icuData;
         return this;
     }
 
+    public TimeZoneDistroBuilder setTzLookupFile(File tzLookupFile) throws IOException {
+        return setTzLookupXml(readFileAsUtf8(tzLookupFile));
+    }
+
+    public TimeZoneDistroBuilder setTzLookupXml(String tzlookupXml) {
+        this.tzLookupXml = tzlookupXml;
+        return this;
+    }
+
     // For use in tests.
     public TimeZoneDistroBuilder clearIcuDataForTests() {
         this.icuData = null;
@@ -102,6 +113,10 @@
             if (icuData != null) {
                 addZipEntry(zos, TimeZoneDistro.ICU_DATA_FILE_NAME, icuData);
             }
+            if (tzLookupXml != null) {
+                addZipEntry(zos, TimeZoneDistro.TZLOOKUP_FILE_NAME,
+                        tzLookupXml.getBytes(StandardCharsets.UTF_8));
+            }
         } catch (IOException e) {
             throw new DistroException("Unable to create zip file", e);
         }
@@ -140,7 +155,7 @@
     /**
      * Returns the contents of 'path' as a byte array.
      */
-    public static byte[] readFileAsByteArray(File file) throws IOException {
+    private static byte[] readFileAsByteArray(File file) throws IOException {
         byte[] buffer = new byte[8192];
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         try (FileInputStream  fis = new FileInputStream(file)) {
@@ -151,5 +166,12 @@
         }
         return baos.toByteArray();
     }
+
+    /**
+     * Returns the contents of 'path' as a String, having interpreted the file as UTF-8.
+     */
+    private String readFileAsUtf8(File file) throws IOException {
+        return new String(readFileAsByteArray(file), StandardCharsets.UTF_8);
+    }
 }
 
diff --git a/tzdata/tools2/testing/prepareTzDataUpdates.sh b/tzdata/tools2/testing/prepareTzDataUpdates.sh
index 5b9b2ab..9a41942 100755
--- a/tzdata/tools2/testing/prepareTzDataUpdates.sh
+++ b/tzdata/tools2/testing/prepareTzDataUpdates.sh
@@ -26,6 +26,7 @@
 
 # Get the current tzdata version and find both the previous and new versions.
 TZDATA=libc/zoneinfo/tzdata
+TZLOOKUP=libc/zoneinfo/tzlookup.xml
 
 TZHEADER=$(head -n1 bionic/$TZDATA | cut -c1-11)
 
@@ -88,6 +89,7 @@
 rules.version=${TZ_PREVIOUS}
 bionic.file=${TMP_PREVIOUS}/tzdata
 icu.file=${TMP_PREVIOUS}/icu_tzdata.dat
+tzlookup.file=${ANDROID_BUILD_TOP}/bionic/${TZLOOKUP}
 EOF
 
 TZ_PREVIOUS_UPDATE_ZIP=update_${TZ_PREVIOUS}_test.zip
@@ -117,6 +119,7 @@
 rules.version=${TZ_CURRENT}
 bionic.file=${TMP_CURRENT}/tzdata
 icu.file=${TMP_CURRENT}/icu_tzdata.dat
+tzlookup.file=${ANDROID_BUILD_TOP}/bionic/${TZLOOKUP}
 EOF
 
 TZ_CURRENT_UPDATE_ZIP=update_${TZ_CURRENT}_test.zip
@@ -146,6 +149,7 @@
 rules.version=${TZ_NEXT}
 bionic.file=${TMP_NEXT}/tzdata
 icu.file=${TMP_NEXT}/icu_tzdata.dat
+tzlookup.file=${ANDROID_BUILD_TOP}/bionic/${TZLOOKUP}
 EOF
 
 TZ_NEXT_UPDATE_ZIP=update_${TZ_NEXT}_test.zip
diff --git a/tzdata/tools2/tzupdate.properties b/tzdata/tools2/tzupdate.properties
index 82fa2c4..264960b 100644
--- a/tzdata/tools2/tzupdate.properties
+++ b/tzdata/tools2/tzupdate.properties
@@ -8,4 +8,4 @@
 
 bionic.file=
 icu.file=
-
+tzlookup.file=
diff --git a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
index e1ed794..baadc85 100644
--- a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
+++ b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
@@ -25,6 +25,7 @@
 import libcore.tzdata.shared2.FileUtils;
 import libcore.tzdata.shared2.StagedDistroOperation;
 import libcore.tzdata.shared2.TimeZoneDistro;
+import libcore.util.TimeZoneFinder;
 import libcore.util.ZoneInfoDB;
 
 /**
@@ -150,6 +151,7 @@
                 return INSTALL_FAIL_RULES_TOO_OLD;
             }
 
+            // Validate the tzdata file.
             File zoneInfoFile = new File(workingDir, TimeZoneDistro.TZDATA_FILE_NAME);
             ZoneInfoDB.TzData tzData = ZoneInfoDB.TzData.loadTzData(zoneInfoFile.getPath());
             if (tzData == null) {
@@ -164,7 +166,23 @@
             } finally {
                 tzData.close();
             }
-            // TODO(nfuller): Add deeper validity checks / canarying before applying.
+
+            // Validate the tzlookup.xml file.
+            File tzLookupFile = new File(workingDir, TimeZoneDistro.TZLOOKUP_FILE_NAME);
+            if (!tzLookupFile.exists()) {
+                Slog.i(logTag, "Update not applied: " + tzLookupFile + " does not exist");
+                return INSTALL_FAIL_BAD_DISTRO_STRUCTURE;
+            }
+            try {
+                TimeZoneFinder timeZoneFinder =
+                        TimeZoneFinder.createInstance(tzLookupFile.getPath());
+                timeZoneFinder.validate();
+            } catch (IOException e) {
+                Slog.i(logTag, "Update not applied: " + tzLookupFile + " failed validation", e);
+                return INSTALL_FAIL_VALIDATION_ERROR;
+            }
+
+            // TODO(nfuller): Add validity checks for ICU data / canarying before applying.
             // http://b/31008728
 
             Slog.i(logTag, "Applying time zone update");
diff --git a/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java b/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
index cf40bf6..3af9f15 100644
--- a/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
+++ b/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
@@ -24,7 +24,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Arrays;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
@@ -190,7 +189,7 @@
         assertNoInstalledDistro();
     }
 
-    /** Tests that a distro with a missing file will not update the content. */
+    /** Tests that a distro with a missing tzdata file will not update the content. */
     public void testStageInstallWithErrorCode_missingTzDataFile() throws Exception {
         TimeZoneDistro stagedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
         assertEquals(
@@ -209,7 +208,7 @@
         assertNoInstalledDistro();
     }
 
-    /** Tests that a distro with a missing file will not update the content. */
+    /** Tests that a distro with a missing ICU file will not update the content. */
     public void testStageInstallWithErrorCode_missingIcuFile() throws Exception {
         TimeZoneDistro stagedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
         assertEquals(
@@ -228,6 +227,44 @@
         assertNoInstalledDistro();
     }
 
+    /** Tests that a distro with a missing tzlookup file will not update the content. */
+    public void testStageInstallWithErrorCode_missingTzLookupFile() throws Exception {
+        TimeZoneDistro stagedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
+        assertEquals(
+                TimeZoneDistroInstaller.INSTALL_SUCCESS,
+                installer.stageInstallWithErrorCode(stagedDistro.getBytes()));
+        assertInstallDistroStaged(stagedDistro);
+
+        TimeZoneDistro incompleteDistro =
+                createValidTimeZoneDistroBuilder(NEWER_RULES_VERSION, 1)
+                        .setTzLookupXml(null)
+                        .buildUnvalidated();
+        assertEquals(
+                TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+                installer.stageInstallWithErrorCode(incompleteDistro.getBytes()));
+        assertInstallDistroStaged(stagedDistro);
+        assertNoInstalledDistro();
+    }
+
+    /** Tests that a distro with a bad tzlookup file will not update the content. */
+    public void testStageInstallWithErrorCode_badTzLookupFile() throws Exception {
+        TimeZoneDistro stagedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
+        assertEquals(
+                TimeZoneDistroInstaller.INSTALL_SUCCESS,
+                installer.stageInstallWithErrorCode(stagedDistro.getBytes()));
+        assertInstallDistroStaged(stagedDistro);
+
+        TimeZoneDistro incompleteDistro =
+                createValidTimeZoneDistroBuilder(NEWER_RULES_VERSION, 1)
+                        .setTzLookupXml("<foo />")
+                        .buildUnvalidated();
+        assertEquals(
+                TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR,
+                installer.stageInstallWithErrorCode(incompleteDistro.getBytes()));
+        assertInstallDistroStaged(stagedDistro);
+        assertNoInstalledDistro();
+    }
+
     /**
      * Tests that an update will be unpacked even if there is a partial update from a previous run.
      */
@@ -449,6 +486,17 @@
 
         byte[] bionicTzData = createTzData(rulesVersion);
         byte[] icuData = new byte[] { 'a' };
+        String tzlookupXml = "<timezones>\n"
+                + "  <countryzones>\n"
+                + "    <country code=\"us\">\n"
+                + "      <id>America/New_York\"</id>\n"
+                + "      <id>America/Los_Angeles</id>\n"
+                + "    </country>\n"
+                + "    <country code=\"gb\">\n"
+                + "      <id>Europe/London</id>\n"
+                + "    </country>\n"
+                + "  </countryzones>\n"
+                + "</timezones>\n";
         DistroVersion distroVersion = new DistroVersion(
                 DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
                 DistroVersion.CURRENT_FORMAT_MINOR_VERSION,
@@ -456,8 +504,9 @@
                 revision);
         return new TimeZoneDistroBuilder()
                 .setDistroVersion(distroVersion)
-                .setTzData(bionicTzData)
-                .setIcuData(icuData);
+                .setTzDataFile(bionicTzData)
+                .setIcuDataFile(icuData)
+                .setTzLookupXml(tzlookupXml);
     }
 
     private void assertInstallDistroStaged(TimeZoneDistro expectedDistro) throws Exception {
@@ -476,6 +525,9 @@
         File icuFile = new File(stagedTzDataDir, TimeZoneDistro.ICU_DATA_FILE_NAME);
         assertTrue(icuFile.exists());
 
+        File tzLookupFile = new File(stagedTzDataDir, TimeZoneDistro.TZLOOKUP_FILE_NAME);
+        assertTrue(tzLookupFile.exists());
+
         // Assert getStagedDistroState() is reporting correctly.
         StagedDistroOperation stagedDistroOperation = installer.getStagedDistroOperation();
         assertNotNull(stagedDistroOperation);
@@ -494,6 +546,8 @@
                     actualFile = icuFile;
                 } else if (entryName.endsWith(TimeZoneDistro.TZDATA_FILE_NAME)) {
                     actualFile = bionicFile;
+                } else if (entryName.endsWith(TimeZoneDistro.TZLOOKUP_FILE_NAME)) {
+                    actualFile = tzLookupFile;
                 } else {
                     throw new AssertionFailedError("Unknown file found");
                 }