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 <countryzones>
+ * 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 <countryzones> 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 <countryzones> 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");
}